pax_global_header00006660000000000000000000000064146631001430014510gustar00rootroot0000000000000052 comment=bdb1a643038bae0c3467a701a33b8ba5fbf07841 nnn-5.0/000077500000000000000000000000001466310014300121455ustar00rootroot00000000000000nnn-5.0/.circleci/000077500000000000000000000000001466310014300140005ustar00rootroot00000000000000nnn-5.0/.circleci/config.yml000066400000000000000000000074321466310014300157760ustar00rootroot00000000000000version: 2 jobs: compile: docker: - image: ubuntu:22.04 working_directory: ~/nnn environment: CI_FORCE_TEST: 1 parallelism: 4 steps: - run: command: | apt update -qq DEBIAN_FRONTEND="noninteractive" TZ="America/New_York" apt-get -y install tzdata apt install -y --no-install-recommends software-properties-common wget gpg-agent shellcheck apt install -y --no-install-recommends git make pkg-config libncurses-dev libreadline-dev apt install -y --no-install-recommends gcc-9 gcc-10 gcc-11 gcc-12 apt install -y --no-install-recommends clang-11 clang-12 clang-13 clang-14 clang-15 clang-tidy-15 - checkout - run: command: | export CFLAGS=-Werror make clean echo echo "########## gcc-9 ##########" CC=gcc-9 make strip ls -l nnn make clean echo echo "########## gcc-10 ##########" CC=gcc-10 make strip ls -l nnn make clean echo echo "########## gcc-11 ##########" CC=gcc-11 make strip ls -l nnn make clean echo echo "########## gcc-12 ##########" CC=gcc-12 make strip ls -l nnn make clean echo echo "########## clang-11 ##########" CC=clang-11 make strip ls -l nnn make clean echo echo "########## clang-12 ##########" CC=clang-12 make strip ls -l nnn make clean echo echo "########## clang-13 ##########" CC=clang-13 make strip ls -l nnn make clean echo echo "########## clang-14 ##########" CC=clang-14 make strip ls -l nnn make clean echo echo "########## clang-tidy-15 ##########" clang-tidy-15 src/* -- -I/usr/include -I/usr/include/ncursesw echo "########## shellcheck ##########" find plugins/ -type f ! \( -name "*.md" -o -name "*-mac" \) -exec shellcheck {} + package-and-publish: machine: true working_directory: ~/nnn steps: - checkout - run: name: "auto-generate packages" command: | # Create dist directory if it doesn't exist mkdir ./dist # Clean up rm -rf ./dist/* # Pack source git archive -o ../${CIRCLE_PROJECT_REPONAME}-${CIRCLE_TAG}.tar.gz --format tar.gz --prefix=${CIRCLE_PROJECT_REPONAME}-${CIRCLE_TAG#v}/ ${CIRCLE_TAG} # Use latest installed python3 from pyenv export PYENV_VERSION="$(pyenv versions | grep -Po '\b3\.\d+\.\d+' | tail -1)" #pip install packagecore #packagecore -c misc/packagecore/packagecore.yaml -o ./dist/ ${CIRCLE_TAG#v} # Move source pack to dist mv ../${CIRCLE_PROJECT_REPONAME}-${CIRCLE_TAG}.tar.gz ./dist/ - run: name: "publish to GitHub" command: | go install github.com/tcnksm/ghr@latest ghr -t ${GITHUB_API_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -replace ${CIRCLE_TAG} ./dist/ workflows: version: 2 CircleCI: jobs: &all-tests - compile nightly: triggers: - schedule: cron: "0 0 * * 6" filters: branches: only: - master jobs: *all-tests publish-github-release: jobs: - package-and-publish: filters: tags: only: /^v.*/ branches: ignore: /.*/ nnn-5.0/.github/000077500000000000000000000000001466310014300135055ustar00rootroot00000000000000nnn-5.0/.github/FUNDING.yml000066400000000000000000000000751466310014300153240ustar00rootroot00000000000000# These are supported funding model platforms github: jarun nnn-5.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001466310014300156705ustar00rootroot00000000000000nnn-5.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000032371466310014300203670ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- `nnn` comes with excellent documentation (including a Troubleshooting section). If you have a _How to do x_ question, it may have already been answered. Wiki: https://github.com/jarun/nnn/wiki If it looks like a local environment issue, please try to debug yourself. Debugging local setup issues is not our top priority. If you are looking for a new feature or program option, ensure you have the correct version with the feature installed. Refer to the release notes to confirm. Before opening an issue, please try to reproduce on latest master. The bug you noticed might have already been fixed. Useful links: - compile `nnn` from source - https://github.com/jarun/nnn/wiki/Usage#from-source - debugging `nnn` - https://github.com/jarun/nnn/wiki/Developer-guides#debugging-nnn If the issue can be reproduced on master, log it. Please provide the environment details. **If that's missing, the issue will be closed without any cited reason.** If we need more information and there is no communication from the bug reporter within 7 days from the date of request, we will close the issue. If you have relevant information, resume discussion any time. --- PLEASE DELETE THIS LINE AND EVERYTHING ABOVE --- #### Environment details (Put `x` in the checkbox along with the information) - [ ] Operating System: - [ ] Desktop Environment: - [ ] Terminal Emulator: - [ ] Shell: - [ ] Custom desktop opener (if applicable): - [ ] Program options used: - [ ] Configuration options set: - [ ] Plugins are installed - [ ] Issue exists on `nnn` master #### Exact steps to reproduce the issue nnn-5.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000003431466310014300176600ustar00rootroot00000000000000contact_links: - name: Idea, Enhancement, Question, Support url: https://github.com/jarun/nnn/discussions about: If you have a question, need support or want to discuss new ideas then please open a discussion thread. nnn-5.0/.github/workflows/000077500000000000000000000000001466310014300155425ustar00rootroot00000000000000nnn-5.0/.github/workflows/ci.yml000066400000000000000000000022271466310014300166630ustar00rootroot00000000000000name: GitHubCI on: push: branches: [master] pull_request: branches: [master] jobs: macOS-gcc: runs-on: macOS-latest steps: - uses: actions/checkout@v2 - name: Compile with gcc env: CC: gcc run: | export CFLAGS="$CFLAGS -Werror" make clean make make clean macOS-clang: runs-on: macOS-latest steps: - uses: actions/checkout@v2 - name: Compile with clang env: CC: clang run: | # see: https://github.com/actions/setup-python/issues/577 brew update || true brew install llvm || true brew unlink python@3.11 && brew link python@3.11 export PATH="/usr/local/opt/llvm/bin:$PATH" export PATH="/opt/homebrew/opt/llvm/bin:$PATH" export CFLAGS="$CFLAGS -Werror" make clean make make clean clang-tidy src/* -- -I/usr/include ubuntu-patches: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Compile patches with gcc env: CC: gcc run: | make checkpatches nnn-5.0/.github/workflows/codeql.yml000066400000000000000000000016261466310014300175410ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "master" ] pull_request: branches: [ "master" ] schedule: - cron: "11 23 * * 3" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ cpp, python ] steps: - name: Checkout uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild uses: github/codeql-action/autobuild@v2 if: ${{ matrix.language == 'cpp' || matrix.language == 'python' }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{ matrix.language }}" nnn-5.0/.github/workflows/lock.yml000066400000000000000000000007421466310014300172200ustar00rootroot00000000000000name: 'Lock threads' on: schedule: - cron: '0 0 * * *' workflow_dispatch: permissions: issues: write pull-requests: write discussions: write concurrency: group: lock-threads jobs: lock: runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} issue-lock-inactive-days: '30' issue-lock-reason: '' pr-lock-inactive-days: '30' pr-lock-reason: '' nnn-5.0/.gitignore000066400000000000000000000000711466310014300141330ustar00rootroot00000000000000*.o *.dSYM nnn src/icons-generated*.h src/icons-hash-gen nnn-5.0/CHANGELOG000066400000000000000000001452701466310014300133700ustar00rootroot00000000000000nnn v5.0 Daiquiri 2024-08-26 - show relative line numbering when jumping (#1808) - option `-N` to use native prompt when compiled with libreadline - rm improvements - log removed filename - cancel on 'n' or 'N' - show name of the hovered file to be removed - show number of selected files to be removed - new keybind X to force `rm -rf` always (#1811) - fix sort order getting changed on context switch (#1757) - fix current selection on new file creation, if symlinks exist (#1767) - fix filter toggle with mouse click on last 2 rows (#1765) - fix file creation (#1864) - when handling bookmark, use readlink, not realpath - set `$PWD` on directory switch - add option `-0` to null-separate file paths in picker mode - quitcd.nu (for nushell) now supports modular import (#1806) - add _command as plugin_ example to cd to user input directory - `cbcopy-mac`, `cbpaste-mac`: plugins for integration with macOS clipboard - `fzhist` plugin: add support for zsh history - `preview-tui` plugin: support eza as replacement for exa, multiple fixes - `preview-tui` plugin: add full svg support (#1865) - `preview-tabbed`: show (n)sxiv in thumbnail mode inside "Pictures" directory - mpv sixel/kitty support for preview (#1590) ------------------------------------------------------------------------------- nnn v4.9 Elixir 2023-08-27 - config option `NNN_ARCHMNT` to specify archive mounter utility - key ^y to jump to next young file - filter adjustment when opening context from plugin - properly update mode after `chmod` - pre-fill selected file name to create link if sel is preferred over hovered - fix crash when `PWD` is empty - make `quitcd.bash_zsh` POSIX-compliant - `nmount` - support `udiskctl` as default - `preview-tui` - support wezterm split size percentage - `preview-tui` - move to bash for environment manipulation through arrays - `fzopen` - handle empty selection - `finder` - use default path to find - add icons for `djvu` files - support Nerd Fonts v3.0.0 and above (older versions are broken by v3.0.0) ------------------------------------------------------------------------------- nnn v4.8 Spritz 2023-04-13 - show total size (key S) of non-filtered selection in a directory - fix tilde (~) handling in file name - plugin `.nmv` now respects `-u` flag - env var `$NNN_PREFER_SELECTION` exported to all plugins - support for wezterm in `preview-tui` - create new file or directory (tree) on startup - run command as plugin now supports exported variables - use `"$nnn"` anywhere when running command as plugin - set defaults for some prompts on Enter - improve archive, rename and create new workflows - optimize link creation - allow overwriting regular files on new empty file creation - add patch for colemak keyboard (existing renamed to colemak-dh) - add correct check for Wayland in clipboard plugins - add quitcd script for nushell - plugin `kdeconnect` - send multiple files - plugin `preview-tui`: add `chafa` as preferred image viewer, multiple fixes - plugin `nmount` - misc. improvements - add icon for jxl files ------------------------------------------------------------------------------- nnn v4.7 Cuba libre 2022-11-24 - fix ^N not working sometimes (#1449) - fix file remove confirmation prompt - bring back `atool` as the default archive handler - add option `-B` to use `bsdtar` as the archive utility - find and list mode improvements ntinue even if max paths/data size limit is exceeded eed improvements pport listing maximum 16K paths of 64 MiB of data - key J to jump to an entry or relative offset from current entry - prefill the hard link creation prompt when there's a single target (#1507) - documented workaround for docker container crash (#1407, #1476) - plugin `imgview`: handle arguments as strings (#1509) - plugin `wallpaper`: support Wayland (#1512) - plugin `upload`: handle selection using `ffsend` (#1523) - add Rust icons (#1502) ------------------------------------------------------------------------------- nnn v4.6 Absinthe 2022-07-26 - icon handling overhaul (#1432, #1436) - better performance, memory usage and reduced binary size - emoji support for supporting distros and terminals (#1346) - open the target directory of symlinked bookmarks (#1353) - enable show hidden when a hidden file is passed as argument - add Colemak-DH layout keybinds to patch framework (#1421) - set `bsdtar` as the default archive utility - support 4 byte unicode keybinds (#1428) - enable directory auto-enter during filter operation (`-A` to disable) - enable filter prompt inside the bookmark/plugin dirs - show volume usage information in help - add new icon colors for mp4 and flac files - use `stat -x` for file details on *BSD and macOS (#1389) - interpret suffix `$nnn` when paging (#1355) - disable key e (edit file) in explorer mode (#1394) - fix double order chars on filter case match change - `.cbcp`: more verbose message on paste without a selection - plugin `preview-tui`: scale-up kitty previews - plugin `preview-tui`: account for ueberzug offset - plugin `preview-tui`: support `SPLIT_SIZE` for preview pane (#1431) - plugin `autojump`: support z.lua - new Makefile target `shellcheck` to verify plugins ------------------------------------------------------------------------------- nnn v4.5 Cachaça 2022-04-26 - disable filter info if file details (option `-i`) enabled - open previous active context on context quit - switch ^J and + functionality: - +: toggle file selection - ^J: toggle auto-jump on file open - allow symlink creation with name `@` to a single file (#1345) - clear selection on successful operation at native prompt with "%j" (#1330) - reverse timestamps of entries modified/created within 5 minutes - avoid using non-portable `xargs` flags on macOS (#1299) - quitcd script for Elvish shell > 0.17.0 (#1344) - plugin `openall` to open selected files together (#1333) - plugin `gitroot` to jump to git root directory from a subtree - plugin `gsconnect` to send the selected files to Android using gsconnect - icon for opus and webp files - `preview-tui` - fix gif conversion and whitespace name - `preview-tui` - add support for windows terminal split - `preview-tui` - djvu file previews - `nuke` - add support for `imv` when named _imv_ - `gsconnect` - support connection to multiple devices - export `NNN_INCLUDE_HIDDEN` to plugins (#1308) - respect `NNN_TRASH` in `.nmv` (#1306) - add GNU sed as a dependency with support for env var `SED` - use bold `>` to point at current entry in detail mode - add 2 spaces after icons for better visibility - documentation refresh - make option `O_NOSORT` to load directories unsorted on entry ------------------------------------------------------------------------------- nnn v4.4 Tequila 2021-11-23 - support macOS iterm2 in plugin preview-tui (#1196) - use selection at native command prompt with `%j` and `%J` - docs - https://github.com/jarun/nnn/wiki/concepts#special-variables - scroll strings longer than columns in rename/new prompts (#1213, #279) - batch rename symlink targets in listing mode (#1214) - option for recursive rename in plugin .nmv (#1186) - more frequent checks for cancellation during du (#1236) - picker mode: enable auto-proceed - picker mode: don't pick hovered file on Enter if selection exists - picker mode: fix issue in plugin `fzopen` when used to pick files - send file to explorer FIFO on double left click instead of opening it - new neovim plugin [nnn.nvim](https://github.com/luukvbaal/nnn.nvim) - nvim-only, featuring explorer mode (`-F` flag) - explorer mode for [nnn.vim](https://github.com/mcchrish/nnn.vim#explorer) - remove option `-w`: always place HW cursor on current entry - accept link name when linking a single target (#1201) - option `-i` to show current file information in info bar - force GNU sed on *BSD and Solaris - add `nsxiv` support to nuke, preview-tabbed and imgview (#1230) - fix preview-tui without `-a` (#1208) - pass `pts` in env var for preview-tui to use in `tput` (#1235) - disable editing file in picker mode (#1183) - save session in picker mode (#1190) - use nerd icons for gitstatus patch (#1220) ------------------------------------------------------------------------------- nnn v4.3 Martini 2021-09-29 - cool ASCII art logo in the help screen - add `bookmarks` directory for flexible symlinked bookmarks - new key B to add a symlinked bookmark for current dir - special variables `$dN`, `$fN` available for plugins/prompt/shell to access dir/hovered file in each context - config `NNN_ORDER` to set directory-specific ordering - show/hide hidden files as per context state in plugin based batch rename - retain search filter history for plugin `finder` - sync multiple instances of `nnn` after operation on selection - signal CWD change to terminal via OSC-7 (#1147) - save complete per-context filter when saving sessions - disable symlink resolution for paths in `NNN_BMS` and arg `PATH` - do not end selection mode on running plugins/prompt/shell - plugin `bookmarks` replaced by symlinked bookmarks support - list open locations in active contexts in help page - make option `O_MATCHFLTR` to discard filter key if no match - configurable `NNN_TMPFILE` to _cd on quit_ - disable auto marking directories (use -) - picker mode improvements - open tty for input if `STDIN` is non-tty - truncate output file before writing - do not double select a file on Enter - legacy macOS (< 10.12.0) support - no redraw during du calculation, show processed dir name - plugin `xdgdefault`: add dmenu support - user patch `restorepreview`: close/restore `preview-tui` for internal edits ------------------------------------------------------------------------------- nnn v4.2 Mojito 2021-07-21 - `NNN_PLUG` indicator symbol interpretation has **changed**: - `!` - _run-cmd-as-plugin_ (earlier `_`) - `&` - _run-gui-cmd-as-plugin_ (earlier `|`) - `|` (new) - page noninteractive _run-cmd-as-plugin_ output - persistent selection markers (#1086) - option _extract to..._ for archives - mount remote and mount/extract archive to a smart context - confirm file trashing to avoid accidental press of x (#1101) - insert the last command executed at prompt on Up or Down - insert the current file name at empty prompt on TAB - handle redraw issue on missed `KEY_RESIZE` (#1067) - add force-tty capability to spawn and set pagers to it (#1064) - clear selection mode on deselecting last selected file (#1098) - remove selected hovered entry from selection on deletion - disable filtering in empty directories - ignore last pressed filter character when no matches - fix broken screen on resize while paging (#1072) - fix archive not hovered on creation - remove libgit2 dependency in `gitstatus` patch (#1095) - add `-G` flag for `gitstatus` patch - option `-X` for explorer (persistent picker) mode - option `-F` decommissioned in favour of config `NNN_HELP` - `-F` redefined to multiplex `NNN_FIFO` to preview or explore - support paging noninterative _run-cmd-as-plugin_ output - `nuke` - add option to execute binaries (#1111) - plugin `fzopen` - call `open` on macOS, add option to use `nuke` - plugin `fzcd` will not modify selection - plugin `suedit` - preserve environment - several `preview-tui` fixes - plugin `wall` renamed to `wallpaper` - remove plugin `fzz` - merged into plugin `autojump` - remove plugin `upgrade` - packaging is on OBS now - remove plugin `treeview` - needs minor `preview-tui` tweak - remove plugin `picker` - `nnn -p -` does the same - remove plugin `pdfview` - needs simple change in `pdfread` - remove plugin `uidgid` - use program option `-U` - remove plugins `mediainf`, `hexview` - simple one-liners ------------------------------------------------------------------------------- nnn v4.1.1 Sake 2021-06-03 - fix segfault on session save (#1041) - remove redundant `_Atomic` usage - move [`patches`](../tree/master/patches) directory to top-level - fix and cleanup gitstatus patch - plugin `imgview` improvements (#1049) - restore source-code packing on CircleCI - add Makefile target to compile with musl ------------------------------------------------------------------------------- nnn v4.1 2021-06-02 - a patch management model for approved patches - multi-threaded disk usage calculation using pthreads and FTS - dynamic view update when calculating disk usage - Bksp/Del at empty filter prompt to refresh dir - Try to create new context with Shift-TAB, else reverse cycle - Alt+Esc to quit context from filter prompt - fix zombies left behind after running plugins (#999) - named persistent sessions (mcchrish/nnn.vim#43) - consider nanosecond resolution when sorting by time (#978) - check external selection in `'c'urrent / 's'el` prompt (#976) - show number of files selected in local selection buffer, if any - `nnn` & picker plugin (e.g. `fzopen`) sync (mcchrish/nnn.vim#82) - make batch rename interactive (#971) - cached uid/gid for performance improvement - fixes for `nnn` with `netbsd-curses`, `musl-fts`, `musl` (#998) - script to statically compile `nnn` with `musl gcc` on Ubuntu - restore hovered file when plugin is chosen from plugin dir - support QuickLook on WSL in `preview-tui` (#959) - toggle `preview-tui` with the same _custom_ plugin key - smoother preview toggling in `preview-tui` (#966) - `listen_on` should be set in kitty.conf for `preview-tui` (#970) - minimal `bat` style in `preview-tui`, honors `$BAT_STYLE` - plugin `preview-tui-ext` supersedes `preview-tui` (#1033) - plugin `fzcd` can now fuzzy search multiple directories - plugin `imgview` supersedes `imgthumb` and `vidthumb` - plugin `umounttree`: unmount remote mntpoint from within - plugin `xdgdefault`: set the default app for hovered file type - plugin `fzplug`: fuzzy find, preview and run other plugins - plugin `cmusq`: queue/play music in `cmus` - plugin `mocplay` renamed to `mocq` - plugin `cleanfilename` renamed to `fixname` - go to last dir on ~ (HOME) or ` (ROOT) key repeat - ambiguous key ^Space to select/clear range dropped (#998) - user wiki page for [Themes](https://github.com/jarun/nnn/wiki/Themes) - show selection mark (`+`) in reverse bold for improved visibility - reverse block replaces `>` to mark hovered entry in detail mode - make option `O_CKBOARD` removed - make option `O_NOLOC` renamed to `O_NOLC` - ignore `O_NOLC` if `O_ICONS` or `O_NERD` is specified (#1026) - unicode arrow indicators if `O_ICONS` or `O_NERD` is specified - make option `NOX11`: disable notis, sel-clipboard sync, xterm title - retain filter in _nav-to-type_ mode after file open - fix no files picked with `NNN_TMPFILE` exported and q to quit - disable xterm title setting in picker mode (#974) ------------------------------------------------------------------------------- nnn v4.0 Sushi 2021-04-13 - show xterm title on option `-x` ------------------------------------------------------------------------------- nnn v3.7 2021-04-13 - allow plugins to clear selection (#884, #889, #917) - do not clear selection on hovered file deletion - resurrect `'c'urrent/'s'el` prompt and option `-u` (#889) - show only file name in reverse in detail mode - more file/mime types supported in `preview-tui-ext` - plugin `mtpmount` - (un)mount MTP devices - plugin `cleanfilename` - more shell-friendly file names - plugin `rsynccp` - copy-paste with visual progress - replace `$HOME` by `~` in address bar - show current path in terminal title (#911) - total links and inode number of hardlink in statusbar - fix symlink to text file not opening in CLI editor (#890) - fix symlink size shown as 0B in statusbar (#888) - show symlink target in statusbar (#893) - show correct disk free/total on macOS (#888) - fix directory disk usage showing as 0 on macOS (#941) - fix name col len with `-C` and icons compiled-in (#936) - refactor printing entries in light/detail modes (#934) - make option `O_CKBOARD` for checker board as indicator ------------------------------------------------------------------------------- nnn v3.6 Nina 2021-03-16 - REPL command prompt (Esc or Enter to exit) - invert selection with A - option `-u` removed (always prefer selection to hovered) - visit start dir on @ when start path is a file - exit filter mode and redraw on ^L if no last filter - plugin `fzcd` now selects the chosen file (#876) - `ueberzug` support in plugin `preview-tui` - new plugin `preview-tui-ext` with extra preview support - clear selection after successful plugin invocation - add method to sync subshell `$PWD` in WIki - clear selection on single file deletion (#812) - copy between instances not working (#864) - plugin `togglex` to toggle exe mode of a selection (#813) - fix `memccpy()` buffer overlap fault on macOS (#786) - show `0 selected` msg on cp/mv with empty selection (#855) - fix frozen terminal caused by opener (#858) - migrate macOS CI to GitHub workflows, retire Travis ------------------------------------------------------------------------------- nnn v3.5 Freddie 2020-11-17 - compile-in Alexey Tourbin's QSORT macro - support Nerd Font patched icons [`make O_NERD=1`] - auto-generate static binaries with icons support - audit and adapt all plugins for macOS - enhance plugin `dups` to delete duplicates interactively - plugin `autojump` now supports `jump` and `zoxide` - support `gio trash` to Trash [`export NNN_TRASH=2`] (#740) - quit program on double Esc in normal mode (#775) - ^Space replaces ^K for range selection/clear selection - show selection symbol (`+`) next to filename in detail mode (#741) - error & quit on Q if no selection, else pick to stdout - repeat ^T to cycle sort by time, size and clear - option `-U` to show user & group info in status bar - option `-J` to disable auto-proceed on select (#713) - option `-D` to show dirs in context color with `NNN_FCOLORS` - honor option `-C` for context colors - show indicators if more entries above/below listing (#744) - show missing utility name in flash msg (#753) - exit `preview-tabbed` on ^C (#727) - invoke GNU sed (_gsed_) on macOS (#728) - fix HW cursor moves to wrong line (#735) - fix rollover bug with multiline scroll (#743) - fix input stream not listed with `-s`/`-S` (#777) - fix locker not being invoked - make target `upx` for additional binary compression - compress auto-generated static binaries with upx - make variable `O_NOSSN` to compile out sessions - make variable `O_NOUG` to compile out user & group info ------------------------------------------------------------------------------- nnn v3.4 Emilia 2020-08-18 - icons with icon-specific colors (thanks @KlzXS) - enhanced `NNN_COLORS` with xterm 256 colors support - new colorscheme with `NNN_FCOLORS` (file type specific colors) - switch `-C` to force earlier colorscheme (dirs follow context color) - updates for Haiku (thanks @CodeforEvolution) - fix XFS navigation issue (thanks @ucs1) - optimize archive extension matching on file open - show location in context color - support `host[:dir]` format for remote mounts - clear selection after copy - support traversal on file/dir creation - show selection in reverse in status bar - show status bar indicator `H` when hidden files are listed - show and confirm archive command output - support _cd on quit_ in picker mode ------------------------------------------------------------------------------- nnn v3.3 2020-07-14 - subdir `mounts` for remote and archive mounts - remove mount point on successful unmount of remote/archive - show error and prompt user if `cp`/`mv`/`rm` operation fails - support absolute/relative paths in cp/mv as - mark current path automatically on archive/remote mount - mark current path automatically on target file visit in _find and list_ mode - option `-C` to place HW cursor on hovered for screen readers and braille displays - option `-u` to use selection (if available) and skip `current/sel` prompt - key Alt+Esc to clear filter prompt and redraw - support Esc to cancel remove operation - `gpge` & `gpgd`: encrypt and decrypt with GPG - `blknew`: create new files and directories in bulk - `preview-tui` - unified to support `tmux`/`kitty`/`xterm`/`$TERMINAL` - auto-determine split orientation based on terminal height and width - provision to use [`scope.sh`](https://github.com/ranger/ranger/blob/master/ranger/data/scope.sh) and [`pistol`](https://github.com/doronbehar/pistol) - various other improvements - `upload`: send to Firefox Send if [`ffsend`](https://github.com/timvisee/ffsend) is found - `hexview`: add [`hx`](https://github.com/krpors/hx) as alternative hex viewer - `nuke` and `imgview`: add [`imv`](https://github.com/eXeC64/imv) as alternative image viewer - add find (with `fd`) and grep (with `rg`) examples in plugins doc - key Esc or left click to resend hovered file path to `NNN_FIFO` - show `+` instead of `s` in status bar on selection - F5 removed (misfit for toggle hidden), ^S removed (often masked, redundant) - handle abnormal program termination and remove NNN_PIPE and/or NNN_FIFO - clear selection after successful batch rename, link creation - make option `O_CTX8` for 8 contexts (NOT backward compatible with 4 contexts) - fix issue with child window resize (see #656) - fix issue with `NNNLVL` on macOS (see #639) - fix issue with restoring session with du/au enabled ------------------------------------------------------------------------------- nnn v3.2 2020-05-26 - an official logo - previews - config `NNN_FIFO` to write hovered file paths a previewer can read - plugin `preview-tabbed`: [tabbed](https://tools.suckless.org/tabbed)/xembed based file previewer - plugin `preview-tui`: simple TUI file previewer in tmux/xterm - plugin `preview-kitty`: preview using kitty terminal's capabilities - [live preview](https://github.com/jarun/nnn/wiki/Live-previews) configuration example - find & list - send list of files from (cmd run as) plugin to `nnn` - plugin `finder`: find/fd/fzf/grep/ripgrep/fzf (in subtree) and list in `nnn` - Right or l on symlink in list dir takes to target file - persistent session option `-S` [for disk usage, run `nnn -T d` (see help)] - hover on the file when a file path is passed as positional argument - go to first file or match with ' (followed by ' or char) - config `NNN_SEL` to specify custom selection file - config `NNN_LOCKER` to specify locker program - dim file details in detail mode - call `chdir()` on directory change - option `-l`: number of lines to move on mouse scroll - graphical [keybind map](https://github.com/jarun/nnn/wiki/Usage#graphical-map) - let `NNN_COLORS` override `NO_COLOR` - plugins - option `-P`: run plugin by key at start - run plugins with Alt+key - allow `NNN_PIPE` usage by commands run as plugin - input format to `NNN_PIPE`: `` (see plugins doc) - set `ctxcode` to `+` for smart context usage (next inactive, else current) - `getplugs` to fetch plugins by installed version of `nnn` - plugin `mimelist`: list files by mime type in subtree - plugin `bookmarks`: named bookmarks using symlinks - plugin `nbak`: backup `nnn` config - `nuke` adds lowdown as alternative markdown viewer - several plugin improvements - fix broken screen on resize (see #520) - fix broken version sort (see #550) - fix list and pipe modes not working together - fix multiple issues with listing files - fix `@` shown in detail mode for symlink to dir - fix listing files directly under `/` - move to `-std=c11` ------------------------------------------------------------------------------- nnn v3.1 2020-04-13 - unlimited bookmarks and plugin keys - status bar text in context color - support config `NO_COLOR` to disable colors - config `NNN_OPTS` to specify binary options to `nnn` - config `NNN_MCLICK` to emulate configurable key - toggle selection on right click - ignore hard links when calculating disk usage - dim (hard/sym) link names (symlink to file has `@`) - more special keys at empty filter prompt in _type-to-nav_ - key > to export file list - option `-F` to show fortune in help and settings screen - option `-T` to specify sort order (obsoletes `-v`) - option to clear sort order - key T to change time type (access/change/mod) - `.nmv` - internal fully-functional batch renamer plugin - make var `O_NOBATCH` to disable native batch renamer - `nuke` & `imgview` - open all images in directory sxiv - `nuke` - open log files in vi - plugin `x2sel` - system clipboard to selection copier - plugin `fzy` - cd using z database - plugin `fzopen` - support `FZF_DEFAULT_COMMAND` - create new context on TAB without prompt - hover and connect by dir name (within config dir) - move to next entry on current file delete - on single file copy/move, select the copied/moved file - option `-f` to use readline history file (off by default) - use `s` in status bar to indicate selection in progress - make var `O_NOMOUSE` to disable mouse support - do not store `NNN_TRASH` and `-Q` in config/session - add sample .desktop file for XDG compatible DEs - rename _nav-as-you-type_ to _type-to-nav_ mode - fix PCRE case-insensitive regex search - fix no error msg when filter length limit exceeded - fix static package generation - fix broken abort message when started in du-mode - fix filter lost on context switch in non _type-to-nav_ mode - fix broken readline prompt - fix long strings treated as action keys in filter prompt - fix `NNNLVL` not reset when spawned shell is exited ------------------------------------------------------------------------------- nnn v3.0 2020-02-12 - take list of files as input and show - option `-e` replaces `NNN_USE_EDITOR` - option `-t` replaces `NNN_IDLE_TIMEOUT` - PCRE support - more readline bindings for native prompts - run GUI app as plugin - attempt lazy unmount when regular unmount fails - fix unmount on macOS: use `umount` - detect `sshfs` and `rclone` to prompt intelligently - auto-proceed on file open (toggle key +) - quit with error code on Q - additional key F5 to toggle hidden - key e to edit in EDITOR (back on multiple user requests) - option to edit list of files in selection is changed to E - do not end selection on redraw - `nuke`: [`glow`](https://github.com/charmbracelet/glow) as Markdown viewer - `nuke`: refactor, handle some common video types by extension - file name removed from status bar - static Makefile target - generate, upload static package on release - fix crash on entering empty dir, then Down - fix keypresses lost when showing message - fix #227: `nnn` creates xdg-open zombies ------------------------------------------------------------------------------- nnn v2.9 2020-01-15 - all keybinds and options reviewed by the team and frozen (see #422) - reduced number of keybinds - greatly improved help screen readability - `nuke`: sample opener (CLI-only by default) and plugin - fast line redraws instead of full screen refresh (thanks @annagrram) - auto archive handling by extension (see config `NNN_ARCHIVE`) - Lead key simplified to bookmark key (b or ^/) - single key to toggle order (t or ^T) - plugins - `.cbcp`: copy selection to system clipboard (internal, program option `-x`) - `.ntfy`: show noti on cp, mv, rm completion (internal, program option `-x`) - `autojump`: navigate using autojump - `upload`: paste text files to http://ix.io, upload rest to https://file.io - all fuzzy plugins modified to support both `fzf` and `fzy` - more control on plugins - prefix `-` to skip directory refresh after running (cmd as) plugin - suffix `*` to skip confirmation after running cmd as plugin - indicate range selection mode with `*` - list keys at bookmark and plugin key prompts - visit to pinned dir like bookmarks (Bookmark key followed by ,) - toggle executable (key *) - show mime along with file details - more special keys at empty filter prompt: - apply the last filter (^L) - toggle between string and regex (/) - toggle case-sensitivity (:) - retain filter on Esc, Up, Down - show filter details when filter is on - remove option to run filter as cmd on prompt key (can be disruptive) - program options - option `-x`: enable notis and copy selection to system clipboard - option `-g`: regex filters (string filter is default now) - option `-Q`: quit program without confirmation - option `-s`: load session - option `-n`: start in nav-as-you-type mode - option `-v`: version sort - option `-V`: show program version - option `-A`: disable dir auto-select - ISO 8601 compliant date in status bar - ported to Haiku OS (thanks @annagrram) - sort only filtered entries (to avoid directory refresh) - fix `getplugs` to install hidden files - fix several selection issues (see #400) - fix detail mode not restored on loading session - fix symlink to directory not auto-selected - fix regex error on partial regex patterns - fix symlink not shown if `stat(2)` on target fails - fix flags when spawning a CLI opener as default FM - fix issue with stat flag on Sun (no support for `dirent.d_type`) - fix current file in current context not saved correctly in session - signed source distribution on release - simplified debugging with line numbers in logs ------------------------------------------------------------------------------- nnn v2.8.1 2019-12-05 - Fix always archiving current file - More elaborate docs on selection changes ------------------------------------------------------------------------------- nnn v2.8 2019-12-04 - sessions (thanks @annagrram) - `rclone` support for remote access (mount _any_ cloud storage!!!) - toggle selection with Space or ^J - ignore events during selection so the `+` symbol is not lost - run custom (non-shell-interpreted) commands like plugins - configure _cd-on-quit_ as the default behaviour - create parent dirs for new files and dirs, duplicate a file/dir anywhere - _copy/move as_ workflow (thanks @KlzXS) - edit , flush selection buffer (thanks @KlzXS) - support xargs with minimal options (as in BusyBox) (thanks @KlzXS) - changed the key to size sort to z - additional key ] to show command prompt - mount archives using `archivemount` - smoother double click handling - program option `-R` to disable rollover at edges - keybind collision checker (for custom keybind config) (thanks @annagrram) - show size of file in bytes in status bar in disk usage mode - pass unresolved path as second argument (`$2`) to plugin - mechanism for plugins to control active directory - all binary questions are confirmed by y or Y - plugins - some plugins renamed - integrated `shellcheck` in CI, POSIX-compliance fixes (thanks @koalaman) - `getplugs` - detect modifications in exiting plugin file (thanks @KlzXS) - `drag-file` & `drop-file`: drag & drop files using dragon - `gutenread`: browse, download and read from Project Gutenberg - `suedit` - edit file with superuser permissions - `fzhist` - fuzzy select commands from history, edit and run - `fzcd` - change to a fuzzy-searched directory - `rename` - batch rename directory or selection using qmv or vidir - `pskill` - fuzzy list a process or zombies by name and kill - `exetoggle` - toggle executable status of hovered file - `treeview` - informative tree output with file permissions and size - `chksum` - recursively calculate checksum for files in hovered directory - `fzopen` renamed to `fzopen` - `imgsxiv` instructions added to browse and rename images - create link to current file - additional key ; to execute plugin - more explicit force removal message - force non-detachable internal edits in $EDITOR (option `-E`) - export current file as `$nnn` (instead of `$NN`) - fix file open failure from browser when configured as default FM ------------------------------------------------------------------------------- nnn v2.7 2019-10-06 - plugins for image preview, image and video thumbnails - redesigned selection workflow - drop path prefix for files in current dir for selection based archives - custom direct keybinds for plugins - libreadline `.history` file moved to `nnn` config directory - export current entry as `$NN` at command prompt - more informative status bar in light/detail modes - auto-proceed to next file on single file select - path clipping for long paths - completely revamped wiki - new program options: - `-a` to use file access time throughout the program - `-c` to indicate cli-only opener - `-f` to run filter as command on ^P - `-o` replaces config `NNN_RESTRICT_NAV_OPEN` - `-t` replaces config `NNN_NO_AUTOSELECT` - `-r` replaces config `NNN_OPS_PROG` - plugin changes: - `vidthumb` - show video thumbnails in terminal - `mediainf` - show media info (decoupled as a plugin) - `notes` - open a quick notes file/dir in `$EDITOR` (decoupled as a plugin) - `dups` - list duplicate files in the current directory - `oldbigfile` - list large files by access time - `moclyrics` - show lyrics of the track currently playing in MOC - `uidgid` list uid and gid of files in directory - `mocplay` - now detects if a track is playing or not - `organize` - categorize files and move to respective directories - `pastebin` - now uses ix.io paste service - `fzy-edit` - merged into `fzy-open` - `viuimg` - fix directory view - `checksum` - fixed POSIX compliance issues - `boom` - play music in MOC - keybind changes: - select entry: Space and ^J - select range (or clear selection): m and ^K - select all in dir: a - list selection: M - ^N replaces ^T to toggle _nav-as-you-type_ - Shift TAB to reverse context cycle - ' to jump to first file in dir - S for du, A for apparent du - additional key : to run plugin - additional key F2 to rename file - additional key F5 to redraw - quit context key Leadq is removed - Leader key combinations: - Lead' to jump to first file in dir - Lead] go to next active context - Lead[ go to prev active context - Lead. toggle show hidden files - improved duplicate file workflow - improved batch rename workflow when a selection exists - removed the wild load option (`-w`) - removed quick notes (added plugin `notes`) - fix #225 (thanks @KlzXS) - fix `tar`/`bsdtar` always creating tar archives (and not by suffix) - fix single mouse click to select file not working - fix symlink to dir removed on batch rename - fix detail mode not set with program option `-S` ------------------------------------------------------------------------------- nnn v2.6 2019-08-06 - new plugins - view image or browse a directory of images in terminal - show image thumbnails - PDF and text file reader - calculate and verify checksum of selection or file - append (and play) selection/dir/file music in MOC - variable bitrate mp3 ringtone generator - split current file or join selection - better experience on Termux (and touch based devices) - mouse scrolling support (with ncursesw6.0 and above) - tap/left click to visit parent, toggle nav-as-you-type mode - light mode set as default - show status bar and use reverse video in light mode - changed program options - `-d`: detail mode - `-H`: show hidden files - `-l` is retired - support `XDG_CONFIG_HOME` - support / as an additional Leader key when filter is on - sort by file extension - use zip/unzip/tar if atool/bsdtar not found - support duplicate file (key ^R, same as rename file) - new config option `NNN_SSHFS_OPTS` to specify `sshfs` options - restrict opening 0 byte files (`NNN_RESTRICT_0B` is obsolete) - critical defects fixed - fix #276 - crash with variable length inotify event handling - fix #285 - hang after deleting/moving current directory - fix #274 - a broken prompt on empty input with libreadline - fix #304 - list selection from another instance - `cmatrix` as locker fallback - wait for user input after running a command from prompt - scrolloff set to 3 from 5 ------------------------------------------------------------------------------- nnn v2.5 2019-05-27 - mouse support - new location for config files - `~/.config/nnn` - plugin dir location: `~/.config/nnn/plugins` - selection file `.nnncp` is now `~/.config/nnn/.selection` - plugins: - pdfview: view a PDF in pager - nmount: (un)mount a storage device - ndiff: file and directory diff for selection - hexview: view a file in hex - imgresize: batch resize images to desktop resolution - ipinfo: check your IP address and whois information - transfer: upload a file to transfer.in - pastebin: paste the contents of a text file to paste.ubuntu.com - boom: play random music from a directory - nwal: set an image as wallpaper using nitrogen - pywal: set selected image as wallpaper, change terminal color scheme - getplugs: update plugins - SSHFS support - support `bsdtar`, simplify `patool` integration - native batch rename support (`vidir` dependency dropped) - changes to support [configuration](https://github.com/jarun/nnn/wiki/nnn-as-default-file-manager) as the default file manager - per-context detail/light mode - case-insensitive version compare - shortcut to visit `/` - ` (backtick) - vim-like scrolloff support - ^D & ^U: scroll half page, PgDn & PdUp: scroll full page - fix selection across contexts - recognize Home and End keys at prompt for editing - fix broken program option `-b` - POSIX-compliant user-scripts (wherever possible) - `NNN_SCRIPT` is retired (replaced by plugins) ------------------------------------------------------------------------------- nnn v2.4 2019-03-19 - FreeDesktop.org compliant trashing - mark selected entries with `+` - _wild_ mode (option `-w`, key ^W) for _nav-as-you-type_ - POSIX-compliant GUI app launcher with drop-down menu (key =) - new scripts: - upload image to imgur - send selection to Android using kdeconnect-cli - show permissions in detail mode - cp, mv progress bar for Linux (needs advcpmv) [BSD, macOS shows on ^T] - make libreadline an optional dep (reduces memory usage) - minimize the number of redraws - handle screen resize gracefully - option `-d` to show hidden files (`NNN_SHOW_HIDDEN` is removed) - additional key K to toggle selection - change visit start dir key to @ - option `-C` to disable colors removed - per-context initial directory replaced by program start dir - marker msg when spawning new shell removed - rename debug file to `nnndbg` ------------------------------------------------------------------------------- nnn v2.3 2019-02-19 - file picker mode - repo of user-contributed scripts - substring search for filters (option `-s`) - version sort (option `-n`) - disk usage calculation abort with ^C - create sym/hard link(s) to files in selection - archiving of selection - show dir symlinks along with dirs in top - fixed CJK character handling at prompts - key `N` (1 <= N <= 4) to switch to context N - bring back `NNN_OPENER` to specify file opener - env var `NNN_NOTE` and keybind ^N for quick notes - handle multiple arguments in VISUAL/EDITOR - show the current directory being scanned in `du` mode - select all files (Y) - show command prompt (^P) - key , replaces ` as alternative Leader Key - keybind for visit pinned directory is now ^B - additional key ^V to run or select custom script - use libreadline for command prompt - reduce delay on Esc press - config option to avoid unexpected behaviour on 0-byte file open (see #187) - rename config option `DISABLE_FILE_OPEN_ON_NAV` to `NNN_RESTRICT_NAV_OPEN` - keys removed - $, ^, Backspace, ^H, ^P, ^M, ^W, ` ------------------------------------------------------------------------------- nnn v2.2 2019-01-01 What's in? - (neo)vim plugin [nnn.vim](https://github.com/mcchrish/nnn.vim) - macOS fixes - Fix issues with file copy, move, remove - Handle Del in rename prompt - Pass correct `file` option to identify mime - Support selection across directories and contexts - Offer option `force` before file remove - Keys Tab, ^I to go to next active context - Per-context directory color specified by `$NNN_CONTEXT_COLORS` - Option `-c` is removed - Option `-C` to disable colors - Choose script to run from a script directory - Run a command (or launch an application) - Run file as executable (key C) - Documentation on lftp integration for remote file transfers - Support a _combined_ set of arguments to `$EDITOR`, `$PAGER` and `$SHELL` - Handle > 2 GB files on 32-bit ARM - Env var `$DISABLE_FILE_OPEN_ON_NAV` to disable file open on Right or l - `NUL`-terminated file paths in selection list instead of `LF` - Better support for Termux and Cygwin environments - Remapped keys - ^I - go to next active context - ^T - toggle _navigate-as-you-type_ ------------------------------------------------------------------------------- nnn v2.1 2018-11-23 What's in? - Inclusion in several distros including Arch Linux official repo - Multiple contexts (_aka_ tabs _aka_ workspaces) [max 4] - Copy, move, remove selected files, remove current file - [Leader key](https://github.com/jarun/nnn#leader-key) (like vim) - In-built GUI app launcher with up to 2 arguments (key o) - List copy selection (key y) - Env var `NNN_NO_AUTOSELECT` to disable dir auto-select - Key Esc exits prompt, ^L clears prompt - Program runtime help revamped - Static code analysis integration - gcc-8 warnings fixed - Remapped keys: - ^W - go to pinned dir - ^X - delete current entry - ^Q - quit program - `nlay` is retired (functionality built into `nnn`) - `chdir` prompt is retired - Env var `NNN_NO_X` retired, selection now works out of the box - Only single-char bookmark keys (to work with Leader key) ------------------------------------------------------------------------------- nnn v2.0 2018-10-19 What's in? - Mode to show apparent size (key `S`) - Script to integrate `patool` instead of `atool` - Support `bashlock` (OS X) and `lock` (BSD) as terminal locker - Symbol `@/` for symlink to dir - Dependency on `libreadline` removed ------------------------------------------------------------------------------- nnn v1.9 2018-08-10 What's in? - Support unlimited number of scripts - Pass currently selected filename as first argument to custom scripts - Support directory auto-select in _navigate-as-you-type_ mode - Show selection name in archive name prompt - Support Cygwin opener - Better support on RHEL 25 with earlier version on curses - Sample script for `fzy` integration - Now available on OpenBSD - Disabled package generation for Ubuntu 17.10 ------------------------------------------------------------------------------- nnn v1.8 2018-05-02 What's in? - Run a custom script - Archive selected file/directory - Show number of cherry-picked files in multi-copy mode - Env var `NNN_SHOW_HIDDEN` to show hidden files by default - Additional information in help screen - Give preference to env var VISUAL, if defined, over EDITOR - New/changed/remapped shortcuts - ^] - spawn a new shell in current directory - r - edit directory entries in vidir - R - run a custom script - ^I - toggle navigate-as-you-type mode - L - lock the current terminal (Linux-only) - All Ctrl shortcuts enabled in navigate-as-you-type mode - Fix: GUI programs closing when parent terminal is closed - Recognize `~`, `-` and `&` at bookmark prompt - Recognize ruby (.rb) files as text files - Efficient integer-only file size calculation - Official inclusion on openSUSE and Fedora - Package generation for Ubuntu 18.04 ------------------------------------------------------------------------------- nnn v1.7 2018-02-28 What's in? - Batch rename/move/delete files in vidir from [moreutils](https://joeyh.name/code/moreutils/) - Copy multiple file paths - Copy file paths when X is unavailable - Optionally quote individual file paths with single quotes on copy - Use ISO 8601 date format in file details - New/changed/remapped shortcuts: - ^B - show bookmark prompt (replaces b) - b - pin current dir (replaces ^B) - ^J - toggle du mode - R - batch rename files in vidir - ^F - extract archive (replaces ^X) - ^G - quit nnn and change dir - ^X - quit nnn (replaces ^Q) - Extra shortcuts enabled in nav-as-you-type mode: - ^K, ^Y (file path copy) - ^T (toggles quoted file path copy) - ^R (rename) - ^O (open with...) - ^B (show bookmark prompt) - ^V (visit pinned dir) - ^J (toggle du mode) - ^/ (open desktop opener) - ^F (extract archive) - ^L (refresh) - ^G (quit nnn and change dir) - ^X (quit nnn) ------------------------------------------------------------------------------- nnn v1.6 2017-12-25 What's in? - Shortcut `^O` to open file with custom application - Option `-b` to open bookmarks directly at start - Huge performance improvements around file name storing and handling - Several large static buffers removed or reduced - Several internal algorithms fine tuned for performance/resource usage ------------------------------------------------------------------------------- nnn v1.5 2017-10-05 What's in? - File and directory creation (`n`) - Env variable `NNN_NOWAIT` to unblock nnn when opening files (DE-specific) - Show current entry number in status bar - Support archive listing (`F`) and extraction (`Ctrl-X`) [using `atool`] - Show correct file size on i386 for large files (> 2GB) ------------------------------------------------------------------------------- nnn v1.4 2017-09-04 What's in? - Monitor directory changes - In-place file rename - Pin (`Ctrl-B`) a directory and visit (`Ctrl-V`) it anytime - Auto-completion scripts - Show volume capacity and free in help - Auto-fallback to light mode if too few columns (< 35) - PackageCore integration - Unsupported Function keys (they never work universally): - `F2` (rename), use `Ctrl-R` - `F5` (refresh), use `Ctrl-L` ------------------------------------------------------------------------------- nnn v1.3 2017-07-26 What's in? - Show directories in custom color (default: enabled in blue) - Option `-e` to use exiftool instead of mediainfo - Fixed #34: nftw(3) broken with too many open descriptors - More concise help screen ------------------------------------------------------------------------------- nnn v1.2 2017-06-29 What's in? - Use the desktop opener (xdg-open on Linux, open(1) on OS X) to open files - Option `NNN_USE_EDITOR` to open text files in EDITOR (fallback vi) - Bookmark support (maximum 10, key `b`) - *Navigate-as-you-type* mode (key `Insert` or option `-i`) - Subtree search: gnome-search-tool, fallback catfish (key `^/`) (customizable) - Show current directory content size and file count in disk usage mode - Add detail view mode as default, use `-l` to start in light mode - Shortcuts `F2` and `^L` to refresh and unfilter Note: if filter is empty, `Enter` *opens* the currently selected file now - Help screen shows bookmarks and configuration - Show a message when calculating disk usage - Show the spawned shell level - Linux only: use vlock as the locker on timeout (set using `NNN_IDLE_TIMEOUT`) ------------------------------------------------------------------------------- nnn v1.1 2017-05-12 News - Introducing nlay - a highly customizable bash script to handle media type - nnn is on [Homebrew](http://braumeister.org/formula/nnn) now - RPM packages for CentOS 7 and Fedora 24 generated on release What's in? - *Search-as-you-type* - Unicode support - Option `-S` to start in disk usage analyzer mode - Show media information (using mediainfo) - Use readline at change directory prompt - Jump to prev directories using `cd .....` (with `.` as PWD) - Jump to initial directory using `&` - Show help, mediainfo and file info in PAGER - Several optimizations ------------------------------------------------------------------------------- nnn v1.0 2017-04-13 Modifications - Behaviour and navigation - Detail view (default: disabled) with: - file type (directory, regular, symlink etc.) - modification time - human-readable file size - current item in reverse video - number of items in current directory - full name of currently selected file in 'bar' - Show details of the currently selected file (stat, file) - Disk usage analyzer mode (within the same fs, doesn't follow symlinks) - Directories first (even with sorting) - Sort numeric names in numeric order - Case-insensitive alphabetic content listing instead of upper case first - Key `-` to jump to last visited directory - Roll over at the first and last entries of a directory (with Up/Down keys) - Removed navigation restriction with relative paths (and let permissions handle it) - Sort entries by file size (largest to smallest) - Shortcut to invoke file name copier (set using environment variable `NNN_COPIER`) - File association - Set `NNN_OPENER` to let a desktop opener handle it all. E.g.: export NNN_OPENER=xdg-open export NNN_OPENER=gnome-open export NNN_OPENER=gvfs-open - Selective file associations (ignored if `NNN_OPENER` is set): - Associate plain text files (determined using file) with vi - Associate common audio and video mimes with mpv - Associate PDF files with [zathura](https://pwmt.org/projects/zathura/) - Removed `less` as default file opener (there is no universal standalone opener utility) - You can customize further (see [how to change file associations](#change-file-associations)) - `NNN_FALLBACK_OPENER` is the last line of defense: - If the executable in static file association is missing - If a file type was not handled in static file association - This may be the best option to set your desktop opener to - To enable the desktop file manager key, set `NNN_DE_FILE_MANAGER`. E.g.: export NNN_DE_FILE_MANAGER=thunar - Optimization - All redundant buffer removal - All frequently used local chunks now static - Removed some redundant string allocation and manipulation - Simplified some roundabout procedures - Compiler warnings fixed - strip the final binary ------------------------------------------------------------------------------- nnn-5.0/LICENSE000066400000000000000000000027001466310014300131510ustar00rootroot00000000000000BSD 2-Clause License Copyright (c) 2014-2016, Lazaros Koromilas Copyright (c) 2014-2016, Dimitris Papastamos Copyright (c) 2016-2024, Arun Prakash Jana All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nnn-5.0/Makefile000066400000000000000000000305211466310014300136060ustar00rootroot00000000000000VERSION = $(shell grep -m1 VERSION $(SRC) | cut -f 2 -d'"') PREFIX ?= /usr/local MANPREFIX ?= $(PREFIX)/share/man DESKTOPPREFIX ?= $(PREFIX)/share/applications DESKTOPICONPREFIX ?= $(PREFIX)/share/icons/hicolor STRIP ?= strip PKG_CONFIG ?= pkg-config INSTALL ?= install CP ?= cp CFLAGS_OPTIMIZATION ?= -O3 O_DEBUG := 0 # debug binary O_NORL := 0 # no readline support O_PCRE := 0 # link with PCRE library O_NOLC := 0 # no locale support O_NOMOUSE := 0 # no mouse support O_NOBATCH := 0 # no built-in batch renamer O_NOFIFO := 0 # no FIFO previewer support O_CTX8 := 0 # enable 8 contexts O_ICONS := 0 # support icons-in-terminal O_NERD := 0 # support icons-nerdfont O_EMOJI := 0 # support emoji O_QSORT := 0 # use Alexey Tourbin's QSORT implementation O_BENCH := 0 # benchmark mode (stops at first user input) O_NOSSN := 0 # disable session support O_NOUG := 0 # disable user, group name in status bar O_NOX11 := 0 # disable X11 integration O_MATCHFLTR := 0 # allow filters without matches O_NOSORT := 0 # disable sorting entries on dir load # User patches O_COLEMAK := 0 # change key bindings to colemak compatible layout O_GITSTATUS := 0 # add git status to detail view O_NAMEFIRST := 0 # print file name first, add uid and guid to detail view O_RESTOREPREVIEW := 0 # add preview pipe to close and restore preview pane T_ICONS := 0 # test if multiple icons options are set and fail # convert targets to flags for backwards compatibility ifneq ($(filter debug,$(MAKECMDGOALS)),) O_DEBUG := 1 endif ifneq ($(filter norl,$(MAKECMDGOALS)),) O_NORL := 1 endif ifneq ($(filter nolc,$(MAKECMDGOALS)),) O_NORL := 1 O_NOLC := 1 endif ifeq ($(strip $(O_DEBUG)),1) CPPFLAGS += -DDEBUG CFLAGS += -g3 endif ifeq ($(strip $(O_NORL)),1) CPPFLAGS += -DNORL else ifeq ($(strip $(O_STATIC)),1) CPPFLAGS += -DNORL else LDLIBS += -lreadline endif ifeq ($(strip $(O_PCRE)),1) CPPFLAGS += -DPCRE LDLIBS += -lpcre endif ifeq ($(strip $(O_NOLC)),1) ifeq ($(strip $(O_ICONS)),1) $(info *** Ignoring O_NOLC since O_ICONS is set ***) else ifeq ($(strip $(O_NERD)),1) $(info *** Ignoring O_NOLC since O_NERD is set ***) else ifeq ($(strip $(O_EMOJI)),1) $(info *** Ignoring O_NOLC since O_EMOJI is set ***) else CPPFLAGS += -DNOLC endif endif ifeq ($(strip $(O_NOMOUSE)),1) CPPFLAGS += -DNOMOUSE endif ifeq ($(strip $(O_NOBATCH)),1) CPPFLAGS += -DNOBATCH endif ifeq ($(strip $(O_NOFIFO)),1) CPPFLAGS += -DNOFIFO endif ifeq ($(strip $(O_CTX8)),1) CPPFLAGS += -DCTX8 endif ifeq ($(strip $(O_ICONS)),1) ICONS_INCLUDE = icons-generated-icons-in-term.h CPPFLAGS += -DICONS_IN_TERM -DICONS_INCLUDE=\"$(ICONS_INCLUDE)\" ifeq ($(strip $(T_ICONS)),1) $(error Choose only one system for icons (O_ICONS, O_NERD or O_EMOJI)) endif T_ICONS := 1 endif ifeq ($(strip $(O_NERD)),1) ICONS_INCLUDE = icons-generated-nerd.h CPPFLAGS += -DNERD -DICONS_INCLUDE=\"$(ICONS_INCLUDE)\" ifeq ($(strip $(T_ICONS)),1) $(error Choose only one system for icons (O_ICONS, O_NERD or O_EMOJI)) endif T_ICONS := 1 endif ifeq ($(strip $(O_EMOJI)),1) ICONS_INCLUDE = icons-generated-emoji.h CPPFLAGS += -DEMOJI -DICONS_INCLUDE=\"$(ICONS_INCLUDE)\" ifeq ($(strip $(T_ICONS)),1) $(error Choose only one system for icons (O_ICONS, O_NERD or O_EMOJI)) endif T_ICONS := 1 endif ifeq ($(strip $(O_QSORT)),1) CPPFLAGS += -DTOURBIN_QSORT endif ifeq ($(strip $(O_BENCH)),1) CPPFLAGS += -DBENCH endif ifeq ($(strip $(O_NOSSN)),1) CPPFLAGS += -DNOSSN endif ifeq ($(strip $(O_NOUG)),1) CPPFLAGS += -DNOUG endif ifeq ($(strip $(O_NOX11)),1) CPPFLAGS += -DNOX11 endif ifeq ($(strip $(O_MATCHFLTR)),1) CPPFLAGS += -DMATCHFLTR endif ifeq ($(strip $(O_NOSORT)),1) CPPFLAGS += -DNOSORT endif ifeq ($(shell $(PKG_CONFIG) ncursesw && echo 1),1) CFLAGS_CURSES ?= $(shell $(PKG_CONFIG) --cflags ncursesw) LDLIBS_CURSES ?= $(shell $(PKG_CONFIG) --libs ncursesw) else ifeq ($(shell $(PKG_CONFIG) ncurses && echo 1),1) CFLAGS_CURSES ?= $(shell $(PKG_CONFIG) --cflags ncurses) LDLIBS_CURSES ?= $(shell $(PKG_CONFIG) --libs ncurses) else LDLIBS_CURSES ?= -lncurses endif CFLAGS += -std=c11 -Wall -Wextra -Wshadow CFLAGS += $(CFLAGS_OPTIMIZATION) CFLAGS += $(CFLAGS_CURSES) LDLIBS += $(LDLIBS_CURSES) -lpthread # static compilation needs libgpm development package ifeq ($(strip $(O_STATIC)),1) LDFLAGS += -static LDLIBS += -lgpm endif DISTFILES = src nnn.1 Makefile README.md LICENSE SRC = src/nnn.c HEADERS = src/nnn.h BIN = nnn DESKTOPFILE = misc/desktop/nnn.desktop LOGOSVG = misc/logo/logo.svg LOGO64X64 = misc/logo/logo-64x64.png COLEMAK = patches/colemak GITSTATUS = patches/gitstatus NAMEFIRST = patches/namefirst RESTOREPREVIEW = patches/restorepreview # test if we are on Mac OS X and get X.Y.Z OS version with system binary /usr/bin/sw_vers MACOS_VERSION := $(strip $(shell command -v sw_vers >/dev/null && [ "`sw_vers -productName`" = "Mac OS X" ] && sw_vers -productVersion)) # if Mac OS X detected, test if its version is below 10.12.0 relying on "sort -c" returning "disorder" message if the input is not sorted ifneq ($(MACOS_VERSION),) MACOS_BELOW_1012 := $(if $(strip $(shell printf '10.12.0\n%s' "$(MACOS_VERSION)" | sort -ct. -k1,1n -k2,2n -k3,3n 2>&1)),1) endif # if Mac OS X version is below 10.12.0, compile in the replacement clock_gettime and define MACOS_BELOW_1012 so that it's included in nnn.c ifneq ($(MACOS_BELOW_1012),) GETTIME_C = misc/macos-legacy/mach_gettime.c GETTIME_H = misc/macos-legacy/mach_gettime.h SRC += $(GETTIME_C) HEADERS += $(GETTIME_H) CPPFLAGS += -DMACOS_BELOW_1012 endif ifeq ($(strip $(O_DEBUG)),1) HEADERS += src/dbg.h endif ifeq ($(strip $(O_QSORT)),1) HEADERS += src/qsort.h endif ifeq ($(strip $(O_EMOJI)),1) HEADERS += src/icons.h src/$(ICONS_INCLUDE) endif ifeq ($(strip $(O_NERD)),1) HEADERS += src/icons.h src/$(ICONS_INCLUDE) endif ifeq ($(strip $(O_ICONS)),1) HEADERS += src/icons.h src/$(ICONS_INCLUDE) src/icons-in-terminal.h endif all: $(BIN) $(BIN): $(SRC) $(HEADERS) Makefile @$(MAKE) --silent prepatch $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(GETTIME_C) $< $(LDLIBS) @$(MAKE) --silent postpatch # targets for backwards compatibility debug: $(BIN) norl: $(BIN) nolc: $(BIN) src/$(ICONS_INCLUDE): src/icons-hash.c src/icons.h src/icons-in-terminal.h $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DICONS_GENERATE -o src/icons-hash-gen src/icons-hash.c ./src/icons-hash-gen > $@ install-desktop: $(DESKTOPFILE) $(INSTALL) -m 0755 -d $(DESTDIR)$(DESKTOPPREFIX) $(INSTALL) -m 0644 $(DESKTOPFILE) $(DESTDIR)$(DESKTOPPREFIX) $(INSTALL) -m 0755 -d $(DESTDIR)$(DESKTOPICONPREFIX)/scalable/apps $(INSTALL) -m 0644 $(LOGOSVG) $(DESTDIR)$(DESKTOPICONPREFIX)/scalable/apps/nnn.svg $(INSTALL) -m 0755 -d $(DESTDIR)$(DESKTOPICONPREFIX)/64x64/apps $(INSTALL) -m 0644 $(LOGO64X64) $(DESTDIR)$(DESKTOPICONPREFIX)/64x64/apps/nnn.png uninstall-desktop: $(RM) $(DESTDIR)$(DESKTOPPREFIX)/$(DESKTOPFILE) $(RM) $(DESTDIR)$(DESKTOPICONPREFIX)/scalable/apps/nnn.svg $(RM) $(DESTDIR)$(DESKTOPICONPREFIX)/64x64/apps/nnn.png install: all $(INSTALL) -m 0755 -d $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 0755 $(BIN) $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 0755 -d $(DESTDIR)$(MANPREFIX)/man1 $(INSTALL) -m 0644 $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 uninstall: $(RM) $(DESTDIR)$(PREFIX)/bin/$(BIN) $(RM) $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 strip: $(BIN) $(STRIP) $^ upx: $(BIN) $(STRIP) $^ upx -qqq $^ static: # regular static binary make O_STATIC=1 strip mv $(BIN) $(BIN)-static # static binary with icons-in-terminal support make O_STATIC=1 O_ICONS=1 strip mv $(BIN) $(BIN)-icons-static # static binary with patched nerd font support make O_STATIC=1 O_NERD=1 strip mv $(BIN) $(BIN)-nerd-static # static binary with emoji support make O_STATIC=1 O_EMOJI=1 strip mv $(BIN) $(BIN)-emoji-static musl: cp misc/musl/musl-static-ubuntu.sh . ./musl-static-ubuntu.sh 1 rm ./musl-static-ubuntu.sh shellcheck: find ./plugins/ -type f -not -name "*.md" -exec shellcheck {} + dist: mkdir -p nnn-$(VERSION) $(CP) -r $(DISTFILES) nnn-$(VERSION) tar -cf - nnn-$(VERSION) | gzip > nnn-$(VERSION).tar.gz $(RM) -r nnn-$(VERSION) sign: git archive -o nnn-$(VERSION).tar.gz --format tar.gz --prefix=nnn-$(VERSION)/ v$(VERSION) gpg --detach-sign --yes nnn-$(VERSION).tar.gz rm -f nnn-$(VERSION).tar.gz upload-local: sign static musl $(eval ID=$(shell curl -s 'https://api.github.com/repos/jarun/nnn/releases/tags/v$(VERSION)' | jq .id)) # upload sign file curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=nnn-$(VERSION).tar.gz.sig' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/pgp-signature' \ --upload-file nnn-$(VERSION).tar.gz.sig # upx compress all static binaries upx -qqq $(BIN)-static upx -qqq $(BIN)-icons-static upx -qqq $(BIN)-nerd-static upx -qqq $(BIN)-emoji-static # upload static binary tar -zcf $(BIN)-static-$(VERSION).x86_64.tar.gz $(BIN)-static curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=$(BIN)-static-$(VERSION).x86_64.tar.gz' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/x-sharedlib' \ --upload-file $(BIN)-static-$(VERSION).x86_64.tar.gz # upload icons-in-terminal compiled static binary tar -zcf $(BIN)-icons-static-$(VERSION).x86_64.tar.gz $(BIN)-icons-static curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=$(BIN)-icons-static-$(VERSION).x86_64.tar.gz' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/x-sharedlib' \ --upload-file $(BIN)-icons-static-$(VERSION).x86_64.tar.gz # upload patched nerd font compiled static binary tar -zcf $(BIN)-nerd-static-$(VERSION).x86_64.tar.gz $(BIN)-nerd-static curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=$(BIN)-nerd-static-$(VERSION).x86_64.tar.gz' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/x-sharedlib' \ --upload-file $(BIN)-nerd-static-$(VERSION).x86_64.tar.gz # upload emoji compiled static binary tar -zcf $(BIN)-emoji-static-$(VERSION).x86_64.tar.gz $(BIN)-emoji-static curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=$(BIN)-emoji-static-$(VERSION).x86_64.tar.gz' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/x-sharedlib' \ --upload-file $(BIN)-emoji-static-$(VERSION).x86_64.tar.gz # upload musl static binary tar -zcf $(BIN)-musl-static-$(VERSION).x86_64.tar.gz $(BIN)-musl-static curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=$(BIN)-musl-static-$(VERSION).x86_64.tar.gz' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/x-sharedlib' \ --upload-file $(BIN)-musl-static-$(VERSION).x86_64.tar.gz clean: $(RM) -f $(BIN) nnn-$(VERSION).tar.gz *.sig $(BIN)-static $(BIN)-static-$(VERSION).x86_64.tar.gz $(BIN)-icons-static $(BIN)-icons-static-$(VERSION).x86_64.tar.gz $(BIN)-nerd-static $(BIN)-nerd-static-$(VERSION).x86_64.tar.gz $(BIN)-emoji-static $(BIN)-emoji-static-$(VERSION).x86_64.tar.gz $(BIN)-musl-static $(BIN)-musl-static-$(VERSION).x86_64.tar.gz src/icons-hash-gen src/icons-generated-*.h checkpatches: ./patches/check-patches.sh prepatch: ifeq ($(strip $(O_NAMEFIRST)),1) patch --forward $(PATCH_OPTS) --strip=1 --input=$(NAMEFIRST)/mainline.diff ifeq ($(strip $(O_GITSTATUS)),1) patch --forward $(PATCH_OPTS) --strip=1 --input=$(GITSTATUS)/namefirst.diff endif else ifeq ($(strip $(O_GITSTATUS)),1) patch --forward $(PATCH_OPTS) --strip=1 --input=$(GITSTATUS)/mainline.diff endif ifeq ($(strip $(O_RESTOREPREVIEW)),1) patch --forward $(PATCH_OPTS) --strip=1 --input=$(RESTOREPREVIEW)/mainline.diff endif ifeq ($(strip $(O_COLEMAK)),1) patch --forward $(PATCH_OPTS) --strip=1 --input=$(COLEMAK)/mainline.diff endif postpatch: ifeq ($(strip $(O_NAMEFIRST)),1) ifeq ($(strip $(O_GITSTATUS)),1) patch --reverse $(PATCH_OPTS) --strip=1 --input=$(GITSTATUS)/namefirst.diff endif patch --reverse $(PATCH_OPTS) --strip=1 --input=$(NAMEFIRST)/mainline.diff else ifeq ($(strip $(O_GITSTATUS)),1) patch --reverse $(PATCH_OPTS) --strip=1 --input=$(GITSTATUS)/mainline.diff endif ifeq ($(strip $(O_RESTOREPREVIEW)),1) patch --reverse $(PATCH_OPTS) --strip=1 --input=$(RESTOREPREVIEW)/mainline.diff endif ifeq ($(strip $(O_COLEMAK)),1) patch --reverse $(PATCH_OPTS) --strip=1 --input=$(COLEMAK)/mainline.diff endif skip: ; .PHONY: all install uninstall strip static dist sign upload-local clean install-desktop uninstall-desktop nnn-5.0/README.md000066400000000000000000000236671466310014300134420ustar00rootroot00000000000000

nnn
nnn - Supercharge your productivity!

Latest release Availability CircleCI Status GitHub CI Status Privacy Awareness License

[Features] [Quickstart] [Plugins] [Wiki]

`nnn` (_n³_) is a full-featured terminal file manager. It's tiny, nearly 0-config and [incredibly fast](https://github.com/jarun/nnn/wiki/Performance). It is designed to be unobtrusive with smart workflows to match the trains of thought. `nnn` can analyze disk usage, batch rename, launch apps and pick files. The plugin repository has tons of plugins to extend the capabilities further e.g. [live previews](https://github.com/jarun/nnn/wiki/Live-previews), (un)mount disks, find & list, file/dir diff, upload files. A [patch framework](https://github.com/jarun/nnn/tree/master/patches) hosts sizable user-submitted patches which are subjective in nature. Independent (neo)vim plugins - [nnn.vim](https://github.com/mcchrish/nnn.vim), [vim-floaterm nnn wrapper](https://github.com/voldikss/vim-floaterm#nnn) and [nnn.nvim](https://github.com/luukvbaal/nnn.nvim) (neovim exclusive). Runs on the Pi, [Termux](https://www.youtube.com/embed/AbaauM7gUJw) (Android), Linux, macOS, BSD, Haiku, Cygwin, WSL, across DEs or a strictly CLI env. [_(there's more)_](https://github.com/jarun/nnn/wiki/Basic-use-cases#the_nnn-magic) ## Features - Quality - Privacy-aware (no unconfirmed user data collection) - POSIX-compliant, follows Linux kernel coding style - Highly optimized, static analysis integrated code - Frugal - Typically needs less than 3.5MB resident memory - Works with 8 colors (and xterm 256 colors) - Disk-IO sensitive (few disk reads and writes) - No FPU usage (all integer maths, even for file size) - Minimizes screen refresh with fast line redraws - Tiny binary (typically around 100KB) - 1-column mode for smaller terminals and form factors - Hackable - compile in/out features and dependencies - Portable - Language-agnostic plugins - Static binary available (no need to install) - Minimal library deps, easy to compile - No config file, minimal config with sensible defaults - Plugin to backup configuration - Widely available on many packagers - Touch enabled, handheld-friendly shortcuts - Unicode support - Modes - Light (default), detail - Disk usage analyzer (block/apparent) - File picker, (neo)vim plugin - Navigation - Filter with automatic dir entry on unique match - *Type-to-nav* (turbo navigation/always filter) mode - Contexts (_aka_ tabs/workspaces) with custom colors - Sessions, bookmarks, mark and visit a dir - Remote mounts (needs `sshfs`, `rclone`) - Familiar shortcuts (arrows, ~, -, @), quick look-up - `cd` on quit (*easy* shell integration) - Proceed to next file on file open and selection - Search - Instant filtering with *search-as-you-type* - Regex (POSIX/PCRE) and string (default) filters - Subtree search plugin to open or edit files - Sort - Ordered pure numeric names by default (visit `/proc`) - Case-insensitive version (_aka_ natural) sort - By name, access/change/mod (default) time, size, extn - Reverse sort - Directory-specific ordering - Mimes - Preview hovered files in FIFO-based previewer - Open with desktop opener or specify a custom opener - File-specific colors (or minimal _dirs in context color_) - Icons and Emojis support (customize and compile-in) - Plugin for image, video and audio thumbnails - Create, list, extract (to), mount (FUSE based) archives - Option to open all text files in `$EDITOR` - Convenience - Detailed file stats and mime information - Run plugins and custom commands with hotkeys - FreeDesktop compliant trash utility integration - Cross-dir file/all/range selection - Create (with parents), rename, duplicate files and dirs - Create new file or directory (tree) on startup - Batch renamer for selection or dir - List input stream of file paths from stdin or plugin - Copy (as), move (as), delete, archive, link selection - Dir updates, notification on `cp`, `mv`, `rm` completion - Copy file paths to system clipboard on select - Launch apps, run commands, spawn a shell, toggle exe - Access context paths/files at prompt or spawned shell - Lock terminal after configurable idle timeout - Capture and show output of a program in help screen - Basic support for screen readers and braille displays ## Quickstart 1. [Install](https://github.com/jarun/nnn/wiki/Usage) `nnn` and the dependencies you need. 2. The desktop opener is default. Use `-e` to open text files in the terminal. Optionally [open detached](https://github.com/jarun/nnn/wiki/Basic-use-cases#detached-text). 3. Configure [`cd` on quit](https://github.com/jarun/nnn/wiki/Basic-use-cases#configure-cd-on-quit). 4. [Sync subshell `$PWD`](https://github.com/jarun/nnn/wiki/Basic-use-cases#sync-subshell-pwd) to `nnn`. 5. [Install plugins](https://github.com/jarun/nnn/tree/master/plugins#installation). 6. Use `-x` to sync selection to clipboard, show notis on `cp`, `mv`, `rm` and set xterm title. 7. For a CLI-only environment, set [`NNN_OPENER`](https://github.com/jarun/nnn/wiki/Usage#configuration) to [`nuke`](https://github.com/jarun/nnn/blob/master/plugins/nuke). Use option `-c`. 8. Bid `ls` goodbye! `alias ls='nnn -de'` :sunglasses: 9. Visit the [Live previews](https://github.com/jarun/nnn/wiki/Live-previews) and [Troubleshooting](https://github.com/jarun/nnn/wiki/Troubleshooting) Wiki pages. Don't memorize! Arrows, /, q suffice. Tab creates and/or cycles contexts. ? lists shortcuts. [![](https://i.imgur.com/TN3xYQz.jpg)](https://www.youtube.com/embed/-knZwdd1ScU) [![Wiki](https://img.shields.io/badge/RTFM-nnn%20Wiki-important?maxAge=2592000)](https://github.com/jarun/nnn/wiki) ## Videos - [nnn file manager on Termux (Android)](https://www.youtube.com/embed/AbaauM7gUJw) - [NNN File Manager](https://www.youtube.com/embed/1QXU4XSqXNo) - [This Week in Linux 114 - TuxDigital](https://www.youtube.com/watch?v=5W9ja0DQjSY&t=2059s) - [nnn file manager basics - Linux](https://www.youtube.com/embed/il2Fm-KJJfM) - [I'M GOING TO USE THE NNN FILE BROWSER! 😮](https://www.youtube.com/embed/U2n5aGqou9E) - [NNN: Is This Terminal File Manager As Good As People Say?](https://www.youtube.com/embed/KuJHo-aO_FA) - [nnn - A File Manager (By Uoou, again.)](https://www.youtube.com/embed/cnzuzcCPYsk) ## Elsewhere - [AddictiveTips](https://www.addictivetips.com/ubuntu-linux-tips/navigate-linux-filesystem/) - [ArchWiki](https://wiki.archlinux.org/index.php/Nnn) - [FOSSMint](https://www.fossmint.com/nnn-linux-terminal-file-browser/) - [gHacks Tech News](https://www.ghacks.net/2019/11/01/nnn-is-an-excellent-command-line-based-file-manager-for-linux-macos-and-bsds/) - Hacker News [[1](https://news.ycombinator.com/item?id=18520898)] [[2](https://news.ycombinator.com/item?id=19850656)] - [It's FOSS](https://itsfoss.com/nnn-file-browser-linux/) - [Linux Format Issue 265; Manage files with nnn](https://linuxformat.com/archives?issue=265) - LinuxLinks [[1](https://www.linuxlinks.com/nnn-fast-and-flexible-file-manager/)] [[2](https://www.linuxlinks.com/bestconsolefilemanagers/)] [[3](https://www.linuxlinks.com/excellent-system-tools-nnn-portable-terminal-file-manager/)] - [Linux Magazine; FOSSPicks](https://www.linux-magazine.com/Issues/2017/205/FOSSPicks/(offset)/15) - [Make Tech Easier](https://www.maketecheasier.com/nnn-file-manager-terminal/) - [Opensource.com](https://opensource.com/article/22/12/linux-file-manager-nnn) - [Open Source For You](https://www.opensourceforu.com/2019/12/nnn-this-feature-rich-terminal-file-manager-will-enhance-your-productivity/) - [PCLinuxOS Magazine Issue June 2021](https://pclosmag.com/html/Issues/202106/page08.html) - [Suckless Rocks](https://suckless.org/rocks/) - [Ubuntu Full Circle Magazine Issue 135; Review: nnn](https://fullcirclemagazine.org/issue-135/) - [Using and Administering Linux: Volume 2: Zero to SysAdmin: Advanced Topics](https://books.google.com/books?id=MqjDDwAAQBAJ&pg=PA32) - [Wikipedia](https://en.wikipedia.org/wiki/Nnn_(file_manager)) ## Developers - [Arun Prakash Jana](https://github.com/jarun) (Copyright © 2016-2024) - [0xACE](https://github.com/0xACE) - [Anna Arad](https://github.com/annagrram) - [KlzXS](https://github.com/KlzXS) - [Léo Villeveygoux](https://github.com/leovilok) - [Luuk van Baal](https://github.com/luukvbaal) - [NRK](https://codeberg.org/NRK) - [Sijmen J. Mulder](https://github.com/sjmulder) - and other contributors Visit the [Tracker](https://github.com/jarun/nnn/issues/1546) thread for a list of features in progress and anything up for grabs. Feel free to [discuss](https://github.com/jarun/nnn/discussions) new ideas or enhancement requests. nnn-5.0/misc/000077500000000000000000000000001466310014300131005ustar00rootroot00000000000000nnn-5.0/misc/CONTRIBUTING.md000066400000000000000000000024701466310014300153340ustar00rootroot00000000000000# Contributing Contributions to nnn are welcome! There's always an open issue with the current ToDo list, which contains the proposed features for the next release you can get your hands on. Any small changes or ideas should go in there, rather than in a separate issue. Before suggesting changes, please read a bit about [the design principles nnn follows](https://github.com/jarun/nnn/wiki/concepts#design), and make sure you aren't breaking any of them. Feel free to join the [Discussions](https://github.com/jarun/nnn/discussions). We highly recommended a discussion before raising a PR. ## Coding standard `nnn` follows the Linux kernel coding style closely. The C source code uses TABs and the plugins use 4 spaces for indentation. - Code changes should not break the patch framework. Please run `make checkpatches` to ensure. - Run `make shellcheck` if adding/modifying plugins. CI runs patch framework sanity test and `shellcheck`. Please watch out for any failures after raising the PR. ## Resources The [wiki](https://github.com/jarun/nnn/wiki/Developer-guides) has some resources for developers you might be interested in: building, debugging... ## Communication * [Gitter chat](https://gitter.im/jarun/nnn) * [GitHub team](https://github.com/nnn-devs) (if you plan on contributing regularly, ask for an invitation). nnn-5.0/misc/auto-completion/000077500000000000000000000000001466310014300162175ustar00rootroot00000000000000nnn-5.0/misc/auto-completion/bash/000077500000000000000000000000001466310014300171345ustar00rootroot00000000000000nnn-5.0/misc/auto-completion/bash/nnn-completion.bash000066400000000000000000000031401466310014300227310ustar00rootroot00000000000000# # Rudimentary Bash completion definition for nnn. # # Author: # Arun Prakash Jana # _nnn () { COMPREPLY=() local IFS=$'\n' local cur=$2 prev=$3 local -a opts opts=( -a -A -b -B -c -C -d -D -e -E -f -g -H -i -J -K -l -n -o -p -P -Q -r -R -s -S -t -T -u -U -V -x -0 -h ) if [[ $prev == -b ]]; then local bookmarks=$(echo $NNN_BMS | awk -F: -v RS=\; '{print $1}') COMPREPLY=( $(compgen -W "$bookmarks" -- "$cur") ) elif [[ $prev == -l ]]; then return 1 elif [[ $prev == -p ]]; then COMPREPLY=( $(compgen -f -d -- "$cur") ) elif [[ $prev == -P ]]; then local plugins=$(echo $NNN_PLUG | awk -F: -v RS=\; '{print $1}') COMPREPLY=( $(compgen -W "$plugins" -- "$cur") ) elif [[ $prev == -s ]]; then local sessions_dir=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/sessions COMPREPLY=( $(cd "$sessions_dir" && compgen -f -d -- "$cur") ) elif [[ $prev == -t ]]; then return 1 elif [[ $prev == -T ]]; then local keys=$(echo "a d e r s t v" | awk -v RS=' ' '{print $0}') COMPREPLY=( $(compgen -W "$keys" -- "$cur") ) elif [[ $cur == -* ]]; then COMPREPLY=( $(compgen -W "${opts[*]}" -- "$cur") ) else COMPREPLY=( $(compgen -f -d -- "$cur") ) fi } complete -o filenames -F _nnn nnn nnn-5.0/misc/auto-completion/fish/000077500000000000000000000000001466310014300171505ustar00rootroot00000000000000nnn-5.0/misc/auto-completion/fish/nnn.fish000066400000000000000000000043171466310014300206210ustar00rootroot00000000000000# # Fish completion definition for nnn. # # Author: # Arun Prakash Jana # if test -n "$XDG_CONFIG_HOME" set sessions_dir $XDG_CONFIG_HOME/.config/nnn/sessions else set sessions_dir $HOME/.config/nnn/sessions end complete -c nnn -s a -d 'auto-create NNN_FIFO' complete -c nnn -s A -d 'disable dir auto-enter' complete -c nnn -s b -r -d 'bookmark key to open' -x -a '(echo $NNN_BMS | awk -F: -v RS=\; \'{print $1"\t"$2}\')' complete -c nnn -s B -d 'use bsdtar for archives' complete -c nnn -s c -d 'cli-only opener' complete -c nnn -s C -d 'color by context' complete -c nnn -s d -d 'start in detail mode' complete -c nnn -s D -d 'dirs in context color' complete -c nnn -s e -d 'open text files in $VISUAL/$EDITOR/vi' complete -c nnn -s E -d 'use EDITOR for undetached edits' complete -c nnn -s f -d 'use readline history file' complete -c nnn -s g -d 'regex filters' complete -c nnn -s H -d 'show hidden files' complete -c nnn -s i -d 'show current file info' complete -c nnn -s J -d 'no auto-advance on selection' complete -c nnn -s K -d 'detect key collision and exit' complete -c nnn -s l -r -d 'lines to move per scroll' complete -c nnn -s n -d 'start in type-to-nav mode' complete -c nnn -s o -d 'open files only on Enter' complete -c nnn -s p -r -d 'copy selection to file' -a '-\tstdout' complete -c nnn -s P -r -d 'plugin key to run' -x -a '(echo $NNN_PLUG | awk -F: -v RS=\; \'{print $1"\t"$2}\')' complete -c nnn -s Q -d 'disable quit confirmation' complete -c nnn -s r -d 'show cp, mv progress (Linux-only)' complete -c nnn -s R -d 'disable rollover at edges' complete -c nnn -s s -r -d 'load session by name' -x -a '@\t"last session" (ls $sessions_dir)' complete -c nnn -s S -d 'persistent session' complete -c nnn -s t -r -d 'timeout in seconds to lock' complete -c nnn -s T -r -d 'a d e r s t v' complete -c nnn -s u -d 'use selection (no prompt)' complete -c nnn -s U -d 'show user and group' complete -c nnn -s V -d 'show program version and exit' complete -c nnn -s x -d 'notis, sel to system clipboard, xterm title' complete -c nnn -s 0 -d 'use null separator in picker mode' complete -c nnn -s h -d 'show program help' nnn-5.0/misc/auto-completion/zsh/000077500000000000000000000000001466310014300170235ustar00rootroot00000000000000nnn-5.0/misc/auto-completion/zsh/_nnn000066400000000000000000000033561466310014300177050ustar00rootroot00000000000000#compdef nnn # # Completion definition for nnn. # # Author: # Arun Prakash Jana # setopt localoptions noshwordsplit noksharrays local -a args args=( '(-a)-a[auto-create NNN_FIFO]' '(-A)-A[disable dir auto-enter]' '(-b)-b[bookmark key to open]:key char' '(-B)-B[use bsdtar for archives]' '(-c)-c[cli-only opener]' '(-C)-C[color by context]' '(-d)-d[start in detail mode]' '(-D)-D[dirs in context color]' '(-e)-e[open text files in $VISUAL/$EDITOR/vi]' '(-E)-E[use EDITOR for undetached edits]' '(-f)-f[use readline history file]' '(-F)-F[fifo notification mode]:mode:(( 0\:"notify as previewer" 1\:"notify as explorer" ))' '(-g)-g[regex filters]' '(-H)-H[show hidden files]' '(-i)-i[show current file info]' '(-J)-J[no auto-advance on selection]' '(-K)-K[detect key collision and exit]' '(-l)-l[lines to move per scroll]:val' '(-n)-n[start in type-to-nav mode]' '(-o)-o[open files only on Enter]' '(-p)-p[copy selection to file]:file name' '(-P)-P[plugin key to run]:key char' '(-Q)-Q[disable quit confirmation]' '(-r)-r[show cp, mv progress (Linux-only)]' '(-R)-R[disable rollover at edges]' '(-s)-s[load session]:session name' '(-S)-S[persistent session]' '(-t)-t[timeout to lock]:seconds' '(-T)-T[sort order]:key:(( a\:"apparent disk usasge" d\:"disk usage" e\:"extension" r\:"reverse" s\:"size" t\:"time" v\:"version" ))' '(-u)-u[use selection (no prompt)]' '(-U)-U[show user and group]' '(-V)-V[show program version and exit]' '(-x)-x[notis, sel to system clipboard, xterm title]' '(-0)-0[use null separator in picker mode]' '(-h)-h[show program help]' '*:filename:_files' ) _arguments -S -s $args nnn-5.0/misc/desktop/000077500000000000000000000000001466310014300145515ustar00rootroot00000000000000nnn-5.0/misc/desktop/nnn.desktop000066400000000000000000000003501466310014300167330ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=nnn Comment=Terminal file manager Exec=nnn Terminal=true Icon=nnn MimeType=inode/directory Categories=System;FileTools;FileManager;ConsoleOnly Keywords=File;Manager;Management;Explorer;Launcher nnn-5.0/misc/haiku/000077500000000000000000000000001466310014300142015ustar00rootroot00000000000000nnn-5.0/misc/haiku/Makefile000066400000000000000000000143511466310014300156450ustar00rootroot00000000000000VERSION = $(shell grep -m1 VERSION $(SRC) | cut -f 2 -d'"') PREFIX ?= /boot/system/non-packaged MANPREFIX ?= $(PREFIX)/documentation/man STRIP ?= strip PKG_CONFIG ?= pkg-config INSTALL ?= install CP ?= cp CFLAGS_OPTIMIZATION ?= -O3 O_DEBUG := 0 # debug binary O_NORL := 0 # no readline support O_PCRE := 0 # link with PCRE library O_NOLC := 0 # no locale support O_NOMOUSE := 0 # no mouse support O_NOBATCH := 0 # no built-in batch renamer O_NOFIFO := 0 # no FIFO previewer support O_CTX8 := 0 # enable 8 contexts O_ICONS := 0 # support icons-in-terminal O_NERD := 0 # support icons-nerdfont O_QSORT := 0 # use Alexey Tourbin's QSORT implementation O_BENCH := 0 # benchmark mode (stops at first user input) O_NOSSN := 0 # disable session support O_NOUG := 0 # disable user, group name in status bar O_NOX11 := 0 # disable X11 integration O_MATCHFLTR := 0 # allow filters without matches # User patches O_GITSTATUS := 0 # add git status to detail view O_NAMEFIRST := 0 # print file name first, add uid and guid to detail view ifeq ($(strip $(O_GITSTATUS)),1) LDLIBS += -lgit2 endif # convert targets to flags for backwards compatibility ifneq ($(filter debug,$(MAKECMDGOALS)),) O_DEBUG := 1 endif ifneq ($(filter norl,$(MAKECMDGOALS)),) O_NORL := 1 endif ifneq ($(filter nolc,$(MAKECMDGOALS)),) O_NORL := 1 O_NOLC := 1 endif ifeq ($(strip $(O_DEBUG)),1) CPPFLAGS += -DDEBUG CFLAGS += -g LDLIBS += -lrt endif ifeq ($(strip $(O_NORL)),1) CPPFLAGS += -DNORL else ifeq ($(strip $(O_STATIC)),1) CPPFLAGS += -DNORL else LDLIBS += -lreadline endif ifeq ($(strip $(O_PCRE)),1) CPPFLAGS += -DPCRE LDLIBS += -lpcre endif ifeq ($(strip $(O_NOLC)),1) ifeq ($(strip $(O_ICONS)),1) $(info *** Ignoring O_NOLC since O_ICONS is set ***) else ifeq ($(strip $(O_NERD)),1) $(info *** Ignoring O_NOLC since O_NERD is set ***) else CPPFLAGS += -DNOLC endif endif ifeq ($(strip $(O_NOMOUSE)),1) CPPFLAGS += -DNOMOUSE endif ifeq ($(strip $(O_NOBATCH)),1) CPPFLAGS += -DNOBATCH endif ifeq ($(strip $(O_NOFIFO)),1) CPPFLAGS += -DNOFIFO endif ifeq ($(strip $(O_CTX8)),1) CPPFLAGS += -DCTX8 endif ifeq ($(strip $(O_ICONS)),1) CPPFLAGS += -DICONS endif ifeq ($(strip $(O_NERD)),1) CPPFLAGS += -DNERD endif ifeq ($(strip $(O_QSORT)),1) CPPFLAGS += -DTOURBIN_QSORT endif ifeq ($(strip $(O_BENCH)),1) CPPFLAGS += -DBENCH endif ifeq ($(strip $(O_NOSSN)),1) CPPFLAGS += -DNOSSN endif ifeq ($(strip $(O_NOUG)),1) CPPFLAGS += -DNOUG endif ifeq ($(strip $(O_NOX11)),1) CPPFLAGS += -DNOX11 endif ifeq ($(strip $(O_MATCHFLTR)),1) CPPFLAGS += -DMATCHFLTR endif ifeq ($(shell $(PKG_CONFIG) ncursesw && echo 1),1) CFLAGS_CURSES ?= $(shell $(PKG_CONFIG) --cflags ncursesw) LDLIBS_CURSES ?= $(shell $(PKG_CONFIG) --libs ncursesw) else ifeq ($(shell $(PKG_CONFIG) ncurses && echo 1),1) CFLAGS_CURSES ?= $(shell $(PKG_CONFIG) --cflags ncurses) LDLIBS_CURSES ?= $(shell $(PKG_CONFIG) --libs ncurses) else LDLIBS_CURSES ?= -lncurses endif ifeq ($(shell uname -s), Haiku) LDLIBS_HAIKU ?= -lstdc++ -lgnu -lbe SRC_HAIKU ?= misc/haiku/nm.cpp OBJS_HAIKU ?= misc/haiku/nm.o endif CFLAGS += -std=c11 -Wall -Wextra -Wshadow CFLAGS += $(CFLAGS_OPTIMIZATION) CFLAGS += $(CFLAGS_CURSES) LDLIBS += $(LDLIBS_CURSES) -lpthread $(LDLIBS_HAIKU) # static compilation needs libgpm development package ifeq ($(strip $(O_STATIC)),1) LDFLAGS += -static LDLIBS += -lgpm endif DISTFILES = src nnn.1 Makefile README.md LICENSE SRC = src/nnn.c HEADERS = src/nnn.h BIN = nnn OBJS := nnn.o $(OBJS_HAIKU) GITSTATUS = patches/gitstatus NAMEFIRST = patches/namefirst all: $(BIN) ifeq ($(shell uname -s), Haiku) $(OBJS_HAIKU): $(SRC_HAIKU) $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $< endif nnn.o: $(SRC) $(HEADERS) $(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $< $(BIN): $(OBJS) @$(MAKE) --silent prepatch $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) @$(MAKE) --silent postpatch # targets for backwards compatibility debug: $(BIN) norl: $(BIN) nolc: $(BIN) install: all $(INSTALL) -m 0755 -d $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 0755 $(BIN) $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 0755 -d $(DESTDIR)$(MANPREFIX)/man1 $(INSTALL) -m 0644 $(BIN).1 $(DESTDIR)$(MANPREFIX)/man1 uninstall: $(RM) $(DESTDIR)$(PREFIX)/bin/$(BIN) $(RM) $(DESTDIR)$(MANPREFIX)/man1/$(BIN).1 strip: $(BIN) $(STRIP) $^ static: # regular static binary make O_STATIC=1 strip mv $(BIN) $(BIN)-static dist: mkdir -p nnn-$(VERSION) $(CP) -r $(DISTFILES) nnn-$(VERSION) mkdir -p nnn-$(VERSION)/misc $(CP) -r misc/haiku nnn-$(VERSION)/misc tar -cf - nnn-$(VERSION) | gzip > nnn-$(VERSION).tar.gz $(RM) -r nnn-$(VERSION) sign: git archive -o nnn-$(VERSION).tar.gz --format tar.gz --prefix=nnn-$(VERSION)/ v$(VERSION) gpg --detach-sign --yes nnn-$(VERSION).tar.gz rm -f nnn-$(VERSION).tar.gz upload-local: sign static $(eval ID=$(shell curl -s 'https://api.github.com/repos/jarun/nnn/releases/tags/v$(VERSION)' | jq .id)) # upload sign file curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=nnn-$(VERSION).tar.gz.sig' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/pgp-signature' \ --upload-file nnn-$(VERSION).tar.gz.sig tar -zcf $(BIN)-static-$(VERSION).x86_64.tar.gz $(BIN)-static # upload static binary curl -XPOST 'https://uploads.github.com/repos/jarun/nnn/releases/$(ID)/assets?name=$(BIN)-static-$(VERSION).x86_64.tar.gz' \ -H 'Authorization: token $(NNN_SIG_UPLOAD_TOKEN)' -H 'Content-Type: application/x-sharedlib' \ --upload-file $(BIN)-static-$(VERSION).x86_64.tar.gz clean: $(RM) -f $(BIN) nnn-$(VERSION).tar.gz *.sig $(BIN)-static $(BIN)-static-$(VERSION).x86_64.tar.gz prepatch: ifeq ($(strip $(O_NAMEFIRST)),1) patch --forward --strip=1 --input=$(NAMEFIRST)/mainline.diff ifeq ($(strip $(O_GITSTATUS)),1) patch --forward --strip=1 --input=$(GITSTATUS)/namefirst.diff endif else ifeq ($(strip $(O_GITSTATUS)),1) patch --forward --strip=1 --input=$(GITSTATUS)/mainline.diff endif postpatch: ifeq ($(strip $(O_NAMEFIRST)),1) ifeq ($(strip $(O_GITSTATUS)),1) patch --reverse --strip=1 --input=$(GITSTATUS)/namefirst.diff endif patch --reverse --strip=1 --input=$(NAMEFIRST)/mainline.diff else ifeq ($(strip $(O_GITSTATUS)),1) patch --reverse --strip=1 --input=$(GITSTATUS)/mainline.diff endif skip: ; .PHONY: all install uninstall strip static dist sign upload-local clean nnn-5.0/misc/haiku/haiku_interop.h000066400000000000000000000004661466310014300172210ustar00rootroot00000000000000#ifdef __cplusplus extern "C" { #endif typedef struct haiku_nm_t *haiku_nm_h; haiku_nm_h haiku_init_nm(); void haiku_close_nm(haiku_nm_h hnd); int haiku_watch_dir(haiku_nm_h hnd, const char *path); int haiku_stop_watch(haiku_nm_h hnd); int haiku_is_update_needed(haiku_nm_h hnd); #ifdef __cplusplus } #endif nnn-5.0/misc/haiku/nm.cpp000066400000000000000000000031501466310014300153160ustar00rootroot00000000000000#include #include #include #include #include "haiku_interop.h" filter_result dir_mon_flt(BMessage *message, BHandler **hnd, BMessageFilter *fltr) { (void) hnd; (void) fltr; if (message->what == B_NODE_MONITOR) { int32 val; message->FindInt32("opcode", &val); switch (val) { case B_ENTRY_CREATED: case B_ENTRY_MOVED: case B_ENTRY_REMOVED: return B_DISPATCH_MESSAGE; } } return B_SKIP_MESSAGE; } class DirectoryListener : public BLooper { public: bool recv_reset() { Lock(); bool val = _ev_on; _ev_on = false; Unlock(); return val; } private: void MessageReceived(BMessage * message) override { Lock(); _ev_on = true; Unlock(); BLooper::MessageReceived(message); } bool _ev_on = false; }; struct haiku_nm_t { haiku_nm_t() { dl = new DirectoryListener(); flt = new BMessageFilter(B_PROGRAMMED_DELIVERY, B_LOCAL_SOURCE, dir_mon_flt); dl->AddCommonFilter(flt); dl->Run(); } DirectoryListener *dl; BMessageFilter *flt; node_ref nr; }; haiku_nm_h haiku_init_nm() { return new haiku_nm_t(); } void haiku_close_nm(haiku_nm_h hnd) { delete hnd->flt; // This is the way of deleting a BLooper hnd->dl->PostMessage(B_QUIT_REQUESTED); delete hnd; } int haiku_watch_dir(haiku_nm_h hnd, const char *path) { BDirectory dir(path); dir.GetNodeRef(&(hnd->nr)); return watch_node(&(hnd->nr), B_WATCH_DIRECTORY, nullptr, hnd->dl); } int haiku_stop_watch(haiku_nm_h hnd) { return watch_node(&(hnd->nr), B_STOP_WATCHING, nullptr, hnd->dl); } int haiku_is_update_needed(haiku_nm_h hnd) { return hnd->dl->recv_reset(); } nnn-5.0/misc/haiku/nnn-master.recipe000066400000000000000000000031301466310014300174510ustar00rootroot00000000000000SUMMARY="A blazing-fast lightweight terminal file manager" DESCRIPTION="nnn is a full-featured terminal file manager. It's tiny and \ nearly 0-config with an incredible performance. nnn is also a du analyzer, an app launcher, a batch renamer and a file picker. \ The plugin repository has tons of plugins and documentation to extend the \ capabilities further. You can plug new functionality and play with a \ custom keybind instantly. There's an independent (neo)vim plugin. It runs smoothly on the Raspberry Pi, Termux on Android, Linux, macOS, BSD, \ Cygwin, WSL, Haiku and works seamlessly with DEs and GUI utilities. Visit the Wiki for concepts, program usage, how-tos and troubleshooting." HOMEPAGE="https://github.com/jarun/nnn" COPYRIGHT="2016-2024 Arun Prakash Jana" LICENSE="BSD (2-clause)" REVISION="1" SOURCE_URI="git://github.com/jarun/nnn.git" ARCHITECTURES="!x86_gcc2 x86_64" SECONDARY_ARCHITECTURES="x86" PROVIDES=" nnn$secondaryArchSuffix = $portVersion cmd:nnn = $portVersion " REQUIRES=" haiku$secondaryArchSuffix file$secondaryArchSuffix lib:libncurses$secondaryArchSuffix lib:libreadline$secondaryArchSuffix " BUILD_REQUIRES=" haiku${secondaryArchSuffix}_devel devel:libncurses$secondaryArchSuffix devel:libreadline$secondaryArchSuffix " BUILD_PREREQUIRES=" cmd:g++$secondaryArchSuffix cmd:gcc$secondaryArchSuffix cmd:install cmd:ld$secondaryArchSuffix cmd:make cmd:pkg_config$secondaryArchSuffix " BUILD() { make -f misc/haiku/Makefile $jobArgs } INSTALL() { make -f misc/haiku/Makefile install PREFIX=$prefix addResourcesToBinaries misc/haiku/nnn.rdef $prefix/bin/nnn } nnn-5.0/misc/haiku/nnn.rdef000066400000000000000000000202661466310014300156420ustar00rootroot00000000000000 // How to apply this resource definition file manually (Haiku only): // First, make sure that nnn is compiled. // Next, run "rc nnn.rdef -o nnn.rsrc" to compile this resource file. // Then, run "xres -o nnn nnn.rsrc" to apply the resource file to nnn. // Finally, run "mimeset -f nnn" to refresh Haiku's MIME database. resource app_signature "application/x-vnd.Jarun-nnn"; resource app_version { major = 5, middle = 0, minor = 0, variety = B_APPV_FINAL, internal = 0, short_info = "nnn", long_info = "A blazing-fast lightweight terminal file manager" }; resource app_flags B_MULTIPLE_LAUNCH | B_BACKGROUND_APP | B_ARGV_ONLY; resource file_types message { "types" = "application/x-vnd.Be-directory" }; resource vector_icon { $"6E6369660805FF032E343602011202B8E0C339BAC3B985CFB8B7E549F5FF4994" $"000000FFFF0000020112023745BA3659FABA87063B846942D03049EBF80000FF" $"FF0000020112033BE54D39FE49B857E23A493A4A91FF489000000000AF0000FF" $"00FF020112022D299A3B3A49BCE2FF2ED4134A65A94C096A0000FFFF00000201" $"1202365425358147B6C63037AFB14669664AB9F30000FFFF0000020112023673" $"5235AC4EB7FB7138A81D481E6A48C2C50000FFFF00000F0241BE60B360BE60B3" $"60BE5FB360BE3EB364BE46B362BE2AB369BE17B371BE1FB36DBE03B379BDF3B3" $"85BDFCB37EBDE3B38EBDCEB3A0BDD9B398BDB8B3B1BD8BB3DDBDA4B3C5BD58B4" $"0EBCCDB4A0BD17B450BC93B4DBBC36B539BC61B50DBC35B537BC32B536BC34B5" $"37BBE6B50ABB0FB48BBB7EB4CEBA9EB448B9E8B3DDBA36B40AB9C1B3C5B987B3" $"A5B9A0B3B3B97AB39FB967B394B971B399B961B391B954B38CB95DB390B94FB3" $"89B942B385B94CB388B93FB382B92FB37EB939B381B926B37DB8FFB379B900B3" $"79B8E9B379B87FB3A4B8B1B37DB84CB3C8B82FB418B838B3FBB81FB453B826B4" $"89B825B466B827B4CDB841B5F9B82FB534B848B656B854B6F9B84EB6B1B85AB7" $"41B85CB776B85CB782B85CB75BB83AB7A7B860B772B814B7DBB767B88FB7CEB8" $"29B6E7B90DB65CB9D1B694B955B626BA4CB62ABB5AB62CBAB9B629BC54B606BC" $"9DB634BC1BB5CDBD3EB4B1BEF5B597BDA1B445BF96B3B0C089B3DDC033B31AC1" $"A4B318C3DAB2D2C2D1B371C536B61FC639B4ABC60FB641C680B698C6F2B65CC6" $"CFB698C6F2B69AC6F2B69AC6F2B71EC742B838C6F9B7BDC733B8B2C6BEB96CC5" $"E2B919C65DB99BC59FBA11C4C4B9D8C530BA78C400BB14C34ABAB4C3A1BB75C2" $"F2BD20C209BC0BC29ABDDEC1A6BED6C108BE5244BED6C121BED8C151BED9C139" $"BECCC24CBE29C3E9BE9AC307BDC3C4B6BC1DC63ABD84C4F7BB43C700BA57C7FA" $"BAB9C781B9F5C872B99DC966B9BCC8EBB961CA48B95ECBBBB955CB0830CBFDB9" $"65CC7FB962CC3DBFC3CC7F60CC7FC621CC7FCC7ECBDECC7ACA9BCC7CCB3CCC6F" $"C6D6CA63BE51CB9AC1F5C98736C731B8ADC899BA16C5F2B76EC2C4B648C477B6" $"A1C212B625BFF7B63643B625C013B634C04A28C02FB631C027B5E2BFE0B546C0" $"04B593BFAFB4D8BF5EB436BF81B47BBF4DB415BF30B3E0BF3FB3F8BF28B3D3BF" $"18B3BBBF22B3C9BF11B3B4BEFFB39DBF0CB3ABBEF8B398BEE2B385BEF1B390BE" $"DAB380BEC5B375BED1B37ABEB9B36EBE96B364BE96B364BE96B364BE95B364BE" $"95B364BE93B364BE93B364BE93B364BE93B364BE60B360BE60B360BE60B36002" $"23BCA6C6D2BCA6C6D2BE0FC58CBEDFC443BE70C523BF5BC34EBFA3C159BF97C2" $"6DBFAAC0AEBF80BFC2BF99BFDABF7ABFBBBF34BFE33FBFC7BEA9C048BCC3C154" $"BDD9C0C2BA90C277B95BC467BA2FC2CFB926C4CCB8C5C56FB8E2C542B83EC636" $"B702C644B780C68FB6CEC624B6A8C5B6B6B4C5FDB6A1C587B660C581B69CC584" $"B510C56EB3DDC3A7B426C4C2B3ACC2EDB465C0E9B3E0C1E1B481C0B1B55ABF67" $"B4F0C005B642BE10B6C7BCE0B689BD92B6F5BC5DB6F6BB5AB6F5BC55B6F7BA1A" $"B7F5B921B6F5BA1EB8CDB84DB928B776B928B7CEB928B757B90CB5E9B91BB6A4" $"B8EBB45DB8FFB446B8EBB446B907B446BAA6B53A31B4B4BB85B5C0BC54B62FBC" $"47B62FBC6128BD60B52BBCDBB5BABDF2B491BE6BB42BBE5EB428BE78B42EBF27" $"B59ABEC3B4C2BF5DB613BFCBB704BF95B68DBFDEB703C005B702BFF2B702C0F0" $"B6F1C29CB70FC229B6F9C42AB761C6A0B93DC57CB818C7F0BA8FC9A0BE92C8CC" $"BC13CACFC21CCBAECA9DCBA3C6F5CBAFCAFBCBB15ECBB0CB56C5D85EBA295EC0" $"015EBA21CB0FBA61C99A32CA68BA97C8CBBCA6C6D2BAF4C858BCA6C6D20215C8" $"1C5EC81C5EC80FCB23C7DAC9A7C805CA9DC79EC84BC6E7C4FBC747C6A251C353" $"C5B5C054C61BC1ACC54FBEF9C4A1BD7F4DBDEFC457BD12C3C3BC5FC40EBCB4C3" $"79BC0DC2D8BB87C32B36C285BB4BC1C8BAF0C22BBB19C195BADDC120BAC444BA" $"D2C13EBADEC177BB1CC15CBAF6C19EBB66C1B9BC18C1B5BBBAC1BCBC74C184BD" $"41C1ABBCD8C15CBDA9C0C1BE85C11DBE17C0683EBF5CBFD2BFF2BF64BF69BFCD" $"BF80BFC2BF7EBFBFBF99BFDABFA3C159BFAAC0AEBF97C26DBEE1C443BF5BC34E" $"BE70C523BCA6C6D2BE0FC58CBAF4C858BA61C99ABA97C8CB32CA68BA295EBA21" $"CB0FBECF5EC81C5EC3755EC81C5E021CBE6BB42BBE6BB42BBE5EB428BD60B52B" $"BDF2B491BCDBB5BABC54B62FBC6128BC47B62FBAA6B53ABB85B5C031B4B4B8FF" $"B446B907B446B8EBB446B90CB5E9B8EBB45DB91BB6A4B928B776B928B757B928" $"B7CEB7F5B921B8CDB84DB6F5BA1EB6F6BB5AB6F7BA1AB6F5BC55B6C7BCE0B6F5" $"BC5DB689BD92B55ABF67B642BE10B4F0C005B465C0E9B481C0B1B3E0C1E1B3DD" $"C3A7B3ACC2EEB426C4C2B660C581B510C56EB680C583B697C58AB68FC583B74F" $"C477B90545B815C32CB9FCC041BC10BD70BB07BEB8BC94BCCCBD99BBBBBD18BC" $"36BE19BB41BF0DBAA0BE96BADFBF49BA82BFBBBA5FBF82BA6BBFF3BA52C05CBA" $"59C028BA50C090BA6143BA94C0C2BA75C103BA9FC120BAC4C110BAB6C125BAC5" $"C130BAC7C12ABAC6C0BBB986BFCEB704C043B846BFCDB704BFCBB704BFCDB704" $"BF95B68BBF27B59ABF5DB613BEC4B4C2BE6BB42BBE78B42EBE6BB42B0207BFCE" $"B704BFCEB704C063B85EC18FBB10C0F9B9B7C1C2BB1DC2BCBB74C28DBB61C6B9" $"BBACC6A0B93DCB98BF33C57CB818C29CB70FC42AB761C229B6F9C005B702C0F0" $"B6F1BFF3B702BFCEB704BFDFB703BFCEB7040207BCA6C6D2BCA6C6D2474ACB64" $"C69E5149CB91C805CBAECA9DCBABC963CBAFCAFBCBB15ECBB0CB56C5D85EBA29" $"5EC0015EBA21CB0FBA61C99A32CA68BA97C8CBBCA6C6D2BAF4C858BCA6C6D206" $"04BFB67CC579B67CC579B7C1C49BB900C2AFB8F9C22FB906C32EB6F5C5CDB841" $"C6ABB5AA4DB67CC5790211BD5BC297BD5BC297BDC5C279BEE5C244BE49C25EBF" $"83C22BC0E8C21FC02EC21FC19DC21FC2AAC26CC234C238C323C29CC3C4C33EC3" $"81C2E1C40BC399C45BC485C43EC405C478C502C487C626C487C58DC487C7BCC4" $"87CAE7C487C9514ACAE746CAE7C2EFCAE746C96B46C67346C7EF46C5A0C1DDC4" $"ABC20BC508C1AFC44FC0C2C421C151C421C097C421C040C424C06D4BC018C428" $"BFBFC434BFEEC42CBFBFC66FBFBFCAE7BFBFC8ACBEF3CAE7BD5BCAE7BE27CAE7" $"BD5BC821BD5BC297BD5BC55BBD5BC2970211BE27C363BE27C363BE91C345BFB1" $"C310BF16C32AC050C2F7C1B4C2EBC0FAC2EBC269C2EBC376C338C300C304C3EF" $"C368C490C40AC44DC3ADC4D7C465C527C551C50AC4D1C544C5CE4EC6F24EC659" $"4EC8454ECAE74EC9964CCAE748CAE74ACAE748C9C548C77F48C8A148C6B9C2A9" $"C5D4C2D7C62BC27BC57FC18EC553C21DC553C163C553C10CC557C1394EC0E4C5" $"5A42C565C0BAC55E42C73A42CAE742C91140CAE73CCAE73ECAE73CC866BE27C3" $"63BE27C5E4BE27C3630206B669C28FB669C28FB669C25DB613C207B642C21EB5" $"8645B4F5C338B507C245B4F1C374B4F7C3BEB4F3C3B2B503C3DAB59DC370B507" $"C3D7B632C309B669C28FB669C2CAB669C28F0207BAE6BC94BAE6BC94BC1EBB5D" $"BD8EB8BBBD8EB948BD8EB89BBD1AB854BD5AB86DBBF7B7E7B988B975BA7DB85F" $"B902BA0EB763BDFCB82DBAE5B751BE42B7F5BEBCB6C13FB92ABE1DBAE6BC94B9" $"EDBD8CBAE6BC940207B9EABBF2B9EABBF2BA4CBB90BABFBABCBABFBAE9BABFBA" $"B2BA9BBA9CBAAEBAA3BA40BA79B97BBAF7B9C8BA9EB952BB27B85BBCCCB85BBC" $"B7B85BBCE3B8FCBC9EB89BBCCFB95EBC6CB9EABBF2B99CBC3FB9EABBF2020FC0" $"E42CC0E42CC0BFB7C942B801C09DB7E0C07BB825C096B86CC080B84DC0ADB889" $"C0F8B893C0D4B899C2D4B862C515B9BAC413B8E0C617BA94C789BD20C6D6BBD2" $"C7CFBDA0C8EFC1C5C869BF62C977C428CA19CA85C9F2C73ACA1BCAABCA4FCADD" $"CA30CACCCA6FCAEDCAB5CAD7CA97CAEBCAD4CAC4CAE5CA7DCAE7CAA1CABDC722" $"59C19ACA3FC408C92DBF2BC83CBCC0C89FBD76C786BB6DC599B920C6BEBA17C4" $"73B827C0E42CC2EEB794C0E42C020AC70ABC69C70ABC69C1BB3BC864C462C353" $"50C8CFC440C982C3CEC930C411C983C3CDC984C3CCC983C3CCC985C3CAC987C3" $"C7C986C3C9C9D4C384CA37C2CDCA13C32DCA59C272CA1945CA19C220C9B33FC8" $"5ABC98C9B33DC85ABC98C859BC98C859BC98C7F2BC76C70ABC69C785BC69C70A" $"BC690220C6E2C242C6E2C242C740C242C7A5C216C782C234C7CCC1F8C7DFC1AB" $"C7DFC1D6C7DFC141C6C8C10CC782C10CC690C10CC622C10CC659C10CC622C0A1" $"C622BFC8C622C035C659BFC8C6C8BFC8C690BFC8C705BFC8C75FBFA5C738BFBD" $"C785BF8DC798BF35C798BF67C798BEE3C6EBBEB9C75FBEB9C6ADBEB9C635BED9" $"C66FBEC3C5FABEEEC587BF1BC5BFBF03C555BEB4C4F0BDE5C523BE4CC532BDBA" $"C5E5BD79C584BD98C64ABD5AC709BD4AC6ABBD4AC774BD4AC814BD6CC7CDBD54" $"C85EBD84C8C6BDCEC89ABDA5C8F1BDF5C922BE5CC911BE24C935BE91C93DBF04" $"C93DBECAC93DBF3EC916BFA5C931BF73C8FEBFD4C89AC038C8D4C005C8EEC05D" $"58C0D7C92CC092C975C117C988C1B0C988C160C988C1FAC966C27CC97DC23EC9" $"4EC2BBC8F1C321C927C2F1C8BCC34DC821C38BC878C370C7CCC3A5C6E7C3B4C7" $"63C3B4C68DC3B4C5C5C399C62EC3AAC562C384C4D6C348C511C36AC4F5C2D4C5" $"34C1EAC515C25EC58CC210C616C236C5D7C229C653C23EC6E2C242C698C242C6" $"E2C2420F0A000100000A010101000A020102000A030103000A040104000A0501" $"05000A060106000A010107000A000108000A000109000A07010A000A00010B00" $"0A00010C000A01010D000A00010E00" }; nnn-5.0/misc/logo/000077500000000000000000000000001466310014300140405ustar00rootroot00000000000000nnn-5.0/misc/logo/logo-128x128.png000066400000000000000000000234341466310014300164470ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYsetEXtSoftwarewww.inkscape.org<tEXtTitlen³ logob?tEXtAuthorLéo Villeveygoux IDATxy|eO$  @@T]PUAe<@RE  x,( ( (@t螞L5O?=UO=SO?q,CDjHcю @D.gEdJOsyGD"rʸߟI9_E_EDFsן(#D( 阀MDrK."$D!"JD`(" *x#?ɟCAADZ@DB]GuDiܸ1{t~Wn<LWJE6jT1.İ .Ky~ +B~A Fu1> 6K/eСw\h\ ܤJ^T H*K%K2}LJL0@rr2ujצNjꦦO rL SV-|ÇF\vH " .[0)**[Wiii|hZ|B_}ݿ&t8=2͛7w_ ])e\7/'*L3`ʪƍo#99ܼ̝DŽI91IgGsu>KJYbR@D 8d|nn. ۽{T"99fMttt|rT}u0f3`RjVeTy{Y~~> 7&vs+hղE: }ȇ}~~/W_z c+Ts'Dd80^VXX]CuUi:tvK\R a ޴{NJUqMf08}{Gyqq1WLPB絗_rgU Qv{drwy5Xѝdjb~1wDRw<=\b ,Mpeuo.n]ϲЫgOV^MV~@ҩct+@=_%UEd<0X^r̷ټŹJȃ]ǃ0Z ;{:/((0=h%lS=J-|׮];l82 3:4fMrǭitT{eeBj:U});;7lkJ;'Q0lJc 72e6'6~ Mc'm*7_סn^Ƙenݺ?|]vo@v |YMPʔN?=C)4QRR¸w_6TDʲ1"en趛o3CP~vd۶x4>lnEEEL4Qvҽ[WK~865=]a~a*oAԿB5_`YƘь]TA@GD)DR˅xm4?uhҤ ]p!˖-Gs嶛 6mؤr*$ˤ ^6r oެ}p)߫ >EE3zঁ7T 4`¸1yKuWcƍ3n+pA@u𕆒b Bc)S^/%%5+lso㒋zaF)qy2rk&ߍy9/>:8s;x<,Y-jƮL(LX[3fXS7O5UYۜ4fv|8?a5?8:vg0oem]~W#)M{H/+f ??8GYo?Lm9cfts ;;ZdxzX_1ṞLo$%%Y^+rPNvB נRk=d|ɧ,_[ZzurM72&M֫VOnn.)"3 laT333yad<I 7\y=ty4A|Bi(}5ͣ1Lju>&ѥs'N=%z*RY|9]xt1t , طϜf)sI 3c={xQoΝJ=73qd7KܧDBTꙢq{YXj@0!+sÖ5+t%ɜޡ}uVd2~_DmE.DdS--KNNϏ`s?),( XRR!F0/DlZ%i2h4b< ߯]k:M\.p#=zA呔d_4˹=>W_ -I+c.X9KaΩ6QKP6lM"۹k{dڋjRp˞|f4{z&Ooߞgd=w0Ѩq#?+IIxz˹;Ev <!9Ch!_Dc{bZiii4hPN=5fZz߯6߿;vسx0| /:oo~R󸯔^=r` L <. `iwDItd  |JwDa eof^n?m܀ǛTNgtd7h RJ RJeafCݓ<̙aX(7Zj6ԭ W{ =3ߞe)~-[`݃y޹Ѫdc1qnӇnu0V)=ʪiի} HTWD:`ġ oz_ʻ4]7\;$B 0x*s萩q7szh٢E:nOvm^ 8p6m}MӸi {_?^oE PYSihfRVJRJS:\CkӦs0y?fʕsqک2c@|>憕[{XNpX}<7bȽpݵwAJJ /Lyԇ'k}ˀRJk籧ۥv͞{?ܛY% Ejj*whi<Ln^f͚sR5&nܼ9?^b/h' ~9I[sʯП^y5&7J)3JFD xκugfxD;:*N;o~W^ٹ=jԨAgn$"ܿg$GD_V O?x4%o?ΤRa:jEn]ό](((?̍x_~>d`LS8RZ-rl|@AAŦr#%%)'D"`UJMD|m0@:u[7uS۱˅ngݘȎE>DFD/aa^‰q߄EύgOVd{44ओZs|4m҄&d4ɠiF5jԠvZ_G޽g7mf,_ ^'pB82mgZd| 8Kg{3^ mv͚5ztzÆKuޘ1)^sw]zz;ȕ 9t|3waӌ >Cg}~{+RܨQ;=PJEVJC*ߺU1=\9*jƔM3ء7oq͚ѤqcjծEJq~ڸ 7˯ۢJN=Lv8|KkƲŋHIqNNTJY=܋b^nMl t덃הL4ovgOsԱ#?G:2GOd̀ow9Ǿ05ʊ?qF 3Y)]nXMzS: 8gnE pϗRYxҙ3t@v6VϚ?dfe[.եyЦ7lRji3igTCFϾ}d?@II1yy$%%Q?-SNnG .z۬Lma]⳨o۱1jق$+ӟRj,Pϳkؠб3%pw懵x,z)̜;2%s^>Z?_-[Vf-[pQskU/s>JiO>]:%zD) !D$ nJ&U U+Un7?.RRj60F+.,WN&>7.} ͨ[r~3CœlKE떕nrF&{GH(c0()VJEt%Mf0y׬u]JJ Wy˹_+@Dd+"-/ߊh *YX(jpuݑĉP`Jv/ƮP;vhqѢc.47k E/G>URúͷԮw8MF16nԈ?̤u+ ʸ|i{inՊ۵F@a+p[3xj uN) G񟌆b0lG gLG&l16OIDATr0}ĽO).P%Μ*vǪ솈 Ĺ_ S^<""Vڕ$g {$F=sqT`+.<'egE9*T {\ج"K0i 4c'ٮʯhbX|>~JH(.N\xI`L 3\$PJm*L⿀ H[^^nǮTEp8#ab,p :<AU+!*rHjAD$''n=7p nEŀΚlޕtw>tXjJ <W+0͓o`ۭT\\̽<]+!blċ7aѕs`5X羑gOЃ (u!m7wF 0mM6J+Xm GȒȔAFJF]-d9AQ9E 2^\c! 0)/sԌվRhWA b_~fv2;\^"r*"'I4ӻ8CqZ60*2ALecwi?nHs~W`Hq6փ f:!D `}WF A]B~Pv! >4jE{'utA `5%֟@C=&MdTlDb.: "VFqph.'JA%0x>g(AC)a4 P3F9-L[ 'M컲yNj:$L袛\L`}†eג `>!/.(. [8pY~dX#AEd qCѾ)LI#֌,ZlwFQJ(%@Thg?ظ9d ':؆Du-|~ -vu+ęLG GfaZ0cΝ&%dHT4! `lLF c1CAh#΍S8|w1*BLm q?`KSSA@; 1<ds8|8}3\!:" t]IZ:zu1t|G=^Cq<fOf&Ǎ {7G4$[|>:u>e yPo%UG.hԱZ;VqHiWDe̺8_>^u _P@POyL`9O1^σI` 7ODB;B-زEzxT?Tۦ1AxMGi>}V:_m4yi90B)q +|iY6Y\Q,Z`֌GIOO盭흿CL]:ܪHڋd=-y{+>\W `a`fs4_mȸ~KYY9ߋgLƛe, 0 ˲Rk8x}{fS(-/g:E"iI6>¢", !gY{|M~}A:d6 ee<8m:>7#ذi#o-Y,p|c6D4^?]Ρ-_ww9/iSIdsS\\p\p4 >$kj|jٙED5.{M.-)C]?d?߼5pjjk4\.e2bp^z o{]hZ0 _Bf=4ksO>/[~TpP^~T׍<4Mǰݭs7 0".Gg>/s(dlJ T3xibݫYXr%a:n6ZtuIx^>p VJ5umTD$("\.]M[:^ibY&4EfFwDFF:Ux}>V~)6M$FP4P .,P[SB,{OQ}eÆqeyG哵y,ze1_PJ$"+jE 4͸Ρw1PUUVR[S;'ŋҷO&*-+cx~_uyRK;i ͙;}?BlMPQSj >^&l{e(IPl$W_{wLlD磢̞VRv)i媏8xc,|e iGnYOH}:a $6:s b۠jiXӼ^/ww?%eT >#̚9#𞜜Lm]-lF޽eko7۶{j}O]%6yr\h.[Mm~ؽ'|bb"C.(̆M,t'; ^x *</(☜{j*4at]nt;&[$%%{ԒŚU+TJW ѝȣ3=e K7f4 ~ºݸ\:.MNN"%%$m).)!6֞ 3Sk qm:>_J:?ŋHMM _ \-X]:b>LM8Y!.y]D;.\@l+m0~|1rZrl5הӠ9\9'y_ &6n5W vH c-ݻT?..̄qcILL/+H`iZ[<#== 58vnQ$ R\>|iv]bR '咙$<cOEݺ.- 6III!e]롊 **EFFz*p6`T.'G?? hƬ3RRy޹;PX_~x߾cC./K k㋍3@Dxi,`EvOB,=C~-0N)U?*"YϤG֍3N?,2o{EcǎQZVΆ4oɌt:cڷ?=80ti}GyP"<(,*"99^~a;RJ◝zಆ<3=k2+"|f=4՜˳ޭidfڶ8ዂ/Yy>>d{1n#%%k?( t>ii!x{psz{mͪg v`W_tOn8ϑ@a!@`XT Rj5Zu2%!3#.sS-*L^\K񀃱yՕ'gGzIMM%..GR]SCbBB[i蕋RtBB<'29>`XcLZ%~و{6`"p![b SJ= >B>m0+Hf,zeqă 6R5r4_'@%Ry@^r"2n|qViZUU E'V}@6R$$`i<4>ϏE~dPQQdo=Iϊ3g=y0!{{.9@j[-pcNVҀ@ˣ"3 #Na48~pҶ%Nlџ-tUUU1g򝠩p|`4  Bi m8ZYr.??$n&n`ՖCLДR'+a ALl u۷~ oYp HB)A(A ٱF4' 'F, +m464L?q %D]cIP8`޽X8#<(4@44P"_^4A5 w 'b'g~v$J>"H"'8ˀX)pSHsϚ S86i)t˲0 ?ߥQoXAB &DД򫺀h(냅Uj`0hQT p| SvXIENDB`nnn-5.0/misc/logo/logo.svg000066400000000000000000000711121466310014300155230ustar00rootroot00000000000000 n³ logo image/svg+xml n³ logo Léo Villeveygoux nnn-5.0/misc/macos-legacy/000077500000000000000000000000001466310014300154445ustar00rootroot00000000000000nnn-5.0/misc/macos-legacy/mach_gettime.c000066400000000000000000000022501466310014300202350ustar00rootroot00000000000000#include "mach_gettime.h" #include #define MT_NANO (+1.0E-9) #define MT_GIGA UINT64_C(1000000000) // TODO create a list of timers, static double mt_timebase = 0.0; static uint64_t mt_timestart = 0; int clock_gettime(clockid_t clk_id, struct timespec *tp) { kern_return_t retval = KERN_SUCCESS; if (clk_id == TIMER_ABSTIME) { if (!mt_timestart) { // only one timer, initialized on the first call to the TIMER mach_timebase_info_data_t tb; mach_timebase_info(&tb); mt_timebase = tb.numer; mt_timebase /= tb.denom; mt_timestart = mach_absolute_time(); } double diff = (mach_absolute_time() - mt_timestart) * mt_timebase; tp->tv_sec = diff * MT_NANO; tp->tv_nsec = diff - (tp->tv_sec * MT_GIGA); } else { // other clk_ids are mapped to the corresponding mach clock_service clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), clk_id, &cclock); retval = clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); tp->tv_sec = mts.tv_sec; tp->tv_nsec = mts.tv_nsec; } return retval; } /* Copyright (c) 2015-2018 Alf Watt - Open Source - https://opensource.org/licenses/MIT */ nnn-5.0/misc/macos-legacy/mach_gettime.h000066400000000000000000000014661466310014300202520ustar00rootroot00000000000000#ifndef mach_time_h #define mach_time_h #include #include #include #include /* The opengroup spec isn't clear on the mapping from REALTIME to CALENDAR being appropriate or not. http://pubs.opengroup.org/onlinepubs/009695299/basedefs/time.h.html */ // XXX only supports a single timer #define TIMER_ABSTIME -1 #define CLOCK_REALTIME CALENDAR_CLOCK #define CLOCK_MONOTONIC SYSTEM_CLOCK typedef int clockid_t; /* the mach kernel uses struct mach_timespec, so struct timespec is loaded from for compatibility */ // struct timespec { time_t tv_sec; long tv_nsec; }; int clock_gettime(clockid_t clk_id, struct timespec *tp); #endif /* Copyright (c) 2015-2018 Alf Watt - Open Source - https://opensource.org/licenses/MIT */ nnn-5.0/misc/musl/000077500000000000000000000000001466310014300140605ustar00rootroot00000000000000nnn-5.0/misc/musl/musl-static-ubuntu.sh000077500000000000000000000034711466310014300202110ustar00rootroot00000000000000#!/usr/bin/env sh # Statically compile nnn with netbsd-curses, musl-fts and musl libc on Ubuntu # # netbsd-curses: https://github.com/sabotage-linux/netbsd-curses # musl-fts: https://github.com/void-linux/musl-fts # musl libc: https://www.musl-libc.org/ # # Dependencies: git # # Usage: musl-static-ubuntu.sh [no_run] # # optional argument - do not to execute the binary after compilation # # Notes: # - run the script within the top-level nnn directory # - installs musl & gits netbsd-curses, musl-fts libs # # Tested on Ubuntu 20.04 x86_64 # Author: Arun Prakash Jana # Exit on first failure set -e # Output binary name BIN=nnn-musl-static # Install musl sudo apt install -y --no-install-recommends musl musl-dev musl-tools # Get netbsd-curses [ ! -d "./netbsd-curses" ] && git clone https://github.com/sabotage-linux/netbsd-curses # Enter the library dir cd netbsd-curses # Get the last known working version git checkout v0.3.2 # Compile the static netbsd-curses libraries if [ ! -d "./libs" ]; then mkdir libs else rm -vf -- libs/* fi make CC=musl-gcc CFLAGS=-O3 LDFLAGS=-static all-static -j$(($(nproc)+1)) cp -v libcurses/libcurses.a libterminfo/libterminfo.a libs/ # Get musl-fts library cd .. [ ! -d "./musl-fts" ] && git clone https://github.com/void-linux/musl-fts --depth=1 # Compile the static musl-fts library cd musl-fts ./bootstrap.sh ./configure make CC=musl-gcc CFLAGS=-O3 LDFLAGS=-static -j$(($(nproc)+1)) # Compile nnn cd .. [ -e "./netbsd-curses" ] || rm -- "$BIN" musl-gcc -O3 -DNORL -DNOMOUSE -std=c11 -Wall -Wextra -Wshadow -I./netbsd-curses/libcurses -I./musl-fts -o "$BIN" src/nnn.c -Wl,-Bsymbolic-functions -lpthread -L./netbsd-curses/libs -lcurses -lterminfo -static -L./musl-fts/.libs -lfts strip "$BIN" if [ -z "$1" ]; then # Run the binary with it selected ./"$BIN" -d "$BIN" fi nnn-5.0/misc/natool/000077500000000000000000000000001466310014300143745ustar00rootroot00000000000000nnn-5.0/misc/natool/natool000077500000000000000000000025641466310014300156250ustar00rootroot00000000000000#!/usr/bin/env python3 # ############################################################################# # natool: a wrapper script to patool to list, extract and create archives # # usage: natool [-a] [-l] [-x] [archive] [file/dir] # # Examples: # - create archive : natool -a archive.7z archive_dir # - list archive : natool -l archive.7z # - extract archive: natool -x archive.7z # # Brief: # natool is written to integrate patool (instead of the default atool) with nnn # A copies of this file should be dropped somewhere in $PATH as atool # # Author: Arun Prakash Jana # Email: engineerarun@gmail.com # Homepage: https://github.com/jarun/nnn # Copyright © 2019 Arun Prakash Jana # ############################################################################# import sys from subprocess import Popen, PIPE if len(sys.argv) < 3: print('usage: natool [-a] [-l] [-x] [archive] [file/dir]') sys.exit(0) if sys.argv[1] == '-a': cmd = ['patool', '--non-interactive', 'create', sys.argv[2]] cmd.extend(sys.argv[3:]) elif sys.argv[1] == '-l': cmd = ['patool', '--non-interactive', 'list'] cmd.extend(sys.argv[2:]) elif sys.argv[1] == '-x': cmd = ['patool', '--non-interactive', 'extract'] cmd.extend(sys.argv[2:]) else: sys.exit(0) pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) out, err = pipe.communicate() print(out.decode()) print(err.decode()) nnn-5.0/misc/packagecore/000077500000000000000000000000001466310014300153445ustar00rootroot00000000000000nnn-5.0/misc/packagecore/packagecore.yaml000066400000000000000000000047561466310014300205100ustar00rootroot00000000000000name: nnn maintainer: Arun Prakash Jana license: BSD 2-Clause summary: The unorthodox terminal file manager. homepage: https://github.com/jarun/nnn commands: install: - make PREFIX="/usr" strip install DESTDIR="${BP_DESTDIR}" packages: centos7.5: builddeps: - make - gcc - pkgconfig - ncurses-devel - readline-devel deps: - ncurses - readline commands: pre: - yum install epel-release centos7.6: builddeps: - make - gcc - pkgconfig - ncurses-devel - readline-devel deps: - ncurses - readline commands: pre: - yum install epel-release centos7.7: builddeps: - make - gcc - pkgconfig - ncurses-devel - readline-devel deps: - ncurses - readline commands: pre: - yum install epel-release centos8.0: builddeps: - make - gcc - pkgconfig - ncurses-devel - readline-devel deps: - ncurses - readline commands: pre: - yum install epel-release debian9: builddeps: - make - gcc - pkg-config - libncursesw5-dev - libreadline-dev deps: - libncursesw5 - readline-common debian10: builddeps: - make - gcc - pkg-config - libncurses-dev - libreadline-dev deps: - libncursesw6 - readline-common fedora32: builddeps: - make - gcc - pkg-config - ncurses-devel - readline-devel deps: - ncurses - readline # opensuse15.2: # builddeps: # - make # - gcc # - pkg-config # - readline-devel # - ncurses-devel # deps: # - libncurses6 # - libreadline7 # opensuse.tumbleweed: # builddeps: # - make # - gcc # - pkg-config # - readline-devel # - ncurses-devel # deps: # - libncurses6 # - libreadline8 ubuntu16.04: builddeps: - make - gcc - pkg-config - libncursesw5-dev - libreadline6-dev deps: - libncursesw5 - libreadline6 ubuntu18.04: builddeps: - make - gcc - pkg-config - libncursesw5-dev - libreadline-dev deps: - libncursesw5 - libreadline7 ubuntu20.04: builddeps: - make - gcc - pkg-config - libncurses-dev - libreadline-dev deps: - libncursesw6 - libreadline8 nnn-5.0/misc/quitcd/000077500000000000000000000000001466310014300143715ustar00rootroot00000000000000nnn-5.0/misc/quitcd/quitcd.bash_sh_zsh000066400000000000000000000017411466310014300201020ustar00rootroot00000000000000n () { # Block nesting of nnn in subshells [ "${NNNLVL:-0}" -eq 0 ] || { echo "nnn is already running" return } # The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set) # If NNN_TMPFILE is set to a custom path, it must be exported for nnn to # see. To cd on quit only on ^G, remove the "export" and make sure not to # use a custom path, i.e. set NNN_TMPFILE *exactly* as follows: # NNN_TMPFILE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.lastd" export NNN_TMPFILE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.lastd" # Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn # stty start undef # stty stop undef # stty lwrap undef # stty lnext undef # The command builtin allows one to alias nnn to n, if desired, without # making an infinitely recursive alias command nnn "$@" [ ! -f "$NNN_TMPFILE" ] || { . "$NNN_TMPFILE" rm -f -- "$NNN_TMPFILE" > /dev/null } } nnn-5.0/misc/quitcd/quitcd.csh000066400000000000000000000012371466310014300163640ustar00rootroot00000000000000# NOTE: set NNN_TMPFILE correctly if you use 'XDG_CONFIG_HOME' # The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set) # If NNN_TMPFILE is set to a custom path, it must be exported for nnn to see. # To cd on quit only on ^G, set NNN_TMPFILE after the nnn invocation, and make # sure not to use a custom path. set NNN_TMPFILE=~/.config/nnn/.lastd # Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn # stty start undef # stty stop undef # stty lwrap undef # stty lnext undef # The backslash allows one to alias n to nnn if desired without making an # infinitely recursive alias alias n '\nnn; source "$NNN_TMPFILE"; rm -f -- "$NNN_TMPFILE"' nnn-5.0/misc/quitcd/quitcd.elv000066400000000000000000000016321466310014300163740ustar00rootroot00000000000000# Append this file to ~/.elvish/rc.elv (Elvish > 0.17.0) use path fn n {|@a| # Block nesting of nnn in subshells if (has-env NNNLVL) { try { if (>= $E:NNNLVL 1) { echo "nnn is already running" return } } catch e { nop } } # The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set) # If NNN_TMPFILE is set to a custom path, it must be exported for nnn to # see. if (has-env XDG_CONFIG_HOME) { set-env NNN_TMPFILE $E:XDG_CONFIG_HOME/nnn/.lastd } else { set-env NNN_TMPFILE $E:HOME/.config/nnn/.lastd } # Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn # stty start undef # stty stop undef # stty lwrap undef # stty lnext undef # The e: prefix allows one to alias n to nnn if desired without making an # infinitely recursive alias e:nnn $@a if (path:is-regular $E:NNN_TMPFILE) { eval (slurp < $E:NNN_TMPFILE) rm -- $E:NNN_TMPFILE } } nnn-5.0/misc/quitcd/quitcd.fish000066400000000000000000000022451466310014300165400ustar00rootroot00000000000000# Rename this file to match the name of the function # e.g. ~/.config/fish/functions/n.fish # or, add the lines to the 'config.fish' file. function n --wraps nnn --description 'support nnn quit and change directory' # Block nesting of nnn in subshells if test -n "$NNNLVL" -a "$NNNLVL" -ge 1 echo "nnn is already running" return end # The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set) # If NNN_TMPFILE is set to a custom path, it must be exported for nnn to # see. To cd on quit only on ^G, remove the "-x" from both lines below, # without changing the paths. if test -n "$XDG_CONFIG_HOME" set -x NNN_TMPFILE "$XDG_CONFIG_HOME/nnn/.lastd" else set -x NNN_TMPFILE "$HOME/.config/nnn/.lastd" end # Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn # stty start undef # stty stop undef # stty lwrap undef # stty lnext undef # The command function allows one to alias this function to `nnn` without # making an infinitely recursive alias command nnn $argv if test -e $NNN_TMPFILE source $NNN_TMPFILE rm -- $NNN_TMPFILE end end nnn-5.0/misc/quitcd/quitcd.nu000066400000000000000000000023771466310014300162370ustar00rootroot00000000000000# Run nnn with dynamic changing directory to the environment. # # $env.XDG_CONFIG_HOME sets the home folder for `nnn` folder and its $env.NNN_TMPFILE variable. # See manual NNN(1) for more information. # # Import module using `use quitcd.nu n` to have `n` command in your context. export def --env n [ ...args : string # Extra flags to launch nnn with. --selective = false # Change directory only when exiting via ^G. ] -> nothing { # The behaviour is set to cd on quit (nnn checks if $env.NNN_TMPFILE is set). # Hard-coded to its respective behaviour in `nnn` source-code. let nnn_tmpfile = $env | default '~/.config/' 'XDG_CONFIG_HOME' | get 'XDG_CONFIG_HOME' | path join 'nnn/.lastd' | path expand # Launch nnn. Add desired flags after `^nnn`, ex: `^nnn -eda ...$args`, # or make an alias `alias n = n -eda`. if $selective { ^nnn ...$args } else { NNN_TMPFILE=$nnn_tmpfile ^nnn ...$args } if ($nnn_tmpfile | path exists) { # Remove from the first part of the string and the last single quote <'>. # Fix post-processing of nnn's given path that escapes its single quotes with POSIX syntax. let path = open $nnn_tmpfile | str replace --all --regex `^cd '|'$` `` | str replace --all `'\''` `'` ^rm -- $nnn_tmpfile cd $path } } nnn-5.0/misc/test/000077500000000000000000000000001466310014300140575ustar00rootroot00000000000000nnn-5.0/misc/test/benchmark.sh000077500000000000000000000012331466310014300163470ustar00rootroot00000000000000#!/bin/sh # # Usage: ./misc/test/benchmark.sh ./nnn /tmp/testdir1 ./testdir2 ... # # Don't forget to build nnn in benchmark mode: make O_BENCH=1 # Use a test dir filled with genfiles.sh to get interesting output # (or maybe /usr/lib/) LANG=C TIME_VAL=${TIME_VAL:-"real"} SAMPLES=${SAMPLES:-100} EXE=$1 bench_val () { (time "$1" "$2") 2>&1 |\ awk '$1=="'"$TIME_VAL"'"{match($2, /[0-9]*\.[0-9]*/) ; print substr($2, RSTART, RLENGTH)}' } bench_dir () { i=$SAMPLES printf "$2" while [ $((i--)) -gt 0 ] ; do printf "\t%s" "$(bench_val "$1" "$2")" done printf "\n" } shift for dir in "$@" ; do bench_dir "$EXE" "$dir" done nnn-5.0/misc/test/genfiles.sh000077500000000000000000000002421466310014300162100ustar00rootroot00000000000000#!/bin/sh # Generates 100000 files in the current directory i=1; while [ $i -le 100000 ]; do mktemp -p . -t 'XXXXXXXXXXXXXXXXXXXXX' i=$(( i + 1 )) done nnn-5.0/misc/test/mktest.sh000077500000000000000000000071531466310014300157330ustar00rootroot00000000000000#!/bin/sh # Create test files and directories test -e outdir && { echo "Remove 'outdir' and try again" exit 1 } mkdir -p outdir && cd outdir || exit 1 echo 'It works!' > normal.txt echo 'Με δουλέβει;' > 'κοινό.txt' ln -sf normal.txt ln-normal.txt ln -sf normal.txt ln-normal mkdir -p normal-dir ln -sf normal-dir ln-normal-dir ln -sf nowhere ln-nowhere mkfifo mk-fifo touch no-access && chmod 000 no-access mkdir -p no-access-dir && chmod 000 no-access-dir ln -sf ../normal.txt normal-dir/ln-normal.txt ln -sf ../normal.txt normal-dir/ln-normal echo 'int main(void) { *((char *)0) = 0; }' > ill.c make ill > /dev/null echo 'test/ill' > ill.sh mkdir -p empty-dir mkdir -p cage echo 'chmod 000 test/cage' > cage/lock.sh echo 'chmod 755 test/cage' > cage-unlock.sh mkdir -p cage/lion echo 'chmod 000 test/cage' > cage/lion/lock.sh mkdir -p korean touch 'korean/[ENG sub] PRODUCE48 울림ㅣ김채원ㅣ행복 나눠주는 천사소녀 @자기소개_1분 PR 180615 EP.0-Cgnmr6Fd82' touch 'korean/[ENG sub] PRODUCE48 [48스페셜] 윙크요정, 내꺼야!ㅣ김채원(울림) 180615 EP.0-K7ulTiuJZK8.mp4' touch 'korean/[FULL ENG SUB] 181008 SALEWA x IZ_ONE Long Padding Photoshoot Behind Film-[오늘의 시구] 아이즈원 (IZONE) 장원영&미야와키 사쿠라! 시구 시타! (10.06)-VmDl5eBJ3x0.mkv' touch 'korean/IZ_ONE (아이즈원) - 1st Mini Album [COLOR_IZ] Highlight Medley-w9V2xFrYIgk.web' touch 'korean/IZ_ONE (아이즈원) - 1st Mini Album [COLOR_IZ] MV TEASER 1-uhnJLBNBNto.mkv' touch 'korean/IZ_ONE CHU [1회] ′순도 100%′ 우리즈원 숙소 생활 ★최초 공개★ 181025 EP.1-pcutrQN1Sbg.mkv' touch 'korean/IZ_ONE CHU [1회_예고] 아이즈원 데뷔 준비 과정 ★독점 공개★ 아이즈원 츄 이번주 (목) 밤 11시 첫방송 181025' touch 'korean/IZ_ONE CHU [1회] 도치기현 1호 이모 팬과의 만남! 181025 EP.1-5kYoReT5x44.mp4' touch 'korean/IZ_ONE CHU [1회] ′12명 소녀들의 새로운 시작′ 앞으로 아이즈원 잘 부탁해♥ 181025 EP.1-RVNvgbdLQLQ' touch 'korean/IZ_ONE CHU [1회] ′앗..그것만은!′ 자비없는 합숙생활 폭로전 181025 EP.1-AmP5KzpoI38.mkv' touch 'korean/IZ_ONE CHU [1회] 휴게소 간식 내기 노래 맞히기 게임 181025 EP.1-LyNDKflpWYE.mp4' touch 'korean/IZ_ONE CHU [1회] 2018 아이즈원 걸크러시능력시험 (feat. 치타쌤) 181025 EP.1-9qHWpbo0eB8.mp4' touch 'korean/IZ_ONE CHU [1회] ′돼지요′ 아니죠, ′되지요′ 맞습니다! (feat. 꾸라먹방) 181025EP.1-WDLFqMWiKn' touch 'korean/IZ_ONE CHU [1회] ′두근두근′ 첫 MT를 앞둔 비글력 만렙의 아이즈원 181025 EP.1' mkdir -p unicode touch 'unicode/Malgudi Days - मालगुडी डेज - E05. Swami and Friends - स्वामी और उसके दोस्त (Part 1)' touch 'unicode/Malgudi Days - मालगुडी डेज - E05. Swami and Friends - स्वामी और उसके दोस्त (Part 2)' touch 'unicode/Malgudi Days - मालगुडी डेज - E05. Swami and Friends - स्वामी और उसके दोस्त (Part 3)' chmod +x 'unicode/Malgudi Days - मालगुडी डेज - E05. Swami and Friends - स्वामी और उसके दोस्त (Part 2)' touch 'unicode/Führer' touch 'unicode/Eso eso aamar ghare eso ♫ এসো এসো আমার ঘরে এসো ♫ Swagatalakshmi Dasgupta' touch 'max_chars_filename_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' nnn-5.0/misc/test/plot-bench.py000077500000000000000000000007411466310014300164710ustar00rootroot00000000000000#!/usr/bin/env python3 # # Usage: ./plot-bench.py datafile # (where datafile is the output of benchmark.sh) import matplotlib.pyplot as plt import sys def bench_file_to_lists(infile): return [[float(entry) for entry in line.split('\t')[1:]] for line in infile.readlines()] def plot_data(data): fig = plt.figure() ax = fig.add_axes([0,0,1,1]) ax.violinplot(data) plt.savefig("plot.svg") filename = sys.argv[1] plot_data(bench_file_to_lists(open(filename))) nnn-5.0/nnn.1000066400000000000000000000501321466310014300130210ustar00rootroot00000000000000.Dd Aug 26, 2024 .Dt NNN 1 .Os .Sh NAME .Nm nnn .Nd The unorthodox terminal file manager. .Sh SYNOPSIS .Nm .Op Ar -aAcCdDeEfgHJKnQrRSuUVxh .Op Ar -b key .Op Ar -F val .Op Ar -l val .Op Ar -p file .Op Ar -P key .Op Ar -s name .Op Ar -t secs .Op Ar -T key .Op Ar PATH .Sh DESCRIPTION .Nm .Pq Nnn's Not Noice is a performance-optimized, feature-packed fork of noice .Em http://git.2f30.org/noice/ with seamless desktop integration, simplified navigation, .Em type-to-nav mode with dir auto-enter, disk usage analyzer mode, bookmarks, contexts, application launcher, familiar navigation shortcuts, subshell spawning and much more. It remains a simple and efficient file manager that stays out of your way. .Pp .Nm opens the current working directory if .Ar PATH is not specified. If .Ar PATH is specified and it exists, .Nm will open it. If the .Ar PATH doesn't exist and ends with a \fB/\fR, .Nm will attempt to create the directory tree and open it. Otherwise, .Ar PATH is considered a path to a regular file and .Nm attempts to create the complete directory tree to the file, open the parent directory and prompt to create the new file in it with the base filename. .Sh KEYBINDS .Pp Press .Ql \&? in .Nm to see the list of keybinds. .Sh OPTIONS .Pp .Nm supports the following options: .Pp .Fl a auto-setup temporary \fBNNN_FIFO\fR (described in \fIENVIRONMENT\fR section) .Pp .Fl A disable directory auto-enter on unique filter match .Pp .Fl "b key" specify bookmark key to open .Pp .Fl B use bsdtar for archives (default: atool) .Pp .Fl c indicates that the opener is a cli-only opener (overrides -e) .Pp .Fl C 8-color scheme - color directories by context, disable file colors .Pp .Fl d detail mode .Pp .Fl D show directories in context color with \fBNNN_FCOLORS\fR set .Pp .Fl e open text files in $VISUAL (else $EDITOR, fallback vi) [preferably CLI] .Pp .Fl E use $EDITOR for internal undetached edits .Pp .Fl f use readline history file .Pp .Fl "F val" fifo notification mode 0: notify as previewer, 1: notify as explorer .Pp .Fl g use regex filters instead of substring match .Pp .Fl H show hidden files .Pp .Fl i show current file information in info bar (may be slow) .Pp .Fl J disable auto-advance on selection (eg. selecting an entry will no longer move cursor to the next entry) .Pp .Fl K test for keybind collision and exit .Pp .Fl "l val" number of lines to move per mouse wheel scroll .Pp .Fl n start in type-to-nav mode .Pp .Fl o open files only on Enter key .Pp .Fl "p file" copy (or \fIpick\fR) selection to file, or stdout if file='-' .Pp .Fl "P key" specify plugin key to run .Pp .Fl Q disable confirmation on quit with multiple contexts active .Pp .Fl r show cp, mv progress (Linux-only, needs \fIadvcpmv\fR; '^T' shows the progress on BSD/macOS) .Pp .Fl R disable rollover at edges (eg. pressing \fIdown\fR while on the last entry will no longer move cursor to the first entry and vice\-versa) .Pp .Fl "s name" load a session by name .Pp .Fl S persistent session .Pp .Fl "t secs" idle timeout in seconds to lock terminal .Pp .Fl "T key" sort order keys: 'a'pparent disk usage / 'd'isk usage / 'e'xtension / 'r'everse / 's'ize / 't'ime / 'v'ersion capitalize to reverse (except 'r') .Pp .Fl u use selection if available, don't prompt to choose between selection and hovered entry .Pp .Fl U show user and group names in status bar .Pp .Fl V show version and exit .Pp .Fl x show notifications on selection cp, mv, rm completion (requires \fI.ntfy\fR plugin) copy path to system clipboard on selection (requires \fI.cbcp\fR plugin) show xterm title (if non-picker mode) .Pp .Fl 0 use null separator (instead of newline) to separate file paths in picker mode outout .Pp .Fl h show program help and exit .Sh CONFIGURATION There is no configuration file. Associated files are at .Pp .Pa ${XDG_CONFIG_HOME:-$HOME/.config}/nnn/ .Pp Configuration is done using a few optional (set if you need) environment variables. See ENVIRONMENT section. .Pp .Nm uses \fIxdg-open\fR (on Linux), \fIopen(1)\fR (on macOS), \fIcygstart\fR on (Cygwin) and \fIopen\fR on (Haiku) as the desktop opener. It's also possible to specify a custom opener. See ENVIRONMENT section. .Sh CONTEXTS Open multiple locations with 4 contexts. The status is shown in the top left corner: .Pp - the current context is in reverse video .br - other active contexts are underlined .br - rest are inactive .Pp A new context copies the state of the previous context. Each context can have its own color. See ENVIRONMENT section. .Sh SESSIONS Sessions are a way to save and restore states of work. A session stores the settings and contexts. Sessions can be loaded at runtime or with a program option. .Pp - When a session is loaded at runtime, the last working state is saved automatically to a dedicated "auto session" session file. Session option \fIrestore\fR would restore the "auto session". .br - The persistent session option is global. If it is used, the last active session will be updated with the final state at program quit. .br - The "auto session" is used in persistent session mode if no session is active. .br - Listing input stream and opening a bookmark by key have a higher priority to session options (-s/-S). .Pp All the session files are located by session name in the directory .Pp \fB${XDG_CONFIG_HOME:-$HOME/.config}/nnn/sessions\fR .Pp "@" is the "auto session" file. .Sh FILTERS Filters are strings (or regex patterns) to find matching entries in the current directory instantly (\fIsearch-as-you-type\fR). Matches are case-insensitive by default. The last filter in each context is persisted at runtime or in saved sessions. .br When there's a unique match and it's a directory, .Nm auto enters the directory. Use the relevant program option to disable this. .Pp Special keys at filter prompt: .Bd -literal -------- + --------------------------------------- Key | Function -------- + --------------------------------------- ^char | Usual keybind functionality Esc | Exit filter prompt but skip dir refresh Alt+Esc | Unfilter, quit context -------- + --------------------------------------- .Ed .Pp Special keys at \fBempty filter prompt\fR: .Bd -literal ------ + --------------------------------------- Key | Function ------ + --------------------------------------- ? | Show help and config screen / | Toggle between string and regex : | Toggle case-sensitivity ^L | Clear filter (\fIif prompt is non-empty\fR) | OR apply last filter Bksp | Stay at filter prompt and refresh dir Del | Stay at filter prompt and refresh dir ------ + --------------------------------------- .Ed .Pp Common regex use cases: .Pp (1) To list all matches starting with the filter expression, start the expression with a '^' (caret) symbol. .br (2) Type '\\.mkv' to list all MKV files. .br (3) Use '.*' to match any character (\fIsort of\fR fuzzy search). .br (4) Exclude filenames having 'nnn' (compiled with PCRE lib): '^(?!nnn)' .Pp In the \fBtype-to-nav\fR mode directories are opened in filter mode, allowing continuous navigation. .Pp Additional special keys at \fBempty filter prompt\fR in \fBtype-to-nav\fR mode: .Bd -literal ------ + ------------------------ Key | Function ------ + ------------------------ ' | Go to first non-dir file + | Toggle file selection , | Mark CWD - | Go to last visited dir . | Show hidden files ; | Run a plugin by its key = | Launch a GUI application > | Export file list @ | Visit start dir ] | Show command prompt ` | Visit / ~ | Go HOME ------ + ------------------------ .Ed .Sh SELECTION .Nm allows file selection across directories and contexts! .Pp There are 3 groups of keybinds to add files to selection: .Pp (1) hovered file selection toggle - deselects if '+' is visible before the entry, else adds to selection .br (2) add a range of files to selection - repeat the range key on the same entry twice to clear selection completely .br (3) add all files in the current directory to selection .Pp A selection can be edited, copied, moved, removed, archived or linked. .Pp Absolute paths of the selected files are copied to \fB.selection\fR file in the config directory. The selection file is shared between multiple program instances. Selection from multiple instances are not merged. The last instance writing to the file overwrites earlier contents. If you have 2 instances of .Nm \fIopen\fR in 2 panes of a terminal multiplexer, you can select in one pane and use the selection in the other pane. The selection gets cleared in the .Nm instance where the selection was made on mv/rm (but not on cp). .Pp .Nm clears the selection after a successful operation with the selection. Plugins are allowed to define the behaviour individually. .Pp To edit the selection use the _edit selection_ key. Editing doesn't end the selection mode. You can add more files to the selection and edit the list again. If no file is selected in the current session, this option attempts to list the selection file. .Pp .Nm can show the total size of non-filtered selected files listed in a directory. For directories, only the size of the directory is added by default. To add the size of the contents of a directory, switch to du mode. .Sh FIND AND LIST There are two ways to search and list: .Pp - feed a list of file paths as input .br - search using a plugin (e.g. \fIfinder\fR) and list the results .Pp File paths must be NUL-separated ('\\0'). Paths and can be relative to the current directory or absolute. Invalid paths in the input are ignored. Input processing limit is 16,384 paths or 64 MiB (max_paths x max_path_len) of data. .Pp To list the input stream, start .Nm by writing to its standard input. E.g., to list files in current directory larger than 1M: .Bd -literal find -maxdepth 1 -size +1M -print0 | nnn .Ed .Pp or redirect a list from a file: .Bd -literal nnn < files.txt .Ed .Pp Handy bash/zsh shell function to list files by mime-type in current directory: .Bd -literal # to show video files, run: list video list () { find . -maxdepth 1 | file -if- | grep "$1" | awk -F: '{printf "%s%c", $1, 0}' | nnn } .Ed .Pp A temporary directory will be created containing symlinks to the given paths. Any action performed on these symlinks will be performed only on their targets, after which they might become invalid. .Pp Right arrow or 'l' on a symlink in the listing dir takes to the target file. Press '-' to return to the listing dir. Press 'Enter' to open the symlink. .Pp Listing input stream can be scripted. It can be extended to pick (option -p) selected entries from the listed results. .Sh BOOKMARKS There are 2 ways (can be used together) to manage bookmarks. .Pp (1) Bookmark keys: See \fBNNN_BMS\fR under \fIENVIORNMENT\fR section on how to set bookmark keys. The select bookmark key \fIb\fR lists all the bookmark keys set in \fBNNN_BMS\fR in the bookmarks prompt. .Pp (2) Symlinked bookmarks: A symlinked bookmark to the current directory can be created with the \fIB\fR key (or manually under ~/.config/nnn/bookmarks). Pressing 'Enter' at the bookmarks prompt takes to this directory. If \fBNNN_BMS\fR is not set, the select bookmark key directly opens it. .Pp On entering a bookmark, the directory where the select bookmark key was pressed is set as the previous directory. Press '-' to return to it. .Pp .Sh UNITS The minimum file size unit is byte (B). The rest are K, M, G, T, P, E, Z, Y (powers of 1024), same as the default units in \fIls\fR. .Sh ENVIRONMENT The SHELL, VISUAL (else EDITOR) and PAGER environment variables are used. A single combination of arguments is supported for SHELL and PAGER. .Pp \fBNNN_OPTS:\fR binary options to .Nm .Bd -literal export NNN_OPTS="cEnrx" .Ed .Pp \fBNNN_OPENER:\fR specify a custom file opener. .Bd -literal export NNN_OPENER=nuke NOTE: 'nuke' is a file opener available in the plugin repository. .Ed .Pp \fBNNN_BMS:\fR bookmark string as \fIkey_char:location\fR pairs separated by \fI;\fR: .Bd -literal export NNN_BMS="d:$HOME/Docs;u:/home/user/Cam Uploads;D:$HOME/Downloads/" .Ed .Pp These bookmarks are listed in the help and config screen (key ?). .Pp \fBNNN_PLUG:\fR directly executable plugins as \fIkey_char:plugin\fR pairs separated by \fI;\fR: .Bd -literal export NNN_PLUG='f:finder;o:fzopen;p:mocplay;d:diffs;t:nmount;v:imgview' NOTES: 1. To run a plugin directly, press \fI;\fR followed by the key. 2. Alternatively, combine with \fIAlt\fR (i.e. \fIAlt+key\fR). 3. To skip directory refresh after running a plugin, prefix with \fB-\fR. export NNN_PLUG='p:-plugin' .Ed .Pp To assign keys to arbitrary non-background cli commands and invoke like plugins, add \fB!\fR before the command. .Bd -literal export NNN_PLUG='x:!chmod +x "$nnn";g:!git log;s:!smplayer "$nnn"' To pick and run an unassigned plugin, press \fBEnter\fR at the plugin prompt. To run a plugin at startup, use the option `-P` followed by the plugin key. NOTES: 1. Place $nnn (or exported variables) in double quotes (\fB"$nnn"\fR) 2. Use single quotes for $NNN_PLUG so "$nnn" is not interpreted 3. (Again) add \fB!\fR before the command 4. To disable directory refresh after running a \fIcommand as plugin\fR, prefix with \fB-!\fR 5. To skip user confirmation after command execution, suffix with \fB*\fR Note: Do not use \fB*\fR with programs those run and exit e.g. cat export NNN_PLUG='y:-!sync*' 6. To run a \fIGUI app as plugin\fR, add a \fB&\fR after \fB!\fR. export NNN_PLUG='m:-!&mousepad "$nnn"' 7. To show the output of run-and-exit commands which do not need user input, add \fB|\fR (pipe) after \fB!\fR Note: This option is incompatible with \fB&\fR (terminal output is masked for GUI programs) and ignores \fB*\fR (output is already paged for user). export NNN_PLUG='m:-!|mediainfo "$nnn";t:-!|tree -ps;l:-!|ls -lah --group-directories-first' EXAMPLES: ------------------------------------ + ------------------------------------------------- Key:Command | Description ------------------------------------ + ------------------------------------------------- c:!convert "$nnn" png:- | xclip | Copy image to clipboard -sel clipboard -t image/png* | C:!cp -rv "$nnn" "$nnn".cp* | Create a copy of the hovered file e:-!sudo -E vim "$nnn"* | Edit file as root in vim g:-!git diff | Show git diff h:-!hx "$nnn"* | Open hovered file in hx hex editor k:-!fuser -kiv "$nnn"* | Interactively kill process(es) using hovered file l:-!git log | Show git log n:-!vi /home/user/Dropbox/dir/note* | Take quick notes in a synced file/dir of notes p:-!less -iR "$nnn"* | Page through hovered file in less s:-!&smplayer -minigui "$nnn" | Play hovered media file, even unfinished download x:!chmod +x "$nnn" | Make the hovered file executable y:-!sync* | Flush cached writes ------------------------------------ + ------------------------------------------------- Online docs: https://github.com/jarun/nnn/tree/master/plugins .Ed .Pp \fBNNN_ORDER:\fR directory-specific sort key. .Bd -literal export NNN_ORDER='t:/home/user/Downloads;S:/tmp' NOTE: Sort keys can be a/d/e/r/s/t/v (see program option -T). Capitalize to reverse (except 'r'). Path must be absolute. Timestamps for entries modified/created within 5 minutes are shown in reverse. .Ed .Pp \fBNNN_COLORS:\fR string of color numbers for each context, e.g.: .Bd -literal # 8 color numbers: # 0-black, 1-red, 2-green, 3-yellow, 4-blue (default), 5-magenta, 6-cyan, 7-white export NNN_COLORS='1234' # xterm 256 color numbers (converted to hex, 2 symbols per context): # see https://user-images.githubusercontent.com/1482942/93023823-46a6ba80-f5e1-11ea-9ea3-6a3c757704f4.png export NNN_COLORS='#0a1b2c3d' # both (256 followed by 8 as fallback, separated by ';') export NNN_COLORS='#0a1b2c3d;1234' NOTE: If only 256 colors are specified and the terminal doesn't support, default is used. .Ed .Pp \fBNNN_FCOLORS:\fR specify file-type specific colors: .Bd -literal export NNN_FCOLORS='c1e2272e006033f7c6d6abc4' Specify file-specific colors in xterm 256 color hex numbers (2 symbols per color). Order is strict, use 00 to omit/use default terminal color. Defaults: ------------------------- + --- + ------------- Order | Hex | Color ------------------------- + --- + ------------- Block device | c1 | DarkSeaGreen1 Char device | e2 | Yellow1 Directory | 27 | DeepSkyBlue1 Executable | 2e | Green1 Regular | 00 | Normal Hard link | 60 | Plum4 Symbolic link | 33 | Cyan1 Missing OR file details | f7 | Grey62 Orphaned symbolic link | c6 | DeepPink1 FIFO | d6 | Orange1 Socket | ab | MediumOrchid1 Unknown OR 0B regular/exe | c4 | Red1 ------------------------- + --- + ------------- If the terminal supports xterm 256 colors or more, file-specific colors will be rendered. To force the 8-color scheme use option -C. If xterm 256 colors aren't supported, 8-color scheme will be used. .Ed .Pp \fBNNN_ARCHIVE:\fR archive extensions to be handled silently (default: bzip2, (g)zip, tar). .Bd -literal export NNN_ARCHIVE="\\\\.(7z|bz2|gz|tar|tgz|zip)$" NOTE: Non-default formats may require a third-party utility. .Ed .Pp \fBNNN_ARCHMNT:\fR optional archive mounter utility (default: archivemount). .Bd -literal export NNN_ARCHIVE='fuse-archive' .Ed .Pp \fBNNN_SSHFS:\fR specify custom sshfs command with options: .Bd -literal export NNN_SSHFS='sshfs -o reconnect,idmap=user,cache_timeout=3600' NOTE: The options must be comma-separated without any space between them. .Ed .Pp \fBNNN_RCLONE:\fR pass additional options to rclone command: .Bd -literal export NNN_RCLONE='rclone mount --read-only --no-checksum' NOTE: The options must be preceded by "rclone" and max 5 flags are supported. .Ed .Pp \fBNNN_TRASH:\fR trash (instead of \fIrm -rf\fR) files to desktop Trash. .Bd -literal export NNN_TRASH=n # n=1: trash-cli, n=2: gio trash .Ed .Pp \fBNNN_SEL:\fR absolute path to custom selection file. .Bd -literal export NNN_SEL='/tmp/.sel' .Ed .Pp \fBNNN_FIFO:\fR path of a named pipe to write the hovered file path: .Bd -literal export NNN_FIFO='/tmp/nnn.fifo' NOTES: 1. Overridden by a temporary path with -a option. 2. If the FIFO file doesn't exist it will be created, but not removed (unless it is generated by -a option). Online docs: https://github.com/jarun/nnn/wiki/Live-previews .Ed .Pp \fBNNN_LOCKER:\fR terminal locker program. .Bd -literal export NNN_LOCKER='bmon -p wlp1s0' export NNN_LOCKER='cmatrix' .Ed .Pp \fBNNN_TMPFILE:\fR \fIalways\fR cd on quit and write the command in the file specified. .Bd -literal export NNN_TMPFILE='/tmp/.lastd' .Ed .Pp \fBNNN_HELP:\fR run a program and show the output on top of the program help page. .Bd -literal export NNN_HELP='fortune' .Ed .Pp \fBNNN_MCLICK:\fR key emulated by a middle mouse click. .Bd -literal export NNN_MCLICK='^R' NOTE: Only the first character is considered if not a \fICtrl+key\fR combo. .Ed .Pp \fBnnn:\fR this is a special variable. .Bd -literal Set to the hovered file name before starting the command prompt or spawning a shell. .Ed .Pp \fBNO_COLOR:\fR disable ANSI color output (overridden by \fBNNN_COLORS\fR). .Sh AUTHORS .An Arun Prakash Jana Aq Mt engineerarun@gmail.com , .An Lazaros Koromilas Aq Mt lostd@2f30.org , .An Dimitris Papastamos Aq Mt sin@2f30.org . .Sh HOME .Em https://github.com/jarun/nnn nnn-5.0/patches/000077500000000000000000000000001466310014300135745ustar00rootroot00000000000000nnn-5.0/patches/README.md000066400000000000000000000041061466310014300150540ustar00rootroot00000000000000

User Patch Framework

This directory contains sizable user submitted patches that were rejected from mainline as they tend to be more subjective in nature. The patches will be adapted on each release when necessary (v4.1 onwards). Each patch can be applied through its respective make variable during compilation. In case inter-patch merge conflicts occur, a compatibility patch is provided and will automatically be applied. ## List of patches | Patch (a-z) | Description | Make var | | --- | --- | --- | | colemak | Key bindings for Colemak keyboard layout | `O_COLEMAK` | | gitstatus | Add git status column to the detail view. Provides command line flag `-G` to show column in normal mode. | `O_GITSTATUS` | | namefirst | Print filenames first in the detail view. Print user/group columns when a directory contains different users/groups. | `O_NAMEFIRST` | | restorepreview | Add pipe to close and restore [`preview-tui`](https://github.com/jarun/nnn/blob/master/plugins/preview-tui) for internal undetached edits (e key)| `O_RESTOREPREVIEW` | To apply a patch, use the corresponding make variable, e.g.: make O_NAMEFIRST=1 When contributing/adding a new patch, make sure to add the make variable to the patches array in `./misc/test/check-patches.sh` as well so that patch failures can be easily tested. ## Resolving patch conflicts Patch conflicts can be checked locally by running `make checkpatches` or by running `./patches/check-patches.sh` manually. Whenever patch conflicts occur on the latest master, pull requests resolving them are welcome. Let's say a conflict occurs in the `restorepreview` patch. The best way to resolve this conflict would be something along the lines of: - Ensure you're on latest master and run `PATCH_OPTS="--merge" make O_RESTOREPREVIEW=1`. This will generate the conflict markers in `src/nnn.c`. - Next edit `src/nnn.c`, resolve the conflicts around the conflict markers(`<<<<<<<`), and save. - Then run `git diff > patch.diff && sed -i -e "/^$/{r patch.diff" -e "q;}" patches/restorepreview/mainline.diff` to update the patch. nnn-5.0/patches/check-patches.sh000077500000000000000000000013471466310014300166420ustar00rootroot00000000000000#!/bin/bash # # Usage: ./misc/test/check-patches.sh # # Bash script that checks for any of the patches failing to apply. # Read patches/README.md for more information. export PATCH_OPTS="--merge" patches=("O_COLEMAK" "O_GITSTATUS" "O_NAMEFIRST" "O_RESTOREPREVIEW") z=$(( 1 << ${#patches[@]} )) pid=$$ ret=0 trap 'ret=1' SIGUSR1 for ((n=1; n < z; ++n)); do for ((i=0; i < ${#patches[@]}; ++i)); do printf "%s=%d " "${patches[$i]}" "$(( (n & (1 << i)) != 0 ))" done | tee "/dev/stderr" | ( make clean -s if ! xargs make 2>&1; then echo "[FAILED]" >&2 kill -SIGUSR1 "$pid" else echo "[SUCCESS]" >&2 fi git restore src ) >/dev/null done exit "$ret" nnn-5.0/patches/colemak/000077500000000000000000000000001466310014300152075ustar00rootroot00000000000000nnn-5.0/patches/colemak/mainline.diff000066400000000000000000000114051466310014300176360ustar00rootroot00000000000000diff --git a/src/nnn.c b/src/nnn.c index d7c53166..bb7ff3e8 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -5149,12 +5149,12 @@ static void show_help(const char *path) "2(___n))\n" "0\n" "1NAVIGATION\n" - "9Up k Up%16PgUp ^U Page up\n" - "9Dn j Down%14PgDn ^D Page down\n" + "9Up e Up%16PgUp ^U Page up\n" + "9Dn n Down%14PgDn ^D Page down\n" "9Lt h Parent%12~ ` @ - ~, /, start, prev\n" - "5Ret Rt l Open%20' First file/match\n" - "9g ^A Top%21J Jump to entry/offset\n" - "9G ^E End%20^J Toggle auto-advance on open\n" + "5Ret Rt i Open%20' First file/match\n" + "9g ^E Top%21J Jump to entry/offset\n" + "9G ^N End%20^J Toggle auto-advance on open\n" "8B (,) Book(mark)%11b ^/ Select bookmark\n" "a1-4 Context%11(Sh)Tab Cycle/new context\n" "62Esc ^Q Quit%19^y Next young\n" @@ -5162,27 +5162,27 @@ static void show_help(const char *path) "cq Quit context\n" "0\n" "1FILTER & PROMPT\n" - "c/ Filter%17^N Toggle type-to-nav\n" + "c/ Filter%17^K Toggle type-to-nav\n" "aEsc Exit prompt%12^L Toggle last filter\n" "c. Toggle hidden%05Alt+Esc Unfilter, quit context\n" "0\n" "1FILES\n" - "9o ^O Open with%15n Create new/link\n" + "9o ^O Open with%15c Create new/link\n" "9f ^F File stats%14d Detail mode toggle\n" "b^R Rename/dup%14r Batch rename\n" - "cz Archive%17e Edit file\n" + "cz Archive%17y Edit file\n" "c* Toggle exe%14> Export list\n" "6Space + (Un)select%12m-m Select range/clear\n" "ca Select all%14A Invert sel\n" "9p ^P Copy here%12w ^W Cp/mv sel as\n" - "9v ^V Move here%15E Edit sel list\n" + "9v ^V Move here%15l Edit sel list\n" "9x ^X Delete or trash%09S Listed sel size\n" "cX Delete (rm -rf)%07Esc Send to FIFO\n" "0\n" "1MISC\n" "8Alt ; Select plugin%11= Launch app\n" "9! ^] Shell%19] Cmd prompt\n" - "cc Connect remote%10u Unmount remote/archive\n" + "cC Connect remote%10u Unmount remote/archive\n" "9t ^T Sort toggles%12s Manage session\n" "cT Set time type%110 Lock\n" "b^L Redraw%18? Help, conf\n" diff --git a/src/nnn.h b/src/nnn.h index bd500244..43b7fa22 100644 --- a/src/nnn.h +++ b/src/nnn.h @@ -139,12 +139,12 @@ static struct key bindings[] = { { '\r', SEL_OPEN }, /* Pure navigate inside */ { KEY_RIGHT, SEL_NAV_IN }, - { 'l', SEL_NAV_IN }, + { 'i', SEL_NAV_IN }, /* Next */ - { 'j', SEL_NEXT }, + { 'n', SEL_NEXT }, { KEY_DOWN, SEL_NEXT }, /* Previous */ - { 'k', SEL_PREV }, + { 'e', SEL_PREV }, { KEY_UP, SEL_PREV }, /* Page down */ { KEY_NPAGE, SEL_PGDN }, @@ -157,11 +157,11 @@ static struct key bindings[] = { /* First entry */ { KEY_HOME, SEL_HOME }, { 'g', SEL_HOME }, - { CONTROL('A'), SEL_HOME }, + { CONTROL('E'), SEL_HOME }, /* Last entry */ { KEY_END, SEL_END }, { 'G', SEL_END }, - { CONTROL('E'), SEL_END }, + { CONTROL('N'), SEL_END }, /* Go to first file */ { '\'', SEL_FIRST }, /* Jump to an entry number/offset */ @@ -179,7 +179,7 @@ static struct key bindings[] = { { 'b', SEL_BMOPEN }, { CONTROL('_'), SEL_BMOPEN }, /* Connect to server over SSHFS */ - { 'c', SEL_REMOTE }, + { 'C', SEL_REMOTE }, /* Cycle contexts in forward direction */ { '\t', SEL_CYCLE }, /* Cycle contexts in reverse direction */ @@ -202,7 +202,7 @@ static struct key bindings[] = { /* Filter */ { '/', SEL_FLTR }, /* Toggle filter mode */ - { CONTROL('N'), SEL_MFLTR }, + { CONTROL('K'), SEL_MFLTR }, /* Toggle hide .dot files */ { '.', SEL_HIDDEN }, /* Detailed listing */ @@ -229,7 +229,7 @@ static struct key bindings[] = { /* Invert selection in current dir */ { 'A', SEL_SELINV }, /* List, edit selection */ - { 'E', SEL_SELEDIT }, + { 'l', SEL_SELEDIT }, /* Copy from selection buffer */ { 'p', SEL_CP }, { CONTROL('P'), SEL_CP }, @@ -247,7 +247,7 @@ static struct key bindings[] = { { 'o', SEL_OPENWITH }, { CONTROL('O'), SEL_OPENWITH }, /* Create a new file */ - { 'n', SEL_NEW }, + { 'c', SEL_NEW }, /* Show rename prompt */ { CONTROL('R'), SEL_RENAME }, /* Rename contents of current dir */ @@ -259,7 +259,7 @@ static struct key bindings[] = { /* Toggle auto-advance on file open */ { CONTROL('J'), SEL_AUTONEXT }, /* Edit in EDITOR */ - { 'e', SEL_EDIT }, + { 'y', SEL_EDIT }, /* Run a plugin */ { ';', SEL_PLUGIN }, /* Show total size of listed selection */ nnn-5.0/patches/gitstatus/000077500000000000000000000000001466310014300156235ustar00rootroot00000000000000nnn-5.0/patches/gitstatus/mainline.diff000066400000000000000000000146261466310014300202620ustar00rootroot00000000000000# Description: Add git status column to detail mode. Provides additional # command line flag -G which will render the git status # column also in normal mode. Vim plugin users may consider # adding the -G flag to their command override. # # Authors: Luuk van Baal diff --git a/src/nnn.c b/src/nnn.c index 936e9c02..22032dcd 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -286,6 +286,25 @@ #define VFS_USED 1 #define VFS_SIZE 2 +/* Git icons */ +#ifdef NERD +#define GIT_ADD "" +#define GIT_DEL "" +#define GIT_IGN "" +#define GIT_MOD "" +#define GIT_NEW "" +#define GIT_NON "-" +#define GIT_UPD "󰚰" +#else +#define GIT_ADD "A" +#define GIT_DEL "D" +#define GIT_IGN "!" +#define GIT_MOD "M" +#define GIT_NEW "?" +#define GIT_NON "-" +#define GIT_UPD "U" +#endif + /* TYPE DEFINITIONS */ typedef unsigned int uint_t; typedef unsigned char uchar_t; @@ -310,6 +329,7 @@ typedef struct entry { uid_t uid; /* 4 bytes */ gid_t gid; /* 4 bytes */ #endif + char git_status[2][5]; } *pEntry; /* Selection marker */ @@ -365,6 +385,7 @@ typedef struct { uint_t cliopener : 1; /* All-CLI app opener */ uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */ uint_t rollover : 1; /* Roll over at edges */ + uint_t normalgit : 1; /* Show git status in normal mode */ } settings; /* Non-persistent program-internal states (alphabeical order) */ @@ -418,7 +439,17 @@ typedef struct { } session_header_t; #endif +typedef struct { + char status[2]; + char path[PATH_MAX]; +} git_status_t; + /* GLOBALS */ +struct { + bool show; + size_t len; + git_status_t *statuses; +} git_statuses; /* Configuration, contexts */ static settings cfg = { @@ -3950,6 +3981,47 @@ static int get_kv_key(kv *kvarr, char *val, uchar_t max, uchar_t id) return -1; } +static size_t get_git_statuses(const char *path) +{ + static char gst[] = "git -c core.quotePath= status -s --no-renames --ignored=matching -unormal . 2>/dev/null"; + FILE *fp = popen(gst, "r"); + char status[PATH_MAX]; + size_t pathindex, i = -1; + git_statuses.show = FALSE; + + while (fgets(status, PATH_MAX, fp)) { + pathindex = (status[3] == '"') ? 4 : 3; + if (!cfg.showhidden && status[pathindex] == '.') + continue; + status[xstrlen(status) - pathindex + 2] = '\0'; + git_statuses.statuses = xrealloc(git_statuses.statuses, sizeof(git_status_t) * (++i + 1)); + git_statuses.statuses[i].status[0] = status[0]; + git_statuses.statuses[i].status[1] = status[1]; + mkpath(path, status + pathindex, git_statuses.statuses[i].path); + } + + pclose(fp); + return (i + 1); +} + +static void set_git_status(char status[][5], uint_t nr) +{ + for (int j = 0; j < 2; j++) { + if (status[j][0] == '-') + switch (git_statuses.statuses[nr].status[j]) { + case ' ': xstrsncpy(status[j], GIT_NON, 4); break; + case 'M': xstrsncpy(status[j], GIT_MOD, 4); break; + case 'A': xstrsncpy(status[j], GIT_ADD, 4); break; + case '?': xstrsncpy(status[j], GIT_NEW, 4); break; + case '!': xstrsncpy(status[j], GIT_IGN, 4); break; + case 'D': xstrsncpy(status[j], GIT_DEL, 4); break; + case 'U': xstrsncpy(status[j], GIT_UPD, 4); break; + } + } + if (git_statuses.statuses[nr].status[1] != '!') + git_statuses.show = TRUE; +} + static void resetdircolor(int flags) { /* Directories are always shown on top, clear the color when moving to first file */ @@ -4283,6 +4355,10 @@ static void printent(int pdents_index, uint_t namecols, bool sel) uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs); + if (git_statuses.show && (cfg.showdetail || cfg.normalgit)) + printw("%*s%s%s", (cfg.normalgit && !cfg.showdetail) ? 1 : 0, "", + ent->git_status[0], ent->git_status[1]); + addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' '); if (g_state.oldcolor) @@ -5788,6 +5864,11 @@ static int dentfill(char *path, struct entry **ppdents) attron(COLOR_PAIR(cfg.curctx + 1)); } + char linkpath[PATH_MAX]; + if ((git_statuses.len = get_git_statuses(path))) + if (!realpath(path, linkpath)) + printwarn(NULL); + #if _POSIX_C_SOURCE >= 200112L posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); #endif @@ -5988,6 +6069,29 @@ static int dentfill(char *path, struct entry **ppdents) #endif } + if (git_statuses.len) { + char dentpath[PATH_MAX]; + size_t pathlen = mkpath(linkpath, dentp->name, dentpath); + dentp->git_status[0][0] = dentp->git_status[1][0] = '-'; + dentp->git_status[0][1] = dentp->git_status[1][1] = '\0'; + + if (dentp->flags & DIR_OR_DIRLNK) { + char prefix[PATH_MAX]; + memccpy(prefix, dentpath, '\0', PATH_MAX); + prefix[pathlen - 1] = '/'; + + for (size_t i = 0; i < git_statuses.len; ++i) + if (is_prefix(git_statuses.statuses[i].path, prefix, pathlen)) + set_git_status(dentp->git_status, i); + } else { + for (size_t i = 0; i < git_statuses.len; ++i) + if (!xstrcmp(git_statuses.statuses[i].path, dentpath)) { + set_git_status(dentp->git_status, i); + break; + } + } + } + ++ndents; } while ((dp = readdir(dirp))); @@ -6569,11 +6673,12 @@ static int adjust_cols(int n) #endif if (cfg.showdetail) { /* Fallback to light mode if less than 35 columns */ - if (n < 36) + if (n < 38) cfg.showdetail ^= 1; else /* 2 more accounted for below */ - n -= 32; - } + n -= (git_statuses.show ? 34 : 32); + } else if (cfg.normalgit && git_statuses.show) + n -= 3; /* 2 columns for preceding space and indicator */ return (n - 2); @@ -8401,6 +8506,7 @@ static void usage(void) " -F val fifo mode [0:preview 1:explore]\n" #endif " -g regex filters\n" + " -G always show git status\n" " -H show hidden files\n" " -i show current file info\n" " -J no auto-advance on selection\n" @@ -8544,6 +8650,7 @@ static void cleanup(void) fflush(stdout); } #endif + free(git_statuses.statuses); free(selpath); free(plgpath); free(cfgpath); @@ -8589,7 +8696,7 @@ int main(int argc, char *argv[]) while ((opt = (env_opts_id > 0 ? env_opts[--env_opts_id] - : getopt(argc, argv, "aAb:BcCdDeEfF:gHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) { + : getopt(argc, argv, "aAb:BcCdDeEfF:gGHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) { switch (opt) { #ifndef NOFIFO case 'a': @@ -8643,6 +8750,9 @@ int main(int argc, char *argv[]) cfg.regex = 1; filterfn = &visible_re; break; + case 'G': + cfg.normalgit = 1; + break; case 'H': cfg.showhidden = 1; break; nnn-5.0/patches/gitstatus/namefirst.diff000066400000000000000000000153401466310014300204500ustar00rootroot00000000000000# Description: Add git status column to detail mode. Provides additional # command line flag -G which will render the git status # column also in normal mode. Vim plugin users may consider # adding the -G flag to their command override. # Compatibility patch for the namefirst patch. # # Authors: Luuk van Baal diff --git a/src/nnn.c b/src/nnn.c index 88538787..d4af7c43 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -270,6 +270,25 @@ #define VFS_USED 1 #define VFS_SIZE 2 +/* Git icons */ +#ifdef NERD +#define GIT_ADD "" +#define GIT_DEL "" +#define GIT_IGN "" +#define GIT_MOD "" +#define GIT_NEW "" +#define GIT_NON "-" +#define GIT_UPD "󰚰" +#else +#define GIT_ADD "A" +#define GIT_DEL "D" +#define GIT_IGN "!" +#define GIT_MOD "M" +#define GIT_NEW "?" +#define GIT_NON "-" +#define GIT_UPD "U" +#endif + /* TYPE DEFINITIONS */ typedef unsigned int uint_t; typedef unsigned char uchar_t; @@ -294,6 +313,7 @@ typedef struct entry { uid_t uid; /* 4 bytes */ gid_t gid; /* 4 bytes */ #endif + char git_status[2][5]; } *pEntry; /* Selection marker */ @@ -349,6 +369,7 @@ typedef struct { uint_t cliopener : 1; /* All-CLI app opener */ uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */ uint_t rollover : 1; /* Roll over at edges */ + uint_t normalgit : 1; /* Show git status in normal mode */ } settings; /* Non-persistent program-internal states (alphabeical order) */ @@ -404,7 +425,17 @@ static struct { ushort_t maxnameln, maxsizeln, maxuidln, maxgidln, maxentln, uidln, gidln, printguid; } dtls; +typedef struct { + char status[2]; + char path[PATH_MAX]; +} git_status_t; + /* GLOBALS */ +struct { + bool show; + size_t len; + git_status_t *statuses; +} git_statuses; /* Configuration, contexts */ static settings cfg = { @@ -3804,6 +3835,47 @@ static int get_kv_key(kv *kvarr, char *val, uchar_t max, uchar_t id) return -1; } +static size_t get_git_statuses(const char *path) +{ + static char gst[] = "git -c core.quotePath= status -s --no-renames --ignored=matching -unormal . 2>/dev/null"; + FILE *fp = popen(gst, "r"); + char status[PATH_MAX]; + size_t pathindex, i = -1; + git_statuses.show = FALSE; + + while (fgets(status, PATH_MAX, fp)) { + pathindex = (status[3] == '"') ? 4 : 3; + if (!cfg.showhidden && status[pathindex] == '.') + continue; + status[xstrlen(status) - pathindex + 2] = '\0'; + git_statuses.statuses = xrealloc(git_statuses.statuses, sizeof(git_status_t) * (++i + 1)); + git_statuses.statuses[i].status[0] = status[0]; + git_statuses.statuses[i].status[1] = status[1]; + mkpath(path, status + pathindex, git_statuses.statuses[i].path); + } + + pclose(fp); + return (i + 1); +} + +static void set_git_status(char status[][5], uint_t nr) +{ + for (int j = 0; j < 2; j++) { + if (status[j][0] == '-') + switch (git_statuses.statuses[nr].status[j]) { + case ' ': xstrsncpy(status[j], GIT_NON, 4); break; + case 'M': xstrsncpy(status[j], GIT_MOD, 4); break; + case 'A': xstrsncpy(status[j], GIT_ADD, 4); break; + case '?': xstrsncpy(status[j], GIT_NEW, 4); break; + case '!': xstrsncpy(status[j], GIT_IGN, 4); break; + case 'D': xstrsncpy(status[j], GIT_DEL, 4); break; + case 'U': xstrsncpy(status[j], GIT_UPD, 4); break; + } + } + if (git_statuses.statuses[nr].status[1] != '!') + git_statuses.show = TRUE; +} + static void resetdircolor(int flags) { /* Directories are always shown on top, clear the color when moving to first file */ @@ -4104,6 +4176,9 @@ static void printent(const struct entry *ent, uint_t namecols, bool sel) int attrs = 0, namelen; uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs); + if (git_statuses.show && (cfg.showdetail || cfg.normalgit)) + printw(" %s%s", ent->git_status[0], ent->git_status[1]); + addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' '); if (g_state.oldcolor) @@ -5598,6 +5673,11 @@ static int dentfill(char *path, struct entry **ppdents) attron(COLOR_PAIR(cfg.curctx + 1)); } + char linkpath[PATH_MAX]; + if ((git_statuses.len = get_git_statuses(path))) + if (!realpath(path, linkpath)) + printwarn(NULL); + #if _POSIX_C_SOURCE >= 200112L posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); #endif @@ -5798,6 +5878,29 @@ static int dentfill(char *path, struct entry **ppdents) #endif } + if (git_statuses.len) { + char dentpath[PATH_MAX]; + size_t pathlen = mkpath(linkpath, dentp->name, dentpath); + dentp->git_status[0][0] = dentp->git_status[1][0] = '-'; + dentp->git_status[0][1] = dentp->git_status[1][1] = '\0'; + + if (dentp->flags & DIR_OR_DIRLNK) { + char prefix[PATH_MAX]; + memccpy(prefix, dentpath, '\0', PATH_MAX); + prefix[pathlen - 1] = '/'; + + for (size_t i = 0; i < git_statuses.len; ++i) + if (is_prefix(git_statuses.statuses[i].path, prefix, pathlen)) + set_git_status(dentp->git_status, i); + } else { + for (size_t i = 0; i < git_statuses.len; ++i) + if (!xstrcmp(git_statuses.statuses[i].path, dentpath)) { + set_git_status(dentp->git_status, i); + break; + } + } + } + ++ndents; } while ((dp = readdir(dirp))); @@ -6362,7 +6465,8 @@ static int adjust_cols(int n) cfg.showdetail ^= 1; else /* 2 more accounted for below */ n -= (dtls.maxentln - 2 - dtls.maxnameln); - } + } else if (cfg.normalgit && git_statuses.show) + n -= 3; /* 2 columns for preceding space and indicator */ return (n - 2); @@ -6517,7 +6621,7 @@ static void redraw(char *path) } #endif } - dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 29) : 26); + dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 3) : 0) + (git_statuses.show ? 29 : 26); } ncols = adjust_cols(ncols); @@ -8153,6 +8257,7 @@ static void usage(void) " -F val fifo mode [0:preview 1:explore]\n" #endif " -g regex filters\n" + " -G always show git status\n" " -H show hidden files\n" " -i show current file info\n" " -J no auto-advance on selection\n" @@ -8292,6 +8397,7 @@ static void cleanup(void) fflush(stdout); } #endif + free(git_statuses.statuses); free(selpath); free(plgpath); free(cfgpath); @@ -8336,7 +8442,7 @@ int main(int argc, char *argv[]) while ((opt = (env_opts_id > 0 ? env_opts[--env_opts_id] - : getopt(argc, argv, "aAb:BcCdDeEfF:gHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) { + : getopt(argc, argv, "aAb:BcCdDeEfF:gGHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) { switch (opt) { #ifndef NOFIFO case 'a': @@ -8390,6 +8496,9 @@ int main(int argc, char *argv[]) cfg.regex = 1; filterfn = &visible_re; break; + case 'G': + cfg.normalgit = 1; + break; case 'H': cfg.showhidden = 1; break; nnn-5.0/patches/namefirst/000077500000000000000000000000001466310014300155645ustar00rootroot00000000000000nnn-5.0/patches/namefirst/mainline.diff000066400000000000000000000153261466310014300202210ustar00rootroot00000000000000# Description: Prints filenames first in the detail view. Prints user/group # columns when a directory contains different users/groups. # # Author: Luuk van Baal diff --git a/src/nnn.c b/src/nnn.c index f8a2c58..9802a1f 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -394,6 +394,10 @@ typedef struct { } session_header_t; #endif +static struct { + ushort_t maxnameln, maxsizeln, maxuidln, maxgidln, maxentln, uidln, gidln, printguid; +} dtls; + /* GLOBALS */ /* Configuration, contexts */ @@ -1070,10 +1074,12 @@ static char *getpwname(uid_t uid) static char *namecache; if (uidcache != uid) { + if (dtls.maxuidln && !dtls.printguid) dtls.printguid = 1; struct passwd *pw = getpwuid(uid); uidcache = uid; namecache = pw ? pw->pw_name : NULL; + dtls.uidln = xstrlen(namecache ? namecache : xitoa(uid)); } return namecache ? namecache : xitoa(uid); @@ -1085,10 +1091,12 @@ static char *getgrname(gid_t gid) static char *grpcache; if (gidcache != gid) { + if (dtls.maxgidln && !dtls.printguid) dtls.printguid = 1; struct group *gr = getgrgid(gid); gidcache = gid; grpcache = gr ? gr->gr_name : NULL; + dtls.gidln = xstrlen(grpcache ? grpcache : xitoa(gid)); } return grpcache ? grpcache : xitoa(gid); @@ -3834,14 +3842,13 @@ static void resetdircolor(int flags) * Max supported str length: NAME_MAX; */ #ifdef NOLC -static char *unescape(const char *str, uint_t maxcols) +static size_t unescape(const char *str, uint_t maxcols) { char * const wbuf = g_buf; char *buf = wbuf; - - xstrsncpy(wbuf, str, maxcols); + size_t len = xstrsncpy(wbuf, str, maxcols); #else -static wchar_t *unescape(const char *str, uint_t maxcols) +static size_t unescape(const char *str, uint_t maxcols) { wchar_t * const wbuf = (wchar_t *)g_buf; wchar_t *buf = wbuf; @@ -3866,7 +3873,7 @@ static wchar_t *unescape(const char *str, uint_t maxcols) ++buf; } - return wbuf; + return len; } static off_t get_size(off_t size, off_t *pval, int comp) @@ -4228,38 +4235,13 @@ static void printent(int pdents_index, uint_t namecols, bool sel) { const struct entry *ent = &pdents[pdents_index]; char ind = '\0'; - int attrs; - - if (cfg.showdetail) { - int type = ent->mode & S_IFMT; - char perms[6] = {' ', ' ', (char)('0' + ((ent->mode >> 6) & 7)), - (char)('0' + ((ent->mode >> 3) & 7)), - (char)('0' + (ent->mode & 7)), '\0'}; - - addch(' '); - attrs = g_state.oldcolor ? (resetdircolor(ent->flags), A_DIM) - : (fcolors[C_MIS] ? COLOR_PAIR(C_MIS) : 0); - if (attrs) - attron(attrs); - - /* Print details */ - print_time(&ent->sec, ent->flags); - - printw("%s%9s ", perms, (type == S_IFREG || type == S_IFDIR) - ? coolsize(cfg.blkorder ? (blkcnt_t)ent->blocks << blk_shift : ent->size) - : (type = (uchar_t)get_detail_ind(ent->mode), (char *)&type)); - - if (attrs) - attroff(attrs); - } + int attrs = 0, namelen; if (g_state.showlines) { ptrdiff_t rel_num = pdents_index - cur; printw(rel_num == 0 ? "%4td" : "%+4td", rel_num); } - attrs = 0; - uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs); addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' '); @@ -4284,15 +4266,40 @@ static void printent(int pdents_index, uint_t namecols, bool sel) ++namecols; #ifndef NOLC - addwstr(unescape(ent->name, namecols)); + addwstr((namelen = unescape(ent->name, namecols), (wchar_t *)g_buf)); #else - addstr(unescape(ent->name, MIN(namecols, ent->nlen) + 1)); + addstr((namelen = unescape(ent->name, MIN(namecols, ent->nlen) + 1), (char *)g_buf)); #endif - if (attrs) + if (!sel && attrs) attroff(attrs); if (ind) addch(ind); + if (cfg.showdetail) { + int type = ent->mode & S_IFMT; + char perms[6] = {(char)('0' + ((ent->mode >> 6) & 7)), + (char)('0' + ((ent->mode >> 3) & 7)), + (char)('0' + (ent->mode & 7)), ' ', ' ', '\0'}, *size = NULL; + + if (attrs) + attron(attrs); + if (!g_state.oldcolor && (type == S_IFDIR || (type == S_IFLNK && ent->flags & DIR_OR_DIRLNK))) + attroff(A_BOLD); + int sizelen = (type == S_IFREG || type == S_IFDIR) ? xstrlen(size = coolsize(cfg.blkorder ? ent->blocks << blk_shift : ent->size)) : 1; + printw("%*c%*s%s%s", 1 + MIN(namecols, dtls.maxnameln + (uint_t)(ind ? 0 : 1)) - namelen, ' ', + dtls.maxsizeln - sizelen, "", size ? size : (type = (uchar_t)get_detail_ind(ent->mode), (char *)&type), " "); +#ifndef NOUG + if (g_state.uidgid && dtls.printguid) { + addstr(getpwname(ent->uid)); + printw("%*c%s", dtls.maxuidln + 1 - dtls.uidln, ' ', getgrname(ent->gid)); + printw("%*c", dtls.maxgidln + 2 - dtls.gidln, ' '); + } +#endif + addstr(perms); + print_time(&ent->sec, ent->flags); + } + if (attrs) + attroff(attrs); } /** @@ -6527,26 +6534,19 @@ static void statusbar(char *path) tocursor(); } -static inline void markhovered(void) -{ - if (cfg.showdetail && ndents) { /* Bold forward arrowhead */ - tocursor(); - addch('>' | A_BOLD); - } -} - static int adjust_cols(int n) { /* Calculate the number of cols available to print entry name */ #ifdef ICONS_ENABLED n -= (g_state.oldcolor ? 0 : ICON_SIZE + ICON_PADDING_LEFT_LEN + ICON_PADDING_RIGHT_LEN); #endif + if (cfg.showdetail) { /* Fallback to light mode if less than 35 columns */ - if (n < 36) + if (n < (dtls.maxentln + 1 - dtls.maxnameln)) cfg.showdetail ^= 1; else /* 2 more accounted for below */ - n -= 32; + n -= (dtls.maxentln - 2 - dtls.maxnameln); } /* 2 columns for preceding space and indicator */ @@ -6412,8 +6411,6 @@ static void draw_line(int ncols) /* Must reset e.g. no files in dir */ if (dir) attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); - - markhovered(); } static void redraw(char *path) @@ -6521,6 +6518,21 @@ static void redraw(char *path) onscreen = MIN(onscreen + curscroll, ndents); + if (cfg.showdetail) { + ushort_t lenbuf = dtls.maxnameln = dtls.maxsizeln = dtls.maxuidln = dtls.maxgidln = dtls.printguid = 0; + for (i = curscroll; i < onscreen; ++i) { + if ((lenbuf = pdents[i].nlen - 1) > dtls.maxnameln) dtls.maxnameln = lenbuf; + if ((lenbuf = xstrlen(coolsize(cfg.blkorder ? pdents[i].blocks << blk_shift : pdents[i].size))) > dtls.maxsizeln) dtls.maxsizeln = lenbuf; +#ifndef NOUG + if (g_state.uidgid) { + if ((getpwname(pdents[i].uid), dtls.uidln) > dtls.maxuidln) dtls.maxuidln = dtls.uidln; + if ((getgrname(pdents[i].gid), dtls.gidln) > dtls.maxgidln) dtls.maxgidln = dtls.gidln; + } +#endif + } + dtls.maxentln = dtls.maxnameln + dtls.maxsizeln + (dtls.printguid ? (dtls.maxuidln + dtls.maxgidln + 29) : 26); + } + ncols = adjust_cols(ncols); int len = scanselforpath(path, FALSE); @@ -6551,7 +6563,7 @@ static void redraw(char *path) #endif } - markhovered(); + statusbar(path); } static bool cdprep(char *lastdir, char *lastname, char *path, char *newpath) nnn-5.0/patches/restorepreview/000077500000000000000000000000001466310014300166615ustar00rootroot00000000000000nnn-5.0/patches/restorepreview/mainline.diff000066400000000000000000000174361466310014300213220ustar00rootroot00000000000000# Description: Adds preview pipe to enable closing and re-opening the preview # pane when running an undetached editor. If you are using vim # you might experience incorrectly resized window. Consider adding # the following to your vimrc: # autocmd VimEnter * :silent exec "!kill -s WINCH $PPID" # # Authors: Luuk van Baal diff --git a/src/nnn.c b/src/nnn.c index 0388b23c..66d3316a 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -391,7 +391,8 @@ typedef struct { uint_t usebsdtar : 1; /* Use bsdtar as default archive utility */ uint_t xprompt : 1; /* Use native prompt instead of readline prompt */ uint_t showlines : 1; /* Show line numbers */ - uint_t reserved : 3; /* Adjust when adding/removing a field */ + uint_t previewer : 1; /* Run state of previewer */ + uint_t reserved : 2; /* Adjust when adding/removing a field */ } runstate; /* Contexts or workspaces */ @@ -516,6 +517,9 @@ alignas(max_align_t) static char g_tmpfpath[TMP_LEN_MAX]; /* Buffer to store plugins control pipe location */ alignas(max_align_t) static char g_pipepath[TMP_LEN_MAX]; +/* Buffer to store preview plugins control pipe location */ +static char g_ppipepath[TMP_LEN_MAX] __attribute__ ((aligned)); + /* Non-persistent runtime states */ static runstate g_state; @@ -696,12 +700,13 @@ static const char * const messages[] = { #define NNN_FCOLORS 5 #define NNNLVL 6 #define NNN_PIPE 7 -#define NNN_MCLICK 8 -#define NNN_SEL 9 -#define NNN_ARCHIVE 10 -#define NNN_ORDER 11 -#define NNN_HELP 12 /* strings end here */ -#define NNN_TRASH 13 /* flags begin here */ +#define NNN_PPIPE 8 +#define NNN_MCLICK 9 +#define NNN_SEL 10 +#define NNN_ARCHIVE 11 +#define NNN_ORDER 12 +#define NNN_HELP 13 /* strings end here */ +#define NNN_TRASH 14 /* flags begin here */ static const char * const env_cfg[] = { "NNN_OPTS", @@ -712,6 +717,7 @@ static const char * const env_cfg[] = { "NNN_FCOLORS", "NNNLVL", "NNN_PIPE", + "NNN_PPIPE", "NNN_MCLICK", "NNN_SEL", "NNN_ARCHIVE", @@ -850,7 +856,7 @@ static int set_sort_flags(int r); static void statusbar(char *path); static bool get_output(char *file, char *arg1, char *arg2, int fdout, bool page); #ifndef NOFIFO -static void notify_fifo(bool force); +static void notify_fifo(bool force, bool closepreview); #endif /* Functions */ @@ -3140,7 +3146,7 @@ try_quit: } else { #ifndef NOFIFO if (!g_state.fifomode) - notify_fifo(TRUE); /* Send hovered path to NNN_FIFO */ + notify_fifo(TRUE, FALSE); /* Send hovered path to NNN_FIFO */ #endif escaped = TRUE; settimeout(); @@ -5258,15 +5264,20 @@ static void run_cmd_as_plugin(const char *file, uchar_t flags) static bool plctrl_init(void) { - size_t len; + size_t len, lenbuf; + pid_t pid = getpid(); /* g_tmpfpath is used to generate tmp file names */ g_tmpfpath[tmpfplen - 1] = '\0'; - len = xstrsncpy(g_pipepath, g_tmpfpath, TMP_LEN_MAX); + len = lenbuf = xstrsncpy(g_pipepath, g_tmpfpath, TMP_LEN_MAX); g_pipepath[len - 1] = '/'; - len = xstrsncpy(g_pipepath + len, "nnn-pipe.", TMP_LEN_MAX - len) + len; - xstrsncpy(g_pipepath + len - 1, xitoa(getpid()), TMP_LEN_MAX - len); + xstrsncpy(g_ppipepath, g_pipepath, TMP_LEN_MAX); + len += xstrsncpy(g_pipepath + len, "nnn-pipe.", TMP_LEN_MAX - len); + xstrsncpy(g_pipepath + len - 1, xitoa(pid), TMP_LEN_MAX - len); + len = xstrsncpy(g_ppipepath + lenbuf, "nnn-ppipe.", TMP_LEN_MAX - lenbuf) + lenbuf; + xstrsncpy(g_ppipepath + len - 1, xitoa(pid), TMP_LEN_MAX - len); setenv(env_cfg[NNN_PIPE], g_pipepath, TRUE); + setenv(env_cfg[NNN_PPIPE], g_ppipepath, TRUE); return EXIT_SUCCESS; } @@ -5295,6 +5306,21 @@ static ssize_t read_nointr(int fd, void *buf, size_t count) return len; } +void *previewpipe(void *arg __attribute__ ((unused))) +{ + int fd, buf; + + mkfifo(g_ppipepath, 0600); + fd = open(g_ppipepath, O_RDONLY); + + if (read(fd, &buf, 1) == 1) + g_state.previewer = buf; + + close(fd); + unlink(g_ppipepath); + return NULL; +} + static char *readpipe(int fd, char *ctxnum, char **path) { char ctx, *nextpath = NULL; @@ -5979,7 +6005,7 @@ static void populate(char *path, char *lastname) } #ifndef NOFIFO -static void notify_fifo(bool force) +static void notify_fifo(bool force, bool closepreview) { if (!fifopath) return; @@ -5995,6 +6021,12 @@ static void notify_fifo(bool force) } } + if (closepreview) { + if (write(fifofd, "close\n", 6) != 6) + xerror(); + return; + } + static struct entry lastentry; if (!force && !memcmp(&lastentry, &pdents[cur], sizeof(struct entry))) // NOLINT @@ -6027,7 +6059,7 @@ static void send_to_explorer(int *presel) if (fd > 1) close(fd); } else - notify_fifo(TRUE); /* Send opened path to NNN_FIFO */ + notify_fifo(TRUE, FALSE); /* Send opened path to NNN_FIFO */ } #endif @@ -6060,7 +6092,7 @@ static void move_cursor(int target, int ignore_scrolloff) #ifndef NOFIFO if (!g_state.fifomode) - notify_fifo(FALSE); /* Send hovered path to NNN_FIFO */ + notify_fifo(FALSE, FALSE); /* Send hovered path to NNN_FIFO */ #endif } @@ -6733,7 +6765,7 @@ static bool browse(char *ipath, const char *session, int pkey) pEntry pent; enum action sel; struct stat sb; - int r = -1, presel, selstartid = 0, selendid = 0; + int r = -1, presel, selstartid = 0, selendid = 0, previewkey = 0; const uchar_t opener_flags = (cfg.cliopener ? F_CLI : (F_NOTRACE | F_NOSTDIN | F_NOWAIT)); bool watch = FALSE, cd = TRUE; ino_t inode = 0; @@ -6991,7 +7023,7 @@ nochange: move_cursor(r, 1); #ifndef NOFIFO else if ((event.bstate == BUTTON1_PRESSED) && !g_state.fifomode) - notify_fifo(TRUE); /* Send clicked path to NNN_FIFO */ + notify_fifo(TRUE, FALSE); /* Send clicked path to NNN_FIFO */ #endif /* Handle right click selection */ if (event.bstate == BUTTON3_PRESSED) { @@ -7153,7 +7185,14 @@ nochange: && strstr(g_buf, "text") #endif ) { + if (g_state.previewer) + notify_fifo(FALSE, TRUE); spawn(editor, newpath, NULL, NULL, F_CLI); + if (g_state.previewer) { + pkey = previewkey; + goto run_plugin; + } + if (cfg.filtermode) { presel = FILTER; clearfilter(); @@ -7471,8 +7510,14 @@ nochange: copycurname(); goto nochange; case SEL_EDIT: + if (g_state.previewer) + notify_fifo(FALSE, TRUE); if (!(g_state.picker || g_state.fifomode)) spawn(editor, newpath, NULL, NULL, F_CLI); + if (g_state.previewer) { + pkey = previewkey; + goto run_plugin; + } continue; default: /* SEL_LOCK */ lock_terminal(); @@ -7860,6 +7905,7 @@ nochange: cd = FALSE; goto begin; } +run_plugin: case SEL_PLUGIN: /* Check if directory is accessible */ if (!xdiraccess(plgpath)) { @@ -7885,6 +7931,12 @@ nochange: goto nochange; } + if (xstrcmp(tmp, "preview-tui") == 0) { + previewkey = r; + pthread_t tid; + pthread_create(&tid, NULL, previewpipe, NULL); + } + if (tmp[0] == '-' && tmp[1]) { ++tmp; r = FALSE; /* Do not refresh dir after completion */ @@ -7943,7 +7995,13 @@ nochange: case SEL_SHELL: // fallthrough case SEL_LAUNCH: // fallthrough case SEL_PROMPT: + if (g_state.previewer) + notify_fifo(FALSE, TRUE); r = handle_cmd(sel, path, newpath); + if (g_state.previewer) { + pkey = previewkey; + goto run_plugin; + } /* Continue in type-to-nav mode, if enabled */ if (cfg.filtermode) @@ -8492,8 +8550,10 @@ static void cleanup(void) if (g_state.autofifo) unlink(fifopath); #endif - if (g_state.pluginit) + if (g_state.pluginit){ unlink(g_pipepath); + unlink(g_ppipepath); + } #ifdef DEBUG disabledbg(); #endif @@ -9020,7 +9080,7 @@ int main(int argc, char *argv[]) #ifndef NOFIFO if (!g_state.fifomode) - notify_fifo(FALSE); + notify_fifo(FALSE, TRUE); if (fifofd != -1) close(fifofd); #endif nnn-5.0/plugins/000077500000000000000000000000001466310014300136265ustar00rootroot00000000000000nnn-5.0/plugins/.cbcp000077500000000000000000000027071466310014300145470ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Copy selection to system clipboard as newline-separated entries # Dependencies: # - tr # - xclip/xsel (Linux) # - pbcopy (macOS) # - termux-clipboard-set (Termux) # - clip.exe (WSL) # - clip (Cygwin) # - wl-copy (Wayland) # - clipboard (Haiku) # # Limitation: breaks if a filename has newline in it # # Note: For a space-separated list: # xargs -0 < "$SELECTION" # # Shell: POSIX compliant # Author: Arun Prakash Jana IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} [ -s "$selection" ] || { echo "plugin .cbcp error: empty selection" >&2 ; exit 1; } if [ "$XDG_SESSION_TYPE" = "wayland" ]; then # Wayland tr '\0' '\n' < "$selection" | wl-copy elif type xsel >/dev/null 2>&1; then # Linux tr '\0' '\n' < "$selection" | xsel -bi elif type xclip >/dev/null 2>&1; then # Linux tr '\0' '\n' < "$selection" | xclip -sel clip elif type pbcopy >/dev/null 2>&1; then # macOS tr '\0' '\n' < "$selection" | pbcopy elif type termux-clipboard-set >/dev/null 2>&1; then # Termux tr '\0' '\n' < "$selection" | termux-clipboard-set elif type clip.exe >/dev/null 2>&1; then # WSL tr '\0' '\n' < "$selection" | clip.exe elif type clip >/dev/null 2>&1; then # Cygwin tr '\0' '\n' < "$selection" | clip elif type clipboard >/dev/null 2>&1; then # Haiku tr '\0' '\n' < "$selection" | clipboard --stdin fi nnn-5.0/plugins/.iconlookup000077500000000000000000000530001466310014300160120ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Print icons in front of list of directories/files # Dependencies: awk # Usage # 1. Set colors and/or icons to your liking # 2. Pipe any directory listing to iconlookup and it will output prepended icons # 3. preview-tui uses the script to prepend icon to directory listings # 4. Aditionally you can consider adding it to your PATH and/or FZF_DEFAULT_COMMAND to # make it work with various fzf plugins (make sure you also add --ansi to your FZF_DEFAULT_OPTS) # Shell: POSIX compliant # Author: Luuk van Baal (https://github.com/luukvbaal/iconlookup) icon_lookup() { awk 'BEGIN { # Set your ANSI colorscheme below (https://en.wikipedia.org/wiki/ANSI_escape_code#Colors). # Default uses standard nnn icon colors, 8 and 24-bit nord themes are commented out. colordepth=8 #colordepth=8 #colordepth=24 color_dirtxt=39 #color_dirtxt=111 #color_dirtxt="129;161;193" color_filetxt=15 #color_filetxt=111 #color_filetxt="129;161;193" color_default=39 #color_default=111 #color_default="129;161;193" color_video=93 #color_video=110 #color_video="136;192;208" color_audio=220 #color_audio=150 #color_audio="163;190;140" color_image=82 #color_image=150 #color_image="163;190;140" color_docs=202 #color_docs=173 #color_docs="208;135;112" color_archive=209 #color_archive=179 #color_archive="235;203;139" color_c=81 #color_c=150 #color_c="163;190;140" color_elixir=104 #color_elixir=109 #color_elixir="143;188;187" color_java=32 #color_java=139 #color_java="180;142;173" color_js=47 #color_js=109 #color_js="143;188;187" color_react=39 #color_react=111 #color_react="129;161;193" color_css=199 #color_css=110 #color_css="136;192;208" color_python=227 #color_python=68 #color_python="94;129;172" color_lua=19 #color_lua=167 #color_lua="191;97;106" color_document=15 #color_document=173 #color_document="208;135;112" color_fsharp=31 #color_fsharp=179 #color_fsharp="180;142;173" color_ruby=160 #color_ruby=150 #color_ruby="163;190;140" color_scala=196 #color_scala=139 #color_scala="143;188;187" color_shell=47 #color_shell=109 #color_shell="143;188;187" color_vim=28 #color_vim=109 #color_vim="143;188;187" # icons[][1] contains icon and icons[][2] contains color icons["directory"][1] = ""; icons["directory"][2] = color_default icons["file"][1] = "󰈔"; icons["file"][2] = color_default icons["exec"][1] = ""; icons["exec"][2] = color_default icons["manual"][1] = "󱓷"; icons["manual"][2] = color_docs icons["pipe"][1] = "󰟥"; icons["pipe"][2] = color_default icons["socket"][1] = "󰟩"; icons["socket"][2] = color_default icons["door"][1] = "➡"; icons["door"][2] = color_default # top level and common icons icons[".git"][1] = ""; icons[".git"][2] = color_default icons["desktop"][1] = "󰟀"; icons["desktop"][2] = color_default icons["briefcase"][1] = "󰃖"; icons["briefcase"][2] = color_default icons["document"][1] = "󰃖"; icons["document"][2] = color_default icons["downloads"][1] = "󰃘"; icons["downloads"][2] = color_default icons["music"][1] = "󱍙"; icons["music"][2] = color_default icons["musicfile"][1] = "󰎈"; icons["musicfile"][2] = color_audio icons["pictures"][1] = "󰉔"; icons["pictures"][2] = color_default icons["picturefile"][1] = "󰈟"; icons["picturefile"][2] = color_image icons["public"][1] = ""; icons["public"][2] = color_default icons["templates"][1] = "󰗇"; icons["templates"][2] = color_default icons["videos"][1] = "󰈰"; icons["videos"][2] = color_default icons["videofile"][1] = "󰈫"; icons["videofile"][2] = color_video icons["changelog"][1] = "󰋚"; icons["changelog"][2] = color_docs icons["configure"][1] = ""; icons["configure"][2] = color_default icons["license"][1] = "󰈙"; icons["license"][2] = color_docs icons["makefile"][1] = "󰆍"; icons["makefile"][2] = color_default icons["archive"][1] = "󰀼"; icons["archive"][2] = color_archive icons["rust"][1] = ""; icons["rust"][2] = color_default icons["script"][1] = ""; icons["script"][2] = color_shell icons["subtitle"][1] = "󰅺"; icons["subtitle"][2] = color_default icons["cplusplus"][1] = ""; icons["cplusplus"][2] = color_c icons["java"][1] = ""; icons["java"][2] = color_java icons["clojure"][1] = ""; icons["clojure"][2] = color_default icons["js"][1] = "󰌞"; icons["js"][2] = color_js icons["linux"][1] = "󰌽"; icons["linux"][2] = color_default icons["elixir"][1] = ""; icons["elixir"][2] = color_fsharp icons["fsharp"][1] = ""; icons["fsharp"][2] = color_fsharp icons["ruby"][1] = ""; icons["ruby"][2] = color_ruby icons["c"][1] = ""; icons["c"][2] = color_c icons["chess"][1] = "󰄺"; icons["chess"][2] = color_default icons["haskell"][1] = ""; icons["haskell"][2] = color_vim icons["font"][1] = ""; icons["font"][2] = color_default icons["html"][1] = "󰌝"; icons["html"][2] = color_default icons["react"][1] = ""; icons["react"][2] = color_react icons["python"][1] = ""; icons["python"][2] = color_python icons["database"][1] = "󰆼"; icons["database"][2] = color_default icons["worddoc"][1] = "󰈬"; icons["worddoc"][2] = color_document icons["playlist"][1] = "󱍙"; icons["playlist"][2] = color_audio icons["opticaldisk"][1] = ""; icons["opticaldisk"][2] = color_archive # numbers icons["1"][1] = icons["manual"][1]; icons["1"][2] = icons["manual"][2] icons["7z"][1] = icons["archive"][1]; icons["7z"][2] = icons["archive"][2] # a icons["a"][1] = icons["manual"][1]; icons["a"][2] = icons["manual"][2] icons["apk"][1] = icons["archive"][1]; icons["apk"][2] = icons["archive"][2] icons["asm"][1] = icons["file"][1]; icons["asm"][2] = icons["file"][2] icons["aup"][1] = icons["musicfile"][1]; icons["aup"][2] = icons["musicfile"][2] icons["avi"][1] = icons["videofile"][1]; icons["avi"][2] = icons["videofile"][2] # b icons["bat"][1] = icons["script"][1]; icons["bat"][2] = icons["script"][2] icons["bin"][1] = ""; icons["bin"][2] = color_default icons["bmp"][1] = icons["picturefile"][1]; icons["bmp"][2] = icons["picturefile"][2] icons["bz2"][1] = icons["archive"][1]; icons["bz2"][2] = icons["archive"][2] # c icons["cplusplus"][1] = icons["cplusplus"][1]; icons["cplusplus"][2] = icons["cplusplus"][2] icons["cabal"][1] = icons["haskell"][1]; icons["cab"][2] = icons["haskell"][2] icons["cab"][1] = icons["archive"][1]; icons["cab"][2] = icons["archive"][2] icons["cbr"][1] = icons["archive"][1]; icons["cbr"][2] = icons["archive"][2] icons["cbz"][1] = icons["archive"][1]; icons["cbz"][2] = icons["archive"][2] icons["cc"][1] = icons["cplusplus"][1]; icons["cc"][2] = icons["cplusplus"][2] icons["class"][1] = icons["java"][1]; icons["class"][2] = icons["java"][2] icons["clj"][1] = icons["clojure"][1]; icons["clj"][2] = icons["clojure"][2] icons["cljc"][1] = icons["clojure"][1]; icons["cljc"][2] = icons["clojure"][2] icons["cljs"][1] = icons["clojure"][1]; icons["cljs"][2] = icons["clojure"][2] icons["cmake"][1] = icons["makefile"][1]; icons["cmake"][2] = icons["makefile"][2] icons["coffee"][1] = ""; icons["coffee"][2] = color_default icons["conf"][1] = icons["configure"][1]; icons["conf"][2] = icons["configure"][2] icons["cpio"][1] = icons["archive"][1]; icons["cpio"][2] = icons["archive"][2] icons["cpp"][1] = icons["cplusplus"][1]; icons["cpp"][2] = icons["cplusplus"][2] icons["css"][1] = ""; icons["css"][2] = color_css icons["cue"][1] = icons["playlist"][1]; icons["cue"][2] = icons["playlist"][2] icons["cvs"][1] = icons["configure"][1]; icons["cvs"][2] = icons["configure"][2] icons["cxx"][1] = icons["cplusplus"][1]; icons["cxx"][2] = icons["cplusplus"][2] # d icons["db"][1] = icons["database"][1]; icons["db"][2] = icons["database"][2] icons["deb"][1] = ""; icons["deb"][2] = color_archive icons["diff"][1] = ""; icons["diff"][2] = color_default icons["dll"][1] = icons["script"][1]; icons["dll"][2] = icons["script"][2] icons["doc"][1] = icons["worddoc"][1]; icons["doc"][2] = icons["worddoc"][2] icons["docx"][1] = icons["worddoc"][1]; icons["docx"][2] = icons["worddoc"][2] # e icons["ejs"][1] = icons["js"][1]; icons["ejs"][2] = icons["js"][2] icons["elf"][1] = icons["linux"][1]; icons["elf"][2] = icons["linux"][2] icons["epub"][1] = icons["manual"][1]; icons["epub"][2] = icons["manual"][2] icons["exe"][1] = icons["exec"][1]; icons["exe"][2] = icons["exec"][2] icons["ex"][1] = icons["elixir"][1]; icons["ex"][2] = icons["elixir"][2] icons["eex"][1] = icons["elixir"][1]; icons["eex"][2] = icons["elixir"][2] icons["exs"][1] = icons["elixir"][1]; icons["exs"][2] = icons["elixir"][2] # f icons["fsharp"][1] = icons["fsharp"][1]; icons["fsharp"][2] = icons["fsharp"][2] icons["flac"][1] = icons["musicfile"][1]; icons["flac"][2] = icons["musicfile"][2] icons["fen"][1] = icons["chess"][1]; icons["fen"][2] = icons["chess"][2] icons["flv"][1] = icons["videofile"][1]; icons["flv"][2] = icons["videofile"][2] icons["fs"][1] = icons["fsharp"][1]; icons["fs"][2] = icons["fsharp"][2] icons["fsi"][1] = icons["fsharp"][1]; icons["fsi"][2] = icons["fsharp"][2] icons["fsscript"][1] = icons["fsharp"][1]; icons["fsscript"][2] = icons["fsharp"][2] icons["fsx"][1] = icons["fsharp"][1]; icons["fsx"][2] = icons["fsharp"][2] # g icons["gem"][1] = icons["ruby"][1]; icons["gem"][2] = icons["ruby"][2] icons["gif"][1] = icons["picturefile"][1]; icons["gif"][2] = icons["picturefile"][2] icons["go"][1] = "󰟓"; icons["go"][2] = color_default icons["gz"][1] = icons["archive"][1]; icons["gz"][2] = icons["archive"][2] icons["gzip"][1] = icons["archive"][1]; icons["gzip"][2] = icons["archive"][2] # h icons["h"][1] = icons["c"][1]; icons["h"][2] = icons["c"][2] icons["hh"][1] = icons["cplusplus"][1]; icons["hh"][2] = icons["cplusplus"][2] icons["hpp"][1] = icons["cplusplus"][1]; icons["hpp"][2] = icons["cplusplus"][2] icons["hs"][1] = icons["haskell"][1]; icons["hs"][2] = icons["haskell"][2] icons["htaccess"][1] = icons["configure"][1]; icons["htaccess"][2] = icons["configure"][2] icons["htpasswd"][1] = icons["configure"][1]; icons["htpasswd"][2] = icons["configure"][2] icons["htm"][1] = icons["html"][1]; icons["htm"][2] = icons["html"][2] icons["hxx"][1] = icons["cplusplus"][1]; icons["hxx"][2] = icons["cplusplus"][2] icons["heex"][1] = icons["elixir"][1]; icons["heex"][2] = icons["elixir"][2] # i icons["ico"][1] = icons["picturefile"][1]; icons["ico"][2] = icons["picturefile"][2] icons["img"][1] = icons["opticaldisk"][1]; icons["img"][2] = icons["opticaldisk"][2] icons["ini"][1] = icons["configure"][1]; icons["ini"][2] = icons["configure"][2] icons["iso"][1] = icons["opticaldisk"][1]; icons["iso"][2] = icons["opticaldisk"][2] icons["isub"][1] = icons["subtitle"][1]; icons["isub"][2] = icons["subtitle"][2] # j icons["jar"][1] = icons["java"][1]; icons["jar"][2] = icons["java"][2] icons["java"][1] = icons["java"][1]; icons["java"][2] = icons["java"][2] icons["jl"][1] = icons["configure"][1]; icons["jl"][2] = icons["configure"][2] icons["jpeg"][1] = icons["picturefile"][1]; icons["jpeg"][2] = icons["picturefile"][2] icons["jpg"][1] = icons["picturefile"][1]; icons["jpg"][2] = icons["picturefile"][2] icons["json"][1] = ""; icons["json"][2] = color_js icons["jsx"][1] = icons["react"][1]; icons["jsx"][2] = icons["react"][2] icons["jxl"][1] = icons["picturefile"][1]; icons["jxl"][2] = icons["picturefile"][2] # k icons["ksh"][1] = icons["font"][1]; icons["ksf"][2] = icons["font"][2] # l icons["lha"][1] = icons["archive"][1]; icons["lha"][2] = icons["archive"][2] icons["lhs"][1] = icons["haskell"][1]; icons["lhs"][2] = icons["haskell"][2] icons["ilog"][1] = icons["document"][1]; icons["ilog"][2] = icons["document"][2] icons["lua"][1] = ""; icons["lua"][2] = color_lua icons["lzh"][1] = icons["archive"][1]; icons["lzh"][2] = icons["archive"][2] icons["lzma"][1] = icons["archive"][1]; icons["lzma"][2] = icons["archive"][2] # m icons["m"][1] = "󰠞"; icons["mat"][2] = color_c icons["m4a"][1] = icons["musicfile"][1]; icons["m4a"][2] = icons["musicfile"][2] icons["m4v"][1] = icons["videofile"][1]; icons["m4v"][2] = icons["videofile"][2] icons["mat"][1] = ""; icons["mat"][2] = color_c icons["markdown"][1] = ""; icons["markdown"][2] = color_docs icons["md"][1] = ""; icons["md"][2] = color_docs icons["mk"][1] = icons["makefile"][1]; icons["mk"][2] = icons["makefile"][2] icons["mkv"][1] = icons["videofile"][1]; icons["mkv"][2] = icons["videofile"][2] icons["mov"][1] = icons["videofile"][1]; icons["mov"][2] = icons["videofile"][2] icons["mp3"][1] = icons["musicfile"][1]; icons["mp3"][2] = icons["musicfile"][2] icons["mp4"][1] = icons["videofile"][1]; icons["mp4"][2] = icons["videofile"][2] icons["mpeg"][1] = icons["videofile"][1]; icons["mpeg"][2] = icons["videofile"][2] icons["mpg"][1] = icons["videofile"][1]; icons["mpg"][2] = icons["videofile"][2] icons["msi"][1] = "󰍲"; icons["msi"][2] = color_default # n icons["nix"][1] = ""; icons["nix"][2] = color_fsharp # o icons["o"][1] = icons["manual"][1]; icons["o"][2] = icons["manual"][2] icons["ogg"][1] = icons["musicfile"][1]; icons["ogg"][2] = icons["musicfile"][2] icons["odownload"][1] = icons["download"][1]; icons["odownload"][2] = icons["download"][2] icons["otf"][1] = icons["font"][1]; icons["otf"][2] = icons["font"][2] icons["out"][1] = icons["linux"][1]; icons["out"][2] = icons["linux"][2] # p icons["part"][1] = icons["download"][1]; icons["part"][2] = icons["download"][2] icons["patch"][1] = icons["diff"][1]; icons["patch"][2] = icons["diff"][2] icons["pdf"][1] = "󰈦"; icons["pdf"][2] = color_docs icons["pgn"][1] = icons["chess"][1]; icons["pgn"][2] = icons["chess"][2] icons["php"][1] = ""; icons["php"][2] = color_default icons["png"][1] = icons["picturefile"][1]; icons["png"][2] = icons["picturefile"][2] icons["ppt"][1] = "󰈧"; icons["ppt"][2] = color_default icons["pptx"][1] = "󰈧"; icons["pptx"][2] = color_default icons["psb"][1] = ""; icons["psb"][2] = color_default icons["psd"][1] = ""; icons["psd"][2] = color_default icons["py"][1] = icons["python"][1]; icons["py"][2] = icons["python"][2] icons["pyc"][1] = icons["python"][1]; icons["pyc"][2] = icons["python"][2] icons["pyd"][1] = icons["python"][1]; icons["pyd"][2] = icons["python"][2] icons["pyo"][1] = icons["python"][1]; icons["pyo"][2] = icons["python"][2] # q # r icons["rar"][1] = icons["archive"][1]; icons["rar"][2] = icons["archive"][2] icons["rc"][1] = icons["configure"][1]; icons["rc"][2] = icons["configure"][2] icons["rom"][1] = "󰊖"; icons["rom"][2] = color_default icons["rpm"][1] = icons["archive"][1]; icons["rpm"][2] = icons["archive"][2] icons["rss"][1] = ""; icons["rss"][2] = color_default icons["rtf"][1] = "󰈦"; icons["rtf"][2] = color_default # s icons["sass"][1] = ""; icons["sass"][2] = color_css icons["scss"][1] = ""; icons["scss"][2] = color_css icons["so"][1] = icons["manual"][1]; icons["so"][2] = icons["manual"][2] icons["scala"][1] = ""; icons["scala"][2] = color_scala icons["sh"][1] = icons["script"][1]; icons["sh"][2] = icons["script"][2] icons["slim"][1] = icons["script"][1]; icons["slim"][2] = icons["script"][2] icons["sln"][1] = ""; icons["sln"][2] = color_default icons["sql"][1] = icons["database"][1]; icons["sql"][2] = icons["database"][2] icons["srt"][1] = icons["subtitle"][1]; icons["srt"][2] = icons["subtitle"][2] icons["svg"][1] = icons["picturefile"][1]; icons["svg"][2] = icons["picturefile"][2] # t icons["tar"][1] = icons["archive"][1]; icons["tar"][2] = icons["archive"][2] icons["tex"][1] = "󰙩"; icons["tex"][2] = color_default icons["tgz"][1] = icons["archive"][1]; icons["tgz"][2] = icons["archive"][2] icons["ts"][1] = ""; icons["ts"][2] = color_js icons["tsx"][1] = icons["react"][1]; icons["tsx"][2] = icons["react"][2] icons["txt"][1] = icons["document"][1]; icons["txt"][2] = icons["document"][2] icons["txz"][1] = icons["archive"][1]; icons["txz"][2] = icons["archive"][2] icons["ttf"][1] = icons["font"][1]; icons["ttf"][2] = icons["font"][2] # u # v icons["vid"][1] = icons["videofile"][1]; icons["vid"][2] = icons["videofile"][2] icons["vim"][1] = ""; icons["vim"][2] = color_vim icons["vimrc"][1] = ""; icons["vimrc"][2] = color_vim icons["vtt"][1] = icons["subtitle"][1]; icons["vtt"][2] = icons["subtitle"][2] # w icons["wav"][1] = icons["musicfile"][1]; icons["wav"][2] = icons["musicfile"][2] icons["webm"][1] = icons["videofile"][1]; icons["webm"][2] = icons["videofile"][2] icons["wma"][1] = icons["videofile"][1]; icons["wma"][2] = icons["videofile"][2] icons["wmv"][1] = icons["videofile"][1]; icons["wmv"][2] = icons["videofile"][2] # x icons["xbps"][1] = icons["archive"][1]; icons["xbps"][2] = color_archive icons["xcf"][1] = icons["picturefile"][1]; icons["xcf"][2] = color_image icons["xhtml"][1] = icons["html"][1]; icons["xhtml"][2] = icons["html"][2] icons["xls"][1] = "󰈛"; icons["xls"][2] = color_default icons["xlsx"][1] = "󰈛"; icons["xlsx"][2] = color_default icons["xml"][1] = icons["html"][1]; icons["xml"][2] = icons["html"][2] icons["xz"][1] = icons["archive"][1]; icons["xz"][2] = icons["archive"][2] # y icons["yaml"][1] = icons["configure"][1]; icons["yaml"][2] = icons["configure"][2] icons["yml"][1] = icons["configure"][1]; icons["yml"][2] = icons["configure"][2] # z icons["zip"][1] = icons["archive"][1]; icons["zip"][2] = icons["archive"][2] icons["zsh"][1] = icons["script"][1]; icons["zsh"][2] = icons["script"][2] icons["zst"][1] = icons["archive"][1]; icons["zst"][2] = icons["archive"][2] FS = "." limit = ENVIRON["limit"] switch (colordepth) { case "4": escape="\033[" break; case "8": escape="\033[38;5;" break; case "24": escape="\033[38;2;" break; } bstr = ENVIRON["beforestr"] } { # dont print cwd . and leading ./ from tree -f if ($0 ~/^\.$/) next ent = ($0 ~/^\.\//) ? substr($0, 3, length($0) - 2) : $0 ext = $NF # Print icons, set color and bold directories by using ansi escape codes if (ext in icons) printcolor(icons[ext][1], icons[ext][2], color_filetxt, ent, "10") else switch (substr(ent, length(ent), 1)) { case "/": printcolor(icons["directory"][1], color_default, color_dirtxt, ent, "1") break; case "*": printcolor(icons["exe"][1], color_default, color_filetxt, ent, "10") break; case "|": printcolor(icons["pipe"][1], color_default, color_filetxt, ent, "10") break; case "=": printcolor(icons["socket"][1], color_default, color_filetxt, ent, "10") break; case ">": printcolor(icons["door"][1], color_default, color_filetxt, ent, "10") break; default: printcolor(icons["file"][1], color_default, color_filetxt, ent, "10") } } function printcolor(i, c, d, n, b) { if (limit != "" && length(n) + 2 > limit) n = substr(n, 1, limit - 2) printf "\033[0m" printf "%s%s%s;%sm%s %s%sm%s\n", bstr, escape, c, b, i, escape, d, n }' printf '\033[0m' } print_begin() { printf '%s\n' "$1" | sed 's/\\n/\n/g' } print_end() { printf '%s\n' "$1" | sed 's/\\n/\n/g' } print_help() { printf 'Icon Lookup\n Usage: iconlookup [options] iconlookup [-bBe] [string] iconlookup -l [number] iconlookup (-h | --help) Prepend icons to list of files based on extension or appended indicator by ls/tree "-F" flag ("/" for directory, "*" for executable etc.) Options: -h --help -? Show this screen. -b --before Prepend str before icon. -B --begin Prepend str before output. -e --end Append str after output. -l --limit Limit line length to [number] characters.' } while :; do case $1 in -h|-\?|--help) print_help exit ;; -B|--begin) if [ -n "$2" ]; then print_begin "$2" fi shift ;; -e|--end) if [ -n "$2" ]; then end=1 endstr="$2" fi shift ;; -b|--before) if [ -n "$2" ]; then export beforestr="$2" fi shift ;; -l|--limit) if [ -n "$2" ]; then export limit="$2" shift else printf 'ERROR: "--limit" requires a non-empty option argument.\n' exit fi ;; --) shift break ;; -?*) printf 'WARNING: Unknown option ignored: %s\n' "$1" ;; *) break ;; esac shift done if [ ! -t 0 ]; then [ -n "$beforestr" ] && limit="$((limit - ${#beforestr}))" icon_lookup else printf 'ERROR: no data provided...\nExpecting a directory listing in stdin\n' fi if [ -n "$end" ]; then print_end "$endstr" fi nnn-5.0/plugins/.nmv000077500000000000000000000072271466310014300144420ustar00rootroot00000000000000#!/usr/bin/env bash # Description: An almost fully POSIX compliant batch file renamer # # Note: nnn auto-detects and invokes this plugin if available # Whitespace is used as delimiter for read. # The plugin doesn't support filenames with leading or trailing whitespace # To use NNN_LIST your shell must support readlink(1) # # Capabilities: # 1. Basic file rename # 2. Detects order change # 3. Can move files # 4. Can remove files # 5. Switch number pairs to swap filenames # # Shell: bash # Author: KlzXS # shellcheck disable=SC1090,SC1091 . "$(dirname "$0")"/.nnn-plugin-helper EDITOR="${EDITOR:-vi}" TMPDIR="${TMPDIR:-/tmp}" NNN_INCLUDE_HIDDEN="${NNN_INCLUDE_HIDDEN:-0}" VERBOSE="${VERBOSE:-0}" RECURSIVE="${RECURSIVE:-0}" case "$NNN_TRASH" in 1) RM_UTIL="trash-put" ;; 2) RM_UTIL="gio trash" ;; *) RM_UTIL="rm -ri --" ;; esac exit_status=0 if nnn_use_selection "Rename"; then # shellcheck disable=SC2154 arr=$(tr '\0' '\n' < "$selection") else findcmd="find . ! -name ." if [ "$RECURSIVE" -eq 0 ]; then findcmd="$findcmd -prune" fi if [ "$NNN_INCLUDE_HIDDEN" -eq 0 ]; then findcmd="$findcmd ! -name \".*\"" fi if [ -z "$NNN_LIST" ]; then findcmd="$findcmd -print" else findcmd="$findcmd -printf "'"'"$NNN_LIST/%P\n"'"' fi arr=$(eval "$findcmd" | sort) fi lines=$(printf "%s\n" "$arr" | wc -l) width=${#lines} dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX") trap 'rm -f -- "$dst_file"' EXIT printf "%s" "$arr" | awk '{printf("%'"${width}"'d %s\n", NR, $0)}' > "$dst_file" items=("~") while IFS='' read -r line; do if [ -n "$NNN_LIST" ]; then line=$(readlink "$line" || printf "%s" "$line") fi items+=("$line"); done < <(printf "%s\n" "$arr") $EDITOR "$dst_file" while read -r num name; do if [ -z "$name" ]; then if [ -z "$num" ]; then continue fi printf "%s: unable to parse line, aborting\n" "$0" exit 1 fi # check if $num is an integer if [ ! "$num" -eq "$num" ] 2> /dev/null; then printf "%s: unable to parse line, aborting\n" "$0" exit 1 fi src=${items[$num]} if [ -z "$src" ]; then printf "%s: unknown item number %s\n" "$0" "$num" > /dev/stderr continue elif [ "$name" != "$src" ]; then if [ -z "$name" ]; then continue fi if [ ! -e "$src" ] && [ ! -L "$src" ]; then printf "%s: %s does not exit\n" "$0" "$src" > /dev/stderr unset "items[$num]" continue fi # handle swaps if [ -e "$name" ] || [ -L "$name" ]; then tmp="$name~" c=0 while [ -e "$tmp" ] || [ -L "$tmp" ]; do c=$((c+1)) tmp="$tmp~$c" done if mv -- "$name" "$tmp"; then if [ "$VERBOSE" -ne 0 ]; then printf "'%s' -> '%s'\n" "$name" "$tmp" fi else printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr exit_status=1 fi for key in "${!items[@]}"; do if [ "${items[$key]}" = "$name" ]; then items[$key]="$tmp" fi done fi dir=$(dirname "$name") if [ ! -d "$dir" ] && ! mkdir -p "$dir"; then printf "%s: failed to create directory tree %s\n" "$0" "$dir" > /dev/stderr exit_status=1 elif ! mv -i -- "$src" "$name"; then printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr exit_status=1 else if [ -d "$name" ]; then for key in "${!items[@]}"; do items[$key]=$(printf "%s" "${items[$key]}" | sed "s|^$src\(\$\|\/\)|$name\1|") done if [ "$VERBOSE" -ne 0 ]; then printf "'%s' => '%s'\n" "$src" "$name" fi else true if [ "$VERBOSE" -ne 0 ]; then printf "'%s' -> '%s'\n" "$src" "$name" fi fi fi fi unset "items[$num]" done <"$dst_file" unset "items[0]" for item in "${items[@]}"; do $RM_UTIL "$item" done exit $exit_status nnn-5.0/plugins/.nnn-plugin-helper000066400000000000000000000023501466310014300171710ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Helper script for plugins # # Shell: POSIX compliant # Author: Anna Arad selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} export selection ## Set CUR_CTX to 1 to open directory in current context CUR_CTX=0 export CUR_CTX NNN_PREFER_SELECTION="${NNN_PREFER_SELECTION:-0}" export NNN_PREFER_SELECTION ## Ask nnn to switch to directory $1 in context $2. ## If $2 is not provided, the function asks explicitly. nnn_cd () { dir="$1" if [ -z "$NNN_PIPE" ]; then echo "No pipe file found" 1>&2 return fi if [ -n "$2" ]; then context=$2 elif [ $CUR_CTX -ne 1 ]; then printf "Choose context 1-4 (blank for current): " read -r context fi printf "%s" "${context:-0}c$dir" > "$NNN_PIPE" } cmd_exists () { type "$1" > /dev/null 2>&1 echo $? } nnn_use_selection() { if ! [ -s "$selection" ]; then return 1 fi if [ "$NNN_PREFER_SELECTION" -eq 1 ]; then return 0 else [ -n "$1" ] && printf "%s " "$1" printf "(s)election/(c)urrent? [default=c] " read -r resp__ if [ "$resp__" = "s" ]; then return 0 else return 1 fi fi } nnn-5.0/plugins/.ntfy000077500000000000000000000011601466310014300146100ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Show a notification # # Details: nnn invokes this plugin to show notification when a cp/mv/rm operation is complete. # # Dependencies: notify-send (Ubuntu)/ntfy (https://github.com/dschep/ntfy)/osascript (macOS)/notify (Haiku) # # Shell: POSIX compliant # Author: Anna Arad OS="$(uname)" if type notify-send >/dev/null 2>&1; then notify-send nnn "Done!" elif [ "$OS" = "Darwin" ]; then osascript -e 'display notification "Done!" with title "nnn"' elif type ntfy >/dev/null 2>&1; then ntfy -t nnn send "Done!" elif [ "$OS" = "Haiku" ]; then notify --title "nnn" "Done!" fi nnn-5.0/plugins/README.md000066400000000000000000000421121466310014300151050ustar00rootroot00000000000000

nnn plugins

read ebooks with plugin gutenread (Android)

## Introduction Plugins extend the capabilities of `nnn`. They are _executable_ scripts (or binaries) `nnn` can communicate with and trigger. This mechanism fits perfectly with the fundamental design to keep the core file manager lean and fast, by delegating repetitive (but not necessarily file manager-specific) tasks to the plugins which can be run with custom hotkeys. `nnn` is _**language-agnostic**_ when it comes to plugins. You can write a plugin in any (scripting) language you are comfortable in! ## List of plugins | Plugin (a-z) | Description [Clears selection1] | Lang | Dependencies | | --- | --- | --- | --- | | [autojump](autojump) | Navigate to dir/path | sh | [jump](https://github.com/gsamokovarov/jump)/autojump/
zoxide/z/[z.lua](https://github.com/skywind3000/z.lua) | | [boom](boom) | Play random music from dir | sh | [moc](http://moc.daper.net/) | | [bulknew](bulknew) | Create multiple files/dirs at once | bash | sed, xargs, mktemp | | [cbcopy-mac](cbcopy-mac) | Copy the hovered file to MacOS clipboard | applescript | macos | | [cbpaste-mac](cbpaste-mac) | Pastes files from MacOS clipboard into currect directory | macos | | [cdpath](cdpath) | `cd` to the directory from `CDPATH` | sh | fzf | | [chksum](chksum) | Create and verify checksums [✓] | sh | md5sum,
sha256sum | | [cmusq](cmusq) | Queue/play files/dirs in cmus player [✓] | sh | cmus, pgrep | | [diffs](diffs) | Diff for selection (limited to 2 for directories) [✓] | sh | vimdiff, mktemp | | [dragdrop](dragdrop) | Drag/drop files from/into nnn | sh | [dragon](https://github.com/mwh/dragon) | | [dups](dups) | List non-empty duplicate files in current dir | bash | find, md5sum,
sort uniq xargs | | [finder](finder) | Run custom find command (**stored in histfile**) and list | sh | - | | [fixname](fixname) | Clean filename to be more shell-friendly [✓] | bash | sed | | [fzcd](fzcd) | Fuzzy search multiple dirs (or `$PWD`) and visit file | sh | fzf, (find) | | [fzhist](fzhist) | Fuzzy-select a cmd from history, edit in `$EDITOR` and run | sh | fzf, mktemp | | [fzopen](fzopen) | Fuzzy find file(s) in subtree to edit/open/pick | sh | fzf, xdg-open/open | | [fzplug](fzplug) | Fuzzy find, preview and run other plugins | sh | fzf | | [getplugs](getplugs) | Update plugins to installed `nnn` version | sh | curl | | [gitroot](gitroot) | Cd to the root of current git repo | sh | git | | [gpge](gpge) | Encrypt/decrypt files using GPG [✓] | sh | gpg | | [gutenread](gutenread) | Browse, download, read from Project Gutenberg | sh | curl, unzip, w3m
[epr](https://github.com/wustho/epr) (optional) | | [gsconnect](gsconnect) | GNOME's implementation of kdeconnect [✓] | sh | gsconnect | | [imgresize](imgresize) | Batch resize images in dir to screen resolution | sh | [imgp](https://github.com/jarun/imgp) | | [imgur](imgur) | Upload an image to imgur (from [imgur-screenshot](https://github.com/jomo/imgur-screenshot)) | bash | - | | [imgview](imgview) | View (thumbnail)images, set wallpaper, [rename](https://github.com/jarun/nnn/wiki/Basic-use-cases#browse-rename-images) and [more](https://wiki.archlinux.org/index.php/Sxiv#Assigning_keyboard_shortcuts)| sh | _see in-file docs_ | | [ipinfo](ipinfo) | Fetch external IP address and whois information | sh | curl, whois | | [kdeconnect](kdeconnect) | Send selected files to an Android device [✓] | sh | kdeconnect-cli | | [launch](launch) | GUI application launcher | sh | fzf | | [mimelist](mimelist) | List files by mime in subtree | sh | file/mimetype | | [moclyrics](moclyrics) | Show lyrics of the track playing in moc | sh | [ddgr](https://github.com/jarun/ddgr), [moc](http://moc.daper.net/) | | [mocq](mocq) | Queue/play selection/dir/file in moc [✓] | sh | [moc](http://moc.daper.net/) | | [mp3conv](mp3conv) | Extract audio from multimedia as mp3 | sh | ffmpeg | | [mtpmount](mtpmount) | Toggle mount of MTP device (eg. Android) | sh | gvfs-mtp | | [nbak](nbak) | Backs up `nnn` config | sh | tar, awk, mktemp | | [nmount](nmount) | Toggle mount status of a device as normal user | sh | pmount (optional), udisks2 | | [nuke](nuke) | Sample file opener (CLI-only by default) | sh | _see in-file docs_ | | [oldbigfile](oldbigfile) | List large files by access time | sh | find, sort | | [openall](openall) | Open selected files together or one by one [✓] | bash | - | | [organize](organize) | Auto-organize files in directories by file type [✓] | sh | file | | [pdfread](pdfread) | Read a PDF or text file aloud | sh | pdftotext, mpv,
pico2wave | | [preview-tabbed](preview-tabbed) | Preview files with Tabbed/xembed | bash | _see in-file docs_ | | [preview-tui](preview-tui) | Preview with Tmux/kitty/[QuickLook](https://github.com/QL-Win/QuickLook)/xterm/`$TERMINAL` | sh | _see in-file docs_ | | [pskill](pskill) | Fuzzy list by name and kill process or zombie | sh | fzf, ps, sudo/doas | | [renamer](renamer) | Batch rename selection or files in dir [✓] | sh | [qmv](https://www.nongnu.org/renameutils/)/[vidir](https://joeyh.name/code/moreutils/) | | [ringtone](ringtone) | Create a variable bitrate mp3 ringtone from file | sh | date, ffmpeg | | [rsynccp](rsynccp) | Gives copy-paste verbose progress percentage [✓] | sh | rsync | | [splitjoin](splitjoin) | Split file or join selection [✓] | sh | split, cat | | [suedit](suedit) | Edit file using superuser permissions | sh | sudoedit/sudo/doas | | [togglex](togglex) | Toggle executable mode for selection [✓] | sh | chmod | | [umounttree](umounttree) | Unmount a remote mountpoint from within | sh | fusermount | | [upload](upload) | Upload to Firefox Send or ix.io (text) or file.io (bin) | sh | [ffsend](https://github.com/timvisee/ffsend), curl, jq, tr | | [wallpaper](wallpaper) | Set wallpaper or change colorscheme | sh | nitrogen/pywal | | [x2sel](x2sel) | Copy file list from system clipboard to selection | sh | _see in-file docs_ | | [xdgdefault](xdgdefault) | Set the default app for the hovered file type | sh | xdg-utils, fzf/dmenu | Notes: 1. A plugin has to explicitly request `nnn` to clear the selection e.g. after operating on the selected files. 2. Files starting with a dot in the `plugins` directory are internal files and should not be used as plugins. ### Table of contents - [Installation](#installation) - [Configuration](#configuration) - [Skip directory refresh after running a plugin](#skip-directory-refresh-after-running-a-plugin--) - [Running commands as plugin](#running-commands-as-plugin-) - [Skip user confirmation after command execution](#skip-user-confirmation-after-command-execution-) - [Run a GUI app as plugin](#run-a-gui-app-as-plugin-) - [Page non-interactive command output](#page-non-interactive-command-output-) - [Some useful key-command examples](#some-useful-key-command-examples) - [Access level of plugins](#access-level-of-plugins) - [Create your own plugins](#create-your-own-plugins) - [Send data to `nnn`](#send-data-to-nnn) - [Get notified on file hover](#get-notified-on-file-hover) - [Examples](#examples) - [Contributing plugins](#contributing-plugins) ## Installation The following command installs or updates (after backup) all plugins: ```sh sh -c "$(curl -Ls https://raw.githubusercontent.com/jarun/nnn/master/plugins/getplugs)" ``` Plugins are installed to `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins`. ## Configuration Set environment variable `NNN_PLUG` to assign keybinds and invoke plugins directly using the plugin shortcut (;) followed by the assigned key character. E.g., with the below config: ```sh export NNN_PLUG='f:finder;o:fzopen;p:mocq;d:diffs;t:nmount;v:imgview' ``` plugin `finder` can be invoked with the keybind ;f, `fzopen` can be run with ;o and so on... The key vs. plugin pairs are shown in the help and config screen. Alternatively, combine with Alt (i.e. Alt+key). To pick and run an unassigned plugin, press Enter (to _enter_ the plugin dir) at the plugin prompt. To run a plugin at startup, use the option `-P` followed by the plugin key. If the plugins list gets too long, try breaking them up into sections: ``` NNN_PLUG_PERSONAL='g:personal/convert2zoom;p:personal/echo' NNN_PLUG_WORK='j:work/prettyjson;d:work/foobar' NNN_PLUG_INLINE='e:!go run "$nnn"*' NNN_PLUG_DEFAULT='1:ipinfo;p:preview-tui;o:fzz;b:nbak' NNN_PLUG="$NNN_PLUG_PERSONAL;$NNN_PLUG_WORK;$NNN_PLUG_DEFAULT;$NNN_PLUG_INLINE" export NNN_PLUG ``` Note: - `'g:personal/convert2zoom'` will look in the personal sub-folder inside the plugin folder. - `'b:boom;b:bulknew` will result in only the first definition of *b* (`b:boom`) being used. - A keybinding definition of more than 1 character will prevent nnn from starting. #### Skip directory refresh after running a plugin [`-`] `nnn` refreshes the directory after running a plugin to reflect any changes by the plugin. To disable this add a `-` before the plugin name: ```sh export NNN_PLUG='p:-plugin' ``` ## Running commands as plugin [`!`] To assign keys to arbitrary non-background cli commands and invoke like plugins, add `!` before the command. ```sh export NNN_PLUG='x:!chmod +x "$nnn";g:!git log;s:!smplayer "$nnn"' ``` Now ;x can be used to make a file executable, ;g can be used to the git log of a git project directory, ;s can be used to preview a partially downloaded media file. #### Skip user confirmation after command execution [`*`] `nnn` waits for user confirmation (the prompt `Press Enter to continue`) after it executes a command as plugin (unlike plugins which can add a `read` to wait). To skip this, add a `*` after the command. ```sh export NNN_PLUG='s:!smplayer "$nnn"*;n:-!vim /home/vaio/Dropbox/Public/synced_note*' ``` Now there will be no prompt after ;s and ;n. Note: Do not use `*` with programs that run and exit e.g. cat. #### Run a GUI app as plugin [`&`] To run a GUI app as plugin, add a `&` after `!`. ```sh export NNN_PLUG='m:-!&mousepad "$nnn"' ``` #### Page non-interactive command output [`|`] To show the output of run-and-exit commands which do not need user input, add `|` (pipe) after `!`. ```sh export NNN_PLUG='m:-!|mediainfo "$nnn";t:-!|tree -ps;l:-!|ls -lah --group-directories-first' ``` This option is incompatible with `&` (terminal output is masked for GUI programs) and ignores `*` (output is already paged for user). Notes: 1. Place `$nnn` (or exported variables) in double quotes (**`"$nnn"`**) 2. Use single quotes for `$NNN_PLUG` so `"$nnn"` is not interpreted 3. (_Again_) add `!` before the command 4. To disable directory refresh after running a _command as plugin_, prefix with `-!` #### Some useful key-command examples | Key:Command | Description | |---|---| | `c:!convert "$nnn" png:- \| xclip -sel clipboard -t image/png*` | Copy image to clipboard | | `C:!cp -rv "$nnn" "$nnn".cp` | Create a copy of the hovered file | | `e:-!sudo -E vim "$nnn"*` | Edit file as root in vim | | `g:-!git diff` | Show git diff | | `h:-!hx "$nnn"*` | Open hovered file in [hx](https://github.com/krpors/hx) hex editor | | `k:-!fuser -kiv "$nnn"*` | Interactively kill process(es) using hovered file | | `l:-!git log` | Show git log | | `n:-!vi /home/user/Dropbox/dir/note*` | Take quick notes in a synced file/dir of notes | | `p:-!less -iR "$nnn"*` | Page through hovered file in less | | `s:-!&smplayer -minigui "$nnn"` | Play hovered media file, even unfinished download | | `x:!chmod +x "$nnn"` | Make the hovered file executable | | `y:-!sync*` | Flush cached writes | ## Access level of plugins When `nnn` executes a plugin, it does the following: - Changes to the directory where the plugin is to be run (`$PWD` pointing to the active directory) - Passes three arguments to the script: 1. `$1`: The hovered file's name. 2. `$2`: The working directory (might differ from `$PWD` in case of symlinked paths; non-canonical). 3. `$3`: The picker mode output file (`-` for stdout) if `nnn` is executed as a file picker. - Sets the environment variable `NNN_PIPE` used to control `nnn` active directory. - Sets the environment variable `NNN_INCLUDE_HIDDEN` to `1` if hidden files are active, `0` otherwise. - Sets the environment variable `NNN_PREFER_SELECTION` to `1` if user prefers to use selection (see nnn's `-u` flag), `0` otherwise. - Exports the [special variables](https://github.com/jarun/nnn/wiki/Concepts#special-variables). Plugins can also read the `.selection` file in the config directory. ## Create your own plugins Plugins can be written in any scripting language. However, POSIX-compliant shell scripts runnable in `sh` are preferred. **Make the file executable**. Drop it in the plugin directory. Optionally add a hotkey in `$NNN_PLUG` for frequent usage. #### Send data to `nnn` `nnn` provides a mechanism for plugins to send data to `nnn` to control its active directory or invoke the list mode. The way to do so is by writing to the pipe pointed by the environment variable `NNN_PIPE`. The plugin should write a single string in the format `(<->)` without a newline at the end. For example, `1c/etc`. The optional `-` at the **beginning of the stream** instructs `nnn` to clear the selection. In cases where the data transfer to `nnn` has to happen while the selection file is being read (e.g. in a loop), the plugin should create a tmp copy of the selection file, inform `nnn` to clear the selection and then do the subsequent processing with the tmp file. A paged [`|`] or GUI [`&`] cmd run as plugin cannot clear selection. The `ctxcode` indicates the context to change the active directory of. | Context code | Meaning | |:---:| --- | | `+` | smart context (next inactive else current) | | `0` | current context | | `1`-`4` | context number | The `opcode` indicates the operation type. | Opcode | Operation | |:---:| --- | | `c` | change directory | | `l` | list files in list mode | | `p` | picker file overwritten | For convenience, we provided a helper script named `.nnn-plugin-helper` and a function named `nnn_cd` to ease this process. `nnn_cd` receives the path to change to as the first argument, and the context as an optional second argument. If a context is not provided, it is asked for explicitly. To skip this and choose the current context, set the `CUR_CTX` variable in `.nnn-plugin-helper` (or in the specific plugin after sourcing `.nnn-plugin-helper`) to 1. Usage examples can be found in the Examples section below. #### Get notified on file hover If `NNN_FIFO` is set, `nnn` will open it and write every hovered files. This can be used in plugins and external scripts, e.g. to implement file previews. Don't forget to fork in the background to avoid blocking `nnn`. For more details on configuration and usage of the preview plugins, visit [Live Previews](https://github.com/jarun/nnn/wiki/Live-previews). ## Examples There are many plugins provided by `nnn` which can be used as examples. Here are a few simple selected examples. #### Show the git log of changes to the particular file along with the code for a quick and easy review. ```sh #!/usr/bin/env sh git log -p -- "$1" ``` #### Change to directory in clipboard using helper script ```sh #!/usr/bin/env sh . $(dirname $0)/.nnn-plugin-helper nnn_cd "$(xsel -ob)" ``` #### Change directory to the location of a link using helper script with specific context (current) ```sh #!/usr/bin/env sh . $(dirname $0)/.nnn-plugin-helper nnn_cd "$(dirname $(readlink -fn $1))" 0 ``` #### Change to arbitrary directory without helper script ```sh #!/usr/bin/env sh printf "cd to: " read -r dir printf "%s" "0c$dir" > "$NNN_PIPE" ``` #### Send every hovered file to X selection ```sh #!/usr/bin/env sh if [ -z "$NNN_FIFO" ] ; then exit 1 fi while read FILE ; do printf "%s" "$FILE" | xsel done < "$NNN_FIFO" & disown ``` #### Quick `find` the first match in subtree and open in `nuke` ```sh #!/usr/bin/env sh NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke" printf "file name: " read -r pattern entry=$(find . -type f -iname "$pattern" -print -quit 2>/dev/null) if [ -n "$entry" ]; then "$NUKE" "$entry" fi ``` #### Quick find (using `fd`) ```sh #!/usr/bin/env sh . "$(dirname "$0")"/.nnn-plugin-helper printf "pattern: " read -r pattern if [ -n "$pattern" ]; then printf "%s" "+l" > "$NNN_PIPE" eval "fd -HI $pattern -0" > "$NNN_PIPE" fi ``` #### Quick grep (using `rg`) ```sh #!/usr/bin/env sh . "$(dirname "$0")"/.nnn-plugin-helper printf "pattern: " read -r pattern if [ -n "$pattern" ]; then printf "%s" "+l" > "$NNN_PIPE" eval "rg -l0 --hidden -S $pattern" > "$NNN_PIPE" fi ``` #### Change directory NNN_PLUG='c:!read -r to && [ -n "$to" ] && printf "0c%s" "${to}" > "$NNN_PIPE"*' ## Contributing plugins 1. Add informative sections like _Description_, _Notes_, _Dependencies_, _Shell_, _Author_ etc. in the plugin. 2. Add an entry in the table above. Note that the list is alphabetically ordered by plugin name. 3. Keep non-portable commands (like `notify-send`) commented so users from any other OS/DE aren't surprised. 4. The plugin file should be executable. 5. If your plugin stores data, use `${XDG_CACHE_HOME:-$HOME/.cache}/nnn`. Document it _in-file_. nnn-5.0/plugins/autojump000077500000000000000000000051251466310014300154230ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Navigate to directory using jump/autojump/zoxide/z # # Dependencies: # - jump - https://github.com/gsamokovarov/jump # - OR autojump - https://github.com/wting/autojump # - OR zoxide - https://github.com/ajeetdsouza/zoxide # - OR z - https://github.com/rupa/z (z requires fzf) # - OR z (fish) - https://github.com/jethrokuan/z (z requires fzf) # - OR z.lua - https://github.com/skywind3000/z.lua (z.lua can enhanced with fzf) # # Note: The dependencies STORE NAVIGATION PATTERNS # # to make z.lua work, you need to set $NNN_ZLUA to the path of script z.lua # # Shell: POSIX compliant # Authors: Marty Buchaus, Dave Snider, Tim Adler, Nick Waywood if [ ! -p "$NNN_PIPE" ]; then printf 'ERROR: NNN_PIPE is not set!' read -r _ exit 2 fi if type jump >/dev/null 2>&1; then printf "jump to : " IFS= read -r line # shellcheck disable=SC2086 odir="$(jump cd ${line})" printf "%s" "0c$odir" > "$NNN_PIPE" elif type autojump >/dev/null 2>&1; then printf "jump to : " read -r dir odir="$(autojump "$dir")" printf "%s" "0c$odir" > "$NNN_PIPE" elif type zoxide >/dev/null 2>&1; then if type fzf >/dev/null 2>&1; then odir="$(zoxide query -i --)" printf "%s" "0c$odir" > "$NNN_PIPE" else printf "jump to : " read -r dir odir="$(zoxide query -- "$dir")" printf "%s" "0c$odir" > "$NNN_PIPE" fi elif type lua >/dev/null 2>&1 && [ -n "$NNN_ZLUA" ]; then printf "jump to : " read -r line if type fzf >/dev/null 2>&1; then odir="$(lua "$NNN_ZLUA" -l "$line" | fzf --nth 2.. --reverse --inline-info --tac +s -e --height 35%)" printf "%s" "0c$(echo "$odir" | awk '{print $2}')" > "$NNN_PIPE" else odir="$(lua "$NNN_ZLUA" -e "$line")" printf "%s" "0c$odir" > "$NNN_PIPE" fi else # rupa/z uses $_Z_DATA, jethrokuan/z (=port of z for fish) uses $Z_DATA datafile="${_Z_DATA:-${Z_DATA:-$HOME/.z}}" if type fzf >/dev/null 2>&1 && [ -f "$datafile" ]; then # Read the data from z's file instead of calling # z so the data doesn't need to be processed twice sel=$(awk -F "|" '{print $1}' "$datafile" | fzf | awk '{$1=$1};1') # NOTE: Uncomment this line and comment out the line above if # you want to see the weightings of the dir's in the fzf pane # sel=$(awk -F "|" '{printf "%s %s\n", $2, $1}' "$datafile" | fzf | sed 's/^[0-9,.]* *//' | awk '{$1=$1};1') printf "%s" "0c$sel" > "$NNN_PIPE" else printf "No supported autojump script [jump/autojump/zoxide/z (needs fzf)] found" read -r _ fi fi nnn-5.0/plugins/boom000077500000000000000000000026701466310014300145150ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Play random music (MP3, FLAC, M4A, WEBM, WMA) from current dir. # # Dependencies: mocp (or custom) # # Note: You may want to set GUIPLAYER. # # Shell: POSIX compliant # Author: Arun Prakash Jana GUIPLAYER="${GUIPLAYER:-""}" NUMTRACKS="${NUMTRACKS:-100}" if [ -n "$GUIPLAYER" ]; then find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | shuf -n "$NUMTRACKS" | xargs -d "\n" "$GUIPLAYER" > /dev/null 2>&1 & # detach the player sleep 1 elif type mocp >/dev/null 2>&1; then cmd=$(pgrep -x mocp 2>/dev/null) ret=$cmd if [ -z "$ret" ]; then # start MOC server mocp -S mocp -o shuffle else # mocp running, check if it's playing state=$(mocp -i | grep "State:" | cut -d' ' -f2) if [ "$state" = 'PLAY' ]; then # add up to 100 random audio files find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | head -n "$NUMTRACKS" | xargs -d "\n" mocp -a exit fi fi # clear MOC playlist mocp -c mocp -o shuffle # add up to 100 random audio files find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | head -n "$NUMTRACKS" | xargs -d "\n" mocp -a # start playing mocp -p else printf "moc missing" read -r _ fi nnn-5.0/plugins/bulknew000077500000000000000000000013431466310014300152240ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Allows for creation of multiple files/dirs simultaneously # Creates a tmp file to write each entry in a separate line # # Note: Only relative paths are supported. Absolute paths are ignored # Leading and trailing whitespace in path names is also ignored # # Shell: POSIX compliant # Author: KlzXS EDITOR="${EDITOR:-vi}" TMPDIR="${TMPDIR:-/tmp}" printf "'f'ile / 'd'ir? " read -r resp if [ "$resp" = "f" ]; then #shellcheck disable=SC2016 cmd='mkdir -p "$(dirname "{}")" && touch "{}"' elif [ "$resp" = "d" ]; then cmd='mkdir -p {}' else exit 1 fi tmpfile=$(mktemp "$TMPDIR/.nnnXXXXXX") $EDITOR "$tmpfile" sed "/^\//d" "$tmpfile" | xargs -n1 -I{} sh -c "$cmd" rm -- "$tmpfile" nnn-5.0/plugins/cbcopy-mac000077500000000000000000000005201466310014300155660ustar00rootroot00000000000000#!/usr/bin/osascript # Description: Copy the hovered file to MacOS clipboard. # # Note: Supports only MacOS # # Shell: POSIX compliant # Author: Syed Umar Anis on run args set filePath to (second item of args & "/" & first item of args) tell application "Finder" set the clipboard to (filePath as POSIX file) end tell end nnn-5.0/plugins/cbpaste-mac000077500000000000000000000013251466310014300157340ustar00rootroot00000000000000#!/usr/bin/env sh # shellcheck disable=all # Description: Paste the clipboard files into the current directory. # Only works if clipboard contents are files. # # Note: Supports only MacOS # # Shell: POSIX compliant # Author: Syed Umar Anis fs=($( osascript -e "use framework \"Foundation\" property this : a reference to the current application property NSPasteboard : a reference to NSPasteboard of this property NSURL : a reference to NSURL of this property pb : a reference to NSPasteboard's generalPasteboard property text item delimiters : linefeed pb's readObjectsForClasses:[NSURL] options:[] (result's valueForKey:\"path\") as list as text" )) cp -R -- "${fs[@]}" "$2/" nnn-5.0/plugins/cdpath000077500000000000000000000035551466310014300150270ustar00rootroot00000000000000#!/usr/bin/env sh # Description: 'cd' to the directory from CDPATH # # Details: If the CDPATH environment variable is not set, the default value of # ${XDG_CONFIG_HOME:-$HOME/.config}/nnn/bookmarks will be used. # You can create this directory and fill it with symbolic links to your # favorite directories. It's a good idea to add it to CDPATH so that it # could also be used from the command line outside of nnn. # The fzf search is done on the directory basename (the first column). # # This plugin is an extended version of the bookmarks plugin. # If you set your CDPATH to ${XDG_CACHE_HOME:-$HOME/.cache}/nnn/bookmarks # or to the value of BOOKMARKS_DIR, you can use it as a bookmarks replacement. # # Shell: POSIX compliant # Author: Yuri Kloubakov # shellcheck disable=SC1090,SC1091 . "$(dirname "$0")"/.nnn-plugin-helper # Get a list of (symbolic links to) directories for every element of CDPATH get_dirs() { IFS=':' for path in $CDPATH; do for entry in "$path"/*; do if [ -d "$entry" ]; then name=$(basename "$entry" | grep -o '^.\{1,24\}') if [ -h "$entry" ]; then slink=$(ls -dl -- "$entry") entry=${slink#*" $entry -> "} fi printf "%-24s :%s\n" "${name}" "$entry" fi done done } abort() { echo "$1" read -r _ exit 1 } if [ -z "$CDPATH" ]; then CDPATH="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/bookmarks" [ -d "$CDPATH" ] || abort "CDPATH is not set and there is no \"$CDPATH\" directory" fi dir_list=$(get_dirs) [ -n "$dir_list" ] || abort "There are no directories to choose from. Check your \"$CDPATH\"." dir=$(echo "$dir_list" | fzf --nth=1 --delimiter=':' | awk -F: 'END { print $2 }') if [ -n "$dir" ]; then nnn_cd "$dir" 0 fi nnn-5.0/plugins/chksum000077500000000000000000000045021466310014300150470ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Create and verify checksums # # Note: On macOS, install the relevant checksum packages from Homebrew with: # brew install coreutils # # Details: # - selection: it will generate one file with the checksums and filenames # (and with paths if they are in another directory) # output checksum filename format: checksum_timestamp.checksum_type # - file: if the file is a checksum, the plugin does the verification # if the file is not a checksum, checksum will be generated for it # the output checksum filename will be filename.checksum_type # - directory: recursively calculates checksum for all the files in the dir # the output checksum filename will be directory.checksum_type # # Shell: POSIX compliant # Authors: ath3, Arun Prakash Jana selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} resp=f chsum=md5 checksum_type() { echo "possible checksums: md5, sha1, sha224, sha256, sha384, sha512" printf "create md5 (m), sha256 (s), sha512 (S) (or type one of the above checksums) [default=m]: " read -r chsum_resp for chks in md5 sha1 sha224 sha256 sha384 sha512 do if [ "$chsum_resp" = "$chks" ]; then chsum=$chsum_resp return fi done if [ "$chsum_resp" = "s" ]; then chsum=sha256 elif [ "$chsum_resp" = "S" ]; then chsum=sha512 fi } if [ -s "$selection" ]; then printf "work with selection (s) or current file (f) [default=f]: " read -r resp fi if [ "$resp" = "s" ]; then checksum_type sed 's|'"$PWD/"'||g' < "$selection" | xargs -0 -I{} ${chsum}sum {} > "checksum_$(date '+%Y%m%d%H%M').$chsum" # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi elif [ -n "$1" ]; then if [ -f "$1" ]; then for chks in md5 sha1 sha224 sha256 sha384 sha512 do if echo "$1" | grep -q \.${chks}$; then ${chks}sum -c < "$1" read -r _ exit fi done checksum_type file=$(basename "$1").$chsum ${chsum}sum "$1" > "$file" elif [ -d "$1" ]; then checksum_type file=$(basename "$1").$chsum find "$1" -type f -exec ${chsum}sum "{}" + > "$file" fi fi nnn-5.0/plugins/cmusq000077500000000000000000000043541466310014300147120ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Add selection or hovered file/directory to cmus queue # # Dependencies: cmus, pgrep, xdotool (optional) # # Notes: # 1. If adding selection, files/dirs are added in the same order they were selected in nnn # 2. A new window will be opened if cmus is not running already, playback will start immediately # 3. If cmus is already running, files will be appended to the queue with no forced playback # # TODO: # 1. Add cava and cmus-lyrics as optional dependencies # 2. Start cava and/or cmus-lyrics in tmux or kitty panes next to cmus # # Shell: POSIX compliant # Author: Kabouik # (Optional) Set preferred terminal emulator for cmus if not set in your env, # or leave commented out to use OS default #TERMINAL="kitty" if ! type cmus >/dev/null; then printf "cmus missing" read -r _ exit 1 fi selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} start_cmus() { type xdotool >/dev/null && nnnwindow="$(xdotool getactivewindow)" case "$TERMINAL" in kitty | gnome-terminal | st) nohup "$TERMINAL" -- cmus & ;; havoc) nohup "$TERMINAL" cmus & ;; "") nohup x-terminal-emulator -e cmus & ;; *) nohup "$TERMINAL" -e cmus & ;; esac # Give the new terminal some time to open until cmus-remote -C; do sleep 0.1; done [ -n "$nnnwindow" ] && xdotool windowactivate "$nnnwindow" } >/dev/null 2>&1 fill_queue() { if [ "$REPLY" = "s" ]; then xargs < "$selection" -0 cmus-remote -q elif [ -n "$1" ]; then cmus-remote -q "$1" fi } # If active selection,then ask what to do if [ -s "$selection" ]; then printf "Queue [s]election or [c]urrently hovered? [default=c]: " read -r REPLY fi # If cmus is not running, start and play queue if ! pgrep cmus >/dev/null; then printf "cmus is not running, starting it in a new %s window.\n" "$TERMINAL" start_cmus fill_queue "$1" cmus-remote -p printf "Files added to cmus queue.\n" else # Append to existing queue if cmus is already running fill_queue "$1" printf "Files appended to current cmus queue.\n" fi # Change view cmus-remote -C "view 4" # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi nnn-5.0/plugins/diffs000077500000000000000000000034451466310014300146550ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Show diff of 2 directories or multiple files in vimdiff # # Notes: # 1. vim may show the warning: 'Vim: Warning: Input is not from a terminal' # press 'Enter' to ignore and proceed. # 2. if only one file is in selection, the hovered file is considered as the # second file to diff with # # Shell: POSIX compliant # Authors: Arun Prakash Jana, ath3 selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} if type nvim >/dev/null 2>&1; then diffcmd="nvim -d" else diffcmd="vimdiff +0" fi dirdiff() { dir1=$(mktemp "${TMPDIR:-/tmp}"/nnn-"$(basename "$1")".XXXXXXXX) dir2=$(mktemp "${TMPDIR:-/tmp}"/nnn-"$(basename "$2")".XXXXXXXX) ls -A1 "$1" > "$dir1" ls -A1 "$2" > "$dir2" $diffcmd "$dir1" "$dir2" rm -- "$dir1" "$dir2" } if [ -s "$selection" ]; then arr=$(tr '\0' '\n' < "$selection") if [ "$(echo "$arr" | wc -l)" -gt 1 ]; then f1="$(echo "$arr" | sed -n '1p')" f2="$(echo "$arr" | sed -n '2p')" if [ -d "$f1" ] && [ -d "$f2" ]; then dirdiff "$f1" "$f2" else # If xargs supports the -o option, use it to get rid of: # Vim: Warning: Input is not from a terminal # xargs -0 -o vimdiff < $selection eval xargs -0 "$diffcmd" < "$selection" fi elif [ -n "$1" ]; then f1="$(echo "$arr" | sed -n '1p')" if [ -d "$f1" ] && [ -d "$1" ]; then dirdiff "$f1" "$1" elif [ -f "$f1" ] && [ -f "$1" ]; then $diffcmd "$f1" "$1" else echo "cannot compare file with directory" fi else echo "needs at least 2 files or directories selected for comparison" fi fi # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi nnn-5.0/plugins/dragdrop000077500000000000000000000036361466310014300153660ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Open a Drag and drop window, to drop files onto other programs. # Also provides drag and drop window for files. # # Dependencies: dragon - https://github.com/mwh/dragon # # Notes: # 1. Files that are dropped will be added to nnn's selection # Some web-based files will be downloaded to current dir # with curl and it may overwrite some existing files # 2. The user has to mm to clear nnn's selection first # # Shell: POSIX compliant # Author: 0xACE selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} resp=f all= if type dragon-drag-and-drop >/dev/null 2>&1; then dnd="dragon-drag-and-drop" elif type dragon-drop >/dev/null 2>&1; then dnd="dragon-drop" else dnd="dragon" fi add_file () { printf '%s\0' "$@" >> "$selection" } use_all () { printf "mark --all (a) [default=none]: " read -r resp if [ "$resp" = "a" ]; then all="--all" else all="" fi } if [ -s "$selection" ]; then printf "Drop file (r). Drag selection (s), Drag current directory (d) or drag current file (f) [default=f]: " read -r resp else printf "Drop file (r). Drag current directory (d) or drag current file (f) [default=f]: " read -r resp if [ "$resp" = "s" ]; then resp=f fi fi if [ "$resp" = "s" ]; then use_all sed -z 's|'"$PWD/"'||g' < "$selection" | xargs -0 "$dnd" "$all" & elif [ "$resp" = "d" ]; then use_all "$dnd" "$all" "$PWD/"* & elif [ "$resp" = "r" ]; then true > "$selection" "$dnd" --print-path --target | while read -r f do if printf "%s" "$f" | grep '^\(https\?\|ftps\?\|s\?ftp\):\/\/' ; then curl -LJO "$f" add_file "$PWD/$(basename "$f")" elif [ -e "$f" ]; then add_file "$f" fi done & else if [ -n "$1" ] && [ -e "$1" ]; then "$dnd" "$1" & fi fi nnn-5.0/plugins/dups000077500000000000000000000037471466310014300145420ustar00rootroot00000000000000#!/usr/bin/env sh # Description: List non-empty duplicates in the current dir (based on size followed by MD5) # # Source: https://www.commandlinefu.com/commands/view/3555/find-duplicate-files-based-on-size-first-then-md5-hash # # Dependencies: find md5sum sort uniq xargs gsed # # Notes: # 1. If the file size exceeds $size_digits digits the file will be misplaced # 12 digits fit files up to 931GiB # 2. Bash compatible required for mktemp # # Shell: Bash # Authors: syssyphus, KlzXS EDITOR="${EDITOR:-vi}" TMPDIR="${TMPDIR:-/tmp}" size_digits=12 tmpfile=$(mktemp "$TMPDIR/.nnnXXXXXX") printf "\ ## This is an overview of all duplicate files found. ## Comment out the files you wish to remove. You will be given an option to cancel. ## Lines with double comments (##) are ignored. ## You will have the option to remove the files with force or interactively.\n " > "$tmpfile" # shellcheck disable=SC2016 find . -size +0 -type f -printf "%${size_digits}s %p\n" | sort -rn | uniq -w"${size_digits}" -D | sed -e ' s/^ \{0,12\}\([0-9]\{0,12\}\) \(.*\)$/printf "%s %s\\n" "$(md5sum "\2")" "d\1"/ ' | tr '\n' '\0' | xargs -0 -n1 sh -c | sort | { uniq -w32 --all-repeated=separate; echo; } | sed -ne ' h s/^\(.\{32\}\).* d\([0-9]*\)$/## md5sum: \1 size: \2 bytes/p g :loop N /.*\n$/!b loop p' | sed -e 's/^.\{32\} \(.*\) d[0-9]*$/\1/' >> "$tmpfile" "$EDITOR" "$tmpfile" printf "Remove commented files? (yes/no) [default=n]: " read -r commented if [ "$commented" = "y" ]; then sedcmd="/^##.*/d; /^[^#].*/d; /^$/d; s/^# *\(.*\)$/\1/" else printf "Press any key to exit" read -r _ exit fi printf "Remove with force or interactive? (f/i) [default=i]: " read -r force if [ "$force" = "f" ]; then #shellcheck disable=SC2016 sed -e "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -f -- "$0" "$@" /dev/null; then fexpr=${fexprs[$((fexpr - 1))]} read -r -e -p "Search expression: " -i "$fexpr" fexpr else return 1 fi } readexpr() { case "$fexpr" in h) clear printf "Examples:\n" mapfile -t fexprs < <(printexamples) printexprs 0 read -r -p "Search expression or index: " fexpr mapexpr [ -n "$fexpr" ] && readexpr ;; \$*) cmd="${fexpr:1}" ;; *) mapexpr && readexpr cmd="find . $fexpr -print0" ;; esac } clear [ -f "$NNN_FINDHIST" ] || printexamples > "$NNN_FINDHIST" mapfile -t fexprs < <(sort "$NNN_FINDHIST" | uniq -c | sort -nr | head -n5 |\ awk '{for (i=2; i "$NNN_PIPE" while :; do readexpr eval "$cmd" > "$NNN_PIPE" && break read -r -e -p "Search expression: " -i "$fexpr" fexpr done if [ -n "$fexpr" ]; then tail -n"$NNN_FINDHISTLEN" "$NNN_FINDHIST" > "$TMPDIR/finderbms" printf "%s\n" "$fexpr" >> "$TMPDIR/finderbms" mv -- "$TMPDIR/finderbms" "$NNN_FINDHIST" fi fi nnn-5.0/plugins/fixname000077500000000000000000000036621466310014300152120ustar00rootroot00000000000000#!/usr/bin/env bash # Description: Clean filename or dirname (either hovered or selections) # to be more shell-friendly. This script cleans # non A-Za-z0-9._- characters. # and replaces it with underscore (_). # # It supports cleaning single/double quote, newline, # leading, trailing spaces. # # eg. # to be continued (つづく).mp4 -> to_be_continued______.mp4 # [work] stuff.txt -> _work__stuff.txt # home's server -> home_s_server # qwe\trty -> __qwe_rty # # And if there are two almost similar filenames # like: 'asd]f' and 'asd f' both will be renamed to 'asd_f', # to avoid overwriting, the last file will be prepended by _. # So they will be: 'asd_f' and '_asd_f' # # Dependencies: sed # # Shell: Bash # Author: Benawi Adha prompt=true sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} cleanup() { # printf "%s" "$1" | sed -e 's/[^A-Za-z0-9._-]/_/g' printf "%s" "$1" | sed 's/[^A-Za-z0-9._-]/_/g' | sed ':a;N;$!ba;s/\n/_/g' } if [ -s "$sel" ]; then targets=() while IFS= read -r -d '' i || [ -n "$i" ]; do targets+=( "$(basename "$i")" ) done < "$sel" else targets=("$1") fi for i in "${targets[@]}"; do printf "%s -> %s\n" "$i" "$(cleanup "$i")"; done if $prompt; then echo printf "Proceed [Yn]? " read -r input case "$input" in y|Y|'') ;; *) echo "Canceled" exit ;; esac fi for i in "${targets[@]}"; do if [ "$i" != "$(cleanup "$i")" ]; then tmp='' if [ -e "$(cleanup "$i")" ]; then tmp='_' fi mv -- "$i" "$tmp$(cleanup "$i")"; fi done # Clear selection if [ -s "$sel" ] && [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi nnn-5.0/plugins/fzcd000077500000000000000000000052111466310014300145010ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Fuzzy search multiple locations read-in from a path-list file # (or $PWD) and open the selected file's dir in a smart context. # Dependencies: fzf, find (only for multi-location search) # # Details: Paths in list file should be newline-separated absolute paths. # Paths can be file paths; the script will scan the parent dirs. # # The path-list file precedence is: # - "$1" (the hovered file) if it exists, is plain-text and the # first line points to an existing file # - "$LIST" if set below # - "$2" (the current directory) [mimics plugin fzcd behaviour] # # The path-list file can be generated easily: # - pick the (file)paths in picker mode to path-list file # - OR, edit selection in nnn and save as path-list file # # Shell: POSIX compliant # Author: Anna Arad, Arun Prakash Jana, KlzXS IFS="$(printf '\n\r')" # shellcheck disable=SC1090,SC1091 . "$(dirname "$0")"/.nnn-plugin-helper CTX=+ LIST="${LIST:-""}" if ! type fzf >/dev/null 2>&1; then printf "fzf missing" read -r _ exit 1 fi if [ -n "$1" ] && [ "$(file -b --mime-type "$1")" = 'text/plain' ] && [ -e "$(head -1 "$1")" ]; then LIST="$1" elif ! [ -s "$LIST" ]; then sel=$(fzf) # Show only the file and parent dir # sel=$(fzf --delimiter / --with-nth=-2,-1 --tiebreak=begin --info=hidden) LIST='' fi if [ -n "$LIST" ]; then if type find >/dev/null 2>&1; then tmpfile=$(mktemp /tmp/abc-script.XXXXXX) while IFS= read -r path; do if [ -d "$path" ]; then printf "%s\n" "$path" >> "$tmpfile" elif [ -f "$path" ]; then printf "%s\n" "$(dirname "$path")" >> "$tmpfile" fi done < "$LIST" sel=$(xargs -d '\n' < "$tmpfile" -I{} find {} -type f -printf "%H//%P\n" | sed '/.*\/\/\(\..*\|.*\/\..*\)/d; s:/\+:/:g' | fzf --delimiter / --tiebreak=begin --info=hidden) # Alternative for 'fd' # sel=$(xargs -d '\n' < "$tmpfile" fd . | fzf --delimiter / --tiebreak=begin --info=hidden) rm -- "$tmpfile" else printf "find missing" read -r _ exit 1 fi fi if [ -n "$sel" ]; then if [ "$sel" = "." ] || { ! [ -d "$sel" ] && ! [ -f "$sel" ]; }; then exit 0 fi # Check if the selected path returned by fzf command is absolute case $sel in /*) nnn_cd "$sel" "$CTX" ;; *) # Remove "./" prefix if it exists sel="${sel#./}" if [ "$PWD" = "/" ]; then nnn_cd "/$sel" "$CTX" else nnn_cd "$PWD/$sel" "$CTX" fi;; esac fi nnn-5.0/plugins/fzhist000077500000000000000000000023421466310014300150640ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Fuzzy find a command from history, # edit in $EDITOR and run as a command # # Note: Supports only bash, zsh and fish history # # TODO: For zsh there's also $ZDOTDIR which might need to be checked as well for the -z $HISTFILE && -n $ZDOTDIR case. # # Shell: POSIX compliant # Author: Arun Prakash Jana if type fzf >/dev/null 2>&1; then fuzzy=fzf else exit 1 fi shellname="$(basename "$SHELL")" if [ "$shellname" = "bash" ]; then if [ -f "$HISTFILE" ]; then hist_file="$HISTFILE" else hist_file="$HOME/.bash_history" fi entry="$("$fuzzy" < "$hist_file")" elif [ "$shellname" = "zsh" ]; then if [ -f "$HISTFILE" ]; then hist_file="$HISTFILE" else hist_file="$HOME/.zsh_history" fi entry="$("$fuzzy" < "$hist_file")" elif [ "$shellname" = "fish" ]; then hist_file="$HOME/.local/share/fish/fish_history" entry="$(grep "\- cmd: " "$hist_file" | cut -c 8- | "$fuzzy")" fi if [ -n "$entry" ]; then tmpfile=$(mktemp) echo "$entry" >> "$tmpfile" $EDITOR "$tmpfile" if [ -s "$tmpfile" ]; then $SHELL -c "$(cat "$tmpfile")" fi rm -- "$tmpfile" printf "Press any key to exit" read -r _ fi nnn-5.0/plugins/fzopen000077500000000000000000000043271466310014300150630ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Regular mode: # Fuzzy find a file in directory subtree. # Opens in $VISUAL or $EDITOR if text. # Opens other type of files with xdg-open. # Work only with a single file selected. # # Picker mode: # If picker mode output file is passed, it # will be overwritten with any picked files. # Leaves untouched if no file is picked. # Works with single/multiple files selected. # # Dependencies: fd/find, fzf/skim, xdg-open/open (on macOS) # # Shell: POSIX compliant # Author: Arun Prakash Jana NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke" USE_NUKE=0 # shellcheck disable=SC1090,SC1091 . "$(dirname "$0")"/.nnn-plugin-helper if type fzf >/dev/null 2>&1; then cmd="$FZF_DEFAULT_COMMAND" if type fd >/dev/null 2>&1; then [ -z "$cmd" ] && cmd="fd -t f 2>/dev/null" else [ -z "$cmd" ] && cmd="find . -type f 2>/dev/null" fi entry="$(eval "$cmd" | fzf -m)" # To show only the file name # entry=$(find . -type f 2>/dev/null | fzf --delimiter / --with-nth=-1 --tiebreak=begin --info=hidden) elif type sk >/dev/null 2>&1; then entry=$(find . -type f 2>/dev/null | sk) else exit 1 fi # Check for picker mode if [ "$3" ]; then if [ "$entry" ]; then case "$entry" in /*) fullpath="$entry" ;; *) fullpath="$PWD/$entry" ;; esac if [ "-" = "$3" ]; then printf "%s\n" "$fullpath" else printf "%s\n" "$fullpath" > "$3" fi # Tell `nnn` to clear its internal selection printf "%s" "0p" > "$NNN_PIPE" fi exit 0 fi if [ "$entry" ]; then if [ "$USE_NUKE" -ne 0 ]; then "$NUKE" "$entry" exit 0 fi # Open the file (works for a single file only) cmd_file="" cmd_open="" if uname | grep -q "Darwin"; then cmd_file="file -bIL" cmd_open="open" else cmd_file="file -biL" cmd_open="xdg-open" fi case "$($cmd_file "$entry")" in *text*) "${VISUAL:-$EDITOR}" "$entry" ;; *) $cmd_open "$entry" >/dev/null 2>&1 ;; esac fi nnn-5.0/plugins/fzplug000077500000000000000000000041601466310014300150640ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Fuzzy find and execute nnn plugins (and optionally, # custom scripts located elsewhere). # Description and details of plugins can be previewed # from the fzf interface. Use `?` to toggle preview # pane on and off, ^Up/^Dn to scroll. # # Dependencies: find, fzf, cat (or bat, if installed) # # Note: For better compatibility with as many nnn plugins as possible, # fzplug will first execute the chosen script on the file hovered # in nnn, and upon failure, try to run it with no target (i.e on # an active selection, if present). # # Shell: POSIX compliant # Author: Kabouik # Optional scripts sources # Leave blank or fill with the absolute path of a folder containing executable # scripts other than nnn plugins (e.g., "$HOME/.local/share/nautilus/scripts", # since there are numerous Nautilus script git repositories). # Add extra variables if needed, make sure you call them in the find command. #CUSTOMDIR1="$HOME/.local/share/nautilus/scripts" CUSTOMDIR1="" CUSTOMDIR2="" nnnpluginsdir="$HOME/.config/nnn/plugins" # Preview with bat if installed if type bat >/dev/null; then BAT="bat --terminal-width='$(tput cols)' --decorations=always --color=always --style='${BAT_STYLE:-header,numbers}'" fi plugin=$(find "$nnnpluginsdir" "$CUSTOMDIR1" "$CUSTOMDIR2" \ -maxdepth 3 -perm -111 -type f 2>/dev/null | fzf --ansi --preview \ "${BAT:-cat} {}" --preview-window="right:66%:wrap" --delimiter / \ --with-nth -1 --bind="?:toggle-preview") # Try running the script on the hovered file, and abort # abort if no plugin was selected (ESC or ^C pressed). err=0 if ! [ "$plugin" = "" ]; then "$plugin" "$1" || err=1 fi # If attempt with hovered file fails, try without any target # (nnn selections should still be passed to the script in that case) if [ "$err" -eq "1" ]; then clear && "$plugin" || err=2 fi # Abort and show error if both fail if [ "$err" -eq "2" ]; then sep="\n---\n" printf "$sep""Failed to execute '%s'. See error above or try without fzfplug. Press return to continue. " "$plugin" && read -r _ && clear fi nnn-5.0/plugins/getplugs000077500000000000000000000031441466310014300154100ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Update nnn plugins to installed nnn version # # Shell: POSIX compliant # Authors: Arun Prakash Jana, KlzXS CONFIG_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/ PLUGIN_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins merge () { if type nvim >/dev/null 2>&1; then nvim -d "$1" "$2" else vimdiff +0 "$1" "$2" fi } prompt () { printf "%s\n" "Plugin $1 already exists and is different." printf "Keep (k), merge (m), overwrite (o) [default: k]? " read -r operation if [ "$operation" = "m" ]; then op="merge" elif [ "$operation" = "o" ]; then op="cp -vRf --" else op="true" fi } if [ "$1" = "master" ] ; then VER="master" ARCHIVE_URL=https://github.com/jarun/nnn/archive/master.tar.gz elif type nnn >/dev/null 2>&1; then VER=$(nnn -V) ARCHIVE_URL=https://github.com/jarun/nnn/releases/download/v"$VER"/nnn-v"$VER".tar.gz else echo "nnn is not installed" exit 1 fi # backup any earlier plugins if [ -d "$PLUGIN_DIR" ]; then tar -C "$CONFIG_DIR" -czf "$CONFIG_DIR""plugins-$(date '+%Y%m%d%H%M').tar.gz" plugins/ fi mkdir -p "$PLUGIN_DIR" cd "$CONFIG_DIR" || exit 1 curl -Ls "$ARCHIVE_URL" -o nnn-"$VER".tar.gz tar -zxf nnn-"$VER".tar.gz cd nnn-"$VER"/plugins || exit 1 # shellcheck disable=SC2044 # We do not use obnoxious names for plugins for f in $(find . -maxdepth 1 \( ! -iname "." ! -iname "*.md" \)); do if [ -f ../../plugins/"$f" ]; then if [ "$(diff --brief "$f" ../../plugins/"$f")" ]; then prompt "$f" $op "$f" ../../plugins/ fi else cp -vRf -- "$f" ../../plugins/ fi done cd ../.. || exit 1 rm -rf nnn-"$VER"/ nnn-"$VER".tar.gz nnn-5.0/plugins/gitroot000077500000000000000000000005551466310014300152500ustar00rootroot00000000000000#!/usr/bin/env sh # Description: cd to the top level of the current git repository in the current context # Dependencies: git # Shell: sh # Author: https://github.com/PatrickF1 root="$(git rev-parse --show-toplevel 2>/dev/null)" if [ -n "$root" ]; then printf "%s" "0c$root" > "$NNN_PIPE" else printf "Not in a git repository" read -r _ exit 1 fi nnn-5.0/plugins/gpgd000077500000000000000000000013321466310014300144740ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Decrypts selected files using gpg. The contents of the # decrypted file are stored in a file with extension .dec # # Note: If an appropriate private key cannot be found gpg silently # prints a message in the background and no files are written. # # Shell: POSIX compliant # Author: KlzXS selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} printf "(s)election/(c)urrent? [default=c] " read -r resp if [ "$resp" = "s" ]; then files=$(tr '\0' '\n' < "$selection") else files=$1 fi printf "%s" "$files" | xargs -n1 -I{} gpg --decrypt --output "{}.dec" {} # Clear selection if [ "$resp" = "s" ] && [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi nnn-5.0/plugins/gpge000077500000000000000000000024621466310014300145020ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Encrypts selected files using gpg. Can encrypt # asymmetrically (key) or symmetrically (passphrase). # If asymmetric encryption is chosen a key can be # chosen from the list of capable public keys using fzf. # # Note: Symmetric encryption only works for a single (current) file as per gpg limitations # # Shell: POSIX compliant # Author: KlzXS # shellcheck disable=SC1090,SC1091 . "$(dirname "$0")"/.nnn-plugin-helper printf "(s)ymmetric, (a)symmetric? [default=a] " read -r symmetry if [ "$symmetry" = "s" ]; then gpg --symmetric "$1" else if nnn_use_selection; then clear_sel=1 # shellcheck disable=SC2154 files=$(tr '\0' '\n' < "$selection") else clear_sel=0 files=$1 fi keyids=$(gpg --list-public-keys --with-colons | grep -E "pub:(.*:){10}.*[eE].*:" | awk -F ":" '{print $5}') #awk needs literal $10 #shellcheck disable=SC2016 keyuids=$(printf "%s" "$keyids" | xargs -n1 -I{} sh -c 'gpg --list-key --with-colons "{}" | grep "uid" | awk -F ":" '\''{printf "%s %s\n", "{}", $10}'\''') recipient=$(printf "%s" "$keyuids" | fzf | awk '{print $1}') printf "%s" "$files" | xargs -n1 gpg --encrypt --recipient "$recipient" # Clear selection if [ "$clear_sel" -eq 1 ] && [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi fi nnn-5.0/plugins/gsconnect000077500000000000000000000012441466310014300155400ustar00rootroot00000000000000#!/usr/bin/env sh #set -x # Description: Send the selected (or hovered) files to your Android device using gsconnect daemon.js. # GSConnect must be configured on the Android device and the PC. # # Shell: POSIX compliant # Author: Darukutsu selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} gsconnect=$HOME/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/daemon.js ids=$($gsconnect -l) for id in $ids; do if [ -s "$selection" ]; then xargs -0 < "$selection" -I{} "$gsconnect" -d "$id" --share-file="{}" # Clear selection printf "-" > "$NNN_PIPE" else "$gsconnect" -d "$id" --share-file="$2/$1" fi done nnn-5.0/plugins/gutenread000077500000000000000000000031341466310014300155330ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Browse Project Gutenberg catalogue by popularity, then download # and read a book of your choice. # # Details: Set the variable EBOOK_ID to download in html format and read in w3m. # Clear EBOOK_ID to browse available ebooks by popularity and set it to # the ID once you find an interesting one. # To download and read in epub format set READER to an epub reader like # epr: https://github.com/wustho/epr # # More on EBOOK_ID: # Wuthering Heights by Emily Brontë is at https://www.gutenberg.org/ebooks/768 # So EBOOK_ID would be 768 # # Downloaded ebooks are at ${XDG_CACHE_HOME:-$HOME/.cache}/nnn/gutenbooks/ # # Shell: POSIX compliant # Author: Arun Prakash Jana EBOOK_ID="${EBOOK_ID:-""}" DIR="${XDG_CACHE_HOME:-$HOME/.cache}/nnn/gutenbooks/$EBOOK_ID" BROWSE_LINK="https://www.gutenberg.org/ebooks/search/?sort_order=downloads" BROWSER="${BROWSER:-w3m}" READER="${READER:-""}" if [ -n "$EBOOK_ID" ]; then if [ ! -e "$DIR" ]; then mkdir -p "$DIR" cd "$DIR" || exit 1 if [ -z "$READER" ]; then curl -L -O "https://www.gutenberg.org/files/$EBOOK_ID/$EBOOK_ID-h.zip" unzip "$EBOOK_ID"-h.zip else curl -L -o "$EBOOK_ID".epub "https://www.gutenberg.org/ebooks/$EBOOK_ID.epub.noimages" fi fi if [ -d "$DIR" ]; then if [ -z "$READER" ]; then "$BROWSER" "$DIR/$EBOOK_ID-h/$EBOOK_ID-h.htm" else "$READER" "$DIR/$EBOOK_ID.epub" fi fi else "$BROWSER" "$BROWSE_LINK" fi nnn-5.0/plugins/imgresize000077500000000000000000000014261466310014300155550ustar00rootroot00000000000000#!/usr/bin/env sh # Description: Resize images in a directory to screen resolution with imgp # # Dependencipes: imgp - https://github.com/jarun/imgp # # Notes: # 1. Set res to avoid the desktop resolution prompt each time # 2. MINSIZE is set to 1MB by default, adjust it if you want # 3. imgp options used: # a - adaptive mode # c - convert PNG to JPG # k - skip images matching specified hres/vres # # Shell: POSIX compliant # Author: Arun Prakash Jana # set resolution (e.g. 1920x1080) res="${RESOLUTION}" # set minimum image size (in bytes) to resize (default: 1MB) MINSIZE="${MINSIZE:-1048576}" if [ -z "$res" ]; then printf "desktop resolution (hxv): " read -r res fi if [ -n "$res" ] && [ -n "$MINSIZE" ]; then imgp -ackx "$res" -s "$MINSIZE" fi nnn-5.0/plugins/imgur000077500000000000000000000505521466310014300147060ustar00rootroot00000000000000#!/usr/bin/env bash ########################################################################## # The MIT License # # Copyright (c) jomo # # Permission is hereby granted, free of charge, # to any person obtaining a copy of this software and # associated documentation files (the "Software"), to # deal in the Software without restriction, including # without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom # the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice # shall be included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ########################################################################## # https://github.com/jomo/imgur-screenshot # https://help.imgur.com/hc/en-us/articles/209592766-Tools-for-Imgur # # Slightly modified for `nnn` integration # # Shell: Bash # Description: Upload an image file to imgur if [ "${1}" = "--debug" ]; then echo "########################################" echo "Enabling debug mode" echo "Please remove credentials before pasting" echo "########################################" echo "" uname -a for arg in ${0} "${@}"; do echo -n "'${arg}' " done echo -e "\n" shift set -x fi current_version="v1.7.4" function is_mac() { uname | grep -q "Darwin" } ### IMGUR-SCREENSHOT DEFAULT CONFIG #### # You can override the config in ~/.config/imgur-screenshot/settings.conf imgur_anon_id="ea6c0ef2987808e" imgur_icon_path="${HOME}/Pictures/imgur.png" imgur_acct_key="" imgur_secret="" login="false" album_title="" album_id="" credentials_file="${HOME}/.config/imgur-screenshot/credentials.conf" file_name_format="imgur-%Y_%m_%d-%H:%M:%S.png" # when using scrot, must end with .png! file_dir="${HOME}/Pictures" upload_connect_timeout="5" upload_timeout="120" upload_retries="1" # shellcheck disable=SC2034 if is_mac; then screenshot_select_command="screencapture -i %img" screenshot_window_command="screencapture -iWa %img" screenshot_full_command="screencapture %img" open_command="open %url" else screenshot_select_command="scrot -s %img" screenshot_window_command="scrot %img" screenshot_full_command="scrot %img" open_command="xdg-open %url" fi open="true" mode="select" edit_command="gimp %img" edit="false" exit_on_album_creation_fail="true" log_file="${HOME}/.imgur-screenshot.log" auto_delete="" copy_url="true" keep_file="true" check_update="true" # NOTICE: if you make changes here, also edit the docs at # https://github.com/jomo/imgur-screenshot/wiki/Config # You can override the config in ~/.config/imgur-screenshot/settings.conf ############## END CONFIG ############## settings_path="${HOME}/.config/imgur-screenshot/settings.conf" if [ -f "${settings_path}" ]; then # shellcheck disable=SC1090 source "${settings_path}" fi # dependency check if [ "${1}" = "--check" ]; then (type grep &>/dev/null && echo "OK: found grep") || echo "ERROR: grep not found" if is_mac; then if type growlnotify &>/dev/null; then echo "OK: found growlnotify" elif type terminal-notifier &>/dev/null; then echo "OK: found terminal-notifier" else echo "ERROR: growlnotify nor terminal-notifier found" fi (type screencapture &>/dev/null && echo "OK: found screencapture") || echo "ERROR: screencapture not found" (type pbcopy &>/dev/null && echo "OK: found pbcopy") || echo "ERROR: pbcopy not found" else (type notify-send &>/dev/null && echo "OK: found notify-send") || echo "ERROR: notify-send (from libnotify-bin) not found" (type scrot &>/dev/null && echo "OK: found scrot") || echo "ERROR: scrot not found" (type xclip &>/dev/null && echo "OK: found xclip") || echo "ERROR: xclip not found" fi (type curl &>/dev/null && echo "OK: found curl") || echo "ERROR: curl not found" exit 0 fi # notify <'ok'|'error'> <text> function notify() { if is_mac; then if type growlnotify &>/dev/null; then growlnotify --icon "${imgur_icon_path}" --iconpath "${imgur_icon_path}" --title "${2}" --message "${3}" else terminal-notifier -appIcon "${imgur_icon_path}" -contentImage "${imgur_icon_path}" -title "imgur: ${2}" -message "${3}" fi else if [ "${1}" = "error" ]; then notify-send -a ImgurScreenshot -u critical -c "im.error" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}" else notify-send -a ImgurScreenshot -u low -c "transfer.complete" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}" fi fi } function take_screenshot() { echo "Please select area" is_mac || sleep 0.1 # https://bbs.archlinux.org/viewtopic.php?pid=1246173#p1246173 cmd="screenshot_${mode}_command" cmd=${!cmd//\%img/${1}} if ! shot_err="$(${cmd} &>/dev/null)"; then #takes a screenshot with selection echo "Failed to take screenshot '${1}': '${shot_err}'. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}" notify error "Something went wrong :(" "Information has been logged" exit 1 fi } function check_for_update() { # exit non-zero on HTTP error, output only the body (no stats) but output errors, follow redirects, output everything to stdout remote_version="$(curl --compressed -fsSL --stderr - "https://api.github.com/repos/jomo/imgur-screenshot/releases" | grep -Em 1 --color 'tag_name":\s*".*"' | cut -d '"' -f 4)" if [ -n "$remote_version" ]; then if [ ! "${current_version}" = "${remote_version}" ] && [ -n "${current_version}" ] && [ -n "${remote_version}" ]; then echo "Update found!" echo "Version ${remote_version} is available (You have ${current_version})" notify ok "Update found" "Version ${remote_version} is available (You have ${current_version}). https://github.com/jomo/imgur-screenshot" echo "Check https://github.com/jomo/imgur-screenshot/releases/${remote_version} for more info." elif [ -z "${current_version}" ] || [ -z "${remote_version}" ]; then echo "Invalid empty version string" echo "Current (local) version: '${current_version}'" echo "Latest (remote) version: '${remote_version}'" else echo "Version ${current_version} is up to date." fi else echo "Failed to check for latest version: ${remote_version}" fi } function check_oauth2_client_secrets() { if [ -z "${imgur_acct_key}" ] || [ -z "${imgur_secret}" ]; then echo "In order to upload to your account, register a new application at:" echo "https://api.imgur.com/oauth2/addclient" echo "Select 'OAuth 2 authorization without a callback URL'" echo "Then, set the imgur_acct_key (Client ID) and imgur_secret in your config." exit 1 fi } function load_access_token() { token_expire_time=0 # check for saved access_token and its expiration date if [ -f "${credentials_file}" ]; then # shellcheck disable=SC1090 source "${credentials_file}" fi current_time="$(date +%s)" preemptive_refresh_time="$((10*60))" expired="$((current_time > (token_expire_time - preemptive_refresh_time)))" if [ -n "${refresh_token}" ]; then # token already set if [ "${expired}" -eq "0" ]; then # token expired refresh_access_token "${credentials_file}" fi else acquire_access_token "${credentials_file}" fi } function acquire_access_token() { check_oauth2_client_secrets # prompt for a PIN authorize_url="https://api.imgur.com/oauth2/authorize?client_id=${imgur_acct_key}&response_type=pin" echo "Go to" echo "${authorize_url}" echo "and grant access to this application." read -rp "Enter the PIN: " imgur_pin if [ -z "${imgur_pin}" ]; then echo "PIN not entered, exiting" exit 1 fi # exchange the PIN for access token and refresh token response="$(curl --compressed -fsSL --stderr - \ -F "client_id=${imgur_acct_key}" \ -F "client_secret=${imgur_secret}" \ -F "grant_type=pin" \ -F "pin=${imgur_pin}" \ https://api.imgur.com/oauth2/token)" save_access_token "${response}" "${1}" } function refresh_access_token() { check_oauth2_client_secrets token_url="https://api.imgur.com/oauth2/token" # exchange the refresh token for access_token and refresh_token if ! response="$(curl --compressed -fsSL --stderr - \ -F "client_id=${imgur_acct_key}" \ -F "client_secret=${imgur_secret}" \ -F "grant_type=refresh_token" \ -F "refresh_token=${refresh_token}" \ "${token_url}" )"; then # curl failed handle_upload_error "${response}" "${token_url}" exit 1 fi save_access_token "${response}" "${1}" } function save_access_token() { if ! grep -q "access_token" <<<"${1}"; then # server did not send access_token echo "Error: Something is wrong with your credentials:" echo "${1}" exit 1 fi access_token="$(grep -Eo 'access_token":".*"' <<<"${1}" | cut -d '"' -f 3)" refresh_token="$(grep -Eo 'refresh_token":".*"' <<<"${1}" | cut -d '"' -f 3)" expires_in="$(grep -Eo 'expires_in":[0-9]*' <<<"${1}" | cut -d ':' -f 2)" token_expire_time="$(( $(date +%s) + expires_in ))" # create dir if not exist mkdir -p "$(dirname "${2}")" 2>/dev/null touch "${2}" && chmod 600 "${2}" cat <<EOF > "${2}" access_token="${access_token}" refresh_token="${refresh_token}" token_expire_time="${token_expire_time}" EOF } function fetch_account_info() { response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/account/me)" if grep -Eq '"success":\s*true' <<<"${response}"; then username="$(grep -Eo '"url":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" echo "Logged in as ${username}." echo "https://${username}.imgur.com" else echo "Failed to fetch info: ${response}" fi } function delete_image() { response="$(curl --compressed -X DELETE -fsSL --stderr - -H "Authorization: Client-ID ${1}" "https://api.imgur.com/3/image/${2}")" if grep -Eq '"success":\s*true' <<<"${response}"; then echo "Image successfully deleted (delete hash: ${2})." >> "${3}" else echo "The Image could not be deleted: ${response}." >> "${3}" fi } function upload_authenticated_image() { echo "Uploading '${1}'..." title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)" if [ -n "${album_id}" ]; then response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)" else response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)" fi # JSON parser premium edition (not really) if grep -Eq '"success":\s*true' <<<"${response}"; then img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https! del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" if [ -n "${auto_delete}" ]; then export -f delete_image echo "Deleting image in ${auto_delete} seconds." nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" & fi handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}" else # upload failed err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" test -z "${err_msg}" && err_msg="${response}" handle_upload_error "${err_msg}" "${1}" fi } function upload_anonymous_image() { echo "Uploading '${1}'..." title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)" if [ -n "${album_id}" ]; then response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" https://api.imgur.com/3/image)" else response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" https://api.imgur.com/3/image)" fi # JSON parser premium edition (not really) if grep -Eq '"success":\s*true' <<<"${response}"; then img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https! del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" if [ -n "${auto_delete}" ]; then export -f delete_image echo "Deleting image in ${auto_delete} seconds." nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" & fi handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}" else # upload failed err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" test -z "${err_msg}" && err_msg="${response}" handle_upload_error "${err_msg}" "${1}" fi } function handle_upload_success() { echo "" echo "image link: ${1}" echo "delete link: ${2}" if [ "${copy_url}" = "true" ] && [ -z "${album_title}" ]; then if is_mac; then echo -n "${1}" | pbcopy else echo -n "${1}" | xclip -selection clipboard fi echo "URL copied to clipboard" fi # print to log file: image link, image location, delete link echo -e "${1}\t${3}\t${2}" >> "${log_file}" notify ok "Upload done!" "${1}" # if [ ! -z "${open_command}" ] && [ "${open}" = "true" ]; then # open_cmd=${open_command//\%url/${1}} # open_cmd=${open_cmd//\%img/${2}} # echo "Opening '${open_cmd}'" # eval "${open_cmd}" # fi } function handle_upload_error() { error="Upload failed: \"${1}\"" echo "${error}" echo -e "Error\t${2}\t${error}" >> "${log_file}" notify error "Upload failed :(" "${1}" } function handle_album_creation_success() { echo "" echo "Album link: ${1}" echo "Delete hash: ${2}" echo "" notify ok "Album created!" "${1}" if [ "${copy_url}" = "true" ]; then if is_mac; then echo -n "${1}" | pbcopy else echo -n "${1}" | xclip -selection clipboard fi echo "URL copied to clipboard" fi # print to log file: album link, album title, delete hash echo -e "${1}\t\"${3}\"\t${2}" >> "${log_file}" } function handle_album_creation_error() { error="Album creation failed: \"${1}\"" echo -e "Error\t${2}\t${error}" >> "${log_file}" notify error "Album creation failed :(" "${1}" if [ ${exit_on_album_creation_fail} ]; then exit 1 fi } while [ ${#} != 0 ]; do case "${1}" in -h | --help) echo "usage: ${0} [--debug] [-c | --check | -v | -h | -u]" echo " ${0} [--debug] [option]... [file]..." echo "" echo " --debug Enable debugging, must be first option" echo " -h, --help Show this help, exit" echo " -v, --version Show current version, exit" echo " --check Check if all dependencies are installed, exit" echo " -c, --connect Show connected imgur account, exit" echo " -o, --open <true|false> Override 'open' config" echo " -e, --edit <true|false> Override 'edit' config" echo " -i, --edit-command <command> Override 'edit_command' config (include '%img'), sets --edit 'true'" echo " -l, --login <true|false> Override 'login' config" echo " -a, --album <album_title> Create new album and upload there" echo " -A, --album-id <album_id> Override 'album_id' config" echo " -k, --keep-file <true|false> Override 'keep_file' config" echo " -d, --auto-delete <s> Automatically delete image after <s> seconds" echo " -u, --update Check for updates, exit" echo " file Upload file instead of taking a screenshot" exit 0;; -v | --version) echo "${current_version}" exit 0;; -s | --select) mode="select" shift;; -w | --window) mode="window" shift;; -f | --full) mode="full" shift;; -o | --open) # shellcheck disable=SC2034 open="${2}" shift 2;; -e | --edit) edit="${2}" shift 2;; -i | --edit-command) edit_command="${2}" edit="true" shift 2;; -l | --login) login="${2}" shift 2;; -c | --connect) load_access_token fetch_account_info exit 0;; -a | --album) album_title="${2}" shift 2;; -A | --album-id) album_id="${2}" shift 2;; -k | --keep-file) keep_file="${2}" shift 2;; -d | --auto-delete) auto_delete="${2}" shift 2;; -u | --update) check_for_update exit 0;; *) upload_files=("${@}") break;; esac done if [ "${login}" = "true" ]; then # load before changing directory load_access_token fi if [ -n "${album_title}" ]; then if [ "${login}" = "true" ]; then response="$(curl -fsSL --stderr - \ -F "title=${album_title}" \ -H "Authorization: Bearer ${access_token}" \ https://api.imgur.com/3/album)" else response="$(curl -fsSL --stderr - \ -F "title=${album_title}" \ -H "Authorization: Client-ID ${imgur_anon_id}" \ https://api.imgur.com/3/album)" fi if grep -Eq '"success":\s*true' <<<"${response}"; then # Album creation successful echo "Album '${album_title}' successfully created" album_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" handle_album_creation_success "https://imgur.com/a/${album_id}" "${del_id}" "${album_title}" if [ "${login}" = "false" ]; then album_id="${del_id}" fi else # Album creation failed err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)" test -z "${err_msg}" && err_msg="${response}" handle_album_creation_error "${err_msg}" "${album_title}" fi fi if [ -z "${upload_files[*]}" ]; then upload_files[0]="" fi for upload_file in "${upload_files[@]}"; do if [ -z "${upload_file}" ]; then cd "${file_dir}" || exit 1 # new filename with date img_file="$(date +"${file_name_format}")" take_screenshot "${img_file}" else # upload file instead of screenshot img_file="${upload_file}" fi # get full path #cd "$(dirname "$(realpath "${img_file}")")" #img_file="$(realpath "${img_file}")" # check if file exists if ! [ -f "${img_file}" ]; then echo "file '${img_file}' doesn't exist !" read -r _ exit 1 fi # open image in editor if configured if [ "${edit}" = "true" ]; then edit_cmd=${edit_command//\%img/${img_file}} echo "Opening editor '${edit_cmd}'" if ! (eval "${edit_cmd}"); then echo "Error for image '${img_file}': command '${edit_cmd}' failed, not uploading. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}" notify error "Something went wrong :(" "Information has been logged" exit 1 fi fi if [ "${login}" = "true" ]; then upload_authenticated_image "${img_file}" else upload_anonymous_image "${img_file}" fi # delete file if configured if [ "${keep_file}" = "false" ] && [ -z "${1}" ]; then echo "Deleting temp file ${file_dir}/${img_file}" rm -rf -- "${img_file}" fi echo "" done if [ "${check_update}" = "true" ]; then check_for_update fi read -r _ ������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/imgview�����������������������������������������������������������������������������0000775�0000000�0000000�00000007670�14663100143�0015235�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Open hovered or current directory in image viewer. # Generates media thumbnails with optional dependencies. # # Dependencies: # - imv (https://github.com/eXeC64/imv) or, # - sxiv (https://github.com/muennich/sxiv) or, # - nsxiv (https://codeberg.org/nsxiv/nsxiv) or, # - ucollage (https://github.com/ckardaris/ucollage) or, # - lsix (https://github.com/hackerb9/lsix), or # - viu (https://github.com/atanunq/viu), or # - catimg (https://github.com/posva/catimg), or # - optional: ffmpeg for audio thumbnails (album art) # - optional: ffmpegthumbnailer for video thumbnails # # Shell: POSIX compliant # Author: Arun Prakash Jana, Luuk van Baal # # Consider setting NNN_PREVIEWDIR to $XDG_CACHE_HOME/nnn/previews # if you want to keep media thumbnails on disk between reboots. NNN_PREVIEWDIR="${NNN_PREVIEWDIR:-${TMPDIR:-/tmp}/nnn/previews}" exit_prompt() { [ -n "$1" ] && printf "%s\n" "$1" printf "%s" "Press any key to exit..." cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" clear exit } make_thumbs() { mkdir -p "$NNN_PREVIEWDIR$dir" || return if [ "$1" = "viu" ] || [ "$1" = "catimg" ]; then [ -d "$target" ] && exit_prompt "$1 can only display a single image" mime="$(file -bL --mime-type -- "$target")" case "$mime" in audio/*) ffmpeg -i "$target" "$NNN_PREVIEWDIR$target.jpg" -y >/dev/null 2>&1 ret="$NNN_PREVIEWDIR/$target.jpg" ;; video/*) ffmpegthumbnailer -i "$target" -o "$NNN_PREVIEWDIR$target.jpg" 2> /dev/null ret="$NNN_PREVIEWDIR/$target.jpg" ;; *) ret="$target" ;; esac fi for file in "$dir"/*; do if [ ! -f "$NNN_PREVIEWDIR$file.jpg" ]; then case "$(file -bL --mime-type -- "$file")" in audio/*) [ "$1" != "sxiv" ] && ffmpeg -i "$file" "$NNN_PREVIEWDIR$file.jpg" -y >/dev/null 2>&1 ;; video/*) [ "$1" != "ucollage" ] && ffmpegthumbnailer -i "$file" -o "$NNN_PREVIEWDIR$file.jpg" 2> /dev/null ;; esac fi done for file in "$NNN_PREVIEWDIR$dir"/*; do filename="$(basename "$file" .jpg)" [ ! -e "$dir/$filename" ] && rm -- "$file" 2>/dev/null done } listimages() { find -L "$dir" "$NNN_PREVIEWDIR$dir" -maxdepth 1 -type f -print0 2>/dev/null | sort -z } view_files() { [ -f "$target" ] && count="-n $(listimages | grep -a -m 1 -ZznF "$target" | cut -d: -f1)" case "$1" in nsxiv) listimages | xargs -0 nsxiv -a "${count:--t}" -- ;; sxiv) listimages | xargs -0 sxiv -a "${count:--t}" -- ;; imv*) listimages | xargs -0 "$1" "${count:-}" -- ;; esac } target="$(readlink -f "$1")" [ -d "$target" ] && dir="$target" || dir="${target%/*}" if uname | grep -q "Darwin"; then [ -f "$1" ] && open "$1" >/dev/null 2>&1 & elif type lsix >/dev/null 2>&1; then if [ -d "$target" ]; then cd "$target" || exit_prompt fi make_thumbs lsix clear lsix cd "$NNN_PREVIEWDIR$dir" && lsix exit_prompt elif type ucollage >/dev/null 2>&1; then type ffmpeg >/dev/null 2>&1 && make_thumbs ucollage UCOLLAGE_EXPAND_DIRS=1 ucollage "$dir" "$NNN_PREVIEWDIR$dir" || exit_prompt elif type sxiv >/dev/null 2>&1; then type ffmpegthumbnailer >/dev/null 2>&1 && make_thumbs sxiv view_files sxiv >/dev/null 2>&1 & elif type nsxiv >/dev/null 2>&1; then type ffmpegthumbnailer >/dev/null 2>&1 && make_thumbs sxiv view_files nsxiv >/dev/null 2>&1 & elif type imv >/dev/null 2>&1; then make_thumbs imv view_files imv >/dev/null 2>&1 & elif type imvr >/dev/null 2>&1; then make_thumbs imv view_files imvr >/dev/null 2>&1 & elif type viu >/dev/null 2>&1; then clear make_thumbs viu viu -n "$ret" exit_prompt elif type catimg >/dev/null 2>&1; then make_thumbs catimg catimg "$ret" exit_prompt else exit_prompt "Please install sxiv/nsxiv/imv/viu/catimg/lsix." fi ������������������������������������������������������������������������nnn-5.0/plugins/ipinfo������������������������������������������������������������������������������0000775�0000000�0000000�00000000372�14663100143�0015042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Shows the external IP address and whois information. Useful over VPNs. # # Shell: POSIX compliant # Author: Arun Prakash Jana IP=$(curl -s ifconfig.me) whois "$IP" echo your external IP address is "$IP" read -r _ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/kdeconnect��������������������������������������������������������������������������0000775�0000000�0000000�00000003146�14663100143�0015675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Send files or folders to your Android device using kdeconnect-cli. # kdeconnect must be configured on the Android device and the PC. # # Usage: # - Hover over a file or a folder and call the plugin. # - Alternatively, select the files and folders you would like to send, and activate the plugin. # # Shell: POSIX compliant # Author: juacq97, raffaem, N-R-K # If you want system notification, put this equal to 1 notify=0 note() { if [ $notify = 1 ]; then notify-send -a "Kdeconnect" "$1" else echo "[Kdeconnect] $1" fi } # kdeconnect doesn't cope with non-files filter_files() { xargs -0 -I{} sh -c ' if [ -f "{}" ]; then printf "%s\0" "{}"; else printf "Error: not a regular file: %s\n" "{}" >&2; fi;' } send() { filter_files | xargs -0 -I{} kdeconnect-cli --name "$devname" --share {} note "Files sent" } # Select paired device names=$(kdeconnect-cli --list-available --name-only 2>/dev/null) if [ -z "$names" ]; then note "No devices paired and available" exit fi ndevs=$(printf "%s" "$names" | awk 'END{print NR}') if [ "$ndevs" -eq 1 ]; then devname="$names" else printf "%s" "$names" | awk '{ print NR ". " $0 }' printf "Pick a device: " read -r pick if [ -n "$pick" ] && [ "$pick" -eq "$pick" ]; then devname=$(printf '%s' "$names" | awk "NR==$pick") fi fi selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} if [ -s "$selection" ]; then send < "$selection" [ -p "$NNN_PIPE" ] && printf "-" > "$NNN_PIPE" # clear selection elif [ -n "$1" ]; then printf "%s" "$1" | send else note "No selection and no hovered file" fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/launch������������������������������������������������������������������������������0000775�0000000�0000000�00000002041�14663100143�0015023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Independent POSIX-compliant GUI application launcher. # Fuzzy find executables in $PATH and launch an application. # stdin, stdout, stderr are suppressed so CLI tools exit silently. # # To configure launch as an independent app launcher add a keybind # to open launch in a terminal e.g., # # xfce4-terminal -e "${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/launch # # Dependencies: fzf # # Usage: launch [delay] # delay is in seconds, if omitted launch waits for 1 sec # # Integration with nnn: launch is installed with other plugins, nnn picks it up. # # Shell: POSIX compliant # Author: Arun Prakash Jana # shellcheck disable=SC2086 IFS=':' get_selection() { if type fzf >/dev/null 2>&1; then { IFS=':'; ls -H $PATH; } | sort | fzf else exit 1 fi } if selection=$( get_selection ); then setsid "$selection" 2>/dev/null 1>/dev/null & if [ -n "$1" ]; then sleep "$1" else sleep 1 fi fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/mimelist����������������������������������������������������������������������������0000775�0000000�0000000�00000001151�14663100143�0015375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Find and list files by mime type in smart context # # Dependencies: # - file # - mimetype (optional, PERL File MimeInfo) # # Shell: POSIX compliant # Author: Arun Prakash Jana, Michel DHOOGE # shellcheck disable=SC1090,SC1091 . "$(dirname "$0")"/.nnn-plugin-helper printf "mime (e.g., video/audio/image): " read -r mime printf "%s" "+l" > "$NNN_PIPE" if type mimetype >/dev/null 2>&1; then find . | mimetype -f - | grep "$mime" | awk -F: '{printf "%s%c", $1, 0}' > "$NNN_PIPE" else find . | file -if- | grep "$mime" | awk -F: '{printf "%s%c", $1, 0}' > "$NNN_PIPE" fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/moclyrics���������������������������������������������������������������������������0000775�0000000�0000000�00000002050�14663100143�0015555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Fetches the lyrics of the track currently playing in MOC # # Dependencies: ddgr (https://github.com/jarun/ddgr) # # Shell: POSIX compliant # Author: Arun Prakash Jana # Check if MOC server is running cmd=$(pgrep -x mocp 2>/dev/null) ret=$cmd if [ -z "$ret" ]; then exit fi # Grab the output out="$(mocp -i)" # Check if anything is playing state=$(echo "$out" | grep "State:" | cut -d' ' -f2) if ! [ "$state" = 'PLAY' ]; then exit fi # Try by Artist and Song Title first ARTIST="$(echo "$out" | grep 'Artist:' | cut -d':' -f2 | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" TITLE="$(echo "$out" | grep 'SongTitle:' | cut -d':' -f2 | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" if [ -n "$ARTIST" ] && [ -n "$TITLE" ]; then ddgr -w azlyrics.com --ducky "$ARTIST" "$TITLE" else # Try by file name FILENAME="$(basename "$(echo "$out" | grep 'File:' | cut -d':' -f2)")" FILENAME="$(echo "${FILENAME%%.*}" | tr -d -)" if [ -n "$FILENAME" ]; then ddgr -w azlyrics.com --ducky "$FILENAME" fi fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/mocq��������������������������������������������������������������������������������0000775�0000000�0000000�00000004017�14663100143�0014515�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Appends and optionally plays music in MOC # # Notes: # - if selection is available, plays it, else plays the current file or directory # - appends tracks and exits is MOC is running, else clears playlist and adds tracks # - to let mocp shuffle tracks, set SHUFFLE=1 # # Shell: POSIX compliant # Authors: Arun Prakash Jana, ath3 IFS="$(printf '\n\r')" selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} cmd=$(pgrep -x mocp 2>/dev/null) ret=$cmd SHUFFLE="${SHUFFLE:-0}" mocp_add () { if [ "$SHUFFLE" = 1 ]; then if [ "$resp" = "y" ]; then arr=$(tr '\0' '\n' < "$selection") elif [ -n "$1" ]; then arr="$1" fi for entry in $arr do if [ -d "$entry" ]; then arr2=$arr2$(find "$entry" -type f \( ! -iname "*.m3u" ! -iname "*.pls" \)) elif echo "$entry" | grep -qv '\.m3u$\|\.pls$' ; then arr2=$(printf "%s\n%s" "$entry" "$arr2") fi done mocp -o shuffle echo "$arr2" | xargs -d "\n" mocp -a else if [ "$resp" = "y" ]; then xargs < "$selection" -0 mocp -a else mocp -a "$1" fi fi } if [ ! -s "$selection" ] && [ -z "$1" ]; then exit fi if [ "$2" = "opener" ]; then : elif [ -s "$selection" ]; then printf "Work with selection? Enter 'y' to confirm: " read -r resp fi if [ -z "$ret" ]; then # mocp not running mocp -S else # mocp running, check if it's playing state=$(mocp -i | grep "State:" | cut -d' ' -f2) if [ "$state" = 'PLAY' ]; then # add to playlist and exit mocp_add "$1" # uncomment the line below to show mocp interface after appending # mocp exit fi fi # clear selection and play mocp -c mocp_add "$1" "$resp" mocp -p # uncomment the line below to show mocp interface after appending # mocp # Clear selection if [ "$resp" = "y" ] && [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/mp3conv�����������������������������������������������������������������������������0000775�0000000�0000000�00000001665�14663100143�0015151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Extract audio from multimedia files and convert to mp3 # # Dependencies: ffmpeg compiled with libmp3lame audio codec support # # Shell: POSIX compliant # Author: Arun Prakash Jana outdir=_mp3files handle_multimedia() { mime="${1}" file="${2}" case "${mime}" in audio/* | video/*) ffmpeg -i "${file}" -vn -codec:a libmp3lame -q:a 2 "${outdir}/${file%.*}.mp3" ;; *) ;; esac } printf "Process 'a'll in directory or 'c'urrent? " read -r resp if [ "$resp" = "a" ]; then if ! [ -e "${outdir}" ]; then mkdir "${outdir}" fi for f in *; do if [ -f "${f}" ]; then mimestr="$( file --dereference --brief --mime-type -- "${f}" )" handle_multimedia "${mimestr}" "${f}" fi done elif [ "$resp" = "c" ] && [ -f "$1" ]; then ffmpeg -i "${1}" -vn -codec:a libmp3lame -q:a 2 "${1%.*}.mp3" fi ���������������������������������������������������������������������������nnn-5.0/plugins/mtpmount����������������������������������������������������������������������������0000775�0000000�0000000�00000003761�14663100143�0015446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Toggle mount of MTP device (eg. Android device) # 'l' to list mountable devices # 'n' integer associated to device to mount # 'q'/'Return' exit # # Dependencies: gvfs-mtp # # Notes: The MTP device should be mounted at /run/user/$UID/gvfs. # Put /run/user/$UID/gvfs to bookmark entries (NNN_BMS) for faster access. # Make sure the device is unlocked when mounting. # # When doing copy-paste into MTP device, you will get an error like this: # cp: preserving times for './gambar1.png': Operation not supported # That just means the file is copied but timestamp won't be preserved. # It's like doing `cp -p localfile.txt file-to-SMB.txt`. # # Shell: POSIX compliant # Author: Benawi Adha prompt="Device number ('l' to list): " IFS=' ' lsmtp () { devs=$(gio mount -li | grep -e 'activation_root' | sed 's/\s*activation_root=//g') c=1 printf "Devices list:\n" for i in $devs; do printf "%s %s\\n" "$c" "$i" c=$(( c + 1 )) done echo } lsmtp printf "%s" "$prompt" read -r input while [ -n "$input" ] do if [ "$input" = "l" ]; then lsmtp elif [ "$input" = "q" ] || [ "$input" -eq 0 ]; then exit elif [ "$input" -le "$(printf '%s\n' "${devs}" | grep -c '^')" ]; then # dev=$(printf "%s\n" "$devs" | cut -d$'\n' -f${input}) c=1 for i in $devs; do dev=$i if [ "$input" -eq $c ]; then break fi c=$(( c + 1 )) done if (gio mount -l | grep '^Mount([1-9]).*'"$dev" ) 1>/dev/null; then if gio mount -u "${dev}"; then printf "%s unmounted\n" "$dev" fi else if gio mount "${dev}"; then printf "%s mounted to /run/user/\$UID/gvfs\n" "$dev" fi fi echo else printf "Invalid input\n" fi printf "%s" "$prompt" read -r input done ���������������nnn-5.0/plugins/nbak��������������������������������������������������������������������������������0000775�0000000�0000000�00000003057�14663100143�0014474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Backup nnn configuration # - config dir content # - environment config # - shell functions and aliases # # Shell: POSIX compliant # Author: Léo Villeveygoux nnn_aliases="n nnn" outdir="nnn-$(whoami)@$(hostname)" outfile="${outdir}.tar.bz2" shellname="$(basename "$SHELL")" conffile="config.txt" configdir="${XDG_CONFIG_HOME:-$HOME/.config}/nnn" workdir="$PWD" tempdir="$(mktemp -d)" mkdir "$tempdir/$outdir" if [ ! -d "$tempdir" ]; then echo "Can't create work directory." >&2 exit 1 fi cd "$tempdir/$outdir" || exit 1 # Backing up config dir content cp -r -- "$configdir" . || exit 1 # Environment config env | sed "s/'/'\\\\''/" |\ awk '/^NNN_/{print "export '\''"$0"'\''"}' > "$conffile" # Shell functions/aliases case "$shellname" in bash) for name in $nnn_aliases ; do if [ "$(bash -ic "type -t $name")" = "function" ] ; then bash -ic "type $name" | tail -n+2 >> "$conffile" elif bash -ic "alias $name" >/dev/null 2>&1 ; then bash -ic "alias $name" >> "$conffile" fi done ;; zsh) for name in $nnn_aliases ; do if zsh -ic "functions $name" ; then zsh -ic "functions $name" >> "$conffile" elif zsh -ic "alias $name" ; then echo alias "$(zsh -ic "alias $name")" >> "$conffile" fi done ;; *) echo "Unknown shell, skipping alias/function checking." >&2 ;; esac cd .. || exit 1 printf "Saving as '%s' ... " "$workdir/$outfile" tar caf "$workdir/$outfile" "$outdir" && echo "Done" || echo "Failed" cd "$workdir" && rm -rf -- "$tempdir" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/nmount������������������������������������������������������������������������������0000775�0000000�0000000�00000007043�14663100143�0015100�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Toggle mount status of a device using pmount # If the device is not mounted, it will be mounted. # If the device is mounted, it will be unmounted and powered down. # # Dependencies: lsblk, pmount (optional), udisks2 # # Usage: Runs `lsblk` on 'l', exits on 'Return`. # # Notes: # - The script uses Linux-specific lsblk to list block devices. Alternatives: # macOS: "diskutil list" # BSD: "geom disk list" # - The script uses udisksctl (from udisks2) to power down devices. This is also Linux-specific. # Users on non-Linux platforms can comment it and use an alternative to power-down disks. # # Shell: POSIX compliant # Author: Arun Prakash Jana prompt="device name [e.g. sdXn] ('l'ist, 'q'uit): " lsblk printf "\nEnsure you aren't still in the mounted device.\n" printf "%s" "$prompt" read -r dev while [ -n "$dev" ]; do if [ "$dev" = "l" ]; then lsblk elif [ "$dev" = "q" ]; then exit else # LUKS volumes mounted with udisksctl appear differently than with pmount if grep -qs "$dev " /proc/mounts || [ -n "$(lsblk -n "/dev/$dev" -o MOUNTPOINT)" ]; then sync "$(lsblk -n "/dev/$dev" -o MOUNTPOINT | sed "/^$/d")" if type pumount >/dev/null 2>&1; then pumount "/dev/$dev" exit_code="$?" else # Unlike pmount, udisksctl does not transparently handle LUKS volumes # We need to manually get the unlocked device, unmount it, and then lock the volume if lsblk -n "/dev/$dev" -o FSTYPE | grep "crypto_LUKS" >/dev/null; then dev_map="$(udisksctl info -b /dev/"$dev" | grep "CleartextDevice" | grep -o "dm_2d[[:digit:]]*" | sed "s/_2d/-/")" udisksctl unmount -b "/dev/$dev_map" --no-user-interaction >/dev/null exit_code="$?" udisksctl lock -b "/dev/$dev" --no-user-interaction >/dev/null else udisksctl unmount -b "/dev/$dev" --no-user-interaction >/dev/null exit_code="$?" fi fi if [ $exit_code -eq 0 ]; then echo "/dev/$dev unmounted." if udisksctl power-off -b "/dev/$dev" --no-user-interaction; then echo "/dev/$dev ejected." fi fi elif [ -b "/dev/$dev" ]; then if type pmount >/dev/null 2>&1; then pmount "/dev/$dev" exit_code="$?" else # Unlike pmount, udisksctl does not transparently handle LUKS volumes # We need to manually get the unlocked device and mount that instead of the volume itself if [ "$(lsblk "/dev/$dev" -n -o FSTYPE)" = "crypto_LUKS" ]; then dev_map=$(udisksctl unlock -b "/dev/$dev" --no-user-interaction | grep -o "dm-[[:digit:]]*") udisksctl mount -b "/dev/$dev_map" --no-user-interaction >/dev/null exit_code="$?" else udisksctl mount -b "/dev/$dev" --no-user-interaction >/dev/null exit_code="$?" fi fi if [ $exit_code -eq 0 ]; then sleep 1 echo "/dev/$dev mounted to $(lsblk -n "/dev/$dev" -o MOUNTPOINT | sed "/^$/d")." fi else echo "/dev/$dev does not exist or is not a block device." fi fi echo printf "%s" "$prompt" read -r dev done ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/nuke��������������������������������������������������������������������������������0000775�0000000�0000000�00000044514�14663100143�0014526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Sample script to play files in apps by file type or mime # # Shell: POSIX compliant # Usage: nuke filepath # # Integration with nnn: # 1. Export the required config: # export NNN_OPENER=/absolute/path/to/nuke # # Otherwise, if nuke is in $PATH # # export NNN_OPENER=nuke # 2. Run nnn with the program option to indicate a CLI opener # nnn -c # # The -c program option overrides option -e # 3. nuke can use nnn plugins (e.g. mocq is used for audio), $PATH is updated. # # Details: # Inspired by ranger's scope.sh, modified for usage with nnn. # # Guards against accidentally opening mime types like executables, shared libs etc. # # Tries to play 'file' (1st argument) in the following order: # 1. by extension # 2. by mime (image, video, audio, pdf) # 3. by mime (other file types) # 4. by mime (prompt and run executables) # # Modification tips: # 1. Invokes CLI utilities by default. Set GUI to 1 to enable GUI apps. # 2. PAGER is "less -R". # 3. Start GUI apps in bg to unblock. Redirect stdout and strerr if required. # 4. Some CLI utilities are piped to the $PAGER, to wait and quit uniformly. # 5. If the output cannot be paged use "read -r _" to wait for user input. # 6. On a DE, try 'xdg-open' or 'open' in handle_fallback() as last resort. # # Feel free to change the utilities to your favourites and add more mimes. # # Defaults: # By extension (only the enabled ones): # most archives: list with atool, bsdtar # rar: list with unrar # 7-zip: list with 7z # pdf: zathura (GUI), pdftotext, mutool, exiftool # audio: mocq (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool # avi|mkv|mp4: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool # log: vi # torrent: rtorrent, transmission-show # odt|ods|odp|sxw: odt2txt # md: glow (https://github.com/charmbracelet/glow), lowdown (https://kristaps.bsd.lv/lowdown) # htm|html|xhtml: w3m, lynx, elinks # json: jq, python (json.tool module) # Multimedia by mime: # image/*: imv/sxiv/nsxiv (GUI), viu (https://github.com/atanunq/viu), img2txt, exiftool # video/*: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool # audio/*: mocq (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool # application/pdf: zathura (GUI), pdftotext, mutool, exiftool # Other mimes: # text/troff: man -l # text/* | */xml: vi # image/vnd.djvu): djvutxt, exiftool # # TODO: # 1. Adapt, test and enable all mimes # 2. Clean-up the unnecessary exit codes # set to 1 to enable GUI apps and/or BIN execution GUI="${GUI:-0}" BIN="${BIN:-0}" set -euf -o noclobber -o noglob -o nounset IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n PATH=$PATH:"${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins" IMAGE_CACHE_PATH="$(dirname "$1")"/.thumbs FPATH="$1" FNAME=$(basename "$1") EDITOR="${VISUAL:-${EDITOR:-vi}}" PAGER="${PAGER:-less -R}" ext="${FNAME##*.}" if [ -n "$ext" ]; then ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" fi is_mac() { uname | grep -q "Darwin" } handle_pdf() { if [ "$GUI" -ne 0 ]; then if is_mac; then nohup open "${FPATH}" >/dev/null 2>&1 & elif type zathura >/dev/null 2>&1; then nohup zathura "${FPATH}" >/dev/null 2>&1 & else return fi elif type pdftotext >/dev/null 2>&1; then ## Preview as text conversion pdftotext -l 10 -nopgbrk -q -- "${FPATH}" - | eval "$PAGER" elif type mutool >/dev/null 2>&1; then mutool draw -F txt -i -- "${FPATH}" 1-10 | eval "$PAGER" elif type exiftool >/dev/null 2>&1; then exiftool "${FPATH}" | eval "$PAGER" else return fi exit 0 } handle_audio() { if type mocp >/dev/null 2>&1 && type mocq >/dev/null 2>&1; then mocq "${FPATH}" "opener" >/dev/null 2>&1 elif type mpv >/dev/null 2>&1; then mpv "${FPATH}" >/dev/null 2>&1 & elif type media_client >/dev/null 2>&1; then media_client play "${FPATH}" >/dev/null 2>&1 & elif type mediainfo >/dev/null 2>&1; then mediainfo "${FPATH}" | eval "$PAGER" elif type exiftool >/dev/null 2>&1; then exiftool "${FPATH}"| eval "$PAGER" else return fi exit 0 } handle_video() { if [ "$GUI" -ne 0 ]; then if is_mac; then nohup open "${FPATH}" >/dev/null 2>&1 & elif type smplayer >/dev/null 2>&1; then nohup smplayer "${FPATH}" >/dev/null 2>&1 & elif type mpv >/dev/null 2>&1; then nohup mpv "${FPATH}" >/dev/null 2>&1 & else return fi elif type ffmpegthumbnailer >/dev/null 2>&1; then # Thumbnail [ -d "${IMAGE_CACHE_PATH}" ] || mkdir "${IMAGE_CACHE_PATH}" ffmpegthumbnailer -i "${FPATH}" -o "${IMAGE_CACHE_PATH}/${FNAME}.jpg" -s 0 viu -n "${IMAGE_CACHE_PATH}/${FNAME}.jpg" | eval "$PAGER" elif type mediainfo >/dev/null 2>&1; then mediainfo "${FPATH}" | eval "$PAGER" elif type exiftool >/dev/null 2>&1; then exiftool "${FPATH}"| eval "$PAGER" else return fi exit 0 } # handle this extension and exit handle_extension() { case "${ext}" in ## Archive a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\ rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip) if type atool >/dev/null 2>&1; then atool --list -- "${FPATH}" | eval "$PAGER" exit 0 elif type bsdtar >/dev/null 2>&1; then bsdtar --list --file "${FPATH}" | eval "$PAGER" exit 0 fi exit 1;; rar) if type unrar >/dev/null 2>&1; then ## Avoid password prompt by providing empty password unrar lt -p- -- "${FPATH}" | eval "$PAGER" fi exit 1;; 7z) if type 7z >/dev/null 2>&1; then ## Avoid password prompt by providing empty password 7z l -p -- "${FPATH}" | eval "$PAGER" exit 0 fi exit 1;; ## PDF pdf) handle_pdf exit 1;; ## Audio aac|flac|m4a|mid|midi|mpa|mp2|mp3|ogg|wav|wma) handle_audio exit 1;; ## Video avi|mkv|mp4) handle_video exit 1;; ## Log files log) "$EDITOR" "${FPATH}" exit 0;; ## BitTorrent torrent) if type rtorrent >/dev/null 2>&1; then rtorrent "${FPATH}" exit 0 elif type transmission-show >/dev/null 2>&1; then transmission-show -- "${FPATH}" exit 0 fi exit 1;; ## OpenDocument odt|ods|odp|sxw) if type odt2txt >/dev/null 2>&1; then ## Preview as text conversion odt2txt "${FPATH}" | eval "$PAGER" exit 0 fi exit 1;; ## Markdown md) if type glow >/dev/null 2>&1; then glow -sdark "${FPATH}" | eval "$PAGER" exit 0 elif type lowdown >/dev/null 2>&1; then lowdown -Tterm "${FPATH}" | eval "$PAGER" exit 0 fi ;; ## HTML htm|html|xhtml) ## Preview as text conversion if type w3m >/dev/null 2>&1; then w3m -dump "${FPATH}" | eval "$PAGER" exit 0 elif type lynx >/dev/null 2>&1; then lynx -dump -- "${FPATH}" | eval "$PAGER" exit 0 elif type elinks >/dev/null 2>&1; then elinks -dump "${FPATH}" | eval "$PAGER" exit 0 fi ;; ## JSON json) if type jq >/dev/null 2>&1; then jq --color-output . "${FPATH}" | eval "$PAGER" exit 0 elif type python >/dev/null 2>&1; then python -m json.tool -- "${FPATH}" | eval "$PAGER" exit 0 fi ;; esac } # sets the variable abs_target, this should be faster than calling printf abspath() { case "$1" in /*) abs_target="$1";; *) abs_target="$PWD/$1";; esac } # storing the result to a tmp file is faster than calling listimages twice listimages() { find -L "///${1%/*}" -maxdepth 1 -type f -print0 | grep -izZE '\.(jpe?g|png|gif|webp|tiff|bmp|ico|svg)$' | sort -zV | tee "$tmp" } load_dir() { abspath "$2" tmp="${TMPDIR:-/tmp}/nuke_$$" trap 'rm -f -- "$tmp"' EXIT count="$(listimages "$abs_target" | grep -a -m 1 -ZznF "$abs_target" | cut -d: -f1)" if [ -n "$count" ]; then if [ "$GUI" -ne 0 ]; then xargs -0 nohup "$1" -n "$count" -- < "$tmp" else xargs -0 "$1" -n "$count" -- < "$tmp" fi else shift "$1" -- "$@" # fallback fi } handle_multimedia() { ## Size of the preview if there are multiple options or it has to be ## rendered from vector graphics. If the conversion program allows ## specifying only one dimension while keeping the aspect ratio, the width ## will be used. # local DEFAULT_SIZE="1920x1080" mimetype="${1}" case "${mimetype}" in ## SVG # image/svg+xml|image/svg) # convert -- "${FPATH}" "${IMAGE_CACHE_PATH}" && exit 6 # exit 1;; ## DjVu # image/vnd.djvu) # ddjvu -format=tiff -quality=90 -page=1 -size="${DEFAULT_SIZE}" \ # - "${IMAGE_CACHE_PATH}" < "${FPATH}" \ # && exit 6 || exit 1;; ## Image image/*) if [ "$GUI" -ne 0 ]; then if is_mac; then nohup open "${FPATH}" >/dev/null 2>&1 & exit 0 elif type imv >/dev/null 2>&1; then load_dir imv "${FPATH}" >/dev/null 2>&1 & exit 0 elif type imvr >/dev/null 2>&1; then load_dir imvr "${FPATH}" >/dev/null 2>&1 & exit 0 elif type sxiv >/dev/null 2>&1; then load_dir sxiv "${FPATH}" >/dev/null 2>&1 & exit 0 elif type nsxiv >/dev/null 2>&1; then load_dir nsxiv "${FPATH}" >/dev/null 2>&1 & exit 0 fi elif type viu >/dev/null 2>&1; then viu -n "${FPATH}" | eval "$PAGER" exit 0 elif type img2txt >/dev/null 2>&1; then img2txt --gamma=0.6 -- "${FPATH}" | eval "$PAGER" exit 0 elif type exiftool >/dev/null 2>&1; then exiftool "${FPATH}" | eval "$PAGER" exit 0 fi # local orientation # orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FPATH}" )" ## If orientation data is present and the image actually ## needs rotating ("1" means no rotation)... # if [[ -n "$orientation" && "$orientation" != 1 ]]; then ## ...auto-rotate the image according to the EXIF data. # convert -- "${FPATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6 # fi ## `w3mimgdisplay` will be called for all images (unless overridden ## as above), but might fail for unsupported types. exit 7;; ## PDF application/pdf) handle_pdf exit 1;; ## Audio audio/*) handle_audio exit 1;; ## Video video/*) handle_video exit 1;; # pdftoppm -f 1 -l 1 \ # -scale-to-x "${DEFAULT_SIZE%x*}" \ # -scale-to-y -1 \ # -singlefile \ # -jpeg -tiffcompression jpeg \ # -- "${FPATH}" "${IMAGE_CACHE_PATH%.*}" \ # && exit 6 || exit 1;; ## ePub, MOBI, FB2 (using Calibre) # application/epub+zip|application/x-mobipocket-ebook|\ # application/x-fictionbook+xml) # # ePub (using https://github.com/marianosimone/epub-thumbnailer) # epub-thumbnailer "${FPATH}" "${IMAGE_CACHE_PATH}" \ # "${DEFAULT_SIZE%x*}" && exit 6 # ebook-meta --get-cover="${IMAGE_CACHE_PATH}" -- "${FPATH}" \ # >/dev/null && exit 6 # exit 1;; ## Font # application/font*|application/*opentype) # preview_png="/tmp/$(basename "${IMAGE_CACHE_PATH%.*}").png" # if fontimage -o "${preview_png}" \ # --pixelsize "120" \ # --fontname \ # --pixelsize "80" \ # --text " ABCDEFGHIJKLMNOPQRSTUVWXYZ " \ # --text " abcdefghijklmnopqrstuvwxyz " \ # --text " 0123456789.:,;(*!?') ff fl fi ffi ffl " \ # --text " The quick brown fox jumps over the lazy dog. " \ # "${FPATH}"; # then # convert -- "${preview_png}" "${IMAGE_CACHE_PATH}" \ # && rm -- "${preview_png}" \ # && exit 6 # else # exit 1 # fi # ;; ## Preview archives using the first image inside. ## (Very useful for comic book collections for example.) # application/zip|application/x-rar|application/x-7z-compressed|\ # application/x-xz|application/x-bzip2|application/x-gzip|application/x-tar) # local fn=""; local fe="" # local zip=""; local rar=""; local tar=""; local bsd="" # case "${mimetype}" in # application/zip) zip=1 ;; # application/x-rar) rar=1 ;; # application/x-7z-compressed) ;; # *) tar=1 ;; # esac # { [ "$tar" ] && fn=$(tar --list --file "${FPATH}"); } || \ # { fn=$(bsdtar --list --file "${FPATH}") && bsd=1 && tar=""; } || \ # { [ "$rar" ] && fn=$(unrar lb -p- -- "${FPATH}"); } || \ # { [ "$zip" ] && fn=$(zipinfo -1 -- "${FPATH}"); } || return # # fn=$(echo "$fn" | python -c "import sys; import mimetypes as m; \ # [ print(l, end='') for l in sys.stdin if \ # (m.guess_type(l[:-1])[0] or '').startswith('image/') ]" |\ # sort -V | head -n 1) # [ "$fn" = "" ] && return # [ "$bsd" ] && fn=$(printf '%b' "$fn") # # [ "$tar" ] && tar --extract --to-stdout \ # --file "${FPATH}" -- "$fn" > "${IMAGE_CACHE_PATH}" && exit 6 # fe=$(echo -n "$fn" | sed 's/[][*?\]/\\\0/g') # [ "$bsd" ] && bsdtar --extract --to-stdout \ # --file "${FPATH}" -- "$fe" > "${IMAGE_CACHE_PATH}" && exit 6 # [ "$bsd" ] || [ "$tar" ] && rm -- "${IMAGE_CACHE_PATH}" # [ "$rar" ] && unrar p -p- -inul -- "${FPATH}" "$fn" > \ # "${IMAGE_CACHE_PATH}" && exit 6 # [ "$zip" ] && unzip -pP "" -- "${FPATH}" "$fe" > \ # "${IMAGE_CACHE_PATH}" && exit 6 # [ "$rar" ] || [ "$zip" ] && rm -- "${IMAGE_CACHE_PATH}" # ;; esac } handle_mime() { mimetype="${1}" case "${mimetype}" in ## Manpages text/troff) man -l "${FPATH}" exit 0;; ## Text text/* | */xml) "$EDITOR" "${FPATH}" exit 0;; ## Syntax highlight # if [[ "$( stat --printf='%s' -- "${FPATH}" )" -gt "${HIGHLIGHT_SIZE_MAX}" ]]; then # exit 2 # fi # if [[ "$( tput colors )" -ge 256 ]]; then # local pygmentize_format='terminal256' # local highlight_format='xterm256' # else # local pygmentize_format='terminal' # local highlight_format='ansi' # fi # env HIGHLIGHT_OPTIONS="${HIGHLIGHT_OPTIONS}" highlight \ # --out-format="${highlight_format}" \ # --force -- "${FPATH}" && exit 5 # pygmentize -f "${pygmentize_format}" -O "style=${PYGMENTIZE_STYLE}"\ # -- "${FPATH}" && exit 5 # exit 2;; ## DjVu image/vnd.djvu) if type djvutxt >/dev/null 2>&1; then ## Preview as text conversion (requires djvulibre) djvutxt "${FPATH}" | eval "$PAGER" exit 0 elif type exiftool >/dev/null 2>&1; then exiftool "${FPATH}" | eval "$PAGER" exit 0 fi exit 1;; esac } handle_fallback() { if [ "$GUI" -ne 0 ]; then if type xdg-open >/dev/null 2>&1; then nohup xdg-open "${FPATH}" >/dev/null 2>&1 & exit 0 elif type open >/dev/null 2>&1; then nohup open "${FPATH}" >/dev/null 2>&1 & exit 0 fi fi echo '----- File details -----' && file --dereference --brief -- "${FPATH}" exit 1 } handle_blocked() { case "${MIMETYPE}" in application/x-sharedlib) exit 0;; application/x-shared-library-la) exit 0;; application/x-executable) exit 0;; application/x-shellscript) exit 0;; application/octet-stream) exit 0;; esac } handle_bin() { case "${MIMETYPE}" in application/x-executable|application/x-shellscript) clear echo '-------- Executable File --------' && file --dereference --brief -- "${FPATH}" printf "Run executable (y/N/'a'rgs)? " read -r answer case "$answer" in [Yy]* ) exec "${FPATH}";; [Aa]* ) printf "args: " read -r args exec "${FPATH}" "$args";; [Nn]* ) exit;; esac esac } MIMETYPE="$( file -bL --mime-type -- "${FPATH}" )" handle_extension handle_multimedia "${MIMETYPE}" handle_mime "${MIMETYPE}" [ "$BIN" -ne 0 ] && [ -x "${FPATH}" ] && handle_bin handle_blocked "${MIMETYPE}" handle_fallback exit 1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/oldbigfile��������������������������������������������������������������������������0000775�0000000�0000000�00000000502�14663100143�0015651�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: List files bigger than input size by ascending access date. # # Dependencies: find sort # # Shell: POSIX compliant # Author: Arun Prakash Jana printf "Min file size (MB): " read -r size find . -size +"$size"M -type f -printf '%A+ %s %p\n' | sort echo "Press any key to exit" read -r _ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/openall�����������������������������������������������������������������������������0000775�0000000�0000000�00000002625�14663100143�0015213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash # Description: Open selected files in nuke one by one or in oneshot # # Notes: 1. Opens the hovered file if the selection is empty # 2. nuke is the default, set OPENER below for custom # 3. Opener is invoked once for each file in a loop # 4. Keep pressing "Enter" to open files one by one # # Shell: bash # Author: Arun Prakash Jana sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} OPENER="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke" if [ -s "$sel" ]; then targets=() while IFS= read -r -d '' entry || [ -n "$entry" ]; do targets+=( "$entry" ) done < "$sel" elements=${#targets[@]} if (( elements == 1 )); then # If there's only one file selected, open without prompts "$OPENER" "${targets[0]}" else printf "open [A]ll? " read -r all for ((index=0; index <= ${#targets[@]}; index++)); do "$OPENER" "${targets[index]}" if [ "$all" != "A" ] && (( index+1 < elements )); then printf "press Enter to open '%s'\n" "${targets[index+1]}" read -r -s -n 1 key if [[ $key != "" ]]; then break fi fi done fi # Clear selection if [ -s "$sel" ] && [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi elif [ -n "$1" ]; then "$OPENER" "$1" fi �����������������������������������������������������������������������������������������������������������nnn-5.0/plugins/organize����������������������������������������������������������������������������0000775�0000000�0000000�00000003225�14663100143�0015374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Organize files in directories by category # # Note: This plugin clears the selection as it changes the contents of the current dir # # Shell: POSIX compliant # Author: th3lusive sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} organize() { case "$(file -biL "$1")" in *video*) [ ! -d "Videos" ] && mkdir "Videos" mv -- "$1" "Videos/$1" printf "Moved %s to Videos\n" "$1" ;; *audio*) [ ! -d "Audio" ] && mkdir "Audio" mv -- "$1" "Audio/$1" printf "Moved %s to Audio\n" "$1" ;; *image*) [ ! -d "Images" ] && mkdir "Images" mv -- "$1" "Images/$1" printf "Moved %s to Images\n" "$1" ;; *pdf*|*document*|*epub*|*djvu*|*cb*) [ ! -d "Documents" ] && mkdir "Documents" mv -- "$1" "Documents/$1" printf "Moved %s to Documents\n" "$1" ;; *text*) [ ! -d "Plaintext" ] && mkdir "Plaintext" mv -- "$1" "Plaintext/$1" printf "Moved %s to Plaintext\n" "$1" ;; *tar*|*xz*|*compress*|*7z*|*rar*|*zip*) [ ! -d "Archives" ] && mkdir "Archives" mv -- "$1" "Archives/$1" printf "Moved %s to Archives\n" "$1" ;; *binary*) [ ! -d "Binaries" ] && mkdir "Binaries" mv -- "$1" "Binaries/$1" printf "Moved %s to Binaries\n" "$1" ;; esac } main() { for file in * do [ -f "$file" ] && organize "$file" done # Clear selection if [ -s "$sel" ] && [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi } main "$@" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/pdfread�����������������������������������������������������������������������������0000775�0000000�0000000�00000001341�14663100143�0015160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Read a text or PDF file in British English # # Shell: POSIX compliant # Author: Arun Prakash Jana if [ -n "$1" ]; then tmpf="$(basename "$1")" tmpf="${TMPDIR:-/tmp}"/"${tmpf%.*}" if [ "$(head -c 4 "$1")" = "%PDF" ]; then # Convert using pdftotext pdftotext -nopgbrk -layout "$1" - | sed 's/\xe2\x80\x8b//g' > "$tmpf".txt pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$tmpf".txt)" rm -- "$tmpf".txt else pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$1")" fi # to jump around and note the time mpv "$tmpf".wav # flat read but better quality # play -qV0 "$tmpf".wav treble 2 gain -l 2 rm -- "$tmpf".wav fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/preview-tabbed����������������������������������������������������������������������0000775�0000000�0000000�00000015345�14663100143�0016464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash # Description: tabbed/xembed based file previewer # # Dependencies: # - tabbed (https://tools.suckless.org/tabbed): xembed host # - xterm (or urxvt or st or alacritty) : xembed client for text-based preview # - mpv (https://mpv.io): xembed client for video/audio # - sxiv (https://github.com/muennich/sxiv) or, # - nsxiv (https://codeberg.org/nsxiv/nsxiv) : xembed client for images # - zathura (https://pwmt.org/projects/zathura): xembed client for PDF # - nnn's nuke plugin for text preview and fallback # nuke is a fallback for 'mpv', 'sxiv'/'nsxiv', and 'zathura', but has its # own dependencies, see the script for more information # - vim (or any editor/pager really) # - file # - mktemp # - xdotool (optional, to keep main window focused) # # Usage: # - Install the dependencies. Then set a NNN_FIFO # and set a key for the plugin, then start `nnn`: # $ NNN_FIFO=/tmp/nnn.fifo nnn # - Launch the plugin with the designated key from nnn # # Notes: # 1. This plugin needs a "NNN_FIFO" to work. See man. # 2. If the same NNN_FIFO is used in multiple nnn instances, there will be one # common preview window. With different FIFO paths, they will be independent. # 3. This plugin only works on X, not on Wayland. # # How it works: # We use `tabbed` [1] as a xembed [2] host, to have a single window # owning each previewer window. So each previewer must be a xembed client. # For text previewers, this is not an issue, as there are a lot of # xembed-able terminal emulator (we default to `xterm`, but examples are # provided for `urxvt` and `st`). For graphic preview this can be trickier, # but a few popular viewers are xembed-able, we use: # - `mpv`: multimedia player, for video/audio preview # - `sxiv`/`nsxiv`: image viewer # - `zathura`: PDF viewer # - but we always fallback to `nuke` plugin # # [1]: https://tools.suckless.org/tabbed/ # [2]: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html # # Shell: Bash (job control is weakly specified in POSIX) # Author: Léo Villeveygoux XDOTOOL_TIMEOUT=2 PAGER=${PAGER:-"vim -R"} NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke" if [ -n "$WAYLAND_DISPLAY" ] ; then echo "Wayland is not supported in preview-tabbed, this plugin could freeze your session!" >&2 exit 1 fi if type xterm >/dev/null 2>&1 ; then TERMINAL="xterm -into" elif type urxvt >/dev/null 2>&1 ; then TERMINAL="urxvt -embed" elif type st >/dev/null 2>&1 ; then TERMINAL="st -w" elif type alacritty >/dev/null 2>&1 ; then TERMINAL="alacritty --embed" else echo "No xembed term found" >&2 fi if type xdg-user-dir >/dev/null 2>&1 ; then PICTURES_DIR=$(xdg-user-dir PICTURES) fi term_nuke () { # $1 -> $XID, $2 -> $FILE $TERMINAL "$1" -e "$NUKE" "$2" & } start_tabbed () { FIFO="$(mktemp -u)" mkfifo "$FIFO" tabbed > "$FIFO" & jobs # Get rid of the "Completed" entries TABBEDPID="$(jobs -p %%)" if [ -z "$TABBEDPID" ] ; then echo "Can't start tabbed" exit 1 fi read -r XID < "$FIFO" rm -- "$FIFO" } get_viewer_pid () { VIEWERPID="$(jobs -p %%)" } kill_viewer () { if [ -n "$VIEWERPID" ] && jobs -p | grep "$VIEWERPID" ; then kill "$VIEWERPID" fi } sigint_kill () { kill_viewer kill "$TABBEDPID" exit 0 } previewer_loop () { unset -v NNN_FIFO # mute from now exec >/dev/null 2>&1 MAINWINDOW="$(xdotool getactivewindow)" start_tabbed trap sigint_kill SIGINT xdotool windowactivate "$MAINWINDOW" # Bruteforce focus stealing prevention method, # works well in floating window managers like XFCE # but make interaction with the preview window harder # (uncomment to use): #xdotool behave "$XID" focus windowactivate "$MAINWINDOW" & while read -r FILE ; do jobs # Get rid of the "Completed" entries if ! jobs | grep tabbed ; then break fi if [ ! -e "$FILE" ] ; then continue fi kill_viewer MIME="$(file -bL --mime-type "$FILE")" case "$MIME" in video/*) if type mpv >/dev/null 2>&1 ; then mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" & else term_nuke "$XID" "$FILE" fi ;; audio/*) if type mpv >/dev/null 2>&1 ; then mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" & else term_nuke "$XID" "$FILE" fi ;; image/*) if type sxiv >/dev/null 2>&1 ; then sxiv -ae "$XID" "$FILE" & elif type nsxiv >/dev/null 2>&1 ; then nsxiv -ae "$XID" "$FILE" & else term_nuke "$XID" "$FILE" fi ;; application/pdf) if type zathura >/dev/null 2>&1 ; then zathura -e "$XID" "$FILE" & else term_nuke "$XID" "$FILE" fi ;; inode/directory) if [[ -n $PICTURES_DIR && "$FILE" == "$PICTURES_DIR"* ]] ; then if type sxiv >/dev/null 2>&1 ; then sxiv -te "$XID" "$FILE" & elif type nsxiv >/dev/null 2>&1 ; then nsxiv -te "$XID" "$FILE" & else $TERMINAL "$XID" -e nnn "$FILE" & fi else $TERMINAL "$XID" -e nnn "$FILE" & fi ;; text/*) if [ -x "$NUKE" ] ; then term_nuke "$XID" "$FILE" else # shellcheck disable=SC2086 $TERMINAL "$XID" -e $PAGER "$FILE" & fi ;; *) if [ -x "$NUKE" ] ; then term_nuke "$XID" "$FILE" else $TERMINAL "$XID" -e sh -c "file '$FILE' | $PAGER -" & fi ;; esac get_viewer_pid # following lines are not needed with the bruteforce xdotool method ACTIVE_XID="$(xdotool getactivewindow)" if [ $((ACTIVE_XID == XID)) -ne 0 ] ; then xdotool windowactivate "$MAINWINDOW" else timeout "$XDOTOOL_TIMEOUT" xdotool behave "$XID" focus windowactivate "$MAINWINDOW" & fi done kill "$TABBEDPID" kill_viewer } if [ ! -r "$NNN_FIFO" ] ; then echo "Can't read \$NNN_FIFO ('$NNN_FIFO')" exit 1 fi previewer_loop < "$NNN_FIFO" & disown �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/preview-tui�������������������������������������������������������������������������0000775�0000000�0000000�00000054102�14663100143�0016036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash # Description: Terminal based file previewer # # Note: This plugin needs a "NNN_FIFO" to work. See man. # # Dependencies: # - Supports 6 independent methods to preview with: # - tmux (>=3.0), or # - kitty with allow_remote_control and listen_on set in kitty.conf, or # - wezterm (https://wezfurlong.org/wezterm), or # - QuickLook on WSL (https://github.com/QL-Win/QuickLook), or # - Windows Terminal (https://github.com/Microsoft/Terminal | https://aka.ms/terminal) with WSL, or # - $NNN_TERMINAL set to a terminal (it's xterm by default). # - less or $NNN_PAGER # - tree or exa/eza or (GNU) ls # - mediainfo or file # - mktemp # - unzip # - tar # - man # - optional: bsdtar or atool for additional archive preview # - optional: bat for code syntax highlighting # - optional: ueberzug, kitty terminal, wezterm terminal, img2sixel, viu, catimg or chafa for images # - optional: convert(ImageMagick) for playing gif preview (mandatory for kitty image previews) # - optional: mpv for gif and video # Also requires a terminal supporting the sixel (https://www.arewesixelyet.com/) # or kitty (https://sw.kovidgoyal.net/kitty/graphics-protocol) video_output backends. # Requires tmux compiled with `./configure --enable-sixel` if used. # - optional: ffmpegthumbnailer for video thumbnails (https://github.com/dirkvdb/ffmpegthumbnailer) # - optional: ffmpeg for audio thumbnails # - optional: libreoffce for opendocument/officedocument preview # - optional: pdftoppm(poppler) for pdf thumbnails # - optional: gnome-epub-thumbnailer for epub thumbnails (https://gitlab.gnome.org/GNOME/gnome-epub-thumbnailer) # - optional: fontpreview for font preview (https://github.com/sdushantha/fontpreview) # - optional: djvulibre for djvu # - optional: glow or lowdown for markdown # - optional: w3m or lynx or elinks for html # - optional: set/export NNN_ICONLOOKUP as 1 to enable file icons in front of directory previews with .iconlookup # Icons and colors are configurable in .iconlookup # - optional: scope.sh file viewer from ranger. # 1. drop scope.sh executable in $PATH # 2. set/export $NNN_SCOPE as 1 # - optional: pistol file viewer (https://github.com/doronbehar/pistol). # 1. install pistol # 2. set/export $NNN_PISTOL as 1 # - optional: librsvg for rsvg-convert # # Usage: # You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, # then start `nnn`: # # $ nnn -a # # or # # $ NNN_FIFO=/tmp/nnn.fifo nnn # # Then launch the `preview-tui` plugin in `nnn`. # # If you provide the same NNN_FIFO to all nnn instances, there will be a # single common preview window. If you provide different FIFO path (e.g. # with -a), they will be independent. # # The previews will be shown in a tmux split. If that isn't possible, it # will try to use a kitty terminal split. And as a final fallback, a # different terminal window will be used ($NNN_TERMINAL). # # Kitty users need something similar to the following in their kitty.conf: # - `allow_remote_control yes` # - `listen_on unix:$TMPDIR/kitty` # - `enabled_layouts splits` (optional) # With ImageMagick installed, this terminal can use the icat kitten to display images. # Refer to kitty documentation for further details. # # Users with both tmux and kitty can leverage image previews inside tmux with kitty's icat kitten # - setup kitty as stated above # - tmux >= v3.3a required # - add the following to your tmux.conf: # - `set -g allow-passthrough on` # # Wezterm should work out of the box. If `NNN_PREVIEWIMGPROG` is not specified it will use # built in iTerm2 image protocol. # # Note that GNU ls is used for its `--group-directories-first` flag. # On MacOS this may be installed with `brew install coreutils`, or the flag can be removed. # iTerm2 users are recommended to use viu to view images without getting pixelated. # # Windows Terminal users can set "Profile termination behavior" under "Profile > Advanced" settings # to automatically close pane on quit when exit code is 0. # # When specifying a different terminal, additional arguments are supported. In particular, you can # append a specific title to the terminal and set it to "nofocus" in your WM config. # E.g for alacritty and i3, you can set $NNN_TERMINAL to 'alacritty --title preview-tui' and add # 'no_focus [title="preview-tui"]' to your i3 config file. # # Shell: Bash (for environment manipulation through arrays) # Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz, @flipflop133 # Configurable environment variables: NNN_SPLIT=${NNN_SPLIT:-} # permanent split direction NNN_TERMINAL=${NNN_TERMINAL:-} # external terminal to be used NNN_SPLITSIZE=${NNN_SPLITSIZE:-50} # previewer split size percentage TMPDIR=${TMPDIR:-/tmp} # location of temporary files ENVVARS=( "NNN_SCOPE=${NNN_SCOPE:-0}" # use scope "NNN_PISTOL=${NNN_PISTOL:-0}" # use pistol "NNN_ICONLOOKUP=${NNN_ICONLOOKUP:-0}" # use .iconlookup "NNN_PAGER=${NNN_PAGER:-less -P?n -R -C}" # pager options "NNN_BATTHEME=${NNN_BATTHEME:-ansi}" # bat theme "NNN_BATSTYLE=${NNN_BATSTYLE:-numbers}" # bat style "NNN_PREVIEWWIDTH=${NNN_PREVIEWWIDTH:-1920}" # width of generated preview images "NNN_PREVIEWHEIGHT=${NNN_PREVIEWHEIGHT:-1080}" # height of generated preview images "NNN_PREVIEWDIR=${NNN_PREVIEWDIR:-$TMPDIR/nnn/previews}" # location of generated preview images "NNN_PREVIEWIMGPROG=${NNN_PREVIEWIMGPROG:-}" # program used to preview images "NNN_PREVIEWVIDEO=${NNN_PREVIEWVIDEO:-}" # mpv backend used to preview video ) # Non-configurable environment variables NNN_PARENT=${NNN_FIFO#*.} [ "$NNN_PARENT" -eq "$NNN_PARENT" ] 2>/dev/null || NNN_PARENT="" # Make empty if non-numeric ENVVARS+=( "PWD=$PWD" "PATH=$PATH" "NNN_FIFO=$NNN_FIFO" "FIFOPID=$TMPDIR/nnn-preview-tui-fifopid.$NNN_PARENT" "FIFOPATH=$TMPDIR/nnn-preview-tui-fifo.$NNN_PARENT" "PREVIEWPID=$TMPDIR/nnn-preview-tui-previewpid.$NNN_PARENT" "CURSEL=$TMPDIR/nnn-preview-tui-selection.$NNN_PARENT" "FIFO_UEBERZUG=$TMPDIR/nnn-preview-tui-ueberzug-fifo.$NNN_PARENT" "POSOFFSET=$TMPDIR/nnn-preview-tui-posoffset" ) trap '' PIPE exists() { type "$1" >/dev/null 2>&1 ;} pkill() { command pkill "$@" >/dev/null 2>&1 ;} prompt() { clear; printf "%b" "$@"; cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" ;} pidkill() { if [ -f "$1" ]; then PID="$(cat "$1" 2>/dev/null)" || return 1 kill "$PID" >/dev/null 2>&1 RET=$? wait "$PID" 2>/dev/null return $RET fi return 1 } start_preview() { if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then NNN_TERMINAL=tmux exists mpv && tmux display -p '#{client_termfeatures}' | grep -q 'sixel' && ENVVARS+=("NNN_PREVIEWVIDEO=sixel") elif [ -n "$KITTY_LISTEN_ON" ]; then NNN_TERMINAL=kitty exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=kitty") elif [ -n "$WEZTERM_PANE" ]; then NNN_TERMINAL=wezterm exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=kitty") elif [ -z "$NNN_TERMINAL" ] && [ "$TERM_PROGRAM" = "iTerm.app" ]; then NNN_TERMINAL=iterm exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=sixel") elif [ -n "$WT_SESSION" ]; then NNN_TERMINAL=winterm else NNN_TERMINAL="${NNN_TERMINAL:-xterm}" fi if [ -z "$NNN_SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then NNN_SPLIT='h' elif [ "$NNN_SPLIT" != 'h' ]; then NNN_SPLIT='v' fi ENVVARS+=("NNN_TERMINAL=$NNN_TERMINAL" "NNN_SPLIT=$NNN_SPLIT" "QLPATH=$2" "PREVIEW_MODE=1") case "$NNN_TERMINAL" in iterm|winterm) # has to run in separate shell command: escape ENVVARS=("${ENVVARS[@]/#/\\\"}") ENVVARS=("${ENVVARS[@]/%/\\\"}") command="$SHELL -c 'env ${ENVVARS[*]} \\\"$0\\\" \\\"$1\\\"'" ;; esac case "$NNN_TERMINAL" in tmux) # tmux splits are inverted ENVVARS=("${ENVVARS[@]/#/-e}") if [ "$NNN_SPLIT" = "v" ]; then split="h"; else split="v"; fi tmux split-window -l"$NNN_SPLITSIZE"% "${ENVVARS[@]}" -d"$split" -p"$NNN_SPLITSIZE" "$0" "$1" ;; kitty) # Setting the layout for the new window. It will be restored after the script ends. ENVVARS=("${ENVVARS[@]/#/--env=}") kitty @ goto-layout splits # Trying to use kitty's integrated window management as the split window. kitty @ launch --no-response --title "preview-tui" --keep-focus \ --cwd "$PWD" "${ENVVARS[@]}" --location "${NNN_SPLIT}split" "$0" "$1" ;; wezterm) export "${ENVVARS[@]}" if [ "$NNN_SPLIT" = "v" ]; then split="--horizontal"; else split="--bottom"; fi wezterm cli split-pane --cwd "$PWD" $split --percent "$NNN_SPLITSIZE" "$0" "$1" >/dev/null wezterm cli activate-pane-direction Prev ;; iterm) if [ "$NNN_SPLIT" = "h" ]; then split="horizontally"; else split="vertically"; fi osascript <<-EOF tell application "iTerm" tell current session of current window split $split with default profile command "$command" end tell end tell EOF ;; winterm) if [ "$NNN_SPLIT" = "h" ]; then split="H"; else split="V"; fi wt -w 0 sp -$split -s"0.$NNN_SPLITSIZE" "$command" \; -w 0 mf previous 2>/dev/null ;; *) if [ -n "$2" ]; then env "${ENVVARS[@]}" QUICKLOOK=1 "$0" "$1" & else # shellcheck disable=SC2086 # (allow arguments) env "${ENVVARS[@]}" $NNN_TERMINAL -e "$0" "$1" & fi ;; esac } toggle_preview() { export "${ENVVARS[@]}" if exists QuickLook.exe; then QLPATH="QuickLook.exe" elif exists Bridge.exe; then QLPATH="Bridge.exe" fi if pidkill "$FIFOPID"; then [ -p "$NNN_PPIPE" ] && printf "0" > "$NNN_PPIPE" pidkill "$PREVIEWPID" pkill -f "tail --follow $FIFO_UEBERZUG" if [ -n "$QLPATH" ] && stat "$1"; then f="$(wslpath -w "$1")" && "$QLPATH" "$f" & fi else [ -p "$NNN_PPIPE" ] && printf "1" > "$NNN_PPIPE" start_preview "$1" "$QLPATH" fi } fifo_pager() { cmd="$1" shift # We use a FIFO to access $NNN_PAGER PID in jobs control mkfifo "$FIFOPATH" || return $NNN_PAGER < "$FIFOPATH" & printf "%s" "$!" > "$PREVIEWPID" ( exec > "$FIFOPATH" if [ "$cmd" = "pager" ]; then if exists bat; then bat --terminal-width="$cols" --decorations=always --color=always \ --paging=never --style="$NNN_BATSTYLE" --theme="$NNN_BATTHEME" "$@" & else $NNN_PAGER "$@" & fi else "$cmd" "$@" & fi ) rm -- "$FIFOPATH" } # Binary file: show file info inside the pager print_bin_info() { printf -- "-------- \033[1;31mBinary file\033[0m --------\n" if exists mediainfo; then mediainfo "$1" else file -b "$1" fi } handle_mime() { case "$2" in image/jpeg) image_preview "$cols" "$lines" "$1" ;; image/gif) generate_preview "$cols" "$lines" "$1" "gif" ;; image/vnd.djvu) generate_preview "$cols" "$lines" "$1" "djvu" ;; image/*) generate_preview "$cols" "$lines" "$1" "image" ;; video/*) generate_preview "$cols" "$lines" "$1" "video" ;; audio/*) generate_preview "$cols" "$lines" "$1" "audio" ;; application/font*|application/*opentype|font/*) generate_preview "$cols" "$lines" "$1" "font" ;; */*office*|*/*document*|*/*msword|*/*ms-excel) generate_preview "$cols" "$lines" "$1" "office" ;; application/zip) fifo_pager unzip -l "$1" ;; text/troff) if exists man; then fifo_pager man -Pcat -l "$1" else fifo_pager pager "$1" fi ;; *) handle_ext "$1" "$3" "$4" ;; esac } handle_ext() { case "$2" in epub) generate_preview "$cols" "$lines" "$1" "epub" ;; pdf) generate_preview "$cols" "$lines" "$1" "pdf" ;; gz|bz2) fifo_pager tar -tvf "$1" ;; md) if exists glow; then fifo_pager glow -s dark "$1" elif exists lowdown; then fifo_pager lowdown -Tterm "$1" else fifo_pager pager "$1" fi ;; htm|html|xhtml) if exists w3m; then fifo_pager w3m "$1" elif exists lynx; then fifo_pager lynx "$1" elif exists elinks; then fifo_pager elinks "$1" else fifo_pager pager "$1" fi ;; 7z|a|ace|alz|arc|arj|bz|cab|cpio|deb|jar|lha|lz|lzh|lzma|lzo\ |rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z) if exists atool; then fifo_pager atool -l "$1" elif exists bsdtar; then fifo_pager bsdtar -tvf "$1" fi ;; *) if [ "$3" = "bin" ]; then fifo_pager print_bin_info "$1" else fifo_pager pager "$1" fi ;; esac } preview_file() { clear # Trying to use pistol if it's available. if [ "$NNN_PISTOL" -ne 0 ] && exists pistol; then fifo_pager pistol "$1" return fi # Trying to use scope.sh if it's available. if [ "$NNN_SCOPE" -ne 0 ] && exists scope.sh; then fifo_pager scope.sh "$1" "$cols" "$lines" "$(mktemp -d)" "True" return fi # Use QuickLook if it's available. if [ -n "$QUICKLOOK" ]; then stat "$1" && f="$(wslpath -w "$1")" && "$QLPATH" "$f" & return fi # Detecting the exact type of the file: the encoding, mime type, and extension in lowercase. encoding="$(file -bL --mime-encoding -- "$1")" mimetype="$(file -bL --mime-type -- "$1")" ext="${1##*.}" [ -n "$ext" ] && ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" lines=$(tput lines) cols=$(tput cols) # Otherwise, falling back to the defaults. if [ -d "$1" ]; then cd "$1" || return if [ "$NNN_ICONLOOKUP" -ne 0 ] && [ -f "$(dirname "$0")"/.iconlookup ]; then [ "$NNN_SPLIT" = v ] && BSTR="\n" # shellcheck disable=SC2012 ls -F --group-directories-first | head -n "$((lines - 3))" | "$(dirname "$0")"/.iconlookup -l "$cols" -B "$BSTR" -b " " elif exists tree; then fifo_pager tree --filelimit "$(find . -maxdepth 1 | wc -l)" -L 3 -C -F --dirsfirst --noreport elif exists exa; then fifo_pager exa -T --group-directories-first --colour=always -L 3 elif exists eza; then # eza is a community fork of exa (exa is unmaintained) fifo_pager eza -T --group-directories-first --colour=always -L 3 else fifo_pager ls -F --group-directories-first --color=always fi cd .. elif [ "${encoding#*)}" = "binary" ]; then handle_mime "$1" "$mimetype" "$ext" "bin" else handle_mime "$1" "$mimetype" "$ext" fi } generate_preview() { if [ -n "$QLPATH" ] && stat "$3"; then f="$(wslpath -w "$3")" && "$QLPATH" "$f" & elif [ -n "$NNN_PREVIEWVIDEO" ] && [[ "$4" == +(gif|video) ]]; then [ "$4" = "video" ] && args=(--start=10% --length=4) || args=() video_preview "$1" "$2" "$3" "${args[@]}" && return elif [ ! -f "$NNN_PREVIEWDIR/$3.jpg" ] || [ -n "$(find -L "$3" -newer "$NNN_PREVIEWDIR/$3.jpg")" ]; then mkdir -p "$NNN_PREVIEWDIR/${3%/*}" case $4 in audio) ffmpeg -i "$3" -filter_complex "scale=iw*min(1\,min($NNN_PREVIEWWIDTH/iw\,ih)):-1" "$NNN_PREVIEWDIR/$3.jpg" -y ;; epub) gnome-epub-thumbnailer "$3" "$NNN_PREVIEWDIR/$3.jpg" ;; font) fontpreview -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" ;; gif) if [ -p "$FIFO_UEBERZUG" ] && exists convert; then frameprefix="$NNN_PREVIEWDIR/$3/${3##*/}" if [ ! -d "$NNN_PREVIEWDIR/$3" ]; then mkdir -p "$NNN_PREVIEWDIR/$3" convert -coalesce -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$3" "$frameprefix.jpg" || MAGICK_TMPDIR="/tmp" convert -coalesce -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$3" "$frameprefix.jpg" fi frames=$(($(find "$NNN_PREVIEWDIR/$3" | wc -l) - 2)) [ $frames -lt 0 ] && return while true; do for i in $(seq 0 $frames); do image_preview "$1" "$2" "$frameprefix-$i.jpg" sleep 0.1 done done & printf "%s" "$!" > "$PREVIEWPID" return elif [ -n "$NNN_PREVIEWVIDEO" ]; then video_preview "$1" "$2" "$3" && return else image_preview "$1" "$2" "$3" && return fi ;; image) if exists rsvg-convert && [[ "${3##*.}" == "svg" ]]; then rsvg-convert -a -w "$NNN_PREVIEWWIDTH" -h "$NNN_PREVIEWHEIGHT" -f png -o "$NNN_PREVIEWDIR/$3.png" "$3" elif exists convert; then convert "$3" -flatten -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$NNN_PREVIEWDIR/$3.jpg" else image_preview "$1" "$2" "$3" && return fi ;; office) libreoffice --convert-to jpg "$3" --outdir "$NNN_PREVIEWDIR/${3%/*}" filename="$(printf "%s" "${3##*/}" | cut -d. -f1)" mv -- "$NNN_PREVIEWDIR/${3%/*}/$filename.jpg" "$NNN_PREVIEWDIR/$3.jpg" ;; pdf) pdftoppm -jpeg -f 1 -singlefile "$3" "$NNN_PREVIEWDIR/$3" ;; djvu) ddjvu -format=ppm -page=1 "$3" "$NNN_PREVIEWDIR/$3.jpg" ;; video) video_preview "$1" "$2" "$3" && return ;; esac fi if [ -f "$NNN_PREVIEWDIR/$3.jpg" ]; then image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.jpg" elif [[ "${3##*.}" == "svg" ]] && [ -f "$NNN_PREVIEWDIR/$3.png" ]; then image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.png" else fifo_pager print_bin_info "$3" fi } >/dev/null 2>&1 image_preview() { clear exec >/dev/tty if [ "$NNN_TERMINAL" = "kitty" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|icat) ]]; then kitty +kitten icat --silent --scale-up --place "$1"x"$2"@0x0 --transfer-mode=stream --stdin=no "$3" & elif [ "$NNN_TERMINAL" = "tmux" ] && [[ -n "$KITTY_LISTEN_ON" ]] && [[ "$NNN_PREVIEWIMGPROG" == +(|icat) ]]; then kitty +kitten icat --silent --scale-up --place "$(($1 - 1))x$(($2 - 1))"@0x0 --transfer-mode=memory --stdin=no "$3" & elif [ "$NNN_TERMINAL" = "wezterm" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|imgcat) ]]; then wezterm imgcat "$3" & elif exists ueberzug && [[ "$NNN_PREVIEWIMGPROG" == +(|ueberzug) ]]; then ueberzug_layer "$1" "$2" "$3" && return elif exists catimg && [[ "$NNN_PREVIEWIMGPROG" == +(|catimg) ]]; then catimg "$3" & elif exists viu && [[ "$NNN_PREVIEWIMGPROG" == +(|viu) ]]; then viu -t "$3" & elif exists chafa && [[ "$NNN_PREVIEWIMGPROG" == +(|chafa) ]]; then chafa "$3" & elif exists img2sixel && [[ "$NNN_PREVIEWIMGPROG" == +(|img2sixel) ]]; then img2sixel -g "$3" & else fifo_pager print_bin_info "$3" && return fi printf "%s" "$!" > "$PREVIEWPID" } video_preview() { clear exec >/dev/tty if [ -n "$NNN_PREVIEWVIDEO" ]; then mpv --no-config --really-quiet --vo="$NNN_PREVIEWVIDEO" --profile=sw-fast --loop-file --no-audio "$4" "$3" & else ffmpegthumbnailer -m -s0 -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" || rm -- "$NNN_PREVIEWDIR/$3.jpg" & fi printf "%s" "$!" > "$PREVIEWPID" } ueberzug_layer() { [ -f "$POSOFFSET" ] && read -r x y < "$POSOFFSET" printf '{"action": "add", "identifier": "nnn_ueberzug", "x": %d, "y": %d, "width": "%d", "height": "%d", "scaler": "fit_contain", "path": "%s"}\n'\ "${x:-0}" "${y:-0}" "$1" "$2" "$3" > "$FIFO_UEBERZUG" } ueberzug_remove() { printf '{"action": "remove", "identifier": "nnn_ueberzug"}\n' > "$FIFO_UEBERZUG" } winch_handler() { clear pidkill "$PREVIEWPID" if [ -p "$FIFO_UEBERZUG" ]; then pkill -f "tail --follow $FIFO_UEBERZUG" tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & fi preview_file "$(cat "$CURSEL")" } preview_fifo() { while read -r selection; do if [ -n "$selection" ]; then pidkill "$PREVIEWPID" [ -p "$FIFO_UEBERZUG" ] && ueberzug_remove [ "$selection" = "close" ] && break preview_file "$selection" printf "%s" "$selection" > "$CURSEL" fi done < "$NNN_FIFO" sleep 0.1 # make sure potential preview by winch_handler is killed pkill -P "$$" } if [ "$PREVIEW_MODE" -eq 1 ] 2>/dev/null; then if exists ueberzug && [ "$NNN_TERMINAL" != "kitty" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|ueberzug) ]]; then mkfifo "$FIFO_UEBERZUG" tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & fi preview_file "$PWD/$1" preview_fifo & WAITPID=$! printf "%s" "$!" > "$FIFOPID" printf "%s" "$PWD/$1" > "$CURSEL" trap 'winch_handler' WINCH trap 'rm -- "$PREVIEWPID" "$CURSEL" "$FIFO_UEBERZUG" "$FIFOPID" "$POSOFFSET" 2>/dev/null' INT HUP EXIT while kill -s 0 $WAITPID; do wait $WAITPID 2>/dev/null done exit 0 else if [ ! -r "$NNN_FIFO" ]; then prompt "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')\nPlease read Usage in '$0'." elif [ "$KITTY_WINDOW_ID" ] && [ -z "$TMUX" ] && [ -z "$KITTY_LISTEN_ON" ]; then prompt "\$KITTY_LISTEN_ON not set!\nPlease read Usage in '$0'." else toggle_preview "$1" & fi fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/pskill������������������������������������������������������������������������������0000775�0000000�0000000�00000001345�14663100143�0015055�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Fuzzy list and kill a (zombie) process by name # # Dependencies: fzf, ps # # Note: To kill a zombie process enter "zombie" # # Shell: POSIX compliant # Author: Arun Prakash Jana printf "Enter process name ['defunct' for zombies]: " read -r psname # shellcheck disable=SC2009 if [ -n "$psname" ]; then if type sudo >/dev/null 2>&1; then sucmd=sudo elif type doas >/dev/null 2>&1; then sucmd=doas else sucmd=: # noop fi if type fzf >/dev/null 2>&1; then fuzzy=fzf else exit 1 fi cmd="$(ps -ax | grep -iw "$psname" | "$fuzzy" | sed -e 's/^[ \t]*//' | cut -d' ' -f1)" if [ -n "$cmd" ]; then $sucmd kill -9 "$cmd" fi fi �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/renamer�����������������������������������������������������������������������������0000775�0000000�0000000�00000002202�14663100143�0015201�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Batch rename selection or current directory with qmv or vidir # # Notes: # - Try to mimic current batch rename functionality but with correct # handling of edge cases by qmv or vidir. # - Qmv opens with hidden files if no selection is used. Selected # directories are shown. # - Vidir don't show directories nor hidden files. # # Shell: POSIX compliant # Author: José Neder selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} if type qmv >/dev/null 2>&1; then batchrenamesel="qmv -fdo -da" batchrename="qmv -fdo -a" elif type vidir >/dev/null 2>&1; then batchrenamesel="vidir" batchrename="vidir" else printf "there is not batchrename program installed." exit fi if [ -s "$selection" ]; then printf "rename selection? " read -r resp fi if [ "$resp" = "y" ]; then # -o flag is necessary for interactive editors xargs -o -0 $batchrenamesel < "$selection" # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi elif [ ! "$(LC_ALL=C ls -a)" = ". .." ]; then # On older systems that don't have ls -A $batchrename fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/ringtone����������������������������������������������������������������������������0000775�0000000�0000000�00000001662�14663100143�0015406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Create an mp3 ringtone out of an audio file in any format # Needs user to provide start and end where to cut the file # Input file audio.ext results in audio_ringtone.mp3 # # Tip: To convert a complete media file, set start as 0 and # the runtime of the file as end. # # Dependencies: date, ffmpeg # # Shell: POSIX compliant # Author: Arun Prakash Jana if [ -n "$1" ]; then printf "start (hh:mm:ss): " read -r start st=$(date -d "$start" +%s) || exit 1 printf "end (hh:mm:ss): " read -r end et=$(date -d "$end" +%s) || exit 1 if [ "$st" -ge "$et" ]; then printf "error: start >= end " read -r _ exit 1 fi interval=$(( et - st )) outfile=$(basename "$1") outfile="${outfile%.*}"_ringtone.mp3 ffmpeg -i "$1" -ss "$start" -t "$interval" -vn -sn -acodec libmp3lame -q:a 2 "$outfile" fi ������������������������������������������������������������������������������nnn-5.0/plugins/rsynccp�����������������������������������������������������������������������������0000775�0000000�0000000�00000001142�14663100143�0015233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Simple script to give copy-paste a progress percentage # by utilizing rsync. # # LIMITATION: this won't work when pasting to MTP device. # # Dependencies: rsync # # Shell: POSIX compliant # Author: Benawi Adha sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} # Choose one of these two schemes by commenting # more verbose xargs -0 -I % rsync -ah --progress % "$PWD" < "$sel" # less verbose # xargs -0 -I % rsync -ah --info=progress2 % "$PWD" < "$sel" # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/splitjoin���������������������������������������������������������������������������0000775�0000000�0000000�00000002613�14663100143�0015571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Splits the file passed as argument or joins selection # # Note: Adds numeric suffix to split files # Adds '.out suffix to the first file to be joined and saves as output file for join # # Shell: POSIX compliant # Authors: Arun Prakash Jana, ath3 selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} resp=s if [ -s "$selection" ]; then printf "press 's' (split current file) or 'j' (join selection): " read -r resp fi if [ "$resp" = "j" ]; then if [ -s "$selection" ]; then arr=$(tr '\0' '\n' < "$selection") if [ "$(echo "$arr" | wc -l)" -lt 2 ]; then echo "joining needs at least 2 files" exit fi for entry in $arr do if [ -d "$entry" ]; then echo "can't join directories" exit fi done file="$(basename "$(echo "$arr" | sed -n '1p' | sed -e 's/[0-9][0-9]$//')")" sort -z < "$selection" | xargs -0 -I{} cat {} > "${file}.out" # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi fi elif [ "$resp" = "s" ]; then if [ -n "$1" ] && [ -f "$1" ]; then # a single file is passed printf "split size in MB: " read -r size if [ -n "$size" ]; then split -d -b "$size"M "$1" "$1" fi fi fi ���������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/suedit������������������������������������������������������������������������������0000775�0000000�0000000�00000000476�14663100143�0015060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Edit file as superuser # # Shell: POSIX compliant # Author: Anna Arad EDITOR="${EDITOR:-vim}" if type sudo >/dev/null 2>&1; then sudo -E "$EDITOR" "$1" elif type sudoedit >/dev/null 2>&1; then sudoedit -E "$1" elif type doas >/dev/null 2>&1; then doas "$EDITOR" "$1" fi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/togglex�����������������������������������������������������������������������������0000775�0000000�0000000�00000001034�14663100143�0015223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Toggles executable mode for selection # # Dependencies: chmod # # Note: Works _only_ with selection (nnn can toggle the mode for the hovered file) # # Shell: POSIX compliant # Author: Arun Prakash Jana selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} if [ -s "$selection" ]; then xargs -0 -I {} sh -c 'if [ -x "{}" ] ; then chmod -x "{}" ; else chmod +x "{}" ; fi' < "$selection" # Clear selection if [ -p "$NNN_PIPE" ]; then printf "-" > "$NNN_PIPE" fi fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/umounttree��������������������������������������������������������������������������0000775�0000000�0000000�00000003500�14663100143�0015761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Autodetects a nnn remote mountpoint (mounted with `c`) # from any of its subfolders and allows unmounting it # from the subdir without navigating to the mountppoint # or entering the remote name. Also works when hovering # the mountpoint directly like vanilla `u`. # # Dependencies: fusermount # # Shell: POSIX compliant # Authors: Kabouik & 0xACE # # TODO: # - Avoid lazy unmount by forcing nnn context to leave the subfolder before fusermount. # Tried `printf "%s" "0c$m" > "$NNN_PIPE"` but it breaks the nnn interface, see #854. err=0 m=$HOME/.config/nnn/mounts if [ "$PWD" = "$m" ]; then # Allow running the script on hovered directory if user is in ~/.config/nnn/mounts d="$1" else d=$(dirname "$(readlink -f "$1")" | grep -oP "^$m\K.*" | cut -d"/" -f2) fi # Test if user is within $m or a subdir, abort if not if [ "$d" = "" ]; then clear && printf "You are not in a remote folder mounted with nnn. Press return to continue. " && read -r _ else # Test if $m/$d is a mountpoint and try unmounting if it is mountpoint -q -- "$m/$d" if [ "$?" -eq "1" ]; then clear && printf "Parent '%s' is not a mountpoint. Press return to continue. " "$d" && read -r _ else cd "$m" && fusermount -uq "$m/$d" || err=1 if [ "$err" -eq "0" ]; then rmdir "$m/$d" && clear && printf "Parent '%s' unmounted." "$d" else clear && printf "Failed to unmount. Try lazy unmount? [Yy/Nn] " && read -r fi fi fi # If unmount fails, offer lazy unmount if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then err=0 cd "$m" && fusermount -uqz "$m/$d" || err=1 if [ "$err" -eq "0" ]; then rmdir "$m/$d" && clear && printf "Parent '%s' unmounted with lazy unmount. " "$d" fi fi ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/upload������������������������������������������������������������������������������0000775�0000000�0000000�00000002420�14663100143�0015036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Selections are archived into a tar file (uncompressed) and uploaded to file.io # For single files: # Paste contents of a text file to http://ix.io # Upload a binary file to file.io # # Dependencies: curl, jq, tar, file with `--mime-type` support # # Note: Binary file set to expire after a week # # Shell: POSIX compliant # Author: Arun Prakash Jana selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} if [ -s "$selection" ]; then xargs -0 tar -c < "$selection" | \ curl -s -F "file=@/dev/stdin;filename=selection.tar" 'https://file.io/?expires=1w' | \ jq '.link' | tr -d '"' # Clear selection printf "-" > "$NNN_PIPE" else if [ -n "$1" ] && [ -s "$1" ]; then if file --mime-type "$1" | grep -q -F "text/"; then curl -F "file=@$1" https://0x0.st else # Upload the file, show the download link and wait till user presses any key curl -s -F "file=@$1" https://file.io/?expires=1w | jq '.link' | tr -d '"' # To write download link to "$1".loc and exit # curl -s -F "file=@$1" https://file.io/?expires=1w -o `basename "$1"`.loc fi else printf "empty file!" fi fi read -r _ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/wallpaper���������������������������������������������������������������������������0000775�0000000�0000000�00000002513�14663100143�0015544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Set the selected image as wallpaper. # Uses nitrogen or pywal on X11, swww on wayland. # # Usage: Hover on an image and run the script to set it as wallpaper. # # Shell: POSIX compliant # Author: juacq97 resp= if [ -n "$1" ]; then if [ "$(file --mime-type "$1" | awk '{print $NF}' | awk -F '/' '{print $1}')" = "image" ]; then if [ "$XDG_SESSION_TYPE" = "x11" ]; then if type nitrogen >/dev/null 2>&1; then printf "Set to full desktop or a specific monitors? [0, 1, etc. Defaults to full.]" read -r resp if [ "$resp" != "" ]; then nitrogen --set-zoom-fill --save "$1" --head="$resp" else nitrogen --set-zoom-fill --save "$1" fi elif type wal >/dev/null 2>&1; then wal -i "$1" else printf "nitrogen or pywal missing" read -r _ fi else if type swww >/dev/null 2>&1; then swww img "$1" else printf "swww missing" read -r _ fi fi # If you want a system notification, uncomment the next 3 lines. # notify-send -a "nnn" "Wallpaper changed!" # else # notify-send -a "nnn" "No image selected" fi fi �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/x2sel�������������������������������������������������������������������������������0000775�0000000�0000000�00000002602�14663100143�0014611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Copy system clipboard newline-separated file list to selection # # Dependencies: # - tr # - xclip/xsel (Linux) # - pbpaste (macOS) # - termux-clipboard-get (Termux) # - powershell (WSL) # - cygwim's /dev/clipboard (Cygwin) # - wl-paste (Wayland) # - clipboard (Haiku) # # Note: # - Limitation: breaks if a filename has newline in it # # Shell: POSIX compliant # Author: Léo Villeveygoux, after Arun Prakash Jana's .cbcp IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} getclip () { if [ "$XDG_SESSION_TYPE" = "wayland" ]; then # Wayland wl-paste elif type xsel >/dev/null 2>&1; then # Linux xsel -bo elif type xclip >/dev/null 2>&1; then # Linux xclip -sel clip -o elif type pbpaste >/dev/null 2>&1; then # macOS pbpaste elif type termux-clipboard-get >/dev/null 2>&1; then # Termux termux-clipboard-get elif type powershell.exe >/dev/null 2>&1; then # WSL powershell.exe Get-Clipboard elif [ -r /dev/clipboard ] ; then # Cygwin cat /dev/clipboard elif type clipboard >/dev/null 2>&1; then # Haiku clipboard --print fi } CLIPBOARD=$(getclip) # Check if clipboard actually contains a file list for file in $CLIPBOARD ; do if [ ! -e "$file" ] ; then exit 1; fi done printf "%s" "$CLIPBOARD" | tr '\n' '\0' > "$selection" ������������������������������������������������������������������������������������������������������������������������������nnn-5.0/plugins/xdgdefault��������������������������������������������������������������������������0000775�0000000�0000000�00000002602�14663100143�0015703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env sh # Description: Sets the xdg-open's default application for the current entry's file # type. ${XDG_DATA_DIRS} and ${XDG_DATA_HOME} are set to the recommended # defaults if unset, as specified in XDG Base Directory Specification # - http://specifications.freedesktop.org/basedir-spec/. # # Dependencies: xdg-utils, fzf or dmenu (GUI) # # Shell: POSIX compliant # Author: lwnctd # set to 1 to enable GUI apps GUI="${GUI:-0}" if [ "$GUI" -ne 0 ] && command -v dmenu > /dev/null 2>& 1; then menu="dmenu -i -l 7" elif command -v fzf > /dev/null 2>& 1; then menu="fzf -e --tiebreak=begin" fi if [ -z "$1" ] || [ -z "$menu" ] > /dev/null 2>& 1; then exit 1 fi ftype=$(xdg-mime query filetype "$2/$1") if [ -z "$ftype" ]; then exit 1 fi dirs=${XDG_DATA_DIRS:-/usr/local/share:/usr/share} dirs=${dirs}:${XDG_DATA_HOME:-$HOME/.local/share}: while [ -n "$dirs" ]; do d=${dirs%%:*} if [ -n "$d" ] && [ -d "$d"/applications ]; then set -- "$@" "$d"/applications fi dirs=${dirs#*:} done app=$(find "$@" -iname '*.desktop' -exec grep '^Name=' {} + \ | sort -u -t ':' -k 1,1 \ | sed -e 's;..*/\(..*desktop\):Name=\(..*\);\2:\1;' \ | sort -t ':' -k 1,1 \ | column -t -s ':' -o "$(printf '\t')" \ | $menu \ | cut -f 2) if [ -n "$app" ]; then xdg-mime default "${app%%[[:blank:]]*}" "$ftype" fi ������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14663100143�0012734�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/.clang-tidy�����������������������������������������������������������������������������0000664�0000000�0000000�00000002750�14663100143�0014774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- Checks: > clang-diagnostic-*, clang-analyzer-*, readability-*, modernize-*, bugprone-*, misc-*, google-runtime-int, fuchsia-restrict-system-includes, -misc-unused-parameters, -misc-include-cleaner, -llvm-header-guard, -clang-analyzer-valist.Uninitialized, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, -clang-analyzer-security.insecureAPI.rand, -clang-analyzer-alpha.*, -modernize-macro-to-enum, -readability-magic-numbers, -readability-braces-around-statements, -readability-function-cognitive-complexity, -readability-identifier-length, -readability-isolate-declaration, -readability-suspicious-call-argument, -readability-avoid-nested-conditional-operator, -bugprone-easily-swappable-parameters, -bugprone-narrowing-conversions, -bugprone-reserved-identifier, -bugprone-switch-missing-default-case, -bugprone-inc-dec-in-conditions, -bugprone-multi-level-implicit-pointer-conversion, WarningsAsErrors: '*' HeaderFilterRegex: '.*(?<!lookup3.c)$' FormatStyle: 'file' CheckOptions: - key: readability-braces-around-statements.ShortStatementLines value: '1' - key: google-runtime-int.TypeSufix value: '_t' - key: fuchsia-restrict-system-includes.Includes value: '*,-stdint.h,-stdbool.h' - key: readability-function-size.StatementThreshold value: '950' ... ������������������������nnn-5.0/src/dbg.h�����������������������������������������������������������������������������������0000664�0000000�0000000�00000005235�14663100143�0013646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * BSD 2-Clause License * * Copyright (C) 2014-2016, Lazaros Koromilas <lostd@2f30.org> * Copyright (C) 2014-2016, Dimitris Papastamos <sin@2f30.org> * Copyright (C) 2016-2024, Arun Prakash Jana <engineerarun@gmail.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * 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 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #ifdef DEBUG static int DEBUG_FD; static int xprintf(int fd, const char *fmt, ...) { char buf[BUFSIZ]; int r; va_list ap; va_start(ap, fmt); r = vsnprintf(buf, sizeof(buf), fmt, ap); if (r > 0 && (unsigned int)r < sizeof(buf)) r = write(fd, buf, r); va_end(ap); return r; } static int enabledbg(void) { FILE *fp = fopen("/tmp/nnndbg", "w"); if (!fp) { perror("dbg(1)"); fp = fopen("./nnndbg", "w"); if (!fp) { perror("dbg(2)"); return -1; } } DEBUG_FD = dup(fileno(fp)); fclose(fp); if (DEBUG_FD == -1) { perror("dbg(3)"); return -1; } return 0; } static void disabledbg(void) { close(DEBUG_FD); } #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define DPRINTF_D(x) xprintf(DEBUG_FD, "ln " TOSTRING(__LINE__) ": " #x "=%d\n", x) #define DPRINTF_U(x) xprintf(DEBUG_FD, "ln " TOSTRING(__LINE__) ": " #x "=%u\n", x) #define DPRINTF_S(x) xprintf(DEBUG_FD, "ln " TOSTRING(__LINE__) ": " #x "=%s\n", x) #define DPRINTF_P(x) xprintf(DEBUG_FD, "ln " TOSTRING(__LINE__) ": " #x "=%p\n", x) #else #define DPRINTF_D(x) #define DPRINTF_U(x) #define DPRINTF_S(x) #define DPRINTF_P(x) #endif /* DEBUG */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/icons-hash.c����������������������������������������������������������������������������0000664�0000000�0000000�00000021140�14663100143�0015132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * simple program which outputs a hash-table of `icons_ext` with low collusion. * the hash function is case-insensitive, it also doesn't hash beyond the * length of the longest extension. */ #include <stddef.h> #include <stdint.h> #include <inttypes.h> #define GOLDEN_RATIO_32 UINT32_C(2654442313) /* golden ratio for 32bits: (2^32) / 1.61803 */ #define GOLDEN_RATIO_64 UINT64_C(0x9E3793492EEDC3F7) #define ICONS_TABLE_SIZE 8 /* size in bits. 8 = 256 */ #ifndef TOUPPER #define TOUPPER(ch) (((ch) >= 'a' && (ch) <= 'z') ? ((ch) - 'a' + 'A') : (ch)) #endif /* all of this is just for the static hash-table generation. only the hash * function gets included in `nnn` binary. */ #ifdef ICONS_GENERATE #include <stdio.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include "icons.h" /* like assert, but always sticks around during generation. */ #define ENSURE(X) do { \ if (!(X)) { \ fprintf(stderr, "%s:%d: `%s`\n", __FILE__, __LINE__, #X); \ abort(); \ } \ } while (0) #define ARRLEN(X) (sizeof(X) / sizeof((X)[0])) #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define HGEN_ITERARATION (1ul << 13) #define ICONS_PROBE_MAX_ALLOWED 6 #define ICONS_MATCH_MAX (512) #if 0 /* for logging some interesting info to stderr */ #define log(...) fprintf(stderr, "[INFO]: " __VA_ARGS__) #else #define log(...) ((void)0) #endif static uint32_t icon_ext_hash(const char *s); /* change ICONS_TABLE_SIZE to increase the size of the table */ static struct icon_pair table[1u << ICONS_TABLE_SIZE]; static uint8_t seen[ARRLEN(table)]; /* arbitrarily picked starting position. change if needed. * but ensure they're above 1 and prefer prime numbers. */ static uint32_t hash_start = 7; static uint32_t hash_mul = 251; /* unused as of now */ /* * use robin-hood insertion to reduce the max probe length */ static void rh_insert(const struct icon_pair item, uint32_t idx, uint32_t n) { ENSURE(n != 0); for (uint32_t tries = 0; tries < ARRLEN(table); ++tries, ++n) { if (seen[idx] < n) { struct icon_pair tmp_item = table[idx]; uint32_t tmp_n = seen[idx]; ENSURE(n < (uint8_t)-1); table[idx] = item; seen[idx] = n; if (tmp_n != 0) /* the slot we inserted to wasn't empty */ rh_insert(tmp_item, idx, tmp_n); return; } idx = (idx + 1) % ARRLEN(table); } ENSURE(0 && "unreachable"); } enum { PROBE_MAX, PROBE_TOTAL, PROBE_CNT }; static unsigned int * table_populate(unsigned int p[static PROBE_CNT]) { memset(seen, 0x0, sizeof seen); memset(table, 0x0, sizeof table); for (size_t i = 0; i < ARRLEN(icons_ext); ++i) { if (icons_ext[i].icon[0] == '\0') /* skip empty entries */ continue; uint32_t h = icon_ext_hash(icons_ext[i].match); rh_insert(icons_ext[i], h, 1); } p[PROBE_MAX] = p[PROBE_TOTAL] = 0; for (size_t i = 0; i < ARRLEN(seen); ++i) { p[PROBE_MAX] = MAX(p[PROBE_MAX], seen[i]); p[PROBE_TOTAL] += seen[i]; } return p; } /* permuted congruential generator */ static uint32_t pcg(uint64_t *state) { uint64_t oldstate = *state; *state *= GOLDEN_RATIO_64; uint32_t r = (oldstate >> 59); uint32_t v = (oldstate ^ (oldstate >> 18)) >> 27; return (v >> (-r & 31)) | (v << r); } int main(void) { ENSURE(ARRLEN(icons_ext) <= ARRLEN(table)); ENSURE(ICONS_TABLE_SIZE < 16); ENSURE(1u << ICONS_TABLE_SIZE == ARRLEN(table)); ENSURE((GOLDEN_RATIO_32 & 1) == 1); /* must be odd */ ENSURE((GOLDEN_RATIO_64 & 1) == 1); /* must be odd */ ENSURE(hash_start > 1); ENSURE(hash_mul > 1); /* ensure power of 2 hashtable size which allows compiler to optimize * away mod (`%`) operations */ ENSURE((ARRLEN(table) & (ARRLEN(table) - 1)) == 0); unsigned int max_probe = (unsigned)-1; uint32_t best_hash_start = 0, best_hash_mul = 0, best_total_probe = 9999; uint64_t hash_start_rng = hash_start, hash_mul_rng = hash_mul; for (size_t i = 0; i < HGEN_ITERARATION; ++i) { unsigned *p = table_populate((unsigned [PROBE_CNT]){0}); if (p[PROBE_MAX] < max_probe || (p[PROBE_MAX] == max_probe && p[PROBE_TOTAL] < best_total_probe)) { max_probe = p[PROBE_MAX]; best_total_probe = p[PROBE_TOTAL]; best_hash_start = hash_start; best_hash_mul = hash_mul; } hash_start = pcg(&hash_start_rng); hash_mul = pcg(&hash_mul_rng); } ENSURE(max_probe < ICONS_PROBE_MAX_ALLOWED); hash_start = best_hash_start; hash_mul = best_hash_mul; { unsigned *p = table_populate((unsigned [PROBE_CNT]){0}); ENSURE(p[PROBE_MAX] == max_probe); ENSURE(p[PROBE_TOTAL] == best_total_probe); } /* sanity check */ double nitems = 0; unsigned int total_probe = 0; for (size_t i = 0; i < ARRLEN(icons_ext); ++i) { if (icons_ext[i].icon[0] == 0) continue; uint32_t found = 0, h = icon_ext_hash(icons_ext[i].match); for (uint32_t k = 0; k < max_probe; ++k) { uint32_t z = (h + k) % ARRLEN(table); ++total_probe; if (table[z].match && strcasecmp(icons_ext[i].match, table[z].match) == 0) { found = 1; break; } } ENSURE(found); ++nitems; } ENSURE(total_probe == best_total_probe); size_t match_max = 0, icon_max = 0; for (size_t i = 0; i < ARRLEN(icons_name); ++i) { match_max = MAX(match_max, strlen(icons_name[i].match) + 1); icon_max = MAX(icon_max, strlen(icons_name[i].icon) + 1); } for (size_t i = 0; i < ARRLEN(icons_ext); ++i) { match_max = MAX(match_max, strlen(icons_ext[i].match) + 1); icon_max = MAX(icon_max, strlen(icons_ext[i].icon) + 1); } icon_max = MAX(icon_max, strlen(dir_icon.icon) + 1); icon_max = MAX(icon_max, strlen(exec_icon.icon) + 1); icon_max = MAX(icon_max, strlen(file_icon.icon) + 1); ENSURE(icon_max < ICONS_MATCH_MAX); const char *uniq[ARRLEN(icons_ext)] = {0}; size_t uniq_head = 0; for (size_t i = 0; i < ARRLEN(icons_ext); ++i) { if (icons_ext[i].icon[0] == 0) continue; int isuniq = 1; for (size_t k = 0; k < uniq_head; ++k) { if (strcasecmp(uniq[k], icons_ext[i].icon) == 0) { isuniq = 0; break; } } if (isuniq) { ENSURE(uniq_head < ARRLEN(uniq)); uniq[uniq_head++] = icons_ext[i].icon; } } ENSURE(uniq_head < (unsigned char)-1); log("load-factor: %.2f (%u/%zu)\n", (nitems * 100.0) / (double)ARRLEN(table), (unsigned int)nitems, ARRLEN(table)); log("max_probe : %6u\n", max_probe); log("total_probe: %6u\n", total_probe); log("uniq icons : %6zu\n", uniq_head); log("no-compact : %6zu bytes\n", ARRLEN(table) * icon_max); log("compaction : %6zu bytes\n", uniq_head * icon_max + ARRLEN(table)); log("hash_start : %6" PRIu32 "\n", hash_start); log("hash_mul : %6" PRIu32 "\n", hash_mul); printf("#ifndef INCLUDE_ICONS_GENERATED\n"); printf("#define INCLUDE_ICONS_GENERATED\n\n"); printf("/*\n * NOTE: This file is automatically generated.\n"); printf(" * DO NOT EDIT THIS FILE DIRECTLY.\n"); printf(" * Use `icons.h` to customize icons\n */\n\n"); printf("#define hash_start UINT32_C(%" PRIu32 ")\n", hash_start); printf("#define hash_mul UINT32_C(%" PRIu32 ")\n\n", hash_mul); printf("#define ICONS_PROBE_MAX %uu\n", max_probe); printf("#define ICONS_MATCH_MAX %zuu\n\n", match_max); printf("#define ICONS_STR_MAX %zuu\n\n", icon_max); printf("struct icon_pair { const char match[ICONS_MATCH_MAX]; " "const char icon[ICONS_STR_MAX]; unsigned char color; };\n\n"); printf("static const char icons_ext_uniq[%zu][ICONS_STR_MAX] = {\n", uniq_head); for (size_t i = 0; i < uniq_head; ++i) printf("\t\"%s\",\n", uniq[i]); printf("};\n\n"); printf("static const struct {\n\tconst char match[ICONS_MATCH_MAX];\n" "\tunsigned char idx;\n\tunsigned char color;\n} icons_ext[%zu] = {\n", ARRLEN(table)); for (size_t i = 0; i < ARRLEN(table); ++i) { if (table[i].icon == NULL || table[i].icon[0] == '\0') /* skip empty entries */ continue; size_t k; for (k = 0; k < uniq_head; ++k) { if (strcasecmp(table[i].icon, uniq[k]) == 0) break; } ENSURE(k < uniq_head); printf("\t[%3zu] = {\"%s\", %zu, %hhu },\n", i, table[i].match, k, table[i].color); } printf("};\n\n"); printf("#endif /* INCLUDE_ICONS_GENERATED */\n"); } #else #define ENSURE(X) ((void)0) #endif /* ICONS_GENERATE */ #if defined(ICONS_GENERATE) || defined(ICONS_ENABLED) static uint32_t icon_ext_hash(const char *str) { uint32_t i, hash = hash_start; enum { wsz = sizeof hash * CHAR_BIT, z = wsz - ICONS_TABLE_SIZE, r = 5 }; /* just an xor-rotate hash. in general, this is a horrible hash * function but for our specific input it works fine while being * computationally cheap. */ for (i = 0; i < ICONS_MATCH_MAX && str[i] != '\0'; ++i) { hash ^= TOUPPER((unsigned char)str[i]); hash = (hash >> (wsz - r)) | (hash << r); } /* finalizer: https://probablydance.com/2018/06/16 */ hash ^= (hash >> z); hash *= GOLDEN_RATIO_32; hash >>= z; ENSURE(hash < ARRLEN(table)); return hash; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/icons-in-terminal.h���������������������������������������������������������������������0000664�0000000�0000000�00000402503�14663100143�0016441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef ICONS_IN_TERMINAL #define ICONS_IN_TERMINAL # define POWERLINE_BRANCH "\ue0a0" # define POWERLINE_LINE_NUMBER "\ue0a1" # define POWERLINE_READONLY "\ue0a2" # define POWERLINE_EXTRA_COLUMN_NUMBER "\ue0a3" # define POWERLINE_LEFT_HARD_DIVIDER "\ue0b0" # define POWERLINE_LEFT_SOFT_DIVIDER "\ue0b1" # define POWERLINE_RIGHT_HARD_DIVIDER "\ue0b2" # define POWERLINE_RIGHT_SOFT_DIVIDER "\ue0b3" # define POWERLINE_EXTRA_RIGHT_HALF_CIRCLE_THICK "\ue0b4" # define POWERLINE_EXTRA_RIGHT_HALF_CIRCLE_THIN "\ue0b5" # define POWERLINE_EXTRA_LEFT_HALF_CIRCLE_THICK "\ue0b6" # define POWERLINE_EXTRA_LEFT_HALF_CIRCLE_THIN "\ue0b7" # define POWERLINE_EXTRA_LOWER_LEFT_TRIANGLE "\ue0b8" # define POWERLINE_EXTRA_BACKSLASH_SEPARATOR "\ue0b9" # define POWERLINE_EXTRA_LOWER_RIGHT_TRIANGLE "\ue0ba" # define POWERLINE_EXTRA_FORWARDSLASH_SEPARATOR "\ue0bb" # define POWERLINE_EXTRA_UPPER_LEFT_TRIANGLE "\ue0bc" # define POWERLINE_EXTRA_FORWARDSLASH_SEPARATOR_REDUNDANT "\ue0bd" # define POWERLINE_EXTRA_UPPER_RIGHT_TRIANGLE "\ue0be" # define POWERLINE_EXTRA_BACKSLASH_SEPARATOR_REDUNDANT "\ue0bf" # define POWERLINE_EXTRA_FLAME_THICK "\ue0c0" # define POWERLINE_EXTRA_FLAME_THIN "\ue0c1" # define POWERLINE_EXTRA_FLAME_THICK_MIRRORED "\ue0c2" # define POWERLINE_EXTRA_FLAME_THIN_MIRRORED "\ue0c3" # define POWERLINE_EXTRA_PIXELATED_SQUARES_SMALL "\ue0c4" # define POWERLINE_EXTRA_PIXELATED_SQUARES_SMALL_MIRRORED "\ue0c5" # define POWERLINE_EXTRA_PIXELATED_SQUARES_BIG "\ue0c6" # define POWERLINE_EXTRA_PIXELATED_SQUARES_BIG_MIRRORED "\ue0c7" # define POWERLINE_EXTRA_ICE_WAVEFORM "\ue0c8" # define POWERLINE_EXTRA_ICE_WAVEFORM_MIRRORED "\ue0ca" # define POWERLINE_EXTRA_HONEYCOMB "\ue0cc" # define POWERLINE_EXTRA_HONEYCOMB_OUTLINE "\ue0cd" # define POWERLINE_EXTRA_LEGO_SEPARATOR "\ue0ce" # define POWERLINE_EXTRA_LEGO_SEPARATOR_THIN "\ue0cf" # define POWERLINE_EXTRA_LEGO_BLOCK_FACING "\ue0d0" # define POWERLINE_EXTRA_LEGO_BLOCK_SIDEWAYS "\ue0d1" # define POWERLINE_EXTRA_TRAPEZOID_TOP_BOTTOM "\ue0d2" # define POWERLINE_EXTRA_TRAPEZOID_TOP_BOTTOM_MIRRORED "\ue0d4" # define OCT_HEART "\ue000" # define OCT_ZAP "\ue001" # define OCT_LIGHT_BULB "\ue002" # define OCT_REPO "\ue003" # define OCT_REPO_FORKED "\ue004" # define OCT_REPO_PUSH "\ue005" # define OCT_REPO_PULL "\ue006" # define OCT_BOOK "\ue007" # define OCT_OCTOFACE "\ue008" # define OCT_GIT_PULL_REQUEST "\ue009" # define OCT_MARK_GITHUB "\ue00a" # define OCT_CLOUD_DOWNLOAD "\ue00b" # define OCT_CLOUD_UPLOAD "\ue00c" # define OCT_KEYBOARD "\ue00d" # define OCT_GIST "\ue00e" # define OCT_FILE_CODE "\ue00f" # define OCT_FILE_TEXT "\ue010" # define OCT_FILE_MEDIA "\ue011" # define OCT_FILE_ZIP "\ue012" # define OCT_FILE_PDF "\ue013" # define OCT_TAG "\ue014" # define OCT_FILE_DIRECTORY "\ue015" # define OCT_FILE_SUBMODULE "\ue016" # define OCT_PERSON "\ue017" # define OCT_JERSEY "\ue018" # define OCT_GIT_COMMIT "\ue019" # define OCT_GIT_BRANCH "\ue01a" # define OCT_GIT_MERGE "\ue01b" # define OCT_MIRROR "\ue01c" # define OCT_ISSUE_OPENED "\ue01d" # define OCT_ISSUE_REOPENED "\ue01e" # define OCT_ISSUE_CLOSED "\ue01f" # define OCT_STAR "\ue020" # define OCT_COMMENT "\ue021" # define OCT_QUESTION "\ue022" # define OCT_ALERT "\ue023" # define OCT_SEARCH "\ue024" # define OCT_GEAR "\ue025" # define OCT_RADIO_TOWER "\ue026" # define OCT_TOOLS "\ue027" # define OCT_SIGN_OUT "\ue028" # define OCT_ROCKET "\ue029" # define OCT_RSS "\ue02a" # define OCT_CLIPPY "\ue02b" # define OCT_SIGN_IN "\ue02c" # define OCT_ORGANIZATION "\ue02d" # define OCT_DEVICE_MOBILE "\ue02e" # define OCT_UNFOLD "\ue02f" # define OCT_CHECK "\ue030" # define OCT_MAIL "\ue031" # define OCT_MAIL_READ "\ue032" # define OCT_ARROW_UP "\ue033" # define OCT_ARROW_RIGHT "\ue034" # define OCT_ARROW_DOWN "\ue035" # define OCT_ARROW_LEFT "\ue036" # define OCT_PIN "\ue037" # define OCT_GIFT "\ue038" # define OCT_GRAPH "\ue039" # define OCT_TRIANGLE_LEFT "\ue03a" # define OCT_CREDIT_CARD "\ue03b" # define OCT_CLOCK "\ue03c" # define OCT_RUBY "\ue03d" # define OCT_BROADCAST "\ue03e" # define OCT_KEY "\ue03f" # define OCT_REPO_FORCE_PUSH "\ue040" # define OCT_REPO_CLONE "\ue041" # define OCT_DIFF "\ue042" # define OCT_EYE "\ue043" # define OCT_COMMENT_DISCUSSION "\ue044" # define OCT_MAIL_REPLY "\ue045" # define OCT_PRIMITIVE_DOT "\ue046" # define OCT_PRIMITIVE_SQUARE "\ue047" # define OCT_DEVICE_CAMERA "\ue048" # define OCT_DEVICE_CAMERA_VIDEO "\ue049" # define OCT_PENCIL "\ue04a" # define OCT_INFO "\ue04b" # define OCT_TRIANGLE_RIGHT "\ue04c" # define OCT_TRIANGLE_DOWN "\ue04d" # define OCT_LINK "\ue04e" # define OCT_PLUS "\ue04f" # define OCT_THREE_BARS "\ue050" # define OCT_CODE "\ue051" # define OCT_LOCATION "\ue052" # define OCT_LIST_UNORDERED "\ue053" # define OCT_LIST_ORDERED "\ue054" # define OCT_QUOTE "\ue055" # define OCT_VERSIONS "\ue056" # define OCT_CALENDAR "\ue057" # define OCT_LOCK "\ue058" # define OCT_DIFF_ADDED "\ue059" # define OCT_DIFF_REMOVED "\ue05a" # define OCT_DIFF_MODIFIED "\ue05b" # define OCT_DIFF_RENAMED "\ue05c" # define OCT_HORIZONTAL_RULE "\ue05d" # define OCT_ARROW_SMALL_RIGHT "\ue05e" # define OCT_MILESTONE "\ue05f" # define OCT_CHECKLIST "\ue060" # define OCT_MEGAPHONE "\ue061" # define OCT_CHEVRON_RIGHT "\ue062" # define OCT_BOOKMARK "\ue063" # define OCT_SETTINGS "\ue064" # define OCT_DASHBOARD "\ue065" # define OCT_HISTORY "\ue066" # define OCT_LINK_EXTERNAL "\ue067" # define OCT_MUTE "\ue068" # define OCT_X "\ue069" # define OCT_CIRCLE_SLASH "\ue06a" # define OCT_PULSE "\ue06b" # define OCT_SYNC "\ue06c" # define OCT_TELESCOPE "\ue06d" # define OCT_GIST_SECRET "\ue06e" # define OCT_HOME "\ue06f" # define OCT_STOP "\ue070" # define OCT_BUG "\ue071" # define OCT_LOGO_GITHUB "\ue072" # define OCT_FILE_BINARY "\ue073" # define OCT_DATABASE "\ue074" # define OCT_SERVER "\ue075" # define OCT_DIFF_IGNORED "\ue076" # define OCT_ELLIPSIS "\ue077" # define OCT_NO_NEWLINE "\ue078" # define OCT_HUBOT "\ue079" # define OCT_ARROW_SMALL_UP "\ue07a" # define OCT_ARROW_SMALL_DOWN "\ue07b" # define OCT_ARROW_SMALL_LEFT "\ue07c" # define OCT_CHEVRON_UP "\ue07d" # define OCT_CHEVRON_DOWN "\ue07e" # define OCT_CHEVRON_LEFT "\ue07f" # define OCT_TRIANGLE_UP "\ue080" # define OCT_GIT_COMPARE "\ue081" # define OCT_LOGO_GIST "\ue082" # define OCT_FILE_SYMLINK_FILE "\ue083" # define OCT_FILE_SYMLINK_DIRECTORY "\ue084" # define OCT_SQUIRREL "\ue085" # define OCT_GLOBE "\ue086" # define OCT_UNMUTE "\ue087" # define OCT_MENTION "\ue088" # define OCT_PACKAGE "\ue089" # define OCT_BROWSER "\ue08a" # define OCT_TERMINAL "\ue08b" # define OCT_MARKDOWN "\ue08c" # define OCT_DASH "\ue08d" # define OCT_FOLD "\ue08e" # define OCT_INBOX "\ue08f" # define OCT_TRASHCAN "\ue090" # define OCT_PAINTCAN "\ue091" # define OCT_FLAME "\ue092" # define OCT_BRIEFCASE "\ue093" # define OCT_PLUG "\ue094" # define OCT_CIRCUIT_BOARD "\ue095" # define OCT_MORTAR_BOARD "\ue096" # define OCT_LAW "\ue097" # define OCT_THUMBSUP "\ue098" # define OCT_THUMBSDOWN "\ue099" # define OCT_DESKTOP_DOWNLOAD "\ue09a" # define OCT_BEAKER "\ue09b" # define OCT_BELL "\ue09c" # define OCT_WATCH "\ue09d" # define OCT_SHIELD "\ue09e" # define OCT_BOLD "\ue09f" # define OCT_TEXT_SIZE "\ue0d5" # define OCT_ITALIC "\ue0d6" # define OCT_TASKLIST "\ue0d7" # define OCT_VERIFIED "\ue0d8" # define OCT_SMILEY "\ue0d9" # define OCT_UNVERIFIED "\ue0da" # define OCT_ELLIPSES "\ue0db" # define OCT_FILE "\ue0dc" # define OCT_GRABBER "\ue0dd" # define OCT_PLUS_SMALL "\ue0de" # define OCT_REPLY "\ue0df" # define OCT_DEVICE_DESKTOP "\ue0e0" # define FA_GLASS "\ue0e1" # define FA_MUSIC "\ue0e2" # define FA_SEARCH "\ue0e3" # define FA_ENVELOPE_O "\ue0e4" # define FA_HEART "\ue0e5" # define FA_STAR "\ue0e6" # define FA_STAR_O "\ue0e7" # define FA_USER "\ue0e8" # define FA_FILM "\ue0e9" # define FA_TH_LARGE "\ue0ea" # define FA_TH "\ue0eb" # define FA_TH_LIST "\ue0ec" # define FA_CHECK "\ue0ed" # define FA_CLOSE "\ue0ee" # define FA_SEARCH_PLUS "\ue0ef" # define FA_SEARCH_MINUS "\ue0f0" # define FA_POWER_OFF "\ue0f1" # define FA_SIGNAL "\ue0f2" # define FA_COG "\ue0f3" # define FA_TRASH_O "\ue0f4" # define FA_HOME "\ue0f5" # define FA_FILE_O "\ue0f6" # define FA_CLOCK_O "\ue0f7" # define FA_ROAD "\ue0f8" # define FA_DOWNLOAD "\ue0f9" # define FA_ARROW_CIRCLE_O_DOWN "\ue0fa" # define FA_ARROW_CIRCLE_O_UP "\ue0fb" # define FA_INBOX "\ue0fc" # define FA_PLAY_CIRCLE_O "\ue0fd" # define FA_REPEAT "\ue0fe" # define FA_REFRESH "\ue0ff" # define FA_LIST_ALT "\ue100" # define FA_LOCK "\ue101" # define FA_FLAG "\ue102" # define FA_HEADPHONES "\ue103" # define FA_VOLUME_OFF "\ue104" # define FA_VOLUME_DOWN "\ue105" # define FA_VOLUME_UP "\ue106" # define FA_QRCODE "\ue107" # define FA_BARCODE "\ue108" # define FA_TAG "\ue109" # define FA_TAGS "\ue10a" # define FA_BOOK "\ue10b" # define FA_BOOKMARK "\ue10c" # define FA_PRINT "\ue10d" # define FA_CAMERA "\ue10e" # define FA_FONT "\ue10f" # define FA_BOLD "\ue110" # define FA_ITALIC "\ue111" # define FA_TEXT_HEIGHT "\ue112" # define FA_TEXT_WIDTH "\ue113" # define FA_ALIGN_LEFT "\ue114" # define FA_ALIGN_CENTER "\ue115" # define FA_ALIGN_RIGHT "\ue116" # define FA_ALIGN_JUSTIFY "\ue117" # define FA_LIST "\ue118" # define FA_DEDENT "\ue119" # define FA_INDENT "\ue11a" # define FA_VIDEO_CAMERA "\ue11b" # define FA_IMAGE "\ue11c" # define FA_PENCIL "\ue11d" # define FA_MAP_MARKER "\ue11e" # define FA_ADJUST "\ue11f" # define FA_TINT "\ue120" # define FA_EDIT "\ue121" # define FA_SHARE_SQUARE_O "\ue122" # define FA_CHECK_SQUARE_O "\ue123" # define FA_ARROWS "\ue124" # define FA_STEP_BACKWARD "\ue125" # define FA_FAST_BACKWARD "\ue126" # define FA_BACKWARD "\ue127" # define FA_PLAY "\ue128" # define FA_PAUSE "\ue129" # define FA_STOP "\ue12a" # define FA_FORWARD "\ue12b" # define FA_FAST_FORWARD "\ue12c" # define FA_STEP_FORWARD "\ue12d" # define FA_EJECT "\ue12e" # define FA_CHEVRON_LEFT "\ue12f" # define FA_CHEVRON_RIGHT "\ue130" # define FA_PLUS_CIRCLE "\ue131" # define FA_MINUS_CIRCLE "\ue132" # define FA_TIMES_CIRCLE "\ue133" # define FA_CHECK_CIRCLE "\ue134" # define FA_QUESTION_CIRCLE "\ue135" # define FA_INFO_CIRCLE "\ue136" # define FA_CROSSHAIRS "\ue137" # define FA_TIMES_CIRCLE_O "\ue138" # define FA_CHECK_CIRCLE_O "\ue139" # define FA_BAN "\ue13a" # define FA_ARROW_LEFT "\ue13b" # define FA_ARROW_RIGHT "\ue13c" # define FA_ARROW_UP "\ue13d" # define FA_ARROW_DOWN "\ue13e" # define FA_MAIL_FORWARD "\ue13f" # define FA_EXPAND "\ue140" # define FA_COMPRESS "\ue141" # define FA_PLUS "\ue142" # define FA_MINUS "\ue143" # define FA_ASTERISK "\ue144" # define FA_EXCLAMATION_CIRCLE "\ue145" # define FA_GIFT "\ue146" # define FA_LEAF "\ue147" # define FA_FIRE "\ue148" # define FA_EYE "\ue149" # define FA_EYE_SLASH "\ue14a" # define FA_EXCLAMATION_TRIANGLE "\ue14b" # define FA_PLANE "\ue14c" # define FA_CALENDAR "\ue14d" # define FA_RANDOM "\ue14e" # define FA_COMMENT "\ue14f" # define FA_MAGNET "\ue150" # define FA_CHEVRON_UP "\ue151" # define FA_CHEVRON_DOWN "\ue152" # define FA_RETWEET "\ue153" # define FA_SHOPPING_CART "\ue154" # define FA_FOLDER "\ue155" # define FA_FOLDER_OPEN "\ue156" # define FA_ARROWS_V "\ue157" # define FA_ARROWS_H "\ue158" # define FA_BAR_CHART "\ue159" # define FA_TWITTER_SQUARE "\ue15a" # define FA_FACEBOOK_SQUARE "\ue15b" # define FA_CAMERA_RETRO "\ue15c" # define FA_KEY "\ue15d" # define FA_COGS "\ue15e" # define FA_COMMENTS "\ue15f" # define FA_THUMBS_O_UP "\ue160" # define FA_THUMBS_O_DOWN "\ue161" # define FA_STAR_HALF "\ue162" # define FA_HEART_O "\ue163" # define FA_SIGN_OUT "\ue164" # define FA_LINKEDIN_SQUARE "\ue165" # define FA_THUMB_TACK "\ue166" # define FA_EXTERNAL_LINK "\ue167" # define FA_SIGN_IN "\ue168" # define FA_TROPHY "\ue169" # define FA_GITHUB_SQUARE "\ue16a" # define FA_UPLOAD "\ue16b" # define FA_LEMON_O "\ue16c" # define FA_PHONE "\ue16d" # define FA_SQUARE_O "\ue16e" # define FA_BOOKMARK_O "\ue16f" # define FA_PHONE_SQUARE "\ue170" # define FA_TWITTER "\ue171" # define FA_FACEBOOK "\ue172" # define FA_GITHUB "\ue173" # define FA_UNLOCK "\ue174" # define FA_CREDIT_CARD "\ue175" # define FA_FEED "\ue176" # define FA_HDD_O "\ue177" # define FA_BULLHORN "\ue178" # define FA_BELL_O "\ue179" # define FA_CERTIFICATE "\ue17a" # define FA_HAND_O_RIGHT "\ue17b" # define FA_HAND_O_LEFT "\ue17c" # define FA_HAND_O_UP "\ue17d" # define FA_HAND_O_DOWN "\ue17e" # define FA_ARROW_CIRCLE_LEFT "\ue17f" # define FA_ARROW_CIRCLE_RIGHT "\ue180" # define FA_ARROW_CIRCLE_UP "\ue181" # define FA_ARROW_CIRCLE_DOWN "\ue182" # define FA_GLOBE "\ue183" # define FA_WRENCH "\ue184" # define FA_TASKS "\ue185" # define FA_FILTER "\ue186" # define FA_BRIEFCASE "\ue187" # define FA_ARROWS_ALT "\ue188" # define FA_GROUP "\ue189" # define FA_CHAIN "\ue18a" # define FA_CLOUD "\ue18b" # define FA_FLASK "\ue18c" # define FA_CUT "\ue18d" # define FA_COPY "\ue18e" # define FA_PAPERCLIP "\ue18f" # define FA_FLOPPY_O "\ue190" # define FA_SQUARE "\ue191" # define FA_BARS "\ue192" # define FA_LIST_UL "\ue193" # define FA_LIST_OL "\ue194" # define FA_STRIKETHROUGH "\ue195" # define FA_UNDERLINE "\ue196" # define FA_TABLE "\ue197" # define FA_MAGIC "\ue198" # define FA_TRUCK "\ue199" # define FA_PINTEREST "\ue19a" # define FA_PINTEREST_SQUARE "\ue19b" # define FA_GOOGLE_PLUS_SQUARE "\ue19c" # define FA_GOOGLE_PLUS "\ue19d" # define FA_MONEY "\ue19e" # define FA_CARET_DOWN "\ue19f" # define FA_CARET_UP "\ue1a0" # define FA_CARET_LEFT "\ue1a1" # define FA_CARET_RIGHT "\ue1a2" # define FA_COLUMNS "\ue1a3" # define FA_SORT "\ue1a4" # define FA_SORT_DESC "\ue1a5" # define FA_SORT_ASC "\ue1a6" # define FA_ENVELOPE "\ue1a7" # define FA_LINKEDIN "\ue1a8" # define FA_ROTATE_LEFT "\ue1a9" # define FA_GAVEL "\ue1aa" # define FA_DASHBOARD "\ue1ab" # define FA_COMMENT_O "\ue1ac" # define FA_COMMENTS_O "\ue1ad" # define FA_BOLT "\ue1ae" # define FA_SITEMAP "\ue1af" # define FA_UMBRELLA "\ue1b0" # define FA_CLIPBOARD "\ue1b1" # define FA_LIGHTBULB_O "\ue1b2" # define FA_EXCHANGE "\ue1b3" # define FA_CLOUD_DOWNLOAD "\ue1b4" # define FA_CLOUD_UPLOAD "\ue1b5" # define FA_USER_MD "\ue1b6" # define FA_STETHOSCOPE "\ue1b7" # define FA_SUITCASE "\ue1b8" # define FA_BELL "\ue1b9" # define FA_COFFEE "\ue1ba" # define FA_CUTLERY "\ue1bb" # define FA_FILE_TEXT_O "\ue1bc" # define FA_BUILDING_O "\ue1bd" # define FA_HOSPITAL_O "\ue1be" # define FA_AMBULANCE "\ue1bf" # define FA_MEDKIT "\ue1c0" # define FA_FIGHTER_JET "\ue1c1" # define FA_BEER "\ue1c2" # define FA_H_SQUARE "\ue1c3" # define FA_PLUS_SQUARE "\ue1c4" # define FA_ANGLE_DOUBLE_LEFT "\ue1c5" # define FA_ANGLE_DOUBLE_RIGHT "\ue1c6" # define FA_ANGLE_DOUBLE_UP "\ue1c7" # define FA_ANGLE_DOUBLE_DOWN "\ue1c8" # define FA_ANGLE_LEFT "\ue1c9" # define FA_ANGLE_RIGHT "\ue1ca" # define FA_ANGLE_UP "\ue1cb" # define FA_ANGLE_DOWN "\ue1cc" # define FA_DESKTOP "\ue1cd" # define FA_LAPTOP "\ue1ce" # define FA_TABLET "\ue1cf" # define FA_MOBILE "\ue1d0" # define FA_CIRCLE_O "\ue1d1" # define FA_QUOTE_LEFT "\ue1d2" # define FA_QUOTE_RIGHT "\ue1d3" # define FA_SPINNER "\ue1d4" # define FA_CIRCLE "\ue1d5" # define FA_MAIL_REPLY "\ue1d6" # define FA_GITHUB_ALT "\ue1d7" # define FA_FOLDER_O "\ue1d8" # define FA_FOLDER_OPEN_O "\ue1d9" # define FA_EXPAND_ALT "\ue1da" # define FA_COLLAPSE_ALT "\ue1db" # define FA_SMILE_O "\ue1dc" # define FA_FROWN_O "\ue1dd" # define FA_MEH_O "\ue1de" # define FA_GAMEPAD "\ue1df" # define FA_KEYBOARD_O "\ue1e0" # define FA_FLAG_O "\ue1e1" # define FA_FLAG_CHECKERED "\ue1e2" # define FA_TERMINAL "\ue1e3" # define FA_CODE "\ue1e4" # define FA_MAIL_REPLY_ALL "\ue1e5" # define FA_STAR_HALF_EMPTY "\ue1e6" # define FA_LOCATION_ARROW "\ue1e7" # define FA_CROP "\ue1e8" # define FA_CODE_FORK "\ue1e9" # define FA_CHAIN_BROKEN "\ue1ea" # define FA_QUESTION "\ue1eb" # define FA_INFO "\ue1ec" # define FA_EXCLAMATION "\ue1ed" # define FA_SUPERSCRIPT "\ue1ee" # define FA_SUBSCRIPT "\ue1ef" # define FA_ERASER "\ue1f0" # define FA_PUZZLE_PIECE "\ue1f1" # define FA_MICROPHONE "\ue1f2" # define FA_MICROPHONE_SLASH "\ue1f3" # define FA_SHIELD "\ue1f4" # define FA_CALENDAR_O "\ue1f5" # define FA_FIRE_EXTINGUISHER "\ue1f6" # define FA_ROCKET "\ue1f7" # define FA_MAXCDN "\ue1f8" # define FA_CHEVRON_CIRCLE_LEFT "\ue1f9" # define FA_CHEVRON_CIRCLE_RIGHT "\ue1fa" # define FA_CHEVRON_CIRCLE_UP "\ue1fb" # define FA_CHEVRON_CIRCLE_DOWN "\ue1fc" # define FA_HTML5 "\ue1fd" # define FA_CSS3 "\ue1fe" # define FA_ANCHOR "\ue1ff" # define FA_UNLOCK_ALT "\ue200" # define FA_BULLSEYE "\ue201" # define FA_ELLIPSIS_H "\ue202" # define FA_ELLIPSIS_V "\ue203" # define FA_RSS_SQUARE "\ue204" # define FA_PLAY_CIRCLE "\ue205" # define FA_TICKET "\ue206" # define FA_MINUS_SQUARE "\ue207" # define FA_MINUS_SQUARE_O "\ue208" # define FA_LEVEL_UP "\ue209" # define FA_LEVEL_DOWN "\ue20a" # define FA_CHECK_SQUARE "\ue20b" # define FA_PENCIL_SQUARE "\ue20c" # define FA_EXTERNAL_LINK_SQUARE "\ue20d" # define FA_SHARE_SQUARE "\ue20e" # define FA_COMPASS "\ue20f" # define FA_CARET_SQUARE_O_DOWN "\ue210" # define FA_CARET_SQUARE_O_UP "\ue211" # define FA_CARET_SQUARE_O_RIGHT "\ue212" # define FA_EUR "\ue213" # define FA_GBP "\ue214" # define FA_DOLLAR "\ue215" # define FA_INR "\ue216" # define FA_CNY "\ue217" # define FA_ROUBLE "\ue218" # define FA_KRW "\ue219" # define FA_BITCOIN "\ue21a" # define FA_FILE "\ue21b" # define FA_FILE_TEXT "\ue21c" # define FA_SORT_ALPHA_ASC "\ue21d" # define FA_SORT_ALPHA_DESC "\ue21e" # define FA_SORT_AMOUNT_ASC "\ue21f" # define FA_SORT_AMOUNT_DESC "\ue220" # define FA_SORT_NUMERIC_ASC "\ue221" # define FA_SORT_NUMERIC_DESC "\ue222" # define FA_THUMBS_UP "\ue223" # define FA_THUMBS_DOWN "\ue224" # define FA_YOUTUBE_SQUARE "\ue225" # define FA_YOUTUBE "\ue226" # define FA_XING "\ue227" # define FA_XING_SQUARE "\ue228" # define FA_YOUTUBE_PLAY "\ue229" # define FA_DROPBOX "\ue22a" # define FA_STACK_OVERFLOW "\ue22b" # define FA_INSTAGRAM "\ue22c" # define FA_FLICKR "\ue22d" # define FA_ADN "\ue22e" # define FA_BITBUCKET "\ue22f" # define FA_BITBUCKET_SQUARE "\ue230" # define FA_TUMBLR "\ue231" # define FA_TUMBLR_SQUARE "\ue232" # define FA_LONG_ARROW_DOWN "\ue233" # define FA_LONG_ARROW_UP "\ue234" # define FA_LONG_ARROW_LEFT "\ue235" # define FA_LONG_ARROW_RIGHT "\ue236" # define FA_APPLE "\ue237" # define FA_WINDOWS "\ue238" # define FA_ANDROID "\ue239" # define FA_LINUX "\ue23a" # define FA_DRIBBBLE "\ue23b" # define FA_SKYPE "\ue23c" # define FA_FOURSQUARE "\ue23d" # define FA_TRELLO "\ue23e" # define FA_FEMALE "\ue23f" # define FA_MALE "\ue240" # define FA_GITTIP "\ue241" # define FA_SUN_O "\ue242" # define FA_MOON_O "\ue243" # define FA_ARCHIVE "\ue244" # define FA_BUG "\ue245" # define FA_VK "\ue246" # define FA_WEIBO "\ue247" # define FA_RENREN "\ue248" # define FA_PAGELINES "\ue249" # define FA_STACK_EXCHANGE "\ue24a" # define FA_ARROW_CIRCLE_O_RIGHT "\ue24b" # define FA_ARROW_CIRCLE_O_LEFT "\ue24c" # define FA_CARET_SQUARE_O_LEFT "\ue24d" # define FA_DOT_CIRCLE_O "\ue24e" # define FA_WHEELCHAIR "\ue24f" # define FA_VIMEO_SQUARE "\ue250" # define FA_TRY "\ue251" # define FA_PLUS_SQUARE_O "\ue252" # define FA_SPACE_SHUTTLE "\ue253" # define FA_SLACK "\ue254" # define FA_ENVELOPE_SQUARE "\ue255" # define FA_WORDPRESS "\ue256" # define FA_OPENID "\ue257" # define FA_BANK "\ue258" # define FA_GRADUATION_CAP "\ue259" # define FA_YAHOO "\ue25a" # define FA_GOOGLE "\ue25b" # define FA_REDDIT "\ue25c" # define FA_REDDIT_SQUARE "\ue25d" # define FA_STUMBLEUPON_CIRCLE "\ue25e" # define FA_STUMBLEUPON "\ue25f" # define FA_DELICIOUS "\ue260" # define FA_DIGG "\ue261" # define FA_PIED_PIPER_PP "\ue262" # define FA_PIED_PIPER_ALT "\ue263" # define FA_DRUPAL "\ue264" # define FA_JOOMLA "\ue265" # define FA_LANGUAGE "\ue266" # define FA_FAX "\ue267" # define FA_BUILDING "\ue268" # define FA_CHILD "\ue269" # define FA_PAW "\ue26a" # define FA_SPOON "\ue26b" # define FA_CUBE "\ue26c" # define FA_CUBES "\ue26d" # define FA_BEHANCE "\ue26e" # define FA_BEHANCE_SQUARE "\ue26f" # define FA_STEAM "\ue270" # define FA_STEAM_SQUARE "\ue271" # define FA_RECYCLE "\ue272" # define FA_AUTOMOBILE "\ue273" # define FA_CAB "\ue274" # define FA_TREE "\ue275" # define FA_SPOTIFY "\ue276" # define FA_DEVIANTART "\ue277" # define FA_SOUNDCLOUD "\ue278" # define FA_DATABASE "\ue279" # define FA_FILE_PDF_O "\ue27a" # define FA_FILE_WORD_O "\ue27b" # define FA_FILE_EXCEL_O "\ue27c" # define FA_FILE_POWERPOINT_O "\ue27d" # define FA_FILE_IMAGE_O "\ue27e" # define FA_FILE_ARCHIVE_O "\ue27f" # define FA_FILE_AUDIO_O "\ue280" # define FA_FILE_MOVIE_O "\ue281" # define FA_FILE_CODE_O "\ue282" # define FA_VINE "\ue283" # define FA_CODEPEN "\ue284" # define FA_JSFIDDLE "\ue285" # define FA_LIFE_BOUY "\ue286" # define FA_CIRCLE_O_NOTCH "\ue287" # define FA_RA "\ue288" # define FA_EMPIRE "\ue289" # define FA_GIT_SQUARE "\ue28a" # define FA_GIT "\ue28b" # define FA_HACKER_NEWS "\ue28c" # define FA_TENCENT_WEIBO "\ue28d" # define FA_QQ "\ue28e" # define FA_WECHAT "\ue28f" # define FA_PAPER_PLANE "\ue290" # define FA_PAPER_PLANE_O "\ue291" # define FA_HISTORY "\ue292" # define FA_CIRCLE_THIN "\ue293" # define FA_HEADER "\ue294" # define FA_PARAGRAPH "\ue295" # define FA_SLIDERS "\ue296" # define FA_SHARE_ALT "\ue297" # define FA_SHARE_ALT_SQUARE "\ue298" # define FA_BOMB "\ue299" # define FA_FUTBOL_O "\ue29a" # define FA_TTY "\ue29b" # define FA_BINOCULARS "\ue29c" # define FA_PLUG "\ue29d" # define FA_SLIDESHARE "\ue29e" # define FA_TWITCH "\ue29f" # define FA_YELP "\ue2a0" # define FA_NEWSPAPER_O "\ue2a1" # define FA_WIFI "\ue2a2" # define FA_CALCULATOR "\ue2a3" # define FA_PAYPAL "\ue2a4" # define FA_GOOGLE_WALLET "\ue2a5" # define FA_CC_VISA "\ue2a6" # define FA_CC_MASTERCARD "\ue2a7" # define FA_CC_DISCOVER "\ue2a8" # define FA_CC_AMEX "\ue2a9" # define FA_CC_PAYPAL "\ue2aa" # define FA_CC_STRIPE "\ue2ab" # define FA_BELL_SLASH "\ue2ac" # define FA_BELL_SLASH_O "\ue2ad" # define FA_TRASH "\ue2ae" # define FA_COPYRIGHT "\ue2af" # define FA_AT "\ue2b0" # define FA_EYEDROPPER "\ue2b1" # define FA_PAINT_BRUSH "\ue2b2" # define FA_BIRTHDAY_CAKE "\ue2b3" # define FA_AREA_CHART "\ue2b4" # define FA_PIE_CHART "\ue2b5" # define FA_LINE_CHART "\ue2b6" # define FA_LASTFM "\ue2b7" # define FA_LASTFM_SQUARE "\ue2b8" # define FA_TOGGLE_OFF "\ue2b9" # define FA_TOGGLE_ON "\ue2ba" # define FA_BICYCLE "\ue2bb" # define FA_BUS "\ue2bc" # define FA_IOXHOST "\ue2bd" # define FA_ANGELLIST "\ue2be" # define FA_CC "\ue2bf" # define FA_ILS "\ue2c0" # define FA_MEANPATH "\ue2c1" # define FA_BUYSELLADS "\ue2c2" # define FA_CONNECTDEVELOP "\ue2c3" # define FA_DASHCUBE "\ue2c4" # define FA_FORUMBEE "\ue2c5" # define FA_LEANPUB "\ue2c6" # define FA_SELLSY "\ue2c7" # define FA_SHIRTSINBULK "\ue2c8" # define FA_SIMPLYBUILT "\ue2c9" # define FA_SKYATLAS "\ue2ca" # define FA_CART_PLUS "\ue2cb" # define FA_CART_ARROW_DOWN "\ue2cc" # define FA_DIAMOND "\ue2cd" # define FA_SHIP "\ue2ce" # define FA_USER_SECRET "\ue2cf" # define FA_MOTORCYCLE "\ue2d0" # define FA_STREET_VIEW "\ue2d1" # define FA_HEARTBEAT "\ue2d2" # define FA_VENUS "\ue2d3" # define FA_MARS "\ue2d4" # define FA_MERCURY "\ue2d5" # define FA_INTERSEX "\ue2d6" # define FA_TRANSGENDER_ALT "\ue2d7" # define FA_VENUS_DOUBLE "\ue2d8" # define FA_MARS_DOUBLE "\ue2d9" # define FA_VENUS_MARS "\ue2da" # define FA_MARS_STROKE "\ue2db" # define FA_MARS_STROKE_V "\ue2dc" # define FA_MARS_STROKE_H "\ue2dd" # define FA_NEUTER "\ue2de" # define FA_GENDERLESS "\ue2df" # define FA__523 "\ue2e0" # define FA__524 "\ue2e1" # define FA_FACEBOOK_OFFICIAL "\ue2e2" # define FA_PINTEREST_P "\ue2e3" # define FA_WHATSAPP "\ue2e4" # define FA_SERVER "\ue2e5" # define FA_USER_PLUS "\ue2e6" # define FA_USER_TIMES "\ue2e7" # define FA_BED "\ue2e8" # define FA_VIACOIN "\ue2e9" # define FA_TRAIN "\ue2ea" # define FA_SUBWAY "\ue2eb" # define FA_MEDIUM "\ue2ec" # define FA_Y_COMBINATOR "\ue2ed" # define FA_OPTIN_MONSTER "\ue2ee" # define FA_OPENCART "\ue2ef" # define FA_EXPEDITEDSSL "\ue2f0" # define FA_BATTERY "\ue2f1" # define FA_BATTERY_3 "\ue2f2" # define FA_BATTERY_2 "\ue2f3" # define FA_BATTERY_1 "\ue2f4" # define FA_BATTERY_0 "\ue2f5" # define FA_MOUSE_POINTER "\ue2f6" # define FA_I_CURSOR "\ue2f7" # define FA_OBJECT_GROUP "\ue2f8" # define FA_OBJECT_UNGROUP "\ue2f9" # define FA_STICKY_NOTE "\ue2fa" # define FA_STICKY_NOTE_O "\ue2fb" # define FA_CC_JCB "\ue2fc" # define FA_CC_DINERS_CLUB "\ue2fd" # define FA_CLONE "\ue2fe" # define FA_BALANCE_SCALE "\ue2ff" # define FA_HOURGLASS_O "\ue300" # define FA_HOURGLASS_1 "\ue301" # define FA_HOURGLASS_2 "\ue302" # define FA_HOURGLASS_3 "\ue303" # define FA_HOURGLASS "\ue304" # define FA_HAND_GRAB_O "\ue305" # define FA_HAND_PAPER_O "\ue306" # define FA_HAND_SCISSORS_O "\ue307" # define FA_HAND_LIZARD_O "\ue308" # define FA_HAND_SPOCK_O "\ue309" # define FA_HAND_POINTER_O "\ue30a" # define FA_HAND_PEACE_O "\ue30b" # define FA_TRADEMARK "\ue30c" # define FA_REGISTERED "\ue30d" # define FA_CREATIVE_COMMONS "\ue30e" # define FA_GG "\ue30f" # define FA_GG_CIRCLE "\ue310" # define FA_TRIPADVISOR "\ue311" # define FA_ODNOKLASSNIKI "\ue312" # define FA_ODNOKLASSNIKI_SQUARE "\ue313" # define FA_GET_POCKET "\ue314" # define FA_WIKIPEDIA_W "\ue315" # define FA_SAFARI "\ue316" # define FA_CHROME "\ue317" # define FA_FIREFOX "\ue318" # define FA_OPERA "\ue319" # define FA_INTERNET_EXPLORER "\ue31a" # define FA_TELEVISION "\ue31b" # define FA_CONTAO "\ue31c" # define FA_500PX "\ue31d" # define FA_AMAZON "\ue31e" # define FA_CALENDAR_PLUS_O "\ue31f" # define FA_CALENDAR_MINUS_O "\ue320" # define FA_CALENDAR_TIMES_O "\ue321" # define FA_CALENDAR_CHECK_O "\ue322" # define FA_INDUSTRY "\ue323" # define FA_MAP_PIN "\ue324" # define FA_MAP_SIGNS "\ue325" # define FA_MAP_O "\ue326" # define FA_MAP "\ue327" # define FA_COMMENTING "\ue328" # define FA_COMMENTING_O "\ue329" # define FA_HOUZZ "\ue32a" # define FA_VIMEO "\ue32b" # define FA_BLACK_TIE "\ue32c" # define FA_FONTICONS "\ue32d" # define FA_REDDIT_ALIEN "\ue32e" # define FA_EDGE "\ue32f" # define FA_CREDIT_CARD_ALT "\ue330" # define FA_CODIEPIE "\ue331" # define FA_MODX "\ue332" # define FA_FORT_AWESOME "\ue333" # define FA_USB "\ue334" # define FA_PRODUCT_HUNT "\ue335" # define FA_MIXCLOUD "\ue336" # define FA_SCRIBD "\ue337" # define FA_PAUSE_CIRCLE "\ue338" # define FA_PAUSE_CIRCLE_O "\ue339" # define FA_STOP_CIRCLE "\ue33a" # define FA_STOP_CIRCLE_O "\ue33b" # define FA_SHOPPING_BAG "\ue33c" # define FA_SHOPPING_BASKET "\ue33d" # define FA_HASHTAG "\ue33e" # define FA_BLUETOOTH "\ue33f" # define FA_BLUETOOTH_B "\ue340" # define FA_PERCENT "\ue341" # define FA_GITLAB "\ue342" # define FA_WPBEGINNER "\ue343" # define FA_WPFORMS "\ue344" # define FA_ENVIRA "\ue345" # define FA_UNIVERSAL_ACCESS "\ue346" # define FA_WHEELCHAIR_ALT "\ue347" # define FA_QUESTION_CIRCLE_O "\ue348" # define FA_BLIND "\ue349" # define FA_AUDIO_DESCRIPTION "\ue34a" # define FA_VOLUME_CONTROL_PHONE "\ue34b" # define FA_BRAILLE "\ue34c" # define FA_ASSISTIVE_LISTENING_SYSTEMS "\ue34d" # define FA_AMERICAN_SIGN_LANGUAGE_INTERPRETING "\ue34e" # define FA_DEAF "\ue34f" # define FA_GLIDE "\ue350" # define FA_GLIDE_G "\ue351" # define FA_SIGN_LANGUAGE "\ue352" # define FA_LOW_VISION "\ue353" # define FA_VIADEO "\ue354" # define FA_VIADEO_SQUARE "\ue355" # define FA_SNAPCHAT "\ue356" # define FA_SNAPCHAT_GHOST "\ue357" # define FA_SNAPCHAT_SQUARE "\ue358" # define FA_PIED_PIPER "\ue359" # define FA_FIRST_ORDER "\ue35a" # define FA_YOAST "\ue35b" # define FA_THEMEISLE "\ue35c" # define FA_GOOGLE_PLUS_CIRCLE "\ue35d" # define FA_FA "\ue35e" # define FA_HANDSHAKE_O "\ue35f" # define FA_ENVELOPE_OPEN "\ue360" # define FA_ENVELOPE_OPEN_O "\ue361" # define FA_LINODE "\ue362" # define FA_ADDRESS_BOOK "\ue363" # define FA_ADDRESS_BOOK_O "\ue364" # define FA_ADDRESS_CARD "\ue365" # define FA_ADDRESS_CARD_O "\ue366" # define FA_USER_CIRCLE "\ue367" # define FA_USER_CIRCLE_O "\ue368" # define FA_USER_O "\ue369" # define FA_ID_BADGE "\ue36a" # define FA_DRIVERS_LICENSE "\ue36b" # define FA_DRIVERS_LICENSE_O "\ue36c" # define FA_QUORA "\ue36d" # define FA_FREE_CODE_CAMP "\ue36e" # define FA_TELEGRAM "\ue36f" # define FA_THERMOMETER "\ue370" # define FA_THERMOMETER_3 "\ue371" # define FA_THERMOMETER_2 "\ue372" # define FA_THERMOMETER_1 "\ue373" # define FA_THERMOMETER_0 "\ue374" # define FA_SHOWER "\ue375" # define FA_BATH "\ue376" # define FA_PODCAST "\ue377" # define FA_WINDOW_MAXIMIZE "\ue378" # define FA_WINDOW_MINIMIZE "\ue379" # define FA_WINDOW_RESTORE "\ue37a" # define FA_TIMES_RECTANGLE "\ue37b" # define FA_TIMES_RECTANGLE_O "\ue37c" # define FA_BANDCAMP "\ue37d" # define FA_GRAV "\ue37e" # define FA_ETSY "\ue37f" # define FA_IMDB "\ue380" # define FA_RAVELRY "\ue381" # define FA_EERCAST "\ue382" # define FA_MICROCHIP "\ue383" # define FA_SNOWFLAKE_O "\ue384" # define FA_SUPERPOWERS "\ue385" # define FA_WPEXPLORER "\ue386" # define FA_MEETUP "\ue387" # define MD_ERROR "\ue388" # define MD_ERROR_OUTLINE "\ue389" # define MD_WARNING "\ue38a" # define MD_ADD_ALERT "\ue38b" # define MD_ALBUM "\ue38c" # define MD_AV_TIMER "\ue38d" # define MD_CLOSED_CAPTION "\ue38e" # define MD_EQUALIZER "\ue38f" # define MD_EXPLICIT "\ue390" # define MD_FAST_FORWARD "\ue391" # define MD_FAST_REWIND "\ue392" # define MD_GAMES "\ue393" # define MD_HEARING "\ue394" # define MD_HIGH_QUALITY "\ue395" # define MD_LOOP "\ue396" # define MD_MIC "\ue397" # define MD_MIC_NONE "\ue398" # define MD_MIC_OFF "\ue399" # define MD_MOVIE "\ue39a" # define MD_LIBRARY_ADD "\ue39b" # define MD_LIBRARY_BOOKS "\ue39c" # define MD_LIBRARY_MUSIC "\ue39d" # define MD_NEW_RELEASES "\ue39e" # define MD_NOT_INTERESTED "\ue39f" # define MD_PAUSE "\ue3a0" # define MD_PAUSE_CIRCLE_FILLED "\ue3a1" # define MD_PAUSE_CIRCLE_OUTLINE "\ue3a2" # define MD_PLAY_ARROW "\ue3a3" # define MD_PLAY_CIRCLE_FILLED "\ue3a4" # define MD_PLAY_CIRCLE_OUTLINE "\ue3a5" # define MD_PLAYLIST_ADD "\ue3a6" # define MD_QUEUE "\ue3a7" # define MD_QUEUE_MUSIC "\ue3a8" # define MD_RADIO "\ue3a9" # define MD_RECENT_ACTORS "\ue3aa" # define MD_REPEAT "\ue3ab" # define MD_REPEAT_ONE "\ue3ac" # define MD_REPLAY "\ue3ad" # define MD_SHUFFLE "\ue3ae" # define MD_SKIP_NEXT "\ue3af" # define MD_SKIP_PREVIOUS "\ue3b0" # define MD_SNOOZE "\ue3b1" # define MD_STOP "\ue3b2" # define MD_SUBTITLES "\ue3b3" # define MD_SURROUND_SOUND "\ue3b4" # define MD_VIDEO_LIBRARY "\ue3b5" # define MD_VIDEOCAM "\ue3b6" # define MD_VIDEOCAM_OFF "\ue3b7" # define MD_VOLUME_DOWN "\ue3b8" # define MD_VOLUME_MUTE "\ue3b9" # define MD_VOLUME_OFF "\ue3ba" # define MD_VOLUME_UP "\ue3bb" # define MD_WEB "\ue3bc" # define MD_HD "\ue3bd" # define MD_SORT_BY_ALPHA "\ue3be" # define MD_AIRPLAY "\ue3bf" # define MD_FORWARD_10 "\ue3c0" # define MD_FORWARD_30 "\ue3c1" # define MD_FORWARD_5 "\ue3c2" # define MD_REPLAY_10 "\ue3c3" # define MD_REPLAY_30 "\ue3c4" # define MD_REPLAY_5 "\ue3c5" # define MD_ADD_TO_QUEUE "\ue3c6" # define MD_FIBER_DVR "\ue3c7" # define MD_FIBER_NEW "\ue3c8" # define MD_PLAYLIST_PLAY "\ue3c9" # define MD_ART_TRACK "\ue3ca" # define MD_FIBER_MANUAL_RECORD "\ue3cb" # define MD_FIBER_SMART_RECORD "\ue3cc" # define MD_MUSIC_VIDEO "\ue3cd" # define MD_SUBSCRIPTIONS "\ue3ce" # define MD_PLAYLIST_ADD_CHECK "\ue3cf" # define MD_QUEUE_PLAY_NEXT "\ue3d0" # define MD_REMOVE_FROM_QUEUE "\ue3d1" # define MD_SLOW_MOTION_VIDEO "\ue3d2" # define MD_WEB_ASSET "\ue3d3" # define MD_FIBER_PIN "\ue3d4" # define MD_BRANDING_WATERMARK "\ue3d5" # define MD_CALL_TO_ACTION "\ue3d6" # define MD_FEATURED_PLAY_LIST "\ue3d7" # define MD_FEATURED_VIDEO "\ue3d8" # define MD_NOTE "\ue3d9" # define MD_VIDEO_CALL "\ue3da" # define MD_VIDEO_LABEL "\ue3db" # define MD_BUSINESS "\ue3dc" # define MD_CALL "\ue3dd" # define MD_CALL_END "\ue3de" # define MD_CALL_MADE "\ue3df" # define MD_CALL_MERGE "\ue3e0" # define MD_CALL_MISSED "\ue3e1" # define MD_CALL_RECEIVED "\ue3e2" # define MD_CALL_SPLIT "\ue3e3" # define MD_CHAT "\ue3e4" # define MD_CLEAR_ALL "\ue3e5" # define MD_COMMENT "\ue3e6" # define MD_CONTACTS "\ue3e7" # define MD_DIALER_SIP "\ue3e8" # define MD_DIALPAD "\ue3e9" # define MD_EMAIL "\ue3ea" # define MD_FORUM "\ue3eb" # define MD_IMPORT_EXPORT "\ue3ec" # define MD_INVERT_COLORS_OFF "\ue3ed" # define MD_LIVE_HELP "\ue3ee" # define MD_LOCATION_OFF "\ue3ef" # define MD_LOCATION_ON "\ue3f0" # define MD_MESSAGE "\ue3f1" # define MD_CHAT_BUBBLE "\ue3f2" # define MD_CHAT_BUBBLE_OUTLINE "\ue3f3" # define MD_NO_SIM "\ue3f4" # define MD_PHONE "\ue3f5" # define MD_PORTABLE_WIFI_OFF "\ue3f6" # define MD_CONTACT_PHONE "\ue3f7" # define MD_CONTACT_MAIL "\ue3f8" # define MD_RING_VOLUME "\ue3f9" # define MD_SPEAKER_PHONE "\ue3fa" # define MD_STAY_CURRENT_LANDSCAPE "\ue3fb" # define MD_STAY_CURRENT_PORTRAIT "\ue3fc" # define MD_STAY_PRIMARY_LANDSCAPE "\ue3fd" # define MD_STAY_PRIMARY_PORTRAIT "\ue3fe" # define MD_SWAP_CALLS "\ue3ff" # define MD_TEXTSMS "\ue400" # define MD_VOICEMAIL "\ue401" # define MD_VPN_KEY "\ue402" # define MD_PHONELINK_ERASE "\ue403" # define MD_PHONELINK_LOCK "\ue404" # define MD_PHONELINK_RING "\ue405" # define MD_PHONELINK_SETUP "\ue406" # define MD_PRESENT_TO_ALL "\ue407" # define MD_IMPORT_CONTACTS "\ue408" # define MD_MAIL_OUTLINE "\ue409" # define MD_SCREEN_SHARE "\ue40a" # define MD_STOP_SCREEN_SHARE "\ue40b" # define MD_CALL_MISSED_OUTGOING "\ue40c" # define MD_RSS_FEED "\ue40d" # define MD_ADD "\ue40e" # define MD_ADD_BOX "\ue40f" # define MD_ADD_CIRCLE "\ue410" # define MD_ADD_CIRCLE_OUTLINE "\ue411" # define MD_ARCHIVE "\ue412" # define MD_BACKSPACE "\ue413" # define MD_BLOCK "\ue414" # define MD_CLEAR "\ue415" # define MD_CONTENT_COPY "\ue416" # define MD_CONTENT_CUT "\ue417" # define MD_CONTENT_PASTE "\ue418" # define MD_CREATE "\ue419" # define MD_DRAFTS "\ue41a" # define MD_FILTER_LIST "\ue41b" # define MD_FLAG "\ue41c" # define MD_FORWARD "\ue41d" # define MD_GESTURE "\ue41e" # define MD_INBOX "\ue41f" # define MD_LINK "\ue420" # define MD_MAIL "\ue421" # define MD_MARKUNREAD "\ue422" # define MD_REDO "\ue423" # define MD_REMOVE "\ue424" # define MD_REMOVE_CIRCLE "\ue425" # define MD_REMOVE_CIRCLE_OUTLINE "\ue426" # define MD_REPLY "\ue427" # define MD_REPLY_ALL "\ue428" # define MD_REPORT "\ue429" # define MD_SAVE "\ue42a" # define MD_SELECT_ALL "\ue42b" # define MD_SEND "\ue42c" # define MD_SORT "\ue42d" # define MD_TEXT_FORMAT "\ue42e" # define MD_UNDO "\ue42f" # define MD_FONT_DOWNLOAD "\ue430" # define MD_MOVE_TO_INBOX "\ue431" # define MD_UNARCHIVE "\ue432" # define MD_NEXT_WEEK "\ue433" # define MD_WEEKEND "\ue434" # define MD_DELETE_SWEEP "\ue435" # define MD_LOW_PRIORITY "\ue436" # define MD_ACCESS_ALARM "\ue437" # define MD_ACCESS_ALARMS "\ue438" # define MD_ACCESS_TIME "\ue439" # define MD_ADD_ALARM "\ue43a" # define MD_AIRPLANEMODE_INACTIVE "\ue43b" # define MD_AIRPLANEMODE_ACTIVE "\ue43c" # define MD_BATTERY_ALERT "\ue43d" # define MD_BATTERY_CHARGING_FULL "\ue43e" # define MD_BATTERY_FULL "\ue43f" # define MD_BATTERY_STD "\ue440" # define MD_BATTERY_UNKNOWN "\ue441" # define MD_BLUETOOTH "\ue442" # define MD_BLUETOOTH_CONNECTED "\ue443" # define MD_BLUETOOTH_DISABLED "\ue444" # define MD_BLUETOOTH_SEARCHING "\ue445" # define MD_BRIGHTNESS_AUTO "\ue446" # define MD_BRIGHTNESS_HIGH "\ue447" # define MD_BRIGHTNESS_LOW "\ue448" # define MD_BRIGHTNESS_MEDIUM "\ue449" # define MD_DATA_USAGE "\ue44a" # define MD_DEVELOPER_MODE "\ue44b" # define MD_DEVICES "\ue44c" # define MD_DVR "\ue44d" # define MD_GPS_FIXED "\ue44e" # define MD_GPS_NOT_FIXED "\ue44f" # define MD_GPS_OFF "\ue450" # define MD_LOCATION_DISABLED "\ue451" # define MD_LOCATION_SEARCHING "\ue452" # define MD_GRAPHIC_EQ "\ue453" # define MD_NETWORK_CELL "\ue454" # define MD_NETWORK_WIFI "\ue455" # define MD_NFC "\ue456" # define MD_WALLPAPER "\ue457" # define MD_WIDGETS "\ue458" # define MD_SCREEN_LOCK_LANDSCAPE "\ue459" # define MD_SCREEN_LOCK_PORTRAIT "\ue45a" # define MD_SCREEN_LOCK_ROTATION "\ue45b" # define MD_SCREEN_ROTATION "\ue45c" # define MD_SD_STORAGE "\ue45d" # define MD_SETTINGS_SYSTEM_DAYDREAM "\ue45e" # define MD_SIGNAL_CELLULAR_4_BAR "\ue45f" # define MD_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_4_BAR "\ue460" # define MD_SIGNAL_CELLULAR_NO_SIM "\ue461" # define MD_SIGNAL_CELLULAR_NULL "\ue462" # define MD_SIGNAL_CELLULAR_OFF "\ue463" # define MD_SIGNAL_WIFI_4_BAR "\ue464" # define MD_SIGNAL_WIFI_4_BAR_LOCK "\ue465" # define MD_SIGNAL_WIFI_OFF "\ue466" # define MD_STORAGE "\ue467" # define MD_USB "\ue468" # define MD_WIFI_LOCK "\ue469" # define MD_WIFI_TETHERING "\ue46a" # define MD_ATTACH_FILE "\ue46b" # define MD_ATTACH_MONEY "\ue46c" # define MD_BORDER_ALL "\ue46d" # define MD_BORDER_BOTTOM "\ue46e" # define MD_BORDER_CLEAR "\ue46f" # define MD_BORDER_COLOR "\ue470" # define MD_BORDER_HORIZONTAL "\ue471" # define MD_BORDER_INNER "\ue472" # define MD_BORDER_LEFT "\ue473" # define MD_BORDER_OUTER "\ue474" # define MD_BORDER_RIGHT "\ue475" # define MD_BORDER_STYLE "\ue476" # define MD_BORDER_TOP "\ue477" # define MD_BORDER_VERTICAL "\ue478" # define MD_FORMAT_ALIGN_CENTER "\ue479" # define MD_FORMAT_ALIGN_JUSTIFY "\ue47a" # define MD_FORMAT_ALIGN_LEFT "\ue47b" # define MD_FORMAT_ALIGN_RIGHT "\ue47c" # define MD_FORMAT_BOLD "\ue47d" # define MD_FORMAT_CLEAR "\ue47e" # define MD_FORMAT_COLOR_FILL "\ue47f" # define MD_FORMAT_COLOR_RESET "\ue480" # define MD_FORMAT_COLOR_TEXT "\ue481" # define MD_FORMAT_INDENT_DECREASE "\ue482" # define MD_FORMAT_INDENT_INCREASE "\ue483" # define MD_FORMAT_ITALIC "\ue484" # define MD_FORMAT_LINE_SPACING "\ue485" # define MD_FORMAT_LIST_BULLETED "\ue486" # define MD_FORMAT_LIST_NUMBERED "\ue487" # define MD_FORMAT_PAINT "\ue488" # define MD_FORMAT_QUOTE "\ue489" # define MD_FORMAT_SIZE "\ue48a" # define MD_FORMAT_STRIKETHROUGH "\ue48b" # define MD_FORMAT_TEXTDIRECTION_L_TO_R "\ue48c" # define MD_FORMAT_TEXTDIRECTION_R_TO_L "\ue48d" # define MD_FORMAT_UNDERLINED "\ue48e" # define MD_FUNCTIONS "\ue48f" # define MD_INSERT_CHART "\ue490" # define MD_INSERT_COMMENT "\ue491" # define MD_INSERT_DRIVE_FILE "\ue492" # define MD_INSERT_EMOTICON "\ue493" # define MD_INSERT_INVITATION "\ue494" # define MD_INSERT_LINK "\ue495" # define MD_INSERT_PHOTO "\ue496" # define MD_MERGE_TYPE "\ue497" # define MD_MODE_COMMENT "\ue498" # define MD_MODE_EDIT "\ue499" # define MD_PUBLISH "\ue49a" # define MD_SPACE_BAR "\ue49b" # define MD_STRIKETHROUGH_S "\ue49c" # define MD_VERTICAL_ALIGN_BOTTOM "\ue49d" # define MD_VERTICAL_ALIGN_CENTER "\ue49e" # define MD_VERTICAL_ALIGN_TOP "\ue49f" # define MD_WRAP_TEXT "\ue4a0" # define MD_MONEY_OFF "\ue4a1" # define MD_DRAG_HANDLE "\ue4a2" # define MD_FORMAT_SHAPES "\ue4a3" # define MD_HIGHLIGHT "\ue4a4" # define MD_LINEAR_SCALE "\ue4a5" # define MD_SHORT_TEXT "\ue4a6" # define MD_TEXT_FIELDS "\ue4a7" # define MD_MONETIZATION_ON "\ue4a8" # define MD_TITLE "\ue4a9" # define MD_ATTACHMENT "\ue4aa" # define MD_CLOUD "\ue4ab" # define MD_CLOUD_CIRCLE "\ue4ac" # define MD_CLOUD_DONE "\ue4ad" # define MD_CLOUD_DOWNLOAD "\ue4ae" # define MD_CLOUD_OFF "\ue4af" # define MD_CLOUD_QUEUE "\ue4b0" # define MD_CLOUD_UPLOAD "\ue4b1" # define MD_FILE_DOWNLOAD "\ue4b2" # define MD_FILE_UPLOAD "\ue4b3" # define MD_FOLDER "\ue4b4" # define MD_FOLDER_OPEN "\ue4b5" # define MD_FOLDER_SHARED "\ue4b6" # define MD_CREATE_NEW_FOLDER "\ue4b7" # define MD_CAST "\ue4b8" # define MD_CAST_CONNECTED "\ue4b9" # define MD_COMPUTER "\ue4ba" # define MD_DESKTOP_MAC "\ue4bb" # define MD_DESKTOP_WINDOWS "\ue4bc" # define MD_DEVELOPER_BOARD "\ue4bd" # define MD_DOCK "\ue4be" # define MD_GAMEPAD "\ue4bf" # define MD_HEADSET "\ue4c0" # define MD_HEADSET_MIC "\ue4c1" # define MD_KEYBOARD "\ue4c2" # define MD_KEYBOARD_ARROW_DOWN "\ue4c3" # define MD_KEYBOARD_ARROW_LEFT "\ue4c4" # define MD_KEYBOARD_ARROW_RIGHT "\ue4c5" # define MD_KEYBOARD_ARROW_UP "\ue4c6" # define MD_KEYBOARD_BACKSPACE "\ue4c7" # define MD_KEYBOARD_CAPSLOCK "\ue4c8" # define MD_KEYBOARD_HIDE "\ue4c9" # define MD_KEYBOARD_RETURN "\ue4ca" # define MD_KEYBOARD_TAB "\ue4cb" # define MD_KEYBOARD_VOICE "\ue4cc" # define MD_LAPTOP "\ue4cd" # define MD_LAPTOP_CHROMEBOOK "\ue4ce" # define MD_LAPTOP_MAC "\ue4cf" # define MD_LAPTOP_WINDOWS "\ue4d0" # define MD_MEMORY "\ue4d1" # define MD_MOUSE "\ue4d2" # define MD_PHONE_ANDROID "\ue4d3" # define MD_PHONE_IPHONE "\ue4d4" # define MD_PHONELINK "\ue4d5" # define MD_PHONELINK_OFF "\ue4d6" # define MD_ROUTER "\ue4d7" # define MD_SCANNER "\ue4d8" # define MD_SECURITY "\ue4d9" # define MD_SIM_CARD "\ue4da" # define MD_SMARTPHONE "\ue4db" # define MD_SPEAKER "\ue4dc" # define MD_SPEAKER_GROUP "\ue4dd" # define MD_TABLET "\ue4de" # define MD_TABLET_ANDROID "\ue4df" # define MD_TABLET_MAC "\ue4e0" # define MD_TOYS "\ue4e1" # define MD_TV "\ue4e2" # define MD_WATCH "\ue4e3" # define MD_DEVICE_HUB "\ue4e4" # define MD_POWER_INPUT "\ue4e5" # define MD_DEVICES_OTHER "\ue4e6" # define MD_VIDEOGAME_ASSET "\ue4e7" # define MD_ADD_TO_PHOTOS "\ue4e8" # define MD_ADJUST "\ue4e9" # define MD_ASSISTANT "\ue4ea" # define MD_ASSISTANT_PHOTO "\ue4eb" # define MD_AUDIOTRACK "\ue4ec" # define MD_BLUR_CIRCULAR "\ue4ed" # define MD_BLUR_LINEAR "\ue4ee" # define MD_BLUR_OFF "\ue4ef" # define MD_BLUR_ON "\ue4f0" # define MD_BRIGHTNESS_1 "\ue4f1" # define MD_BRIGHTNESS_2 "\ue4f2" # define MD_BRIGHTNESS_3 "\ue4f3" # define MD_BRIGHTNESS_4 "\ue4f4" # define MD_BRIGHTNESS_5 "\ue4f5" # define MD_BRIGHTNESS_6 "\ue4f6" # define MD_BRIGHTNESS_7 "\ue4f7" # define MD_BROKEN_IMAGE "\ue4f8" # define MD_BRUSH "\ue4f9" # define MD_CAMERA "\ue4fa" # define MD_CAMERA_ALT "\ue4fb" # define MD_CAMERA_FRONT "\ue4fc" # define MD_CAMERA_REAR "\ue4fd" # define MD_CAMERA_ROLL "\ue4fe" # define MD_CENTER_FOCUS_STRONG "\ue4ff" # define MD_CENTER_FOCUS_WEAK "\ue500" # define MD_COLLECTIONS "\ue501" # define MD_COLOR_LENS "\ue502" # define MD_COLORIZE "\ue503" # define MD_COMPARE "\ue504" # define MD_CONTROL_POINT "\ue505" # define MD_CONTROL_POINT_DUPLICATE "\ue506" # define MD_CROP_16_9 "\ue507" # define MD_CROP_3_2 "\ue508" # define MD_CROP "\ue509" # define MD_CROP_5_4 "\ue50a" # define MD_CROP_7_5 "\ue50b" # define MD_CROP_DIN "\ue50c" # define MD_CROP_FREE "\ue50d" # define MD_CROP_LANDSCAPE "\ue50e" # define MD_CROP_ORIGINAL "\ue50f" # define MD_CROP_PORTRAIT "\ue510" # define MD_CROP_SQUARE "\ue511" # define MD_DEHAZE "\ue512" # define MD_DETAILS "\ue513" # define MD_EDIT "\ue514" # define MD_EXPOSURE "\ue515" # define MD_EXPOSURE_NEG_1 "\ue516" # define MD_EXPOSURE_NEG_2 "\ue517" # define MD_EXPOSURE_PLUS_1 "\ue518" # define MD_EXPOSURE_PLUS_2 "\ue519" # define MD_EXPOSURE_ZERO "\ue51a" # define MD_FILTER_1 "\ue51b" # define MD_FILTER_2 "\ue51c" # define MD_FILTER_3 "\ue51d" # define MD_FILTER "\ue51e" # define MD_FILTER_4 "\ue51f" # define MD_FILTER_5 "\ue520" # define MD_FILTER_6 "\ue521" # define MD_FILTER_7 "\ue522" # define MD_FILTER_8 "\ue523" # define MD_FILTER_9 "\ue524" # define MD_FILTER_9_PLUS "\ue525" # define MD_FILTER_B_AND_W "\ue526" # define MD_FILTER_CENTER_FOCUS "\ue527" # define MD_FILTER_DRAMA "\ue528" # define MD_FILTER_FRAMES "\ue529" # define MD_FILTER_HDR "\ue52a" # define MD_FILTER_NONE "\ue52b" # define MD_FILTER_TILT_SHIFT "\ue52c" # define MD_FILTER_VINTAGE "\ue52d" # define MD_FLARE "\ue52e" # define MD_FLASH_AUTO "\ue52f" # define MD_FLASH_OFF "\ue530" # define MD_FLASH_ON "\ue531" # define MD_FLIP "\ue532" # define MD_GRADIENT "\ue533" # define MD_GRAIN "\ue534" # define MD_GRID_OFF "\ue535" # define MD_GRID_ON "\ue536" # define MD_HDR_OFF "\ue537" # define MD_HDR_ON "\ue538" # define MD_HDR_STRONG "\ue539" # define MD_HDR_WEAK "\ue53a" # define MD_HEALING "\ue53b" # define MD_IMAGE "\ue53c" # define MD_IMAGE_ASPECT_RATIO "\ue53d" # define MD_ISO "\ue53e" # define MD_LANDSCAPE "\ue53f" # define MD_LEAK_ADD "\ue540" # define MD_LEAK_REMOVE "\ue541" # define MD_LENS "\ue542" # define MD_LOOKS_3 "\ue543" # define MD_LOOKS "\ue544" # define MD_LOOKS_4 "\ue545" # define MD_LOOKS_5 "\ue546" # define MD_LOOKS_6 "\ue547" # define MD_LOOKS_ONE "\ue548" # define MD_LOOKS_TWO "\ue549" # define MD_LOUPE "\ue54a" # define MD_MONOCHROME_PHOTOS "\ue54b" # define MD_MOVIE_CREATION "\ue54c" # define MD_MUSIC_NOTE "\ue54d" # define MD_NATURE "\ue54e" # define MD_NATURE_PEOPLE "\ue54f" # define MD_NAVIGATE_BEFORE "\ue550" # define MD_NAVIGATE_NEXT "\ue551" # define MD_PALETTE "\ue552" # define MD_PANORAMA "\ue553" # define MD_PANORAMA_FISH_EYE "\ue554" # define MD_PANORAMA_HORIZONTAL "\ue555" # define MD_PANORAMA_VERTICAL "\ue556" # define MD_PANORAMA_WIDE_ANGLE "\ue557" # define MD_PHOTO "\ue558" # define MD_PHOTO_ALBUM "\ue559" # define MD_PHOTO_CAMERA "\ue55a" # define MD_PHOTO_LIBRARY "\ue55b" # define MD_PICTURE_AS_PDF "\ue55c" # define MD_PORTRAIT "\ue55d" # define MD_REMOVE_RED_EYE "\ue55e" # define MD_ROTATE_90_DEGREES_CCW "\ue55f" # define MD_ROTATE_LEFT "\ue560" # define MD_ROTATE_RIGHT "\ue561" # define MD_SLIDESHOW "\ue562" # define MD_STRAIGHTEN "\ue563" # define MD_STYLE "\ue564" # define MD_SWITCH_CAMERA "\ue565" # define MD_SWITCH_VIDEO "\ue566" # define MD_TAG_FACES "\ue567" # define MD_TEXTURE "\ue568" # define MD_TIMELAPSE "\ue569" # define MD_TIMER_10 "\ue56a" # define MD_TIMER_3 "\ue56b" # define MD_TIMER "\ue56c" # define MD_TIMER_OFF "\ue56d" # define MD_TONALITY "\ue56e" # define MD_TRANSFORM "\ue56f" # define MD_TUNE "\ue570" # define MD_VIEW_COMFY "\ue571" # define MD_VIEW_COMPACT "\ue572" # define MD_WB_AUTO "\ue573" # define MD_WB_CLOUDY "\ue574" # define MD_WB_INCANDESCENT "\ue575" # define MD_WB_SUNNY "\ue576" # define MD_COLLECTIONS_BOOKMARK "\ue577" # define MD_PHOTO_SIZE_SELECT_ACTUAL "\ue578" # define MD_PHOTO_SIZE_SELECT_LARGE "\ue579" # define MD_PHOTO_SIZE_SELECT_SMALL "\ue57a" # define MD_VIGNETTE "\ue57b" # define MD_WB_IRIDESCENT "\ue57c" # define MD_CROP_ROTATE "\ue57d" # define MD_LINKED_CAMERA "\ue57e" # define MD_ADD_A_PHOTO "\ue57f" # define MD_MOVIE_FILTER "\ue580" # define MD_PHOTO_FILTER "\ue581" # define MD_BURST_MODE "\ue582" # define MD_BEENHERE "\ue583" # define MD_DIRECTIONS "\ue584" # define MD_DIRECTIONS_BIKE "\ue585" # define MD_DIRECTIONS_BUS "\ue586" # define MD_DIRECTIONS_CAR "\ue587" # define MD_DIRECTIONS_BOAT "\ue588" # define MD_DIRECTIONS_SUBWAY "\ue589" # define MD_DIRECTIONS_RAILWAY "\ue58a" # define MD_DIRECTIONS_TRANSIT "\ue58b" # define MD_DIRECTIONS_WALK "\ue58c" # define MD_FLIGHT "\ue58d" # define MD_HOTEL "\ue58e" # define MD_LAYERS "\ue58f" # define MD_LAYERS_CLEAR "\ue590" # define MD_LOCAL_AIRPORT "\ue591" # define MD_LOCAL_ATM "\ue592" # define MD_LOCAL_ACTIVITY "\ue593" # define MD_LOCAL_BAR "\ue594" # define MD_LOCAL_CAFE "\ue595" # define MD_LOCAL_CAR_WASH "\ue596" # define MD_LOCAL_CONVENIENCE_STORE "\ue597" # define MD_LOCAL_DRINK "\ue598" # define MD_LOCAL_FLORIST "\ue599" # define MD_LOCAL_GAS_STATION "\ue59a" # define MD_LOCAL_GROCERY_STORE "\ue59b" # define MD_LOCAL_HOSPITAL "\ue59c" # define MD_LOCAL_HOTEL "\ue59d" # define MD_LOCAL_LAUNDRY_SERVICE "\ue59e" # define MD_LOCAL_LIBRARY "\ue59f" # define MD_LOCAL_MALL "\ue5a0" # define MD_LOCAL_MOVIES "\ue5a1" # define MD_LOCAL_OFFER "\ue5a2" # define MD_LOCAL_PARKING "\ue5a3" # define MD_LOCAL_PHARMACY "\ue5a4" # define MD_LOCAL_PHONE "\ue5a5" # define MD_LOCAL_PIZZA "\ue5a6" # define MD_LOCAL_PLAY "\ue5a7" # define MD_LOCAL_POST_OFFICE "\ue5a8" # define MD_LOCAL_PRINTSHOP "\ue5a9" # define MD_LOCAL_DINING "\ue5aa" # define MD_LOCAL_SEE "\ue5ab" # define MD_LOCAL_SHIPPING "\ue5ac" # define MD_LOCAL_TAXI "\ue5ad" # define MD_PERSON_PIN "\ue5ae" # define MD_MAP "\ue5af" # define MD_MY_LOCATION "\ue5b0" # define MD_NAVIGATION "\ue5b1" # define MD_PIN_DROP "\ue5b2" # define MD_PLACE "\ue5b3" # define MD_RATE_REVIEW "\ue5b4" # define MD_RESTAURANT_MENU "\ue5b5" # define MD_SATELLITE "\ue5b6" # define MD_STORE_MALL_DIRECTORY "\ue5b7" # define MD_TERRAIN "\ue5b8" # define MD_TRAFFIC "\ue5b9" # define MD_DIRECTIONS_RUN "\ue5ba" # define MD_ADD_LOCATION "\ue5bb" # define MD_EDIT_LOCATION "\ue5bc" # define MD_NEAR_ME "\ue5bd" # define MD_PERSON_PIN_CIRCLE "\ue5be" # define MD_ZOOM_OUT_MAP "\ue5bf" # define MD_RESTAURANT "\ue5c0" # define MD_EV_STATION "\ue5c1" # define MD_STREETVIEW "\ue5c2" # define MD_SUBWAY "\ue5c3" # define MD_TRAIN "\ue5c4" # define MD_TRAM "\ue5c5" # define MD_TRANSFER_WITHIN_A_STATION "\ue5c6" # define MD_APPS "\ue5c7" # define MD_ARROW_BACK "\ue5c8" # define MD_ARROW_DROP_DOWN "\ue5c9" # define MD_ARROW_DROP_DOWN_CIRCLE "\ue5ca" # define MD_ARROW_DROP_UP "\ue5cb" # define MD_ARROW_FORWARD "\ue5cc" # define MD_CANCEL "\ue5cd" # define MD_CHECK "\ue5ce" # define MD_CHEVRON_LEFT "\ue5cf" # define MD_CHEVRON_RIGHT "\ue5d0" # define MD_CLOSE "\ue5d1" # define MD_EXPAND_LESS "\ue5d2" # define MD_EXPAND_MORE "\ue5d3" # define MD_FULLSCREEN "\ue5d4" # define MD_FULLSCREEN_EXIT "\ue5d5" # define MD_MENU "\ue5d6" # define MD_MORE_HORIZ "\ue5d7" # define MD_MORE_VERT "\ue5d8" # define MD_REFRESH "\ue5d9" # define MD_UNFOLD_LESS "\ue5da" # define MD_UNFOLD_MORE "\ue5db" # define MD_ARROW_UPWARD "\ue5dc" # define MD_SUBDIRECTORY_ARROW_LEFT "\ue5dd" # define MD_SUBDIRECTORY_ARROW_RIGHT "\ue5de" # define MD_ARROW_DOWNWARD "\ue5df" # define MD_FIRST_PAGE "\ue5e0" # define MD_LAST_PAGE "\ue5e1" # define MD_ADB "\ue5e2" # define MD_BLUETOOTH_AUDIO "\ue5e3" # define MD_DISC_FULL "\ue5e4" # define MD_DO_NOT_DISTURB_ALT "\ue5e5" # define MD_DO_NOT_DISTURB "\ue5e6" # define MD_DRIVE_ETA "\ue5e7" # define MD_EVENT_AVAILABLE "\ue5e8" # define MD_EVENT_BUSY "\ue5e9" # define MD_EVENT_NOTE "\ue5ea" # define MD_FOLDER_SPECIAL "\ue5eb" # define MD_MMS "\ue5ec" # define MD_MORE "\ue5ed" # define MD_NETWORK_LOCKED "\ue5ee" # define MD_PHONE_BLUETOOTH_SPEAKER "\ue5ef" # define MD_PHONE_FORWARDED "\ue5f0" # define MD_PHONE_IN_TALK "\ue5f1" # define MD_PHONE_LOCKED "\ue5f2" # define MD_PHONE_MISSED "\ue5f3" # define MD_PHONE_PAUSED "\ue5f4" # define MD_SD_CARD "\ue5f5" # define MD_SIM_CARD_ALERT "\ue5f6" # define MD_SMS "\ue5f7" # define MD_SMS_FAILED "\ue5f8" # define MD_SYNC "\ue5f9" # define MD_SYNC_DISABLED "\ue5fa" # define MD_SYNC_PROBLEM "\ue5fb" # define MD_SYSTEM_UPDATE "\ue5fc" # define MD_TAP_AND_PLAY "\ue5fd" # define MD_TIME_TO_LEAVE "\ue5fe" # define MD_VIBRATION "\ue5ff" # define MD_VOICE_CHAT "\ue600" # define MD_VPN_LOCK "\ue601" # define MD_AIRLINE_SEAT_FLAT "\ue602" # define MD_AIRLINE_SEAT_FLAT_ANGLED "\ue603" # define MD_AIRLINE_SEAT_INDIVIDUAL_SUITE "\ue604" # define MD_AIRLINE_SEAT_LEGROOM_EXTRA "\ue605" # define MD_AIRLINE_SEAT_LEGROOM_NORMAL "\ue606" # define MD_AIRLINE_SEAT_LEGROOM_REDUCED "\ue607" # define MD_AIRLINE_SEAT_RECLINE_EXTRA "\ue608" # define MD_AIRLINE_SEAT_RECLINE_NORMAL "\ue609" # define MD_CONFIRMATION_NUMBER "\ue60a" # define MD_LIVE_TV "\ue60b" # define MD_ONDEMAND_VIDEO "\ue60c" # define MD_PERSONAL_VIDEO "\ue60d" # define MD_POWER "\ue60e" # define MD_WC "\ue60f" # define MD_WIFI "\ue610" # define MD_ENHANCED_ENCRYPTION "\ue611" # define MD_NETWORK_CHECK "\ue612" # define MD_NO_ENCRYPTION "\ue613" # define MD_RV_HOOKUP "\ue614" # define MD_DO_NOT_DISTURB_OFF "\ue615" # define MD_DO_NOT_DISTURB_ON "\ue616" # define MD_PRIORITY_HIGH "\ue617" # define MD_PIE_CHART "\ue618" # define MD_PIE_CHART_OUTLINED "\ue619" # define MD_BUBBLE_CHART "\ue61a" # define MD_MULTILINE_CHART "\ue61b" # define MD_SHOW_CHART "\ue61c" # define MD_CAKE "\ue61d" # define MD_DOMAIN "\ue61e" # define MD_GROUP "\ue61f" # define MD_GROUP_ADD "\ue620" # define MD_LOCATION_CITY "\ue621" # define MD_MOOD "\ue622" # define MD_MOOD_BAD "\ue623" # define MD_NOTIFICATIONS "\ue624" # define MD_NOTIFICATIONS_NONE "\ue625" # define MD_NOTIFICATIONS_OFF "\ue626" # define MD_NOTIFICATIONS_ACTIVE "\ue627" # define MD_NOTIFICATIONS_PAUSED "\ue628" # define MD_PAGES "\ue629" # define MD_PARTY_MODE "\ue62a" # define MD_PEOPLE "\ue62b" # define MD_PEOPLE_OUTLINE "\ue62c" # define MD_PERSON "\ue62d" # define MD_PERSON_ADD "\ue62e" # define MD_PERSON_OUTLINE "\ue62f" # define MD_PLUS_ONE "\ue630" # define MD_POLL "\ue631" # define MD_PUBLIC "\ue632" # define MD_SCHOOL "\ue633" # define MD_SHARE "\ue634" # define MD_WHATSHOT "\ue635" # define MD_SENTIMENT_DISSATISFIED "\ue636" # define MD_SENTIMENT_NEUTRAL "\ue637" # define MD_SENTIMENT_SATISFIED "\ue638" # define MD_SENTIMENT_VERY_DISSATISFIED "\ue639" # define MD_SENTIMENT_VERY_SATISFIED "\ue63a" # define MD_CHECK_BOX "\ue63b" # define MD_CHECK_BOX_OUTLINE_BLANK "\ue63c" # define MD_RADIO_BUTTON_UNCHECKED "\ue63d" # define MD_RADIO_BUTTON_CHECKED "\ue63e" # define MD_STAR "\ue63f" # define MD_STAR_HALF "\ue640" # define MD_STAR_BORDER "\ue641" # define MD_3D_ROTATION "\ue642" # define MD_ACCESSIBILITY "\ue643" # define MD_ACCOUNT_BALANCE "\ue644" # define MD_ACCOUNT_BALANCE_WALLET "\ue645" # define MD_ACCOUNT_BOX "\ue646" # define MD_ACCOUNT_CIRCLE "\ue647" # define MD_ADD_SHOPPING_CART "\ue648" # define MD_ALARM "\ue649" # define MD_ALARM_ADD "\ue64a" # define MD_ALARM_OFF "\ue64b" # define MD_ALARM_ON "\ue64c" # define MD_ANDROID "\ue64d" # define MD_ANNOUNCEMENT "\ue64e" # define MD_ASPECT_RATIO "\ue64f" # define MD_ASSESSMENT "\ue650" # define MD_ASSIGNMENT "\ue651" # define MD_ASSIGNMENT_IND "\ue652" # define MD_ASSIGNMENT_LATE "\ue653" # define MD_ASSIGNMENT_RETURN "\ue654" # define MD_ASSIGNMENT_RETURNED "\ue655" # define MD_ASSIGNMENT_TURNED_IN "\ue656" # define MD_AUTORENEW "\ue657" # define MD_BACKUP "\ue658" # define MD_BOOK "\ue659" # define MD_BOOKMARK "\ue65a" # define MD_BOOKMARK_BORDER "\ue65b" # define MD_BUG_REPORT "\ue65c" # define MD_BUILD "\ue65d" # define MD_CACHED "\ue65e" # define MD_CHANGE_HISTORY "\ue65f" # define MD_CHECK_CIRCLE "\ue660" # define MD_CHROME_READER_MODE "\ue661" # define MD_CLASS "\ue662" # define MD_CODE "\ue663" # define MD_CREDIT_CARD "\ue664" # define MD_DASHBOARD "\ue665" # define MD_DELETE "\ue666" # define MD_DESCRIPTION "\ue667" # define MD_DNS "\ue668" # define MD_DONE "\ue669" # define MD_DONE_ALL "\ue66a" # define MD_EVENT "\ue66b" # define MD_EXIT_TO_APP "\ue66c" # define MD_EXPLORE "\ue66d" # define MD_EXTENSION "\ue66e" # define MD_FACE "\ue66f" # define MD_FAVORITE "\ue670" # define MD_FAVORITE_BORDER "\ue671" # define MD_FEEDBACK "\ue672" # define MD_FIND_IN_PAGE "\ue673" # define MD_FIND_REPLACE "\ue674" # define MD_FLIP_TO_BACK "\ue675" # define MD_FLIP_TO_FRONT "\ue676" # define MD_GET_APP "\ue677" # define MD_GRADE "\ue678" # define MD_GROUP_WORK "\ue679" # define MD_HELP "\ue67a" # define MD_HIGHLIGHT_OFF "\ue67b" # define MD_HISTORY "\ue67c" # define MD_HOME "\ue67d" # define MD_HOURGLASS_EMPTY "\ue67e" # define MD_HOURGLASS_FULL "\ue67f" # define MD_HTTPS "\ue680" # define MD_INFO "\ue681" # define MD_INFO_OUTLINE "\ue682" # define MD_INPUT "\ue683" # define MD_INVERT_COLORS "\ue684" # define MD_LABEL "\ue685" # define MD_LABEL_OUTLINE "\ue686" # define MD_LANGUAGE "\ue687" # define MD_LAUNCH "\ue688" # define MD_LIST "\ue689" # define MD_LOCK "\ue68a" # define MD_LOCK_OPEN "\ue68b" # define MD_LOCK_OUTLINE "\ue68c" # define MD_LOYALTY "\ue68d" # define MD_MARKUNREAD_MAILBOX "\ue68e" # define MD_NOTE_ADD "\ue68f" # define MD_OPEN_IN_BROWSER "\ue690" # define MD_OPEN_IN_NEW "\ue691" # define MD_OPEN_WITH "\ue692" # define MD_PAGEVIEW "\ue693" # define MD_PAYMENT "\ue694" # define MD_PERM_CAMERA_MIC "\ue695" # define MD_PERM_CONTACT_CALENDAR "\ue696" # define MD_PERM_DATA_SETTING "\ue697" # define MD_PERM_DEVICE_INFORMATION "\ue698" # define MD_PERM_IDENTITY "\ue699" # define MD_PERM_MEDIA "\ue69a" # define MD_PERM_PHONE_MSG "\ue69b" # define MD_PERM_SCAN_WIFI "\ue69c" # define MD_PICTURE_IN_PICTURE "\ue69d" # define MD_POLYMER "\ue69e" # define MD_POWER_SETTINGS_NEW "\ue69f" # define MD_PRINT "\ue6a0" # define MD_QUERY_BUILDER "\ue6a1" # define MD_QUESTION_ANSWER "\ue6a2" # define MD_RECEIPT "\ue6a3" # define MD_REDEEM "\ue6a4" # define MD_REPORT_PROBLEM "\ue6a5" # define MD_RESTORE "\ue6a6" # define MD_ROOM "\ue6a7" # define MD_SCHEDULE "\ue6a8" # define MD_SEARCH "\ue6a9" # define MD_SETTINGS "\ue6aa" # define MD_SETTINGS_APPLICATIONS "\ue6ab" # define MD_SETTINGS_BACKUP_RESTORE "\ue6ac" # define MD_SETTINGS_BLUETOOTH "\ue6ad" # define MD_SETTINGS_CELL "\ue6ae" # define MD_SETTINGS_BRIGHTNESS "\ue6af" # define MD_SETTINGS_ETHERNET "\ue6b0" # define MD_SETTINGS_INPUT_ANTENNA "\ue6b1" # define MD_SETTINGS_INPUT_COMPONENT "\ue6b2" # define MD_SETTINGS_INPUT_COMPOSITE "\ue6b3" # define MD_SETTINGS_INPUT_HDMI "\ue6b4" # define MD_SETTINGS_INPUT_SVIDEO "\ue6b5" # define MD_SETTINGS_OVERSCAN "\ue6b6" # define MD_SETTINGS_PHONE "\ue6b7" # define MD_SETTINGS_POWER "\ue6b8" # define MD_SETTINGS_REMOTE "\ue6b9" # define MD_SETTINGS_VOICE "\ue6ba" # define MD_SHOP "\ue6bb" # define MD_SHOP_TWO "\ue6bc" # define MD_SHOPPING_BASKET "\ue6bd" # define MD_SHOPPING_CART "\ue6be" # define MD_SPEAKER_NOTES "\ue6bf" # define MD_SPELLCHECK "\ue6c0" # define MD_STARS "\ue6c1" # define MD_STORE "\ue6c2" # define MD_SUBJECT "\ue6c3" # define MD_SUPERVISOR_ACCOUNT "\ue6c4" # define MD_SWAP_HORIZ "\ue6c5" # define MD_SWAP_VERT "\ue6c6" # define MD_SWAP_VERTICAL_CIRCLE "\ue6c7" # define MD_SYSTEM_UPDATE_ALT "\ue6c8" # define MD_TAB "\ue6c9" # define MD_TAB_UNSELECTED "\ue6ca" # define MD_THEATERS "\ue6cb" # define MD_THUMB_DOWN "\ue6cc" # define MD_THUMB_UP "\ue6cd" # define MD_THUMBS_UP_DOWN "\ue6ce" # define MD_TOC "\ue6cf" # define MD_TODAY "\ue6d0" # define MD_TOLL "\ue6d1" # define MD_TRACK_CHANGES "\ue6d2" # define MD_TRANSLATE "\ue6d3" # define MD_TRENDING_DOWN "\ue6d4" # define MD_TRENDING_FLAT "\ue6d5" # define MD_TRENDING_UP "\ue6d6" # define MD_TURNED_IN "\ue6d7" # define MD_TURNED_IN_NOT "\ue6d8" # define MD_VERIFIED_USER "\ue6d9" # define MD_VIEW_AGENDA "\ue6da" # define MD_VIEW_ARRAY "\ue6db" # define MD_VIEW_CAROUSEL "\ue6dc" # define MD_VIEW_COLUMN "\ue6dd" # define MD_VIEW_DAY "\ue6de" # define MD_VIEW_HEADLINE "\ue6df" # define MD_VIEW_LIST "\ue6e0" # define MD_VIEW_MODULE "\ue6e1" # define MD_VIEW_QUILT "\ue6e2" # define MD_VIEW_STREAM "\ue6e3" # define MD_VIEW_WEEK "\ue6e4" # define MD_VISIBILITY "\ue6e5" # define MD_VISIBILITY_OFF "\ue6e6" # define MD_CARD_GIFTCARD "\ue6e7" # define MD_CARD_MEMBERSHIP "\ue6e8" # define MD_CARD_TRAVEL "\ue6e9" # define MD_WORK "\ue6ea" # define MD_YOUTUBE_SEARCHED_FOR "\ue6eb" # define MD_EJECT "\ue6ec" # define MD_CAMERA_ENHANCE "\ue6ed" # define MD_HELP_OUTLINE "\ue6ee" # define MD_REORDER "\ue6ef" # define MD_ZOOM_IN "\ue6f0" # define MD_ZOOM_OUT "\ue6f1" # define MD_HTTP "\ue6f2" # define MD_EVENT_SEAT "\ue6f3" # define MD_FLIGHT_LAND "\ue6f4" # define MD_FLIGHT_TAKEOFF "\ue6f5" # define MD_PLAY_FOR_WORK "\ue6f6" # define MD_GIF "\ue6f7" # define MD_INDETERMINATE_CHECK_BOX "\ue6f8" # define MD_OFFLINE_PIN "\ue6f9" # define MD_ALL_OUT "\ue6fa" # define MD_COPYRIGHT "\ue6fb" # define MD_FINGERPRINT "\ue6fc" # define MD_GAVEL "\ue6fd" # define MD_LIGHTBULB_OUTLINE "\ue6fe" # define MD_PICTURE_IN_PICTURE_ALT "\ue6ff" # define MD_IMPORTANT_DEVICES "\ue700" # define MD_TOUCH_APP "\ue701" # define MD_ACCESSIBLE "\ue702" # define MD_COMPARE_ARROWS "\ue703" # define MD_DATE_RANGE "\ue704" # define MD_DONUT_LARGE "\ue705" # define MD_DONUT_SMALL "\ue706" # define MD_LINE_STYLE "\ue707" # define MD_LINE_WEIGHT "\ue708" # define MD_MOTORCYCLE "\ue709" # define MD_OPACITY "\ue70a" # define MD_PETS "\ue70b" # define MD_PREGNANT_WOMAN "\ue70c" # define MD_RECORD_VOICE_OVER "\ue70d" # define MD_ROUNDED_CORNER "\ue70e" # define MD_ROWING "\ue70f" # define MD_TIMELINE "\ue710" # define MD_UPDATE "\ue711" # define MD_WATCH_LATER "\ue712" # define MD_PAN_TOOL "\ue713" # define MD_EURO_SYMBOL "\ue714" # define MD_G_TRANSLATE "\ue715" # define MD_REMOVE_SHOPPING_CART "\ue716" # define MD_RESTORE_PAGE "\ue717" # define MD_SPEAKER_NOTES_OFF "\ue718" # define MD_DELETE_FOREVER "\ue719" # define MD_AC_UNIT "\ue71a" # define MD_AIRPORT_SHUTTLE "\ue71b" # define MD_ALL_INCLUSIVE "\ue71c" # define MD_BEACH_ACCESS "\ue71d" # define MD_BUSINESS_CENTER "\ue71e" # define MD_CASINO "\ue71f" # define MD_CHILD_CARE "\ue720" # define MD_CHILD_FRIENDLY "\ue721" # define MD_FITNESS_CENTER "\ue722" # define MD_FREE_BREAKFAST "\ue723" # define MD_GOLF_COURSE "\ue724" # define MD_HOT_TUB "\ue725" # define MD_KITCHEN "\ue726" # define MD_POOL "\ue727" # define MD_ROOM_SERVICE "\ue728" # define MD_SMOKE_FREE "\ue729" # define MD_SMOKING_ROOMS "\ue72a" # define MD_SPA "\ue72b" # define MD_U10FFFD "\ue72c" # define FILE_REGEX "\ue72d" # define FILE_ARCH_LINUX "\ue72e" # define FILE_E "\ue72f" # define FILE_GLYPHS "\ue730" # define FILE_KNOCKOUT "\ue731" # define FILE_LEAN "\ue732" # define FILE_METAL "\ue733" # define FILE_POVRAY "\ue734" # define FILE_S "\ue735" # define FILE_TT "\ue736" # define FILE_VAGRANT "\ue737" # define FILE_XMOS "\ue738" # define FILE_A "\ue739" # define FILE_CHAI "\ue73a" # define FILE_STYLUS "\ue73b" # define FILE_TEXTILE "\ue73c" # define FILE_UNREAL "\ue73d" # define FILE_PUREBASIC "\ue73e" # define FILE_TS "\ue73f" # define FILE_SCHEME "\ue740" # define FILE_TEXTMATE "\ue741" # define FILE_X10 "\ue742" # define FILE_APL "\ue743" # define FILE_ANSIBLE "\ue744" # define FILE_ARTTEXT "\ue745" # define FILE_ANTWAR "\ue746" # define FILE_OPA "\ue747" # define FILE_CODECOV "\ue748" # define FILE_YANG "\ue749" # define FILE_PM2 "\ue74a" # define FILE_HG "\ue74b" # define FILE_PAWN "\ue74c" # define FILE_JULIA "\ue74d" # define FILE_SHIPIT "\ue74e" # define FILE_MOCHA "\ue74f" # define FILE_NIB "\ue750" # define FILE_SHURIKEN "\ue751" # define FILE_ALEX "\ue752" # define FILE_TWIG "\ue753" # define FILE_1C "\ue754" # define FILE_TEX "\ue755" # define FILE_BIBTEX "\ue756" # define FILE_MUSTACHE "\ue757" # define FILE_GULP "\ue758" # define FILE_GRUNT "\ue759" # define FILE_EMBER "\ue75a" # define FILE_GO "\ue75b" # define FILE_JENKINS "\ue75c" # define FILE_GNU "\ue75d" # define FILE_COMPOSER "\ue75e" # define FILE_METEOR "\ue75f" # define FILE_AI "\ue760" # define FILE_PSD "\ue761" # define FILE_SILVERSTRIPE "\ue762" # define FILE_MAXSCRIPT "\ue763" # define FILE_KIVY "\ue764" # define FILE_CRYSTAL "\ue765" # define FILE_GRADLE "\ue766" # define FILE_GROOVY "\ue767" # define FILE_R "\ue768" # define FILE_VUE "\ue769" # define FILE_HAXE "\ue76a" # define FILE_LISP "\ue76b" # define FILE_E909 "\ue76c" # define FILE_FORTRAN "\ue76d" # define FILE_ADA "\ue76e" # define FILE_DYALOG "\ue76f" # define FILE_JADE "\ue770" # define FILE_E90E "\ue771" # define FILE_FONT "\ue772" # define FILE_POSTCSS "\ue773" # define FILE_SCAD "\ue774" # define FILE_E912 "\ue775" # define FILE_RAML "\ue776" # define FILE_LS "\ue777" # define FILE_SALTSTACK "\ue778" # define FILE_TERRAFORM "\ue779" # define FILE_ORG "\ue77a" # define FILE_ASCIIDOC "\ue77b" # define FILE_RIOT "\ue77c" # define FILE_OCAML "\ue77d" # define FILE_LUA "\ue77e" # define FILE_NPM "\ue77f" # define FILE_LLVM "\ue780" # define FILE_E91E "\ue781" # define FILE_BABEL "\ue782" # define FILE_MARKO "\ue783" # define FILE_FLOW "\ue784" # define FILE_BROCCOLI "\ue785" # define FILE_APPVEYOR "\ue786" # define FILE_CAKEFILE "\ue787" # define FILE_APPLE "\ue788" # define FILE_EMACS "\ue789" # define FILE_SKETCH "\ue78a" # define FILE_DOXYGEN "\ue78b" # define FILE_CF "\ue78c" # define FILE_PASCAL "\ue78d" # define FILE_ABAP "\ue78e" # define FILE_ANTLR "\ue78f" # define FILE_API "\ue790" # define FILE_AS "\ue791" # define FILE_ARC "\ue792" # define FILE_ARDUINO "\ue793" # define FILE_AUGEAS "\ue794" # define FILE_AHK "\ue795" # define FILE_AUTOIT "\ue796" # define FILE_ATS "\ue797" # define FILE_ALLOY "\ue798" # define FILE_MANPAGE "\ue799" # define FILE_J "\ue79a" # define FILE_GLADE "\ue79b" # define FILE_BOO "\ue79c" # define FILE_BRAIN "\ue79d" # define FILE_BRO "\ue79e" # define FILE_BLUESPEC "\ue79f" # define FILE_STYLELINT "\ue7a0" # define FILE_ANT "\ue7a1" # define FILE_CMAKE "\ue7a2" # define FILE_CLIPS "\ue7a3" # define FILE_MAPBOX "\ue7a4" # define FILE_CP "\ue7a5" # define FILE_CHUCK "\ue7a6" # define FILE_JINJA "\ue7a7" # define FILE_ISABELLE "\ue7a8" # define FILE_DOGE "\ue7a9" # define FILE_IDL "\ue7aa" # define FILE_JAKE "\ue7ab" # define FILE_VERILOG "\ue7ac" # define FILE_PHALCON "\ue7ad" # define FILE_FABFILE "\ue7ae" # define FILE_LFE "\ue7af" # define FILE_NMAP "\ue7b0" # define FILE_AMPL "\ue7b1" # define FILE_CEYLON "\ue7b2" # define FILE_CHAPEL "\ue7b3" # define FILE_CIRRU "\ue7b4" # define FILE_CLARION "\ue7b5" # define FILE_NUNJUCKS "\ue7b6" # define FILE_MEDIAWIKI "\ue7b7" # define FILE_POSTSCRIPT "\ue7b8" # define FILE_TCL "\ue7b9" # define FILE_OWL "\ue7ba" # define FILE_JSONLD "\ue7bb" # define FILE_SPARQL "\ue7bc" # define FILE_SAS "\ue7bd" # define FILE_CLEAN "\ue7be" # define FILE_CLICK "\ue7bf" # define FILE_NVIDIA "\ue7c0" # define FILE_CREOLE "\ue7c1" # define FILE_COQ "\ue7c2" # define FILE_DIFF "\ue7c3" # define FILE_PATCH "\ue7c4" # define FILE_BYOND "\ue7c5" # define FILE_CYTHON "\ue7c6" # define FILE_DARCS "\ue7c7" # define FILE_EAGLE "\ue7c8" # define FILE_ECERE "\ue7c9" # define FILE_EIFFEL "\ue7ca" # define FILE_EM "\ue7cb" # define FILE_FLUX "\ue7cc" # define FILE_FACTOR "\ue7cd" # define FILE_FANCY "\ue7ce" # define FILE_PERL6 "\ue7cf" # define FILE_GENTOO "\ue7d0" # define FILE_FREGE "\ue7d1" # define FILE_FANTOM "\ue7d2" # define FILE_FREEMARKER "\ue7d3" # define FILE_GAP "\ue7d4" # define FILE_CL "\ue7d5" # define FILE_GAMS "\ue7d6" # define FILE_GODOT "\ue7d7" # define FILE_GML "\ue7d8" # define FILE_GENSHI "\ue7d9" # define FILE_POINTWISE "\ue7da" # define FILE_GF "\ue7db" # define FILE_GOLO "\ue7dc" # define FILE_GOSU "\ue7dd" # define FILE_HARBOUR "\ue7de" # define FILE_GRAPHQL "\ue7df" # define FILE_GRAPHVIZ "\ue7e0" # define FILE_HASHICORP "\ue7e1" # define FILE_HY "\ue7e2" # define FILE_IGORPRO "\ue7e3" # define FILE_IO "\ue7e4" # define FILE_IOKE "\ue7e5" # define FILE_IDRIS "\ue7e6" # define FILE_INFORM7 "\ue7e7" # define FILE_INNO "\ue7e8" # define FILE_SUBLIME "\ue7e9" # define FILE_JUPYTER "\ue7ea" # define FILE_KRL "\ue7eb" # define FILE_KOTLIN "\ue7ec" # define FILE_LABVIEW "\ue7ed" # define FILE_LSL "\ue7ee" # define FILE_LASSO "\ue7ef" # define FILE_LOGTALK "\ue7f0" # define FILE_LOOKML "\ue7f1" # define FILE_MAKO "\ue7f2" # define FILE_MATHEMATICA "\ue7f3" # define FILE_MATLAB "\ue7f4" # define FILE_E992 "\ue7f5" # define FILE_MAX "\ue7f6" # define FILE_MERCURY "\ue7f7" # define FILE_MIRAH "\ue7f8" # define FILE_MODULA2 "\ue7f9" # define FILE_MONKEY "\ue7fa" # define FILE_NIMROD "\ue7fb" # define FILE_NIT "\ue7fc" # define FILE_NIX "\ue7fd" # define FILE_AMX "\ue7fe" # define FILE_NETLOGO "\ue7ff" # define FILE_NUMPY "\ue800" # define FILE_OBJJ "\ue801" # define FILE_OPENCL "\ue802" # define FILE_PROCESSING "\ue803" # define FILE_OX "\ue804" # define FILE_SCD "\ue805" # define FILE_STATA "\ue806" # define FILE_STAN "\ue807" # define FILE_SQF "\ue808" # define FILE_SLASH "\ue809" # define FILE_SHEN "\ue80a" # define FILE_SELF "\ue80b" # define FILE_SCILAB "\ue80c" # define FILE_VHDL "\ue80d" # define FILE_SAGE "\ue80e" # define FILE_ROBOT "\ue80f" # define FILE_RED "\ue810" # define FILE_REBOL "\ue811" # define FILE_XOJO "\ue812" # define FILE_RDOC "\ue813" # define FILE_RACKET "\ue814" # define FILE_PURESCRIPT "\ue815" # define FILE_UNO "\ue816" # define FILE_VARNISH "\ue817" # define FILE_PROPELLER "\ue818" # define FILE_TURING "\ue819" # define FILE_PONY "\ue81a" # define FILE_POGO "\ue81b" # define FILE_PIKE "\ue81c" # define FILE_URWEB "\ue81d" # define FILE_PARROT "\ue81e" # define FILE_PAPYRUS "\ue81f" # define FILE_PAN "\ue820" # define FILE_OZ "\ue821" # define FILE_OXYGENE "\ue822" # define FILE_PROGRESS "\ue823" # define FILE_TXL "\ue824" # define FILE_CABAL "\ue825" # define FILE_SYSVERILOG "\ue826" # define FILE_PICKLE "\ue827" # define FILE_XPAGES "\ue828" # define FILE_XTEND "\ue829" # define FILE_ZEPHIR "\ue82a" # define FILE_ZIMPL "\ue82b" # define FILE_EC "\ue82c" # define FILE_MUPAD "\ue82d" # define FILE_OOC "\ue82e" # define FILE_RST "\ue82f" # define FILE_KARMA "\ue830" # define FILE_HACK "\ue831" # define FILE_SHOPIFY "\ue832" # define FILE_PUG_ALT "\ue833" # define FILE_E9D1 "\ue834" # define FILE_SBT "\ue835" # define FILE_E9D3 "\ue836" # define FILE_SCRUTINIZER "\ue837" # define FILE_CC "\ue838" # define FILE_BRAKEMAN "\ue839" # define FILE_NEWRELIC "\ue83a" # define FILE_THOR "\ue83b" # define FILE_NUGET "\ue83c" # define FILE_POWERSHELL "\ue83d" # define FILE_SF "\ue83e" # define FILE_MINECRAFT "\ue83f" # define FILE_SQLITE "\ue840" # define FILE_PROTRACTOR "\ue841" # define FILE_TYPINGS "\ue842" # define FILE_STRINGS "\ue843" # define FILE_NANT "\ue844" # define FILE_CSSCRIPT "\ue845" # define FILE_CAKE "\ue846" # define FILE_OPENOFFICE "\ue847" # define FILE_KEYNOTE "\ue848" # define FILE_JSX "\ue849" # define FILE_TSX "\ue84a" # define FILE_MODEL "\ue84b" # define FILE_FINDER "\ue84c" # define FILE_ACCESS "\ue84d" # define FILE_ONENOTE "\ue84e" # define FILE_POWERPOINT "\ue84f" # define FILE_WORD "\ue850" # define FILE_EXCEL "\ue851" # define FILE_STORYIST "\ue852" # define FILE_CSOUND "\ue853" # define FILE_DBASE "\ue854" # define FILE_ZBRUSH "\ue855" # define FILE_AE "\ue856" # define FILE_INDESIGN "\ue857" # define FILE_PREMIERE "\ue858" # define FILE_MAYA "\ue859" # define FILE_E9F7 "\ue85a" # define FILE_KHRONOS "\ue85b" # define FILE_AUDACITY "\ue85c" # define FILE_BLENDER "\ue85d" # define FILE_LIGHTWAVE "\ue85e" # define FILE_FBX "\ue85f" # define FILE_E9FD "\ue860" # define FILE_TYPEDOC "\ue861" # define FILE_ALPINE "\ue862" # define FILE_YUI "\ue863" # define FILE_EA01 "\ue864" # define FILE_EA02 "\ue865" # define FILE_EA03 "\ue866" # define FILE_NORMALIZE "\ue867" # define FILE_NEKO "\ue868" # define FILE_MATHJAX "\ue869" # define FILE_LEAFLET "\ue86a" # define FILE_GDB "\ue86b" # define FILE_FUELUX "\ue86c" # define FILE_EQ "\ue86d" # define FILE_CHARTJS "\ue86e" # define FILE_EA0C "\ue86f" # define FILE_EA0D "\ue870" # define FILE_EA0E "\ue871" # define FILE_ESLINT "\ue872" # define FILE_D3 "\ue873" # define FILE_CORDOVA "\ue874" # define FILE_CIRCLECI "\ue875" # define FILE_PUG "\ue876" # define FILE_POWERBUILDER "\ue877" # define FILE_DYLIB "\ue878" # define FILE_REXX "\ue879" # define FILE_SVN "\ue87a" # define FILE_MRUBY "\ue87b" # define FILE_WERCKER "\ue87c" # define FILE_YARN "\ue87d" # define FILE_EDITORCONFIG "\ue87e" # define FILE_SNYK "\ue87f" # define FILE_REASON "\ue880" # define FILE_NSIS "\ue881" # define FILE_V8 "\ue882" # define FILE_ROLLUP "\ue883" # define FILE_EA21 "\ue884" # define FILE_EA22 "\ue885" # define FILE_EA23 "\ue886" # define FILE_RASCAL "\ue887" # define FILE_GN "\ue888" # define FILE_NODEMON "\ue889" # define FILE_ELECTRON "\ue88a" # define FILE_1C_ALT "\ue88b" # define FILE_SWAGGER "\ue88c" # define FILE_BITHOUND "\ue88d" # define FILE_POLYMER "\ue88e" # define FILE_PLATFORMIO "\ue88f" # define FILE_SHIPPABLE "\ue890" # define FILE_EA2E "\ue891" # define FILE_SEQUELIZE "\ue892" # define FILE_REDUX "\ue893" # define FILE_RSPEC "\ue894" # define FILE_PHPUNIT "\ue895" # define FILE_OCTAVE "\ue896" # define FILE_NUCLIDE "\ue897" # define FILE_INFOPATH "\ue898" # define FILE_LIME "\ue899" # define FILE_LERNA "\ue89a" # define FILE_KITCHENCI "\ue89b" # define FILE_JEST "\ue89c" # define FILE_JASMINE "\ue89d" # define FILE_HAXEDEVELOP "\ue89e" # define FILE_GITLAB "\ue89f" # define FILE_DRONE "\ue8a0" # define FILE_VIRTUALBOX "\ue8a1" # define FILE_DOCLETS "\ue8a2" # define FILE_DELPHI "\ue8a3" # define FILE_CODEKIT "\ue8a4" # define FILE_CHEF "\ue8a5" # define FILE_CAKEPHP "\ue8a6" # define FILE_COBOL "\ue8a7" # define FILE_BUNDLER "\ue8a8" # define FILE_BUCK "\ue8a9" # define FILE_BRUNCH "\ue8aa" # define FILE_AURELIA "\ue8ab" # define FILE_VMWARE "\ue8ac" # define FILE_RHINO "\ue8ad" # define FILE_EJS "\ue8ae" # define FILE_KICAD "\ue8af" # define FILE_HOPLON "\ue8b0" # define FILE_ABIF "\ue8b1" # define FILE_WATCHMAN "\ue8b2" # define FILE_P4 "\ue8b3" # define FILE_NANOC "\ue8b4" # define FILE_MIRANDA "\ue8b5" # define FILE_MINIZINC "\ue8b6" # define FILE_MESON "\ue8b7" # define FILE_JISON "\ue8b8" # define FILE_FRANCA "\ue8b9" # define FILE_DEVICETREE "\ue8ba" # define FILE_CADDY "\ue8bb" # define FILE_BEM "\ue8bc" # define FILE_BAZEL "\ue8bd" # define FILE_ANGELSCRIPT "\ue8be" # define FILE_ESDOC "\ue8bf" # define FILE_TWINE "\ue8c0" # define FILE_SQUARESPACE "\ue8c1" # define FILE_PHOENIX "\ue8c2" # define FILE_TEST_DIR "\ue8c3" # define FILE_WEBPACK "\ue8c4" # define FILE_TEST_COFFEE "\ue8c5" # define FILE_TEST_GENERIC "\ue8c6" # define FILE_TEST_JS "\ue8c7" # define FILE_TEST_PERL "\ue8c8" # define FILE_TEST_PYTHON "\ue8c9" # define FILE_TEST_REACT "\ue8ca" # define FILE_TEST_RUBY "\ue8cb" # define FILE_TEST_TS "\ue8cc" # define FILE_CODESHIP "\ue8cd" # define FILE_NXC "\ue8ce" # define FILE_BROTLI "\ue8cf" # define FILE_PROSELINT "\ue8d0" # define FILE_BINTRAY "\ue8d1" # define FILE_MJML "\ue8d2" # define FILE_WASM "\ue8d3" # define FILE_EA71 "\ue8d4" # define FILE_NASM "\ue8d5" # define FILE_EA73 "\ue8d6" # define FILE_PEG "\ue8d7" # define FILE_JOLIE "\ue8d8" # define FILE_NANO "\ue8d9" # define FILE_XAMARIN "\ue8da" # define FILE_F012 "\ue8db" # define FILE_TAG "\ue8dc" # define FILE_CUCUMBER "\ue8dd" # define FILE_VIDEO "\ue8de" # define FILE_CONFIG "\ue8df" # define FILE_DASHBOARD "\ue8e0" # define FILE_PUPPET "\ue8e1" # define FILE_TERMINAL "\ue8e2" # define FILE_MARKDOWNLINT "\ue8e3" # define FILE_REACT "\ue8e4" # define FILE_F101 "\ue8e5" # define FILE_ELM "\ue8e6" # define FILE_BOOT "\ue8e7" # define FILE_CLJS "\ue8e8" # define FILE_LEIN "\ue8e9" # define FILE_DOCKER "\ue8ea" # define FILE_PHP "\ue8eb" # define FILE_IONIC "\ue8ec" # define FILE_HAML "\ue8ed" # define FILE_F17B "\ue8ee" # define FILE_FF "\ue8ef" # define FILE_U1F3C1 "\ue8f0" # define FILE_TERN "\ue8f1" # define FILE_DEFAULT "\ue8f2" # define FILE_SIGILS "\ue8f3" # define FILE_NGINX "\ue8f4" # define WEATHER_DAY_CLOUDY_GUSTS "\ue8f5" # define WEATHER_DAY_CLOUDY_WINDY "\ue8f6" # define WEATHER_DAY_CLOUDY "\ue8f7" # define WEATHER_DAY_FOG "\ue8f8" # define WEATHER_DAY_HAIL "\ue8f9" # define WEATHER_DAY_LIGHTNING "\ue8fa" # define WEATHER_DAY_RAIN_MIX "\ue8fb" # define WEATHER_DAY_RAIN_WIND "\ue8fc" # define WEATHER_DAY_RAIN "\ue8fd" # define WEATHER_DAY_SHOWERS "\ue8fe" # define WEATHER_DAY_SNOW "\ue8ff" # define WEATHER_DAY_SPRINKLE "\ue900" # define WEATHER_DAY_SUNNY_OVERCAST "\ue901" # define WEATHER_DAY_SUNNY "\ue902" # define WEATHER_DAY_STORM_SHOWERS "\ue903" # define WEATHER_DAY_THUNDERSTORM "\ue904" # define WEATHER_CLOUDY_GUSTS "\ue905" # define WEATHER_CLOUDY_WINDY "\ue906" # define WEATHER_CLOUDY "\ue907" # define WEATHER_FOG "\ue908" # define WEATHER_HAIL "\ue909" # define WEATHER_LIGHTNING "\ue90a" # define WEATHER_RAIN_MIX "\ue90b" # define WEATHER_RAIN_WIND "\ue90c" # define WEATHER_RAIN "\ue90d" # define WEATHER_SHOWERS "\ue90e" # define WEATHER_SNOW "\ue90f" # define WEATHER_SPRINKLE "\ue910" # define WEATHER_STORM_SHOWERS "\ue911" # define WEATHER_THUNDERSTORM "\ue912" # define WEATHER_WINDY "\ue913" # define WEATHER_NIGHT_ALT_CLOUDY_GUSTS "\ue914" # define WEATHER_NIGHT_ALT_CLOUDY_WINDY "\ue915" # define WEATHER_NIGHT_ALT_HAIL "\ue916" # define WEATHER_NIGHT_ALT_LIGHTNING "\ue917" # define WEATHER_NIGHT_ALT_RAIN_MIX "\ue918" # define WEATHER_NIGHT_ALT_RAIN_WIND "\ue919" # define WEATHER_NIGHT_ALT_RAIN "\ue91a" # define WEATHER_NIGHT_ALT_SHOWERS "\ue91b" # define WEATHER_NIGHT_ALT_SNOW "\ue91c" # define WEATHER_NIGHT_ALT_SPRINKLE "\ue91d" # define WEATHER_NIGHT_ALT_STORM_SHOWERS "\ue91e" # define WEATHER_NIGHT_ALT_THUNDERSTORM "\ue91f" # define WEATHER_NIGHT_CLEAR "\ue920" # define WEATHER_NIGHT_CLOUDY_GUSTS "\ue921" # define WEATHER_NIGHT_CLOUDY_WINDY "\ue922" # define WEATHER_NIGHT_CLOUDY "\ue923" # define WEATHER_NIGHT_HAIL "\ue924" # define WEATHER_NIGHT_LIGHTNING "\ue925" # define WEATHER_NIGHT_RAIN_MIX "\ue926" # define WEATHER_NIGHT_RAIN_WIND "\ue927" # define WEATHER_NIGHT_RAIN "\ue928" # define WEATHER_NIGHT_SHOWERS "\ue929" # define WEATHER_NIGHT_SNOW "\ue92a" # define WEATHER_NIGHT_SPRINKLE "\ue92b" # define WEATHER_NIGHT_STORM_SHOWERS "\ue92c" # define WEATHER_NIGHT_THUNDERSTORM "\ue92d" # define WEATHER_CELSIUS "\ue92e" # define WEATHER_CLOUD_DOWN "\ue92f" # define WEATHER_CLOUD_REFRESH "\ue930" # define WEATHER_CLOUD_UP "\ue931" # define WEATHER_CLOUD "\ue932" # define WEATHER_DEGREES "\ue933" # define WEATHER_DIRECTION_DOWN_LEFT "\ue934" # define WEATHER_DIRECTION_DOWN "\ue935" # define WEATHER_FAHRENHEIT "\ue936" # define WEATHER_HORIZON_ALT "\ue937" # define WEATHER_HORIZON "\ue938" # define WEATHER_DIRECTION_LEFT "\ue939" # define WEATHER_F049 "\ue93a" # define WEATHER_NIGHT_FOG "\ue93b" # define WEATHER_REFRESH_ALT "\ue93c" # define WEATHER_REFRESH "\ue93d" # define WEATHER_DIRECTION_RIGHT "\ue93e" # define WEATHER_RAINDROPS "\ue93f" # define WEATHER_STRONG_WIND "\ue940" # define WEATHER_SUNRISE "\ue941" # define WEATHER_SUNSET "\ue942" # define WEATHER_THERMOMETER_EXTERIOR "\ue943" # define WEATHER_THERMOMETER_INTERNAL "\ue944" # define WEATHER_THERMOMETER "\ue945" # define WEATHER_TORNADO "\ue946" # define WEATHER_DIRECTION_UP_RIGHT "\ue947" # define WEATHER_DIRECTION_UP "\ue948" # define WEATHER_F059 "\ue949" # define WEATHER_F05A "\ue94a" # define WEATHER_F05B "\ue94b" # define WEATHER_F05C "\ue94c" # define WEATHER_F05D "\ue94d" # define WEATHER_F05E "\ue94e" # define WEATHER_F060 "\ue94f" # define WEATHER_F061 "\ue950" # define WEATHER_SMOKE "\ue951" # define WEATHER_DUST "\ue952" # define WEATHER_SNOW_WIND "\ue953" # define WEATHER_DAY_SNOW_WIND "\ue954" # define WEATHER_NIGHT_SNOW_WIND "\ue955" # define WEATHER_NIGHT_ALT_SNOW_WIND "\ue956" # define WEATHER_DAY_SLEET_STORM "\ue957" # define WEATHER_NIGHT_SLEET_STORM "\ue958" # define WEATHER_NIGHT_ALT_SLEET_STORM "\ue959" # define WEATHER_DAY_SNOW_THUNDERSTORM "\ue95a" # define WEATHER_NIGHT_SNOW_THUNDERSTORM "\ue95b" # define WEATHER_NIGHT_ALT_SNOW_THUNDERSTORM "\ue95c" # define WEATHER_SOLAR_ECLIPSE "\ue95d" # define WEATHER_LUNAR_ECLIPSE "\ue95e" # define WEATHER_METEOR "\ue95f" # define WEATHER_HOT "\ue960" # define WEATHER_HURRICANE "\ue961" # define WEATHER_SMOG "\ue962" # define WEATHER_ALIEN "\ue963" # define WEATHER_SNOWFLAKE_COLD "\ue964" # define WEATHER_STARS "\ue965" # define WEATHER_RAINDROP "\ue966" # define WEATHER_BAROMETER "\ue967" # define WEATHER_HUMIDITY "\ue968" # define WEATHER_NA "\ue969" # define WEATHER_FLOOD "\ue96a" # define WEATHER_DAY_CLOUDY_HIGH "\ue96b" # define WEATHER_NIGHT_ALT_CLOUDY_HIGH "\ue96c" # define WEATHER_NIGHT_CLOUDY_HIGH "\ue96d" # define WEATHER_NIGHT_ALT_PARTLY_CLOUDY "\ue96e" # define WEATHER_SANDSTORM "\ue96f" # define WEATHER_NIGHT_PARTLY_CLOUDY "\ue970" # define WEATHER_UMBRELLA "\ue971" # define WEATHER_DAY_WINDY "\ue972" # define WEATHER_NIGHT_ALT_CLOUDY "\ue973" # define WEATHER_DIRECTION_UP_LEFT "\ue974" # define WEATHER_DIRECTION_DOWN_RIGHT "\ue975" # define WEATHER_TIME_12 "\ue976" # define WEATHER_TIME_1 "\ue977" # define WEATHER_TIME_2 "\ue978" # define WEATHER_TIME_3 "\ue979" # define WEATHER_TIME_4 "\ue97a" # define WEATHER_TIME_5 "\ue97b" # define WEATHER_TIME_6 "\ue97c" # define WEATHER_TIME_7 "\ue97d" # define WEATHER_TIME_8 "\ue97e" # define WEATHER_TIME_9 "\ue97f" # define WEATHER_TIME_10 "\ue980" # define WEATHER_TIME_11 "\ue981" # define WEATHER_MOON_NEW "\ue982" # define WEATHER_MOON_WAXING_CRESCENT_1 "\ue983" # define WEATHER_MOON_WAXING_CRESCENT_2 "\ue984" # define WEATHER_MOON_WAXING_CRESCENT_3 "\ue985" # define WEATHER_MOON_WAXING_CRESCENT_4 "\ue986" # define WEATHER_MOON_WAXING_CRESCENT_5 "\ue987" # define WEATHER_MOON_WAXING_CRESCENT_6 "\ue988" # define WEATHER_MOON_FIRST_QUARTER "\ue989" # define WEATHER_MOON_WAXING_GIBBOUS_1 "\ue98a" # define WEATHER_MOON_WAXING_GIBBOUS_2 "\ue98b" # define WEATHER_MOON_WAXING_GIBBOUS_3 "\ue98c" # define WEATHER_MOON_WAXING_GIBBOUS_4 "\ue98d" # define WEATHER_MOON_WAXING_GIBBOUS_5 "\ue98e" # define WEATHER_MOON_WAXING_GIBBOUS_6 "\ue98f" # define WEATHER_MOON_FULL "\ue990" # define WEATHER_MOON_WANING_GIBBOUS_1 "\ue991" # define WEATHER_MOON_WANING_GIBBOUS_2 "\ue992" # define WEATHER_MOON_WANING_GIBBOUS_3 "\ue993" # define WEATHER_MOON_WANING_GIBBOUS_4 "\ue994" # define WEATHER_MOON_WANING_GIBBOUS_5 "\ue995" # define WEATHER_MOON_WANING_GIBBOUS_6 "\ue996" # define WEATHER_MOON_THIRD_QUARTER "\ue997" # define WEATHER_MOON_WANING_CRESCENT_1 "\ue998" # define WEATHER_MOON_WANING_CRESCENT_2 "\ue999" # define WEATHER_MOON_WANING_CRESCENT_3 "\ue99a" # define WEATHER_MOON_WANING_CRESCENT_4 "\ue99b" # define WEATHER_MOON_WANING_CRESCENT_5 "\ue99c" # define WEATHER_MOON_WANING_CRESCENT_6 "\ue99d" # define WEATHER_WIND_DIRECTION "\ue99e" # define WEATHER_DAY_SLEET "\ue99f" # define WEATHER_NIGHT_SLEET "\ue9a0" # define WEATHER_NIGHT_ALT_SLEET "\ue9a1" # define WEATHER_SLEET "\ue9a2" # define WEATHER_DAY_HAZE "\ue9a3" # define WEATHER_WIND_BEAUFORT_0 "\ue9a4" # define WEATHER_WIND_BEAUFORT_1 "\ue9a5" # define WEATHER_WIND_BEAUFORT_2 "\ue9a6" # define WEATHER_WIND_BEAUFORT_3 "\ue9a7" # define WEATHER_WIND_BEAUFORT_4 "\ue9a8" # define WEATHER_WIND_BEAUFORT_5 "\ue9a9" # define WEATHER_WIND_BEAUFORT_6 "\ue9aa" # define WEATHER_WIND_BEAUFORT_7 "\ue9ab" # define WEATHER_WIND_BEAUFORT_8 "\ue9ac" # define WEATHER_WIND_BEAUFORT_9 "\ue9ad" # define WEATHER_WIND_BEAUFORT_10 "\ue9ae" # define WEATHER_WIND_BEAUFORT_11 "\ue9af" # define WEATHER_WIND_BEAUFORT_12 "\ue9b0" # define WEATHER_DAY_LIGHT_WIND "\ue9b1" # define WEATHER_TSUNAMI "\ue9b2" # define WEATHER_EARTHQUAKE "\ue9b3" # define WEATHER_FIRE "\ue9b4" # define WEATHER_VOLCANO "\ue9b5" # define WEATHER_MOONRISE "\ue9b6" # define WEATHER_MOONSET "\ue9b7" # define WEATHER_TRAIN "\ue9b8" # define WEATHER_SMALL_CRAFT_ADVISORY "\ue9b9" # define WEATHER_GALE_WARNING "\ue9ba" # define WEATHER_STORM_WARNING "\ue9bb" # define WEATHER_HURRICANE_WARNING "\ue9bc" # define WEATHER_MOON_ALT_WAXING_CRESCENT_1 "\ue9bd" # define WEATHER_MOON_ALT_WAXING_CRESCENT_2 "\ue9be" # define WEATHER_MOON_ALT_WAXING_CRESCENT_3 "\ue9bf" # define WEATHER_MOON_ALT_WAXING_CRESCENT_4 "\ue9c0" # define WEATHER_MOON_ALT_WAXING_CRESCENT_5 "\ue9c1" # define WEATHER_MOON_ALT_WAXING_CRESCENT_6 "\ue9c2" # define WEATHER_MOON_ALT_FIRST_QUARTER "\ue9c3" # define WEATHER_MOON_ALT_WAXING_GIBBOUS_1 "\ue9c4" # define WEATHER_MOON_ALT_WAXING_GIBBOUS_2 "\ue9c5" # define WEATHER_MOON_ALT_WAXING_GIBBOUS_3 "\ue9c6" # define WEATHER_MOON_ALT_WAXING_GIBBOUS_4 "\ue9c7" # define WEATHER_MOON_ALT_WAXING_GIBBOUS_5 "\ue9c8" # define WEATHER_MOON_ALT_WAXING_GIBBOUS_6 "\ue9c9" # define WEATHER_MOON_ALT_FULL "\ue9ca" # define WEATHER_MOON_ALT_WANING_GIBBOUS_1 "\ue9cb" # define WEATHER_MOON_ALT_WANING_GIBBOUS_2 "\ue9cc" # define WEATHER_MOON_ALT_WANING_GIBBOUS_3 "\ue9cd" # define WEATHER_MOON_ALT_WANING_GIBBOUS_4 "\ue9ce" # define WEATHER_MOON_ALT_WANING_GIBBOUS_5 "\ue9cf" # define WEATHER_MOON_ALT_WANING_GIBBOUS_6 "\ue9d0" # define WEATHER_MOON_ALT_THIRD_QUARTER "\ue9d1" # define WEATHER_MOON_ALT_WANING_CRESCENT_1 "\ue9d2" # define WEATHER_MOON_ALT_WANING_CRESCENT_2 "\ue9d3" # define WEATHER_MOON_ALT_WANING_CRESCENT_3 "\ue9d4" # define WEATHER_MOON_ALT_WANING_CRESCENT_4 "\ue9d5" # define WEATHER_MOON_ALT_WANING_CRESCENT_5 "\ue9d6" # define WEATHER_MOON_ALT_WANING_CRESCENT_6 "\ue9d7" # define WEATHER_MOON_ALT_NEW "\ue9d8" # define LINUX_ARCHLINUX "\ue9d9" # define LINUX_CENTOS "\ue9da" # define LINUX_DEBIAN "\ue9db" # define LINUX_FEDORA "\ue9dc" # define LINUX_LINUXMINT "\ue9dd" # define LINUX_LINUXMINT_INVERSE "\ue9de" # define LINUX_MAGEIA "\ue9df" # define LINUX_MANDRIVA "\ue9e0" # define LINUX_OPENSUSE "\ue9e1" # define LINUX_REDHAT "\ue9e2" # define LINUX_SLACKWARE "\ue9e3" # define LINUX_SLACKWARE_INVERSE "\ue9e4" # define LINUX_UBUNTU "\ue9e5" # define LINUX_UBUNTU_INVERSE "\ue9e6" # define LINUX_FREEBSD "\ue9e7" # define LINUX_COREOS "\ue9e8" # define LINUX_GENTOO "\ue9e9" # define LINUX_ELEMENTARY "\ue9ea" # define LINUX_FEDORA_INVERSE "\ue9eb" # define LINUX_SABAYON "\ue9ec" # define LINUX_AOSC "\ue9ed" # define LINUX_NIXOS "\ue9ee" # define LINUX_TUX "\ue9ef" # define LINUX_RASPBERRY_PI "\ue9f0" # define LINUX_MANJARO "\ue9f1" # define LINUX_APPLE "\ue9f2" # define LINUX_DOCKER "\ue9f3" # define LINUX_ALPINE "\ue9f4" # define MYICONS_0001 "\ue9f5" # define MYICONS_0002 "\ue9f6" # define MYICONS_0003 "\ue9f7" # define MYICONS_0004 "\ue9f8" # define MYICONS_0005 "\ue9f9" # define MYICONS_0006 "\ue9fa" # define MYICONS_0007 "\ue9fb" # define MYICONS_0008 "\ue9fc" # define MYICONS_0009 "\ue9fd" # define MYICONS_000A "\ue9fe" # define MYICONS_000B "\ue9ff" # define MYICONS_000D "\uea00" # define MYICONS_000E "\uea01" # define MYICONS_0010 "\uea02" # define MYICONS_0011 "\uea03" # define MYICONS_0013 "\uea04" # define MYICONS_0014 "\uea05" # define MYICONS_ARCH_LINUX_ARROW "\uea06" # define DEV_BING_SMALL "\uea07" # define DEV_CSS_TRICKS "\uea08" # define DEV_GIT "\uea09" # define DEV_BITBUCKET "\uea0a" # define DEV_MYSQL "\uea0b" # define DEV_STREAMLINE "\uea0c" # define DEV_DATABASE "\uea0d" # define DEV_DROPBOX "\uea0e" # define DEV_GITHUB_ALT "\uea0f" # define DEV_GITHUB_BADGE "\uea10" # define DEV_GITHUB "\uea11" # define DEV_WORDPRESS "\uea12" # define DEV_VISUALSTUDIO "\uea13" # define DEV_JEKYLL_SMALL "\uea14" # define DEV_ANDROID "\uea15" # define DEV_WINDOWS "\uea16" # define DEV_STACKOVERFLOW "\uea17" # define DEV_APPLE "\uea18" # define DEV_LINUX "\uea19" # define DEV_APPSTORE "\uea1a" # define DEV_GHOST_SMALL "\uea1b" # define DEV_YAHOO "\uea1c" # define DEV_CODEPEN "\uea1d" # define DEV_GITHUB_FULL "\uea1e" # define DEV_NODEJS_SMALL "\uea1f" # define DEV_NODEJS "\uea20" # define DEV_HACKERNEWS "\uea21" # define DEV_EMBER "\uea22" # define DEV_DOJO "\uea23" # define DEV_DJANGO "\uea24" # define DEV_NPM "\uea25" # define DEV_GHOST "\uea26" # define DEV_MODERNIZR "\uea27" # define DEV_UNITY_SMALL "\uea28" # define DEV_RASPBERRY_PI "\uea29" # define DEV_BLACKBERRY "\uea2a" # define DEV_GO "\uea2b" # define DEV_GIT_BRANCH "\uea2c" # define DEV_GIT_PULL_REQUEST "\uea2d" # define DEV_GIT_MERGE "\uea2e" # define DEV_GIT_COMPARE "\uea2f" # define DEV_GIT_COMMIT "\uea30" # define DEV_CSSDECK "\uea31" # define DEV_YAHOO_SMALL "\uea32" # define DEV_TECHCRUNCH "\uea33" # define DEV_SMASHING_MAGAZINE "\uea34" # define DEV_NETMAGAZINE "\uea35" # define DEV_CODROPS "\uea36" # define DEV_PHONEGAP "\uea37" # define DEV_GOOGLE_DRIVE "\uea38" # define DEV_HTML5_MULTIMEDIA "\uea39" # define DEV_HTML5_DEVICE_ACCESS "\uea3a" # define DEV_HTML5_CONNECTIVITY "\uea3b" # define DEV_HTML5_3D_EFFECTS "\uea3c" # define DEV_HTML5 "\uea3d" # define DEV_SCALA "\uea3e" # define DEV_JAVA "\uea3f" # define DEV_RUBY "\uea40" # define DEV_UBUNTU "\uea41" # define DEV_RUBY_ON_RAILS "\uea42" # define DEV_PYTHON "\uea43" # define DEV_PHP "\uea44" # define DEV_MARKDOWN "\uea45" # define DEV_LARAVEL "\uea46" # define DEV_MAGENTO "\uea47" # define DEV_JOOMLA "\uea48" # define DEV_DRUPAL "\uea49" # define DEV_CHROME "\uea4a" # define DEV_IE "\uea4b" # define DEV_FIREFOX "\uea4c" # define DEV_OPERA "\uea4d" # define DEV_BOOTSTRAP "\uea4e" # define DEV_SAFARI "\uea4f" # define DEV_CSS3 "\uea50" # define DEV_CSS3_FULL "\uea51" # define DEV_SASS "\uea52" # define DEV_GRUNT "\uea53" # define DEV_BOWER "\uea54" # define DEV_JAVASCRIPT "\uea55" # define DEV_JAVASCRIPT_SHIELD "\uea56" # define DEV_JQUERY "\uea57" # define DEV_COFFEESCRIPT "\uea58" # define DEV_BACKBONE "\uea59" # define DEV_ANGULAR "\uea5a" # define DEV_JQUERY_UI "\uea5b" # define DEV_SWIFT "\uea5c" # define DEV_SYMFONY "\uea5d" # define DEV_SYMFONY_BADGE "\uea5e" # define DEV_LESS "\uea5f" # define DEV_STYLUS "\uea60" # define DEV_TRELLO "\uea61" # define DEV_ATLASSIAN "\uea62" # define DEV_JIRA "\uea63" # define DEV_ENVATO "\uea64" # define DEV_SNAP_SVG "\uea65" # define DEV_RAPHAEL "\uea66" # define DEV_GOOGLE_ANALYTICS "\uea67" # define DEV_COMPASS "\uea68" # define DEV_ONEDRIVE "\uea69" # define DEV_GULP "\uea6a" # define DEV_ATOM "\uea6b" # define DEV_CISCO "\uea6c" # define DEV_NANCY "\uea6d" # define DEV_JENKINS "\uea6e" # define DEV_CLOJURE "\uea6f" # define DEV_PERL "\uea70" # define DEV_CLOJURE_ALT "\uea71" # define DEV_CELLULOID "\uea72" # define DEV_W3C "\uea73" # define DEV_REDIS "\uea74" # define DEV_POSTGRESQL "\uea75" # define DEV_WEBPLATFORM "\uea76" # define DEV_REQUIREJS "\uea77" # define DEV_OPENSOURCE "\uea78" # define DEV_TYPO3 "\uea79" # define DEV_UIKIT "\uea7a" # define DEV_DOCTRINE "\uea7b" # define DEV_GROOVY "\uea7c" # define DEV_NGINX "\uea7d" # define DEV_HASKELL "\uea7e" # define DEV_ZEND "\uea7f" # define DEV_GNU "\uea80" # define DEV_YEOMAN "\uea81" # define DEV_HEROKU "\uea82" # define DEV_MSQL_SERVER "\uea83" # define DEV_DEBIAN "\uea84" # define DEV_TRAVIS "\uea85" # define DEV_DOTNET "\uea86" # define DEV_CODEIGNITER "\uea87" # define DEV_JAVASCRIPT_BADGE "\uea88" # define DEV_YII "\uea89" # define DEV_COMPOSER "\uea8a" # define DEV_KRAKENJS_BADGE "\uea8b" # define DEV_KRAKENJS "\uea8c" # define DEV_MOZILLA "\uea8d" # define DEV_FIREBASE "\uea8e" # define DEV_SIZZLEJS "\uea8f" # define DEV_CREATIVECOMMONS "\uea90" # define DEV_CREATIVECOMMONS_BADGE "\uea91" # define DEV_MITLICENCE "\uea92" # define DEV_SENCHATOUCH "\uea93" # define DEV_BUGSENSE "\uea94" # define DEV_EXTJS "\uea95" # define DEV_MOOTOOLS_BADGE "\uea96" # define DEV_MOOTOOLS "\uea97" # define DEV_RUBY_ROUGH "\uea98" # define DEV_KOMODO "\uea99" # define DEV_CODA "\uea9a" # define DEV_BINTRAY "\uea9b" # define DEV_TERMINAL "\uea9c" # define DEV_CODE "\uea9d" # define DEV_RESPONSIVE "\uea9e" # define DEV_DART "\uea9f" # define DEV_APTANA "\ueaa0" # define DEV_MAILCHIMP "\ueaa1" # define DEV_NETBEANS "\ueaa2" # define DEV_DREAMWEAVER "\ueaa3" # define DEV_BRACKETS "\ueaa4" # define DEV_ECLIPSE "\ueaa5" # define DEV_CLOUD9 "\ueaa6" # define DEV_SCRUM "\ueaa7" # define DEV_PROLOG "\ueaa8" # define DEV_TERMINAL_BADGE "\ueaa9" # define DEV_CODE_BADGE "\ueaaa" # define DEV_MONGODB "\ueaab" # define DEV_METEOR "\ueaac" # define DEV_METEORFULL "\ueaad" # define DEV_FSHARP "\ueaae" # define DEV_RUST "\ueaaf" # define DEV_IONIC "\ueab0" # define DEV_SUBLIME "\ueab1" # define DEV_APPCELERATOR "\ueab2" # define DEV_ASTERISK "\ueab3" # define DEV_AWS "\ueab4" # define DEV_DIGITAL_OCEAN "\ueab5" # define DEV_DLANG "\ueab6" # define DEV_DOCKER "\ueab7" # define DEV_ERLANG "\ueab8" # define DEV_GOOGLE_CLOUD_PLATFORM "\ueab9" # define DEV_GRAILS "\ueaba" # define DEV_ILLUSTRATOR "\ueabb" # define DEV_INTELLIJ "\ueabc" # define DEV_MATERIALIZECSS "\ueabd" # define DEV_OPENSHIFT "\ueabe" # define DEV_PHOTOSHOP "\ueabf" # define DEV_RACKSPACE "\ueac0" # define DEV_REACT "\ueac1" # define DEV_REDHAT "\ueac2" # define DEV_SCRIPTCS "\ueac3" # define DEV_E6BD "\ueac4" # define DEV_E6BE "\ueac5" # define DEV_E6BF "\ueac6" # define DEV_E6C0 "\ueac7" # define DEV_E6C1 "\ueac8" # define DEV_E6C2 "\ueac9" # define DEV_E6C3 "\ueaca" # define DEV_SQLLITE "\ueacb" # define DEV_VIM "\ueacc" # define POM_CLEAN_CODE "\ueacd" # define POM_POMODORO_DONE "\ueace" # define POM_POMODORO_ESTIMATED "\ueacf" # define POM_POMODORO_TICKING "\uead0" # define POM_POMODORO_SQUASHED "\uead1" # define POM_SHORT_PAUSE "\uead2" # define POM_LONG_PAUSE "\uead3" # define POM_AWAY "\uead4" # define POM_PAIR_PROGRAMMING "\uead5" # define POM_INTERNAL_INTERRUPTION "\uead6" # define POM_EXTERNAL_INTERRUPTION "\uead7" # define LINEA_ARROWS_ANTICLOCKWISE "\uead8" # define LINEA_ARROWS_ANTICLOCKWISE_DASHED "\uead9" # define LINEA_ARROWS_BUTTON_DOWN "\ueada" # define LINEA_ARROWS_BUTTON_OFF "\ueadb" # define LINEA_ARROWS_BUTTON_ON "\ueadc" # define LINEA_ARROWS_BUTTON_UP "\ueadd" # define LINEA_ARROWS_CHECK "\ueade" # define LINEA_ARROWS_CIRCLE_CHECK "\ueadf" # define LINEA_ARROWS_CIRCLE_DOWN "\ueae0" # define LINEA_ARROWS_CIRCLE_DOWNLEFT "\ueae1" # define LINEA_ARROWS_CIRCLE_DOWNRIGHT "\ueae2" # define LINEA_ARROWS_CIRCLE_LEFT "\ueae3" # define LINEA_ARROWS_CIRCLE_MINUS "\ueae4" # define LINEA_ARROWS_CIRCLE_PLUS "\ueae5" # define LINEA_ARROWS_CIRCLE_REMOVE "\ueae6" # define LINEA_ARROWS_CIRCLE_RIGHT "\ueae7" # define LINEA_ARROWS_CIRCLE_UP "\ueae8" # define LINEA_ARROWS_CIRCLE_UPLEFT "\ueae9" # define LINEA_ARROWS_CIRCLE_UPRIGHT "\ueaea" # define LINEA_ARROWS_CLOCKWISE "\ueaeb" # define LINEA_ARROWS_CLOCKWISE_DASHED "\ueaec" # define LINEA_ARROWS_COMPRESS "\ueaed" # define LINEA_ARROWS_DENY "\ueaee" # define LINEA_ARROWS_DIAGONAL "\ueaef" # define LINEA_ARROWS_DIAGONAL2 "\ueaf0" # define LINEA_ARROWS_DOWN "\ueaf1" # define LINEA_ARROWS_DOWN_DOUBLE "\ueaf2" # define LINEA_ARROWS_DOWNLEFT "\ueaf3" # define LINEA_ARROWS_DOWNRIGHT "\ueaf4" # define LINEA_ARROWS_DRAG_DOWN "\ueaf5" # define LINEA_ARROWS_DRAG_DOWN_DASHED "\ueaf6" # define LINEA_ARROWS_DRAG_HORIZ "\ueaf7" # define LINEA_ARROWS_DRAG_LEFT "\ueaf8" # define LINEA_ARROWS_DRAG_LEFT_DASHED "\ueaf9" # define LINEA_ARROWS_DRAG_RIGHT "\ueafa" # define LINEA_ARROWS_DRAG_RIGHT_DASHED "\ueafb" # define LINEA_ARROWS_DRAG_UP "\ueafc" # define LINEA_ARROWS_DRAG_UP_DASHED "\ueafd" # define LINEA_ARROWS_DRAG_VERT "\ueafe" # define LINEA_ARROWS_EXCLAMATION "\ueaff" # define LINEA_ARROWS_EXPAND "\ueb00" # define LINEA_ARROWS_EXPAND_DIAGONAL1 "\ueb01" # define LINEA_ARROWS_EXPAND_HORIZONTAL1 "\ueb02" # define LINEA_ARROWS_EXPAND_VERTICAL1 "\ueb03" # define LINEA_ARROWS_FIT_HORIZONTAL "\ueb04" # define LINEA_ARROWS_FIT_VERTICAL "\ueb05" # define LINEA_ARROWS_GLIDE "\ueb06" # define LINEA_ARROWS_GLIDE_HORIZONTAL "\ueb07" # define LINEA_ARROWS_GLIDE_VERTICAL "\ueb08" # define LINEA_ARROWS_HAMBURGER1 "\ueb09" # define LINEA_ARROWS_HAMBURGER_2 "\ueb0a" # define LINEA_ARROWS_HORIZONTAL "\ueb0b" # define LINEA_ARROWS_INFO "\ueb0c" # define LINEA_ARROWS_KEYBOARD_ALT "\ueb0d" # define LINEA_ARROWS_KEYBOARD_CMD "\ueb0e" # define LINEA_ARROWS_KEYBOARD_DELETE "\ueb0f" # define LINEA_ARROWS_KEYBOARD_DOWN "\ueb10" # define LINEA_ARROWS_KEYBOARD_LEFT "\ueb11" # define LINEA_ARROWS_KEYBOARD_RETURN "\ueb12" # define LINEA_ARROWS_KEYBOARD_RIGHT "\ueb13" # define LINEA_ARROWS_KEYBOARD_SHIFT "\ueb14" # define LINEA_ARROWS_KEYBOARD_TAB "\ueb15" # define LINEA_ARROWS_KEYBOARD_UP "\ueb16" # define LINEA_ARROWS_LEFT "\ueb17" # define LINEA_ARROWS_LEFT_DOUBLE_32 "\ueb18" # define LINEA_ARROWS_MINUS "\ueb19" # define LINEA_ARROWS_MOVE "\ueb1a" # define LINEA_ARROWS_MOVE2 "\ueb1b" # define LINEA_ARROWS_MOVE_BOTTOM "\ueb1c" # define LINEA_ARROWS_MOVE_LEFT "\ueb1d" # define LINEA_ARROWS_MOVE_RIGHT "\ueb1e" # define LINEA_ARROWS_MOVE_TOP "\ueb1f" # define LINEA_ARROWS_PLUS "\ueb20" # define LINEA_ARROWS_QUESTION "\ueb21" # define LINEA_ARROWS_REMOVE "\ueb22" # define LINEA_ARROWS_RIGHT "\ueb23" # define LINEA_ARROWS_RIGHT_DOUBLE "\ueb24" # define LINEA_ARROWS_ROTATE "\ueb25" # define LINEA_ARROWS_ROTATE_ANTI "\ueb26" # define LINEA_ARROWS_ROTATE_ANTI_DASHED "\ueb27" # define LINEA_ARROWS_ROTATE_DASHED "\ueb28" # define LINEA_ARROWS_SHRINK "\ueb29" # define LINEA_ARROWS_SHRINK_DIAGONAL1 "\ueb2a" # define LINEA_ARROWS_SHRINK_DIAGONAL2 "\ueb2b" # define LINEA_ARROWS_SHRINK_HORIZONAL2 "\ueb2c" # define LINEA_ARROWS_SHRINK_HORIZONTAL1 "\ueb2d" # define LINEA_ARROWS_SHRINK_VERTICAL1 "\ueb2e" # define LINEA_ARROWS_SHRINK_VERTICAL2 "\ueb2f" # define LINEA_ARROWS_SIGN_DOWN "\ueb30" # define LINEA_ARROWS_SIGN_LEFT "\ueb31" # define LINEA_ARROWS_SIGN_RIGHT "\ueb32" # define LINEA_ARROWS_SIGN_UP "\ueb33" # define LINEA_ARROWS_SLIDE_DOWN1 "\ueb34" # define LINEA_ARROWS_SLIDE_DOWN2 "\ueb35" # define LINEA_ARROWS_SLIDE_LEFT1 "\ueb36" # define LINEA_ARROWS_SLIDE_LEFT2 "\ueb37" # define LINEA_ARROWS_SLIDE_RIGHT1 "\ueb38" # define LINEA_ARROWS_SLIDE_RIGHT2 "\ueb39" # define LINEA_ARROWS_SLIDE_UP1 "\ueb3a" # define LINEA_ARROWS_SLIDE_UP2 "\ueb3b" # define LINEA_ARROWS_SLIM_DOWN "\ueb3c" # define LINEA_ARROWS_SLIM_DOWN_DASHED "\ueb3d" # define LINEA_ARROWS_SLIM_LEFT "\ueb3e" # define LINEA_ARROWS_SLIM_LEFT_DASHED "\ueb3f" # define LINEA_ARROWS_SLIM_RIGHT "\ueb40" # define LINEA_ARROWS_SLIM_RIGHT_DASHED "\ueb41" # define LINEA_ARROWS_SLIM_UP "\ueb42" # define LINEA_ARROWS_SLIM_UP_DASHED "\ueb43" # define LINEA_ARROWS_SQUARE_CHECK "\ueb44" # define LINEA_ARROWS_SQUARE_DOWN "\ueb45" # define LINEA_ARROWS_SQUARE_DOWNLEFT "\ueb46" # define LINEA_ARROWS_SQUARE_DOWNRIGHT "\ueb47" # define LINEA_ARROWS_SQUARE_LEFT "\ueb48" # define LINEA_ARROWS_SQUARE_MINUS "\ueb49" # define LINEA_ARROWS_SQUARE_PLUS "\ueb4a" # define LINEA_ARROWS_SQUARE_REMOVE "\ueb4b" # define LINEA_ARROWS_SQUARE_RIGHT "\ueb4c" # define LINEA_ARROWS_SQUARE_UP "\ueb4d" # define LINEA_ARROWS_SQUARE_UPLEFT "\ueb4e" # define LINEA_ARROWS_SQUARE_UPRIGHT "\ueb4f" # define LINEA_ARROWS_SQUARES "\ueb50" # define LINEA_ARROWS_STRETCH_DIAGONAL1 "\ueb51" # define LINEA_ARROWS_STRETCH_DIAGONAL2 "\ueb52" # define LINEA_ARROWS_STRETCH_DIAGONAL3 "\ueb53" # define LINEA_ARROWS_STRETCH_DIAGONAL4 "\ueb54" # define LINEA_ARROWS_STRETCH_HORIZONTAL1 "\ueb55" # define LINEA_ARROWS_STRETCH_HORIZONTAL2 "\ueb56" # define LINEA_ARROWS_STRETCH_VERTICAL1 "\ueb57" # define LINEA_ARROWS_STRETCH_VERTICAL2 "\ueb58" # define LINEA_ARROWS_SWITCH_HORIZONTAL "\ueb59" # define LINEA_ARROWS_SWITCH_VERTICAL "\ueb5a" # define LINEA_ARROWS_UP "\ueb5b" # define LINEA_ARROWS_UP_DOUBLE_33 "\ueb5c" # define LINEA_ARROWS_UPLEFT "\ueb5d" # define LINEA_ARROWS_UPRIGHT "\ueb5e" # define LINEA_ARROWS_VERTICAL "\ueb5f" # define LINEA_BASIC_LOCK_OPEN "\ueb60" # define LINEA_BASIC_MAGIC_MOUSE "\ueb61" # define LINEA_BASIC_MAGNIFIER "\ueb62" # define LINEA_BASIC_MAGNIFIER_MINUS "\ueb63" # define LINEA_BASIC_MAGNIFIER_PLUS "\ueb64" # define LINEA_BASIC_MAIL "\ueb65" # define LINEA_BASIC_MAIL_MULTIPLE "\ueb66" # define LINEA_BASIC_MAIL_OPEN "\ueb67" # define LINEA_BASIC_MAIL_OPEN_TEXT "\ueb68" # define LINEA_BASIC_MALE "\ueb69" # define LINEA_BASIC_MAP "\ueb6a" # define LINEA_BASIC_MESSAGE "\ueb6b" # define LINEA_BASIC_MESSAGE_MULTIPLE "\ueb6c" # define LINEA_BASIC_MESSAGE_TXT "\ueb6d" # define LINEA_BASIC_MIXER2 "\ueb6e" # define LINEA_BASIC_INFO "\ueb6f" # define LINEA_BASIC_IPOD "\ueb70" # define LINEA_BASIC_JOYPAD "\ueb71" # define LINEA_BASIC_KEY "\ueb72" # define LINEA_BASIC_KEYBOARD "\ueb73" # define LINEA_BASIC_LAPTOP "\ueb74" # define LINEA_BASIC_LIFE_BUOY "\ueb75" # define LINEA_BASIC_LIGHTBULB "\ueb76" # define LINEA_BASIC_LINK "\ueb77" # define LINEA_BASIC_LOCK "\ueb78" # define LINEA_BASIC_MOUSE "\ueb79" # define LINEA_BASIC_NOTEBOOK "\ueb7a" # define LINEA_BASIC_NOTEBOOK_PEN "\ueb7b" # define LINEA_BASIC_NOTEBOOK_PENCIL "\ueb7c" # define LINEA_BASIC_PAPERPLANE "\ueb7d" # define LINEA_BASIC_PENCIL_RULER "\ueb7e" # define LINEA_BASIC_PENCIL_RULER_PEN "\ueb7f" # define LINEA_BASIC_CLUBS "\ueb80" # define LINEA_BASIC_COMPASS "\ueb81" # define LINEA_BASIC_CUP "\ueb82" # define LINEA_BASIC_DIAMONDS "\ueb83" # define LINEA_BASIC_DISPLAY "\ueb84" # define LINEA_BASIC_DOWNLOAD "\ueb85" # define LINEA_BASIC_EXCLAMATION "\ueb86" # define LINEA_BASIC_EYE "\ueb87" # define LINEA_BASIC_EYE_CLOSED "\ueb88" # define LINEA_BASIC_FEMALE "\ueb89" # define LINEA_BASIC_FLAG1 "\ueb8a" # define LINEA_BASIC_FLAG2 "\ueb8b" # define LINEA_BASIC_FLOPPYDISK "\ueb8c" # define LINEA_BASIC_FOLDER "\ueb8d" # define LINEA_BASIC_FOLDER_MULTIPLE "\ueb8e" # define LINEA_BASIC_GEAR "\ueb8f" # define LINEA_BASIC_GEOLOCALIZE_01 "\ueb90" # define LINEA_BASIC_GEOLOCALIZE_05 "\ueb91" # define LINEA_BASIC_GLOBE "\ueb92" # define LINEA_BASIC_GUNSIGHT "\ueb93" # define LINEA_BASIC_HAMMER "\ueb94" # define LINEA_BASIC_HEADSET "\ueb95" # define LINEA_BASIC_HEART "\ueb96" # define LINEA_BASIC_HEART_BROKEN "\ueb97" # define LINEA_BASIC_HELM "\ueb98" # define LINEA_BASIC_HOME "\ueb99" # define LINEA_BASIC_PHOTO "\ueb9a" # define LINEA_BASIC_RSS "\ueb9b" # define LINEA_BASIC_PICTURE "\ueb9c" # define LINEA_BASIC_PICTURE_MULTIPLE "\ueb9d" # define LINEA_BASIC_PIN1 "\ueb9e" # define LINEA_BASIC_PIN2 "\ueb9f" # define LINEA_BASIC_ACCELERATOR "\ueba0" # define LINEA_BASIC_ALARM "\ueba1" # define LINEA_BASIC_ANCHOR "\ueba2" # define LINEA_BASIC_ANTICLOCKWISE "\ueba3" # define LINEA_BASIC_ARCHIVE "\ueba4" # define LINEA_BASIC_ARCHIVE_FULL "\ueba5" # define LINEA_BASIC_BAN "\ueba6" # define LINEA_BASIC_BATTERY_CHARGE "\ueba7" # define LINEA_BASIC_BATTERY_EMPTY "\ueba8" # define LINEA_BASIC_BATTERY_FULL "\ueba9" # define LINEA_BASIC_BATTERY_HALF "\uebaa" # define LINEA_BASIC_BOLT "\uebab" # define LINEA_BASIC_BOOK "\uebac" # define LINEA_BASIC_BOOK_PEN "\uebad" # define LINEA_BASIC_BOOK_PENCIL "\uebae" # define LINEA_BASIC_BOOKMARK "\uebaf" # define LINEA_BASIC_CALCULATOR "\uebb0" # define LINEA_BASIC_CALENDAR "\uebb1" # define LINEA_BASIC_CARDS_DIAMONDS "\uebb2" # define LINEA_BASIC_CARDS_HEARTS "\uebb3" # define LINEA_BASIC_CASE "\uebb4" # define LINEA_BASIC_CHRONOMETER "\uebb5" # define LINEA_BASIC_CLESSIDRE "\uebb6" # define LINEA_BASIC_CLOCK "\uebb7" # define LINEA_BASIC_CLOCKWISE "\uebb8" # define LINEA_BASIC_CLOUD "\uebb9" # define LINEA_BASIC_POSTCARD "\uebba" # define LINEA_BASIC_POSTCARD_MULTIPLE "\uebbb" # define LINEA_BASIC_PRINTER "\uebbc" # define LINEA_BASIC_QUESTION "\uebbd" # define LINEA_BASIC_SERVER "\uebbe" # define LINEA_BASIC_SERVER2 "\uebbf" # define LINEA_BASIC_SERVER_CLOUD "\uebc0" # define LINEA_BASIC_SERVER_DOWNLOAD "\uebc1" # define LINEA_BASIC_SERVER_UPLOAD "\uebc2" # define LINEA_BASIC_SETTINGS "\uebc3" # define LINEA_BASIC_SHARE "\uebc4" # define LINEA_BASIC_SHEET "\uebc5" # define LINEA_BASIC_SHEET_MULTIPLE "\uebc6" # define LINEA_BASIC_SHEET_PEN "\uebc7" # define LINEA_BASIC_SHEET_PENCIL "\uebc8" # define LINEA_BASIC_SHEET_TXT "\uebc9" # define LINEA_BASIC_SIGNS "\uebca" # define LINEA_BASIC_SMARTPHONE "\uebcb" # define LINEA_BASIC_SPADES "\uebcc" # define LINEA_BASIC_SPREAD "\uebcd" # define LINEA_BASIC_SPREAD_BOOKMARK "\uebce" # define LINEA_BASIC_SPREAD_TEXT "\uebcf" # define LINEA_BASIC_SPREAD_TEXT_BOOKMARK "\uebd0" # define LINEA_BASIC_STAR "\uebd1" # define LINEA_BASIC_TABLET "\uebd2" # define LINEA_BASIC_TARGET "\uebd3" # define LINEA_BASIC_TODO "\uebd4" # define LINEA_BASIC_TODO_PEN "\uebd5" # define LINEA_BASIC_TODO_PENCIL "\uebd6" # define LINEA_BASIC_TODO_TXT "\uebd7" # define LINEA_BASIC_TODOLIST_PEN "\uebd8" # define LINEA_BASIC_TODOLIST_PENCIL "\uebd9" # define LINEA_BASIC_TRASHCAN "\uebda" # define LINEA_BASIC_TRASHCAN_FULL "\uebdb" # define LINEA_BASIC_TRASHCAN_REFRESH "\uebdc" # define LINEA_BASIC_TRASHCAN_REMOVE "\uebdd" # define LINEA_BASIC_UPLOAD "\uebde" # define LINEA_BASIC_USB "\uebdf" # define LINEA_BASIC_VIDEO "\uebe0" # define LINEA_BASIC_WATCH "\uebe1" # define LINEA_BASIC_WEBPAGE "\uebe2" # define LINEA_BASIC_WEBPAGE_IMG_TXT "\uebe3" # define LINEA_BASIC_WEBPAGE_MULTIPLE "\uebe4" # define LINEA_BASIC_WEBPAGE_TXT "\uebe5" # define LINEA_BASIC_WORLD "\uebe6" # define LINEA_ELABORATION_DOCUMENT_PREVIOUS "\uebe7" # define LINEA_ELABORATION_DOCUMENT_REFRESH "\uebe8" # define LINEA_ELABORATION_DOCUMENT_REMOVE "\uebe9" # define LINEA_ELABORATION_DOCUMENT_SEARCH "\uebea" # define LINEA_ELABORATION_DOCUMENT_STAR "\uebeb" # define LINEA_ELABORATION_DOCUMENT_UPLOAD "\uebec" # define LINEA_ELABORATION_FOLDER_CHECK "\uebed" # define LINEA_ELABORATION_FOLDER_CLOUD "\uebee" # define LINEA_ELABORATION_FOLDER_DOCUMENT "\uebef" # define LINEA_ELABORATION_FOLDER_DOWNLOAD "\uebf0" # define LINEA_ELABORATION_FOLDER_FLAGGED "\uebf1" # define LINEA_ELABORATION_FOLDER_GRAPH "\uebf2" # define LINEA_ELABORATION_FOLDER_HEART "\uebf3" # define LINEA_ELABORATION_FOLDER_MINUS "\uebf4" # define LINEA_ELABORATION_FOLDER_NEXT "\uebf5" # define LINEA_ELABORATION_DOCUMENT_FLAGGED "\uebf6" # define LINEA_ELABORATION_DOCUMENT_GRAPH "\uebf7" # define LINEA_ELABORATION_DOCUMENT_HEART "\uebf8" # define LINEA_ELABORATION_DOCUMENT_MINUS "\uebf9" # define LINEA_ELABORATION_DOCUMENT_NEXT "\uebfa" # define LINEA_ELABORATION_DOCUMENT_NOACCESS "\uebfb" # define LINEA_ELABORATION_DOCUMENT_NOTE "\uebfc" # define LINEA_ELABORATION_DOCUMENT_PENCIL "\uebfd" # define LINEA_ELABORATION_DOCUMENT_PICTURE "\uebfe" # define LINEA_ELABORATION_DOCUMENT_PLUS "\uebff" # define LINEA_ELABORATION_FOLDER_NOACCESS "\uec00" # define LINEA_ELABORATION_FOLDER_NOTE "\uec01" # define LINEA_ELABORATION_FOLDER_PENCIL "\uec02" # define LINEA_ELABORATION_FOLDER_PICTURE "\uec03" # define LINEA_ELABORATION_FOLDER_PLUS "\uec04" # define LINEA_ELABORATION_FOLDER_PREVIOUS "\uec05" # define LINEA_ELABORATION_FOLDER_REFRESH "\uec06" # define LINEA_ELABORATION_CALENDAR_EMPTY "\uec07" # define LINEA_ELABORATION_CALENDAR_FLAGGED "\uec08" # define LINEA_ELABORATION_CALENDAR_HEART "\uec09" # define LINEA_ELABORATION_CALENDAR_MINUS "\uec0a" # define LINEA_ELABORATION_CALENDAR_NEXT "\uec0b" # define LINEA_ELABORATION_CALENDAR_NOACCESS "\uec0c" # define LINEA_ELABORATION_CALENDAR_PENCIL "\uec0d" # define LINEA_ELABORATION_CALENDAR_PLUS "\uec0e" # define LINEA_ELABORATION_CALENDAR_PREVIOUS "\uec0f" # define LINEA_ELABORATION_CALENDAR_REFRESH "\uec10" # define LINEA_ELABORATION_CALENDAR_REMOVE "\uec11" # define LINEA_ELABORATION_CALENDAR_SEARCH "\uec12" # define LINEA_ELABORATION_CALENDAR_STAR "\uec13" # define LINEA_ELABORATION_CALENDAR_UPLOAD "\uec14" # define LINEA_ELABORATION_CLOUD_CHECK "\uec15" # define LINEA_ELABORATION_CLOUD_DOWNLOAD "\uec16" # define LINEA_ELABORATION_CLOUD_MINUS "\uec17" # define LINEA_ELABORATION_CLOUD_NOACCESS "\uec18" # define LINEA_ELABORATION_CLOUD_PLUS "\uec19" # define LINEA_ELABORATION_CLOUD_REFRESH "\uec1a" # define LINEA_ELABORATION_CLOUD_REMOVE "\uec1b" # define LINEA_ELABORATION_CLOUD_SEARCH "\uec1c" # define LINEA_ELABORATION_CLOUD_UPLOAD "\uec1d" # define LINEA_ELABORATION_DOCUMENT_CHECK "\uec1e" # define LINEA_ELABORATION_DOCUMENT_CLOUD "\uec1f" # define LINEA_ELABORATION_DOCUMENT_DOWNLOAD "\uec20" # define LINEA_ELABORATION_FOLDER_REMOVE "\uec21" # define LINEA_ELABORATION_MAIL_HEART "\uec22" # define LINEA_ELABORATION_FOLDER_SEARCH "\uec23" # define LINEA_ELABORATION_FOLDER_STAR "\uec24" # define LINEA_ELABORATION_FOLDER_UPLOAD "\uec25" # define LINEA_ELABORATION_MAIL_CHECK "\uec26" # define LINEA_ELABORATION_BOOKMARK_CHECCK "\uec27" # define LINEA_ELABORATION_BOOKMARK_MINUS "\uec28" # define LINEA_ELABORATION_BOOKMARK_PLUS "\uec29" # define LINEA_ELABORATION_BOOKMARK_REMOVE "\uec2a" # define LINEA_ELABORATION_BRIEFCASE_CHECK "\uec2b" # define LINEA_ELABORATION_BRIEFCASE_DOWNLOAD "\uec2c" # define LINEA_ELABORATION_BRIEFCASE_FLAGGED "\uec2d" # define LINEA_ELABORATION_BRIEFCASE_MINUS "\uec2e" # define LINEA_ELABORATION_BRIEFCASE_PLUS "\uec2f" # define LINEA_ELABORATION_BRIEFCASE_REFRESH "\uec30" # define LINEA_ELABORATION_BRIEFCASE_REMOVE "\uec31" # define LINEA_ELABORATION_BRIEFCASE_SEARCH "\uec32" # define LINEA_ELABORATION_BRIEFCASE_STAR "\uec33" # define LINEA_ELABORATION_BRIEFCASE_UPLOAD "\uec34" # define LINEA_ELABORATION_BROWSER_CHECK "\uec35" # define LINEA_ELABORATION_BROWSER_DOWNLOAD "\uec36" # define LINEA_ELABORATION_BROWSER_MINUS "\uec37" # define LINEA_ELABORATION_BROWSER_PLUS "\uec38" # define LINEA_ELABORATION_BROWSER_REFRESH "\uec39" # define LINEA_ELABORATION_BROWSER_REMOVE "\uec3a" # define LINEA_ELABORATION_BROWSER_SEARCH "\uec3b" # define LINEA_ELABORATION_BROWSER_STAR "\uec3c" # define LINEA_ELABORATION_BROWSER_UPLOAD "\uec3d" # define LINEA_ELABORATION_CALENDAR_CHECK "\uec3e" # define LINEA_ELABORATION_CALENDAR_CLOUD "\uec3f" # define LINEA_ELABORATION_CALENDAR_DOWNLOAD "\uec40" # define LINEA_ELABORATION_MAIL_CLOUD "\uec41" # define LINEA_ELABORATION_MAIL_DOCUMENT "\uec42" # define LINEA_ELABORATION_MAIL_DOWNLOAD "\uec43" # define LINEA_ELABORATION_MAIL_FLAGGED "\uec44" # define LINEA_ELABORATION_MAIL_NEXT "\uec45" # define LINEA_ELABORATION_MAIL_NOACCESS "\uec46" # define LINEA_ELABORATION_MAIL_NOTE "\uec47" # define LINEA_ELABORATION_MAIL_PENCIL "\uec48" # define LINEA_ELABORATION_MAIL_PICTURE "\uec49" # define LINEA_ELABORATION_MAIL_PREVIOUS "\uec4a" # define LINEA_ELABORATION_MAIL_REFRESH "\uec4b" # define LINEA_ELABORATION_MAIL_REMOVE "\uec4c" # define LINEA_ELABORATION_MAIL_SEARCH "\uec4d" # define LINEA_ELABORATION_MAIL_STAR "\uec4e" # define LINEA_ELABORATION_MAIL_UPLOAD "\uec4f" # define LINEA_ELABORATION_MESSAGE_CHECK "\uec50" # define LINEA_ELABORATION_MESSAGE_DOTS "\uec51" # define LINEA_ELABORATION_MESSAGE_HAPPY "\uec52" # define LINEA_ELABORATION_MESSAGE_HEART "\uec53" # define LINEA_ELABORATION_MESSAGE_MINUS "\uec54" # define LINEA_ELABORATION_MESSAGE_NOTE "\uec55" # define LINEA_ELABORATION_MESSAGE_PLUS "\uec56" # define LINEA_ELABORATION_MESSAGE_REFRESH "\uec57" # define LINEA_ELABORATION_MESSAGE_REMOVE "\uec58" # define LINEA_ELABORATION_MESSAGE_SAD "\uec59" # define LINEA_ELABORATION_SMARTPHONE_CLOUD "\uec5a" # define LINEA_ELABORATION_SMARTPHONE_HEART "\uec5b" # define LINEA_ELABORATION_SMARTPHONE_NOACCESS "\uec5c" # define LINEA_ELABORATION_SMARTPHONE_NOTE "\uec5d" # define LINEA_ELABORATION_SMARTPHONE_PENCIL "\uec5e" # define LINEA_ELABORATION_SMARTPHONE_PICTURE "\uec5f" # define LINEA_ELABORATION_SMARTPHONE_REFRESH "\uec60" # define LINEA_ELABORATION_SMARTPHONE_SEARCH "\uec61" # define LINEA_ELABORATION_TABLET_CLOUD "\uec62" # define LINEA_ELABORATION_TABLET_HEART "\uec63" # define LINEA_ELABORATION_TABLET_NOACCESS "\uec64" # define LINEA_ELABORATION_TABLET_NOTE "\uec65" # define LINEA_ELABORATION_TABLET_PENCIL "\uec66" # define LINEA_ELABORATION_TABLET_PICTURE "\uec67" # define LINEA_ELABORATION_TABLET_REFRESH "\uec68" # define LINEA_ELABORATION_TABLET_SEARCH "\uec69" # define LINEA_ELABORATION_TODOLIST_2 "\uec6a" # define LINEA_ELABORATION_TODOLIST_CHECK "\uec6b" # define LINEA_ELABORATION_TODOLIST_CLOUD "\uec6c" # define LINEA_ELABORATION_TODOLIST_DOWNLOAD "\uec6d" # define LINEA_ELABORATION_TODOLIST_FLAGGED "\uec6e" # define LINEA_ELABORATION_TODOLIST_MINUS "\uec6f" # define LINEA_ELABORATION_TODOLIST_NOACCESS "\uec70" # define LINEA_ELABORATION_TODOLIST_PENCIL "\uec71" # define LINEA_ELABORATION_TODOLIST_PLUS "\uec72" # define LINEA_ELABORATION_TODOLIST_REFRESH "\uec73" # define LINEA_ELABORATION_TODOLIST_REMOVE "\uec74" # define LINEA_ELABORATION_TODOLIST_SEARCH "\uec75" # define LINEA_ELABORATION_TODOLIST_STAR "\uec76" # define LINEA_ELABORATION_TODOLIST_UPLOAD "\uec77" # define LINEA_ECOMMERCE_RECEIPT_KIPS "\uec78" # define LINEA_ECOMMERCE_RECEIPT_LIRA "\uec79" # define LINEA_ECOMMERCE_RECEIPT_NAIRA "\uec7a" # define LINEA_ECOMMERCE_RECEIPT_PESOS "\uec7b" # define LINEA_ECOMMERCE_RECEIPT_POUND "\uec7c" # define LINEA_ECOMMERCE_RECEIPT_RUBLO "\uec7d" # define LINEA_ECOMMERCE_RECEIPT_RUPEE "\uec7e" # define LINEA_ECOMMERCE_RECEIPT_TUGRIK "\uec7f" # define LINEA_ECOMMERCE_RECEIPT_WON "\uec80" # define LINEA_ECOMMERCE_RECEIPT_YEN "\uec81" # define LINEA_ECOMMERCE_RECEIPT_YEN2 "\uec82" # define LINEA_ECOMMERCE_RECEPT_COLON "\uec83" # define LINEA_ECOMMERCE_RUBLO "\uec84" # define LINEA_ECOMMERCE_RUPEE "\uec85" # define LINEA_ECOMMERCE_SAFE "\uec86" # define LINEA_ECOMMERCE_NAIRA "\uec87" # define LINEA_ECOMMERCE_PESOS "\uec88" # define LINEA_ECOMMERCE_POUND "\uec89" # define LINEA_ECOMMERCE_RECEIPT "\uec8a" # define LINEA_ECOMMERCE_RECEIPT_BATH "\uec8b" # define LINEA_ECOMMERCE_RECEIPT_CENT "\uec8c" # define LINEA_ECOMMERCE_RECEIPT_DOLLAR "\uec8d" # define LINEA_ECOMMERCE_RECEIPT_EURO "\uec8e" # define LINEA_ECOMMERCE_RECEIPT_FRANC "\uec8f" # define LINEA_ECOMMERCE_RECEIPT_GUARANI "\uec90" # define LINEA_ECOMMERCE_SALE "\uec91" # define LINEA_ECOMMERCE_SALES "\uec92" # define LINEA_ECOMMERCE_TICKET "\uec93" # define LINEA_ECOMMERCE_TUGRIKS "\uec94" # define LINEA_ECOMMERCE_WALLET "\uec95" # define LINEA_ECOMMERCE_WON "\uec96" # define LINEA_ECOMMERCE_YEN "\uec97" # define LINEA_ECOMMERCE_CART_CONTENT "\uec98" # define LINEA_ECOMMERCE_CART_DOWNLOAD "\uec99" # define LINEA_ECOMMERCE_CART_MINUS "\uec9a" # define LINEA_ECOMMERCE_CART_PLUS "\uec9b" # define LINEA_ECOMMERCE_CART_REFRESH "\uec9c" # define LINEA_ECOMMERCE_CART_REMOVE "\uec9d" # define LINEA_ECOMMERCE_CART_SEARCH "\uec9e" # define LINEA_ECOMMERCE_CART_UPLOAD "\uec9f" # define LINEA_ECOMMERCE_CENT "\ueca0" # define LINEA_ECOMMERCE_COLON "\ueca1" # define LINEA_ECOMMERCE_CREDITCARD "\ueca2" # define LINEA_ECOMMERCE_DIAMOND "\ueca3" # define LINEA_ECOMMERCE_DOLLAR "\ueca4" # define LINEA_ECOMMERCE_EURO "\ueca5" # define LINEA_ECOMMERCE_FRANC "\ueca6" # define LINEA_ECOMMERCE_GIFT "\ueca7" # define LINEA_ECOMMERCE_GRAPH1 "\ueca8" # define LINEA_ECOMMERCE_GRAPH2 "\ueca9" # define LINEA_ECOMMERCE_GRAPH3 "\uecaa" # define LINEA_ECOMMERCE_GRAPH_DECREASE "\uecab" # define LINEA_ECOMMERCE_GRAPH_INCREASE "\uecac" # define LINEA_ECOMMERCE_GUARANI "\uecad" # define LINEA_ECOMMERCE_KIPS "\uecae" # define LINEA_ECOMMERCE_LIRA "\uecaf" # define LINEA_ECOMMERCE_MEGAPHONE "\uecb0" # define LINEA_ECOMMERCE_MONEY "\uecb1" # define LINEA_ECOMMERCE_YEN2 "\uecb2" # define LINEA_ECOMMERCE_BAG "\uecb3" # define LINEA_ECOMMERCE_BAG_CHECK "\uecb4" # define LINEA_ECOMMERCE_BAG_CLOUD "\uecb5" # define LINEA_ECOMMERCE_BAG_DOWNLOAD "\uecb6" # define LINEA_ECOMMERCE_BAG_MINUS "\uecb7" # define LINEA_ECOMMERCE_BAG_PLUS "\uecb8" # define LINEA_ECOMMERCE_BAG_REFRESH "\uecb9" # define LINEA_ECOMMERCE_BAG_REMOVE "\uecba" # define LINEA_ECOMMERCE_BAG_SEARCH "\uecbb" # define LINEA_ECOMMERCE_BAG_UPLOAD "\uecbc" # define LINEA_ECOMMERCE_BANKNOTE "\uecbd" # define LINEA_ECOMMERCE_BANKNOTES "\uecbe" # define LINEA_ECOMMERCE_BASKET "\uecbf" # define LINEA_ECOMMERCE_BASKET_CHECK "\uecc0" # define LINEA_ECOMMERCE_BASKET_CLOUD "\uecc1" # define LINEA_ECOMMERCE_BASKET_DOWNLOAD "\uecc2" # define LINEA_ECOMMERCE_BASKET_MINUS "\uecc3" # define LINEA_ECOMMERCE_BASKET_PLUS "\uecc4" # define LINEA_ECOMMERCE_BASKET_REFRESH "\uecc5" # define LINEA_ECOMMERCE_BASKET_REMOVE "\uecc6" # define LINEA_ECOMMERCE_BASKET_SEARCH "\uecc7" # define LINEA_ECOMMERCE_BASKET_UPLOAD "\uecc8" # define LINEA_ECOMMERCE_BATH "\uecc9" # define LINEA_ECOMMERCE_CART "\uecca" # define LINEA_ECOMMERCE_CART_CHECK "\ueccb" # define LINEA_ECOMMERCE_CART_CLOUD "\ueccc" # define LINEA_MUSIC_STOP_BUTTON "\ueccd" # define LINEA_MUSIC_TAPE "\uecce" # define LINEA_MUSIC_VOLUME_DOWN "\ueccf" # define LINEA_MUSIC_VOLUME_UP "\uecd0" # define LINEA_MUSIC_BEGINNING_BUTTON "\uecd1" # define LINEA_MUSIC_BELL "\uecd2" # define LINEA_MUSIC_CD "\uecd3" # define LINEA_MUSIC_DIAPASON "\uecd4" # define LINEA_MUSIC_EJECT_BUTTON "\uecd5" # define LINEA_MUSIC_END_BUTTON "\uecd6" # define LINEA_MUSIC_FASTFORWARD_BUTTON "\uecd7" # define LINEA_MUSIC_HEADPHONES "\uecd8" # define LINEA_MUSIC_IPOD "\uecd9" # define LINEA_MUSIC_LOUDSPEAKER "\uecda" # define LINEA_MUSIC_MICROPHONE "\uecdb" # define LINEA_MUSIC_MICROPHONE_OLD "\uecdc" # define LINEA_MUSIC_MIXER "\uecdd" # define LINEA_MUSIC_MUTE "\uecde" # define LINEA_MUSIC_NOTE_MULTIPLE "\uecdf" # define LINEA_MUSIC_NOTE_SINGLE "\uece0" # define LINEA_MUSIC_PAUSE_BUTTON "\uece1" # define LINEA_MUSIC_PLAY_BUTTON "\uece2" # define LINEA_MUSIC_PLAYLIST "\uece3" # define LINEA_MUSIC_RADIO_GHETTOBLASTER "\uece4" # define LINEA_MUSIC_RADIO_PORTABLE "\uece5" # define LINEA_MUSIC_RECORD "\uece6" # define LINEA_MUSIC_RECORDPLAYER "\uece7" # define LINEA_MUSIC_REPEAT_BUTTON "\uece8" # define LINEA_MUSIC_REWIND_BUTTON "\uece9" # define LINEA_MUSIC_SHUFFLE_BUTTON "\uecea" # define LINEA_SOFTWARE_PARAGRAPH_JUSTIFY_CENTER "\ueceb" # define LINEA_SOFTWARE_PARAGRAPH_JUSTIFY_LEFT "\uecec" # define LINEA_SOFTWARE_PARAGRAPH_JUSTIFY_RIGHT "\ueced" # define LINEA_SOFTWARE_PARAGRAPH_SPACE_AFTER "\uecee" # define LINEA_SOFTWARE_PARAGRAPH_SPACE_BEFORE "\uecef" # define LINEA_SOFTWARE_PATHFINDER_EXCLUDE "\uecf0" # define LINEA_SOFTWARE_PATHFINDER_INTERSECT "\uecf1" # define LINEA_SOFTWARE_PATHFINDER_SUBTRACT "\uecf2" # define LINEA_SOFTWARE_PATHFINDER_UNITE "\uecf3" # define LINEA_SOFTWARE_PEN "\uecf4" # define LINEA_SOFTWARE_PEN_ADD "\uecf5" # define LINEA_SOFTWARE_PEN_REMOVE "\uecf6" # define LINEA_SOFTWARE_PENCIL "\uecf7" # define LINEA_SOFTWARE_POLYGONALLASSO "\uecf8" # define LINEA_SOFTWARE_REFLECT_HORIZONTAL "\uecf9" # define LINEA_SOFTWARE_MAGNETE "\uecfa" # define LINEA_SOFTWARE_PAGES "\uecfb" # define LINEA_SOFTWARE_PAINTBRUSH "\uecfc" # define LINEA_SOFTWARE_PAINTBUCKET "\uecfd" # define LINEA_SOFTWARE_PAINTROLLER "\uecfe" # define LINEA_SOFTWARE_PARAGRAPH "\uecff" # define LINEA_SOFTWARE_PARAGRAPH_ALIGN_LEFT "\ued00" # define LINEA_SOFTWARE_PARAGRAPH_ALIGN_RIGHT "\ued01" # define LINEA_SOFTWARE_PARAGRAPH_CENTER "\ued02" # define LINEA_SOFTWARE_PARAGRAPH_JUSTIFY_ALL "\ued03" # define LINEA_SOFTWARE_REFLECT_VERTICAL "\ued04" # define LINEA_SOFTWARE_REMOVE_VECTORPOINT "\ued05" # define LINEA_SOFTWARE_SCALE_EXPAND "\ued06" # define LINEA_SOFTWARE_SCALE_REDUCE "\ued07" # define LINEA_SOFTWARE_SELECTION_OVAL "\ued08" # define LINEA_SOFTWARE_SELECTION_POLYGON "\ued09" # define LINEA_SOFTWARE_SELECTION_RECTANGLE "\ued0a" # define LINEA_SOFTWARE_INDENT_FIRSTLINE "\ued0b" # define LINEA_SOFTWARE_INDENT_LEFT "\ued0c" # define LINEA_SOFTWARE_INDENT_RIGHT "\ued0d" # define LINEA_SOFTWARE_LASSO "\ued0e" # define LINEA_SOFTWARE_LAYERS1 "\ued0f" # define LINEA_SOFTWARE_LAYERS2 "\ued10" # define LINEA_SOFTWARE_LAYOUT "\ued11" # define LINEA_SOFTWARE_LAYOUT_2COLUMNS "\ued12" # define LINEA_SOFTWARE_LAYOUT_3COLUMNS "\ued13" # define LINEA_SOFTWARE_LAYOUT_4BOXES "\ued14" # define LINEA_SOFTWARE_LAYOUT_4COLUMNS "\ued15" # define LINEA_SOFTWARE_LAYOUT_4LINES "\ued16" # define LINEA_SOFTWARE_LAYOUT_8BOXES "\ued17" # define LINEA_SOFTWARE_LAYOUT_HEADER "\ued18" # define LINEA_SOFTWARE_LAYOUT_HEADER_2COLUMNS "\ued19" # define LINEA_SOFTWARE_LAYOUT_HEADER_3COLUMNS "\ued1a" # define LINEA_SOFTWARE_LAYOUT_HEADER_4BOXES "\ued1b" # define LINEA_SOFTWARE_LAYOUT_HEADER_4COLUMNS "\ued1c" # define LINEA_SOFTWARE_LAYOUT_HEADER_COMPLEX "\ued1d" # define LINEA_SOFTWARE_LAYOUT_HEADER_COMPLEX2 "\ued1e" # define LINEA_SOFTWARE_LAYOUT_HEADER_COMPLEX3 "\ued1f" # define LINEA_SOFTWARE_LAYOUT_HEADER_COMPLEX4 "\ued20" # define LINEA_SOFTWARE_LAYOUT_HEADER_SIDELEFT "\ued21" # define LINEA_SOFTWARE_LAYOUT_HEADER_SIDERIGHT "\ued22" # define LINEA_SOFTWARE_LAYOUT_SIDEBAR_LEFT "\ued23" # define LINEA_SOFTWARE_LAYOUT_SIDEBAR_RIGHT "\ued24" # define LINEA_SOFTWARE_SELECTION_ROUNDEDRECTANGLE "\ued25" # define LINEA_SOFTWARE_VECTOR_LINE "\ued26" # define LINEA_SOFTWARE_SHAPE_OVAL "\ued27" # define LINEA_SOFTWARE_SHAPE_POLYGON "\ued28" # define LINEA_SOFTWARE_SHAPE_RECTANGLE "\ued29" # define LINEA_SOFTWARE_SHAPE_ROUNDEDRECTANGLE "\ued2a" # define LINEA_SOFTWARE_ADD_VECTORPOINT "\ued2b" # define LINEA_SOFTWARE_BOX_OVAL "\ued2c" # define LINEA_SOFTWARE_BOX_POLYGON "\ued2d" # define LINEA_SOFTWARE_BOX_RECTANGLE "\ued2e" # define LINEA_SOFTWARE_BOX_ROUNDEDRECTANGLE "\ued2f" # define LINEA_SOFTWARE_CHARACTER "\ued30" # define LINEA_SOFTWARE_CROP "\ued31" # define LINEA_SOFTWARE_EYEDROPPER "\ued32" # define LINEA_SOFTWARE_FONT_ALLCAPS "\ued33" # define LINEA_SOFTWARE_FONT_BASELINE_SHIFT "\ued34" # define LINEA_SOFTWARE_FONT_HORIZONTAL_SCALE "\ued35" # define LINEA_SOFTWARE_FONT_KERNING "\ued36" # define LINEA_SOFTWARE_FONT_LEADING "\ued37" # define LINEA_SOFTWARE_FONT_SIZE "\ued38" # define LINEA_SOFTWARE_FONT_SMALLCAPITAL "\ued39" # define LINEA_SOFTWARE_FONT_SMALLCAPS "\ued3a" # define LINEA_SOFTWARE_FONT_STRIKETHROUGH "\ued3b" # define LINEA_SOFTWARE_FONT_TRACKING "\ued3c" # define LINEA_SOFTWARE_FONT_UNDERLINE "\ued3d" # define LINEA_SOFTWARE_FONT_VERTICAL_SCALE "\ued3e" # define LINEA_SOFTWARE_HORIZONTAL_ALIGN_CENTER "\ued3f" # define LINEA_SOFTWARE_HORIZONTAL_ALIGN_LEFT "\ued40" # define LINEA_SOFTWARE_HORIZONTAL_ALIGN_RIGHT "\ued41" # define LINEA_SOFTWARE_HORIZONTAL_DISTRIBUTE_CENTER "\ued42" # define LINEA_SOFTWARE_HORIZONTAL_DISTRIBUTE_LEFT "\ued43" # define LINEA_SOFTWARE_HORIZONTAL_DISTRIBUTE_RIGHT "\ued44" # define LINEA_SOFTWARE_SLICE "\ued45" # define LINEA_SOFTWARE_TRANSFORM_BEZIER "\ued46" # define LINEA_SOFTWARE_VECTOR_BOX "\ued47" # define LINEA_SOFTWARE_VECTOR_COMPOSITE "\ued48" # define LINEA_SOFTWARE_VERTICAL_ALIGN_BOTTOM "\ued49" # define LINEA_SOFTWARE_VERTICAL_ALIGN_CENTER "\ued4a" # define LINEA_SOFTWARE_VERTICAL_ALIGN_TOP "\ued4b" # define LINEA_SOFTWARE_VERTICAL_DISTRIBUTE_BOTTOM "\ued4c" # define LINEA_SOFTWARE_VERTICAL_DISTRIBUTE_CENTER "\ued4d" # define LINEA_SOFTWARE_VERTICAL_DISTRIBUTE_TOP "\ued4e" # define LINEA_WEATHER_AQUARIUS "\ued4f" # define LINEA_WEATHER_ARIES "\ued50" # define LINEA_WEATHER_CANCER "\ued51" # define LINEA_WEATHER_CAPRICORN "\ued52" # define LINEA_WEATHER_CLOUD "\ued53" # define LINEA_WEATHER_CLOUD_DROP "\ued54" # define LINEA_WEATHER_CLOUD_LIGHTNING "\ued55" # define LINEA_WEATHER_CLOUD_SNOWFLAKE "\ued56" # define LINEA_WEATHER_DOWNPOUR_FULLMOON "\ued57" # define LINEA_WEATHER_DOWNPOUR_HALFMOON "\ued58" # define LINEA_WEATHER_DOWNPOUR_SUN "\ued59" # define LINEA_WEATHER_DROP "\ued5a" # define LINEA_WEATHER_FIRST_QUARTER "\ued5b" # define LINEA_WEATHER_FOG "\ued5c" # define LINEA_WEATHER_FOG_FULLMOON "\ued5d" # define LINEA_WEATHER_FOG_HALFMOON "\ued5e" # define LINEA_WEATHER_FOG_SUN "\ued5f" # define LINEA_WEATHER_FULLMOON "\ued60" # define LINEA_WEATHER_GEMINI "\ued61" # define LINEA_WEATHER_HAIL "\ued62" # define LINEA_WEATHER_HAIL_FULLMOON "\ued63" # define LINEA_WEATHER_HAIL_HALFMOON "\ued64" # define LINEA_WEATHER_HAIL_SUN "\ued65" # define LINEA_WEATHER_LAST_QUARTER "\ued66" # define LINEA_WEATHER_LEO "\ued67" # define LINEA_WEATHER_LIBRA "\ued68" # define LINEA_WEATHER_LIGHTNING "\ued69" # define LINEA_WEATHER_MISTYRAIN "\ued6a" # define LINEA_WEATHER_MISTYRAIN_FULLMOON "\ued6b" # define LINEA_WEATHER_MISTYRAIN_HALFMOON "\ued6c" # define LINEA_WEATHER_MISTYRAIN_SUN "\ued6d" # define LINEA_WEATHER_MOON "\ued6e" # define LINEA_WEATHER_MOONDOWN_FULL "\ued6f" # define LINEA_WEATHER_MOONDOWN_HALF "\ued70" # define LINEA_WEATHER_MOONSET_FULL "\ued71" # define LINEA_WEATHER_MOONSET_HALF "\ued72" # define LINEA_WEATHER_MOVE2 "\ued73" # define LINEA_WEATHER_NEWMOON "\ued74" # define LINEA_WEATHER_PISCES "\ued75" # define LINEA_WEATHER_RAIN "\ued76" # define LINEA_WEATHER_RAIN_FULLMOON "\ued77" # define LINEA_WEATHER_RAIN_HALFMOON "\ued78" # define LINEA_WEATHER_RAIN_SUN "\ued79" # define LINEA_WEATHER_SAGITTARIUS "\ued7a" # define LINEA_WEATHER_SCORPIO "\ued7b" # define LINEA_WEATHER_SNOW "\ued7c" # define LINEA_WEATHER_SNOW_FULLMOON "\ued7d" # define LINEA_WEATHER_SNOW_HALFMOON "\ued7e" # define LINEA_WEATHER_SNOW_SUN "\ued7f" # define LINEA_WEATHER_SNOWFLAKE "\ued80" # define LINEA_WEATHER_STAR "\ued81" # define LINEA_WEATHER_STORM_11 "\ued82" # define LINEA_WEATHER_STORM_32 "\ued83" # define LINEA_WEATHER_STORM_FULLMOON "\ued84" # define LINEA_WEATHER_STORM_HALFMOON "\ued85" # define LINEA_WEATHER_STORM_SUN "\ued86" # define LINEA_WEATHER_SUN "\ued87" # define LINEA_WEATHER_SUNDOWN "\ued88" # define LINEA_WEATHER_SUNSET "\ued89" # define LINEA_WEATHER_TAURUS "\ued8a" # define LINEA_WEATHER_TEMPEST "\ued8b" # define LINEA_WEATHER_TEMPEST_FULLMOON "\ued8c" # define LINEA_WEATHER_TEMPEST_HALFMOON "\ued8d" # define LINEA_WEATHER_TEMPEST_SUN "\ued8e" # define LINEA_WEATHER_VARIABLE_FULLMOON "\ued8f" # define LINEA_WEATHER_VARIABLE_HALFMOON "\ued90" # define LINEA_WEATHER_VARIABLE_SUN "\ued91" # define LINEA_WEATHER_VIRGO "\ued92" # define LINEA_WEATHER_WANING_CRESENT "\ued93" # define LINEA_WEATHER_WANING_GIBBOUS "\ued94" # define LINEA_WEATHER_WAXING_CRESENT "\ued95" # define LINEA_WEATHER_WAXING_GIBBOUS "\ued96" # define LINEA_WEATHER_WIND "\ued97" # define LINEA_WEATHER_WIND_E "\ued98" # define LINEA_WEATHER_WIND_FULLMOON "\ued99" # define LINEA_WEATHER_WIND_HALFMOON "\ued9a" # define LINEA_WEATHER_WIND_N "\ued9b" # define LINEA_WEATHER_WIND_NE "\ued9c" # define LINEA_WEATHER_WIND_NW "\ued9d" # define LINEA_WEATHER_WIND_S "\ued9e" # define LINEA_WEATHER_WIND_SE "\ued9f" # define LINEA_WEATHER_WIND_SUN "\ueda0" # define LINEA_WEATHER_WIND_SW "\ueda1" # define LINEA_WEATHER_WIND_W "\ueda2" # define LINEA_WEATHER_WINDGUST "\ueda3" # define MFIZZ_3DPRINT "\ueda4" # define MFIZZ_ALPINELINUX "\ueda5" # define MFIZZ_ANGULAR "\ueda6" # define MFIZZ_ANGULAR_ALT "\ueda7" # define MFIZZ_ANTENNA "\ueda8" # define MFIZZ_APACHE "\ueda9" # define MFIZZ_ARCHLINUX "\uedaa" # define MFIZZ_AWS "\uedab" # define MFIZZ_AZURE "\uedac" # define MFIZZ_BACKBONE "\uedad" # define MFIZZ_BLACKBERRY "\uedae" # define MFIZZ_BOMB "\uedaf" # define MFIZZ_BOOTSTRAP "\uedb0" # define MFIZZ_C "\uedb1" # define MFIZZ_CASSANDRA "\uedb2" # define MFIZZ_CENTOS "\uedb3" # define MFIZZ_CLOJURE "\uedb4" # define MFIZZ_CODEIGNITER "\uedb5" # define MFIZZ_CODEPEN "\uedb6" # define MFIZZ_COFFEE_BEAN "\uedb7" # define MFIZZ_CPLUSPLUS "\uedb8" # define MFIZZ_CSHARP "\uedb9" # define MFIZZ_CSS "\uedba" # define MFIZZ_CSS3 "\uedbb" # define MFIZZ_CSS3_ALT "\uedbc" # define MFIZZ_D3 "\uedbd" # define MFIZZ_DATABASE "\uedbe" # define MFIZZ_DATABASE_ALT "\uedbf" # define MFIZZ_DATABASE_ALT2 "\uedc0" # define MFIZZ_DEBIAN "\uedc1" # define MFIZZ_DOCKER "\uedc2" # define MFIZZ_DREAMHOST "\uedc3" # define MFIZZ_ELIXIR "\uedc4" # define MFIZZ_ELM "\uedc5" # define MFIZZ_ERLANG "\uedc6" # define MFIZZ_EXHERBO "\uedc7" # define MFIZZ_FEDORA "\uedc8" # define MFIZZ_FIRE_ALT "\uedc9" # define MFIZZ_FREEBSD "\uedca" # define MFIZZ_FREECODECAMP "\uedcb" # define MFIZZ_GENTOO "\uedcc" # define MFIZZ_GHOST "\uedcd" # define MFIZZ_GIT "\uedce" # define MFIZZ_GNOME "\uedcf" # define MFIZZ_GO "\uedd0" # define MFIZZ_GO_ALT "\uedd1" # define MFIZZ_GOOGLE "\uedd2" # define MFIZZ_GOOGLE_ALT "\uedd3" # define MFIZZ_GOOGLE_CODE "\uedd4" # define MFIZZ_GOOGLE_DEVELOPERS "\uedd5" # define MFIZZ_GRADLE "\uedd6" # define MFIZZ_GRAILS "\uedd7" # define MFIZZ_GRAILS_ALT "\uedd8" # define MFIZZ_GRUNT "\uedd9" # define MFIZZ_GULP "\uedda" # define MFIZZ_GULP_ALT "\ueddb" # define MFIZZ_HADOOP "\ueddc" # define MFIZZ_HASKELL "\ueddd" # define MFIZZ_HEROKU "\uedde" # define MFIZZ_HTML "\ueddf" # define MFIZZ_HTML5 "\uede0" # define MFIZZ_HTML5_ALT "\uede1" # define MFIZZ_IPHONE "\uede2" # define MFIZZ_JAVA "\uede3" # define MFIZZ_JAVA_BOLD "\uede4" # define MFIZZ_JAVA_DUKE "\uede5" # define MFIZZ_JAVASCRIPT "\uede6" # define MFIZZ_JAVASCRIPT_ALT "\uede7" # define MFIZZ_JETTY "\uede8" # define MFIZZ_JQUERY "\uede9" # define MFIZZ_KDE "\uedea" # define MFIZZ_LARAVEL "\uedeb" # define MFIZZ_LINE_GRAPH "\uedec" # define MFIZZ_LINUX_MINT "\ueded" # define MFIZZ_LOOKING "\uedee" # define MFIZZ_MAGENTO "\uedef" # define MFIZZ_MARIADB "\uedf0" # define MFIZZ_MAVEN "\uedf1" # define MFIZZ_MICROSCOPE "\uedf2" # define MFIZZ_MOBILE_DEVICE "\uedf3" # define MFIZZ_MOBILE_PHONE_ALT "\uedf4" # define MFIZZ_MOBILE_PHONE_BROADCAST "\uedf5" # define MFIZZ_MONGODB "\uedf6" # define MFIZZ_MSSQL "\uedf7" # define MFIZZ_MYSQL "\uedf8" # define MFIZZ_MYSQL_ALT "\uedf9" # define MFIZZ_NETBSD "\uedfa" # define MFIZZ_NGINX "\uedfb" # define MFIZZ_NGINX_ALT "\uedfc" # define MFIZZ_NGINX_ALT2 "\uedfd" # define MFIZZ_NODEJS "\uedfe" # define MFIZZ_NPM "\uedff" # define MFIZZ_OBJC "\uee00" # define MFIZZ_OPENSHIFT "\uee01" # define MFIZZ_ORACLE "\uee02" # define MFIZZ_ORACLE_ALT "\uee03" # define MFIZZ_OSX "\uee04" # define MFIZZ_PERL "\uee05" # define MFIZZ_PHONE_ALT "\uee06" # define MFIZZ_PHONE_GAP "\uee07" # define MFIZZ_PHONE_RETRO "\uee08" # define MFIZZ_PHP "\uee09" # define MFIZZ_PHP_ALT "\uee0a" # define MFIZZ_PLAYFRAMEWORK "\uee0b" # define MFIZZ_PLAYFRAMEWORK_ALT "\uee0c" # define MFIZZ_PLONE "\uee0d" # define MFIZZ_POSTGRES "\uee0e" # define MFIZZ_POSTGRES_ALT "\uee0f" # define MFIZZ_PYTHON "\uee10" # define MFIZZ_RASPBERRYPI "\uee11" # define MFIZZ_REACTJS "\uee12" # define MFIZZ_REDHAT "\uee13" # define MFIZZ_REDIS "\uee14" # define MFIZZ_RUBY "\uee15" # define MFIZZ_RUBY_ON_RAILS "\uee16" # define MFIZZ_RUBY_ON_RAILS_ALT "\uee17" # define MFIZZ_RUST "\uee18" # define MFIZZ_SASS "\uee19" # define MFIZZ_SATELLITE "\uee1a" # define MFIZZ_SCALA "\uee1b" # define MFIZZ_SCALA_ALT "\uee1c" # define MFIZZ_SCRIPT "\uee1d" # define MFIZZ_SCRIPT_ALT "\uee1e" # define MFIZZ_SHELL "\uee1f" # define MFIZZ_SITEFINITY "\uee20" # define MFIZZ_SOLARIS "\uee21" # define MFIZZ_SPLATTER "\uee22" # define MFIZZ_SPRING "\uee23" # define MFIZZ_SUSE "\uee24" # define MFIZZ_SVG "\uee25" # define MFIZZ_SYMFONY "\uee26" # define MFIZZ_TOMCAT "\uee27" # define MFIZZ_UBUNTU "\uee28" # define MFIZZ_UNITY "\uee29" # define MFIZZ_WIRELESS "\uee2a" # define MFIZZ_WORDPRESS "\uee2b" # define MFIZZ_X11 "\uee2c" # define FIRACODE_ASTERISK "\uee2d" # define FIRACODE_PLUS "\uee2e" # define FIRACODE_HYPHEN "\uee2f" # define FIRACODE_SAD "\uee30" # define FIRACODE_W_W_W "\uee31" # define FIRACODE_ASTERISK_ASTERISK "\uee32" # define FIRACODE_ASTERISK_ASTERISK_ASTERISK "\uee33" # define FIRACODE_ASTERISK_ASTERISK_SLASH "\uee34" # define FIRACODE_ASTERISK_GREATER "\uee35" # define FIRACODE_ASTERISK_SLASH "\uee36" # define FIRACODE_BACKSLASH_BACKSLASH "\uee37" # define FIRACODE_BBB "\uee38" # define FIRACODE_BRACELEFT_HYPHEN "\uee39" # define FIRACODE_BRACKETLEFT_BRACKETRIGHT "\uee3a" # define FIRACODE_COLON_COLON "\uee3b" # define FIRACODE_COLON_COLON_COLON "\uee3c" # define FIRACODE_COLON_EQUAL "\uee3d" # define FIRACODE_EXCLAM_EXCLAM "\uee3e" # define FIRACODE_EXCLAM_EQUAL "\uee3f" # define FIRACODE_EXCLAM_EQUAL_EQUAL "\uee40" # define FIRACODE_HYPHEN_BRACERIGHT "\uee41" # define FIRACODE_HYPHEN_HYPHEN "\uee42" # define FIRACODE_HYPHEN_HYPHEN_HYPHEN "\uee43" # define FIRACODE_HYPHEN_HYPHEN_GREATER "\uee44" # define FIRACODE_HYPHEN_GREATER "\uee45" # define FIRACODE_HYPHEN_GREATER_GREATER "\uee46" # define FIRACODE_HYPHEN_LESS "\uee47" # define FIRACODE_HYPHEN_LESS_LESS "\uee48" # define FIRACODE_HYPHEN_ASCIITILDE "\uee49" # define FIRACODE_NUMBERSIGN_BRACELEFT "\uee4a" # define FIRACODE_NUMBERSIGN_BRACKETLEFT "\uee4b" # define FIRACODE_NUMBERSIGN_NUMBERSIGN "\uee4c" # define FIRACODE_NNN "\uee4d" # define FIRACODE_NNNN "\uee4e" # define FIRACODE_NUMBERSIGN_PARENLEFT "\uee4f" # define FIRACODE_NUMBERSIGN_QUESTION "\uee50" # define FIRACODE_NUMBERSIGN_UNDERSCORE "\uee51" # define FIRACODE_NUP "\uee52" # define FIRACODE_PERIOD_HYPHEN "\uee53" # define FIRACODE_PERIOD_EQUAL "\uee54" # define FIRACODE_PERIOD_PERIOD "\uee55" # define FIRACODE_PERIOD_PERIOD_LESS "\uee56" # define FIRACODE_PERIOD_PERIOD_PERIOD "\uee57" # define FIRACODE_QUESTION_EQUAL "\uee58" # define FIRACODE_QUESTION_QUESTION "\uee59" # define FIRACODE_SEMICOLON_SEMICOLON "\uee5a" # define FIRACODE_SLASH_ASTERISK "\uee5b" # define FIRACODE_SLASH_ASTERISK_ASTERISK "\uee5c" # define FIRACODE_SLASH_EQUAL "\uee5d" # define FIRACODE_SLASH_EQUAL_EQUAL "\uee5e" # define FIRACODE_SLASH_GREATER "\uee5f" # define FIRACODE_SLASH_SLASH "\uee60" # define FIRACODE_SLASH_SLASH_SLASH "\uee61" # define FIRACODE_AMPERSAND_AMPERSAND "\uee62" # define FIRACODE_BAR_BAR "\uee63" # define FIRACODE_BAR_BAR_EQUAL "\uee64" # define FIRACODE_BAR_EQUAL "\uee65" # define FIRACODE_BAR_GREATER "\uee66" # define FIRACODE_ASCIICIRCUM_EQUAL "\uee67" # define FIRACODE_DOLLAR_GREATER "\uee68" # define FIRACODE_PLUS_PLUS "\uee69" # define FIRACODE_PLUS_PLUS_PLUS "\uee6a" # define FIRACODE_PLUS_GREATER "\uee6b" # define FIRACODE_EQUAL_COLON_EQUAL "\uee6c" # define FIRACODE_EQUAL_EQUAL "\uee6d" # define FIRACODE_EQUAL_EQUAL_EQUAL "\uee6e" # define FIRACODE_EQUAL_EQUAL_GREATER "\uee6f" # define FIRACODE_EQUAL_GREATER "\uee70" # define FIRACODE_EQUAL_GREATER_GREATER "\uee71" # define FIRACODE_EQUAL_LESS "\uee72" # define FIRACODE_EQUAL_LESS_LESS "\uee73" # define FIRACODE_EQUAL_SLASH_EQUAL "\uee74" # define FIRACODE_GREATER_HYPHEN "\uee75" # define FIRACODE_GREATER_EQUAL "\uee76" # define FIRACODE_GREATER_EQUAL_GREATER "\uee77" # define FIRACODE_GREATER_GREATER "\uee78" # define FIRACODE_GREATER_GREATER_HYPHEN "\uee79" # define FIRACODE_GREATER_GREATER_EQUAL "\uee7a" # define FIRACODE_GREATER_GREATER_GREATER "\uee7b" # define FIRACODE_LESS_ASTERISK "\uee7c" # define FIRACODE_LESS_ASTERISK_GREATER "\uee7d" # define FIRACODE_LESS_BAR "\uee7e" # define FIRACODE_LESS_BAR_GREATER "\uee7f" # define FIRACODE_LESS_DOLLAR "\uee80" # define FIRACODE_LESS_DOLLAR_GREATER "\uee81" # define FIRACODE_LESS_EXCLAM_HYPHEN_HYPHEN "\uee82" # define FIRACODE_LESS_HYPHEN "\uee83" # define FIRACODE_LESS_HYPHEN_HYPHEN "\uee84" # define FIRACODE_LESS_HYPHEN_GREATER "\uee85" # define FIRACODE_LESS_PLUS "\uee86" # define FIRACODE_LESS_PLUS_GREATER "\uee87" # define FIRACODE_LESS_EQUAL "\uee88" # define FIRACODE_LESS_EQUAL_EQUAL "\uee89" # define FIRACODE_LESS_EQUAL_GREATER "\uee8a" # define FIRACODE_LESS_EQUAL_LESS "\uee8b" # define FIRACODE_LESS_GREATER "\uee8c" # define FIRACODE_LESS_LESS "\uee8d" # define FIRACODE_LESS_LESS_HYPHEN "\uee8e" # define FIRACODE_LESS_LESS_EQUAL "\uee8f" # define FIRACODE_LESS_LESS_LESS "\uee90" # define FIRACODE_LESS_ASCIITILDE "\uee91" # define FIRACODE_LESS_ASCIITILDE_ASCIITILDE "\uee92" # define FIRACODE_LESS_SLASH "\uee93" # define FIRACODE_LESS_SLASH_GREATER "\uee94" # define FIRACODE_ASCIITILDE_AT "\uee95" # define FIRACODE_ASCIITILDE_HYPHEN "\uee96" # define FIRACODE_ASCIITILDE_EQUAL "\uee97" # define FIRACODE_ASCIITILDE_GREATER "\uee98" # define FIRACODE_ASCIITILDE_ASCIITILDE "\uee99" # define FIRACODE_AAG "\uee9a" # define FIRACODE_PERCENT_PERCENT "\uee9b" # define FIRACODE_X_MULTIPLY "\uee9c" # define FIRACODE_COLON_UC "\uee9d" # define FIRACODE_PLUS_LC "\uee9e" # define FIRACODE_PLUS_TOSF2 "\uee9f" # define FIRACODE_NAMEME_1114119 "\ueea0" #endif // ICONS_IN_TERMINAL ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/icons.h���������������������������������������������������������������������������������0000664�0000000�0000000�00000043503�14663100143�0014225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef INCLUDE_ICONS_H #define INCLUDE_ICONS_H #if defined(ICONS_GENERATE) || defined(ICONS_ENABLED) /* * 1st arg = ICONS_IN_TERM * * 2nd arg = NERD ICONS * You can find hex codes for nerd fonts here: https://www.nerdfonts.com/cheat-sheet * * 3rd arg = EMOJIS * You can find a list of emoji here: https://unicode.org/Public/emoji/5.0/emoji-test.txt * * Any entry with empty icon gets removed by the hash-table generator */ #if defined(ICONS_IN_TERM) #define ICON_STR(I, N, E) I #include "icons-in-terminal.h" #elif defined(NERD) #define ICON_STR(I, N, E) N #elif defined(EMOJI) #define ICON_STR(I, N, E) E #endif /* * Define a string to be printed before and after the icon * Adjust if the icons are not printed properly */ #if defined(EMOJI) /* * NOTE: As some emojis take up two cells, all of the emoji icons must * be of width 2. Therefore, right pad single-width emoji with a space. */ #define ICON_SIZE 2 #define ICON_PADDING_RIGHT " " #else #define ICON_SIZE 1 #define ICON_PADDING_RIGHT " " #endif #define ICON_PADDING_LEFT "" #define ICON_PADDING_LEFT_LEN (sizeof ICON_PADDING_LEFT - 1) #define ICON_PADDING_RIGHT_LEN (sizeof ICON_PADDING_RIGHT - 1) /* ARROWS */ #define ICON_ARROW_UP ICON_STR(MD_ARROW_UPWARD, "󰁝", "⬆") #define ICON_ARROW_FORWARD ICON_STR(MD_ARROW_FORWARD, "󰁔", "➡") #define ICON_ARROW_DOWN ICON_STR(MD_ARROW_DOWNWARD, "󰁅", "⬇") /* GENERIC */ #define ICON_DIRECTORY ICON_STR(FA_FOLDER, "", "📂") #define ICON_FILE ICON_STR(FA_FILE, "󰈔", "📃") #define ICON_EXEC ICON_STR(FA_COG, "", "⚙️ ") /* Top level and common icons */ #define ICON_ARCHIVE ICON_STR(FA_FILE_ARCHIVE_O, "󰀼", "📦") #define ICON_BRIEFCASE ICON_STR(FA_BRIEFCASE, "󰃖", "💼") #define ICON_C ICON_STR(MFIZZ_C, "", "🇨 ") #define ICON_CHANGELOG ICON_STR(FA_HISTORY, "󰋚", "🔺") #define ICON_CHESS ICON_STR("", "󰄺", "") #define ICON_CLOJURE ICON_STR(MFIZZ_CLOJURE, "", "") #define ICON_CONFIGURE ICON_STR(FILE_CONFIG, "", "🔧") #define ICON_CPLUSPLUS ICON_STR(MFIZZ_CPLUSPLUS, "", ICON_C) #define ICON_DATABASE ICON_STR(MFIZZ_DATABASE_ALT2, "󰆼", "🗃️ ") #define ICON_DESKTOP ICON_STR(FA_DESKTOP, "󰟀", "🖥️ ") #define ICON_DJVU ICON_STR(FA_PAPERCLIP, "", "📎") #define ICON_DOCUMENT ICON_STR(FA_FILE_TEXT_O, "󰈙", "🗒 ") #define ICON_DOWNLOADS ICON_STR(FA_DOWNLOAD, "󰃘", "📥") #define ICON_ELIXIR ICON_STR(MFIZZ_ELIXIR, "", "💧") #define ICON_ENCRYPT ICON_STR("", "󰌆", "🔒") #define ICON_FSHARP ICON_STR(DEV_FSHARP, "", "") #define ICON_FONT ICON_STR(FILE_FONT, "", "") #define ICON_GIT ICON_STR(FA_GIT, "", "🌱") #define ICON_HASKELL ICON_STR("", "", "") #define ICON_HTML ICON_STR(FA_FILE_CODE_O, "󰌝", "") #define ICON_JAVA ICON_STR(MFIZZ_JAVA, "", "☕") #define ICON_JAVASCRIPT ICON_STR(FA_FILE_CODE_O, "󰌞", "") #define ICON_LICENSE ICON_STR(FA_COPYRIGHT, "󰈙", "⚖️ ") #define ICON_LINUX ICON_STR(FA_LINUX, "󰌽", "🐧") #define ICON_MAKEFILE ICON_STR(FILE_CMAKE, "󰆍", "🛠 ") #define ICON_MANUAL ICON_STR(FILE_MANPAGE, "󱓷", "❓") #define ICON_MS_EXCEL ICON_STR(FILE_EXCEL, "󰈛", ICON_WORDDOC) #define ICON_MUSIC ICON_STR(FA_MUSIC, "󱍙", "🎧") #define ICON_MUSICFILE ICON_STR(FA_FILE_AUDIO_O, "󰎈", ICON_MUSIC) #define ICON_OPTICALDISK ICON_STR(LINEA_MUSIC_CD, "", "💿") #define ICON_PDF ICON_STR(FA_FILE_PDF_O, "󰈦", "📕") #define ICON_PHOTOSHOP ICON_STR(DEV_PHOTOSHOP, "", ICON_PICTUREFILE) #define ICON_PICTUREFILE ICON_STR(FA_FILE_IMAGE_O, "󰈟", ICON_PICTURES) #define ICON_PICTURES ICON_STR(MD_CAMERA_ALT, "󰉔", "🎨") #define ICON_PLAYLIST ICON_STR(ICON_MUSICFILE, "󱍙", "") #define ICON_POWERPOINT ICON_STR(FILE_POWERPOINT, "󰈧", "📊") #define ICON_PUBLIC ICON_STR(FA_INBOX, "", "👀") #define ICON_PYTHON ICON_STR(MFIZZ_PYTHON, "", "🐍") #define ICON_REACT ICON_STR(FILE_JSX, "", ICON_JAVASCRIPT) #define ICON_RUBY ICON_STR(MFIZZ_RUBY, "", "💎") #define ICON_RUST ICON_STR(DEV_RUST, "", "") #define ICON_SASS ICON_STR("", "", "") #define ICON_SCRIPT ICON_STR(MFIZZ_SCRIPT, "", "📜") #define ICON_SUBTITLE ICON_STR(FA_COMMENTS_O, "󰅺", "💬") #define ICON_TEMPLATES ICON_STR(FA_PAPERCLIP, "󰗇", "📎") #define ICON_TEX ICON_STR(FILE_TEX, "󰙩", ICON_DOCUMENT) #define ICON_VIDEOFILE ICON_STR(FA_FILE_MOVIE_O, "󰈫", ICON_VIDEOS) #define ICON_VIDEOS ICON_STR(FA_FILM, "󰈰", "🎞 ") #define ICON_VIM ICON_STR(DEV_VIM, "", "") #define ICON_WORDDOC ICON_STR(FILE_WORD, "󰈬", "📘") #define ICON_EXT_ASM ICON_STR(FILE_NASM, "", "") #define ICON_EXT_BIN ICON_STR(OCT_FILE_BINARY, "", "📓") #define ICON_EXT_COFFEE ICON_STR(MFIZZ_COFFEE_BEAN, "", "") #define ICON_EXT_CSS ICON_STR(MFIZZ_CSS3, "", "🦋") #define ICON_EXT_DEB ICON_STR(MFIZZ_DEBIAN, "", ICON_LINUX) #define ICON_EXT_DIFF ICON_STR(FILE_DIFF, "", "📋") #define ICON_EXT_GO ICON_STR(MFIZZ_GO, "󰟓", "") #define ICON_EXT_JSON ICON_STR(ICON_JAVASCRIPT, "", ICON_JAVASCRIPT) #define ICON_EXT_LUA ICON_STR(FILE_LUA, "", "🌘") #define ICON_EXT_M ICON_STR("", "󰠞", "📊") #define ICON_EXT_MAT ICON_STR("", "", "") #define ICON_EXT_MD ICON_STR(DEV_MARKDOWN, "", "📝") #define ICON_EXT_MSI ICON_STR(FA_WINDOWS, "󰍲", "🪟") #define ICON_EXT_NIX ICON_STR("", "", "") #define ICON_EXT_PATCH ICON_STR(FILE_PATCH, "", "🩹") #define ICON_EXT_PHP ICON_STR(MFIZZ_PHP, "", "🌐") #define ICON_EXT_ROM ICON_STR(FA_LOCK, "󰊖", "") #define ICON_EXT_RSS ICON_STR(FA_RSS_SQUARE, "", "📡") #define ICON_EXT_RTF ICON_STR(ICON_PDF, "󰈦", ICON_PDF) #define ICON_EXT_SCALA ICON_STR(MFIZZ_SCALA, "", "") #define ICON_EXT_SLN ICON_STR(DEV_VISUALSTUDIO, "", "") #define ICON_EXT_TS ICON_STR(FILE_TS, "", "") /* * Hex xterm 256 color code, 0 to follow file specific (if any) * Codes: https://jonasjacek.github.io/colors/ * Spectrum sorted: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg * Color names: https://www.ditig.com/256-colors-cheat-sheet */ #define COLOR_LIST \ COLOR_X(COLOR_VIDEO, 45) /* Turquoise2 */ \ COLOR_X(COLOR_VIDEO1, 226) /* Yellow1 */ \ COLOR_X(COLOR_AUDIO, 220) /* Gold1 */ \ COLOR_X(COLOR_AUDIO1, 205) /* HotPink */ \ COLOR_X(COLOR_IMAGE, 82) /* Chartreuse2 */ \ COLOR_X(COLOR_DOCS, 202) /* OrangeRed1 */ \ COLOR_X(COLOR_ARCHIVE, 209) /* Salmon1 */ \ COLOR_X(COLOR_C, 81) /* SteelBlue1 */ \ COLOR_X(COLOR_JAVA, 32) /* DeepSkyBlue3 */ \ COLOR_X(COLOR_JAVASCRIPT, 47) /* SpringGreen2 */ \ COLOR_X(COLOR_REACT, 39) /* DeepSkyBlue1 */ \ COLOR_X(COLOR_CSS, 199) /* DeepPink1 */ \ COLOR_X(COLOR_PYTHON, 227) /* LightGoldenrod1 */ \ COLOR_X(COLOR_LUA, 19) /* Blue3 */ \ COLOR_X(COLOR_DOCUMENT, 15) /* White */ \ COLOR_X(COLOR_FSHARP, 31) /* DeepSkyBlue3 */ \ COLOR_X(COLOR_RUBY, 160) /* Red3 */ \ COLOR_X(COLOR_SCALA, 196) /* Red1 */ \ COLOR_X(COLOR_SHELL, 47) /* SpringGreen2 */ \ COLOR_X(COLOR_VIM, 28) /* Green4 */ \ COLOR_X(COLOR_ELIXIR, 104) /* MediumPurple */ \ /* X-Macro: https://en.wikipedia.org/wiki/X_Macro */ #define COLOR_X(N, V) N = (V), enum { COLOR_LIST }; #undef COLOR_X #define COLOR_X(N, V) N, static const unsigned char init_colors[] = { COLOR_LIST }; #undef COLOR_X #ifdef ICONS_GENERATE /* temporary struct using `char *`. the hash-table generator will * output a more optimized version which uses `char[]` instead reducing * indirection and the total binary size. */ struct icon_pair { const char *match; const char *icon; unsigned char color; }; #endif struct icon { const char *icon; unsigned char color; }; static const struct icon dir_icon = {ICON_DIRECTORY, 0}; static const struct icon file_icon = {ICON_FILE, 0}; static const struct icon exec_icon = {ICON_EXEC, 0}; static const struct icon_pair icons_name[] = { {".git", ICON_GIT, 0}, {"Desktop", ICON_DESKTOP, 0}, {"Documents", ICON_BRIEFCASE, 0}, {"Downloads", ICON_DOWNLOADS, 0}, {"Music", ICON_MUSIC, 0}, {"Pictures", ICON_PICTURES, 0}, {"Public", ICON_PUBLIC, 0}, {"Templates", ICON_TEMPLATES, 0}, {"Videos", ICON_VIDEOS, 0}, {"CHANGELOG", ICON_CHANGELOG, COLOR_DOCS}, {"configure", ICON_CONFIGURE, 0}, {"License", ICON_LICENSE, COLOR_DOCS}, {"Makefile", ICON_MAKEFILE, 0}, }; #ifdef ICONS_GENERATE /* * The goal here is to provide a small set of default values. We don't try to * provide icons for everything under the sun because keeping a _huge_ table of * icons would: increase binary size, increase memory usage, decrease performance. * * Users are free to customize this *locally* as they see fit. Only open a * pull-request if you think your changes are aligned with the goal described * above. */ static const struct icon_pair icons_ext[] = { /* All entries are case-insensitive */ /* Numbers */ {"1", ICON_MANUAL, COLOR_DOCS}, {"7z", ICON_ARCHIVE, COLOR_ARCHIVE}, /* A */ {"a", ICON_MANUAL, 0}, {"apk", ICON_ARCHIVE, COLOR_ARCHIVE}, {"asm", ICON_EXT_ASM, 0}, {"aup", ICON_MUSICFILE, COLOR_AUDIO}, {"avi", ICON_VIDEOFILE, COLOR_VIDEO}, /* B */ {"bat", ICON_SCRIPT, 0}, {"bib", ICON_TEX, 0}, {"bin", ICON_EXT_BIN, 0}, {"bmp", ICON_PICTUREFILE, COLOR_IMAGE}, {"bz2", ICON_ARCHIVE, COLOR_ARCHIVE}, /* C */ {"c", ICON_C, COLOR_C}, {"c++", ICON_CPLUSPLUS, COLOR_C}, {"cabal", ICON_HASKELL, COLOR_VIDEO}, {"cab", ICON_ARCHIVE, COLOR_ARCHIVE}, {"cbr", ICON_ARCHIVE, COLOR_ARCHIVE}, {"cbz", ICON_ARCHIVE, COLOR_ARCHIVE}, {"cc", ICON_CPLUSPLUS, COLOR_C}, {"class", ICON_JAVA, COLOR_JAVA}, {"clj", ICON_CLOJURE, 0}, {"cljc", ICON_CLOJURE, 0}, {"cljs", ICON_CLOJURE, 0}, {"cls", ICON_TEX, 0}, {"cmake", ICON_MAKEFILE, 0}, {"coffee", ICON_EXT_COFFEE, 0}, {"conf", ICON_CONFIGURE, 0}, {"cpio", ICON_ARCHIVE, COLOR_ARCHIVE}, {"cpp", ICON_CPLUSPLUS, COLOR_C}, {"css", ICON_EXT_CSS, COLOR_CSS}, {"cue", ICON_PLAYLIST, COLOR_AUDIO}, {"cvs", ICON_CONFIGURE, 0}, {"cxx", ICON_CPLUSPLUS, COLOR_C}, /* D */ {"db", ICON_DATABASE, 0}, {"deb", ICON_EXT_DEB, COLOR_ARCHIVE}, {"diff", ICON_EXT_DIFF, 0}, {"dll", ICON_SCRIPT, 0}, {"djvu", ICON_DJVU, COLOR_DOCS}, {"doc", ICON_WORDDOC, COLOR_DOCUMENT}, {"docx", ICON_WORDDOC, COLOR_DOCUMENT}, /* E */ {"ejs", ICON_JAVASCRIPT, COLOR_JAVASCRIPT}, {"elf", ICON_LINUX, 0}, {"epub", ICON_PDF, COLOR_DOCS}, {"exe", ICON_EXEC, 0}, {"ex", ICON_ELIXIR, COLOR_ELIXIR}, {"eex", ICON_ELIXIR, COLOR_ELIXIR}, {"exs", ICON_ELIXIR, COLOR_ELIXIR}, /* F */ {"f#", ICON_FSHARP, COLOR_FSHARP}, {"fen", ICON_CHESS, 0}, {"flac", ICON_MUSICFILE, COLOR_AUDIO1}, {"flv", ICON_VIDEOFILE, COLOR_VIDEO}, {"fs", ICON_FSHARP, COLOR_FSHARP}, {"fsi", ICON_FSHARP, COLOR_FSHARP}, {"fsscript", ICON_FSHARP, COLOR_FSHARP}, {"fsx", ICON_FSHARP, COLOR_FSHARP}, /* G */ {"gem", ICON_RUBY, COLOR_RUBY}, {"gif", ICON_PICTUREFILE, COLOR_IMAGE}, {"go", ICON_EXT_GO, COLOR_C}, {"gpg", ICON_ENCRYPT, COLOR_ARCHIVE}, {"gz", ICON_ARCHIVE, COLOR_ARCHIVE}, {"gzip", ICON_ARCHIVE, COLOR_ARCHIVE}, /* H */ {"h", ICON_C, COLOR_C}, {"hh", ICON_CPLUSPLUS, COLOR_C}, {"hpp", ICON_CPLUSPLUS, COLOR_C}, {"hs", ICON_HASKELL, COLOR_ELIXIR}, {"htaccess", ICON_CONFIGURE, 0}, {"htpasswd", ICON_CONFIGURE, 0}, {"htm", ICON_HTML, 0}, {"html", ICON_HTML, 0}, {"hxx", ICON_CPLUSPLUS, COLOR_C}, {"heex", ICON_ELIXIR, COLOR_ELIXIR}, /* I */ {"ico", ICON_PICTUREFILE, COLOR_IMAGE}, {"ini", ICON_CONFIGURE, 0}, {"img", ICON_OPTICALDISK, COLOR_ARCHIVE}, {"iso", ICON_OPTICALDISK, COLOR_ARCHIVE}, /* J */ {"jar", ICON_JAVA, COLOR_JAVA}, {"java", ICON_JAVA, COLOR_JAVA}, {"jl", ICON_CONFIGURE, 0}, {"jpeg", ICON_PICTUREFILE, COLOR_IMAGE}, {"jpg", ICON_PICTUREFILE, COLOR_IMAGE}, {"js", ICON_JAVASCRIPT, COLOR_JAVASCRIPT}, {"json", ICON_EXT_JSON, COLOR_JAVASCRIPT}, {"jsx", ICON_REACT, COLOR_REACT}, {"jxl", ICON_PICTUREFILE, COLOR_IMAGE}, /* K */ {"ksh", ICON_SCRIPT, COLOR_SHELL}, /* L */ {"lha", ICON_ARCHIVE, COLOR_ARCHIVE}, {"lhs", ICON_HASKELL, COLOR_VIM}, {"log", ICON_DOCUMENT, 0}, {"lua", ICON_EXT_LUA, COLOR_LUA}, {"lzh", ICON_ARCHIVE, COLOR_ARCHIVE}, {"lzma", ICON_ARCHIVE, COLOR_ARCHIVE}, /* M */ {"m", ICON_EXT_M, COLOR_C}, {"m4a", ICON_MUSICFILE, COLOR_AUDIO}, {"m4v", ICON_VIDEOFILE, COLOR_VIDEO}, {"markdown", ICON_EXT_MD, COLOR_DOCS}, {"mat", ICON_EXT_MAT, COLOR_C}, {"md", ICON_EXT_MD, COLOR_DOCS}, {"mk", ICON_MAKEFILE, 0}, {"mkv", ICON_VIDEOFILE, COLOR_VIDEO}, {"mov", ICON_VIDEOFILE, COLOR_VIDEO}, {"mp3", ICON_MUSICFILE, COLOR_AUDIO}, {"mp4", ICON_VIDEOFILE, COLOR_VIDEO1}, {"mpeg", ICON_VIDEOFILE, COLOR_VIDEO}, {"mpg", ICON_VIDEOFILE, COLOR_VIDEO}, {"msi", ICON_EXT_MSI, 0}, /* N */ {"nix", ICON_EXT_NIX, COLOR_FSHARP}, /* O */ {"o", ICON_MANUAL, 0}, {"ogg", ICON_MUSICFILE, COLOR_AUDIO}, {"opus", ICON_MUSICFILE, COLOR_AUDIO}, {"opdownload", ICON_DOWNLOADS, 0}, {"otf", ICON_FONT, 0}, {"out", ICON_LINUX, 0}, /* P */ {"part", ICON_DOWNLOADS, 0}, {"patch", ICON_EXT_PATCH, 0}, {"pdf", ICON_PDF, COLOR_DOCS}, {"pgn", ICON_CHESS, 0}, {"php", ICON_EXT_PHP, 0}, {"png", ICON_PICTUREFILE, COLOR_IMAGE}, {"ppt", ICON_POWERPOINT, 0}, {"pptx", ICON_POWERPOINT, 0}, {"psb", ICON_PHOTOSHOP, 0}, {"psd", ICON_PHOTOSHOP, 0}, {"py", ICON_PYTHON, COLOR_PYTHON}, {"pyc", ICON_PYTHON, COLOR_PYTHON}, {"pyd", ICON_PYTHON, COLOR_PYTHON}, {"pyo", ICON_PYTHON, COLOR_PYTHON}, /* Q */ /* R */ {"rar", ICON_ARCHIVE, COLOR_ARCHIVE}, {"rb", ICON_RUBY, COLOR_RUBY}, {"rc", ICON_CONFIGURE, 0}, {"rom", ICON_EXT_ROM, 0}, {"rpm", ICON_ARCHIVE, COLOR_ARCHIVE}, {"rs", ICON_RUST, COLOR_DOCS}, {"rss", ICON_EXT_RSS, 0}, {"rtf", ICON_EXT_RTF, 0}, /* S */ {"sass", ICON_SASS, COLOR_CSS}, {"scss", ICON_SASS, COLOR_CSS}, {"so", ICON_MANUAL, 0}, {"scala", ICON_EXT_SCALA, COLOR_SCALA}, {"sh", ICON_SCRIPT, COLOR_SHELL}, {"slim", ICON_SCRIPT, COLOR_DOCUMENT}, {"sln", ICON_EXT_SLN, 0}, {"sql", ICON_DATABASE, 0}, {"srt", ICON_SUBTITLE, 0}, {"sty", ICON_TEX, 0}, {"sub", ICON_SUBTITLE, 0}, {"svg", ICON_PICTUREFILE, COLOR_IMAGE}, /* T */ {"tar", ICON_ARCHIVE, COLOR_ARCHIVE}, {"tex", ICON_TEX, 0}, {"tgz", ICON_ARCHIVE, COLOR_ARCHIVE}, {"ts", ICON_EXT_TS, COLOR_JAVASCRIPT}, {"tsx", ICON_REACT, COLOR_REACT}, {"txt", ICON_DOCUMENT, COLOR_DOCUMENT}, {"txz", ICON_ARCHIVE, COLOR_ARCHIVE}, {"ttf", ICON_FONT, 0}, /* U */ /* V */ {"vid", ICON_VIDEOFILE, COLOR_VIDEO}, {"vim", ICON_VIM, COLOR_VIM}, {"vimrc", ICON_VIM, COLOR_VIM}, {"vtt", ICON_SUBTITLE, 0}, /* W */ {"wav", ICON_MUSICFILE, COLOR_AUDIO}, {"webm", ICON_VIDEOFILE, COLOR_VIDEO}, {"webp", ICON_PICTUREFILE, COLOR_IMAGE}, {"wma", ICON_VIDEOFILE, COLOR_AUDIO}, {"wmv", ICON_VIDEOFILE, COLOR_VIDEO}, /* X */ {"xbps", ICON_ARCHIVE, COLOR_ARCHIVE}, {"xcf", ICON_PICTUREFILE, COLOR_IMAGE}, {"xhtml", ICON_HTML, 0}, {"xls", ICON_MS_EXCEL, 0}, {"xlsx", ICON_MS_EXCEL, 0}, {"xml", ICON_HTML, 0}, {"xz", ICON_ARCHIVE, COLOR_ARCHIVE}, /* Y */ {"yaml", ICON_CONFIGURE, COLOR_DOCUMENT}, {"yml", ICON_CONFIGURE, COLOR_DOCUMENT}, /* Z */ {"zip", ICON_ARCHIVE, COLOR_ARCHIVE}, {"zsh", ICON_SCRIPT, COLOR_SHELL}, {"zst", ICON_ARCHIVE, COLOR_ARCHIVE}, /* Other */ }; #endif #endif /* defined(ICONS_GENERATE) || defined(ICONS_ENABLED) */ #endif /* INCLUDE_ICONS_H */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/nnn.c�����������������������������������������������������������������������������������0000664�0000000�0000000�00000627067�14663100143�0013713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * BSD 2-Clause License * * Copyright (C) 2014-2016, Lazaros Koromilas <lostd@2f30.org> * Copyright (C) 2014-2016, Dimitris Papastamos <sin@2f30.org> * Copyright (C) 2016-2024, Arun Prakash Jana <engineerarun@gmail.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * 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 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _FILE_OFFSET_BITS 64 /* Support large files on 32-bit glibc */ #if defined(__linux__) || defined(MINGW) || defined(__MINGW32__) \ || defined(__MINGW64__) || defined(__CYGWIN__) #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #if defined(__linux__) #include <sys/inotify.h> #define LINUX_INOTIFY #endif #if !defined(__GLIBC__) #include <sys/types.h> #endif #endif #include <sys/resource.h> #include <sys/stat.h> #include <sys/statvfs.h> #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> #define BSD_KQUEUE #elif defined(__HAIKU__) #include "../misc/haiku/haiku_interop.h" #define HAIKU_NM #else #include <sys/sysmacros.h> #endif #include <sys/wait.h> #ifdef __linux__ /* Fix failure due to mvaddnwstr() */ #ifndef NCURSES_WIDECHAR #define NCURSES_WIDECHAR 1 #endif #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ || defined(__APPLE__) || defined(__sun) #ifndef _XOPEN_SOURCE_EXTENDED #define _XOPEN_SOURCE_EXTENDED #endif #endif #ifndef __USE_XOPEN /* Fix wcswidth() failure, ncursesw/curses.h includes whcar.h on Ubuntu 14.04 */ #define __USE_XOPEN #endif #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <fts.h> #include <libgen.h> #include <limits.h> #ifndef NOLC #include <locale.h> #endif #include <pthread.h> #include <stdio.h> #ifndef NORL #include <readline/history.h> #include <readline/readline.h> #endif #ifdef PCRE #include <pcre.h> #else #include <regex.h> #endif #include <signal.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <time.h> #include <unistd.h> #include <stddef.h> #include <stdalign.h> #ifndef __USE_XOPEN_EXTENDED #define __USE_XOPEN_EXTENDED 1 #endif #include <ftw.h> #include <pwd.h> #include <grp.h> #ifdef MACOS_BELOW_1012 #include "../misc/macos-legacy/mach_gettime.h" #endif #if !defined(alloca) && defined(__GNUC__) /* * GCC doesn't expand alloca() to __builtin_alloca() in standards mode * (-std=...) and not all standard libraries do or supply it, e.g. * NetBSD/arm64 so explicitly use the builtin. */ #define alloca(size) __builtin_alloca(size) #endif #include "nnn.h" #include "dbg.h" #if defined(ICONS_IN_TERM) || defined(NERD) || defined(EMOJI) #define ICONS_ENABLED #include ICONS_INCLUDE #include "icons-hash.c" #include "icons.h" #endif #if defined(ICONS_ENABLED) && defined(__APPLE__) /* * For some reason, wcswidth returns 2 for certain icons on macOS * leading to duplicated first characters in filenames when navigating. * https://github.com/jarun/nnn/issues/1692 * There might be a better way to fix it without requiring a refresh. */ #define macos_icons_hack() do { clrtoeol(); refresh(); } while(0) #else #define macos_icons_hack() #endif #ifdef TOURBIN_QSORT #include "qsort.h" #endif /* Macro definitions */ #define VERSION "5.0" #define GENERAL_INFO "BSD 2-Clause\nhttps://github.com/jarun/nnn" #ifndef NOSSN #define SESSIONS_VERSION 1 #endif #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 accommodating fallback values. */ #ifndef NAME_MAX #define NAME_MAX 255 #endif #ifndef PATH_MAX #define PATH_MAX 4096 #endif #define _ABSSUB(N, M) (((N) <= (M)) ? ((M) - (N)) : ((N) - (M))) #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 TOLOWER(ch) (((ch) >= 'A' && (ch) <= 'Z') ? ((ch) - 'A' + 'a') : (ch)) #define ISUPPER_(ch) ((ch) >= 'A' && (ch) <= 'Z') #define ISLOWER_(ch) ((ch) >= 'a' && (ch) <= 'z') #define CMD_LEN_MAX (PATH_MAX + ((NAME_MAX + 1) << 1)) #define ALIGN_UP(x, A) ((((x) + (A) - 1) / (A)) * (A)) #define READLINE_MAX 256 #define FILTER '/' #define RFILTER '\\' #define CASE ':' #define MSGWAIT '$' #define SELECT ' ' #define PROMPT ">>> " #undef NEWLINE #define NEWLINE "\n" #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 file name = 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 10 #define LIST_FILES_MAX (1 << 14) /* Support listing 16K files */ #define LIST_INPUT_MAX ((size_t)LIST_FILES_MAX * PATH_MAX) #define SCROLLOFF 3 #define COLOR_256 256 #define CREATE_NEW_KEY (-1) /* Time intervals */ #define DBLCLK_INTERVAL_NS (400000000) #define XDELAY_INTERVAL_MS (350000) /* 350 ms delay */ #ifndef CTX8 #define CTX_MAX 4 #else #define CTX_MAX 8 #endif #ifndef SED /* BSDs or Solaris or SunOS */ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(sun) || defined(__sun) #define SED "gsed" #else #define SED "sed" #endif #endif /* Large selection threshold */ #ifndef LARGESEL #define LARGESEL 1000 #endif #define MIN_DISPLAY_COL (CTX_MAX * 2) #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_DIRLNK 0x01 #define HARD_LINK 0x02 #define SYM_ORPHAN 0x04 #define FILE_MISSING 0x08 #define FILE_SELECTED 0x10 #define FILE_SCANNED 0x20 #define FILE_YOUNG 0x40 /* 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 stderr (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_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_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 VFS_AVAIL 0 #define VFS_USED 1 #define VFS_SIZE 2 /* TYPE DEFINITIONS */ typedef unsigned int uint_t; typedef unsigned char uchar_t; typedef unsigned short ushort_t; typedef unsigned long long ullong_t; /* STRUCTURES */ /* Directory entry */ typedef struct entry { char *name; /* 8 bytes */ time_t sec; /* 8 bytes */ uint_t nsec; /* 4 bytes (enough to store nanosec) */ mode_t mode; /* 4 bytes */ off_t size; /* 8 bytes */ struct { ullong_t blocks : 40; /* 5 bytes (enough for 512 TiB in 512B blocks allocated) */ ullong_t nlen : 16; /* 2 bytes (length of file name) */ ullong_t flags : 8; /* 1 byte (flags specific to the file) */ }; #ifndef NOUG uid_t uid; /* 4 bytes */ gid_t gid; /* 4 bytes */ #endif } *pEntry; /* Selection marker */ typedef struct { char *startpos; size_t len; } selmark; /* 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 */ typedef struct { uint_t filtermode : 1; /* Set to enter filter mode */ uint_t timeorder : 1; /* Set to sort by time */ uint_t sizeorder : 1; /* Set to sort by file size */ uint_t apparentsz : 1; /* Set to sort by apparent size (disk usage) */ uint_t blkorder : 1; /* Set to sort by blocks used (disk usage) */ uint_t extnorder : 1; /* Order by extension */ uint_t showhidden : 1; /* Set to show hidden files */ uint_t reserved0 : 1; uint_t showdetail : 1; /* Clear to show lesser file info */ uint_t ctxactive : 1; /* Context active or not */ uint_t reverse : 1; /* Reverse sort */ uint_t version : 1; /* Version sort */ uint_t reserved1 : 1; /* The following settings are global */ uint_t curctx : 3; /* Current context number */ uint_t prefersel : 1; /* Prefer selection over current, if exists */ uint_t fileinfo : 1; /* Show file information on hover */ uint_t nonavopen : 1; /* Open file on right arrow or `l` */ uint_t autoenter : 1; /* auto-enter dir in type-to-nav mode */ uint_t reserved2 : 1; uint_t useeditor : 1; /* Use VISUAL to open text files */ uint_t reserved3 : 3; uint_t regex : 1; /* Use regex filters */ uint_t x11 : 1; /* Copy to system clipboard, show notis, xterm title */ uint_t timetype : 2; /* Time sort type (0: access, 1: change, 2: modification) */ uint_t cliopener : 1; /* All-CLI app opener */ uint_t waitedit : 1; /* For ops that can't be detached, used EDITOR */ uint_t rollover : 1; /* Roll over at edges */ } settings; /* Non-persistent program-internal states (alphabeical order) */ typedef struct { uint_t autofifo : 1; /* Auto-create NNN_FIFO */ uint_t autonext : 1; /* Auto-advance on file open */ uint_t dircolor : 1; /* Current status of dir color */ uint_t dirctx : 1; /* Show dirs in context color */ uint_t duinit : 1; /* Initialize disk usage */ uint_t fifomode : 1; /* FIFO notify mode: 0: preview, 1: explorer */ uint_t forcequit : 1; /* Do not prompt on quit */ uint_t initfile : 1; /* Positional arg is a file */ uint_t interrupt : 1; /* Program received an interrupt */ uint_t move : 1; /* Move operation */ uint_t oldcolor : 1; /* Use older colorscheme */ uint_t picked : 1; /* Plugin has picked files */ 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 runplugin : 1; /* Choose plugin mode */ uint_t selbm : 1; /* Select a bookmark from bookmarks directory */ uint_t selmode : 1; /* Set when selecting files */ uint_t stayonsel : 1; /* Disable auto-advance on selection */ uint_t trash : 2; /* Trash method 0: rm -rf, 1: trash-cli, 2: gio trash */ uint_t uidgid : 1; /* Show owner and group info */ uint_t usebsdtar : 1; /* Use bsdtar as default archive utility */ uint_t xprompt : 1; /* Use native prompt instead of readline prompt */ uint_t showlines : 1; /* Show line numbers */ uint_t reserved : 3; /* Adjust when adding/removing a field */ } 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_t color; /* Color code for directories */ } context; #ifndef NOSSN 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; #endif /* GLOBALS */ /* Configuration, contexts */ static settings cfg = { .ctxactive = 1, .autoenter = 1, .timetype = 2, /* T_MOD */ .rollover = 1, }; alignas(max_align_t) static context g_ctx[CTX_MAX]; 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 time_t gtimesecs; static uint_t idletimeout, selbufpos, selbuflen; static ushort_t xlines, xcols; static ushort_t idle; static uchar_t maxbm, maxplug, maxorder; static uchar_t cfgsort[CTX_MAX + 1]; static char *bmstr; static char *pluginstr; static char *orderstr; 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, *findselpos; static char *mark; #ifndef NOX11 static char hostname[_POSIX_HOST_NAME_MAX + 1]; #endif #ifndef NOFIFO static char *fifopath; #endif static char *lastcmd; static ullong_t *ihashbmp; static struct entry *pdents; static blkcnt_t dir_blocks; static kv *bookmark; static kv *plug; static kv *order; static uchar_t tmpfplen, homelen; static uchar_t 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 /* pthread related */ #define NUM_DU_THREADS (4) /* Can use sysconf(_SC_NPROCESSORS_ONLN) */ #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) static int threadbmp = -1; /* Has 1 in the bit position for idle threads */ static volatile int active_threads; static pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t hardlink_mutex = PTHREAD_MUTEX_INITIALIZER; static ullong_t *core_files; static blkcnt_t *core_blocks; static ullong_t num_files; typedef struct { char path[PATH_MAX]; int entnum; ushort_t core; bool mntpoint; } thread_data; static thread_data *core_data; /* Retain old signal handlers */ static struct sigaction oldsighup; static struct sigaction oldsigtstp; static struct sigaction oldsigwinch; /* For use in functions which are isolated and don't return the buffer */ alignas(max_align_t) static char g_buf[CMD_LEN_MAX]; /* For use as a scratch buffer in selection manipulation */ alignas(max_align_t) static char g_sel[PATH_MAX]; /* Buffer to store tmp file path to show selection, file stats and help */ alignas(max_align_t) static char g_tmpfpath[TMP_LEN_MAX]; /* Buffer to store plugins control pipe location */ alignas(max_align_t) static char g_pipepath[TMP_LEN_MAX]; /* 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_SSHFS 9 #define UTIL_RCLONE 10 #define UTIL_VI 11 #define UTIL_LESS 12 #define UTIL_SH 13 #define UTIL_FZF 14 #define UTIL_NTFY 15 #define UTIL_CBCP 16 #define UTIL_NMV 17 #define UTIL_TRASH_CLI 18 #define UTIL_GIO_TRASH 19 #define UTIL_RM_RF 20 #define UTIL_ARCHMNT 21 /* 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", "sshfs", "rclone", "vi", "less", "sh", "fzf", ".ntfy", ".cbcp", ".nmv", "trash-put", "gio trash", "rm -rf --", "archivemount", }; /* Common strings */ #define MSG_ZERO 0 /* Unused */ #define MSG_0_ENTRIES 1 #define STR_TMPFILE 2 #define MSG_0_SELECTED 3 #define MSG_CANCEL 4 #define MSG_FAILED 5 #define MSG_SSN_NAME 6 #define MSG_CP_MV_AS 7 #define MSG_CUR_SEL_OPTS 8 #define MSG_FILE_LIMIT 9 #define MSG_SIZE_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_NEW_PATH 19 #define MSG_LINK_PREFIX 20 #define MSG_COPY_NAME 21 #define MSG_ENTER 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_KEYS 34 #define MSG_INVALID_REG 35 #define MSG_ORDER 36 #define MSG_LAZY 37 #define MSG_FIRST 38 #define MSG_RM_TMP 39 #define MSG_INVALID_KEY 40 #define MSG_NOCHANGE 41 #define MSG_DIR_CHANGED 42 #define MSG_BM_NAME 43 static const char * const messages[] = { "", "0 entries", "/.nnnXXXXXX", "0 selected", "cancelled", "failed!", "session name: ", "'c'p/'m'v as?", "'c'urrent/'s'el?", "file limit exceeded", "size limit exceeded", "['f'ile]/'d'ir/'s'ym/'h'ard?", "['g'ui]/'c'li?", "overwrite?", "'s'ave/'l'oad/'r'estore?", "Quit all contexts?", "remote name (- for hovered): ", "archive [path/]name: ", "open with: ", "[path/]name: ", "link prefix [@ for none]: ", "copy [path/]name: ", "\n'Enter' to continue", "open failed", "dir inaccessible", "empty! edit/open with", "?", "not set", "entry exists", "too few cols!", "'s'shfs/'r'clone?", "refresh if slow", "app: ", "['l's]/'o'pen/e'x'tract/'m'nt?", "keys:", "invalid regex", "'a'u/'d'u/'e'xt/'r'ev/'s'z/'t'm/'v'er/'c'lr/'^T'?", "unmount failed! try lazy?", "first file (\')/char?", "remove tmp file?", "invalid key", "unchanged", "dir changed, range sel off", "name: ", }; /* 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 NNN_FCOLORS 5 #define NNNLVL 6 #define NNN_PIPE 7 #define NNN_MCLICK 8 #define NNN_SEL 9 #define NNN_ARCHIVE 10 #define NNN_ORDER 11 #define NNN_HELP 12 /* strings end here */ #define NNN_TRASH 13 /* flags begin here */ static const char * const env_cfg[] = { "NNN_OPTS", "NNN_BMS", "NNN_PLUG", "NNN_OPENER", "NNN_COLORS", "NNN_FCOLORS", "NNNLVL", "NNN_PIPE", "NNN_MCLICK", "NNN_SEL", "NNN_ARCHIVE", "NNN_ORDER", "NNN_HELP", "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 #define PROGRESS_CP "cpg -giRp --" #define PROGRESS_MV "mvg -gi --" static char cp[sizeof PROGRESS_CP] = "cp -iRp --"; static char mv[sizeof PROGRESS_MV] = "mv -i --"; /* Archive commands */ static char * const archive_cmd[] = {"atool -a", "bsdtar -acvf", "zip -r", "tar -acvf"}; /* Tokens used for path creation */ #define TOK_BM 0 #define TOK_SSN 1 #define TOK_MNT 2 #define TOK_PLG 3 static const char * const toks[] = { "bookmarks", "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 #define P_ARCHIVE_CMD 4 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)$", /* Basic formats that don't need external tools */ SED" -i 's|^%s\\(.*\\)$|%s\\1|' %s", "xargs -0 %s %s < '%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_t 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_t 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_t 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 - curscroll, 0) #define exitcurses() endwin() #define printwarn(presel) printwait(strerror(errno), presel) #define istopdir(path) ((path)[1] == '\0' && (path)[0] == '/') #define copycurname() xstrsncpy(lastname, ndents ? pdents[cur].name : "\0", 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 TOURBIN_QSORT #define ENTLESS(i, j) (entrycmpfn(pdents + (i), pdents + (j)) < 0) #define ENTSWAP(i, j) (swap_ent((i), (j))) #define ENTSORT(pdents, ndents, entrycmpfn) QSORT((ndents), ENTLESS, ENTSWAP) #else #define ENTSORT(pdents, ndents, entrycmpfn) qsort((pdents), (ndents), sizeof(*(pdents)), (entrycmpfn)) #endif /* Forward declarations */ static void redraw(char *path); static int spawn(char *file, char *arg1, char *arg2, char *arg3, ushort_t flag); static void move_cursor(int target, int ignore_scrolloff); static char *load_input(int fd, const char *path); static int set_sort_flags(int r); static void statusbar(char *path); static bool get_output(char *file, char *arg1, char *arg2, int fdout, bool page); #ifndef NOFIFO static void notify_fifo(bool force); #endif /* Functions */ static void sigint_handler(int sig) { (void) sig; g_state.interrupt = 1; } static void clean_exit_sighandler(int sig) { (void) sig; exitcurses(); /* This triggers cleanup() thanks to atexit() */ exit(EXIT_SUCCESS); } static char *xitoa(uint_t val) { static char dst[32] = {'\0'}; static const char digits[201] = "0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" "4041424344454647484950515253545556575859" "6061626364656667686970717273747576777879" "8081828384858687888990919293949596979899"; uint_t next = 30, quo, i; while (val >= 100) { quo = val / 100; i = (val - (quo * 100)) * 2; val = quo; dst[next] = digits[i + 1]; dst[--next] = digits[i]; --next; } /* Handle last 1-2 digits */ if (val < 10) dst[next] = '0' + val; else { i = val * 2; dst[next] = digits[i + 1]; dst[--next] = digits[i]; } return &dst[next]; } /* Return the integer value of a char representing HEX */ static uchar_t xchartohex(uchar_t 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_t nr) { nr &= HASH_BITS; pthread_mutex_lock(&hardlink_mutex); ullong_t *m = ihashbmp + (nr >> 6); if (*m & (1 << (nr & 63))) { pthread_mutex_unlock(&hardlink_mutex); return FALSE; } *m |= 1 << (nr & 63); pthread_mutex_unlock(&hardlink_mutex); return TRUE; } #ifndef __APPLE__ /* Increase the limit on open file descriptors, if possible */ static void max_openfds(void) { struct rlimit rl; if (!getrlimit(RLIMIT_NOFILE, &rl)) if (rl.rlim_cur < rl.rlim_max) { rl.rlim_cur = rl.rlim_max; setrlimit(RLIMIT_NOFILE, &rl); } } #endif /* * Wrapper to realloc() * Frees current memory if realloc() fails and returns NULL. * * The *alloc() family returns aligned address: https://man7.org/linux/man-pages/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); return ptr ? memcpy(ptr, s, len) : NULL; } 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 inline bool is_prefix(const char *restrict str, const char *restrict prefix, size_t len) { return !strncmp(str, prefix, len); } static inline bool is_bad_len_or_dir(const char *restrict path) { size_t len = xstrlen(path); return ((len >= PATH_MAX) || (path[len - 1] == '/')); } static char *get_cwd_entry(const char *restrict cwdpath, char *entrypath, size_t *tokenlen) { size_t len = xstrlen(cwdpath); char *end; if (!is_prefix(entrypath, cwdpath, len)) return NULL; entrypath += len + 1; /* Add 1 for trailing / */ end = strchr(entrypath, '/'); if (end) *tokenlen = end - entrypath; else *tokenlen = xstrlen(entrypath); DPRINTF_U(*tokenlen); return entrypath; } /* * The poor man's implementation of memrchr(3). * We are only looking for '/' and '.' in this program. * And we are NOT expecting a '/' at the end. * Ideally 0 < n <= xstrlen(s). */ static void *xmemrchr(uchar_t *restrict s, uchar_t 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_t *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_t *)path, '/', xstrlen(path)); if (base == path) path[1] = '\0'; else *base = '\0'; return path; } static char *xbasename(char *path) { char *base = xmemrchr((uchar_t *)path, '/', xstrlen(path)); // NOLINT return base ? base + 1 : path; } static inline char *xextension(const char *fname, size_t len) { return xmemrchr((uchar_t *)fname, '.', len); } #ifndef NOUG /* * 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 * the guid is not found in the password database. */ static char *getpwname(uid_t uid) { static uint_t uidcache = UINT_MAX; static char *namecache; if (uidcache != uid) { struct passwd *pw = getpwuid(uid); uidcache = uid; namecache = pw ? pw->pw_name : NULL; } return namecache ? namecache : xitoa(uid); } static char *getgrname(gid_t gid) { static uint_t gidcache = UINT_MAX; static char *grpcache; if (gidcache != gid) { struct group *gr = getgrgid(gid); gidcache = gid; grpcache = gr ? gr->gr_name : NULL; } return grpcache ? grpcache : xitoa(gid); } #endif static inline bool getutil(char *util) { return spawn("which", util, NULL, NULL, F_NORMAL | F_NOTRACE) == 0; } static inline bool tilde_is_home(const char *s) { return s[0] == '~' && (s[1] == '\0' || s[1] == '/'); } static inline bool tilde_is_home_strict(const char *s) { return s[0] == '~' && s[1] == '/'; } /* * Updates out with "dir/name or "/name" * 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 */ static size_t mkpath(const char *dir, const char *name, char *out) { size_t len = 0; /* same rational for being strict as abspath() */ if (tilde_is_home_strict(name)) { //NOLINT len = xstrsncpy(out, home, PATH_MAX); --len; ++name; } else if (name[0] != '/') { // NOLINT /* Handle root case */ len = istopdir(dir) ? 1 : 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_t *)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. * Resolves ./../~ in filepath */ static char *abspath(const char *filepath, char *cwd, char *buf) { const char *path = filepath; bool allocated = FALSE; if (!path) return NULL; /* when dealing with tilde, we need to be strict. * otherwise a file named "~" can end up expanding to * $HOME and causing disaster */ if (tilde_is_home_strict(path)) { cwd = home; path += 2; /* advance 2 bytes past the "~/" */ } else if ((path[0] != '/') && !cwd) { cwd = getcwd(NULL, 0); if (!cwd) return NULL; allocated = TRUE; } size_t dst_size = 0, src_size = xstrlen(path), cwd_size = cwd ? xstrlen(cwd) : 0; 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 = buf ? buf : malloc(src_size + cwd_size + 2); if (!resolved_path) { if (allocated) free(cwd); return NULL; } /* Turn relative paths into absolute */ if (path[0] != '/') { if (!cwd) { if (!buf) free(resolved_path); return NULL; } dst_size = xstrsncpy(resolved_path, cwd, cwd_size + 1) - 1; if (allocated) free(cwd); } 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_t *)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'; } if (xstrlen(resolved_path) >= PATH_MAX) { if (!buf) free(resolved_path); else buf[0] = '\0'; return NULL; } return resolved_path; } /* finds abspath of link pointed by filepath, taking cwd into account */ static char *bmtarget(const char *filepath, char *cwd, char *buf) { char target[PATH_MAX + 1]; ssize_t n = readlink(filepath, target, PATH_MAX); if (n != -1) { target[n] = '\0'; return abspath(target, cwd, buf); } return NULL; } /* wraps the argument in single quotes so it can be safely fed to shell */ static bool shell_escape(char *output, size_t outlen, const char *s) { size_t n = xstrlen(s), w = 0; if (s == output) { DPRINTF_S("s == output"); return FALSE; } output[w++] = '\''; /* begin single quote */ for (size_t r = 0; r < n; ++r) { /* potentially too big: 4 for the single quote case, 2 from * outside the loop */ if (w + 6 >= outlen) return FALSE; switch (s[r]) { /* the only thing that has special meaning inside single * quotes are single quotes themselves. */ case '\'': output[w++] = '\''; /* end single quote */ output[w++] = '\\'; /* put \' so it's treated as literal single quote */ output[w++] = '\''; output[w++] = '\''; /* start single quoting again */ break; default: output[w++] = s[r]; break; } } output[w++] = '\''; /* end single quote */ output[w++] = '\0'; /* nul terminator */ return TRUE; } static bool set_tilde_in_path(char *path) { if (is_prefix(path, home, homelen)) { home[homelen] = path[homelen - 1]; path[homelen - 1] = '~'; return TRUE; } return FALSE; } static void reset_tilde_in_path(char *path) { path[homelen - 1] = home[homelen]; home[homelen] = '\0'; } #ifndef NOX11 static void xterm_cfg(char *path) { if (cfg.x11 && !g_state.picker) { /* Signal CWD change to terminal */ printf("\033]7;file://%s%s\033\\", hostname, path); /* Set terminal window title */ bool r = set_tilde_in_path(path); printf("\033]2;%s\007", r ? &path[homelen - 1] : path); fflush(stdout); if (r) reset_tilde_in_path(path); } } #endif static bool convert_tilde(const char *path, char *buf) { if (tilde_is_home(path)) { ssize_t len = xstrlen(home); ssize_t loclen = xstrlen(path); xstrsncpy(buf, home, len + 1); xstrsncpy(buf + len, path + 1, loclen); return true; } return false; } 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; } static void msg(const char *message) { fprintf(stderr, "%s\n", message); } #ifdef KEY_RESIZE static void handle_key_resize(void) { endwin(); refresh(); } /* Clear the old prompt */ static void clearoldprompt(void) { // clear info line move(xlines - 2, 0); clrtoeol(); tolastln(); clrtoeol(); handle_key_resize(); } #endif /* Messages show up at the bottom */ static inline void printmsg_nc(const char *msg) { tolastln(); addstr(msg); clrtoeol(); } 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) { wint_t ch[1]; if (prompt) printmsg(prompt); cleartimeout(); get_wch(ch); #ifdef KEY_RESIZE while (*ch == KEY_RESIZE) { if (prompt) { clearoldprompt(); xlines = LINES; printmsg(prompt); } get_wch(ch); } #endif settimeout(); return (int)*ch; } static bool isselfileempty(void) { struct stat sb; return (stat(selpath, &sb) == -1) || (!sb.st_size); } static int get_cur_or_sel(void) { bool sel = (selbufpos || !isselfileempty()); /* Check both local buffer and selection file for external selection */ if (sel && ndents) { /* If selection is preferred and we have a local selection, return selection. * Always show the prompt in case of an external selection. */ if (cfg.prefersel && selbufpos) return 's'; int choice = get_input(messages[MSG_CUR_SEL_OPTS]); return ((choice == 'c' || choice == 's') ? choice : 0); } if (sel) return 's'; if (ndents) return 'c'; return 0; } static void xdelay(useconds_t delay) { refresh(); usleep(delay); } static char confirm_force(bool selection, bool use_trash) { char str[300]; /* Note: ideally we should use utils[UTIL_RM_RF] instead of the "rm -rf" string */ int r = snprintf(str, 20, "%s", use_trash ? utils[UTIL_GIO_TRASH] + 4 : "rm -rf"); if (selection) snprintf(str + r, 280, " %d files?", nselected); else snprintf(str + r, 280, " '%s'?", pdents[cur].name); r = get_input(str); if (r == ESC) return '\0'; /* cancel */ if (r == 'y' || r == 'Y') return 'f'; /* forceful for rm */ if (r == 'n' || r == 'N') return '\0'; /* cancel */ return (use_trash ? '\0' : 'i'); /* interactive for rm */ } /* Writes buflen char(s) from buf to a file */ static void writesel(const char *buf, const size_t buflen) { if (!selpath) return; int fd = open(selpath, O_CREAT | O_WRONLY | O_TRUNC, S_IWUSR | S_IRUSR); if (fd != -1) { if (write(fd, buf, buflen) != (ssize_t)buflen) printwarn(NULL); close(fd); } 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); } 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 */ static size_t seltofile(int fd, uint_t *pcount, const char *separator) { uint_t 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, separator, 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) { if (isselfileempty()) return FALSE; snprintf(g_buf, CMD_LEN_MAX, "tr \'\\0\' \'\\n\' < %s", selpath); spawn(utils[UTIL_SH_EXEC], g_buf, NULL, 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; } } } static void clearselection(void) { nselected = 0; selbufpos = 0; g_state.selmode = 0; writesel(NULL, 0); } static char *findinsel(char *startpos, int len) { if (!selbufpos) return FALSE; if (!startpos) startpos = pselbuf; char *found = startpos; size_t buflen = selbufpos - (startpos - pselbuf); while (1) { /* memmem(3): not specified in POSIX.1, but present on a number of other systems. */ 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 forward */ if (found >= startpos + buflen) return NULL; } } static int markcmp(const void *va, const void *vb) { const selmark *ma = (selmark*)va; const selmark *mb = (selmark*)vb; return ma->startpos - mb->startpos; } /* scanselforpath() must be called before calling this */ static inline void findmarkentry(size_t len, struct entry *dentp) { if (!(dentp->flags & FILE_SCANNED)) { if (findinsel(findselpos, len + xstrsncpy(g_sel + len, dentp->name, dentp->nlen))) dentp->flags |= FILE_SELECTED; dentp->flags |= FILE_SCANNED; } } /* * 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; bool scan = FALSE; selmark *marked = malloc(nselected * sizeof(selmark)); if (!marked) { printwarn(NULL); return; } /* First pass: inversion */ for (i = 0; i < ndents; ++i) { dentp = &pdents[i]; 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 (scan) { len = pathlen + xstrsncpy(pbuf, dentp->name, NAME_MAX); found = findinsel(findselpos, len); if (found) { if (findselpos == found) findselpos += len; if (nmarked && (found == (marked[nmarked - 1].startpos + marked[nmarked - 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; } } /* * Files marked for deselection could be found in arbitrary order. * 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); /* Some files might be adjacent. Merge them into a single entry */ for (i = 1; i < nmarked; ++i) { if (marked[i].startpos == marked[prev].startpos + marked[prev].len) 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 twice */ 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(); } /* * 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) { int i; size_t len, alloclen = 0; struct entry *dentp; char *found; char * const pbuf = g_sel + pathlen; /* Remember current selection buffer position */ for (i = startid; i <= endid; ++i) { dentp = &pdents[i]; 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; pdents[i].flags |= (FILE_SCANNED | FILE_SELECTED); } } 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 NULL) */ 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 order. * 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 */ static void endselection(bool endselmode) { int fd; ssize_t count; char buf[sizeof(patterns[P_REPLACE]) + PATH_MAX + (TMP_LEN_MAX << 1)]; if (endselmode && g_state.selmode) g_state.selmode = 0; /* The code below is only for listing mode */ if (!listpath || !selbufpos) return; fd = create_tmp_file(); if (fd == -1) { DPRINTF_S("couldn't create tmp file"); return; } seltofile(fd, NULL, NEWLINE); 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, 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); } /* 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) /* External selection is only editable at source */ return listselfile(); fd = create_tmp_file(); if (fd == -1) { DPRINTF_S("couldn't create tmp file"); return -1; } seltofile(fd, NULL, NEWLINE); 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, 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; } static void export_file_list(void) { if (!ndents) return; struct entry *pdent = pdents; int fd = create_tmp_file(); if (fd == -1) { DPRINTF_S(strerror(errno)); return; } for (int r = 0; r < ndents; ++pdent, ++r) { if (write(fd, pdent->name, pdent->nlen - 1) != (pdent->nlen - 1)) break; if ((r != ndents - 1) && (write(fd, "\n", 1) != 1)) break; } if (close(fd)) { DPRINTF_S(strerror(errno)); } spawn(editor, g_tmpfpath, NULL, NULL, F_CLI); if (xconfirm(get_input(messages[MSG_RM_TMP]))) unlink(g_tmpfpath); } static bool init_fcolors(void) { char *f_colors = getenv(env_cfg[NNN_FCOLORS]); if (!f_colors || !*f_colors) f_colors = gcolors; for (uchar_t id = C_BLK; *f_colors && id <= C_UND; ++id) { fcolors[id] = xchartohex(*f_colors) << 4; if (*++f_colors) { fcolors[id] += xchartohex(*f_colors); if (fcolors[id]) init_pair(id, fcolors[id], -1); } else return FALSE; ++f_colors; } return TRUE; } /* Initialize curses mode */ static bool initcurses(void *oldmask) { #ifdef NOMOUSE (void) oldmask; #endif if (g_state.picker) { if (!newterm(NULL, stderr, stdin)) { msg("newterm!"); return FALSE; } } else if (!initscr()) { msg("initscr!"); DPRINTF_S(getenv("TERM")); return FALSE; } cbreak(); noecho(); nonl(); //intrflush(stdscr, FALSE); keypad(stdscr, TRUE); #ifndef NOMOUSE #if NCURSES_MOUSE_VERSION <= 1 mousemask(BUTTON1_PRESSED | BUTTON1_DOUBLE_CLICKED | BUTTON2_PRESSED | BUTTON3_PRESSED, (mmask_t *)oldmask); #else mousemask(BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED | BUTTON5_PRESSED, (mmask_t *)oldmask); #endif mouseinterval(0); #endif curs_set(FALSE); /* Hide cursor */ char *colors = getenv(env_cfg[NNN_COLORS]); if (colors || !getenv("NO_COLOR")) { uint_t *pcode; bool ext = FALSE; start_color(); use_default_colors(); /* Initialize file colors */ if (COLORS >= COLOR_256) { if (!(g_state.oldcolor || init_fcolors())) { exitcurses(); msg(env_cfg[NNN_FCOLORS]); return FALSE; } } else g_state.oldcolor = 1; DPRINTF_D(COLORS); DPRINTF_D(COLOR_PAIRS); if (colors && *colors == '#') { char *sep = strchr(colors, ';'); if (!g_state.oldcolor && COLORS >= COLOR_256) { ++colors; ext = TRUE; /* * If fallback colors are specified, set the separator * to NULL so we don't interpret separator and fallback * if fewer than CTX_MAX xterm 256 colors are specified. */ if (sep) *sep = '\0'; } else { colors = sep; /* Detect if 8 colors fallback is appended */ if (colors) ++colors; } } /* Get and set the context colors */ for (uchar_t i = 0; i < CTX_MAX; ++i) { pcode = &g_ctx[i].color; if (colors && *colors) { if (ext) { *pcode = xchartohex(*colors) << 4; if (*++colors) fcolors[i + 1] = *pcode += xchartohex(*colors); else { /* Each color code must be 2 hex symbols */ exitcurses(); msg(env_cfg[NNN_COLORS]); return FALSE; } } else { *pcode = (*colors < '0' || *colors > '7') ? 4 : *colors - '0'; fcolors[i + 1] = *pcode; } ++colors; } else *pcode = 4; init_pair(i + 1, *pcode, -1); } } #ifdef ICONS_ENABLED if (!g_state.oldcolor) { for (uint_t i = 0; i < ELEMENTS(init_colors); ++i) init_pair(C_UND + 1 + init_colors[i], init_colors[i], -1); } #endif settimeout(); /* One second */ set_escdelay(25); return TRUE; } /* No NULL check here as spawn() guards against it */ static char *parseargs(char *cmd, char **argv, int *pindex) { 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; cmd = line; while (*line) { // NOLINT if (ISBLANK(*line)) { *line++ = '\0'; if (!*line) // NOLINT break; argv[count++] = line; if (count == EXEC_ARGS_MAX) { count = -1; break; } } ++line; } 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) { pid_t p = fork(); if (p > 0) { /* the parent ignores the interrupt, quit and hangup signals */ sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_IGN}, &oldsighup); sigaction(SIGTSTP, &(struct sigaction){.sa_handler = SIG_DFL}, &oldsigtstp); sigaction(SIGWINCH, &(struct sigaction){.sa_handler = SIG_IGN}, &oldsigwinch); } 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) { enable_signals(); setsid(); return p; } perror("fork"); _exit(EXIT_FAILURE); } /* So they can be used to stop the child */ enable_signals(); } /* This is the parent waiting for the child to create grandchild */ if (flag & F_NOWAIT) waitpid(p, NULL, 0); if (p == -1) perror("fork"); return p; } static int join(pid_t p, uchar_t 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); sigaction(SIGWINCH, &oldsigwinch, NULL); return status; } /* * Spawns a child process. Behaviour can be controlled using flag. * Limited to 3 arguments to a program, flag works on bit set. */ static int spawn(char *file, char *arg1, char *arg2, char *arg3, ushort_t 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 the other 2 aren't */ if (!arg1 && arg2) { arg1 = arg2; if (arg3) { arg2 = arg3; arg3 = NULL; } else arg2 = NULL; } if (flag & F_MULTI) { cmd = parseargs(file, argv, &status); if (!cmd) return -1; } else argv[status++] = file; argv[status] = arg1; argv[++status] = arg2; argv[++status] = arg3; 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); if (flag & F_NOSTDIN) dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); } else if (flag & F_TTY) { /* If stdout has been redirected to a non-tty, force output to tty */ if (!isatty(STDOUT_FILENO)) { int fd = open(ctermid(NULL), O_WRONLY, 0200); dup2(fd, STDOUT_FILENO); close(fd); } } execvp(*argv, argv); _exit(EXIT_SUCCESS); } else { retstatus = join(pid, flag); DPRINTF_D(pid); if ((flag & F_CONFIRM) || ((flag & F_CHKRTN) && retstatus)) { status = write(STDOUT_FILENO, messages[MSG_ENTER], xstrlen(messages[MSG_ENTER])); (void)status; while ((read(STDIN_FILENO, &status, 1) > 0) && (status != '\n')); } if (flag & F_NORMAL) refresh(); free(cmd); } return retstatus; } /* Get program name from env var, else return fallback program */ static char *xgetenv(const char * const name, char *fallback) { char *value = getenv(name); return value && value[0] ? value : fallback; } /* Checks if an env variable is set to 1 */ static inline uint_t xgetenv_val(const char *name) { char *str = getenv(name); if (str && str[0]) return atoi(str); return 0; } /* Check if a dir exists, IS a dir, and is readable */ static bool xdiraccess(const char *path) { DIR *dirp = opendir(path); if (!dirp) { printwarn(NULL); return FALSE; } closedir(dirp); 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) { snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c '%s \"$0\" \"$@\" . < /dev/tty' < %s", op, selpath); } static bool rmmulstr(char *buf, bool use_trash) { char r = confirm_force(TRUE, use_trash); if (!r) return FALSE; if (!use_trash) snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cvr -- \"$0\" \"$@\" < /dev/tty' < %s", r, selpath); else snprintf(buf, CMD_LEN_MAX, "xargs -0 %s < %s", utils[(g_state.trash == 1) ? UTIL_TRASH_CLI : UTIL_GIO_TRASH], selpath); return TRUE; } /* Returns TRUE if file is removed, else FALSE */ static bool xrm(char * const fpath, bool use_trash) { char r = confirm_force(FALSE, use_trash); if (!r) return FALSE; if (!use_trash) { char rm_opts[5] = "-vr\0"; rm_opts[3] = r; spawn("rm", rm_opts, "--", fpath, F_NORMAL | F_CHKRTN); } else spawn(utils[(g_state.trash == 1) ? UTIL_TRASH_CLI : UTIL_GIO_TRASH], fpath, NULL, NULL, F_NORMAL | F_MULTI); 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) { ssize_t len; uint_t count = 0; while ((len = read(fd, buf, buflen)) > 0) while (len) count += (buf[--len] == '\n'); /* For all use cases 0 linecount is considered as error */ return ((len < 0) ? 0 : count); } static bool cpmv_rename(int choice, const char *path) { int fd; uint_t count = 0, lines = 0; bool ret = FALSE; char *cmd = (choice == 'c' ? cp : mv); char buf[sizeof(patterns[P_CPMVRNM]) + (MAX(sizeof(cp), sizeof(mv))) + (PATH_MAX << 1)]; fd = create_tmp_file(); if (fd == -1) return ret; /* selsafe() returned TRUE for this to be called */ if (!selbufpos) { snprintf(buf, sizeof(buf), "tr '\\0' '\\n' < %s > %s", selpath, g_tmpfpath); spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI); count = lines_in_file(fd, buf, sizeof(buf)); if (!count) goto finish; } else seltofile(fd, &count, NEWLINE); close(fd); snprintf(buf, sizeof(buf), patterns[P_CPMVFMT], g_tmpfpath); spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI); spawn((cfg.waitedit ? enveditor : editor), g_tmpfpath, NULL, NULL, F_CLI); fd = open(g_tmpfpath, O_RDONLY); if (fd == -1) goto finish; lines = lines_in_file(fd, buf, sizeof(buf)); DPRINTF_U(count); DPRINTF_U(lines); if (!lines || (2 * count != lines)) { DPRINTF_S("num mismatch"); goto finish; } snprintf(buf, sizeof(buf), patterns[P_CPMVRNM], path, g_tmpfpath, cmd); if (!spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI | F_CHKRTN)) ret = TRUE; finish: if (fd >= 0) close(fd); return ret; } static bool cpmvrm_selection(enum action sel, char *path) { int r; if (isselfileempty()) { if (nselected) clearselection(); printmsg(messages[MSG_0_SELECTED]); return FALSE; } if (!selsafe()) return FALSE; switch (sel) { case SEL_CP: opstr(g_buf, cp); break; case SEL_MV: opstr(g_buf, mv); break; case SEL_CPMVAS: r = get_input(messages[MSG_CP_MV_AS]); if (r != 'c' && r != 'm') { printmsg(messages[MSG_INVALID_KEY]); return FALSE; } if (!cpmv_rename(r, path)) { printmsg(messages[MSG_FAILED]); return FALSE; } break; default: /* SEL_TRASH, SEL_RM_ONLY */ if (!rmmulstr(g_buf, g_state.trash && sel == SEL_TRASH)) { printmsg(messages[MSG_CANCEL]); return FALSE; } } if (sel != SEL_CPMVAS && spawn(utils[UTIL_SH_EXEC], g_buf, NULL, NULL, F_CLI | F_CHKRTN)) { printmsg(messages[MSG_FAILED]); return FALSE; } /* Clear selection */ clearselection(); return TRUE; } #ifndef NOBATCH static bool batch_rename(void) { int fd1, fd2; uint_t count = 0, lines = 0; bool dir = FALSE, ret = FALSE; char foriginal[TMP_LEN_MAX] = {0}; static const char batchrenamecmd[] = "paste -d'\n' %s %s | "SED" 'N; /^\\(.*\\)\\n\\1$/!p;d' | " "tr '\n' '\\0' | xargs -0 -n2 sh -c 'mv -i -- \"$0\" \"$@\" <" " /dev/tty'"; char buf[sizeof(batchrenamecmd) + (PATH_MAX << 1)]; int i = get_cur_or_sel(); if (!i) return ret; if (i == 'c') { /* Rename entries in current dir */ selbufpos = 0; dir = TRUE; } fd1 = create_tmp_file(); if (fd1 == -1) return ret; xstrsncpy(foriginal, g_tmpfpath, xstrlen(g_tmpfpath) + 1); fd2 = create_tmp_file(); if (fd2 == -1) { unlink(foriginal); close(fd1); return ret; } if (dir) for (i = 0; i < ndents; ++i) appendfpath(pdents[i].name, NAME_MAX); seltofile(fd1, &count, NEWLINE); seltofile(fd2, NULL, NEWLINE); close(fd2); if (dir) /* Don't retain dir entries in selection */ selbufpos = 0; spawn((cfg.waitedit ? enveditor : editor), g_tmpfpath, NULL, NULL, F_CLI); /* Reopen file descriptor to get updated contents */ fd2 = open(g_tmpfpath, O_RDONLY); if (fd2 == -1) goto finish; lines = lines_in_file(fd2, buf, sizeof(buf)); DPRINTF_U(count); DPRINTF_U(lines); if (!lines || (count != lines)) { DPRINTF_S("cannot delete files"); goto finish; } snprintf(buf, sizeof(buf), batchrenamecmd, foriginal, g_tmpfpath); spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI); ret = TRUE; finish: if (fd1 >= 0) close(fd1); unlink(foriginal); if (fd2 >= 0) close(fd2); unlink(g_tmpfpath); return ret; } #endif static char *get_archive_cmd(const char *archive) { uchar_t i = 3; if (!g_state.usebsdtar && getutil(utils[UTIL_ATOOL])) i = 0; else if (getutil(utils[UTIL_BSDTAR])) i = 1; else if (is_suffix(archive, ".zip")) i = 2; // else tar return archive_cmd[i]; } static void archive_selection(const char *cmd, const char *archive) { char *buf = malloc((xstrlen(patterns[P_ARCHIVE_CMD]) + xstrlen(cmd) + xstrlen(archive) + xstrlen(selpath)) * sizeof(char)); if (!buf) { DPRINTF_S(strerror(errno)); printwarn(NULL); return; } snprintf(buf, CMD_LEN_MAX, patterns[P_ARCHIVE_CMD], cmd, archive, selpath); spawn(utils[UTIL_SH_EXEC], buf, NULL, NULL, F_CLI | F_CONFIRM); free(buf); } static void write_lastdir(const char *curpath, const char *outfile) { bool tilde = false; if (!outfile) xstrsncpy(cfgpath + xstrlen(cfgpath), "/.lastd", 8); else tilde = convert_tilde(outfile, g_buf); int fd = open(outfile ? (tilde ? g_buf : outfile) : cfgpath, O_CREAT | O_WRONLY | O_TRUNC, S_IWUSR | S_IRUSR); if (fd != -1 && shell_escape(g_buf, sizeof(g_buf), curpath)) { if (write(fd, "cd ", 3) == 3) { if (write(fd, g_buf, strlen(g_buf)) != (ssize_t)strlen(g_buf)) { DPRINTF_S("write failed!"); } } close(fd); } } /* * We assume none of the strings are NULL. * * 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. * * If the absolute numeric values are same, we fallback to alphasort. */ static int xstricmp(const char * const s1, const char * const s2) { char *p1, *p2; long long v1 = strtoll(s1, &p1, 10); long long v2 = strtoll(s2, &p2, 10); /* Check if at least 1 string is numeric */ if (s1 != p1 || s2 != p2) { /* Handle both pure numeric */ if (s1 != p1 && s2 != p2) { if (v2 > v1) return -1; if (v1 > v2) return 1; } /* Only first string non-numeric */ if (s1 == p1) return 1; /* Only second string non-numeric */ if (s2 == p2) return -1; } /* Handle 1. all non-numeric and 2. both same numeric value cases */ #ifndef NOLC return strcoll(s1, s2); #else return strcasecmp(s1, s2); #endif } /* * Version comparison * * The code for version compare is a modified version of the GLIBC * and uClibc implementation of strverscmp(). The source is here: * https://elixir.bootlin.com/uclibc-ng/latest/source/libc/string/strverscmp.c */ /* * Compare S1 and S2 as strings holding indices/version numbers, * returning less than, equal to or greater than zero if S1 is less than, * equal to or greater than S2 (for more info, see the texinfo doc). * * Ignores case. */ static int xstrverscasecmp(const char * const s1, const char * const s2) { const uchar_t *p1 = (const uchar_t *)s1; const uchar_t *p2 = (const uchar_t *)s2; int state, diff; uchar_t c1, c2; /* * Symbol(s) 0 [1-9] others * Transition (10) 0 (01) d (00) x */ static const uint8_t next_state[] = { /* state x d 0 */ /* S_N */ S_N, S_I, S_Z, /* S_I */ S_N, S_I, S_I, /* S_F */ S_N, S_F, S_F, /* S_Z */ S_N, S_F, S_Z }; alignas(max_align_t) static const int8_t result_type[] = { /* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */ /* S_N */ VCMP, VCMP, VCMP, VCMP, VLEN, VCMP, VCMP, VCMP, VCMP, /* S_I */ VCMP, -1, -1, 1, VLEN, VLEN, 1, VLEN, VLEN, /* S_F */ VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, VCMP, /* S_Z */ VCMP, 1, 1, -1, VCMP, VCMP, -1, VCMP, VCMP }; if (p1 == p2) return 0; c1 = TOUPPER(*p1); ++p1; c2 = TOUPPER(*p2); ++p2; /* Hint: '0' is a digit too. */ state = S_N + ((c1 == '0') + (xisdigit(c1) != 0)); while ((diff = c1 - c2) == 0) { if (c1 == '\0') return diff; state = next_state[state]; c1 = TOUPPER(*p1); ++p1; c2 = TOUPPER(*p2); ++p2; state += (c1 == '0') + (xisdigit(c1) != 0); } state = result_type[state * 3 + (((c2 == '0') + (xisdigit(c2) != 0)))]; // NOLINT switch (state) { case VCMP: return diff; case VLEN: while (xisdigit(*p1++)) if (!xisdigit(*p2++)) return 1; return xisdigit(*p2) ? -1 : diff; default: return state; } } static int (*namecmpfn)(const char * const s1, const char * const s2) = &xstricmp; static char * (*fnstrstr)(const char *haystack, const char *needle) = &strcasestr; #ifdef PCRE static const unsigned char *tables; static int pcreflags = PCRE_NO_AUTO_CAPTURE | PCRE_EXTENDED | PCRE_CASELESS | PCRE_UTF8; #else static int regflags = REG_NOSUB | REG_EXTENDED | REG_ICASE; #endif #ifdef PCRE static int setfilter(pcre **pcrex, const char *filter) { const char *errstr = NULL; int erroffset = 0; *pcrex = pcre_compile(filter, pcreflags, &errstr, &erroffset, tables); return errstr ? -1 : 0; } #else static int setfilter(regex_t *regex, const char *filter) { return regcomp(regex, filter, regflags); } #endif static int visible_re(const fltrexp_t *fltrexp, const char *fname) { #ifdef PCRE return pcre_exec(fltrexp->pcrex, NULL, fname, xstrlen(fname), 0, 0, NULL, 0) == 0; #else return regexec(fltrexp->regex, fname, 0, NULL, 0) == 0; #endif } static int visible_str(const fltrexp_t *fltrexp, const char *fname) { return fnstrstr(fname, fltrexp->str) != NULL; } static int (*filterfn)(const fltrexp_t *fltr, const char *fname) = &visible_str; static void clearfilter(void) { char * const fltr = g_ctx[cfg.curctx].c_fltr; if (fltr[1]) { fltr[REGEX_MAX - 1] = fltr[1]; fltr[1] = '\0'; } } static int entrycmp(const void *va, const void *vb) { const struct entry *pa = (pEntry)va; const struct entry *pb = (pEntry)vb; if ((pb->flags & DIR_OR_DIRLNK) != (pa->flags & DIR_OR_DIRLNK)) { if (pb->flags & DIR_OR_DIRLNK) return 1; return -1; } /* Sort based on specified order */ if (cfg.timeorder) { if (pb->sec > pa->sec) return 1; if (pb->sec < pa->sec) return -1; /* If sec matches, comare nsec */ if (pb->nsec > pa->nsec) return 1; if (pb->nsec < pa->nsec) return -1; } else if (cfg.sizeorder) { if (pb->size > pa->size) return 1; if (pb->size < pa->size) return -1; } else if (cfg.blkorder) { if (pb->blocks > pa->blocks) return 1; if (pb->blocks < pa->blocks) return -1; } else if (cfg.extnorder && !(pb->flags & DIR_OR_DIRLNK)) { char *extna = xextension(pa->name, pa->nlen - 1); char *extnb = xextension(pb->name, pb->nlen - 1); if (extna || extnb) { if (!extna) return -1; if (!extnb) return 1; int ret = strcasecmp(extna, extnb); if (ret) return ret; } } return namecmpfn(pa->name, pb->name); } static int reventrycmp(const void *va, const void *vb) { if ((((pEntry)vb)->flags & DIR_OR_DIRLNK) != (((pEntry)va)->flags & DIR_OR_DIRLNK)) { if (((pEntry)vb)->flags & DIR_OR_DIRLNK) return 1; return -1; } return -entrycmp(va, vb); } static int (*entrycmpfn)(const void *va, const void *vb) = &entrycmp; /* In case of an error, resets *wch to Esc */ static int handle_alt_key(wint_t *wch) { timeout(0); int r = get_wch(wch); if (r == ERR) *wch = ESC; cleartimeout(); return r; } static inline int handle_event(void) { if (nselected && isselfileempty()) clearselection(); return CONTROL('L'); } /* * Returns SEL_* if key is bound and 0 otherwise. * Also modifies the run and env pointers (used on SEL_{RUN,RUNARG}). * The next keyboard input can be simulated by presel. */ static int nextsel(int presel) { #ifdef BENCH return SEL_QUIT; #endif wint_t c = presel; int i = 0; bool escaped = FALSE; if (c == 0 || c == MSGWAIT) { try_quit: i = get_wch(&c); //DPRINTF_D(c); //DPRINTF_S(keyname(c)); #ifdef KEY_RESIZE if (c == KEY_RESIZE) handle_key_resize(); #endif /* Handle Alt+key */ if (c == ESC) { timeout(0); i = get_wch(&c); if (i != ERR) { if (c == ESC) c = CONTROL('L'); else { unget_wch(c); c = ';'; } settimeout(); } else if (escaped) { settimeout(); c = CONTROL('Q'); } else { #ifndef NOFIFO if (!g_state.fifomode) notify_fifo(TRUE); /* Send hovered path to NNN_FIFO */ #endif escaped = TRUE; settimeout(); goto try_quit; } } if (i == ERR && presel == MSGWAIT) c = (cfg.filtermode || filterset()) ? FILTER : CONTROL('L'); else if (c == FILTER || c == CONTROL('L')) /* Clear previous filter when manually starting */ clearfilter(); } if (i == ERR) { ++idle; /* * Do not check for directory changes in du mode. * A redraw forces du calculation. * Check for changes every odd second. */ #ifdef LINUX_INOTIFY if (!cfg.blkorder && inotify_wd >= 0 && (idle & 1)) { struct inotify_event *event; char inotify_buf[EVENT_BUF_LEN] = {0}; i = read(inotify_fd, inotify_buf, EVENT_BUF_LEN); if (i > 0) { for (char *ptr = inotify_buf; ptr + ((struct inotify_event *)ptr)->len < inotify_buf + i; ptr += sizeof(struct inotify_event) + event->len) { event = (struct inotify_event *)ptr; DPRINTF_D(event->wd); DPRINTF_D(event->mask); if (!event->wd) break; if (event->mask & INOTIFY_MASK) { c = handle_event(); break; } } DPRINTF_S("inotify read done"); } } #elif defined(BSD_KQUEUE) if (!cfg.blkorder && event_fd >= 0 && (idle & 1)) { struct kevent event_data[NUM_EVENT_SLOTS] = {0}; if (kevent(kq, events_to_monitor, NUM_EVENT_SLOTS, event_data, NUM_EVENT_FDS, >imeout) > 0) c = handle_event(); } #elif defined(HAIKU_NM) if (!cfg.blkorder && haiku_nm_active && (idle & 1) && haiku_is_update_needed(haiku_hnd)) c = handle_event(); #endif } else idle = 0; for (i = 0; i < (int)ELEMENTS(bindings); ++i) if (c == bindings[i].sym) return bindings[i].act; return 0; } static int getorderstr(char *sort) { int i = 0; if (cfg.showhidden) sort[i++] = 'H'; if (cfg.timeorder) sort[i++] = (cfg.timetype == T_MOD) ? 'M' : ((cfg.timetype == T_ACCESS) ? 'A' : 'C'); else if (cfg.sizeorder) sort[i++] = 'S'; else if (cfg.extnorder) sort[i++] = 'E'; if (entrycmpfn == &reventrycmp) sort[i++] = 'R'; if (namecmpfn == &xstrverscasecmp) sort[i++] = 'V'; if (i) sort[i] = ' '; return i; } static void showfilterinfo(void) { int i = 0; char info[REGEX_MAX] = "\0\0\0\0\0"; i = getorderstr(info); if (cfg.fileinfo && ndents && get_output("file", "-b", pdents[cur].name, -1, FALSE)) mvaddstr(xlines - 2, 2, g_buf); else { snprintf(info + i, REGEX_MAX - i - 1, " %s [/], %4s [:]", (cfg.regex ? "reg" : "str"), ((fnstrstr == &strcasestr) ? "ic" : "noic")); } mvaddstr(xlines - 2, xcols - xstrlen(info), info); } static void showfilter(char *str) { attron(COLOR_PAIR(cfg.curctx + 1)); showfilterinfo(); printmsg(str); // printmsg calls attroff() } static inline void swap_ent(int id1, int id2) { struct entry _dent, *pdent1 = &pdents[id1], *pdent2 = &pdents[id2]; *(&_dent) = *pdent1; *pdent1 = *pdent2; *pdent2 = *(&_dent); } #ifdef PCRE static int fill(const char *fltr, pcre *pcrex) #else static int fill(const char *fltr, regex_t *re) #endif { #ifdef PCRE fltrexp_t fltrexp = { .pcrex = pcrex, .str = fltr }; #else fltrexp_t fltrexp = { .regex = re, .str = fltr }; #endif for (int count = 0; count < ndents; ++count) { if (filterfn(&fltrexp, pdents[count].name) == 0) { if (count != --ndents) { swap_ent(count, ndents); --count; } continue; } } return ndents; } static int matches(const char *fltr) { #ifdef PCRE pcre *pcrex = NULL; /* Search filter */ if (cfg.regex && setfilter(&pcrex, fltr)) return -1; ndents = fill(fltr, pcrex); if (cfg.regex) pcre_free(pcrex); #else regex_t re; /* Search filter */ if (cfg.regex && setfilter(&re, fltr)) return -1; ndents = fill(fltr, &re); if (cfg.regex) regfree(&re); #endif ENTSORT(pdents, ndents, entrycmpfn); return ndents; } /* * Return the position of the matching entry or 0 otherwise * Note there's no NULL check for fname */ static int dentfind(const char *fname, int n) { for (int i = 0; i < n; ++i) if (xstrcmp(fname, pdents[i].name) == 0) return i; return 0; } static int filterentries(char *path, char *lastname) { alignas(max_align_t) wchar_t wln[REGEX_MAX]; char *ln = g_ctx[cfg.curctx].c_fltr; wint_t ch[1]; int r, total = ndents, len; char *pln = g_ctx[cfg.curctx].c_fltr + 1; DPRINTF_S(__func__); if (ndents && (ln[0] == FILTER || ln[0] == RFILTER) && *pln) { if (matches(pln) != -1) { move_cursor(dentfind(lastname, ndents), 0); redraw(path); } if (!cfg.filtermode) { statusbar(path); return 0; } len = mbstowcs(wln, ln, REGEX_MAX); } else { ln[0] = wln[0] = cfg.regex ? RFILTER : FILTER; ln[1] = wln[1] = '\0'; len = 1; } cleartimeout(); curs_set(TRUE); showfilter(ln); while ((r = get_wch(ch)) != ERR) { //DPRINTF_D(*ch); //DPRINTF_S(keyname(*ch)); switch (*ch) { #ifdef KEY_RESIZE case 0: // fallthrough case KEY_RESIZE: clearoldprompt(); redraw(path); showfilter(ln); continue; #endif case KEY_DC: // fallthrough case KEY_BACKSPACE: // fallthrough case '\b': // fallthrough case DEL: /* handle DEL */ if (len != 1) { wln[--len] = '\0'; wcstombs(ln, wln, REGEX_MAX); ndents = total; } else { *ch = FILTER; goto end; } // fallthrough case CONTROL('L'): if (*ch == CONTROL('L')) { if (wln[1]) { ln[REGEX_MAX - 1] = ln[1]; ln[1] = wln[1] = '\0'; len = 1; ndents = total; } else if (ln[REGEX_MAX - 1]) { /* Show the previous filter */ ln[1] = ln[REGEX_MAX - 1]; ln[REGEX_MAX - 1] = '\0'; len = mbstowcs(wln, ln, REGEX_MAX); } else goto end; } /* Go to the top, we don't know if the hovered file will match the filter */ cur = 0; if (matches(pln) != -1) redraw(path); showfilter(ln); continue; #ifndef NOMOUSE case KEY_MOUSE: { MEVENT event = {0}; getmouse(&event); if (event.bstate == 0) continue; ungetmouse(&event); goto end; } #endif case ESC: if (handle_alt_key(ch) != ERR) { if (*ch == ESC) /* Handle Alt+Esc */ *ch = 'q'; /* Quit context */ else { unget_wch(*ch); *ch = ';'; /* Run plugin */ } } goto end; } if (r != OK) /* Handle Fn keys in main loop */ break; /* Handle all control chars in main loop */ if (*ch < ASCII_MAX && keyname(*ch)[0] == '^' && *ch != '^') goto end; if (len == 1) { if (*ch == '?') /* Help and config key, '?' is an invalid regex */ goto end; if (cfg.filtermode) { switch (*ch) { case '\'': // fallthrough /* Go to first non-dir file */ case '+': // fallthrough /* Toggle file selection */ case ',': // fallthrough /* Mark CWD */ case '-': // fallthrough /* Visit last visited dir */ case '.': // fallthrough /* Show hidden files */ case ';': // fallthrough /* Run plugin key */ case '=': // fallthrough /* Launch app */ case '>': // fallthrough /* Export file list */ case '@': // fallthrough /* Visit start dir */ case ']': // fallthorugh /* Prompt key */ case '`': // fallthrough /* Visit / */ case '~': /* Go HOME */ goto end; } } /* Toggle case-sensitivity */ if (*ch == CASE) { fnstrstr = (fnstrstr == &strcasestr) ? &strstr : &strcasestr; #ifdef PCRE pcreflags ^= PCRE_CASELESS; #else regflags ^= REG_ICASE; #endif showfilter(ln); continue; } /* Toggle string or regex filter */ if (*ch == FILTER) { ln[0] = (ln[0] == FILTER) ? RFILTER : FILTER; wln[0] = (uchar_t)ln[0]; cfg.regex ^= 1; filterfn = cfg.regex ? &visible_re : &visible_str; showfilter(ln); continue; } /* Reset cur in case it's a repeat search */ cur = 0; } else if (len == REGEX_MAX - 1) continue; wln[len] = (wchar_t)*ch; wln[++len] = '\0'; wcstombs(ln, wln, REGEX_MAX); /* Forward-filtering optimization: * - new matches can only be a subset of current matches. */ /* ndents = total; */ #ifdef MATCHFLTR r = matches(pln); if (r <= 0) { !r ? unget_wch(KEY_BACKSPACE) : showfilter(ln); #else if (matches(pln) == -1) { showfilter(ln); #endif continue; } /* If the only match is a dir, auto-enter and cd into it */ if ((ndents == 1) && cfg.autoenter && (pdents[0].flags & DIR_OR_DIRLNK)) { *ch = KEY_ENTER; cur = 0; goto end; } /* * redraw() should be above the auto-enter optimization, for * the case where there's an issue with dir auto-enter, say, * due to a permission problem. The transition is _jumpy_ in * case of such an error. However, we optimize for successful * cases where the dir has permissions. This skips a redraw(). */ redraw(path); showfilter(ln); } end: /* Save last working filter in-filter */ if (ln[1]) ln[REGEX_MAX - 1] = ln[1]; /* Save current */ copycurname(); curs_set(FALSE); settimeout(); /* Return keys for navigation etc. */ return *ch; } /* Show a prompt with input string and return the changes */ static char *xreadline(const char *prefill, const char *prompt) { size_t len, pos; int x, r; const int WCHAR_T_WIDTH = sizeof(wchar_t); wint_t ch[1]; wchar_t * const buf = malloc(sizeof(wchar_t) * READLINE_MAX); if (!buf) errexit(); cleartimeout(); printmsg(prompt); if (prefill) { DPRINTF_S(prefill); len = pos = mbstowcs(buf, prefill, READLINE_MAX); } else len = (size_t)-1; if (len == (size_t)-1) { buf[0] = '\0'; len = pos = 0; } x = getcurx(stdscr); curs_set(TRUE); while (1) { buf[len] = ' '; attron(COLOR_PAIR(cfg.curctx + 1)); if (pos > (size_t)(xcols - x)) { mvaddnwstr(xlines - 1, x, buf + (pos - (xcols - x) + 1), xcols - x); move(xlines - 1, xcols - 1); } else { mvaddnwstr(xlines - 1, x, buf, len + 1); move(xlines - 1, x + wcswidth(buf, pos)); } attroff(COLOR_PAIR(cfg.curctx + 1)); r = get_wch(ch); if (r == ERR) continue; if (r == OK) { switch (*ch) { case KEY_ENTER: // fallthrough case '\n': // fallthrough case '\r': goto END; case CONTROL('D'): if (pos < len) ++pos; else if (!(pos || len)) { /* Exit on ^D at empty prompt */ len = 0; goto END; } else continue; // fallthrough case DEL: // fallthrough case '\b': /* rhel25 sends '\b' for backspace */ if (pos > 0) { memmove(buf + pos - 1, buf + pos, (len - pos) * WCHAR_T_WIDTH); --len, --pos; } continue; case '\t': if (!(len || pos) && ndents) len = pos = mbstowcs(buf, pdents[cur].name, READLINE_MAX); continue; case CONTROL('F'): if (pos < len) ++pos; continue; case CONTROL('B'): if (pos > 0) --pos; continue; case CONTROL('W'): printmsg(prompt); do { if (pos == 0) break; memmove(buf + pos - 1, buf + pos, (len - pos) * WCHAR_T_WIDTH); --pos, --len; } while (buf[pos - 1] != ' ' && buf[pos - 1] != '/'); // NOLINT continue; case CONTROL('K'): printmsg(prompt); len = pos; continue; case CONTROL('L'): printmsg(prompt); len = pos = 0; continue; case CONTROL('A'): pos = 0; continue; case CONTROL('E'): pos = len; continue; case CONTROL('U'): printmsg(prompt); memmove(buf, buf + pos, (len - pos) * WCHAR_T_WIDTH); len -= pos; pos = 0; continue; case ESC: /* Exit prompt on Esc, but just filter out Alt+key */ if (handle_alt_key(ch) != ERR) continue; len = 0; goto END; } /* Filter out all other control chars */ if (*ch < ASCII_MAX && keyname(*ch)[0] == '^') continue; if (pos < READLINE_MAX - 1) { memmove(buf + pos + 1, buf + pos, (len - pos) * WCHAR_T_WIDTH); buf[pos] = *ch; ++len, ++pos; continue; } } else { switch (*ch) { #ifdef KEY_RESIZE case KEY_RESIZE: clearoldprompt(); xlines = LINES; printmsg(prompt); break; #endif case KEY_LEFT: if (pos > 0) --pos; break; case KEY_RIGHT: if (pos < len) ++pos; break; case KEY_BACKSPACE: if (pos > 0) { memmove(buf + pos - 1, buf + pos, (len - pos) * WCHAR_T_WIDTH); --len, --pos; } break; case KEY_DC: if (pos < len) { memmove(buf + pos, buf + pos + 1, (len - pos - 1) * WCHAR_T_WIDTH); --len; } break; case KEY_END: pos = len; break; case KEY_HOME: pos = 0; break; case KEY_UP: // fallthrough case KEY_DOWN: if (prompt && lastcmd && (xstrcmp(prompt, PROMPT) == 0)) { printmsg(prompt); len = pos = mbstowcs(buf, lastcmd, READLINE_MAX); // fallthrough } default: break; } } } END: curs_set(FALSE); settimeout(); printmsg(""); buf[len] = '\0'; pos = wcstombs(g_buf, buf, READLINE_MAX - 1); if (pos >= READLINE_MAX - 1) g_buf[READLINE_MAX - 1] = '\0'; free(buf); return g_buf; } #ifndef NORL /* * Caller should check the value of presel to confirm if it needs to wait to show warning */ static char *getreadline(const char *prompt) { exitcurses(); char *input = readline(prompt); refresh(); if (input && input[0]) { add_history(input); xstrsncpy(g_buf, input, CMD_LEN_MAX); free(input); return g_buf; } free(input); return NULL; } #endif /* * Create symbolic/hard link(s) to file(s) in selection list * Returns the number of links created, -1 on error */ static int xlink(char *prefix, char *path, char *curfname, char *buf, int type) { int count = 0, choice; char *psel = pselbuf, *fname; size_t pos = 0, len, r; int (*link_fn)(const char *, const char *) = NULL; char lnpath[PATH_MAX]; choice = get_cur_or_sel(); if (!choice) return -1; if (type == 's') /* symbolic link */ link_fn = &symlink; else /* hard link */ link_fn = &link; if (choice == 'c' || (nselected == 1)) { prefix = abspath(prefix, path, lnpath); /* Generate link path */ if (!prefix) return -1; if (choice == 'c') mkpath(path, curfname, buf); /* Generate target file path */ if (!link_fn((choice == 'c') ? buf : pselbuf, lnpath)) { if (choice == 's') clearselection(); return 1; /* One link created */ } return 0; } r = xstrsncpy(buf, prefix, NAME_MAX + 1); /* Copy prefix */ while (pos < selbufpos) { len = xstrlen(psel); fname = xbasename(psel); xstrsncpy(buf + r - 1, fname, NAME_MAX - r); /* Suffix target file name */ mkpath(path, buf, lnpath); /* Generate link path */ if (!link_fn(psel, lnpath)) ++count; pos += len + 1; psel += len + 1; } if (count == nselected) /* Clear selection if all links are generated */ clearselection(); return count; } static bool parsekvpair(kv **arr, char **envcpy, const uchar_t id, uchar_t *items) { bool new = TRUE; const uchar_t INCR = 8; uint_t i = 0; kv *kvarr = NULL; char *ptr = getenv(env_cfg[id]); if (!ptr || !*ptr) return TRUE; *envcpy = xstrdup(ptr); if (!*envcpy) { xerror(); return FALSE; } ptr = *envcpy; while (*ptr && i < 100) { if (new) { if (!(i & (INCR - 1))) { kvarr = xrealloc(kvarr, sizeof(kv) * (i + INCR)); *arr = kvarr; if (!kvarr) { xerror(); return FALSE; } memset(kvarr + i, 0, sizeof(kv) * INCR); } kvarr[i].key = (uchar_t)*ptr; if (*++ptr != ':' || *++ptr == '\0' || *ptr == ';') return FALSE; kvarr[i].off = ptr - *envcpy; ++i; new = FALSE; } if (*ptr == ';') { *ptr = '\0'; new = TRUE; } ++ptr; } *items = i; return (i != 0); } /* * Get the value corresponding to a key * * NULL is returned in case of no match, path resolution failure etc. * buf would be modified, so check return value before access */ static char *get_kv_val(kv *kvarr, char *buf, int key, uchar_t max, uchar_t id) { char *val; if (!kvarr) return NULL; for (int r = 0; r < max && kvarr[r].key; ++r) { if (kvarr[r].key == key) { /* Do not allocate new memory for plugin */ if (id == NNN_PLUG) return pluginstr + kvarr[r].off; val = bmstr + kvarr[r].off; bool tilde = convert_tilde(val, g_buf); return abspath((tilde ? g_buf : val), NULL, buf); } } DPRINTF_S("Invalid key"); return NULL; } static int get_kv_key(kv *kvarr, char *val, uchar_t max, uchar_t id) { if (!kvarr) return -1; if (id != NNN_ORDER) /* For now this function supports only order string */ return -1; for (int r = 0; r < max && kvarr[r].key; ++r) { if (xstrcmp((orderstr + kvarr[r].off), val) == 0) return kvarr[r].key; } return -1; } static void resetdircolor(int flags) { /* Directories are always shown on top, clear the color when moving to first file */ if (g_state.dircolor && !(flags & DIR_OR_DIRLNK)) { attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); g_state.dircolor = 0; } } /* * Replace escape characters in a string with '?' * Adjust string length to maxcols if > 0; * Max supported str length: NAME_MAX; */ #ifdef NOLC static char *unescape(const char *str, uint_t maxcols) { char * const wbuf = g_buf; char *buf = wbuf; xstrsncpy(wbuf, str, maxcols); #else static wchar_t *unescape(const char *str, uint_t maxcols) { wchar_t * const wbuf = (wchar_t *)g_buf; wchar_t *buf = wbuf; size_t len = mbstowcs(wbuf, str, maxcols); /* Convert multi-byte to wide char */ len = wcswidth(wbuf, len); if (len >= maxcols) { size_t lencount = maxcols; while (len > maxcols) /* Reduce wide chars one by one till it fits */ len = wcswidth(wbuf, --lencount); wbuf[lencount] = L'\0'; } #endif while (*buf) { if (*buf <= '\x1f' || *buf == '\x7f') *buf = '\?'; ++buf; } return wbuf; } static off_t get_size(off_t size, off_t *pval, int comp) { off_t rem = *pval; off_t quo = rem / 10; if ((rem - (quo * 10)) >= 5) { rem = quo + 1; if (rem == comp) { ++size; rem = 0; } } else rem = quo; *pval = rem; return size; } static char *coolsize(off_t size) { const char * const U = "BKMGTPEZY"; static char size_buf[12]; /* Buffer to hold human readable size */ off_t rem = 0; size_t ret; int i = 0; while (size >= 1024) { rem = size & (0x3FF); /* 1024 - 1 = 0x3FF */ size >>= 10; ++i; } if (i == 1) { rem = (rem * 1000) >> 10; rem /= 10; size = get_size(size, &rem, 10); } else if (i == 2) { rem = (rem * 1000) >> 10; size = get_size(size, &rem, 100); } else if (i > 2) { rem = (rem * 10000) >> 10; size = get_size(size, &rem, 1000); } if (i > 0 && i < 6 && rem) { ret = xstrsncpy(size_buf, xitoa(size), 12); size_buf[ret - 1] = '.'; char *frac = xitoa(rem); size_t toprint = i > 3 ? 3 : i; size_t len = xstrlen(frac); if (len < toprint) { size_buf[ret] = size_buf[ret + 1] = size_buf[ret + 2] = '0'; xstrsncpy(size_buf + ret + (toprint - len), frac, len + 1); } else xstrsncpy(size_buf + ret, frac, toprint + 1); ret += toprint; } else { ret = xstrsncpy(size_buf, size ? xitoa(size) : "0", 12); --ret; } size_buf[ret] = U[i]; size_buf[ret + 1] = '\0'; return size_buf; } /* Convert a mode field into "ls -l" type perms field. */ static char *get_lsperms(mode_t mode) { static const char * const rwx[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"}; static char bits[11] = {'\0'}; switch (mode & S_IFMT) { case S_IFREG: bits[0] = '-'; break; case S_IFDIR: bits[0] = 'd'; break; case S_IFLNK: bits[0] = 'l'; break; case S_IFSOCK: bits[0] = 's'; break; case S_IFIFO: bits[0] = 'p'; break; case S_IFBLK: bits[0] = 'b'; break; case S_IFCHR: bits[0] = 'c'; break; default: bits[0] = '?'; break; } xstrsncpy(&bits[1], rwx[(mode >> 6) & 7], 4); xstrsncpy(&bits[4], rwx[(mode >> 3) & 7], 4); xstrsncpy(&bits[7], rwx[(mode & 7)], 4); if (mode & S_ISUID) bits[3] = (mode & 0100) ? 's' : 'S'; /* user executable */ if (mode & S_ISGID) bits[6] = (mode & 0010) ? 's' : 'l'; /* group executable */ if (mode & S_ISVTX) bits[9] = (mode & 0001) ? 't' : 'T'; /* others executable */ return bits; } #ifdef ICONS_ENABLED static struct icon get_icon(const struct entry *ent) { for (size_t i = 0; i < ELEMENTS(icons_name); ++i) if (strcasecmp(ent->name, icons_name[i].match) == 0) return (struct icon){ icons_name[i].icon, icons_name[i].color }; if (ent->flags & DIR_OR_DIRLNK) return dir_icon; char *tmp = xextension(ent->name, ent->nlen); if (tmp) { uint16_t z, k, h = icon_ext_hash(++tmp); /* ++tmp to skip '.' */ for (k = 0; k < ICONS_PROBE_MAX; ++k) { z = (h + k) % ELEMENTS(icons_ext); if (strcasecmp(tmp, icons_ext[z].match) == 0) return (struct icon){ icons_ext_uniq[icons_ext[z].idx], icons_ext[z].color }; } } /* If there's no match and the file is executable, icon that */ if (ent->mode & 0100) return exec_icon; return file_icon; } static void print_icon(const struct entry *ent, const int attrs) { const struct icon icon = get_icon(ent); addstr(ICON_PADDING_LEFT); if (icon.color) attron(COLOR_PAIR(C_UND + 1 + icon.color)); else if (attrs) attron(attrs); addstr(icon.icon); if (icon.color) attroff(COLOR_PAIR(C_UND + 1 + icon.color)); else if (attrs) attroff(attrs); addstr(ICON_PADDING_RIGHT); } #endif static void print_time(const time_t *timep, const uchar_t flags) { struct tm t; /* Highlight timestamp for entries 5 minutes young */ if (flags & FILE_YOUNG) attron(A_REVERSE); localtime_r(timep, &t); printw("%s-%02d-%02d %02d:%02d", xitoa(t.tm_year + 1900), t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min); if (flags & FILE_YOUNG) attroff(A_REVERSE); } static char get_detail_ind(const mode_t mode) { switch (mode & S_IFMT) { case S_IFDIR: // fallthrough case S_IFREG: return ' '; case S_IFLNK: return '@'; case S_IFSOCK: return '='; case S_IFIFO: return '|'; case S_IFBLK: return 'b'; case S_IFCHR: return 'c'; } return '?'; } /* Note: attribute and indicator values must be initialized to 0 */ static uchar_t get_color_pair_name_ind(const struct entry *ent, char *pind, int *pattr) { switch (ent->mode & S_IFMT) { case S_IFREG: if (!ent->size) { if (ent->mode & 0100) *pind = '*'; return C_UND; } if (ent->flags & HARD_LINK) { if (ent->mode & 0100) *pind = '*'; return C_HRD; } if (ent->mode & 0100) { *pind = '*'; return C_EXE; } return C_FIL; case S_IFDIR: *pind = '/'; if (g_state.oldcolor) return C_DIR; *pattr |= A_BOLD; return g_state.dirctx ? cfg.curctx + 1 : C_DIR; case S_IFLNK: if (ent->flags & DIR_OR_DIRLNK) { *pind = '/'; *pattr |= g_state.oldcolor ? A_DIM : A_BOLD; } else { *pind = '@'; if (g_state.oldcolor) *pattr |= A_DIM; } if (!g_state.oldcolor || cfg.showdetail) return (ent->flags & SYM_ORPHAN) ? C_ORP : C_LNK; return 0; case S_IFSOCK: *pind = '='; return C_SOC; case S_IFIFO: *pind = '|'; return C_PIP; case S_IFBLK: return C_BLK; case S_IFCHR: return C_CHR; } *pind = '?'; return C_UND; } static void printent(int pdents_index, uint_t namecols, bool sel) { const struct entry *ent = &pdents[pdents_index]; char ind = '\0'; int attrs; if (cfg.showdetail) { int type = ent->mode & S_IFMT; char perms[6] = {' ', ' ', (char)('0' + ((ent->mode >> 6) & 7)), (char)('0' + ((ent->mode >> 3) & 7)), (char)('0' + (ent->mode & 7)), '\0'}; addch(' '); attrs = g_state.oldcolor ? (resetdircolor(ent->flags), A_DIM) : (fcolors[C_MIS] ? COLOR_PAIR(C_MIS) : 0); if (attrs) attron(attrs); /* Print details */ print_time(&ent->sec, ent->flags); printw("%s%9s ", perms, (type == S_IFREG || type == S_IFDIR) ? coolsize(cfg.blkorder ? (blkcnt_t)ent->blocks << blk_shift : ent->size) : (type = (uchar_t)get_detail_ind(ent->mode), (char *)&type)); if (attrs) attroff(attrs); } if (g_state.showlines) { ptrdiff_t rel_num = pdents_index - cur; printw(rel_num == 0 ? "%4td" : "%+4td", rel_num); } attrs = 0; uchar_t color_pair = get_color_pair_name_ind(ent, &ind, &attrs); addch((ent->flags & FILE_SELECTED) ? '+' | A_REVERSE | A_BOLD : ' '); if (g_state.oldcolor) resetdircolor(ent->flags); else { if (ent->flags & FILE_MISSING) color_pair = C_MIS; if (color_pair && fcolors[color_pair]) attrs |= COLOR_PAIR(color_pair); #ifdef ICONS_ENABLED print_icon(ent, attrs); #endif } if (sel) attrs |= A_REVERSE; if (attrs) attron(attrs); if (!ind) ++namecols; #ifndef NOLC addwstr(unescape(ent->name, namecols)); #else addstr(unescape(ent->name, MIN(namecols, ent->nlen) + 1)); #endif if (attrs) attroff(attrs); if (ind) addch(ind); } /** * Sets the global cfg variable and restores related state to match the new * cfg. */ static void setcfg(settings newcfg) { cfg = newcfg; /* Synchronize the global function pointers to match the new cfg. */ entrycmpfn = cfg.reverse ? &reventrycmp : &entrycmp; namecmpfn = cfg.version ? &xstrverscasecmp : &xstricmp; } static void savecurctx(char *path, char *curname, int nextctx) { settings tmpcfg = cfg; context *ctxr = &g_ctx[nextctx]; /* Save current context */ if (curname) xstrsncpy(g_ctx[tmpcfg.curctx].c_name, curname, NAME_MAX + 1); else g_ctx[tmpcfg.curctx].c_name[0] = '\0'; g_ctx[tmpcfg.curctx].c_cfg = tmpcfg; if (ctxr->c_cfg.ctxactive) { /* Switch to saved context */ tmpcfg = ctxr->c_cfg; /* Skip ordering an open context */ if (order) { cfgsort[CTX_MAX] = cfgsort[nextctx]; cfgsort[nextctx] = '0'; } } else { /* Set up a new context from current context */ ctxr->c_cfg.ctxactive = 1; xstrsncpy(ctxr->c_path, path, PATH_MAX); ctxr->c_last[0] = ctxr->c_name[0] = ctxr->c_fltr[0] = ctxr->c_fltr[1] = '\0'; ctxr->c_cfg = tmpcfg; /* If already in an ordered dir, clear ordering for the new context and let it order */ if (cfgsort[cfg.curctx] == 'z') cfgsort[nextctx] = 'z'; } tmpcfg.curctx = nextctx; setcfg(tmpcfg); } #ifndef NOSSN static void save_session(const char *sname, int *presel) { int fd, i; session_header_t header = {0}; bool status = FALSE; char ssnpath[PATH_MAX]; char spath[PATH_MAX]; header.ver = SESSIONS_VERSION; for (i = 0; i < CTX_MAX; ++i) { if (g_ctx[i].c_cfg.ctxactive) { if (cfg.curctx == i && ndents) /* Update current file name, arrows don't update it */ xstrsncpy(g_ctx[i].c_name, pdents[cur].name, NAME_MAX + 1); header.pathln[i] = MIN(xstrlen(g_ctx[i].c_path), PATH_MAX) + 1; header.lastln[i] = MIN(xstrlen(g_ctx[i].c_last), PATH_MAX) + 1; header.nameln[i] = MIN(xstrlen(g_ctx[i].c_name), NAME_MAX) + 1; header.fltrln[i] = REGEX_MAX; } } mkpath(cfgpath, toks[TOK_SSN], ssnpath); mkpath(ssnpath, sname, spath); fd = open(spath, O_CREAT | O_WRONLY | O_TRUNC, S_IWUSR | S_IRUSR); if (fd == -1) { printwait(messages[MSG_SEL_MISSING], presel); return; } if ((write(fd, &header, sizeof(header)) != (ssize_t)sizeof(header)) || (write(fd, &cfg, sizeof(cfg)) != (ssize_t)sizeof(cfg))) goto END; for (i = 0; i < CTX_MAX; ++i) if ((write(fd, &g_ctx[i].c_cfg, sizeof(settings)) != (ssize_t)sizeof(settings)) || (write(fd, &g_ctx[i].color, sizeof(uint_t)) != (ssize_t)sizeof(uint_t)) || (header.nameln[i] > 0 && write(fd, g_ctx[i].c_name, header.nameln[i]) != (ssize_t)header.nameln[i]) || (header.lastln[i] > 0 && write(fd, g_ctx[i].c_last, header.lastln[i]) != (ssize_t)header.lastln[i]) || (header.fltrln[i] > 0 && write(fd, g_ctx[i].c_fltr, header.fltrln[i]) != (ssize_t)header.fltrln[i]) || (header.pathln[i] > 0 && write(fd, g_ctx[i].c_path, header.pathln[i]) != (ssize_t)header.pathln[i])) goto END; status = TRUE; END: close(fd); if (!status) printwait(messages[MSG_FAILED], presel); } static bool load_session(const char *sname, char **path, char **lastdir, char **lastname, bool restore) { int fd, i = 0; session_header_t header; bool has_loaded_dynamically = !(sname || restore); bool status = (sname && g_state.picker); /* Picker mode with session program option */ char ssnpath[PATH_MAX]; char spath[PATH_MAX]; mkpath(cfgpath, toks[TOK_SSN], ssnpath); if (!restore) { sname = sname ? sname : xreadline(NULL, messages[MSG_SSN_NAME]); if (!sname[0]) return FALSE; mkpath(ssnpath, sname, spath); /* If user is explicitly loading the "last session", skip auto-save */ if ((sname[0] == '@') && !sname[1]) has_loaded_dynamically = FALSE; } else mkpath(ssnpath, "@", spath); if (has_loaded_dynamically) save_session("@", NULL); fd = open(spath, O_RDONLY, S_IWUSR | S_IRUSR); if (fd == -1) { if (!status) { printmsg(messages[MSG_SEL_MISSING]); xdelay(XDELAY_INTERVAL_MS); } return FALSE; } status = FALSE; if ((read(fd, &header, sizeof(header)) != (ssize_t)sizeof(header)) || (header.ver != SESSIONS_VERSION) || (read(fd, &cfg, sizeof(cfg)) != (ssize_t)sizeof(cfg))) goto END; 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'; for (; i < CTX_MAX; ++i) if ((read(fd, &g_ctx[i].c_cfg, sizeof(settings)) != (ssize_t)sizeof(settings)) || (read(fd, &g_ctx[i].color, sizeof(uint_t)) != (ssize_t)sizeof(uint_t)) || (header.nameln[i] > 0 && read(fd, g_ctx[i].c_name, header.nameln[i]) != (ssize_t)header.nameln[i]) || (header.lastln[i] > 0 && read(fd, g_ctx[i].c_last, header.lastln[i]) != (ssize_t)header.lastln[i]) || (header.fltrln[i] > 0 && read(fd, g_ctx[i].c_fltr, header.fltrln[i]) != (ssize_t)header.fltrln[i]) || (header.pathln[i] > 0 && read(fd, g_ctx[i].c_path, header.pathln[i]) != (ssize_t)header.pathln[i])) goto END; *path = g_ctx[cfg.curctx].c_path; *lastdir = g_ctx[cfg.curctx].c_last; *lastname = g_ctx[cfg.curctx].c_name; set_sort_flags('\0'); /* Set correct sort options */ status = TRUE; END: close(fd); if (!status) { printmsg(messages[MSG_FAILED]); xdelay(XDELAY_INTERVAL_MS); } else if (restore) unlink(spath); return status; } #endif static uchar_t get_free_ctx(void) { uchar_t r = cfg.curctx; do r = (r + 1) & ~CTX_MAX; while (g_ctx[r].c_cfg.ctxactive && (r != cfg.curctx)); return r; } /* ctx is absolute: 1 to 4, + for smart context */ static void set_smart_ctx(int ctx, char *nextpath, char **path, char *file, char **lastname, char **lastdir) { if (ctx == '+') /* Get smart context */ ctx = (int)(get_free_ctx() + 1); if (ctx == 0 || ctx == cfg.curctx + 1) { /* Same context */ clearfilter(); 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; DPRINTF_S(nextpath); savecurctx(nextpath, file, ctx); *path = g_ctx[ctx].c_path; *lastdir = g_ctx[ctx].c_last; *lastname = g_ctx[ctx].c_name; } } /* * This function does one of the following depending on the values of `fdout` and `page`: * 1) fdout == -1 && !page: Write up to CMD_LEN_MAX bytes of command output into g_buf * 2) fdout == -1 && page: Create a temp file, write full command output into it and show in pager. * 3) fdout != -1 && !page: Write full command output into the provided file. * 4) fdout != -1 && page: Don't use! Returns FALSE. * * g_buf is modified only in case 1. * g_tmpfpath is modified only in case 2. */ static bool get_output(char *file, char *arg1, char *arg2, int fdout, bool page) { pid_t pid; int pipefd[2]; int index = 0, flags; bool ret = FALSE; bool have_file = fdout != -1; int cmd_in_fd = -1; int cmd_out_fd = -1; ssize_t len; /* * In this case the logic of the function dictates that we should write the output of the command * to `fd` and show it in the pager. But since we didn't open the file descriptor we have no right * to close it, the caller must do it. We don't even know the path to pass to the pager and * it's a real hassle to get it. In general this just invites problems so we are blocking it. */ if (have_file && page) { DPRINTF_S("invalid get_ouptput() call"); return FALSE; } /* Setup file descriptors for child command */ if (!have_file && page) { // Case 2 fdout = create_tmp_file(); if (fdout == -1) return FALSE; cmd_in_fd = STDIN_FILENO; cmd_out_fd = fdout; } else if (have_file) { // Case 3 cmd_in_fd = STDIN_FILENO; cmd_out_fd = fdout; } else { // Case 1 if (pipe(pipefd) == -1) errexit(); for (index = 0; index < 2; ++index) { /* Get previous flags */ flags = fcntl(pipefd[index], F_GETFL, 0); /* Set bit for non-blocking flag */ flags |= O_NONBLOCK; /* Change flags on fd */ fcntl(pipefd[index], F_SETFL, flags); } cmd_in_fd = pipefd[0]; cmd_out_fd = pipefd[1]; } pid = fork(); if (pid == 0) { /* In child */ close(cmd_in_fd); dup2(cmd_out_fd, STDOUT_FILENO); dup2(cmd_out_fd, STDERR_FILENO); close(cmd_out_fd); spawn(file, arg1, arg2, NULL, F_MULTI); _exit(EXIT_SUCCESS); } /* In parent */ waitpid(pid, NULL, 0); /* Do what each case should do */ if (!have_file && page) { // Case 2 close(fdout); spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY); unlink(g_tmpfpath); return TRUE; } if (have_file) // Case 3 return TRUE; // Case 1 len = read(pipefd[0], g_buf, CMD_LEN_MAX - 1); if (len > 0) ret = TRUE; close(pipefd[0]); close(pipefd[1]); return ret; } /* * Follows the stat(1) output closely */ static bool show_stats(char *fpath) { static char * const cmds[] = { #ifdef FILE_MIME_OPTS ("file " FILE_MIME_OPTS), #endif "file -b", #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) "stat -x", #else "stat", #endif }; size_t r = ELEMENTS(cmds); int fd = create_tmp_file(); if (fd == -1) return FALSE; while (r) get_output(cmds[--r], fpath, NULL, fd, FALSE); close(fd); spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY); unlink(g_tmpfpath); return TRUE; } static bool xchmod(const char *fpath, mode_t *mode) { /* (Un)set (S_IXUSR | S_IXGRP | S_IXOTH) */ (0100 & *mode) ? (*mode &= ~0111) : (*mode |= 0111); return (chmod(fpath, *mode) == 0); } static size_t get_fs_info(const char *path, uchar_t type) { struct statvfs svb; if (statvfs(path, &svb) == -1) return 0; if (type == VFS_AVAIL) return (size_t)svb.f_bavail << ffs((int)(svb.f_frsize >> 1)); if (type == VFS_USED) return ((size_t)svb.f_blocks - (size_t)svb.f_bfree) << ffs((int)(svb.f_frsize >> 1)); return (size_t)svb.f_blocks << ffs((int)(svb.f_frsize >> 1)); /* VFS_SIZE */ } /* Create non-existent parents and a file or dir */ static bool xmktree(char *path, bool dir) { char *p = path; char *slash = path; if (!p || !*p) return FALSE; /* Skip the first '/' */ ++p; while (*p != '\0') { if (*p == '/') { slash = p; *p = '\0'; } else { ++p; continue; } /* Create folder from path to '\0' inserted at p */ if (mkdir(path, 0777) == -1 && errno != EEXIST) { #ifdef __HAIKU__ // XDG_CONFIG_HOME contains a directory // that is read-only, but the full path // is writeable. // Try to continue and see what happens. // TODO: Find a more robust solution. if (errno == B_READ_ONLY_DEVICE) goto next; #endif DPRINTF_S("mkdir1!"); DPRINTF_S(strerror(errno)); *slash = '/'; return FALSE; } #ifdef __HAIKU__ next: #endif /* Restore path */ *slash = '/'; ++p; } if (dir) { if (mkdir(path, 0777) == -1 && errno != EEXIST) { DPRINTF_S("mkdir2!"); DPRINTF_S(strerror(errno)); return FALSE; } } else { int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); /* Forced create mode for files */ if (fd == -1 && errno != EEXIST) { DPRINTF_S("open!"); DPRINTF_S(strerror(errno)); return FALSE; } close(fd); } 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 = (!g_state.usebsdtar && getutil(utils[UTIL_ATOOL])); if (op == 'x') { outdir = xreadline(is_atool ? "." : xbasename(fpath), messages[MSG_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 = getcwd(NULL, 0); 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); 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) { char *dir, *cmd = xgetenv("NNN_ARCHMNT", utils[UTIL_ARCHMNT]); char *name = pdents[cur].name; size_t len = pdents[cur].nlen; char mntpath[PATH_MAX]; if (!getutil(cmd)) { printmsg("install utility"); return FALSE; } dir = xstrdup(name); if (!dir) { printmsg(messages[MSG_FAILED]); return FALSE; } while (len > 1) if (dir[--len] == '.') { dir[len] = '\0'; break; } DPRINTF_S(dir); /* Create the mount point */ mkpath(cfgpath, toks[TOK_MNT], mntpath); mkpath(mntpath, dir, newpath); free(dir); if (!xmktree(newpath, TRUE)) { printwarn(NULL); return FALSE; } /* Mount archive */ DPRINTF_S(name); DPRINTF_S(newpath); if (spawn(cmd, name, newpath, NULL, F_NORMAL)) { printmsg(messages[MSG_FAILED]); return FALSE; } return TRUE; } static bool remote_mount(char *newpath) { uchar_t flag = F_CLI; int opt; char *tmp, *env; bool r = getutil(utils[UTIL_RCLONE]), s = getutil(utils[UTIL_SSHFS]); char mntpath[PATH_MAX]; if (!(r || s)) { printmsg("install sshfs/rclone"); return FALSE; } if (r && s) opt = get_input(messages[MSG_REMOTE_OPTS]); else opt = (!s) ? 'r' : 's'; if (opt == 's') env = xgetenv("NNN_SSHFS", utils[UTIL_SSHFS]); else if (opt == 'r') { flag |= F_NOWAIT | F_NOTRACE; env = xgetenv("NNN_RCLONE", "rclone mount"); } else { printmsg(messages[MSG_INVALID_KEY]); return FALSE; } tmp = xreadline(NULL, "host[:dir] > "); if (!tmp[0]) { printmsg(messages[MSG_CANCEL]); return FALSE; } char *div = strchr(tmp, ':'); if (div) *div = '\0'; /* Create the mount point */ mkpath(cfgpath, toks[TOK_MNT], mntpath); mkpath(mntpath, tmp, newpath); if (!xmktree(newpath, TRUE)) { printwarn(NULL); return FALSE; } if (!div) { /* Convert "host" to "host:" */ size_t len = xstrlen(tmp); tmp[len] = ':'; tmp[len + 1] = '\0'; } else *div = ':'; /* Connect to remote */ if (opt == 's') { if (spawn(env, tmp, newpath, NULL, flag)) { printmsg(messages[MSG_FAILED]); return FALSE; } } else { spawn(env, tmp, newpath, NULL, flag); printmsg(messages[MSG_RCLONE_DELAY]); xdelay(XDELAY_INTERVAL_MS << 2); /* Set 4 times the usual delay */ } return TRUE; } /* * Unmounts if the directory represented by name is a mount point. * Otherwise, asks for hostname * Returns TRUE if directory needs to be refreshed *. */ static bool unmount(char *name, char *newpath, int *presel, char *currentpath) { #if defined(__APPLE__) || defined(__FreeBSD__) static char cmd[] = "umount"; #else static char cmd[] = "fusermount3"; /* Arch Linux utility */ static bool found = FALSE; #endif char *tmp = name; struct stat sb, psb; bool child = FALSE; bool parent = FALSE; bool hovered = FALSE; char mntpath[PATH_MAX]; #if !defined(__APPLE__) && !defined(__FreeBSD__) /* On Ubuntu it's fusermount */ if (!found && !getutil(cmd)) { cmd[10] = '\0'; found = TRUE; } #endif mkpath(cfgpath, toks[TOK_MNT], mntpath); if (tmp && strcmp(mntpath, currentpath) == 0) { mkpath(mntpath, tmp, newpath); child = lstat(newpath, &sb) != -1; parent = lstat(xdirname(newpath), &psb) != -1; if (!child && !parent) { *presel = MSGWAIT; return FALSE; } } if (!tmp || !child || !S_ISDIR(sb.st_mode) || (child && parent && sb.st_dev == psb.st_dev)) { tmp = xreadline(NULL, messages[MSG_HOSTNAME]); if (!tmp[0]) return FALSE; if (name && (tmp[0] == '-') && (tmp[1] == '\0')) { mkpath(currentpath, name, newpath); hovered = TRUE; } } if (!hovered) mkpath(mntpath, tmp, newpath); if (!xdiraccess(newpath)) { *presel = MSGWAIT; return FALSE; } #if defined(__APPLE__) || defined(__FreeBSD__) if (spawn(cmd, newpath, NULL, NULL, F_NORMAL)) { #else if (spawn(cmd, "-qu", newpath, NULL, F_NORMAL)) { #endif if (!xconfirm(get_input(messages[MSG_LAZY]))) return FALSE; #ifdef __APPLE__ if (spawn(cmd, "-l", newpath, NULL, F_NORMAL)) { #elif defined(__FreeBSD__) if (spawn(cmd, "-f", newpath, NULL, F_NORMAL)) { #else if (spawn(cmd, "-quz", newpath, NULL, F_NORMAL)) { #endif printwait(messages[MSG_FAILED], presel); return FALSE; } } if (rmdir(newpath) == -1) { printwarn(presel); return FALSE; } return TRUE; } static void lock_terminal(void) { spawn(xgetenv("NNN_LOCKER", utils[UTIL_LOCKER]), NULL, NULL, NULL, F_CLI); } static void printkv(kv *kvarr, FILE *f, uchar_t max, uchar_t id) { char *val = (id == NNN_BMS) ? bmstr : pluginstr; for (uchar_t i = 0; i < max && kvarr[i].key; ++i) fprintf(f, " %c: %s\n", (char)kvarr[i].key, val + kvarr[i].off); } static void printkeys(kv *kvarr, char *buf, uchar_t max) { uchar_t i = 0; for (; i < max && kvarr[i].key; ++i) { buf[i << 1] = ' '; buf[(i << 1) + 1] = kvarr[i].key; } buf[i << 1] = '\0'; } static size_t handle_bookmark(const char *bmark, char *newpath) { int fd = '\r'; size_t r; if (maxbm || bmark) { r = xstrsncpy(g_buf, messages[MSG_KEYS], CMD_LEN_MAX); if (bmark) { /* There is a marked directory */ g_buf[--r] = ' '; g_buf[++r] = ','; g_buf[++r] = '\0'; ++r; } printkeys(bookmark, g_buf + r - 1, maxbm); printmsg(g_buf); fd = get_input(NULL); } r = FALSE; if (fd == ',') /* Visit marked directory */ bmark ? xstrsncpy(newpath, bmark, PATH_MAX) : (r = MSG_NOT_SET); else if (fd == '\r') { /* Visit bookmarks directory */ mkpath(cfgpath, toks[TOK_BM], newpath); g_state.selbm = 1; } else if (!get_kv_val(bookmark, newpath, fd, maxbm, NNN_BMS)) r = MSG_INVALID_KEY; if (!r && chdir(newpath) == -1) { r = MSG_ACCESS; if (g_state.selbm) g_state.selbm = 0; } return r; } static void add_bookmark(char *path, char *newpath, int *presel) { char *dir = xbasename(path); dir = xreadline(dir[0] ? dir : NULL, messages[MSG_BM_NAME]); if (dir && *dir) { size_t r = mkpath(cfgpath, toks[TOK_BM], newpath); newpath[r - 1] = '/'; xstrsncpy(newpath + r, dir, PATH_MAX - r); printwait((symlink(path, newpath) == -1) ? strerror(errno) : newpath, presel); } else printwait(messages[MSG_CANCEL], presel); } /* * The help string tokens (each line) start with a HEX value which indicates * the number of spaces to print before the particular token. In the middle, * %NN can be used to insert a run of spaces, e.g %10 will print 10 spaces. * %NN MUST be 2 characters long, e.g %05 for 5 spaces. * * This method was chosen instead of a flat string because the number of bytes * in help was increasing the binary size by around a hundred bytes. This would * only have increased as we keep adding new options. */ static void show_help(const char *path) { static const char helpstr[] = { "2|V\\_\n" "2/. \\\\\n" "1(;^; ||\n" "3/___3\n" "2(___n))\n" "0\n" "1NAVIGATION\n" "9Up k Up%16PgUp ^U Page up\n" "9Dn j Down%14PgDn ^D Page down\n" "9Lt h Parent%12~ ` @ - ~, /, start, prev\n" "5Ret Rt l Open%20' First file/match\n" "9g ^A Top%21J Jump to entry/offset\n" "9G ^E End%20^J Toggle auto-advance on open\n" "8B (,) Book(mark)%11b ^/ Select bookmark\n" "a1-4 Context%11(Sh)Tab Cycle/new context\n" "62Esc ^Q Quit%19^y Next young\n" "b^G QuitCD%18Q Pick/err, quit\n" "cq Quit context\n" "0\n" "1FILTER & PROMPT\n" "c/ Filter%17^N Toggle type-to-nav\n" "aEsc Exit prompt%12^L Toggle last filter\n" "c. Toggle hidden%05Alt+Esc Unfilter, quit context\n" "0\n" "1FILES\n" "9o ^O Open with%15n Create new/link\n" "9f ^F File stats%14d Detail mode toggle\n" "b^R Rename/dup%14r Batch rename\n" "cz Archive%17e Edit file\n" "c* Toggle exe%14> Export list\n" "6Space + (Un)select%12m-m Select range/clear\n" "ca Select all%14A Invert sel\n" "9p ^P Copy here%12w ^W Cp/mv sel as\n" "9v ^V Move here%15E Edit sel list\n" "9x ^X Delete or trash%09S Listed sel size\n" "cX Delete (rm -rf)%07Esc Send to FIFO\n" "0\n" "1MISC\n" "8Alt ; Select plugin%11= Launch app\n" "9! ^] Shell%19] Cmd prompt\n" "cc Connect remote%10u Unmount remote/archive\n" "9t ^T Sort toggles%12s Manage session\n" "cT Set time type%110 Lock\n" "b^L Redraw%18? Help, conf\n" }; int fd = create_tmp_file(); if (fd == -1) return; FILE *f = fdopen(fd, "wb"); if (f == NULL) { close(fd); unlink(g_tmpfpath); return; } char *prog = xgetenv(env_cfg[NNN_HELP], NULL); if (prog) get_output(prog, NULL, NULL, fd, FALSE); bool hex = true; const char *end = helpstr + sizeof(helpstr) - 1; for (const char *s = helpstr; s < end; ++s) { if (hex) { for (int k = 0, n = xchartohex(*s); k < n; ++k) fputc(' ', f); } else if (*s == '%') { int n = ((s[1] - '0') * 10) + (s[2] - '0'); for (int k = 0; k < n; ++k) fputc(' ', f); s += 2; } else { fputc(*s, f); } hex = (*s == '\n'); } fprintf(f, "\nLOCATIONS\n"); for (uchar_t i = 0; i < CTX_MAX; ++i) if (g_ctx[i].c_cfg.ctxactive) fprintf(f, " %u: %s\n", i + 1, g_ctx[i].c_path); fprintf(f, "\nVOLUME: avail:%s ", coolsize(get_fs_info(path, VFS_AVAIL))); fprintf(f, "used:%s ", coolsize(get_fs_info(path, VFS_USED))); fprintf(f, "size:%s\n\n", coolsize(get_fs_info(path, VFS_SIZE))); if (bookmark) { fprintf(f, "BOOKMARKS\n"); printkv(bookmark, f, maxbm, NNN_BMS); fprintf(f, "\n"); } if (plug) { fprintf(f, "PLUGIN KEYS\n"); printkv(plug, f, maxplug, NNN_PLUG); fprintf(f, "\n"); } for (uchar_t i = NNN_OPENER; i <= NNN_TRASH; ++i) { char *s = getenv(env_cfg[i]); if (s) fprintf(f, "%s: %s\n", env_cfg[i], s); } if (selpath) fprintf(f, "SELECTION FILE: %s\n", selpath); fprintf(f, "\nv%s\n%s\n", VERSION, GENERAL_INFO); fclose(f); // also closes fd spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY); unlink(g_tmpfpath); } static void setexports(const char *path) { char dvar[] = "d0"; char fvar[] = "f0"; if (ndents) { setenv(envs[ENV_NCUR], pdents[cur].name, 1); xstrsncpy(g_ctx[cfg.curctx].c_name, pdents[cur].name, NAME_MAX + 1); } else if (g_ctx[cfg.curctx].c_name[0]) g_ctx[cfg.curctx].c_name[0] = '\0'; for (uchar_t i = 0; i < CTX_MAX; ++i) { if (g_ctx[i].c_cfg.ctxactive) { dvar[1] = fvar[1] = '1' + i; setenv(dvar, g_ctx[i].c_path, 1); if (g_ctx[i].c_name[0]) { mkpath(g_ctx[i].c_path, g_ctx[i].c_name, g_buf); setenv(fvar, g_buf, 1); } } } setenv("NNN_INCLUDE_HIDDEN", xitoa(cfg.showhidden), 1); setenv("NNN_PREFER_SELECTION", xitoa(cfg.prefersel), 1); setenv("PWD", path, 1); } static void run_cmd_as_plugin(const char *file, uchar_t flags) { size_t len; xstrsncpy(g_buf, file, PATH_MAX); len = xstrlen(g_buf); if (len > 1 && g_buf[len - 1] == '*') { flags &= ~F_CONFIRM; /* Skip user confirmation */ g_buf[len - 1] = '\0'; /* Get rid of trailing no confirmation symbol */ --len; } if (flags & F_PAGE) get_output(utils[UTIL_SH_EXEC], g_buf, NULL, -1, TRUE); else spawn(utils[UTIL_SH_EXEC], g_buf, NULL, NULL, flags); } static bool plctrl_init(void) { size_t len; /* g_tmpfpath is used to generate tmp file names */ g_tmpfpath[tmpfplen - 1] = '\0'; len = xstrsncpy(g_pipepath, g_tmpfpath, TMP_LEN_MAX); g_pipepath[len - 1] = '/'; len = xstrsncpy(g_pipepath + len, "nnn-pipe.", TMP_LEN_MAX - len) + len; xstrsncpy(g_pipepath + len - 1, xitoa(getpid()), TMP_LEN_MAX - len); setenv(env_cfg[NNN_PIPE], g_pipepath, TRUE); return EXIT_SUCCESS; } static void rmlistpath(void) { if (listpath) { DPRINTF_S(__func__); DPRINTF_S(listpath); spawn(utils[UTIL_RM_RF], listpath, NULL, NULL, F_NOTRACE | F_MULTI); /* Do not free if program was started in list mode */ if (listpath != initpath) free(listpath); listpath = NULL; } } static ssize_t read_nointr(int fd, void *buf, size_t count) { ssize_t len; do len = read(fd, buf, count); while (len == -1 && errno == EINTR); return len; } static char *readpipe(int fd, char *ctxnum, char **path) { char ctx, *nextpath = NULL; if (read_nointr(fd, g_buf, 1) != 1) return NULL; if (g_buf[0] == '-') { /* Clear selection on '-' */ clearselection(); if (read_nointr(fd, g_buf, 1) != 1) return NULL; } if (g_buf[0] == '+') ctx = (char)(get_free_ctx() + 1); else if (g_buf[0] < '0') return NULL; else { ctx = g_buf[0] - '0'; if (ctx > CTX_MAX) return NULL; } if (read_nointr(fd, g_buf, 1) != 1) return NULL; char op = g_buf[0]; if (op == 'c') { ssize_t len = read_nointr(fd, g_buf, PATH_MAX); if (len <= 0) return NULL; g_buf[len] = '\0'; /* Terminate the path read */ 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') { rmlistpath(); /* Remove last list mode path, if any */ nextpath = load_input(fd, *path); } else if (op == 'p') { free(selpath); selpath = NULL; clearselection(); g_state.picker = 0; g_state.picked = 1; } *ctxnum = ctx; return nextpath; } static bool run_plugin(char **path, const char *file, char *runfile, char **lastname, char **lastdir) { pid_t p; char ctx = 0; uchar_t flags = 0; bool cmd_as_plugin = FALSE; char *nextpath; if (!g_state.pluginit) { plctrl_init(); g_state.pluginit = 1; } setexports(*path); /* Check for run-cmd-as-plugin mode */ if (*file == '!') { flags = F_MULTI | F_CONFIRM; ++file; if (*file == '|') { /* Check if output should be paged */ flags |= F_PAGE; ++file; } else if (*file == '&') { /* Check if GUI flags are to be used */ flags = F_MULTI | F_NOTRACE | F_NOWAIT; ++file; } if (!*file) return FALSE; if ((flags & F_NOTRACE) || (flags & F_PAGE)) { run_cmd_as_plugin(file, flags); return TRUE; } cmd_as_plugin = TRUE; } if (mkfifo(g_pipepath, 0600) != 0) return FALSE; exitcurses(); p = fork(); if (!p) { // In child int wfd = open(g_pipepath, O_WRONLY | O_CLOEXEC); if (wfd == -1) _exit(EXIT_FAILURE); if (!cmd_as_plugin) { char *sel = NULL; char std[2] = "-"; /* Generate absolute path to plugin */ mkpath(plgpath, file, g_buf); if (g_state.picker) sel = selpath ? selpath : std; if (runfile && runfile[0]) { xstrsncpy(*lastname, runfile, NAME_MAX); spawn(g_buf, *lastname, *path, sel, 0); } else spawn(g_buf, NULL, *path, sel, 0); } else run_cmd_as_plugin(file, flags); close(wfd); _exit(EXIT_SUCCESS); } int rfd; do rfd = open(g_pipepath, O_RDONLY); while (rfd == -1 && errno == EINTR); nextpath = readpipe(rfd, &ctx, path); if (nextpath) set_smart_ctx(ctx, nextpath, path, runfile, lastname, lastdir); close(rfd); /* wait for the child to finish. no zombies allowed */ waitpid(p, NULL, 0); refresh(); unlink(g_pipepath); return TRUE; } static bool launch_app(char *newpath) { int r = F_NORMAL; char *tmp = newpath; mkpath(plgpath, utils[UTIL_LAUNCH], newpath); if (!getutil(utils[UTIL_FZF]) || access(newpath, X_OK) < 0) { tmp = xreadline(NULL, messages[MSG_APP_NAME]); r = F_NOWAIT | F_NOTRACE | F_MULTI; } if (tmp && *tmp) // NOLINT spawn(tmp, (r == F_NORMAL) ? "0" : NULL, NULL, NULL, r); return FALSE; } /* Returns TRUE if at least one command was run */ static bool prompt_run(void) { bool ret = FALSE; char *cmdline, *next; int cnt_j, cnt_J, cmd_ret; size_t len; const char *xargs_j = "xargs -0 -I{} %s < %s"; const char *xargs_J = "xargs -0 %s < %s"; char cmd[CMD_LEN_MAX + 32]; // 32 for xargs format strings while (1) { #ifndef NORL if (g_state.picker || g_state.xprompt) { #endif cmdline = xreadline(NULL, PROMPT); #ifndef NORL } else cmdline = getreadline("\n"PROMPT); #endif // Check for an empty command if (!cmdline || !cmdline[0]) break; free(lastcmd); lastcmd = xstrdup(cmdline); ret = TRUE; len = xstrlen(cmdline); cnt_j = 0; next = cmdline; while ((next = strstr(next, "%j"))) { ++cnt_j; // replace %j with {} for xargs later next[0] = '{'; next[1] = '}'; ++next; } cnt_J = 0; next = cmdline; while ((next = strstr(next, "%J"))) { ++cnt_J; // %J should be the last thing in the command if (next == cmdline + len - 2) { cmdline[len - 2] = '\0'; } ++next; } // We can't handle both %j and %J in a single command if (cnt_j && cnt_J) break; if (cnt_j) snprintf(cmd, CMD_LEN_MAX + 32, xargs_j, cmdline, selpath); else if (cnt_J) snprintf(cmd, CMD_LEN_MAX + 32, xargs_J, cmdline, selpath); cmd_ret = spawn(shell, "-c", (cnt_j || cnt_J) ? cmd : cmdline, NULL, F_CLI | F_CONFIRM); if ((cnt_j || cnt_J) && cmd_ret == 0) clearselection(); } return ret; } static bool handle_cmd(enum action sel, char *path, char *newpath) { endselection(FALSE); if (sel == SEL_LAUNCH) return launch_app(newpath); setexports(path); if (sel == SEL_PROMPT) return prompt_run(); /* Set nnn nesting level */ char *tmp = getenv(env_cfg[NNNLVL]); int r = tmp ? atoi(tmp) : 0; setenv(env_cfg[NNNLVL], xitoa(r + 1), 1); spawn(shell, NULL, NULL, NULL, F_CLI); setenv(env_cfg[NNNLVL], xitoa(r), 1); return TRUE; } static void dentfree(void) { free(pnamebuf); free(pdents); free(mark); /* Thread data cleanup */ free(core_blocks); free(core_data); free(core_files); } static void *du_thread(void *p_data) { thread_data *pdata = (thread_data *)p_data; char *path[2] = {pdata->path, NULL}; ullong_t tfiles = 0; blkcnt_t tblocks = 0; struct stat *sb; FTS *tree = fts_open(path, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, 0); FTSENT *node; while ((node = fts_read(tree))) { if (node->fts_info & FTS_D) { if (g_state.interrupt) break; continue; } sb = node->fts_statp; if (cfg.apparentsz) { if (sb->st_size && DU_TEST) tblocks += sb->st_size; } else if (sb->st_blocks && DU_TEST) tblocks += sb->st_blocks; ++tfiles; } fts_close(tree); if (pdata->entnum >= 0) pdents[pdata->entnum].blocks = tblocks; if (!pdata->mntpoint) { core_blocks[pdata->core] += tblocks; core_files[pdata->core] += tfiles; } else core_files[pdata->core] += 1; pthread_mutex_lock(&running_mutex); threadbmp |= (1 << pdata->core); --active_threads; pthread_mutex_unlock(&running_mutex); return NULL; } static void dirwalk(char *path, int entnum, bool mountpoint) { /* Loop till any core is free */ while (active_threads == NUM_DU_THREADS); if (g_state.interrupt) return; pthread_mutex_lock(&running_mutex); int core = ffs(threadbmp) - 1; threadbmp &= ~(1 << core); ++active_threads; pthread_mutex_unlock(&running_mutex); xstrsncpy(core_data[core].path, path, PATH_MAX); core_data[core].entnum = entnum; core_data[core].core = (ushort_t)core; core_data[core].mntpoint = mountpoint; pthread_t tid = 0; pthread_create(&tid, NULL, du_thread, (void *)&(core_data[core])); tolastln(); addstr(xbasename(path)); addstr(" [^C aborts]\n"); refresh(); } static bool prep_threads(void) { if (!g_state.duinit) { /* drop MSB 1s */ threadbmp >>= (32 - NUM_DU_THREADS); if (!core_blocks) core_blocks = calloc(NUM_DU_THREADS, sizeof(blkcnt_t)); if (!core_data) core_data = calloc(NUM_DU_THREADS, sizeof(thread_data)); if (!core_files) core_files = calloc(NUM_DU_THREADS, sizeof(ullong_t)); if (!core_blocks || !core_data || !core_files) { printwarn(NULL); return FALSE; } #ifndef __APPLE__ /* Increase current open file descriptor limit */ max_openfds(); #endif g_state.duinit = TRUE; } else { memset(core_blocks, 0, NUM_DU_THREADS * sizeof(blkcnt_t)); memset(core_data, 0, NUM_DU_THREADS * sizeof(thread_data)); memset(core_files, 0, NUM_DU_THREADS * sizeof(ullong_t)); } return TRUE; } /* Skip self and parent */ static inline bool selforparent(const char *path) { return path[0] == '.' && (path[1] == '\0' || (path[1] == '.' && path[2] == '\0')); } static int dentfill(char *path, struct entry **ppdents) { uchar_t entflags = 0; int flags = 0; struct dirent *dp; char *namep, *pnb, *buf; struct entry *dentp; size_t off = 0, namebuflen = NAMEBUF_INCR; struct stat sb_path, sb; DIR *dirp = opendir(path); ndents = 0; gtimesecs = time(NULL); DPRINTF_S(__func__); if (!dirp) return 0; int fd = dirfd(dirp); if (cfg.blkorder) { num_files = 0; dir_blocks = 0; buf = g_buf; if (fstatat(fd, path, &sb_path, 0) == -1) goto exit; if (!ihashbmp) { ihashbmp = calloc(1, HASH_OCTETS << 3); if (!ihashbmp) goto exit; } else memset(ihashbmp, 0, HASH_OCTETS << 3); if (!prep_threads()) goto exit; attron(COLOR_PAIR(cfg.curctx + 1)); } #if _POSIX_C_SOURCE >= 200112L posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); #endif dp = readdir(dirp); if (!dp) goto exit; #if defined(__sun) || defined(__HAIKU__) flags = AT_SYMLINK_NOFOLLOW; /* no d_type */ #else if (cfg.blkorder || dp->d_type == DT_UNKNOWN) { /* * Optimization added for filesystems which support dirent.d_type * see readdir(3) * Known drawbacks: * - the symlink size is set to 0 * - the modification time of the symlink is set to that of the target file */ flags = AT_SYMLINK_NOFOLLOW; } #endif do { namep = dp->d_name; if (selforparent(namep)) continue; if (!cfg.showhidden && namep[0] == '.') { if (!cfg.blkorder) continue; if (fstatat(fd, namep, &sb, AT_SYMLINK_NOFOLLOW) == -1) continue; if (S_ISDIR(sb.st_mode)) { if (sb_path.st_dev == sb.st_dev) { // NOLINT mkpath(path, namep, buf); // NOLINT dirwalk(buf, -1, FALSE); if (g_state.interrupt) goto exit; } } else { /* Do not recount hard links */ if (sb.st_nlink <= 1 || test_set_bit((uint_t)sb.st_ino)) dir_blocks += (cfg.apparentsz ? sb.st_size : sb.st_blocks); ++num_files; } continue; } if (fstatat(fd, namep, &sb, flags) == -1) { if (flags || (fstatat(fd, namep, &sb, AT_SYMLINK_NOFOLLOW) == -1)) { /* Missing file */ DPRINTF_U(flags); if (!flags) { DPRINTF_S(namep); DPRINTF_S(strerror(errno)); } entflags = FILE_MISSING; memset(&sb, 0, sizeof(struct stat)); } else /* Orphaned symlink */ entflags = SYM_ORPHAN; } if (ndents == total_dents) { if (cfg.blkorder) while (active_threads); total_dents += ENTRY_INCR; *ppdents = xrealloc(*ppdents, total_dents * sizeof(**ppdents)); if (!*ppdents) { free(pnamebuf); closedir(dirp); errexit(); } DPRINTF_P(*ppdents); } /* If not enough bytes left to copy a file name of length NAME_MAX, re-allocate */ if (namebuflen - off < NAME_MAX + 1) { namebuflen += NAMEBUF_INCR; pnb = pnamebuf; pnamebuf = (char *)xrealloc(pnamebuf, namebuflen); if (!pnamebuf) { free(*ppdents); closedir(dirp); errexit(); } DPRINTF_P(pnamebuf); /* realloc() may result in memory move, we must re-adjust if that happens */ if (pnb != pnamebuf) { dentp = *ppdents; dentp->name = pnamebuf; for (int count = 1; count < ndents; ++dentp, ++count) /* Current file name starts at last file name start + length */ (dentp + 1)->name = (char *)((size_t)dentp->name + dentp->nlen); } } dentp = *ppdents + ndents; /* Selection file name */ dentp->name = (char *)((size_t)pnamebuf + off); dentp->nlen = xstrsncpy(dentp->name, namep, NAME_MAX + 1); off += dentp->nlen; /* Copy other fields */ if (cfg.timetype == T_MOD) { dentp->sec = sb.st_mtime; #ifdef __APPLE__ dentp->nsec = (uint_t)sb.st_mtimespec.tv_nsec; #else dentp->nsec = (uint_t)sb.st_mtim.tv_nsec; #endif } else if (cfg.timetype == T_ACCESS) { dentp->sec = sb.st_atime; #ifdef __APPLE__ dentp->nsec = (uint_t)sb.st_atimespec.tv_nsec; #else dentp->nsec = (uint_t)sb.st_atim.tv_nsec; #endif } else { dentp->sec = sb.st_ctime; #ifdef __APPLE__ dentp->nsec = (uint_t)sb.st_ctimespec.tv_nsec; #else dentp->nsec = (uint_t)sb.st_ctim.tv_nsec; #endif } if ((gtimesecs - sb.st_mtime <= 300) || (gtimesecs - sb.st_ctime <= 300)) entflags |= FILE_YOUNG; #if !(defined(__sun) || defined(__HAIKU__)) if (!flags && dp->d_type == DT_LNK) { /* Do not add sizes for links */ dentp->mode = (sb.st_mode & ~S_IFMT) | S_IFLNK; dentp->size = listpath ? sb.st_size : 0; } else { dentp->mode = sb.st_mode; dentp->size = sb.st_size; } #else dentp->mode = sb.st_mode; dentp->size = sb.st_size; #endif #ifndef NOUG dentp->uid = sb.st_uid; dentp->gid = sb.st_gid; #endif dentp->flags = S_ISDIR(sb.st_mode) ? 0 : ((sb.st_nlink > 1) ? HARD_LINK : 0); if (entflags) { dentp->flags |= entflags; entflags = 0; } if (cfg.blkorder) { if (S_ISDIR(sb.st_mode)) { mkpath(path, namep, buf); // NOLINT /* Need to show the disk usage of this dir */ dirwalk(buf, ndents, (sb_path.st_dev != sb.st_dev)); // NOLINT if (g_state.interrupt) goto exit; } else { dentp->blocks = (cfg.apparentsz ? sb.st_size : sb.st_blocks); /* Do not recount hard links */ if (sb.st_nlink <= 1 || test_set_bit((uint_t)sb.st_ino)) dir_blocks += dentp->blocks; ++num_files; } } if (flags) { /* Flag if this is a dir or symlink to a dir */ if (S_ISLNK(sb.st_mode)) { sb.st_mode = 0; fstatat(fd, namep, &sb, 0); } if (S_ISDIR(sb.st_mode)) dentp->flags |= DIR_OR_DIRLNK; #if !(defined(__sun) || defined(__HAIKU__)) /* no d_type */ } else if (dp->d_type == DT_DIR || ((dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN) && S_ISDIR(sb.st_mode))) { dentp->flags |= DIR_OR_DIRLNK; #endif } ++ndents; } while ((dp = readdir(dirp))); exit: if (g_state.duinit && cfg.blkorder) { while (active_threads); attroff(COLOR_PAIR(cfg.curctx + 1)); for (int i = 0; i < NUM_DU_THREADS; ++i) { num_files += core_files[i]; dir_blocks += core_blocks[i]; } } /* Should never be null */ if (closedir(dirp) == -1) errexit(); return ndents; } static void populate(char *path, char *lastname) { #ifdef DEBUG struct timespec ts1, ts2; clock_gettime(CLOCK_REALTIME, &ts1); /* Use CLOCK_MONOTONIC on FreeBSD */ #endif ndents = dentfill(path, &pdents); if (!ndents) return; #ifndef NOSORT ENTSORT(pdents, ndents, entrycmpfn); #endif #ifdef DEBUG clock_gettime(CLOCK_REALTIME, &ts2); DPRINTF_U(ts2.tv_nsec - ts1.tv_nsec); #endif /* Find cur from history */ /* No NULL check for lastname, always points to an array */ move_cursor(*lastname ? dentfind(lastname, ndents) : 0, 0); // Force full redraw last_curscroll = -1; } #ifndef NOFIFO static void notify_fifo(bool force) { if (!fifopath) return; if (fifofd == -1) { fifofd = open(fifopath, O_WRONLY|O_NONBLOCK|O_CLOEXEC); if (fifofd == -1) { if (errno != ENXIO) /* Unexpected error, the FIFO file might have been removed */ /* We give up FIFO notification */ fifopath = NULL; return; } } static struct entry lastentry; if (!force && !memcmp(&lastentry, &pdents[cur], sizeof(struct entry))) // NOLINT return; lastentry = pdents[cur]; char path[PATH_MAX]; size_t len = mkpath(g_ctx[cfg.curctx].c_path, ndents ? pdents[cur].name : "", path); path[len - 1] = '\n'; ssize_t ret = write(fifofd, path, len); if (ret != (ssize_t)len && !(ret == -1 && (errno == EAGAIN || errno == EPIPE))) { DPRINTF_S(strerror(errno)); } } static void send_to_explorer(int *presel) { if (nselected) { int fd = open(fifopath, O_WRONLY|O_NONBLOCK|O_CLOEXEC, 0600); if ((fd == -1) || (seltofile(fd, NULL, NEWLINE) != (size_t)(selbufpos))) printwarn(presel); else { resetselind(); clearselection(); } if (fd > 1) close(fd); } else notify_fifo(TRUE); /* Send opened path to NNN_FIFO */ } #endif static void move_cursor(int target, int ignore_scrolloff) { int onscreen = xlines - 4; /* Leave top 2 and bottom 2 lines */ target = MAX(0, MIN(ndents - 1, target)); last_curscroll = curscroll; last = cur; cur = target; if (!ignore_scrolloff) { int delta = target - last; int scrolloff = MIN(SCROLLOFF, onscreen >> 1); /* * When ignore_scrolloff is 1, the cursor can jump into the scrolloff * margin area, but when ignore_scrolloff is 0, act like a boa * constrictor and squeeze the cursor towards the middle region of the * screen by allowing it to move inward and disallowing it to move * outward (deeper into the scrolloff margin area). */ if (((cur < (curscroll + scrolloff)) && delta < 0) || ((cur > (curscroll + onscreen - scrolloff - 1)) && delta > 0)) curscroll += delta; } curscroll = MIN(curscroll, MIN(cur, ndents - onscreen)); curscroll = MAX(curscroll, MAX(cur - (onscreen - 1), 0)); #ifndef NOFIFO if (!g_state.fifomode) notify_fifo(FALSE); /* Send hovered path to NNN_FIFO */ #endif } static void handle_screen_move(enum action sel) { int onscreen; switch (sel) { case SEL_NEXT: if (cfg.rollover || (cur != ndents - 1)) move_cursor((cur + 1) % ndents, 0); break; case SEL_PREV: if (cfg.rollover || cur) move_cursor((cur + ndents - 1) % ndents, 0); break; case SEL_PGDN: onscreen = xlines - 4; move_cursor(curscroll + (onscreen - 1), 1); curscroll += onscreen - 1; break; case SEL_CTRL_D: onscreen = xlines - 4; move_cursor(curscroll + (onscreen - 1), 1); curscroll += onscreen >> 1; break; case SEL_PGUP: onscreen = xlines - 4; move_cursor(curscroll, 1); curscroll -= onscreen - 1; break; case SEL_CTRL_U: onscreen = xlines - 4; move_cursor(curscroll, 1); curscroll -= onscreen >> 1; break; case SEL_JUMP: { char *input = xreadline(NULL, "jump (+n/-n/n): "); if (!input || !*input) break; if (input[0] == '-') { cur -= atoi(input + 1); if (cur < 0) cur = 0; } else if (input[0] == '+') { cur += atoi(input + 1); if (cur >= ndents) cur = ndents - 1; } else { int index = atoi(input); if ((index < 1) || (index > ndents)) break; cur = index - 1; } onscreen = xlines - 4; move_cursor(cur, 1); curscroll -= onscreen >> 1; break; } case SEL_HOME: move_cursor(0, 1); break; case SEL_END: move_cursor(ndents - 1, 1); break; case SEL_YOUNG: { for (int r = cur;;) { if (++r >= ndents) r = 0; if (r == cur) break; if (pdents[r].flags & FILE_YOUNG) { move_cursor(r, 0); break; } } break; } default: /* case SEL_FIRST */ { int c = get_input(messages[MSG_FIRST]); if (!c) break; c = TOUPPER(c); int r = (c == TOUPPER(*pdents[cur].name)) ? (cur + 1) : 0; for (; r < ndents; ++r) { if (((c == '\'') && !(pdents[r].flags & DIR_OR_DIRLNK)) || (c == TOUPPER(*pdents[r].name))) { move_cursor((r) % ndents, 0); break; } } break; } } } static void handle_openwith(const char *path, const char *name, char *newpath, char *tmp) { /* Confirm if app is CLI or GUI */ int r = get_input(messages[MSG_CLI_MODE]); r = (r == 'c' ? F_CLI : ((r == 'g' || r == '\r') ? (F_NOWAIT | F_NOTRACE | F_MULTI) : 0)); if (r) { mkpath(path, name, newpath); spawn(tmp, newpath, NULL, NULL, r); } } static void copynextname(char *lastname) { if (cur) { cur += (cur != (ndents - 1)) ? 1 : -1; copycurname(); } else lastname[0] = '\0'; } static int handle_context_switch(enum action sel) { int r = -1; switch (sel) { case SEL_CYCLE: // fallthrough case SEL_CYCLER: /* visit next and previous contexts */ r = cfg.curctx; if (sel == SEL_CYCLE) do r = (r + 1) & ~CTX_MAX; while (!g_ctx[r].c_cfg.ctxactive); else { do /* Attempt to create a new context */ r = (r + 1) & ~CTX_MAX; while (g_ctx[r].c_cfg.ctxactive && (r != cfg.curctx)); if (r == cfg.curctx) /* If all contexts are active, reverse cycle */ do r = (r + (CTX_MAX - 1)) & (CTX_MAX - 1); while (!g_ctx[r].c_cfg.ctxactive); } // fallthrough default: /* SEL_CTXN */ if (sel >= SEL_CTX1) /* CYCLE keys are lesser in value */ r = sel - SEL_CTX1; /* Save the next context id */ if (cfg.curctx == r) { if (sel == SEL_CYCLE) (r == CTX_MAX - 1) ? (r = 0) : ++r; else if (sel == SEL_CYCLER) (r == 0) ? (r = CTX_MAX - 1) : --r; else return -1; } } return r; } static int set_sort_flags(int r) { bool session = (r == '\0'); bool reverse = FALSE; if (ISUPPER_(r) && (r != 'R') && (r != 'C')) { reverse = TRUE; r = TOLOWER(r); } /* Set the correct input in case of a session load */ if (session) { if (cfg.apparentsz) { cfg.apparentsz = 0; r = 'a'; } else if (cfg.blkorder) { cfg.blkorder = 0; r = 'd'; } /* Ensure function pointers are in sync with cfg. */ entrycmpfn = cfg.reverse ? &reventrycmp : &entrycmp; namecmpfn = cfg.version ? &xstrverscasecmp : &xstricmp; } else if (r == CONTROL('T')) { /* Cycling order: clear -> size -> time -> clear */ if (cfg.timeorder) r = 's'; else if (cfg.sizeorder) r = 'c'; else r = 't'; } switch (r) { case 'a': /* Apparent du */ cfg.apparentsz ^= 1; if (cfg.apparentsz) { cfg.blkorder = 1; blk_shift = 0; } else cfg.blkorder = 0; // fallthrough case 'd': /* Disk usage */ if (r == 'd') { if (!cfg.apparentsz) cfg.blkorder ^= 1; cfg.apparentsz = 0; blk_shift = ffs(S_BLKSIZE) - 1; } if (cfg.blkorder) cfg.showdetail = 1; cfg.timeorder = 0; cfg.sizeorder = 0; cfg.extnorder = 0; if (!session) { cfg.reverse = 0; entrycmpfn = &entrycmp; } endselection(TRUE); /* We are going to reload dir */ break; case 'c': cfg.timeorder = 0; cfg.sizeorder = 0; cfg.apparentsz = 0; cfg.blkorder = 0; cfg.extnorder = 0; cfg.reverse = 0; cfg.version = 0; entrycmpfn = &entrycmp; namecmpfn = &xstricmp; break; case 'e': /* File extension */ cfg.extnorder ^= 1; cfg.sizeorder = 0; cfg.timeorder = 0; cfg.apparentsz = 0; cfg.blkorder = 0; cfg.reverse = 0; entrycmpfn = &entrycmp; break; case 'r': /* Reverse sort */ cfg.reverse ^= 1; entrycmpfn = cfg.reverse ? &reventrycmp : &entrycmp; break; case 's': /* File size */ cfg.sizeorder ^= 1; cfg.timeorder = 0; cfg.apparentsz = 0; cfg.blkorder = 0; cfg.extnorder = 0; cfg.reverse = 0; entrycmpfn = &entrycmp; break; case 't': /* Time */ cfg.timeorder ^= 1; cfg.sizeorder = 0; cfg.apparentsz = 0; cfg.blkorder = 0; cfg.extnorder = 0; cfg.reverse = 0; entrycmpfn = &entrycmp; break; case 'v': /* Version */ cfg.version ^= 1; namecmpfn = cfg.version ? &xstrverscasecmp : &xstricmp; cfg.timeorder = 0; cfg.sizeorder = 0; cfg.apparentsz = 0; cfg.blkorder = 0; cfg.extnorder = 0; break; default: return 0; } if (reverse) { cfg.reverse = 1; entrycmpfn = &reventrycmp; } cfgsort[cfg.curctx] = (uchar_t)r; return r; } static bool set_time_type(int *presel) { bool ret = FALSE; char buf[] = "'a'ccess / 'c'hange / 'm'od [ ]"; buf[sizeof(buf) - 3] = cfg.timetype == T_MOD ? 'm' : (cfg.timetype == T_ACCESS ? 'a' : 'c'); int r = get_input(buf); if (r == 'a' || r == 'c' || r == 'm') { r = (r == 'm') ? T_MOD : ((r == 'a') ? T_ACCESS : T_CHANGE); if (cfg.timetype != r) { cfg.timetype = r; if (cfg.filtermode || g_ctx[cfg.curctx].c_fltr[1]) *presel = FILTER; ret = TRUE; } else r = MSG_NOCHANGE; } else r = MSG_INVALID_KEY; if (!ret) printwait(messages[r], presel); return ret; } static void statusbar(char *path) { int i = 0, len = 0; char *ptr; pEntry pent = &pdents[cur]; if (!ndents) { printmsg("0/0"); return; } /* Get the file extension for regular files */ if (S_ISREG(pent->mode)) { i = (int)(pent->nlen - 1); ptr = xextension(pent->name, i); if (ptr) len = i - (ptr - pent->name); if (!ptr || len > 5 || len < 2) ptr = "\b"; } else ptr = "\b"; attron(COLOR_PAIR(cfg.curctx + 1)); if (cfg.fileinfo && get_output("file", "-b", pdents[cur].name, -1, FALSE)) mvaddstr(xlines - 2, 2, g_buf); tolastln(); printw("%d/%s ", cur + 1, xitoa(ndents)); if (g_state.selmode || nselected) { attron(A_REVERSE); addch(' '); if (g_state.rangesel) addch('*'); else if (g_state.selmode) addch('+'); if (nselected) addstr(xitoa(nselected)); addch(' '); attroff(A_REVERSE); addch(' '); } if (cfg.blkorder) { /* du mode */ char buf[24]; xstrsncpy(buf, coolsize(dir_blocks << blk_shift), 12); printw("%cu:%s avail:%s files:%llu %lluB %s\n", (cfg.apparentsz ? 'a' : 'd'), buf, coolsize(get_fs_info(path, VFS_AVAIL)), num_files, (ullong_t)pent->blocks << blk_shift, ptr); } else { /* light or detail mode */ char sort[] = "\0\0\0\0\0"; if (getorderstr(sort)) addstr(sort); /* Timestamp */ print_time(&pent->sec, pent->flags); addch(' '); addstr(get_lsperms(pent->mode)); addch(' '); #ifndef NOUG if (g_state.uidgid) { addstr(getpwname(pent->uid)); addch(':'); addstr(getgrname(pent->gid)); addch(' '); } #endif if (S_ISLNK(pent->mode)) { if (!cfg.fileinfo) { i = readlink(pent->name, g_buf, PATH_MAX); addstr(coolsize(i >= 0 ? i : pent->size)); /* Show symlink size */ if (i > 1) { /* Show symlink target */ int y; addstr(" ->"); getyx(stdscr, len, y); i = MIN(i, xcols - y); g_buf[i] = '\0'; addstr(g_buf); } } } else { addstr(coolsize(pent->size)); addch(' '); addstr(ptr); if (pent->flags & HARD_LINK) { struct stat sb; if (stat(pent->name, &sb) != -1) { addch(' '); addstr(xitoa((int)sb.st_nlink)); /* Show number of links */ addch('-'); addstr(xitoa((int)sb.st_ino)); /* Show inode number */ } } } clrtoeol(); } attroff(COLOR_PAIR(cfg.curctx + 1)); /* Place HW cursor on current for Braille systems */ tocursor(); } static inline void markhovered(void) { if (cfg.showdetail && ndents) { /* Bold forward arrowhead */ tocursor(); addch('>' | A_BOLD); } } static int adjust_cols(int n) { /* Calculate the number of cols available to print entry name */ #ifdef ICONS_ENABLED n -= (g_state.oldcolor ? 0 : ICON_SIZE + ICON_PADDING_LEFT_LEN + ICON_PADDING_RIGHT_LEN); #endif if (cfg.showdetail) { /* Fallback to light mode if less than 35 columns */ if (n < 36) cfg.showdetail ^= 1; else /* 2 more accounted for below */ n -= 32; } /* 2 columns for preceding space and indicator */ return (n - 2); } static void draw_line(int ncols) { bool dir = FALSE; ncols = adjust_cols(ncols); if (g_state.oldcolor && (pdents[last].flags & DIR_OR_DIRLNK)) { attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); dir = TRUE; } move(2 + last - curscroll, 0); macos_icons_hack(); printent(last, ncols, FALSE); if (g_state.oldcolor && (pdents[cur].flags & DIR_OR_DIRLNK)) { if (!dir) {/* First file is not a directory */ attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); dir = TRUE; } } else if (dir) { /* Second file is not a directory */ attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); dir = FALSE; } move(2 + cur - curscroll, 0); macos_icons_hack(); printent(cur, ncols, TRUE); /* Must reset e.g. no files in dir */ if (dir) attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); markhovered(); } static void redraw(char *path) { getmaxyx(stdscr, xlines, xcols); int ncols = (xcols <= PATH_MAX) ? xcols : PATH_MAX; int onscreen = xlines - 4; int i, j = 1; // Fast redraw if (g_state.move) { g_state.move = 0; if (ndents && (last_curscroll == curscroll)) return draw_line(ncols); } DPRINTF_S(__func__); /* Clear screen */ erase(); /* Enforce scroll/cursor invariants */ move_cursor(cur, 1); /* Fail redraw if < than 10 columns, context info prints 10 chars */ if (ncols <= MIN_DISPLAY_COL) { printmsg(messages[MSG_FEW_COLUMNS]); return; } //DPRINTF_D(cur); DPRINTF_S(path); for (i = 0; i < CTX_MAX; ++i) { /* 8 chars printed for contexts - "1 2 3 4 " */ if (!g_ctx[i].c_cfg.ctxactive) addch(i + '1'); else addch((i + '1') | (COLOR_PAIR(i + 1) | A_BOLD /* active: underline, current: reverse */ | ((cfg.curctx != i) ? A_UNDERLINE : A_REVERSE))); addch(' '); } attron(A_UNDERLINE | COLOR_PAIR(cfg.curctx + 1)); /* Print path */ bool in_home = set_tilde_in_path(path); char *ptr = in_home ? &path[homelen - 1] : path; i = (int)xstrlen(ptr); if ((i + MIN_DISPLAY_COL) <= ncols) addnstr(ptr, ncols - MIN_DISPLAY_COL); else { char *base = xmemrchr((uchar_t *)ptr, '/', i); if (in_home) { addch(*ptr); ++ptr; i = 1; } else i = 0; if (ptr && (base != ptr)) { while (ptr < base) { if (*ptr == '/') { i += 2; /* 2 characters added */ if (ncols < i + MIN_DISPLAY_COL) { base = NULL; /* Can't print more characters */ break; } addch(*ptr); addch(*(++ptr)); } ++ptr; } } if (base) addnstr(base, ncols - (MIN_DISPLAY_COL + i)); } if (in_home) reset_tilde_in_path(path); attroff(A_UNDERLINE | COLOR_PAIR(cfg.curctx + 1)); /* Go to first entry */ if (curscroll > 0) { move(1, 0); #ifdef ICONS_ENABLED addstr(ICON_ARROW_UP); #else addch('^'); #endif } if (g_state.oldcolor) { attron(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); g_state.dircolor = 1; } onscreen = MIN(onscreen + curscroll, ndents); ncols = adjust_cols(ncols); int len = scanselforpath(path, FALSE); /* Print listing */ for (i = curscroll; i < onscreen; ++i) { move(++j, 0); if (len) findmarkentry(len, &pdents[i]); printent(i, ncols, i == cur); } /* Must reset e.g. no files in dir */ if (g_state.dircolor) { attroff(COLOR_PAIR(cfg.curctx + 1) | A_BOLD); g_state.dircolor = 0; } /* Go to last entry */ if (onscreen < ndents) { move(xlines - 2, 0); #ifdef ICONS_ENABLED addstr(ICON_ARROW_DOWN); #else addch('v'); #endif } markhovered(); } static bool cdprep(char *lastdir, char *lastname, char *path, char *newpath) { if (lastname) lastname[0] = '\0'; /* Save last working directory */ xstrsncpy(lastdir, path, PATH_MAX); /* Save the newly opted dir in path */ xstrsncpy(path, newpath, PATH_MAX); DPRINTF_S(path); clearfilter(); return cfg.filtermode; } static void showselsize(const char *path) { off_t sz = 0; int len = scanselforpath(path, FALSE); for (int r = 0, selcount = nselected; (r < ndents) && selcount; ++r) if (findinsel(findselpos, len + xstrsncpy(g_sel + len, pdents[r].name, pdents[r].nlen))) { sz += cfg.blkorder ? pdents[r].blocks : pdents[r].size; --selcount; } printmsg(coolsize(cfg.blkorder ? sz << blk_shift : sz)); } static bool browse(char *ipath, const char *session, int pkey) { alignas(max_align_t) char newpath[PATH_MAX]; alignas(max_align_t) char runfile[NAME_MAX + 1]; char *path, *lastdir, *lastname, *dir, *tmp; pEntry pent; enum action sel; struct stat sb; int r = -1, presel, selstartid = 0, selendid = 0; const uchar_t opener_flags = (cfg.cliopener ? F_CLI : (F_NOTRACE | F_NOSTDIN | F_NOWAIT)); bool watch = FALSE, cd = TRUE; ino_t inode = 0; #ifndef NOMOUSE MEVENT event = {0}; struct timespec mousetimings[2] = {{.tv_sec = 0, .tv_nsec = 0}, {.tv_sec = 0, .tv_nsec = 0}}; int mousedent[2] = {-1, -1}; bool currentmouse = 1, rightclicksel = 0; #endif atexit(dentfree); getmaxyx(stdscr, xlines, xcols); #ifndef NOSSN /* set-up first context */ if (!session || !load_session(session, &path, &lastdir, &lastname, FALSE)) { #else (void)session; #endif g_ctx[0].c_last[0] = '\0'; lastdir = g_ctx[0].c_last; /* last visited directory */ if (g_state.initfile) { xstrsncpy(g_ctx[0].c_name, xbasename(ipath), sizeof(g_ctx[0].c_name)); xdirname(ipath); } else g_ctx[0].c_name[0] = '\0'; lastname = g_ctx[0].c_name; /* last visited file name */ xstrsncpy(g_ctx[0].c_path, ipath, PATH_MAX); /* If the initial path is a file, retain a way to return to start dir */ if (g_state.initfile) { free(initpath); initpath = ipath = getcwd(NULL, 0); } path = g_ctx[0].c_path; /* current directory */ g_ctx[0].c_fltr[0] = g_ctx[0].c_fltr[1] = '\0'; g_ctx[0].c_cfg = cfg; /* current configuration */ #ifndef NOSSN } #endif newpath[0] = runfile[0] = '\0'; presel = pkey ? ((pkey == CREATE_NEW_KEY) ? 'n' : ';') : ((cfg.filtermode || (session && (g_ctx[cfg.curctx].c_fltr[0] == FILTER || g_ctx[cfg.curctx].c_fltr[0] == RFILTER) && g_ctx[cfg.curctx].c_fltr[1])) ? FILTER : 0); pdents = xrealloc(pdents, total_dents * sizeof(struct entry)); if (!pdents) errexit(); /* Allocate buffer to hold names */ pnamebuf = (char *)xrealloc(pnamebuf, NAMEBUF_INCR); if (!pnamebuf) errexit(); /* The following call is added to handle a broken window at start */ if (presel == FILTER) handle_key_resize(); begin: /* * Can fail when permissions change while browsing. * It's assumed that path IS a directory when we are here. */ if (chdir(path) == -1) { DPRINTF_S("directory inaccessible"); valid_parent(path, lastname); setdirwatch(); } #ifndef NOX11 xterm_cfg(path); #endif #ifdef LINUX_INOTIFY if ((presel == FILTER || watch) && inotify_wd >= 0) { inotify_rm_watch(inotify_fd, inotify_wd); inotify_wd = -1; watch = FALSE; } #elif defined(BSD_KQUEUE) if ((presel == FILTER || watch) && event_fd >= 0) { close(event_fd); event_fd = -1; watch = FALSE; } #elif defined(HAIKU_NM) if ((presel == FILTER || watch) && haiku_hnd != NULL) { haiku_stop_watch(haiku_hnd); haiku_nm_active = FALSE; watch = FALSE; } #endif if (order && cd) { if (cfgsort[cfg.curctx] != '0') { if (cfgsort[cfg.curctx] == 'z') set_sort_flags('c'); if ((!cfgsort[cfg.curctx] || (cfgsort[cfg.curctx] == 'c')) && ((r = get_kv_key(order, path, maxorder, NNN_ORDER)) > 0)) { // NOLINT set_sort_flags(r); cfgsort[cfg.curctx] = 'z'; } } else cfgsort[cfg.curctx] = cfgsort[CTX_MAX]; } cd = TRUE; populate(path, lastname); if (g_state.interrupt) { g_state.interrupt = cfg.apparentsz = cfg.blkorder = 0; blk_shift = BLK_SHIFT_512; presel = CONTROL('L'); } #ifdef LINUX_INOTIFY if (presel != FILTER && inotify_wd == -1) inotify_wd = inotify_add_watch(inotify_fd, path, INOTIFY_MASK); #elif defined(BSD_KQUEUE) if (presel != FILTER && event_fd == -1) { #if defined(O_EVTONLY) event_fd = open(path, O_EVTONLY); #else event_fd = open(path, O_RDONLY); #endif if (event_fd >= 0) EV_SET(&events_to_monitor[0], event_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, KQUEUE_FFLAGS, 0, path); } #elif defined(HAIKU_NM) haiku_nm_active = haiku_watch_dir(haiku_hnd, path) == EXIT_SUCCESS; #endif while (1) { /* Do not do a double redraw in filterentries */ if ((presel != FILTER) || !filterset()) { redraw(path); statusbar(path); } nochange: /* Exit if parent has exited */ if (getppid() == 1) _exit(EXIT_FAILURE); /* If CWD is deleted or moved or perms changed, find an accessible parent */ if (chdir(path) == -1) goto begin; /* If STDIN is no longer a tty (closed) we should exit */ if (!isatty(STDIN_FILENO) && !g_state.picker) return EXIT_FAILURE; sel = nextsel(presel); if (presel) presel = 0; switch (sel) { #ifndef NOMOUSE case SEL_CLICK: if (getmouse(&event) != OK) goto nochange; /* Handle clicking on a context at the top */ if (event.bstate == BUTTON1_PRESSED && event.y == 0) { /* Get context from: "[1 2 3 4]..." */ r = event.x >> 1; /* If clicked after contexts, go to parent */ if (r >= CTX_MAX) sel = SEL_BACK; else if (r >= 0 && r != cfg.curctx) { savecurctx(path, ndents ? pdents[cur].name : NULL, r); /* Reset the pointers */ path = g_ctx[r].c_path; lastdir = g_ctx[r].c_last; lastname = g_ctx[r].c_name; setdirwatch(); goto begin; } } #endif // fallthrough case SEL_BACK: #ifndef NOMOUSE if (sel == SEL_BACK) { #endif dir = visit_parent(path, newpath, &presel); if (!dir) goto nochange; /* Save history */ xstrsncpy(lastname, xbasename(path), NAME_MAX + 1); cdprep(lastdir, NULL, path, dir) ? (presel = FILTER) : (watch = TRUE); goto begin; #ifndef NOMOUSE } #endif #ifndef NOMOUSE /* Middle click action */ if (event.bstate == BUTTON2_PRESSED) { presel = middle_click_key; goto nochange; } #if NCURSES_MOUSE_VERSION > 1 /* Scroll up */ if (event.bstate == BUTTON4_PRESSED && ndents && (cfg.rollover || cur)) { move_cursor((!cfg.rollover && cur < scroll_lines ? 0 : (cur + ndents - scroll_lines) % ndents), 0); break; } /* Scroll down */ if (event.bstate == BUTTON5_PRESSED && ndents && (cfg.rollover || (cur != ndents - 1))) { move_cursor((!cfg.rollover && cur >= ndents - scroll_lines) ? (ndents - 1) : ((cur + scroll_lines) % ndents), 0); break; } #endif /* Toggle filter mode on left click on last 2 lines */ if (event.y >= xlines - 2 && event.bstate == BUTTON1_PRESSED) { clearfilter(); cfg.filtermode ^= 1; if (cfg.filtermode) { presel = FILTER; goto nochange; } /* Start watching the directory */ watch = TRUE; copycurname(); cd = FALSE; goto begin; } /* Handle clicking on a file */ if (event.y >= 2 && event.y <= ndents + 1 && (event.bstate == BUTTON1_PRESSED || event.bstate == BUTTON3_PRESSED)) { r = curscroll + (event.y - 2); if (r != cur) move_cursor(r, 1); #ifndef NOFIFO else if ((event.bstate == BUTTON1_PRESSED) && !g_state.fifomode) notify_fifo(TRUE); /* Send clicked path to NNN_FIFO */ #endif /* Handle right click selection */ if (event.bstate == BUTTON3_PRESSED) { rightclicksel = 1; presel = SELECT; goto nochange; } currentmouse ^= 1; clock_gettime( #if defined(CLOCK_MONOTONIC_RAW) CLOCK_MONOTONIC_RAW, #elif defined(CLOCK_MONOTONIC) CLOCK_MONOTONIC, #else CLOCK_REALTIME, #endif &mousetimings[currentmouse]); mousedent[currentmouse] = cur; /* Single click just selects, double click falls through to SEL_OPEN */ if ((mousedent[0] != mousedent[1]) || (((_ABSSUB(mousetimings[0].tv_sec, mousetimings[1].tv_sec) << 30) + (_ABSSUB(mousetimings[0].tv_nsec, mousetimings[1].tv_nsec))) > DBLCLK_INTERVAL_NS)) break; /* Double click */ mousetimings[currentmouse].tv_sec = 0; mousedent[currentmouse] = -1; sel = SEL_OPEN; } else { if (cfg.filtermode || filterset()) presel = FILTER; copycurname(); goto nochange; } #endif // fallthrough case SEL_NAV_IN: // fallthrough case SEL_OPEN: /* Cannot descend in empty directories */ if (!ndents) { cd = FALSE; g_state.selbm = g_state.runplugin = 0; goto begin; } pent = &pdents[cur]; if (!g_state.selbm || !(S_ISLNK(pent->mode) && bmtarget(pent->name, path, newpath) && xstrsncpy(path, lastdir, PATH_MAX))) mkpath(path, pent->name, newpath); g_state.selbm = 0; DPRINTF_S(newpath); /* Visit directory */ if (pent->flags & DIR_OR_DIRLNK) { if (chdir(newpath) == -1) { printwarn(&presel); goto nochange; } cdprep(lastdir, lastname, path, newpath) ? (presel = FILTER) : (watch = TRUE); goto begin; } /* Cannot use stale data in entry, file may be missing by now */ if (stat(newpath, &sb) == -1) { printwarn(&presel); goto nochange; } DPRINTF_U(sb.st_mode); /* Do not open non-regular files */ if (!S_ISREG(sb.st_mode)) { printwait(messages[MSG_UNSUPPORTED], &presel); goto nochange; } /* Handle plugin selection mode */ if (g_state.runplugin) { g_state.runplugin = 0; /* Must be in plugin dir and same context to select plugin */ if ((g_state.runctx == cfg.curctx) && !strcmp(path, plgpath)) { endselection(FALSE); /* Copy path so we can return back to earlier dir */ xstrsncpy(path, lastdir, PATH_MAX); clearfilter(); if (chdir(path) == -1 || !run_plugin(&path, pent->name, runfile, &lastname, &lastdir)) { DPRINTF_S("plugin failed!"); } if (g_state.picked) return EXIT_SUCCESS; if (runfile[0]) { xstrsncpy(lastname, runfile, NAME_MAX + 1); runfile[0] = '\0'; } setdirwatch(); goto begin; } } #ifndef NOFIFO if (g_state.fifomode && (sel == SEL_OPEN)) { send_to_explorer(&presel); /* Write selection to explorer fifo */ break; } #endif /* If opened as vim plugin and Enter/^M pressed, pick */ if (g_state.picker && (sel == SEL_OPEN)) { if (nselected == 0) /* Pick if none selected */ appendfpath(newpath, mkpath(path, pent->name, newpath)); return EXIT_SUCCESS; } if (sel == SEL_NAV_IN) { /* If in listing dir, go to target on `l` or Right on symlink */ if (listpath && S_ISLNK(pent->mode) && is_prefix(path, listpath, xstrlen(listpath))) { if (!realpath(pent->name, newpath)) { printwarn(&presel); goto nochange; } xdirname(newpath); if (chdir(newpath) == -1) { printwarn(&presel); goto nochange; } cdprep(lastdir, NULL, path, newpath) ? (presel = FILTER) : (watch = TRUE); xstrsncpy(lastname, pent->name, NAME_MAX + 1); goto begin; } /* Open file disabled on right arrow or `l` */ if (cfg.nonavopen) goto nochange; } if (!sb.st_size) { printwait(messages[MSG_EMPTY_FILE], &presel); goto nochange; } if (cfg.useeditor #ifdef FILE_MIME_OPTS && get_output("file", FILE_MIME_OPTS, newpath, -1, FALSE) && is_prefix(g_buf, "text/", 5) #else /* no MIME option; guess from description instead */ && get_output("file", "-bL", newpath, -1, FALSE) && strstr(g_buf, "text") #endif ) { spawn(editor, newpath, NULL, NULL, F_CLI); if (cfg.filtermode) { presel = FILTER; clearfilter(); } continue; } /* Get the extension for regex match */ tmp = xextension(pent->name, pent->nlen - 1); #ifdef PCRE if (tmp && !pcre_exec(archive_pcre, NULL, tmp, pent->nlen - (tmp - pent->name) - 1, 0, 0, NULL, 0)) { #else if (tmp && !regexec(&archive_re, tmp, 0, NULL, 0)) { #endif r = get_input(messages[MSG_ARCHIVE_OPTS]); if (r == '\r') r = 'l'; if (r == 'l' || r == 'x') { mkpath(path, pent->name, newpath); if (!handle_archive(newpath, r)) { presel = MSGWAIT; goto nochange; } if (r == 'l') { statusbar(path); goto nochange; } } if ((r == 'm') && !archive_mount(newpath)) { presel = MSGWAIT; goto nochange; } if (r == 'x' || r == 'm') { if (newpath[0]) set_smart_ctx('+', newpath, &path, ndents ? pdents[cur].name : NULL, &lastname, &lastdir); else copycurname(); clearfilter(); goto begin; } if (r != 'o') { printwait(messages[MSG_INVALID_KEY], &presel); goto nochange; } } /* Invoke desktop opener as last resort */ spawn(opener, newpath, NULL, NULL, opener_flags); /* Move cursor to the next entry if not the last entry */ if (g_state.autonext && cur != ndents - 1) move_cursor((cur + 1) % ndents, 0); if (cfg.filtermode) { presel = FILTER; clearfilter(); } continue; case SEL_NEXT: // fallthrough case SEL_PREV: // fallthrough case SEL_PGDN: // fallthrough case SEL_CTRL_D: // fallthrough case SEL_PGUP: // fallthrough case SEL_CTRL_U: // fallthrough case SEL_HOME: // fallthrough case SEL_END: // fallthrough case SEL_FIRST: // fallthrough case SEL_YOUNG: if (ndents) { g_state.move = 1; handle_screen_move(sel); } break; case SEL_JUMP: if (ndents) { g_state.showlines = 1; redraw(path); handle_screen_move(sel); g_state.showlines = 0; } break; case SEL_CDHOME: // fallthrough case SEL_CDBEGIN: // fallthrough case SEL_CDLAST: // fallthrough case SEL_CDROOT: dir = (sel == SEL_CDHOME) ? home : ((sel == SEL_CDBEGIN) ? ipath : ((sel == SEL_CDLAST) ? lastdir : "/" /* SEL_CDROOT */)); if (!dir || !*dir) { printwait(messages[MSG_NOT_SET], &presel); goto nochange; } g_state.selbm = 0; if (strcmp(path, dir) == 0) { if (dir == ipath) { if (cfg.filtermode) presel = FILTER; goto nochange; } dir = lastdir; /* Go to last dir on home/root key repeat */ } if (chdir(dir) == -1) { presel = MSGWAIT; goto nochange; } /* SEL_CDLAST: dir pointing to lastdir */ xstrsncpy(newpath, dir, PATH_MAX); // fallthrough case SEL_BMOPEN: if (sel == SEL_BMOPEN) { r = (int)handle_bookmark(mark, newpath); if (r) { printwait(messages[r], &presel); goto nochange; } if (g_state.selbm == 1) /* Allow filtering in bookmarks directory */ presel = FILTER; if (strcmp(path, newpath) == 0) break; } /* In list mode, retain the last file name to highlight it, if possible */ cdprep(lastdir, listpath && sel == SEL_CDLAST ? NULL : lastname, path, newpath) ? (presel = FILTER) : (watch = TRUE); goto begin; case SEL_REMOTE: if ((sel == SEL_REMOTE) && !remote_mount(newpath)) { presel = MSGWAIT; goto nochange; } set_smart_ctx('+', newpath, &path, ndents ? pdents[cur].name : NULL, &lastname, &lastdir); clearfilter(); goto begin; case SEL_CYCLE: // fallthrough case SEL_CYCLER: // fallthrough case SEL_CTX1: // fallthrough case SEL_CTX2: // fallthrough case SEL_CTX3: // fallthrough case SEL_CTX4: #ifdef CTX8 case SEL_CTX5: case SEL_CTX6: case SEL_CTX7: case SEL_CTX8: #endif r = handle_context_switch(sel); if (r < 0) continue; savecurctx(path, ndents ? pdents[cur].name : NULL, r); /* Reset the pointers */ path = g_ctx[r].c_path; lastdir = g_ctx[r].c_last; lastname = g_ctx[r].c_name; tmp = g_ctx[r].c_fltr; if (cfg.filtermode || ((tmp[0] == FILTER || tmp[0] == RFILTER) && tmp[1])) presel = FILTER; else watch = TRUE; goto begin; case SEL_MARK: free(mark); mark = xstrdup(path); printwait(mark, &presel); goto nochange; case SEL_BMARK: add_bookmark(path, newpath, &presel); goto nochange; case SEL_FLTR: /* Unwatch dir if we are still in a filtered view */ #ifdef LINUX_INOTIFY if (inotify_wd >= 0) { inotify_rm_watch(inotify_fd, inotify_wd); inotify_wd = -1; } #elif defined(BSD_KQUEUE) if (event_fd >= 0) { close(event_fd); event_fd = -1; } #elif defined(HAIKU_NM) if (haiku_nm_active) { haiku_stop_watch(haiku_hnd); haiku_nm_active = FALSE; } #endif presel = filterentries(path, lastname); if (presel == ESC) { presel = 0; break; } if (presel == FILTER) { /* Refresh dir and filter again */ cd = FALSE; goto begin; } goto nochange; case SEL_MFLTR: // fallthrough case SEL_HIDDEN: // fallthrough case SEL_DETAIL: // fallthrough case SEL_SORT: switch (sel) { case SEL_MFLTR: cfg.filtermode ^= 1; if (cfg.filtermode) { presel = FILTER; clearfilter(); goto nochange; } watch = TRUE; // fallthrough case SEL_HIDDEN: if (sel == SEL_HIDDEN) { cfg.showhidden ^= 1; if (cfg.filtermode) presel = FILTER; clearfilter(); } copycurname(); cd = FALSE; goto begin; case SEL_DETAIL: cfg.showdetail ^= 1; cfg.blkorder = 0; continue; default: /* SEL_SORT */ r = set_sort_flags(get_input(messages[MSG_ORDER])); if (!r) { printwait(messages[MSG_INVALID_KEY], &presel); goto nochange; } } if (cfg.filtermode || filterset()) presel = FILTER; if (ndents) { copycurname(); if (r == 'd' || r == 'a') { presel = 0; goto begin; } ENTSORT(pdents, ndents, entrycmpfn); move_cursor(ndents ? dentfind(lastname, ndents) : 0, 0); } continue; case SEL_STATS: // fallthrough case SEL_CHMODX: if (ndents) { tmp = (listpath && xstrcmp(path, listpath) == 0) ? listroot : path; mkpath(tmp, pdents[cur].name, newpath); if ((sel == SEL_STATS && !show_stats(newpath)) || (lstat(newpath, &sb) == -1) || (sel == SEL_CHMODX && !xchmod(newpath, &sb.st_mode))) { printwarn(&presel); goto nochange; } if (sel == SEL_CHMODX) pdents[cur].mode = sb.st_mode; } break; case SEL_REDRAW: // fallthrough case SEL_RENAMEMUL: // fallthrough case SEL_HELP: // fallthrough case SEL_AUTONEXT: // fallthrough case SEL_EDIT: // fallthrough case SEL_LOCK: { bool refresh = FALSE; if (ndents) mkpath(path, pdents[cur].name, newpath); else if (sel == SEL_EDIT) /* Avoid trying to edit a non-existing file */ goto nochange; switch (sel) { case SEL_REDRAW: refresh = TRUE; break; case SEL_RENAMEMUL: endselection(TRUE); setenv("NNN_INCLUDE_HIDDEN", xitoa(cfg.showhidden), 1); setenv("NNN_PREFER_SELECTION", xitoa(cfg.prefersel), 1); setenv("NNN_LIST", listpath ? listroot : "", 1); if (!(getutil(utils[UTIL_BASH]) && plugscript(utils[UTIL_NMV], F_CLI)) #ifndef NOBATCH && !batch_rename() #endif ) { printwait(messages[MSG_FAILED], &presel); goto nochange; } clearselection(); refresh = TRUE; break; case SEL_HELP: show_help(path); // fallthrough case SEL_AUTONEXT: if (sel == SEL_AUTONEXT) g_state.autonext ^= 1; if (cfg.filtermode) presel = FILTER; copycurname(); goto nochange; case SEL_EDIT: if (!(g_state.picker || g_state.fifomode)) spawn(editor, newpath, NULL, NULL, F_CLI); continue; default: /* SEL_LOCK */ lock_terminal(); break; } /* In case of successful operation, reload contents */ /* Continue in type-to-nav mode, if enabled */ if ((cfg.filtermode || filterset()) && !refresh) { presel = FILTER; goto nochange; } /* Save current */ copycurname(); /* Repopulate as directory content may have changed */ cd = FALSE; goto begin; } case SEL_SEL: if (!ndents) goto nochange; startselection(); if (g_state.rangesel) g_state.rangesel = 0; /* Toggle selection status */ pdents[cur].flags ^= FILE_SELECTED; if (pdents[cur].flags & FILE_SELECTED) { ++nselected; appendfpath(newpath, mkpath(path, pdents[cur].name, newpath)); writesel(pselbuf, selbufpos - 1); /* Truncate NULL from end */ } else { --nselected; rmfromselbuf(mkpath(path, pdents[cur].name, g_sel)); } #ifndef NOX11 if (cfg.x11) plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE); #endif #ifndef NOMOUSE if (rightclicksel) rightclicksel = 0; else #endif /* move cursor to the next entry if this is not the last entry */ if (!g_state.stayonsel && (cur != ndents - 1)) move_cursor((cur + 1) % ndents, 0); break; case SEL_SELMUL: if (!ndents) goto nochange; startselection(); g_state.rangesel ^= 1; if (stat(path, &sb) == -1) { printwarn(&presel); goto nochange; } if (g_state.rangesel) { /* Range selection started */ inode = sb.st_ino; selstartid = cur; continue; } if (inode != sb.st_ino) { printwait(messages[MSG_DIR_CHANGED], &presel); goto nochange; } if (cur < selstartid) { selendid = selstartid; selstartid = cur; } else selendid = cur; /* Clear selection on repeat on same file */ if (selstartid == selendid) { resetselind(); clearselection(); break; } // fallthrough case SEL_SELALL: // fallthrough case SEL_SELINV: if (sel == SEL_SELALL || sel == SEL_SELINV) { if (!ndents) goto nochange; startselection(); if (g_state.rangesel) g_state.rangesel = 0; selstartid = 0; selendid = ndents - 1; } if ((nselected > LARGESEL) || (nselected && (ndents > LARGESEL))) { printmsg("processing..."); refresh(); } r = scanselforpath(path, TRUE); /* Get path length suffixed by '/' */ ((sel == SEL_SELINV) && findselpos) ? invertselbuf(r) : addtoselbuf(r, selstartid, selendid); #ifndef NOX11 if (cfg.x11) plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE); #endif continue; case SEL_SELEDIT: r = editselection(); if (r <= 0) { r = !r ? MSG_0_SELECTED : MSG_FAILED; printwait(messages[r], &presel); } else { #ifndef NOX11 if (cfg.x11) plugscript(utils[UTIL_CBCP], F_NOWAIT | F_NOTRACE); #endif cfg.filtermode ? presel = FILTER : statusbar(path); } goto nochange; case SEL_CP: // fallthrough case SEL_MV: // fallthrough case SEL_CPMVAS: // fallthrough case SEL_TRASH: // fallthrough case SEL_RM_ONLY: { if (sel == SEL_TRASH || sel == SEL_RM_ONLY) { r = get_cur_or_sel(); if (!r) { statusbar(path); goto nochange; } if (r == 'c') { tmp = (listpath && xstrcmp(path, listpath) == 0) ? listroot : path; mkpath(tmp, pdents[cur].name, newpath); if (!xrm(newpath, g_state.trash && sel == SEL_TRASH)) continue; xrmfromsel(tmp, newpath); copynextname(lastname); if (cfg.filtermode || filterset()) presel = FILTER; cd = FALSE; goto begin; } } (nselected == 1 && (sel == SEL_CP || sel == SEL_MV)) ? mkpath(path, xbasename(pselbuf), newpath) : (newpath[0] = '\0'); endselection(TRUE); if (!cpmvrm_selection(sel, path)) { presel = MSGWAIT; goto nochange; } if (cfg.filtermode) presel = FILTER; clearfilter(); #ifndef NOX11 /* Show notification on operation complete */ if (cfg.x11) plugscript(utils[UTIL_NTFY], F_NOWAIT | F_NOTRACE); #endif if (newpath[0] && !access(newpath, F_OK)) xstrsncpy(lastname, xbasename(newpath), NAME_MAX+1); else copycurname(); cd = FALSE; goto begin; } case SEL_ARCHIVE: // fallthrough case SEL_OPENWITH: // fallthrough case SEL_NEW: // fallthrough case SEL_RENAME: { int ret = 'n'; size_t len; if (!ndents && (sel == SEL_OPENWITH || sel == SEL_RENAME)) break; if (sel != SEL_OPENWITH) endselection(TRUE); switch (sel) { case SEL_ARCHIVE: r = get_cur_or_sel(); if (!r) { statusbar(path); goto nochange; } if (r == 's') { if (!selsafe()) { presel = MSGWAIT; goto nochange; } tmp = NULL; } else tmp = pdents[cur].name; tmp = xreadline(tmp, messages[MSG_ARCHIVE_NAME]); break; case SEL_OPENWITH: #ifndef NORL if (g_state.picker || g_state.xprompt) { #endif tmp = xreadline(NULL, messages[MSG_OPEN_WITH]); #ifndef NORL } else tmp = getreadline(messages[MSG_OPEN_WITH]); #endif break; case SEL_NEW: if (!pkey) { r = get_input(messages[MSG_NEW_OPTS]); if (r == '\r') r = 'f'; tmp = NULL; } else { r = 'f'; tmp = g_ctx[0].c_name; pkey = '\0'; } if (r == 'f' || r == 'd') tmp = xreadline(tmp, messages[MSG_NEW_PATH]); else if (r == 's' || r == 'h') tmp = xreadline((nselected == 1 && cfg.prefersel) ? xbasename(pselbuf) : NULL, messages[nselected <= 1 ? MSG_NEW_PATH : MSG_LINK_PREFIX]); else tmp = NULL; break; default: /* SEL_RENAME */ tmp = xreadline(pdents[cur].name, ""); break; } if (!tmp || !*tmp || is_bad_len_or_dir(tmp)) break; switch (sel) { case SEL_ARCHIVE: if (r == 'c' && strcmp(tmp, pdents[cur].name) == 0) continue; /* Cannot overwrite the hovered file */ tmp = abspath(tmp, NULL, newpath); if (!tmp) continue; if (access(tmp, F_OK) == 0) { if (!xconfirm(get_input(messages[MSG_OVERWRITE]))) { statusbar(path); goto nochange; } } (r == 's') ? archive_selection(get_archive_cmd(tmp), tmp) : spawn(get_archive_cmd(tmp), tmp, pdents[cur].name, NULL, F_CLI | F_CONFIRM); if (tmp && (access(tmp, F_OK) == 0)) { /* File created */ if (r == 's') clearselection(); /* Archive operation complete */ /* Check if any entry is created in the current directory */ tmp = get_cwd_entry(path, tmp, &len); if (tmp) { xstrsncpy(lastname, tmp, len + 1); clearfilter(); /* Archive name may not match */ } if (cfg.filtermode) presel = FILTER; cd = FALSE; goto begin; } continue; case SEL_OPENWITH: handle_openwith(path, pdents[cur].name, newpath, tmp); cfg.filtermode ? presel = FILTER : statusbar(path); copycurname(); goto nochange; case SEL_RENAME: r = 0; /* Skip renaming to same name */ if (strcmp(tmp, pdents[cur].name) == 0) { tmp = xreadline(pdents[cur].name, messages[MSG_COPY_NAME]); if (!tmp || !tmp[0] || is_bad_len_or_dir(tmp) || !strcmp(tmp, pdents[cur].name)) { cfg.filtermode ? presel = FILTER : statusbar(path); copycurname(); goto nochange; } ret = 'd'; } break; default: /* SEL_NEW */ break; } if (!(r == 's' || r == 'h')) { tmp = abspath(tmp, path, newpath); if (!tmp) { printwarn(&presel); goto nochange; } } /* Check if another file with same name exists */ if (lstat(tmp, &sb) == 0) { if ((sel == SEL_RENAME) || ((r == 'f') && (S_ISREG(sb.st_mode)))) { /* Overwrite file with same name? */ if (!xconfirm(get_input(messages[MSG_OVERWRITE]))) break; } else { /* Do nothing for SEL_NEW if a non-regular entry exists */ printwait(messages[MSG_EXISTS], &presel); goto nochange; } } if (sel == SEL_RENAME) { /* Rename the file */ if (ret == 'd') spawn("cp -rp --", pdents[cur].name, tmp, NULL, F_SILENT); else if (rename(pdents[cur].name, tmp) != 0) { printwarn(&presel); goto nochange; } /* Check if any entry is created in the current directory */ tmp = get_cwd_entry(path, tmp, &len); if (tmp) xstrsncpy(lastname, tmp, len + 1); /* Directory must be reloeaded for rename case */ } else { /* SEL_NEW */ presel = 0; /* Check if it's a dir or file */ if (r == 'f' || r == 'd') { ret = xmktree(tmp, r == 'f' ? FALSE : TRUE); } else if (r == 's' || r == 'h') { if (nselected > 1 && tmp[0] == '@' && tmp[1] == '\0') tmp[0] = '\0'; ret = xlink(tmp, path, (ndents ? pdents[cur].name : NULL), newpath, r); } if (!ret) printwarn(&presel); if (ret <= 0) goto nochange; if (r == 'f' || r == 'd') { tmp = get_cwd_entry(path, tmp, &len); if (tmp) xstrsncpy(lastname, tmp, len + 1); else continue; /* No change in directory */ } else if (ndents) { if (cfg.filtermode) presel = FILTER; copycurname(); } } clearfilter(); cd = FALSE; goto begin; } case SEL_PLUGIN: /* Check if directory is accessible */ if (!xdiraccess(plgpath)) { printwarn(&presel); goto nochange; } if (!pkey) { r = xstrsncpy(g_buf, messages[MSG_KEYS], CMD_LEN_MAX); printkeys(plug, g_buf + r - 1, maxplug); printmsg(g_buf); r = get_input(NULL); } else { r = pkey; pkey = '\0'; } if (r != '\r') { endselection(FALSE); tmp = get_kv_val(plug, NULL, r, maxplug, NNN_PLUG); if (!tmp) { printwait(messages[MSG_INVALID_KEY], &presel); goto nochange; } if (tmp[0] == '-' && tmp[1]) { ++tmp; r = FALSE; /* Do not refresh dir after completion */ } else r = TRUE; if (!run_plugin(&path, tmp, (ndents ? pdents[cur].name : NULL), &lastname, &lastdir)) { printwait(messages[MSG_FAILED], &presel); goto nochange; } if (g_state.picked) return EXIT_SUCCESS; copycurname(); if (!r) { cfg.filtermode ? presel = FILTER : statusbar(path); goto nochange; } } else { /* 'Return/Enter' enters the plugin directory */ g_state.runplugin ^= 1; if (!g_state.runplugin) { /* * If toggled, and still in the plugin dir, * switch to original directory */ if (strcmp(path, plgpath) == 0) { xstrsncpy(path, lastdir, PATH_MAX); xstrsncpy(lastname, runfile, NAME_MAX + 1); runfile[0] = '\0'; setdirwatch(); goto begin; } /* Otherwise, initiate choosing plugin again */ g_state.runplugin = 1; } xstrsncpy(lastdir, path, PATH_MAX); xstrsncpy(path, plgpath, PATH_MAX); if (ndents) xstrsncpy(runfile, pdents[cur].name, NAME_MAX); g_state.runctx = cfg.curctx; lastname[0] = '\0'; clearfilter(); } setdirwatch(); if (g_state.runplugin == 1) /* Allow filtering in plugins directory */ presel = FILTER; goto begin; case SEL_SELSIZE: showselsize(path); goto nochange; case SEL_SHELL: // fallthrough case SEL_LAUNCH: // fallthrough case SEL_PROMPT: r = handle_cmd(sel, path, newpath); /* Continue in type-to-nav mode, if enabled */ if (cfg.filtermode) presel = FILTER; /* Save current */ copycurname(); if (!r) goto nochange; /* Repopulate as directory content may have changed */ cd = FALSE; goto begin; case SEL_UMOUNT: presel = MSG_ZERO; if (!unmount((ndents ? pdents[cur].name : NULL), newpath, &presel, path)) { if (presel == MSG_ZERO) statusbar(path); goto nochange; } /* Dir removed, go to next entry */ copynextname(lastname); cd = FALSE; goto begin; #ifndef NOSSN case SEL_SESSIONS: r = get_input(messages[MSG_SSN_OPTS]); if (r == 's') { tmp = xreadline(NULL, messages[MSG_SSN_NAME]); if (tmp && *tmp) save_session(tmp, &presel); } else if (r == 'l' || r == 'r') { if (load_session(NULL, &path, &lastdir, &lastname, r == 'r')) { setdirwatch(); goto begin; } } statusbar(path); goto nochange; #endif case SEL_EXPORT: export_file_list(); cfg.filtermode ? presel = FILTER : statusbar(path); goto nochange; case SEL_TIMETYPE: if (!set_time_type(&presel)) goto nochange; cd = FALSE; goto begin; case SEL_QUITCTX: // fallthrough case SEL_QUITCD: // fallthrough case SEL_QUIT: case SEL_QUITERR: if (sel == SEL_QUITCTX) { int ctx = cfg.curctx; for (r = (ctx - 1) & (CTX_MAX - 1); (r != ctx) && !g_ctx[r].c_cfg.ctxactive; r = ((r - 1) & (CTX_MAX - 1))) { }; if (r != ctx) { g_ctx[ctx].c_cfg.ctxactive = 0; /* Switch to next active context */ path = g_ctx[r].c_path; lastdir = g_ctx[r].c_last; lastname = g_ctx[r].c_name; g_ctx[r].c_cfg.curctx = r; setcfg(g_ctx[r].c_cfg); setdirwatch(); goto begin; } } else if (!g_state.forcequit) { for (r = 0; r < CTX_MAX; ++r) if (r != cfg.curctx && g_ctx[r].c_cfg.ctxactive) { r = get_input(messages[MSG_QUIT_ALL]); break; } if (!(r == CTX_MAX || xconfirm(r))) break; // fallthrough } /* CD on Quit */ tmp = getenv("NNN_TMPFILE"); if ((sel == SEL_QUITCD) || tmp) { write_lastdir(path, tmp); /* ^G is a way to quit picker mode without picking anything */ if ((sel == SEL_QUITCD) && g_state.picker) selbufpos = 0; } if (sel != SEL_QUITERR) return EXIT_SUCCESS; if (selbufpos && !g_state.picker) { /* Pick files to stdout and exit */ g_state.picker = 1; free(selpath); selpath = NULL; return EXIT_SUCCESS; } return EXIT_FAILURE; default: if (xlines != LINES || xcols != COLS) continue; if (idletimeout && idle == idletimeout) { lock_terminal(); /* Locker */ idle = 0; } copycurname(); goto nochange; } /* switch (sel) */ } } static char *make_tmp_tree(char **paths, ssize_t entries, const char *prefix) { /* tmpdir holds the full path */ /* tmp holds the path without the tmp dir prefix */ int err; struct stat sb; char *slash, *tmp; ssize_t len = xstrlen(prefix); char *tmpdir = malloc(PATH_MAX); if (!tmpdir) { DPRINTF_S(strerror(errno)); return NULL; } tmp = tmpdir + tmpfplen - 1; xstrsncpy(tmpdir, g_tmpfpath, tmpfplen); xstrsncpy(tmp, "/nnnXXXXXX", 11); /* Points right after the base tmp dir */ tmp += 10; /* handle the case where files are directly under / */ if (!prefix[1] && (prefix[0] == '/')) len = 0; if (!mkdtemp(tmpdir)) { free(tmpdir); DPRINTF_S(strerror(errno)); return NULL; } listpath = tmpdir; for (ssize_t i = 0; i < entries; ++i) { if (!paths[i]) continue; err = stat(paths[i], &sb); if (err && errno == ENOENT) continue; /* Don't copy the common prefix */ xstrsncpy(tmp, paths[i] + len, xstrlen(paths[i]) - len + 1); /* Get the dir containing the path */ slash = xmemrchr((uchar_t *)tmp, '/', xstrlen(paths[i]) - len); if (slash) *slash = '\0'; if (access(tmpdir, F_OK)) /* Create directory if it doesn't exist */ xmktree(tmpdir, TRUE); if (slash) *slash = '/'; if (symlink(paths[i], tmpdir)) { DPRINTF_S(paths[i]); DPRINTF_S(strerror(errno)); } } /* Get the dir in which to start */ *tmp = '\0'; return tmpdir; } static char *load_input(int fd, const char *path) { size_t i, chunk_count = 1, chunk = 512UL * 1024 /* 512 KiB chunk size */, entries = 0; char *input = malloc(sizeof(char) * chunk), *tmpdir = NULL; char cwd[PATH_MAX], *next; size_t offsets[LIST_FILES_MAX]; char **paths = NULL; ssize_t input_read, total_read = 0, off = 0; int msgnum = 0; if (!input) { DPRINTF_S(strerror(errno)); return NULL; } if (!path) { if (!getcwd(cwd, PATH_MAX)) { free(input); return NULL; } } else xstrsncpy(cwd, path, PATH_MAX); while (chunk_count < LIST_INPUT_MAX / chunk && !msgnum) { input_read = read(fd, input + total_read, chunk); if (input_read < 0) { if (errno == EINTR) continue; DPRINTF_S(strerror(errno)); goto malloc_1; } if (input_read == 0) break; total_read += input_read; while (off < total_read) { next = memchr(input + off, '\0', total_read - off); if (!next) break; ++next; if (next - input == off + 1) { off = next - input; continue; } if (entries == LIST_FILES_MAX) { msgnum = MSG_FILE_LIMIT; break; } offsets[entries++] = off; off = next - input; } /* We don't need to allocate another chunk */ if (chunk_count > (total_read + chunk - 1) / chunk) continue; /* We can never need more than one additional chunk */ ++chunk_count; if (chunk_count > LIST_INPUT_MAX / chunk) { msgnum = MSG_SIZE_LIMIT; break; } input = xrealloc(input, chunk_count * chunk); if (!input) goto malloc_1; } /* We close fd outside this function. Any extra data is left to the kernel to handle */ if (off != total_read) { if (entries == LIST_FILES_MAX) msgnum = MSG_FILE_LIMIT; else offsets[entries++] = off; } DPRINTF_D(entries); DPRINTF_D(total_read); DPRINTF_D(chunk_count); if (!entries) { msgnum = MSG_0_ENTRIES; goto malloc_1; } input[total_read] = '\0'; paths = malloc(entries * sizeof(char *)); if (!paths) goto malloc_1; for (i = 0; i < entries; ++i) paths[i] = input + offsets[i]; listroot = malloc(sizeof(char) * PATH_MAX); if (!listroot) goto malloc_1; listroot[0] = '\0'; DPRINTF_S(paths[0]); for (i = 0; i < entries; ++i) { if (paths[i][0] == '\n' || selforparent(paths[i])) { paths[i] = NULL; continue; } paths[i] = abspath(paths[i], cwd, NULL); if (!paths[i]) { entries = i; // free from the previous entry goto malloc_2; } DPRINTF_S(paths[i]); xstrsncpy(g_buf, paths[i], PATH_MAX); if (!common_prefix(xdirname(g_buf), listroot)) { entries = i + 1; // free from the current entry goto malloc_2; } DPRINTF_S(listroot); } DPRINTF_S(listroot); if (listroot[0]) tmpdir = make_tmp_tree(paths, entries, listroot); malloc_2: for (i = 0; i < entries; ++i) free(paths[i]); malloc_1: if (msgnum) { /* Check if we are past init stage and show msg */ if (home) { printmsg(messages[msgnum]); xdelay(XDELAY_INTERVAL_MS << 2); } else { msg(messages[msgnum]); usleep(XDELAY_INTERVAL_MS << 2); } } free(input); free(paths); return tmpdir; } static void check_key_collision(void) { wint_t key; bool bitmap[KEY_MAX] = {FALSE}; for (ullong_t i = 0; i < ELEMENTS(bindings); ++i) { key = bindings[i].sym; if (bitmap[key]) fprintf(stderr, "key collision! [%s]\n", keyname(key)); else bitmap[key] = TRUE; } } static void usage(void) { fprintf(stdout, "%s: nnn [OPTIONS] [PATH]\n\n" "The unorthodox terminal file manager.\n\n" "positional args:\n" " PATH start dir/file [default: .]\n\n" "optional args:\n" #ifndef NOFIFO " -a auto NNN_FIFO\n" #endif " -A no dir auto-enter during filter\n" " -b key open bookmark key (trumps -s/S)\n" " -B use bsdtar for archives\n" " -c cli-only NNN_OPENER (trumps -e)\n" " -C 8-color scheme\n" " -d detail mode\n" " -D dirs in context color\n" " -e text in $VISUAL/$EDITOR/vi\n" " -E internal edits in EDITOR\n" #ifndef NORL " -f use readline history file\n" #endif #ifndef NOFIFO " -F val fifo mode [0:preview 1:explore]\n" #endif " -g regex filters\n" " -H show hidden files\n" " -i show current file info\n" " -J no auto-advance on selection\n" " -K detect key collision and exit\n" " -l val set scroll lines\n" " -n type-to-nav mode\n" #ifndef NORL " -N use native prompt\n" #endif " -o open files only on Enter\n" " -p file selection file [-:stdout]\n" " -P key run plugin key\n" " -Q no quit confirmation\n" " -r use advcpmv patched cp, mv\n" " -R no rollover at edges\n" #ifndef NOSSN " -s name load session by name\n" " -S persistent session\n" #endif " -t secs timeout to lock\n" " -T key sort order [a/d/e/r/s/t/v]\n" " -u use selection (no prompt)\n" #ifndef NOUG " -U show user and group\n" #endif " -V show version\n" #ifndef NOX11 " -x notis, selection sync, xterm title\n" #endif " -0 null separator in picker mode\n" " -h show help\n\n" "v%s\n%s\n", __func__, VERSION, GENERAL_INFO); } static bool setup_config(void) { size_t r, len; char *xdgcfg = getenv("XDG_CONFIG_HOME"); bool xdg = FALSE; /* Set up configuration file paths */ if (xdgcfg && xdgcfg[0]) { DPRINTF_S(xdgcfg); if (tilde_is_home(xdgcfg)) { r = xstrsncpy(g_buf, home, PATH_MAX); xstrsncpy(g_buf + r - 1, xdgcfg + 1, PATH_MAX); xdgcfg = g_buf; DPRINTF_S(xdgcfg); } if (!xdiraccess(xdgcfg)) { xerror(); return FALSE; } len = xstrlen(xdgcfg) + xstrlen("/nnn/bookmarks") + 1; xdg = TRUE; } if (!xdg) len = xstrlen(home) + xstrlen("/.config/nnn/bookmarks") + 1; cfgpath = (char *)malloc(len); plgpath = (char *)malloc(len); if (!cfgpath || !plgpath) { xerror(); return FALSE; } if (xdg) { xstrsncpy(cfgpath, xdgcfg, len); r = len - xstrlen("/nnn/bookmarks"); } else { r = xstrsncpy(cfgpath, home, len); /* Create ~/.config */ xstrsncpy(cfgpath + r - 1, "/.config", len - r); DPRINTF_S(cfgpath); r += 8; /* length of "/.config" */ } /* Create ~/.config/nnn */ xstrsncpy(cfgpath + r - 1, "/nnn", len - r); DPRINTF_S(cfgpath); /* Create bookmarks, sessions, mounts and plugins directories */ for (r = 0; r < ELEMENTS(toks); ++r) { mkpath(cfgpath, toks[r], plgpath); /* The dirs are created on first run, check if they already exist */ if (access(plgpath, F_OK) && !xmktree(plgpath, TRUE)) { DPRINTF_S(toks[r]); xerror(); return FALSE; } } /* Set selection file path */ if (!g_state.picker) { char *env_sel = xgetenv(env_cfg[NNN_SEL], NULL); selpath = env_sel ? xstrdup(env_sel) : (char *)malloc(len + 3); /* Length of "/.config/nnn/.selection" */ if (!selpath) { xerror(); return FALSE; } if (!env_sel) { r = xstrsncpy(selpath, cfgpath, len + 3); xstrsncpy(selpath + r - 1, "/.selection", 12); DPRINTF_S(selpath); } } return TRUE; } static bool set_tmp_path(void) { char *tmp = "/tmp"; char *path = xdiraccess(tmp) ? tmp : getenv("TMPDIR"); if (!path) { msg("set TMPDIR"); return FALSE; } tmpfplen = (uchar_t)xstrsncpy(g_tmpfpath, path, TMP_LEN_MAX); DPRINTF_S(g_tmpfpath); DPRINTF_U(tmpfplen); return TRUE; } static void cleanup(void) { #ifndef NOX11 if (cfg.x11 && !g_state.picker) { printf("\033[23;0t"); /* reset terminal window title */ fflush(stdout); } #endif free(selpath); free(plgpath); free(cfgpath); free(initpath); free(bmstr); free(pluginstr); free(listroot); free(ihashbmp); free(bookmark); free(plug); free(lastcmd); #ifndef NOFIFO if (g_state.autofifo) unlink(fifopath); #endif if (g_state.pluginit) unlink(g_pipepath); #ifdef DEBUG disabledbg(); #endif } int main(int argc, char *argv[]) { char *arg = NULL; char *session = NULL; int fd, opt, sort = 0, pkey = '\0'; /* Plugin key */ bool sepnul = FALSE; #ifndef NOMOUSE mmask_t mask; char *middle_click_env = xgetenv(env_cfg[NNN_MCLICK], "\0"); middle_click_key = (middle_click_env[0] == '^' && middle_click_env[1]) ? CONTROL(middle_click_env[1]) : (uchar_t)middle_click_env[0]; #endif const char * const env_opts = xgetenv(env_cfg[NNN_OPTS], NULL); int env_opts_id = env_opts ? (int)xstrlen(env_opts) : -1; #ifndef NORL bool rlhist = FALSE; #endif while ((opt = (env_opts_id > 0 ? env_opts[--env_opts_id] : getopt(argc, argv, "aAb:BcCdDeEfF:gHiJKl:nNop:P:QrRs:St:T:uUVx0h"))) != -1) { switch (opt) { #ifndef NOFIFO case 'a': g_state.autofifo = 1; break; #endif case 'A': cfg.autoenter = 0; break; case 'b': if (env_opts_id < 0) arg = optarg; break; case 'B': g_state.usebsdtar = 1; break; case 'c': cfg.cliopener = 1; break; case 'C': g_state.oldcolor = 1; break; case 'd': cfg.showdetail = 1; break; case 'D': g_state.dirctx = 1; break; case 'e': cfg.useeditor = 1; break; case 'E': cfg.waitedit = 1; break; case 'f': #ifndef NORL rlhist = TRUE; #endif break; #ifndef NOFIFO case 'F': if (env_opts_id < 0) { fd = atoi(optarg); if ((fd < 0) || (fd > 1)) return EXIT_FAILURE; g_state.fifomode = fd; } break; #endif case 'g': cfg.regex = 1; filterfn = &visible_re; break; case 'H': cfg.showhidden = 1; break; case 'i': cfg.fileinfo = 1; break; case 'J': g_state.stayonsel = 1; break; case 'K': check_key_collision(); return EXIT_SUCCESS; case 'l': if (env_opts_id < 0) scroll_lines = atoi(optarg); break; case 'n': cfg.filtermode = 1; break; #ifndef NORL case 'N': g_state.xprompt = 1; break; #endif case 'o': cfg.nonavopen = 1; break; case 'p': if (env_opts_id >= 0) break; g_state.picker = 1; if (!(optarg[0] == '-' && optarg[1] == '\0')) { fd = open(optarg, O_WRONLY | O_CREAT, 0600); if (fd == -1) { xerror(); return EXIT_FAILURE; } close(fd); selpath = abspath(optarg, NULL, NULL); unlink(selpath); } break; case 'P': if (env_opts_id < 0 && !optarg[1]) pkey = (uchar_t)optarg[0]; break; case 'Q': g_state.forcequit = 1; break; case 'r': #ifdef __linux__ memcpy(cp, PROGRESS_CP, sizeof PROGRESS_CP); memcpy(mv, PROGRESS_MV, sizeof PROGRESS_MV); #endif break; case 'R': cfg.rollover = 0; break; #ifndef NOSSN case 's': if (env_opts_id < 0) session = optarg; break; case 'S': g_state.prstssn = 1; if (!session) /* Support named persistent sessions */ session = "@"; break; #endif case 't': if (env_opts_id < 0) idletimeout = atoi(optarg); break; case 'T': if (env_opts_id < 0) sort = (uchar_t)optarg[0]; break; case 'u': cfg.prefersel = 1; break; case 'U': g_state.uidgid = 1; break; case 'V': fprintf(stdout, "%s\n", VERSION); return EXIT_SUCCESS; case 'x': cfg.x11 = 1; break; case '0': sepnul = TRUE; break; case 'h': usage(); return EXIT_SUCCESS; default: usage(); return EXIT_FAILURE; } if (env_opts_id == 0) env_opts_id = -1; } #ifdef DEBUG enabledbg(); DPRINTF_S(VERSION); #endif /* Prefix for temporary files */ if (!set_tmp_path()) return EXIT_FAILURE; atexit(cleanup); /* Check if we are in path list mode */ if (!isatty(STDIN_FILENO)) { /* This is the same as listpath */ initpath = load_input(STDIN_FILENO, NULL); if (!initpath) return EXIT_FAILURE; /* We return to tty */ if (!isatty(STDOUT_FILENO)) { fd = open(ctermid(NULL), O_RDONLY, 0400); dup2(fd, STDIN_FILENO); close(fd); } else dup2(STDOUT_FILENO, STDIN_FILENO); if (session) session = NULL; } home = getenv("HOME"); if (!home) { msg("set HOME"); return EXIT_FAILURE; } DPRINTF_S(home); homelen = (uchar_t)xstrlen(home); if (!setup_config()) return EXIT_FAILURE; /* Get custom opener, if set */ opener = xgetenv(env_cfg[NNN_OPENER], utils[UTIL_OPENER]); DPRINTF_S(opener); /* Parse bookmarks string */ if (!parsekvpair(&bookmark, &bmstr, NNN_BMS, &maxbm)) { msg(env_cfg[NNN_BMS]); return EXIT_FAILURE; } /* Parse plugins string */ if (!parsekvpair(&plug, &pluginstr, NNN_PLUG, &maxplug)) { msg(env_cfg[NNN_PLUG]); return EXIT_FAILURE; } /* Parse order string */ if (!parsekvpair(&order, &orderstr, NNN_ORDER, &maxorder)) { msg(env_cfg[NNN_ORDER]); return EXIT_FAILURE; } if (!initpath) { if (arg) { /* Open a bookmark directly */ if (!arg[1]) /* Bookmarks keys are single char */ initpath = get_kv_val(bookmark, NULL, *arg, maxbm, NNN_BMS); if (!initpath) { msg(messages[MSG_INVALID_KEY]); return EXIT_FAILURE; } if (session) session = NULL; } else if (argc == optind) { /* Start in the current directory */ char *startpath = getenv("PWD"); initpath = (startpath && *startpath) ? xstrdup(startpath) : getcwd(NULL, 0); if (!initpath) initpath = "/"; } else { /* Open a file */ arg = argv[optind]; DPRINTF_S(arg); size_t len = xstrlen(arg); if (len > 7 && is_prefix(arg, "file://", 7)) { arg = arg + 7; len -= 7; } initpath = abspath(arg, NULL, NULL); DPRINTF_S(initpath); if (!initpath) { xerror(); return EXIT_FAILURE; } /* If the file is hidden, enable hidden option */ if (*xbasename(initpath) == '.') cfg.showhidden = 1; /* * If nnn is set as the file manager, applications may try to open * files by invoking nnn. In that case pass the file path to the * desktop opener and exit. */ struct stat sb; if (stat(initpath, &sb) == -1) { bool dir = (arg[len - 1] == '/'); if (!dir) { arg = xbasename(initpath); initpath = xdirname(initpath); pkey = CREATE_NEW_KEY; /* Override plugin key */ g_state.initfile = 1; } if (dir || (arg != initpath)) { /* We have a directory */ if (!xdiraccess(initpath) && !xmktree(initpath, TRUE)) { xerror(); /* Fail if directory cannot be created */ return EXIT_FAILURE; } if (!dir) /* Restore the complete path */ *--arg = '/'; } } else if (!S_ISDIR(sb.st_mode)) g_state.initfile = 1; if (session) session = NULL; } } /* Set archive handling (enveditor used as tmp var) */ enveditor = getenv(env_cfg[NNN_ARCHIVE]); #ifdef PCRE if (setfilter(&archive_pcre, (enveditor ? enveditor : patterns[P_ARCHIVE]))) { #else if (setfilter(&archive_re, (enveditor ? enveditor : patterns[P_ARCHIVE]))) { #endif msg(messages[MSG_INVALID_REG]); return EXIT_FAILURE; } /* An all-CLI opener overrides option -e) */ if (cfg.cliopener) cfg.useeditor = 0; /* Get VISUAL/EDITOR */ enveditor = xgetenv(envs[ENV_EDITOR], utils[UTIL_VI]); editor = xgetenv(envs[ENV_VISUAL], enveditor); DPRINTF_S(getenv(envs[ENV_VISUAL])); DPRINTF_S(getenv(envs[ENV_EDITOR])); DPRINTF_S(editor); /* Get PAGER */ pager = xgetenv(envs[ENV_PAGER], utils[UTIL_LESS]); DPRINTF_S(pager); /* Get SHELL */ shell = xgetenv(envs[ENV_SHELL], utils[UTIL_SH]); DPRINTF_S(shell); DPRINTF_S(getenv("PWD")); #ifndef NOFIFO /* Create fifo */ if (g_state.autofifo) { g_tmpfpath[tmpfplen - 1] = '\0'; size_t r = mkpath(g_tmpfpath, "nnn-fifo.", g_buf); xstrsncpy(g_buf + r - 1, xitoa(getpid()), PATH_MAX - r); setenv("NNN_FIFO", g_buf, TRUE); } fifopath = xgetenv("NNN_FIFO", NULL); if (fifopath) { if (mkfifo(fifopath, 0600) != 0 && !(errno == EEXIST && access(fifopath, W_OK) == 0)) { xerror(); return EXIT_FAILURE; } sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, NULL); } #endif #ifdef LINUX_INOTIFY /* Initialize inotify */ inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (inotify_fd < 0) { xerror(); return EXIT_FAILURE; } #elif defined(BSD_KQUEUE) kq = kqueue(); if (kq < 0) { xerror(); return EXIT_FAILURE; } #elif defined(HAIKU_NM) haiku_hnd = haiku_init_nm(); if (!haiku_hnd) { xerror(); return EXIT_FAILURE; } #endif /* Configure trash preference */ opt = xgetenv_val(env_cfg[NNN_TRASH]); if (opt && opt <= 2) g_state.trash = opt; /* Ignore/handle certain signals */ struct sigaction act = {.sa_handler = sigint_handler}; if (sigaction(SIGINT, &act, NULL) < 0) { xerror(); return EXIT_FAILURE; } act.sa_handler = clean_exit_sighandler; if (sigaction(SIGTERM, &act, NULL) < 0 || sigaction(SIGHUP, &act, NULL) < 0) { xerror(); return EXIT_FAILURE; } act.sa_handler = SIG_IGN; if (sigaction(SIGQUIT, &act, NULL) < 0) { xerror(); return EXIT_FAILURE; } #ifndef NOLC /* Set locale */ setlocale(LC_ALL, ""); #ifdef PCRE tables = pcre_maketables(); #endif #endif #ifndef NORL #if RL_READLINE_VERSION >= 0x0603 /* readline would overwrite the WINCH signal hook */ rl_change_environment = 0; #endif /* Bind TAB to cycling */ rl_variable_bind("completion-ignore-case", "on"); #ifdef __linux__ rl_bind_key('\t', rl_menu_complete); #else rl_bind_key('\t', rl_complete); #endif if (rlhist) { mkpath(cfgpath, ".history", g_buf); read_history(g_buf); } #endif #ifndef NOX11 if (cfg.x11 && !g_state.picker) { /* Save terminal window title */ printf("\033[22;0t"); fflush(stdout); gethostname(hostname, sizeof(hostname)); hostname[sizeof(hostname) - 1] = '\0'; } #endif #ifndef NOMOUSE if (!initcurses(&mask)) #else if (!initcurses(NULL)) #endif return EXIT_FAILURE; if (sort) set_sort_flags(sort); opt = browse(initpath, session, pkey); #ifndef NOSSN if (session && g_state.prstssn) save_session(session, NULL); #endif #ifndef NOMOUSE mousemask(mask, NULL); #endif exitcurses(); #ifndef NORL if (rlhist && !g_state.xprompt) { mkpath(cfgpath, ".history", g_buf); write_history(g_buf); } #endif if (g_state.picker) { if (selbufpos) { fd = selpath ? open(selpath, O_WRONLY | O_CREAT | O_TRUNC, 0600) : STDOUT_FILENO; if ((fd == -1) || (seltofile(fd, NULL, sepnul ? "\0" : NEWLINE) != (size_t)(selbufpos))) xerror(); if (fd > 1) close(fd); } } else if (selpath) unlink(selpath); /* Remove tmp dir in list mode */ rmlistpath(); /* Free the regex */ #ifdef PCRE pcre_free(archive_pcre); #else regfree(&archive_re); #endif /* Free the selection buffer */ free(pselbuf); #ifdef LINUX_INOTIFY /* Shutdown inotify */ if (inotify_wd >= 0) inotify_rm_watch(inotify_fd, inotify_wd); close(inotify_fd); #elif defined(BSD_KQUEUE) if (event_fd >= 0) close(event_fd); close(kq); #elif defined(HAIKU_NM) haiku_close_nm(haiku_hnd); #endif #ifndef NOFIFO if (!g_state.fifomode) notify_fifo(FALSE); if (fifofd != -1) close(fifofd); #endif return opt; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/nnn.h�����������������������������������������������������������������������������������0000664�0000000�0000000�00000016501�14663100143�0013701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * BSD 2-Clause License * * Copyright (C) 2014-2016, Lazaros Koromilas <lostd@2f30.org> * Copyright (C) 2014-2016, Dimitris Papastamos <sin@2f30.org> * Copyright (C) 2016-2024, Arun Prakash Jana <engineerarun@gmail.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * 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 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include <curses.h> #include <wchar.h> #define CONTROL(c) ((c) & 0x1f) #ifndef ESC #define ESC (27) #endif #ifndef DEL #define DEL (127) #endif /* Supported actions */ enum action { SEL_BACK = 1, SEL_OPEN, SEL_NAV_IN, SEL_NEXT, SEL_PREV, SEL_PGDN, SEL_PGUP, SEL_CTRL_D, SEL_CTRL_U, SEL_HOME, SEL_END, SEL_FIRST, SEL_JUMP, SEL_YOUNG, SEL_CDHOME, SEL_CDBEGIN, SEL_CDLAST, SEL_CDROOT, SEL_BMOPEN, SEL_REMOTE, SEL_CYCLE, SEL_CYCLER, SEL_CTX1, SEL_CTX2, SEL_CTX3, SEL_CTX4, #ifdef CTX8 SEL_CTX5, SEL_CTX6, SEL_CTX7, SEL_CTX8, #endif SEL_MARK, SEL_BMARK, SEL_FLTR, SEL_MFLTR, SEL_HIDDEN, SEL_DETAIL, SEL_STATS, SEL_CHMODX, SEL_ARCHIVE, SEL_SORT, SEL_REDRAW, SEL_SEL, SEL_SELMUL, SEL_SELALL, SEL_SELINV, SEL_SELEDIT, SEL_CP, SEL_MV, SEL_CPMVAS, SEL_TRASH, SEL_RM_ONLY, SEL_OPENWITH, SEL_NEW, SEL_RENAME, SEL_RENAMEMUL, SEL_UMOUNT, SEL_HELP, SEL_AUTONEXT, SEL_EDIT, SEL_PLUGIN, SEL_SELSIZE, SEL_SHELL, SEL_LAUNCH, SEL_PROMPT, SEL_LOCK, SEL_SESSIONS, SEL_EXPORT, SEL_TIMETYPE, SEL_QUITCTX, SEL_QUITCD, SEL_QUIT, SEL_QUITERR, #ifndef NOMOUSE SEL_CLICK, #endif }; /* Associate a pressed key to an action */ struct key { wint_t sym; /* Key pressed */ enum action act; /* Action */ }; static struct key bindings[] = { /* Back */ { KEY_LEFT, SEL_BACK }, { 'h', SEL_BACK }, /* Inside or select */ { KEY_ENTER, SEL_OPEN }, { '\r', SEL_OPEN }, /* Pure navigate inside */ { KEY_RIGHT, SEL_NAV_IN }, { 'l', SEL_NAV_IN }, /* Next */ { 'j', SEL_NEXT }, { KEY_DOWN, SEL_NEXT }, /* Previous */ { 'k', SEL_PREV }, { KEY_UP, SEL_PREV }, /* Page down */ { KEY_NPAGE, SEL_PGDN }, /* Page up */ { KEY_PPAGE, SEL_PGUP }, /* Ctrl+D */ { CONTROL('D'), SEL_CTRL_D }, /* Ctrl+U */ { CONTROL('U'), SEL_CTRL_U }, /* First entry */ { KEY_HOME, SEL_HOME }, { 'g', SEL_HOME }, { CONTROL('A'), SEL_HOME }, /* Last entry */ { KEY_END, SEL_END }, { 'G', SEL_END }, { CONTROL('E'), SEL_END }, /* Go to first file */ { '\'', SEL_FIRST }, /* Jump to an entry number/offset */ { 'J', SEL_JUMP }, { CONTROL('Y'), SEL_YOUNG }, /* HOME */ { '~', SEL_CDHOME }, /* Initial directory */ { '@', SEL_CDBEGIN }, /* Last visited dir */ { '-', SEL_CDLAST }, /* Go to / */ { '`', SEL_CDROOT }, /* Leader key */ { 'b', SEL_BMOPEN }, { CONTROL('_'), SEL_BMOPEN }, /* Connect to server over SSHFS */ { 'c', SEL_REMOTE }, /* Cycle contexts in forward direction */ { '\t', SEL_CYCLE }, /* Cycle contexts in reverse direction */ { KEY_BTAB, SEL_CYCLER }, /* Go to/create context N */ { '1', SEL_CTX1 }, { '2', SEL_CTX2 }, { '3', SEL_CTX3 }, { '4', SEL_CTX4 }, #ifdef CTX8 { '5', SEL_CTX5 }, { '6', SEL_CTX6 }, { '7', SEL_CTX7 }, { '8', SEL_CTX8 }, #endif /* Mark a path to visit later */ { ',', SEL_MARK }, /* Create a bookmark */ { 'B', SEL_BMARK }, /* Filter */ { '/', SEL_FLTR }, /* Toggle filter mode */ { CONTROL('N'), SEL_MFLTR }, /* Toggle hide .dot files */ { '.', SEL_HIDDEN }, /* Detailed listing */ { 'd', SEL_DETAIL }, /* File details */ { 'f', SEL_STATS }, { CONTROL('F'), SEL_STATS }, /* Toggle executable status */ { '*', SEL_CHMODX }, /* Create archive */ { 'z', SEL_ARCHIVE }, /* Sort toggles */ { 't', SEL_SORT }, { CONTROL('T'), SEL_SORT }, /* Redraw window */ { CONTROL('L'), SEL_REDRAW }, /* Select current file path */ { ' ', SEL_SEL }, { '+', SEL_SEL }, /* Toggle select multiple files */ { 'm', SEL_SELMUL }, /* Select all files in current dir */ { 'a', SEL_SELALL }, /* Invert selection in current dir */ { 'A', SEL_SELINV }, /* List, edit selection */ { 'E', SEL_SELEDIT }, /* Copy from selection buffer */ { 'p', SEL_CP }, { CONTROL('P'), SEL_CP }, /* Move from selection buffer */ { 'v', SEL_MV }, { CONTROL('V'), SEL_MV }, /* Copy/move from selection buffer and rename */ { 'w', SEL_CPMVAS }, { CONTROL('W'), SEL_CPMVAS }, /* Delete from selection buffer */ { 'x', SEL_TRASH }, { CONTROL('X'), SEL_TRASH }, { 'X', SEL_RM_ONLY }, /* Open in a custom application */ { 'o', SEL_OPENWITH }, { CONTROL('O'), SEL_OPENWITH }, /* Create a new file */ { 'n', SEL_NEW }, /* Show rename prompt */ { CONTROL('R'), SEL_RENAME }, /* Rename contents of current dir */ { 'r', SEL_RENAMEMUL }, /* Disconnect a SSHFS mount point */ { 'u', SEL_UMOUNT }, /* Show help */ { '?', SEL_HELP }, /* Toggle auto-advance on file open */ { CONTROL('J'), SEL_AUTONEXT }, /* Edit in EDITOR */ { 'e', SEL_EDIT }, /* Run a plugin */ { ';', SEL_PLUGIN }, /* Show total size of listed selection */ { 'S', SEL_SELSIZE }, /* Run command */ { '!', SEL_SHELL }, { CONTROL(']'), SEL_SHELL }, /* Launcher */ { '=', SEL_LAUNCH }, /* Show command prompt */ { ']', SEL_PROMPT }, /* Lock screen */ { '0', SEL_LOCK }, /* Manage sessions */ { 's', SEL_SESSIONS }, /* Export list */ { '>', SEL_EXPORT }, /* Set time type */ { 'T', SEL_TIMETYPE }, /* Quit a context */ { 'q', SEL_QUITCTX }, /* Change dir on quit */ { CONTROL('G'), SEL_QUITCD }, /* Quit */ { CONTROL('Q'), SEL_QUIT }, /* Quit with an error code */ { 'Q', SEL_QUITERR }, #ifndef NOMOUSE { KEY_MOUSE, SEL_CLICK }, #endif }; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nnn-5.0/src/qsort.h���������������������������������������������������������������������������������0000664�0000000�0000000�00000016406�14663100143�0014264�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2013, 2017 Alexey Tourbin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * This is a traditional Quicksort implementation which mostly follows * [Sedgewick 1978]. Sorting is performed entirely on array indices, * while actual access to the array elements is abstracted out with the * user-defined `LESS` and `SWAP` primitives. * * Synopsis: * QSORT(N, LESS, SWAP); * where * N - the number of elements in A[]; * LESS(i, j) - compares A[i] to A[j]; * SWAP(i, j) - exchanges A[i] with A[j]. */ #ifndef QSORT_H #define QSORT_H /* Sort 3 elements. */ #define Q_SORT3(q_a1, q_a2, q_a3, Q_LESS, Q_SWAP) \ do { \ if (Q_LESS(q_a2, q_a1)) { \ if (Q_LESS(q_a3, q_a2)) \ Q_SWAP(q_a1, q_a3); \ else { \ Q_SWAP(q_a1, q_a2); \ if (Q_LESS(q_a3, q_a2)) \ Q_SWAP(q_a2, q_a3); \ } \ } \ else if (Q_LESS(q_a3, q_a2)) { \ Q_SWAP(q_a2, q_a3); \ if (Q_LESS(q_a2, q_a1)) \ Q_SWAP(q_a1, q_a2); \ } \ } while (0) /* Partition [q_l,q_r] around a pivot. After partitioning, * [q_l,q_j] are the elements that are less than or equal to the pivot, * while [q_i,q_r] are the elements greater than or equal to the pivot. */ #define Q_PARTITION(q_l, q_r, q_i, q_j, Q_UINT, Q_LESS, Q_SWAP) \ do { \ /* The middle element, not to be confused with the median. */ \ Q_UINT q_m = (q_l) + (((q_r) - (q_l)) >> 1); \ /* Reorder the second, the middle, and the last items. \ * As [Edelkamp Weiss 2016] explain, using the second element \ * instead of the first one helps avoid bad behaviour for \ * decreasingly sorted arrays. This method is used in recent \ * versions of gcc's std::sort, see gcc bug 58437#c13, although \ * the details are somewhat different (cf. #c14). */ \ Q_SORT3((q_l) + 1, q_m, q_r, Q_LESS, Q_SWAP); \ /* Place the median at the beginning. */ \ Q_SWAP(q_l, q_m); \ /* Partition [q_l+2, q_r-1] around the median which is in q_l. \ * q_i and q_j are initially off by one, they get decremented \ * in the do-while loops. */ \ (q_i) = (q_l) + 1; (q_j) = q_r; \ while (1) { \ do (q_i)++; while (Q_LESS(q_i, q_l)); \ do (q_j)--; while (Q_LESS(q_l, q_j)); \ if ((q_i) >= (q_j)) break; /* Sedgewick says "until j < i" */ \ Q_SWAP(q_i, q_j); \ } \ /* Compensate for the i==j case. */ \ (q_i) = (q_j) + 1; \ /* Put the median to its final place. */ \ Q_SWAP(q_l, q_j); \ /* The median is not part of the left subfile. */ \ (q_j)--; \ } while (0) /* Insertion sort is applied to small subfiles - this is contrary to * Sedgewick's suggestion to run a separate insertion sort pass after * the partitioning is done. The reason I don't like a separate pass * is that it triggers extra comparisons, because it can't see that the * medians are already in their final positions and need not be rechecked. * Since I do not assume that comparisons are cheap, I also do not try * to eliminate the (q_j > q_l) boundary check. */ #define Q_INSERTION_SORT(q_l, q_r, Q_UINT, Q_LESS, Q_SWAP) \ do { \ Q_UINT q_i, q_j; \ /* For each item starting with the second... */ \ for (q_i = (q_l) + 1; q_i <= (q_r); q_i++) \ /* move it down the array so that the first part is sorted. */ \ for (q_j = q_i; q_j > (q_l) && (Q_LESS(q_j, q_j - 1)); q_j--) \ Q_SWAP(q_j, q_j - 1); \ } while (0) /* When the size of [q_l,q_r], i.e. q_r-q_l+1, is greater than or equal to * Q_THRESH, the algorithm performs recursive partitioning. When the size * drops below Q_THRESH, the algorithm switches to insertion sort. * The minimum valid value is probably 5 (with 5 items, the second and * the middle items, the middle itself being rounded down, are distinct). */ #define Q_THRESH 16 /* The main loop. */ #define Q_LOOP(Q_UINT, Q_N, Q_LESS, Q_SWAP) \ do { \ Q_UINT q_l = 0; \ Q_UINT q_r = (Q_N) - 1; \ Q_UINT q_sp = 0; /* the number of frames pushed to the stack */ \ struct { Q_UINT q_l, q_r; } \ /* On 32-bit platforms, to sort a "char[3GB+]" array, \ * it may take full 32 stack frames. On 64-bit CPUs, \ * though, the address space is limited to 48 bits. \ * The usage is further reduced if Q_N has a 32-bit type. */ \ q_st[sizeof(Q_UINT) > 4 && sizeof(Q_N) > 4 ? 48 : 32]; \ while (1) { \ if (q_r - q_l + 1 >= Q_THRESH) { \ Q_UINT q_i, q_j; \ Q_PARTITION(q_l, q_r, q_i, q_j, Q_UINT, Q_LESS, Q_SWAP); \ /* Now have two subfiles: [q_l,q_j] and [q_i,q_r]. \ * Dealing with them depends on which one is bigger. */ \ if (q_j - q_l >= q_r - q_i) \ Q_SUBFILES(q_l, q_j, q_i, q_r); \ else \ Q_SUBFILES(q_i, q_r, q_l, q_j); \ } \ else { \ Q_INSERTION_SORT(q_l, q_r, Q_UINT, Q_LESS, Q_SWAP); \ /* Pop subfiles from the stack, until it gets empty. */ \ if (q_sp == 0) break; \ q_sp--; \ q_l = q_st[q_sp].q_l; \ q_r = q_st[q_sp].q_r; \ } \ } \ } while (0) /* The missing part: dealing with subfiles. * Assumes that the first subfile is not smaller than the second. */ #define Q_SUBFILES(q_l1, q_r1, q_l2, q_r2) \ do { \ /* If the second subfile is only a single element, it needs \ * no further processing. The first subfile will be processed \ * on the next iteration (both subfiles cannot be only a single \ * element, due to Q_THRESH). */ \ if ((q_l2) == (q_r2)) { \ q_l = q_l1; \ q_r = q_r1; \ } \ else { \ /* Otherwise, both subfiles need processing. \ * Push the larger subfile onto the stack. */ \ q_st[q_sp].q_l = q_l1; \ q_st[q_sp].q_r = q_r1; \ q_sp++; \ /* Process the smaller subfile on the next iteration. */ \ q_l = q_l2; \ q_r = q_r2; \ } \ } while (0) /* And now, ladies and gentlemen, may I proudly present to you... */ #define QSORT(Q_N, Q_LESS, Q_SWAP) \ do { \ if ((Q_N) > 1) \ /* We could check sizeof(Q_N) and use "unsigned", but at least \ * on x86_64, this has the performance penalty of up to 5%. */ \ Q_LOOP(unsigned long, Q_N, Q_LESS, Q_SWAP); \ } while (0) #endif /* ex:set ts=8 sts=4 sw=4 noet: */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������