diff options
author | Arun Prakash Jana <engineerarun@gmail.com> | 2020-08-07 23:16:17 +0530 |
---|---|---|
committer | Arun Prakash Jana <engineerarun@gmail.com> | 2020-08-07 23:16:17 +0530 |
commit | c566afd81948f6a208bef1973efac064a3d17a38 (patch) | |
tree | d1e7c8b46cc8ac1771b34012d282524485ee395c /src/nnn.h | |
parent | 1fecdb2393c31283267c01dd4f48d9ff6df17474 (diff) | |
download | nnn-c566afd81948f6a208bef1973efac064a3d17a38.tar.gz |
Revert "Move helper APIs to header file"
This reverts commit 1fecdb2393c31283267c01dd4f48d9ff6df17474.
Diffstat (limited to 'src/nnn.h')
-rw-r--r-- | src/nnn.h | 1613 |
1 files changed, 0 insertions, 1613 deletions
@@ -32,114 +32,7 @@ #include <curses.h> -#ifndef S_BLKSIZE -#define S_BLKSIZE 512 /* S_BLKSIZE is missing on Android NDK (Termux) */ -#endif - -/* - * NAME_MAX and PATH_MAX may not exist, e.g. with dirent.c_name being a - * flexible array on Illumos. Use somewhat accomodating fallback values. - */ -#ifndef NAME_MAX -#define NAME_MAX 255 -#endif - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - #define CONTROL(c) ((c) & 0x1f) -#define _ABSSUB(N, M) (((N) <= (M)) ? ((M) - (N)) : ((N) - (M))) -#define DOUBLECLICK_INTERVAL_NS (400000000) -#define XDELAY_INTERVAL_MS (350000) /* 350 ms delay */ -#define ELEMENTS(x) (sizeof(x) / sizeof(*(x))) -#undef MIN -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#undef MAX -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#define ISODD(x) ((x) & 1) -#define ISBLANK(x) ((x) == ' ' || (x) == '\t') -#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 FILTER '/' -#define RFILTER '\\' -#define CASE ':' -#define MSGWAIT '$' -#define SELECT ' ' -#define REGEX_MAX 48 -#define ENTRY_INCR 64 /* Number of dir 'entry' structures to allocate per shot */ -#define NAMEBUF_INCR 0x800 /* 64 dir entries at once, avg. 32 chars per filename = 64*32B = 2KB */ -#define DESCRIPTOR_LEN 32 -#define _ALIGNMENT 0x10 /* 16-byte alignment */ -#define _ALIGNMENT_MASK 0xF -#define TMP_LEN_MAX 64 -#define DOT_FILTER_LEN 7 -#define ASCII_MAX 128 -#define EXEC_ARGS_MAX 8 -#define LIST_FILES_MAX (1 << 16) -#define SCROLLOFF 3 - -#ifndef CTX8 -#define CTX_MAX 4 -#else -#define CTX_MAX 8 -#endif - -#define MIN_DISPLAY_COLS ((CTX_MAX * 2) + 2) /* Two chars for [ and ] */ -#define LONG_SIZE sizeof(ulong) -#define ARCHIVE_CMD_LEN 16 -#define BLK_SHIFT_512 9 - -/* Detect hardlinks in du */ -#define HASH_BITS (0xFFFFFF) -#define HASH_OCTETS (HASH_BITS >> 6) /* 2^6 = 64 */ - -/* Entry flags */ -#define DIR_OR_LINK_TO_DIR 0x01 -#define HARD_LINK 0x02 -#define SYM_ORPHAN 0x04 -#define FILE_MISSING 0x08 -#define FILE_SELECTED 0x10 - -/* Macros to define process spawn behaviour as flags */ -#define F_NONE 0x00 /* no flag set */ -#define F_MULTI 0x01 /* first arg can be combination of args; to be used with F_NORMAL */ -#define F_NOWAIT 0x02 /* don't wait for child process (e.g. file manager) */ -#define F_NOTRACE 0x04 /* suppress stdout and strerr (no traces) */ -#define F_NORMAL 0x08 /* spawn child process in non-curses regular CLI mode */ -#define F_CONFIRM 0x10 /* run command - show results before exit (must have F_NORMAL) */ -#define F_CHKRTN 0x20 /* wait for user prompt if cmd returns failure status */ -#define F_CLI (F_NORMAL | F_MULTI) -#define F_SILENT (F_CLI | F_NOTRACE) - -/* Version compare macros */ -/* - * states: S_N: normal, S_I: comparing integral part, S_F: comparing - * fractional parts, S_Z: idem but with leading Zeroes only - */ -#define S_N 0x0 -#define S_I 0x3 -#define S_F 0x6 -#define S_Z 0x9 - -/* result_type: VCMP: return diff; VLEN: compare using len_diff/diff */ -#define VCMP 2 -#define VLEN 3 - -/* Volume info */ -#define FREE 0 -#define CAPACITY 1 - -/* TYPE DEFINITIONS */ -typedef unsigned long ulong; -typedef unsigned int uint; -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef long long ll; -typedef unsigned long long ull; - -/* STRUCTURES */ /* Supported actions */ enum action { @@ -381,1509 +274,3 @@ static struct key bindings[] = { { KEY_MOUSE, SEL_CLICK }, #endif }; - -/* Directory entry */ -typedef struct entry { - char *name; - time_t t; - off_t size; - blkcnt_t blocks; /* number of 512B blocks allocated */ - mode_t mode; - ushort nlen; /* Length of file name */ - uchar flags; /* Flags specific to the file */ -} *pEntry; - -/* Key-value pairs from env */ -typedef struct { - int key; - int off; -} kv; - -typedef struct { -#ifdef PCRE - const pcre *pcrex; -#else - const regex_t *regex; -#endif - const char *str; -} fltrexp_t; - -/* - * Settings - * NOTE: update default values if changing order - */ -typedef struct { - uint filtermode : 1; /* Set to enter filter mode */ - uint timeorder : 1; /* Set to sort by time */ - uint sizeorder : 1; /* Set to sort by file size */ - uint apparentsz : 1; /* Set to sort by apparent size (disk usage) */ - uint blkorder : 1; /* Set to sort by blocks used (disk usage) */ - uint extnorder : 1; /* Order by extension */ - uint showhidden : 1; /* Set to show hidden files */ - uint reserved0 : 1; - uint showdetail : 1; /* Clear to show lesser file info */ - uint ctxactive : 1; /* Context active or not */ - uint reverse : 1; /* Reverse sort */ - uint version : 1; /* Version sort */ - uint reserved1 : 1; - /* The following settings are global */ - uint curctx : 3; /* Current context number */ - uint prefersel : 1; /* Prefer selection over current, if exists */ - uint reserved2 : 1; - uint nonavopen : 1; /* Open file on right arrow or `l` */ - uint autoselect : 1; /* Auto-select dir in type-to-nav mode */ - uint cursormode : 1; /* Move hardware cursor with selection */ - uint useeditor : 1; /* Use VISUAL to open text files */ - uint reserved3 : 3; - uint regex : 1; /* Use regex filters */ - uint x11 : 1; /* Copy to system clipboard and show notis */ - uint timetype : 2; /* Time sort type (0: access, 1: change, 2: modification) */ - uint cliopener : 1; /* All-CLI app opener */ - uint waitedit : 1; /* For ops that can't be detached, used EDITOR */ - uint rollover : 1; /* Roll over at edges */ -} settings; - -/* Non-persistent program-internal states */ -typedef struct { - uint pluginit : 1; /* Plugin framework initialized */ - uint interrupt : 1; /* Program received an interrupt */ - uint rangesel : 1; /* Range selection on */ - uint move : 1; /* Move operation */ - uint autonext : 1; /* Auto-proceed on open */ - uint fortune : 1; /* Show fortune messages in help */ - uint trash : 1; /* Use trash to delete files */ - uint forcequit : 1; /* Do not prompt on quit */ - uint autofifo : 1; /* Auto-create NNN_FIFO */ - uint initfile : 1; /* Positional arg is a file */ - uint dircolor : 1; /* Current status of dir color */ - uint picker : 1; /* Write selection to user-specified file */ - uint pickraw : 1; /* Write selection to sdtout before exit */ - uint runplugin : 1; /* Choose plugin mode */ - uint runctx : 2; /* The context in which plugin is to be run */ - uint selmode : 1; /* Set when selecting files */ - uint oldcolor : 1; /* Show dirs in context colors */ - uint reserved : 14; -} runstate; - -/* Contexts or workspaces */ -typedef struct { - char c_path[PATH_MAX]; /* Current dir */ - char c_last[PATH_MAX]; /* Last visited dir */ - char c_name[NAME_MAX + 1]; /* Current file name */ - char c_fltr[REGEX_MAX]; /* Current filter */ - settings c_cfg; /* Current configuration */ - uint color; /* Color code for directories */ -} context; - -typedef struct { - size_t ver; - size_t pathln[CTX_MAX]; - size_t lastln[CTX_MAX]; - size_t nameln[CTX_MAX]; - size_t fltrln[CTX_MAX]; -} session_header_t; - -/* GLOBALS */ - -/* Configuration, contexts */ -static settings cfg = { - 0, /* filtermode */ - 0, /* timeorder */ - 0, /* sizeorder */ - 0, /* apparentsz */ - 0, /* blkorder */ - 0, /* extnorder */ - 0, /* showhidden */ - 0, /* reserved0 */ - 0, /* showdetail */ - 1, /* ctxactive */ - 0, /* reverse */ - 0, /* version */ - 0, /* reserved1 */ - 0, /* curctx */ - 0, /* prefersel */ - 0, /* reserved2 */ - 0, /* nonavopen */ - 1, /* autoselect */ - 0, /* cursormode */ - 0, /* useeditor */ - 0, /* reserved3 */ - 0, /* regex */ - 0, /* x11 */ - 2, /* timetype (T_MOD) */ - 0, /* cliopener */ - 0, /* waitedit */ - 1, /* rollover */ -}; - -static context g_ctx[CTX_MAX] __attribute__ ((aligned)); - -static int ndents, cur, last, curscroll, last_curscroll, total_dents = ENTRY_INCR, scroll_lines = 1; -static int nselected; -#ifndef NOFIFO -static int fifofd = -1; -#endif -static uint idletimeout, selbufpos, lastappendpos, selbuflen; -static ushort xlines, xcols; -static ushort idle; -static uchar maxbm, maxplug; -static char *bmstr; -static char *pluginstr; -static char *opener; -static char *editor; -static char *enveditor; -static char *pager; -static char *shell; -static char *home; -static char *initpath; -static char *cfgpath; -static char *selpath; -static char *listpath; -static char *listroot; -static char *plgpath; -static char *pnamebuf, *pselbuf; -static char *mark; -#ifndef NOFIFO -static char *fifopath; -#endif -static ull *ihashbmp; -static struct entry *pdents; -static blkcnt_t ent_blocks; -static blkcnt_t dir_blocks; -static ulong num_files; -static kv *bookmark; -static kv *plug; -static uchar tmpfplen; -static uchar blk_shift = BLK_SHIFT_512; -#ifndef NOMOUSE -static int middle_click_key; -#endif -#ifdef PCRE -static pcre *archive_pcre; -#else -static regex_t archive_re; -#endif - -/* Retain old signal handlers */ -static struct sigaction oldsighup; -static struct sigaction oldsigtstp; - -/* For use in functions which are isolated and don't return the buffer */ -static char g_buf[CMD_LEN_MAX] __attribute__ ((aligned)); - -/* Buffer to store tmp file path to show selection, file stats and help */ -static char g_tmpfpath[TMP_LEN_MAX] __attribute__ ((aligned)); - -/* Buffer to store plugins control pipe location */ -static char g_pipepath[TMP_LEN_MAX] __attribute__ ((aligned)); - -/* Non-persistent runtime states */ -static runstate g_state; - -/* Options to identify file mime */ -#if defined(__APPLE__) -#define FILE_MIME_OPTS "-bIL" -#elif !defined(__sun) /* no mime option for 'file' */ -#define FILE_MIME_OPTS "-biL" -#endif - -/* Macros for utilities */ -#define UTIL_OPENER 0 -#define UTIL_ATOOL 1 -#define UTIL_BSDTAR 2 -#define UTIL_UNZIP 3 -#define UTIL_TAR 4 -#define UTIL_LOCKER 5 -#define UTIL_LAUNCH 6 -#define UTIL_SH_EXEC 7 -#define UTIL_BASH 8 -#define UTIL_ARCHIVEMOUNT 9 -#define UTIL_SSHFS 10 -#define UTIL_RCLONE 11 -#define UTIL_VI 12 -#define UTIL_LESS 13 -#define UTIL_SH 14 -#define UTIL_FZF 15 -#define UTIL_NTFY 16 -#define UTIL_CBCP 17 -#define UTIL_NMV 18 - -/* Utilities to open files, run actions */ -static char * const utils[] = { -#ifdef __APPLE__ - "/usr/bin/open", -#elif defined __CYGWIN__ - "cygstart", -#elif defined __HAIKU__ - "open", -#else - "xdg-open", -#endif - "atool", - "bsdtar", - "unzip", - "tar", -#ifdef __APPLE__ - "bashlock", -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - "lock", -#elif defined __HAIKU__ - "peaclock", -#else - "vlock", -#endif - "launch", - "sh -c", - "bash", - "archivemount", - "sshfs", - "rclone", - "vi", - "less", - "sh", - "fzf", - ".ntfy", - ".cbcp", - ".nmv", -}; - -/* Common strings */ -#define MSG_NO_TRAVERSAL 0 -#define MSG_INVALID_KEY 1 -#define STR_TMPFILE 2 -#define MSG_0_SELECTED 3 -#define MSG_UTIL_MISSING 4 -#define MSG_FAILED 5 -#define MSG_SSN_NAME 6 -#define MSG_CP_MV_AS 7 -#define MSG_CUR_SEL_OPTS 8 -#define MSG_FORCE_RM 9 -#define MSG_LIMIT 10 -#define MSG_NEW_OPTS 11 -#define MSG_CLI_MODE 12 -#define MSG_OVERWRITE 13 -#define MSG_SSN_OPTS 14 -#define MSG_QUIT_ALL 15 -#define MSG_HOSTNAME 16 -#define MSG_ARCHIVE_NAME 17 -#define MSG_OPEN_WITH 18 -#define MSG_REL_PATH 19 -#define MSG_LINK_PREFIX 20 -#define MSG_COPY_NAME 21 -#define MSG_CONTINUE 22 -#define MSG_SEL_MISSING 23 -#define MSG_ACCESS 24 -#define MSG_EMPTY_FILE 25 -#define MSG_UNSUPPORTED 26 -#define MSG_NOT_SET 27 -#define MSG_EXISTS 28 -#define MSG_FEW_COLUMNS 29 -#define MSG_REMOTE_OPTS 30 -#define MSG_RCLONE_DELAY 31 -#define MSG_APP_NAME 32 -#define MSG_ARCHIVE_OPTS 33 -#define MSG_PLUGIN_KEYS 34 -#define MSG_BOOKMARK_KEYS 35 -#define MSG_INVALID_REG 36 -#define MSG_ORDER 37 -#define MSG_LAZY 38 -#define MSG_FIRST 39 -#define MSG_RM_TMP 40 -#define MSG_NOCHNAGE 41 -#define MSG_CANCEL 42 -#define MSG_0_ENTRIES 43 -#ifndef DIR_LIMITED_SELECTION -#define MSG_DIR_CHANGED 44 /* Must be the last entry */ -#endif - -static const char * const messages[] = { - "no traversal", - "invalid key", - "/.nnnXXXXXX", - "0 selected", - "missing util", - "failed!", - "session name: ", - "'c'p / 'm'v as?", - "'c'urrent / 's'el?", - "rm -rf %s file%s? [Esc cancels]", - "limit exceeded", - "'f'ile / 'd'ir / 's'ym / 'h'ard?", - "'c'li / 'g'ui?", - "overwrite?", - "'s'ave / 'l'oad / 'r'estore?", - "Quit all contexts?", - "remote name ('-' for hovered): ", - "archive name: ", - "open with: ", - "relative path: ", - "link prefix [@ for none]: ", - "copy name: ", - "\n'Enter' to continue", - "open failed", - "dir inaccessible", - "empty: edit/open with", - "unknown", - "not set", - "entry exists", - "too few columns!", - "'s'shfs / 'r'clone?", - "refresh if slow", - "app name: ", - "'d'efault / e'x'tract / 'l'ist / 'm'ount?", - "plugin keys:", - "bookmark keys:", - "invalid regex", - "'a'u / 'd'u / 'e'xtn / 'r'ev / 's'ize / 't'ime / 'v'er / 'c'lear?", - "unmount failed! try lazy?", - "first file (\')/char?", - "remove tmp file?", - "unchanged", - "cancelled", - "0 entries", -#ifndef DIR_LIMITED_SELECTION - "dir changed, range sel off", /* Must be the last entry */ -#endif -}; - -/* Supported configuration environment variables */ -#define NNN_OPTS 0 -#define NNN_BMS 1 -#define NNN_PLUG 2 -#define NNN_OPENER 3 -#define NNN_COLORS 4 -#define NNNLVL 5 -#define NNN_PIPE 6 -#define NNN_MCLICK 7 -#define NNN_SEL 8 -#define NNN_ARCHIVE 9 /* strings end here */ -#define NNN_TRASH 10 /* flags begin here */ - -static const char * const env_cfg[] = { - "NNN_OPTS", - "NNN_BMS", - "NNN_PLUG", - "NNN_OPENER", - "NNN_COLORS", - "NNNLVL", - "NNN_PIPE", - "NNN_MCLICK", - "NNN_SEL", - "NNN_ARCHIVE", - "NNN_TRASH", -}; - -/* Required environment variables */ -#define ENV_SHELL 0 -#define ENV_VISUAL 1 -#define ENV_EDITOR 2 -#define ENV_PAGER 3 -#define ENV_NCUR 4 - -static const char * const envs[] = { - "SHELL", - "VISUAL", - "EDITOR", - "PAGER", - "nnn", -}; - -/* Time type used */ -#define T_ACCESS 0 -#define T_CHANGE 1 -#define T_MOD 2 - -#ifdef __linux__ -static char cp[] = "cp -iRp"; -static char mv[] = "mv -i"; -#else -static char cp[] = "cp -iRp"; -static char mv[] = "mv -i"; -#endif - -/* Tokens used for path creation */ -#define TOK_SSN 0 -#define TOK_MNT 1 -#define TOK_PLG 2 - -static const char * const toks[] = { - "sessions", - "mounts", - "plugins", /* must be the last entry */ -}; - -/* Patterns */ -#define P_CPMVFMT 0 -#define P_CPMVRNM 1 -#define P_ARCHIVE 2 -#define P_REPLACE 3 - -static const char * const patterns[] = { - "sed -i 's|^\\(\\(.*/\\)\\(.*\\)$\\)|#\\1\\n\\3|' %s", - "sed 's|^\\([^#/][^/]\\?.*\\)$|%s/\\1|;s|^#\\(/.*\\)$|\\1|' " - "%s | tr '\\n' '\\0' | xargs -0 -n2 sh -c '%s \"$0\" \"$@\" < /dev/tty'", - "\\.(bz|bz2|gz|tar|taz|tbz|tbz2|tgz|z|zip)$", - "sed -i 's|^%s\\(.*\\)$|%s\\1|' %s", -}; - -/* Colors */ -#define C_BLK (CTX_MAX + 1) /* Block device: DarkSeaGreen1 */ -#define C_CHR (C_BLK + 1) /* Character device: Yellow1 */ -#define C_DIR (C_CHR + 1) /* Directory: DeepSkyBlue1 */ -#define C_EXE (C_DIR + 1) /* Executable file: Green1 */ -#define C_FIL (C_EXE + 1) /* Regular file: Normal */ -#define C_HRD (C_FIL + 1) /* Hard link: Plum4 */ -#define C_LNK (C_HRD + 1) /* Symbolic link: Cyan1 */ -#define C_MIS (C_LNK + 1) /* Missing file OR file details: Grey62 */ -#define C_ORP (C_MIS + 1) /* Orphaned symlink: DeepPink1 */ -#define C_PIP (C_ORP + 1) /* Named pipe (FIFO): Orange1 */ -#define C_SOC (C_PIP + 1) /* Socket: MediumOrchid1 */ -#define C_UND (C_SOC + 1) /* Unknown OR 0B regular/exe file: Red1 */ - -static char gcolors[] = "c1e2272e006033f7c6d6abc4"; -static uint fcolors[C_UND + 1] = {0}; - -/* Event handling */ -#ifdef LINUX_INOTIFY -#define NUM_EVENT_SLOTS 32 /* Make room for 32 events */ -#define EVENT_SIZE (sizeof(struct inotify_event)) -#define EVENT_BUF_LEN (EVENT_SIZE * NUM_EVENT_SLOTS) -static int inotify_fd, inotify_wd = -1; -static uint INOTIFY_MASK = /* IN_ATTRIB | */ IN_CREATE | IN_DELETE | IN_DELETE_SELF - | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO; -#elif defined(BSD_KQUEUE) -#define NUM_EVENT_SLOTS 1 -#define NUM_EVENT_FDS 1 -static int kq, event_fd = -1; -static struct kevent events_to_monitor[NUM_EVENT_FDS]; -static uint KQUEUE_FFLAGS = NOTE_DELETE | NOTE_EXTEND | NOTE_LINK - | NOTE_RENAME | NOTE_REVOKE | NOTE_WRITE; -static struct timespec gtimeout; -#elif defined(HAIKU_NM) -static bool haiku_nm_active = FALSE; -static haiku_nm_h haiku_hnd; -#endif - -/* Function macros */ -#define tolastln() move(xlines - 1, 0) -#define tocursor() move(cur + 2, 0) -#define exitcurses() endwin() -#define printwarn(presel) printwait(strerror(errno), presel) -#define istopdir(path) ((path)[1] == '\0' && (path)[0] == '/') -#define copycurname() xstrsncpy(lastname, pdents[cur].name, NAME_MAX + 1) -#define settimeout() timeout(1000) -#define cleartimeout() timeout(-1) -#define errexit() printerr(__LINE__) -#define setdirwatch() (cfg.filtermode ? (presel = FILTER) : (watch = TRUE)) -#define filterset() (g_ctx[cfg.curctx].c_fltr[1]) -/* We don't care about the return value from strcmp() */ -#define xstrcmp(a, b) (*(a) != *(b) ? -1 : strcmp((a), (b))) -/* A faster version of xisdigit */ -#define xisdigit(c) ((unsigned int) (c) - '0' <= 9) -#define xerror() perror(xitoa(__LINE__)) - -#ifdef __GNUC__ -#define UNUSED(x) UNUSED_##x __attribute__((__unused__)) -#else -#define UNUSED(x) UNUSED_##x -#endif /* __GNUC__ */ - -/* HELPER FUNCTIONS */ - -static void sigint_handler(int UNUSED(sig)) -{ - g_state.interrupt = 1; -} - -static void clean_exit_sighandler(int UNUSED(sig)) -{ - exitcurses(); - /* This triggers cleanup() thanks to atexit() */ - exit(EXIT_SUCCESS); -} - -static char *xitoa(uint val) -{ - static char ascbuf[32] = {0}; - int i = 30; - uint rem; - - if (!val) - return "0"; - - while (val && i) { - rem = val / 10; - ascbuf[i] = '0' + (val - (rem * 10)); - val = rem; - --i; - } - - return &ascbuf[++i]; -} - -/* Return the integer value of a char representing HEX */ -static uchar xchartohex(uchar c) -{ - if (xisdigit(c)) - return c - '0'; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - return c; -} - -/* - * Source: https://elixir.bootlin.com/linux/latest/source/arch/alpha/include/asm/bitops.h - */ -static bool test_set_bit(uint nr) -{ - nr &= HASH_BITS; - - ull *m = ((ull *)ihashbmp) + (nr >> 6); - - if (*m & (1 << (nr & 63))) - return FALSE; - - *m |= 1 << (nr & 63); - - return TRUE; -} - -#if 0 -static bool test_clear_bit(uint nr) -{ - nr &= HASH_BITS; - - ull *m = ((ull *) ihashbmp) + (nr >> 6); - - if (!(*m & (1 << (nr & 63)))) - return FALSE; - - *m &= ~(1 << (nr & 63)); - return TRUE; -} -#endif - -/* Increase the limit on open file descriptors, if possible */ -static rlim_t max_openfds(void) -{ - struct rlimit rl; - rlim_t limit = getrlimit(RLIMIT_NOFILE, &rl); - - if (!limit) { - limit = rl.rlim_cur; - rl.rlim_cur = rl.rlim_max; - - /* Return ~75% of max possible */ - if (setrlimit(RLIMIT_NOFILE, &rl) == 0) { - limit = rl.rlim_max - (rl.rlim_max >> 2); - /* - * 20K is arbitrary. If the limit is set to max possible - * value, the memory usage increases to more than double. - */ - if (limit > 20480) - limit = 20480; - } - } else - limit = 32; - - return limit; -} - -/* - * Wrapper to realloc() - * Frees current memory if realloc() fails and returns NULL. - * - * As per the docs, the *alloc() family is supposed to be memory aligned: - * Ubuntu: http://manpages.ubuntu.com/manpages/xenial/man3/malloc.3.html - * macOS: https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/malloc.3.html - */ -static void *xrealloc(void *pcur, size_t len) -{ - void *pmem = realloc(pcur, len); - - if (!pmem) - free(pcur); - - return pmem; -} - -/* - * Just a safe strncpy(3) - * Always null ('\0') terminates if both src and dest are valid pointers. - * Returns the number of bytes copied including terminating null byte. - */ -static size_t xstrsncpy(char *restrict dst, const char *restrict src, size_t n) -{ - char *end = memccpy(dst, src, '\0', n); - - if (!end) { - dst[n - 1] = '\0'; // NOLINT - end = dst + n; /* If we return n here, binary size increases due to auto-inlining */ - } - - return end - dst; -} - -static inline size_t xstrlen(const char *restrict s) -{ -#if !defined(__GLIBC__) - return strlen(s); // NOLINT -#else - return (char *)rawmemchr(s, '\0') - s; // NOLINT -#endif -} - -static char *xstrdup(const char *restrict s) -{ - size_t len = xstrlen(s) + 1; - char *ptr = malloc(len); - - if (ptr) - xstrsncpy(ptr, s, len); - return ptr; -} - -static bool is_suffix(const char *restrict str, const char *restrict suffix) -{ - if (!str || !suffix) - return FALSE; - - size_t lenstr = xstrlen(str); - size_t lensuffix = xstrlen(suffix); - - if (lensuffix > lenstr) - return FALSE; - - return (xstrcmp(str + (lenstr - lensuffix), suffix) == 0); -} - -static bool is_prefix(const char *restrict str, const char *restrict prefix, size_t len) -{ - return !strncmp(str, prefix, len); -} - -/* - * The poor man's implementation of memrchr(3). - * We are only looking for '/' in this program. - * And we are NOT expecting a '/' at the end. - * Ideally 0 < n <= xstrlen(s). - */ -static void *xmemrchr(uchar *restrict s, uchar ch, size_t n) -{ -#if defined(__GLIBC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - return memrchr(s, ch, n); -#else - - if (!s || !n) - return NULL; - - uchar *ptr = s + n; - - do - if (*--ptr == ch) - return ptr; - while (s != ptr); - - return NULL; -#endif -} - -/* A very simplified implementation, changes path */ -static char *xdirname(char *path) -{ - char *base = xmemrchr((uchar *)path, '/', xstrlen(path)); - - if (base == path) - path[1] = '\0'; - else - *base = '\0'; - - return path; -} - -static char *xbasename(char *path) -{ - char *base = xmemrchr((uchar *)path, '/', xstrlen(path)); // NOLINT - - return base ? base + 1 : path; -} - -static char *xextension(const char *fname, size_t len) -{ - return xmemrchr((uchar *)fname, '.', len); -} - -/* - * Updates out with "dir/name or "/name" - * Returns the number of bytes copied including the terminating NULL byte - */ -static size_t mkpath(const char *dir, const char *name, char *out) -{ - size_t len; - - /* Handle absolute path */ - if (name[0] == '/') // NOLINT - return xstrsncpy(out, name, PATH_MAX); - - /* Handle root case */ - if (istopdir(dir)) - len = 1; - else - len = xstrsncpy(out, dir, PATH_MAX); - - out[len - 1] = '/'; // NOLINT - return (xstrsncpy(out + len, name, PATH_MAX - len) + len); -} - -/* Assumes both the paths passed are directories */ -static char *common_prefix(const char *path, char *prefix) -{ - const char *x = path, *y = prefix; - char *sep; - - if (!path || !*path || !prefix) - return NULL; - - if (!*prefix) { - xstrsncpy(prefix, path, PATH_MAX); - return prefix; - } - - while (*x && *y && (*x == *y)) - ++x, ++y; - - /* Strings are same */ - if (!*x && !*y) - return prefix; - - /* Path is shorter */ - if (!*x && *y == '/') { - xstrsncpy(prefix, path, y - path); - return prefix; - } - - /* Prefix is shorter */ - if (!*y && *x == '/') - return prefix; - - /* Shorten prefix */ - prefix[y - prefix] = '\0'; - - sep = xmemrchr((uchar *)prefix, '/', y - prefix); - if (sep != prefix) - *sep = '\0'; - else /* Just '/' */ - prefix[1] = '\0'; - - return prefix; -} - -/* - * The library function realpath() resolves symlinks. - * If there's a symlink in file list we want to show the symlink not what it's points to. - */ -static char *abspath(const char *path, const char *cwd) -{ - if (!path || !cwd) - return NULL; - - size_t dst_size = 0, src_size = xstrlen(path), cwd_size = xstrlen(cwd); - size_t len = src_size; - const char *src; - char *dst; - /* - * We need to add 2 chars at the end as relative paths may start with: - * ./ (find .) - * no separator (fd .): this needs an additional char for '/' - */ - char *resolved_path = malloc(src_size + (*path == '/' ? 0 : cwd_size) + 2); - if (!resolved_path) - return NULL; - - /* Turn relative paths into absolute */ - if (path[0] != '/') - dst_size = xstrsncpy(resolved_path, cwd, cwd_size + 1) - 1; - else - resolved_path[0] = '\0'; - - src = path; - dst = resolved_path + dst_size; - for (const char *next = NULL; next != path + src_size;) { - next = memchr(src, '/', len); - if (!next) - next = path + src_size; - - if (next - src == 2 && src[0] == '.' && src[1] == '.') { - if (dst - resolved_path) { - dst = xmemrchr((uchar *)resolved_path, '/', dst - resolved_path); - *dst = '\0'; - } - } else if (next - src == 1 && src[0] == '.') { - /* NOP */ - } else if (next - src) { - *(dst++) = '/'; - xstrsncpy(dst, src, next - src + 1); - dst += next - src; - } - - src = next + 1; - len = src_size - (src - path); - } - - if (*resolved_path == '\0') { - resolved_path[0] = '/'; - resolved_path[1] = '\0'; - } - - return resolved_path; -} - -static int create_tmp_file(void) -{ - xstrsncpy(g_tmpfpath + tmpfplen - 1, messages[STR_TMPFILE], TMP_LEN_MAX - tmpfplen); - - int fd = mkstemp(g_tmpfpath); - - if (fd == -1) { - DPRINTF_S(strerror(errno)); - } - - return fd; -} - -/* PRINT I/O FUNCTIONS */ - -static void clearinfoln(void) -{ - move(xlines - 2, 0); - clrtoeol(); -} - -#ifdef KEY_RESIZE -/* Clear the old prompt */ -static void clearoldprompt(void) -{ - clearinfoln(); - tolastln(); - addch('\n'); -} -#endif - -/* Messages show up at the bottom */ -static inline void printmsg_nc(const char *msg) -{ - tolastln(); - addstr(msg); - addch('\n'); -} - -static void printmsg(const char *msg) -{ - attron(COLOR_PAIR(cfg.curctx + 1)); - printmsg_nc(msg); - attroff(COLOR_PAIR(cfg.curctx + 1)); -} - -static void printwait(const char *msg, int *presel) -{ - printmsg(msg); - if (presel) { - *presel = MSGWAIT; - if (ndents) - xstrsncpy(g_ctx[cfg.curctx].c_name, pdents[cur].name, NAME_MAX + 1); - } -} - -/* Kill curses and display error before exiting */ -static void printerr(int linenum) -{ - exitcurses(); - perror(xitoa(linenum)); - if (!g_state.picker && selpath) - unlink(selpath); - free(pselbuf); - exit(1); -} - -static inline bool xconfirm(int c) -{ - return (c == 'y' || c == 'Y'); -} - -static int get_input(const char *prompt) -{ - if (prompt) - printmsg(prompt); - cleartimeout(); - - int r = getch(); - -#ifdef KEY_RESIZE - while (r == KEY_RESIZE) { - if (prompt) { - clearoldprompt(); - xlines = LINES; - printmsg(prompt); - } - - r = getch(); - } -#endif - settimeout(); - return r; -} - -static int get_cur_or_sel(void) -{ - if (selbufpos && ndents) { - if (cfg.prefersel) - return 's'; - - int choice = get_input(messages[MSG_CUR_SEL_OPTS]); - - return ((choice == 'c' || choice == 's') ? choice : 0); - } - - if (selbufpos) - return 's'; - - if (ndents) - return 'c'; - - return 0; -} - -static void xdelay(useconds_t delay) -{ - refresh(); - usleep(delay); -} - -static char confirm_force(bool selection) -{ - char str[64]; - - snprintf(str, 64, messages[MSG_FORCE_RM], - (selection ? xitoa(nselected) : "current"), (selection ? "(s)" : "")); - - int r = get_input(str); - - if (r == 27) - return '\0'; /* cancel */ - if (r == 'y' || r == 'Y') - return 'f'; /* forceful */ - return 'i'; /* interactive */ -} - -/* FORK FUNCTIONS */ - -/* No NULL check here as spawn() guards against it */ -static int parseargs(char *line, char **argv) -{ - int count = 0; - - argv[count++] = line; - - while (*line) { // NOLINT - if (ISBLANK(*line)) { - *line++ = '\0'; - - if (!*line) // NOLINT - return count; - - argv[count++] = line; - if (count == EXEC_ARGS_MAX) - return -1; - } - - ++line; - } - - return count; -} - -static pid_t xfork(uchar flag) -{ - int status; - pid_t p = fork(); - struct sigaction dfl_act = {.sa_handler = SIG_DFL}; - - if (p > 0) { - /* the parent ignores the interrupt, quit and hangup signals */ - sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_IGN}, &oldsighup); - sigaction(SIGTSTP, &dfl_act, &oldsigtstp); - } else if (p == 0) { - /* We create a grandchild to detach */ - if (flag & F_NOWAIT) { - p = fork(); - - if (p > 0) - _exit(EXIT_SUCCESS); - else if (p == 0) { - sigaction(SIGHUP, &dfl_act, NULL); - sigaction(SIGINT, &dfl_act, NULL); - sigaction(SIGQUIT, &dfl_act, NULL); - sigaction(SIGTSTP, &dfl_act, NULL); - - setsid(); - return p; - } - - perror("fork"); - _exit(EXIT_FAILURE); - } - - /* so they can be used to stop the child */ - sigaction(SIGHUP, &dfl_act, NULL); - 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*/ - if (flag & F_NOWAIT) - waitpid(p, &status, 0); - - if (p == -1) - perror("fork"); - return p; -} - -static int join(pid_t p, uchar flag) -{ - int status = 0xFFFF; - - if (!(flag & F_NOWAIT)) { - /* wait for the child to exit */ - do { - } while (waitpid(p, &status, 0) == -1); - - if (WIFEXITED(status)) { - status = WEXITSTATUS(status); - DPRINTF_D(status); - } - } - - /* restore parent's signal handling */ - sigaction(SIGHUP, &oldsighup, NULL); - sigaction(SIGTSTP, &oldsigtstp, NULL); - - return status; -} - -/* - * Spawns a child process. Behaviour can be controlled using flag. - * Limited to 2 arguments to a program, flag works on bit set. - */ -static int spawn(char *file, char *arg1, char *arg2, uchar flag) -{ - pid_t pid; - int status = 0, retstatus = 0xFFFF; - char *argv[EXEC_ARGS_MAX] = {0}; - char *cmd = NULL; - - if (!file || !*file) - return retstatus; - - /* Swap args if the first arg is NULL and second isn't */ - if (!arg1 && arg2) { - arg1 = arg2; - arg2 = NULL; - } - - if (flag & F_MULTI) { - size_t len = xstrlen(file) + 1; - - cmd = (char *)malloc(len); - if (!cmd) { - DPRINTF_S("malloc()!"); - return retstatus; - } - - xstrsncpy(cmd, file, len); - status = parseargs(cmd, argv); - if (status == -1 || status > (EXEC_ARGS_MAX - 3)) { /* arg1, arg2 and last NULL */ - free(cmd); - DPRINTF_S("NULL or too many args"); - return retstatus; - } - } else - argv[status++] = file; - - argv[status] = arg1; - argv[++status] = arg2; - - if (flag & F_NORMAL) - exitcurses(); - - pid = xfork(flag); - if (pid == 0) { - /* Suppress stdout and stderr */ - if (flag & F_NOTRACE) { - int fd = open("/dev/null", O_WRONLY, 0200); - - dup2(fd, 1); - dup2(fd, 2); - close(fd); - } - - execvp(*argv, argv); - _exit(EXIT_SUCCESS); - } else { - retstatus = join(pid, flag); - - DPRINTF_D(pid); - - if ((flag & F_CONFIRM) || ((flag & F_CHKRTN) && retstatus)) { - printf("%s", messages[MSG_CONTINUE]); -#ifndef NORL - fflush(stdout); -#endif - while (getchar() != '\n'); - } - - if (flag & F_NORMAL) - refresh(); - - free(cmd); - } - - return retstatus; -} - -/* SELECTION HANDLER FUNCTIONS */ - -/* Writes buflen char(s) from buf to a file */ -static void writesel(const char *buf, const size_t buflen) -{ - if (g_state.pickraw || !selpath) - return; - - FILE *fp = fopen(selpath, "w"); - - if (fp) { - if (fwrite(buf, 1, buflen, fp) != buflen) - printwarn(NULL); - fclose(fp); - } else - printwarn(NULL); -} - -static void appendfpath(const char *path, const size_t len) -{ - if ((selbufpos >= selbuflen) || ((len + 3) > (selbuflen - selbufpos))) { - selbuflen += PATH_MAX; - pselbuf = xrealloc(pselbuf, selbuflen); - if (!pselbuf) - errexit(); - } - - selbufpos += xstrsncpy(pselbuf + selbufpos, path, len); -} - -/* Write selected file paths to fd, linefeed separated */ -static size_t seltofile(int fd, uint *pcount) -{ - uint lastpos, count = 0; - char *pbuf = pselbuf; - size_t pos = 0; - ssize_t len, prefixlen = 0, initlen = 0; - - if (pcount) - *pcount = 0; - - if (!selbufpos) - return 0; - - lastpos = selbufpos - 1; - - if (listpath) { - prefixlen = (ssize_t)xstrlen(listroot); - initlen = (ssize_t)xstrlen(listpath); - } - - while (pos <= lastpos) { - DPRINTF_S(pbuf); - len = (ssize_t)xstrlen(pbuf); - - if (!listpath || !is_prefix(pbuf, listpath, initlen)) { - if (write(fd, pbuf, len) != len) - return pos; - } else { - if (write(fd, listroot, prefixlen) != prefixlen) - return pos; - if (write(fd, pbuf + initlen, len - initlen) != (len - initlen)) - return pos; - } - - pos += len; - if (pos <= lastpos) { - if (write(fd, "\n", 1) != 1) - return pos; - pbuf += len + 1; - } - ++pos; - ++count; - } - - if (pcount) - *pcount = count; - - return pos; -} - -/* List selection from selection file (another instance) */ -static bool listselfile(void) -{ - struct stat sb; - - if (stat(selpath, &sb) == -1) - return FALSE; - - /* Nothing selected if file size is 0 */ - if (!sb.st_size) - return FALSE; - - snprintf(g_buf, CMD_LEN_MAX, "tr \'\\0\' \'\\n\' < %s", selpath); - spawn(utils[UTIL_SH_EXEC], g_buf, NULL, F_CLI | F_CONFIRM); - - return TRUE; -} - -/* Reset selection indicators */ -static void resetselind(void) -{ - for (int r = 0; r < ndents; ++r) - if (pdents[r].flags & FILE_SELECTED) - pdents[r].flags &= ~FILE_SELECTED; -} - -static void startselection(void) -{ - if (!g_state.selmode) { - g_state.selmode = 1; - nselected = 0; - - if (selbufpos) { - resetselind(); - writesel(NULL, 0); - selbufpos = 0; - } - - lastappendpos = 0; - } -} - -static void updateselbuf(const char *path, char *newpath) -{ - size_t r; - - for (int i = 0; i < ndents; ++i) - if (pdents[i].flags & FILE_SELECTED) { - r = mkpath(path, pdents[i].name, newpath); - appendfpath(newpath, r); - } -} - -/* Finish selection procedure before an operation */ -static void endselection(void) -{ - int fd; - ssize_t count; - char buf[sizeof(patterns[P_REPLACE]) + PATH_MAX + (TMP_LEN_MAX << 1)]; - - if (g_state.selmode) - g_state.selmode = 0; - - if (!listpath || !selbufpos) - return; - - fd = create_tmp_file(); - if (fd == -1) { - DPRINTF_S("couldn't create tmp file"); - return; - } - - seltofile(fd, NULL); - if (close(fd)) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - return; - } - - snprintf(buf, sizeof(buf), patterns[P_REPLACE], listpath, listroot, g_tmpfpath); - spawn(utils[UTIL_SH_EXEC], buf, NULL, F_CLI); - - fd = open(g_tmpfpath, O_RDONLY); - if (fd == -1) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - if (unlink(g_tmpfpath)) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - } - return; - } - - count = read(fd, pselbuf, selbuflen); - if (count < 0) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - if (close(fd) || unlink(g_tmpfpath)) { - DPRINTF_S(strerror(errno)); - } - return; - } - - if (close(fd) || unlink(g_tmpfpath)) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - return; - } - - selbufpos = count; - pselbuf[--count] = '\0'; - for (--count; count > 0; --count) - if (pselbuf[count] == '\n' && pselbuf[count+1] == '/') - pselbuf[count] = '\0'; - - writesel(pselbuf, selbufpos - 1); -} - -static void clearselection(void) -{ - nselected = 0; - selbufpos = 0; - g_state.selmode = 0; - writesel(NULL, 0); -} - -/* Returns: 1 - success, 0 - none selected, -1 - other failure */ -static int editselection(void) -{ - int ret = -1; - int fd, lines = 0; - ssize_t count; - struct stat sb; - time_t mtime; - - if (!selbufpos) - return listselfile(); - - fd = create_tmp_file(); - if (fd == -1) { - DPRINTF_S("couldn't create tmp file"); - return -1; - } - - seltofile(fd, NULL); - if (close(fd)) { - DPRINTF_S(strerror(errno)); - return -1; - } - - /* Save the last modification time */ - if (stat(g_tmpfpath, &sb)) { - DPRINTF_S(strerror(errno)); - unlink(g_tmpfpath); - return -1; - } - mtime = sb.st_mtime; - - spawn((cfg.waitedit ? enveditor : editor), g_tmpfpath, NULL, F_CLI); - - fd = open(g_tmpfpath, O_RDONLY); - if (fd == -1) { - DPRINTF_S(strerror(errno)); - unlink(g_tmpfpath); - return -1; - } - - fstat(fd, &sb); - - if (mtime == sb.st_mtime) { - DPRINTF_S("selection is not modified"); - unlink(g_tmpfpath); - return 1; - } - - if (sb.st_size > selbufpos) { - DPRINTF_S("edited buffer larger than previous"); - unlink(g_tmpfpath); - goto emptyedit; - } - - count = read(fd, pselbuf, selbuflen); - if (count < 0) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - if (close(fd) || unlink(g_tmpfpath)) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - } - goto emptyedit; - } - - if (close(fd) || unlink(g_tmpfpath)) { - DPRINTF_S(strerror(errno)); - printwarn(NULL); - goto emptyedit; - } - - if (!count) { - ret = 1; - goto emptyedit; - } - - resetselind(); - selbufpos = count; - /* The last character should be '\n' */ - pselbuf[--count] = '\0'; - for (--count; count > 0; --count) { - /* Replace every '\n' that separates two paths */ - if (pselbuf[count] == '\n' && pselbuf[count + 1] == '/') { - ++lines; - pselbuf[count] = '\0'; - } - } - - /* Add a line for the last file */ - ++lines; - - if (lines > nselected) { - DPRINTF_S("files added to selection"); - goto emptyedit; - } - - nselected = lines; - writesel(pselbuf, selbufpos - 1); - - return 1; - -emptyedit: - resetselind(); - clearselection(); - return ret; -} - -static bool selsafe(void) -{ - /* Fail if selection file path not generated */ - if (!selpath) { - printmsg(messages[MSG_SEL_MISSING]); - return FALSE; - } - - /* Fail if selection file path isn't accessible */ - if (access(selpath, R_OK | W_OK) == -1) { - errno == ENOENT ? printmsg(messages[MSG_0_SELECTED]) : printwarn(NULL); - return FALSE; - } - - return TRUE; -} |