Compare commits
45 Commits
98612f5492
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
977d1a7120
|
|||
|
9b274417c5
|
|||
|
cc14a9f735
|
|||
|
b070df6010
|
|||
|
3075d47aeb
|
|||
|
85b2d8a2ee
|
|||
|
272ee62ad8
|
|||
|
a85426541a
|
|||
|
c9b003735d
|
|||
|
52a28f01c8
|
|||
|
3607757554
|
|||
|
6eb216a40a
|
|||
|
9ce6f47716
|
|||
|
c9662f1a7b
|
|||
|
9ddeb03652
|
|||
|
acb187c6b1
|
|||
|
9427df62e7
|
|||
|
4d6999c415
|
|||
|
30ed61fdd2
|
|||
|
2df916c9b3
|
|||
|
24401825b4
|
|||
|
2bfb490798
|
|||
|
338d00d605
|
|||
|
015652e379
|
|||
|
c298b6fc97
|
|||
|
7c2ab8ab59
|
|||
|
e423a3a1b1
|
|||
|
916f354c9b
|
|||
|
050f875c47
|
|||
|
aeffe40efc
|
|||
|
536aa57761
|
|||
|
0d10ae06e6
|
|||
|
e1b0831854
|
|||
|
4e93dfbb8d
|
|||
|
8a6bb54eb5
|
|||
|
4ef7c9edf7
|
|||
|
3eea106c3c
|
|||
|
7de8c84e8f
|
|||
|
e17c5e2083
|
|||
|
9bd3739122
|
|||
|
ec1f1031cc
|
|||
|
bc99b3dd48
|
|||
|
e948741864
|
|||
|
0adbac2066
|
|||
|
2238db5a4e
|
14
.clang-format
Normal file
14
.clang-format
Normal 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']
|
||||||
@@ -1,32 +1,42 @@
|
|||||||
# target_compile_features has been introduced in that version
|
# target_compile_features has been introduced in that version
|
||||||
cmake_minimum_required (VERSION 3.1)
|
cmake_minimum_required (VERSION 3.1...3.27)
|
||||||
project (sdn VERSION 0.1 LANGUAGES CXX)
|
project (sdn VERSION 1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
|
||||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
|
set (CMAKE_CXX_FLAGS
|
||||||
endif ()
|
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-misleading-indentation -pedantic")
|
||||||
|
|
||||||
# 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}")
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package (PkgConfig REQUIRED)
|
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)
|
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.cpp)
|
||||||
target_include_directories (${PROJECT_NAME} PUBLIC ${NCURSESW_INCLUDE_DIRS})
|
target_include_directories (${PROJECT_NAME}
|
||||||
target_link_libraries (${PROJECT_NAME} PUBLIC ${NCURSESW_LIBRARIES} acl)
|
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_features (${PROJECT_NAME} PUBLIC cxx_std_14)
|
||||||
target_compile_definitions (${PROJECT_NAME} PUBLIC
|
target_compile_definitions (${PROJECT_NAME} PUBLIC
|
||||||
-DPROJECT_NAME=\"${PROJECT_NAME}\" -DPROJECT_VERSION=\"${PROJECT_VERSION}\")
|
-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)
|
include (GNUInstallDirs)
|
||||||
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
# sdn-mc-ext should be in libexec, but we prefer it in PATH.
|
||||||
install (PROGRAMS ${PROJECT_NAME}-install DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install (TARGETS sdn sdn-mc-ext
|
||||||
install (FILES sdn.1 sdn-install.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
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})
|
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||||
|
|
||||||
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Directory navigator")
|
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Directory navigator")
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -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
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted.
|
purpose with or without fee is hereby granted.
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -1,19 +1,19 @@
|
|||||||
.POSIX:
|
.POSIX:
|
||||||
SHELL = /bin/sh
|
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 \([^ )]*\).*/ \
|
CPPFLAGS = `sed -ne '/^project (\([^ )]*\) VERSION \([^ )]*\).*/ \
|
||||||
s//-DPROJECT_NAME="\1" -DPROJECT_VERSION="\2"/p' CMakeLists.txt`
|
s//-DPROJECT_NAME="\1" -DPROJECT_VERSION="\2"/p' CMakeLists.txt`
|
||||||
|
|
||||||
sdn: sdn.cpp CMakeLists.txt
|
sdn: sdn.cpp CMakeLists.txt
|
||||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o sdn \
|
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o $@ \
|
||||||
-lacl `pkg-config --libs --cflags ncursesw`
|
-lacl `pkg-config --libs --cflags ncursesw`
|
||||||
static: sdn.cpp CMakeLists.txt
|
sdn-static: sdn.cpp CMakeLists.txt
|
||||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o sdn \
|
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $< -o $@ \
|
||||||
-static-libstdc++ \
|
-static-libstdc++ \
|
||||||
-Wl,--start-group,-Bstatic \
|
-Wl,--start-group,-Bstatic \
|
||||||
-lacl `pkg-config --static --libs --cflags ncursesw` \
|
-lacl `pkg-config --static --libs --cflags ncursesw` \
|
||||||
-Wl,--end-group,-Bdynamic
|
-Wl,--end-group,-Bdynamic
|
||||||
clean:
|
clean:
|
||||||
rm -f sdn
|
rm -f sdn sdn-static
|
||||||
|
|
||||||
.PHONY: static clean
|
.PHONY: clean
|
||||||
|
|||||||
20
NEWS
Normal file
20
NEWS
Normal 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
|
||||||
|
|
||||||
32
README.adoc
32
README.adoc
@@ -5,26 +5,29 @@ sdn
|
|||||||
'sdn' is a simple directory navigator that you can invoke while editing shell
|
'sdn' is a simple directory navigator that you can invoke while editing shell
|
||||||
commands. It enables you to:
|
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:
|
* 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
|
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
|
can be simply forwarded if it is to be edited. What's more, it will always
|
||||||
be obvious whether the navigator is running.
|
be obvious whether the navigator is running.
|
||||||
|
|
||||||
The only supported platform is Linux. I wanted to try a different, simpler
|
'sdn' runs on Linux and all BSD derivatives. I wanted to try a different,
|
||||||
approach here, and the end result is very friendly to tinkering.
|
simpler approach here, and the end result is very friendly to tinkering.
|
||||||
|
|
||||||
image::sdn.png[align="center"]
|
image::sdn.png[align="center"]
|
||||||
|
|
||||||
Packages
|
Packages
|
||||||
--------
|
--------
|
||||||
Regular releases are sporadic. git master should be stable enough. You can get
|
Regular releases are sporadic. git master should be stable enough.
|
||||||
a package with the latest development version from Archlinux's AUR.
|
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
|
Building
|
||||||
--------
|
--------
|
||||||
Build dependencies: CMake and/or make, a C++14 compiler, pkg-config +
|
Build dependencies: CMake and/or make, a C++17 compiler, pkg-config +
|
||||||
Runtime dependencies: ncursesw, libacl
|
Runtime dependencies: ncursesw, libacl (on Linux)
|
||||||
|
|
||||||
// Working around libasciidoc's missing support for escaping it like \++
|
// Working around libasciidoc's missing support for escaping it like \++
|
||||||
Unfortunately most LLVM libc{plus}{plus} versions have a bug that crashes 'sdn'
|
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
|
cursor 231 202
|
||||||
|
select 202 bold
|
||||||
bar 16 255 ul
|
bar 16 255 ul
|
||||||
cwd bold
|
cwd bold
|
||||||
input
|
input
|
||||||
@@ -89,15 +93,19 @@ To obtain more vifm-like controls, you may write the following to your
|
|||||||
|
|
||||||
....
|
....
|
||||||
normal h parent
|
normal h parent
|
||||||
normal l choose
|
normal l enter
|
||||||
....
|
....
|
||||||
|
|
||||||
Helper programs
|
Helper programs
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
The F3 and F4 keys are normally bound to actions 'view' and 'edit', similarly to
|
The F3, F13 and F4 keys are normally bound to actions 'view', 'view-raw',
|
||||||
Norton Commander and other orthodox file managers. The helper programs used
|
and 'edit', similarly to Norton Commander and other orthodox file managers.
|
||||||
here may be changed by setting the PAGER and VISUAL (or EDITOR) environment
|
The helper programs used here may be changed by setting the PAGER and VISUAL
|
||||||
variables.
|
(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
|
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
|
`PAGER='mcview -u' sdn`, beware that this helper cannot read files from its
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ sdn-navigate () {
|
|||||||
# helpers after the terminal has been resized while running sdn
|
# helpers after the terminal has been resized while running sdn
|
||||||
command true
|
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
|
/bin/sh -c "$helper" </dev/tty || break
|
||||||
done
|
done
|
||||||
# optionally: zle zle-line-init
|
# optionally: zle zle-line-init
|
||||||
@@ -51,6 +54,7 @@ sdn-navigate () {
|
|||||||
((SDN_P=SDN_P+${#insert}+1))
|
((SDN_P=SDN_P+${#insert}+1))
|
||||||
}
|
}
|
||||||
[[ -z $helper ]] && break
|
[[ -z $helper ]] && break
|
||||||
|
history -s -- "$helper"
|
||||||
/bin/sh -c "$helper" || break
|
/bin/sh -c "$helper" || break
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -121,7 +125,7 @@ done
|
|||||||
|
|
||||||
# Figure out the shell to integrate with
|
# Figure out the shell to integrate with
|
||||||
login=$(basename "$SHELL")
|
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" ]
|
if [ -z "$shell" ]
|
||||||
then
|
then
|
||||||
if [ "$login" != "$actual" ]
|
if [ "$login" != "$actual" ]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.Dd October 27, 2020
|
.Dd October 27, 2020
|
||||||
.Dt SDN-INSTALL 1
|
.Dt SDN-INSTALL 1
|
||||||
.Os Linux
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm sdn-install
|
.Nm sdn-install
|
||||||
.Nd integrate sdn with the shell
|
.Nd integrate sdn with the shell
|
||||||
|
|||||||
222
sdn-mc-ext.cpp
Normal file
222
sdn-mc-ext.cpp
Normal 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> §ion) -> 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 §ion) -> 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 §ion : 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
54
sdn-view
Executable 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
24
sdn-view.1
Normal 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
13
sdn.1
@@ -1,7 +1,7 @@
|
|||||||
\" https://mandoc.bsd.lv/man/roff.7.html#Sentence_Spacing
|
\" https://mandoc.bsd.lv/man/roff.7.html#Sentence_Spacing
|
||||||
.Dd October 27, 2020
|
.Dd December 30, 2024
|
||||||
.Dt SDN 1
|
.Dt SDN 1
|
||||||
.Os Linux
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm sdn
|
.Nm sdn
|
||||||
.Nd directory navigator
|
.Nd directory navigator
|
||||||
@@ -68,8 +68,8 @@ and you can use the
|
|||||||
.Xr dircolors 1
|
.Xr dircolors 1
|
||||||
utility to initialize this variable.
|
utility to initialize this variable.
|
||||||
.It Ev PAGER
|
.It Ev PAGER
|
||||||
The viewer program to be launched by the F3 key binding as well as to show
|
The viewer program to be launched by the F3 and F13 key bindings as well as
|
||||||
the internal help message.
|
to show the internal help message.
|
||||||
If none is set, it defaults to
|
If none is set, it defaults to
|
||||||
.Xr less 1 .
|
.Xr less 1 .
|
||||||
.It Ev VISUAL , Ev EDITOR
|
.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:
|
To obtain more vifm-like controls and Windows-like quit abilities:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
normal h parent
|
normal h parent
|
||||||
normal l choose
|
normal l enter
|
||||||
normal M-f4 quit
|
normal M-f4 quit
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -107,7 +107,7 @@ For rxvt, that would be:
|
|||||||
define C-ppage ^[[5^
|
define C-ppage ^[[5^
|
||||||
define C-npage ^[[6^
|
define C-npage ^[[6^
|
||||||
normal C-ppage parent
|
normal C-ppage parent
|
||||||
normal C-npage choose
|
normal C-npage enter
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
Escape characters must be inserted verbatim, e.g., by pressing C-v ESC in vi,
|
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:
|
following may work:
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
cursor 231 202
|
cursor 231 202
|
||||||
|
select 202 bold
|
||||||
bar 16 255 ul
|
bar 16 255 ul
|
||||||
cwd bold
|
cwd bold
|
||||||
input
|
input
|
||||||
|
|||||||
Reference in New Issue
Block a user