]> Sergey Matveev's repositories - nnn.git/commitdiff
NNN_PLUG: use `|` to page run-and-exit cmd output
authorArun Prakash Jana <engineerarun@gmail.com>
Sun, 6 Jun 2021 16:21:30 +0000 (21:51 +0530)
committerArun Prakash Jana <engineerarun@gmail.com>
Sun, 6 Jun 2021 16:21:30 +0000 (21:51 +0530)
nnn.1
plugins/README.md
src/nnn.c

diff --git a/nnn.1 b/nnn.1
index 55555deaf475a24433da7ff69d40b5ff0ade14fe..b1dab368d581321c788ffd8a6b97efa7e12516bf 100644 (file)
--- a/nnn.1
+++ b/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
 separated by \fI;\fR:
 .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
 .Pp
 \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
        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
 
-    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:
     ----------------------------------- + -------------------------------------------------
index b6f383fb3ab520dc3c04830929d15c07b4cb2bc4..dd7ca14b6f36fb91d028cacfee889c9bed1d8240 100644 (file)
@@ -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.
 
-For example:
-
 ```sh
 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
 
-`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
 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
 
-To run a GUI app as plugin, add a `&` after `!`. For example:
+To run a GUI app as plugin, add a `&` after `!`.
 
 ```sh
 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:
 
 1. Use single quotes for `$NNN_PLUG` so `$nnn` is not interpreted
index 5cc97c311859015ee798bbe52d035c1d75f21c23..c7347e67f0701e532da0e523e635340623fa38a5 100644 (file)
--- a/src/nnn.c
+++ b/src/nnn.c
 #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_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_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_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_SILENT  (F_CLI | F_NOTRACE)
 
@@ -1921,28 +1922,46 @@ static bool initcurses(void *oldmask)
 }
 
 /* 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;
+       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;
+       cmd = line;
 
        while (*line) { // NOLINT
                if (ISBLANK(*line)) {
                        *line++ = '\0';
 
                        if (!*line) // NOLINT
-                               return count;
+                               break;
 
                        argv[count++] = line;
-                       if (count == EXEC_ARGS_MAX)
-                               return -1;
+                       if (count == EXEC_ARGS_MAX) {
+                               count = -1;
+                               break;
+                       }
                }
 
                ++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)
@@ -2038,21 +2057,9 @@ static int spawn(char *file, char *arg1, char *arg2, char *arg3, uchar_t flag)
        }
 
        if (flag & F_MULTI) {
-               size_t len = xstrlen(file) + 1;
-
-               cmd = (char *)malloc(len);
-               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;
-               }
+               cmd = parseargs(file, argv, &status);
+               if (!cmd)
+                       return -1;
        } else
                argv[status++] = file;
 
@@ -4014,29 +4021,42 @@ static uchar_t get_free_ctx(void)
  * Gets only a single line (that's what we need
  * 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,
-                       const char *arg1, const char *arg2, const bool page)
+static char *get_output(char *buf, const size_t bytes, char *file, char *arg1, char *arg2, bool page)
 {
        pid_t pid;
        int pipefd[2];
        FILE *pf;
-       int tmp, flags;
+       int index = 0, flags;
        char *ret = NULL;
+       char * argv[EXEC_ARGS_MAX];
+       char *cmd = NULL;
+
+       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)
+       if (pipe(pipefd) == -1) {
+               free(cmd);
                errexit();
+       }
 
-       for (tmp = 0; tmp < 2; ++tmp) {
+       for (index = 0; index < 2; ++index) {
                /* Get previous flags */
-               flags = fcntl(pipefd[tmp], F_GETFL, 0);
+               flags = fcntl(pipefd[index], F_GETFL, 0);
 
                /* Set bit for non-blocking flag */
                flags |= O_NONBLOCK;
 
                /* Change flags on fd */
-               fcntl(pipefd[tmp], F_SETFL, flags);
+               fcntl(pipefd[index], F_SETFL, flags);
        }
 
        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], STDERR_FILENO);
                close(pipefd[1]);
-               execlp(file, file, arg1, arg2, NULL);
+               execvp(*argv, argv);
                _exit(EXIT_SUCCESS);
        }
 
        /* In parent */
        waitpid(pid, NULL, 0);
        close(pipefd[1]);
+       free(cmd);
 
        if (!page) {
                pf = fdopen(pipefd[0], "r");
@@ -4095,7 +4116,7 @@ static void pipetof(char *cmd, FILE *fout)
 /*
  * 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;
        FILE *fp;
@@ -4693,7 +4714,11 @@ static bool run_cmd_as_plugin(const char *file, char *runfile, uchar_t flags)
        else
                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;
 }
 
@@ -4812,22 +4837,27 @@ static bool run_selected_plugin(char **path, const char *file, char *runfile, ch
                g_state.pluginit = 1;
        }
 
+       /* Check for run-cmd-as-plugin mode */
        if (*file == '!') {
                flags = F_MULTI | F_CONFIRM;
-
-               /* Get rid of preceding ! */
                ++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 */
                if (*file == '&') {
                        flags = F_NOTRACE | F_NOWAIT;
                        ++file;
+               }
 
-                       if (!*file)
-                               return FALSE;
+               if (!*file)
+                       return FALSE;
 
+               if (flags & F_NOTRACE) {
                        run_cmd_as_plugin(file, runfile, flags);
                        return TRUE;
                }