Compare commits
No commits in common. "6f66aa3c06d3ed29cdc0d5e680840e4125fccb04" and "1ba2709fdae04e62442a045fb3892aa8710e9d81" have entirely different histories.
6f66aa3c06
...
1ba2709fda
397
sdn.cpp
397
sdn.cpp
@ -26,7 +26,6 @@
|
||||
#include <cwchar>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
@ -265,8 +264,6 @@ fun decode_attrs (const vector<string> &attrs) -> chtype {
|
||||
|
||||
// --- Application -------------------------------------------------------------
|
||||
|
||||
enum { ALT = 1 << 24, SYM = 1 << 25 }; // Outside the range of Unicode
|
||||
#define KEY(name) (SYM | KEY_ ## name)
|
||||
#define CTRL 31 &
|
||||
|
||||
struct entry {
|
||||
@ -283,47 +280,6 @@ struct entry {
|
||||
}
|
||||
};
|
||||
|
||||
#define ACTIONS(XX) XX(NONE) XX(CHOOSE) XX(CHOOSE_FULL) XX(QUIT) \
|
||||
XX(UP) XX(DOWN) XX(TOP) XX(BOTTOM) XX(PAGE_PREVIOUS) XX(PAGE_NEXT) \
|
||||
XX(SCROLL_UP) XX(SCROLL_DOWN) XX(GO_START) XX(GO_HOME) \
|
||||
XX(SEARCH) XX(RENAME) XX(RENAME_PREFILL) \
|
||||
XX(TOGGLE_FULL) XX(REDRAW) XX(RELOAD) \
|
||||
XX(INPUT_ABORT) XX(INPUT_CONFIRM) XX(INPUT_B_DELETE)
|
||||
|
||||
#define XX(name) ACTION_ ## name,
|
||||
enum action { ACTIONS(XX) ACTION_COUNT };
|
||||
#undef XX
|
||||
|
||||
#define XX(name) #name,
|
||||
static const char *g_action_names[] = {ACTIONS(XX)};
|
||||
#undef XX
|
||||
|
||||
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},
|
||||
// M-o ought to be the same shortcut the navigator is launched with
|
||||
{ALT | 'o', ACTION_QUIT}, {'q', ACTION_QUIT},
|
||||
{'k', ACTION_UP}, {CTRL 'p', ACTION_UP}, {KEY (UP), ACTION_UP},
|
||||
{'j', ACTION_DOWN}, {CTRL 'n', ACTION_DOWN}, {KEY (DOWN), ACTION_DOWN},
|
||||
{'g', ACTION_TOP}, {ALT | '<', ACTION_TOP}, {KEY (HOME), ACTION_TOP},
|
||||
{'G', ACTION_BOTTOM}, {ALT | '>', ACTION_BOTTOM}, {KEY(END), ACTION_BOTTOM},
|
||||
{KEY (PPAGE), ACTION_PAGE_PREVIOUS}, {KEY (NPAGE), ACTION_PAGE_NEXT},
|
||||
{CTRL 'y', ACTION_SCROLL_UP}, {CTRL 'e', ACTION_SCROLL_DOWN},
|
||||
{'&', ACTION_GO_START}, {'~', ACTION_GO_HOME},
|
||||
{'/', ACTION_SEARCH}, {'s', ACTION_SEARCH},
|
||||
{ALT | 'e', ACTION_RENAME_PREFILL}, {'e', ACTION_RENAME},
|
||||
{'t', ACTION_TOGGLE_FULL}, {ALT | 't', ACTION_TOGGLE_FULL},
|
||||
{CTRL 'L', ACTION_REDRAW}, {'r', ACTION_RELOAD},
|
||||
};
|
||||
static map<wint_t, action> g_input_actions = {
|
||||
{27, ACTION_INPUT_ABORT}, {CTRL 'g', ACTION_INPUT_ABORT},
|
||||
{L'\r', ACTION_INPUT_CONFIRM}, {KEY (ENTER), ACTION_INPUT_CONFIRM},
|
||||
{KEY (BACKSPACE), ACTION_INPUT_B_DELETE},
|
||||
};
|
||||
static const map<string, map<wint_t, action>*> g_binding_contexts = {
|
||||
{"normal", &g_normal_actions}, {"input", &g_input_actions},
|
||||
};
|
||||
|
||||
#define LS(XX) XX(NORMAL, "no") XX(FILE, "fi") XX(RESET, "rs") \
|
||||
XX(DIRECTORY, "di") XX(SYMLINK, "ln") XX(MULTIHARDLINK, "mh") \
|
||||
XX(FIFO, "pi") XX(SOCKET, "so") XX(DOOR, "do") XX(BLOCK, "bd") \
|
||||
@ -339,14 +295,6 @@ enum { LS(XX) LS_COUNT };
|
||||
static const char *g_ls_colors[] = {LS(XX)};
|
||||
#undef XX
|
||||
|
||||
struct stringcaseless {
|
||||
bool operator () (const string &a, const string &b) const {
|
||||
const auto &c = locale::classic();
|
||||
return lexicographical_compare (begin (a), end (a), begin (b), end (b),
|
||||
[&](char m, char n) { return tolower (m, c) < tolower (n, c); });
|
||||
}
|
||||
};
|
||||
|
||||
static struct {
|
||||
string cwd; ///< Current working directory
|
||||
string start_dir; ///< Starting directory
|
||||
@ -370,9 +318,6 @@ static struct {
|
||||
|
||||
map<int, chtype> ls_colors; ///< LS_COLORS decoded
|
||||
map<string, chtype> ls_exts; ///< LS_COLORS file extensions
|
||||
bool ls_symlink_as_target; ///< ln=target in dircolors
|
||||
|
||||
map<string, wint_t, stringcaseless> key_names;
|
||||
|
||||
// Refreshed by reload():
|
||||
|
||||
@ -381,34 +326,17 @@ static struct {
|
||||
struct tm now; ///< Current local time for display
|
||||
} g;
|
||||
|
||||
// The coloring logic has been more or less exactly copied from GNU ls,
|
||||
// simplified and rewritten to reflect local implementation specifics
|
||||
fun ls_is_colored (int type) -> bool {
|
||||
auto i = g.ls_colors.find (type);
|
||||
return i != g.ls_colors.end () && i->second != 0;
|
||||
}
|
||||
|
||||
fun ls_format (const entry &e, bool for_target) -> chtype {
|
||||
fun ls_format (const string &filename, const struct stat &info) -> chtype {
|
||||
int type = LS_ORPHAN;
|
||||
auto set = [&](int t) { if (ls_is_colored (t)) type = t; };
|
||||
|
||||
const auto &name = for_target
|
||||
? e.target_path : e.filename;
|
||||
const auto &info =
|
||||
(for_target || (g.ls_symlink_as_target && e.target_info.st_mode))
|
||||
? e.target_info : e.info;
|
||||
|
||||
if (for_target && info.st_mode == 0) {
|
||||
// This differs from GNU ls: we use ORPHAN when MISSING is not set,
|
||||
// but GNU ls colors by dirent::d_type
|
||||
set (LS_MISSING);
|
||||
} else if (S_ISREG (info.st_mode)) {
|
||||
auto set = [&](int t) { if (g.ls_colors.count (t)) type = t; };
|
||||
// TODO: LS_MISSING if available and this is a missing symlink target
|
||||
if (S_ISREG (info.st_mode)) {
|
||||
type = LS_FILE;
|
||||
if (info.st_nlink > 1)
|
||||
set (LS_MULTIHARDLINK);
|
||||
if ((info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
|
||||
set (LS_EXECUTABLE);
|
||||
if (lgetxattr (name.c_str (), "security.capability", NULL, 0) >= 0)
|
||||
if (lgetxattr (filename.c_str (), "security.capability", NULL, 0) >= 0)
|
||||
set (LS_CAPABILITY);
|
||||
if ((info.st_mode & S_ISGID))
|
||||
set (LS_SETGID);
|
||||
@ -423,10 +351,10 @@ fun ls_format (const entry &e, bool for_target) -> chtype {
|
||||
if ((info.st_mode & S_ISVTX) && (info.st_mode & S_IWOTH))
|
||||
set (LS_STICKY_OTHER_WRITABLE);
|
||||
} else if (S_ISLNK (info.st_mode)) {
|
||||
// TODO: LS_ORPHAN when symlink target is missing and either
|
||||
// a/ "li" is "target", or
|
||||
// b/ LS_ORPHAN is available
|
||||
type = LS_SYMLINK;
|
||||
if (!e.target_info.st_mode
|
||||
&& (ls_is_colored (LS_ORPHAN) || g.ls_symlink_as_target))
|
||||
type = LS_ORPHAN;
|
||||
} else if (S_ISFIFO (info.st_mode)) {
|
||||
type = LS_FIFO;
|
||||
} else if (S_ISSOCK (info.st_mode)) {
|
||||
@ -442,9 +370,9 @@ fun ls_format (const entry &e, bool for_target) -> chtype {
|
||||
if (x != g.ls_colors.end ())
|
||||
format = x->second;
|
||||
|
||||
auto dot = name.find_last_of ('.');
|
||||
auto dot = filename.find_last_of ('.');
|
||||
if (dot != string::npos && type == LS_FILE) {
|
||||
const auto x = g.ls_exts.find (name.substr (++dot));
|
||||
const auto x = g.ls_exts.find (filename.substr (++dot));
|
||||
if (x != g.ls_exts.end ())
|
||||
format = x->second;
|
||||
}
|
||||
@ -467,8 +395,8 @@ fun make_entry (const struct dirent *f) -> entry {
|
||||
e.cols[entry::USER] = e.cols[entry::GROUP] =
|
||||
e.cols[entry::SIZE] = e.cols[entry::MTIME] = apply_attrs (L"?", 0);
|
||||
|
||||
e.cols[entry::FILENAME] =
|
||||
apply_attrs (to_wide (e.filename), ls_format (e, false));
|
||||
auto format = ls_format (e.filename, info);
|
||||
e.cols[entry::FILENAME] = apply_attrs (to_wide (e.filename), format);
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -479,8 +407,7 @@ fun make_entry (const struct dirent *f) -> entry {
|
||||
e.target_path = "?";
|
||||
} else {
|
||||
e.target_path = buf;
|
||||
// If a symlink links to another symlink, we follow all the way
|
||||
(void) stat (buf, &e.target_info);
|
||||
(void) lstat (buf, &e.target_info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,10 +441,11 @@ fun make_entry (const struct dirent *f) -> entry {
|
||||
e.cols[entry::MTIME] = apply_attrs (to_wide (buf), 0);
|
||||
|
||||
auto &fn = e.cols[entry::FILENAME] =
|
||||
apply_attrs (to_wide (e.filename), ls_format (e, false));
|
||||
apply_attrs (to_wide (e.filename), ls_format (e.filename, info));
|
||||
if (!e.target_path.empty ()) {
|
||||
fn.append (apply_attrs (to_wide (" -> "), 0));
|
||||
fn.append (apply_attrs (to_wide (e.target_path), ls_format (e, true)));
|
||||
fn.append (apply_attrs (to_wide (e.target_path),
|
||||
ls_format (e.target_path, e.target_info)));
|
||||
}
|
||||
return e;
|
||||
}
|
||||
@ -621,6 +549,30 @@ fun search (const wstring &needle) {
|
||||
g.cursor = best;
|
||||
}
|
||||
|
||||
fun handle_editor (wint_t c, bool is_char) {
|
||||
if (c == 27 || c == (CTRL L'g')) {
|
||||
g.editor_line.clear ();
|
||||
g.editor = 0;
|
||||
} else if (c == L'\r' || (!is_char && c == KEY_ENTER)) {
|
||||
if (g.editor == L'e') {
|
||||
auto mb = to_mb (g.editor_line);
|
||||
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
||||
reload ();
|
||||
}
|
||||
g.editor_line.clear ();
|
||||
g.editor = 0;
|
||||
} else if (is_char) {
|
||||
g.editor_line += c;
|
||||
if (g.editor == L'/'
|
||||
|| g.editor == L's')
|
||||
search (g.editor_line);
|
||||
} else if (c == KEY_BACKSPACE) {
|
||||
if (!g.editor_line.empty ())
|
||||
g.editor_line.erase (g.editor_line.length () - 1);
|
||||
} else
|
||||
beep ();
|
||||
}
|
||||
|
||||
fun change_dir (const string& path) {
|
||||
if (chdir (path.c_str ())) {
|
||||
beep ();
|
||||
@ -642,118 +594,97 @@ fun choose (const entry &entry) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fun handle_editor (wint_t c) {
|
||||
// FIXME: do not check editor actions by the prompt letter
|
||||
auto i = g_input_actions.find (c);
|
||||
switch (i == g_input_actions.end () ? ACTION_NONE : i->second) {
|
||||
case ACTION_INPUT_ABORT:
|
||||
g.editor_line.clear ();
|
||||
g.editor = 0;
|
||||
break;
|
||||
case ACTION_INPUT_CONFIRM:
|
||||
if (g.editor == L'e') {
|
||||
auto mb = to_mb (g.editor_line);
|
||||
rename (g.entries[g.cursor].filename.c_str (), mb.c_str ());
|
||||
reload ();
|
||||
}
|
||||
g.editor_line.clear ();
|
||||
g.editor = 0;
|
||||
break;
|
||||
case ACTION_INPUT_B_DELETE:
|
||||
if (!g.editor_line.empty ())
|
||||
g.editor_line.erase (g.editor_line.length () - 1);
|
||||
break;
|
||||
default:
|
||||
if (c & (ALT | SYM)) {
|
||||
beep ();
|
||||
} else {
|
||||
g.editor_line += c;
|
||||
if (g.editor == L'/'
|
||||
|| g.editor == L's')
|
||||
search (g.editor_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handle (wint_t c) -> bool {
|
||||
fun handle (wint_t c, bool is_char) -> bool {
|
||||
// If an editor is active, let it handle the key instead and eat it
|
||||
if (g.editor) {
|
||||
handle_editor (c);
|
||||
handle_editor (c, is_char);
|
||||
c = WEOF;
|
||||
}
|
||||
|
||||
// Translate the Alt key into a bit outside the range of Unicode
|
||||
enum { ALT = 1 << 24 };
|
||||
if (c == 27) {
|
||||
if (get_wch (&c) == ERR) {
|
||||
beep ();
|
||||
return true;
|
||||
}
|
||||
c |= ALT;
|
||||
}
|
||||
|
||||
const auto ¤t = g.entries[g.cursor];
|
||||
auto i = g_normal_actions.find (c);
|
||||
switch (i == g_normal_actions.end () ? ACTION_NONE : i->second) {
|
||||
case ACTION_CHOOSE_FULL:
|
||||
switch (c) {
|
||||
case ALT | L'\r':
|
||||
case ALT | KEY_ENTER:
|
||||
g.chosen_full = true;
|
||||
g.chosen = current.filename;
|
||||
return false;
|
||||
case ACTION_CHOOSE:
|
||||
case L'\r':
|
||||
case KEY_ENTER:
|
||||
if (choose (current))
|
||||
break;
|
||||
return false;
|
||||
case ACTION_QUIT:
|
||||
|
||||
// M-o ought to be the same shortcut the navigator is launched with
|
||||
case ALT | L'o':
|
||||
case L'q':
|
||||
return false;
|
||||
|
||||
case ACTION_UP:
|
||||
case L'k': case CTRL L'p': case KEY_UP:
|
||||
g.cursor--;
|
||||
break;
|
||||
case ACTION_DOWN:
|
||||
case L'j': case CTRL L'n': case KEY_DOWN:
|
||||
g.cursor++;
|
||||
break;
|
||||
case ACTION_TOP:
|
||||
case L'g': case ALT | L'<': case KEY_HOME:
|
||||
g.cursor = 0;
|
||||
break;
|
||||
case ACTION_BOTTOM:
|
||||
case L'G': case ALT | L'>': case KEY_END:
|
||||
g.cursor = int (g.entries.size ()) - 1;
|
||||
break;
|
||||
|
||||
case ACTION_PAGE_PREVIOUS:
|
||||
g.cursor -= LINES;
|
||||
break;
|
||||
case ACTION_PAGE_NEXT:
|
||||
g.cursor += LINES;
|
||||
break;
|
||||
case ACTION_SCROLL_DOWN:
|
||||
g.offset++;
|
||||
break;
|
||||
case ACTION_SCROLL_UP:
|
||||
g.offset--;
|
||||
break;
|
||||
case KEY_PPAGE: g.cursor -= LINES; break;
|
||||
case KEY_NPAGE: g.cursor += LINES; break;
|
||||
|
||||
case ACTION_GO_START:
|
||||
case CTRL L'e': g.offset++; break;
|
||||
case CTRL L'y': g.offset--; break;
|
||||
|
||||
case '&':
|
||||
change_dir (g.start_dir);
|
||||
break;
|
||||
case ACTION_GO_HOME:
|
||||
case '~':
|
||||
if (const auto *home = getenv ("HOME"))
|
||||
change_dir (home);
|
||||
else if (const auto *pw = getpwuid (getuid ()))
|
||||
change_dir (pw->pw_dir);
|
||||
break;
|
||||
|
||||
case ACTION_SEARCH:
|
||||
g.editor = c;
|
||||
break;
|
||||
case ACTION_RENAME_PREFILL:
|
||||
g.editor_line = to_wide (current.filename);
|
||||
// Fall-through
|
||||
case ACTION_RENAME:
|
||||
g.editor = c & ~ALT;
|
||||
break;
|
||||
|
||||
case ACTION_TOGGLE_FULL:
|
||||
case L't':
|
||||
case ALT | L't':
|
||||
g.full_view = !g.full_view;
|
||||
break;
|
||||
case ACTION_REDRAW:
|
||||
|
||||
case ALT | L'e':
|
||||
g.editor_line = to_wide (current.filename);
|
||||
// Fall-through
|
||||
case L'e':
|
||||
g.editor = c & ~ALT;
|
||||
break;
|
||||
case L'/':
|
||||
case L's':
|
||||
g.editor = c;
|
||||
break;
|
||||
|
||||
case CTRL L'L':
|
||||
clear ();
|
||||
break;
|
||||
case ACTION_RELOAD:
|
||||
case L'r':
|
||||
reload ();
|
||||
break;
|
||||
case KEY_RESIZE:
|
||||
case WEOF:
|
||||
break;
|
||||
default:
|
||||
if (c != KEY (RESIZE) && c != WEOF)
|
||||
beep ();
|
||||
beep ();
|
||||
}
|
||||
g.cursor = max (g.cursor, 0);
|
||||
g.cursor = min (g.cursor, int (g.entries.size ()) - 1);
|
||||
@ -836,11 +767,11 @@ fun load_ls_colors (vector<string> colors) {
|
||||
auto equal = pair.find ('=');
|
||||
if (equal == string::npos)
|
||||
continue;
|
||||
auto key = pair.substr (0, equal), value = pair.substr (equal + 1);
|
||||
if (key != g_ls_colors[LS_SYMLINK]
|
||||
|| !(g.ls_symlink_as_target = value == "target"))
|
||||
attrs[key] = decode_ansi_sgr (split (value, ";"));
|
||||
attrs[pair.substr (0, equal)] =
|
||||
decode_ansi_sgr (split (pair.substr (equal + 1), ";"));
|
||||
}
|
||||
|
||||
// `LINK target` i.e. `ln=target` is not supported now
|
||||
for (int i = 0; i < LS_COUNT; i++) {
|
||||
auto m = attrs.find (g_ls_colors[i]);
|
||||
if (m != attrs.end ())
|
||||
@ -852,17 +783,15 @@ fun load_ls_colors (vector<string> colors) {
|
||||
}
|
||||
}
|
||||
|
||||
fun load_colors () {
|
||||
// Bail out on dumb terminals, there's not much one can do about them
|
||||
if (!has_colors () || start_color () == ERR || use_default_colors () == ERR)
|
||||
return;
|
||||
if (const char *colors = getenv ("LS_COLORS"))
|
||||
load_ls_colors (split (colors, ":"));
|
||||
|
||||
fun load_configuration () {
|
||||
auto config = xdg_config_find ("/" PROJECT_NAME "/look");
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
// Bail out on dumb terminals, there's not much one can do about them
|
||||
if (!has_colors () || start_color () == ERR || use_default_colors () == ERR)
|
||||
return;
|
||||
|
||||
string line;
|
||||
while (getline (*config, line)) {
|
||||
auto tokens = split (line, " ");
|
||||
@ -873,117 +802,9 @@ fun load_colors () {
|
||||
if (name == g.attr_names[i])
|
||||
g.attrs[i] = decode_attrs (tokens);
|
||||
}
|
||||
}
|
||||
|
||||
fun read_key (wint_t &c) -> bool {
|
||||
int res = get_wch (&c);
|
||||
if (res == ERR)
|
||||
return false;
|
||||
|
||||
wint_t metafied{};
|
||||
if (c == 27 && (res = get_wch (&metafied)) != ERR)
|
||||
c = ALT | metafied;
|
||||
if (res == KEY_CODE_YES)
|
||||
c |= SYM;
|
||||
return true;
|
||||
}
|
||||
|
||||
fun parse_key (const string &key_name) -> wint_t {
|
||||
wint_t c{};
|
||||
auto p = key_name.c_str ();
|
||||
if (!strncmp (p, "M-", 2)) {
|
||||
c |= ALT;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
if (!strncmp (p, "C-", 2)) {
|
||||
p += 2;
|
||||
if (*p < 32) {
|
||||
cerr << "bindings: invalid combination: " << key_name << endl;
|
||||
return WEOF;
|
||||
}
|
||||
c |= CTRL *p;
|
||||
p += 1;
|
||||
} else if (g.key_names.count (p)) {
|
||||
c |= g.key_names.at (p);
|
||||
p += strlen (p);
|
||||
} else {
|
||||
wchar_t w; mbstate_t mb {};
|
||||
auto len = strlen (p) + 1, res = mbrtowc (&w, p, len, &mb);
|
||||
if (res == 0) {
|
||||
cerr << "bindings: missing key name: " << key_name << endl;
|
||||
return WEOF;
|
||||
}
|
||||
if (res == size_t (-1) || res == size_t (-2)) {
|
||||
cerr << "bindings: invalid encoding: " << key_name << endl;
|
||||
return WEOF;
|
||||
}
|
||||
c |= w;
|
||||
p += res;
|
||||
}
|
||||
if (*p) {
|
||||
cerr << "key name has unparsable trailing part: " << key_name << endl;
|
||||
return WEOF;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
fun load_bindings () {
|
||||
g.key_names["space"] = ' ';
|
||||
for (int kc = KEY_MIN; kc < KEY_MAX; kc++) {
|
||||
const char *name = keyname (kc);
|
||||
if (!name)
|
||||
continue;
|
||||
if (!strncmp (name, "KEY_", 4))
|
||||
name += 4;
|
||||
string filtered;
|
||||
for (; *name; name++) {
|
||||
if (*name != '(' && *name != ')')
|
||||
filtered += *name;
|
||||
}
|
||||
g.key_names[filtered] = kc;
|
||||
}
|
||||
|
||||
auto config = xdg_config_find ("/" PROJECT_NAME "/bindings");
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
// Stringization in the preprocessor is a bit limited, we want lisp-case
|
||||
map<string, action> actions;
|
||||
int a;
|
||||
for (auto p : g_action_names) {
|
||||
string name;
|
||||
for (; *p; p++)
|
||||
name += *p == '_' ? '-' : *p + 'a' - 'A';
|
||||
actions[name] = action (a++);
|
||||
}
|
||||
|
||||
string line;
|
||||
while (getline (*config, line)) {
|
||||
auto tokens = split (line, " ");
|
||||
if (tokens.empty () || line.front () == '#')
|
||||
continue;
|
||||
if (tokens.size () < 3) {
|
||||
cerr << "bindings: expected: context binding action";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto context = tokens[0], key_name = tokens[1], action = tokens[2];
|
||||
auto m = g_binding_contexts.find (context);
|
||||
if (m == g_binding_contexts.end ()) {
|
||||
cerr << "bindings: invalid context: " << context << endl;
|
||||
continue;
|
||||
}
|
||||
wint_t c = parse_key (key_name);
|
||||
if (c == WEOF)
|
||||
continue;
|
||||
auto i = actions.find (action);
|
||||
if (i == actions.end ()) {
|
||||
cerr << "bindings: invalid action: " << action << endl;
|
||||
continue;
|
||||
}
|
||||
(*m->second)[c] = i->second;
|
||||
}
|
||||
if (const char *colors = getenv ("LS_COLORS"))
|
||||
load_ls_colors (split (colors, ":"));
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
@ -1007,14 +828,12 @@ int main (int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
locale::global (locale (""));
|
||||
load_bindings ();
|
||||
|
||||
if (!initscr () || cbreak () == ERR || noecho () == ERR || nonl () == ERR) {
|
||||
cerr << "cannot initialize screen" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
load_colors ();
|
||||
load_configuration ();
|
||||
reload ();
|
||||
g.start_dir = g.cwd;
|
||||
update ();
|
||||
@ -1023,13 +842,17 @@ int main (int argc, char *argv[]) {
|
||||
// which would worsen start-up flickering
|
||||
if (halfdelay (1) == ERR || keypad (stdscr, TRUE) == ERR) {
|
||||
endwin ();
|
||||
cerr << "cannot configure input" << endl;
|
||||
cerr << "cannot initialize screen" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
wint_t c;
|
||||
while (!read_key (c) || handle (c))
|
||||
while (1) {
|
||||
inotify_check ();
|
||||
int res = get_wch (&c);
|
||||
if (res != ERR && !handle (c, res == OK))
|
||||
break;
|
||||
}
|
||||
endwin ();
|
||||
|
||||
// Presumably it is going to end up as an argument, so quote it
|
||||
|
Loading…
x
Reference in New Issue
Block a user