Compare commits

...

38 Commits

Author SHA1 Message Date
977d1a7120 Update documentation
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
2024-12-30 19:36:51 +01:00
9b274417c5 Make pressing / in search activate ACTION_ENTER
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
You can disable this in the bindings file using: search / none

Closes #6
2024-12-30 00:57:55 +01:00
cc14a9f735 Add selection functionality
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
At least for now, other actions mostly ignore the selection,
which is consistent with MC.

The selection is saved in the config/state file.

Closes #2
2024-12-30 00:30:34 +01:00
b070df6010 Rewrite sdn-view AWK core in C++
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
sdn-view is still slower than I'd like it to be,
just no longer ridiculously so.
2024-12-29 02:58:35 +01:00
3075d47aeb 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.
2024-12-28 04:58:13 +01:00
85b2d8a2ee Use more of the relevant pkg-config variables
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
2024-12-21 16:57:43 +01:00
272ee62ad8 Make a release
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
2024-12-21 09:10:54 +01:00
a85426541a sdn-install: improve macOS installation
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
OpenBSD 7.5 Success
Login shells have a dash at the beginning of their first argument.
2024-12-21 08:16:23 +01:00
c9b003735d Fix OpenBSD build
All checks were successful
Arch Linux AUR Success
Alpine 3.20 Success
OpenBSD 7.5 Success
2024-12-21 07:59:44 +01:00
52a28f01c8 Add support for BSD derivatives, fix macOS
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
2024-12-21 07:37:06 +01:00
3607757554 Improve filename passing
All checks were successful
Alpine 3.19 Success
Arch Linux AUR Success
c9662f1 forgot about internal helpers.

Moreover, it is annoying to see the -- in shell history
for every single external helper call.
2024-04-17 02:00:10 +02:00
6eb216a40a Remove linker preference from CMakeLists.txt
All checks were successful
Arch Linux AUR Success
Alpine 3.19 Success
ld.gold doesn't understand all options that ld.bfd does.

In particular, this broke AUR builds with the current makepkg.conf.
2024-04-08 01:49:55 +02:00
9ce6f47716 Use more precise filesizes
Some checks failed
Arch Linux AUR Scripts failed
The behaviour differs from GNU `ls -lh` in that we use binary units,
meaning we get 1023 before 1.0K rather than 999 before 1.0K,
which is nonetheless still four characters wide.
2024-04-07 16:00:21 +02:00
c9662f1a7b Fix passing filenames starting with -
We don't want to pass them as program options.
2024-01-20 08:33:17 +01:00
9ddeb03652 CMakeLists.txt: declare compatibility with 3.27
Sadly, the 3.5 deprecation warning doesn't go away after this.
2023-08-01 03:11:11 +02:00
acb187c6b1 README.adoc: update package information 2023-07-01 21:58:29 +02:00
9427df62e7 Fix code formatting, bump copyright years 2023-06-12 14:00:58 +02:00
4d6999c415 Do not beep on window resizes 2023-06-11 21:11:20 +02:00
30ed61fdd2 Implement ^W in the editor 2023-06-11 21:05:55 +02:00
2df916c9b3 Support wildcards in interactive search
The previous search for the longest match is functionally
duplicated by typing individual characters on the input line,
and wildcards can be escaped, so there shouldn't be regressions
in terms of capability.
2022-09-03 12:17:02 +02:00
24401825b4 Add a missing case break 2022-01-08 11:23:36 +01:00
2bfb490798 Add and bind an action to center the cursor
"z" stands for VIM's "zz".
2022-01-08 11:17:24 +01:00
338d00d605 Do not crash on opendir() failures
Show an error message, and a way out.
2021-11-09 07:52:48 +01:00
015652e379 Fix build with recent ncurses
Easily gained, easily lost.  No more ^S binding.
2021-11-09 07:45:20 +01:00
c298b6fc97 bash integration: add helpers to command history 2021-11-05 22:11:30 +01:00
7c2ab8ab59 zsh integration: add helpers to command history 2021-11-05 21:54:56 +01:00
e423a3a1b1 Add clang-format configuration, clean up 2021-11-05 21:34:05 +01:00
916f354c9b Move the cursor to just created directories 2021-10-30 12:32:40 +02:00
050f875c47 Clean up 2021-10-30 12:13:45 +02:00
aeffe40efc Bind ^S to search, as in Emacs, with a trick
We heavily depend on ncurses anyway, so no worries about portability.
2021-10-18 11:23:17 +02:00
536aa57761 Slightly optimize very large directories
Cumulatively 10% of user time, give or take.

