summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--stream.c863
2 files changed, 438 insertions, 432 deletions
diff --git a/ChangeLog b/ChangeLog
index bea91235..dd39ac58 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2015-07-29 Kaz Kylheku <kaz@kylheku.com>
+
+ * stream.c: Rearranging definitions and declarations so that
+ code for each stream type is together. Moving catenated
+ streams above utility functions, so they are together with
+ other stream definitions.
+
2015-07-28 Kaz Kylheku <kaz@kylheku.com>
Centered fields in format.
diff --git a/stream.c b/stream.c
index e84025d5..022d274a 100644
--- a/stream.c
+++ b/stream.c
@@ -804,7 +804,6 @@ static val tail_get_byte(val stream)
return ret;
}
-
static struct strm_ops tail_ops =
strm_ops_init(cobj_ops_init(eq,
stdio_stream_print,
@@ -922,6 +921,264 @@ static struct strm_ops pipe_ops =
stdio_get_error_str,
stdio_clear_error);
+struct stdio_mode {
+ int malformed;
+ int read;
+ int write;
+ int create;
+ int append;
+ int binary;
+ int interactive;
+};
+
+#define stdio_mode_init_trivial(read) { 0, read, 0, 0, 0, 0, 0 }
+
+static struct stdio_mode parse_mode(val mode_str)
+{
+ struct stdio_mode m = stdio_mode_init_trivial(0);
+ const wchar_t *ms = c_str(mode_str);
+
+ switch (*ms) {
+ case 'r':
+ ms++;
+ m.read = 1;
+ break;
+ case 'w':
+ ms++;
+ m.write = 1;
+ m.create = 1;
+ break;
+ case 'a':
+ ms++;
+ m.write = 1;
+ m.append = 1;
+ break;
+ default:
+ m.malformed = 1;
+ return m;
+ }
+
+ if (*ms == '+') {
+ ms++;
+ if (m.read)
+ m.write = 1;
+ m.read = 1;
+ }
+
+ for (; *ms; ms++) {
+ switch (*ms) {
+ case 'b':
+ m.binary = 1;
+ break;
+ case 'i':
+ m.interactive = 1;
+ break;
+ default:
+ m.malformed = 1;
+ return m;
+ }
+ }
+
+ return m;
+}
+
+static val format_mode(const struct stdio_mode m)
+{
+ wchar_t buf[8], *ptr = buf;
+
+ if (m.malformed)
+ return lit("###");
+
+ if (m.append) {
+ *ptr++ = 'a';
+ if (m.read)
+ *ptr++ = '+';
+ } else if (m.create) {
+ *ptr++ = 'w';
+ if (m.read)
+ *ptr++ = '+';
+ } else {
+ *ptr++ = 'r';
+ if (m.write)
+ *ptr++ = '+';
+ }
+
+ if (m.binary)
+ *ptr++ = 'b';
+
+ *ptr = 0;
+ return string(buf);
+}
+
+static val normalize_mode(struct stdio_mode *m, val mode_str)
+{
+ struct stdio_mode blank = stdio_mode_init_trivial(1);
+
+ if (null_or_missing_p(mode_str)) {
+ *m = blank;
+ return lit("r");
+ } else {
+ *m = parse_mode(mode_str);
+
+ if (m->malformed)
+ uw_throwf(file_error_s, lit("invalid file open mode ~a"), mode_str, nao);
+
+ if (!m->interactive)
+ return mode_str;
+
+ return format_mode(*m);
+ }
+}
+
+static val set_mode_props(const struct stdio_mode m, val stream)
+{
+ if (m.interactive)
+ stream_set_prop(stream, real_time_k, t);
+ return stream;
+}
+
+static val make_stdio_stream_common(FILE *f, val descr, struct cobj_ops *ops)
+{
+ struct stdio_handle *h = coerce(struct stdio_handle *, chk_malloc(sizeof *h));
+ val stream = cobj(coerce(mem_t *, h), stream_s, ops);
+ h->f = f;
+ h->descr = descr;
+ h->unget_c = nil;
+ utf8_decoder_init(&h->ud);
+ h->err = nil;
+ h->pid = 0;
+ h->mode = nil;
+ h->is_rotated = 0;
+#if HAVE_ISATTY
+ h->is_real_time = if3(opt_compat && opt_compat <= 105,
+ (h->f != 0 && isatty(fileno(h->f)) == 1), 0);
+#else
+ h->is_real_time = 0;
+#endif
+ return stream;
+}
+
+val make_stdio_stream(FILE *f, val descr)
+{
+ return make_stdio_stream_common(f, descr, &stdio_ops.cobj_ops);
+}
+
+val make_tail_stream(FILE *f, val descr)
+{
+ val stream = make_stdio_stream_common(f, descr, &tail_ops.cobj_ops);
+ stream_set_prop(stream, real_time_k, t);
+ return stream;
+}
+
+val make_pipe_stream(FILE *f, val descr)
+{
+ return make_stdio_stream_common(f, descr, &pipe_ops.cobj_ops);
+}
+
+#if HAVE_FORK_STUFF
+static val make_pipevp_stream(FILE *f, val descr, pid_t pid)
+{
+ val stream = make_stdio_stream_common(f, descr, &pipe_ops.cobj_ops);
+ struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
+ h->pid = pid;
+ return stream;
+}
+#endif
+
+struct dir_handle {
+ DIR *d;
+ val err;
+};
+
+static void dir_destroy(val stream)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ common_destroy(stream);
+ free(h);
+}
+
+static void dir_mark(val stream)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ gc_mark(h->err);
+}
+
+static val dir_get_line(val stream)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+
+ if (h->d == 0) {
+ return nil;
+ } else {
+ for (;;) {
+ struct dirent *e = readdir(h->d);
+ if (!e) {
+ h->err = num(errno);
+ return nil;
+ }
+ if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
+ continue;
+ return string_utf8(e->d_name);
+ }
+ }
+}
+
+static val dir_close(val stream, val throw_on_error)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+
+ if (h->d != 0) {
+ closedir(coerce(DIR *, h->d));
+ h->d = 0;
+ }
+
+ return nil;
+}
+
+static val dir_get_error(val stream)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ return h->err;
+}
+
+static val dir_get_error_str(val stream)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ return errno_to_string(h->err);
+}
+
+static val dir_clear_error(val stream)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ val ret = h->err;
+ h->err = nil;
+ return ret;
+}
+
+static struct strm_ops dir_ops =
+ strm_ops_init(cobj_ops_init(eq,
+ stream_print_op,
+ dir_destroy,
+ dir_mark,
+ cobj_hash_op),
+ wli("dir-stream"),
+ 0, 0, 0,
+ dir_get_line,
+ 0, 0, 0, 0,
+ dir_close,
+ 0, 0, 0, 0,
+ dir_get_error,
+ dir_get_error_str,
+ dir_clear_error);
+
+val make_dir_stream(DIR *dir)
+{
+ struct dir_handle *h = coerce(struct dir_handle *, chk_malloc(sizeof *h));
+ h->d = dir;
+ h->err = nil;
+ return cobj(coerce(mem_t *, h), stream_s, &dir_ops.cobj_ops);
+}
+
static void string_in_stream_mark(val stream)
{
val stuff = coerce(val, stream->co.handle);
@@ -1041,6 +1298,11 @@ static struct strm_ops string_in_ops =
string_in_get_error_str,
0);
+val make_string_input_stream(val string)
+{
+ return cobj(coerce(mem_t *, cons(string, zero)), stream_s, &string_in_ops.cobj_ops);
+}
+
struct byte_input {
unsigned char *buf;
size_t size;
@@ -1108,6 +1370,20 @@ static struct strm_ops byte_in_ops =
byte_in_get_error_str,
0);
+val make_string_byte_input_stream(val string)
+{
+ type_assert (stringp(string), (lit("~a is not a string"), string, nao));
+
+ {
+ struct byte_input *bi = coerce(struct byte_input *, chk_malloc(sizeof *bi));
+ unsigned char *utf8 = utf8_dup_to_uc(c_str(string));
+ bi->buf = utf8;
+ bi->size = strlen(coerce(char *, utf8));
+ bi->index = 0;
+ return cobj(coerce(mem_t *, bi), stream_s, &byte_in_ops.cobj_ops);
+ }
+}
+
struct string_output {
wchar_t *buf;
size_t size;
@@ -1231,6 +1507,53 @@ static struct strm_ops string_out_ops =
0, /* TODO: seek; fill-with-spaces semantics if past end. */
0, 0, 0, 0, 0);
+val make_string_output_stream(void)
+{
+ struct string_output *so = coerce(struct string_output *, chk_malloc(sizeof *so));
+ so->size = 128;
+ so->buf = coerce(wchar_t *, chk_malloc(so->size * sizeof so->buf));
+ so->fill = 0;
+ so->buf[0] = 0;
+ utf8_decoder_init(&so->ud);
+ so->head = so->tail = 0;
+ return cobj(coerce(mem_t *, so), stream_s, &string_out_ops.cobj_ops);
+}
+
+val get_string_from_stream(val stream)
+{
+ type_check (stream, COBJ);
+ type_assert (stream->co.cls == stream_s,
+ (lit("~a is not a stream"),
+ stream, nao));
+
+ if (stream->co.ops == &string_out_ops.cobj_ops) {
+ struct string_output *so = coerce(struct string_output *, stream->co.handle);
+ val out = nil;
+
+ if (!so)
+ return out;
+
+ while (so->head != so->tail)
+ out = string_out_byte_flush(so, stream);
+
+ stream->co.handle = 0;
+
+ /* Trim to actual size */
+ so->buf = coerce(wchar_t *, chk_realloc(coerce(mem_t *, so->buf),
+ (so->fill + 1) * sizeof *so->buf));
+ out = string_own(so->buf);
+ free(so);
+ return out;
+ } else {
+ type_assert (stream->co.ops == &string_in_ops.cobj_ops,
+ (lit("~a is not a string stream"), stream, nao));
+ {
+ val pair = coerce(val, stream->co.handle);
+ return pair ? car(pair) : nil;
+ }
+ }
+}
+
static void strlist_mark(val stream)
{
val stuff = coerce(val, stream->co.handle);
@@ -1318,330 +1641,175 @@ val get_list_from_stream(val stream)
type_mismatch(lit("~s is not a string list stream"), stream);
}
-struct dir_handle {
- DIR *d;
- val err;
-};
-
-static void dir_destroy(val stream)
-{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
- common_destroy(stream);
- free(h);
-}
-
-static void dir_mark(val stream)
-{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
- gc_mark(h->err);
-}
-
-static val dir_get_line(val stream)
+static void cat_stream_print(val stream, val out)
{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ val streams = coerce(val, stream->co.handle);
+ struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
+ val name = static_str(ops->name);
- if (h->d == 0) {
- return nil;
- } else {
- for (;;) {
- struct dirent *e = readdir(h->d);
- if (!e) {
- h->err = num(errno);
- return nil;
- }
- if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
- continue;
- return string_utf8(e->d_name);
- }
- }
+ format(out, lit("#<~a ~s>"), name, streams, nao);
}
-static val dir_close(val stream, val throw_on_error)
+static val cat_get_line(val stream)
{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ val streams = coerce(val, stream->co.handle);
- if (h->d != 0) {
- closedir(coerce(DIR *, h->d));
- h->d = 0;
+ while (streams) {
+ val fs = first(streams);
+ val line = get_line(fs);
+ if (line)
+ return line;
+ close_stream(fs, t);
+ if ((streams = rest(streams)) != nil)
+ stream->co.handle = coerce(mem_t *, streams);
}
return nil;
}
-static val dir_get_error(val stream)
+static val cat_get_char(val stream)
{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
- return h->err;
-}
+ val streams = coerce(val, stream->co.handle);
-static val dir_get_error_str(val stream)
-{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
- return errno_to_string(h->err);
-}
+ while (streams) {
+ val fs = first(streams);
+ val ch = get_char(fs);
+ if (ch)
+ return ch;
+ close_stream(fs, t);
+ if ((streams = rest(streams)) != nil)
+ stream->co.handle = coerce(mem_t *, streams);
+ }
-static val dir_clear_error(val stream)
-{
- struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
- val ret = h->err;
- h->err = nil;
- return ret;
+ return nil;
}
-static struct strm_ops dir_ops =
- strm_ops_init(cobj_ops_init(eq,
- stream_print_op,
- dir_destroy,
- dir_mark,
- cobj_hash_op),
- wli("dir-stream"),
- 0, 0, 0,
- dir_get_line,
- 0, 0, 0, 0,
- dir_close,
- 0, 0, 0, 0,
- dir_get_error,
- dir_get_error_str,
- dir_clear_error);
-
-struct stdio_mode {
- int malformed;
- int read;
- int write;
- int create;
- int append;
- int binary;
- int interactive;
-};
-
-#define stdio_mode_init_trivial(read) { 0, read, 0, 0, 0, 0, 0 }
-
-static struct stdio_mode parse_mode(val mode_str)
+static val cat_get_byte(val stream)
{
- struct stdio_mode m = stdio_mode_init_trivial(0);
- const wchar_t *ms = c_str(mode_str);
-
- switch (*ms) {
- case 'r':
- ms++;
- m.read = 1;
- break;
- case 'w':
- ms++;
- m.write = 1;
- m.create = 1;
- break;
- case 'a':
- ms++;
- m.write = 1;
- m.append = 1;
- break;
- default:
- m.malformed = 1;
- return m;
- }
-
- if (*ms == '+') {
- ms++;
- if (m.read)
- m.write = 1;
- m.read = 1;
- }
+ val streams = coerce(val, stream->co.handle);
- for (; *ms; ms++) {
- switch (*ms) {
- case 'b':
- m.binary = 1;
- break;
- case 'i':
- m.interactive = 1;
- break;
- default:
- m.malformed = 1;
- return m;
- }
+ while (streams) {
+ val fs = first(streams);
+ val byte = get_byte(fs);
+ if (byte)
+ return byte;
+ close_stream(fs, t);
+ if ((streams = rest(streams)) != nil)
+ stream->co.handle = coerce(mem_t *, streams);
}
- return m;
+ return nil;
}
-static val format_mode(const struct stdio_mode m)
+static val cat_unget_byte(val stream, int byte)
{
- wchar_t buf[8], *ptr = buf;
-
- if (m.malformed)
- return lit("###");
+ val streams = coerce(val, stream->co.handle);
- if (m.append) {
- *ptr++ = 'a';
- if (m.read)
- *ptr++ = '+';
- } else if (m.create) {
- *ptr++ = 'w';
- if (m.read)
- *ptr++ = '+';
+ if (!streams) {
+ uw_throwf(file_error_s,
+ lit("unget-byte on catenated stream ~a: stream list empty"),
+ stream, nao);
} else {
- *ptr++ = 'r';
- if (m.write)
- *ptr++ = '+';
+ val stream = car(streams);
+ return unget_byte(num_fast(byte), stream);
}
- if (m.binary)
- *ptr++ = 'b';
-
- *ptr = 0;
- return string(buf);
+ return nil;
}
-static val normalize_mode(struct stdio_mode *m, val mode_str)
+static val cat_unget_char(val stream, val ch)
{
- struct stdio_mode blank = stdio_mode_init_trivial(1);
+ val streams = coerce(val, stream->co.handle);
- if (null_or_missing_p(mode_str)) {
- *m = blank;
- return lit("r");
+ if (!streams) {
+ uw_throwf(file_error_s,
+ lit("unget-char on catenated stream ~a: stream list empty"),
+ stream, nao);
} else {
- *m = parse_mode(mode_str);
-
- if (m->malformed)
- uw_throwf(file_error_s, lit("invalid file open mode ~a"), mode_str, nao);
-
- if (!m->interactive)
- return mode_str;
-
- return format_mode(*m);
+ val stream = car(streams);
+ return unget_char(ch, stream );
}
}
-static val set_mode_props(const struct stdio_mode m, val stream)
-{
- if (m.interactive)
- stream_set_prop(stream, real_time_k, t);
- return stream;
-}
-
-static val make_stdio_stream_common(FILE *f, val descr, struct cobj_ops *ops)
+static val cat_get_prop(val stream, val ind)
{
- struct stdio_handle *h = coerce(struct stdio_handle *, chk_malloc(sizeof *h));
- val stream = cobj(coerce(mem_t *, h), stream_s, ops);
- h->f = f;
- h->descr = descr;
- h->unget_c = nil;
- utf8_decoder_init(&h->ud);
- h->err = nil;
- h->pid = 0;
- h->mode = nil;
- h->is_rotated = 0;
-#if HAVE_ISATTY
- h->is_real_time = if3(opt_compat && opt_compat <= 105,
- (h->f != 0 && isatty(fileno(h->f)) == 1), 0);
-#else
- h->is_real_time = 0;
-#endif
- return stream;
+ val streams = coerce(val, stream->co.handle);
+ if (streams)
+ return stream_get_prop(first(streams), ind);
+ return nil;
}
-val make_stdio_stream(FILE *f, val descr)
+static void cat_mark(val stream)
{
- return make_stdio_stream_common(f, descr, &stdio_ops.cobj_ops);
+ val obj = coerce(val, stream->co.handle);
+ gc_mark(obj);
}
-val make_tail_stream(FILE *f, val descr)
+static val cat_get_error(val stream)
{
- val stream = make_stdio_stream_common(f, descr, &tail_ops.cobj_ops);
- stream_set_prop(stream, real_time_k, t);
- return stream;
+ val streams = coerce(val, stream->co.handle);
+ return if3(streams, get_error(first(streams)), t);
}
-val make_pipe_stream(FILE *f, val descr)
+static val cat_get_error_str(val stream)
{
- return make_stdio_stream_common(f, descr, &pipe_ops.cobj_ops);
+ val streams = coerce(val, stream->co.handle);
+ return if3(streams, get_error_str(first(streams)), lit("eof"));
}
-#if HAVE_FORK_STUFF
-static val make_pipevp_stream(FILE *f, val descr, pid_t pid)
+static val cat_clear_error(val stream)
{
- val stream = make_stdio_stream_common(f, descr, &pipe_ops.cobj_ops);
- struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
- h->pid = pid;
- return stream;
+ val streams = coerce(val, stream->co.handle);
+ return if2(streams, clear_error(first(streams)));
}
-#endif
-val make_string_input_stream(val string)
-{
- return cobj(coerce(mem_t *, cons(string, zero)), stream_s, &string_in_ops.cobj_ops);
-}
+static struct strm_ops cat_stream_ops =
+ strm_ops_init(cobj_ops_init(eq,
+ cat_stream_print,
+ cobj_destroy_stub_op,
+ cat_mark,
+ cobj_hash_op),
+ wli("catenated-stream"),
+ 0, 0, 0,
+ cat_get_line,
+ cat_get_char,
+ cat_get_byte,
+ cat_unget_char,
+ cat_unget_byte,
+ 0, 0, 0,
+ cat_get_prop,
+ 0,
+ cat_get_error,
+ cat_get_error_str,
+ cat_clear_error);
-val make_string_byte_input_stream(val string)
+val make_catenated_stream(val stream_list)
{
- type_assert (stringp(string), (lit("~a is not a string"), string, nao));
-
- {
- struct byte_input *bi = coerce(struct byte_input *, chk_malloc(sizeof *bi));
- unsigned char *utf8 = utf8_dup_to_uc(c_str(string));
- bi->buf = utf8;
- bi->size = strlen(coerce(char *, utf8));
- bi->index = 0;
- return cobj(coerce(mem_t *, bi), stream_s, &byte_in_ops.cobj_ops);
- }
+ return cobj(coerce(mem_t *, stream_list), stream_s, &cat_stream_ops.cobj_ops);
}
-val make_string_output_stream(void)
+val catenated_stream_p(val obj)
{
- struct string_output *so = coerce(struct string_output *, chk_malloc(sizeof *so));
- so->size = 128;
- so->buf = coerce(wchar_t *, chk_malloc(so->size * sizeof so->buf));
- so->fill = 0;
- so->buf[0] = 0;
- utf8_decoder_init(&so->ud);
- so->head = so->tail = 0;
- return cobj(coerce(mem_t *, so), stream_s, &string_out_ops.cobj_ops);
+ return if2(streamp(obj),
+ c_true(obj->co.ops == &cat_stream_ops.cobj_ops));
}
-val get_string_from_stream(val stream)
+val catenated_stream_push(val new_stream, val cat_stream)
{
- type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_s,
- (lit("~a is not a stream"),
- stream, nao));
-
- if (stream->co.ops == &string_out_ops.cobj_ops) {
- struct string_output *so = coerce(struct string_output *, stream->co.handle);
- val out = nil;
-
- if (!so)
- return out;
-
- while (so->head != so->tail)
- out = string_out_byte_flush(so, stream);
-
- stream->co.handle = 0;
+ type_assert (streamp(new_stream),
+ (lit("~a is not a stream"), new_stream, nao));
+ type_assert (catenated_stream_p(cat_stream),
+ (lit("~a is not a stream"), cat_stream, nao));
- /* Trim to actual size */
- so->buf = coerce(wchar_t *, chk_realloc(coerce(mem_t *, so->buf),
- (so->fill + 1) * sizeof *so->buf));
- out = string_own(so->buf);
- free(so);
- return out;
- } else {
- type_assert (stream->co.ops == &string_in_ops.cobj_ops,
- (lit("~a is not a string stream"), stream, nao));
- {
- val pair = coerce(val, stream->co.handle);
- return pair ? car(pair) : nil;
- }
+ {
+ val streams = coerce(val, cat_stream->co.handle);
+ loc l = mkloc(streams, cat_stream);
+ set(l, cons(new_stream, streams));
+ cat_stream->co.handle = coerce(mem_t *, deref(l));
+ return nil;
}
}
-val make_dir_stream(DIR *dir)
-{
- struct dir_handle *h = coerce(struct dir_handle *, chk_malloc(sizeof *h));
- h->d = dir;
- h->err = nil;
- return cobj(coerce(mem_t *, h), stream_s, &dir_ops.cobj_ops);
-}
-
val streamp(val obj)
{
return typeof(obj) == stream_s ? t : nil;
@@ -2870,175 +3038,6 @@ static val run(val command, val args)
}
#endif
-static void cat_stream_print(val stream, val out)
-{
- val streams = coerce(val, stream->co.handle);
- struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
- val name = static_str(ops->name);
-
- format(out, lit("#<~a ~s>"), name, streams, nao);
-}
-
-static val cat_get_line(val stream)
-{
- val streams = coerce(val, stream->co.handle);
-
- while (streams) {
- val fs = first(streams);
- val line = get_line(fs);
- if (line)
- return line;
- close_stream(fs, t);
- if ((streams = rest(streams)) != nil)
- stream->co.handle = coerce(mem_t *, streams);
- }
-
- return nil;
-}
-
-static val cat_get_char(val stream)
-{
- val streams = coerce(val, stream->co.handle);
-
- while (streams) {
- val fs = first(streams);
- val ch = get_char(fs);
- if (ch)
- return ch;
- close_stream(fs, t);
- if ((streams = rest(streams)) != nil)
- stream->co.handle = coerce(mem_t *, streams);
- }
-
- return nil;
-}
-
-static val cat_get_byte(val stream)
-{
- val streams = coerce(val, stream->co.handle);
-
- while (streams) {
- val fs = first(streams);
- val byte = get_byte(fs);
- if (byte)
- return byte;
- close_stream(fs, t);
- if ((streams = rest(streams)) != nil)
- stream->co.handle = coerce(mem_t *, streams);
- }
-
- return nil;
-}
-
-static val cat_unget_byte(val stream, int byte)
-{
- val streams = coerce(val, stream->co.handle);
-
- if (!streams) {
- uw_throwf(file_error_s,
- lit("unget-byte on catenated stream ~a: stream list empty"),
- stream, nao);
- } else {
- val stream = car(streams);
- return unget_byte(num_fast(byte), stream);
- }
-
- return nil;
-}
-
-static val cat_unget_char(val stream, val ch)
-{
- val streams = coerce(val, stream->co.handle);
-
- if (!streams) {
- uw_throwf(file_error_s,
- lit("unget-char on catenated stream ~a: stream list empty"),
- stream, nao);
- } else {
- val stream = car(streams);
- return unget_char(ch, stream );
- }
-}
-
-static val cat_get_prop(val stream, val ind)
-{
- val streams = coerce(val, stream->co.handle);
- if (streams)
- return stream_get_prop(first(streams), ind);
- return nil;
-}
-
-static void cat_mark(val stream)
-{
- val obj = coerce(val, stream->co.handle);
- gc_mark(obj);
-}
-
-static val cat_get_error(val stream)
-{
- val streams = coerce(val, stream->co.handle);
- return if3(streams, get_error(first(streams)), t);
-}
-
-static val cat_get_error_str(val stream)
-{
- val streams = coerce(val, stream->co.handle);
- return if3(streams, get_error_str(first(streams)), lit("eof"));
-}
-
-static val cat_clear_error(val stream)
-{
- val streams = coerce(val, stream->co.handle);
- return if2(streams, clear_error(first(streams)));
-}
-
-static struct strm_ops cat_stream_ops =
- strm_ops_init(cobj_ops_init(eq,
- cat_stream_print,
- cobj_destroy_stub_op,
- cat_mark,
- cobj_hash_op),
- wli("catenated-stream"),
- 0, 0, 0,
- cat_get_line,
- cat_get_char,
- cat_get_byte,
- cat_unget_char,
- cat_unget_byte,
- 0, 0, 0,
- cat_get_prop,
- 0,
- cat_get_error,
- cat_get_error_str,
- cat_clear_error);
-
-val make_catenated_stream(val stream_list)
-{
- return cobj(coerce(mem_t *, stream_list), stream_s, &cat_stream_ops.cobj_ops);
-}
-
-val catenated_stream_p(val obj)
-{
- return if2(streamp(obj),
- c_true(obj->co.ops == &cat_stream_ops.cobj_ops));
-}
-
-val catenated_stream_push(val new_stream, val cat_stream)
-{
- type_assert (streamp(new_stream),
- (lit("~a is not a stream"), new_stream, nao));
- type_assert (catenated_stream_p(cat_stream),
- (lit("~a is not a stream"), cat_stream, nao));
-
- {
- val streams = coerce(val, cat_stream->co.handle);
- loc l = mkloc(streams, cat_stream);
- set(l, cons(new_stream, streams));
- cat_stream->co.handle = coerce(mem_t *, deref(l));
- return nil;
- }
-}
-
val remove_path(val path)
{
if (w_remove(c_str(path)) < 0)