Přemysl Eric Janouch
3075d47aeb
Tsk, tsk, parasiting on what we wanted to replace. macOS is annoying to port to. Unfortunately, this script is also very slow, for some reason.
212 lines
5.1 KiB
Bash
Executable File
212 lines
5.1 KiB
Bash
Executable File
#!/bin/sh -e
|
|
# sdn-view: a viewer for sdn that makes use of Midnight Commander configuration
|
|
# to make more kinds of files directly viewable
|
|
|
|
if [ "$#" -ne 1 ]
|
|
then
|
|
echo "Usage: $0 FILE" >&2
|
|
exit 2
|
|
fi
|
|
|
|
# This handles both MC_DATADIR and odd installation locations.
|
|
datadir=
|
|
if command -v mc >/dev/null
|
|
then datadir=$(mc --datadir | sed 's/ (.*)$//')
|
|
fi
|
|
|
|
export SDN_VIEW_CONFIG=
|
|
for dir in "$HOME"/.config/mc "$datadir" /etc/mc
|
|
do
|
|
if [ -n "$dir" -a -f "$dir/mc.ext.ini" ]
|
|
then
|
|
SDN_VIEW_CONFIG=$dir/mc.ext.ini
|
|
break
|
|
fi
|
|
done
|
|
|
|
export PAGER=${PAGER:-less}
|
|
export MC_EXT_FILENAME=$(realpath "$1")
|
|
export MC_EXT_BASENAME=$(basename "$1")
|
|
export MC_EXT_CURRENTDIR=$(dirname "$1")
|
|
export SDN_VIEW_TYPE=$(file -bz "$1")
|
|
process() (awk -f - <<'EOF'
|
|
BEGIN {
|
|
if (!(Config = ENVIRON["SDN_VIEW_CONFIG"]))
|
|
exit
|
|
|
|
Verb = "View"
|
|
Section = ""
|
|
while ((getline < Config) > 0) {
|
|
if (/^\s*(#.*)?$/) {
|
|
# Skip.
|
|
} else if (/^\[[^]]+\]$/) {
|
|
Sections[++SectionsLen] = Section = substr($0, 2, length($0) - 2)
|
|
} else if (/^[^=]+=[^=]*$/) {
|
|
split($0, kv, "=")
|
|
Keys[Section, kv[1]] = kv[2]
|
|
}
|
|
}
|
|
|
|
Type = ENVIRON["SDN_VIEW_TYPE"]
|
|
Path = ENVIRON["MC_EXT_FILENAME"]
|
|
Basename = ENVIRON["MC_EXT_BASENAME"]
|
|
Dirname = ENVIRON["MC_EXT_CURRENTDIR"]
|
|
|
|
for (i = 1; i <= SectionsLen; i++) {
|
|
if (Sections[i] == "mc.ext.ini" ||
|
|
Sections[i] == "Default" ||
|
|
Sections[i] ~ /^Include[\/]/)
|
|
continue
|
|
try(Sections[i])
|
|
}
|
|
|
|
# Not attempting any inclusions here.
|
|
print expand_command(Keys["Default", Verb])
|
|
}
|
|
|
|
function try(section, pair, a, key, full, include) {
|
|
for (pair in Keys) {
|
|
split(pair, a, SUBSEP)
|
|
if (a[1] == section)
|
|
full[a[2]] = Keys[pair]
|
|
}
|
|
if ("Include" in full) {
|
|
delete full["Open"]
|
|
delete full["View"]
|
|
delete full["Edit"]
|
|
include = "Include/" full["Include"]
|
|
for (pair in Keys) {
|
|
split(pair, a, SUBSEP)
|
|
if (a[1] == include)
|
|
full[a[2]] = Keys[pair]
|
|
}
|
|
}
|
|
if (ENVIRON["SDN_VIEW_DEBUG"]) {
|
|
print "[" section "]" > "/dev/stderr"
|
|
for (key in full)
|
|
print " " key ": " full[key] > "/dev/stderr"
|
|
}
|
|
if (Verb in full && section_matches(full, Type, Basename)) {
|
|
print expand_command(full[Verb])
|
|
exit
|
|
}
|
|
}
|
|
|
|
function shell_escape(string) {
|
|
gsub(/'/, "'\\''", string)
|
|
return "'" string "'"
|
|
}
|
|
|
|
function expand_command(cmd, toview, out, seq, argument, value, a, pipe) {
|
|
out = ""
|
|
while (match(cmd, /%[a-zA-Z]*\{[^}]*\}|%[a-zA-Z]+|%%/)) {
|
|
out = out substr(cmd, 1, RSTART - 1)
|
|
seq = substr(cmd, RSTART + 1, RLENGTH - 1)
|
|
cmd = substr(cmd, RSTART + RLENGTH)
|
|
|
|
argument = ""
|
|
if (match(seq, /\{.*\}$/)) {
|
|
argument = substr(seq, RSTART + 1, RLENGTH - 2)
|
|
seq = substr(seq, 1, RSTART - 1)
|
|
}
|
|
|
|
if (seq == "%") {
|
|
out = out "%"
|
|
} else if (seq == "p") {
|
|
out = out shell_escape(Basename)
|
|
} else if (seq == "f") {
|
|
out = out shell_escape(Path)
|
|
} else if (seq == "d") {
|
|
out = out shell_escape(Dirname)
|
|
} else if (seq == "view") {
|
|
toview = 1
|
|
|
|
sub(/^ +/, "", cmd)
|
|
split(argument, a, /,/)
|
|
for (value in a) {
|
|
if (a[value] == "hex")
|
|
pipe = pipe " | od -t x1"
|
|
|
|
# more(1) and less(1) either ignore or display this:
|
|
#if (a[value] == "nroff")
|
|
# pipe = pipe " | col -b"
|
|
}
|
|
} else if (seq == "var") {
|
|
value = ""
|
|
if (!match(argument, /:.*/)) {
|
|
if (argument in ENVIRON)
|
|
value = ENVIRON[argument]
|
|
} else {
|
|
value = substr(argument, RSTART + 1)
|
|
argument = substr(argument, 1, RSTART - 1)
|
|
if (argument in ENVIRON)
|
|
value = ENVIRON[argument]
|
|
}
|
|
out = out shell_escape(value)
|
|
} else if (seq == "") {
|
|
print Config ": prompting not supported" > "/dev/stderr"
|
|
return
|
|
} else {
|
|
print Config ": unsupported: %" seq > "/dev/stderr"
|
|
return
|
|
}
|
|
}
|
|
out = out cmd pipe
|
|
|
|
# While the processing is mostly generic for all verbs,
|
|
# we'd have to distinguish non-view commands in this AWK script's output.
|
|
if (!toview)
|
|
return
|
|
|
|
# In the case of out == "", we should just explicitly pass it to the pager,
|
|
# however it currently mixes with the case of "we can't use this View=".
|
|
return out
|
|
}
|
|
|
|
function section_matches(section, type, basename, value) {
|
|
if ("Directory" in section)
|
|
return 0
|
|
|
|
if ("Type" in section) {
|
|
value = section["Type"]
|
|
if ("TypeIgnoreCase" in section &&
|
|
section["TypeIgnoreCase"] == "true") {
|
|
type = tolower(type)
|
|
value = tolower(value)
|
|
}
|
|
gsub(/\\\\/, "\\", value)
|
|
gsub(/\\ /, " ", value)
|
|
if (type !~ value)
|
|
return 0
|
|
}
|
|
if ("Regex" in section) {
|
|
value = section["Regex"]
|
|
if ("RegexIgnoreCase" in section &&
|
|
section["RegexIgnoreCase"] == "true") {
|
|
basename = tolower(basename)
|
|
value = tolower(value)
|
|
}
|
|
gsub(/\\\\/, "\\", value)
|
|
return basename ~ value
|
|
} else if ("Shell" in section) {
|
|
value = section["Shell"]
|
|
if ("RegexIgnoreCase" in section &&
|
|
section["ShellIgnoreCase"] == "true") {
|
|
basename = tolower(basename)
|
|
value = tolower(value)
|
|
}
|
|
if (value !~ /^[.]/)
|
|
return value == basename
|
|
return length(basename) >= length(value) &&
|
|
substr(basename, length(basename) - length(value) + 1) == value
|
|
}
|
|
return type != ""
|
|
}
|
|
EOF
|
|
)
|
|
command=$(process)
|
|
if [ -z "$command" ]
|
|
then "$PAGER" -- "$MC_EXT_FILENAME"
|
|
else eval "$command" | "$PAGER"
|
|
fi
|