Automagically handle archives

This commit is contained in:
Arun Prakash Jana 2019-12-29 23:21:18 +05:30
parent 146da5911d
commit f90c37cd5e
No known key found for this signature in database
GPG key ID: A75979F35C080412
5 changed files with 69 additions and 48 deletions

View file

@ -93,7 +93,7 @@ A curses library with wide char support (e.g. ncursesw), libreadline (optional)
| --- | --- | --- | | --- | --- | --- |
| xdg-open (Linux), open(1) (macOS), cygstart (Cygwin) | base | desktop opener | | xdg-open (Linux), open(1) (macOS), cygstart (Cygwin) | base | desktop opener |
| file, coreutils (cp, mv, rm), xargs | base | file type, copy, move and remove | | file, coreutils (cp, mv, rm), xargs | base | file type, copy, move and remove |
| tar, (un)zip [atool/bsdtar for more formats] | base | create, list, extract tar, gzip, bzip2, zip | | tar, (un)zip [atool/bsdtar for more formats] | base | create, list, extract bzip2, (g)zip, tar |
| archivemount, fusermount(3) | optional | mount, unmount archives | | archivemount, fusermount(3) | optional | mount, unmount archives |
| sshfs, [rclone](https://rclone.org/), fusermount(3) | optional | mount, unmount remotes | | sshfs, [rclone](https://rclone.org/), fusermount(3) | optional | mount, unmount remotes |
| trash-cli | optional | trash files (default action: rm) | | trash-cli | optional | trash files (default action: rm) |

28
nnn.1
View file

@ -174,6 +174,13 @@ The minimum file size unit is byte (B). The rest are K, M, G, T, P, E, Z, Y (pow
The SHELL, EDITOR (VISUAL, if defined) and PAGER environment variables take precedence The SHELL, EDITOR (VISUAL, if defined) and PAGER environment variables take precedence
when dealing with the !, e and p commands respectively. A single combination to arguments is supported for SHELL and PAGER. when dealing with the !, e and p commands respectively. A single combination to arguments is supported for SHELL and PAGER.
.Pp .Pp
\fBNNN_OPENER:\fR specify a custom file opener.
.Bd -literal
export NNN_OPENER=nuke
NOTE: `nuke` is a file opener available in plugin repository
.Ed
.Pp
\fBNNN_BMS:\fR bookmark string as \fIkey_char:location\fR pairs (max 10) separated by \fBNNN_BMS:\fR bookmark string as \fIkey_char:location\fR pairs (max 10) separated by
\fI;\fR: \fI;\fR:
.Bd -literal .Bd -literal
@ -187,7 +194,11 @@ when dealing with the !, e and p commands respectively. A single combination to
.Bd -literal .Bd -literal
export NNN_PLUG='o:fzopen;p:mocplay;d:diffs;m:nmount;t:imgthumb;i:mediainf' export NNN_PLUG='o:fzopen;p:mocplay;d:diffs;m:nmount;t:imgthumb;i:mediainf'
NOTE: To run a plugin directly, press \fI:\fR followed by the plugin key. NOTES:
1. To run a plugin directly, press \fI;\fR followed by the plugin key
2. To skip directory refresh after running a plugin,prefix with \fB-\fR
export NNN_PLUG='m:-mediainfo'
.Ed .Ed
.Pp .Pp
To assign keys to arbitrary non-background non-shell-interpreted cli To assign keys to arbitrary non-background non-shell-interpreted cli
@ -198,7 +209,11 @@ when dealing with the !, e and p commands respectively. A single combination to
NOTES: NOTES:
1. Use single quotes for $NNN_PLUG so $nnn is not interpreted 1. Use single quotes for $NNN_PLUG so $nnn is not interpreted
2. $nnn should be the last argument (IF you want to pass the hovered file name) 2. $nnn should be the last argument (IF you want to pass the hovered file name)
3. (Again) add \fI_\fR before the command 3. (Again) add \fB_\fR before the command
4. To disable directory refresh after running a \fIcommand as plugin\fR, prefix the command with \fB-_\fR
5. To skip user confirmation after command execution, suffix with \fB*\fR
export NNN_PLUG='y:-_sync*'
.Ed .Ed
.Pp .Pp
\fBNNN_USE_EDITOR:\fR use VISUAL (else EDITOR, preferably CLI, fallback vi) to handle text files. \fBNNN_USE_EDITOR:\fR use VISUAL (else EDITOR, preferably CLI, fallback vi) to handle text files.
@ -227,13 +242,6 @@ when dealing with the !, e and p commands respectively. A single combination to
NOTE: The options must be preceded by `rclone` and max 5 flags are supported. NOTE: The options must be preceded by `rclone` and max 5 flags are supported.
.Ed .Ed
.Pp .Pp
\fBNNN_OPENER:\fR specify a custom file opener.
.Bd -literal
export NNN_OPENER=nuke
NOTE: `nuke` is a file opener available in plugin repository
.Ed
.Pp
\fBNNN_IDLE_TIMEOUT:\fR set idle timeout (in seconds) to invoke terminal locker (default: disabled). \fBNNN_IDLE_TIMEOUT:\fR set idle timeout (in seconds) to invoke terminal locker (default: disabled).
.Pp .Pp
\fBNNN_TRASH:\fR trash (instead of \fIdelete\fR) files to desktop Trash. \fBNNN_TRASH:\fR trash (instead of \fIdelete\fR) files to desktop Trash.
@ -241,7 +249,7 @@ when dealing with the !, e and p commands respectively. A single combination to
export NNN_TRASH=1 export NNN_TRASH=1
.Ed .Ed
.Pp .Pp
\fBNNN:\fR this is a special variable set to the current entry before executing a command from the command prompt or spawning a shell. \fBNNN:\fR this is a special variable set to the hovered entry before executing a command from the command prompt or spawning a shell.
.Sh KNOWN ISSUES .Sh KNOWN ISSUES
.Nm .Nm
may not handle keypresses correctly when used with tmux (see issue #104 for more details). Set \fBTERM=xterm-256color\fR to address it. may not handle keypresses correctly when used with tmux (see issue #104 for more details). Set \fBTERM=xterm-256color\fR to address it.

View file

@ -81,7 +81,7 @@ Plugins are installed to `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins`. You ca
`nnn` refreshes a directory after running a plugin by key (method 1 above) to reflect any changes by the plugin. To disable this (say while running the `mediainfo` plugin on some filtered files), add a `-` before the plugin name: `nnn` refreshes a directory after running a plugin by key (method 1 above) to reflect any changes by the plugin. To disable this (say while running the `mediainfo` plugin on some filtered files), add a `-` before the plugin name:
export NNN_PLUG='o:fzopen;m:-mediainfo;p:mocplay; export NNN_PLUG='m:-mediainfo'
Now `nnn` will not refresh the directory after running the `mediainfo` plugin. Now `nnn` will not refresh the directory after running the `mediainfo` plugin.
@ -99,7 +99,7 @@ Now <kbd>;x</kbd> can be used to make a file executable, <kbd>;g</kbd> can be us
`nnn` waits for user confirmation (the prompt `Press Enter to continue`) after it executes a command as plugin (unlike plugins which can add a `read` to wait). To skip this, add a `*` after the command. For example: `nnn` waits for user confirmation (the prompt `Press Enter to continue`) after it executes a command as plugin (unlike plugins which can add a `read` to wait). To skip this, add a `*` after the command. For example:
export NNN_PLUG='x:_chmod +x $nnn;g:_git log;s:_smplayer $nnn*;o:fzopen' export NNN_PLUG='s:_smplayer $nnn*'
Now there will be no prompt after <kbd>;s</kbd>. Now there will be no prompt after <kbd>;s</kbd>.
@ -108,7 +108,7 @@ Notes:
1. Use single quotes for `$NNN_PLUG` so `$nnn` is not interpreted 1. Use single quotes for `$NNN_PLUG` so `$nnn` is not interpreted
2. `$nnn` should be the last argument (IF you want to pass the hovered file name) 2. `$nnn` should be the last argument (IF you want to pass the hovered file name)
3. (_Again_) add `_` before the command 3. (_Again_) add `_` before the command
4. To disable directory refresh after running a command as plugin prefix the command with `-_` 4. To disable directory refresh after running a _command as plugin_, prefix the command with `-_`
## Access level of plugins ## Access level of plugins

View file

@ -334,6 +334,7 @@ static kv bookmark[BM_MAX];
static kv plug[PLUGIN_MAX]; static kv plug[PLUGIN_MAX];
static uchar g_tmpfplen; static uchar g_tmpfplen;
static uchar blk_shift = BLK_SHIFT_512; static uchar blk_shift = BLK_SHIFT_512;
static regex_t archive_re;
/* Retain old signal handlers */ /* Retain old signal handlers */
#ifdef __linux__ #ifdef __linux__
@ -464,6 +465,7 @@ static char * const utils[] = {
#define MSG_ARCHIVE_OPTS 34 #define MSG_ARCHIVE_OPTS 34
#define MSG_PLUGIN_KEYS 35 #define MSG_PLUGIN_KEYS 35
#define MSG_BOOKMARK_KEYS 36 #define MSG_BOOKMARK_KEYS 36
#define MSG_INVALID_REG 37
static const char * const messages[] = { static const char * const messages[] = {
"no traversal", "no traversal",
@ -500,9 +502,10 @@ static const char * const messages[] = {
"'s'shfs / 'r'clone?", "'s'shfs / 'r'clone?",
"may take a while, try refresh", "may take a while, try refresh",
"app name: ", "app name: ",
"e'x'tract / 'l'ist / 'm'ount?", "'d'efault, e'x'tract / 'l'ist / 'm'ount?",
"plugin keys:", "plugin keys:",
"bookmark keys:", "bookmark keys:",
"invalid regex",
}; };
/* Supported configuration environment variables */ /* Supported configuration environment variables */
@ -511,9 +514,10 @@ static const char * const messages[] = {
#define NNN_CONTEXT_COLORS 2 #define NNN_CONTEXT_COLORS 2
#define NNN_IDLE_TIMEOUT 3 #define NNN_IDLE_TIMEOUT 3
#define NNNLVL 4 #define NNNLVL 4
#define NNN_PIPE 5 /* strings end here */ #define NNN_PIPE 5
#define NNN_USE_EDITOR 6 /* flags begin here */ #define NNN_ARCHIVE 6 /* strings end here */
#define NNN_TRASH 7 #define NNN_USE_EDITOR 7 /* flags begin here */
#define NNN_TRASH 8
static const char * const env_cfg[] = { static const char * const env_cfg[] = {
"NNN_BMS", "NNN_BMS",
@ -522,6 +526,7 @@ static const char * const env_cfg[] = {
"NNN_IDLE_TIMEOUT", "NNN_IDLE_TIMEOUT",
"NNNLVL", "NNNLVL",
"NNN_PIPE", "NNN_PIPE",
"NNN_ARCHIVE",
"NNN_USE_EDITOR", "NNN_USE_EDITOR",
"NNN_TRASH", "NNN_TRASH",
}; };
@ -552,6 +557,7 @@ static char mv[] = "mv -i";
static const char cpmvformatcmd[] = "sed -i 's|^\\(\\(.*/\\)\\(.*\\)$\\)|#\\1\\n\\3|' %s"; static const char cpmvformatcmd[] = "sed -i 's|^\\(\\(.*/\\)\\(.*\\)$\\)|#\\1\\n\\3|' %s";
static const char cpmvrenamecmd[] = "sed 's|^\\([^#][^/]\\?.*\\)$|%s/\\1|;s|^#\\(/.*\\)$|\\1|' %s | tr '\\n' '\\0' | xargs -0 -n2 sh -c '%s \"$0\" \"$@\" < /dev/tty'"; static const char cpmvrenamecmd[] = "sed 's|^\\([^#][^/]\\?.*\\)$|%s/\\1|;s|^#\\(/.*\\)$|\\1|' %s | tr '\\n' '\\0' | xargs -0 -n2 sh -c '%s \"$0\" \"$@\" < /dev/tty'";
static const char batchrenamecmd[] = "paste -d'\n' %s %s | sed 'N; /^\\(.*\\)\\n\\1$/!p;d' | tr '\n' '\\0' | xargs -0 -n2 mv 2>/dev/null"; static const char batchrenamecmd[] = "paste -d'\n' %s %s | sed 'N; /^\\(.*\\)\\n\\1$/!p;d' | tr '\n' '\\0' | xargs -0 -n2 mv 2>/dev/null";
static const char archive_regex[] ="\\.(bz|bz2|gz|tar|taz|tbz|tbz2|tgz|z|zip)$";
/* Event handling */ /* Event handling */
#ifdef LINUX_INOTIFY #ifdef LINUX_INOTIFY
@ -599,7 +605,7 @@ static int dentfind(const char *fname, int n);
static void move_cursor(int target, int ignore_scrolloff); static void move_cursor(int target, int ignore_scrolloff);
static inline bool getutil(char *util); static inline bool getutil(char *util);
static size_t mkpath(const char *dir, const char *name, char *out); static size_t mkpath(const char *dir, const char *name, char *out);
static char *xgetenv(const char *name, char *fallback); static char *xgetenv(const char * const name, char *fallback);
static void plugscript(const char *plugin, char *newpath, uchar flags); static void plugscript(const char *plugin, char *newpath, uchar flags);
/* Functions */ /* Functions */
@ -1372,7 +1378,7 @@ static void prompt_run(char *cmd, const char *cur, const char *path)
} }
/* Get program name from env var, else return fallback program */ /* Get program name from env var, else return fallback program */
static char *xgetenv(const char *name, char *fallback) static char *xgetenv(const char * const name, char *fallback)
{ {
char *value = getenv(name); char *value = getenv(name);
@ -2090,7 +2096,7 @@ static int filterentries(char *path, char *lastname)
case '=': // fallthrough /* Launch app */ case '=': // fallthrough /* Launch app */
case ']': // fallthorugh /*Prompt key */ case ']': // fallthorugh /*Prompt key */
case ';': // fallthrough /* Run plugin key */ case ';': // fallthrough /* Run plugin key */
case ',': // falltrough /* Pin CWD */ case ',': // fallthrough /* Pin CWD */
case '?': /* Help and config key, '?' is an invalid regex */ case '?': /* Help and config key, '?' is an invalid regex */
if (len == 1) if (len == 1)
goto end; goto end;
@ -3480,8 +3486,8 @@ static void show_help(const char *path)
"cP Copy sel here%-10c^Y Edit sel\n" "cP Copy sel here%-10c^Y Edit sel\n"
"cV Move sel here%-10c^V Copy/move sel as\n" "cV Move sel here%-10c^V Copy/move sel as\n"
"cX Delete sel%-13c^X Delete entry\n" "cX Delete sel%-13c^X Delete entry\n"
"cf Archive%-16c^F Archive ops\n"
"ce Edit in EDITOR%-10cp Open in PAGER\n" "ce Edit in EDITOR%-10cp Open in PAGER\n"
"ci Archive entry%-0c\n"
"1ORDER TOGGLES\n" "1ORDER TOGGLES\n"
"cS Disk usage%-14cA Apparent du\n" "cS Disk usage%-14cA Apparent du\n"
"cz Size%-20ct Time\n" "cz Size%-20ct Time\n"
@ -4560,6 +4566,28 @@ nochange:
continue; continue;
} }
if (!regexec(&archive_re, dents[cur].name, 0, NULL, 0)) {
r = get_input(messages[MSG_ARCHIVE_OPTS]);
if (r == 'l' || r == 'x') {
mkpath(path, dents[cur].name, newpath);
handle_archive(newpath, path, r);
copycurname();
goto begin;
}
fd = FALSE;
if (r == 'm') {
if (!archive_mount(dents[cur].name, path, newpath, &presel))
fd = MSG_FAILED;
} else if (r != 'd')
fd = MSG_INVALID_KEY;
if (r != 'd') {
fd ? printwait(messages[fd], &presel) : clearprompt();
goto nochange;
}
}
if (!sb.st_size) { if (!sb.st_size) {
printwait(messages[MSG_EMPTY_FILE], &presel); printwait(messages[MSG_EMPTY_FILE], &presel);
goto nochange; goto nochange;
@ -5324,28 +5352,6 @@ nochange:
copycurname(); copycurname();
/* Repopulate as directory content may have changed */ /* Repopulate as directory content may have changed */
goto begin; goto begin;
case SEL_ARCHIVEOPS:
if (!ndents)
goto nochange;
r = get_input(messages[MSG_ARCHIVE_OPTS]);
if (r == 'l' || r == 'x') {
mkpath(path, dents[cur].name, newpath);
handle_archive(newpath, path, r);
copycurname();
goto begin;
}
if (r != 'm') {
printwait(messages[MSG_INVALID_KEY], &presel);
goto nochange;
}
if (!archive_mount(dents[cur].name, path, newpath, &presel)) {
printwait(messages[MSG_FAILED], &presel);
goto nochange;
}
// fallthrough
case SEL_REMOTE: case SEL_REMOTE:
if (sel == SEL_REMOTE && !remote_mount(newpath, &presel)) if (sel == SEL_REMOTE && !remote_mount(newpath, &presel))
goto nochange; goto nochange;
@ -5802,6 +5808,13 @@ int main(int argc, char *argv[])
} }
} }
/* Set archive handling (enveditor used as tmp var) */
enveditor = getenv(env_cfg[NNN_ARCHIVE]);
if (setfilter(&archive_re, (enveditor ? enveditor : archive_regex))) {
fprintf(stderr, "%s\n", messages[MSG_INVALID_REG]);
return _FAILURE;
}
/* Edit text in EDITOR if opted (and opener is not all-CLI) */ /* Edit text in EDITOR if opted (and opener is not all-CLI) */
if (!cfg.cliopener && xgetenv_set(env_cfg[NNN_USE_EDITOR])) if (!cfg.cliopener && xgetenv_set(env_cfg[NNN_USE_EDITOR]))
cfg.useeditor = 1; cfg.useeditor = 1;
@ -5928,6 +5941,9 @@ int main(int argc, char *argv[])
} else if (!cfg.picker && g_selpath) } else if (!cfg.picker && g_selpath)
unlink(g_selpath); unlink(g_selpath);
/* Free the regex */
regfree(&archive_re);
/* Free the selection buffer */ /* Free the selection buffer */
free(pselbuf); free(pselbuf);

View file

@ -87,7 +87,6 @@ enum action {
SEL_NEW, SEL_NEW,
SEL_RENAME, SEL_RENAME,
SEL_RENAMEMUL, SEL_RENAMEMUL,
SEL_ARCHIVEOPS,
SEL_REMOTE, SEL_REMOTE,
SEL_UMOUNT, SEL_UMOUNT,
SEL_HELP, SEL_HELP,
@ -179,7 +178,7 @@ static struct key bindings[] = {
/* File details */ /* File details */
{ 'D', SEL_STATS }, { 'D', SEL_STATS },
/* Create archive */ /* Create archive */
{ 'f', SEL_ARCHIVE }, { 'i', SEL_ARCHIVE },
/* Toggle sort by size */ /* Toggle sort by size */
{ 'z', SEL_FSIZE }, { 'z', SEL_FSIZE },
/* Sort by apparent size including dir contents */ /* Sort by apparent size including dir contents */
@ -226,8 +225,6 @@ static struct key bindings[] = {
{ KEY_F(2), SEL_RENAME }, { KEY_F(2), SEL_RENAME },
/* Rename contents of current dir */ /* Rename contents of current dir */
{ 'r', SEL_RENAMEMUL }, { 'r', SEL_RENAMEMUL },
/* Mount an archive */
{ CONTROL('F'), SEL_ARCHIVEOPS },
/* Connect to server over SSHFS */ /* Connect to server over SSHFS */
{ 'c', SEL_REMOTE }, { 'c', SEL_REMOTE },
/* Disconnect a SSHFS mount point */ /* Disconnect a SSHFS mount point */