These are mainly pointless multibyte to wide string conversions.

The hit to source code readibility is minimal.
2021-10-05 21:01:32 +02:00
0d10ae06e6 Measured stat performance, io_uring not worth it
Large directories are slow for different reasons.
2021-10-05 19:18:15 +02:00
e1b0831854 Don't reload on sort changes
It is unexpected behaviour, and likes to take too long.
2021-09-26 09:38:16 +02:00
4e93dfbb8d Add a comment about libacl
So that I don't repeat my actions in the future.
2021-09-26 09:19:54 +02:00
8a6bb54eb5 Ignore empty XDG_CONFIG_DIRS
As the specification says we should.  GLib does this as well.

It is still possible to achieve an empty set by using ":",
which are two non-absolute paths that should be ignored.
GLib doesn't implement this.  Thus, we're now better than GLib.
2021-09-26 08:59:57 +02:00
4ef7c9edf7 Makefile: make the static build a non-phony target 2021-07-19 09:28:50 +02:00
3eea106c3c Explicitly disable GCC's -Wmisleading-indentation 2021-07-19 09:23:28 +02:00
7de8c84e8f Fix a signedness compiler warning 2021-07-19 09:12:17 +02:00
13 changed files with 848 additions and 227 deletions

14
.clang-format Normal file
View File

@@ -0,0 +1,14 @@
BasedOnStyle: LLVM
ColumnLimit: 80
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation
SpaceAfterCStyleCast: true
SpaceBeforeParens: Always
AlignAfterOpenBracket: DontAlign
AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign
AlignConsecutiveMacros: Consecutive
BreakBeforeTernaryOperators: true
SpacesBeforeTrailingComments: 2
WhitespaceSensitiveMacros: ['XX', 'ACTIONS', 'LS']

View File

