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
commitf69e58dda6c39b5710cd774a3a4ad2939051da97 (patch)
treef4b10bd2b1555a274ecdf9df9d3d63511d8b60c3
parent083c746fe7c9b0b4d7b87eadf204483b10f750be (diff)
downloadtxr-f69e58dda6c39b5710cd774a3a4ad2939051da97.tar.gz
txr-f69e58dda6c39b5710cd774a3a4ad2939051da97.tar.bz2
txr-f69e58dda6c39b5710cd774a3a4ad2939051da97.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.