diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2021-06-14 22:41:07 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2021-06-14 22:41:07 -0700 |
commit | a4692d447d475a3f58e222b8363958d0659a1281 (patch) | |
tree | a6bb4f1deb2594adae60c4ae07e8f11c01507e73 | |
parent | eb327c0155b982619c4b8d1e2f7e6487adb0834a (diff) | |
download | txr-a4692d447d475a3f58e222b8363958d0659a1281.tar.gz txr-a4692d447d475a3f58e222b8363958d0659a1281.tar.bz2 txr-a4692d447d475a3f58e222b8363958d0659a1281.zip |
streams: *stdnull* lazily opens /dev/null
The *stdnull* stream has been purely a stream-level
abstraction. To make it useful for redirecting real file
descriptors around the execution of external programs,
we endow it with the ability to open /dev/null when it is
asked to provide its file descriptor.
* stream.c (struct dev_null): New structure.
(dev_null_close, dev_null_get_fd, dev_null_get_prop): New
static functions.
(null_ops): Wire in the above functions instead of null_close,
null_get_fd and null_get_prop. We need new functions because
null_close and others do not belong to just the null stream;
they are base operations used by other streams as default
implementations for some kinds of unimplemented functions.
(make_null_stream): Alocate a struct dev_null instead of a
struct strm_base. Set the fd to -1.
* txr.1: Documented.
-rw-r--r-- | stream.c | 51 | ||||
-rw-r--r-- | txr.1 | 33 |
2 files changed, 75 insertions, 9 deletions
@@ -432,6 +432,44 @@ void fill_stream_ops(struct strm_ops *ops) ops->set_sock_peer = unimpl_set_sock_peer; } +struct dev_null { + struct strm_base a; + int fd; +}; + +static val dev_null_close(val stream, val throw_on_error) +{ + struct dev_null *n = coerce(struct dev_null *, stream->co.handle); + (void) throw_on_error; + if (n->fd != -1) { + close(n->fd); + n->fd = -1; + } + return nil; +} + +static val dev_null_get_fd(val stream) +{ + struct dev_null *n = coerce(struct dev_null *, stream->co.handle); + if (n->fd == -1 && (n->fd = open("/dev/null", O_RDWR)) == -1) { + int eno = errno; + uw_throwf(errno_to_file_error(eno), lit("error opening /dev/null: ~d/~s"), + num(eno), errno_to_str(eno), nao); + } + return num(n->fd); +} + +static val dev_null_get_prop(val stream, val ind) +{ + if (ind == name_k) + return null_get_prop(stream, ind); + + if (ind == fd_k) + return dev_null_get_fd(stream); + + return nil; +} + static struct strm_ops null_ops = strm_ops_init(cobj_ops_init(eq, stream_print_op, @@ -443,16 +481,17 @@ static struct strm_ops null_ops = null_get_char, null_get_byte, unimpl_unget_char, unimpl_unget_byte, unimpl_put_buf, unimpl_fill_buf, - null_close, null_flush, null_seek, unimpl_truncate, - null_get_prop, null_set_prop, + dev_null_close, null_flush, null_seek, unimpl_truncate, + dev_null_get_prop, null_set_prop, null_get_error, null_get_error_str, null_clear_error, - null_get_fd); + dev_null_get_fd); val make_null_stream(void) { - struct strm_base *s = coerce(struct strm_base *, chk_malloc(sizeof *s)); - strm_base_init(s); - return cobj(coerce(mem_t *, s), stream_s, &null_ops.cobj_ops); + struct dev_null *n = coerce(struct dev_null *, chk_malloc(sizeof *n)); + strm_base_init(&n->a); + n->fd = -1; + return cobj(coerce(mem_t *, n), stream_s, &null_ops.cobj_ops); } #if CONFIG_STDIO_STRICT @@ -54188,11 +54188,38 @@ debugging output to be separated from normal output. The .code *stdnull* -stream is a special kind of stream called a null stream. -This stream is not connected to any device or file. It is similar to +stream is a special kind of stream called a null stream. To read operations, +the stream appears empty, like a stream open on an empty file. To write +operations, it appears as a data sink of infinite capacity which consumes data +and discards it. This stream is similar to the .code /dev/null -device on Unix, but does not involve the operating system. +device on Unix, and in fact has a relationship to it. If an attempt is made +to obtain the underlying file descriptor of +.code *stdnull* +using the +.code fileno +function, then the +.code /dev/null +device is open, if the host platform supports it. The resulting file +descriptor number is returned, and also retained in the +.code *stdnull* +device. When +.code close-stream +is invoked on +.codn *stdnull* , +that descriptor is closed. This feature of +.code *stdnull* +allows it to be useful for establishing redirections around the +execution of external utilities. + +.TP* Example: + +.verb + ;; redirect output of ls *.txt command to /dev/null + (let ((*stderr *stdnull*)) + (sh "ls *.txt")) +.brev .coNP Special variables @ *print-flo-format* and @ *pprint-flo-format* .desc |