# LispKit Port

Ports represent abstractions for handling input and output. They are used to access files, devices, and similar things on the host system on which LispKit is running.

An input port is a LispKit object that can deliver data upon command, while an output port is an object that can accept data. In LispKit, input and output port types are disjoint, i.e. a port is either an input or an output port.

Different port types operate on different data. LispKit provides two differnt types of ports: textual ports and binary ports. Textual ports and binary ports are disjoint, i.e. a port is either textual or binary.

A textual port supports reading or writing of individual characters from or to a backing store containing characters using `read-char` and `write-char`, and it supports operations defined in terms of characters, such as `read` and `write`.

A binary port supports reading or writing of individual bytes from or to a backing store containing bytes using `read-u8` and `write-u8` below, as well as operations defined in terms of bytes.

## Default ports

current-output-port [parameter object]
current-input-port
current-error-port

These parameter objects represent the current default input port, output port, or error port (an output port), respectively. These parameter objects can be overridden with `parameterize`.

default-output-port [constant]
default-input-port

These two ports are the initial values of `current-output-port` and `current-input-port` when LispKit gets initialized. They are typically referring to the default output and input device of the system on which LispKit is running.

## Predicates

(port? obj) [procedure]

Returns `#t` if obj is a port object; otherwise `#f` is returned.

(input-port? obj) [procedure]
(output-port? obj)

These predicates return `#t` if obj is an input port or output port; otherwise they return `#f`.

(textual-port? obj) [procedure]
(binary-port? obj)

These predicates return `#t` if obj is a textual or a binary port; otherwise they return `#f`.

(input-port-open? port) [procedure]
(output-port-open? port)

Returns `#t` if port is still open and capable of performing input or output, respectively, and `#f` otherwise.

(eof-object? obj) [procedure]

Returns `#t` if obj is an end-of-file object, otherwise returns `#f`.

## General ports

(close-port port) [procedure]
(close-input-port port)
(close-output-port port)

Closes the resource associated with port, rendering the port incapable of delivering or accepting data. It is an error to apply `close-input-port` and `close-output-port` to a port which is not an input or output port, respectively. All procedures for closing ports have no effect if the provided port has already been closed.

(with-input-from-port port thunk) [procedure]
(with-output-to-port port thunk)

The given port is made to be the value returned by `current-input-port` or `current-output-port` (as used by `(read)`, `(write obj)`, and so forth). The thunk is then called with no arguments. When the thunk returns, the port is closed and the previous default is restored. It is an error if thunk does not accept zero arguments. Both procedures return the values yielded by thunk. If an escape procedure is used to escape from the continuation of these procedures, they behave exactly as if the current input or output port had been bound dynamically with `parameterize`.

(call-with-port port proc) [procedure]

The `call-with-port` procedure calls proc with port as an argument. It is an error if proc does not accept one argument.

If proc returns, then the port is closed automatically and the values yielded by proc are returned. If proc does not return, then the port will not be closed automatically unless it is possible to prove that the port will never again be used for a read or write operation.

This is necessary, because LispKit’s escape procedures have unlimited extent and thus it is possible to escape from the current continuation but later to resume it. If LispKit would be permitted to close the port on any escape from the current continuation, then it would be impossible to write portable code using both `call-with-current-continuation` and `call-with-port`.

## File ports

(open-input-file filepath) [procedure]
(open-input-file filepath fail)

Takes a filepath referring to an existing file and returns a textual input port that is capable of delivering data from the file. If the file does not exist or cannot be opened, an error that satisfies `file-error?` is signaled if argument fail is not provided. If fail is provided, it is returned in case an error occured.

(open-binary-input-file filepath) [procedure]
(open-binary-input-file filepath fail)

Takes a filepath referring to an existing file and returns a binary input port that is capable of delivering data from the file. If the file does not exist or cannot be opened, an error that satisfies `file-error?` is signaled if argument fail is not provided. If fail is provided, it is returned in case an error occured.

(open-output-file filepath) [procedure]
(open-output-file filepath fail)

