mirror of
https://github.com/jarun/nnn.git
synced 2024-12-01 02:49:44 +00:00
NNN_PLUG: use |
to page run-and-exit cmd output
This commit is contained in:
parent
3b6938f782
commit
15eed29227
13
nnn.1
13
nnn.1
|
@ -369,7 +369,7 @@ used. A single combination of arguments is supported for SHELL and PAGER.
|
||||||
\fBNNN_BMS:\fR bookmark string as \fIkey_char:location\fR pairs
|
\fBNNN_BMS:\fR bookmark string as \fIkey_char:location\fR pairs
|
||||||
separated by \fI;\fR:
|
separated by \fI;\fR:
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
export NNN_BMS="d:$HOME/Documents;u:/home/user/Cam Uploads;D:$HOME/Downloads/"
|
export NNN_BMS="d:$HOME/Docs;u:/home/user/Cam Uploads;D:$HOME/Downloads/"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
\fBNNN_PLUG:\fR directly executable plugins as \fIkey_char:plugin\fR pairs
|
\fBNNN_PLUG:\fR directly executable plugins as \fIkey_char:plugin\fR pairs
|
||||||
|
@ -403,11 +403,18 @@ separated by \fI;\fR:
|
||||||
5. To skip user confirmation after command execution, suffix with \fB*\fR
|
5. To skip user confirmation after command execution, suffix with \fB*\fR
|
||||||
Note: Do not use \fB*\fR with programs those run and exit e.g. cat
|
Note: Do not use \fB*\fR with programs those run and exit e.g. cat
|
||||||
|
|
||||||
export NNN_PLUG='y:-!sync*'
|
export NNN_PLUG='y:-!sync*'
|
||||||
|
|
||||||
6. To run a \fIGUI app as plugin\fR, add a \fB&\fR after \fB!\fR
|
6. To run a \fIGUI app as plugin\fR, add a \fB&\fR after \fB!\fR
|
||||||
|
|
||||||
export NNN_PLUG='m:-!&mousepad $nnn'
|
export NNN_PLUG='m:-!&mousepad $nnn'
|
||||||
|
|
||||||
|
7. To show the output of run-to-completion commands which do not need user
|
||||||
|
input, add \fB|\fR (pipe) after \fB!\fR
|
||||||
|
Note: This option is incompatible with \fB&\fR (terminal output is masked
|
||||||
|
for GUI programs) and ignores \fB*\fR (output is already paged for user)
|
||||||
|
|
||||||
|
export NNN_PLUG='m:-!|mediainfo $nnn'
|
||||||
|
|
||||||
EXAMPLES:
|
EXAMPLES:
|
||||||
----------------------------------- + -------------------------------------------------
|
----------------------------------- + -------------------------------------------------
|
||||||
|
|
|
@ -124,8 +124,6 @@ export NNN_PLUG='p:-plugin'
|
||||||
|
|
||||||
To assign keys to arbitrary non-background, non-shell-interpreted cli commands and invoke like plugins, add `!` (underscore) before the command.
|
To assign keys to arbitrary non-background, non-shell-interpreted cli commands and invoke like plugins, add `!` (underscore) before the command.
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export NNN_PLUG='x:!chmod +x $nnn;g:!git log;s:!smplayer $nnn'
|
export NNN_PLUG='x:!chmod +x $nnn;g:!git log;s:!smplayer $nnn'
|
||||||
```
|
```
|
||||||
|
@ -134,7 +132,7 @@ Now <kbd>;x</kbd> can be used to make a file executable, <kbd>;g</kbd> can be us
|
||||||
|
|
||||||
#### Skip user confirmation after command execution
|
#### Skip user confirmation after command execution
|
||||||
|
|
||||||
`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.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export NNN_PLUG='s:!smplayer $nnn*;n:-!vim /home/vaio/Dropbox/Public/synced_note*'
|
export NNN_PLUG='s:!smplayer $nnn*;n:-!vim /home/vaio/Dropbox/Public/synced_note*'
|
||||||
|
@ -146,12 +144,22 @@ Note: Do not use `*` with programs those run and exit e.g. cat.
|
||||||
|
|
||||||
#### Run GUI app as plugin
|
#### Run GUI app as plugin
|
||||||
|
|
||||||
To run a GUI app as plugin, add a `&` after `!`. For example:
|
To run a GUI app as plugin, add a `&` after `!`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export NNN_PLUG='m:-!&mousepad $nnn'
|
export NNN_PLUG='m:-!&mousepad $nnn'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Page run-and-exit command output
|
||||||
|
|
||||||
|
To show the output of run-to-completion commands which do not need user input, add `|` (pipe) after `!`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='m:-!|mediainfo $nnn'
|
||||||
|
```
|
||||||
|
|
||||||
|
This option is incompatible with `&` (terminal output is masked for GUI programs) and ignores `*` (output is already paged for user).
|
||||||
|
|
||||||
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
|
||||||
|
|
106
src/nnn.c
106
src/nnn.c
|
@ -213,11 +213,12 @@
|
||||||
#define F_NONE 0x00 /* no flag set */
|
#define F_NONE 0x00 /* no flag set */
|
||||||
#define F_MULTI 0x01 /* first arg can be combination of args; to be used with F_NORMAL */
|
#define F_MULTI 0x01 /* first arg can be combination of args; to be used with F_NORMAL */
|
||||||
#define F_NOWAIT 0x02 /* don't wait for child process (e.g. file manager) */
|
#define F_NOWAIT 0x02 /* don't wait for child process (e.g. file manager) */
|
||||||
#define F_NOTRACE 0x04 /* suppress stdout and strerr (no traces) */
|
#define F_NOTRACE 0x04 /* suppress stdout and stderr (no traces) */
|
||||||
#define F_NORMAL 0x08 /* spawn child process in non-curses regular CLI mode */
|
#define F_NORMAL 0x08 /* spawn child process in non-curses regular CLI mode */
|
||||||
#define F_CONFIRM 0x10 /* run command - show results before exit (must have F_NORMAL) */
|
#define F_CONFIRM 0x10 /* run command - show results before exit (must have F_NORMAL) */
|
||||||
#define F_CHKRTN 0x20 /* wait for user prompt if cmd returns failure status */
|
#define F_CHKRTN 0x20 /* wait for user prompt if cmd returns failure status */
|
||||||
#define F_NOSTDIN 0x40 /* suppress stdin */
|
#define F_NOSTDIN 0x40 /* suppress stdin */
|
||||||
|
#define F_PAGE 0x80 /* page output in run-cmd-as-plugin mode */
|
||||||
#define F_CLI (F_NORMAL | F_MULTI)
|
#define F_CLI (F_NORMAL | F_MULTI)
|
||||||
#define F_SILENT (F_CLI | F_NOTRACE)
|
#define F_SILENT (F_CLI | F_NOTRACE)
|
||||||
|
|
||||||
|
@ -1921,28 +1922,46 @@ static bool initcurses(void *oldmask)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No NULL check here as spawn() guards against it */
|
/* No NULL check here as spawn() guards against it */
|
||||||
static int parseargs(char *line, char **argv)
|
static char *parseargs(char *cmd, char **argv, int *pindex)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
size_t len = xstrlen(cmd) + 1;
|
||||||
|
char *line = (char *)malloc(len);
|
||||||
|
|
||||||
|
if (!line) {
|
||||||
|
DPRINTF_S("malloc()!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
xstrsncpy(line, cmd, len);
|
||||||
argv[count++] = line;
|
argv[count++] = line;
|
||||||
|
cmd = line;
|
||||||
|
|
||||||
while (*line) { // NOLINT
|
while (*line) { // NOLINT
|
||||||
if (ISBLANK(*line)) {
|
if (ISBLANK(*line)) {
|
||||||
*line++ = '\0';
|
*line++ = '\0';
|
||||||
|
|
||||||
if (!*line) // NOLINT
|
if (!*line) // NOLINT
|
||||||
return count;
|
break;
|
||||||
|
|
||||||
argv[count++] = line;
|
argv[count++] = line;
|
||||||
if (count == EXEC_ARGS_MAX)
|
if (count == EXEC_ARGS_MAX) {
|
||||||
return -1;
|
count = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++line;
|
++line;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
if (count == -1 || count > (EXEC_ARGS_MAX - 4)) { /* 3 args and last NULL */
|
||||||
|
free(cmd);
|
||||||
|
cmd = NULL;
|
||||||
|
DPRINTF_S("NULL or too many args");
|
||||||
|
}
|
||||||
|
|
||||||
|
*pindex = count;
|
||||||
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pid_t xfork(uchar_t flag)
|
static pid_t xfork(uchar_t flag)
|
||||||
|
@ -2038,21 +2057,9 @@ static int spawn(char *file, char *arg1, char *arg2, char *arg3, uchar_t flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag & F_MULTI) {
|
if (flag & F_MULTI) {
|
||||||
size_t len = xstrlen(file) + 1;
|
cmd = parseargs(file, argv, &status);
|
||||||
|
if (!cmd)
|
||||||
cmd = (char *)malloc(len);
|
return -1;
|
||||||
if (!cmd) {
|
|
||||||
DPRINTF_S("malloc()!");
|
|
||||||
return retstatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
xstrsncpy(cmd, file, len);
|
|
||||||
status = parseargs(cmd, argv);
|
|
||||||
if (status == -1 || status > (EXEC_ARGS_MAX - 4)) { /* 3 args and last NULL */
|
|
||||||
free(cmd);
|
|
||||||
DPRINTF_S("NULL or too many args");
|
|
||||||
return retstatus;
|
|
||||||
}
|
|
||||||
} else
|
} else
|
||||||
argv[status++] = file;
|
argv[status++] = file;
|
||||||
|
|
||||||
|
@ -4014,29 +4021,42 @@ static uchar_t get_free_ctx(void)
|
||||||
* Gets only a single line (that's what we need
|
* Gets only a single line (that's what we need
|
||||||
* for now) or shows full command output in pager.
|
* for now) or shows full command output in pager.
|
||||||
*
|
*
|
||||||
* If page is valid, returns NULL
|
* If page is valid, parses argument 'file' as multi-arg, returns NULL
|
||||||
*/
|
*/
|
||||||
static char *get_output(char *buf, const size_t bytes, const char *file,
|
static char *get_output(char *buf, const size_t bytes, char *file, char *arg1, char *arg2, bool page)
|
||||||
const char *arg1, const char *arg2, const bool page)
|
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
FILE *pf;
|
FILE *pf;
|
||||||
int tmp, flags;
|
int index = 0, flags;
|
||||||
char *ret = NULL;
|
char *ret = NULL;
|
||||||
|
char * argv[EXEC_ARGS_MAX];
|
||||||
|
char *cmd = NULL;
|
||||||
|
|
||||||
if (pipe(pipefd) == -1)
|
if (page) {
|
||||||
|
cmd = parseargs(file, argv, &index);
|
||||||
|
if (!cmd)
|
||||||
|
return NULL;
|
||||||
|
} else
|
||||||
|
argv[index++] = file;
|
||||||
|
|
||||||
|
argv[index] = arg1;
|
||||||
|
argv[++index] = arg2;
|
||||||
|
|
||||||
|
if (pipe(pipefd) == -1) {
|
||||||
|
free(cmd);
|
||||||
errexit();
|
errexit();
|
||||||
|
}
|
||||||
|
|
||||||
for (tmp = 0; tmp < 2; ++tmp) {
|
for (index = 0; index < 2; ++index) {
|
||||||
/* Get previous flags */
|
/* Get previous flags */
|
||||||
flags = fcntl(pipefd[tmp], F_GETFL, 0);
|
flags = fcntl(pipefd[index], F_GETFL, 0);
|
||||||
|
|
||||||
/* Set bit for non-blocking flag */
|
/* Set bit for non-blocking flag */
|
||||||
flags |= O_NONBLOCK;
|
flags |= O_NONBLOCK;
|
||||||
|
|
||||||
/* Change flags on fd */
|
/* Change flags on fd */
|
||||||
fcntl(pipefd[tmp], F_SETFL, flags);
|
fcntl(pipefd[index], F_SETFL, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
|
@ -4046,13 +4066,14 @@ static char *get_output(char *buf, const size_t bytes, const char *file,
|
||||||
dup2(pipefd[1], STDOUT_FILENO);
|
dup2(pipefd[1], STDOUT_FILENO);
|
||||||
dup2(pipefd[1], STDERR_FILENO);
|
dup2(pipefd[1], STDERR_FILENO);
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
execlp(file, file, arg1, arg2, NULL);
|
execvp(*argv, argv);
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In parent */
|
/* In parent */
|
||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
|
free(cmd);
|
||||||
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
pf = fdopen(pipefd[0], "r");
|
pf = fdopen(pipefd[0], "r");
|
||||||
|
@ -4095,7 +4116,7 @@ static void pipetof(char *cmd, FILE *fout)
|
||||||
/*
|
/*
|
||||||
* Follows the stat(1) output closely
|
* Follows the stat(1) output closely
|
||||||
*/
|
*/
|
||||||
static bool show_stats(const char *fpath, const struct stat *sb)
|
static bool show_stats(char *fpath, const struct stat *sb)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
@ -4693,7 +4714,11 @@ static bool run_cmd_as_plugin(const char *file, char *runfile, uchar_t flags)
|
||||||
else
|
else
|
||||||
runfile = NULL;
|
runfile = NULL;
|
||||||
|
|
||||||
spawn(g_buf, runfile, NULL, NULL, flags);
|
if (flags & F_PAGE)
|
||||||
|
get_output(NULL, 0, g_buf, runfile, NULL, TRUE);
|
||||||
|
else
|
||||||
|
spawn(g_buf, runfile, NULL, NULL, flags);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4812,22 +4837,27 @@ static bool run_selected_plugin(char **path, const char *file, char *runfile, ch
|
||||||
g_state.pluginit = 1;
|
g_state.pluginit = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check for run-cmd-as-plugin mode */
|
||||||
if (*file == '!') {
|
if (*file == '!') {
|
||||||
flags = F_MULTI | F_CONFIRM;
|
flags = F_MULTI | F_CONFIRM;
|
||||||
|
|
||||||
/* Get rid of preceding ! */
|
|
||||||
++file;
|
++file;
|
||||||
if (!*file)
|
|
||||||
return FALSE;
|
/* Check if output should be paged */
|
||||||
|
if (*file == '|') {
|
||||||
|
flags |= F_PAGE;
|
||||||
|
++file;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if GUI flags are to be used */
|
/* Check if GUI flags are to be used */
|
||||||
if (*file == '&') {
|
if (*file == '&') {
|
||||||
flags = F_NOTRACE | F_NOWAIT;
|
flags = F_NOTRACE | F_NOWAIT;
|
||||||
++file;
|
++file;
|
||||||
|
}
|
||||||
|
|
||||||
if (!*file)
|
if (!*file)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (flags & F_NOTRACE) {
|
||||||
run_cmd_as_plugin(file, runfile, flags);
|
run_cmd_as_plugin(file, runfile, flags);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue