interp:library "afnix-sio"
Input and output streams
The afnix-sio module is based on facilities provided by two base classes, namely, the Input stream and the Output stream. Both classes have associated predicates with the name input-p and output-p. The base class associated is the Stream class those sole purpose is to define the stream coding mode.
Stream base class
The Stream class is the base class for the Input and Output classes. The Stream class is used to define the stream coding mode that affects how characters are read or written. When a stream operates in byte mode, each character is assumed to be encoded in one byte. In that case, the input stream methods read and rduc are equivalent and no transformation is performed when writing characters. This behavior is the default stream behavior. For certain stream, like terminal, this behavior is changed depending on the current localization settings. For instance, if the current locale is operating with an UTF-8 codeset, the Terminal stream coding mode is automatically adjusted to reflect this situation. Since the US-ASCII codeset is predominant and the default steam coding mode is the byte mode, there should be no conflict during the read and write operations.
Stream transcoding
The Stream class provides the support for the transcoding of different codesets. All ISO-8859 codesets are supported. Since the AFNIX engine operates internally with Unicode characters, the transcoding operation takes care of changing a character in one particular codeset into its equivalent Unicode representation. This operation is done for an input stream that operates in byte mode. For an output stream, the opposite operation is done. An internal Unicode characters representation is therefore mapped into a particular codeset. Note that only the codeset characters can be mapped.
| Codeset | Description |
| DEFAULT | Default codeset, i.e US-ASCII |
| ISO-01 | ISO-8859-1 codeset |
| ISO-02 | ISO-8859-2 codeset |
| ISO-03 | ISO-8859-3 codeset |
| ISO-04 | ISO-8859-4 codeset |
| ISO-05 | ISO-8859-5 codeset |
| ISO-06 | ISO-8859-6 codeset |
| ISO-07 | ISO-8859-7 codeset |
| ISO-08 | ISO-8859-8 codeset |
| ISO-09 | ISO-8859-9 codeset |
| ISO-10 | ISO-8859-10 codeset |
| ISO-11 | ISO-8859-11 codeset |
| ISO-13 | ISO-8859-13 codeset |
| ISO-14 | ISO-8859-14 codeset |
| ISO-15 | ISO-8859-15 codeset |
| ISO-16 | ISO-8859-16 codeset |
| UTF-08 | Unicode UTF-8 codeset |
The set-encoding-mode can be used to set the stream encoding codeset. The operates either by enumeration or string. The get-encoding-mode returns the stream encoding mode. There are some time good reasons to force a stream encoding mode. For example, a file encoded in UTF-8 that is read will require this call since the default stream mode is to work in byte mode. It should be noted that there is a difference between the enumeration and the string encoding mode. The enumeration mode defines whether the stream operates in byte or UTF-8 mode. When the stream operates in byte mode, it is also necessary to define the transcoding mode with the set-transcoding-mode method. For simplicity, the string version of the set-encoding-mode takes care of setting both the stream mode and the transcoding mode. It is also worth to note that internally, the Stream class is derived from the Transcoder class.
Input stream
The Input base class has several method for reading and testing for byte availability. Moreover, the class provides a push-back buffer. Reading bytes is in the form of three methods. The read method without argument returns the next available byte or the end-of-fileeof. With an integer argument, the read method returns a Buffer with at most the number of requested bytes. The readln method returns the next available line. When it is necessary to read characters instead of bytes, the rduc is more appropriate since it returns an Unicode character.
Output stream
The Output base class provides the base methods to write to an output stream. The write method takes literal objects which are automatically converted to string representation and then written to the output stream. Note that for the case of a Buffer object, it is the buffer itself that take a stream argument and not the opposite.
The valid-p predicate
The input stream provides a general mechanism to test and read for bytes. The base method is the valid-p predicate that returns true if a byte can be read from the stream. It is important to understand its behavior which depends on the stream type. Without argument, the valid-p predicate checks for an available byte from the input stream. This predicate will block if no byte is available. On the other end, for a bounded stream like an input file, the method will not block at the end of file. With one integer argument, the valid-p predicate will timeout after the specified time specified in milliseconds. This second behavior is particularly useful with unbound stream like socket stream.
The eof-p predicate
The eof-p predicate does not take argument. The predicate behaves like not (valid-p 0). However, there are more subtle behaviors. For an input file, the predicate will return true if and only if a byte cannot be read. If a byte has been pushed-back and the end-of-file marker is reached, the method will return false. For an input terminal, the method returns true if the user and entered the end-of-file byte. Once again, the method reacts to the contents of the push-back buffer. For certain input stream, like a tcp socket, the method will return true when no byte can be read, that is here, the connection has been closed. For an udp socket, the method will return true when all datagram bytes have be read.
The read method
The read method is sometimes disturbing. Nevertheless, the method is a blocking one and will return a byte when completed. The noticeable exception is the returned byte when an end-of-file marker has been reached. The method returns the ctrl-d byte. Since a binary file might contains valid byte like ctrl-d it is necessary to use the valid-p or eof-p predicate to check for a file reading completion. This remark apply also to bounded streams like a tcp socket. For some type of streams like a udp socket, the method will block when all datagram bytes have been consumed and no more datagram has arrived. With this kind of stream, there is no end-of-file condition and therefore care should be taken to properly assert the stream content. This last remark is especially true for the readln method. The method will return when the end-of-file marker is reached, even if a newline byte has not been read. With an udp socket, such behavior will not happen.
Buffer read mode
The read method with an integer argument, returns a buffer with at least the number of bytes specified as an argument. This method is particularly useful when the contents has a precise size. The method returns a Buffer object which can later be used to read, or transform bytes. Multi-byte conversion to number should use such approach. The read method does not necessarily returns the number of requested bytes. Once the buffer is returned, the length method can be used to check the buffer size. Note also the existence of the to-string method which returns a string representation of the buffer.
# try to read 256 bytes const buf (is:read 256) # get the buffer size println (buf:length) # get a string representation println (buf:to-string)
File stream
The afnix-sio module provides two classes for file access. The InputFile class open a file for input. The OutputFile class opens a file for output. The InputFile class is derived from the Input base class. The OutputFile class is derived from the Output class. By default an output file is created if it does not exist. If the file already exist, the file is truncated to 0. Another constructor for the output file gives more control about this behavior. It takes two boolean flags that defines the truncate and append mode.
# load the module interp:library "afnix-sio" # create an input file by name const if (afnix:sio:InputFile "orig.txt") # create an output file by name const of (afnix:sio:OutputFile "copy.txt")
Stream information
Both InputFile and OutputFile supports the get-name method which returns the file name.
println (if:get-name) println (of:get-name)
Predicates are also available for these classes. The input-file-p returns true for an input file object.The output-file-p returns true for an output file object.
afnix:sio:input-p if afnix:sio:output-p of afnix:sio:input-file-p if afnix:sio:output-file-p of
Reading and writing
The read method reads a byte on an input stream. The write method writes one or more literal arguments on the output stream. The writeln method writes one or more literal arguments followed by a newline byte on the output stream. The newline method write a newline byte on the output stream. The eof-p predicate returns true for an input stream, if the stream is at the end. The valid-p predicate returns true if an input stream is in a valid state. With these methods, copying a file is a simple operation.
# load the module and open the files interp:library "afnix-sio" const if (afnix:sio:InputFile "orig.txt") const of (afnix:sio:OutputFile "copy.txt") # loop in the input file and write while (if:valid-p) (of:write (if:read))
The use of the readln method can be more effective. The example below is a simple cat program which take the file name an argument.
# cat a file on the output terminal
# usage: axi 0601.als file
# get the io module
interp:library "afnix-sio"
# cat a file
const cat (name) {
const f (afnix:sio:InputFile name)
while (f:valid-p) (println (f:readln))
f:close
}
# get the file
if (== 0 (interp:argv:length)) {
errorln "usage: axi 0601.als file"
} {
cat (interp:argv:get 0)
}
Multiplexing
I/O multiplexing is the ability to manipulate several streams at the same time and process one at a time. Although the use of threads reduce the needs for i/o multiplexing, there is still situations where they are needed. In other words, I/O multiplexing is identical to the valid-p predicate, except that it works with several stream objects.
Selector object
I/O multiplexing is accomplished with the Selector class. The constructor takes 0 or several stream arguments. The class manages automatically to differentiate between Input stream and Output streams. Once the class is constructed, it is possible to get the first stream ready for reading or writing or all of them. We assume in the following example that is and os are respectively an input and an output stream.
# create a selector const slt (afnix:sio:Selector is) # at this stage the selector has one stream # the add method can add more streams slt:add os
The add method adds a new stream to the selector. The stream must be either an Input and Output stream or an exception is raised. The input-length method returns the number of input streams in this selector. The output-length method returns the number of output streams in this selector. The input-get method returns the selector input stream by index. The output-get method returns the selector output stream by index.
Waiting for i/o event
The wait and wait-all methods can be used to detect a status change in the selector. Without argument both methods will block indefinitely until one stream change. With one integer argument, both method blocks until one stream change or the integer argument timeout expires. The timeout is expressed in milliseconds. Note that 0 indicates an immediate return. The wait method returns the first stream which is ready either for reading or writing depending whether it is an input or output stream. The wait-all method returns a vector with all streams that have changed their status. The wait method returns nil if the no stream have changed. Similarly, the wait-all method returns an empty vector.
# wait for a status change const is (slt:wait) # is is ready for reading - make sure it is an input one if (afnix:sio:input-p is) (is:read)
A call to the wait method will always returns the first input stream.
Multiplexing policy
When used with several input streams in a multi-threaded context, the selector behavior can becomes quite complicated. Either wait and wait-all methods check first the input streams push-back buffer. If one or several buffers are not empty, the method returns with these streams. During this operation, the input streams are locked, so no other thread can push-back a byte. The selector then checks for status change and unlock the streams. Note that the output streams are not locked. Note also that a thread which rely on the input stream push-back method to release a selector will result in a dead lock.
Terminal streams
Terminal streams are another kind of streams available in the standard i/o module. The InputTerm, OutputTerm and ErrorTerm classes are low level classes used to read or write from or to the standard streams. The basic methods to read or write are the same as the file streams. Reading from the input terminal is not a good idea, since the class does not provide any formatting capability. One may prefer to use the Terminal class. The use of the output terminal or error terminal streams is convenient when the interpreter standard streams have been changed but one still need to print to the terminal.
Terminal class
The Terminal class combines an input stream and an output stream with some line editing capabilities. When the class is created, the constructed attempts to detect if the input and output streams are bounded to a terminal (i.e tty). If the line editing capabilities can be loaded (i.e non canonical mode), the terminal is initialized for line editing. Arrows, backspace, delete and other control sequences are available when using the read-line method. The standard methods like read or readln do not use the line editing features. When using a terminal, the prompt can be set to whatever the user wishes with the methods set-primary-prompt or set-secondary-prompt. A secondary prompt is displayed when the read-line method is called with the boolean argument false.
const term (Terminal) term:set-primary-prompt "demo:" const line (term:read-line) errorln line
Using the error terminal
The ErrorTerm class is the most frequently used class for printing data on the standard error stream. AFNIX provides the reserved keyword error or errorln to write on the interpreter error stream. If the interpreter error stream has been changed, the use of the ErrorTerm will provide the facility required to print directly on the terminal. The cat program can be rewritten to do exactly this.
# cat a file on the error terminal
# get the io module
interp:library "afnix-sio"
# cat a file
const cat (name es) {
const f (afnix:sio:InputFile name)
while (f:valid-p) (es:writeln (f:readln))
f:close
}
Directory
The Directory class provides a facility to manipulate directories. A directory object is created either by name or without argument by considering the current working directory. Once the directory object is created, it is possible to retrieve its contents, create new directory or remove empty one.
Reading a directory
A Directory object is created either by name or without argument. With no argument, the current directory is opened. When the current directory is opened, its full name is computed internally and can be retrieved with the get-name method.
# print the current directory const pwd (afnix:sio:Directory) println (pwd:get-name)
Once the directory object is opened, it is possible to list its contents. The get-list method returns the full contents of the directory object. The get-files method returns a list of files in this directory. The get-subdirs method returns a list of sub directories in this directory.
# print a list of files const pwd (afnix:sio:Directory) const lsf (d:get-files) for (name) (lsf) (println name)
Creating and removing directories
The mkdir and rmdir methods can be used to create or remove a directory. Both methods take a string argument and construct a full path name from the directory name and the argument. This approach has the advantage of being file system independent. If the directory already exists, the mkdir methods succeeds. The rmdir method requires the directory to be empty.
const tmp (afnix:sio:Directory (afnix:sio:absolute-path "tmp")) const exp (tmp:mkdir "examples") const lsf (exp:get-files) println (lsf:length) tmp:rmdir "examples"
The function absolute-path constructs an absolute path name from the argument list. If relative path needs to be constructed, the function relative-path might be used instead.
Logtee
The Logtee class is a message logger facility associated with an output stream. When a message is added to the logger object, the message is also sent to the output stream, depending on the controlling flags. The name "logtee" comes from the contraction of "logger" and "tee". One particularity of the class is that without a stream, the class behaves like a regular logger.
Creating a logger
The Logtee default constructor creates a standard logger object without an output stream. The instance can also be created by size or with an output stream or both. A third method can also attach an information string.
# create a logger with the interpreter stream const log (Logtee (interp:get-output-stream)) assert true (logger-p log)
Adding messages
The process of adding messages is similar to the regular logger. The only difference is that the message is placed on the output stream if a control flag is set and the message level is less or equal the report level. In the other word, the control flag controls the message display -- the tee operation -- while the report level filters some of the messages.
log:add 2 "a level 2 message"
The set-tee method sets the control flag. The set-report-level method sets the report level. Note that the set-report-level and its associated get-report-level method is part of the base Logger class.
Path name
The Pathname class is a base class designed to ease the manipulation of system path. It is particularly useful when it come to manipulate directory component.
Creating a path name
A path name is created either by file name or by file and directory name. In the first case, only the file name is used. In the second case, the full path name is characterized.
# create a new path name const path (afnix:sio:Pathname "axi")
Adding a directory path
The best way to add a directory path is to use the absolute-path or the relative-path functions.
# adding a directory path const name (afnix:sio:absolute-path "usr" "bin") path:setdirectory-name name
Getting the path information
The path information can be obtained individually or globally. The get-file-name and get-directory-name methods return respectively the file and directory name. The get-root method returns the root component of the directory name. The get-full method returns the full path name.
| Symbol | Description |
| afnix-sio | module |
| afnix:sio | nameset |
Transcoder
The Transcoder class is a codeset transcoder class. The class is responsible to map a byte character in a given codeset into its associated unicode character. It should be noted that not all characters can be transcoded.
Predicate
Inheritance
Constants
Constructors
Methods
Stream
The Stream class is a base class for the AFNIX standard streams. The class is automatically constructed by a derived class and provides the common methods for all streams.
Predicate
Inheritance
Constants
Methods
Input
The Input class is a base class for the AFNIX standard i/o module. The class is automatically constructed by a derived class and provides the common methods for all input streams.
Predicate
Inheritance
Methods
InputFile
The InputFile class provide the facility for an input file stream. An input file instance is created with a file name. If the file does not exist or cannot be opened, an exception is raised. The InputFile class is derived from the Input class.
Predicate
Inheritance
Constructors
Methods
InputMapped
The InputMapped class provide the facility for an input file stream with offset and size. The class is similar to the InputFile class except that the constructor can also accepts an integer offset and size argument. If the file offset or size are out of range, the class behaves like an input file. If the file does not exist or cannot be opened, an exception is raised.
Predicate
Inheritance
Constructors
Methods
InputString
The InputString class provide the facility for an input string stream. The class is initialized or set with a string and then behaves like a stream. This class is very useful to handle generic stream method without knowing what kind of stream is behind it.
Predicate
Inheritance
Constructors
Methods
InputTerm
The InputTerm class provide the facility for an input terminal stream. The input terminal reads byte from the standard input stream. No line editing facility is provided with this class This is a low level class, and normally, the Terminal class should be used instead.
Predicate
Inheritance
Constructors
Methods
Output
The Output class is a base class for the AFNIX standard i/o module. The class is automatically constructed by a derived class and provide the common methods for all output streams.
Predicate
Inheritance
Methods
OutputFile
The OutputFile class provide the facility for an output file stream. An output file instance is created with a file name. If the file does not exist, it is created. If the file cannot be created, an exception is raised. Once the file is created, it is possible to write literals. The class is derived from the Output class. By default an output file is created if it does not exist. If the file already exist, the file is truncated to 0. Another constructor for the output file gives more control about this behavior. It takes two boolean flags that defines the truncate and append mode. The t-flag is the truncate flag. The a-flag is the append flag.
Predicate
Inheritance
Constructors
Methods
OutputString
The OutputString class provide the facility for an output string stream. The class is initially empty and acts as a buffer which accumulate the write method bytes. The to-string method can be used to retrieve the buffer content.
Predicate
Inheritance
Constructors
Methods
OutputTerm
The OutputTerm class provide the facility for an output terminal. The output terminal is defined as the standard output stream. If the standard error stream needs to be used, the ErrorTerm class is more appropriate.
Predicate
Inheritance
Constructors
Terminal
The Terminal class provides the facility for an i/o terminal with line editing capability. The class combines the InputTerm and OutputTerm methods.
Predicate
Inheritance
Constructors
Methods
Selector
The Selector class provides some facilities to perform i/o multiplexing. The constructor takes 0 or several stream arguments.The class manages automatically to differentiate between Input and Output streams. Once the class is constructed, it is possible to get the first stream ready for reading or writing or all of them. It is also possible to add more steams after construction with the add method. When used with several input streams in a multi-threaded context, the selector behavior can becomes quite complicated. Either wait and wait-all methods check first the input streams push-back buffer. If one or several buffer are not empty, the method returns with these streams. During this operation, the input streams are locked, so no other thread can push-back a byte. The selector then checks for status change and unlock the streams. Note that the output streams are not locked. Note also that a thread which rely on the input stream push-back method to release a selector will result in a dead-lock.
Predicate
Inheritance
Constructors
Methods
Logtee
The Logtee class provides the facility of a logger object associated with an output stream. When a message is added, the message is written to the output stream depending on an internal flag. By default the tee mode is false and can be activated with the set-tee method.
Predicate
Inheritance
Constructors
Methods
Pathname
The Pathname class is a base class designed to manipulate system i/o paths. The class operates with a directory name and a file name. Both names are kept separated to ease the path manipulation. The path components can be extracted individually. However, it shall be noted that the first componenent has a special treatment to process the root directory name.
Predicate
Inheritance
Constructors
Methods
Functions
Directory
The Directory class provides some facilities to access a directory. By default, a directory object is constructed to represent the current directory. With one argument, the object is constructed from the directory name. Once the object is constructed, it is possible to retrieve its content.
Predicate
Inheritance
Constructors
Methods
Logtee
The Logtee class is a message logger facility associated with an output stream. When a message is added to the logger object, the message is also sent to the output stream, depending on the controlling flags. The name "logtee" comes from the contraction of "logger" and "tee". One particularity of the class is that without a stream, the class behaves like a regular logger.
Predicate
Inheritance
Constructors
Methods