Blob Blame History Raw
From 858a705a79a53890eb97bf1f234692c0347c932a Mon Sep 17 00:00:00 2001
From: christos <christos>
Date: Tue, 8 Sep 2015 15:49:53 +0000
Subject: [PATCH] add noclobber/ask options (Martin Tournoij)

---
 Fixes           |  1 +
 sh.decls.h      |  1 +
 sh.func.c       | 17 +++++++++++++++++
 sh.h            |  6 ++++++
 sh.sem.c        | 10 +++++++++-
 sh.set.c        | 32 ++++++++++++++++++++++++++++++++
 tc.const.c      |  2 ++
 tc.func.c       | 14 ++------------
 tcsh.man        |  3 +++
 tests/syntax.at | 26 ++++++++++++++++++++++++++
 10 files changed, 99 insertions(+), 13 deletions(-)

diff --git a/Fixes b/Fixes
index 0481286..689aeb0 100644
--- a/Fixes
+++ b/Fixes
@@ -1,3 +1,4 @@
+  8. Add notempty and ask values for the noclobber setting (Martin Tournoij)
   7. more correct $wordchars for vimode (Luke Mewburn)
   6. expose VImode in $vimode (Luke Mewburn)
   5. display what the compiled in editor is in bindkey -d (Luke Mewburn)
diff --git a/sh.decls.h b/sh.decls.h
index 78bbed9..671a0b7 100644
--- a/sh.decls.h
+++ b/sh.decls.h
@@ -185,6 +185,7 @@ extern	void		  unalias	(Char **, struct command *);
 extern	void		  wfree		(void);
 extern	void		  dobuiltins	(Char **, struct command *);
 extern	void		  reexecute	(struct command *);
+extern  int		  getYN		(const char *);
 
 /*
  * sh.glob.c
diff --git a/sh.func.c b/sh.func.c
index bb670b8..41f9e71 100644
--- a/sh.func.c
+++ b/sh.func.c
@@ -2722,3 +2722,20 @@ nlsclose(void)
     }
 #endif /* NLS_CATALOGS */
 }
+
+int
+getYN(const char *prompt)
+{
+    int doit, c;
+    xprintf("%s", prompt);
+    flush();
+    (void) force_read(SHIN, &c, 1);
+    /* 
+     * Perhaps we should use the yesexpr from the
+     * actual locale
+     */
+    doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
+    while (c != '\n' && force_read(SHIN, &c, 1) == 1)
+	continue;
+    return doit;
+}
diff --git a/sh.h b/sh.h
index 51d3f3b..38b7efd 100644
--- a/sh.h
+++ b/sh.h
@@ -193,6 +193,11 @@ static __inline void tcsh_ignore(intptr_t a)
 # endif /* SYSVREL */
 #endif /* ECHO_STYLE */
 
+/* values for noclobber */
+#define NOCLOBBER_DEFAULT  1
+#define NOCLOBBER_NOTEMPTY 2
+#define NOCLOBBER_ASK      4
+
 /*
  * The shell moves std in/out/diag and the old std input away from units
  * 0, 1, and 2 so that it is easy to set up these standards for invoked
@@ -577,6 +582,7 @@ EXTERN int    arun IZERO;	/* Currently running multi-line-aliases */
 EXTERN int    implicit_cd IZERO;/* implicit cd enabled?(1=enabled,2=verbose) */
 EXTERN int    cdtohome IZERO;	/* cd without args goes home */
 EXTERN int    inheredoc IZERO;	/* Currently parsing a heredoc */
+EXTERN int    no_clobber IZERO;	/* no clobber enabled? 1=yes 2=notempty, 4=ask*/
 /* We received a window change event */
 EXTERN volatile sig_atomic_t windowchg IZERO;
 #if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE)
