summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--misc-utils/rename.125
-rw-r--r--misc-utils/rename.c114
3 files changed, 124 insertions, 17 deletions
diff --git a/configure.ac b/configure.ac
index 3ad35abec..2a8d4e490 100644
--- a/configure.ac
+++ b/configure.ac
@@ -449,6 +449,8 @@ AC_CHECK_DECL([SO_PASSCRED],
#include <sys/socket.h>])
AC_CHECK_FUNCS([ \
+ __fpurge \
+ fpurge \
__fpending \
secure_getenv \
__secure_getenv \
diff --git a/misc-utils/rename.1 b/misc-utils/rename.1
index df329461b..1f6047920 100644
--- a/misc-utils/rename.1
+++ b/misc-utils/rename.1
@@ -32,6 +32,9 @@ Do not overwrite existing files. When
.BR \-\-symlink
is active, do not overwrite symlinks pointing to existing targets.
.TP
+.BR \-i , " \-\-interactive"
+Ask before overwriting existing files.
+.TP
.BR \-V , " \-\-version"
Display version information and exit.
.TP
@@ -70,13 +73,29 @@ rename '_with_long_name' '' file_with_long_name.*
.RE
will remove the substring in the filenames.
.SH WARNING
-The renaming has no safeguards except the
-.B \-\-no\-act
-option. If the user has
+The renaming has no safeguards by default or without any one of the options
+.B \-\-no-overwrite\fR,
+.B \-\-interactive
+or
+.B \-\-no\-act\fR.
+If the user has
permission to rewrite file names, the command will perform the action without
any questions. For example, the result can be quite drastic when the command
is run as root in the /lib directory. Always make a backup before running the
command, unless you truly know what you are doing.
+.SH INTERACTIVE MODE
+As most standard utilities rename can be used with a terminal device (tty in
+short) in canonical mode, where the line is buffered by the tty and you press
+ENTER to validate the user input. If you put your tty in cbreak mode however,
+rename requires only a single key press to answer the prompt. To set cbreak
+mode, run for example:
+.RS
+.PP
+.nf
+sh -c 'stty -icanon min 1; "$0" "$@"; stty icanon' rename -i from to files
+.fi
+.PP
+.RE
.SH "EXIT STATUS"
.RS
.PD 0
diff --git a/misc-utils/rename.c b/misc-utils/rename.c
index 147e54fe9..0e3e1b9e1 100644
--- a/misc-utils/rename.c
+++ b/misc-utils/rename.c
@@ -14,11 +14,20 @@ for i in $@; do N=`echo "$i" | sed "s/$FROM/$TO/g"`; mv "$i" "$N"; done
* in cases involving special characters. Here a C version.
*/
#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+# include <stdio_ext.h>
+#endif
+#ifdef HAVE_FPURGE
+# define HAVE___FPURGE 1
+# define __fpurge fpurge
+#endif
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
+#include <fcntl.h>
#include <unistd.h>
+#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -26,11 +35,14 @@ for i in $@; do N=`echo "$i" | sed "s/$FROM/$TO/g"`; mv "$i" "$N"; done
#include "xalloc.h"
#include "c.h"
#include "closestream.h"
+#include "rpmatch.h"
#define RENAME_EXIT_SOMEOK 2
#define RENAME_EXIT_NOTHING 4
#define RENAME_EXIT_UNEXPLAINED 64
+static int tty_cbreak = 0;
+
static int string_replace(char *from, char *to, char *s, char *orig, char **newname)
{
char *p, *q, *where;
@@ -53,12 +65,55 @@ static int string_replace(char *from, char *to, char *s, char *orig, char **newn
return 0;
}
-static int do_symlink(char *from, char *to, char *s, int verbose, int noact, int nooverwrite)
+static int ask(char *name)
+{
+ int c;
+ char buf[2];
+ printf(_("%s: overwrite `%s'? "), program_invocation_short_name, name);
+ fflush(stdout);
+ if ((c = fgetc(stdin)) == EOF) {
+ buf[0] = 'n';
+ printf("n\n");
+ }
+ else {
+ buf[0] = c;
+ if (c != '\n' && tty_cbreak) {
+#ifdef HAVE___FPURGE
+ /* Possibly purge a multi-byte character; or do a
+ required purge of the rest of the line (including
+ the newline) if the tty has been put back in
+ canonical mode (for example by a shell after a
+ SIGTSTP signal). */
+ __fpurge(stdin);
+#endif
+ printf("\n");
+ }
+ else if (c != '\n')
+ while ((c = fgetc(stdin)) != '\n' && c != EOF);
+ }
+ buf[1] = '\0';
+ if (rpmatch(buf) == RPMATCH_YES)
+ return 0;
+ else
+ return 1;
+}
+
+static int do_symlink(char *from, char *to, char *s, int verbose, int noact,
+ int nooverwrite, int interactive)
{
char *newname = NULL, *target = NULL;
int ret = 1;
struct stat sb;
+ if ( faccessat(AT_FDCWD, s, F_OK, AT_SYMLINK_NOFOLLOW) != 0 &&
+ errno != EINVAL )
+ /* Skip if AT_SYMLINK_NOFOLLOW is not supported; lstat() below will
+ detect the access error */
+ {
+ warn(_("%s: not accessible"), s);
+ return 2;
+ }
+
if (lstat(s, &sb) == -1) {
warn(_("stat of %s failed"), s);
return 2;
@@ -74,13 +129,17 @@ static int do_symlink(char *from, char *to, char *s, int verbose, int noact, int
return 2;
}
target[sb.st_size] = '\0';
- if (string_replace(from, to, target, target, &newname))
+ if (string_replace(from, to, target, target, &newname) != 0)
ret = 0;
- if (ret == 1 && nooverwrite && lstat(target, &sb) == 0) {
+ if (ret == 1 && (nooverwrite || interactive) && lstat(newname, &sb) != 0)
+ nooverwrite = interactive = 0;
+
+ if ( ret == 1 &&
+ (nooverwrite || (interactive && (noact || ask(newname) != 0))) )
+ {
if (verbose)
printf(_("Skipping existing link: `%s' -> `%s'\n"), s, target);
-
ret = 0;
}
@@ -88,31 +147,41 @@ static int do_symlink(char *from, char *to, char *s, int verbose, int noact, int
if (!noact && 0 > unlink(s)) {
warn(_("%s: unlink failed"), s);
ret = 2;
- } else if (!noact && symlink(newname, s) != 0) {
+ }
+ else if (!noact && symlink(newname, s) != 0) {
warn(_("%s: symlinking to %s failed"), s, newname);
ret = 2;
}
}
if (verbose && (noact || ret == 1))
- if (verbose)
- printf("%s: `%s' -> `%s'\n", s, target, newname);
+ printf("%s: `%s' -> `%s'\n", s, target, newname);
free(newname);
free(target);
return ret;
}
-static int do_file(char *from, char *to, char *s, int verbose, int noact, int nooverwrite)
+static int do_file(char *from, char *to, char *s, int verbose, int noact,
+ int nooverwrite, int interactive)
{
char *newname = NULL, *file=NULL;
int ret = 1;
+ if (access(s, F_OK) != 0) {
+ warn(_("%s: not accessible"), s);
+ return 2;
+ }
+
if (strchr(from, '/') == NULL && strchr(to, '/') == NULL)
file = strrchr(s, '/');
if (file == NULL)
file = s;
- if (string_replace(from, to, file, s, &newname))
+ if (string_replace(from, to, file, s, &newname) != 0)
return 0;
- if (nooverwrite && access(newname, F_OK) == 0) {
+
+ if ((nooverwrite || interactive) && access(newname, F_OK) != 0)
+ nooverwrite = interactive = 0;
+
+ if (nooverwrite || (interactive && (noact || ask(newname) != 0))) {
if (verbose)
printf(_("Skipping existing file: `%s'\n"), newname);
ret = 0;
@@ -143,6 +212,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -s, --symlink act on the target of symlinks\n"), out);
fputs(_(" -n, --no-act do not make any changes\n"), out);
fputs(_(" -o, --no-overwrite don't overwrite existing files\n"), out);
+ fputs(_(" -i, --interactive prompt before overwrite\n"), out);
fputs(USAGE_SEPARATOR, out);
printf(USAGE_HELP_OPTIONS(21));
printf(USAGE_MAN_TAIL("rename(1)"));
@@ -152,8 +222,10 @@ static void __attribute__((__noreturn__)) usage(void)
int main(int argc, char **argv)
{
char *from, *to;
- int i, c, ret = 0, verbose = 0, noact = 0, nooverwrite = 0;
- int (*do_rename)(char *from, char *to, char *s, int verbose, int noact, int nooverwrite) = do_file;
+ int i, c, ret = 0, verbose = 0, noact = 0, nooverwrite = 0, interactive = 0;
+ struct termios tio;
+ int (*do_rename)(char *from, char *to, char *s, int verbose, int noact,
+ int nooverwrite, int interactive) = do_file;
static const struct option longopts[] = {
{"verbose", no_argument, NULL, 'v'},
@@ -161,6 +233,7 @@ int main(int argc, char **argv)
{"help", no_argument, NULL, 'h'},
{"no-act", no_argument, NULL, 'n'},
{"no-overwrite", no_argument, NULL, 'o'},
+ {"interactive", no_argument, NULL, 'i'},
{"symlink", no_argument, NULL, 's'},
{NULL, 0, NULL, 0}
};
@@ -170,7 +243,7 @@ int main(int argc, char **argv)
textdomain(PACKAGE);
atexit(close_stdout);
- while ((c = getopt_long(argc, argv, "vsVhno", longopts, NULL)) != -1)
+ while ((c = getopt_long(argc, argv, "vsVhnoi", longopts, NULL)) != -1)
switch (c) {
case 'n':
noact = 1;
@@ -180,6 +253,11 @@ int main(int argc, char **argv)
break;
case 'o':
nooverwrite = 1;
+ interactive = 0;
+ break;
+ case 'i':
+ interactive = 1;
+ nooverwrite = 0;
break;
case 's':
do_rename = do_symlink;
@@ -207,8 +285,16 @@ int main(int argc, char **argv)
if (!strcmp(from, to))
return RENAME_EXIT_NOTHING;
+ tty_cbreak = 0;
+ if (interactive && isatty(STDIN_FILENO) != 0) {
+ if (tcgetattr(STDIN_FILENO, &tio) != 0)
+ warn(_("failed to get terminal attributes"));
+ else if (!(tio.c_lflag & ICANON) && tio.c_cc[VMIN] == 1)
+ tty_cbreak = 1;
+ }
+
for (i = 2; i < argc; i++)
- ret |= do_rename(from, to, argv[i], verbose, noact, nooverwrite);
+ ret |= do_rename(from, to, argv[i], verbose, noact, nooverwrite, interactive);
switch (ret) {
case 0: