summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2025-05-24 21:09:02 -0700
committerKaz Kylheku <kaz@kylheku.com>2025-05-24 21:09:02 -0700
commita76b786482431a3cc3a85d493ca3e972a0ee027a (patch)
tree71c942abac780422d6903167f428b6aab92eaa31
parent2707ec86cb50f81cce392fb2024f9e854cdf1b6a (diff)
downloadtxr-a76b786482431a3cc3a85d493ca3e972a0ee027a.tar.gz
txr-a76b786482431a3cc3a85d493ca3e972a0ee027a.tar.bz2
txr-a76b786482431a3cc3a85d493ca3e972a0ee027a.zip
streams: replace get_line virtual with new interface.
* stream.h (struct strm_ops): The simple get_line virtual is replaced by get_string, which takes a character limit and a delimiting stop character. (strm_ops_init): Rename get_line parameter to get_string. (get_string_s): Declared. (generic_get_line): Declaration removed. (generic_get_string, get_delimited_string): Declared. * stream.c (get_string_s): New symbol variable. (unimpl_get_line): Function removed. (unimpl_get_string): New function. (null_get_line): Function removed. (null_get_string): New function. (fill_stream_ops): Configure ops->get_string rather than ops->get_line. (null_ops): Wire null_get_string in place of null_get_line. (generic_get_line): Renamed to generic_get_string. (generic_get_string): Implement the limit and stop_char parameters. (get_line_limited_check): New static function. (stdio_ops): Wire in generic_get_string instead of generic_get_line. (tail_get_line): Replaced by tail_get_string. (tail_get_string): Call generic_get_string instead of generic_get_line, and pass the limit and stop_char arguments down. (tail_ops): Wire in tail_get_string instead of tail_get_line. (pipe_ops): Wire generic_get_string instead of generic_get_line. (dir_get_line): Renamed to dir_get_string. (dir_get_string): Use get_line_limited_check to defend against unhandled argument values. (dir_ops): Wire dir_get_string instead of dir_get_line. (string_in_get_line): Replaced by string_in_get_string. (string_in_get_string): Implement limit and stop_char parameters. (string_in_ops): Wire string_in_get_string instead of string_in_get_line. (strlist_in_get_line): Replaced with strlist_in_get_string. (strlist_in_get_string): Use get_line_limited_check to defend against unsupported arguments. (strlist_in_ops): Wire in strlist_in_get_string instead of strlist_in_get_line. (cat_get_line): Replaced by cat_get_string. (cat_get_string): Rather than recursing into the get_line public interface, we fetch the stream's get_string virtual and pass all arguments to it. (cat_stream_ops): Wire cat_get_string instead of cat_get_line. (record_adapter_get_line): Replaced by record_adapter_get_string. (record_adapter_get_string): use get_line_limited_check to guard against unsupported arguments. (record_adapter_ops): Wire record_adapter_get_string instead of record_adapter_get_line. (get_line): Implement using get_string virtual now. We pass UINT_PTR_MAX as limit, which means no character limit, and '\n' as the delimiter for reading a line. (get_delimited_string): New function, which exposes the full semantics of the get_string virtual. (stream_init): Initialize get_string_s. Register get-delimited-string function. Use get_string_s symbol in registration of get-string. * strudel.c (strudel_get_line): Replaced by strudel_get_string. (strudel_get_string): Call look up the get-string method and pass all arguments to it, encoded into Lisp values in the right way, nil indicating not present. (strudel_ops): Wire strudel_get_string in place of strudel_get_line. * parser.c (shadow_ops_template): Replace generic_get_line with generic_get_string. * buf.c (buf_strm_ops): Likewise. * socket.c (dgram_strm_ops): Likewise. * gzio.c (gzio_ops_rd): Likewise. * stdlib/stream-wrap.tl (stream-wrap get-line): Method replaced by (stream-wrap get-string). This calls get-delimited-string rather than get-line. * tests/018/streams.tl: New tests, mainly concerned with the new logic in the string input stream which has its own implementation of get_string with several cases. * txr.1: Document new get-delimited-string function, and the get-string method of the delegate stream, removing the documentation for removed get-line method.
-rw-r--r--buf.c2
-rw-r--r--gzio.c2
-rw-r--r--parser.c2
-rw-r--r--socket.c2
-rw-r--r--stdlib/stream-wrap.tl5
-rw-r--r--stream.c140
-rw-r--r--stream.h11
-rw-r--r--strudel.c10
-rw-r--r--tests/018/streams.tl55
-rw-r--r--txr.163
10 files changed, 239 insertions, 53 deletions
diff --git a/buf.c b/buf.c
index 94472e46..d095472a 100644
--- a/buf.c
+++ b/buf.c
@@ -1279,7 +1279,7 @@ static struct strm_ops buf_strm_ops =
buf_strm_put_string,
buf_strm_put_char,
buf_strm_put_byte,
- generic_get_line,
+ generic_get_string,
buf_strm_get_char,
buf_strm_get_byte,
buf_strm_unget_char,
diff --git a/gzio.c b/gzio.c
index bda0bd04..844f75cc 100644
--- a/gzio.c
+++ b/gzio.c
@@ -450,7 +450,7 @@ static struct strm_ops gzio_ops_rd =
0,
0,
0,
- generic_get_line,
+ generic_get_string,
gzio_get_char,
gzio_get_byte,
gzio_unget_char,
diff --git a/parser.c b/parser.c
index 40ae4272..18c127fe 100644
--- a/parser.c
+++ b/parser.c
@@ -2297,7 +2297,7 @@ static struct strm_ops shadow_ops_template =
0),
wli("shadow-stream"),
shadow_put_string, shadow_put_char, shadow_put_byte,
- generic_get_line, shadow_get_char, shadow_get_byte,
+ generic_get_string, shadow_get_char, shadow_get_byte,
shadow_unget_char, shadow_unget_byte,
shadow_put_buf, shadow_fill_buf,
shadow_close, shadow_flush, shadow_seek, shadow_truncate,
diff --git a/socket.c b/socket.c
index 495b6768..7df31f1e 100644
--- a/socket.c
+++ b/socket.c
@@ -685,7 +685,7 @@ static_def(struct strm_ops dgram_strm_ops =
dgram_put_string,
dgram_put_char,
dgram_put_byte,
- generic_get_line,
+ generic_get_string,
dgram_get_char,
dgram_get_byte,
dgram_unget_char,
diff --git a/stdlib/stream-wrap.tl b/stdlib/stream-wrap.tl
index a257e741..60c7cd1e 100644
--- a/stdlib/stream-wrap.tl
+++ b/stdlib/stream-wrap.tl
@@ -33,8 +33,9 @@
(put-char chr me.stream))
(:method put-byte (me byte)
(put-byte byte me.stream))
- (:method get-line (me)
- (get-line me.stream))
+ (:method get-string (me nchars stopchar self)
+ (ignore self)
+ (get-delimited-string me.stream nchars stopchar))
(:method get-char (me)
(get-char me.stream))
(:method get-byte (me)
diff --git a/stream.c b/stream.c
index 4fd134e0..1abd7d7c 100644
--- a/stream.c
+++ b/stream.c
@@ -92,7 +92,7 @@ val stdin_s, stdout_s, stddebug_s, stderr_s, stdnull_s;
val put_string_s, put_char_s, put_byte_s, get_line_s, get_char_s;
val get_byte_s, unget_char_s, unget_byte_s, put_buf_s, fill_buf_s;
val flush_s, seek_s, truncate_s, get_prop_s, set_prop_s;
-val get_error_s, get_error_str_s, clear_error_s, get_fd_s;
+val get_error_s, get_error_str_s, clear_error_s, get_fd_s, get_string_s;
val print_flo_precision_s, print_flo_digits_s, print_flo_format_s;
val pprint_flo_format_s, print_base_s, print_circle_s;
@@ -183,9 +183,13 @@ static NORETURN val unimpl_put_byte(val stream, int byte)
unimpl(stream, lit("put-byte"));
}
-static NORETURN val unimpl_get_line(val stream)
+static NORETURN val unimpl_get_string(val stream, ucnum limit,
+ wint_t stopchar, val self)
{
- unimpl(stream, lit("get-line"));
+ (void) limit;
+ (void) stopchar;
+ (void) self;
+ unimpl(stream, self);
}
static NORETURN val unimpl_get_char(val stream)
@@ -281,9 +285,12 @@ static val null_put_byte(val stream, int byte)
return nil;
}
-static val null_get_line(val stream)
+static val null_get_string(val stream, ucnum limit, wint_t stopchar, val self)
{
(void) stream;
+ (void) limit;
+ (void) stopchar;
+ (void) self;
return nil;
}
@@ -406,8 +413,8 @@ void fill_stream_ops(struct strm_ops *ops)
ops->put_char = unimpl_put_char;
if (!ops->put_byte)
ops->put_byte = unimpl_put_byte;
- if (!ops->get_line)
- ops->get_line = unimpl_get_line;
+ if (!ops->get_string)
+ ops->get_string = unimpl_get_string;
if (!ops->get_char)
ops->get_char = unimpl_get_char;
if (!ops->get_byte)
@@ -497,7 +504,7 @@ static struct strm_ops null_ops =
cobj_eq_hash_op,
0),
wli("null-stream"),
- null_put_string, null_put_char, null_put_byte, null_get_line,
+ null_put_string, null_put_char, null_put_byte, null_get_string,
null_get_char, null_get_byte,
unimpl_unget_char, unimpl_unget_byte,
unimpl_put_buf, unimpl_fill_buf,
@@ -830,17 +837,20 @@ static val stdio_get_fd(val stream)
return h->f ? num(fileno(h->f)) : nil;
}
-val generic_get_line(val stream)
+val generic_get_string(val stream, ucnum limit, wint_t stopchar, val self)
{
const size_t min_size = 512;
size_t size = 0;
size_t fill = 0;
wchar_t *volatile buf = 0;
val out = nil;
+ ucnum count = limit;
+
+ (void) self;
uw_simple_catch_begin;
- for (;;) {
+ for (; limit == UINT_PTR_MAX || count > 0; --count) {
struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
val chr = ops->get_char(stream);
wint_t ch = chr ? convert(wint_t, c_chr(chr)) : WEOF;
@@ -855,7 +865,7 @@ val generic_get_line(val stream)
size = newsize;
}
- if (ch == '\n' || ch == WEOF) {
+ if (ch == stopchar || ch == WEOF) {
buf[fill++] = 0;
break;
}
@@ -882,6 +892,20 @@ val generic_get_line(val stream)
return out;
}
+static void get_line_limited_check(val stream, ucnum limit,
+ wint_t stopchar, val self)
+{
+ if (limit != UINT_PTR_MAX)
+ uw_throwf(file_error_s,
+ lit("~a: ~s doesn't support count-limited string read"),
+ self, stream, nao);
+
+ if (stopchar != '\n' && stopchar != WEOF)
+ uw_throwf(file_error_s,
+ lit("~a: ~s doesn't read delimited by character ~s"),
+ self, stream, chr(stopchar), nao);
+}
+
static val stdio_get_char(val stream)
{
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
@@ -1123,7 +1147,7 @@ static struct strm_ops stdio_ops =
stdio_put_string,
stdio_put_char,
stdio_put_byte,
- generic_get_line,
+ generic_get_string,
stdio_get_char,
stdio_get_byte,
stdio_unget_char,
@@ -1314,12 +1338,12 @@ static void tail_strategy(val stream, unsigned long *state)
}
}
-static val tail_get_line(val stream)
+static val tail_get_string(val stream, ucnum limit, wint_t stopchar, val self)
{
unsigned long state = 0;
val ret;
- while ((ret = generic_get_line(stream)) == nil)
+ while ((ret = generic_get_string(stream, limit, stopchar, self)) == nil)
tail_strategy(stream, &state);
return ret;
@@ -1358,7 +1382,7 @@ static struct strm_ops tail_ops =
stdio_put_string,
stdio_put_char,
stdio_put_byte,
- tail_get_line,
+ tail_get_string,
tail_get_char,
tail_get_byte,
stdio_unget_char,
@@ -1440,7 +1464,7 @@ static struct strm_ops pipe_ops =
stdio_put_string,
stdio_put_char,
stdio_put_byte,
- generic_get_line,
+ generic_get_string,
stdio_get_char,
stdio_get_byte,
stdio_unget_char,
@@ -1866,10 +1890,15 @@ static void dir_mark(val stream)
gc_mark(h->err);
}
-static val dir_get_line(val stream)
+static val dir_get_string(val stream, ucnum limit, wint_t stopchar, val self)
{
struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ get_line_limited_check(stream, limit, stopchar, self);
+
+ (void) limit;
+ (void) stopchar;
+
if (h->d == 0) {
return nil;
} else {
@@ -1929,7 +1958,7 @@ static struct strm_ops dir_ops =
0),
wli("dir-stream"),
0, 0, 0,
- dir_get_line,
+ dir_get_string,
0, 0, 0, 0, 0, 0,
dir_close,
0, 0, 0, 0, 0,
@@ -1970,15 +1999,36 @@ static val find_char(val string, val start, val ch)
return nil;
}
-static val string_in_get_line(val stream)
+static val string_in_get_string(val stream, ucnum limit,
+ wint_t stopchar, val self)
{
struct string_in *s = coerce(struct string_in *, stream->co.handle);
+ (void) self;
if (length_str_gt(s->string, s->pos)) {
- val nlpos = find_char(s->string, s->pos, chr('\n'));
- val result = sub_str(s->string, s->pos, nlpos);
- set(mkloc(s->pos, stream), nlpos ? plus(nlpos, one) : length_str(s->string));
- return result;
+ if (limit == UINT_PTR_MAX && stopchar != WEOF) {
+ val chpos = find_char(s->string, s->pos, chr(stopchar));
+ val result = sub_str(s->string, s->pos, chpos);
+ set(mkloc(s->pos, stream), chpos ? plus(chpos, one) : length_str(s->string));
+ return result;
+ } else if (limit == UINT_PTR_MAX && stopchar == WEOF) {
+ val result = sub_str(s->string, s->pos, nil);;
+ set(mkloc(s->pos, stream), length_str(s->string));
+ return result;
+ } else if (limit != UINT_PTR_MAX && stopchar == WEOF) {
+ val topos = min2(length_str(s->string), plus(s->pos, unum(limit)));
+ val result = sub_str(s->string, s->pos, topos);
+ set(mkloc(s->pos, stream), topos);
+ return result;
+ } else {
+ val lpos = min2(length_str(s->string), plus(s->pos, unum(limit)));
+ val chpos = find_char(s->string, s->pos, chr(stopchar));
+ val topos = chpos ? min2(lpos, chpos) : lpos;
+ val nxpos = chpos ? min2(lpos, succ(chpos)) : lpos;
+ val result = sub_str(s->string, s->pos, topos);
+ set(mkloc(s->pos, stream), nxpos);
+ return result;
+ }
}
return nil;
@@ -2049,7 +2099,7 @@ static struct strm_ops string_in_ops =
0),
wli("string-input-stream"),
0, 0, 0,
- string_in_get_line,
+ string_in_get_string,
string_in_get_char,
0,
string_in_unget_char,
@@ -2242,10 +2292,13 @@ static void strlist_in_stream_mark(val stream)
gc_mark(s->list);
}
-static val strlist_in_get_line(val stream)
+static val strlist_in_get_string(val stream, ucnum limit,
+ wint_t stopchar, val self)
{
struct strlist_in *s = coerce(struct strlist_in *, stream->co.handle);
+ get_line_limited_check(stream, limit, stopchar, self);
+
if (s->string) {
val result = sub_str(s->string, s->pos, t);
s->string = pop(&s->list);
@@ -2339,7 +2392,7 @@ static struct strm_ops strlist_in_ops =
0),
wli("strlist-input-stream"),
0, 0, 0,
- strlist_in_get_line,
+ strlist_in_get_string,
strlist_in_get_char,
0,
strlist_in_unget_char,
@@ -2662,14 +2715,17 @@ static void cat_stream_print(val stream, val out, val pretty,
format(out, lit("#<~a ~s>"), name, s->streams, nao);
}
-static val cat_get_line(val stream)
+static val cat_get_string(val stream, ucnum limit, wint_t stopchar, val self)
{
+ uses_or2;
struct cat_strm *s = coerce(struct cat_strm *, stream->co.handle);
val streams = s->streams;
while (streams) {
- val fs = first(streams);
- val line = get_line(fs);
+ val fs = or2(first(streams), std_input);
+ struct strm_ops *ops = coerce(struct strm_ops *,
+ cobj_ops(self, fs, stream_cls));
+ val line = ops->get_string(fs, limit, stopchar, self);
if (line)
return line;
if ((streams = rest(streams)) != nil) {
@@ -2793,7 +2849,7 @@ static struct strm_ops cat_stream_ops =
0),
wli("catenated-stream"),
0, 0, 0,
- cat_get_line,
+ cat_get_string,
cat_get_char,
cat_get_byte,
cat_unget_char,
@@ -3037,10 +3093,12 @@ static void record_adapter_mark_op(val stream)
record_adapter_base_mark(rb);
}
-static val record_adapter_get_line(val stream)
+static val record_adapter_get_string(val stream, ucnum limit,
+ wint_t stopchar, val self)
{
struct record_adapter_base *rb = coerce(struct record_adapter_base *,
stream->co.handle);
+ get_line_limited_check(stream, limit, stopchar, self);
return read_until_match(rb->regex, rb->db.target_stream, rb->include_match);
}
@@ -3053,7 +3111,7 @@ static struct strm_ops record_adapter_ops =
0),
wli("record-adapter"),
delegate_put_string, delegate_put_char, delegate_put_byte,
- record_adapter_get_line, delegate_get_char, delegate_get_byte,
+ record_adapter_get_string, delegate_get_char, delegate_get_byte,
delegate_unget_char, delegate_unget_byte,
delegate_put_buf, delegate_fill_buf,
delegate_close, delegate_flush, delegate_seek,
@@ -3161,7 +3219,21 @@ val get_line(val stream_in)
val stream = default_arg_strict(stream_in, std_input);
struct strm_ops *ops = coerce(struct strm_ops *,
cobj_ops(self, stream, stream_cls));
- return ops->get_line(stream);
+ return ops->get_string(stream, UINT_PTR_MAX, '\n', self);
+}
+
+val get_delimited_string(val stream_in, val nchars_in, val stop_char_in)
+{
+ val self = lit("get-delimited-string");
+ val stream = default_arg_strict(stream_in, std_input);
+ struct strm_ops *ops = coerce(struct strm_ops *,
+ cobj_ops(self, stream, stream_cls));
+ val nchars = default_null_arg(nchars_in);
+ val stop_char = default_null_arg(stop_char_in);
+ ucnum nc = if3(nchars, c_unum(nchars, self), UINT_PTR_MAX);
+ wint_t sc = if3(stop_char, convert(wint_t, c_chr(stop_char)), WEOF);
+
+ return ops->get_string(stream, nc, sc, self);
}
val get_char(val stream_in)
@@ -5769,6 +5841,7 @@ void stream_init(void)
put_char_s = intern(lit("put-char"), user_package);
put_byte_s = intern(lit("put-byte"), user_package);
get_line_s = intern(lit("get-line"), user_package);
+ get_string_s = intern(lit("get-string"), user_package);
get_char_s = intern(lit("get-char"), user_package);
get_byte_s = intern(lit("get-byte"), user_package);
unget_char_s = intern(lit("unget-char"), user_package);
@@ -5839,9 +5912,10 @@ void stream_init(void)
reg_fun(get_error_str_s, func_n1(get_error_str));
reg_fun(clear_error_s, func_n1(clear_error));
reg_fun(get_line_s, func_n1o(get_line, 0));
+ reg_fun(intern(lit("get-delimited-string"), user_package), func_n3o(get_delimited_string, 0));
reg_fun(get_char_s, func_n1o(get_char, 0));
reg_fun(get_byte_s, func_n1o(get_byte, 0));
- reg_fun(intern(lit("get-string"), user_package), func_n3o(get_string, 0));
+ reg_fun(get_string_s, func_n3o(get_string, 0));
reg_fun(put_string_s, func_n2o(put_string, 1));
reg_fun(intern(lit("put-line"), user_package), func_n2o(put_line, 0));
reg_fun(put_char_s, func_n2o(put_char, 1));
diff --git a/stream.h b/stream.h
index 0f207c65..0b413776 100644
--- a/stream.h
+++ b/stream.h
@@ -70,7 +70,7 @@ struct strm_ops {
val (*put_string)(val, val);
val (*put_char)(val, val);
val (*put_byte)(val, int);
- val (*get_line)(val);
+ val (*get_string)(val, ucnum limit, wint_t stopchar, val self);
val (*get_char)(val);
val (*get_byte)(val);
val (*unget_char)(val, val);
@@ -94,12 +94,12 @@ struct strm_ops {
};
#define strm_ops_init(cobj_init_macro, name, put_string, put_char, put_byte, \
- get_line, get_char, get_byte, unget_char, unget_byte, \
+ get_string, get_char, get_byte, unget_char, unget_byte,\
put_buf, fill_buf, \
close, flush, seek, truncate, get_prop, set_prop, \
get_error, get_error_str, clear_error, get_fd) \
{ \
- cobj_init_macro, name, put_string, put_char, put_byte, get_line, \
+ cobj_init_macro, name, put_string, put_char, put_byte, get_string, \
get_char, get_byte, unget_char, unget_byte, put_buf, fill_buf, \
close, flush, seek, truncate, get_prop, set_prop, \
get_error, get_error_str, clear_error, get_fd, 0, 0, 0, 0 \
@@ -164,7 +164,7 @@ extern val stdin_s, stdout_s, stddebug_s, stderr_s, stdnull_s;
extern val put_string_s, put_char_s, put_byte_s, get_line_s, get_char_s;
extern val get_byte_s, unget_char_s, unget_byte_s, put_buf_s, fill_buf_s;
extern val close_s, flush_s, seek_s, truncate_s, get_prop_s, set_prop_s;
-extern val get_error_s, get_error_str_s, clear_error_s, get_fd_s;
+extern val get_error_s, get_error_str_s, clear_error_s, get_fd_s, get_string_s;
extern val print_flo_precision_s, print_flo_digits_s, print_flo_format_s;
extern val pprint_flo_format_s, print_base_s, print_circle_s;
@@ -198,7 +198,7 @@ val normalize_mode_no_bin(struct stdio_mode *m, val mode_str,
struct stdio_mode m_dfl, val self);
val set_mode_props(const struct stdio_mode m, val stream);
ucnum generic_fill_buf(val stream, mem_t *ptr, ucnum len, ucnum pos);
-val generic_get_line(val stream);
+val generic_get_string(val stream, ucnum limit, wint_t stopchar, val self);
val errno_to_string(val err);
val make_null_stream(void);
val make_stdio_stream(FILE *, val descr);
@@ -233,6 +233,7 @@ val get_error(val stream);
val get_error_str(val stream);
val clear_error(val stream);
val get_line(val);
+val get_delimited_string(val, val nchars, val stop_char);
val get_char(val);
val get_byte(val);
val get_bytes(val self, val, mem_t *ptr, ucnum len);
diff --git a/strudel.c b/strudel.c
index 0ddb33ff..23a56931 100644
--- a/strudel.c
+++ b/strudel.c
@@ -81,12 +81,14 @@ static val strudel_put_byte(val stream, int byte)
return funcall2(meth, obj, num_fast(byte));
}
-static val strudel_get_line(val stream)
+static val strudel_get_string(val stream, ucnum limit, wint_t stopchar, val self)
{
struct strudel_base *sb = coerce(struct strudel_base *, stream->co.handle);
val obj = sb->obj;
- val meth = slot(obj, get_line_s);
- return funcall1(meth, obj);
+ val meth = slot(obj, get_string_s);
+ return funcall4(meth, obj,
+ if2(limit != UINT_PTR_MAX, unum(limit)),
+ if2(stopchar != WEOF, chr(stopchar)), self);
}
static val strudel_get_char(val stream)
@@ -247,7 +249,7 @@ static struct strm_ops strudel_ops =
0),
wli("struct-delegate-stream"),
strudel_put_string, strudel_put_char, strudel_put_byte,
- strudel_get_line, strudel_get_char, strudel_get_byte,
+ strudel_get_string, strudel_get_char, strudel_get_byte,
strudel_unget_char, strudel_unget_byte,
strudel_put_buf, strudel_fill_buf,
strudel_close, strudel_flush, strudel_seek,
diff --git a/tests/018/streams.tl b/tests/018/streams.tl
index e3ee783b..1abde546 100644
--- a/tests/018/streams.tl
+++ b/tests/018/streams.tl
@@ -84,3 +84,58 @@
(read-until-match #/:/ s t) "foo:"
(iread s) (1 2 3)
(get-char s) #\x))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 3 #\:) "abc"
+ (get-string s) "d:"))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 4 #\:) "abcd"
+ (get-string s) ":"))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 5 #\:) "abcd"
+ (get-string s) ""))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s) "abcd:"
+ (get-string s) ""))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 3) "abc"
+ (get-string s) "d:"))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 4) "abcd"
+ (get-string s) ":"))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 5) "abcd:"
+ (get-string s) ""))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s 6) "abcd:"
+ (get-string s) ""))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s) "abcd:"
+ (get-string s) ""))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s : #\:) "abcd"
+ (get-string s) ""))
+
+(with-in-string-stream (s "abcd:")
+ (mtest
+ (get-delimited-string s : #\a) ""
+ (get-string s) "bcd:"))
diff --git a/txr.1 b/txr.1
index a58a28b2..5e31fd3f 100644
--- a/txr.1
+++ b/txr.1
@@ -67408,6 +67408,46 @@ longer than
.metn count ,
but may be shorter.
+.coNP Function @ get-delimited-string
+.synb
+.mets (get-delimited-string >> [ stream >> [ count <> [ stop-char ]]])
+.syne
+.desc
+The
+.code get-delimited-string
+function reads characters from a stream, returning them as a
+string.
+
+If the
+.meta count
+argument is given, it should be a non-negative integer. The function
+stops reading when it has accumulated
+.meta count
+characters. If
+.meta count
+is zero, no characters are read and an empty string is returned.
+
+If the
+.meta stop-char
+argument is given, it should be a character. When the function
+reads that character, it discards it and stops reading.
+
+If both
+.meta count
+and
+.meta stop-char
+are specified, the behavior is determined by whichever terminating
+condition occurs first. In the case when exactly
+.meta count
+characters have been read, and the most recently read character is
+.metn stop-char ,
+the semantics of
+.meta stop-char
+prevails, as if
+.meta count
+were absent: the character is ignored and a string consisting
+of the previous characters is returned.
+
.coNP Functions @ unget-char and @ unget-byte
.synb
.mets (unget-char < char <> [ stream ])
@@ -70954,7 +70994,7 @@ subsets of, or all of, the following methods:
.codn put-string ,
.codn put-char ,
.codn put-byte ,
-.codn get-line ,
+.codn get-string ,
.codn get-char ,
.codn get-byte ,
.codn unget-char ,
@@ -71021,19 +71061,32 @@ description of the
.code put-byte
stream I/O function.
-.coNP Method @ get-line
+.coNP Method @ get-string
.synb
-.mets << stream .(get-line)
+.mets << stream .(get-string < count << stop-char )
.syne
.desc
The
-.code get-line
+.code get-string
method is implemented on a stream interface object.
It should behave in a manner consistent with the
description of the
-.code get-line
+.code get-delimited-string
stream I/O function.
+This function is used as the basis for the
+.code get-line
+function also.
+
+All arguments are always passed to this method;
+the parameters are not optional. When
+.meta count
+or
+.meta stop-char
+are understood as not specified, they appear as
+.code nil
+values.
+
.coNP Method @ get-char
.synb
.mets << stream .(get-char)