diff options
-rw-r--r-- | ChangeLog | 26 | ||||
-rw-r--r-- | stream.c | 147 | ||||
-rw-r--r-- | txr.1 | 87 | ||||
-rw-r--r-- | txr.c | 12 | ||||
-rw-r--r-- | txr.h | 1 |
5 files changed, 242 insertions, 31 deletions
@@ -1,3 +1,29 @@ +2015-03-29 Kaz Kylheku <kaz@kylheku.com> + + 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. + 2015-03-28 Kaz Kylheku <kaz@kylheku.com> * eval.c (prinl, pprinl): Become external functions. @@ -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)); @@ -446,9 +446,32 @@ of the query, only during its execution. .coIP -d .coIP --debugger - Invoke the interactive \*(TX debugger. See the DEBUGGER section. +.coIP -n +.coIP --noninteractive +This option affects behavior related to \*(TX's +.code *std-input* +stream. Normally, if this stream is connected to a terminal device, it is +automatically marked as having the real-time property when \*(TX starts up (see +the functions .code stream-set-prop and +.codn real-time-stream-p ). +The +.code -n +option suppresses this behavior; the +.code *std-input* +stream remains ordinary. + +The \*(TX pattern language reads standard input via +a lazy list, created by applying the +.code lazy-stream-cons +function to the +.code *std-input* +stream. If that stream is marked real-time, then the lazy list which is +returned by that function has behaviors that are better suited for scanning +interactive input. A more detailed explanation is given under the description +of this function. + .coIP -v Verbose operation. Detailed logging is enabled. @@ -22344,12 +22367,15 @@ item. The ordinary lazy streams read ahead by one line and suppress this extra item, so their representation is more accurate. -Streams connected to TTY devices (devices which for which the +When \*(TX starts up, it automatically marks the +.code *std-input* +stream as real-time, if it is connected to a TTY device (a device for which +the POSIX function .code isatty -POSIX function reports true) are automatically marked as real-time. This is -only supported on platforms that have an -.code isatty -function. +reports true). This is only supported on platforms that have this function. +The behvior is overridden by the +.code -n +command line option. .coNP Function @ make-string-input-stream .synb @@ -22849,7 +22875,7 @@ also returned if the property exists, but its value happens to be Properties are currently used for marking certain streams as "real-time" (see the -.code stream-real-time-p +.code real-time-stream-p function above), and also for setting the priority at which messages are reported to syslog by the .code *stdlog* @@ -23182,11 +23208,11 @@ The argument is a string which uses the same conventions as the mode argument of the C language .code fopen -function. +function, with some extensions. The mode string determines whether the stream is an input stream or output stream. Note that the .str b -mode is not supported. +mode is passed through to the C library, but has no special meaning to \*(TX. Whether a stream is text or binary depends on which operations are invoked on it. @@ -23196,9 +23222,14 @@ argument is omitted, mode .str r is used. +The option letter +.str i +is supported. If present, it will create a stream which has the real-time +property set. + .coNP Function @ open-tail .synb -.mets (open-tail < path <> [[ mode-string ] << seek-to-end-p ]) +.mets (open-tail < path >> [ mode-string <> [ seek-to-end-p ]]) .syne .desc The @@ -23211,14 +23242,12 @@ The argument is a string which uses the same conventions as the mode argument of the C language .code fopen -function. -If it is missing, it defaults to -.strn r . -Note that the -.str b -mode is not supported. -Whether a stream is text or binary depends on which operations are invoked on -it. +function. If this argument is omitted, then +.str r +is used. +See the +.code open-file +function for a discussion of modes. The .code seek-to-end-p @@ -23307,7 +23336,9 @@ the argument is optional, defaulting to the value .str r -if it is missing. +if it is missing. See the +.code open-file +function for a discussion of modes. The .code open-command @@ -27219,9 +27250,23 @@ option can be used to request emulation of old behavior. The option was introduced in \*(TX 98, and so the oldest \*(TX version which can be emulated is \*(TX 97. -Here are values which have a special meaning as arguments to the +Here are version values which have a special meaning as arguments to the +.code -C +option, along with a description of what behaviors are affected. For each +of these version values, the described behaviors are provided if .code -C -option, along with a description of what behaviors are affected: +is given an argument which is equal or lower. For instance +.code -C 103 +selects the behaviors described below for verison 105, but not those for 102. + +.IP 105 +Provides the behavior that the +.code open-file +function automatically marks a stream open on a TTY devices as a real-time stream +(subject to the availability of the POSIX +.code isatty +function). + .IP 102 Up to \*(TX 102, the .code get-string @@ -60,6 +60,7 @@ const wchli_t *version = wli(TXR_VER); const wchar_t *progname = L"txr"; static const char *progname_u8; static val progpath = nil; +int opt_noninteractive; int opt_compat; /* @@ -111,6 +112,8 @@ static void help(void) " if termination is unsuccessful.\n" "-l If dumping bindings, use TXR Lisp format.\n" "-d Debugger mode.\n" +"-n Noninteractive input mode for standard input stream,\n" +" even if its connected to a terminal device.\n" "-a N Generate array variables up to N dimensions.\n" " N is a decimal integer. The default value is 1.\n" " Additional dimensions beyond N are fudged\n" @@ -137,6 +140,7 @@ static void help(void) " section at the bottom of the license.\n" "--lisp-bindings Synonym for -l\n" "--debugger Synonym for -d\n" +"--noninteractive Synonym for -n\n" "--compat=N Synonym for -C N\n" "--gc-delta=N Invoke garbage collection when malloc activity\n" " increments by N megabytes since last collection.\n" @@ -499,6 +503,10 @@ int txr_main(int argc, char **argv) prog_string, arg, nao); return EXIT_FAILURE; #endif + } else if (equal(opt, lit("noninteractive"))) { + opt_noninteractive = 1; + stream_set_prop(std_input, real_time_k, nil); + continue; } } @@ -587,6 +595,10 @@ int txr_main(int argc, char **argv) return EXIT_FAILURE; #endif break; + case 'n': + opt_noninteractive = 1; + stream_set_prop(std_input, real_time_k, nil); + break; case 'a': case 'c': case 'e': @@ -33,6 +33,7 @@ extern int opt_gc_debug; extern int opt_vg_debug; #endif extern int opt_derivative_regex; +extern int opt_noninteractive; extern int opt_compat; extern alloc_bytes_t opt_gc_delta; extern const wchli_t *version; |