diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2015-03-29 13:16:46 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2015-03-29 13:16:46 -0700 |
commit | c6e5383217c70914a933f1911bf48a1e54b26595 (patch) | |
tree | 5e40a74c23c75fc2bd786490732682618585ec9e /stream.c | |
parent | 084bde656bac142bba5311b519c7bb78e2c45dad (diff) | |
download | txr-c6e5383217c70914a933f1911bf48a1e54b26595.tar.gz txr-c6e5383217c70914a933f1911bf48a1e54b26595.tar.bz2 txr-c6e5383217c70914a933f1911bf48a1e54b26595.zip |
New -n option. New "i" mode letter in file opening functions.
* stream.c (struct stdio_mode): New struct type.
(stdio_mode_init_trivial): New initializer macro.
(parse_mode, format_mode, normalize_mode, set_mode_props):
New static functions.
(make_stdio_stream_common): the isatty check, and automatic marking
of tty device streams as real-time is no longer done, unless
backward compatibility 105 or earlier is requested.
(open_file, open_tail, open_command, open_process): Use new
mode parsing, which supports the "i" flag.
(stream_init): If we have isatty, and standard input is a tty,
then mark the *std-input* stream as real-time.
* txr.1: Document -n/--noninteractive option. Fix typo where
real-time-stream-p is referred to as stream-real-time-p.
Document "i" flag on open-file and others.
* txr.c (opt_noninteractive): New global.
(help): Help text for -n/--noninteractive.
(txr_main): Handle new options.
* txr.h (opt_noninteractive): Declared.
Diffstat (limited to 'stream.c')
-rw-r--r-- | stream.c | 147 |
1 files changed, 137 insertions, 10 deletions
@@ -1220,6 +1220,122 @@ static struct strm_ops dir_ops = 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) +{ + 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)); @@ -1233,7 +1349,8 @@ static val make_stdio_stream_common(FILE *f, val descr, struct cobj_ops *ops) h->mode = nil; h->is_rotated = 0; #if HAVE_ISATTY - h->is_real_time = (h->f != 0 && isatty(fileno(h->f)) == 1); + 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 @@ -2183,18 +2300,20 @@ val open_directory(val path) val open_file(val path, val mode_str) { - FILE *f = w_fopen(c_str(path), c_str(default_arg(mode_str, lit("r")))); + struct stdio_mode m; + FILE *f = w_fopen(c_str(path), c_str(normalize_mode(&m, mode_str))); if (!f) uw_throwf(file_error_s, lit("error opening ~a: ~a/~s"), path, num(errno), string_utf8(strerror(errno)), nao); - return make_stdio_stream(f, path); + return set_mode_props(m, make_stdio_stream(f, path)); } val open_tail(val path, val mode_str, val seek_end_p) { - val mode = default_arg(mode_str, lit("r")); + struct stdio_mode m; + val mode = normalize_mode(&m, mode_str); FILE *f = w_fopen(c_str(path), c_str(mode)); struct stdio_handle *h; val stream; @@ -2210,24 +2329,27 @@ val open_tail(val path, val mode_str, val seek_end_p) h->mode = mode; if (!f) tail_strategy(stream, &state); - return stream; + return set_mode_props(m, stream); } val open_command(val path, val mode_str) { - FILE *f = w_popen(c_str(path), c_str(default_arg(mode_str, lit("r")))); + struct stdio_mode m; + FILE *f = w_popen(c_str(path), c_str(normalize_mode(&m, mode_str))); if (!f) uw_throwf(file_error_s, lit("error opening pipe ~a: ~a/~s"), path, num(errno), string_utf8(strerror(errno)), nao); - return make_pipe_stream(f, path); + return set_mode_props(m, make_pipe_stream(f, path)); } #if HAVE_FORK_STUFF val open_process(val name, val mode_str, val args) { - int input = equal(mode_str, lit("r")) || equal(mode_str, lit("rb")); + struct stdio_mode m; + val mode = normalize_mode(&m, mode_str); + int input = m.read != 0; int fd[2]; pid_t pid; char **argv = 0; @@ -2277,7 +2399,7 @@ val open_process(val name, val mode_str, val args) _exit(errno); } else { int whichfd; - char *utf8mode = utf8_dup_to(c_str(mode_str)); + char *utf8mode = utf8_dup_to(c_str(mode)); FILE *f; if (input) { @@ -2309,7 +2431,7 @@ val open_process(val name, val mode_str, val args) free(utf8mode); /* TODO: catch potential OOM exception here and kill process. */ - return make_pipevp_stream(f, name, pid); + return set_mode_props(m, make_pipevp_stream(f, name, pid)); } } #else @@ -2712,6 +2834,11 @@ void stream_init(void) reg_var(stdnull_s = intern(lit("*stdnull*"), user_package), make_null_stream()); +#if HAVE_ISATTY + if (isatty(fileno(stdin)) == 1) + stream_set_prop(std_input, real_time_k, t); +#endif + reg_fun(format_s, func_n2v(formatv)); reg_fun(intern(lit("make-string-input-stream"), user_package), func_n1(make_string_input_stream)); reg_fun(intern(lit("make-string-byte-input-stream"), user_package), func_n1(make_string_byte_input_stream)); |