mirror of
https://github.com/jarun/nnn.git
synced 2024-11-24 11:51:27 +00:00
List of files as input (#443)
* Start implementation on show list of files as input * Remove tmp dir on exit, set global flag * Bug fixes and improvements * Follow symlinks when in list paths mode * Fix bugs * Add flag check * Add message for invalid paths and style fixes * Change the message * Addressing review comments * Handle errno being set * Fix CI break * Decreased startup memory usage and removed loops * Fix CI break
This commit is contained in:
parent
099a9af289
commit
2390ac7b74
|
@ -77,6 +77,7 @@ It runs smoothly on the Pi, [Termux](https://www.youtube.com/watch?v=AbaauM7gUJw
|
||||||
- FreeDesktop compliant trash (needs trash-cli)
|
- FreeDesktop compliant trash (needs trash-cli)
|
||||||
- Cross-dir file/all/range selection
|
- Cross-dir file/all/range selection
|
||||||
- Batch renamer (feature-limited) for selection or dir
|
- Batch renamer (feature-limited) for selection or dir
|
||||||
|
- Display a list of files from stdin
|
||||||
- Copy (as), move (as), delete, archive, link selection
|
- Copy (as), move (as), delete, archive, link selection
|
||||||
- Dir updates, notification on cp, mv, rm completion
|
- Dir updates, notification on cp, mv, rm completion
|
||||||
- Copy file paths to system clipboard on select
|
- Copy file paths to system clipboard on select
|
||||||
|
|
443
src/nnn.c
443
src/nnn.c
|
@ -342,6 +342,8 @@ static char *home;
|
||||||
static char *initpath;
|
static char *initpath;
|
||||||
static char *cfgdir;
|
static char *cfgdir;
|
||||||
static char *g_selpath;
|
static char *g_selpath;
|
||||||
|
static char *g_listpath;
|
||||||
|
static char *g_prefixpath;
|
||||||
static char *plugindir;
|
static char *plugindir;
|
||||||
static char *sessiondir;
|
static char *sessiondir;
|
||||||
static char *pnamebuf, *pselbuf;
|
static char *pnamebuf, *pselbuf;
|
||||||
|
@ -353,6 +355,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 const uint _WSHIFT = (LONG_SIZE == 8) ? 3 : 2;
|
||||||
#ifdef PCRE
|
#ifdef PCRE
|
||||||
static pcre *archive_pcre;
|
static pcre *archive_pcre;
|
||||||
#else
|
#else
|
||||||
|
@ -386,6 +389,7 @@ static char g_pipepath[TMP_LEN_MAX] __attribute__ ((aligned));
|
||||||
#define STATE_RANGESEL 0x4
|
#define STATE_RANGESEL 0x4
|
||||||
#define STATE_MOVE_OP 0x8
|
#define STATE_MOVE_OP 0x8
|
||||||
#define STATE_AUTONEXT 0x10
|
#define STATE_AUTONEXT 0x10
|
||||||
|
#define STATE_MSG 0x20
|
||||||
|
|
||||||
static uchar g_states;
|
static uchar g_states;
|
||||||
|
|
||||||
|
@ -496,8 +500,9 @@ static char * const utils[] = {
|
||||||
#define MSG_INVALID_REG 36
|
#define MSG_INVALID_REG 36
|
||||||
#define MSG_ORDER 37
|
#define MSG_ORDER 37
|
||||||
#define MSG_LAZY 38
|
#define MSG_LAZY 38
|
||||||
|
#define MSG_IGNORED 39
|
||||||
#ifndef DIR_LIMITED_SELECTION
|
#ifndef DIR_LIMITED_SELECTION
|
||||||
#define MSG_DIR_CHANGED 39 /* Must be the last entry */
|
#define MSG_DIR_CHANGED 40 /* Must be the last entry */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char * const messages[] = {
|
static const char * const messages[] = {
|
||||||
|
@ -540,6 +545,7 @@ static const char * const messages[] = {
|
||||||
"invalid regex",
|
"invalid regex",
|
||||||
"toggle 'a'u / 'd'u / 'e'xtn / 'r'everse / 's'ize / 't'ime / 'v'ersion?",
|
"toggle 'a'u / 'd'u / 'e'xtn / 'r'everse / 's'ize / 't'ime / 'v'ersion?",
|
||||||
"unmount failed! try lazy?",
|
"unmount failed! try lazy?",
|
||||||
|
"ignoring invalid paths...",
|
||||||
#ifndef DIR_LIMITED_SELECTION
|
#ifndef DIR_LIMITED_SELECTION
|
||||||
"dir changed, range sel off", /* Must be the last entry */
|
"dir changed, range sel off", /* Must be the last entry */
|
||||||
#endif
|
#endif
|
||||||
|
@ -596,6 +602,7 @@ static const char cpmvrenamecmd[] = "sed 's|^\\([^#][^/]\\?.*\\)$|%s/\\1|;s|^#\\
|
||||||
static const char batchrenamecmd[] = "paste -d'\n' %s %s | sed 'N; /^\\(.*\\)\\n\\1$/!p;d' | "
|
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";
|
"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)$";
|
static const char archive_regex[] = "\\.(bz|bz2|gz|tar|taz|tbz|tbz2|tgz|z|zip)$";
|
||||||
|
static const char replaceprefixcmd[] = "sed -i 's|^%s\\(.*\\)$|%s\\1|' %s";
|
||||||
|
|
||||||
/* Event handling */
|
/* Event handling */
|
||||||
#ifdef LINUX_INOTIFY
|
#ifdef LINUX_INOTIFY
|
||||||
|
@ -859,7 +866,6 @@ static size_t xstrlcpy(char *dest, const char *src, size_t n)
|
||||||
|
|
||||||
ulong *s, *d;
|
ulong *s, *d;
|
||||||
size_t len = strlen(src) + 1, blocks;
|
size_t len = strlen(src) + 1, blocks;
|
||||||
const uint _WSHIFT = (LONG_SIZE == 8) ? 3 : 2;
|
|
||||||
|
|
||||||
if (n > len)
|
if (n > len)
|
||||||
n = len;
|
n = len;
|
||||||
|
@ -869,7 +875,7 @@ static size_t xstrlcpy(char *dest, const char *src, size_t n)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To enable -O3 ensure src and dest are 16-byte aligned
|
* To enable -O3 ensure src and dest are 16-byte aligned
|
||||||
* More info: http://www.felixcloutier.com/x86/MOVDQA.html
|
* More info: https://www.felixcloutier.com/x86/MOVDQA:VMOVDQA32:VMOVDQA64
|
||||||
*/
|
*/
|
||||||
if ((n >= LONG_SIZE) && (((ulong)src & _ALIGNMENT_MASK) == 0 &&
|
if ((n >= LONG_SIZE) && (((ulong)src & _ALIGNMENT_MASK) == 0 &&
|
||||||
((ulong)dest & _ALIGNMENT_MASK) == 0)) {
|
((ulong)dest & _ALIGNMENT_MASK) == 0)) {
|
||||||
|
@ -940,6 +946,80 @@ static void *xmemrchr(uchar *s, uchar ch, size_t n)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *common_prefix(const char *s, char *prefix)
|
||||||
|
{
|
||||||
|
if (!s || !prefix)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Only accept non-empty strings */
|
||||||
|
if (*s == '\0' || *prefix == '\0')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ulong *x, *y;
|
||||||
|
size_t i = 0, j = 0, blocks = 0;
|
||||||
|
size_t len_s = strlen(s), len_prefix = strlen(prefix);
|
||||||
|
size_t len = MIN(len_s, len_prefix);
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To enable -O3 ensure s and prefix are 16-byte aligned
|
||||||
|
* More info: https://www.felixcloutier.com/x86/MOVDQA:VMOVDQA32:VMOVDQA64
|
||||||
|
*/
|
||||||
|
if ((len >= LONG_SIZE) && (((ulong)s & _ALIGNMENT_MASK) == 0
|
||||||
|
&& ((ulong)prefix & _ALIGNMENT_MASK) == 0)) {
|
||||||
|
x = (ulong *)s;
|
||||||
|
y = (ulong *)prefix;
|
||||||
|
blocks = len >> _WSHIFT;
|
||||||
|
len &= LONG_SIZE - 1;
|
||||||
|
|
||||||
|
while (i < blocks && !(*x ^ *y))
|
||||||
|
++x, ++y, ++i;
|
||||||
|
|
||||||
|
/* This should always return */
|
||||||
|
if (i < blocks) {
|
||||||
|
i *= LONG_SIZE;
|
||||||
|
for (; j < LONG_SIZE; ++j)
|
||||||
|
if (s[i + j] != prefix[i + j]) {
|
||||||
|
tmp = xmemrchr((uchar *)prefix, '/', i + j);
|
||||||
|
if (!tmp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*(tmp != prefix ? tmp : tmp + 1) = '\0';
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = blocks * LONG_SIZE;
|
||||||
|
while (j < len && s[i + j] == prefix[i + j])
|
||||||
|
++j;
|
||||||
|
|
||||||
|
if (j < len) {
|
||||||
|
tmp = xmemrchr((uchar *)prefix, '/', i + j);
|
||||||
|
if (!tmp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*(tmp != prefix ? tmp : tmp + 1) = '\0';
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* complete match but lenghts might differ */
|
||||||
|
if (len_s < len_prefix || (len_s > len_prefix && s[len_prefix] != '/')) {
|
||||||
|
tmp = xmemrchr((uchar *)prefix, '/', len);
|
||||||
|
if (!tmp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*(tmp != prefix ? tmp : tmp + 1) = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
static char *xbasename(char *path)
|
static char *xbasename(char *path)
|
||||||
{
|
{
|
||||||
char *base = xmemrchr((uchar *)path, '/', strlen(path)); // NOLINT
|
char *base = xmemrchr((uchar *)path, '/', strlen(path)); // NOLINT
|
||||||
|
@ -947,6 +1027,55 @@ static char *xbasename(char *path)
|
||||||
return base ? base + 1 : path;
|
return base ? base + 1 : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *xrealpath(const char *path, const char *cwd)
|
||||||
|
{
|
||||||
|
if (!path || !cwd)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t dst_size = 0, src_size = strlen(path), cwd_size = strlen(cwd);
|
||||||
|
const char *src, *next;
|
||||||
|
char *dst;
|
||||||
|
char *resolved_path = malloc(src_size + (*path == '/' ? 0 : cwd_size) + 1);
|
||||||
|
if (!resolved_path)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Turn relative paths into absolute */
|
||||||
|
if (path[0] != '/')
|
||||||
|
dst_size = xstrlcpy(resolved_path, cwd, cwd_size + 1) - 1;
|
||||||
|
else
|
||||||
|
resolved_path[0] = '\0';
|
||||||
|
|
||||||
|
src = path;
|
||||||
|
dst = resolved_path + dst_size;
|
||||||
|
for (next = NULL; next != path + src_size;) {
|
||||||
|
next = strchr(src, '/');
|
||||||
|
if (!next)
|
||||||
|
next = path + src_size;
|
||||||
|
|
||||||
|
if (next - src == 2 && src[0] == '.' && src[1] == '.') {
|
||||||
|
if (dst - resolved_path) {
|
||||||
|
dst = xmemrchr((uchar *)resolved_path, '/', dst-resolved_path);
|
||||||
|
*dst = '\0';
|
||||||
|
}
|
||||||
|
} else if (next - src == 1 && src[0] == '.') {
|
||||||
|
/* NOP */
|
||||||
|
} else if (next - src) {
|
||||||
|
*(dst++) = '/';
|
||||||
|
xstrlcpy(dst, src, next - src + 1);
|
||||||
|
dst += next - src;
|
||||||
|
}
|
||||||
|
|
||||||
|
src = next + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*resolved_path == '\0') {
|
||||||
|
resolved_path[0] = '/';
|
||||||
|
resolved_path[1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved_path;
|
||||||
|
}
|
||||||
|
|
||||||
static int create_tmp_file(void)
|
static int create_tmp_file(void)
|
||||||
{
|
{
|
||||||
xstrlcpy(g_tmpfpath + g_tmpfplen - 1, messages[STR_TMPFILE], TMP_LEN_MAX - g_tmpfplen);
|
xstrlcpy(g_tmpfpath + g_tmpfplen - 1, messages[STR_TMPFILE], TMP_LEN_MAX - g_tmpfplen);
|
||||||
|
@ -1086,8 +1215,66 @@ static void updateselbuf(const char *path, char *newpath)
|
||||||
/* Finish selection procedure before an operation */
|
/* Finish selection procedure before an operation */
|
||||||
static void endselection(void)
|
static void endselection(void)
|
||||||
{
|
{
|
||||||
|
int fd;
|
||||||
|
ssize_t count;
|
||||||
|
char buf[sizeof(replaceprefixcmd) + PATH_MAX + (TMP_LEN_MAX << 1)];
|
||||||
|
|
||||||
if (cfg.selmode)
|
if (cfg.selmode)
|
||||||
cfg.selmode = 0;
|
cfg.selmode = 0;
|
||||||
|
|
||||||
|
if (!g_listpath || !selbufpos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fd = create_tmp_file();
|
||||||
|
if (fd == -1) {
|
||||||
|
DPRINTF_S("couldn't create tmp file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
seltofile(fd, NULL);
|
||||||
|
if (close(fd)) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), replaceprefixcmd, g_listpath, g_prefixpath, g_tmpfpath);
|
||||||
|
spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI);
|
||||||
|
|
||||||
|
fd = open(g_tmpfpath, O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
if (unlink(g_tmpfpath)) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = read(fd, pselbuf, selbuflen);
|
||||||
|
if (count < 0) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
if (close(fd) || unlink(g_tmpfpath)) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close(fd) || unlink(g_tmpfpath)) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selbufpos = count;
|
||||||
|
pselbuf[--count] = '\0';
|
||||||
|
for (--count; count > 0; --count)
|
||||||
|
if (pselbuf[count] == '\n' && pselbuf[count+1] == '/')
|
||||||
|
pselbuf[count] = '\0';
|
||||||
|
|
||||||
|
writesel(pselbuf, selbufpos - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clearselection(void)
|
static void clearselection(void)
|
||||||
|
@ -1117,7 +1304,10 @@ static int editselection(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
seltofile(fd, NULL);
|
seltofile(fd, NULL);
|
||||||
close(fd);
|
if (close(fd)) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Save the last modification time */
|
/* Save the last modification time */
|
||||||
if (stat(g_tmpfpath, &sb)) {
|
if (stat(g_tmpfpath, &sb)) {
|
||||||
|
@ -1151,16 +1341,24 @@ static int editselection(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
count = read(fd, pselbuf, selbuflen);
|
count = read(fd, pselbuf, selbuflen);
|
||||||
close(fd);
|
if (count < 0) {
|
||||||
unlink(g_tmpfpath);
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
if (!count) {
|
if (close(fd) || unlink(g_tmpfpath)) {
|
||||||
ret = 1;
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
}
|
||||||
goto emptyedit;
|
goto emptyedit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count < 0) {
|
if (close(fd) || unlink(g_tmpfpath)) {
|
||||||
DPRINTF_S("error reading tmp file");
|
DPRINTF_S(strerror(errno));
|
||||||
|
printwarn(NULL);
|
||||||
|
goto emptyedit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count) {
|
||||||
|
ret = 1;
|
||||||
goto emptyedit;
|
goto emptyedit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4791,6 +4989,13 @@ nochange:
|
||||||
return _FAILURE;
|
return _FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Display a one-time message */
|
||||||
|
if (g_states & STATE_MSG) {
|
||||||
|
g_states &= ~STATE_MSG;
|
||||||
|
printwait(messages[MSG_IGNORED], &presel);
|
||||||
|
goto nochange;
|
||||||
|
}
|
||||||
|
|
||||||
sel = nextsel(presel);
|
sel = nextsel(presel);
|
||||||
if (presel)
|
if (presel)
|
||||||
presel = 0;
|
presel = 0;
|
||||||
|
@ -5239,7 +5444,9 @@ nochange:
|
||||||
case SEL_STATS: // fallthrough
|
case SEL_STATS: // fallthrough
|
||||||
case SEL_CHMODX:
|
case SEL_CHMODX:
|
||||||
if (ndents) {
|
if (ndents) {
|
||||||
mkpath(path, dents[cur].name, newpath);
|
tmp = (g_listpath && xstrcmp(path, g_listpath) == 0) ? g_prefixpath : path;
|
||||||
|
mkpath(tmp, dents[cur].name, newpath);
|
||||||
|
|
||||||
if (lstat(newpath, &sb) == -1
|
if (lstat(newpath, &sb) == -1
|
||||||
|| (sel == SEL_STATS && !show_stats(newpath, &sb))
|
|| (sel == SEL_STATS && !show_stats(newpath, &sb))
|
||||||
|| (sel == SEL_CHMODX && !xchmod(newpath, sb.st_mode))) {
|
|| (sel == SEL_CHMODX && !xchmod(newpath, sb.st_mode))) {
|
||||||
|
@ -5421,7 +5628,8 @@ nochange:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r == 'c') {
|
if (r == 'c') {
|
||||||
mkpath(path, dents[cur].name, newpath);
|
tmp = (g_listpath && xstrcmp(path, g_listpath) == 0) ? g_prefixpath : path;
|
||||||
|
mkpath(tmp, dents[cur].name, newpath);
|
||||||
xrm(newpath);
|
xrm(newpath);
|
||||||
|
|
||||||
if (cur && access(newpath, F_OK) == -1) {
|
if (cur && access(newpath, F_OK) == -1) {
|
||||||
|
@ -5859,6 +6067,175 @@ nochange:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *make_tmp_tree(char **paths, ssize_t entries, const char *prefix)
|
||||||
|
{
|
||||||
|
/* tmpdir holds the full path */
|
||||||
|
/* tmp holds the path without the tmp dir prefix */
|
||||||
|
int err, ignore = 0;
|
||||||
|
struct stat sb;
|
||||||
|
char *slash, *tmp;
|
||||||
|
ssize_t i, len = strlen(prefix);
|
||||||
|
char *tmpdir = malloc(sizeof(char) * (PATH_MAX + TMP_LEN_MAX));
|
||||||
|
|
||||||
|
if (!tmpdir) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = tmpdir + g_tmpfplen - 1;
|
||||||
|
xstrlcpy(tmpdir, g_tmpfpath, g_tmpfplen);
|
||||||
|
xstrlcpy(tmp, "/nnnXXXXXX", 11);
|
||||||
|
|
||||||
|
if (!mkdtemp(tmpdir)) {
|
||||||
|
free(tmpdir);
|
||||||
|
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_listpath = tmpdir;
|
||||||
|
|
||||||
|
for (i = 0; i < entries; ++i) {
|
||||||
|
err = stat(paths[i], &sb);
|
||||||
|
if (err && errno == ENOENT) {
|
||||||
|
ignore = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
xstrlcpy(tmp + 10, paths[i] + len, strlen(paths[i]) + 1);
|
||||||
|
|
||||||
|
slash = xmemrchr((uchar *)tmp, '/', strlen(paths[i]) + 11);
|
||||||
|
*slash = '\0';
|
||||||
|
|
||||||
|
xmktree(tmpdir, TRUE);
|
||||||
|
|
||||||
|
*slash = '/';
|
||||||
|
if (symlink(paths[i], tmpdir)) {
|
||||||
|
DPRINTF_S(paths[i]);
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignore)
|
||||||
|
g_states |= STATE_MSG;
|
||||||
|
|
||||||
|
tmp[10] = '\0';
|
||||||
|
return tmpdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *load_input()
|
||||||
|
{
|
||||||
|
/* 512 KiB chunk size */
|
||||||
|
ssize_t i, chunk_count = 1, chunk = 512 * 1024, entries = 0;
|
||||||
|
char *input = malloc(sizeof(char) * chunk), *tmpdir;
|
||||||
|
char cwd[PATH_MAX], *next, *prev, *tmp;
|
||||||
|
size_t offsets[1 << 16];
|
||||||
|
char *paths[1 << 16];
|
||||||
|
ssize_t input_read, total_read = 0, off = 0;
|
||||||
|
|
||||||
|
if (!input) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (chunk_count < 512) {
|
||||||
|
input_read = read(STDIN_FILENO, input, chunk);
|
||||||
|
if (input_read < 0) {
|
||||||
|
DPRINTF_S(strerror(errno));
|
||||||
|
goto malloc_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_read += input_read;
|
||||||
|
++chunk_count;
|
||||||
|
|
||||||
|
while (off < total_read) {
|
||||||
|
next = memchr(input + off, '\0', total_read - off) + 1;
|
||||||
|
if (next == (void *)1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (next - input == off + 1) {
|
||||||
|
off = next - input;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries == (1 << 16))
|
||||||
|
goto malloc_1;
|
||||||
|
|
||||||
|
offsets[entries++] = off;
|
||||||
|
off = next - input;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_read < chunk)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (chunk_count == 512 || !(input = xrealloc(input, (chunk_count + 1) * chunk)))
|
||||||
|
goto malloc_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (off != total_read) {
|
||||||
|
if (entries == (1 << 16))
|
||||||
|
goto malloc_1;
|
||||||
|
|
||||||
|
offsets[entries++] = off;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entries)
|
||||||
|
goto malloc_1;
|
||||||
|
|
||||||
|
input[total_read] = '\0';
|
||||||
|
|
||||||
|
for (i = 0; i < entries; ++i)
|
||||||
|
paths[i] = input + offsets[i];
|
||||||
|
|
||||||
|
/* prev used as tmp variable */
|
||||||
|
prev = getcwd(cwd, PATH_MAX);
|
||||||
|
if (!prev)
|
||||||
|
goto malloc_1;
|
||||||
|
|
||||||
|
for (i = 0; i < entries; ++i) {
|
||||||
|
if (!(paths[i] = xrealpath(paths[i], cwd))) {
|
||||||
|
for (--i; i >= 0; --i)
|
||||||
|
free(paths[i]);
|
||||||
|
goto malloc_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_prefixpath = malloc(sizeof(char) * PATH_MAX);
|
||||||
|
if (!g_prefixpath)
|
||||||
|
goto malloc_2;
|
||||||
|
|
||||||
|
xstrlcpy(g_prefixpath, paths[0], strlen(paths[0]) + 1);
|
||||||
|
for (i = 1; i < entries; ++i) {
|
||||||
|
if (!common_prefix(paths[i], g_prefixpath))
|
||||||
|
goto malloc_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries == 1) {
|
||||||
|
tmp = xmemrchr((uchar *)g_prefixpath, '/', strlen(g_prefixpath));
|
||||||
|
if (!tmp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*(tmp != g_prefixpath ? tmp : tmp + 1) = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir = make_tmp_tree(paths, entries, g_prefixpath);
|
||||||
|
|
||||||
|
if (tmpdir) {
|
||||||
|
for (i = entries - 1; i >= 0; --i)
|
||||||
|
free(paths[i]);
|
||||||
|
free(input);
|
||||||
|
|
||||||
|
return tmpdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
malloc_2:
|
||||||
|
for (i = entries - 1; i >= 0; --i)
|
||||||
|
free(paths[i]);
|
||||||
|
malloc_1:
|
||||||
|
free(input);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void check_key_collision(void)
|
static void check_key_collision(void)
|
||||||
{
|
{
|
||||||
int key;
|
int key;
|
||||||
|
@ -6034,6 +6411,7 @@ static void cleanup(void)
|
||||||
free(initpath);
|
free(initpath);
|
||||||
free(bmstr);
|
free(bmstr);
|
||||||
free(pluginstr);
|
free(pluginstr);
|
||||||
|
free(g_prefixpath);
|
||||||
|
|
||||||
unlink(g_pipepath);
|
unlink(g_pipepath);
|
||||||
|
|
||||||
|
@ -6146,17 +6524,36 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Confirm we are in a terminal */
|
|
||||||
if (!cfg.picker && !(isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)))
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
#ifdef DBGMODE
|
#ifdef DBGMODE
|
||||||
enabledbg();
|
enabledbg();
|
||||||
DPRINTF_S(VERSION);
|
DPRINTF_S(VERSION);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Prefix for temporary files */
|
||||||
|
if (!set_tmp_path())
|
||||||
|
return _FAILURE;
|
||||||
|
|
||||||
atexit(cleanup);
|
atexit(cleanup);
|
||||||
|
|
||||||
|
if (!cfg.picker) {
|
||||||
|
/* Confirm we are in a terminal */
|
||||||
|
if (!isatty(STDOUT_FILENO))
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
/* Now we are in path list mode */
|
||||||
|
if (!isatty(STDIN_FILENO)) {
|
||||||
|
|
||||||
|
/* This is the same as g_listpath */
|
||||||
|
initpath = load_input();
|
||||||
|
if (!initpath)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
/* We return to tty */
|
||||||
|
dup2(STDOUT_FILENO, STDIN_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
home = getenv("HOME");
|
home = getenv("HOME");
|
||||||
if (!home) {
|
if (!home) {
|
||||||
fprintf(stderr, "set HOME\n");
|
fprintf(stderr, "set HOME\n");
|
||||||
|
@ -6183,7 +6580,9 @@ int main(int argc, char *argv[])
|
||||||
return _FAILURE;
|
return _FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg) { /* Open a bookmark directly */
|
if (initpath) {
|
||||||
|
/* NOP */
|
||||||
|
} else if (arg) { /* Open a bookmark directly */
|
||||||
if (!arg[1]) /* Bookmarks keys are single char */
|
if (!arg[1]) /* Bookmarks keys are single char */
|
||||||
initpath = get_kv_val(bookmark, NULL, *arg, BM_MAX, TRUE);
|
initpath = get_kv_val(bookmark, NULL, *arg, BM_MAX, TRUE);
|
||||||
|
|
||||||
|
@ -6285,10 +6684,6 @@ int main(int argc, char *argv[])
|
||||||
if (xgetenv_set(env_cfg[NNN_TRASH]))
|
if (xgetenv_set(env_cfg[NNN_TRASH]))
|
||||||
cfg.trash = 1;
|
cfg.trash = 1;
|
||||||
|
|
||||||
/* Prefix for temporary files */
|
|
||||||
if (!set_tmp_path())
|
|
||||||
return _FAILURE;
|
|
||||||
|
|
||||||
/* Ignore/handle certain signals */
|
/* Ignore/handle certain signals */
|
||||||
struct sigaction act = {.sa_handler = sigint_handler};
|
struct sigaction act = {.sa_handler = sigint_handler};
|
||||||
|
|
||||||
|
@ -6327,6 +6722,10 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
opt = browse(initpath, session);
|
opt = browse(initpath, session);
|
||||||
mousemask(mask, NULL);
|
mousemask(mask, NULL);
|
||||||
|
|
||||||
|
if (g_listpath)
|
||||||
|
spawn("rm -rf", initpath, NULL, NULL, F_SILENT);
|
||||||
|
|
||||||
exitcurses();
|
exitcurses();
|
||||||
|
|
||||||
#ifndef NORL
|
#ifndef NORL
|
||||||
|
|
Loading…
Reference in a new issue