Added copy/move-as (copy/move and rename) (#356)

* Added copy/move-as (copy/move and rename)

* Little refactorings

* Also handle selection file for cpmv_rename()
This commit is contained in:
KlzXS 2019-10-14 18:36:45 +02:00 committed by Mischievous Meerkat
parent 04cba31128
commit b9d183db26
3 changed files with 100 additions and 13 deletions

View file

@ -211,8 +211,9 @@ The list below is from the **dev branch**. Press <kbd>?</kbd> in `nnn` to see th
⎵ ^J Select entry r Batch rename ⎵ ^J Select entry r Batch rename
m ^K Sel range, clear M List selection m ^K Sel range, clear M List selection
a Select all K Edit selection a Select all K Edit selection
P Copy selection X Delete selection P Copy selection w Copy selection as
V Move selection ^X Delete entry V Move selection W Move selection as
X Delete selection ^X Delete entry
f Create archive T Mount archive f Create archive T Mount archive
^F Extract archive F List archive ^F Extract archive F List archive
e Edit in EDITOR p Open in PAGER e Edit in EDITOR p Open in PAGER

100
src/nnn.c
View file

@ -390,7 +390,7 @@ static char mv[] = "mvg -gi";
#define STR_TMPFILE 3 #define STR_TMPFILE 3
#define NONE_SELECTED 4 #define NONE_SELECTED 4
#define UTIL_MISSING 5 #define UTIL_MISSING 5
#define MOUNT_FAILED 6 #define OPERATION_FAILED 6
static const char * const messages[] = { static const char * const messages[] = {
"no traversal", "no traversal",
@ -399,7 +399,7 @@ static const char * const messages[] = {
"/.nnnXXXXXX", "/.nnnXXXXXX",
"0 selected", "0 selected",
"missing dep", "missing dep",
"mount failed", "failed!",
}; };
/* Supported configuration environment variables */ /* Supported configuration environment variables */
@ -1280,6 +1280,70 @@ static void xrm(char *path)
} }
} }
static bool cpmv_rename(const char *path, const char *cmd)
{
int fd, i;
uint count = 0, lines = 0;
bool ret = FALSE;
const char formatcmd[] = "sed -i 's|^\\(\\(.*/\\)\\(.*\\)$\\)|#\\1\\n\\3|' %s";
const char renamecmd[] =
"sed 's|^\\([^#][^/]\\?.*\\)$|%s/\\1|;s|^#\\(/.*\\)$|\\1|' %s | tr '\\n' '\\0' | xargs -0 -o -n2 %s";
char buf[sizeof(renamecmd) + sizeof(cmd) + (PATH_MAX << 1)];
if ((fd = create_tmp_file()) == -1)
return ret;
/* selsafe() returned TRUE for this to be called */
if (!selbufpos) {
snprintf(buf, sizeof(buf), "cat %s | tr '\\0' '\\n' > %s", g_selpath, g_tmpfpath);
spawn("sh", "-c", buf, NULL, F_NORMAL | F_CMD);
while ((i = read(fd, buf, sizeof(buf))) > 0)
while(i)
count += (buf[--i] == '\n');
if (!count)
goto finish;
} else {
seltofile(fd, &count);
}
close(fd);
snprintf(buf, sizeof(buf), formatcmd, g_tmpfpath);
spawn("sh", "-c", buf, path, F_NORMAL);
spawn(editor, g_tmpfpath, NULL, path, F_CLI);
if ((fd = open(g_tmpfpath, O_RDONLY)) == -1)
goto finish;
while ((i = read(fd, buf, sizeof(buf))) > 0)
while (i)
lines += (buf[--i] == '\n');
if (i < 0)
goto finish;
DPRINTF_U(count);
DPRINTF_U(lines);
if (2 * count != lines) {
DPRINTF_S("cannot delete files");
goto finish;
}
snprintf(buf, sizeof(buf), renamecmd, path, g_tmpfpath, cmd);
spawn("sh", "-c", buf, path, F_NORMAL);
ret = TRUE;
finish:
if (fd >= 0)
close(fd);
return ret;
}
static bool batch_rename(const char *path) static bool batch_rename(const char *path)
{ {
int fd1, fd2, i; int fd1, fd2, i;
@ -2847,7 +2911,7 @@ static bool archive_mount(char *name, char *path, char *newpath, int *presel)
DPRINTF_S(name); DPRINTF_S(name);
DPRINTF_S(newpath); DPRINTF_S(newpath);
if (spawn(cmd, name, newpath, path, F_NORMAL)) { if (spawn(cmd, name, newpath, path, F_NORMAL)) {
printwait(messages[MOUNT_FAILED], presel); printwait(messages[OPERATION_FAILED], presel);
return FALSE; return FALSE;
} }
@ -2889,7 +2953,7 @@ static bool sshfs_mount(char *newpath, int *presel)
/* Connect to remote */ /* Connect to remote */
if (spawn(env, tmp, newpath, NULL, flag)) { if (spawn(env, tmp, newpath, NULL, flag)) {
printwait(messages[MOUNT_FAILED], presel); printwait(messages[OPERATION_FAILED], presel);
return FALSE; return FALSE;
} }
@ -2939,7 +3003,7 @@ static bool unmount(char *name, char *newpath, int *presel, char *currentpath)
} }
if (spawn(cmd, "-u", newpath, NULL, F_NORMAL)) { if (spawn(cmd, "-u", newpath, NULL, F_NORMAL)) {
printwait("unmount failed", presel); printwait(messages[OPERATION_FAILED], presel);
return FALSE; return FALSE;
} }
@ -2997,8 +3061,9 @@ static void show_help(const char *path)
"9⎵ ^J Select entry r Batch rename\n" "9⎵ ^J Select entry r Batch rename\n"
"9m ^K Sel range, clear M List selection\n" "9m ^K Sel range, clear M List selection\n"
"ca Select all K Edit selection\n" "ca Select all K Edit selection\n"
"cP Copy selection X Delete selection\n" "cP Copy selection w Copy selection as\n"
"cV Move selection ^X Delete entry\n" "cV Move selection W Move selection as\n"
"cX Delete selection ^X Delete entry\n"
"cf Create archive T Mount archive\n" "cf Create archive T Mount archive\n"
"b^F Extract archive F List archive\n" "b^F Extract archive F List archive\n"
"ce Edit in EDITOR p Open in PAGER\n" "ce Edit in EDITOR p Open in PAGER\n"
@ -4187,7 +4252,7 @@ nochange:
endselection(); endselection();
if (!batch_rename(path)) { if (!batch_rename(path)) {
printwait("batch rename failed", &presel); printwait(messages[OPERATION_FAILED], &presel);
goto nochange; goto nochange;
} }
break; break;
@ -4319,12 +4384,14 @@ nochange:
goto nochange; goto nochange;
case SEL_SELEDIT: case SEL_SELEDIT:
if (!seledit()){ if (!seledit()){
printwait("edit failed!", &presel); printwait(messages[OPERATION_FAILED], &presel);
goto nochange; goto nochange;
} }
break; break;
case SEL_CP: case SEL_CP:
case SEL_MV: case SEL_MV:
case SEL_CPAS:
case SEL_MVAS:
case SEL_RMMUL: case SEL_RMMUL:
{ {
endselection(); endselection();
@ -4341,15 +4408,28 @@ nochange:
case SEL_MV: case SEL_MV:
mvstr(g_buf); mvstr(g_buf);
break; break;
case SEL_CPAS:
if (!cpmv_rename(path, cp)) {
printwait(messages[OPERATION_FAILED], &presel);
goto nochange;
}
break;
case SEL_MVAS:
if (!cpmv_rename(path, mv)) {
printwait(messages[OPERATION_FAILED], &presel);
goto nochange;
}
break;
default: /* SEL_RMMUL */ default: /* SEL_RMMUL */
rmmulstr(g_buf); rmmulstr(g_buf);
break; break;
} }
if (sel != SEL_CPAS && sel != SEL_MVAS)
spawn("sh", "-c", g_buf, path, F_NORMAL); spawn("sh", "-c", g_buf, path, F_NORMAL);
/* Clear selection on move or delete */ /* Clear selection on move or delete */
if (sel == SEL_MV || sel == SEL_RMMUL) { if (sel == SEL_MV || sel == SEL_MVAS || sel == SEL_RMMUL) {
nselected = 0; nselected = 0;
selbufpos = 0; selbufpos = 0;
writesel(NULL, 0); writesel(NULL, 0);

View file

@ -82,6 +82,8 @@ enum action {
SEL_SELEDIT, SEL_SELEDIT,
SEL_CP, SEL_CP,
SEL_MV, SEL_MV,
SEL_CPAS,
SEL_MVAS,
SEL_RMMUL, SEL_RMMUL,
SEL_RM, SEL_RM,
SEL_OPENWITH, SEL_OPENWITH,
@ -217,6 +219,10 @@ static struct key bindings[] = {
{ 'P', SEL_CP }, { 'P', SEL_CP },
/* Move from selection buffer */ /* Move from selection buffer */
{ 'V', SEL_MV }, { 'V', SEL_MV },
/* Copyfrom selection buffer and rename */
{ 'w', SEL_CPAS },
/* Movefrom selection buffer and rename */
{ 'W', SEL_MVAS },
/* Delete from selection buffer */ /* Delete from selection buffer */
{ 'X', SEL_RMMUL }, { 'X', SEL_RMMUL },
/* Delete currently selected */ /* Delete currently selected */