diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2009-11-16 22:05:28 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2009-11-16 22:05:28 -0800 |
commit | 3a6c04927b4136a195b0bc259f50caf8249dfced (patch) | |
tree | d8972160fe49f6ccb60ea868a0af2fdd4d60845e /stream.c | |
parent | fb2f0af8bd14283524e5842b43461ea3fc7701ca (diff) | |
download | txr-3a6c04927b4136a195b0bc259f50caf8249dfced.tar.gz txr-3a6c04927b4136a195b0bc259f50caf8249dfced.tar.bz2 txr-3a6c04927b4136a195b0bc259f50caf8249dfced.zip |
Big round of changes to switch the code base to use the stream
abstraction instead of directly using C standard I/O,
to eliminate most uses of C formatted I/O,
and fix numerous bugs, such variadic argument lists which
lack a terminating ``nao'' sentinel.
Bug 28033 is addressed by this patch, since streams no longer provide
printf-compatible formatting. The native formatter is extended with
some additional capabilities to take over.
The work on literal objects is expanded and they are now used
throughout the code base.
Fixed bad realloc in string output stream: reallocating by number
of wide chars rather than bytes.
Diffstat (limited to 'stream.c')
-rw-r--r-- | stream.c | 491 |
1 files changed, 289 insertions, 202 deletions
@@ -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) |