aboutsummaryrefslogtreecommitdiff
path: root/sdn-view
diff options
context:
space:
mode:
Diffstat (limited to 'sdn-view')
-rwxr-xr-xsdn-view211
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