Handles¶
Schemers will know these as ports.
We want to read from and write to files (and file-like objects) and we do that through handles. In particular, the reader will use a handle to read input from.
From Scheme we also get string handles a very neat abstraction that is right up our shell-street and also means that the reader is equally at home with string handles.
To make all that work there is a high-level interface, the handle, and two low-level implementations, file handle and string handle.
The high-level interface maintains:
a pointer to the low-level stream data – which is file-/string-specific
a lookahead char – think LL(1) grammars
a line number within the stream, starting at 1
Essentially, as bytes are consumed from the stream, any U+000A (LINE FEED) character causes a line number increment.
The line number cannot be maintained and will be set to 0 (zero) if you
seeksomewhere in the stream other than position 0 (zero) which resets the line number to 1.a position (offset) within the stream – always maintained though less human-friendly
the name the user used to access the file
For a string handle the name is a construction from
inputand/oroutput(with a/if necessary),string-handle #and a monotonically increasing number.the actual pathname of the file
For a string handle it is the name, as above.
a table of methods to manipulate the stream
The methods are:
free- used when garbage collecting the handlereadyp- a check that a character is available to be read without blockingNote it will return
#tfor interactive handles at end-of-file.getc- analogous to fgetc(3)eofp- analogous to feof(3)close- analogous to close(2)putc- analogous to fputc(3)puts- analogous to fputs(3)flush- analogous to fflush(3)seek- analogous to fseek(3)There isn’t a
telllow-level method as all the other methods studiously update the stream’s position in the high-level interface andhandle-pos handlewill return itprint- a mechanism to convert an object to a string andputsthat to the streamTo note, here, is that the object is considered to be displayed – in other words, strings are not double-quoted.
We can now define a handle abstraction that almost everything else will use and two specific implementations.
That’s the basic Scheme model but we can (and must!) extend it for our own nefarious shell-like purposes. Our obvious first need is to be able to write into or read from commands and the obvious implementation is a pipe(2).
Hence we get pipe handles which are a variation on a file handle re-using most methods as, ultimately, they are simple wrappers around standard operating system file descriptors. The only useful difference is that you cannot seek on a pipe handle.
As a handy generic form, there are fd handle primitives, ie. file descriptor handle primitives, which are useful generic tests for either a file or a pipe handle.
You can imagine other file descriptor types will be available in due course.
Implementation¶
Following the above description the two sets of interfaces and accessors are reasonably straightforward:
typedef struct idio_handle_methods_s {
void (*free) (struct idio_s *h);
int (*readyp) (struct idio_s *h);
int (*getc) (struct idio_s *h);
int (*eofp) (struct idio_s *h);
int (*close) (struct idio_s *h);
int (*putc) (struct idio_s *h, int c);
ptrdiff_t (*puts) (struct idio_s *h, char *s, size_t slen);
int (*flush) (struct idio_s *h);
off_t (*seek) (struct idio_s *h, off_t offset, int whence);
void (*print) (struct idio_s *h, struct idio_s *o);
} idio_handle_methods_t;
#define IDIO_HANDLE_FLAG_NONE 0
#define IDIO_HANDLE_FLAG_READ (1<<0)
#define IDIO_HANDLE_FLAG_WRITE (1<<1)
#define IDIO_HANDLE_FLAG_CLOSED (1<<2)
#define IDIO_HANDLE_FLAG_FILE (1<<3)
#define IDIO_HANDLE_FLAG_PIPE (1<<4)
#define IDIO_HANDLE_FLAG_STRING (1<<5)
typedef struct idio_handle_s {
struct idio_s *grey;
void *stream; /* file/string specific stream data */
idio_handle_methods_t *methods; /* file/string specific methods */
int lc; /* lookahead char */
off_t line; /* 1+ */
off_t pos; /* position in file: 0+ */
struct idio_s *filename; /* filename the user used */
struct idio_s *pathname; /* pathname or some other identifying data */
} idio_handle_t;
#define IDIO_HANDLE_GREY(H) ((H)->u.handle->grey)
#define IDIO_HANDLE_STREAM(H) ((H)->u.handle->stream)
#define IDIO_HANDLE_METHODS(H) ((H)->u.handle->methods)
#define IDIO_HANDLE_LC(H) ((H)->u.handle->lc)
#define IDIO_HANDLE_LINE(H) ((H)->u.handle->line)
#define IDIO_HANDLE_POS(H) ((H)->u.handle->pos)
#define IDIO_HANDLE_FILENAME(H) ((H)->u.handle->filename)
#define IDIO_HANDLE_PATHNAME(H) ((H)->u.handle->pathname)
#define IDIO_HANDLE_FLAGS(H) ((H)->tflags)
#define IDIO_INPUTP_HANDLE(H) (IDIO_HANDLE_FLAGS(H) & IDIO_HANDLE_FLAG_READ)
#define IDIO_OUTPUTP_HANDLE(H) (IDIO_HANDLE_FLAGS(H) & IDIO_HANDLE_FLAG_WRITE)
#define IDIO_CLOSEDP_HANDLE(H) (IDIO_HANDLE_FLAGS(H) & IDIO_HANDLE_FLAG_CLOSED)
#define IDIO_FILEP_HANDLE(H) (IDIO_HANDLE_FLAGS(H) & IDIO_HANDLE_FLAG_FILE)
#define IDIO_PIPEP_HANDLE(H) (IDIO_HANDLE_FLAGS(H) & IDIO_HANDLE_FLAG_PIPE)
#define IDIO_STRINGP_HANDLE(H) (IDIO_HANDLE_FLAGS(H) & IDIO_HANDLE_FLAG_STRING)
#define IDIO_HANDLE_M_FREE(H) (IDIO_HANDLE_METHODS (H)->free)
#define IDIO_HANDLE_M_READYP(H) (IDIO_HANDLE_METHODS (H)->readyp)
#define IDIO_HANDLE_M_GETC(H) (IDIO_HANDLE_METHODS (H)->getc)
#define IDIO_HANDLE_M_EOFP(H) (IDIO_HANDLE_METHODS (H)->eofp)
#define IDIO_HANDLE_M_CLOSE(H) (IDIO_HANDLE_METHODS (H)->close)
#define IDIO_HANDLE_M_PUTC(H) (IDIO_HANDLE_METHODS (H)->putc)
#define IDIO_HANDLE_M_PUTS(H) (IDIO_HANDLE_METHODS (H)->puts)
#define IDIO_HANDLE_M_FLUSH(H) (IDIO_HANDLE_METHODS (H)->flush)
#define IDIO_HANDLE_M_SEEK(H) (IDIO_HANDLE_METHODS (H)->seek)
#define IDIO_HANDLE_M_PRINT(H) (IDIO_HANDLE_METHODS (H)->print)
Lookahead Char¶
When I first implemented this in C-mode, I could note that:
fgetc(3) returns either an
unsigned charcast to anintor the sentinel valueEOF. We require a sentinel value for our lookahead char andEOFis as handy for us as it was for fgetc(3). So, if the lookahead char isEOFit actually means call fgetc(3) to get the next char.
However, UTF-8 laughs at our simple plan. Or my C laughs at
me. Either way, 0xff – which will be recast to -1, aka, EOF –
is invalid UTF-8 but it is explicitly used because it is invalid in
UTF-8 test cases. So we have to be able to handle -1/EOF as a
viable, if invalid, char.
The upshot of which is, we can’t have a sentinel value in the
lookahead char but have to explicitly call the eofp method
instead. Hardly the end of the world! (But close, right?)
Handles¶
Nothing creates a handle directly. Instead file-handles, pipe handles or string-handles are created which, in turn, create a handle.
So you open-input-file name (where name is a
string) and get in return a handle of the file handle variety.
Similarly, open-output-string returns a handle of the string
handle variety.
You can now run any of the plethora of handle-oriented operations on either.
Reading¶
There is no reader input form for a handle.
Writing¶
As there is no reader input form then if we print a handle out it’ll
take a #<...> (invalid reader) form.
For a handle we can print out some useful info like:
the type:
Hfor handleopen or closed:
o/cfile, pipe or string:
f/p/SSis used for string as in readiness for a socket(2) based handle thenf,pandsmatch up nicely with thef?,p?ands?predicates.open for reading and/or writing:
rand/orwsome fd handle flags:
O_CLOEXECor not:e/!ehere matching theemode flag passed to theopen-*commandsiif the handle is interactiveFif the handle is an original stdio handle (stdin,stdoutorstderr) with theFa mnemonic for the CFILE*objectEif the handle is registered as EOFthe file descriptor
some handle details
the name (the user used – or we concocted)
the line number
the position
Which might give us:
Idio> (current-input-handle)
#<H ofr!iF 0:"*stdin*":2:23>
Idio> osh := (open-output-string)
#<H oSw:"output string-handle #1889":1:0>
In the first instance, #<H ofr!iF 0:"*stdin*":2:23> we can see the
value is:
a handle
open
a file
open for read
does not have
O_CLOEXECset (by us, anyway)is interactive
is constructed from the original STDIO set
the file descriptor is 0
the handle’s name is
*stdin*we are on line 2
position 23
In the second case, #<H oSw:"output string-handle #1889":1:0> we can infer:
it’s a handle
open
a string
writeable
it’s the 1889th string handle this process has created
we’re on line 1
position 0
File Handles¶
File handles were implemented using libc’s FILE type
and you could imagine that all the implementation methods were
essentially the libc equivalents.
However, the introduction of pipe handles (and some recurring “issues” with what was, in effect, double buffered input and output) has had them rewritten as native read(2), write(2) and friends.
The file handle stream data is:
#define IDIO_FILE_HANDLE_FLAG_NONE 0
#define IDIO_FILE_HANDLE_FLAG_EOF (1<<0)
#define IDIO_FILE_HANDLE_FLAG_INTERACTIVE (1<<1)
#define IDIO_FILE_HANDLE_FLAG_STDIO (1<<2)
#define IDIO_FILE_HANDLE_FLAG_CLOEXEC (1<<3)
typedef struct idio_file_handle_stream_s {
int fd;
IDIO_FLAGS_T flags; /* IDIO_FILE_HANDLE_FLAG_* */
char *buf; /* buffer */
int bufsiz;
char *ptr; /* ptr into buffer */
int count; /* bytes in buffer */
} idio_file_handle_stream_t;
Of note here is that we buffer data to and from the stream – essentially we’ve re-implemented STDIO!
There is a generic file opening method:
static IDIO idio_open_file_handle (IDIO filename,
char *pathname,
int fd,
int h_type,
int h_flags,
int s_flags);
with:
filenamethe name the user suppliedpathnamethe “real” name of the opened file – this might be*stdin*, for examplefdis the file descriptorh_typeisIDIO_HANDLE_FLAG_FILE,IDIO_HANDLE_FLAG_PIPEetc..h_flagsare handle flags as per thestruct idio_handle_sstructure definitions_flagsare the (file handle) stream-specific flags as seen above, such as a flag if the file handle is interactive.
Of interest, static IDIO idio_open_std_file_handle (FILE
*filep) is used to alternately wrapper the usual three STDIO
variables, stdin, stdout and stderr, with a Idio
handle. The handle’s name is set to be *stdin*, *stdout* or
*stderr*.
File handles need a finalizer which will close(2) the contained file descriptor when the GC is trying to free the corresponding handle.
file-handle.c¶
src/file-handle.c contains a bundle of functions relating to
the business of finding Idio library files.
We will have the usual sort of library search list environment
variable, cleverly called IDIOLIB. When a user loads a library
file we need to find it.
Note
Before I read of an issue with systemd I had some
preconceived notions about PATH_MAX.
I still do, but hopefully fewer incorrect ones. If I understand
things correctly then PATH_MAX is a (glibc?)
constraint on user-supplied pathnames (as distinct from filenames
in directory entries) but is not a constraint on the pathnames
retrievable from the filesystem.
Causing a bit more fun, PATH_MAX is probably, in reality,
filesystem-dependent, see pathconf(3), and may return a
very large number indeed with the suggestion being there is no
limit in the general case.
As to how to open(2) a file that has a pathname more
than PATH_MAX - 1 bytes you’ll need to refactor the original
pathname into leading directory segments, each up up to
PATH_MAX - 1 bytes, and make repeated calls to
openat(2).
There is a similar note in “APPLICATION USAGE” in pwd(1p).
Most of that work is delegated to idio_libfile_find_C(char *file)
which potters about worrying about:
splitting
IDIOLIBinto directory entriesPATH_MAXwhen appending name elementsvarious library file extensions
The obvious extension is
.idiobut we might have other forms, for example, a compiled format which may require a different reader. Consequently there is, elsewhere, a table of(extension, reader, evaluator)tuples.
—
There were two file loading mechanisms which derived from me changing my mind.
Consider how we read and evaluate files (technically, handles but we’re all used to thinking about files). We could:
while read expression
evaluate expression
run expression
or
read all expressions
evaluate all expressions
run all expressions
I liked the latter as it solved a recurring problem I have when developing scripts, the sort of scripts that take a minute or twenty to chunter through yet you’ve spotted a bugfeature just after it’s started and edit the script. Of course you save the script with a satisfying kerthunk!…while the script is still running. Oops. The next read from the file by Bash will get a garbled string. It’s all gone pear-shaped.
I ran, for a long time, with the latter, “all in one,” then changed my mind (and deleted it). Why? What’s the difference?
The problem is subtle and primarily affects templates, indeed, anything that has a meta-effect on the program, like operators.
The problem is that we say “evaluate” but what we really mean is:
determine the meaning of the expression
implement any template use
prep the code for the VM
But running the code in the VM may or may not happen now.
If I define a template then I cannot use it until the code for the definition is run – which will add to the list of “expander” terms that the evaluator will recognise. That means I cannot use the template until the byte code is run by the VM.
That might not sound like a big problem but it does prevent you defining and using a macro within a module: for the latter variant, the definition isn’t run until we’ve read and evaluated everything. In other words the evaluator will not have been informed that this new template exists until we’ve hit the end of the file and can run all the statements.
Part of this might arise from me not having written enough gnarly code to require a template to be defined and used in the same module but when porting Scheme code you quickly discover that other people are altogether more with it.
*
I did keep the “expression by expression” and “all in one” variants around for a while but eventually ditched the “all in one” when documenting module and realising it was banjaxed for similar reasons.
So, the normal C file loading function is:
IDIO idio_load_file_name (IDIO filename, IDIO cs)
and its Idio equivalent load filename will load
“expression by expression.”
Pipe Handles¶
We would like to read from and write to external commands for which we need to pipe their output from them or pipe our output to them. Maintaining the handle-style is important, hence pipe handles.
The implementation of pipe handles will hold no surprises,
open-input-pipe is essentially the open-input-file-from-fd
style of construction.
Note
There is obvious naming confusion with pipes in that the output from a command will be an input handle as we are reading from that input stream. Similarly the input to a pipe will be an output handle.
So pipe-into returns an output handle and pipe-from returns
an input handle.
A more intriguing question is how do we do this for the user?
We’re a shell and, despite our use of reader operators, have a leaning
towards the cmd args style. Hence, if we want to filter
the output of a command then it makes sense for us to declare that
intention as a cmd, hence:
pipe-from zcat file.tgz | tar tf -
or, maybe:
pipe-from tar tzf file.tgz
ie. note that this works for simple commands or pipelines.
Although what we clearly want pipe-from to do is return us a pipe
handle hence what we should have written is:
ih := pipe-from zcat file.tgz | tar tf -
ih is now a regular (input) handle that we can manipulate is
regular handle-like ways, read-line springs to mind.
close-handle should also spring to mind otherwise, depending on
the scope of ih you’ll be accumulating both unclosed
pipe(2)s and a number of zombie processes.
String Handles¶
String handles are in some sense just the buffer part of a file
handle. For an input string handle we will have initialised the
buffer with whatever the string argument was and with an
output string handle we just keep extending the buffer.
We can zip about inside a string handle, just like a file handle – with the same effects on line number and position.
The string handle stream data looks like:
typedef struct idio_string_handle_stream_s {
char *buf; /* buffer */
size_t blen;
char *ptr; /* ptr into buffer */
char *end; /* end of buffer */
char eof; /* EOF flag */
} idio_string_handle_stream_t;
We don’t get an end-of-file chime as we do with file handles so we
need to fake one up by maintaining and end of buffer marker and
“raise” end of file when we get there.
—
It turns out that string handles are really handy for generating error
messages within the code base from, say, a bit of C string
and a bit of Idio object. Take, for example, the calls to
fdopen(3) in src/file-handle.c, above.
If the system call fails we want to raise an error condition indicating the system call and the arguments:
idio_error_system_errno ("fdopen",
IDIO_LIST2 (idio_string_C (name),
idio_string_C (mode)),
IDIO_C_FUNC_LOCATION ());
We’re passing
a C string with the problem’s contextual message
an Idio list of the
nameassociated with the file descriptor (defaulting to/dev/fd/X) and themode(defaulting tor/w).IDIO_C_FUNC_LOCATION ()is a macro which, when compiled withDEBUG, brings together__FILE__,__LINE__and GNU’s__func__for some handy diagnosis.
idio_error_system_errno() is a wrapper to idio_error_system()
with errno added as an argument and we get:
void idio_error_system (char *msg, IDIO args, int err, IDIO c_location)
{
...
IDIO msh = idio_open_output_string_handle_C ();
idio_display_C (msg, msh);
if (idio_S_nil != args) {
idio_display_C (": ", msh);
idio_display (args, msh);
}
idio_display_C (": ", msh);
idio_display_C (strerror (err), msh);
IDIO location = idio_vm_source_location ();
IDIO dsh = idio_open_output_string_handle_C ();
#ifdef IDIO_DEBUG
idio_display (c_location, dsh);
#endif
IDIO c = idio_struct_instance (idio_condition_system_error_type,
IDIO_LIST4 (idio_get_output_string (msh),
location,
idio_get_output_string (dsh),
idio_C_int (err)));
idio_raise_condition (idio_S_true, c);
}
All conditions derived from ^idio-error take three standard
parameters: message, location and detail.
We can use two helper functions, idio_display() for displaying
Idio values and idio_display_C() for displaying
C strings. Of course, at the end of some tumultuous
computation and constructing a (huge?) string in C,
idio_display() will also be calling idio_display_C().
Here we can first create a “message” string handle, msh, into
which we print (display!) the C string msg (fdopen
in this case) then, if some args were passed, add a C
: string then whatever the printed representation of the
args are.
Add on another : then the operating system’s description
of the system call error for a bit of normalised guidance. Secondly,
recover the user-land source code location (from the EXPR register
in the thread). Finally, we can create a “detail” string handle and
add the C source location if we’re running under debug for
extra clarification. The fourth parameter for a ^system-error is,
in effect, errno.
Finally, we can construct a condition, of the “system error” type which is expecting a further four arguments including the re-constituted strings from the two output string handles.
handle.c¶
src/handle.c is the front-end for manipulating handles and its
methods are largely just calls to the underlying file handle or string
handle methods.
Many of them differ in that they do not require a handle to be passed to them, it is an optional argument. The premise, here, is that the default should be the current input handle or current output handle. Entities maintained by the current thread.
idio_getc_handle() (and idio_ungetc_handle()) maintain the
line and position as U+000A (LINE FEED) characters pass through.
read¶
While we’re here we can muse on an example of naming hell.
read is a heavily overloaded name. At our lowest level, in
libc, the primitive read is read(2).
However, in Idio, the primitive read is an invocation of
the reader, an instruction to return a complete Idio “line”
of input (which might be multiple lines if we have some unmatched
parentheses or braces or line continuations or …).
We can complicate the issue with read-expr which will have the
reader consume a single expression (and not consume expressions to the
end of the line).
As a user of Idio I’m unlikely to call either of those and am
more likely interested in reading a line of text from a file, hence,
read-line and its greedier sibling, read-lines.
As a user I might also want to read-char to get back the next
(UTF-8 encoded) Unicode code point. There is no (exposed)
read-byte function.
read (and read-expr) isn’t really used other than for testing
so I suspect they could be re-worked into a more reader-oriented name
or, indeed, namespace clearing the way for “normal” usage of the names
by users.
load¶
There is a handle-variant of load: load-handle.
More interesting is the C-only
idio_load_handle_interactive() which is the REPL.
Previously, I’ve mentioned that I don’t want to spend much time here as I’d rather target scripting, however it does get used.
I’ve noted above that the REPL is C-only in the sense that
there isn’t an Idio entry-point. Like most things it could
probably do with being re-arranged slightly so that there is an
Idio primitive which calls the existing
idio_load_handle_interactive() but one we could replace with a
pure-Idio function.
In the meanwhile, the only useful difference between the REPL and
load-handle-ebe is that:
a prompt is printed to the current error handle
and the value returned by the expression is printed to the current output handle
Notably, then
the prompt is currently fixed to being the name of the current module followed by
>, soIdio>by default. That could easily be passed off to some Idio function which might use whatever to construct a prompt.Bash, obviously, uses
PS1with its myriad of baskslash-escaped special characters.there is no readline-style interactive editing
(which is annoying – although you can fall back on rlwrap)
Operations¶
File Handles¶
See src/file-handle.c.
Note that when manipulating a file descriptor (rather than a file
handle) you are manipulating a C-int, not a fixnum (and certainly
not a bignum). Whilst you can construct an arbitrary C-int and
therefore “file descriptor”, like regular systems programming, it
behooves the user to choose values wisely. Normally, you would
receive such a value from a call known to provide one and pass it
around opaquely.
- function open-file-from-fd fd [name [mode]]¶
construct a file handle from fd using the optional name instead of the default /dev/fd/fd and the optional mode mode instead of
re
- function open-input-file-from-fd fd [name]¶
construct a file handle from fd using the optional name instead of the default /dev/fd/fd and the optional mode mode instead of
re
- function open-output-file-from-fd fd [name]¶
construct a file handle from fd using the optional name instead of the default /dev/fd/fd and the optional mode mode instead of
we
- function open-input-pipe fd [name]¶
construct a pipe handle from fd using the optional name instead of the default /dev/fd/fd and the optional mode mode instead of
re
- function open-output-pipe fd [name]¶
construct a pipe handle from fd using the optional name instead of the default /dev/fd/fd and the optional mode mode instead of
we
- function open-file name mode¶
construct a file handle by opening the file name and the mode mode
open-filehas to handle the resource contention issue mentioned previously.If the fopen(3) call fails with
EMFILE(a process limit) orENFILE(a system-wide limit) indicating the lack of available file descriptors then it has to forcibly invoke the garbage collector and try again.For reasons that escape me, it tries that twice….
- function open-input-file name¶
construct a file handle by opening the file name with the mode
re
- function open-output-file name¶
construct a file handle by opening the file name with the mode
we
- function file-handle? value¶
is value a file handle
- function input-file-handle? value¶
is value a file handle capable of being read from
Obviously this is input file handles but also files opened for writing with the “+” mode flag: “w+”, “a+”.
- function output-file-handle? value¶
is value a file handle capable of being written to
Obviously this is output file handles but also files opened for reading with the “+” mode flag: “r+”.
- function file-handle-fd fh¶
return the file descriptor associated with file handle fh
- function fd-handle? value¶
is value a fd handle
- function input-fd-handle? value¶
is value a fd handle capable of being read from
Obviously this is input fd handles but also fds opened for writing with the “+” mode flag: “w+”, “a+”.
- function output-fd-handle? value¶
is value a fd handle capable of being written to
Obviously this is output fd handles but also fds opened for reading with the “+” mode flag: “r+”.
- function fd-handle-fd fh¶
return the file descriptor associated with fd handle fh
- function pipe-handle? value¶
is value a pipe handle
- function input-pipe-handle? value¶
is value a pipe handle capable of being read from
The “+” mode flag is ignored for a pipe.
- function output-pipe-handle? value¶
is value a pipe handle capable of being written to
The “+” mode flag is ignored for a pipe.
- function pipe-handle-fd ph¶
return the file descriptor associated with pipe handle ph
- function close-fd-handle-on-exec fh¶
call fcntl(3) on the underlying C file descriptor in fd handle fh with
F_SETFDandFD_CLOEXECarguments.
—
- function find-lib filename¶
search
IDIOLIBfor filename using a set of possible filename extensions
- function load filename¶
search
IDIOLIBfor filename using a set of possible filename extensions and then load it in “expression by expression.”
—
- function file-exists? filename¶
does filename exist
Technically, the test is a call to access(2) with the
R_OKflag.
- function delete-file filename¶
remove(3) filename
String Handles¶
See src/string-handle.c.
- function open-input-string string¶
construct an input string handle from the string string
- function open-output-string¶
construct an output string handle
- function string-handle? value¶
is value a string handle
- function input-string-handle? value¶
is value an input string handle
- function output-string-handle? value¶
is value an output string handle
- function get-output-string sh¶
return a string constructed from the contents of the output string handle sh
Handles¶
See src/handle.c.
- function handle? value¶
is value a handle
- function input-handle? value¶
is value an input handle
- function output-handle? value¶
is value an output handle
- function ready? handle¶
is handle handle ready, ie. not at end-of-file
- function eof? handle¶
has handle handle seen end-of-file
- function peek-char handle¶
return the Unicode code point of the next character in handle handle without moving the position in the handle forward
- function puts value [handle]¶
invoke the
putsmethod associated with handle handle, if supplied or the current output handle otherwise, with valueputswill use the printed conversion of value rather than the displayed version
- function flush-handle handle¶
invoke the
flushmethod associated with handle handle
- function seek-handle handle pos [whence]¶
invoke the
seekmethod associated with handle handle with pos and whence, if supplied or'setotherwisewhence can be one of the symbols
set,endorcur.See handle-pos for the equivalent of a
tell-handle.Invoking
seek-handleon a pipe handle will generate a ^rt-parameter-value-error.
- function rewind-handle handle¶
invoke the
seekmethod associated with handle handle with a position of zero and whence ofset.
- function close-handle handle¶
invoke the
closemethod associated with handle handle
- function close-input-handle handle¶
invoke the
closemethod associated with input handle handle
- function close-output-handle handle¶
invoke the
closemethod associated with output handle handle
- function closed-handle? handle¶
return
#tif the handle handle has been closed and#fotherwise
- function eof-object? value¶
return
#tif the value value is the end-of-file value
- function handle-line [handle]¶
return the current line number in handle handle if supplied otherwise the current input handle
The line number can be invalidated by a seek-handle other than to position zero.
- function handle-pos [handle]¶
return the current position in handle handle if supplied otherwise the current input handle
- function handle-location handle¶
return a description of the location handle handle consisting of the handle’s name, line number and position
- function load-handle handle¶
load from handle handle “expression by expression.”
—
- function current-input-handle¶
return the current input handle
- function current-output-handle¶
return the current output handle
- function current-error-handle¶
return the current error handle
- function set-input-handle! handle¶
set the current input handle to handle handle
- function set-output-handle! handle¶
set the current output handle to handle handle
- function set-error-handle! handle¶
set the current error handle to handle handle
—
- function read [handle]¶
invoke the reader with handle handle if supplied otherwise the current input handle
- function read-expr [handle]¶
invoke the expression reader with handle handle if supplied otherwise the current input handle
- function read-line [handle]¶
return the next canonical line of text (up to a newline) as a string from handle handle if supplied otherwise the current input handle
- function read-lines [handle]¶
return the remaining lines of text as a string from handle handle if supplied otherwise the current input handle
- function read-char [handle]¶
return the UTF-8 character as a Unicode code point from handle handle if supplied otherwise the current input handle
- function write value [handle]¶
invoke the
putsmethod associated with handle handle if supplied otherwise the current input handle with valuewritewill use the printed conversion of value rather than the displayed version. See display below.
- function write-char cp [handle]¶
invoke the
putcmethod associated with handle handle if supplied otherwise the current input handle with Unicode code point cpError
putcdoesn’t generate UTF-8
- function newline [handle]¶
invoke the
putcmethod associated with handle handle if supplied otherwise the current input handle with Unicode code point U+000A (LINE FEED)
- function display value [handle]¶
invoke the
putsmethod associated with handle handle if supplied otherwise the current input handle with valuedisplaywill use the displayed conversion of value rather than the printed version. See write above.A function
display*(and sibling functionsedisplayandedisplay*) have been written to display multiple values separated by a space and with a trailing newline (to the current error handle). These are largely deprecated in favour of printf (and eprintf)
- function %printf handle format [args]¶
[deprecated in favour of printf]
rudimentary support for printf(3) and can only handle
%[flags][width][.prec]sfor strings (with all Idio values being converted to strings).
—
In lib/common.idio there are some extra utility functions.
- function %format type format [args]¶
This
%formatfunction (inlib/common.idio) makes a much better attempt at the vagaries of printf(3) by utilising some dynamic variables to convey the print conversion format and precision to other parts of the systemYou would not normally invoke
%formatdirectly but rather use format or one of the printf variants, below.type is one of the symbols:
'argsin which case a%character in the format string starts an escape sequence which has the general form%[flags][width][.prec]KwhereKis a printf(3)-ish format character with arguments in the parameter list argsSo, like a normal printf. The idea being that we can print, say, Idio integers as decimal (fixnums or bignums) or hexadecimal, octal and binary (fixnums).
%sshould work for any Idio type.'keyedin which case a%character in the format string starts an escape sequence which has the general form%[flags][width][.prec]KwhereKis a single Unicode code point (satisfyingAlphabetic?) which is expected to be a key in the optional hash table – unless it is another%character. The value associated with the key will be printed according to the specification.'timeformatwhich is essentially the same as'keyedexcept we avoid a double application of any precisionThis is to support the
timefunction’s TIMEFORMAT format string which is of the form:"Real %.3R\nUser %.3U\nSyst %.3S\n"where%R,%Uand%Sare now the consumed real time, user time and system time.If
Kis a%character then a%is printed according to the specification.The possible flags are:
%formatsupported flags¶-U+002D (HYPHEN-MINUS)
left align the output within width if applicable
U+0020 (SPACE)
use
#\{space}as the left padding character0U+0030 (DIGIT ZERO)
use
#\0(zero) as the left padding characterThe default padding character is
#\{space}.*
As the start of some work to make the printer more dynamic, you can redefine how a struct instance or a C pointer is printed. By default, the format is #<SI typename fields> where fields is a space-separated list of fieldname:value.
As an alternative, you can register a “printer” against a struct type and it will be called when an instance of that type is printed. The printer should return a string.
By way of a simple example:
define-struct point x y P := make-point 1 2 printf "%s\n" P ; #<SI point x:1 y:2> define (point-as-string p seen) if (point? p) { r := (open-output-string) hprintf r "#<SI point" hprintf r " x is %d" p.x hprintf r " and y is %d" p.y hprintf r ">" get-output-string r #n } %%add-as-string point point-as-string printf "%s\n" P ; #<SI point x is 1 and y is 2>
The seen parameter to the printer function can be used for any purpose. The obvious use is to record values as they are seen and pass seen onto similar printer thus preventing circular loops.
In this case, suppose x should be another
pointor#n(rather than an integer as we seem to be using it as). Instead of calling p.x we might call (point-as-string x updated-seen). To avoid loops we can add some checks and updates:define (point-as-string p seen) if (point? p) { if (assq p seen) { "@" r := (open-output-string) hprintf r "#<SI point" hprintf r " x is %s" (point-as-string x (pair (list p) seen)) hprintf r " and y is %d" p.y hprintf r ">" get-output-string r } #n }
An example is in
state-as-stringinlib/SRFI-115.idiowhere the struct has an ID which can be used to identify where the circular loop starts (or ends?) rather than just returning"@"as we do.
- function format format [args]¶
An invocation of %format ‘args format [args].
- function hprintf handle format [args]¶
For the
hinhprintfthink of the leadingfinfprintfin C – this is the generic “print to handle” variant.In practice it calls display with the result of format format [args].
- function sprintf format [args]¶
A call to hprintf with an output string handle. The result is a call to get-output-string on that output string handle.
Last built at 2025-12-07T07:11:02Z+0000 from 463152b (dev)