summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2020-02-07 19:47:25 -0800
committerKaz Kylheku <kaz@kylheku.com>2020-02-07 19:47:25 -0800
commit418f661cedc045889b23a0f2881a7049b3d008a4 (patch)
treeb14f981b5f75759fa8f7b80631afba75c29339e4
parent177964f81055367da1b7aec8638dfc8a63ec99c5 (diff)
downloadtxr-418f661cedc045889b23a0f2881a7049b3d008a4.tar.gz
txr-418f661cedc045889b23a0f2881a7049b3d008a4.tar.bz2
txr-418f661cedc045889b23a0f2881a7049b3d008a4.zip
New "m" file open mode: non-truncating "w".
Quite surprisingly ISO C lacks a way in fopen to open a file for writing such that it is not truncated if it already exists, and not opened in append mode. (But you will be glad to know that ISO C is adding incredibly useful features in this area, like Microsoft's fopen_s!) Let us add modes "m" and "m+" which will be like "w" and "w+", but without the truncation to zero length (no O_TRUNC is passed to open). * stream.c (w_fopen_mode): New static function. (open_file, open_tail, tail_strategy): Use w_fopen_mode instead of directly calling w_fopen. (do_parse_mode): Handle 'm' and set new notrunc flag. * stream.h (struct stdio_mode): New member, notrunc flag. (stdio_mode_init_blank, stdio_mode_init_r, stdio_mode_init_rpb): Initializer macros updated to include initializer for notrunc flag. * txr.1: Documented "m" mode.
-rw-r--r--stream.c35
-rw-r--r--stream.h7
-rw-r--r--txr.110
3 files changed, 45 insertions, 7 deletions
diff --git a/stream.c b/stream.c
index 0ca0f799..c0362f3f 100644
--- a/stream.c
+++ b/stream.c
@@ -1044,6 +1044,28 @@ static struct strm_ops stdio_ops =
static struct strm_ops stdio_sock_ops;
#endif
+static FILE *w_fopen_mode(const wchar_t *wname, const wchar_t *mode,
+ const struct stdio_mode m)
+{
+#if HAVE_FCNTL
+ if (m.notrunc) {
+ char *name = utf8_dup_to(wname);
+ int flags = (m.read ? O_RDWR : O_WRONLY) | O_CREAT;
+ int fd = open(name, flags, 0777);
+ free(name);
+ if (fd < 0)
+ return NULL;
+ return (fd < 0) ? NULL : w_fdopen(fd, mode);
+ }
+#else
+ if (m.notrunc)
+ uw_throwf(file_error_s,
+ lit("open-file: system doesn't support \"o\" mode"), nao);
+#endif
+ return w_fopen(wname, mode);
+}
+
+
static void tail_calc(unsigned long *state, int *usec, int *mod)
{
unsigned long count = (*state)++;
@@ -1096,7 +1118,7 @@ static void tail_strategy(val stream, unsigned long *state)
/* Try to open the file.
*/
- if (!(newf = w_fopen(c_str(h->descr), c_str(mode)))) {
+ if (!(newf = w_fopen_mode(c_str(h->descr), c_str(mode), m))) {
/* If already have the file open previously, and the name
* does not open any more, then the file has rotated.
* Have the caller try to read the last bit of data
@@ -1349,6 +1371,12 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
m.write = 1;
m.append = 1;
break;
+ case 'm':
+ ms++;
+ m.write = 1;
+ m.create = 1;
+ m.notrunc = 1;
+ break;
default:
break;
}
@@ -3947,7 +3975,8 @@ val open_directory(val path)
val open_file(val path, val mode_str)
{
struct stdio_mode m, m_r = stdio_mode_init_r;
- FILE *f = w_fopen(c_str(path), c_str(normalize_mode(&m, mode_str, m_r)));
+ val norm_mode = normalize_mode(&m, mode_str, m_r);
+ FILE *f = w_fopen_mode(c_str(path), c_str(norm_mode), m);
if (!f) {
int eno = errno;
@@ -3979,7 +4008,7 @@ val open_tail(val path, val mode_str, val seek_end_p)
{
struct stdio_mode m, m_r = stdio_mode_init_r;
val mode = normalize_mode(&m, mode_str, m_r);
- FILE *f = w_fopen(c_str(path), c_str(mode));
+ FILE *f = w_fopen_mode(c_str(path), c_str(mode), m);
struct stdio_handle *h;
val stream;
unsigned long state = 0;
diff --git a/stream.h b/stream.h
index 6fb245d6..2d7688c6 100644
--- a/stream.h
+++ b/stream.h
@@ -108,6 +108,7 @@ struct stdio_mode {
unsigned create : 1;
unsigned append : 1;
unsigned binary : 1;
+ unsigned notrunc : 1;
unsigned interactive : 1;
unsigned unbuf : 1;
unsigned linebuf : 1;
@@ -115,9 +116,9 @@ struct stdio_mode {
int redir[STDIO_MODE_NREDIRS][2];
};
-#define stdio_mode_init_blank { 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 }
-#define stdio_mode_init_r { 0, 1, 0, 0, 0, 0, 0, 0, 0, -1 }
-#define stdio_mode_init_rpb { 0, 1, 1, 0, 0, 1, 0, 0, 0, -1 }
+#define stdio_mode_init_blank { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 }
+#define stdio_mode_init_r { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -1 }
+#define stdio_mode_init_rpb { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, -1 }
#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 9765dc2f..6f3f2258 100644
--- a/txr.1
+++ b/txr.1
@@ -48925,7 +48925,7 @@ grammar. Note that it permits no whitespace characters:
.mono
.mets < mode-string := [ < mode ] [ < options ]
.mets < mode := { < selector [ + ] | + }
-.mets < selector := { r | w | a }
+.mets < selector := { r | w | a | m }
.mets < options := { b | l | u | < digit | < redirection }
.mets < digit := { 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 }
.onom
@@ -48968,6 +48968,14 @@ to zero length. If it doesn't exist, it is created.
.coIP w+
The file is opened for reading and writing. If it exists, it
is truncated to zero length. If it doesn't exist, it is created.
+.coIP m
+The file is opened for modification. This is the same as
+.code w
+except that the file is not truncated if it exists.
+.coIP m+
+The file is opened for reading and modification. This is the same as
+.code w+
+except that the file is not truncated if it exists.
.coIP a
The file is opened for writing. If it doesn't exist, it is
created. If it exists, the current position is advanced to