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.
This commit is contained in:
parent
85b2d8a2ee
commit
3075d47aeb
@ -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")
|
||||
|
8
NEWS
8
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
|
||||
|
@ -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
|
||||
|
211
sdn-view
Executable file
211
sdn-view
Executable file
@ -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
|
23
sdn-view.1
Normal file
23
sdn-view.1
Normal file
@ -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.
|
6
sdn.1
6
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
|
||||
|
18
sdn.cpp
18
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<wint_t, action> g_normal_actions {
|
||||
{ALT | '\r', ACTION_CHOOSE_FULL}, {ALT | KEY (ENTER), ACTION_CHOOSE_FULL},
|
||||
{'\r', ACTION_CHOOSE}, {KEY (ENTER), ACTION_CHOOSE},
|
||||
{KEY (F (1)), ACTION_HELP}, {'h', ACTION_HELP},
|
||||
{KEY (F (3)), ACTION_VIEW}, {KEY (F (4)), ACTION_EDIT},
|
||||
{KEY (F (3)), ACTION_VIEW}, {KEY (F (13)), ACTION_VIEW_RAW},
|
||||
{KEY (F (4)), ACTION_EDIT},
|
||||
{'q', ACTION_QUIT}, {ALT | 'q', ACTION_QUIT_NO_CHDIR},
|
||||
// M-o ought to be the same shortcut the navigator is launched with
|
||||
{ALT | 'o', ACTION_QUIT},
|
||||
@ -1015,12 +1017,17 @@ fun run_program (initializer_list<const char *> list, const string &filename) {
|
||||
update ();
|
||||
}
|
||||
|
||||
fun view (const string &filename) {
|
||||
fun view_raw (const string &filename) {
|
||||
// XXX: we cannot realistically detect that the pager hasn't made a pause
|
||||
// at the end of the file, so we can't ensure all contents have been seen
|
||||
run_program ({(const char *) getenv ("PAGER"), "less", "cat"}, filename);
|
||||
}
|
||||
|
||||
fun view (const string &filename) {
|
||||
run_program ({(const char *) getenv ("SDN_VIEWER"), "sdn-view",
|
||||
(const char *) getenv ("PAGER"), "less", "cat"}, filename);
|
||||
}
|
||||
|
||||
fun edit (const string &filename) {
|
||||
run_program ({(const char *) getenv ("VISUAL"),
|
||||
(const char *) getenv ("EDITOR"), "vi"}, filename);
|
||||
@ -1445,8 +1452,11 @@ fun handle (wint_t c) -> bool {
|
||||
case ACTION_CHOOSE:
|
||||
choose (current);
|
||||
break;
|
||||
case ACTION_VIEW:
|
||||
case ACTION_VIEW_RAW:
|
||||
// Mimic mc, it does not seem sensible to page directories
|
||||
(is_directory ? change_dir : view_raw) (current.filename);
|
||||
break;
|
||||
case ACTION_VIEW:
|
||||
(is_directory ? change_dir : view) (current.filename);
|
||||
break;
|
||||
case ACTION_EDIT:
|
||||
|
Loading…
Reference in New Issue
Block a user