From 469cd5ab27bba7679bebe60cd0617e09f4838e50 Mon Sep 17 00:00:00 2001 From: "Paul A. Patience" Date: Sun, 29 Aug 2021 04:42:04 -0400 Subject: open-file: add mode option "x". * stream.h (struct stdio_mode): New member, excl flag. (stdio_mode_init_blank, stdio_mode_init_r, stdio_mode_init_rpb): Add initializer for excl flag. * stream.c (do_parse_mode): Handle 'x' in mode string. (w_fopen_mode): Add O_EXCL flag if m.excl is set. Throw an error if we don't HAVE_FCNTL and m.excl is set. * txr.1: Document mode option "x". --- stream.c | 12 +++++++++++- stream.h | 7 ++++--- txr.1 | 12 +++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/stream.c b/stream.c index f055a987..1183f7f5 100644 --- a/stream.c +++ b/stream.c @@ -1130,6 +1130,7 @@ static FILE *w_fopen_mode(const wchar_t *wname, const wchar_t *mode, if3(m.create || m.append, if3(!m.notrunc, O_TRUNC, 0) | O_CREAT, 0) | if3(m.append, O_APPEND, 0) | + if3(m.excl, O_EXCL, 0) | if3(m.nonblock, O_NONBLOCK, 0)); char *stkname = coerce(char *, alloca(nsiz)); int fd; @@ -1143,7 +1144,8 @@ static FILE *w_fopen_mode(const wchar_t *wname, const wchar_t *mode, return (fd < 0) ? NULL : w_fdopen(fd, mode); #else - if (m.notrunc || m.nonblock) + /* TODO: detect if fopen supports "x" in mode */ + if (m.notrunc || m.excl || m.nonblock) uw_throwf(file_error_s, lit("open-file: specified mode not supported on this system"), nao); @@ -1484,6 +1486,14 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl, case 'b': m.binary = 1; break; + case 'x': + /* Ensure only "w" and "w+" can have the "x" option. */ + if (!m.write || !m.create || m.notrunc) { + m.malformed = 1; + return m; + } + m.excl = 1; + break; case 'i': m.interactive = 1; break; diff --git a/stream.h b/stream.h index ae7946e1..8454a6a7 100644 --- a/stream.h +++ b/stream.h @@ -111,6 +111,7 @@ struct stdio_mode { unsigned append : 1; unsigned binary : 1; unsigned notrunc : 1; + unsigned excl : 1; unsigned nonblock : 1; unsigned interactive : 1; unsigned unbuf : 1; @@ -119,9 +120,9 @@ struct stdio_mode { int redir[STDIO_MODE_NREDIRS][2]; }; -#define stdio_mode_init_blank { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } -#define stdio_mode_init_r { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } -#define stdio_mode_init_rpb { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, { { 0 } } } +#define stdio_mode_init_blank { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } +#define stdio_mode_init_r { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } +#define stdio_mode_init_rpb { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } #define std_input (deref(lookup_var_l(nil, stdin_s))) #define std_output (deref(lookup_var_l(nil, stdout_s))) diff --git a/txr.1 b/txr.1 index 19c67f52..3cfa373a 100644 --- a/txr.1 +++ b/txr.1 @@ -56510,7 +56510,7 @@ Note that it permits no whitespace characters: .mets < mode-string := [ < mode ] [ < options ] .mets < mode := { < selector [ + ] | + } .mets < selector := { r | w | a | m } -.mets < options := { b | l | u | i | n | < digit | < redirection } +.mets < options := { b | x | l | u | i | n | < digit | < redirection } .mets < digit := { 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 } .onom @@ -56578,6 +56578,16 @@ The file is opened in binary mode: no line ending translation takes place. In the absence of this option, files are opened in text mode, in which newline characters in the stream are an abstract indication of the end of a line, translate to a system-specific way of terminating lines in text files. +.coIP x +The file is created and opened only if it does not already exist. +Otherwise, a +.code file-error +exception is thrown. +This option is allowed only with the +.code w +and +.code w+ +modes. .coIP l Specifies that the stream will be line buffered. This means that an implicit flush operation takes place whenever the newline character is output. -- cgit v1.2.3