-
Notifications
You must be signed in to change notification settings - Fork 0
/
b
executable file
·257 lines (248 loc) · 8.76 KB
/
b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/bin/bash
# [b]rowse - overview of given files or current directory
# depends: tput stat bat checkaccess(in my dotfiles)
# optdepends: timg, neovim (compressed files), pdftoppm (PDF), mtn (video), audiowaveform, imagemagick (images)
# args: files to inspect, any arg starting with dash is passed on to bat
#
# Supports:
# - listing directories
# - listing contents of any compressed file with neovim
# - visual files are displayed with timg
# video thumbnails via mtn, pdf pages from pdftoppm
# - text files are displayed through bat
# Usually automatically requests elevation through sudo when needed
set -o pipefail
inspect=false
opts='itvh'
while getopts "$opts" OPTION; do
case "$OPTION" in
# inspect: Show file info without preview
(i) inspect=true;;
(t) tree=true;;
(v) set -eo xtrace;
export PS4='+\#> ';;
(h|?) echo "Usage: $(basename $0) [-$opts] <paths...>" && exit 2;;
(--) break;;
esac
done
shift "$(($OPTIND -1))"
checkperm() {
checkaccess -r "$@" || elevate=sudo
mime="$(test -n "$shifted" || $elevate file --dereference --mime -- "$@")"
}
fileinfo() {
tput setaf 4
for arg
do case "$arg" in (-*) continue;; esac
tput smso
$elevate file --exclude elf -E "$arg"
tput rmso
size=$(stat --format=%s "$arg")
# Check if SSH key (<10KB then read)
if test "$size" -lt 10000
then $elevate ssh-keygen -l -f "$arg" 2>/dev/null
fi
# I think this check is here to avoid scrolling text interpreted as video
#probe="$($elevate ffprobe "$arg" 2>&1)"
#echo $probe | grep -v -e '00:00:00.04' -e 'ansi' &&
# Print media infos over file infos when <2G
if ! { test "$size" -lt 2000000000 && $elevate ffprobe -hide_banner "$arg" 2>&1 | grep -E "bitrate: .{3,5} " | sed 's/, start:[^,]\+,/,/'; } || $inspect
then $elevate stat --format "%U:%G %A %s $(
size="$($elevate unzip -l "$arg" 2>/dev/null | tail -1)" &&
echo "(uncompressed $(echo $size | cut -d' ' -f1 | numfmt --to=iec-i --suffix=B))"
) - birth %.10w mod %.10y" "$arg" | numfmt --field=3 --to=iec-i --padding=6 --suffix=B
fi
done
tput sgr0
}
prefix=/tmp/b
mkdir -p "$prefix"
declare -a timg timga bat batplain ls
for arg; do
test -z "$noflags" && case "$arg" in (--) noflags=true; continue;; (-*) flags="$flags $arg"; continue;; esac
checkperm "$arg"
if ! $elevate test -e "$arg"
then if test -h "$arg"
then fileinfo "$arg"
else echo "File not found: '$arg'" 1>&2
fi
continue
fi
# amount of items to display per line in a grid for two lines max
grid=$(expr $(tput cols) / \( 25 - \( $# / 2 \) \& $# \< 30 \| 5 \))
tmpfile="$prefix/$(basename -- "$arg")_$(dd "if=$arg" bs=512 count=10 2>/dev/null | md5sum | tr -d ' ' || true)"
mkdir -p "$prefix"
case "$mime" in
(*\ application/pdf\;*)
grid=$(expr $(tput cols) \* $# / $(tput lines))
grid=$(expr 3 \& $grid \< 3 \| $grid)
#limit=$(expr $grid \& \( $grid \> 3 \| $# \> 1 \) \| $grid '*' 2)
limit=$grid
if ! test -f "$tmpfile-1.ppm"
then echo Converting "$arg"
pdftoppm -forcenum -r 70 "$arg" "$tmpfile" -l $limit
fi
find "$prefix" -path "$tmpfile*.ppm" | sort | head -$limit | xargs -d'\n' timg -W --grid=$grid
;;
(*\ application/*document*|*.xlsx:\ *)
# https://ask.libreoffice.org/t/convert-to-command-line-parameter/840/4
echo Converting "$arg"
soffice --headless --convert-to png --outdir "$prefix" "$arg" >/dev/null
timg+=("${tmpfile%.*}.png");;
(*/x-xcf*)
echo Converting "$arg"
convert -flatten "$arg" png:"$tmpfile"
timg+=("$tmpfile");;
# TODO .raw .img
(*/x-iso*|*/x-qemu-disk*) fdisk -l "$arg";;
(*\ video/*)
suffix=_thumbs.jpg
! $inspect &&
# TODO sometimes duration mismatch for short videos
test $(printf "%.0f" $(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$arg")) -gt 3 &&
mtn -q -i -t -W -r$(expr 5 - $# \& $# \< 4 \| 1) -D6 -b 0.6 -c $grid -w $(expr $(tput cols) '*' 20) \
-O "$prefix" -o "$suffix" "$arg" &&
timg -W "$prefix/$(basename -- "${arg%.*}")$suffix"
;;
(*\ image/*)
timg+=("$arg")
which identify >/dev/null && continue;;
(*\ inode/directory\;*)
ls+=("$arg")
test -L "$arg" || continue
;;
(*)
case "$(file --dereference "$arg")" in
(*\ ?udio*|*\ ADTS,*)
# TODO preconvert aac - |*\ ADTS\ *
if ! $inspect && which audiowaveform >/dev/null; then
img="$tmpfile.png"
case $TERM in (*-kitty) kitty=true; audioheight=2;; (*) audioheight=5;; esac
find "$img" -not -empty 2>/dev/null | grep --quiet . ||
audiowaveform --quiet --pixels-per-second 2 --height 36 --width 2000 --amplitude-scale auto \
--background-color 000000 --waveform-color 99BBFF --axis-label-color 000000 \
--input-filename "$arg" --output-format png >"$img" && {
test "$kitty" && timg -g x$audioheight "$arg" &&
printf "\\033[${audioheight}A%$(expr $audioheight \* 3)s"
timg -g x$audioheight --auto-crop --upscale "$img"
}
fi
timga+=("$arg")
;;
(*:\ *compress*|*\ archive*)
list="$tmpfile-list.txt"
case "$arg" in
(*.7z) test $# = 1 && 7z l "$arg";;
(*.tar*) tar --list --file "$arg";;
(*)
if test $# = 1
then nvim "$arg"
else case "$arg" in (*.part);; (*)
nvim -es "+2w$list|5,w>>$list" "$arg"
batplain+=("$list");;
esac
fi
esac
;;
(*:\ SQLite\ *\ database*) highlight "Tables" && sqlite3 "$arg" ".tables";; # TODO for few tables: SELECT * FROM db LIMIT 3; | cut -c-$col
(*:\ data) ;;
(*\ key) bat+=("$arg");;
(*) bat+=("$arg")
timga+=("$arg")
continue
;;
esac
;;
esac
fileinfo "$arg"
done
# timg: images
# timga: potentially viewable as image
if test "$timg"; then
# TODO Don't show info on all images for gifs
$inspect ||
if which timg >/dev/null
then $elevate timg $(test "$timga" && echo "-V") --rotate=exif -g $(tput cols)x$(expr $(tput lines) / 2) \
$(test $# -gt 1 &&
echo "-t0.2 --auto-crop --center $(test $# -lt 20 && echo "--title") --grid=$(((grid < $# ? grid : $#) / 2))x") \
"${timg[@]}" "${timga[@]}" 2>/dev/null || true
else for img in "${timg[@]}"
do catimg -H $(expr $(tput lines) / 2) $img
done
fi
if which identify >/dev/null && ( $inspect || test $# -lt 10 ); then
tput setaf 6
for img in "${timg[@]}"
do case $img in (*.gif) continue;; esac
ident="$(identify -ping -precision 3 -format "%wx%h %b %m %[bit-depth]-bit %[colorspace]" "$img")"
printf "%11s %-30s %s\n" "${ident%% *}" "$(basename -- "$img")" "${ident#* }"
done
tput sgr0
fi
fi
pager="${PAGER:-'less -rF'}"
# bat: unknown files
# batplain: files to print without header
if test "$bat" -o "$batplain"; then
# TODO allow plain less
test "$(bat --version | cut -d. -f2)" -gt 16 && rule=,rule
if test $# -gt ${#bat[@]} -a $# -gt ${#batplain[@]} && test -z "$flags"
then cut="--line-range :7"
batpager="cut -c-$(expr $(tput cols) \* 19 / 10 | cut -d. -f1)"
fi
batcommand="$elevate bat $cut $flags --pager"
batstyle="--style plain$rule"
test "$batplain" && $batcommand "${batpager:-$pager}" $batstyle "${batplain[@]}"
test "$bat" &&
if test "$cut" && ! $inspect
then case $TERM in (*kitty)
declare -a timgtxt
for file in "${bat[@]}"
do txt="$prefix/$(basename -- "$file").txt"
cp -- "$file" "$txt"
timgtxt+=("$txt")
done
timg -V --grid="$(expr 4 \& ${#bat[@]} \> 5 \| ${#bat[@]})" --title="%b" --frames=3 "${timgtxt[@]}" ||
head -3 "${timgtxt[@]}"
#fileinfo "${bat[@]}"
;;
(*) for file in "${bat[@]}"; do
$batcommand "${batpager:-$pager}" $batstyle "$file"
fileinfo "$file"
done;;
esac
else
if ! $inspect
then json=false
if which cj >/dev/null; then
json=true
for file in "${bat[@]}"; do
case $file in (*.json|*.geojson);; (*) json=false;; esac
done
fi
if $json
then cj "${bat[@]}"
else $batcommand "${batpager:-$pager}" $batstyle,header$(test $# -gt 1 && echo ",numbers") "${bat[@]}"
fi
fi
if $inspect || test $# -lt $(expr $(tput lines) / 10);
then fileinfo "${bat[@]}"
fi
fi
fi
if test "$ls" -o $# -le $(echo "$flags" | wc -w); then
checkperm .
# Alternative: find -exec ls -dl {} +
{
# TODO handle single quotes in filenames
# TODO listing sometimes doubles as exa prints partial listings while working
timeout .6s sh -c "
if test '$tree'
then $elevate tree -a --dirsfirst --du -h -C -L 3 $flags -- $(printf "'%s' " "${ls[@]:-.}")
elif which exa 2>/dev/null >&2
then $elevate exa --icons --color=always --long --group --classify --all --all --sort=modified --reverse $flags -- $(printf "'%s' " "${ls[@]:-.}")
else $elevate ls -l $(test $# -gt ${#ls[@]} && echo '-d') --color=always --human-readable --si --group-directories-first --file-type --dereference-command-line-symlink-to-dir --all $flags -- $(printf "'%s' " "${ls[@]:-.}")
fi
" || $elevate ls $(test $# -gt ${#ls[@]} && echo "-d") --color=always --human-readable --si --dereference-command-line --all --sort=none $flags -- "${ls[@]:-.}"
} | $pager --quit-if-one-screen
fi