Takes a filepath referring to an output file to be created and returns a textual output port that is capable of writing data to the new file. If a file with the given name exists already, the effect is unspecified. If the file cannot be opened, an error that satisfies `file-error?` is signaled if argument fail is not provided. If fail is provided, it is returned in case an error occured.

(open-binary-output-file filepath) [procedure]
(open-binary-output-file filepath fail)

Takes a filepath referring to an output file to be created and returns a binary output port that is capable of writing data to the new file. If a file with the given name exists already, the effect is unspecified. If the file cannot be opened, an error that satisfies `file-error?` is signaled if argument fail is not provided. If fail is provided, it is returned in case an error occured.

(with-input-from-file filepath thunk) [procedure]
(with-output-to-file filepath thunk)

The file determined by filepath is opened for input or output as if by `open-input-file` or `open-output-file`, and the new port is made to be the value returned by `current-input-port` or `current-output-port` (as used by `(read)`, `(write obj)`, and so forth). The thunk is then called with no arguments. When the thunk returns, the port is closed and the previous default is restored. It is an error if thunk does not accept zero arguments. Both procedures return the values yielded by thunk. If an escape procedure is used to escape from the continuation of these procedures, they behave exactly as if the current input or output port had been bound dynamically with `parameterize`.

(call-with-input-file filepath proc) [procedure]
(call-with-output-file filepath proc)

These procedures create a textual port obtained by opening the file referred to by filepath (a string) for input or output as if by `open-input-file` or `open-output-file`. This port and proc are then passed to a procedure equivalent to `call-with-port`. It is an error if proc does not accept one argument.

## String ports

(open-input-string str) [procedure]

Takes a string and returns a textual input port that delivers characters from the string. If the string is modified, the effect is unspecified.

(open-output-string) [procedure]

Returns a textual output port that will accumulate characters for retrieval by `get-output-string`.

    (parameterize ((current-output-port (open-output-string)))
      (display "piece")
      (display " by piece ")
      (display "by piece.")
      (get-output-string (current-output-port)))
        ⇒ "piece by piece by piece."

(get-output-string port) [procedure]

It is an error if port was not created with `open-output-string`.

Returns a string consisting of the characters that have been output to port so far in the order they were output.

    (parameterize ((current-output-port (open-output-string)))
      (display "piece")
      (display " by piece ")
      (display "by piece.")
      (newline)
      (get-output-string (current-output-port)))
        ⇒ "piece by piece by piece.\n"

(with-input-from-string str thunk) [procedure]

String str is opened for input as if by `open-input-string`, and the new textual string port is made to be the value returned by `current-input-port`. The thunk is then called with no arguments. When the thunk returns, the port is closed and the previous default is restored. It is an error if thunk does not accept zero arguments. `with-input-from-string` returns the values yielded by thunk. If an escape procedure is used to escape from the continuation of these procedures, they behave exactly as if the current input port had been bound dynamically with `parameterize`.

(with-output-to-string thunk) [procedure]

A new string output port is created as if by calling `open-output-string`, and the new port is made to be the value returned by `current-output-port`. The thunk is then called with no arguments. When the thunk returns, the port is closed and the previous default is restored. It is an error if thunk does not accept zero arguments. Both procedures return the values yielded by thunk. If an escape procedure is used to escape from the continuation of these procedures, they behave exactly as if the current input or output port had been bound dynamically with `parameterize`.

(call-with-output-string proc) [procedure]

The procedure proc is called with one argument, a textual output port. The values yielded by proc are ignored. When proc returns, `call-with-output-string` returns the port’s accumulated output as a string.

This procedure is defined as follows:

    (define (call-with-output-string procedure)
      (let ((port (open-output-string)))
        (procedure port)
        (get-output-string port)))

## Bytevector ports

(open-input-bytevector bvector) [procedure]

Takes a bytevector bvector and returns a binary input port that delivers bytes from the bytevector bvector.

(open-output-bytevector) [procedure]

Returns a binary output port that will accumulate bytes for retrieval by `get-output-bytevector`.

(get-output-bytevector port) [procedure]

