Plugin mimelist: support reading file list from (cmd as) plugin

This commit is contained in:
Arun Prakash Jana 2020-05-03 14:55:33 +05:30
parent e8e87f6ba2
commit eee5057da5
No known key found for this signature in database
GPG key ID: A75979F35C080412
6 changed files with 120 additions and 40 deletions

View file

@ -29,7 +29,7 @@ nnn_cd () {
read -r context
fi
printf "%s" "${context:-0}$dir" > "$NNN_PIPE"
printf "%s" "${context:-0}c$dir" > "$NNN_PIPE"
}
cmd_exists () {

View file

@ -47,6 +47,7 @@ Plugins are installed to `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins`.
| kdeconnect | Send selected files to an Android device | sh | kdeconnect-cli |
| launch | GUI application launcher | sh | fzf/fzy |
| mediainf | Show media information | sh | mediainfo |
| mimelist | List files by mime in subtree | sh | fd/find |
| moclyrics | Show lyrics of the track playing in moc | sh | [ddgr](https://github.com/jarun/ddgr), [moc](http://moc.daper.net/) |
| mocplay | Append (and/or play) selection/dir/file in moc | sh | [moc](http://moc.daper.net/) |
| mp3conv | Extract audio from multimedia as mp3 | sh | ffmpeg |
@ -163,8 +164,14 @@ Drop the plugin in `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins` and make it e
#### Controlling `nnn`'s active directory
`nnn` provides a mechanism for plugins to control its active directory.
The way to do so is by writing to the pipe pointed by the environment variable `NNN_PIPE`.
The plugin should write a single string in the format `<number><path>` without a newline at the end. For example, `1/etc`.
The number indicates the context to change the active directory of (0 is used to indicate the current context).
The plugin should write a single string in the format `<context number><char><path>` without a newline at the end. For example, `1c/etc`.
The context number indicates the context to change the active directory of (0 is used to indicate the current context).
The `<char>` indicates the operation type.
: Char : Operation :
|:---:| --- |
| c | cd |
| l | list files in list mode |
For convenience, we provided a helper script named `.nnn-plugin-helper` and a function named `nnn_cd` to ease this process. `nnn_cd` receives the path to change to as the first argument, and the context as an optional second argument.
If a context is not provided, it is asked for explicitly. To skip this and choose the current context, set the `CUR_CTX` variable in `.nnn-plugin-helper` to `1`.
@ -201,7 +208,7 @@ There are many plugins provided by `nnn` which can be used as examples. Here are
printf "cd to: "
read -r dir
printf "%s" "0$dir" > "$NNN_PIPE"
printf "%s" "0c$dir" > "$NNN_PIPE"
```
## Contributing plugins

View file

@ -13,7 +13,7 @@ if which autojump >/dev/null 2>&1; then
printf "jump to: "
read -r dir
odir="$(autojump "$dir")"
printf "%s" "0$odir" > "$NNN_PIPE"
printf "%s" "0c$odir" > "$NNN_PIPE"
else
printf "autojump missing"
read -r _

View file

@ -25,4 +25,4 @@ else
exit 1
fi
printf "%s" "0$sel" > "$NNN_PIPE"
printf "%s" "0c$sel" > "$NNN_PIPE"

21
plugins/mimelist Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env sh
# Description: Run fd/find in subtree and list files by mime type in current context
# Requires: fd/find
#
# Shell: POSIX compliant
# Author: Arun Prakash jana
. "$(dirname "$0")"/.nnn-plugin-helper
if [ "$(cmd_exists fd)" -eq "0" ]; then
fd=fd
else
fd=find
fi
printf "mime: "
read -r mime
printf "%s" "0l" > "$NNN_PIPE"
$fd | file -if- | grep "$mime" | awk -F: '{printf "%s\0", $1}' > "$NNN_PIPE"

120
src/nnn.c
View file

@ -514,8 +514,9 @@ static char * const utils[] = {
#define MSG_RM_TMP 40
#define MSG_NOCHNAGE 41
#define MSG_CANCEL 42
#define MSG_0_ENTRIES 43
#ifndef DIR_LIMITED_SELECTION
#define MSG_DIR_CHANGED 43 /* Must be the last entry */
#define MSG_DIR_CHANGED 44 /* Must be the last entry */
#endif
static const char * const messages[] = {
@ -562,6 +563,7 @@ static const char * const messages[] = {
"unchanged",
"cancelled",
"first file (\')/char?",
"0 entries",
#ifndef DIR_LIMITED_SELECTION
"dir changed, range sel off", /* Must be the last entry */
#endif
@ -691,6 +693,7 @@ static inline bool getutil(char *util);
static size_t mkpath(const char *dir, const char *name, char *out);
static char *xgetenv(const char *name, char *fallback);
static bool plugscript(const char *plugin, const char *path, uchar flags);
static char *load_input(int fd, char *path);
/* Functions */
@ -4218,11 +4221,72 @@ static bool plctrl_init(void)
return _SUCCESS;
}
static void rmlistpath()
{
if (listpath) {
DPRINTF_S(__FUNCTION__);
DPRINTF_S(initpath);
spawn("rm -rf", initpath, NULL, NULL, F_NOTRACE | F_MULTI);
listpath = NULL;
}
}
static void readpipe(int fd, char **path, char **lastname, char **lastdir)
{
char *nextpath = NULL;
ssize_t len = read(fd, g_buf, 1);
if (len != 1)
return;
char ctx = g_buf[0] - '0';
if (ctx > CTX_MAX)
return;
len = read(fd, g_buf, 1);
if (len != 1)
return;
char op = g_buf[0];
if (op == 'c') {
len = read(fd, g_buf, PATH_MAX);
if (len <= 0)
return;
nextpath = g_buf;
} else if (op == 'l') {
/* Remove last list mode path, if any */
rmlistpath();
nextpath = load_input(fd, *path);
if (nextpath) {
free(initpath);
initpath = nextpath;
DPRINTF_S(initpath);
}
}
if (nextpath) {
if (ctx == 0 || ctx == cfg.curctx + 1) {
xstrsncpy(*lastdir, *path, PATH_MAX);
xstrsncpy(*path, nextpath, PATH_MAX);
} else {
int r = ctx - 1;
g_ctx[r].c_cfg.ctxactive = 0;
savecurctx(&cfg, nextpath, dents[cur].name, r);
*path = g_ctx[r].c_path;
*lastdir = g_ctx[r].c_last;
*lastname = g_ctx[r].c_name;
}
}
}
static bool run_selected_plugin(char **path, const char *file, char *runfile, char **lastname, char **lastdir)
{
int fd;
size_t len;
if (!(g_states & STATE_PLUGIN_INIT)) {
plctrl_init();
g_states |= STATE_PLUGIN_INIT;
@ -4248,27 +4312,9 @@ static bool run_selected_plugin(char **path, const char *file, char *runfile, ch
spawn(g_buf, NULL, *path, *path, F_NORMAL);
}
len = read(fd, g_buf, PATH_MAX);
g_buf[len] = '\0';
readpipe(fd, path, lastname, lastdir);
close(fd);
if (len > 1) {
int ctx = g_buf[0] - '0';
if (ctx == 0 || ctx == cfg.curctx + 1) {
xstrsncpy(*lastdir, *path, PATH_MAX);
xstrsncpy(*path, g_buf + 1, PATH_MAX);
} else if (ctx >= 1 && ctx <= CTX_MAX) {
int r = ctx - 1;
g_ctx[r].c_cfg.ctxactive = 0;
savecurctx(&cfg, g_buf + 1, dents[cur].name, r);
*path = g_ctx[r].c_path;
*lastdir = g_ctx[r].c_last;
*lastname = g_ctx[r].c_name;
}
}
return TRUE;
}
@ -6318,7 +6364,7 @@ static char *make_tmp_tree(char **paths, ssize_t entries, const char *prefix)
struct stat sb;
char *slash, *tmp;
ssize_t len = xstrlen(prefix);
char *tmpdir = malloc(sizeof(char) * (PATH_MAX + TMP_LEN_MAX));
char *tmpdir = malloc(PATH_MAX);
if (!tmpdir) {
DPRINTF_S(strerror(errno));
@ -6377,7 +6423,7 @@ static char *make_tmp_tree(char **paths, ssize_t entries, const char *prefix)
return tmpdir;
}
static char *load_input()
static char *load_input(int fd, char *path)
{
/* 512 KiB chunk size */
ssize_t i, chunk_count = 1, chunk = 512 * 1024, entries = 0;
@ -6392,13 +6438,16 @@ static char *load_input()
return NULL;
}
if (!getcwd(cwd, PATH_MAX)) {
free(input);
return NULL;
}
if (!path) {
if (!getcwd(cwd, PATH_MAX)) {
free(input);
return NULL;
}
} else
xstrsncpy(cwd, path, PATH_MAX);
while (chunk_count < 512) {
input_read = read(STDIN_FILENO, input + total_read, chunk);
input_read = read(fd, input + total_read, chunk);
if (input_read < 0) {
DPRINTF_S(strerror(errno));
goto malloc_1;
@ -6461,7 +6510,11 @@ static char *load_input()
DPRINTF_D(chunk_count);
if (!entries) {
fprintf(stderr, "0 entries\n");
if (g_states & STATE_PLUGIN_INIT) {
printmsg(messages[MSG_0_ENTRIES]);
xdelay(XDELAY_INTERVAL_MS);
} else
fprintf(stderr, "%s\n", messages[MSG_0_ENTRIES]);
goto malloc_1;
}
@ -6842,7 +6895,7 @@ int main(int argc, char *argv[])
/* Check if we are in path list mode */
if (!isatty(STDIN_FILENO)) {
/* This is the same as listpath */
initpath = load_input();
initpath = load_input(STDIN_FILENO, NULL);
if (!initpath)
return _FAILURE;
@ -7054,8 +7107,7 @@ int main(int argc, char *argv[])
unlink(selpath);
/* Remove tmp dir in list mode */
if (listpath)
spawn("rm -rf", initpath, NULL, NULL, F_NOTRACE | F_MULTI);
rmlistpath();
/* Free the regex */
#ifdef PCRE