"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/nnn.c" between
nnn-v4.1.1.tar.gz and nnn-v4.2.tar.gz

About: nnn is a full-featured terminal file manager.

nnn.c  (nnn-v4.1.1):nnn.c  (nnn-v4.2)
skipping to change at line 31 skipping to change at line 31
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AR E * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AR E
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#if defined(__linux__) || defined(MINGW) || defined(__MINGW32__) || defined(__MI #if defined(__linux__) || defined(MINGW) || defined(__MINGW32__) \
NGW64__) || defined(__CYGWIN__) || defined(__MINGW64__) || defined(__CYGWIN__)
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#if defined(__arm__) || defined(__i386__) #if defined(__arm__) || defined(__i386__)
#define _FILE_OFFSET_BITS 64 /* Support large files on 32-bit */ #define _FILE_OFFSET_BITS 64 /* Support large files on 32-bit */
#endif #endif
#if defined(__linux__) #if defined(__linux__)
#include <sys/inotify.h> #include <sys/inotify.h>
#define LINUX_INOTIFY #define LINUX_INOTIFY
#endif #endif
skipping to change at line 66 skipping to change at line 67
#define HAIKU_NM #define HAIKU_NM
#else #else
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#endif #endif
#include <sys/wait.h> #include <sys/wait.h>
#ifdef __linux__ /* Fix failure due to mvaddnwstr() */ #ifdef __linux__ /* Fix failure due to mvaddnwstr() */
#ifndef NCURSES_WIDECHAR #ifndef NCURSES_WIDECHAR
#define NCURSES_WIDECHAR 1 #define NCURSES_WIDECHAR 1
#endif #endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || def #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
ined(__APPLE__) || defined(__sun) || defined(__APPLE__) || defined(__sun)
#ifndef _XOPEN_SOURCE_EXTENDED #ifndef _XOPEN_SOURCE_EXTENDED
#define _XOPEN_SOURCE_EXTENDED #define _XOPEN_SOURCE_EXTENDED
#endif #endif
#endif #endif
#ifndef __USE_XOPEN /* Fix wcswidth() failure, ncursesw/curses.h includes whcar. h on Ubuntu 14.04 */ #ifndef __USE_XOPEN /* Fix wcswidth() failure, ncursesw/curses.h includes whcar. h on Ubuntu 14.04 */
#define __USE_XOPEN #define __USE_XOPEN
#endif #endif
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
skipping to change at line 131 skipping to change at line 133
#if defined(ICONS) || defined(NERD) #if defined(ICONS) || defined(NERD)
#include "icons.h" #include "icons.h"
#define ICONS_ENABLED #define ICONS_ENABLED
#endif #endif
#ifdef TOURBIN_QSORT #ifdef TOURBIN_QSORT
#include "qsort.h" #include "qsort.h"
#endif #endif
/* Macro definitions */ /* Macro definitions */
#define VERSION "4.1.1" #define VERSION "4.2"
#define GENERAL_INFO "BSD 2-Clause\nhttps://github.com/jarun/nnn" #define GENERAL_INFO "BSD 2-Clause\nhttps://github.com/jarun/nnn"
#ifndef NOSSN #ifndef NOSSN
#define SESSIONS_VERSION 1 #define SESSIONS_VERSION 1
#endif #endif
#ifndef S_BLKSIZE #ifndef S_BLKSIZE
#define S_BLKSIZE 512 /* S_BLKSIZE is missing on Android NDK (Termux) */ #define S_BLKSIZE 512 /* S_BLKSIZE is missing on Android NDK (Termux) */
#endif #endif
skipping to change at line 154 skipping to change at line 156
* flexible array on Illumos. Use somewhat accommodating fallback values. * flexible array on Illumos. Use somewhat accommodating fallback values.
*/ */
#ifndef NAME_MAX #ifndef NAME_MAX
#define NAME_MAX 255 #define NAME_MAX 255
#endif #endif
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
#define _ABSSUB(N, M) (((N) <= (M)) ? ((M) - (N)) : ((N) - (M))) #define _ABSSUB(N, M) (((N) <= (M)) ? ((M) - (N)) : ((N) - (M)))
#define DOUBLECLICK_INTERVAL_NS (400000000) #define ELEMENTS(x) (sizeof(x) / sizeof(*(x)))
#define XDELAY_INTERVAL_MS (350000) /* 350 ms delay */
#define ELEMENTS(x) (sizeof(x) / sizeof(*(x)))
#undef MIN #undef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MIN(x, y) ((x) < (y) ? (x) : (y))
#undef MAX #undef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y))
#define ISODD(x) ((x) & 1) #define ISODD(x) ((x) & 1)
#define ISBLANK(x) ((x) == ' ' || (x) == '\t') #define ISBLANK(x) ((x) == ' ' || (x) == '\t')
#define TOUPPER(ch) (((ch) >= 'a' && (ch) <= 'z') ? ((ch) - 'a' + 'A') : (ch)) #define TOUPPER(ch) (((ch) >= 'a' && (ch) <= 'z') ? ((ch) - 'a' + 'A') : (ch
#define CMD_LEN_MAX (PATH_MAX + ((NAME_MAX + 1) << 1)) ))
#define READLINE_MAX 256 #define CMD_LEN_MAX (PATH_MAX + ((NAME_MAX + 1) << 1))
#define FILTER '/' #define ALIGN_UP(x, A) ((((x) + (A) - 1) / (A)) * (A))
#define RFILTER '\\' #define READLINE_MAX 256
#define CASE ':' #define FILTER '/'
#define MSGWAIT '$' #define RFILTER '\\'
#define SELECT ' ' #define CASE ':'
#define REGEX_MAX 48 #define MSGWAIT '$'
#define ENTRY_INCR 64 /* Number of dir 'entry' structures to allocate per shot * #define SELECT ' '
/ #define PROMPT ">>> "
#define NAMEBUF_INCR 0x800 /* 64 dir entries at once, avg. 32 chars per file nam #define REGEX_MAX 48
e = 64*32B = 2KB */ #define ENTRY_INCR 64 /* Number of dir 'entry' structures to allocate per s
#define DESCRIPTOR_LEN 32 hot */
#define _ALIGNMENT 0x10 /* 16-byte alignment */ #define NAMEBUF_INCR 0x800 /* 64 dir entries at once, avg. 32 chars per file
name = 64*32B = 2KB */
#define DESCRIPTOR_LEN 32
#define _ALIGNMENT 0x10 /* 16-byte alignment */
#define _ALIGNMENT_MASK 0xF #define _ALIGNMENT_MASK 0xF
#define TMP_LEN_MAX 64 #define TMP_LEN_MAX 64
#define DOT_FILTER_LEN 7 #define DOT_FILTER_LEN 7
#define ASCII_MAX 128 #define ASCII_MAX 128
#define EXEC_ARGS_MAX 10 #define EXEC_ARGS_MAX 10
#define LIST_FILES_MAX (1 << 16) #define LIST_FILES_MAX (1 << 16)
#define SCROLLOFF 3 #define SCROLLOFF 3
/* Time intervals */
#define DBLCLK_INTERVAL_NS (400000000)
#define XDELAY_INTERVAL_MS (350000) /* 350 ms delay */
#ifndef CTX8 #ifndef CTX8
#define CTX_MAX 4 #define CTX_MAX 4
#else #else
#define CTX_MAX 8 #define CTX_MAX 8
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
#define SED "gsed" #define SED "gsed"
#else #else
#define SED "sed" #define SED "sed"
#endif #endif
#define MIN_DISPLAY_COLS (CTX_MAX * 2) /* Large selection threshold */
#ifndef LARGESEL
#define LARGESEL 1000
#endif
#define MIN_DISPLAY_COL (CTX_MAX * 2)
#define ARCHIVE_CMD_LEN 16 #define ARCHIVE_CMD_LEN 16
#define BLK_SHIFT_512 9 #define BLK_SHIFT_512 9
/* Detect hardlinks in du */ /* Detect hardlinks in du */
#define HASH_BITS (0xFFFFFF) #define HASH_BITS (0xFFFFFF)
#define HASH_OCTETS (HASH_BITS >> 6) /* 2^6 = 64 */ #define HASH_OCTETS (HASH_BITS >> 6) /* 2^6 = 64 */
/* Entry flags */ /* Entry flags */
#define DIR_OR_LINK_TO_DIR 0x01 #define DIR_OR_DIRLNK 0x01
#define HARD_LINK 0x02 #define HARD_LINK 0x02
#define SYM_ORPHAN 0x04 #define SYM_ORPHAN 0x04
#define FILE_MISSING 0x08 #define FILE_MISSING 0x08
#define FILE_SELECTED 0x10 #define FILE_SELECTED 0x10
#define FILE_SCANNED 0x20
/* Macros to define process spawn behaviour as flags */ /* Macros to define process spawn behaviour as flags */
#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_N ORMAL) */ #define F_CONFIRM 0x10 /* run command - show results before exit (must have F_N ORMAL) */
#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_TTY 0x100 /* Force stdout to go to tty if redirected to a non-tty
*/
#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)
/* Version compare macros */ /* Version compare macros */
/* /*
* states: S_N: normal, S_I: comparing integral part, S_F: comparing * states: S_N: normal, S_I: comparing integral part, S_F: comparing
* fractional parts, S_Z: idem but with leading Zeroes only * fractional parts, S_Z: idem but with leading Zeroes only
*/ */
#define S_N 0x0 #define S_N 0x0
#define S_I 0x3 #define S_I 0x3
#define S_F 0x6 #define S_F 0x6
#define S_Z 0x9 #define S_Z 0x9
/* result_type: VCMP: return diff; VLEN: compare using len_diff/diff */ /* result_type: VCMP: return diff; VLEN: compare using len_diff/diff */
#define VCMP 2 #define VCMP 2
#define VLEN 3 #define VLEN 3
/* Volume info */ /* Volume info */
#define FREE 0 #define FREE 0
#define CAPACITY 1 #define CAPACITY 1
/* TYPE DEFINITIONS */ /* TYPE DEFINITIONS */
typedef unsigned int uint_t; typedef unsigned int uint_t;
typedef unsigned char uchar_t; typedef unsigned char uchar_t;
typedef unsigned short ushort_t; typedef unsigned short ushort_t;
typedef unsigned long long ulong_t; typedef unsigned long long ullong_t;
/* STRUCTURES */ /* STRUCTURES */
/* Directory entry */ /* Directory entry */
typedef struct entry { typedef struct entry {
char *name; /* 8 bytes */ char *name; /* 8 bytes */
time_t sec; /* 8 bytes */ time_t sec; /* 8 bytes */
uint_t nsec; /* 4 bytes (enough to store nanosec) */ uint_t nsec; /* 4 bytes (enough to store nanosec) */
mode_t mode; /* 4 bytes */ mode_t mode; /* 4 bytes */
off_t size; /* 8 bytes */ off_t size; /* 8 bytes */
struct { struct {
ulong_t blocks : 40; /* 5 bytes (enough for 512 TiB in 512B block ullong_t blocks : 40; /* 5 bytes (enough for 512 TiB in 512B bloc
s allocated) */ ks allocated) */
ulong_t nlen : 16; /* 2 bytes (length of file name) */ ullong_t nlen : 16; /* 2 bytes (length of file name) */
ulong_t flags : 8; /* 1 byte (flags specific to the file) */ ullong_t flags : 8; /* 1 byte (flags specific to the file) */
}; };
#ifndef NOUG #ifndef NOUG
uid_t uid; /* 4 bytes */ uid_t uid; /* 4 bytes */
gid_t gid; /* 4 bytes */ gid_t gid; /* 4 bytes */
#endif #endif
} *pEntry; } *pEntry;
/* Selection marker */
typedef struct {
char *startpos;
size_t len;
} selmark;
/* Key-value pairs from env */ /* Key-value pairs from env */
typedef struct { typedef struct {
int key; int key;
int off; int off;
} kv; } kv;
typedef struct { typedef struct {
#ifdef PCRE #ifdef PCRE
const pcre *pcrex; const pcre *pcrex;
#else #else
skipping to change at line 318 skipping to change at line 338
uint_t useeditor : 1; /* Use VISUAL to open text files */ uint_t useeditor : 1; /* Use VISUAL to open text files */
uint_t reserved3 : 3; uint_t reserved3 : 3;
uint_t regex : 1; /* Use regex filters */ uint_t regex : 1; /* Use regex filters */
uint_t x11 : 1; /* Copy to system clipboard, show notis, xterm ti tle */ uint_t x11 : 1; /* Copy to system clipboard, show notis, xterm ti tle */
uint_t timetype : 2; /* Time sort type (0: access, 1: change, 2: modif ication) */ uint_t timetype : 2; /* Time sort type (0: access, 1: change, 2: modif ication) */
uint_t cliopener : 1; /* All-CLI app opener */ uint_t cliopener : 1; /* All-CLI app opener */
uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */ uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */
uint_t rollover : 1; /* Roll over at edges */ uint_t rollover : 1; /* Roll over at edges */
} settings; } settings;
/* Non-persistent program-internal states */ /* Non-persistent program-internal states (alphabeical order) */
typedef struct { typedef struct {
uint_t pluginit : 1; /* Plugin framework initialized */ uint_t autofifo : 1; /* Auto-create NNN_FIFO */
uint_t interrupt : 1; /* Program received an interrupt */
uint_t rangesel : 1; /* Range selection on */
uint_t move : 1; /* Move operation */
uint_t autonext : 1; /* Auto-proceed on open */ uint_t autonext : 1; /* Auto-proceed on open */
uint_t fortune : 1; /* Show fortune messages in help */ uint_t dircolor : 1; /* Current status of dir color */
uint_t trash : 2; /* Use trash to delete files 1: trash-cli, 2: gio uint_t dirctx : 1; /* Show dirs in context color */
trash */ uint_t duinit : 1; /* Initialize disk usage */
uint_t fifomode : 1; /* FIFO notify mode: 0: preview, 1: explore */
uint_t forcequit : 1; /* Do not prompt on quit */ uint_t forcequit : 1; /* Do not prompt on quit */
uint_t autofifo : 1; /* Auto-create NNN_FIFO */
uint_t initfile : 1; /* Positional arg is a file */ uint_t initfile : 1; /* Positional arg is a file */
uint_t dircolor : 1; /* Current status of dir color */ uint_t interrupt : 1; /* Program received an interrupt */
uint_t picker : 1; /* Write selection to user-specified file */ uint_t move : 1; /* Move operation */
uint_t oldcolor : 1; /* Use older colorscheme */
uint_t picked : 1; /* Plugin has picked files */ uint_t picked : 1; /* Plugin has picked files */
uint_t runplugin : 1; /* Choose plugin mode */ uint_t picker : 1; /* Write selection to user-specified file */
uint_t pluginit : 1; /* Plugin framework initialized */
uint_t prstssn : 1; /* Persistent session */
uint_t rangesel : 1; /* Range selection on */
uint_t runctx : 3; /* The context in which plugin is to be run */ uint_t runctx : 3; /* The context in which plugin is to be run */
uint_t runplugin : 1; /* Choose plugin mode */
uint_t selmode : 1; /* Set when selecting files */ uint_t selmode : 1; /* Set when selecting files */
uint_t oldcolor : 1; /* Use older colorscheme */
uint_t stayonsel : 1; /* Disable auto-proceed on select */ uint_t stayonsel : 1; /* Disable auto-proceed on select */
uint_t dirctx : 1; /* Show dirs in context color */ uint_t trash : 2; /* Use trash to delete files 1: trash-cli, 2: gio trash */
uint_t uidgid : 1; /* Show owner and group info */ uint_t uidgid : 1; /* Show owner and group info */
uint_t prstssn : 1; /* Persistent session */
uint_t duinit : 1; /* Initialize disk usage */
uint_t reserved : 7; /* Adjust when adding/removing a field */ uint_t reserved : 7; /* Adjust when adding/removing a field */
} runstate; } runstate;
/* Contexts or workspaces */ /* Contexts or workspaces */
typedef struct { typedef struct {
char c_path[PATH_MAX]; /* Current dir */ char c_path[PATH_MAX]; /* Current dir */
char c_last[PATH_MAX]; /* Last visited dir */ char c_last[PATH_MAX]; /* Last visited dir */
char c_name[NAME_MAX + 1]; /* Current file name */ char c_name[NAME_MAX + 1]; /* Current file name */
char c_fltr[REGEX_MAX]; /* Current filter */ char c_fltr[REGEX_MAX]; /* Current filter */
settings c_cfg; /* Current configuration */ settings c_cfg; /* Current configuration */
uint_t color; /* Color code for directories */ uint_t color; /* Color code for directories */
} context; } context;
#ifndef NOSSN #ifndef NOSSN
typedef struct { typedef struct {
size_t ver; size_t ver;
size_t pathln[CTX_MAX]; size_t pathln[CTX_MAX];
size_t lastln[CTX_MAX]; size_t lastln[CTX_MAX];
size_t nameln[CTX_MAX]; size_t nameln[CTX_MAX];
size_t fltrln[CTX_MAX]; size_t fltrln[CTX_MAX];
} session_header_t; } session_header_t;
skipping to change at line 405 skipping to change at line 425
1, /* rollover */ 1, /* rollover */
}; };
static context g_ctx[CTX_MAX] __attribute__ ((aligned)); static context g_ctx[CTX_MAX] __attribute__ ((aligned));
static int ndents, cur, last, curscroll, last_curscroll, total_dents = ENTRY_INC R, scroll_lines = 1; static int ndents, cur, last, curscroll, last_curscroll, total_dents = ENTRY_INC R, scroll_lines = 1;
static int nselected; static int nselected;
#ifndef NOFIFO #ifndef NOFIFO
static int fifofd = -1; static int fifofd = -1;
#endif #endif
static uint_t idletimeout, selbufpos, lastappendpos, selbuflen; static uint_t idletimeout, selbufpos, selbuflen;
static ushort_t xlines, xcols; static ushort_t xlines, xcols;
static ushort_t idle; static ushort_t idle;
static uchar_t maxbm, maxplug; static uchar_t maxbm, maxplug;
static char *bmstr; static char *bmstr;
static char *pluginstr; static char *pluginstr;
static char *opener; static char *opener;
static char *editor; static char *editor;
static char *enveditor; static char *enveditor;
static char *pager; static char *pager;
static char *shell; static char *shell;
static char *home; static char *home;
static char *initpath; static char *initpath;
static char *cfgpath; static char *cfgpath;
static char *selpath; static char *selpath;
static char *listpath; static char *listpath;
static char *listroot; static char *listroot;
static char *plgpath; static char *plgpath;
static char *pnamebuf, *pselbuf; static char *pnamebuf, *pselbuf, *findselpos;
static char *mark; static char *mark;
#ifndef NOFIFO #ifndef NOFIFO
static char *fifopath; static char *fifopath;
#endif #endif
static unsigned long long *ihashbmp; static char *lastcmd;
static ullong_t *ihashbmp;
static struct entry *pdents; static struct entry *pdents;
static blkcnt_t dir_blocks; static blkcnt_t dir_blocks;
static kv *bookmark; static kv *bookmark;
static kv *plug; static kv *plug;
static uchar_t tmpfplen, homelen; static uchar_t tmpfplen, homelen;
static uchar_t blk_shift = BLK_SHIFT_512; static uchar_t blk_shift = BLK_SHIFT_512;
#ifndef NOMOUSE #ifndef NOMOUSE
static int middle_click_key; static int middle_click_key;
#endif #endif
#ifdef PCRE #ifdef PCRE
static pcre *archive_pcre; static pcre *archive_pcre;
#else #else
static regex_t archive_re; static regex_t archive_re;
#endif #endif
/* pthread related */ /* pthread related */
#define NUM_DU_THREADS (4) /* Can use sysconf(_SC_NPROCESSORS_ONLN) */ #define NUM_DU_THREADS (4) /* Can use sysconf(_SC_NPROCESSORS_ONLN) */
#define DU_TEST (((node->fts_info & FTS_F) && \ #define DU_TEST (((node->fts_info & FTS_F) && \
(sb->st_nlink <= 1 || test_set_bit((uint_t)sb->st_ino))) || node->fts_info & FTS_DP) (sb->st_nlink <= 1 || test_set_bit((uint_t)sb->st_ino))) || node- >fts_info & FTS_DP)
static int threadbmp = -1; /* Has 1 in the bit position for idle threads */ static int threadbmp = -1; /* Has 1 in the bit position for idle threads */
static volatile int active_threads = 0; static volatile int active_threads;
static pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t hardlink_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t hardlink_mutex = PTHREAD_MUTEX_INITIALIZER;
static ulong_t *core_files; static ullong_t *core_files;
static blkcnt_t *core_blocks; static blkcnt_t *core_blocks;
static ulong_t num_files; static ullong_t num_files;
typedef struct { typedef struct {
char path[PATH_MAX]; char path[PATH_MAX];
int entnum; int entnum;
ushort_t core; ushort_t core;
bool mntpoint; bool mntpoint;
} thread_data; } thread_data;
static thread_data *core_data; static thread_data *core_data;
/* Retain old signal handlers */ /* Retain old signal handlers */
static struct sigaction oldsighup; static struct sigaction oldsighup;
static struct sigaction oldsigtstp; static struct sigaction oldsigtstp;
static struct sigaction oldsigwinch;
/* For use in functions which are isolated and don't return the buffer */ /* For use in functions which are isolated and don't return the buffer */
static char g_buf[CMD_LEN_MAX] __attribute__ ((aligned)); static char g_buf[CMD_LEN_MAX] __attribute__ ((aligned));
/* For use as a scratch buffer in selection manipulation */
static char g_sel[PATH_MAX] __attribute__ ((aligned));
/* Buffer to store tmp file path to show selection, file stats and help */ /* Buffer to store tmp file path to show selection, file stats and help */
static char g_tmpfpath[TMP_LEN_MAX] __attribute__ ((aligned)); static char g_tmpfpath[TMP_LEN_MAX] __attribute__ ((aligned));
/* Buffer to store plugins control pipe location */ /* Buffer to store plugins control pipe location */
static char g_pipepath[TMP_LEN_MAX] __attribute__ ((aligned)); static char g_pipepath[TMP_LEN_MAX] __attribute__ ((aligned));
/* Non-persistent runtime states */ /* Non-persistent runtime states */
static runstate g_state; static runstate g_state;
/* Options to identify file MIME */ /* Options to identify file MIME */
#if defined(__APPLE__) #if defined(__APPLE__)
#define FILE_MIME_OPTS "-bIL" #define FILE_MIME_OPTS "-bIL"
#elif !defined(__sun) /* no MIME option for 'file' */ #elif !defined(__sun) /* no MIME option for 'file' */
#define FILE_MIME_OPTS "-biL" #define FILE_MIME_OPTS "-biL"
#endif #endif
/* Macros for utilities */ /* Macros for utilities */
#define UTIL_OPENER 0 #define UTIL_OPENER 0
#define UTIL_ATOOL 1 #define UTIL_ATOOL 1
#define UTIL_BSDTAR 2 #define UTIL_BSDTAR 2
#define UTIL_UNZIP 3 #define UTIL_UNZIP 3
#define UTIL_TAR 4 #define UTIL_TAR 4
#define UTIL_LOCKER 5 #define UTIL_LOCKER 5
#define UTIL_LAUNCH 6 #define UTIL_LAUNCH 6
#define UTIL_SH_EXEC 7 #define UTIL_SH_EXEC 7
#define UTIL_BASH 8 #define UTIL_BASH 8
#define UTIL_SSHFS 9 #define UTIL_SSHFS 9
#define UTIL_RCLONE 10 #define UTIL_RCLONE 10
#define UTIL_VI 11 #define UTIL_VI 11
#define UTIL_LESS 12 #define UTIL_LESS 12
#define UTIL_SH 13 #define UTIL_SH 13
#define UTIL_FZF 14 #define UTIL_FZF 14
#define UTIL_NTFY 15 #define UTIL_NTFY 15
#define UTIL_CBCP 16 #define UTIL_CBCP 16
#define UTIL_NMV 17 #define UTIL_NMV 17
#define UTIL_TRASH_CLI 18
#define UTIL_GIO_TRASH 19
#define UTIL_RM_RF 20
/* Utilities to open files, run actions */ /* Utilities to open files, run actions */
static char * const utils[] = { static char * const utils[] = {
#ifdef __APPLE__ #ifdef __APPLE__
"/usr/bin/open", "/usr/bin/open",
#elif defined __CYGWIN__ #elif defined __CYGWIN__
"cygstart", "cygstart",
#elif defined __HAIKU__ #elif defined __HAIKU__
"open", "open",
#else #else
skipping to change at line 545 skipping to change at line 573
"bash", "bash",
"sshfs", "sshfs",
"rclone", "rclone",
"vi", "vi",
"less", "less",
"sh", "sh",
"fzf", "fzf",
".ntfy", ".ntfy",
".cbcp", ".cbcp",
".nmv", ".nmv",
"trash-put",
"gio trash",
"rm -rf",
}; };
/* Common strings */ /* Common strings */
#define MSG_ZERO 0 /* Unused */ #define MSG_ZERO 0 /* Unused */
#define MSG_0_ENTRIES 1 #define MSG_0_ENTRIES 1
#define STR_TMPFILE 2 #define STR_TMPFILE 2
#define MSG_0_SELECTED 3 #define MSG_0_SELECTED 3
#define MSG_CANCEL 4 #define MSG_CANCEL 4
#define MSG_FAILED 5 #define MSG_FAILED 5
#define MSG_SSN_NAME 6 #define MSG_SSN_NAME 6
#define MSG_CP_MV_AS 7 #define MSG_CP_MV_AS 7
#define MSG_CUR_SEL_OPTS 8 #define MSG_CUR_SEL_OPTS 8
#define MSG_FORCE_RM 9 #define MSG_FORCE_RM 9
#define MSG_LIMIT 10 #define MSG_LIMIT 10
#define MSG_NEW_OPTS 11 #define MSG_NEW_OPTS 11
#define MSG_CLI_MODE 12 #define MSG_CLI_MODE 12
#define MSG_OVERWRITE 13 #define MSG_OVERWRITE 13
#define MSG_SSN_OPTS 14 #define MSG_SSN_OPTS 14
#define MSG_QUIT_ALL 15 #define MSG_QUIT_ALL 15
#define MSG_HOSTNAME 16 #define MSG_HOSTNAME 16
#define MSG_ARCHIVE_NAME 17 #define MSG_ARCHIVE_NAME 17
#define MSG_OPEN_WITH 18 #define MSG_OPEN_WITH 18
#define MSG_NEW_PATH 19 #define MSG_NEW_PATH 19
#define MSG_LINK_PREFIX 20 #define MSG_LINK_PREFIX 20
#define MSG_COPY_NAME 21 #define MSG_COPY_NAME 21
#define MSG_RETURN 22 #define MSG_ENTER 22
#define MSG_SEL_MISSING 23 #define MSG_SEL_MISSING 23
#define MSG_ACCESS 24 #define MSG_ACCESS 24
#define MSG_EMPTY_FILE 25 #define MSG_EMPTY_FILE 25
#define MSG_UNSUPPORTED 26 #define MSG_UNSUPPORTED 26
#define MSG_NOT_SET 27 #define MSG_NOT_SET 27
#define MSG_EXISTS 28 #define MSG_EXISTS 28
#define MSG_FEW_COLUMNS 29 #define MSG_FEW_COLUMNS 29
#define MSG_REMOTE_OPTS 30 #define MSG_REMOTE_OPTS 30
#define MSG_RCLONE_DELAY 31 #define MSG_RCLONE_DELAY 31
#define MSG_APP_NAME 32 #define MSG_APP_NAME 32
#define MSG_ARCHIVE_OPTS 33 #define MSG_ARCHIVE_OPTS 33
#define MSG_KEYS 34 #define MSG_KEYS 34
#define MSG_INVALID_REG 35 #define MSG_INVALID_REG 35
#define MSG_ORDER 36 #define MSG_ORDER 36
#define MSG_LAZY 37 #define MSG_LAZY 37
#define MSG_FIRST 38 #define MSG_FIRST 38
#define MSG_RM_TMP 39 #define MSG_RM_TMP 39
#define MSG_INVALID_KEY 40 #define MSG_INVALID_KEY 40
#define MSG_NOCHANGE 41 #define MSG_NOCHANGE 41
#ifndef DIR_LIMITED_SELECTION #define MSG_DIR_CHANGED 42
#define MSG_DIR_CHANGED 42 /* Must be the last entry */
#endif
static const char * const messages[] = { static const char * const messages[] = {
"", "",
"0 entries", "0 entries",
"/.nnnXXXXXX", "/.nnnXXXXXX",
"0 selected", "0 selected",
"cancelled", "cancelled",
"failed!", "failed!",
"session name: ", "session name: ",
"'c'p / 'm'v as?", "'c'p / 'm'v as?",
"'c'urrent / 's'el?", "'c'urrent / 's'el?",
"rm -rf %s file%s? [Esc cancels]", "%s %s file%s? [Esc cancels]",
"limit exceeded", "limit exceeded",
"'f'ile / 'd'ir / 's'ym / 'h'ard?", "'f'ile / 'd'ir / 's'ym / 'h'ard?",
"'c'li / 'g'ui?", "'c'li / 'g'ui?",
"overwrite?", "overwrite?",
"'s'ave / 'l'oad / 'r'estore?", "'s'ave / 'l'oad / 'r'estore?",
"Quit all contexts?", "Quit all contexts?",
"remote name (- for hovered): ", "remote name (- for hovered): ",
"archive [path/]name: ", "archive [path/]name: ",
"open with: ", "open with: ",
"[path/]name: ", "[path/]name: ",
skipping to change at line 637 skipping to change at line 666
"app name: ", "app name: ",
"'o'pen / e'x'tract / 'l's / 'm'nt?", "'o'pen / e'x'tract / 'l's / 'm'nt?",
"keys:", "keys:",
"invalid regex", "invalid regex",
"'a'u / 'd'u / 'e'xt / 'r'ev / 's'z / 't'm / 'v'er / 'c'lr / '^T'?", "'a'u / 'd'u / 'e'xt / 'r'ev / 's'z / 't'm / 'v'er / 'c'lr / '^T'?",
"unmount failed! try lazy?", "unmount failed! try lazy?",
"first file (\')/char?", "first file (\')/char?",
"remove tmp file?", "remove tmp file?",
"invalid key", "invalid key",
"unchanged", "unchanged",
#ifndef DIR_LIMITED_SELECTION "dir changed, range sel off",
"dir changed, range sel off", /* Must be the last entry */
#endif
}; };
/* Supported configuration environment variables */ /* Supported configuration environment variables */
#define NNN_OPTS 0 #define NNN_OPTS 0
#define NNN_BMS 1 #define NNN_BMS 1
#define NNN_PLUG 2 #define NNN_PLUG 2
#define NNN_OPENER 3 #define NNN_OPENER 3
#define NNN_COLORS 4 #define NNN_COLORS 4
#define NNNLVL 5 #define NNN_FCOLORS 5
#define NNN_PIPE 6 #define NNNLVL 6
#define NNN_MCLICK 7 #define NNN_PIPE 7
#define NNN_SEL 8 #define NNN_MCLICK 8
#define NNN_ARCHIVE 9 /* strings end here */ #define NNN_SEL 9
#define NNN_TRASH 10 /* flags begin here */ #define NNN_ARCHIVE 10
#define NNN_HELP 11 /* strings end here */
#define NNN_TRASH 12 /* flags begin here */
static const char * const env_cfg[] = { static const char * const env_cfg[] = {
"NNN_OPTS", "NNN_OPTS",
"NNN_BMS", "NNN_BMS",
"NNN_PLUG", "NNN_PLUG",
"NNN_OPENER", "NNN_OPENER",
"NNN_COLORS", "NNN_COLORS",
"NNN_FCOLORS",
"NNNLVL", "NNNLVL",
"NNN_PIPE", "NNN_PIPE",
"NNN_MCLICK", "NNN_MCLICK",
"NNN_SEL", "NNN_SEL",
"NNN_ARCHIVE", "NNN_ARCHIVE",
"NNN_HELP",
"NNN_TRASH", "NNN_TRASH",
}; };
/* Required environment variables */ /* Required environment variables */
#define ENV_SHELL 0 #define ENV_SHELL 0
#define ENV_VISUAL 1 #define ENV_VISUAL 1
#define ENV_EDITOR 2 #define ENV_EDITOR 2
#define ENV_PAGER 3 #define ENV_PAGER 3
#define ENV_NCUR 4 #define ENV_NCUR 4
static const char * const envs[] = { static const char * const envs[] = {
"SHELL", "SHELL",
"VISUAL", "VISUAL",
"EDITOR", "EDITOR",
"PAGER", "PAGER",
"nnn", "nnn",
}; };
/* Time type used */ /* Time type used */
#define T_ACCESS 0 #define T_ACCESS 0
#define T_CHANGE 1 #define T_CHANGE 1
#define T_MOD 2 #define T_MOD 2
#ifdef __linux__ #ifdef __linux__
static char cp[] = "cp -iRp"; static char cp[] = "cp -iRp";
static char mv[] = "mv -i"; static char mv[] = "mv -i";
#else #else
static char cp[] = "cp -iRp"; static char cp[] = "cp -iRp";
static char mv[] = "mv -i"; static char mv[] = "mv -i";
#endif #endif
/* Archive commands */ /* Archive commands */
const char *archive_cmd[] = {"atool -a", "bsdtar -acvf", "zip -r", "tar -acvf"}; static const char * const archive_cmd[] = {"atool -a", "bsdtar -acvf", "zip -r", "tar -acvf"};
/* Tokens used for path creation */ /* Tokens used for path creation */
#define TOK_SSN 0 #define TOK_SSN 0
#define TOK_MNT 1 #define TOK_MNT 1
#define TOK_PLG 2 #define TOK_PLG 2
static const char * const toks[] = { static const char * const toks[] = {
"sessions", "sessions",
"mounts", "mounts",
"plugins", /* must be the last entry */ "plugins", /* must be the last entry */
skipping to change at line 727 skipping to change at line 758
static const char * const patterns[] = { static const char * const patterns[] = {
SED" -i 's|^\\(\\(.*/\\)\\(.*\\)$\\)|#\\1\\n\\3|' %s", SED" -i 's|^\\(\\(.*/\\)\\(.*\\)$\\)|#\\1\\n\\3|' %s",
SED" 's|^\\([^#/][^/]\\?.*\\)$|%s/\\1|;s|^#\\(/.*\\)$|\\1|' " SED" 's|^\\([^#/][^/]\\?.*\\)$|%s/\\1|;s|^#\\(/.*\\)$|\\1|' "
"%s | tr '\\n' '\\0' | xargs -0 -n2 sh -c '%s \"$0\" \"$@\" < /de v/tty'", "%s | tr '\\n' '\\0' | xargs -0 -n2 sh -c '%s \"$0\" \"$@\" < /de v/tty'",
"\\.(bz|bz2|gz|tar|taz|tbz|tbz2|tgz|z|zip)$", "\\.(bz|bz2|gz|tar|taz|tbz|tbz2|tgz|z|zip)$",
SED" -i 's|^%s\\(.*\\)$|%s\\1|' %s", SED" -i 's|^%s\\(.*\\)$|%s\\1|' %s",
}; };
/* Colors */ /* Colors */
#define C_BLK (CTX_MAX + 1) /* Block device: DarkSeaGreen1 */ #define C_BLK (CTX_MAX + 1) /* Block device: DarkSeaGreen1 */
#define C_CHR (C_BLK + 1) /* Character device: Yellow1 */ #define C_CHR (C_BLK + 1) /* Character device: Yellow1 */
#define C_DIR (C_CHR + 1) /* Directory: DeepSkyBlue1 */ #define C_DIR (C_CHR + 1) /* Directory: DeepSkyBlue1 */
#define C_EXE (C_DIR + 1) /* Executable file: Green1 */ #define C_EXE (C_DIR + 1) /* Executable file: Green1 */
#define C_FIL (C_EXE + 1) /* Regular file: Normal */ #define C_FIL (C_EXE + 1) /* Regular file: Normal */
#define C_HRD (C_FIL + 1) /* Hard link: Plum4 */ #define C_HRD (C_FIL + 1) /* Hard link: Plum4 */
#define C_LNK (C_HRD + 1) /* Symbolic link: Cyan1 */ #define C_LNK (C_HRD + 1) /* Symbolic link: Cyan1 */
#define C_MIS (C_LNK + 1) /* Missing file OR file details: Grey62 */ #define C_MIS (C_LNK + 1) /* Missing file OR file details: Grey62 */
#define C_ORP (C_MIS + 1) /* Orphaned symlink: DeepPink1 */ #define C_ORP (C_MIS + 1) /* Orphaned symlink: DeepPink1 */
#define C_PIP (C_ORP + 1) /* Named pipe (FIFO): Orange1 */ #define C_PIP (C_ORP + 1) /* Named pipe (FIFO): Orange1 */
#define C_SOC (C_PIP + 1) /* Socket: MediumOrchid1 */ #define C_SOC (C_PIP + 1) /* Socket: MediumOrchid1 */
#define C_UND (C_SOC + 1) /* Unknown OR 0B regular/exe file: Red1 */ #define C_UND (C_SOC + 1) /* Unknown OR 0B regular/exe file: Red1 */
#ifdef ICONS_ENABLED #ifdef ICONS_ENABLED
/* 0-9, A-Z, OTHER = 36. */ /* 0-9, A-Z, OTHER = 36. */
static ushort_t icon_positions[37]; static ushort_t icon_positions[37];
#endif #endif
static char gcolors[] = "c1e2272e006033f7c6d6abc4"; static char gcolors[] = "c1e2272e006033f7c6d6abc4";
static uint_t fcolors[C_UND + 1] = {0}; static uint_t fcolors[C_UND + 1] = {0};
/* Event handling */ /* Event handling */
skipping to change at line 794 skipping to change at line 825
#define xerror() perror(xitoa(__LINE__)) #define xerror() perror(xitoa(__LINE__))
#ifdef TOURBIN_QSORT #ifdef TOURBIN_QSORT
#define ENTLESS(i, j) (entrycmpfn(pdents + (i), pdents + (j)) < 0) #define ENTLESS(i, j) (entrycmpfn(pdents + (i), pdents + (j)) < 0)
#define ENTSWAP(i, j) (swap_ent((i), (j))) #define ENTSWAP(i, j) (swap_ent((i), (j)))
#define ENTSORT(pdents, ndents, entrycmpfn) QSORT((ndents), ENTLESS, ENTSWAP) #define ENTSORT(pdents, ndents, entrycmpfn) QSORT((ndents), ENTLESS, ENTSWAP)
#else #else
#define ENTSORT(pdents, ndents, entrycmpfn) qsort((pdents), (ndents), sizeof(*(p dents)), (entrycmpfn)) #define ENTSORT(pdents, ndents, entrycmpfn) qsort((pdents), (ndents), sizeof(*(p dents)), (entrycmpfn))
#endif #endif
#ifdef __GNUC__
#define UNUSED(x) UNUSED_##x __attribute__((__unused__))
#else
#define UNUSED(x) UNUSED_##x
#endif /* __GNUC__ */
/* Forward declarations */ /* Forward declarations */
static void redraw(char *path); static void redraw(char *path);
static int spawn(char *file, char *arg1, char *arg2, char *arg3, uchar_t flag); static int spawn(char *file, char *arg1, char *arg2, char *arg3, ushort_t flag);
static void move_cursor(int target, int ignore_scrolloff); static void move_cursor(int target, int ignore_scrolloff);
static char *load_input(int fd, const char *path); static char *load_input(int fd, const char *path);
static int set_sort_flags(int r); static int set_sort_flags(int r);
#ifndef NOFIFO #ifndef NOFIFO
static void notify_fifo(bool force); static void notify_fifo(bool force);
#endif #endif
/* Functions */ /* Functions */
static void sigint_handler(int UNUSED(sig)) static void sigint_handler(int sig)
{ {
(void) sig;
g_state.interrupt = 1; g_state.interrupt = 1;
} }
static void clean_exit_sighandler(int UNUSED(sig)) static void clean_exit_sighandler(int sig)
{ {
(void) sig;
exitcurses(); exitcurses();
/* This triggers cleanup() thanks to atexit() */ /* This triggers cleanup() thanks to atexit() */
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
static char *xitoa(uint_t val) static char *xitoa(uint_t val)
{ {
static char dst[32] = {'\0'}; static char dst[32] = {'\0'};
static const char digits[201] = static const char digits[201] =
"0001020304050607080910111213141516171819" "0001020304050607080910111213141516171819"
skipping to change at line 879 skipping to change at line 906
} }
/* /*
* Source: https://elixir.bootlin.com/linux/latest/source/arch/alpha/include/asm /bitops.h * Source: https://elixir.bootlin.com/linux/latest/source/arch/alpha/include/asm /bitops.h
*/ */
static bool test_set_bit(uint_t nr) static bool test_set_bit(uint_t nr)
{ {
nr &= HASH_BITS; nr &= HASH_BITS;
pthread_mutex_lock(&hardlink_mutex); pthread_mutex_lock(&hardlink_mutex);
ulong_t *m = ((ulong_t *)ihashbmp) + (nr >> 6); ullong_t *m = ((ullong_t *)ihashbmp) + (nr >> 6);
if (*m & (1 << (nr & 63))) { if (*m & (1 << (nr & 63))) {
pthread_mutex_unlock(&hardlink_mutex); pthread_mutex_unlock(&hardlink_mutex);
return FALSE; return FALSE;
} }
*m |= 1 << (nr & 63); *m |= 1 << (nr & 63);
pthread_mutex_unlock(&hardlink_mutex); pthread_mutex_unlock(&hardlink_mutex);
return TRUE; return TRUE;
} }
#ifndef __APPLE__ #ifndef __APPLE__
/* Increase the limit on open file descriptors, if possible */ /* Increase the limit on open file descriptors, if possible */
static void max_openfds(void) static void max_openfds(void)
{ {
struct rlimit rl; struct rlimit rl;
if (!getrlimit(RLIMIT_NOFILE, &rl)) { if (!getrlimit(RLIMIT_NOFILE, &rl))
if (rl.rlim_cur < rl.rlim_max) { if (rl.rlim_cur < rl.rlim_max) {
rl.rlim_cur = rl.rlim_max; rl.rlim_cur = rl.rlim_max;
setrlimit(RLIMIT_NOFILE, &rl); setrlimit(RLIMIT_NOFILE, &rl);
} }
}
} }
#endif #endif
/* /*
* Wrapper to realloc() * Wrapper to realloc()
* Frees current memory if realloc() fails and returns NULL. * Frees current memory if realloc() fails and returns NULL.
* *
* As per the docs, the *alloc() family is supposed to be memory aligned: * The *alloc() family returns aligned address: https://man7.org/linux/man-pages
* Ubuntu: https://manpages.ubuntu.com/manpages/xenial/man3/malloc.3.html /man3/malloc.3.html
* macOS: https://developer.apple.com/legacy/library/documentation/Darwin/Refere
nce/ManPages/man3/malloc.3.html
*/ */
static void *xrealloc(void *pcur, size_t len) static void *xrealloc(void *pcur, size_t len)
{ {
void *pmem = realloc(pcur, len); void *pmem = realloc(pcur, len);
if (!pmem) if (!pmem)
free(pcur); free(pcur);
return pmem; return pmem;
} }
skipping to change at line 1026 skipping to change at line 1050
return path; return path;
} }
static char *xbasename(char *path) static char *xbasename(char *path)
{ {
char *base = xmemrchr((uchar_t *)path, '/', xstrlen(path)); // NOLINT char *base = xmemrchr((uchar_t *)path, '/', xstrlen(path)); // NOLINT
return base ? base + 1 : path; return base ? base + 1 : path;
} }
static char *xextension(const char *fname, size_t len) static inline char *xextension(const char *fname, size_t len)
{ {
return xmemrchr((uchar_t *)fname, '.', len); return xmemrchr((uchar_t *)fname, '.', len);
} }
#ifndef NOUG #ifndef NOUG
/* One-shot cache for getpwuid/getgrgid. Returns the cached name if the /*
* One-shot cache for getpwuid/getgrgid. Returns the cached name if the
* provided uid is the same as the previous uid. Returns xitoa(guid) if * provided uid is the same as the previous uid. Returns xitoa(guid) if
* the guid is not found in the password database. */ * the guid is not found in the password database.
*/
static char *getpwname(uid_t uid) static char *getpwname(uid_t uid)
{ {
static uint_t uidcache = UINT_MAX; static uint_t uidcache = UINT_MAX;
static char *namecache = NULL; static char *namecache;
if (uidcache != uid) { if (uidcache != uid) {
struct passwd *pw = getpwuid(uid); struct passwd *pw = getpwuid(uid);
uidcache = uid; uidcache = uid;
namecache = pw ? pw->pw_name : NULL; namecache = pw ? pw->pw_name : NULL;
} }
return namecache ? namecache : xitoa(uid); return namecache ? namecache : xitoa(uid);
} }
static char *getgrname(gid_t gid) static char *getgrname(gid_t gid)
{ {
static uint_t gidcache = UINT_MAX; static uint_t gidcache = UINT_MAX;
static char *grpcache = NULL; static char *grpcache;
if (gidcache != gid) { if (gidcache != gid) {
struct group *gr = getgrgid(gid); struct group *gr = getgrgid(gid);
gidcache = gid; gidcache = gid;
grpcache = gr ? gr->gr_name : NULL; grpcache = gr ? gr->gr_name : NULL;
} }
return grpcache ? grpcache : xitoa(gid); return grpcache ? grpcache : xitoa(gid);
} }
skipping to change at line 1079 skipping to change at line 1105
} }
/* /*
* Updates out with "dir/name or "/name" * Updates out with "dir/name or "/name"
* Returns the number of bytes copied including the terminating NULL byte * Returns the number of bytes copied including the terminating NULL byte
* *
* Note: dir and out must be PATH_MAX in length to avoid macOS fault * Note: dir and out must be PATH_MAX in length to avoid macOS fault
*/ */
static size_t mkpath(const char *dir, const char *name, char *out) static size_t mkpath(const char *dir, const char *name, char *out)
{ {
size_t len; size_t len = 0;
/* Handle absolute path */ if (name[0] != '/') { // NOLINT
if (name[0] == '/') // NOLINT /* Handle root case */
return xstrsncpy(out, name, PATH_MAX); if (istopdir(dir))
len = 1;
/* Handle root case */ else
if (istopdir(dir)) len = xstrsncpy(out, dir, PATH_MAX);
len = 1;
else
len = xstrsncpy(out, dir, PATH_MAX);
out[len - 1] = '/'; // NOLINT out[len - 1] = '/'; // NOLINT
}
return (xstrsncpy(out + len, name, PATH_MAX - len) + len); return (xstrsncpy(out + len, name, PATH_MAX - len) + len);
} }
/* Assumes both the paths passed are directories */ /* Assumes both the paths passed are directories */
static char *common_prefix(const char *path, char *prefix) static char *common_prefix(const char *path, char *prefix)
{ {
const char *x = path, *y = prefix; const char *x = path, *y = prefix;
char *sep; char *sep;
if (!path || !*path || !prefix) if (!path || !*path || !prefix)
skipping to change at line 1229 skipping to change at line 1253
int fd = mkstemp(g_tmpfpath); int fd = mkstemp(g_tmpfpath);
if (fd == -1) { if (fd == -1) {
DPRINTF_S(strerror(errno)); DPRINTF_S(strerror(errno));
} }
return fd; return fd;
} }
static void msg(const char *message)
{
dprintf(STDERR_FILENO, "%s\n", message);
}
static void clearinfoln(void) static void clearinfoln(void)
{ {
move(xlines - 2, 0); move(xlines - 2, 0);
clrtoeol(); clrtoeol();
} }
#ifdef KEY_RESIZE #ifdef KEY_RESIZE
static void handle_key_resize()
{
endwin();
refresh();
}
/* Clear the old prompt */ /* Clear the old prompt */
static void clearoldprompt(void) static void clearoldprompt(void)
{ {
clearinfoln(); clearinfoln();
tolastln(); tolastln();
addch('\n'); clrtoeol();
handle_key_resize();
} }
#endif #endif
/* Messages show up at the bottom */ /* Messages show up at the bottom */
static inline void printmsg_nc(const char *msg) static inline void printmsg_nc(const char *msg)
{ {
tolastln(); tolastln();
addstr(msg); addstr(msg);
addch('\n'); clrtoeol();
} }
static void printmsg(const char *msg) static void printmsg(const char *msg)
{ {
attron(COLOR_PAIR(cfg.curctx + 1)); attron(COLOR_PAIR(cfg.curctx + 1));
printmsg_nc(msg); printmsg_nc(msg);
attroff(COLOR_PAIR(cfg.curctx + 1)); attroff(COLOR_PAIR(cfg.curctx + 1));
} }
static void printwait(const char *msg, int *presel) static void printwait(const char *msg, int *presel)
skipping to change at line 1351 skipping to change at line 1387
{ {
refresh(); refresh();
usleep(delay); usleep(delay);
} }
static char confirm_force(bool selection) static char confirm_force(bool selection)
{ {
char str[64]; char str[64];
snprintf(str, 64, messages[MSG_FORCE_RM], snprintf(str, 64, messages[MSG_FORCE_RM],
g_state.trash ? utils[UTIL_GIO_TRASH] + 4 : utils[UTIL_RM_RF],
(selection ? xitoa(nselected) : "current"), (selection ? "(s)" : "")); (selection ? xitoa(nselected) : "current"), (selection ? "(s)" : ""));
int r = get_input(str); int r = get_input(str);
if (r == ESC) if (r == ESC)
return '\0'; /* cancel */ return '\0'; /* cancel */
if (r == 'y' || r == 'Y') if (r == 'y' || r == 'Y')
return 'f'; /* forceful */ return 'f'; /* forceful for rm */
return 'i'; /* interactive */ return (g_state.trash ? '\0' : 'i'); /* interactive for rm */
} }
/* Writes buflen char(s) from buf to a file */ /* Writes buflen char(s) from buf to a file */
static void writesel(const char *buf, const size_t buflen) static void writesel(const char *buf, const size_t buflen)
{ {
if (!selpath) if (!selpath)
return; return;
FILE *fp = fopen(selpath, "w"); int fd = open(selpath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (fp) { if (fd != -1) {
if (fwrite(buf, 1, buflen, fp) != buflen) if (write(fd, buf, buflen) != (ssize_t)buflen)
printwarn(NULL); printwarn(NULL);
fclose(fp); close(fd);
} else } else
printwarn(NULL); printwarn(NULL);
} }
static void appendfpath(const char *path, const size_t len) static void appendfpath(const char *path, const size_t len)
{ {
if ((selbufpos >= selbuflen) || ((len + 3) > (selbuflen - selbufpos))) { if ((selbufpos >= selbuflen) || ((len + 3) > (selbuflen - selbufpos))) {
selbuflen += PATH_MAX; selbuflen += PATH_MAX;
pselbuf = xrealloc(pselbuf, selbuflen); pselbuf = xrealloc(pselbuf, selbuflen);
if (!pselbuf) if (!pselbuf)
errexit(); errexit();
} }
selbufpos += xstrsncpy(pselbuf + selbufpos, path, len); selbufpos += xstrsncpy(pselbuf + selbufpos, path, len);
} }
static void selbufrealloc(const size_t alloclen)
{
if ((selbufpos + alloclen) > selbuflen) {
selbuflen = ALIGN_UP(selbufpos + alloclen, PATH_MAX);
pselbuf = xrealloc(pselbuf, selbuflen);
if (!pselbuf)
errexit();
}
}
/* Write selected file paths to fd, linefeed separated */ /* Write selected file paths to fd, linefeed separated */
static size_t seltofile(int fd, uint_t *pcount) static size_t seltofile(int fd, uint_t *pcount)
{ {
uint_t lastpos, count = 0; uint_t lastpos, count = 0;
char *pbuf = pselbuf; char *pbuf = pselbuf;
size_t pos = 0; size_t pos = 0;
ssize_t len, prefixlen = 0, initlen = 0; ssize_t len, prefixlen = 0, initlen = 0;
if (pcount) if (pcount)
*pcount = 0; *pcount = 0;
skipping to change at line 1472 skipping to change at line 1519
{ {
if (!g_state.selmode) { if (!g_state.selmode) {
g_state.selmode = 1; g_state.selmode = 1;
nselected = 0; nselected = 0;
if (selbufpos) { if (selbufpos) {
resetselind(); resetselind();
writesel(NULL, 0); writesel(NULL, 0);
selbufpos = 0; selbufpos = 0;
} }
lastappendpos = 0;
} }
} }
static size_t appendslash(char *path) static void clearselection(void)
{ {
nselected = 0;
selbufpos = 0;
g_state.selmode = 0;
writesel(NULL, 0);
}
size_t len = 1; static char *findinsel(char *startpos, int len)
{
if (!selbufpos)
return FALSE;
if (!startpos)
startpos = pselbuf;
char *found = startpos;
size_t buflen = selbufpos - (startpos - pselbuf);
if (path[1] != '\0') { while (1) {
len = xstrlen(path); /* memmem(3): not specified in POSIX.1, but present on a number o
path[len] = '/'; f other systems. */
++len; found = memmem(found, buflen - (found - startpos), g_sel, len);
if (!found)
return NULL;
if (found == startpos || *(found - 1) == '\0')
return found;
found += len; /* We found g_sel as a substring of a path, move fo
rward */
if (found >= startpos + buflen)
return NULL;
} }
}
return len; static int markcmp(const void *va, const void *vb)
{
const selmark *ma = (selmark*)va;
const selmark *mb = (selmark*)vb;
return ma->startpos - mb->startpos;
} }
static void invertselbuf(char *path, bool toggle) /* scanselforpath() must be called before calling this */
static inline void findmarkentry(size_t len, struct entry *dentp)
{ {
selbufpos = lastappendpos; if (!(dentp->flags & FILE_SCANNED)) {
if (findinsel(findselpos, len + xstrsncpy(g_sel + len, dentp->nam
e, dentp->nlen)))
dentp->flags |= FILE_SELECTED;
dentp->flags |= FILE_SCANNED;
}
}
if (toggle || nselected) { /*
size_t len = appendslash(path); * scanselforpath() must be called before calling this
* pathlen = length of path + 1 (+1 for trailing slash)
*/
static void invertselbuf(const int pathlen)
{
size_t len, endpos, shrinklen = 0, alloclen = 0;
char * const pbuf = g_sel + pathlen;
char *found;
int i, nmarked = 0, prev = 0;
struct entry *dentp;
selmark *marked = malloc(nselected * sizeof(selmark));
bool scan = FALSE;
for (int i = 0; i < ndents; ++i) { /* First pass: inversion */
if (toggle) { /* Toggle selection status */ for (i = 0; i < ndents; ++i) {
pdents[i].flags ^= FILE_SELECTED; dentp = &pdents[i];
pdents[i].flags & FILE_SELECTED ? ++nselected : -
-nselected; if (dentp->flags & FILE_SCANNED) {
if (dentp->flags & FILE_SELECTED) {
dentp->flags ^= FILE_SELECTED; /* Clear selection
status */
scan = TRUE;
} else {
dentp->flags |= FILE_SELECTED;
alloclen += pathlen + dentp->nlen;
} }
} else {
dentp->flags |= FILE_SCANNED;
scan = TRUE;
}
if (pdents[i].flags & FILE_SELECTED) if (scan) {
appendfpath(path, len = pathlen + xstrsncpy(pbuf, dentp->name, NAME_MAX);
len + xstrsncpy(path + len, pdents[i].nam found = findinsel(findselpos, len);
e, PATH_MAX - len)); if (found) {
if (findselpos == found)
findselpos += len;
if (nmarked && (found
== (marked[nmarked - 1].startpos + marked[nma
rked - 1].len)))
marked[nmarked - 1].len += len;
else {
marked[nmarked].startpos = found;
marked[nmarked].len = len;
++nmarked;
}
--nselected;
shrinklen += len; /* buffer size adjustment */
} else {
dentp->flags |= FILE_SELECTED;
alloclen += pathlen + dentp->nlen;
}
scan = FALSE;
} }
}
if (len > 1) /*
--len; * Files marked for deselection could be found in arbitrary order.
path[len] = '\0'; * Sort by appearance in selection buffer.
* With entries sorted we can merge adjacent ones allowing us to
* move them in a single go.
*/
qsort(marked, nmarked, sizeof(selmark), &markcmp);
nselected ? writesel(pselbuf, selbufpos - 1) : writesel(NULL, 0); /* Some files might be adjacent. Merge them into a single entry */
} else for (i = 1; i < nmarked; ++i) {
writesel(NULL, 0); if (marked[i].startpos == marked[prev].startpos + marked[prev].le
n)
marked[prev].len += marked[i].len;
else {
++prev;
marked[prev].startpos = marked[i].startpos;
marked[prev].len = marked[i].len;
}
}
/*
* Number of entries is increased by encountering a non-adjacent entry
* After we finish the loop we should increment it once more.
*/
if (nmarked) /* Make sure there is something to deselect */
nmarked = prev + 1;
/* Using merged entries remove unselected chunks from selection buffer */
for (i = 0; i < nmarked; ++i) {
/*
* found: points to where the current block starts
* variable is recycled from previous for readability
* endpos: points to where the the next block starts
* area between the end of current block (found + len)
* and endpos is selected entries. This is what we are
* moving back.
*/
found = marked[i].startpos;
endpos = (i + 1 == nmarked ? selbufpos : marked[i + 1].startpos -
pselbuf);
len = marked[i].len;
/* Move back only selected entries. No selected memory is moved t
wice */
memmove(found, found + len, endpos - (found + len - pselbuf));
}
free(marked);
/* Buffer size adjustment */
selbufpos -= shrinklen;
selbufrealloc(alloclen);
/* Second pass: append newly selected to buffer */
for (i = 0; i < ndents; ++i) {
if (pdents[i].flags & FILE_SELECTED) {
len = pathlen + xstrsncpy(pbuf, pdents[i].name, NAME_MAX)
;
appendfpath(g_sel, len);
++nselected;
}
}
nselected ? writesel(pselbuf, selbufpos - 1) : clearselection();
} }
static void addtoselbuf(char *path, int startid, int endid) /*
* scanselforpath() must be called before calling this
* pathlen = length of path + 1 (+1 for trailing slash)
*/
static void addtoselbuf(const int pathlen, int startid, int endid)
{ {
size_t len = appendslash(path); int i;
size_t len, alloclen = 0;
struct entry *dentp;
char *found;
char * const pbuf = g_sel + pathlen;
/* Remember current selection buffer position */ /* Remember current selection buffer position */
for (int i = startid; i <= endid; ++i) { for (i = startid; i <= endid; ++i) {
if (!(pdents[i].flags & FILE_SELECTED)) { dentp = &pdents[i];
/* Write the path to selection file to avoid flush */
appendfpath(path, len + xstrsncpy(path + len, pdents[i].n
ame, PATH_MAX - len));
pdents[i].flags |= FILE_SELECTED; if (findselpos) {
len = pathlen + xstrsncpy(pbuf, dentp->name, NAME_MAX);
found = findinsel(findselpos, len);
if (found) {
dentp->flags |= (FILE_SCANNED | FILE_SELECTED);
if (found == findselpos) {
findselpos += len;
if (findselpos == (pselbuf + selbufpos))
findselpos = NULL;
}
} else
alloclen += pathlen + dentp->nlen;
} else
alloclen += pathlen + dentp->nlen;
}
selbufrealloc(alloclen);
for (i = startid; i <= endid; ++i) {
if (!(pdents[i].flags & FILE_SELECTED)) {
len = pathlen + xstrsncpy(pbuf, pdents[i].name, NAME_MAX)
;
appendfpath(g_sel, len);
++nselected; ++nselected;
pdents[i].flags |= (FILE_SCANNED | FILE_SELECTED);
} }
} }
if (len > 1)
--len;
path[len] = '\0';
writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */
} }
/* Removes g_sel from selbuf */
static void rmfromselbuf(size_t len)
{
char *found = findinsel(findselpos, len);
if (!found)
return;
memmove(found, found + len, selbufpos - (found + len - pselbuf));
selbufpos -= len;
nselected ? writesel(pselbuf, selbufpos - 1) : clearselection();
}
static int scanselforpath(const char *path, bool getsize)
{
if (!path[1]) { /* path should always be at least two bytes (including NU
LL) */
g_sel[0] = '/';
findselpos = pselbuf;
return 1; /* Length of '/' is 1 */
}
size_t off = xstrsncpy(g_sel, path, PATH_MAX);
g_sel[off - 1] = '/';
/*
* We set findselpos only here. Directories can be listed in arbitrary or
der.
* This is the best best we can do for remembering position.
*/
findselpos = findinsel(NULL, off);
if (getsize)
return off;
return (findselpos ? off : 0);
}
/* Finish selection procedure before an operation */ /* Finish selection procedure before an operation */
static void endselection(void) static void endselection(void)
{ {
int fd; int fd;
ssize_t count; ssize_t count;
char buf[sizeof(patterns[P_REPLACE]) + PATH_MAX + (TMP_LEN_MAX << 1)]; char buf[sizeof(patterns[P_REPLACE]) + PATH_MAX + (TMP_LEN_MAX << 1)];
if (g_state.selmode) if (g_state.selmode)
g_state.selmode = 0; g_state.selmode = 0;
skipping to change at line 1605 skipping to change at line 1839
selbufpos = count; selbufpos = count;
pselbuf[--count] = '\0'; pselbuf[--count] = '\0';
for (--count; count > 0; --count) for (--count; count > 0; --count)
if (pselbuf[count] == '\n' && pselbuf[count+1] == '/') if (pselbuf[count] == '\n' && pselbuf[count+1] == '/')
pselbuf[count] = '\0'; pselbuf[count] = '\0';
writesel(pselbuf, selbufpos - 1); writesel(pselbuf, selbufpos - 1);
} }
static void clearselection(void)
{
nselected = 0;
selbufpos = 0;
g_state.selmode = 0;
writesel(NULL, 0);
}
/* Returns: 1 - success, 0 - none selected, -1 - other failure */ /* Returns: 1 - success, 0 - none selected, -1 - other failure */
static int editselection(void) static int editselection(void)
{ {
int ret = -1; int ret = -1;
int fd, lines = 0; int fd, lines = 0;
ssize_t count; ssize_t count;
struct stat sb; struct stat sb;
time_t mtime; time_t mtime;
if (!selbufpos) /* External selection is only editable at source */ if (!selbufpos) /* External selection is only editable at source */
skipping to change at line 1771 skipping to change at line 1997
} }
spawn(editor, g_tmpfpath, NULL, NULL, F_CLI); spawn(editor, g_tmpfpath, NULL, NULL, F_CLI);
if (xconfirm(get_input(messages[MSG_RM_TMP]))) if (xconfirm(get_input(messages[MSG_RM_TMP])))
unlink(g_tmpfpath); unlink(g_tmpfpath);
} }
static bool init_fcolors(void) static bool init_fcolors(void)
{ {
char *f_colors = getenv("NNN_FCOLORS"); char *f_colors = getenv(env_cfg[NNN_FCOLORS]);
if (!f_colors || !*f_colors) if (!f_colors || !*f_colors)
f_colors = gcolors; f_colors = gcolors;
for (uchar_t id = C_BLK; *f_colors && id <= C_UND; ++id) { for (uchar_t id = C_BLK; *f_colors && id <= C_UND; ++id) {
fcolors[id] = xchartohex(*f_colors) << 4; fcolors[id] = xchartohex(*f_colors) << 4;
if (*++f_colors) { if (*++f_colors) {
fcolors[id] += xchartohex(*f_colors); fcolors[id] += xchartohex(*f_colors);
if (fcolors[id]) if (fcolors[id])
init_pair(id, fcolors[id], -1); init_pair(id, fcolors[id], -1);
skipping to change at line 1799 skipping to change at line 2025
/* Initialize curses mode */ /* Initialize curses mode */
static bool initcurses(void *oldmask) static bool initcurses(void *oldmask)
{ {
#ifdef NOMOUSE #ifdef NOMOUSE
(void) oldmask; (void) oldmask;
#endif #endif
if (g_state.picker) { if (g_state.picker) {
if (!newterm(NULL, stderr, stdin)) { if (!newterm(NULL, stderr, stdin)) {
fprintf(stderr, "newterm!\n"); msg("newterm!");
return FALSE; return FALSE;
} }
} else if (!initscr()) { } else if (!initscr()) {
fprintf(stderr, "initscr!\n"); msg("initscr!");
DPRINTF_S(getenv("TERM")); DPRINTF_S(getenv("TERM"));
return FALSE; return FALSE;
} }
cbreak(); cbreak();
noecho(); noecho();
nonl(); nonl();
//intrflush(stdscr, FALSE); //intrflush(stdscr, FALSE);
keypad(stdscr, TRUE); keypad(stdscr, TRUE);
#ifndef NOMOUSE #ifndef NOMOUSE
#if NCURSES_MOUSE_VERSION <= 1 #if NCURSES_MOUSE_VERSION <= 1
mousemask(BUTTON1_PRESSED | BUTTON1_DOUBLE_CLICKED | BUTTON2_PRESSED | BU TTON3_PRESSED, mousemask(BUTTON1_PRESSED | BUTTON1_DOUBLE_CLICKED | BUTTON2_PRESSED | BU TTON3_PRESSED,
(mmask_t *)oldmask); (mmask_t *)oldmask);
#else #else
mousemask(BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_P mousemask(BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_P
RESSED | BUTTON5_PRESSED, RESSED
(mmask_t *)oldmask); | BUTTON5_PRESSED, (mmask_t *)oldmask);
#endif #endif
mouseinterval(0); mouseinterval(0);
#endif #endif
curs_set(FALSE); /* Hide cursor */ curs_set(FALSE); /* Hide cursor */
char *colors = getenv(env_cfg[NNN_COLORS]); char *colors = getenv(env_cfg[NNN_COLORS]);
if (colors || !getenv("NO_COLOR")) { if (colors || !getenv("NO_COLOR")) {
uint_t *pcode; uint_t *pcode;
bool ext = FALSE; bool ext = FALSE;
start_color(); start_color();
use_default_colors(); use_default_colors();
/* Initialize file colors */ /* Initialize file colors */
if (COLORS >= 256) { if (COLORS >= 256) {
if (!(g_state.oldcolor || init_fcolors())) { if (!(g_state.oldcolor || init_fcolors())) {
exitcurses(); exitcurses();
fprintf(stderr, "NNN_FCOLORS!\n"); msg(env_cfg[NNN_FCOLORS]);
return FALSE; return FALSE;
} }
} else } else
g_state.oldcolor = 1; g_state.oldcolor = 1;
DPRINTF_D(COLORS); DPRINTF_D(COLORS);
DPRINTF_D(COLOR_PAIRS); DPRINTF_D(COLOR_PAIRS);
if (colors && *colors == '#') { if (colors && *colors == '#') {
char *sep = strchr(colors, ';'); char *sep = strchr(colors, ';');
skipping to change at line 1879 skipping to change at line 2105
for (uchar_t i = 0; i < CTX_MAX; ++i) { for (uchar_t i = 0; i < CTX_MAX; ++i) {
pcode = &g_ctx[i].color; pcode = &g_ctx[i].color;
if (colors && *colors) { if (colors && *colors) {
if (ext) { if (ext) {
*pcode = xchartohex(*colors) << 4; *pcode = xchartohex(*colors) << 4;
if (*++colors) if (*++colors)
fcolors[i + 1] = *pcode += xchart ohex(*colors); fcolors[i + 1] = *pcode += xchart ohex(*colors);
else { /* Each color code must be 2 hex s ymbols */ else { /* Each color code must be 2 hex s ymbols */
exitcurses(); exitcurses();
fprintf(stderr, "NNN_COLORS!\n"); msg(env_cfg[NNN_COLORS]);
return FALSE; return FALSE;
} }
} else } else
*pcode = (*colors < '0' || *colors > '7') ? 4 : *colors - '0'; *pcode = (*colors < '0' || *colors > '7') ? 4 : *colors - '0';
++colors; ++colors;
} else } else
*pcode = 4; *pcode = 4;
init_pair(i + 1, *pcode, -1); init_pair(i + 1, *pcode, -1);
} }
skipping to change at line 1924 skipping to change at line 2150
} }
} }
#endif #endif
settimeout(); /* One second */ settimeout(); /* One second */
set_escdelay(25); set_escdelay(25);
return TRUE; return TRUE;
} }
/* 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 void enable_signals(void)
{
struct sigaction dfl_act = {.sa_handler = SIG_DFL};
sigaction(SIGHUP, &dfl_act, NULL);
sigaction(SIGINT, &dfl_act, NULL);
sigaction(SIGQUIT, &dfl_act, NULL);
sigaction(SIGTSTP, &dfl_act, NULL);
sigaction(SIGWINCH, &dfl_act, NULL);
} }
static pid_t xfork(uchar_t flag) static pid_t xfork(uchar_t flag)
{ {
pid_t p = fork(); pid_t p = fork();
struct sigaction dfl_act = {.sa_handler = SIG_DFL};
if (p > 0) { if (p > 0) {
/* the parent ignores the interrupt, quit and hangup signals */ /* the parent ignores the interrupt, quit and hangup signals */
sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_IGN}, &ol dsighup); sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_IGN}, &ol dsighup);
sigaction(SIGTSTP, &dfl_act, &oldsigtstp); sigaction(SIGTSTP, &(struct sigaction){.sa_handler = SIG_DFL}, &o
ldsigtstp);
sigaction(SIGWINCH, &(struct sigaction){.sa_handler = SIG_IGN}, &
oldsigwinch);
} else if (p == 0) { } else if (p == 0) {
/* We create a grandchild to detach */ /* We create a grandchild to detach */
if (flag & F_NOWAIT) { if (flag & F_NOWAIT) {
p = fork(); p = fork();
if (p > 0) if (p > 0)
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
else if (p == 0) { else if (p == 0) {
sigaction(SIGHUP, &dfl_act, NULL); enable_signals();
sigaction(SIGINT, &dfl_act, NULL);
sigaction(SIGQUIT, &dfl_act, NULL);
sigaction(SIGTSTP, &dfl_act, NULL);
setsid(); setsid();
return p; return p;
} }
perror("fork"); perror("fork");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
/* so they can be used to stop the child */ /* So they can be used to stop the child */
sigaction(SIGHUP, &dfl_act, NULL); enable_signals();
sigaction(SIGINT, &dfl_act, NULL);
sigaction(SIGQUIT, &dfl_act, NULL);
sigaction(SIGTSTP, &dfl_act, NULL);
} }
/* This is the parent waiting for the child to create grandchild */ /* This is the parent waiting for the child to create grandchild */
if (flag & F_NOWAIT) if (flag & F_NOWAIT)
waitpid(p, NULL, 0); waitpid(p, NULL, 0);
if (p == -1) if (p == -1)
perror("fork"); perror("fork");
return p; return p;
} }
skipping to change at line 2012 skipping to change at line 2260
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
status = WEXITSTATUS(status); status = WEXITSTATUS(status);
DPRINTF_D(status); DPRINTF_D(status);
} }
} }
/* restore parent's signal handling */ /* restore parent's signal handling */
sigaction(SIGHUP, &oldsighup, NULL); sigaction(SIGHUP, &oldsighup, NULL);
sigaction(SIGTSTP, &oldsigtstp, NULL); sigaction(SIGTSTP, &oldsigtstp, NULL);
sigaction(SIGWINCH, &oldsigwinch, NULL);
return status; return status;
} }
/* /*
* Spawns a child process. Behaviour can be controlled using flag. * Spawns a child process. Behaviour can be controlled using flag.
* Limited to 3 arguments to a program, flag works on bit set. * Limited to 3 arguments to a program, flag works on bit set.
*/ */
static int spawn(char *file, char *arg1, char *arg2, char *arg3, uchar_t flag) static int spawn(char *file, char *arg1, char *arg2, char *arg3, ushort_t flag)
{ {
pid_t pid; pid_t pid;
int status = 0, retstatus = 0xFFFF; int status = 0, retstatus = 0xFFFF;
char *argv[EXEC_ARGS_MAX] = {0}; char *argv[EXEC_ARGS_MAX] = {0};
char *cmd = NULL; char *cmd = NULL;
if (!file || !*file) if (!file || !*file)
return retstatus; return retstatus;
/* Swap args if the first arg is NULL and the other 2 aren't */ /* Swap args if the first arg is NULL and the other 2 aren't */
if (!arg1 && arg2) { if (!arg1 && arg2) {
arg1 = arg2; arg1 = arg2;
if (arg3) { if (arg3) {
arg2 = arg3; arg2 = arg3;
arg3 = NULL; arg3 = NULL;
} else } else
arg2 = NULL; arg2 = NULL;
} }
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;
argv[status] = arg1; argv[status] = arg1;
argv[++status] = arg2; argv[++status] = arg2;
argv[++status] = arg3; argv[++status] = arg3;
if (flag & F_NORMAL) if (flag & F_NORMAL)
exitcurses(); exitcurses();
pid = xfork(flag); pid = xfork(flag);
if (pid == 0) { if (pid == 0) {
/* Suppress stdout and stderr */ /* Suppress stdout and stderr */
if (flag & F_NOTRACE) { if (flag & F_NOTRACE) {
int fd = open("/dev/null", O_WRONLY, 0200); int fd = open("/dev/null", O_WRONLY, 0200);
if (flag & F_NOSTDIN) if (flag & F_NOSTDIN)
dup2(fd, 0); dup2(fd, STDIN_FILENO);
dup2(fd, 1); dup2(fd, STDOUT_FILENO);
dup2(fd, 2); dup2(fd, STDERR_FILENO);
close(fd); close(fd);
} else if (flag & F_TTY) {
/* If stdout has been redirected to a non-tty, force outp
ut to tty */
if (!isatty(STDOUT_FILENO)) {
int fd = open(ctermid(NULL), O_WRONLY, 0200);
dup2(fd, STDOUT_FILENO);
close(fd);
}
} }
execvp(*argv, argv); execvp(*argv, argv);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} else { } else {
retstatus = join(pid, flag); retstatus = join(pid, flag);
DPRINTF_D(pid); DPRINTF_D(pid);
if ((flag & F_CONFIRM) || ((flag & F_CHKRTN) && retstatus)) { if ((flag & F_CONFIRM) || ((flag & F_CHKRTN) && retstatus)) {
status = write(STDOUT_FILENO, messages[MSG_RETURN], xstrl status = write(STDOUT_FILENO, messages[MSG_ENTER], xstrle
en(messages[MSG_RETURN])); n(messages[MSG_ENTER]));
(void)status;
status = read(STDIN_FILENO, g_buf, PATH_MAX);
(void)status; (void)status;
while ((read(STDIN_FILENO, &status, 1) > 0) && (status != '\n'));
} }
if (flag & F_NORMAL) if (flag & F_NORMAL)
refresh(); refresh();
free(cmd); free(cmd);
} }
return retstatus; return retstatus;
} }
skipping to change at line 2135 skipping to change at line 2377
if (!dirp) { if (!dirp) {
printwarn(NULL); printwarn(NULL);
return FALSE; return FALSE;
} }
closedir(dirp); closedir(dirp);
return TRUE; return TRUE;
} }
static bool plugscript(const char *plugin, uchar_t flags)
{
mkpath(plgpath, plugin, g_buf);
if (!access(g_buf, X_OK)) {
spawn(g_buf, NULL, NULL, NULL, flags);
return TRUE;
}
return FALSE;
}
static void opstr(char *buf, char *op) static void opstr(char *buf, char *op)
{ {
snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c '%s \"$0\" \"$@\" . < /dev/tty ' < %s", snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c '%s \"$0\" \"$@\" . < /dev/tty ' < %s",
op, selpath); op, selpath);
} }
static bool rmmulstr(char *buf) static bool rmmulstr(char *buf)
{ {
if (!g_state.trash) { char r = confirm_force(TRUE);
char r = confirm_force(TRUE); if (!r)
return FALSE;
if (!r)
return FALSE;
if (!g_state.trash)
snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cr \"$0\" \"$@\" < /dev/tty' < %s", snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cr \"$0\" \"$@\" < /dev/tty' < %s",
r, selpath); r, selpath);
} else if (g_state.trash == 1)
snprintf(buf, CMD_LEN_MAX, "xargs -0 trash-put < %s", selpath);
else else
snprintf(buf, CMD_LEN_MAX, "xargs -0 gio trash < %s", selpath); snprintf(buf, CMD_LEN_MAX, "xargs -0 %s < %s",
utils[(g_state.trash == 1) ? UTIL_TRASH_CLI : UTIL_GIO_T
RASH], selpath);
return TRUE; return TRUE;
} }
/* Returns TRUE if file is removed, else FALSE */ /* Returns TRUE if file is removed, else FALSE */
static bool xrm(char *fpath) static bool xrm(char * const fpath)
{ {
char r = confirm_force(FALSE);
if (!r)
return FALSE;
if (!g_state.trash) { if (!g_state.trash) {
char rm_opts[] = "-ir"; char rm_opts[] = "-ir";
rm_opts[1] = confirm_force(FALSE); rm_opts[1] = r;
if (!rm_opts[1])
return FALSE;
spawn("rm", rm_opts, fpath, NULL, F_NORMAL | F_CHKRTN); spawn("rm", rm_opts, fpath, NULL, F_NORMAL | F_CHKRTN);
} else if (g_state.trash == 1) } else
spawn("trash-put", fpath, NULL, NULL, F_NORMAL); spawn(utils[(g_state.trash == 1) ? UTIL_TRASH_CLI : UTIL_GIO_TRAS
else H],
spawn("gio trash", fpath, NULL, NULL, F_NORMAL | F_MULTI); fpath, NULL, NULL, F_NORMAL | F_MULTI);
return (access(fpath, F_OK) == -1); /* File is removed */ return (access(fpath, F_OK) == -1); /* File is removed */
} }
static void xrmfromsel(char *path, char *fpath)
{
#ifndef NOX11
bool selected = TRUE;
#endif
if ((pdents[cur].flags & DIR_OR_DIRLNK) && scanselforpath(fpath, FALSE))
clearselection();
else if (pdents[cur].flags & FILE_SELECTED) {
--nselected;
rmfromselbuf(mkpath(path, pdents[cur].name, g_sel));
}
#ifndef NOX11
else
selected = FALSE;
if (selected && cfg.x11)
plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE);
#endif
}
static uint_t lines_in_file(int fd, char *buf, size_t buflen) static uint_t lines_in_file(int fd, char *buf, size_t buflen)
{ {
ssize_t len; ssize_t len;
uint_t count = 0; uint_t count = 0;
while ((len = read(fd, buf, buflen)) > 0) while ((len = read(fd, buf, buflen)) > 0)
while (len) while (len)
count += (buf[--len] == '\n'); count += (buf[--len] == '\n');
/* For all use cases 0 linecount is considered as error */ /* For all use cases 0 linecount is considered as error */
skipping to change at line 2300 skipping to change at line 2572
} }
#ifndef NOBATCH #ifndef NOBATCH
static bool batch_rename(void) static bool batch_rename(void)
{ {
int fd1, fd2; int fd1, fd2;
uint_t count = 0, lines = 0; uint_t count = 0, lines = 0;
bool dir = FALSE, ret = FALSE; bool dir = FALSE, ret = FALSE;
char foriginal[TMP_LEN_MAX] = {0}; char foriginal[TMP_LEN_MAX] = {0};
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 sh -c "tr '\n' '\\0' | xargs -0 -n2 sh -c
'mv -i \"$0\" \"$@\" < /dev/tty'"; 'mv -i \"$0\" \"$@\" <"
" /dev/tty'";
char buf[sizeof(batchrenamecmd) + (PATH_MAX << 1)]; char buf[sizeof(batchrenamecmd) + (PATH_MAX << 1)];
int i = get_cur_or_sel(); int i = get_cur_or_sel();
if (!i) if (!i)
return ret; return ret;
if (i == 'c') { /* Rename entries in current dir */ if (i == 'c') { /* Rename entries in current dir */
selbufpos = 0; selbufpos = 0;
dir = TRUE; dir = TRUE;
} }
skipping to change at line 2408 skipping to change at line 2681
"tr '\\0' '\n' < '%s' | "SED" -e 's|^%s/||' | tr '\n' '\\0' | xar gs -0 %s %s", "tr '\\0' '\n' < '%s' | "SED" -e 's|^%s/||' | tr '\n' '\\0' | xar gs -0 %s %s",
selpath, curpath, cmd, archive selpath, curpath, cmd, archive
#endif #endif
); );
spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI | F_CONFIRM); spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI | F_CONFIRM);
free(buf); free(buf);
} }
static bool write_lastdir(const char *curpath) static bool write_lastdir(const char *curpath)
{ {
bool ret = TRUE; bool ret = FALSE;
size_t len = xstrlen(cfgpath); size_t len = xstrlen(cfgpath);
xstrsncpy(cfgpath + len, "/.lastd", 8); xstrsncpy(cfgpath + len, "/.lastd", 8);
DPRINTF_S(cfgpath);
FILE *fp = fopen(cfgpath, "w");
if (fp) {
if (fprintf(fp, "cd \"%s\"", curpath) < 0)
ret = FALSE;
fclose(fp); int fd = open(cfgpath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
} else
ret = FALSE;
if (fd != -1) {
dprintf(fd, "cd \"%s\"", curpath);
close(fd);
ret = TRUE;
}
return ret; return ret;
} }
/* /*
* We assume none of the strings are NULL. * We assume none of the strings are NULL.
* *
* Let's have the logic to sort numeric names in numeric order. * Let's have the logic to sort numeric names in numeric order.
* E.g., the order '1, 10, 2' doesn't make sense to human eyes. * E.g., the order '1, 10, 2' doesn't make sense to human eyes.
* *
* If the absolute numeric values are same, we fallback to alphasort. * If the absolute numeric values are same, we fallback to alphasort.
skipping to change at line 2609 skipping to change at line 2878
fltr[REGEX_MAX - 1] = fltr[1]; fltr[REGEX_MAX - 1] = fltr[1];
fltr[1] = '\0'; fltr[1] = '\0';
} }
} }
static int entrycmp(const void *va, const void *vb) static int entrycmp(const void *va, const void *vb)
{ {
const struct entry *pa = (pEntry)va; const struct entry *pa = (pEntry)va;
const struct entry *pb = (pEntry)vb; const struct entry *pb = (pEntry)vb;
if ((pb->flags & DIR_OR_LINK_TO_DIR) != (pa->flags & DIR_OR_LINK_TO_DIR)) if ((pb->flags & DIR_OR_DIRLNK) != (pa->flags & DIR_OR_DIRLNK)) {
{ if (pb->flags & DIR_OR_DIRLNK)
if (pb->flags & DIR_OR_LINK_TO_DIR)
return 1; return 1;
return -1; return -1;
} }
/* Sort based on specified order */ /* Sort based on specified order */
if (cfg.timeorder) { if (cfg.timeorder) {
if (pb->sec > pa->sec) if (pb->sec > pa->sec)
return 1; return 1;
if (pb->sec < pa->sec) if (pb->sec < pa->sec)
return -1; return -1;
skipping to change at line 2636 skipping to change at line 2905
} else if (cfg.sizeorder) { } else if (cfg.sizeorder) {
if (pb->size > pa->size) if (pb->size > pa->size)
return 1; return 1;
if (pb->size < pa->size) if (pb->size < pa->size)
return -1; return -1;
} else if (cfg.blkorder) { } else if (cfg.blkorder) {
if (pb->blocks > pa->blocks) if (pb->blocks > pa->blocks)
return 1; return 1;
if (pb->blocks < pa->blocks) if (pb->blocks < pa->blocks)
return -1; return -1;
} else if (cfg.extnorder && !(pb->flags & DIR_OR_LINK_TO_DIR)) { } else if (cfg.extnorder && !(pb->flags & DIR_OR_DIRLNK)) {
char *extna = xextension(pa->name, pa->nlen - 1); char *extna = xextension(pa->name, pa->nlen - 1);
char *extnb = xextension(pb->name, pb->nlen - 1); char *extnb = xextension(pb->name, pb->nlen - 1);
if (extna || extnb) { if (extna || extnb) {
if (!extna) if (!extna)
return -1; return -1;
if (!extnb) if (!extnb)
return 1; return 1;
skipping to change at line 2659 skipping to change at line 2928
if (ret) if (ret)
return ret; return ret;
} }
} }
return namecmpfn(pa->name, pb->name); return namecmpfn(pa->name, pb->name);
} }
static int reventrycmp(const void *va, const void *vb) static int reventrycmp(const void *va, const void *vb)
{ {
if ((((pEntry)vb)->flags & DIR_OR_LINK_TO_DIR) if ((((pEntry)vb)->flags & DIR_OR_DIRLNK)
!= (((pEntry)va)->flags & DIR_OR_LINK_TO_DIR)) { != (((pEntry)va)->flags & DIR_OR_DIRLNK)) {
if (((pEntry)vb)->flags & DIR_OR_LINK_TO_DIR) if (((pEntry)vb)->flags & DIR_OR_DIRLNK)
return 1; return 1;
return -1; return -1;
} }
return -entrycmp(va, vb); return -entrycmp(va, vb);
} }
static int (*entrycmpfn)(const void *va, const void *vb) = &entrycmp; static int (*entrycmpfn)(const void *va, const void *vb) = &entrycmp;
/* In case of an error, resets *wch to Esc */ /* In case of an error, resets *wch to Esc */
skipping to change at line 2705 skipping to change at line 2974
int c = presel; int c = presel;
uint_t i; uint_t i;
bool escaped = FALSE; bool escaped = FALSE;
if (c == 0 || c == MSGWAIT) { if (c == 0 || c == MSGWAIT) {
try_quit: try_quit:
c = getch(); c = getch();
//DPRINTF_D(c); //DPRINTF_D(c);
//DPRINTF_S(keyname(c)); //DPRINTF_S(keyname(c));
#ifdef KEY_RESIZE
if (c == KEY_RESIZE)
handle_key_resize();
#endif
/* Handle Alt+key */ /* Handle Alt+key */
if (c == ESC) { if (c == ESC) {
timeout(0); timeout(0);
c = getch(); c = getch();
if (c != ERR) { if (c != ERR) {
if (c == ESC) if (c == ESC)
c = CONTROL('L'); c = CONTROL('L');
else { else {
ungetch(c); ungetch(c);
c = ';'; c = ';';
} }
settimeout(); settimeout();
} else if (escaped) { } else if (escaped) {
settimeout(); settimeout();
c = CONTROL('Q'); c = CONTROL('Q');
} else { } else {
#ifndef NOFIFO #ifndef NOFIFO
/* Send hovered path to NNN_FIFO */ if (!g_state.fifomode)
notify_fifo(TRUE); notify_fifo(TRUE); /* Send hovered path t
o NNN_FIFO */
#endif #endif
escaped = TRUE; escaped = TRUE;
settimeout(); settimeout();
goto try_quit; goto try_quit;
} }
} }
if (c == ERR && presel == MSGWAIT) if (c == ERR && presel == MSGWAIT)
c = (cfg.filtermode || filterset()) ? FILTER : CONTROL('L '); c = (cfg.filtermode || filterset()) ? FILTER : CONTROL('L ');
else if (c == FILTER || c == CONTROL('L')) else if (c == FILTER || c == CONTROL('L'))
skipping to change at line 2777 skipping to change at line 3051
} }
} }
DPRINTF_S("inotify read done"); DPRINTF_S("inotify read done");
} }
} }
#elif defined(BSD_KQUEUE) #elif defined(BSD_KQUEUE)
if (!g_state.selmode && !cfg.blkorder && event_fd >= 0 && idle & 1) { if (!g_state.selmode && !cfg.blkorder && event_fd >= 0 && idle & 1) {
struct kevent event_data[NUM_EVENT_SLOTS]; struct kevent event_data[NUM_EVENT_SLOTS];
memset((void *)event_data, 0x0, sizeof(struct kevent) * N UM_EVENT_SLOTS); memset((void *)event_data, 0x0, sizeof(struct kevent) * N UM_EVENT_SLOTS);
if (kevent(kq, events_to_monitor, NUM_EVENT_SLOTS, event_ if (kevent(kq, events_to_monitor, NUM_EVENT_SLOTS,
data, NUM_EVENT_FDS, &gtimeout) > 0) event_data, NUM_EVENT_FDS, &gtimeout) > 0)
c = CONTROL('L'); c = CONTROL('L');
} }
#elif defined(HAIKU_NM) #elif defined(HAIKU_NM)
if (!g_state.selmode && !cfg.blkorder && haiku_nm_active && idle if (!g_state.selmode && !cfg.blkorder && haiku_nm_active
& 1 && haiku_is_update_needed(haiku_hnd)) && (idle & 1) && haiku_is_update_needed(haiku_hnd))
c = CONTROL('L'); c = CONTROL('L');
#endif #endif
} else } else
idle = 0; idle = 0;
for (i = 0; i < (int)ELEMENTS(bindings); ++i) for (i = 0; i < (int)ELEMENTS(bindings); ++i)
if (c == bindings[i].sym) if (c == bindings[i].sym)
return bindings[i].act; return bindings[i].act;
return 0; return 0;
skipping to change at line 3081 skipping to change at line 3357
continue; continue;
wln[len] = (wchar_t)*ch; wln[len] = (wchar_t)*ch;
wln[++len] = '\0'; wln[++len] = '\0';
wcstombs(ln, wln, REGEX_MAX); wcstombs(ln, wln, REGEX_MAX);
/* Forward-filtering optimization: /* Forward-filtering optimization:
* - new matches can only be a subset of current matches. * - new matches can only be a subset of current matches.
*/ */
/* ndents = total; */ /* ndents = total; */
r = matches(pln);
if (matches(pln) == -1) { if (r <= 0) {
showfilter(ln); !r ? unget_wch(KEY_BACKSPACE) : showfilter(ln);
continue; continue;
} }
/* If the only match is a dir, auto-select and cd into it */ /* If the only match is a dir, auto-select and cd into it */
if (ndents == 1 && cfg.filtermode if (ndents == 1 && cfg.filtermode
&& cfg.autoselect && (pdents[0].flags & DIR_OR_LINK_TO_DIR)) { && cfg.autoselect && (pdents[0].flags & DIR_OR_DIRLNK)) {
*ch = KEY_ENTER; *ch = KEY_ENTER;
cur = 0; cur = 0;
goto end; goto end;
} }
/* /*
* redraw() should be above the auto-select optimization, for * redraw() should be above the auto-select optimization, for
* the case where there's an issue with dir auto-select, say, * the case where there's an issue with dir auto-select, say,
* due to a permission problem. The transition is _jumpy_ in * due to a permission problem. The transition is _jumpy_ in
* case of such an error. However, we optimize for successful * case of such an error. However, we optimize for successful
skipping to change at line 3184 skipping to change at line 3460
goto END; goto END;
} else } else
continue; continue;
// fallthrough // fallthrough
case DEL: // fallthrough case DEL: // fallthrough
case '\b': /* rhel25 sends '\b' for backspace */ case '\b': /* rhel25 sends '\b' for backspace */
if (pos > 0) { if (pos > 0) {
memmove(buf + pos - 1, buf + pos, memmove(buf + pos - 1, buf + pos,
(len - pos) * WCHAR_T_WIDTH); (len - pos) * WCHAR_T_WIDTH);
--len, --pos; --len, --pos;
} // fallthrough }
case '\t': /* Tab breaks cursor position, ignore it */ continue;
case '\t':
if (!(len || pos) && ndents)
len = pos = mbstowcs(buf, pdents[cur].nam
e, READLINE_MAX);
continue; continue;
case CONTROL('F'): case CONTROL('F'):
if (pos < len) if (pos < len)
++pos; ++pos;
continue; continue;
case CONTROL('B'): case CONTROL('B'):
if (pos > 0) if (pos > 0)
--pos; --pos;
continue; continue;
case CONTROL('W'): case CONTROL('W'):
skipping to change at line 3281 skipping to change at line 3560
(len - pos - 1) * WCHAR_T_WIDTH); (len - pos - 1) * WCHAR_T_WIDTH);
--len; --len;
} }
break; break;
case KEY_END: case KEY_END:
pos = len; pos = len;
break; break;
case KEY_HOME: case KEY_HOME:
pos = 0; pos = 0;
break; break;
case KEY_UP: // fallthrough
case KEY_DOWN:
if (prompt && lastcmd && (xstrcmp(prompt, PROMPT)
== 0)) {
printmsg(prompt);
len = pos = mbstowcs(buf, lastcmd, READLI
NE_MAX); // fallthrough
}
default: default:
break; break;
} }
} }
} }
END: END:
curs_set(FALSE); curs_set(FALSE);
settimeout(); settimeout();
printmsg(""); printmsg("");
skipping to change at line 3470 skipping to change at line 3755
} }
} }
DPRINTF_S("Invalid key"); DPRINTF_S("Invalid key");
return NULL; return NULL;
} }
static void resetdircolor(int flags) static void resetdircolor(int flags)
{ {
/* Directories are always shown on top, clear the color when moving to fi rst file */ /* Directories are always shown on top, clear the color when moving to fi rst file */
if (g_state.dircolor && !(flags & DIR_OR_LINK_TO_DIR)) { if (g_state.dircolor && !(flags & DIR_OR_DIRLNK)) {
attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
g_state.dircolor = 0; g_state.dircolor = 0;
} }
} }
/* /*
* Replace escape characters in a string with '?' * Replace escape characters in a string with '?'
* Adjust string length to maxcols if > 0; * Adjust string length to maxcols if > 0;
* Max supported str length: NAME_MAX; * Max supported str length: NAME_MAX;
*/ */
skipping to change at line 3499 skipping to change at line 3784
static wchar_t *unescape(const char *str, uint_t maxcols) static wchar_t *unescape(const char *str, uint_t maxcols)
{ {
wchar_t * const wbuf = (wchar_t *)g_buf; wchar_t * const wbuf = (wchar_t *)g_buf;
wchar_t *buf = wbuf; wchar_t *buf = wbuf;
size_t len = mbstowcs(wbuf, str, maxcols); /* Convert multi-byte to wide char */ size_t len = mbstowcs(wbuf, str, maxcols); /* Convert multi-byte to wide char */
len = wcswidth(wbuf, len); len = wcswidth(wbuf, len);
if (len >= maxcols) { if (len >= maxcols) {
size_t lencount = maxcols; size_t lencount = maxcols;
while (len > maxcols) /* Reduce wide chars one by one till it fit s */ while (len > maxcols) /* Reduce wide chars one by one till it fit s */
len = wcswidth(wbuf, --lencount); len = wcswidth(wbuf, --lencount);
wbuf[lencount] = L'\0'; wbuf[lencount] = L'\0';
} }
#endif #endif
while (*buf) { while (*buf) {
if (*buf <= '\x1f' || *buf == '\x7f') if (*buf <= '\x1f' || *buf == '\x7f')
*buf = '\?'; *buf = '\?';
++buf; ++buf;
} }
return wbuf; return wbuf;
} }
static off_t get_size(off_t size, off_t *pval, uint_t comp) static off_t get_size(off_t size, off_t *pval, int comp)
{ {
off_t rem = *pval; off_t rem = *pval;
off_t quo = rem / 10; off_t quo = rem / 10;
if ((rem - (quo * 10)) >= 5) { if ((rem - (quo * 10)) >= 5) {
rem = quo + 1; rem = quo + 1;
if (rem == comp) { if (rem == comp) {
++size; ++size;
rem = 0; rem = 0;
} }
skipping to change at line 3642 skipping to change at line 3928
#ifdef ICONS_ENABLED #ifdef ICONS_ENABLED
static const struct icon_pair *get_icon(const struct entry *ent) static const struct icon_pair *get_icon(const struct entry *ent)
{ {
ushort_t i = 0; ushort_t i = 0;
for (; i < sizeof(icons_name)/sizeof(struct icon_pair); ++i) for (; i < sizeof(icons_name)/sizeof(struct icon_pair); ++i)
if (strcasecmp(ent->name, icons_name[i].match) == 0) if (strcasecmp(ent->name, icons_name[i].match) == 0)
return &icons_name[i]; return &icons_name[i];
if (ent->flags & DIR_OR_LINK_TO_DIR) if (ent->flags & DIR_OR_DIRLNK)
return &dir_icon; return &dir_icon;
char *tmp = xextension(ent->name, ent->nlen); char *tmp = xextension(ent->name, ent->nlen);
if (!tmp) { if (!tmp) {
if (ent->mode & 0100) if (ent->mode & 0100)
return &exec_icon; return &exec_icon;
return &file_icon; return &file_icon;
} }
skipping to change at line 3744 skipping to change at line 4030
return C_EXE; return C_EXE;
} }
return C_FIL; return C_FIL;
case S_IFDIR: case S_IFDIR:
*pind = '/'; *pind = '/';
if (g_state.oldcolor) if (g_state.oldcolor)
return C_DIR; return C_DIR;
*pattr |= A_BOLD; *pattr |= A_BOLD;
return g_state.dirctx ? cfg.curctx + 1 : C_DIR; return g_state.dirctx ? cfg.curctx + 1 : C_DIR;
case S_IFLNK: case S_IFLNK:
if (ent->flags & DIR_OR_LINK_TO_DIR) { if (ent->flags & DIR_OR_DIRLNK) {
*pind = '/'; *pind = '/';
*pattr |= g_state.oldcolor ? A_DIM : A_BOLD; *pattr |= g_state.oldcolor ? A_DIM : A_BOLD;
} else { } else {
*pind = '@'; *pind = '@';
if (g_state.oldcolor) if (g_state.oldcolor)
*pattr |= A_DIM; *pattr |= A_DIM;
} }
if (!g_state.oldcolor || cfg.showdetail) if (!g_state.oldcolor || cfg.showdetail)
return (ent->flags & SYM_ORPHAN) ? C_ORP : C_LNK; return (ent->flags & SYM_ORPHAN) ? C_ORP : C_LNK;
return 0; return 0;
skipping to change at line 3836 skipping to change at line 4122
#else #else
addstr(unescape(ent->name, MIN(namecols, ent->nlen) + 1)); addstr(unescape(ent->name, MIN(namecols, ent->nlen) + 1));
#endif #endif
if (attrs) if (attrs)
attroff(attrs); attroff(attrs);
if (ind) if (ind)
addch(ind); addch(ind);
} }
static void savecurctx(settings *curcfg, char *path, char *curname, int nextctx) static void savecurctx(char *path, char *curname, int nextctx)
{ {
settings tmpcfg = *curcfg; settings tmpcfg = cfg;
context *ctxr = &g_ctx[nextctx]; context *ctxr = &g_ctx[nextctx];
/* Save current context */ /* Save current context */
if (ndents) if (ndents)
xstrsncpy(g_ctx[tmpcfg.curctx].c_name, curname, NAME_MAX + 1); xstrsncpy(g_ctx[tmpcfg.curctx].c_name, curname, NAME_MAX + 1);
else else
g_ctx[tmpcfg.curctx].c_name[0] = '\0'; g_ctx[tmpcfg.curctx].c_name[0] = '\0';
g_ctx[tmpcfg.curctx].c_cfg = tmpcfg; g_ctx[tmpcfg.curctx].c_cfg = tmpcfg;
if (ctxr->c_cfg.ctxactive) /* Switch to saved context */ if (ctxr->c_cfg.ctxactive) /* Switch to saved context */
tmpcfg = ctxr->c_cfg; tmpcfg = ctxr->c_cfg;
else { /* Set up a new context from current context */ else { /* Set up a new context from current context */
ctxr->c_cfg.ctxactive = 1; ctxr->c_cfg.ctxactive = 1;
xstrsncpy(ctxr->c_path, path, PATH_MAX); xstrsncpy(ctxr->c_path, path, PATH_MAX);
ctxr->c_last[0] = ctxr->c_name[0] = ctxr->c_fltr[0] = ctxr->c_flt r[1] = '\0'; ctxr->c_last[0] = ctxr->c_name[0] = ctxr->c_fltr[0] = ctxr->c_flt r[1] = '\0';
ctxr->c_cfg = tmpcfg; ctxr->c_cfg = tmpcfg;
} }
tmpcfg.curctx = nextctx; tmpcfg.curctx = nextctx;
*curcfg = tmpcfg; cfg = tmpcfg;
} }
#ifndef NOSSN #ifndef NOSSN
static void save_session(const char *sname, int *presel) static void save_session(const char *sname, int *presel)
{ {
int i; int fd, i;
session_header_t header; session_header_t header;
FILE *fsession;
bool status = FALSE; bool status = FALSE;
char ssnpath[PATH_MAX]; char ssnpath[PATH_MAX];
char spath[PATH_MAX]; char spath[PATH_MAX];
memset(&header, 0, sizeof(session_header_t)); memset(&header, 0, sizeof(session_header_t));
header.ver = SESSIONS_VERSION; header.ver = SESSIONS_VERSION;
for (i = 0; i < CTX_MAX; ++i) { for (i = 0; i < CTX_MAX; ++i) {
if (g_ctx[i].c_cfg.ctxactive) { if (g_ctx[i].c_cfg.ctxactive) {
skipping to change at line 3891 skipping to change at line 4176
header.pathln[i] = strnlen(g_ctx[i].c_path, PATH_MAX) + 1 ; header.pathln[i] = strnlen(g_ctx[i].c_path, PATH_MAX) + 1 ;
header.lastln[i] = strnlen(g_ctx[i].c_last, PATH_MAX) + 1 ; header.lastln[i] = strnlen(g_ctx[i].c_last, PATH_MAX) + 1 ;
header.nameln[i] = strnlen(g_ctx[i].c_name, NAME_MAX) + 1 ; header.nameln[i] = strnlen(g_ctx[i].c_name, NAME_MAX) + 1 ;
header.fltrln[i] = strnlen(g_ctx[i].c_fltr, REGEX_MAX) + 1; header.fltrln[i] = strnlen(g_ctx[i].c_fltr, REGEX_MAX) + 1;
} }
} }
mkpath(cfgpath, toks[TOK_SSN], ssnpath); mkpath(cfgpath, toks[TOK_SSN], ssnpath);
mkpath(ssnpath, sname, spath); mkpath(ssnpath, sname, spath);
fsession = fopen(spath, "wb"); fd = open(spath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (!fsession) { if (fd == -1) {
printwait(messages[MSG_SEL_MISSING], presel); printwait(messages[MSG_SEL_MISSING], presel);
return; return;
} }
if ((fwrite(&header, sizeof(header), 1, fsession) != 1) if ((write(fd, &header, sizeof(header)) != (ssize_t)sizeof(header))
|| (fwrite(&cfg, sizeof(cfg), 1, fsession) != 1)) || (write(fd, &cfg, sizeof(cfg)) != (ssize_t)sizeof(cfg)))
goto END; goto END;
for (i = 0; i < CTX_MAX; ++i) for (i = 0; i < CTX_MAX; ++i)
if ((fwrite(&g_ctx[i].c_cfg, sizeof(settings), 1, fsession) != 1) if ((write(fd, &g_ctx[i].c_cfg, sizeof(settings)) != (ssize_t)siz
|| (fwrite(&g_ctx[i].color, sizeof(uint_t), 1, fsession) eof(settings))
!= 1) || (write(fd, &g_ctx[i].color, sizeof(uint_t)) != (ssize_
t)sizeof(uint_t))
|| (header.nameln[i] > 0 || (header.nameln[i] > 0
&& fwrite(g_ctx[i].c_name, header.nameln[i], 1, fsess ion) != 1) && write(fd, g_ctx[i].c_name, header.nameln[i]) != (s size_t)header.nameln[i])
|| (header.lastln[i] > 0 || (header.lastln[i] > 0
&& fwrite(g_ctx[i].c_last, header.lastln[i], 1, fsess ion) != 1) && write(fd, g_ctx[i].c_last, header.lastln[i]) != (s size_t)header.lastln[i])
|| (header.fltrln[i] > 0 || (header.fltrln[i] > 0
&& fwrite(g_ctx[i].c_fltr, header.fltrln[i], 1, fsess ion) != 1) && write(fd, g_ctx[i].c_fltr, header.fltrln[i]) != (s size_t)header.fltrln[i])
|| (header.pathln[i] > 0 || (header.pathln[i] > 0
&& fwrite(g_ctx[i].c_path, header.pathln[i], 1, fsess ion) != 1)) && write(fd, g_ctx[i].c_path, header.pathln[i]) != (s size_t)header.pathln[i]))
goto END; goto END;
status = TRUE; status = TRUE;
END: END:
fclose(fsession); close(fd);
if (!status) if (!status)
printwait(messages[MSG_FAILED], presel); printwait(messages[MSG_FAILED], presel);
} }
static bool load_session(const char *sname, char **path, char **lastdir, char ** lastname, bool restore) static bool load_session(const char *sname, char **path, char **lastdir, char ** lastname, bool restore)
{ {
int i = 0; int fd, i = 0;
session_header_t header; session_header_t header;
FILE *fsession;
bool has_loaded_dynamically = !(sname || restore); bool has_loaded_dynamically = !(sname || restore);
bool status = (sname && g_state.picker); /* Picker mode with session prog ram option */ bool status = (sname && g_state.picker); /* Picker mode with session prog ram option */
char ssnpath[PATH_MAX]; char ssnpath[PATH_MAX];
char spath[PATH_MAX]; char spath[PATH_MAX];
mkpath(cfgpath, toks[TOK_SSN], ssnpath); mkpath(cfgpath, toks[TOK_SSN], ssnpath);
if (!restore) { if (!restore) {
sname = sname ? sname : xreadline(NULL, messages[MSG_SSN_NAME]); sname = sname ? sname : xreadline(NULL, messages[MSG_SSN_NAME]);
if (!sname[0]) if (!sname[0])
skipping to change at line 3951 skipping to change at line 4235
/* If user is explicitly loading the "last session", skip auto-sa ve */ /* If user is explicitly loading the "last session", skip auto-sa ve */
if ((sname[0] == '@') && !sname[1]) if ((sname[0] == '@') && !sname[1])
has_loaded_dynamically = FALSE; has_loaded_dynamically = FALSE;
} else } else
mkpath(ssnpath, "@", spath); mkpath(ssnpath, "@", spath);
if (has_loaded_dynamically) if (has_loaded_dynamically)
save_session("@", NULL); save_session("@", NULL);
fsession = fopen(spath, "rb"); fd = open(spath, O_RDONLY, 0666);
if (!fsession) { if (fd == -1) {
if (!status) { if (!status) {
printmsg(messages[MSG_SEL_MISSING]); printmsg(messages[MSG_SEL_MISSING]);
xdelay(XDELAY_INTERVAL_MS); xdelay(XDELAY_INTERVAL_MS);
} }
return FALSE; return FALSE;
} }
status = FALSE; status = FALSE;
if ((fread(&header, sizeof(header), 1, fsession) != 1) if ((read(fd, &header, sizeof(header)) != (ssize_t)sizeof(header))
|| (header.ver != SESSIONS_VERSION) || (header.ver != SESSIONS_VERSION)
|| (fread(&cfg, sizeof(cfg), 1, fsession) != 1)) || (read(fd, &cfg, sizeof(cfg)) != (ssize_t)sizeof(cfg)))
goto END; goto END;
g_ctx[cfg.curctx].c_name[0] = g_ctx[cfg.curctx].c_last[0] g_ctx[cfg.curctx].c_name[0] = g_ctx[cfg.curctx].c_last[0]
= g_ctx[cfg.curctx].c_fltr[0] = g_ctx[cfg.curctx].c_fltr[1] = '\0 '; = g_ctx[cfg.curctx].c_fltr[0] = g_ctx[cfg.curctx].c_fltr[1] = '\0 ';
for (; i < CTX_MAX; ++i) for (; i < CTX_MAX; ++i)
if ((fread(&g_ctx[i].c_cfg, sizeof(settings), 1, fsession) != 1) if ((read(fd, &g_ctx[i].c_cfg, sizeof(settings)) != (ssize_t)size
|| (fread(&g_ctx[i].color, sizeof(uint_t), 1, fsession) ! of(settings))
= 1) || (read(fd, &g_ctx[i].color, sizeof(uint_t)) != (ssize_t
)sizeof(uint_t))
|| (header.nameln[i] > 0 || (header.nameln[i] > 0
&& fread(g_ctx[i].c_name, header.nameln[i], 1, fsessi on) != 1) && read(fd, g_ctx[i].c_name, header.nameln[i]) != (ss ize_t)header.nameln[i])
|| (header.lastln[i] > 0 || (header.lastln[i] > 0
&& fread(g_ctx[i].c_last, header.lastln[i], 1, fsessi on) != 1) && read(fd, g_ctx[i].c_last, header.lastln[i]) != (ss ize_t)header.lastln[i])
|| (header.fltrln[i] > 0 || (header.fltrln[i] > 0
&& fread(g_ctx[i].c_fltr, header.fltrln[i], 1, fsessi on) != 1) && read(fd, g_ctx[i].c_fltr, header.fltrln[i]) != (ss ize_t)header.fltrln[i])
|| (header.pathln[i] > 0 || (header.pathln[i] > 0
&& fread(g_ctx[i].c_path, header.pathln[i], 1, fsessi on) != 1)) && read(fd, g_ctx[i].c_path, header.pathln[i]) != (ss ize_t)header.pathln[i]))
goto END; goto END;
*path = g_ctx[cfg.curctx].c_path; *path = g_ctx[cfg.curctx].c_path;
*lastdir = g_ctx[cfg.curctx].c_last; *lastdir = g_ctx[cfg.curctx].c_last;
*lastname = g_ctx[cfg.curctx].c_name; *lastname = g_ctx[cfg.curctx].c_name;
set_sort_flags('\0'); /* Set correct sort options */ set_sort_flags('\0'); /* Set correct sort options */
status = TRUE; status = TRUE;
END: END:
fclose(fsession); close(fd);
if (!status) { if (!status) {
printmsg(messages[MSG_FAILED]); printmsg(messages[MSG_FAILED]);
xdelay(XDELAY_INTERVAL_MS); xdelay(XDELAY_INTERVAL_MS);
} else if (restore) } else if (restore)
unlink(spath); unlink(spath);
return status; return status;
} }
#endif #endif
skipping to change at line 4013 skipping to change at line 4297
{ {
uchar_t r = cfg.curctx; uchar_t r = cfg.curctx;
do do
r = (r + 1) & ~CTX_MAX; r = (r + 1) & ~CTX_MAX;
while (g_ctx[r].c_cfg.ctxactive && (r != cfg.curctx)); while (g_ctx[r].c_cfg.ctxactive && (r != cfg.curctx));
return r; return r;
} }
/* ctx is absolute: 1 to 4, + for smart context */
static void set_smart_ctx(int ctx, char *nextpath, char **path, char **lastname,
char **lastdir)
{
if (ctx == '+') /* Get smart context */
ctx = (int)(get_free_ctx() + 1);
if (ctx == 0 || ctx == cfg.curctx + 1) { /* Same context */
/* Mark current directory */
free(mark);
mark = xstrdup(*path);
xstrsncpy(*lastdir, *path, PATH_MAX);
xstrsncpy(*path, nextpath, PATH_MAX);
} else { /* New context */
--ctx;
/* Deactivate the new context and build from scratch */
g_ctx[ctx].c_cfg.ctxactive = 0;
savecurctx(nextpath, pdents[cur].name, ctx);
*path = g_ctx[ctx].c_path;
*lastdir = g_ctx[ctx].c_last;
*lastname = g_ctx[ctx].c_name;
}
}
/* /*
* 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 o
* for now) or shows full command output in pager. utput in pager.
* * Uses g_buf internally.
* If page is valid, returns NULL
*/ */
static char *get_output(char *buf, const size_t bytes, const char *file, static bool get_output(char *file, char *arg1, char *arg2, int fdout, bool multi
const char *arg1, const char *arg2, const bool page) , bool page)
{ {
pid_t pid; pid_t pid;
int pipefd[2]; int pipefd[2];
FILE *pf; int index = 0, flags;
int tmp, flags; bool ret = FALSE;
char *ret = NULL; bool tmpfile = ((fdout == -1) && page);
char *argv[EXEC_ARGS_MAX] = {0};
char *cmd = NULL;
int fd = -1;
ssize_t len;
if (tmpfile) {
fdout = create_tmp_file();
if (fdout == -1)
return FALSE;
}
if (multi) {
cmd = parseargs(file, argv, &index);
if (!cmd)
return FALSE;
} else
argv[index++] = file;
argv[index] = arg1;
argv[++index] = arg2;
if (pipe(pipefd) == -1) 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();
if (pid == 0) { if (pid == 0) {
/* In child */ /* In child */
close(pipefd[0]); close(pipefd[0]);
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) { while ((len = read(pipefd[0], g_buf, CMD_LEN_MAX - 1)) > 0) {
pf = fdopen(pipefd[0], "r"); ret = TRUE;
if (pf) { if (fdout == -1) /* Read only the first line of output to buffer
ret = fgets(buf, bytes, pf); */
close(pipefd[0]); break;
} if (write(fdout, g_buf, len) != len)
break;
}
close(pipefd[0]);
if (!page)
return ret; return ret;
}
pid = fork(); if (tmpfile) {
if (pid == 0) { close(fdout);
/* Show in pager in child */ close(fd);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
spawn(pager, NULL, NULL, NULL, F_CLI);
_exit(EXIT_SUCCESS);
} }
/* In parent */ spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY);
waitpid(pid, NULL, 0);
close(pipefd[0]);
return NULL; if (tmpfile)
} unlink(g_tmpfpath);
static void pipetof(char *cmd, FILE *fout)
{
FILE *fin = popen(cmd, "r");
if (fin) { return TRUE;
while (fgets(g_buf, CMD_LEN_MAX - 1, fin))
fprintf(fout, "%s", g_buf);
pclose(fin);
}
} }
/* /*
* 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)
{ {
int fd; static char * const cmds[] = {
FILE *fp; #ifdef FILE_MIME_OPTS
char *p, *begin = g_buf; ("file " FILE_MIME_OPTS),
size_t r; #endif
"file -b",
"stat",
};
fd = create_tmp_file(); size_t r = ELEMENTS(cmds);
int fd = create_tmp_file();
if (fd == -1) if (fd == -1)
return FALSE; return FALSE;
r = xstrsncpy(g_buf, "stat \"", PATH_MAX); while (r)
r += xstrsncpy(g_buf + r - 1, fpath, PATH_MAX); get_output(cmds[--r], fpath, NULL, fd, TRUE, FALSE);
g_buf[r - 2] = '\"';
g_buf[r - 1] = '\0';
DPRINTF_S(g_buf);
fp = fdopen(fd, "w");
if (!fp) {
close(fd);
return FALSE;
}
pipetof(g_buf, fp);
if (S_ISREG(sb->st_mode)) {
/* Show file(1) output */
p = get_output(g_buf, CMD_LEN_MAX, "file", "-b", fpath, FALSE);
if (p) {
fprintf(fp, "\n\n ");
while (*p) {
if (*p == ',') {
*p = '\0';
fprintf(fp, " %s\n", begin);
begin = p + 1;
}
++p;
}
fprintf(fp, " %s\n ", begin);
#ifdef FILE_MIME_OPTS
/* Show the file MIME type */
get_output(g_buf, CMD_LEN_MAX, "file", FILE_MIME_OPTS, fp
ath, FALSE);
fprintf(fp, "%s", g_buf);
#endif
}
}
fprintf(fp, "\n");
fclose(fp);
close(fd); close(fd);
spawn(pager, g_tmpfpath, NULL, NULL, F_CLI); spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY);
unlink(g_tmpfpath); unlink(g_tmpfpath);
return TRUE; return TRUE;
} }
static bool xchmod(const char *fpath, mode_t mode) static bool xchmod(const char *fpath, mode_t mode)
{ {
/* (Un)set (S_IXUSR | S_IXGRP | S_IXOTH) */ /* (Un)set (S_IXUSR | S_IXGRP | S_IXOTH) */
(0100 & mode) ? (mode &= ~0111) : (mode |= 0111); (0100 & mode) ? (mode &= ~0111) : (mode |= 0111);
return (chmod(fpath, mode) == 0); return (chmod(fpath, mode) == 0);
skipping to change at line 4176 skipping to change at line 4459
if (statvfs(path, &svb) == -1) if (statvfs(path, &svb) == -1)
return 0; return 0;
if (type == CAPACITY) if (type == CAPACITY)
return (size_t)svb.f_blocks << ffs((int)(svb.f_frsize >> 1)); return (size_t)svb.f_blocks << ffs((int)(svb.f_frsize >> 1));
return (size_t)svb.f_bavail << ffs((int)(svb.f_frsize >> 1)); return (size_t)svb.f_bavail << ffs((int)(svb.f_frsize >> 1));
} }
/* List or extract archive */
static void handle_archive(char *fpath, char op)
{
char arg[] = "-tvf"; /* options for tar/bsdtar to list files */
char *util;
if (getutil(utils[UTIL_ATOOL])) {
util = utils[UTIL_ATOOL];
arg[1] = op;
arg[2] = '\0';
} else if (getutil(utils[UTIL_BSDTAR])) {
util = utils[UTIL_BSDTAR];
if (op == 'x')
arg[1] = op;
} else if (is_suffix(fpath, ".zip")) {
util = utils[UTIL_UNZIP];
arg[1] = (op == 'l') ? 'v' /* verbose listing */ : '\0';
arg[2] = '\0';
} else {
util = utils[UTIL_TAR];
if (op == 'x')
arg[1] = op;
}
if (op == 'x') /* extract */
spawn(util, arg, fpath, NULL, F_NORMAL);
else /* list */
get_output(NULL, 0, util, arg, fpath, TRUE);
}
static char *visit_parent(char *path, char *newpath, int *presel)
{
char *dir;
/* There is no going back */
if (istopdir(path)) {
/* Continue in type-to-nav mode, if enabled */
if (cfg.filtermode && presel)
*presel = FILTER;
return NULL;
}
/* Use a copy as xdirname() may change the string passed */
if (newpath)
xstrsncpy(newpath, path, PATH_MAX);
else
newpath = path;
dir = xdirname(newpath);
if (chdir(dir) == -1) {
printwarn(presel);
return NULL;
}
return dir;
}
static void valid_parent(char *path, char *lastname)
{
/* Save history */
xstrsncpy(lastname, xbasename(path), NAME_MAX + 1);
while (!istopdir(path))
if (visit_parent(path, NULL, NULL))
break;
printwarn(NULL);
xdelay(XDELAY_INTERVAL_MS);
}
/* Create non-existent parents and a file or dir */ /* Create non-existent parents and a file or dir */
static bool xmktree(char *path, bool dir) static bool xmktree(char *path, bool dir)
{ {
char *p = path; char *p = path;
char *slash = path; char *slash = path;
if (!p || !*p) if (!p || !*p)
return FALSE; return FALSE;
/* Skip the first '/' */ /* Skip the first '/' */
skipping to change at line 4313 skipping to change at line 4526
DPRINTF_S(strerror(errno)); DPRINTF_S(strerror(errno));
return FALSE; return FALSE;
} }
close(fd); close(fd);
} }
return TRUE; return TRUE;
} }
/* List or extract archive */
static bool handle_archive(char *fpath /* in-out param */, char op)
{
char arg[] = "-tvf"; /* options for tar/bsdtar to list files */
char *util, *outdir = NULL;
bool x_to = FALSE;
bool is_atool = getutil(utils[UTIL_ATOOL]);
if (op == 'x') {
outdir = xreadline(is_atool ? "." : xbasename(fpath), messages[MS
G_NEW_PATH]);
if (!outdir || !*outdir) { /* Cancelled */
printwait(messages[MSG_CANCEL], NULL);
return FALSE;
}
/* Do not create smart context for current dir */
if (!(*outdir == '.' && outdir[1] == '\0')) {
if (!xmktree(outdir, TRUE) || (chdir(outdir) == -1)) {
printwarn(NULL);
return FALSE;
}
/* Copy the new dir path to open it in smart context */
outdir = realpath(".", NULL);
x_to = TRUE;
}
}
if (is_atool) {
util = utils[UTIL_ATOOL];
arg[1] = op;
arg[2] = '\0';
} else if (getutil(utils[UTIL_BSDTAR])) {
util = utils[UTIL_BSDTAR];
if (op == 'x')
arg[1] = op;
} else if (is_suffix(fpath, ".zip")) {
util = utils[UTIL_UNZIP];
arg[1] = (op == 'l') ? 'v' /* verbose listing */ : '\0';
arg[2] = '\0';
} else {
util = utils[UTIL_TAR];
if (op == 'x')
arg[1] = op;
}
if (op == 'x') /* extract */
spawn(util, arg, fpath, NULL, F_NORMAL | F_MULTI);
else /* list */
get_output(util, arg, fpath, -1, TRUE, TRUE);
if (x_to) {
if (chdir(xdirname(fpath)) == -1) {
printwarn(NULL);
free(outdir);
return FALSE;
}
xstrsncpy(fpath, outdir, PATH_MAX);
free(outdir);
} else if (op == 'x')
fpath[0] = '\0';
return TRUE;
}
static char *visit_parent(char *path, char *newpath, int *presel)
{
char *dir;
/* There is no going back */
if (istopdir(path)) {
/* Continue in type-to-nav mode, if enabled */
if (cfg.filtermode && presel)
*presel = FILTER;
return NULL;
}
/* Use a copy as xdirname() may change the string passed */
if (newpath)
xstrsncpy(newpath, path, PATH_MAX);
else
newpath = path;
dir = xdirname(newpath);
if (chdir(dir) == -1) {
printwarn(presel);
return NULL;
}
return dir;
}
static void valid_parent(char *path, char *lastname)
{
/* Save history */
xstrsncpy(lastname, xbasename(path), NAME_MAX + 1);
while (!istopdir(path))
if (visit_parent(path, NULL, NULL))
break;
printwarn(NULL);
xdelay(XDELAY_INTERVAL_MS);
}
static bool archive_mount(char *newpath) static bool archive_mount(char *newpath)
{ {
char *str = "install archivemount"; char *str = "install archivemount";
char *dir, *cmd = str + 8; /* Start of "archivemount" */ char *dir, *cmd = str + 8; /* Start of "archivemount" */
char *name = pdents[cur].name; char *name = pdents[cur].name;
size_t len = pdents[cur].nlen; size_t len = pdents[cur].nlen;
char mntpath[PATH_MAX]; char mntpath[PATH_MAX];
if (!getutil(cmd)) { if (!getutil(cmd)) {
printmsg(str); printmsg(str);
skipping to change at line 4438 skipping to change at line 4754
return TRUE; return TRUE;
} }
/* /*
* Unmounts if the directory represented by name is a mount point. * Unmounts if the directory represented by name is a mount point.
* Otherwise, asks for hostname * Otherwise, asks for hostname
* Returns TRUE if directory needs to be refreshed *. * Returns TRUE if directory needs to be refreshed *.
*/ */
static bool unmount(char *name, char *newpath, int *presel, char *currentpath) static bool unmount(char *name, char *newpath, int *presel, char *currentpath)
{ {
#if defined (__APPLE__) || defined (__FreeBSD__) #if defined(__APPLE__) || defined(__FreeBSD__)
static char cmd[] = "umount"; static char cmd[] = "umount";
#else #else
static char cmd[] = "fusermount3"; /* Arch Linux utility */ static char cmd[] = "fusermount3"; /* Arch Linux utility */
static bool found = FALSE; static bool found = FALSE;
#endif #endif
char *tmp = name; char *tmp = name;
struct stat sb, psb; struct stat sb, psb;
bool child = FALSE; bool child = FALSE;
bool parent = FALSE; bool parent = FALSE;
bool hovered = TRUE; bool hovered = FALSE;
char mntpath[PATH_MAX]; char mntpath[PATH_MAX];
#if !defined ( __APPLE__) && !defined (__FreeBSD__) #if !defined(__APPLE__) && !defined(__FreeBSD__)
/* On Ubuntu it's fusermount */ /* On Ubuntu it's fusermount */
if (!found && !getutil(cmd)) { if (!found && !getutil(cmd)) {
cmd[10] = '\0'; cmd[10] = '\0';
found = TRUE; found = TRUE;
} }
#endif #endif
mkpath(cfgpath, toks[TOK_MNT], mntpath); mkpath(cfgpath, toks[TOK_MNT], mntpath);
if (tmp && strcmp(mntpath, currentpath) == 0) { if (tmp && strcmp(mntpath, currentpath) == 0) {
skipping to change at line 4475 skipping to change at line 4791
if (!child && !parent) { if (!child && !parent) {
*presel = MSGWAIT; *presel = MSGWAIT;
return FALSE; return FALSE;
} }
} }
if (!tmp || !child || !S_ISDIR(sb.st_mode) || (child && parent && sb.st_d ev == psb.st_dev)) { if (!tmp || !child || !S_ISDIR(sb.st_mode) || (child && parent && sb.st_d ev == psb.st_dev)) {
tmp = xreadline(NULL, messages[MSG_HOSTNAME]); tmp = xreadline(NULL, messages[MSG_HOSTNAME]);
if (!tmp[0]) if (!tmp[0])
return FALSE; return FALSE;
hovered = FALSE; if (name && (tmp[0] == '-') && (tmp[1] == '\0')) {
mkpath(currentpath, name, newpath);
hovered = TRUE;
}
} }
/* Create the mount point */ if (!hovered)
mkpath(mntpath, tmp, newpath); mkpath(mntpath, tmp, newpath);
if (!xdiraccess(newpath)) { if (!xdiraccess(newpath)) {
*presel = MSGWAIT; *presel = MSGWAIT;
return FALSE; return FALSE;
} }
#if defined (__APPLE__) || defined (__FreeBSD__) #if defined(__APPLE__) || defined(__FreeBSD__)
if (spawn(cmd, newpath, NULL, NULL, F_NORMAL)) { if (spawn(cmd, newpath, NULL, NULL, F_NORMAL)) {
#else #else
if (spawn(cmd, "-u", newpath, NULL, F_NORMAL)) { if (spawn(cmd, "-qu", newpath, NULL, F_NORMAL)) {
#endif #endif
if (!xconfirm(get_input(messages[MSG_LAZY]))) if (!xconfirm(get_input(messages[MSG_LAZY])))
return FALSE; return FALSE;
#ifdef __APPLE__ #ifdef __APPLE__
if (spawn(cmd, "-l", newpath, NULL, F_NORMAL)) { if (spawn(cmd, "-l", newpath, NULL, F_NORMAL)) {
#elif defined (__FreeBSD__) #elif defined(__FreeBSD__)
if (spawn(cmd, "-f", newpath, NULL, F_NORMAL)) { if (spawn(cmd, "-f", newpath, NULL, F_NORMAL)) {
#else #else
if (spawn(cmd, "-uz", newpath, NULL, F_NORMAL)) { if (spawn(cmd, "-quz", newpath, NULL, F_NORMAL)) {
#endif #endif
printwait(messages[MSG_FAILED], presel); printwait(messages[MSG_FAILED], presel);
return FALSE; return FALSE;
} }
} }
if (rmdir(newpath) == -1) { if (rmdir(newpath) == -1) {
printwarn(presel); printwarn(presel);
return FALSE; return FALSE;
} }
return hovered; return TRUE;
} }
static void lock_terminal(void) static void lock_terminal(void)
{ {
spawn(xgetenv("NNN_LOCKER", utils[UTIL_LOCKER]), NULL, NULL, NULL, F_CLI) ; spawn(xgetenv("NNN_LOCKER", utils[UTIL_LOCKER]), NULL, NULL, NULL, F_CLI) ;
} }
static void printkv(kv *kvarr, FILE *fp, uchar_t max, uchar_t id) static void printkv(kv *kvarr, int fd, uchar_t max, uchar_t id)
{ {
char *val = (id == NNN_BMS) ? bmstr : pluginstr; char *val = (id == NNN_BMS) ? bmstr : pluginstr;
for (uchar_t i = 0; i < max && kvarr[i].key; ++i) for (uchar_t i = 0; i < max && kvarr[i].key; ++i)
fprintf(fp, " %c: %s\n", (char)kvarr[i].key, val + kvarr[i].off); dprintf(fd, " %c: %s\n", (char)kvarr[i].key, val + kvarr[i].off);
} }
static void printkeys(kv *kvarr, char *buf, uchar_t max) static void printkeys(kv *kvarr, char *buf, uchar_t max)
{ {
uchar_t i = 0; uchar_t i = 0;
for (; i < max && kvarr[i].key; ++i) { for (; i < max && kvarr[i].key; ++i) {
buf[i << 1] = ' '; buf[i << 1] = ' ';
buf[(i << 1) + 1] = kvarr[i].key; buf[(i << 1) + 1] = kvarr[i].key;
} }
skipping to change at line 4575 skipping to change at line 4895
/* /*
* The help string tokens (each line) start with a HEX value * The help string tokens (each line) start with a HEX value
* which indicates the number of spaces to print before the * which indicates the number of spaces to print before the
* particular token. This method was chosen instead of a flat * particular token. This method was chosen instead of a flat
* string because the number of bytes in help was increasing * string because the number of bytes in help was increasing
* the binary size by around a hundred bytes. This would only * the binary size by around a hundred bytes. This would only
* have increased as we keep adding new options. * have increased as we keep adding new options.
*/ */
static void show_help(const char *path) static void show_help(const char *path)
{ {
int fd;
FILE *fp;
const char *start, *end; const char *start, *end;
const char helpstr[] = { const char helpstr[] = {
"0\n" "0\n"
"1NAVIGATION\n" "1NAVIGATION\n"
"9Up k Up%-16cPgUp ^U Scroll up\n" "9Up k Up%-16cPgUp ^U Page up\n"
"9Dn j Down%-14cPgDn ^D Scroll down\n" "9Dn j Down%-14cPgDn ^D Page down\n"
"9Lt h Parent%-12c~ ` @ - ~, /, start, prev\n" "9Lt h Parent%-12c~ ` @ - ~, /, start, prev\n"
"5Ret Rt l Open%-20c' First file/match\n" "5Ret Rt l Open%-20c' First file/match\n"
"9g ^A Top%-21c. Toggle hidden\n" "9g ^A Top%-21c. Toggle hidden\n"
"9G ^E End%-21c+ Toggle auto-advance\n" "9G ^E End%-21c+ Toggle auto-advance\n"
"9b ^/ Bookmark key%-12c, Mark CWD\n" "c, Mark CWD%-13cb ^/ Select bookmark\n"
"a1-4 Context 1-4%-7c(Sh)Tab Cycle/new context\n" "a1-4 Context%-11c(Sh)Tab Cycle/new context\n"
"aEsc Send to FIFO%-11c^L Redraw\n" "62Esc ^Q Quit%-20cq Quit context\n"
"cQ Pick/err, quit%-9c^G QuitCD\n" "b^G QuitCD%-18cQ Pick/err, quit\n"
"cq Quit context%-6c2Esc ^Q Quit\n" "0\n"
"c? Help, conf\n" "1FILTER & PROMPT\n"
"1FILTER & PROMPT\n" "c/ Filter%-17c^N Toggle type-to-nav\n"
"c/ Filter%-12cAlt+Esc Unfilter, quit context\n" "aEsc Exit prompt%-12c^L Toggle last filter\n"
"aEsc Exit prompt%-12c^L Clear prompt/last filter\n" "d%-20cAlt+Esc Unfilter, quit context\n"
"b^N Toggle type-to-nav%-0c\n" "0\n"
"1FILES\n" "1FILES\n"
"9o ^O Open with...%-12cn Create new/link\n" "9o ^O Open with%-15cn Create new/link\n"
"9f ^F File details%-12cd Detail mode toggle\n" "9f ^F File stats%-14cd Detail mode toggle\n"
"b^R Rename/dup%-14cr Batch rename\n" "b^R Rename/dup%-14cr Batch rename\n"
"cz Archive%-17ce Edit file\n" "cz Archive%-17ce Edit file\n"
"c* Toggle exe%-14c> Export list\n" "c* Toggle exe%-14c> Export list\n"
"5Space ^J (Un)select%-12cm-m Mark range/clear sel\n" "5Space ^J (Un)select%-12cm-m Select range/clear\n"
"ca Select all%-14cA Invert sel\n" "ca Select all%-14cA Invert sel\n"
"9p ^P Copy sel here%-8cw ^W Cp/mv sel as\n" "9p ^P Copy here%-12cw ^W Cp/mv sel as\n"
"9v ^V Move sel here%-11cE Edit sel\n" "9v ^V Move here%-15cE Edit sel list\n"
"9x ^X Delete\n" "9x ^X Delete%-16cEsc Send to FIFO\n"
"1MISC\n" "0\n"
"1MISC\n"
"8Alt ; Select plugin%-11c= Launch app\n" "8Alt ; Select plugin%-11c= Launch app\n"
"9! ^] Shell%-19c] Cmd prompt\n" "9! ^] Shell%-19c] Cmd prompt\n"
"cc Connect remote%-10cu Unmount remote/archive\n" "cc Connect remote%-10cu Unmount remote/archive\n"
"9t ^T Sort toggles%-12cs Manage session\n" "9t ^T Sort toggles%-12cs Manage session\n"
"cT Set time type%-11c0 Lock\n" "cT Set time type%-11c0 Lock\n"
"b^L Redraw%-18c? Help, conf\n"
}; };
fd = create_tmp_file(); int fd = create_tmp_file();
if (fd == -1) if (fd == -1)
return; return;
fp = fdopen(fd, "w"); char *prog = xgetenv(env_cfg[NNN_HELP], NULL);
if (!fp) { if (prog)
close(fd); get_output(prog, NULL, NULL, fd, TRUE, FALSE);
return;
}
if (g_state.fortune && getutil("fortune"))
#ifndef __HAIKU__
pipetof("fortune -s", fp);
#else
pipetof("fortune", fp);
#endif
start = end = helpstr; start = end = helpstr;
while (*end) { while (*end) {
if (*end == '\n') { if (*end == '\n') {
snprintf(g_buf, CMD_LEN_MAX, "%*c%.*s", snprintf(g_buf, CMD_LEN_MAX, "%*c%.*s",
xchartohex(*start), ' ', (int)(end - start), sta rt + 1); xchartohex(*start), ' ', (int)(end - start), sta rt + 1);
fprintf(fp, g_buf, ' '); dprintf(fd, g_buf, ' ');
start = end + 1; start = end + 1;
} }
++end; ++end;
} }
fprintf(fp, "\nVOLUME: %s of ", coolsize(get_fs_info(path, FREE))); dprintf(fd, "\nVOLUME: %s of ", coolsize(get_fs_info(path, FREE)));
fprintf(fp, "%s free\n\n", coolsize(get_fs_info(path, CAPACITY))); dprintf(fd, "%s free\n\n", coolsize(get_fs_info(path, CAPACITY)));
if (bookmark) { if (bookmark) {
fprintf(fp, "BOOKMARKS\n"); dprintf(fd, "BOOKMARKS\n");
printkv(bookmark, fp, maxbm, NNN_BMS); printkv(bookmark, fd, maxbm, NNN_BMS);
fprintf(fp, "\n"); dprintf(fd, "\n");
} }
if (plug) { if (plug) {
fprintf(fp, "PLUGIN KEYS\n"); dprintf(fd, "PLUGIN KEYS\n");
printkv(plug, fp, maxplug, NNN_PLUG); printkv(plug, fd, maxplug, NNN_PLUG);
fprintf(fp, "\n"); dprintf(fd, "\n");
} }
for (uchar_t i = NNN_OPENER; i <= NNN_TRASH; ++i) { for (uchar_t i = NNN_OPENER; i <= NNN_TRASH; ++i) {
start = getenv(env_cfg[i]); start = getenv(env_cfg[i]);
if (start) if (start)
fprintf(fp, "%s: %s\n", env_cfg[i], start); dprintf(fd, "%s: %s\n", env_cfg[i], start);
} }
if (selpath) if (selpath)
fprintf(fp, "SELECTION FILE: %s\n", selpath); dprintf(fd, "SELECTION FILE: %s\n", selpath);
fprintf(fp, "\nv%s\n%s\n", VERSION, GENERAL_INFO); dprintf(fd, "\nv%s\n%s\n", VERSION, GENERAL_INFO);
fclose(fp);
close(fd); close(fd);
spawn(pager, g_tmpfpath, NULL, NULL, F_CLI); spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY);
unlink(g_tmpfpath); unlink(g_tmpfpath);
} }
static bool run_cmd_as_plugin(const char *file, char *runfile, uchar_t flags) static bool run_cmd_as_plugin(const char *file, char *runfile, uchar_t flags)
{ {
size_t len; size_t len;
xstrsncpy(g_buf, file, PATH_MAX); xstrsncpy(g_buf, file, PATH_MAX);
len = xstrlen(g_buf); len = xstrlen(g_buf);
skipping to change at line 4695 skipping to change at line 5005
flags &= ~F_CONFIRM; /* Skip user confirmation */ flags &= ~F_CONFIRM; /* Skip user confirmation */
g_buf[len - 1] = '\0'; /* Get rid of trailing no confirmation sym bol */ g_buf[len - 1] = '\0'; /* Get rid of trailing no confirmation sym bol */
--len; --len;
} }
if (is_suffix(g_buf, " $nnn")) if (is_suffix(g_buf, " $nnn"))
g_buf[len - 5] = '\0'; /* Set `\0` to clear ' $nnn' suffix */ g_buf[len - 5] = '\0'; /* Set `\0` to clear ' $nnn' suffix */
else else
runfile = NULL; runfile = NULL;
spawn(g_buf, runfile, NULL, NULL, flags); if (flags & F_PAGE)
get_output(g_buf, runfile, NULL, -1, TRUE, TRUE);
else
spawn(g_buf, runfile, NULL, NULL, flags);
return TRUE; return TRUE;
} }
static bool plctrl_init(void) static bool plctrl_init(void)
{ {
size_t len; size_t len;
/* g_tmpfpath is used to generate tmp file names */ /* g_tmpfpath is used to generate tmp file names */
g_tmpfpath[tmpfplen - 1] = '\0'; g_tmpfpath[tmpfplen - 1] = '\0';
len = xstrsncpy(g_pipepath, g_tmpfpath, TMP_LEN_MAX); len = xstrsncpy(g_pipepath, g_tmpfpath, TMP_LEN_MAX);
skipping to change at line 4719 skipping to change at line 5033
setenv(env_cfg[NNN_PIPE], g_pipepath, TRUE); setenv(env_cfg[NNN_PIPE], g_pipepath, TRUE);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static void rmlistpath(void) static void rmlistpath(void)
{ {
if (listpath) { if (listpath) {
DPRINTF_S(__func__); DPRINTF_S(__func__);
DPRINTF_S(listpath); DPRINTF_S(listpath);
spawn("rm -rf", listpath, NULL, NULL, F_NOTRACE | F_MULTI); spawn(utils[UTIL_RM_RF], listpath, NULL, NULL, F_NOTRACE | F_MULT I);
/* Do not free if program was started in list mode */ /* Do not free if program was started in list mode */
if (listpath != initpath) if (listpath != initpath)
free(listpath); free(listpath);
listpath = NULL; listpath = NULL;
} }
} }
static ssize_t read_nointr(int fd, void *buf, size_t count) static ssize_t read_nointr(int fd, void *buf, size_t count)
{ {
ssize_t len; ssize_t len;
do do
len = read(fd, buf, count); len = read(fd, buf, count);
while (len == -1 && errno == EINTR); while (len == -1 && errno == EINTR);
return len; return len;
} }
static void readpipe(int fd, char **path, char **lastname, char **lastdir) static void readpipe(int fd, char **path, char **lastname, char **lastdir)
{ {
int r;
char ctx, *nextpath = NULL; char ctx, *nextpath = NULL;
if (read_nointr(fd, g_buf, 1) != 1) if (read_nointr(fd, g_buf, 1) != 1)
return; return;
if (g_buf[0] == '-') { /* Clear selection on '-' */ if (g_buf[0] == '-') { /* Clear selection on '-' */
clearselection(); clearselection();
if (read_nointr(fd, g_buf, 1) != 1) if (read_nointr(fd, g_buf, 1) != 1)
return; return;
} }
skipping to change at line 4774 skipping to change at line 5087
char op = g_buf[0]; char op = g_buf[0];
if (op == 'c') { if (op == 'c') {
ssize_t len = read_nointr(fd, g_buf, PATH_MAX); ssize_t len = read_nointr(fd, g_buf, PATH_MAX);
if (len <= 0) if (len <= 0)
return; return;
g_buf[len] = '\0'; /* Terminate the path read */ g_buf[len] = '\0'; /* Terminate the path read */
nextpath = g_buf; if (g_buf[0] == '/') {
nextpath = g_buf;
len = xstrlen(g_buf);
while (--len && (g_buf[len] == '/')) /* Trim all trailing
'/' */
g_buf[len] = '\0';
}
} else if (op == 'l') { } else if (op == 'l') {
rmlistpath(); /* Remove last list mode path, if any */ rmlistpath(); /* Remove last list mode path, if any */
nextpath = load_input(fd, *path); nextpath = load_input(fd, *path);
} else if (op == 'p') { } else if (op == 'p') {
free(selpath); free(selpath);
selpath = NULL; selpath = NULL;
clearselection(); clearselection();
g_state.picker = 0; g_state.picker = 0;
g_state.picked = 1; g_state.picked = 1;
} }
if (nextpath) { if (nextpath)
if (ctx == 0 || ctx == cfg.curctx + 1) { /* Same context */ set_smart_ctx(ctx, nextpath, path, lastname, lastdir);
xstrsncpy(*lastdir, *path, PATH_MAX);
xstrsncpy(*path, nextpath, PATH_MAX);
DPRINTF_S(*path);
} else { /* New context */
r = ctx - 1;
/* Deactivate the new context and build from scratch */
g_ctx[r].c_cfg.ctxactive = 0;
savecurctx(&cfg, nextpath, pdents[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, ch ar **lastname, char **lastdir) static bool run_plugin(char **path, const char *file, char *runfile, char **last name, char **lastdir)
{ {
pid_t p; pid_t p;
bool cmd_as_plugin = FALSE; bool cmd_as_plugin = FALSE;
uchar_t flags = 0; uchar_t flags = 0;
if (!g_state.pluginit) { if (!g_state.pluginit) {
plctrl_init(); plctrl_init();
g_state.pluginit = 1; g_state.pluginit = 1;
} }
if (*file == '_') { /* Check for run-cmd-as-plugin mode */
if (*file == '!') {
flags = F_MULTI | F_CONFIRM; flags = F_MULTI | F_CONFIRM;
/* Get rid of preceding _ */
++file; ++file;
if (!*file)
return FALSE;
/* Check if GUI flags are to be used */ if (*file == '|') { /* Check if output should be paged */
if (*file == '|') { flags |= F_PAGE;
++file;
} else if (*file == '&') { /* Check if GUI flags are to be used *
/
flags = F_NOTRACE | F_NOWAIT; flags = F_NOTRACE | F_NOWAIT;
++file; ++file;
}
if (!*file) if (!*file)
return FALSE; return FALSE;
run_cmd_as_plugin(file, runfile, flags); if ((flags & F_NOTRACE) || (flags & F_PAGE))
return TRUE; return run_cmd_as_plugin(file, runfile, flags);
}
cmd_as_plugin = TRUE; cmd_as_plugin = TRUE;
} }
if (mkfifo(g_pipepath, 0600) != 0) if (mkfifo(g_pipepath, 0600) != 0)
return EXIT_FAILURE; return FALSE;
exitcurses(); exitcurses();
if ((p = fork()) == 0) { // In child p = fork();
if (!p) { // In child
int wfd = open(g_pipepath, O_WRONLY | O_CLOEXEC); int wfd = open(g_pipepath, O_WRONLY | O_CLOEXEC);
if (wfd == -1) if (wfd == -1)
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
if (!cmd_as_plugin) { if (!cmd_as_plugin) {
char *sel = NULL; char *sel = NULL;
char std[2] = "-"; char std[2] = "-";
/* Generate absolute path to plugin */ /* Generate absolute path to plugin */
skipping to change at line 4889 skipping to change at line 5195
/* wait for the child to finish. no zombies allowed */ /* wait for the child to finish. no zombies allowed */
waitpid(p, NULL, 0); waitpid(p, NULL, 0);
refresh(); refresh();
unlink(g_pipepath); unlink(g_pipepath);
return TRUE; return TRUE;
} }
static bool plugscript(const char *plugin, uchar_t flags)
{
mkpath(plgpath, plugin, g_buf);
if (!access(g_buf, X_OK)) {
spawn(g_buf, NULL, NULL, NULL, flags);
return TRUE;
}
return FALSE;
}
static bool launch_app(char *newpath) static bool launch_app(char *newpath)
{ {
int r = F_NORMAL; int r = F_NORMAL;
char *tmp = newpath; char *tmp = newpath;
mkpath(plgpath, utils[UTIL_LAUNCH], newpath); mkpath(plgpath, utils[UTIL_LAUNCH], newpath);
if (!getutil(utils[UTIL_FZF]) || access(newpath, X_OK) < 0) { if (!getutil(utils[UTIL_FZF]) || access(newpath, X_OK) < 0) {
tmp = xreadline(NULL, messages[MSG_APP_NAME]); tmp = xreadline(NULL, messages[MSG_APP_NAME]);
r = F_NOWAIT | F_NOTRACE | F_MULTI; r = F_NOWAIT | F_NOTRACE | F_MULTI;
skipping to change at line 4930 skipping to change at line 5225
{ {
bool ret = FALSE; bool ret = FALSE;
char *tmp; char *tmp;
setenv(envs[ENV_NCUR], current, 1); setenv(envs[ENV_NCUR], current, 1);
while (1) { while (1) {
#ifndef NORL #ifndef NORL
if (g_state.picker) { if (g_state.picker) {
#endif #endif
tmp = xreadline(NULL, ">>> "); tmp = xreadline(NULL, PROMPT);
#ifndef NORL #ifndef NORL
} else } else
tmp = getreadline("\n>>> "); tmp = getreadline("\n"PROMPT);
#endif #endif
if (tmp && *tmp) { // NOLINT if (tmp && *tmp) { // NOLINT
free(lastcmd);
lastcmd = xstrdup(tmp);
ret = TRUE; ret = TRUE;
spawn(shell, "-c", tmp, NULL, F_CLI | F_CONFIRM); spawn(shell, "-c", tmp, NULL, F_CLI | F_CONFIRM);
} else } else
break; break;
} }
return ret; return ret;
} }
static bool handle_cmd(enum action sel, const char *current, char *newpath) static bool handle_cmd(enum action sel, const char *current, char *newpath)
{ {
endselection(); endselection();
if (sel == SEL_RUNCMD) if (sel == SEL_PROMPT)
return prompt_run(current); return prompt_run(current);
if (sel == SEL_LAUNCH) if (sel == SEL_LAUNCH)
return launch_app(newpath); return launch_app(newpath);
/* Set nnn nesting level */ /* Set nnn nesting level */
char *tmp = getenv(env_cfg[NNNLVL]); char *tmp = getenv(env_cfg[NNNLVL]);
int r = tmp ? atoi(tmp) : 0; int r = tmp ? atoi(tmp) : 0;
setenv(env_cfg[NNNLVL], xitoa(r + 1), 1); setenv(env_cfg[NNNLVL], xitoa(r + 1), 1);
skipping to change at line 4982 skipping to change at line 5279
/* Thread data cleanup */ /* Thread data cleanup */
free(core_blocks); free(core_blocks);
free(core_data); free(core_data);
free(core_files); free(core_files);
} }
static void *du_thread(void *p_data) static void *du_thread(void *p_data)
{ {
thread_data *pdata = (thread_data *)p_data; thread_data *pdata = (thread_data *)p_data;
char *path[2] = {pdata->path, NULL}; char *path[2] = {pdata->path, NULL};
ulong_t tfiles = 0; ullong_t tfiles = 0;
blkcnt_t tblocks = 0; blkcnt_t tblocks = 0;
struct stat *sb; struct stat *sb;
FTS *tree = fts_open(path, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, 0); FTS *tree = fts_open(path, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, 0);
FTSENT *node; FTSENT *node;
while ((node = fts_read(tree))) { while ((node = fts_read(tree))) {
if (node->fts_info & FTS_D) if (node->fts_info & FTS_D)
continue; continue;
sb = node->fts_statp; sb = node->fts_statp;
skipping to change at line 5025 skipping to change at line 5322
threadbmp |= (1 << pdata->core); threadbmp |= (1 << pdata->core);
--active_threads; --active_threads;
pthread_mutex_unlock(&running_mutex); pthread_mutex_unlock(&running_mutex);
return NULL; return NULL;
} }
static void dirwalk(char *dir, char *path, int entnum, bool mountpoint) static void dirwalk(char *dir, char *path, int entnum, bool mountpoint)
{ {
/* Loop till any core is free */ /* Loop till any core is free */
while (active_threads == NUM_DU_THREADS){} while (active_threads == NUM_DU_THREADS);
if (g_state.interrupt) if (g_state.interrupt)
return; return;
pthread_mutex_lock(&running_mutex); pthread_mutex_lock(&running_mutex);
++active_threads;
int core = ffs(threadbmp) - 1; int core = ffs(threadbmp) - 1;
threadbmp &= ~(1 << core); threadbmp &= ~(1 << core);
++active_threads;
pthread_mutex_unlock(&running_mutex); pthread_mutex_unlock(&running_mutex);
xstrsncpy(core_data[core].path, path, PATH_MAX); xstrsncpy(core_data[core].path, path, PATH_MAX);
core_data[core].entnum = entnum; core_data[core].entnum = entnum;
core_data[core].core = (ushort_t)core; core_data[core].core = (ushort_t)core;
core_data[core].mntpoint = mountpoint; core_data[core].mntpoint = mountpoint;
pthread_t tid = 0; pthread_t tid = 0;
pthread_create(&tid, NULL, du_thread, (void *)&(core_data[core])); pthread_create(&tid, NULL, du_thread, (void *)&(core_data[core]));
redraw(dir); redraw(dir);
tolastln(); printmsg("^C aborts");
addstr(" [^C aborts]\n");
refresh(); refresh();
} }
static void prep_threads(void) static void prep_threads(void)
{ {
if (!g_state.duinit) { if (!g_state.duinit) {
/* drop MSB 1s */ /* drop MSB 1s */
threadbmp >>= (32 - NUM_DU_THREADS); threadbmp >>= (32 - NUM_DU_THREADS);
core_blocks = calloc(NUM_DU_THREADS, sizeof(blkcnt_t)); core_blocks = calloc(NUM_DU_THREADS, sizeof(blkcnt_t));
core_data = calloc(NUM_DU_THREADS, sizeof(thread_data)); core_data = calloc(NUM_DU_THREADS, sizeof(thread_data));
core_files = calloc(NUM_DU_THREADS, sizeof(ulong_t)); core_files = calloc(NUM_DU_THREADS, sizeof(ullong_t));
#ifndef __APPLE__ #ifndef __APPLE__
/* Increase current open file descriptor limit */ /* Increase current open file descriptor limit */
max_openfds(); max_openfds();
#endif #endif
g_state.duinit = TRUE; g_state.duinit = TRUE;
} else { } else {
memset(core_blocks, 0, NUM_DU_THREADS * sizeof(blkcnt_t)); memset(core_blocks, 0, NUM_DU_THREADS * sizeof(blkcnt_t));
memset(core_data, 0, NUM_DU_THREADS * sizeof(thread_data)); memset(core_data, 0, NUM_DU_THREADS * sizeof(thread_data));
memset(core_files, 0, NUM_DU_THREADS * sizeof(ulong_t)); memset(core_files, 0, NUM_DU_THREADS * sizeof(ullong_t));
} }
} }
/* Skip self and parent */ /* Skip self and parent */
static bool selforparent(const char *path) static bool selforparent(const char *path)
{ {
return path[0] == '.' && (path[1] == '\0' || (path[1] == '.' && path[2] = = '\0')); return path[0] == '.' && (path[1] == '\0' || (path[1] == '.' && path[2] = = '\0'));
} }
static int dentfill(char *path, struct entry **ppdents) static int dentfill(char *path, struct entry **ppdents)
{ {
uchar_t entflags = 0; uchar_t entflags = 0;
int flags = 0; int flags = 0;
struct dirent *dp; struct dirent *dp;
char *namep, *pnb, *buf = NULL; char *namep, *pnb, *buf;
struct entry *dentp; struct entry *dentp;
size_t off = 0, namebuflen = NAMEBUF_INCR; size_t off = 0, namebuflen = NAMEBUF_INCR;
struct stat sb_path, sb; struct stat sb_path, sb;
DIR *dirp = opendir(path); DIR *dirp = opendir(path);
ndents = 0; ndents = 0;
DPRINTF_S(__func__); DPRINTF_S(__func__);
if (!dirp) if (!dirp)
return 0; return 0;
int fd = dirfd(dirp); int fd = dirfd(dirp);
if (cfg.blkorder) { if (cfg.blkorder) {
num_files = 0; num_files = 0;
dir_blocks = 0; dir_blocks = 0;
buf = (char *)alloca(xstrlen(path) + NAME_MAX + 2); buf = g_buf;
if (!buf)
return 0;
if (fstatat(fd, path, &sb_path, 0) == -1) if (fstatat(fd, path, &sb_path, 0) == -1)
goto exit; goto exit;
if (!ihashbmp) { if (!ihashbmp) {
ihashbmp = calloc(1, HASH_OCTETS << 3); ihashbmp = calloc(1, HASH_OCTETS << 3);
if (!ihashbmp) if (!ihashbmp)
goto exit; goto exit;
} else } else
memset(ihashbmp, 0, HASH_OCTETS << 3); memset(ihashbmp, 0, HASH_OCTETS << 3);
skipping to change at line 5159 skipping to change at line 5454
if (!cfg.showhidden && namep[0] == '.') { if (!cfg.showhidden && namep[0] == '.') {
if (!cfg.blkorder) if (!cfg.blkorder)
continue; continue;
if (fstatat(fd, namep, &sb, AT_SYMLINK_NOFOLLOW) == -1) if (fstatat(fd, namep, &sb, AT_SYMLINK_NOFOLLOW) == -1)
continue; continue;
if (S_ISDIR(sb.st_mode)) { if (S_ISDIR(sb.st_mode)) {
if (sb_path.st_dev == sb.st_dev) { // NOLINT if (sb_path.st_dev == sb.st_dev) { // NOLINT
mkpath(path, namep, buf); mkpath(path, namep, buf); // NOLINT
dirwalk(path, buf, -1, FALSE); dirwalk(path, buf, -1, FALSE);
if (g_state.interrupt) if (g_state.interrupt)
goto exit; goto exit;
} }
} else { } else {
/* Do not recount hard links */ /* Do not recount hard links */
if (sb.st_nlink <= 1 || test_set_bit((uint_t)sb.s t_ino)) if (sb.st_nlink <= 1 || test_set_bit((uint_t)sb.s t_ino))
dir_blocks += (cfg.apparentsz ? sb.st_siz e : sb.st_blocks); dir_blocks += (cfg.apparentsz ? sb.st_siz e : sb.st_blocks);
skipping to change at line 5193 skipping to change at line 5488
} }
entflags = FILE_MISSING; entflags = FILE_MISSING;
memset(&sb, 0, sizeof(struct stat)); memset(&sb, 0, sizeof(struct stat));
} else /* Orphaned symlink */ } else /* Orphaned symlink */
entflags = SYM_ORPHAN; entflags = SYM_ORPHAN;
} }
if (ndents == total_dents) { if (ndents == total_dents) {
if (cfg.blkorder) if (cfg.blkorder)
while (active_threads) {} while (active_threads);
total_dents += ENTRY_INCR; total_dents += ENTRY_INCR;
*ppdents = xrealloc(*ppdents, total_dents * sizeof(**ppde nts)); *ppdents = xrealloc(*ppdents, total_dents * sizeof(**ppde nts));
if (!*ppdents) { if (!*ppdents) {
free(pnamebuf); free(pnamebuf);
closedir(dirp); closedir(dirp);
errexit(); errexit();
} }
DPRINTF_P(*ppdents); DPRINTF_P(*ppdents);
} }
skipping to change at line 5287 skipping to change at line 5582
#endif #endif
dentp->flags = S_ISDIR(sb.st_mode) ? 0 : ((sb.st_nlink > 1) ? HAR D_LINK : 0); dentp->flags = S_ISDIR(sb.st_mode) ? 0 : ((sb.st_nlink > 1) ? HAR D_LINK : 0);
if (entflags) { if (entflags) {
dentp->flags |= entflags; dentp->flags |= entflags;
entflags = 0; entflags = 0;
} }
if (cfg.blkorder) { if (cfg.blkorder) {
if (S_ISDIR(sb.st_mode)) { if (S_ISDIR(sb.st_mode)) {
mkpath(path, namep, buf); mkpath(path, namep, buf); // NOLINT
/* Need to show the disk usage of this dir */ /* Need to show the disk usage of this dir */
dirwalk(path, buf, ndents, (sb_path.st_dev != sb. st_dev)); // NOLINT dirwalk(path, buf, ndents, (sb_path.st_dev != sb. st_dev)); // NOLINT
if (g_state.interrupt) if (g_state.interrupt)
goto exit; goto exit;
} else { } else {
dentp->blocks = (cfg.apparentsz ? sb.st_size : sb .st_blocks); dentp->blocks = (cfg.apparentsz ? sb.st_size : sb .st_blocks);
/* Do not recount hard links */ /* Do not recount hard links */
if (sb.st_nlink <= 1 || test_set_bit((uint_t)sb.s t_ino)) if (sb.st_nlink <= 1 || test_set_bit((uint_t)sb.s t_ino))
skipping to change at line 5311 skipping to change at line 5606
} }
if (flags) { if (flags) {
/* Flag if this is a dir or symlink to a dir */ /* Flag if this is a dir or symlink to a dir */
if (S_ISLNK(sb.st_mode)) { if (S_ISLNK(sb.st_mode)) {
sb.st_mode = 0; sb.st_mode = 0;
fstatat(fd, namep, &sb, 0); fstatat(fd, namep, &sb, 0);
} }
if (S_ISDIR(sb.st_mode)) if (S_ISDIR(sb.st_mode))
dentp->flags |= DIR_OR_LINK_TO_DIR; dentp->flags |= DIR_OR_DIRLNK;
#if !(defined(__sun) || defined(__HAIKU__)) /* no d_type */ #if !(defined(__sun) || defined(__HAIKU__)) /* no d_type */
} else if (dp->d_type == DT_DIR || ((dp->d_type == DT_LNK || dp-> } else if (dp->d_type == DT_DIR || ((dp->d_type == DT_LNK
d_type == DT_UNKNOWN) && S_ISDIR(sb.st_mode))) { || dp->d_type == DT_UNKNOWN) && S_ISDIR(sb.st_mode)))
dentp->flags |= DIR_OR_LINK_TO_DIR; {
dentp->flags |= DIR_OR_DIRLNK;
#endif #endif
} }
++ndents; ++ndents;
} while ((dp = readdir(dirp))); } while ((dp = readdir(dirp)));
exit: exit:
if (cfg.blkorder) { if (cfg.blkorder) {
while (active_threads) {} while (active_threads);
attroff(COLOR_PAIR(cfg.curctx + 1)); attroff(COLOR_PAIR(cfg.curctx + 1));
for (int i = 0; i < NUM_DU_THREADS; ++i) { for (int i = 0; i < NUM_DU_THREADS; ++i) {
num_files += core_files[i]; num_files += core_files[i];
dir_blocks += core_blocks[i]; dir_blocks += core_blocks[i];
} }
} }
/* Should never be null */ /* Should never be null */
if (closedir(dirp) == -1) if (closedir(dirp) == -1)
skipping to change at line 5431 skipping to change at line 5727
* outward (deeper into the scrolloff margin area). * outward (deeper into the scrolloff margin area).
*/ */
if (((cur < (curscroll + scrolloff)) && delta < 0) if (((cur < (curscroll + scrolloff)) && delta < 0)
|| ((cur > (curscroll + onscreen - scrolloff - 1)) && delta > 0)) || ((cur > (curscroll + onscreen - scrolloff - 1)) && delta > 0))
curscroll += delta; curscroll += delta;
} }
curscroll = MIN(curscroll, MIN(cur, ndents - onscreen)); curscroll = MIN(curscroll, MIN(cur, ndents - onscreen));
curscroll = MAX(curscroll, MAX(cur - (onscreen - 1), 0)); curscroll = MAX(curscroll, MAX(cur - (onscreen - 1), 0));
#ifndef NOFIFO #ifndef NOFIFO
notify_fifo(FALSE); if (!g_state.fifomode)
notify_fifo(FALSE); /* Send hovered path to NNN_FIFO */
#endif #endif
} }
static void handle_screen_move(enum action sel) static void handle_screen_move(enum action sel)
{ {
int onscreen; int onscreen;
switch (sel) { switch (sel) {
case SEL_NEXT: case SEL_NEXT:
if (ndents && (cfg.rollover || (cur != ndents - 1))) if (ndents && (cfg.rollover || (cur != ndents - 1)))
skipping to change at line 5486 skipping to change at line 5783
int c = get_input(messages[MSG_FIRST]); int c = get_input(messages[MSG_FIRST]);
if (!c) if (!c)
break; break;
c = TOUPPER(c); c = TOUPPER(c);
int r = (c == TOUPPER(*pdents[cur].name)) ? (cur + 1) : 0; int r = (c == TOUPPER(*pdents[cur].name)) ? (cur + 1) : 0;
for (; r < ndents; ++r) { for (; r < ndents; ++r) {
if (((c == '\'') && !(pdents[r].flags & DIR_OR_LINK_TO_DI R)) if (((c == '\'') && !(pdents[r].flags & DIR_OR_DIRLNK))
|| (c == TOUPPER(*pdents[r].name))) { || (c == TOUPPER(*pdents[r].name))) {
move_cursor((r) % ndents, 0); move_cursor((r) % ndents, 0);
break; break;
} }
} }
break; break;
} }
} }
} }
skipping to change at line 5554 skipping to change at line 5851
r = sel - SEL_CTX1; /* Save the next context id */ r = sel - SEL_CTX1; /* Save the next context id */
if (cfg.curctx == r) { if (cfg.curctx == r) {
if (sel == SEL_CYCLE) if (sel == SEL_CYCLE)
(r == CTX_MAX - 1) ? (r = 0) : ++r; (r == CTX_MAX - 1) ? (r = 0) : ++r;
else if (sel == SEL_CYCLER) else if (sel == SEL_CYCLER)
(r == 0) ? (r = CTX_MAX - 1) : --r; (r == 0) ? (r = CTX_MAX - 1) : --r;
else else
return -1; return -1;
} }
if (g_state.selmode) /* Remember the position from where to conti
nue selection */
lastappendpos = selbufpos;
} }
return r; return r;
} }
static int set_sort_flags(int r) static int set_sort_flags(int r)
{ {
bool session = !r; bool session = !r;
/* Set the correct input in case of a session load */ /* Set the correct input in case of a session load */
skipping to change at line 5754 skipping to change at line 6048
addch(' '); addch(' ');
} }
if (cfg.blkorder) { /* du mode */ if (cfg.blkorder) { /* du mode */
char buf[24]; char buf[24];
xstrsncpy(buf, coolsize(dir_blocks << blk_shift), 12); xstrsncpy(buf, coolsize(dir_blocks << blk_shift), 12);
printw("%cu:%s free:%s files:%llu %lluB %s\n", printw("%cu:%s free:%s files:%llu %lluB %s\n",
(cfg.apparentsz ? 'a' : 'd'), buf, coolsize(get_fs_info(pa th, FREE)), (cfg.apparentsz ? 'a' : 'd'), buf, coolsize(get_fs_info(pa th, FREE)),
num_files, (ulong_t)pent->blocks << blk_shift, ptr); num_files, (ullong_t)pent->blocks << blk_shift, ptr);
} else { /* light or detail mode */ } else { /* light or detail mode */
char sort[] = "\0\0\0\0\0"; char sort[] = "\0\0\0\0\0";
if (getorderstr(sort)) if (getorderstr(sort))
addstr(sort); addstr(sort);
/* Timestamp */ /* Timestamp */
print_time(&pent->sec); print_time(&pent->sec);
addch(' '); addch(' ');
addstr(get_lsperms(pent->mode)); addstr(get_lsperms(pent->mode));
addch(' '); addch(' ');
#ifndef NOUG #ifndef NOUG
if (g_state.uidgid) { if (g_state.uidgid) {
addstr(getpwname(pent->uid)); addstr(getpwname(pent->uid));
addch(':'); addch(':');
addstr(getgrname(pent->gid)); addstr(getgrname(pent->gid));
addch(' '); addch(' ');
} }
#endif #endif
if (S_ISLNK(pent->mode)) if (S_ISLNK(pent->mode)) {
{
i = readlink(pent->name, g_buf, PATH_MAX); i = readlink(pent->name, g_buf, PATH_MAX);
addstr(coolsize(i >= 0 ? i : pent->size)); /* Show symlin k size */ addstr(coolsize(i >= 0 ? i : pent->size)); /* Show symlin k size */
if (i > 1) { /* Show symlink target */ if (i > 1) { /* Show symlink target */
g_buf[i] = '\0'; g_buf[i] = '\0';
#ifdef ICONS_ENABLED #ifdef ICONS_ENABLED
addstr(" "MD_ARROW_FORWARD); addstr(" "MD_ARROW_FORWARD);
#else #else
addstr(" ->"); addstr(" ->");
#endif #endif
addstr(g_buf); addstr(g_buf);
} }
} else { } else {
addstr(coolsize(pent->size)); addstr(coolsize(pent->size));
addch(' '); addch(' ');
addstr(ptr); addstr(ptr);
if (pent->flags & HARD_LINK) if (pent->flags & HARD_LINK) {
{
struct stat sb; struct stat sb;
if (stat(pent->name, &sb) != -1) { if (stat(pent->name, &sb) != -1) {
addch(' '); addch(' ');
addstr(xitoa((int)sb.st_nlink)); /* Show number of links */ addstr(xitoa((int)sb.st_nlink)); /* Show number of links */
addch('-'); addch('-');
addstr(xitoa((int)sb.st_ino)); /* Show in ode number */ addstr(xitoa((int)sb.st_ino)); /* Show in ode number */
} }
} }
} }
addch('\n'); clrtoeol();
} }
attroff(COLOR_PAIR(cfg.curctx + 1)); attroff(COLOR_PAIR(cfg.curctx + 1));
if (cfg.cursormode) if (cfg.cursormode)
tocursor(); tocursor();
} }
static inline void markhovered(void) static inline void markhovered(void)
{ {
skipping to change at line 5838 skipping to change at line 6129
static int adjust_cols(int n) static int adjust_cols(int n)
{ {
/* Calculate the number of cols available to print entry name */ /* Calculate the number of cols available to print entry name */
#ifdef ICONS_ENABLED #ifdef ICONS_ENABLED
n -= (g_state.oldcolor ? 0 : 1 + xstrlen(ICON_PADDING_LEFT) + xstrlen(ICO N_PADDING_RIGHT)); n -= (g_state.oldcolor ? 0 : 1 + xstrlen(ICON_PADDING_LEFT) + xstrlen(ICO N_PADDING_RIGHT));
#endif #endif
if (cfg.showdetail) { if (cfg.showdetail) {
/* Fallback to light mode if less than 35 columns */ /* Fallback to light mode if less than 35 columns */
if (n < 36) if (n < 36)
cfg.showdetail ^= 1; cfg.showdetail ^= 1;
else { else /* 2 more accounted for below */
/* 2 more accounted for below */
n -= 32; n -= 32;
}
} }
/* 2 columns for preceding space and indicator */ /* 2 columns for preceding space and indicator */
return (n - 2); return (n - 2);
} }
static void draw_line(char *path, int ncols) static void draw_line(char *path, int ncols)
{ {
bool dir = FALSE; bool dir = FALSE;
ncols = adjust_cols(ncols); ncols = adjust_cols(ncols);
if (g_state.oldcolor && (pdents[last].flags & DIR_OR_LINK_TO_DIR)) { if (g_state.oldcolor && (pdents[last].flags & DIR_OR_DIRLNK)) {
attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
dir = TRUE; dir = TRUE;
} }
move(2 + last - curscroll, 0); move(2 + last - curscroll, 0);
printent(&pdents[last], ncols, FALSE); printent(&pdents[last], ncols, FALSE);
if (g_state.oldcolor && (pdents[cur].flags & DIR_OR_LINK_TO_DIR)) { if (g_state.oldcolor && (pdents[cur].flags & DIR_OR_DIRLNK)) {
if (!dir) {/* First file is not a directory */ if (!dir) {/* First file is not a directory */
attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
dir = TRUE; dir = TRUE;
} }
} else if (dir) { /* Second file is not a directory */ } else if (dir) { /* Second file is not a directory */
attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
dir = FALSE; dir = FALSE;
} }
move(2 + cur - curscroll, 0); move(2 + cur - curscroll, 0);
skipping to change at line 5909 skipping to change at line 6198
DPRINTF_S(__func__); DPRINTF_S(__func__);
/* Clear screen */ /* Clear screen */
erase(); erase();
/* Enforce scroll/cursor invariants */ /* Enforce scroll/cursor invariants */
move_cursor(cur, 1); move_cursor(cur, 1);
/* Fail redraw if < than 10 columns, context info prints 10 chars */ /* Fail redraw if < than 10 columns, context info prints 10 chars */
if (ncols <= MIN_DISPLAY_COLS) { if (ncols <= MIN_DISPLAY_COL) {
printmsg(messages[MSG_FEW_COLUMNS]); printmsg(messages[MSG_FEW_COLUMNS]);
return; return;
} }
//DPRINTF_D(cur); //DPRINTF_D(cur);
DPRINTF_S(path); DPRINTF_S(path);
for (i = 0; i < CTX_MAX; ++i) { /* 8 chars printed for contexts - "1 2 3 4 " */ for (i = 0; i < CTX_MAX; ++i) { /* 8 chars printed for contexts - "1 2 3 4 " */
if (!g_ctx[i].c_cfg.ctxactive) if (!g_ctx[i].c_cfg.ctxactive)
addch(i + '1'); addch(i + '1');
skipping to change at line 5935 skipping to change at line 6224
addch(' '); addch(' ');
} }
attron(A_UNDERLINE | COLOR_PAIR(cfg.curctx + 1)); attron(A_UNDERLINE | COLOR_PAIR(cfg.curctx + 1));
/* Print path */ /* Print path */
bool in_home = set_tilde_in_path(path); bool in_home = set_tilde_in_path(path);
char *ptr = in_home ? &path[homelen - 1] : path; char *ptr = in_home ? &path[homelen - 1] : path;
i = (int)xstrlen(ptr); i = (int)xstrlen(ptr);
if ((i + MIN_DISPLAY_COLS) <= ncols) if ((i + MIN_DISPLAY_COL) <= ncols)
addnstr(ptr, ncols - MIN_DISPLAY_COLS); addnstr(ptr, ncols - MIN_DISPLAY_COL);
else { else {
char *base = xmemrchr((uchar_t *)ptr, '/', i); char *base = xmemrchr((uchar_t *)ptr, '/', i);
if (in_home) { if (in_home) {
addch(*ptr); addch(*ptr);
++ptr; ++ptr;
i = 1; i = 1;
} else } else
i = 0; i = 0;
if (ptr && (base != ptr)) { if (ptr && (base != ptr)) {
while (ptr < base) { while (ptr < base) {
if (*ptr == '/') { if (*ptr == '/') {
i += 2; /* 2 characters added */ i += 2; /* 2 characters added */
if (ncols < i + MIN_DISPLAY_COLS) { if (ncols < i + MIN_DISPLAY_COL) {
base = NULL; /* Can't print more characters */ base = NULL; /* Can't print more characters */
break; break;
} }
addch(*ptr); addch(*ptr);
addch(*(++ptr)); addch(*(++ptr));
} }
++ptr; ++ptr;
} }
} }
if (base) if (base)
addnstr(base, ncols - (MIN_DISPLAY_COLS + i)); addnstr(base, ncols - (MIN_DISPLAY_COL + i));
} }
if (in_home) if (in_home)
reset_tilde_in_path(path); reset_tilde_in_path(path);
attroff(A_UNDERLINE | COLOR_PAIR(cfg.curctx + 1)); attroff(A_UNDERLINE | COLOR_PAIR(cfg.curctx + 1));
ncols = adjust_cols(ncols);
/* Go to first entry */ /* Go to first entry */
if (curscroll > 0) { if (curscroll > 0) {
move(1, 0); move(1, 0);
#ifdef ICONS_ENABLED #ifdef ICONS_ENABLED
addstr(MD_ARROW_UPWARD); addstr(MD_ARROW_UPWARD);
#else #else
addch('^'); addch('^');
#endif #endif
} }
if (g_state.oldcolor) { if (g_state.oldcolor) {
attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
g_state.dircolor = 1; g_state.dircolor = 1;
} }
onscreen = MIN(onscreen + curscroll, ndents); onscreen = MIN(onscreen + curscroll, ndents);
ncols = adjust_cols(ncols);
int len = scanselforpath(path, FALSE);
/* Print listing */ /* Print listing */
for (i = curscroll; i < onscreen; ++i) { for (i = curscroll; i < onscreen; ++i) {
move(++j, 0); move(++j, 0);
if (len)
findmarkentry(len, &pdents[i]);
printent(&pdents[i], ncols, i == cur); printent(&pdents[i], ncols, i == cur);
} }
/* Must reset e.g. no files in dir */ /* Must reset e.g. no files in dir */
if (g_state.dircolor) { if (g_state.dircolor) {
attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD);
g_state.dircolor = 0; g_state.dircolor = 0;
} }
/* Go to last entry */ /* Go to last entry */
skipping to change at line 6046 skipping to change at line 6341
char newpath[PATH_MAX] __attribute__ ((aligned)), char newpath[PATH_MAX] __attribute__ ((aligned)),
rundir[PATH_MAX] __attribute__ ((aligned)), rundir[PATH_MAX] __attribute__ ((aligned)),
runfile[NAME_MAX + 1] __attribute__ ((aligned)); runfile[NAME_MAX + 1] __attribute__ ((aligned));
char *path, *lastdir, *lastname, *dir, *tmp; char *path, *lastdir, *lastname, *dir, *tmp;
pEntry pent; pEntry pent;
enum action sel; enum action sel;
struct stat sb; struct stat sb;
int r = -1, presel, selstartid = 0, selendid = 0; int r = -1, presel, selstartid = 0, selendid = 0;
const uchar_t opener_flags = (cfg.cliopener ? F_CLI : (F_NOTRACE | F_NOST DIN | F_NOWAIT)); const uchar_t opener_flags = (cfg.cliopener ? F_CLI : (F_NOTRACE | F_NOST DIN | F_NOWAIT));
bool watch = FALSE; bool watch = FALSE;
ino_t inode = 0;
#ifndef NOMOUSE #ifndef NOMOUSE
MEVENT event = {0}; MEVENT event = {0};
struct timespec mousetimings[2] = {{.tv_sec = 0, .tv_nsec = 0}, {.tv_sec = 0, .tv_nsec = 0} }; struct timespec mousetimings[2] = {{.tv_sec = 0, .tv_nsec = 0}, {.tv_sec = 0, .tv_nsec = 0} };
int mousedent[2] = {-1, -1}; int mousedent[2] = {-1, -1};
bool currentmouse = 1, rightclicksel = 0; bool currentmouse = 1, rightclicksel = 0;
#endif #endif
#ifndef DIR_LIMITED_SELECTION
ino_t inode = 0;
#endif
atexit(dentfree); atexit(dentfree);
xlines = LINES; xlines = LINES;
xcols = COLS; xcols = COLS;
#ifndef NOSSN #ifndef NOSSN
/* set-up first context */ /* set-up first context */
if (!session || !load_session(session, &path, &lastdir, &lastname, FALSE) ) { if (!session || !load_session(session, &path, &lastdir, &lastname, FALSE) ) {
#else #else
(void)session; (void)session;
skipping to change at line 6130 skipping to change at line 6422
r = set_tilde_in_path(path); r = set_tilde_in_path(path);
printf("\033]2;%s\007", r ? &path[homelen - 1] : path); printf("\033]2;%s\007", r ? &path[homelen - 1] : path);
fflush(stdout); fflush(stdout);
if (r) if (r)
reset_tilde_in_path(path); reset_tilde_in_path(path);
} }
#endif #endif
if (g_state.selmode && lastdir[0])
lastappendpos = selbufpos;
#ifdef LINUX_INOTIFY #ifdef LINUX_INOTIFY
if ((presel == FILTER || watch) && inotify_wd >= 0) { if ((presel == FILTER || watch) && inotify_wd >= 0) {
inotify_rm_watch(inotify_fd, inotify_wd); inotify_rm_watch(inotify_fd, inotify_wd);
inotify_wd = -1; inotify_wd = -1;
watch = FALSE; watch = FALSE;
} }
#elif defined(BSD_KQUEUE) #elif defined(BSD_KQUEUE)
if ((presel == FILTER || watch) && event_fd >= 0) { if ((presel == FILTER || watch) && event_fd >= 0) {
close(event_fd); close(event_fd);
event_fd = -1; event_fd = -1;
skipping to change at line 6215 skipping to change at line 6504
/* Handle clicking on a context at the top */ /* Handle clicking on a context at the top */
if (event.bstate == BUTTON1_PRESSED && event.y == 0) { if (event.bstate == BUTTON1_PRESSED && event.y == 0) {
/* Get context from: "[1 2 3 4]..." */ /* Get context from: "[1 2 3 4]..." */
r = event.x >> 1; r = event.x >> 1;
/* If clicked after contexts, go to parent */ /* If clicked after contexts, go to parent */
if (r >= CTX_MAX) if (r >= CTX_MAX)
sel = SEL_BACK; sel = SEL_BACK;
else if (r >= 0 && r != cfg.curctx) { else if (r >= 0 && r != cfg.curctx) {
if (g_state.selmode) savecurctx(path, pdents[cur].name, r);
lastappendpos = selbufpos;
savecurctx(&cfg, path, pdents[cur].name,
r);
/* Reset the pointers */ /* Reset the pointers */
path = g_ctx[r].c_path; path = g_ctx[r].c_path;
lastdir = g_ctx[r].c_last; lastdir = g_ctx[r].c_last;
lastname = g_ctx[r].c_name; lastname = g_ctx[r].c_name;
setdirwatch(); setdirwatch();
goto begin; goto begin;
} }
} }
skipping to change at line 6298 skipping to change at line 6584
} }
/* Handle clicking on a file */ /* Handle clicking on a file */
if (event.y >= 2 && event.y <= ndents + 1 && if (event.y >= 2 && event.y <= ndents + 1 &&
(event.bstate == BUTTON1_PRESSED || (event.bstate == BUTTON1_PRESSED ||
event.bstate == BUTTON3_PRESSED)) { event.bstate == BUTTON3_PRESSED)) {
r = curscroll + (event.y - 2); r = curscroll + (event.y - 2);
if (r != cur) if (r != cur)
move_cursor(r, 1); move_cursor(r, 1);
#ifndef NOFIFO #ifndef NOFIFO
else if (event.bstate == BUTTON1_PRESSED) else if ((event.bstate == BUTTON1_PRESSED) && !g_
notify_fifo(TRUE); state.fifomode)
notify_fifo(TRUE); /* Send clicked path t
o NNN_FIFO */
#endif #endif
/* Handle right click selection */ /* Handle right click selection */
if (event.bstate == BUTTON3_PRESSED) { if (event.bstate == BUTTON3_PRESSED) {
rightclicksel = 1; rightclicksel = 1;
presel = SELECT; presel = SELECT;
goto nochange; goto nochange;
} }
currentmouse ^= 1; currentmouse ^= 1;
clock_gettime( clock_gettime(
skipping to change at line 6324 skipping to change at line 6610
#else #else
CLOCK_REALTIME, CLOCK_REALTIME,
#endif #endif
&mousetimings[currentmouse]); &mousetimings[currentmouse]);
mousedent[currentmouse] = cur; mousedent[currentmouse] = cur;
/* Single click just selects, double click falls through to SEL_OPEN */ /* Single click just selects, double click falls through to SEL_OPEN */
if ((mousedent[0] != mousedent[1]) || if ((mousedent[0] != mousedent[1]) ||
(((_ABSSUB(mousetimings[0].tv_sec, mousetimings [1].tv_sec) << 30) (((_ABSSUB(mousetimings[0].tv_sec, mousetimings [1].tv_sec) << 30)
+ (_ABSSUB(mousetimings[0].tv_nsec, mousetiming s[1].tv_nsec))) + (_ABSSUB(mousetimings[0].tv_nsec, mousetiming s[1].tv_nsec)))
> DOUBLECLICK_INTERVAL_NS)) > DBLCLK_INTERVAL_NS))
break; break;
mousetimings[currentmouse].tv_sec = 0; mousetimings[currentmouse].tv_sec = 0;
mousedent[currentmouse] = -1; mousedent[currentmouse] = -1;
} else { } else {
if (cfg.filtermode || filterset()) if (cfg.filtermode || filterset())
presel = FILTER; presel = FILTER;
if (ndents) if (ndents)
copycurname(); copycurname();
goto nochange; goto nochange;
} }
skipping to change at line 6348 skipping to change at line 6634
case SEL_OPEN: case SEL_OPEN:
/* Cannot descend in empty directories */ /* Cannot descend in empty directories */
if (!ndents) if (!ndents)
goto begin; goto begin;
pent = &pdents[cur]; pent = &pdents[cur];
mkpath(path, pent->name, newpath); mkpath(path, pent->name, newpath);
DPRINTF_S(newpath); DPRINTF_S(newpath);
/* Visit directory */ /* Visit directory */
if (pent->flags & DIR_OR_LINK_TO_DIR) { if (pent->flags & DIR_OR_DIRLNK) {
if (chdir(newpath) == -1) { if (chdir(newpath) == -1) {
printwarn(&presel); printwarn(&presel);
goto nochange; goto nochange;
} }
cdprep(lastdir, lastname, path, newpath) ? (prese cdprep(lastdir, lastname, path, newpath)
l = FILTER) : (watch = TRUE); ? (presel = FILTER) : (watch = TRUE);
goto begin; goto begin;
} }
/* Cannot use stale data in entry, file may be missing by now */ /* Cannot use stale data in entry, file may be missing by now */
if (stat(newpath, &sb) == -1) { if (stat(newpath, &sb) == -1) {
printwarn(&presel); printwarn(&presel);
goto nochange; goto nochange;
} }
DPRINTF_U(sb.st_mode); DPRINTF_U(sb.st_mode);
/* Do not open non-regular files */ /* Do not open non-regular files */
if (!S_ISREG(sb.st_mode)) { if (!S_ISREG(sb.st_mode)) {
printwait(messages[MSG_UNSUPPORTED], &presel); printwait(messages[MSG_UNSUPPORTED], &presel);
goto nochange; goto nochange;
}
#ifndef NOFIFO
if (g_state.fifomode && (sel == SEL_OPEN)) {
notify_fifo(TRUE); /* Send opened path to NNN_FIF
O */
goto nochange;
} }
#endif
/* If opened as vim plugin and Enter/^M pressed, pick */ /* If opened as vim plugin and Enter/^M pressed, pick */
if (g_state.picker && sel == SEL_OPEN) { if (g_state.picker && (sel == SEL_OPEN)) {
appendfpath(newpath, mkpath(path, pent->name, new path)); appendfpath(newpath, mkpath(path, pent->name, new path));
writesel(pselbuf, selbufpos - 1); writesel(pselbuf, selbufpos - 1);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if (sel == SEL_NAV_IN) { if (sel == SEL_NAV_IN) {
/* If in listing dir, go to target on `l` or Righ t on symlink */ /* If in listing dir, go to target on `l` or Righ t on symlink */
if (listpath && S_ISLNK(pent->mode) if (listpath && S_ISLNK(pent->mode)
&& is_prefix(path, listpath, xstrlen(listpath ))) { && is_prefix(path, listpath, xstrlen(listpath ))) {
if (!realpath(pent->name, newpath)) { if (!realpath(pent->name, newpath)) {
skipping to change at line 6418 skipping to change at line 6710
/* Handle plugin selection mode */ /* Handle plugin selection mode */
if (g_state.runplugin) { if (g_state.runplugin) {
g_state.runplugin = 0; g_state.runplugin = 0;
/* Must be in plugin dir and same context to sele ct plugin */ /* Must be in plugin dir and same context to sele ct plugin */
if ((g_state.runctx == cfg.curctx) && !strcmp(pat h, plgpath)) { if ((g_state.runctx == cfg.curctx) && !strcmp(pat h, plgpath)) {
endselection(); endselection();
/* Copy path so we can return back to ear lier dir */ /* Copy path so we can return back to ear lier dir */
xstrsncpy(path, rundir, PATH_MAX); xstrsncpy(path, rundir, PATH_MAX);
rundir[0] = '\0'; rundir[0] = '\0';
clearfilter();
if (chdir(path) == -1 if (chdir(path) == -1
|| !run_selected_plugin(&path, pent-> name, || !run_plugin(&path, pent->name,
runfile, &las tname, &lastdir)) { runfile, &las tname, &lastdir)) {
DPRINTF_S("plugin failed!"); DPRINTF_S("plugin failed!");
} }
if (g_state.picked) if (g_state.picked)
return EXIT_SUCCESS; return EXIT_SUCCESS;
if (runfile[0]) { if (runfile[0]) {
xstrsncpy(lastname, runfile, NAME _MAX + 1); xstrsncpy(lastname, runfile, NAME _MAX + 1);
runfile[0] = '\0'; runfile[0] = '\0';
} }
clearfilter();
setdirwatch(); setdirwatch();
goto begin; goto begin;
} }
} }
if (!sb.st_size) { if (!sb.st_size) {
printwait(messages[MSG_EMPTY_FILE], &presel); printwait(messages[MSG_EMPTY_FILE], &presel);
goto nochange; goto nochange;
} }
if (cfg.useeditor if (cfg.useeditor
#ifdef FILE_MIME_OPTS #ifdef FILE_MIME_OPTS
&& get_output(g_buf, CMD_LEN_MAX, "file", FILE_MIME_O PTS, newpath, FALSE) && get_output("file", FILE_MIME_OPTS, newpath, -1, FA LSE, FALSE)
&& is_prefix(g_buf, "text/", 5) && is_prefix(g_buf, "text/", 5)
#else #else
/* no MIME option; guess from description instead */ /* no MIME option; guess from description instead */
&& get_output(g_buf, CMD_LEN_MAX, "file", "-bL", newp ath, FALSE) && get_output("file", "-bL", newpath, -1, FALSE, FALS E)
&& strstr(g_buf, "text") && strstr(g_buf, "text")
#endif #endif
) { ) {
spawn(editor, newpath, NULL, NULL, F_CLI); spawn(editor, newpath, NULL, NULL, F_CLI);
if (cfg.filtermode) { if (cfg.filtermode) {
presel = FILTER; presel = FILTER;
clearfilter(); clearfilter();
} }
continue; continue;
} }
skipping to change at line 6472 skipping to change at line 6764
tmp = xextension(pent->name, pent->nlen - 1); tmp = xextension(pent->name, pent->nlen - 1);
#ifdef PCRE #ifdef PCRE
if (tmp && !pcre_exec(archive_pcre, NULL, tmp, if (tmp && !pcre_exec(archive_pcre, NULL, tmp,
pent->nlen - (tmp - pent->name) - 1 , 0, 0, NULL, 0)) { pent->nlen - (tmp - pent->name) - 1 , 0, 0, NULL, 0)) {
#else #else
if (tmp && !regexec(&archive_re, tmp, 0, NULL, 0)) { if (tmp && !regexec(&archive_re, tmp, 0, NULL, 0)) {
#endif #endif
r = get_input(messages[MSG_ARCHIVE_OPTS]); r = get_input(messages[MSG_ARCHIVE_OPTS]);
if (r == 'l' || r == 'x') { if (r == 'l' || r == 'x') {
mkpath(path, pent->name, newpath); mkpath(path, pent->name, newpath);
handle_archive(newpath, r); if (!handle_archive(newpath, r)) {
presel = MSGWAIT;
goto nochange;
}
if (r == 'l') { if (r == 'l') {
statusbar(path); statusbar(path);
goto nochange; goto nochange;
} }
copycurname();
clearfilter();
goto begin;
} }
if (r == 'm') { if ((r == 'm') && !archive_mount(newpath)) {
if (!archive_mount(newpath)) { presel = MSGWAIT;
presel = MSGWAIT; goto nochange;
goto nochange; }
}
/* Mark current directory */
free(mark);
mark = xstrdup(path);
cdprep(lastdir, lastname, path, newpath) if (r == 'x' || r == 'm') {
? (presel = FILTER) : (watch = TR if (newpath[0])
UE); set_smart_ctx('+', newpath, &path
, &lastname, &lastdir);
else
copycurname();
clearfilter();
goto begin; goto begin;
} }
if (r != 'o') { if (r != 'o') {
printwait(messages[MSG_INVALID_KEY], &pre sel); printwait(messages[MSG_INVALID_KEY], &pre sel);
goto nochange; goto nochange;
} }
} }
/* Invoke desktop opener as last resort */ /* Invoke desktop opener as last resort */
skipping to change at line 6568 skipping to change at line 6859
case SEL_BOOKMARK: case SEL_BOOKMARK:
if (sel == SEL_BOOKMARK) { if (sel == SEL_BOOKMARK) {
r = (int)handle_bookmark(mark, newpath); r = (int)handle_bookmark(mark, newpath);
if (r) { if (r) {
printwait(messages[r], &presel); printwait(messages[r], &presel);
goto nochange; goto nochange;
} }
if (strcmp(path, newpath) == 0) if (strcmp(path, newpath) == 0)
break; break;
} // fallthrough
case SEL_REMOTE:
if (sel == SEL_REMOTE && !remote_mount(newpath)) {
presel = MSGWAIT;
goto nochange;
} }
/* Mark current directory */
free(mark);
mark = xstrdup(path);
/* In list mode, retain the last file name to highlight i t, if possible */ /* In list mode, retain the last file name to highlight i t, if possible */
cdprep(lastdir, listpath && sel == SEL_CDLAST ? NULL : la stname, path, newpath) cdprep(lastdir, listpath && sel == SEL_CDLAST ? NULL : la stname, path, newpath)
? (presel = FILTER) : (watch = TRUE); ? (presel = FILTER) : (watch = TRUE);
goto begin; goto begin;
case SEL_REMOTE:
if ((sel == SEL_REMOTE) && !remote_mount(newpath)) {
presel = MSGWAIT;
goto nochange;
}
set_smart_ctx('+', newpath, &path, &lastname, &lastdir);
clearfilter();
goto begin;
case SEL_CYCLE: // fallthrough case SEL_CYCLE: // fallthrough
case SEL_CYCLER: // fallthrough case SEL_CYCLER: // fallthrough
case SEL_CTX1: // fallthrough case SEL_CTX1: // fallthrough
case SEL_CTX2: // fallthrough case SEL_CTX2: // fallthrough
case SEL_CTX3: // fallthrough case SEL_CTX3: // fallthrough
case SEL_CTX4: case SEL_CTX4:
#ifdef CTX8 #ifdef CTX8
case SEL_CTX5: case SEL_CTX5:
case SEL_CTX6: case SEL_CTX6:
case SEL_CTX7: case SEL_CTX7:
case SEL_CTX8: case SEL_CTX8:
#endif #endif
r = handle_context_switch(sel); r = handle_context_switch(sel);
if (r < 0) if (r < 0)
continue; continue;
savecurctx(&cfg, path, pdents[cur].name, r); savecurctx(path, pdents[cur].name, r);
/* Reset the pointers */ /* Reset the pointers */
path = g_ctx[r].c_path; path = g_ctx[r].c_path;
lastdir = g_ctx[r].c_last; lastdir = g_ctx[r].c_last;
lastname = g_ctx[r].c_name; lastname = g_ctx[r].c_name;
tmp = g_ctx[r].c_fltr; tmp = g_ctx[r].c_fltr;
if (cfg.filtermode || ((tmp[0] == FILTER || tmp[0] == RFI LTER) && tmp[1])) if (cfg.filtermode || ((tmp[0] == FILTER || tmp[0] == RFI LTER) && tmp[1]))
presel = FILTER; presel = FILTER;
else else
watch = TRUE; watch = TRUE;
goto begin; goto begin;
case SEL_MARK: case SEL_MARK:
free(mark); free(mark);
mark = xstrdup(path); mark = xstrdup(path);
printwait(mark, &presel); printwait(mark, &presel);
goto nochange; goto nochange;
case SEL_FLTR: case SEL_FLTR:
if (!ndents)
goto nochange;
/* Unwatch dir if we are still in a filtered view */ /* Unwatch dir if we are still in a filtered view */
#ifdef LINUX_INOTIFY #ifdef LINUX_INOTIFY
if (inotify_wd >= 0) { if (inotify_wd >= 0) {
inotify_rm_watch(inotify_fd, inotify_wd); inotify_rm_watch(inotify_fd, inotify_wd);
inotify_wd = -1; inotify_wd = -1;
} }
#elif defined(BSD_KQUEUE) #elif defined(BSD_KQUEUE)
if (event_fd >= 0) { if (event_fd >= 0) {
close(event_fd); close(event_fd);
event_fd = -1; event_fd = -1;
skipping to change at line 6700 skipping to change at line 6993
ENTSORT(pdents, ndents, entrycmpfn); ENTSORT(pdents, ndents, entrycmpfn);
move_cursor(ndents ? dentfind(lastname, ndents) : 0, 0); move_cursor(ndents ? dentfind(lastname, ndents) : 0, 0);
} }
continue; continue;
case SEL_STATS: // fallthrough case SEL_STATS: // fallthrough
case SEL_CHMODX: case SEL_CHMODX:
if (ndents) { if (ndents) {
tmp = (listpath && xstrcmp(path, listpath) == 0) ? listroot : path; tmp = (listpath && xstrcmp(path, listpath) == 0) ? listroot : path;
mkpath(tmp, pdents[cur].name, newpath); mkpath(tmp, pdents[cur].name, newpath);
if (lstat(newpath, &sb) == -1 if ((sel == SEL_STATS && !show_stats(newpath))
|| (sel == SEL_STATS && !show_stats(newpath, || (lstat(newpath, &sb) == -1)
&sb))
|| (sel == SEL_CHMODX && !xchmod(newpath, sb. st_mode))) { || (sel == SEL_CHMODX && !xchmod(newpath, sb. st_mode))) {
printwarn(&presel); printwarn(&presel);
goto nochange; goto nochange;
} }
if (sel == SEL_CHMODX) if (sel == SEL_CHMODX)
pdents[cur].mode ^= 0111; pdents[cur].mode ^= 0111;
} }
break; break;
case SEL_REDRAW: // fallthrough case SEL_REDRAW: // fallthrough
skipping to change at line 6793 skipping to change at line 7086
/* Toggle selection status */ /* Toggle selection status */
pdents[cur].flags ^= FILE_SELECTED; pdents[cur].flags ^= FILE_SELECTED;
if (pdents[cur].flags & FILE_SELECTED) { if (pdents[cur].flags & FILE_SELECTED) {
++nselected; ++nselected;
appendfpath(newpath, mkpath(path, pdents[cur].nam e, newpath)); appendfpath(newpath, mkpath(path, pdents[cur].nam e, newpath));
writesel(pselbuf, selbufpos - 1); /* Truncate NUL L from end */ writesel(pselbuf, selbufpos - 1); /* Truncate NUL L from end */
} else { } else {
--nselected; --nselected;
invertselbuf(path, FALSE); rmfromselbuf(mkpath(path, pdents[cur].name, g_sel ));
} }
#ifndef NOX11 #ifndef NOX11
if (cfg.x11) if (cfg.x11)
plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE ); plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE );
#endif #endif
if (!nselected)
unlink(selpath);
#ifndef NOMOUSE #ifndef NOMOUSE
if (rightclicksel) if (rightclicksel)
rightclicksel = 0; rightclicksel = 0;
else else
#endif #endif
/* move cursor to the next entry if this is not t he last entry */ /* move cursor to the next entry if this is not t he last entry */
if (!g_state.stayonsel && !g_state.picker && cur != ndents - 1) if (!g_state.stayonsel && !g_state.picker && cur != ndents - 1)
move_cursor((cur + 1) % ndents, 0); move_cursor((cur + 1) % ndents, 0);
break; break;
case SEL_SELMUL: case SEL_SELMUL:
skipping to change at line 6825 skipping to change at line 7115
startselection(); startselection();
g_state.rangesel ^= 1; g_state.rangesel ^= 1;
if (stat(path, &sb) == -1) { if (stat(path, &sb) == -1) {
printwarn(&presel); printwarn(&presel);
goto nochange; goto nochange;
} }
if (g_state.rangesel) { /* Range selection started */ if (g_state.rangesel) { /* Range selection started */
#ifndef DIR_LIMITED_SELECTION
inode = sb.st_ino; inode = sb.st_ino;
#endif
selstartid = cur; selstartid = cur;
continue; continue;
} }
#ifndef DIR_LIMITED_SELECTION
if (inode != sb.st_ino) { if (inode != sb.st_ino) {
printwait(messages[MSG_DIR_CHANGED], &presel); printwait(messages[MSG_DIR_CHANGED], &presel);
goto nochange; goto nochange;
} }
#endif
if (cur < selstartid) { if (cur < selstartid) {
selendid = selstartid; selendid = selstartid;
selstartid = cur; selstartid = cur;
} else } else
selendid = cur; selendid = cur;
/* Clear selection on repeat on same file */ /* Clear selection on repeat on same file */
if (selstartid == selendid) { if (selstartid == selendid) {
resetselind(); resetselind();
clearselection(); clearselection();
skipping to change at line 6864 skipping to change at line 7151
goto nochange; goto nochange;
startselection(); startselection();
if (g_state.rangesel) if (g_state.rangesel)
g_state.rangesel = 0; g_state.rangesel = 0;
selstartid = 0; selstartid = 0;
selendid = ndents - 1; selendid = ndents - 1;
} }
(sel == SEL_SELINV) ? invertselbuf(path, TRUE) : addtosel if ((nselected > LARGESEL) || (nselected && (ndents > LAR
buf(path, selstartid, selendid); GESEL))) {
printmsg("processing...");
refresh();
}
r = scanselforpath(path, TRUE); /* Get path length suffix
ed by '/' */
((sel == SEL_SELINV) && findselpos)
? invertselbuf(r) : addtoselbuf(r, selstartid, se
lendid);
#ifndef NOX11 #ifndef NOX11
if (cfg.x11) if (cfg.x11)
plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE ); plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE );
#endif #endif
continue; continue;
case SEL_SELEDIT: case SEL_SELEDIT:
r = editselection(); r = editselection();
if (r <= 0) { if (r <= 0) {
r = !r ? MSG_0_SELECTED : MSG_FAILED; r = !r ? MSG_0_SELECTED : MSG_FAILED;
skipping to change at line 6903 skipping to change at line 7197
goto nochange; goto nochange;
} }
if (r == 'c') { if (r == 'c') {
tmp = (listpath && xstrcmp(path, listpath ) == 0) tmp = (listpath && xstrcmp(path, listpath ) == 0)
? listroot : path; ? listroot : path;
mkpath(tmp, pdents[cur].name, newpath); mkpath(tmp, pdents[cur].name, newpath);
if (!xrm(newpath)) if (!xrm(newpath))
continue; continue;
xrmfromsel(tmp, newpath);
copynextname(lastname); copynextname(lastname);
if (cfg.filtermode || filterset()) if (cfg.filtermode || filterset())
presel = FILTER; presel = FILTER;
goto begin; goto begin;
} }
} }
if (nselected == 1 && (sel == SEL_CP || sel == SEL_MV)) if (nselected == 1 && (sel == SEL_CP || sel == SEL_MV))
mkpath(path, xbasename(pselbuf), newpath); mkpath(path, xbasename(pselbuf), newpath);
skipping to change at line 7010 skipping to change at line 7306
mkpath(path, tmp, newpath); mkpath(path, tmp, newpath);
if (access(newpath, F_OK) == 0) { if (access(newpath, F_OK) == 0) {
if (!xconfirm(get_input(messages[MSG_OVER WRITE]))) { if (!xconfirm(get_input(messages[MSG_OVER WRITE]))) {
statusbar(path); statusbar(path);
goto nochange; goto nochange;
} }
} }
get_archive_cmd(newpath, tmp); get_archive_cmd(newpath, tmp);
(r == 's') ? archive_selection(newpath, tmp, path ) (r == 's') ? archive_selection(newpath, tmp, path )
: spawn(newpath, tmp, pdents[cur].name : spawn(newpath, tmp, pdents[cur].name
, NULL, F_CLI | F_CONFIRM); ,
NULL, F_CLI | F_CONFIRM);
mkpath(path, tmp, newpath); mkpath(path, tmp, newpath);
if (access(newpath, F_OK) == 0) { /* File created */ if (access(newpath, F_OK) == 0) { /* File created */
xstrsncpy(lastname, tmp, NAME_MAX + 1); xstrsncpy(lastname, tmp, NAME_MAX + 1);
clearfilter(); /* Archive name may not ma tch */ clearfilter(); /* Archive name may not ma tch */
clearselection(); /* Archive operation co mplete */ clearselection(); /* Archive operation co mplete */
goto begin; goto begin;
} }
continue; continue;
case SEL_OPENWITH: case SEL_OPENWITH:
skipping to change at line 7144 skipping to change at line 7441
printwait(messages[MSG_INVALID_KEY], &pre sel); printwait(messages[MSG_INVALID_KEY], &pre sel);
goto nochange; goto nochange;
} }
if (tmp[0] == '-' && tmp[1]) { if (tmp[0] == '-' && tmp[1]) {
++tmp; ++tmp;
r = FALSE; /* Do not refresh dir after co mpletion */ r = FALSE; /* Do not refresh dir after co mpletion */
} else } else
r = TRUE; r = TRUE;
if (!run_selected_plugin(&path, tmp, (ndents ? pd ents[cur].name : NULL), if (!run_plugin(&path, tmp, (ndents ? pdents[cur] .name : NULL),
&lastname, &lastdir)) { &lastname, &lastdir)) {
printwait(messages[MSG_FAILED], &presel); printwait(messages[MSG_FAILED], &presel);
goto nochange; goto nochange;
} }
if (g_state.picked) if (g_state.picked)
return EXIT_SUCCESS; return EXIT_SUCCESS;
if (ndents) if (ndents)
copycurname(); copycurname();
skipping to change at line 7191 skipping to change at line 7488
if (ndents) if (ndents)
xstrsncpy(runfile, pdents[cur].name, NAME _MAX); xstrsncpy(runfile, pdents[cur].name, NAME _MAX);
g_state.runctx = cfg.curctx; g_state.runctx = cfg.curctx;
lastname[0] = '\0'; lastname[0] = '\0';
} }
setdirwatch(); setdirwatch();
clearfilter(); clearfilter();
goto begin; goto begin;
case SEL_SHELL: // fallthrough case SEL_SHELL: // fallthrough
case SEL_LAUNCH: // fallthrough case SEL_LAUNCH: // fallthrough
case SEL_RUNCMD: case SEL_PROMPT:
r = handle_cmd(sel, (ndents ? pdents[cur].name : ""), new path); r = handle_cmd(sel, (ndents ? pdents[cur].name : ""), new path);
/* Continue in type-to-nav mode, if enabled */ /* Continue in type-to-nav mode, if enabled */
if (cfg.filtermode) if (cfg.filtermode)
presel = FILTER; presel = FILTER;
/* Save current */ /* Save current */
if (ndents) if (ndents)
copycurname(); copycurname();
if (!r) if (!r)
goto nochange; goto nochange;
/* Repopulate as directory content may have changed */ /* Repopulate as directory content may have changed */
goto begin; goto begin;
case SEL_UMOUNT: case SEL_UMOUNT:
if (!unmount((ndents ? pdents[cur].name : NULL), newpath, presel = MSG_ZERO;
&presel, path)) if (!unmount((ndents ? pdents[cur].name : NULL), newpath,
&presel, path)) {
if (presel == MSG_ZERO)
statusbar(path);
goto nochange; goto nochange;
}
/* Dir removed, go to next entry */ /* Dir removed, go to next entry */
copynextname(lastname); copynextname(lastname);
goto begin; goto begin;
#ifndef NOSSN #ifndef NOSSN
case SEL_SESSIONS: case SEL_SESSIONS:
r = get_input(messages[MSG_SSN_OPTS]); r = get_input(messages[MSG_SSN_OPTS]);
if (r == 's') { if (r == 's') {
tmp = xreadline(NULL, messages[MSG_SSN_NAME]); tmp = xreadline(NULL, messages[MSG_SSN_NAME]);
skipping to change at line 7530 skipping to change at line 7831
malloc_2: malloc_2:
for (i = entries - 1; i >= 0; --i) for (i = entries - 1; i >= 0; --i)
free(paths[i]); free(paths[i]);
malloc_1: malloc_1:
if (msgnum) { if (msgnum) {
if (home) { /* We are past init stage */ if (home) { /* We are past init stage */
printmsg(messages[msgnum]); printmsg(messages[msgnum]);
xdelay(XDELAY_INTERVAL_MS); xdelay(XDELAY_INTERVAL_MS);
} else } else
fprintf(stderr, "%s\n", messages[msgnum]); msg(messages[msgnum]);
} }
free(input); free(input);
free(paths); free(paths);
return tmpdir; return tmpdir;
} }
static void check_key_collision(void) static void check_key_collision(void)
{ {
int key; int key;
bool bitmap[KEY_MAX] = {FALSE}; bool bitmap[KEY_MAX] = {FALSE};
for (ulong_t i = 0; i < sizeof(bindings) / sizeof(struct key); ++i) { for (ullong_t i = 0; i < sizeof(bindings) / sizeof(struct key); ++i) {
key = bindings[i].sym; key = bindings[i].sym;
if (bitmap[key]) if (bitmap[key])
fprintf(stdout, "key collision! [%s]\n", keyname(key)); dprintf(STDERR_FILENO, "key collision! [%s]\n", keyname(k ey));
else else
bitmap[key] = TRUE; bitmap[key] = TRUE;
} }
} }
static void usage(void) static void usage(void)
{ {
fprintf(stdout, dprintf(STDERR_FILENO,
"%s: nnn [OPTIONS] [PATH]\n\n" "%s: nnn [OPTIONS] [PATH]\n\n"
"The unorthodox terminal file manager.\n\n" "The unorthodox terminal file manager.\n\n"
"positional args:\n" "positional args:\n"
" PATH start dir/file [default: .]\n\n" " PATH start dir/file [default: .]\n\n"
"optional args:\n" "optional args:\n"
#ifndef NOFIFO #ifndef NOFIFO
" -a auto NNN_FIFO\n" " -a auto NNN_FIFO\n"
#endif #endif
" -A no dir auto-select\n" " -A no dir auto-select\n"
" -b key open bookmark key (trumps -s/S)\n" " -b key open bookmark key (trumps -s/S)\n"
" -c cli-only NNN_OPENER (trumps -e)\n" " -c cli-only NNN_OPENER (trumps -e)\n"
" -C 8-color scheme\n" " -C 8-color scheme\n"
" -d detail mode\n" " -d detail mode\n"
" -D dirs in context color\n" " -D dirs in context color\n"
" -e text in $VISUAL/$EDITOR/vi\n" " -e text in $VISUAL/$EDITOR/vi\n"
" -E internal edits in EDITOR\n" " -E internal edits in EDITOR\n"
#ifndef NORL #ifndef NORL
" -f use readline history file\n" " -f use readline history file\n"
#endif #endif
" -F show fortune\n" #ifndef NOFIFO
" -F val fifo mode [0:preview 1:explore]\n"
#endif
" -g regex filters\n" " -g regex filters\n"
" -H show hidden files\n" " -H show hidden files\n"
" -J no auto-proceed on select\n" " -J no auto-proceed on select\n"
" -K detect key collision\n" " -K detect key collision\n"
" -l val set scroll lines\n" " -l val set scroll lines\n"
" -n type-to-nav mode\n" " -n type-to-nav mode\n"
" -o open files only on Enter\n" " -o open files only on Enter\n"
" -p file selection file [stdout if '-']\n" " -p file selection file [-:stdout]\n"
" -P key run plugin key\n" " -P key run plugin key\n"
" -Q no quit confirmation\n" " -Q no quit confirmation\n"
" -r use advcpmv patched cp, mv\n" " -r use advcpmv patched cp, mv\n"
" -R no rollover at edges\n" " -R no rollover at edges\n"
#ifndef NOSSN #ifndef NOSSN
" -s name load session by name\n" " -s name load session by name\n"
" -S persistent session\n" " -S persistent session\n"
#endif #endif
" -t secs timeout to lock\n" " -t secs timeout to lock\n"
" -T key sort order [a/d/e/r/s/t/v]\n" " -T key sort order [a/d/e/r/s/t/v]\n"
skipping to change at line 7695 skipping to change at line 7998
return TRUE; return TRUE;
} }
static bool set_tmp_path(void) static bool set_tmp_path(void)
{ {
char *tmp = "/tmp"; char *tmp = "/tmp";
char *path = xdiraccess(tmp) ? tmp : getenv("TMPDIR"); char *path = xdiraccess(tmp) ? tmp : getenv("TMPDIR");
if (!path) { if (!path) {
fprintf(stderr, "set TMPDIR\n"); msg("set TMPDIR");
return FALSE; return FALSE;
} }
tmpfplen = (uchar_t)xstrsncpy(g_tmpfpath, path, TMP_LEN_MAX); tmpfplen = (uchar_t)xstrsncpy(g_tmpfpath, path, TMP_LEN_MAX);
DPRINTF_S(g_tmpfpath); DPRINTF_S(g_tmpfpath);
DPRINTF_U(tmpfplen); DPRINTF_U(tmpfplen);
return TRUE; return TRUE;
} }
skipping to change at line 7724 skipping to change at line 8027
free(selpath); free(selpath);
free(plgpath); free(plgpath);
free(cfgpath); free(cfgpath);
free(initpath); free(initpath);
free(bmstr); free(bmstr);
free(pluginstr); free(pluginstr);
free(listroot); free(listroot);
free(ihashbmp); free(ihashbmp);
free(bookmark); free(bookmark);
free(plug); free(plug);
free(lastcmd);
#ifndef NOFIFO #ifndef NOFIFO
if (g_state.autofifo) if (g_state.autofifo)
unlink(fifopath); unlink(fifopath);
#endif #endif
if (g_state.pluginit) if (g_state.pluginit)
unlink(g_pipepath); unlink(g_pipepath);
#ifdef DEBUG #ifdef DEBUG
disabledbg(); disabledbg();
#endif #endif
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
char *arg = NULL; char *arg = NULL;
char *session = NULL; char *session = NULL;
int fd, opt, sort = 0, pkey = '\0'; /* Plugin key */ int fd, opt, sort = 0, pkey = '\0'; /* Plugin key */
#ifndef NOMOUSE #ifndef NOMOUSE
mmask_t mask; mmask_t mask;
char *middle_click_env = xgetenv(env_cfg[NNN_MCLICK], "\0"); char *middle_click_env = xgetenv(env_cfg[NNN_MCLICK], "\0");
if (middle_click_env[0] == '^' && middle_click_env[1]) middle_click_key = (middle_click_env[0] == '^' && middle_click_env[1])
middle_click_key = CONTROL(middle_click_env[1]); ? CONTROL(middle_click_env[1])
else : (uchar_t)middle_click_env[0];
middle_click_key = (uchar_t)middle_click_env[0];
#endif #endif
const char * const env_opts = xgetenv(env_cfg[NNN_OPTS], NULL); const char * const env_opts = xgetenv(env_cfg[NNN_OPTS], NULL);
int env_opts_id = env_opts ? (int)xstrlen(env_opts) : -1; int env_opts_id = env_opts ? (int)xstrlen(env_opts) : -1;
#ifndef NORL #ifndef NORL
bool rlhist = FALSE; bool rlhist = FALSE;
#endif #endif
while ((opt = (env_opts_id > 0 while ((opt = (env_opts_id > 0
? env_opts[--env_opts_id] ? env_opts[--env_opts_id]
: getopt(argc, argv, "aAb:cCdDeEfFgHJKl:nop:P:QrRs:St:T:uU Vwxh"))) != -1) { : getopt(argc, argv, "aAb:cCdDeEfF:gHJKl:nop:P:QrRs:St:T:u UVwxh"))) != -1) {
switch (opt) { switch (opt) {
#ifndef NOFIFO #ifndef NOFIFO
case 'a': case 'a':
g_state.autofifo = 1; g_state.autofifo = 1;
break; break;
#endif #endif
case 'A': case 'A':
cfg.autoselect = 0; cfg.autoselect = 0;
break; break;
case 'b': case 'b':
skipping to change at line 7795 skipping to change at line 8098
cfg.useeditor = 1; cfg.useeditor = 1;
break; break;
case 'E': case 'E':
cfg.waitedit = 1; cfg.waitedit = 1;
break; break;
case 'f': case 'f':
#ifndef NORL #ifndef NORL
rlhist = TRUE; rlhist = TRUE;
#endif #endif
break; break;
#ifndef NOFIFO
case 'F': case 'F':
g_state.fortune = 1; if (env_opts_id < 0) {
fd = atoi(optarg);
if ((fd < 0) || (fd > 1))
return EXIT_FAILURE;
g_state.fifomode = fd;
}
break; break;
#endif
case 'g': case 'g':
cfg.regex = 1; cfg.regex = 1;
filterfn = &visible_re; filterfn = &visible_re;
break; break;
case 'H': case 'H':
cfg.showhidden = 1; cfg.showhidden = 1;
break; break;
case 'J': case 'J':
g_state.stayonsel = 1; g_state.stayonsel = 1;
break; break;
skipping to change at line 7880 skipping to change at line 8190
if (env_opts_id < 0) if (env_opts_id < 0)
sort = (uchar_t)optarg[0]; sort = (uchar_t)optarg[0];
break; break;
case 'u': case 'u':
cfg.prefersel = 1; cfg.prefersel = 1;
break; break;
case 'U': case 'U':
g_state.uidgid = 1; g_state.uidgid = 1;
break; break;
case 'V': case 'V':
fprintf(stdout, "%s\n", VERSION); dprintf(STDOUT_FILENO, "%s\n", VERSION);
return EXIT_SUCCESS; return EXIT_SUCCESS;
case 'w': case 'w':
cfg.cursormode = 1; cfg.cursormode = 1;
break; break;
case 'x': case 'x':
cfg.x11 = 1; cfg.x11 = 1;
break; break;
case 'h': case 'h':
usage(); usage();
return EXIT_SUCCESS; return EXIT_SUCCESS;
skipping to change at line 7926 skipping to change at line 8236
/* We return to tty */ /* We return to tty */
dup2(STDOUT_FILENO, STDIN_FILENO); dup2(STDOUT_FILENO, STDIN_FILENO);
if (session) if (session)
session = NULL; session = NULL;
} }
home = getenv("HOME"); home = getenv("HOME");
if (!home) { if (!home) {
fprintf(stderr, "set HOME\n"); msg("set HOME");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
DPRINTF_S(home); DPRINTF_S(home);
homelen = (uchar_t)xstrlen(home); homelen = (uchar_t)xstrlen(home);
if (!setup_config()) if (!setup_config())
return EXIT_FAILURE; return EXIT_FAILURE;
/* Get custom opener, if set */ /* Get custom opener, if set */
opener = xgetenv(env_cfg[NNN_OPENER], utils[UTIL_OPENER]); opener = xgetenv(env_cfg[NNN_OPENER], utils[UTIL_OPENER]);
DPRINTF_S(opener); DPRINTF_S(opener);
/* Parse bookmarks string */ /* Parse bookmarks string */
if (!parsekvpair(&bookmark, &bmstr, NNN_BMS, &maxbm)) { if (!parsekvpair(&bookmark, &bmstr, NNN_BMS, &maxbm)) {
fprintf(stderr, "%s\n", env_cfg[NNN_BMS]); msg(env_cfg[NNN_BMS]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/* Parse plugins string */ /* Parse plugins string */
if (!parsekvpair(&plug, &pluginstr, NNN_PLUG, &maxplug)) { if (!parsekvpair(&plug, &pluginstr, NNN_PLUG, &maxplug)) {
fprintf(stderr, "%s\n", env_cfg[NNN_PLUG]); msg(env_cfg[NNN_PLUG]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (!initpath) { if (!initpath) {
if (arg) { /* Open a bookmark directly */ 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, maxbm , NNN_BMS); initpath = get_kv_val(bookmark, NULL, *arg, maxbm , NNN_BMS);
if (!initpath) { if (!initpath) {
fprintf(stderr, "%s\n", messages[MSG_INVALID_KEY] ); msg(messages[MSG_INVALID_KEY]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (session) if (session)
session = NULL; session = NULL;
} else if (argc == optind) { } else if (argc == optind) {
/* Start in the current directory */ /* Start in the current directory */
initpath = getcwd(NULL, 0); initpath = getcwd(NULL, 0);
if (!initpath) if (!initpath)
initpath = "/"; initpath = "/";
skipping to change at line 8007 skipping to change at line 8317
} }
} }
/* Set archive handling (enveditor used as tmp var) */ /* Set archive handling (enveditor used as tmp var) */
enveditor = getenv(env_cfg[NNN_ARCHIVE]); enveditor = getenv(env_cfg[NNN_ARCHIVE]);
#ifdef PCRE #ifdef PCRE
if (setfilter(&archive_pcre, (enveditor ? enveditor : patterns[P_ARCHIVE] ))) { if (setfilter(&archive_pcre, (enveditor ? enveditor : patterns[P_ARCHIVE] ))) {
#else #else
if (setfilter(&archive_re, (enveditor ? enveditor : patterns[P_ARCHIVE])) ) { if (setfilter(&archive_re, (enveditor ? enveditor : patterns[P_ARCHIVE])) ) {
#endif #endif
fprintf(stderr, "%s\n", messages[MSG_INVALID_REG]); msg(messages[MSG_INVALID_REG]);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/* An all-CLI opener overrides option -e) */ /* An all-CLI opener overrides option -e) */
if (cfg.cliopener) if (cfg.cliopener)
cfg.useeditor = 0; cfg.useeditor = 0;
/* Get VISUAL/EDITOR */ /* Get VISUAL/EDITOR */
enveditor = xgetenv(envs[ENV_EDITOR], utils[UTIL_VI]); enveditor = xgetenv(envs[ENV_EDITOR], utils[UTIL_VI]);
editor = xgetenv(envs[ENV_VISUAL], enveditor); editor = xgetenv(envs[ENV_VISUAL], enveditor);
skipping to change at line 8200 skipping to change at line 8510
close(inotify_fd); close(inotify_fd);
#elif defined(BSD_KQUEUE) #elif defined(BSD_KQUEUE)
if (event_fd >= 0) if (event_fd >= 0)
close(event_fd); close(event_fd);
close(kq); close(kq);
#elif defined(HAIKU_NM) #elif defined(HAIKU_NM)
haiku_close_nm(haiku_hnd); haiku_close_nm(haiku_hnd);
#endif #endif
#ifndef NOFIFO #ifndef NOFIFO
notify_fifo(FALSE); if (!g_state.fifomode)
notify_fifo(FALSE);
if (fifofd != -1) if (fifofd != -1)
close(fifofd); close(fifofd);
#endif #endif
return opt; return opt;
} }
 End of changes. 359 change blocks. 
796 lines changed or deleted 1130 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)