summaryrefslogtreecommitdiffstats
path: root/stream.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2021-06-14 22:41:07 -0700
committerKaz Kylheku <kaz@kylheku.com>2021-06-14 22:41:07 -0700
commita4692d447d475a3f58e222b8363958d0659a1281 (patch)
treea6bb4f1deb2594adae60c4ae07e8f11c01507e73 /stream.c
parenteb327c0155b982619c4b8d1e2f7e6487adb0834a (diff)
downloadtxr-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.
Diffstat (limited to 'stream.c')
-rw-r--r--stream.c51
1 files changed, 45 insertions, 6 deletions
diff --git a/stream.c b/stream.c
index e12bdfbc..cc5a26f8 100644
--- a/stream.c
+++ b/stream.c
@@ -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