diff --git a/sh.sem.c b/sh.sem.c
index c4eb394..4293b1b 100644
--- a/sh.sem.c
+++ b/sh.sem.c
@@ -909,7 +909,7 @@ doio(struct command *t, int *pipein, int *pipeout)
 	else
 	    fd = 0;
 	if ((flags & F_APPEND) == 0 || fd == -1) {
-	    if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) {
+	    if (!(flags & F_OVERWRITE) && no_clobber) {
 		if (flags & F_APPEND)
 		    stderror(ERR_SYSTEM, tmp, strerror(errno));
 		chkclob(tmp);
@@ -981,5 +981,13 @@ chkclob(const char *cp)
 	return;
     if (S_ISCHR(stb.st_mode))
 	return;
+    if (no_clobber & NOCLOBBER_NOTEMPTY && stb.st_size == 0)
+	return;
+    if (no_clobber & NOCLOBBER_ASK) {
+	if (getYN(CGETS(22, 15,
+	    "Do you really want to overwrite an existing file? [N/y] ")))
+	    return;
+    }
+
     stderror(ERR_EXISTS, cp);
 }
diff --git a/sh.set.c b/sh.set.c
index 52602f0..6428562 100644
--- a/sh.set.c
+++ b/sh.set.c
@@ -55,6 +55,7 @@ static	struct varent	*madrof		(Char *, struct varent *);
 static	void		 unsetv1	(struct varent *);
 static	void		 exportpath	(Char **);
 static	void		 balance	(struct varent *, int, int);
+static	int		 set_noclobber  (Char **);
 
 /*
  * C Shell
@@ -72,6 +73,13 @@ update_vars(Char *vp)
 	    dohash(NULL, NULL);
 	}
     }
+    else if (eq(vp, STRnoclobber)) {
+	struct varent *p = adrof(STRnoclobber);
+	if (p == NULL)
+	    stderror(ERR_NAME | ERR_UNDVAR);
+	else
+	    no_clobber = set_noclobber(p->vec);
+    }
     else if (eq(vp, STRhistchars)) {
 	Char *pn = varval(vp);
 
@@ -772,6 +780,8 @@ unset(Char **v, struct command *c)
 	PRCH = tcsh ? '>' : '%';
 	PRCHROOT = '#';
     }
+    if (adrof(STRnoclobber) == 0)
+	no_clobber = 0;
     if (adrof(STRhistlit) == 0)
 	HistLit = 0;
     if (adrof(STRloginsh) == 0)
@@ -937,6 +947,28 @@ exportpath(Char **val)
     cleanup_until(exppath);
 }
 
+static int
+set_noclobber(Char **val)
+{
+    Char *option;
+    int nc = NOCLOBBER_DEFAULT;
+
+    if (val == NULL)
+	return nc;
+    while (*val) {
+	if (*val == 0 || eq(*val, STRRparen))
+	    return nc;
+
+	option = *val++;
+
+	if (eq(option, STRnotempty))
+	    nc |= NOCLOBBER_NOTEMPTY;
+	else if (eq(option, STRask))
+	    nc |= NOCLOBBER_ASK;
+    }
+    return nc;
+}
+
 #ifndef lint
  /*
   * Lint thinks these have null effect
diff --git a/tc.const.c b/tc.const.c
index 06ddd2b..d5d0eb4 100644
--- a/tc.const.c
+++ b/tc.const.c
@@ -372,6 +372,8 @@ Char STRsldotcshrc[]	= { '/', '.', 'c', 's', 'h', 'r', 'c', '\0' };
 Char STRsldotlogin[]	= { '/', '.', 'l', 'o', 'g', 'i', 'n', '\0' };
 Char STRignoreeof[]	= { 'i', 'g', 'n', 'o', 'r', 'e', 'e', 'o', 'f', '\0' };
 Char STRnoclobber[]	= { 'n', 'o', 'c', 'l', 'o', 'b', 'b', 'e', 'r', '\0' };
+Char STRnotempty[]	= { 'n', 'o', 't', 'e', 'm', 'p', 't', 'y', '\0' };
+Char STRask[]		= { 'a', 's', 'k', '\0' };
 Char STRhelpcommand[]	= { 'h', 'e', 'l', 'p', 'c', 'o', 'm', 'm', 'a', 'n', 
 			    'd', '\0' };
 Char STRfignore[]	= { 'f', 'i', 'g', 'n', 'o', 'r', 'e', '\0' };
diff --git a/tc.func.c b/tc.func.c
index 9af4858..f2b1a97 100644
--- a/tc.func.c
+++ b/tc.func.c
@@ -1145,7 +1145,6 @@ rmstar(struct wordent *cp)
     Char   *tag;
 #endif /* RMDEBUG */
     Char   *charac;
-    char    c;
     int     ask, doit, star = 0, silent = 0, opintr_disabled;
 
     if (!adrof(STRrmstar))
@@ -1178,17 +1177,8 @@ rmstar(struct wordent *cp)
 		    if (!Strcmp(args->word, STRstar))
 			star = 1;
 		if (ask && star) {
-		    xprintf("%s", CGETS(22, 8,
-			    "Do you really want to delete all files? [n/y] "));
-		    flush();
-		    (void) force_read(SHIN, &c, 1);
-		    /* 
-		     * Perhaps we should use the yesexpr from the
-		     * actual locale
-		     */
-		    doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
-		    while (c != '\n' && force_read(SHIN, &c, 1) == 1)
-			continue;
+		    doit = getYN(CGETS(22, 8,
+			"Do you really want to delete all files? [N/y] "));
 		    if (!doit) {
 			/* remove the command instead */
 #ifdef RMDEBUG
diff --git a/tcsh.man b/tcsh.man
index 0a35405..2aa37ac 100644
--- a/tcsh.man
+++ b/tcsh.man
@@ -1636,6 +1636,9 @@ If the shell variable \fBnoclobber\fR is set, then the file must not exist or be
 character special file (e.g., a terminal or `/dev/null') or an error results.
 This helps prevent accidental destruction of files.  In this case the `!' forms
 can be used to suppress this check.
+If \fBnotempty\fR is given in \fBnoclobber\fR, `>' is allowed on empty files;
+if \fBask\fR is set, an interacive confirmation is presented, rather than an
+error.
 .PP
 The forms involving `&' route the diagnostic output into the specified file as
 well as the standard output.  \fIname\fR is expanded in the same way as `<'
diff --git a/tests/syntax.at b/tests/syntax.at
index 23fc8d5..35134d1 100644
--- a/tests/syntax.at
+++ b/tests/syntax.at
@@ -161,4 +161,30 @@ AT_CHECK([tcsh -f -c '(echo $this_does_not_exist) |& cat'], 1,
 [this_does_not_exist: Undefined variable.
 ])
 
+dnl noclobber=notempty
+echo Hello > output
+AT_CHECK([tcsh -f -c 'set noclobber=notempty; echo OK >& output'], 1, [],
+[output: File exists.
+])
+
+rm -f output
+touch output
+AT_CHECK([tcsh -f -c 'set noclobber=notempty; echo OK >& output'])
+AT_CHECK([cat output], ,
+[OK
+])
+
+dnl noclobber=ask
+dnl touch output
+dnl AT_CHECK([tcsh -f -c 'set noclobber=ask; echo "n" | echo OK >& output'], 0, [],
+dnl [output: File exists.
+dnl ])
+dnl T_CHECK([tcsh -f -c 'set noclobber=ask; echo "y" | echo OK >& output'])
+
+dnl noclobber=(notempty ask)
+dnl rm -f output
+dnl touch output
+dnl AT_CHECK([tcsh -f -c 'set noclobber=(notempty ask); echo OK >& output'])
+
+
 AT_CLEANUP
-- 
2.5.5