diff --git a/README.md b/README.md index a6229f5b..34f2570c 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ It runs on Linux, macOS, Raspberry Pi, BSD, Cygwin, Linux subsystem for Windows - FreeDesktop compliant trash (needs trash-cli) - Plugin repository - SSHFS mounts (needs sshfs) - - Batch rename (needs vidir) + - Batch rename - Show copy, move progress on Linux (needs avdcpmv) - Per-context directory color (default: blue) - Spawn a shell in the current directory @@ -139,7 +139,6 @@ The following table is a complete list. Some of the utilities may be installed b | trash-cli | trash files (default: delete) | | mediainfo / exiftool | multimedia file details | | atool / bsdtar / patool ([integration](https://github.com/jarun/nnn/wiki/hacking-nnn#integrate-patool)) | create, list and extract archives | -| vidir (from moreutils) | batch rename dir entries | | sshfs, fusermount(3) | mount, unmount remote over SSHFS | | vlock (Linux), bashlock (macOS), lock(1) (BSD) | terminal locker | | advcpmv (Linux) ([integration](https://github.com/jarun/nnn/wiki/hacking-nnn#show-cp-mv-progress)) | copy, move progress | diff --git a/src/nnn.c b/src/nnn.c index 6236c531..1a106ce7 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -334,10 +334,9 @@ static char g_tmpfpath[TMP_LEN_MAX] __attribute__ ((aligned)); #define OPENER 2 #define ATOOL 3 #define BSDTAR 4 -#define VIDIR 5 -#define LOCKER 6 -#define NLAUNCH 7 -#define UNKNOWN 8 +#define LOCKER 5 +#define NLAUNCH 6 +#define UNKNOWN 7 /* Utilities to open files, run actions */ static char * const utils[] = { @@ -352,7 +351,6 @@ static char * const utils[] = { #endif "atool", "bsdtar", - "vidir", #ifdef __APPLE__ "bashlock", #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) @@ -1123,6 +1121,80 @@ static void xrm(char *path) } } +static void rename_selection(const char *path) +{ + const char renamecmd[] = "paste -d'\n' %s %s | xargs -d'\n' -n2 mv 2>/dev/null"; + char buf[sizeof(renamecmd) + 2 * PATH_MAX]; + char foriginal[TMP_LEN_MAX] = {0}; + int fd1 = -1, fd2 = -1, i; + ssize_t len, len2; + + if ((fd1 = create_tmp_file()) == -1) + return; + + xstrlcpy(foriginal, g_tmpfpath, strlen(g_tmpfpath)+1); + + if ((fd2 = create_tmp_file()) == -1) { + unlink(foriginal); + close(fd1); + return; + } + + if (copybufpos > 0) { + // Rename selected files with absolute paths: + selectiontofd(fd1); + if (write(fd1, "\n", 1) < 1) + goto finished_renaming; + selectiontofd(fd2); + if (write(fd2, "\n", 1) < 1) + goto finished_renaming; + } else { + // If nothing is selected, use the directory contents with relative paths: + for (i = 0; i < ndents; ++i) { + len = strlen(dents[i].name); + if (write(fd1, dents[i].name, len) != len || write(fd1, "\n", 1) != 1) + goto finished_renaming; + if (write(fd2, dents[i].name, len) != len || write(fd2, "\n", 1) != 1) + goto finished_renaming; + } + } + + close(fd2); + fd2 = -1; + + spawn(editor, g_tmpfpath, NULL, path, F_CLI); + + // Check that the number of filenames is unchanged: + len = 0, len2 = 0; + lseek(fd1, 0, SEEK_SET); + while ((i = read(fd1, buf, sizeof(buf))) > 0) { + while (i) len += buf[--i] == '\n'; + } + if (i < 0) goto finished_renaming; + + // Reopen file descriptor to get updated contents: + if ((fd2 = open(g_tmpfpath, O_RDONLY)) == -1) + goto finished_renaming; + while ((i = read(fd2, buf, sizeof(buf))) > 0) { + while (i) len2 += buf[--i] == '\n'; + } + if (i < 0) goto finished_renaming; + + if (len2 != len) { + get_input("Error: wrong number of filenames. Press any key to continue..."); + goto finished_renaming; + } + + snprintf(buf, sizeof(buf), renamecmd, foriginal, g_tmpfpath); + spawn("sh", "-c", buf, path, F_NORMAL); + +finished_renaming: + if (fd2 >= 0) close(fd1); + unlink(foriginal); + if (fd2 >= 0) close(fd2); + unlink(g_tmpfpath); +} + static void archive_selection(const char *cmd, const char *archive, const char *curpath) { snprintf(g_buf, CMD_LEN_MAX, @@ -3619,9 +3691,7 @@ nochange: copycurname(); goto begin; case SEL_RENAMEALL: - r = getutil(utils[VIDIR]); - if (r) - spawn(utils[VIDIR], ".", NULL, path, F_NORMAL); + rename_selection(path); break; case SEL_HELP: r = show_help(path);