diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2014-01-13 22:19:03 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2014-01-13 22:19:03 -0800 |
commit | 48c8cec1c83d70e23dc1359b85cfd9e36fdaa60f (patch) | |
tree | 99586c035526f35154fa916e018c7dcffb80f4c5 /stream.c | |
parent | e7dd81f7280612a65b7a466e6d870b808272b34f (diff) | |
download | txr-48c8cec1c83d70e23dc1359b85cfd9e36fdaa60f.tar.gz txr-48c8cec1c83d70e23dc1359b85cfd9e36fdaa60f.tar.bz2 txr-48c8cec1c83d70e23dc1359b85cfd9e36fdaa60f.zip |
Support for pushing back bytes and characters into streams.
* stream.c (null_ops, stdio_ops, tail_ops, pipe_ops,
string_in_ops, byte_in_ops, string_out_ops, strlist_out_ops,
dir_ops, cat_stream_ops): Structure definition updated
with new initializers for two new virtuals.
(stdio_handle): New member, unget_c.
(snarf_line, stdio_get_char): Handle pushed-back character in
h->unget_c.
(stdio_unget_char, stdio_unget_byte, string_in_unget_char,
byte_in_unget_byte): New static functions.
(make_stdio_stream_common): Initialize unget_c member.
(unget_char, unget_byte): New functions.
* stream.h (struct strm_ops): New virtuals: unget_char and unget_byte.
(unget_char, unget_byte): New functions declared.
* syslog.c (syslog_strm_ops): Two new initializers.
* eval.c (eval_init): Registered unget_char and unget_byte as
intrinsics.
* txr.1: Documented.
Diffstat (limited to 'stream.c')
-rw-r--r-- | stream.c | 129 |
1 files changed, 128 insertions, 1 deletions
@@ -98,6 +98,8 @@ static struct strm_ops null_ops = { 0, /* get_line, */ 0, /* get_char, */ 0, /* get_byte, */ + 0, /* unget_char, */ + 0, /* unget_byte, */ 0, /* close, */ 0, /* flush, */ 0, /* seek, */ @@ -114,6 +116,7 @@ struct stdio_handle { FILE *f; val descr; val mode; /* used by tail */ + val unget_c; utf8_decoder_t ud; #if HAVE_FORK_STUFF pid_t pid; @@ -305,7 +308,14 @@ static wchar_t *snarf_line(struct stdio_handle *h) wchar_t *buf = 0; for (;;) { - wint_t ch = utf8_decode(&h->ud, stdio_get_char_callback, (mem_t *) h->f); + wint_t ch; + + if (h->unget_c) { + ch = c_chr(h->unget_c); + h->unget_c = nil; + } else { + ch = utf8_decode(&h->ud, stdio_get_char_callback, (mem_t *) h->f); + } if (ch == WEOF && buf == 0) break; @@ -345,6 +355,11 @@ static val stdio_get_line(val stream) static val stdio_get_char(val stream) { struct stdio_handle *h = (struct stdio_handle *) stream->co.handle; + val uc = h->unget_c; + if (uc) { + h->unget_c = nil; + return uc; + } if (h->f) { wint_t ch = utf8_decode(&h->ud, stdio_get_char_callback, (mem_t *) h->f); return (ch != WEOF) ? chr(ch) : stdio_maybe_read_error(stream); @@ -362,6 +377,29 @@ static val stdio_get_byte(val stream) return stdio_maybe_read_error(stream); } +static val stdio_unget_char(val stream, val ch) +{ + struct stdio_handle *h = (struct stdio_handle *) stream->co.handle; + + if (!is_chr(ch)) + type_mismatch(lit("unget-char: ~s is not a character"), ch, nao); + + if (h->unget_c) + uw_throwf(file_error_s, lit("unget-char overflow on ~a: "), stream, nao); + + h->unget_c = ch; + return ch; +} + +static val stdio_unget_byte(val stream, int byte) +{ + struct stdio_handle *h = (struct stdio_handle *) stream->co.handle; + + return h->f != 0 && ungetc(byte, (FILE *) h->f) != EOF + ? num_fast(byte) + : stdio_maybe_error(stream, lit("pushing back byte into")); +} + static val stdio_close(val stream, val throw_on_error) { struct stdio_handle *h = (struct stdio_handle *) stream->co.handle; @@ -390,6 +428,8 @@ static struct strm_ops stdio_ops = { stdio_get_line, stdio_get_char, stdio_get_byte, + stdio_unget_char, + stdio_unget_byte, stdio_close, stdio_flush, stdio_seek, @@ -509,6 +549,8 @@ static struct strm_ops tail_ops = { tail_get_line, tail_get_char, tail_get_byte, + stdio_unget_char, + stdio_unget_byte, stdio_close, stdio_flush, stdio_seek, @@ -597,6 +639,8 @@ static struct strm_ops pipe_ops = { stdio_get_line, stdio_get_char, stdio_get_byte, + stdio_unget_char, + stdio_unget_byte, pipe_close, stdio_flush, 0, /* seek: not on pipes */ @@ -655,6 +699,27 @@ static val string_in_get_char(val stream) return nil; } +static val string_in_unget_char(val stream, val ch) +{ + val pair = (val) stream->co.handle; + val string = car(pair); + val pos = cdr(pair); + + if (pos == zero) + uw_throwf(file_error_s, + lit("unget-char: cannot push past beginning of string"), nao); + + pos = minus(pos, one); + + if (chr_str(string, pos) != ch) + uw_throwf(file_error_s, + lit("unget-char: ~s doesn't match the character that was read"), + nao); + + set(*cdr_l(pair), plus(pos, one)); + return ch; +} + static val string_in_get_prop(val stream, val ind) { if (ind == name_k) { @@ -676,6 +741,8 @@ static struct strm_ops string_in_ops = { string_in_get_line, string_in_get_char, 0, /* get_byte */ + string_in_unget_char, + 0, /* unget_byte, */ 0, /* close */ 0, /* flush */ 0, /* TODO: seek */ @@ -710,6 +777,19 @@ static val byte_in_get_byte(val stream) return nil; } +static val byte_in_unget_byte(val stream, int byte) +{ + struct byte_input *bi = (struct byte_input *) stream->co.handle; + + if (bi->index == 0) + uw_throwf(file_error_s, + lit("unget-char: cannot push past beginning of byte stream"), + nao); + + bi->buf[--bi->index] = byte; + return num_fast(byte); +} + static struct strm_ops byte_in_ops = { { cobj_equal_op, cobj_print_op, @@ -722,6 +802,8 @@ static struct strm_ops byte_in_ops = { 0, /* get_line */ 0, /* get_char */ byte_in_get_byte, + 0, /* unget_char, */ + byte_in_unget_byte, 0, /* close */ 0, /* flush */ 0, /* TODO: support seek */ @@ -848,6 +930,8 @@ static struct strm_ops string_out_ops = { 0, /* get_line */ 0, /* get_char */ 0, /* get_byte */ + 0, /* unget_char, */ + 0, /* unget_byte, */ 0, /* close */ 0, /* flush */ 0, /* TODO: seek, with fill-with-spaces semantics if past end. */ @@ -919,6 +1003,8 @@ static struct strm_ops strlist_out_ops = { 0, /* get_line */ 0, /* get_char */ 0, /* get_byte */ + 0, /* unget_char, */ + 0, /* unget_byte, */ 0, /* close */ 0, /* flush */ 0, /* seek */ @@ -991,6 +1077,8 @@ static struct strm_ops dir_ops = { dir_get_line, 0, /* get_char */ 0, /* get_byte */ + 0, /* unget_char, */ + 0, /* unget_byte, */ dir_close, 0, /* flush */ 0, /* seek */ @@ -1005,6 +1093,7 @@ static val make_stdio_stream_common(FILE *f, val descr, struct cobj_ops *ops) h->f = f; h->descr = descr; h->mode = nil; + h->unget_c = nil; utf8_decoder_init(&h->ud); h->pid = 0; #if HAVE_ISATTY @@ -1205,6 +1294,42 @@ val get_byte(val stream) } } +val unget_char(val ch, val stream) +{ + if (!stream) + stream = std_input; + + type_check (stream, COBJ); + type_assert (stream->co.cls == stream_s, (lit("~a is not a stream"), + stream, nao)); + + { + struct strm_ops *ops = (struct strm_ops *) stream->co.ops; + return ops->unget_char ? ops->unget_char(stream, ch) : nil; + } +} + +val unget_byte(val byte, val stream) +{ + cnum b = c_num(byte); + + if (!stream) + stream = std_input; + + type_check (stream, COBJ); + type_assert (stream->co.cls == stream_s, (lit("~a is not a stream"), + stream, nao)); + + if (b < 0 || b > 255) + uw_throwf(file_error_s, lit("unget-byte on ~a: byte value ~a out of range"), + stream, byte, nao); + + { + struct strm_ops *ops = (struct strm_ops *) stream->co.ops; + return ops->unget_byte ? ops->unget_byte(stream, b) : nil; + } +} + struct fmt { size_t minsize; const char *dec; @@ -2120,6 +2245,8 @@ static struct strm_ops cat_stream_ops = { cat_get_line, cat_get_char, cat_get_byte, + 0, /* unget_char, */ + 0, /* unget_byte, */ 0, /* close, */ 0, /* flush, */ 0, /* seek, */ |