Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

RE: Any way to allow clobbering empty files when noclobber is set?



> On 03 June 2020 at 13:04 Peter Stephenson <p.stephenson@xxxxxxxxxxx> wrote:
> Martin Tournoij wrote:
> > I switched from tcsh to zsh a while ago (many years too late, I know),
> > and found zsh can do pretty much everything better. There's one thing
> > I rather miss though: the 'notempty' option in 'noclobber'.
> 
> This isn't actually hard to implement.

One thing I missed is that we already open the file and run fstat to
check if it's regular.  We can simply check if it's empty at the
same point, so there's no additional operation except for the
clobbering open if that test succeeded.

This is pasted into a webmail client as an experiment, it could do
anything...

pws

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 2b7637ff4..a42daa56f 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1168,6 +1168,19 @@ 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').
+)
 pindex(CORRECT)
 pindex(NO_CORRECT)
 pindex(NOCORRECT)
diff --git a/Src/exec.c b/Src/exec.c
index 29f4fc5ca..8e9443838 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,25 @@ 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 zero-length, we'll check CLOBBER_EMPTY.
+	     * As we're clobbering, not re-using, in that case
+	     * we'll open again.
+	     */
+	    if (isset(CLOBBEREMPTY) && buf.st_size == 0)
+	    {
+		close(fd);
+		return open(ufname, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY,
+			    0666);
+	    }
+	}
 	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



Messages sorted by: Reverse Date, Date, Thread, Author