It is an error if port was not created with `open-output-bytevector`. `get-output-bytevector` returns a bytevector consisting of the bytes that have been output to the port so far in the order they were output.

(call-with-output-bytevector proc) [procedure]

The procedure proc gets called with one argument, a binary output port. The values yielded by procedure proc are ignored. When it returns, `call-with-output-bytevector` returns the port’s accumulated output as a newly allocated bytevector.

This procedure is defined as follows:

    (define (call-with-output-bytevector procedure)
      (let ((port (open-output-bytevector)))
        (procedure port)
        (get-output-bytevector port)))

## URL ports

(open-input-url url) [procedure]
(open-input-url url timeout)
(open-input-url url timeout fail)

Takes a url referring to an existing resource and returns a textual input port that is capable of reading data from the resource (e.g. via HTTP). timeout specifies a timeout in seconds as a flonum for the operation to wait. If no data is available, the procedure will fail either by throwing an exception or by returning value fail if provided.

(open-binary-input-url url) [procedure]
(open-binary-input-url url timeout)
(open-binary-input-url url timeout fail)

Takes a url referring to an existing resource and returns a binary input port that is capable of reading data from the resource (e.g. via HTTP). timeout specifies a timeout in seconds as a flonum for the operation to wait. If no data is available, the procedure will fail either by throwing an exception or by returning value fail if provided.

(with-input-from-url url thunk) [procedure]

The given url is opened for input as if by `open-input-url`, and the new input port is made to be the value returned by `current-input-port`. The thunk is then called with no arguments. When the thunk returns, the port is closed and the previous default is restored. It is an error if thunk does not accept zero arguments. The procedure returns the values yielded by thunk. If an escape procedure is used to escape from the continuation of this procedure, they behave exactly as if `current-input-port` had been bound dynamically with `parameterize`.

(call-with-input-url url proc) [procedure]

`call-with-input-url` creates a textual input port by opening the resource at url for input as if by `open-input-url`. This port and proc are then passed to a procedure equivalent to `call-with-port`. It is an error if proc does not accept one argument. Here is an implementation of `call-with-input-url`:

    (define (call-with-input-url url proc)
      (let* ((port (open-input-url url))
             (res (proc port)))
        (close-input-port port)
        res))

(try-call-with-input-url url proc thunk) [procedure]

