1
0
Fork 0
forked from mirror/nnn

Compare commits

..

10 commits

Author SHA1 Message Date
b347ad1a9f
plugins + keybind fix 2024-03-26 18:37:03 -07:00
bffaef985e
no 2024-03-26 18:37:03 -07:00
6233c24488
changes 2024-03-26 18:36:57 -07:00
c79cea05
48d7976f90
nuke: use sort -V 2024-03-26 18:36:25 -07:00
Arun Prakash Jana
c772e13da1
Fix deletion prompt when rm is used 2024-03-26 18:36:25 -07:00
KlzXS
a1feab8f79
Add -- to mvg and cpg which were missed 2024-03-26 18:36:25 -07:00
KlzXS
b835cfc40e
Added -- to all instances of cp, mv or rm dealing with user provided paths 2024-03-26 18:36:24 -07:00
João F. (BeyondMagic/koetemagie)
c285111a28
quitcd: fix bugs and feat. for modular export and selective quit 2024-03-26 18:36:24 -07:00
Michel DHOOGE
2c041950c5
fix: use a more generic way to print NUL with awk
Only gawk undestands the \0 syntax
feat(mimelist): use `mimetype` for better type detection
docs(mimelist): add author & dependencies
2024-03-26 18:36:24 -07:00
90
881916e4cb
Add option to rm -rf irrespective of trash setting 2024-03-26 18:36:23 -07:00
31 changed files with 272 additions and 148 deletions

View file

@ -32,7 +32,6 @@ O_NOSORT := 0 # disable sorting entries on dir load
# User patches
O_COLEMAK := 0 # change key bindings to colemak compatible layout
O_COLEMAK-DH := 0 # change key bindings to colemak-dh compatible layout
O_GITSTATUS := 1 # 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

View file

