#!/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