diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | Doc/Zsh/options.yo | 16 | ||||
-rw-r--r-- | Src/exec.c | 27 | ||||
-rw-r--r-- | Src/options.c | 1 | ||||
-rw-r--r-- | Src/zsh.h | 1 | ||||
-rw-r--r-- | Test/A04redirect.ztst | 14 |
6 files changed, 59 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog index 979fa729b..3addf253f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-06-09 Peter Stephenson <p.stephenson@samsung.com> + + * 46026: Doc/Zsh/options.yo, Src/exec.c, Src/options.c, + Src/zsh.h, Test/A04redirect.ztst: Add CLOBBER_EMPTY option. + 2020-06-08 Peter Stephenson <p.w.stephenson@ntlworld.com> * uwers/24909: Src/exec.c: Don't clean up files used for diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 2b7637ff4..6da68308f 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -1168,6 +1168,22 @@ If the option is not set, and the option tt(APPEND_CREATE) is also not set, `tt(>>!)' or `tt(>>|)' must be used to create a file. If either option is set, `tt(>>)' may be used. ) +pindex(CLOBBER_EMPTY) +pindex(NO_CLOBBER_EMPTY) +pindex(CLOBBEREMPTY) +pindex(NOCLOBBEREMPTY) +cindex(clobbering, of empty files) +cindex(file clobbering, of empty files) +item(tt(CLOBBER_EMPTY))( +This option is only used if the option tt(CLOBBER) is not set: note that +it is set by default. + +If this option is set, then regular files of zero length may be +ovewritten (`clobbered'). Note that it is possible another process +has written to the file between this test and use of the file by +the current process. This option should therefore not be used in +cases where files to be clobbered may be written to asynchronously. +) pindex(CORRECT) pindex(NO_CORRECT) pindex(NOCORRECT) diff --git a/Src/exec.c b/Src/exec.c index c72d485b2..045b5d2b9 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2143,14 +2143,15 @@ clobber_open(struct redir *f) { struct stat buf; int fd, oerrno; + char *ufname = unmeta(f->name); /* If clobbering, just open. */ if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type)) - return open(unmeta(f->name), + return open(ufname, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666); /* If not clobbering, attempt to create file exclusively. */ - if ((fd = open(unmeta(f->name), + if ((fd = open(ufname, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0) return fd; @@ -2158,11 +2159,27 @@ clobber_open(struct redir *f) * Try opening, and if it's a regular file then close it again * * because we weren't supposed to open it. */ oerrno = errno; - if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) { - if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode)) - return fd; + if ((fd = open(ufname, O_WRONLY | O_NOCTTY)) != -1) { + if(!fstat(fd, &buf)) { + if (!S_ISREG(buf.st_mode)) + return fd; + /* + * If CLOBBER_EMPTY is in effect and the file is empty, + * we are allowed to re-use it. + * + * Note: there is an intrinsic race here because another + * process can write to this file at any time. The only fix + * would be file locking, which we wish to avoid in basic + * file operations at this level. This would not be + * fixed. just additionally complicated, by re-opening the + * file and truncating. + */ + if (isset(CLOBBEREMPTY) && buf.st_size == 0) + return fd; + } close(fd); } + errno = oerrno; return -1; } diff --git a/Src/options.c b/Src/options.c index 7586d21d2..fba021e7d 100644 --- a/Src/options.c +++ b/Src/options.c @@ -114,6 +114,7 @@ static struct optname optns[] = { {{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS}, {{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS}, {{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER}, +{{NULL, "clobberempty", 0}, CLOBBEREMPTY}, {{NULL, "combiningchars", 0}, COMBININGCHARS}, {{NULL, "completealiases", 0}, COMPLETEALIASES}, {{NULL, "completeinword", 0}, COMPLETEINWORD}, diff --git a/Src/zsh.h b/Src/zsh.h index 1f2d774a1..ed123f2b9 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2378,6 +2378,7 @@ enum { CHECKJOBS, CHECKRUNNINGJOBS, CLOBBER, + CLOBBEREMPTY, APPENDCREATE, COMBININGCHARS, COMPLETEALIASES, diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index d60519064..993138e7d 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -708,3 +708,17 @@ cat <&$testfd 0:Regression test for here document with fd declarator > This is, in some sense, a here document. + + (setopt noclobber clobberempty + rm -f foo + touch foo + print Works >foo + cat foo + print Works not >foo + # Make sure the file was not harmed + cat foo + ) +0:CLOBBER_EMPTY +>Works +>Works +?(eval):6: file exists: foo |