From 3075d47aeb52f6549e957023d353d4c5cb857e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?=
Date: Sat, 28 Dec 2024 00:28:22 +0100
Subject: [PATCH] Add and integrate a script to execute mc.ext.ini
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.
---
CMakeLists.txt | 6 +-
NEWS | 8 ++
sdn-install.1 | 2 +-
sdn-view | 211 +++++++++++++++++++++++++++++++++++++++++++++++++
sdn-view.1 | 23 ++++++
sdn.1 | 6 +-
sdn.cpp | 18 ++++-
7 files changed, 264 insertions(+), 10 deletions(-)
create mode 100755 sdn-view
create mode 100644 sdn-view.1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 87e5206..87a2c7e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,8 +28,10 @@ target_compile_definitions (${PROJECT_NAME} PUBLIC
include (GNUInstallDirs)
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
-install (PROGRAMS ${PROJECT_NAME}-install DESTINATION ${CMAKE_INSTALL_BINDIR})
-install (FILES sdn.1 sdn-install.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+install (PROGRAMS ${PROJECT_NAME}-install ${PROJECT_NAME}-view
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
+install (FILES sdn.1 sdn-install.1 sdn-view.1
+ DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Directory navigator")
diff --git a/NEWS b/NEWS
index 958058d..b5434ef 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+Unreleased
+
+ * Added an sdn-view script that can process Midnight Commander mc.ext.ini files
+ and apply matching filters; this script has been made the default F3 binding,
+ while the original direct pager invocation has been moved to F13 (which also
+ reflects Midnight Commander)
+
+
1.0.0 (2024-12-21)
* Initial release
diff --git a/sdn-install.1 b/sdn-install.1
index c186f63..a6972c8 100644
--- a/sdn-install.1
+++ b/sdn-install.1
@@ -1,6 +1,6 @@
.Dd October 27, 2020
.Dt SDN-INSTALL 1
-.Os Linux
+.Os
.Sh NAME
.Nm sdn-install
.Nd integrate sdn with the shell
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
diff --git a/sdn-view.1 b/sdn-view.1
new file mode 100644
index 0000000..d78d325
--- /dev/null
+++ b/sdn-view.1
@@ -0,0 +1,23 @@
+.Dd December 28, 2024
+.Dt SDN-VIEW 1
+.Os
+.Sh NAME
+.Nm sdn-view
+.Nd run Midnight Commander view configuration externally
+.Sh SYNOPSIS
+.Nm sdn-view
+.Ar path
+.Sh DESCRIPTION
+.Nm
+invokes
+.Ev PAGER
+or a fallback pager on the passed filename.
+.Pp
+If it succeeds in finding a
+.Xr mc 1
+.Pa mc.ext.ini
+file, it will first process it, and apply any matching filter.
+.Sh REPORTING BUGS
+Use
+.Lk https://git.janouch.name/p/sdn
+to report bugs, request features, or submit pull requests.
diff --git a/sdn.1 b/sdn.1
index 097a5de..0c9ee8d 100644
--- a/sdn.1
+++ b/sdn.1
@@ -1,7 +1,7 @@
\" https://mandoc.bsd.lv/man/roff.7.html#Sentence_Spacing
.Dd October 27, 2020
.Dt SDN 1
-.Os Linux
+.Os
.Sh NAME
.Nm sdn
.Nd directory navigator
@@ -68,8 +68,8 @@ and you can use the
.Xr dircolors 1
utility to initialize this variable.
.It Ev PAGER
-The viewer program to be launched by the F3 key binding as well as to show
-the internal help message.
+The viewer program to be launched by the F3 and F13 key bindings as well as
+to show the internal help message.
If none is set, it defaults to
.Xr less 1 .
.It Ev VISUAL , Ev EDITOR
diff --git a/sdn.cpp b/sdn.cpp
index 07f56a2..af2e09e 100644
--- a/sdn.cpp
+++ b/sdn.cpp
@@ -428,7 +428,8 @@ enum { ALT = 1 << 24, SYM = 1 << 25 }; // Outside the range of Unicode
#define CTRL(char) ((char) == '?' ? 0x7f : (char) & 0x1f)
#define ACTIONS(XX) XX(NONE) XX(HELP) XX(QUIT) XX(QUIT_NO_CHDIR) \
- XX(CHOOSE) XX(CHOOSE_FULL) XX(VIEW) XX(EDIT) XX(SORT_LEFT) XX(SORT_RIGHT) \
+ XX(CHOOSE) XX(CHOOSE_FULL) XX(VIEW_RAW) XX(VIEW) XX(EDIT) \
+ XX(SORT_LEFT) XX(SORT_RIGHT) \
XX(UP) XX(DOWN) XX(TOP) XX(BOTTOM) XX(HIGH) XX(MIDDLE) XX(LOW) \
XX(PAGE_PREVIOUS) XX(PAGE_NEXT) XX(SCROLL_UP) XX(SCROLL_DOWN) XX(CENTER) \
XX(CHDIR) XX(PARENT) XX(GO_START) XX(GO_HOME) \
@@ -451,7 +452,8 @@ static map