Support config dir ~/.config/nnn

This commit is contained in:
Arun Prakash Jana 2019-04-21 23:59:51 +05:30
parent f9264577d2
commit a900b2c4fa
7 changed files with 171 additions and 87 deletions

View file

@ -320,11 +320,7 @@ Selected files are visually indicated by a `+`.
The selection can now be listed, copied, moved, removed, archived or linked.
File paths are copied to the temporary file `DIR/.nnncp`, where `DIR` (by priority) is:
$HOME or,
/tmp
$TMPDIR or,
File paths are copied to the temporary file `~/.config/nnn/.selection`.
The path is shown in the help and configuration screen.
@ -379,7 +375,6 @@ The following indicators are used in the detail view:
| `NNN_CONTEXT_COLORS='1234'` | specify per context color [default: '4444' (all blue)] |
| `NNN_IDLE_TIMEOUT=300` | idle seconds before locking terminal [default: disabled] |
| `NNN_COPIER='/absolute/path/to/copier'` | system clipboard copier script [default: none] |
| `NNN_PLUGIN_DIR=/home/user/nnn-plugins` | absolute path to plugins dir |
| `NNN_NOTE=/home/user/Dropbox/notes` | path to note file [default: none] |
| `NNN_TMPFILE=/tmp/nnn` | file to write current open dir path to for cd on quit |
| `NNN_SSHFS_MNT_ROOT=/home/user/.netmnt` | absolute path to SSHFS mount point root |
@ -426,11 +421,9 @@ To lookup keyboard shortcuts at runtime, press <kbd>?</kbd>.
`nnn` can invoke plugins in the current directory (`$PWD` for the plugin) with the currently selected file name as the argument.
Copy the plugins of your interest from the [plugins](https://github.com/jarun/nnn/tree/master/plugins) directory and let `nnn` know the location:
Copy the [plugins](https://github.com/jarun/nnn/tree/master/plugins) of your interest to `~/.config/nnn/plugins`.
export NNN_PLUGIN_DIR=/absolute/path/to/plugins_dir
Use the pick plugin shortcut to visit the plugin directory and pick a plugin. Repeating the same shortcut cancels the operation and puts you back in the original directory.
Use the pick plugin shortcut to visit the plugin directory and execute a plugin. Repeating the same shortcut cancels the operation and puts you back in the original directory.
If you have an interesting plugin feel free to raise a PR.

8
nnn.1
View file

@ -169,16 +169,10 @@ when dealing with the !, e and p commands respectively. A single combination to
.Pp
\fBNNN_COPIER:\fR system clipboard copier script.
.Bd -literal
NOTE: File paths are copied to the tmp file \fBDIR/.nnncp\fR, where 'DIR' (by priority) is:
\fI$HOME\fR or, \fI$TMPDIR\fR or, \fI/tmp\fR.
NOTE: File paths are copied to the tmp file \fB~/.config/nnn/.selection\fR.
The path is shown in the help and configuration screen.
.Ed
.Pp
\fBNNN_PLUGIN_DIR:\fR \fIabsolute\fR path to plugin directory. Selected plugin is invoked with currently selected file name as argument 1.
.Bd -literal
export NNN_PLUGIN_DIR=/absolute/path/to/plugins_dir
.Ed
.Pp
\fBNNN_NOTE:\fR \fIabsolute\fR path to a note file.
.Bd -literal
export NNN_NOTE='/home/user/.mynotes'

View file

@ -24,7 +24,7 @@
Plugins can access:
- all files in the directory (`nnn` switches to the dir where the plugin is to be run so the dir is `$PWD` for the plugin)
- the currently highlighted file (the file name is passed as the argument to a plugin)
- the current selection (by reading the file .nnncp, see the plugin `ndiff`)
- the current selection (by reading the file `~/.config/nnn/.selection`, see the plugin `ndiff`)
Each script has a _Description_ section which provides more details on what the script does, if applicable.

View file

@ -5,11 +5,13 @@
# Shell: POSIX compliant
# Author: juacq97
SELECTION=~/.config/nnn/.selection
id=$(kdeconnect-cli -a --id-only | awk '{print $1}')
if [ "$(find ~/.nnncp)" ]; then
kdeconnect-cli -d "$id" --share "$(cat ~/.nnncp)"
if [ "$(find "$SELECTION")" ]; then
kdeconnect-cli -d "$id" --share "$(cat "$SELECTION")"
# If you want a system notification, uncomment the next 3 lines.
# notify-send -a "Kdeconnect" "Sending $(cat ~/.nnncp)"
# notify-send -a "Kdeconnect" "Sending $(cat "$SELECTION")"
#else
# notify-send -a "Kdeconnect" "No file selected"
fi

View file

@ -5,4 +5,4 @@
# Shell: POSIX compliant
# Author: Arun Prakash Jana
vimdiff $(cat ~/.nnncp | tr '\0' '\n')
vimdiff $(cat ~/.config/nnn/.selection | tr '\0' '\n')

View file

@ -5,14 +5,16 @@
# Shell: POSIX compliant
# Author: Arun Prakash Jana
SELECTION=~/.config/nnn/.selection
# Linux
cat ~/.nnncp | xargs -0 | xsel -bi
cat "$SELECTION" | xargs -0 | xsel -bi
# macOS
# cat ~/.nnncp | xargs -0 | pbcopy
# cat "$SELECTION" | xargs -0 | pbcopy
# Termux
# cat /data/data/com.termux/files/home/.nnncp | xargs -0 | termux-clipboard-set
# cat "$SELECTION" | xargs -0 | termux-clipboard-set
# Cygwin
# cat ~/.nnncp | xargs -0 | clip
# cat "$SELECTION" | xargs -0 | clip

215
src/nnn.c
View file

@ -1,4 +1,5 @@
/*
* BSD 2-Clause License
*
* Copyright (C) 2014-2016, Lazaros Koromilas <lostd@2f30.org>
@ -125,7 +126,7 @@
#define DESCRIPTOR_LEN 32
#define _ALIGNMENT 0x10 /* 16-byte alignment */
#define _ALIGNMENT_MASK 0xF
#define HOME_LEN_MAX 64
#define TMP_LEN_MAX 64
#define CTX_MAX 4
#define DOT_FILTER_LEN 7
#define ASCII_MAX 128
@ -279,6 +280,9 @@ static char *editor;
static char *pager;
static char *shell;
static char *home;
static char *cfgdir;
static char *g_cppath;
static char *plugindir;
static char *sshfsmnt;
static blkcnt_t ent_blocks;
static blkcnt_t dir_blocks;
@ -302,11 +306,8 @@ static sig_t oldsigtstp;
/* For use in functions which are isolated and don't return the buffer */
static char g_buf[CMD_LEN_MAX] __attribute__ ((aligned));
/* Buffer for file path copy file */
static char g_cppath[PATH_MAX] __attribute__ ((aligned));
/* Buffer to store tmp file path */
static char g_tmpfpath[HOME_LEN_MAX] __attribute__ ((aligned));
/* Buffer to store tmp file path to show selection, file stats and help */
static char g_tmpfpath[TMP_LEN_MAX] __attribute__ ((aligned));
/* Replace-str for xargs on different platforms */
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
@ -386,19 +387,18 @@ static const char * const messages[] = {
#define NNN_CONTEXT_COLORS 2
#define NNN_IDLE_TIMEOUT 3
#define NNN_COPIER 4
#define NNN_PLUGIN_DIR 5
#define NNN_NOTE 6
#define NNN_TMPFILE 7
#define NNN_SSHFS_MNT_ROOT 8
#define NNNLVL 9 /* strings end here */
#define NNN_USE_EDITOR 10 /* flags begin here */
#define NNN_NO_AUTOSELECT 11
#define NNN_RESTRICT_NAV_OPEN 12
#define NNN_RESTRICT_0B 13
#define NNN_OPENER_DETACH 14
#define NNN_TRASH 15
#define NNN_NOTE 5
#define NNN_TMPFILE 6
#define NNN_SSHFS_MNT_ROOT 7
#define NNNLVL 8 /* strings end here */
#define NNN_USE_EDITOR 9 /* flags begin here */
#define NNN_NO_AUTOSELECT 10
#define NNN_RESTRICT_NAV_OPEN 11
#define NNN_RESTRICT_0B 12
#define NNN_OPENER_DETACH 13
#define NNN_TRASH 14
#ifdef __linux__
#define NNN_OPS_PROG 16
#define NNN_OPS_PROG 15
#endif
static const char * const env_cfg[] = {
@ -407,7 +407,6 @@ static const char * const env_cfg[] = {
"NNN_CONTEXT_COLORS",
"NNN_IDLE_TIMEOUT",
"NNN_COPIER",
"NNN_PLUGIN_DIR",
"NNN_NOTE",
"NNN_TMPFILE",
"NNN_SSHFS_MNT_ROOT",
@ -566,7 +565,7 @@ static void printerr(int linenum)
{
exitcurses();
perror(xitoa(linenum));
if (!cfg.picker && g_cppath[0])
if (!cfg.picker && g_cppath)
unlink(g_cppath);
free(pcopybuf);
exit(1);
@ -739,7 +738,7 @@ static char *xbasename(char *path)
/* Writes buflen char(s) from buf to a file */
static void writecp(const char *buf, const size_t buflen)
{
if (cfg.pickraw || !*g_cppath)
if (cfg.pickraw || !g_cppath)
return;
FILE *fp = fopen(g_cppath, "w");
@ -799,7 +798,7 @@ static void showcplist(void)
if (g_tmpfpath[0])
xstrlcpy(g_tmpfpath + g_tmpfplen - 1, messages[STR_TMPFILE],
HOME_LEN_MAX - g_tmpfplen);
TMP_LEN_MAX - g_tmpfplen);
else {
DPRINTF_S(messages[STR_NOHOME_ID]);
return;
@ -821,9 +820,9 @@ static void showcplist(void)
static bool cpsafe(void)
{
/* Fail if copy file path not generated */
if (!g_cppath[0]) {
printmsg("copy file not found");
/* Fail if selection file path not generated */
if (!g_cppath) {
printmsg("selection file not found");
return FALSE;
}
@ -833,9 +832,9 @@ static bool cpsafe(void)
return FALSE;
}
/* Fail if copy file path isn't accessible */
/* Fail if selection file path isn't accessible */
if (access(g_cppath, R_OK | W_OK) == -1) {
printmsg("check copyfile permission");
printmsg("check selection file permission");
return FALSE;
}
@ -2263,7 +2262,7 @@ static bool show_stats(const char *fpath, const char *fname, const struct stat *
if (g_tmpfpath[0])
xstrlcpy(g_tmpfpath + g_tmpfplen - 1, messages[STR_TMPFILE],
HOME_LEN_MAX - g_tmpfplen);
TMP_LEN_MAX - g_tmpfplen);
else
return FALSE;
@ -2513,7 +2512,7 @@ static bool show_help(const char *path)
if (g_tmpfpath[0])
xstrlcpy(g_tmpfpath + g_tmpfplen - 1, messages[STR_TMPFILE],
HOME_LEN_MAX - g_tmpfplen);
TMP_LEN_MAX - g_tmpfplen);
else {
printmsg(messages[STR_NOHOME_ID]);
return FALSE;
@ -2557,8 +2556,8 @@ static bool show_help(const char *path)
}
}
if (g_cppath[0])
dprintf(fd, "COPY FILE: %s\n", g_cppath);
if (g_cppath)
dprintf(fd, "SELECTION FILE: %s\n", g_cppath);
dprintf(fd, "\nv%s\n%s\n", VERSION, GENERAL_INFO);
close(fd);
@ -2713,7 +2712,7 @@ static int dentfill(char *path, struct entry **dents)
dentp = *dents + n;
/* Copy file name */
/* Selection file name */
dentp->name = (char *)((size_t)pnamebuf + off);
dentp->nlen = xstrlcpy(dentp->name, namep, NAME_MAX + 1);
off += dentp->nlen;
@ -2965,7 +2964,7 @@ static void browse(char *ipath)
enum action sel;
bool dir_changed = FALSE;
struct stat sb;
char *path, *lastdir, *lastname, *dir, *tmp, *pluginpath = getenv(env_cfg[NNN_PLUGIN_DIR]);
char *path, *lastdir, *lastname, *dir, *tmp;
atexit(dentfree);
@ -3110,9 +3109,9 @@ nochange:
/* Handle plugin selection mode */
if (cfg.runplugin) {
if (!pluginpath || (cfg.runctx != cfg.curctx)
if (!plugindir || (cfg.runctx != cfg.curctx)
/* Must be in plugin directory to select plugin */
|| (strcmp(path, pluginpath) != 0))
|| (strcmp(path, plugindir) != 0))
continue;
mkpath(path, dents[cur].name, newpath);
@ -3534,7 +3533,7 @@ nochange:
if (cfg.copymode) {
/*
* Clear the tmp copy path file on first copy.
* Clear the selection file on first copy.
*
* This ensures that when the first file path is
* copied into memory (but not written to tmp file
@ -3843,8 +3842,9 @@ nochange:
/* Check if file creation failed */
if (r == -1) {
close(fd);
printwarn();
presel = MSGWAIT;
close(fd);
goto nochange;
}
}
@ -3867,12 +3867,12 @@ nochange:
spawn(shell, NULL, NULL, path, F_CLI);
break;
case SEL_PLUGIN:
if (!pluginpath) {
printwait("set NNN_PLUGIN_DIR", &presel);
if (!plugindir) {
printwait("plugins dir missing", &presel);
goto nochange;
}
if (stat(pluginpath, &sb) == -1) {
if (stat(plugindir, &sb) == -1) {
printwarn();
presel = MSGWAIT;
goto nochange;
@ -3888,7 +3888,7 @@ nochange:
* If toggled, and still in the plugin dir,
* switch to original directory
*/
if (strcmp(path, pluginpath) == 0) {
if (strcmp(path, plugindir) == 0) {
xstrlcpy(path, rundir, PATH_MAX);
xstrlcpy(lastname, runfile, NAME_MAX);
rundir[0] = runfile[0] = '\0';
@ -3899,11 +3899,11 @@ nochange:
}
/* Check if directory is accessible */
if (!xdiraccess(pluginpath))
if (!xdiraccess(plugindir))
goto nochange;
xstrlcpy(rundir, path, PATH_MAX);
xstrlcpy(path, pluginpath, PATH_MAX);
xstrlcpy(path, plugindir, PATH_MAX);
if (ndents)
xstrlcpy(runfile, dents[cur].name, NAME_MAX);
cfg.runctx = cfg.curctx;
@ -4082,6 +4082,103 @@ static void usage(void)
"v%s\n%s\n", __func__, VERSION, GENERAL_INFO);
}
static bool create_dir(const char *path)
{
if (!xdiraccess(path)) {
if (errno != ENOENT)
return FALSE;
if (mkdir(path, 0777) == -1)
return FALSE;
}
return TRUE;
}
static bool setup_config(void)
{
size_t r, len;
/* Set up configuration file paths */
len = strlen(home) + 1 + 20; /* add length of "/.config/nnn/plugins" */
cfgdir = (char *)malloc(len);
plugindir = (char *)malloc(len);
if (!cfgdir || !plugindir) {
xerror();
return FALSE;
}
r = xstrlcpy(cfgdir, home, len);
xstrlcpy(cfgdir + r - 1, "/.config/nnn", len - r);
DPRINTF_S(cfgdir);
/* TODO: remove in next release */
if (access(cfgdir, F_OK) == -1)
fprintf(stdout, "WARNING: selection file is ~/.config/nnn/.selection (see CHANGELOG)\n");
/* Create ~/.config/nnn */
if (!create_dir(cfgdir)) {
xerror();
return FALSE;
}
xstrlcpy(cfgdir + r + 12 - 1, "/plugins", 9);
DPRINTF_S(cfgdir);
xstrlcpy(plugindir, cfgdir, len);
DPRINTF_S(plugindir);
/* Create ~/.config/nnn/plugins */
if (!create_dir(cfgdir)) {
xerror();
return FALSE;
}
/* Reset to config path */
cfgdir[r + 11] = '\0';
DPRINTF_S(cfgdir);
/* Set selection file path */
if (!cfg.picker) {
/* Length of "/.config/nnn/.selection" */
g_cppath = (char *)malloc(len + 3);
r = xstrlcpy(g_cppath, cfgdir, len + 3);
xstrlcpy(g_cppath + r - 1, "/.selection", 12);
DPRINTF_S(g_cppath);
}
return TRUE;
}
static bool set_tmp_path()
{
char *path;
if (xdiraccess("/tmp"))
g_tmpfplen = xstrlcpy(g_tmpfpath, "/tmp", TMP_LEN_MAX);
else {
path = getenv("TMPDIR");
if (path)
g_tmpfplen = xstrlcpy(g_tmpfpath, path, TMP_LEN_MAX);
else {
fprintf(stderr, "set TMPDIR\n");
return FALSE;
}
}
return TRUE;
}
static void cleanup(void)
{
free(g_cppath);
free(plugindir);
free(cfgdir);
#ifdef DBGMODE
disabledbg();
#endif
}
int main(int argc, char *argv[])
{
char cwd[PATH_MAX] __attribute__ ((aligned));
@ -4121,7 +4218,7 @@ int main(int argc, char *argv[])
else {
/* copier used as tmp var */
copier = realpath(optarg, g_cppath);
if (!g_cppath[0]) {
if (!g_cppath) {
xerror();
return 1;
}
@ -4170,12 +4267,20 @@ int main(int argc, char *argv[])
#ifdef DBGMODE
enabledbg();
atexit(disabledbg);
#endif
atexit(cleanup);
home = getenv("HOME");
if (!home) {
fprintf(stderr, "set HOME\n");
return 1;
}
DPRINTF_S(home);
if (!setup_config())
return 1;
/* Get custom opener, if set */
opener = xgetenv(env_cfg[NNN_OPENER], utils[OPENER]);
if (getenv(env_cfg[NNN_OPENER_DETACH]))
@ -4276,21 +4381,9 @@ int main(int argc, char *argv[])
if (getenv(env_cfg[NNN_TRASH]))
cfg.trash = 1;
/* Prefix for other temporary ops */
if (home)
g_tmpfplen = xstrlcpy(g_tmpfpath, home, HOME_LEN_MAX);
else if (xdiraccess("/tmp"))
g_tmpfplen = xstrlcpy(g_tmpfpath, "/tmp", HOME_LEN_MAX);
else {
copier = getenv("TMPDIR");
if (copier)
g_tmpfplen = xstrlcpy(g_tmpfpath, copier, HOME_LEN_MAX);
}
if (!cfg.picker && g_tmpfplen) {
xstrlcpy(g_cppath, g_tmpfpath, PATH_MAX);
xstrlcpy(g_cppath + g_tmpfplen - 1, "/.nnncp", PATH_MAX - g_tmpfplen);
}
/* Prefix for temporary files */
if (!set_tmp_path())
return 1;
/* Get SSHFS mountpoint */
sshfsmnt = getenv("NNN_SSHFS_MNT_ROOT");
@ -4365,7 +4458,7 @@ int main(int argc, char *argv[])
if (opt != (int)(copybufpos))
xerror();
}
} else if (!cfg.picker && g_cppath[0])
} else if (!cfg.picker && g_cppath)
unlink(g_cppath);
/* Free the copy buffer */