diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2015-07-29 06:07:00 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2015-07-29 06:07:00 -0700 |
commit | 7a61fe49795002a604094c4f7f9612cabbb30d4a (patch) | |
tree | 340b6e0dbcf3080b2c99c1be1713492fe63a566a /stream.c | |
parent | 4f61ef03a2f9559d93ce43d5a5a5db86cbf33cd5 (diff) | |
download | txr-7a61fe49795002a604094c4f7f9612cabbb30d4a.tar.gz txr-7a61fe49795002a604094c4f7f9612cabbb30d4a.tar.bz2 txr-7a61fe49795002a604094c4f7f9612cabbb30d4a.zip |
* 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.
Diffstat (limited to 'stream.c')
-rw-r--r-- | stream.c | 863 |
1 files changed, 431 insertions, 432 deletions
@@ -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) |