summaryrefslogtreecommitdiffstats
path: root/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'stream.c')
-rw-r--r--stream.c491
1 files changed, 289 insertions, 202 deletions
diff --git a/stream.c b/stream.c
index 7d31c89d..1546ff50 100644
--- a/stream.c
+++ b/stream.c
@@ -50,13 +50,11 @@ obj_t *std_input, *std_output, *std_error;
struct strm_ops {
struct cobj_ops cobj_ops;
- obj_t *(*put_string)(obj_t *, const wchar_t *);
- obj_t *(*put_char)(obj_t *, wchar_t);
+ obj_t *(*put_string)(obj_t *, obj_t *);
+ obj_t *(*put_char)(obj_t *, obj_t *);
obj_t *(*get_line)(obj_t *);
obj_t *(*get_char)(obj_t *);
obj_t *(*get_byte)(obj_t *);
- obj_t *(*vcformat)(obj_t *, const wchar_t *fmt, va_list vl);
- obj_t *(*vformat)(obj_t *, const wchar_t *fmt, va_list vl);
obj_t *(*close)(obj_t *, obj_t *);
};
@@ -70,47 +68,6 @@ static void common_destroy(obj_t *obj)
(void) close_stream(obj, nil);
}
-obj_t *common_vformat(obj_t *stream, const wchar_t *fmt, va_list vl)
-{
- wchar_t ch;
-
- for (; (ch = *fmt) != 0; fmt++) {
- obj_t *obj;
-
- if (ch == '~') {
- ch = *++fmt;
- if (ch == 0)
- abort();
- switch (ch) {
- case '~':
- put_cchar(stream, ch);
- continue;
- case 'a':
- obj = va_arg(vl, obj_t *);
- if (obj == nao)
- abort();
- obj_pprint(obj, stream);
- continue;
- case 's':
- obj = va_arg(vl, obj_t *);
- if (obj == nao)
- abort();
- obj_print(obj, stream);
- continue;
- default:
- abort();
- }
- continue;
- }
-
- put_cchar(stream, ch);
- }
-
- if (va_arg(vl, obj_t *) != nao)
- internal_error("unterminated format argument list");
- return t;
-}
-
struct stdio_handle {
FILE *f;
#ifdef BROKEN_POPEN_GETWC
@@ -122,7 +79,7 @@ struct stdio_handle {
void stdio_stream_print(obj_t *stream, obj_t *out)
{
struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
- format(out, L"#<~s ~s>", stream->co.cls, h->descr, nao);
+ format(out, lit("#<~s ~s>"), stream->co.cls, h->descr, nao);
}
void stdio_stream_destroy(obj_t *stream)
@@ -143,8 +100,8 @@ static obj_t *stdio_maybe_read_error(obj_t *stream)
struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
if (ferror(h->f)) {
clearerr(h->f);
- uw_throwf(file_error, L"error reading ~a: ~a/~s",
- stream, num(errno), string_utf8(strerror(errno)));
+ uw_throwf(file_error, lit("error reading ~a: ~a/~s"),
+ stream, num(errno), string_utf8(strerror(errno)), nao);
}
return nil;
}
@@ -154,22 +111,23 @@ static obj_t *stdio_maybe_write_error(obj_t *stream)
struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
if (ferror(h->f)) {
clearerr(h->f);
- uw_throwf(file_error, L"error writing ~a: ~a/~s",
- stream, num(errno), string_utf8(strerror(errno)));
+ uw_throwf(file_error, lit("error writing ~a: ~a/~s"),
+ stream, num(errno), string_utf8(strerror(errno)), nao);
}
return nil;
}
-static obj_t *stdio_put_string(obj_t *stream, const wchar_t *s)
+static obj_t *stdio_put_string(obj_t *stream, obj_t *s)
{
struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
- return (h->f && fputws(s, h->f) != -1) ? t : stdio_maybe_write_error(stream);
+ return (h->f && fputws(c_str(s), h->f) != -1)
+ ? t : stdio_maybe_write_error(stream);
}
-static obj_t *stdio_put_char(obj_t *stream, wchar_t ch)
+static obj_t *stdio_put_char(obj_t *stream, obj_t *ch)
{
struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
- return (h->f && putwc(ch, h->f) != WEOF)
+ return (h->f && putwc(c_chr(ch), h->f) != WEOF)
? t : stdio_maybe_write_error(stream);
}
@@ -238,17 +196,6 @@ obj_t *stdio_get_byte(obj_t *stream)
return nil;
}
-obj_t *stdio_vcformat(obj_t *stream, const wchar_t *fmt, va_list vl)
-{
- struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
-
- if (h->f) {
- int n = vfwprintf(h->f, fmt, vl);
- return (n >= 0) ? num(n) : stdio_maybe_write_error(stream);
- }
- return nil;
-}
-
static obj_t *stdio_close(obj_t *stream, obj_t *throw_on_error)
{
struct stdio_handle *h = (struct stdio_handle *) stream->co.handle;
@@ -257,8 +204,8 @@ static obj_t *stdio_close(obj_t *stream, obj_t *throw_on_error)
int result = fclose(h->f);
h->f = 0;
if (result == EOF && throw_on_error) {
- uw_throwf(file_error, L"error closing ~a: ~a/~s",
- stream, num(errno), string_utf8(strerror(errno)));
+ uw_throwf(file_error, lit("error closing ~a: ~a/~s"),
+ stream, num(errno), string_utf8(strerror(errno)), nao);
}
return result != EOF ? t : nil;
}
@@ -275,8 +222,6 @@ static struct strm_ops stdio_ops = {
stdio_get_line,
stdio_get_char,
stdio_get_byte,
- stdio_vcformat,
- common_vformat,
stdio_close
};
@@ -296,22 +241,23 @@ static obj_t *pipe_close(obj_t *stream, obj_t *throw_on_error)
if (status != 0 && throw_on_error) {
if (status < 0) {
uw_throwf(process_error,
- L"unable to obtain status of command ~a: ~a/~s",
+ lit("unable to obtain status of command ~a: ~a/~s"),
stream, num(errno), string_utf8(strerror(errno)), nao);
} else if (WIFEXITED(status)) {
int exitstatus = WEXITSTATUS(status);
- uw_throwf(process_error, L"pipe ~a terminated with status ~a",
+ uw_throwf(process_error, lit("pipe ~a terminated with status ~a"),
stream, num(exitstatus), nao);
} else if (WIFSIGNALED(status)) {
int termsig = WTERMSIG(status);
- uw_throwf(process_error, L"pipe ~a terminated by signal ~a",
+ uw_throwf(process_error, lit("pipe ~a terminated by signal ~a"),
stream, num(termsig), nao);
} else if (WIFSTOPPED(status) || WIFCONTINUED(status)) {
- uw_throwf(process_error, L"processes of closed pipe ~a still running",
+ uw_throwf(process_error,
+ lit("processes of closed pipe ~a still running"),
stream, nao);
} else {
- uw_throwf(file_error, L"strange status in when closing pipe ~a",
+ uw_throwf(file_error, lit("strange status in when closing pipe ~a"),
stream, nao);
}
}
@@ -331,8 +277,6 @@ static struct strm_ops pipe_ops = {
stdio_get_line,
stdio_get_char,
stdio_get_byte,
- stdio_vcformat,
- common_vformat,
pipe_close
};
@@ -382,8 +326,6 @@ static struct strm_ops string_in_ops = {
string_in_get_line,
string_in_get_char,
0,
- 0,
- 0,
0
};
@@ -412,8 +354,6 @@ static struct strm_ops byte_in_ops = {
0,
0,
byte_in_get_byte,
- 0,
- 0,
0
};
@@ -436,14 +376,15 @@ static void string_out_stream_destroy(obj_t *stream)
}
}
-static obj_t *string_out_put_string(obj_t *stream, const wchar_t *s)
+static obj_t *string_out_put_string(obj_t *stream, obj_t *str)
{
struct string_output *so = (struct string_output *) stream->co.handle;
if (so == 0) {
return nil;
} else {
- size_t len = wcslen(s);
+ const wchar_t *s = c_str(str);
+ size_t len = c_num(length_str(str));
size_t old_size = so->size;
size_t required_size = len + so->fill + 1;
@@ -456,67 +397,20 @@ static obj_t *string_out_put_string(obj_t *stream, const wchar_t *s)
return nil;
}
- so->buf = chk_realloc(so->buf, so->size * sizeof *so->buf);
- memcpy(so->buf + so->fill, s, (len + 1) * sizeof *so->buf);
+ if (so->size != old_size)
+ so->buf = chk_realloc(so->buf, so->size * sizeof *so->buf);
+ wmemcpy(so->buf + so->fill, s, len + 1);
so->fill += len;
return t;
}
}
-static obj_t *string_out_put_char(obj_t *stream, wchar_t ch)
+static obj_t *string_out_put_char(obj_t *stream, obj_t *ch)
{
wchar_t mini[2];
- mini[0] = ch;
+ mini[0] = c_chr(ch);
mini[1] = 0;
- return string_out_put_string(stream, mini);
-}
-
-obj_t *string_out_vcformat(obj_t *stream, const wchar_t *fmt, va_list vl)
-{
- struct string_output *so = (struct string_output *) stream->co.handle;
-
- if (so == 0) {
- return nil;
- } else {
- int nchars, nchars2;
- wchar_t dummy_buf[1];
- size_t old_size = so->size;
- size_t required_size;
- va_list vl_copy;
-
-#if defined va_copy
- va_copy (vl_copy, vl);
-#elif defined __va_copy
- __va_copy (vl_copy, vl);
-#else
- vl_copy = vl;
-#endif
-
- nchars = vswprintf(dummy_buf, 0, fmt, vl_copy);
-
-#if defined va_copy || defined __va_copy
- va_end (vl_copy);
-#endif
-
- bug_unless (nchars >= 0);
-
- required_size = so->fill + nchars + 1;
-
- if (required_size < so->fill)
- return nil;
-
- while (so->size <= required_size) {
- so->size *= 2;
- if (so->size < old_size)
- return nil;
- }
-
- so->buf = chk_realloc(so->buf, so->size * sizeof *so->buf);
- nchars2 = vswprintf(so->buf + so->fill, so->size-so->fill, fmt, vl);
- bug_unless (nchars == nchars2);
- so->fill += nchars;
- return t;
- }
+ return string_out_put_string(stream, auto_str(mini));
}
static struct strm_ops string_out_ops = {
@@ -529,8 +423,6 @@ static struct strm_ops string_out_ops = {
0,
0,
0,
- string_out_vcformat,
- common_vformat,
0,
};
@@ -573,8 +465,6 @@ static struct strm_ops dir_ops = {
dir_get_line,
0,
0,
- 0,
- 0,
dir_close
};
@@ -605,7 +495,7 @@ obj_t *make_pipe_stream(FILE *f, obj_t *descr, obj_t *input, obj_t *output)
/* Don't leave h uninitialized; it is gc-reachable through stream cobj. */
h->f = h->f_orig_pipe = 0;
h->descr = descr;
- uw_throwf(process_error, L"unable to create pipe ~a: ~a/~s", descr,
+ uw_throwf(process_error, lit("unable to create pipe ~a: ~a/~s"), descr,
num(error), string_utf8(strerror(error)), nao);
}
@@ -625,7 +515,7 @@ obj_t *make_string_input_stream(obj_t *string)
obj_t *make_string_byte_input_stream(obj_t *string)
{
- type_assert (stringp(string), (L"~a is not a string", string));
+ type_assert (stringp(string), (lit("~a is not a string"), string, nao));
{
struct byte_input *bi = (struct byte_input *) chk_malloc(sizeof *bi);
@@ -650,7 +540,8 @@ obj_t *make_string_output_stream(void)
obj_t *get_string_from_stream(obj_t *stream)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t,
+ (lit("~a is not a stream"), stream, nao));
if (stream->co.ops == &string_out_ops.cobj_ops) {
struct string_output *so = (struct string_output *) stream->co.handle;
@@ -661,7 +552,7 @@ obj_t *get_string_from_stream(obj_t *stream)
if (!so)
return out;
- so->buf = chk_realloc(so->buf, so->fill + 1);
+ so->buf = chk_realloc(so->buf, (so->fill + 1) * sizeof *so->buf);
out = string_own(so->buf);
free(so);
return out;
@@ -681,7 +572,8 @@ obj_t *make_dir_stream(DIR *dir)
obj_t *close_stream(obj_t *stream, obj_t *throw_on_error)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
@@ -692,7 +584,8 @@ obj_t *close_stream(obj_t *stream, obj_t *throw_on_error)
obj_t *get_line(obj_t *stream)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
@@ -703,7 +596,8 @@ obj_t *get_line(obj_t *stream)
obj_t *get_char(obj_t *stream)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
@@ -714,7 +608,8 @@ obj_t *get_char(obj_t *stream)
obj_t *get_byte(obj_t *stream)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
@@ -722,57 +617,269 @@ obj_t *get_byte(obj_t *stream)
}
}
-obj_t *vformat(obj_t *stream, const wchar_t *str, va_list vl)
+static obj_t *vformat_num(obj_t *stream, const char *str,
+ int width, int left, int pad, int precision)
{
- type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ int len = strlen(str);
+ int truewidth = (width < precision) ? width : precision;
+ int slack = (len < truewidth) ? truewidth - len : 0;
+ int padlen = (len < precision) ? precision - len : 0;
+ int i;
- {
- struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
- return ops->vformat ? ops->vformat(stream, str, vl) : nil;
- }
+ if (!left)
+ for (i = 0; i < slack; i++)
+ if (!put_char(stream, pad ? chr('0') : chr(' ')))
+ return nil;
+
+ for (i = 0; i < padlen; i++)
+ if (!put_char(stream, pad ? chr('0') : chr(' ')))
+ return nil;
+
+ while (*str)
+ if (!put_char(stream, chr(*str++)))
+ return nil;
+
+ if (left)
+ for (i = 0; i < slack; i++)
+ if (!put_char(stream, chr(' ')))
+ return nil;
+
+ return t;
}
-obj_t *vcformat(obj_t *stream, const wchar_t *string, va_list vl)
+obj_t *vformat_str(obj_t *stream, obj_t *str, int width, int left,
+ int precision)
{
- type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ const wchar_t *cstr = c_str(str);
+ int len = c_num(length_str(str));
+ int truelen = (precision && precision < len) ? precision : len;
+ int slack = (truelen < width) ? width - truelen : 0;
+ int i;
- {
- struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
- return ops->vcformat ? ops->vcformat(stream, string, vl) : nil;
- }
+ if (!left)
+ for (i = 0; i < slack; i++)
+ if (!put_char(stream, chr(' ')))
+ return nil;
+
+ for (i = 0; i < truelen; i++)
+ if (!put_char(stream, chr(cstr[i])))
+ return nil;
+
+ if (left)
+ for (i = 0; i < slack; i++)
+ if (!put_char(stream, chr(' ')))
+ return nil;
+
+ return t;
}
-obj_t *format(obj_t *stream, const wchar_t *str, ...)
+obj_t *vformat(obj_t *stream, obj_t *fmtstr, va_list vl)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
- struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
- va_list vl;
- obj_t *ret;
+ const wchar_t *fmt = c_str(fmtstr);
+ enum {
+ vf_init, vf_width, vf_digits, vf_precision, vf_spec
+ } state = vf_init, saved_state = vf_init;
+ int width = 0, precision = 0, digits = 0;
+ int left = 0, zeropad = 0;
+ long val;
+ void *ptr;
+ char num_buf[64];
- va_start (vl, str);
- ret = ops->vformat ? ops->vformat(stream, str, vl) : nil;
- va_end (vl);
- return ret;
+ for (;;) {
+ obj_t *obj;
+ wchar_t ch = *fmt++;
+
+ switch (state) {
+ case vf_init:
+ switch (ch) {
+ case 0:
+ break;
+ case '~':
+ state = vf_width;
+ width = 0;
+ left = 0;
+ zeropad = 0;
+ precision = 0;
+ digits = 0;
+ continue;
+ default:
+ put_char(stream, chr(ch));
+ continue;
+ }
+ break;
+ case vf_width:
+ switch (ch) {
+ case '~':
+ put_char(stream, chr('~'));
+ continue;
+ case '-':
+ left = 1;
+ continue;
+ case ',':
+ state = vf_precision;
+ continue;
+ case '0':
+ saved_state = state;
+ state = vf_digits;
+ zeropad = 1;
+ continue;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ saved_state = state;
+ state = vf_digits;
+ digits = ch - '0';
+ continue;
+ case '*':
+ obj = va_arg(vl, obj_t *);
+ width = c_num(obj);
+ state = vf_precision;
+ continue;
+ default:
+ state = vf_spec;
+ --fmt;
+ continue;
+ }
+ break;
+ case vf_precision:
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ saved_state = state;
+ state = vf_digits;
+ digits = ch - '0';
+ continue;
+ case '*':
+ obj = va_arg(vl, obj_t *);
+ width = c_num(obj);
+ precision = vf_precision;
+ continue;
+ default:
+ state = vf_spec;
+ continue;
+ }
+ break;
+ case vf_digits:
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ digits = (digits * 10) + (ch - '0');
+ if (digits > 999999)
+ goto toobig;
+ continue;
+ default:
+ switch (saved_state) {
+ case vf_width:
+ if (width < 0) {
+ width = -digits;
+ left = 1;
+ } else {
+ width = digits;
+ }
+ state = (ch == ',') ? vf_precision : vf_spec;
+ continue;
+ case vf_precision:
+ precision = digits;
+ state = vf_spec;
+ --fmt;
+ continue;
+ default:
+ internal_error("unexpected state in formatter");
+ }
+ }
+ break;
+ case vf_spec:
+ state = vf_init;
+ switch (ch) {
+ case 'x':
+ obj = va_arg(vl, obj_t *);
+ val = c_num(obj);
+ sprintf(num_buf, "%lx", val);
+ goto output_num;
+ case 'X':
+ obj = va_arg(vl, obj_t *);
+ val = c_num(obj);
+ sprintf(num_buf, "%lX", val);
+ goto output_num;
+ case 'o':
+ obj = va_arg(vl, obj_t *);
+ val = c_num(obj);
+ sprintf(num_buf, "%lo", val);
+ goto output_num;
+ case 'a':
+ obj = va_arg(vl, obj_t *);
+ if (obj == nao)
+ goto premature;
+ if (nump(obj)) {
+ val = c_num(obj);
+ sprintf(num_buf, "%ld", val);
+ goto output_num;
+ } else if (stringp(obj)) {
+ if (!vformat_str(stream, obj, width, left, precision))
+ return nil;
+ continue;
+ }
+ obj_pprint(obj, stream);
+ continue;
+ case 's':
+ obj = va_arg(vl, obj_t *);
+ if (obj == nao)
+ goto premature;
+ if (nump(obj)) {
+ val = c_num(obj);
+ sprintf(num_buf, "%ld", val);
+ if (vformat_num(stream, num_buf, 0, 0, 0, 0))
+ return nil;
+ continue;
+ }
+ obj_print(obj, stream);
+ continue;
+ case 'p':
+ ptr = va_arg(vl, void *);
+ val = (int) ptr;
+ sprintf(num_buf, "0x%lx", val);
+ goto output_num;
+ default:
+ abort();
+ output_num:
+ if (!vformat_num(stream, num_buf, width, left,
+ precision ? 0 : zeropad,
+ precision ? precision : 1))
+ return nil;
+ continue;
+ }
+ continue;
+ }
+
+ break;
+ }
}
+
+
+ if (va_arg(vl, obj_t *) != nao)
+ internal_error("unterminated format argument list");
+ return t;
+premature:
+ internal_error("insufficient arguments for format");
+toobig:
+ internal_error("ridiculous precision or field width in format");
}
-obj_t *cformat(obj_t *stream, const wchar_t *string, ...)
+obj_t *format(obj_t *stream, obj_t *str, ...)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
- struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
va_list vl;
obj_t *ret;
-
- va_start (vl, string);
- ret = ops->vformat ? ops->vcformat(stream, string, vl) : nil;
+ va_start (vl, str);
+ ret = vformat(stream, str, vl);
va_end (vl);
return ret;
}
@@ -781,40 +888,20 @@ obj_t *cformat(obj_t *stream, const wchar_t *string, ...)
obj_t *put_string(obj_t *stream, obj_t *string)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
-
- {
- struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
- return ops->put_string ? ops->put_string(stream, c_str(string)) : nil;
- }
-}
-
-obj_t *put_cstring(obj_t *stream, const wchar_t *str)
-{
- type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
- return ops->put_string ? ops->put_string(stream, str) : nil;
+ return ops->put_string ? ops->put_string(stream, string) : nil;
}
}
obj_t *put_char(obj_t *stream, obj_t *ch)
{
type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
-
- {
- struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
- return ops->put_char ? ops->put_char(stream, c_chr(ch)) : nil;
- }
-}
-
-obj_t *put_cchar(obj_t *stream, wchar_t ch)
-{
- type_check (stream, COBJ);
- type_assert (stream->co.cls == stream_t, (L"~a is not a stream", stream));
+ type_assert (stream->co.cls == stream_t, (lit("~a is not a stream"),
+ stream, nao));
{
struct strm_ops *ops = (struct strm_ops *) stream->co.ops;
@@ -824,7 +911,7 @@ obj_t *put_cchar(obj_t *stream, wchar_t ch)
obj_t *put_line(obj_t *stream, obj_t *string)
{
- return (put_string(stream, string), put_cchar(stream, '\n'));
+ return (put_string(stream, string), put_char(stream, chr('\n')));
}
void stream_init(void)