d8b6dd5802
> add noclobber/ask options (Martin Tournoij)
277 lines
8.2 KiB
Diff
277 lines
8.2 KiB
Diff
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
|
|
|