From 858a705a79a53890eb97bf1f234692c0347c932a Mon Sep 17 00:00:00 2001 From: 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