@ -40,7 +40,7 @@ git checkout v0.3.2
if [ ! -d "./libs" ]; then
mkdir libs
else
rm -vf libs/*
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/
@ -57,7 +57,7 @@ make CC=musl-gcc CFLAGS=-O3 LDFLAGS=-static -j$(($(nproc)+1))
# Compile nnn
cd ..
[ -e "./netbsd-curses" ] || rm "$BIN"
[ -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"

View file

@ -25,6 +25,6 @@ n ()
[ ! -f "$NNN_TMPFILE" ] || {
. "$NNN_TMPFILE"
rm -f "$NNN_TMPFILE" > /dev/null
rm -f -- "$NNN_TMPFILE" > /dev/null
}
}

View file

@ -14,4 +14,4 @@ set NNN_TMPFILE=~/.config/nnn/.lastd
# 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"'
alias n '\nnn; source "$NNN_TMPFILE"; rm -f -- "$NNN_TMPFILE"'

View file

@ -36,6 +36,6 @@ fn n {|@a|
if (path:is-regular $E:NNN_TMPFILE) {
eval (slurp < $E:NNN_TMPFILE)
rm $E:NNN_TMPFILE
rm -- $E:NNN_TMPFILE
}
}

View file

@ -31,6 +31,6 @@ function n --wraps nnn --description 'support nnn quit and change directory'
if test -e $NNN_TMPFILE
source $NNN_TMPFILE
rm $NNN_TMPFILE
rm -- $NNN_TMPFILE
end
end

View file

@ -1,19 +1,37 @@
# The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set)
let cfgHome = ($env | default $"($env.HOME)/.config" XDG_CONFIG_HOME | get XDG_CONFIG_HOME)
$env.NNN_TMPFILE = $"($cfgHome)/nnn/.lastd"
# 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 {
def --env n [...x] {
# Launch nnn. Add desired flags after `^nnn`, ex: `^nnn -eda ($x | str join)`
^nnn ($x | str join)
let newpath = (
if ($env.NNN_TMPFILE | path exists) {
# FIXME: fails if path contains single-quote
let newpath = (open $env.NNN_TMPFILE | parse "cd '{nnnpath}'").0.nnnpath
^rm -f $env.NNN_TMPFILE
echo $newpath
} else {
pwd
}
)
cd $newpath
# 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 <cd '> 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 substring 4..-1 | str replace --all `'\''` `'`
^rm -- $nnn_tmpfile
cd $path
}
}

2
nnn.1
View file

@ -353,7 +353,7 @@ Handy bash/zsh shell function to list files by mime-type in current directory:
list ()
{
find . -maxdepth 1 | file -if- | grep "$1" | awk -F: '{printf "%s\0", $1}' | nnn
find . -maxdepth 1 | file -if- | grep "$1" | awk -F: '{printf "%s%c", $1, 0}' | nnn
}
.Ed
.Pp

View file

@ -1,12 +1,8 @@
# Description: Change key bindings for comfortable use with Colemak keyboard
# layout. This diff was made in 4.7 release version of nnn.
#
# Author: github.com/jacmoe
diff --git a/src/nnn.c b/src/nnn.c
index 21a7370b..2ddb4053 100644
index 6792d503..0a59e8e3 100644
--- a/src/nnn.c
+++ b/src/nnn.c
@@ -5109,12 +5109,12 @@ static void show_help(const char *path)
@@ -5148,12 +5148,12 @@ static void show_help(const char *path)
"2(___n))\n"
"0\n"
"1NAVIGATION\n"
@ -24,7 +20,7 @@ index 21a7370b..2ddb4053 100644
"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"
@@ -5122,20 +5122,20 @@ static void show_help(const char *path)
@@ -5161,20 +5161,20 @@ static void show_help(const char *path)
"cq Quit context\n"
"0\n"
"1FILTER & PROMPT\n"
@ -46,14 +42,14 @@ index 21a7370b..2ddb4053 100644
"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%18S Listed sel size\n"
"aEsc Send to FIFO\n"
"9x ^X Delete or trash%09S Listed sel size\n"
"cX Delete (rm -rf)%07Esc Send to FIFO\n"
"0\n"
diff --git a/src/nnn.h b/src/nnn.h
index 3e4ea19c..b0eb7cdb 100644
index bd500244..b12df5c0 100644
--- a/src/nnn.h
+++ b/src/nnn.h
@@ -137,12 +137,12 @@ static struct key bindings[] = {
@@ -139,12 +139,12 @@ static struct key bindings[] = {
{ '\r', SEL_OPEN },
/* Pure navigate inside */
{ KEY_RIGHT, SEL_NAV_IN },
@ -69,7 +65,7 @@ index 3e4ea19c..b0eb7cdb 100644
{ KEY_UP, SEL_PREV },
/* Page down */
{ KEY_NPAGE, SEL_PGDN },
@@ -155,11 +155,11 @@ static struct key bindings[] = {
@@ -157,11 +157,11 @@ static struct key bindings[] = {
/* First entry */
{ KEY_HOME, SEL_HOME },
{ 'g', SEL_HOME },
@ -83,7 +79,7 @@ index 3e4ea19c..b0eb7cdb 100644
/* Go to first file */
{ '\'', SEL_FIRST },
/* Jump to an entry number/offset */
@@ -199,7 +199,7 @@ static struct key bindings[] = {
@@ -202,7 +202,7 @@ static struct key bindings[] = {
/* Filter */
{ '/', SEL_FLTR },
/* Toggle filter mode */
@ -92,7 +88,7 @@ index 3e4ea19c..b0eb7cdb 100644
/* Toggle hide .dot files */
{ '.', SEL_HIDDEN },
/* Detailed listing */
@@ -226,7 +226,7 @@ static struct key bindings[] = {
@@ -229,7 +229,7 @@ static struct key bindings[] = {
/* Invert selection in current dir */
{ 'A', SEL_SELINV },
/* List, edit selection */
@ -101,7 +97,7 @@ index 3e4ea19c..b0eb7cdb 100644
/* Copy from selection buffer */
{ 'p', SEL_CP },
{ CONTROL('P'), SEL_CP },
@@ -243,7 +243,7 @@ static struct key bindings[] = {
@@ -247,7 +247,7 @@ static struct key bindings[] = {
{ 'o', SEL_OPENWITH },
{ CONTROL('O'), SEL_OPENWITH },
/* Create a new file */
@ -110,7 +106,7 @@ index 3e4ea19c..b0eb7cdb 100644
/* Show rename prompt */
{ CONTROL('R'), SEL_RENAME },
/* Rename contents of current dir */
@@ -255,7 +255,7 @@ static struct key bindings[] = {
@@ -259,7 +259,7 @@ static struct key bindings[] = {
/* Toggle auto-advance on file open */
{ CONTROL('J'), SEL_AUTONEXT },
/* Edit in EDITOR */

View file

@ -32,7 +32,7 @@ case "$NNN_TRASH" in
2)
RM_UTIL="gio trash" ;;
*)
RM_UTIL="rm -ri" ;;
RM_UTIL="rm -ri --" ;;
esac
exit_status=0
@ -64,7 +64,7 @@ lines=$(printf "%s\n" "$arr" | wc -l)
width=${#lines}
dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX")
trap 'rm -f "$dst_file"' EXIT
trap 'rm -f -- "$dst_file"' EXIT
printf "%s" "$arr" | awk '{printf("%'"${width}"'d %s\n", NR, $0)}' > "$dst_file"
@ -122,7 +122,7 @@ while read -r num name; do
tmp="$tmp~$c"
done
if mv "$name" "$tmp"; then
if mv -- "$name" "$tmp"; then
if [ "$VERBOSE" -ne 0 ]; then
printf "'%s' -> '%s'\n" "$name" "$tmp"
fi
@ -142,7 +142,7 @@ while read -r num name; do
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
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

View file

@ -41,7 +41,7 @@ Plugins extend the capabilities of `nnn`. They are _executable_ scripts (or bina
| [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 | - |
| [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 |

View file

@ -29,4 +29,4 @@ $EDITOR "$tmpfile"
sed "/^\//d" "$tmpfile" | xargs -n1 -I{} sh -c "$cmd"
rm "$tmpfile"
rm -- "$tmpfile"

View file

@ -21,4 +21,4 @@ fs=($( osascript -e "use framework \"Foundation\"
pb's readObjectsForClasses:[NSURL] options:[]
(result's valueForKey:\"path\") as list as text" ))
cp -R "${fs[@]}" "$2/"
cp -R -- "${fs[@]}" "$2/"

View file

@ -25,7 +25,7 @@ dirdiff() {
ls -A1 "$1" > "$dir1"
ls -A1 "$2" > "$dir2"
$diffcmd "$dir1" "$dir2"
rm "$dir1" "$dir2"
rm -- "$dir1" "$dir2"
}
if [ -s "$selection" ]; then

View file

@ -58,13 +58,13 @@ 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/tty'
sed -e "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -f -- "$0" "$@" </dev/tty'
else
#shellcheck disable=SC2016
sed -e "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -i "$0" "$@" </dev/tty'
sed -e "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -i -- "$0" "$@" </dev/tty'
fi
rm "$tmpfile"
rm -- "$tmpfile"
printf "Press any key to exit"
read -r _

View file

@ -84,6 +84,6 @@ if [ -n "$fexpr" ]; then
if [ -n "$fexpr" ]; then
tail -n"$NNN_FINDHISTLEN" "$NNN_FINDHIST" > "$TMPDIR/finderbms"
printf "%s\n" "$fexpr" >> "$TMPDIR/finderbms"
mv "$TMPDIR/finderbms" "$NNN_FINDHIST"
mv -- "$TMPDIR/finderbms" "$NNN_FINDHIST"
fi
fi

View file

@ -65,7 +65,7 @@ for i in "${targets[@]}"; do
if [ -e "$(cleanup "$i")" ]; then
tmp='_'
fi
mv "$i" "$tmp$(cleanup "$i")";
mv -- "$i" "$tmp$(cleanup "$i")";
fi
done

View file

@ -60,7 +60,7 @@ if [ -n "$LIST" ]; then
# Alternative for 'fd'
# sel=$(xargs -d '\n' < "$tmpfile" fd . | fzf --delimiter / --tiebreak=begin --info=hidden)
rm "$tmpfile"
rm -- "$tmpfile"
else
printf "find missing"
read -r _

View file

@ -46,7 +46,7 @@ if [ -n "$entry" ]; then
$SHELL -c "$(cat "$tmpfile")"
fi
rm "$tmpfile"
rm -- "$tmpfile"
printf "Press any key to exit"
read -r _

View file

@ -24,7 +24,7 @@ prompt () {
if [ "$operation" = "m" ]; then
op="merge"
elif [ "$operation" = "o" ]; then
op="cp -vRf"
op="cp -vRf --"
else
op="true"
fi
@ -62,7 +62,7 @@ for f in $(find . -maxdepth 1 \( ! -iname "." ! -iname "*.md" \)); do
$op "$f" ../../plugins/
fi
else
cp -vRf "$f" ../../plugins/
cp -vRf -- "$f" ../../plugins/
fi
done
cd ../.. || exit 1

View file

@ -583,7 +583,7 @@ for upload_file in "${upload_files[@]}"; do
# delete file if configured
if [ "${keep_file}" = "false" ] && [ -z "${1}" ]; then
echo "Deleting temp file ${file_dir}/${img_file}"
rm -rf "${img_file}"
rm -rf -- "${img_file}"
fi
echo ""

View file

@ -54,7 +54,7 @@ make_thumbs() {
done
for file in "$NNN_PREVIEWDIR$dir"/*; do
filename="$(basename "$file" .jpg)"
[ ! -e "$dir/$filename" ] && rm "$file" 2>/dev/null
[ ! -e "$dir/$filename" ] && rm -- "$file" 2>/dev/null
done
}

View file

@ -2,8 +2,12 @@
# 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
# Author: Arun Prakash Jana, Michel DHOOGE
# shellcheck disable=SC1090,SC1091
. "$(dirname "$0")"/.nnn-plugin-helper
@ -12,4 +16,8 @@ printf "mime (e.g., video/audio/image): "
read -r mime
printf "%s" "+l" > "$NNN_PIPE"
find . | file -if- | grep "$mime" | awk -F: '{printf "%s\0", $1}' > "$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

View file

@ -34,7 +34,7 @@ fi
cd "$tempdir/$outdir" || exit 1
# Backing up config dir content
cp -r "$configdir" . || exit 1
cp -r -- "$configdir" . || exit 1
# Environment config
env | sed "s/'/'\\\\''/" |\
@ -72,4 +72,4 @@ printf "Saving as '%s' ... " "$workdir/$outfile"
tar caf "$workdir/$outfile" "$outdir" && echo "Done" || echo "Failed"
cd "$workdir" && rm -rf "$tempdir"
cd "$workdir" && rm -rf -- "$tempdir"

View file

@ -271,13 +271,13 @@ abspath() {
listimages() {
find -L "///${1%/*}" -maxdepth 1 -type f -print0 |
grep -izZE '\.(jpe?g|png|gif|webp|tiff|bmp|ico|svg)$' |
sort -z | tee "$tmp"
sort -zV | tee "$tmp"
}
load_dir() {
abspath "$2"
tmp="${TMPDIR:-/tmp}/nuke_$$"
trap 'rm -f $tmp' EXIT
trap 'rm -f -- "$tmp"' EXIT
count="$(listimages "$abs_target" | grep -a -m 1 -ZznF "$abs_target" | cut -d: -f1)"
if [ -n "$count" ]; then
@ -402,7 +402,7 @@ handle_multimedia() {
# "${FPATH}";
# then
# convert -- "${preview_png}" "${IMAGE_CACHE_PATH}" \
# && rm "${preview_png}" \
# && rm -- "${preview_png}" \
# && exit 6
# else
# exit 1

View file

@ -13,36 +13,36 @@ organize() {
case "$(file -biL "$1")" in
*video*)
[ ! -d "Videos" ] && mkdir "Videos"
mv "$1" "Videos/$1"
mv -- "$1" "Videos/$1"
printf "Moved %s to Videos\n" "$1" ;;
*audio*) [ ! -d "Audio" ] && mkdir "Audio"
mv "$1" "Audio/$1"
mv -- "$1" "Audio/$1"
printf "Moved %s to Audio\n" "$1" ;;
*image*)
[ ! -d "Images" ] && mkdir "Images"
mv "$1" "Images/$1"
mv -- "$1" "Images/$1"
printf "Moved %s to Images\n" "$1" ;;
*pdf*|*document*|*epub*|*djvu*|*cb*)
[ ! -d "Documents" ] && mkdir "Documents"
mv "$1" "Documents/$1"
mv -- "$1" "Documents/$1"
printf "Moved %s to Documents\n" "$1" ;;
*text*)
[ ! -d "Plaintext" ] && mkdir "Plaintext"
mv "$1" "Plaintext/$1"
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"
mv -- "$1" "Archives/$1"
printf "Moved %s to Archives\n" "$1" ;;
*binary*)
[ ! -d "Binaries" ] && mkdir "Binaries"
mv "$1" "Binaries/$1"
mv -- "$1" "Binaries/$1"
printf "Moved %s to Binaries\n" "$1" ;;
esac
}

View file

@ -15,7 +15,7 @@ if [ -n "$1" ]; then
pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$tmpf".txt)"
rm "$tmpf".txt
rm -- "$tmpf".txt
else
pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$1")"
fi
@ -26,5 +26,5 @@ if [ -n "$1" ]; then
# flat read but better quality
# play -qV0 "$tmpf".wav treble 2 gain -l 2
rm "$tmpf".wav
rm -- "$tmpf".wav
fi

View file

@ -92,7 +92,7 @@ start_tabbed () {
read -r XID < "$FIFO"
rm "$FIFO"
rm -- "$FIFO"
}
get_viewer_pid () {

View file

@ -96,38 +96,27 @@
# Shell: Bash (for environment manipulation through arrays)
# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz
# 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"
)
#SPLIT="$SPLIT" # you can set a permanent split here
#TERMINAL="$TERMINAL" # same goes for the terminal
SPLIT_SIZE="${SPLIT_SIZE:-50}" # split size in percentage for supported previewers
USE_SCOPE="${USE_SCOPE:-0}"
USE_PISTOL="${USE_PISTOL:-0}"
ICONLOOKUP="${ICONLOOKUP:-0}"
PAGER="${PAGER:-less -P?n -R}"
TMPDIR="${TMPDIR:-/tmp}"
BAT_STYLE="${BAT_STYLE:-full}"
BAT_THEME="${BAT_THEME:-default}"
# Consider setting NNN_PREVIEWDIR to $XDG_CACHE_HOME/nnn/previews if you want to keep previews on disk between reboots
NNN_PREVIEWDIR="${NNN_PREVIEWDIR:-$TMPDIR/nnn/previews}"
NNN_PREVIEWWIDTH="${NNN_PREVIEWWIDTH:-1920}"
NNN_PREVIEWHEIGHT="${NNN_PREVIEWHEIGHT:-1080}"
NNN_PARENT="${NNN_FIFO#*.}"
[ "$NNN_PARENT" -eq "$NNN_PARENT" ] 2>/dev/null || NNN_PARENT=""
FIFOPID="$TMPDIR/nnn-preview-tui-fifopid.$NNN_PARENT"
PREVIEWPID="$TMPDIR/nnn-preview-tui-pagerpid.$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 ;}
@ -259,7 +248,7 @@ fifo_pager() {
fi
)
rm "$FIFOPATH"
rm -- "$FIFOPATH"
}
# Binary file: show file info inside the pager
@ -419,10 +408,10 @@ generate_preview() {
image) image_preview "$1" "$2" "$3" && return ;;
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" ;;
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 ;;
video) ffmpeg -i "$3" -vf "select=eq(n\,0)" -vframes 1 "$NNN_PREVIEWDIR/$3.jpg" ;;
esac
fi
if [ -f "$NNN_PREVIEWDIR/$3.jpg" ]; then
@ -463,7 +452,7 @@ video_preview() {
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" &
ffmpegthumbnailer -m -s0 -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" || rm -- "$NNN_PREVIEWDIR/$3.jpg" &
fi
printf "%s" "$!" > "$PREVIEWPID"
}
@ -513,7 +502,7 @@ if [ "$PREVIEW_MODE" -eq 1 ] 2>/dev/null; then
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
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

170
src/nnn.c
View file

@ -282,6 +282,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;
@ -306,6 +325,7 @@ typedef struct entry {
uid_t uid; /* 4 bytes */
gid_t gid; /* 4 bytes */
#endif
char git_status[2][5];
} *pEntry;
/* Selection marker */
@ -361,6 +381,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) */
@ -414,7 +435,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 = {
@ -589,7 +620,7 @@ static char * const utils[] = {
".nmv",
"trash-put",
"gio trash",
"rm -rf",
"rm -rf --",
"archivemount",
};
@ -741,10 +772,10 @@ static const char * const envs[] = {
#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";
#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"};
@ -1551,12 +1582,13 @@ static void xdelay(useconds_t delay)
usleep(delay);
}
static char confirm_force(bool selection)
static char confirm_force(bool selection, bool use_trash)
{
char str[64];
/* Note: ideally we should use utils[UTIL_RM_RF] instead of the "rm -rf" string */
snprintf(str, 64, messages[MSG_FORCE_RM],
g_state.trash ? utils[UTIL_GIO_TRASH] + 4 : utils[UTIL_RM_RF],
use_trash ? utils[UTIL_GIO_TRASH] + 4 : "rm -rf",
(selection ? "selected" : "hovered"));
int r = get_input(str);
@ -1565,7 +1597,7 @@ static char confirm_force(bool selection)
return '\0'; /* cancel */
if (r == 'y' || r == 'Y')
return 'f'; /* forceful for rm */
return (g_state.trash ? '\0' : 'i'); /* interactive for rm */
return (use_trash ? '\0' : 'i'); /* interactive for rm */
}
/* Writes buflen char(s) from buf to a file */
@ -2548,14 +2580,14 @@ 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)
static bool rmmulstr(char *buf, bool use_trash)
{
char r = confirm_force(TRUE);
char r = confirm_force(TRUE, use_trash);
if (!r)
return FALSE;
if (!g_state.trash)
snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cr \"$0\" \"$@\" < /dev/tty' < %s",
if (!use_trash)
snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cr -- \"$0\" \"$@\" < /dev/tty' < %s",
r, selpath);
else
snprintf(buf, CMD_LEN_MAX, "xargs -0 %s < %s",
@ -2565,17 +2597,17 @@ static bool rmmulstr(char *buf)
}
/* Returns TRUE if file is removed, else FALSE */
static bool xrm(char * const fpath)
static bool xrm(char * const fpath, bool use_trash)
{
char r = confirm_force(FALSE);
char r = confirm_force(FALSE, use_trash);
if (!r)
return FALSE;
if (!g_state.trash) {
if (!use_trash) {
char rm_opts[] = "-ir";
rm_opts[1] = r;
spawn("rm", rm_opts, fpath, NULL, F_NORMAL | F_CHKRTN);
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);
@ -2702,8 +2734,8 @@ static bool cpmvrm_selection(enum action sel, char *path)
return FALSE;
}
break;
default: /* SEL_RM */
if (!rmmulstr(g_buf)) {
default: /* SEL_TRASH, SEL_RM_ONLY */
if (!rmmulstr(g_buf, g_state.trash && sel == SEL_TRASH)) {
printmsg(messages[MSG_CANCEL]);
return FALSE;
}
@ -2728,7 +2760,7 @@ static bool batch_rename(void)
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\" \"$@\" <"
"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();
@ -3936,6 +3968,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 */
@ -4269,6 +4342,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)
@ -5175,8 +5252,8 @@ static void show_help(const char *path)
"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%18S Listed sel size\n"
"aEsc Send to FIFO\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"
@ -5769,6 +5846,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
@ -5969,6 +6051,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)));
@ -6550,11 +6655,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);
@ -7651,9 +7757,10 @@ nochange:
case SEL_CP: // fallthrough
case SEL_MV: // fallthrough
case SEL_CPMVAS: // fallthrough
case SEL_RM:
case SEL_TRASH: // fallthrough
case SEL_RM_ONLY:
{
if (sel == SEL_RM) {
if (sel == SEL_TRASH || sel == SEL_RM_ONLY) {
r = get_cur_or_sel();
if (!r) {
statusbar(path);
@ -7664,7 +7771,7 @@ nochange:
tmp = (listpath && xstrcmp(path, listpath) == 0)
? listroot : path;
mkpath(tmp, pdents[cur].name, newpath);
if (!xrm(newpath))
if (!xrm(newpath, g_state.trash && sel == SEL_TRASH))
continue;
xrmfromsel(tmp, newpath);
@ -7860,7 +7967,7 @@ nochange:
if (sel == SEL_RENAME) {
/* Rename the file */
if (ret == 'd')
spawn("cp -rp", pdents[cur].name, tmp, NULL, F_SILENT);
spawn("cp -rp --", pdents[cur].name, tmp, NULL, F_SILENT);
else if (rename(pdents[cur].name, tmp) != 0) {
printwarn(&presel);
goto nochange;
@ -8381,6 +8488,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"
@ -8523,6 +8631,7 @@ static void cleanup(void)
fflush(stdout);
}
#endif
free(git_statuses.statuses);
free(selpath);
free(plgpath);
free(cfgpath);
@ -8567,7 +8676,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:uUVxh"))) != -1) {
: getopt(argc, argv, "aAb:BcCdDeEfF:gGHiJKl:nop:P:QrRs:St:T:uUVxh"))) != -1) {
switch (opt) {
#ifndef NOFIFO
case 'a':
@ -8621,6 +8730,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;

View file

@ -96,7 +96,8 @@ enum action {
SEL_CP,
SEL_MV,
SEL_CPMVAS,
SEL_RM,
SEL_TRASH,
SEL_RM_ONLY,
SEL_OPENWITH,
SEL_NEW,
SEL_RENAME,
@ -241,8 +242,9 @@ static struct key bindings[] = {
{ 'w', SEL_CPMVAS },
{ CONTROL('W'), SEL_CPMVAS },
/* Delete from selection buffer */
{ 'x', SEL_RM },
{ CONTROL('X'), SEL_RM },
{ 'x', SEL_TRASH },
{ CONTROL('X'), SEL_TRASH },
{ 'X', SEL_RM_ONLY },
/* Open in a custom application */
{ 'o', SEL_OPENWITH },
{ CONTROL('O'), SEL_OPENWITH },
@ -260,10 +262,10 @@ static struct key bindings[] = {
{ CONTROL('J'), SEL_AUTONEXT },
/* Edit in EDITOR */
{ 'y', SEL_EDIT },
/* Run a plugin */
{ ',', SEL_PLUGIN },
/* Show total size of listed selection */
{ 'S', SEL_SELSIZE },
/* Run a plugin */
{ ',', SEL_PLUGIN },
/* Run command */
// broken!
// { '!', SEL_SHELL },