@@ -1,32 +1,42 @@
# target_compile_features has been introduced in that version
cmake_minimum_required (VERSION 3.1)
project (sdn VERSION 0.1 LANGUAGES CXX)
cmake_minimum_required (VERSION 3.1...3.27)
project (sdn VERSION 1.0 LANGUAGES CXX)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
endif ()
# Since we use a language with slow compilers, let's at least use a fast linker
execute_process (COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version
ERROR_QUIET OUTPUT_VARIABLE ld_version)
if ("${ld_version}" MATCHES "GNU gold")
set (CMAKE_EXE_LINKER_FLAGS "-fuse-ld=gold ${CMAKE_EXE_LINKER_FLAGS}")
set (CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-misleading-indentation -pedantic")
endif ()
find_package (PkgConfig REQUIRED)
pkg_check_modules (NCURSESW QUIET ncursesw)
pkg_check_modules (ACL libacl)
pkg_check_modules (NCURSESW ncursesw)
if (NOT NCURSESW_FOUND)
find_library (NCURSESW_LIBRARIES NAMES ncursesw)
find_path (NCURSESW_INCLUDE_DIRS ncurses.h PATH_SUFFIXES ncurses)
endif ()
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.cpp)
target_include_directories (${PROJECT_NAME} PUBLIC ${NCURSESW_INCLUDE_DIRS})
target_link_libraries (${PROJECT_NAME} PUBLIC ${NCURSESW_LIBRARIES} acl)
target_include_directories (${PROJECT_NAME}
PUBLIC ${NCURSESW_INCLUDE_DIRS} ${ACL_INCLUDE_DIRS})
target_link_directories (${PROJECT_NAME}
PUBLIC ${NCURSESW_LIBRARY_DIRS} ${ACL_LIBRARY_DIRS})
target_link_libraries (${PROJECT_NAME}
PUBLIC ${NCURSESW_LIBRARIES} ${ACL_LIBRARIES})
target_compile_features (${PROJECT_NAME} PUBLIC cxx_std_14)
target_compile_definitions (${PROJECT_NAME} PUBLIC
-DPROJECT_NAME=\"${PROJECT_NAME}\" -DPROJECT_VERSION=\"${PROJECT_VERSION}\")
add_executable (${PROJECT_NAME}-mc-ext ${PROJECT_NAME}-mc-ext.cpp)
target_compile_features (${PROJECT_NAME}-mc-ext PUBLIC cxx_std_17)
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)
# sdn-mc-ext should be in libexec, but we prefer it in PATH.
install (TARGETS sdn sdn-mc-ext
DESTINATION ${CMAKE_INSTALL_BINDIR})
install (PROGRAMS sdn-install sdn-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")

View File

@@ -1,4 +1,4 @@
Copyright (c) 2017 - 2021, Přemysl Eric Janouch <p@janouch.name>
Copyright (c) 2017 - 2024, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

View File

@@ -1,19 +1,19 @@
.POSIX:
SHELL = /bin/sh
CXXFLAGS = -g -std=c++14 -Wall -Wextra -pedantic
CXXFLAGS = -g -std=c++14 -Wall -Wextra -Wno-misleading-indentation -pedantic
CPPFLAGS = `sed -ne '/^project (\([^ )]*\) VERSION \([^ )]*\).*/ \
s//-DPROJECT_NAME="\1" -DPROJECT_VERSION="\2"/p' CMakeLists.txt`
sdn: sdn.cpp CMakeLists.txt
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o sdn \
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o $@ \
-lacl `pkg-config --libs --cflags ncursesw`
static: sdn.cpp CMakeLists.txt
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o sdn \
sdn-static: sdn.cpp CMakeLists.txt
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o $@ \
-static-libstdc++ \
-Wl,--start-group,-Bstatic \
-lacl `pkg-config --static --libs --cflags ncursesw` \
-Wl,--end-group,-Bdynamic
clean:
rm -f sdn
rm -f sdn sdn-static
.PHONY: static clean
.PHONY: clean

20
NEWS Normal file
View File

@@ -0,0 +1,20 @@
Unreleased
* Added selection functionality, and adjusted key bindings:
- C-t or Insert toggle whether the current item is selected;
- + and - adjust the selection using shell globs;
- t and T insert the selection into the external command line
in relative or absolute form, respectively;
- Enter is like t but enters directories, and M-Enter is synonymous to t;
- C-g or Escape clear the selection, similarly to the editor.
* 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

View File

@@ -5,26 +5,29 @@ sdn
'sdn' is a simple directory navigator that you can invoke while editing shell
commands. It enables you to:
* take a quick peek at directory contents without running `ls`
* take a quick peek at directory contents without running `ls`;
* select files to insert into the command line;
* browse the filesystem without all the mess that Midnight Commander does:
there's no need to create a subshell in a new pty. The current command line
can be simply forwarded if it is to be edited. What's more, it will always
be obvious whether the navigator is running.
The only supported platform is Linux. I wanted to try a different, simpler
approach here, and the end result is very friendly to tinkering.
'sdn' runs on Linux and all BSD derivatives. I wanted to try a different,
simpler approach here, and the end result is very friendly to tinkering.
image::sdn.png[align="center"]
Packages
--------
Regular releases are sporadic. git master should be stable enough. You can get
a package with the latest development version from Archlinux's AUR.
Regular releases are sporadic. git master should be stable enough.
You can get a package with the latest development version using Arch Linux's
https://aur.archlinux.org/packages/sdn-git[AUR],
or as a https://git.janouch.name/p/nixexprs[Nix derivation].
Building
--------
Build dependencies: CMake and/or make, a C++14 compiler, pkg-config +
Runtime dependencies: ncursesw, libacl
Build dependencies: CMake and/or make, a C++17 compiler, pkg-config +
Runtime dependencies: ncursesw, libacl (on Linux)
// Working around libasciidoc's missing support for escaping it like \++
Unfortunately most LLVM libc{plus}{plus} versions have a bug that crashes 'sdn'
@@ -73,6 +76,7 @@ that of git, only named colours aren't supported:
....
cursor 231 202
select 202 bold
bar 16 255 ul
cwd bold
input
@@ -89,15 +93,19 @@ To obtain more vifm-like controls, you may write the following to your
....
normal h parent
normal l choose
normal l enter
....
Helper programs
~~~~~~~~~~~~~~~
The F3 and F4 keys are normally bound to actions 'view' and 'edit', similarly to
Norton Commander and other orthodox file managers. The helper programs used
here may be changed by setting the PAGER and VISUAL (or EDITOR) environment
variables.
The F3, F13 and F4 keys are normally bound to actions 'view', 'view-raw',
and 'edit', similarly to Norton Commander and other orthodox file managers.
The helper programs used here may be changed by setting the PAGER and VISUAL
(or EDITOR) environment variables.
If 'view' finds Midnight Commander, it will make use of its configuration
to apply any matching filter, such as to produce archive listings,
or it will run the respective command.
While it is mostly possible to get 'mcview' working using an invocation like
`PAGER='mcview -u' sdn`, beware that this helper cannot read files from its

View File

@@ -16,6 +16,9 @@ sdn-navigate () {
# helpers after the terminal has been resized while running sdn
command true
# Add to history, see https://www.zsh.org/mla/workers/2020/msg00633.html
fc -R =(print -- "$helper")
/bin/sh -c "$helper" </dev/tty || break
done
# optionally: zle zle-line-init
@@ -51,6 +54,7 @@ sdn-navigate () {
((SDN_P=SDN_P+${#insert}+1))
}
[[ -z $helper ]] && break
history -s -- "$helper"
/bin/sh -c "$helper" || break
done
}
@@ -121,7 +125,7 @@ done
# Figure out the shell to integrate with
login=$(basename "$SHELL")
actual=$(ps -p $$ -o ppid= | xargs ps -o comm= -p)
actual=$(ps -p $$ -o ppid= | xargs ps -o comm= -p | sed 's/^-//')
if [ -z "$shell" ]
then
if [ "$login" != "$actual" ]

View File

@@ -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

222
sdn-mc-ext.cpp Normal file
View File

@@ -0,0 +1,222 @@
//
// sdn-mc-ext: Midnight Commander extension file processor
//
// Copyright (c) 2024, Přemysl Eric Janouch <p@janouch.name>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#include <cstdlib>
#include <cctype>
#include <iostream>
#include <regex>
#include <string>
#include <unordered_map>
#include <vector>
// Trailing return types make C++ syntax suck considerably less
#define fun static auto
using namespace std;
// It is completely fine if this only modifies ASCII letters.
fun tolower (const string &s) -> string {
string result;
for (auto c : s) result += tolower (c);
return result;
}
fun shell_escape (const string &v) -> string {
return "'" + regex_replace (v, regex {"'"}, "'\\''") + "'";
}
string arg_type, arg_path, arg_basename, arg_dirname, arg_verb;
unordered_map<string, unordered_map<string, string>> sections;
fun expand_command (string command) -> pair<string, string> {
regex re_sequence {R"(%(%|[[:alpha:]]*\{([^}]*)\}|[[:alpha:]]+))"};
regex re_name {R"([^{}]*)"};
regex re_parameter {R"([^,]+")"};
string kind, out, pipe; smatch m;
while (regex_search (command, m, re_sequence)) {
out.append (m.prefix ());
auto seq = m.str (1);
command = m.suffix ();
string argument = m.str (2);
if (regex_search (seq, m, re_name))
seq = m.str ();
if (seq == "%") {
out += "%";
} else if (seq == "p") {
out += shell_escape (arg_basename);
} else if (seq == "f") {
out += shell_escape (arg_path);
} else if (seq == "d") {
out += shell_escape (arg_dirname);
} else if (seq == "var") {
string value;
if (auto colon = argument.find (':'); colon == argument.npos) {
if (auto v = getenv (argument.c_str ()))
value = v;
} else {
value = argument.substr (colon + 1);
if (auto v = getenv (argument.substr (0, colon).c_str ()))
value = v;
}
out += shell_escape (value);
} else if (seq == "cd") {
kind = seq;
command = regex_replace (command, regex {"^ +"}, "");
} else if (seq == "view") {
kind = seq;
command = regex_replace (command, regex {"^ +"}, "");
sregex_token_iterator it (argument.begin (), argument.end (),
re_parameter, 0), end;
for (; it != end; it++) {
if (*it == "hex")
pipe.append (" | od -t x1");
// more(1) and less(1) either ignore or display this:
//if (*it == "nroff")
// pipe.append (" | col -b");
}
} else if (seq == "") {
cerr << "sdn-mc-ext: prompting not supported" << endl;
return {};
} else {
cerr << "sdn-mc-ext: unsupported: %" << seq << endl;
return {};
}
}
return {kind,
pipe.empty () ? out.append (command) : "(" + out + ")" + pipe};
}
fun print_command (string cmd) {
auto command = expand_command (cmd);
cout << get<0> (command) << endl << get<1> (command) << endl;
}
fun section_matches (const unordered_map<string, string> &section) -> bool {
if (section.count ("Directory"))
return false;
// The configuration went through some funky changes;
// unescape \\ but leave other escapes alone.
auto filter_re = [](const string &s) {
string result;
for (size_t i = 0; i < s.length (); ) {
auto c = s[i++];
if (c == '\\' && i < s.length ())
if (c = s[i++]; c != '\\')
result += '\\';
result += c;
}
return result;
};
auto is_true = [&](const string &name) {
auto value = section.find (name);
return value != section.end () && value->second == "true";
};
if (auto kv = section.find ("Type"); kv != section.end ()) {
auto flags = std::regex::ECMAScript;
if (is_true ("TypeIgnoreCase"))
flags |= regex_constants::icase;
if (!regex_search (arg_type, regex {filter_re (kv->second), flags}))
return false;
}
auto basename = arg_basename;
if (auto kv = section.find ("Regex"); kv != section.end ()) {
auto flags = std::regex::ECMAScript;
if (is_true ("RegexIgnoreCase"))
flags |= regex_constants::icase;
return regex_search (basename, regex {filter_re (kv->second), flags});
}
if (auto kv = section.find ("Shell"); kv != section.end ()) {
auto value = kv->second;
if (is_true ("ShellIgnoreCase")) {
value = tolower (value);
basename = tolower (arg_basename);
}
if (value.empty () || value[0] != '.')
return value == basename;
return basename.length () >= value.length () &&
basename.substr (basename.length () - value.length ()) == value;
}
return !arg_type.empty ();
}
fun process (const string &section) -> bool {
auto full = sections.at (section);
if (auto include = full.find ("Include"); include != full.end ()) {
full.erase ("Open");
full.erase ("View");
full.erase ("Edit");
if (auto included = sections.find ("Include/" + include->second);
included != sections.end ()) {
for (const auto &kv : included->second)
full[kv.first] = kv.second;
}
}
if (getenv ("SDN_MC_EXT_DEBUG")) {
cerr << "[" << section << "]" << endl;
for (const auto &kv : full)
cerr << " " << kv.first << ": " << kv.second << endl;
}
if (full.count (arg_verb) && section_matches (full)) {
print_command (full[arg_verb]);
return true;
}
return false;
}
int main (int argc, char *argv[]) {
if (argc != 6) {
cerr << "Usage: " << argv[0]
<< " TYPE PATH BASENAME DIRNAME VERB < mc.ext.ini" << endl;
return 2;
}
arg_type = argv[1];
arg_path = argv[2], arg_basename = argv[3], arg_dirname = argv[4];
arg_verb = argv[5];
string line, section;
vector<string> order;
regex re_entry {R"(^([-\w]+) *= *(.*)$)"};
smatch m;
while (getline (cin, line)) {
if (line.empty () || line[0] == '#') {
continue;
} else if (auto length = line.length();
line.find_last_of ('[') == 0 &&
line.find_first_of (']') == length - 1) {
order.push_back ((section = line.substr (1, length - 2)));
} else if (regex_match (line, m, re_entry)) {
sections[section][m[1]] = m[2];
}
}
for (const auto &section : order) {
if (section == "mc.ext.ini" ||
section == "Default" ||
section.substr (0, 8) == "Include/")
continue;
if (process (section))
return 0;
}
print_command (sections["Default"][arg_verb]);
return 0;
}

54
sdn-view Executable file
View File

@@ -0,0 +1,54 @@
#!/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
config=
for dir in "$HOME"/.config/mc "$datadir" /etc/mc
do
if [ -n "$dir" -a -f "$dir/mc.ext.ini" ]
then
config=$dir/mc.ext.ini
break
fi
done
# This is often used in %env{} expansion, so let's be on the same page.
export PAGER=${PAGER:-less}
export MC_EXT_FILENAME=$(realpath "$1")
export MC_EXT_BASENAME=$(basename "$1")
export MC_EXT_CURRENTDIR=$(dirname "$MC_EXT_FILENAME")
output=$(sdn-mc-ext <"$config" "$(file -Lbz "$1")" \
"$MC_EXT_FILENAME" "$MC_EXT_BASENAME" "$MC_EXT_CURRENTDIR" View || :)
kind=$(echo "$output" | sed -n 1p)
command=$(echo "$output" | sed -n 2p)
case "$kind" in
view)
if [ -n "$command" ]
then eval "$command" | "$PAGER"
else "$PAGER" -- "$MC_EXT_FILENAME"
fi
;;
'')
if [ -n "$command" ]
then eval "$command"
else "$PAGER" -- "$MC_EXT_FILENAME"
fi
;;
*)
echo "Unsupported: $kind" >&2
exit 1
esac

24
sdn-view.1 Normal file
View File

@@ -0,0 +1,24 @@
.Dd December 30, 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,
or run the respective command.
.Sh REPORTING BUGS
Use
.Lk https://git.janouch.name/p/sdn
to report bugs, request features, or submit pull requests.

13
sdn.1
View File

@@ -1,7 +1,7 @@
\" https://mandoc.bsd.lv/man/roff.7.html#Sentence_Spacing
.Dd October 27, 2020
.Dd December 30, 2024
.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
@@ -95,7 +95,7 @@ names are used for special keys.
To obtain more vifm-like controls and Windows-like quit abilities:
.Bd -literal -offset indent
normal h parent
normal l choose
normal l enter
normal M-f4 quit
.Ed
.Pp
@@ -107,7 +107,7 @@ For rxvt, that would be:
define C-ppage ^[[5^
define C-npage ^[[6^
normal C-ppage parent
normal C-npage choose
normal C-npage enter
.Ed
.Pp
Escape characters must be inserted verbatim, e.g., by pressing C-v ESC in vi,
@@ -120,6 +120,7 @@ For a black-on-white terminal supporting 256 colours, a theme such as the
following may work:
.Bd -literal -offset indent
cursor 231 202
select 202 bold
bar 16 255 ul
cwd bold
input

598
sdn.cpp

File diff suppressed because it is too large Load Diff