summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul A. Patience <paul@apatience.com>2021-08-29 04:42:04 -0400
committerKaz Kylheku <kaz@kylheku.com>2021-08-29 08:06:58 -0700
commit469cd5ab27bba7679bebe60cd0617e09f4838e50 (patch)
treef4b10bd2b1555a274ecdf9df9d3d63511d8b60c3
parent702d02d7958a20e8cd0e6872ca12540c84cdca55 (diff)
downloadtxr-469cd5ab27bba7679bebe60cd0617e09f4838e50.tar.gz
txr-469cd5ab27bba7679bebe60cd0617e09f4838e50.tar.bz2
txr-469cd5ab27bba7679bebe60cd0617e09f4838e50.zip
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".
-rw-r--r--stream.c12
-rw-r--r--stream.h7
-rw-r--r--txr.112
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.