diff options
Diffstat (limited to 'sdn-view')
-rwxr-xr-x | sdn-view | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/sdn-view b/sdn-view new file mode 100755 index 0000000..b53db49 --- /dev/null +++ b/sdn-view @@ -0,0 +1,211 @@ +#!/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 |