`try-call-with-input-url` creates a textual input port by opening the resource at url for input as if by `open-input-url`. This port and proc are then passed to a procedure equivalent to `call-with-port` in case it was possible to open the port. If the port couldn’t be opened, thunk gets invoked. It is an error if proc does not accept one argument and if thunk requires at least one argument. Here is an implementation of `try-call-with-input-url`:

    (define (try-call-with-input-url url proc thunk)
      (let ((port (open-input-url url 60.0 #f)))
        (if port
            (car (cons (proc port) (close-input-port port)))
            (thunk))))

## Asset ports

(open-input-asset name type) [procedure]
(open-input-asset name type dir)

This function can be used to open a textual LispKit asset file located in one of LispKit’s asset paths. An asset is identified via a file name, a file type, and an optional directory path dir. name, type, and dir are all strings. `open-input-asset` constructs a relative file path in the following way (assuming name does not have a suffix already):

dir/name.type

It then searches the asset paths in their given order for a file matching this relative file path. Once the first matching file is found, the file is opened as a text file and a corresponding textual input port that is capable of reading data from the file is returned. It is an error if no matching asset is found.

(open-binary-input-asset name type) [procedure]
(open-binary-input-asset name type dir)

This function can be used to open a binary LispKit asset file located in one of LispKit’s asset paths. An asset is identified via a file name, a file type, and an optional directory path dir. name, type, and dir are all strings. `open-input-asset` constructs a relative file path in the following way (assuming name does not have a suffix already):

dir/name.type

It then searches the asset paths in their given order for a file matching this relative file path. Once the first matching file is found, the file is opened as a binary file and a corresponding binary input port that is capable of reading data from the file is returned. It is an error if no matching asset is found.

## Reading from ports

If port is omitted from any input procedure, it defaults to the value returned by `(current-input-port)`. It is an error to attempt an input operation on a closed port.

(read) [procedure]
(read port)

The `read` procedure converts external representations of Scheme objects into the objects themselves by parsing the input. `read` returns the next object parsable from the given textual input port, updating port to point to the first character past the end of the external representation of the object.

If an end of file is encountered in the input before any characters are found that can begin an object, then an end-of-file object is returned. The port remains open, and further attempts to read will also return an end-of-file object. If an end of file is encountered after the beginning of an object’s external representation, but the external representation is incomplete and therefore not parsable, an error that satisfies `read-error?` is signaled.

(read-char) [procedure]
(read-char port)

Returns the next character available from the textual input port, updating port to point to the following character. If no more characters are available, an end-of-file object is returned.

(peek-char) [procedure]
(peek-char port)

Returns the next character available from the textual input port, but without updating port to point to the following character. If no more characters are available, an end-of-file object is returned.

Note: The value returned by a call to `peek-char` is the same as the value that would have been returned by a call to `read-char` with the same port. The only difference is that the very next call to `read-char` or `peek-char` on that port will return the value returned by the preceding call to `peek-char`. In particular, a call to `peek-char` on an interactive port will hang waiting for input whenever a call to `read-char` would have hung.

(char-ready?) [procedure]
(char-ready? port)

Returns `#t` if a character is ready on the textual input port and returns `#f` otherwise. If `char-ready?` returns `#t` then the next `read-char` operation on the given port is guaranteed not to hang. If the port is at end of file, then `char-ready?` returns #t.

Rationale: The `char-ready?` procedure exists to make it possible for a program to accept characters from interactive ports without getting stuck waiting for input. Any input editors associated with such ports must ensure that characters whose existence has been asserted by `char-ready?` cannot be removed from the input. If `char-ready?` were to return `#f` at end of file, a port at end of file would be indistinguishable from an interactive port that has no ready characters.

(read-token) [procedure]
(read-token port)
(read-token port charset)

Returns the next token of text available from the textual input port, updating port to point to the following character. A token is a non-empty sequence of characters delimited by characters from character set charset. Tokens never contain characters from charset. charset defaults to the set of all whitespace and newline characters.

(read-line obj) [procedure]
(read-line port)

Returns the next line of text available from the textual input port, updating port to point to the following character. If an end of line is read, a string containing all of the text up to (but not including) the end of line is returned, and port is updated to point just past the end of line. If an end of file is encountered before any end of line is read, but some characters have been read, a string containing those characters is returned. If an end of file is encountered before any characters are read, an end-of-file object is returned. For the purpose of this procedure, an end of line consists of either a linefeed character, a carriage return character, or a sequence of a carriage return character followed by a linefeed character.

(read-string k) [procedure]
(read-string k port)

Reads the next k characters, or as many as are available before the end of file, from the textual input port into a newly allocated string in left-to-right order and returns the string. If no characters are available before the end of file, an end-of-file object is returned.

(read-u8) [procedure]
(read-u8 port)

Returns the next byte available from the binary input port, updating port to point to the following byte. If no more bytes are available, an end-of-file object is returned.

(peek-u8 obj) [procedure]

Returns the next byte available from the binary input port, but without updating port to point to the following byte. If no more bytes are available, an end-of-file object is returned.

(u8-ready?) [procedure]
(u8-ready? port)

Returns `#t` if a byte is ready on the binary input port and returns `#f` otherwise. If `u8-ready?` returns `#t` then the next `read-u8` operation on the given port is guaranteed not to hang. If the port is at end of file then `u8-ready?` returns `#t`.

(read-bytevector k) [procedure]
(read-bytevector k port)

Reads the next k bytes, or as many as are available before the end of file, from the binary input port into a newly allocated bytevector in left-to-right order and returns the bytevector. If no bytes are available before the end of file, an end-of-file object is returned.

(read-bytevector! bvector) [procedure]
(read-bytevector! bvector port)
(read-bytevector! bvector port start)
(read-bytevector! bvector port start end)

Reads the next end − start bytes, or as many as are available before the end of file, from the binary input port into bytevector bvector in left-to-right order beginning at the start position. If end is not supplied, reads until the end of bytevector bvector has been reached. If start is not supplied, reads beginning at position 0. Returns the number of bytes read. If no bytes are available, an end-of-file object is returned.

## Writing to ports

If port is omitted from any output procedure, it defaults to the value returned by `(current-output-port)`. It is an error to attempt an output operation on a closed port.

(write obj) [procedure]
(write obj port)

Writes a representation of obj to the given textual output port. Strings that appear in the written representation are enclosed in quotation marks, and within those strings backslash and quotation mark characters are escaped by backslashes. Symbols that contain non-ASCII characters are escaped with vertical lines. Character objects are writ- ten using the `#\` notation.

If obj contains cycles which would cause an infinite loop using the normal written representation, then at least the objects that form part of the cycle will be represented using datum labels. Datum labels will not be used if there are no cycles.

(write-shared obj) [procedure]
(write-shared obj port)

The `write-shared` procedure is the same as `write`, except that shared structures will be represented using datum labels for all pairs and vectors that appear more than once in the output.

(write-simple obj) [procedure]
(write-simple obj port)

The `write-simple` procedure is the same as `write`, except that shared structures will never be represented using datum labels. This can cause `write-simple` not to terminate if obj contains circular structures.

(write-formatted obj) [procedure]
(write-formatted obj config)
(write-formatted obj config port)

Writes a representation of obj to the given textual output port using formatting configuration config as defined by library `(lispkit format)`. config defines how objects of different types are being written. Using this procedure makes it possible to use custom formatting logic instead of the hardcoded logic as provided by procedure `write`.

(display obj) [procedure]
(display obj port)

Writes a representation of obj to the given textual output port. Strings that appear in the written representation are output as if by `write-string` instead of by `write`. Symbols are not escaped. Character objects appear in the representation as if written by `write-char` instead of by `write`. `display` will not loop forever on self-referencing pairs, vectors, or records.

The `write` procedure is intended for producing machine-readable output and `display` for producing human-readable output.

(display* obj …) [procedure]

Writes a representation of obj … to the current default textual output port. Strings that appear in the written representation are output as if by `write-string` instead of by `write`. Symbols are not escaped. Character objects appear in the representation as if written by `write-char` instead of by `write`. `display*` will not loop forever on self-referencing pairs, vectors, or records.

(display-format [port] [config] [locale] [tabw [linew]] cntrl arg …) [procedure]

`display-format` writes formatted output to a textual output port, outputting the characters of the control string cntrl while interpreting formatting directives embedded in cntrl. The control string syntax and the semantics of the provided arguments matches procedure `format` of library `(lispkit format)`. config refers to a `format-config` object which defines environment variables influencing the output of some formatting directives. locale refers to a locale identifier like symbol `en_US` that is used by locale-specific formatting directives. tabw defines the maximum number of space characters that correspond to a single tab character. linew specifies the number of characters per line; this is used by the justification directive only.

(newline) [procedure]
(newline port)

Writes an end of line to textual output port.

(write-char char) [procedure]
(write-char char port)

Writes the character char (not an external representation of the character) to the given textual output port.

(write-string str) [procedure]
(write-string str port)
(write-string str port start)
(write-string str port start end)

Writes the characters of string str from index start to end (exclusive) in left-to-right order to the textual output port. The default of start is 0, the default of end is the length of str.

(write-u8 byte) [procedure]
(write-u8 byte)

Writes the byte to the given binary output port.

(write-bytevector bvector) [procedure]
(write-bytevector bvector port)
(write-bytevector bvector port start)
(write-bytevector bvector port start end)

Writes the bytes of bytevector bvector from start to end (exclusive) in left-to-right order to the binary output port. The default of start is 0, the default of end is the length of bvector.

(flush-output-port) [procedure]
(flush-output-port port)

Flushes any buffered output from the buffer of the given output port to the underlying file or device.

(eof-object) [procedure]

Returns an end-of-file object.