xaizek / pipedial (License: GPLv3 only) (since 2019-01-08)
One more tool for selecting something in console.
Commit 450da964de718cfde4910da573275d3ebf7c2198

Switch to using libvle as a submodule
Author: xaizek
Author date (UTC): 2019-04-21 14:27
Committer name: xaizek
Committer date (UTC): 2019-04-21 14:27
Parent(s): e215c646d1c13db15403a82ab826221be8ce0d1a
Signing key: 99DC5E4DB05F6BE2
Tree: 6bf5aeef7f6330e5ffbdbe51fe5f539a73995a93
File Lines added Lines deleted
.gitmodules 3 0
libs/vle 1 0
libs/vle/KeyDispatcher.cpp 0 63
libs/vle/KeyDispatcher.hpp 0 58
libs/vle/Mode.cpp 0 52
libs/vle/Mode.hpp 0 86
libs/vle/Modes.cpp 0 120
libs/vle/Modes.hpp 0 68
libs/vle/compat/reallocarray.c 0 46
libs/vle/compat/reallocarray.h 0 31
libs/vle/engine/keys.c 0 1404
libs/vle/engine/keys.h 0 217
libs/vle/engine/mode.c 0 59
libs/vle/engine/mode.h 0 77
libs/vle/utils/macros.h 0 157
libs/vle/utils/str.c 0 70
libs/vle/utils/str.h 0 53
File .gitmodules changed (mode: 100644) (index 4b15847..a278fb8)
4 4 [submodule "libs/cursedrl"] [submodule "libs/cursedrl"]
5 5 path = libs/cursedrl path = libs/cursedrl
6 6 url = https://code.reversed.top/user/xaizek/libcursedrl url = https://code.reversed.top/user/xaizek/libcursedrl
7 [submodule "libs/vle"]
8 path = libs/vle
9 url = https://code.reversed.top/user/xaizek/libvle
File libs/vle added (mode: 160000) (index 0000000..32bc9c9)
1 Subproject commit 32bc9c91577dc939193d80455d5413838783e262
File libs/vle/KeyDispatcher.cpp deleted (index 30aacda..0000000)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2019 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #include "KeyDispatcher.hpp"
20
21 #include <cassert>
22 #include <cstddef>
23
24 #include <string>
25
26 #include "engine/keys.h"
27
28 using namespace vle;
29
30 KeyDispatcher::KeyDispatcher() : lastResult(0)
31 { }
32
33 void
34 KeyDispatcher::dispatch(wchar_t key)
35 {
36 buffer.push_back(key);
37
38 std::size_t counter = vle_keys_counter();
39 lastResult = vle_keys_exec(buffer.c_str());
40
41 std::size_t consumedCount = vle_keys_counter() - counter;
42 if (consumedCount > 0) {
43 assert(consumedCount <= buffer.size());
44 buffer.erase(0, consumedCount);
45 }
46
47 if (lastResult == KEYS_UNKNOWN) {
48 reset();
49 }
50 }
51
52 std::wstring
53 KeyDispatcher::getPendingInput() const
54 {
55 return buffer;
56 }
57
58 void
59 KeyDispatcher::reset()
60 {
61 buffer.clear();
62 lastResult = 0;
63 }
File libs/vle/KeyDispatcher.hpp deleted (index 52c26ba..0000000)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2019 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #ifndef LIBVLE__KEYDISPATCHER_HPP__
20 #define LIBVLE__KEYDISPATCHER_HPP__
21
22 #include <string>
23
24 namespace vle {
25
26 // Dispatches input events and manages current input state.
27 class KeyDispatcher
28 {
29 public:
30 // Setups an empty state.
31 KeyDispatcher();
32
33 KeyDispatcher(const KeyDispatcher &rhs) = delete;
34 KeyDispatcher(KeyDispatcher &&rhs) = delete;
35 KeyDispatcher & operator=(const KeyDispatcher &rhs) = delete;
36 KeyDispatcher & operator=(KeyDispatcher &&rhs) = delete;
37
38 public:
39 // Processes the key updating state and triggering matched shortcuts of
40 // current mode.
41 void dispatch(wchar_t key);
42
43 // Retrieves current state of the input buffer that has not yet been
44 // processed.
45 std::wstring getPendingInput() const;
46
47 // Resets to an empty state.
48 void reset();
49
50 private:
51 std::wstring buffer; // Current input sequence.
52 int lastResult; // Result of the last attempt to process input sequence
53 // or 0.
54 };
55
56 }
57
58 #endif // LIBVLE__KEYDISPATCHER_HPP__
File libs/vle/Mode.cpp deleted (index 88d58b7..0000000)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2019 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #include "Mode.hpp"
20
21 #include <deque>
22 #include <stdexcept>
23 #include <utility>
24
25 using namespace vle;
26
27 Shortcut::Shortcut(std::wstring shortcut, std::function<void(void)> handler,
28 std::string descr)
29 : shortcut(std::move(shortcut)), descr(std::move(descr))
30 {
31 this->handler = [handler](int /*count*/) {
32 handler();
33 };
34 }
35
36 Shortcut::Shortcut(std::wstring shortcut, std::function<void(int)> handler,
37 std::string descr)
38 : shortcut(std::move(shortcut)), handler(std::move(handler)),
39 descr(std::move(descr))
40 { }
41
42 Mode::Mode(std::string id) : id(std::move(id))
43 { }
44
45 void
46 Mode::addShortcut(Shortcut shortcut)
47 {
48 if (shortcut.shortcut.length() > 4U) {
49 throw std::invalid_argument("Shortcut value is too long");
50 }
51 shortcuts.emplace_back(std::move(shortcut));
52 }
File libs/vle/Mode.hpp deleted (index 21322f0..0000000)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2019 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #ifndef LIBVLE__MODE_HPP__
20 #define LIBVLE__MODE_HPP__
21
22 #include <deque>
23 #include <functional>
24 #include <string>
25
26 namespace vle {
27
28 // Single shortcut.
29 struct Shortcut
30 {
31 // Canonical (the most complete) type of handler. `count` is negative when
32 // it's not available.
33 using handler_t = void(int count);
34
35 std::wstring shortcut; // Shortcut itself.
36 std::function<handler_t> handler; // Handler to be called on match.
37 std::string descr; // Description of the shortcut.
38
39 // Creates a shortcut that doesn't use count.
40 Shortcut(std::wstring shortcut, std::function<void(void)> handler,
41 std::string descr);
42 // Creates a shortcut that uses count.
43 Shortcut(std::wstring shortcut, std::function<void(int)> handler,
44 std::string descr);
45 };
46
47 // Contains mode configuration.
48 class Mode
49 {
50 public:
51 // Initializes empty mode with the specified id.
52 explicit Mode(std::string id);
53
54 Mode(const Mode &rhs) = delete;
55 Mode(Mode &&rhs) = default;
56 Mode & operator=(const Mode &rhs) = delete;
57 Mode & operator=(Mode &&rhs) = default;
58
59 public:
60 // Retrieves id of the mode.
61 const std::string & getId() const
62 { return id; }
63
64 // Sets whether count is used by shortcuts.
65 void setUsesCount(bool uses)
66 { useCount = uses; }
67 // Checks whether count is used by shortcuts.
68 bool usesCount() const
69 { return useCount; }
70
71 // Registers a shortcut. Throws `std::invalid_argument` if shortcut is too
72 // long.
73 void addShortcut(Shortcut shortcut);
74 // Retrieves shortcuts of this mode.
75 const std::deque<Shortcut> & getShortcuts() const
76 { return shortcuts; }
77
78 private:
79 std::string id; // Identifier of the mode.
80 std::deque<Shortcut> shortcuts; // Shortcuts of this mode.
81 bool useCount = false; // Whether count is used by shortcuts.
82 };
83
84 }
85
86 #endif // LIBVLE__MODE_HPP__
File libs/vle/Modes.cpp deleted (index 253b647..0000000)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2019 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #include "Modes.hpp"
20
21 #include <cassert>
22
23 #include <iterator>
24 #include <stdexcept>
25 #include <utility>
26 #include <vector>
27
28 #include "engine/keys.h"
29 #include "engine/mode.h"
30 #include "Mode.hpp"
31
32 using namespace vle;
33
34 // Dispatches handler invocations from the C layer.
35 static inline void
36 keyHandler(key_info_t key_info, keys_info_t */*keys_info*/)
37 {
38 auto handler = static_cast<std::function<Shortcut::handler_t> *>(
39 key_info.context
40 );
41 (*handler)(key_info.count);
42 }
43
44 Modes::Modes() : modeKeyFlags(1)
45 {
46 auto silence = [](int /*more*/) { };
47 vle_keys_init(1, &modeKeyFlags[0], silence);
48 }
49
50 Modes::~Modes()
51 {
52 reset();
53 }
54
55 void
56 Modes::setModes(std::vector<Mode> modesInfo)
57 {
58 if (modesInfo.empty()) {
59 throw std::invalid_argument("There must be at least one mode");
60 }
61
62 reset();
63 init(std::move(modesInfo));
64 }
65
66 void
67 Modes::reset()
68 {
69 vle_keys_reset();
70 modeKeyFlags.clear();
71 modes.clear();
72 }
73
74 void
75 Modes::init(std::vector<Mode> &&modesInfo)
76 {
77 for (Mode &mode : modesInfo) {
78 if (!modes.emplace(mode.getId(), std::move(mode)).second) {
79 throw std::invalid_argument("Duplicated mode id:" + mode.getId());
80 }
81 }
82
83 modeKeyFlags.reserve(modes.size());
84 for (const auto &entry : modes) {
85 modeKeyFlags.push_back(entry.second.usesCount() ? MF_USES_COUNT : 0);
86 }
87
88 auto silence = [](int /*more*/) { };
89 vle_keys_init(modes.size(), &modeKeyFlags[0], silence);
90
91 int modeId = 0;
92 for (const auto &entry : modes) {
93 std::vector<keys_add_info_t> keys;
94 for (const Shortcut &sh : entry.second.getShortcuts()) {
95 keys_add_info_t addInfo = {};
96 wcscpy(const_cast<wchar_t *>(addInfo.keys), sh.shortcut.c_str());
97 addInfo.info.data.handler = &keyHandler;
98 addInfo.info.descr = sh.descr.c_str();
99 addInfo.info.context = const_cast<void *>(
100 static_cast<const void *>(&sh.handler)
101 );
102 keys.push_back(addInfo);
103 }
104
105 int ret_code = vle_keys_add(&keys[0], keys.size(), modeId++);
106 assert(ret_code == 0 && "Failed to add builtin keys");
107 (void)ret_code;
108 }
109 }
110
111 void
112 Modes::switchTo(const std::string &modeId)
113 {
114 auto it = modes.find(modeId);
115 if (it == modes.end()) {
116 throw std::invalid_argument("Unknown mode id:" + modeId);
117 }
118
119 vle_mode_set(std::distance(modes.begin(), it), VMT_PRIMARY);
120 }
File libs/vle/Modes.hpp deleted (index ebabafc..0000000)
1 // libvle -- library for building Vim-like applications
2 // Copyright (C) 2019 xaizek <xaizek@posteo.net>
3 //
4 // This file is part of libvle.
5 //
6 // libvle is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // libvle is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with libvle. If not, see <https://www.gnu.org/licenses/>.
18
19 #ifndef LIBVLE__MODES_HPP__
20 #define LIBVLE__MODES_HPP__
21
22 #include <string>
23 #include <unordered_map>
24 #include <vector>
25
26 #include "Mode.hpp"
27
28 namespace vle {
29
30 // Handles initialization and deinitialization of modes.
31 class Modes
32 {
33 public:
34 // Initializes no modes.
35 Modes();
36 // Deinitializes all modes.
37 ~Modes();
38
39 Modes(const Modes &rhs) = delete;
40 Modes(Modes &&rhs) = delete;
41 Modes & operator=(const Modes &rhs) = delete;
42 Modes & operator=(Modes &&rhs) = delete;
43
44 public:
45 // Initializes modes. Throws `std::invalid_argument` on duplicated mode ids
46 // or empty list of modes.
47 void setModes(std::vector<Mode> modesInfo);
48 // Switches to the mode specified by its id. Throws `std::invalid_argument`
49 // on unknown mode id.
50 void switchTo(const std::string &modeId);
51
52 private:
53 // Deinitializes all modes.
54 void reset();
55 // Initializes modes. Throws `std::invalid_argument` on duplicated mode
56 // ids.
57 void init(std::vector<Mode> &&modesInfo);
58
59 private:
60 // Data that describes modes.
61 std::unordered_map<std::string, Mode> modes;
62 // Key processing flags of each mode.
63 std::vector<int> modeKeyFlags;
64 };
65
66 }
67
68 #endif // LIBVLE__MODES_HPP__
File libs/vle/compat/reallocarray.c deleted (index 3372379..0000000)
1 /*
2 * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/types.h>
18
19 #include <errno.h> /* errno EINVAL ENOMEM */
20 #include <stddef.h> /* NULL size_t */
21 #include <stdint.h> /* SIZE_MAX */
22 #include <stdlib.h> /* realloc() */
23
24 /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
25 * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */
26 #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
27
28 void *
29 reallocarray(void *optr, size_t nmemb, size_t size)
30 {
31 if(nmemb == 0U)
32 {
33 errno = EINVAL;
34 return NULL;
35 }
36 if((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
37 nmemb > 0U && SIZE_MAX/nmemb < size)
38 {
39 errno = ENOMEM;
40 return NULL;
41 }
42 return realloc(optr, size*nmemb);
43 }
44
45 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
46 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/compat/reallocarray.h deleted (index bc7fc6b..0000000)
1 /* vifm
2 * Copyright (C) 2015 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #ifndef VIFM__COMPAT__REALLOCARRAY_H__
20 #define VIFM__COMPAT__REALLOCARRAY_H__
21
22 #include <stddef.h> /* size_t */
23
24 /* Almost the same as realloc(), but with a different way of specifying size
25 * and checks for integer overflow in the calculation of nmemb * size. */
26 void * reallocarray(void *optr, size_t nmemb, size_t size);
27
28 #endif /* VIFM__COMPAT__REALLOCARRAY_H__ */
29
30 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
31 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/engine/keys.c deleted (index caec4f4..0000000)
1 /* vifm
2 * Copyright (C) 2011 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 /* WARNING: code implementing execution of keys deserves a prize in nomination
20 * "insanely perplexed control flow". You have been warned.
21 * TODO: seriously, indirect recursion and jumps between routines is a horrible
22 * mess, even not touching tree representation this should be possible to
23 * rewrite with master loop that simply goes through the string in
24 * left-to-right order. */
25
26 #include "keys.h"
27
28 #include <assert.h> /* assert() */
29 #include <ctype.h>
30 #include <limits.h> /* INT_MAX */
31 #include <stddef.h> /* NULL wchar_t */
32 #include <stdio.h> /* snprintf() */
33 #include <stdlib.h>
34 #include <string.h>
35 #include <wctype.h> /* iswdigit() */
36 #include <wchar.h> /* wcscat() wcslen() */
37
38 #include "../utils/macros.h"
39 #include "../utils/str.h"
40 #include "mode.h"
41
42 /* Type of key chunk. */
43 typedef enum
44 {
45 BUILTIN_WAIT_POINT, /* Infinite wait of next key press. */
46 BUILTIN_KEYS, /* Normal builtin key. */
47 BUILTIN_NIM_KEYS, /* NIM - number in the middle. */
48 USER_CMD, /* User mapping. */
49 }
50 KeyType;
51
52 typedef struct key_chunk_t
53 {
54 wchar_t key;
55 size_t children_count;
56 /* Number of current uses. To prevent stack overflow and manager lifetime. */
57 int enters;
58 /* General key type. */
59 KeyType type : 2;
60 /* Whether RHS should be treated as if there are no user mappings. */
61 unsigned int no_remap : 1;
62 /* Postpone free() call for proper lazy deletion. */
63 unsigned int deleted : 1;
64 /* Postpone UI updates until RHS is done executing. */
65 unsigned int silent : 1;
66 /* Do not use short wait to resolve conflict against builtin mapping, do long
67 * wait instead. */
68 unsigned int wait : 1;
69 key_conf_t conf;
70 struct key_chunk_t *child;
71 struct key_chunk_t *parent;
72 struct key_chunk_t *prev, *next;
73 }
74 key_chunk_t;
75
76 /* Callback of traverse_children(). */
77 typedef void (*traverse_func)(const key_chunk_t *chunk, const wchar_t lhs[],
78 void *arg);
79
80 static key_chunk_t *builtin_cmds_root;
81 static key_chunk_t *selectors_root;
82 static key_chunk_t *user_cmds_root;
83 static int max_modes;
84 static int *mode_flags;
85 static default_handler *def_handlers;
86 static size_t counter;
87 /* Main external functions enter recursion level. */
88 static size_t enters_counter;
89 /* Shows whether a mapping handler is being executed at the moment. */
90 static int inside_mapping;
91 /* User-provided callback for silencing UI. */
92 static vle_silence_func silence_ui;
93
94 static void free_forest(key_chunk_t *forest, size_t size);
95 static void free_tree(key_chunk_t *root);
96 static void free_chunk(key_chunk_t *chunk);
97 static int execute_keys_general_wrapper(const wchar_t keys[], int timed_out,
98 int mapped, int no_remap);
99 static int execute_keys_general(const wchar_t keys[], int timed_out, int mapped,
100 int no_remap);
101 static int dispatch_keys(const wchar_t keys[], keys_info_t *keys_info,
102 int no_remap, int prev_count);
103 static int dispatch_keys_at_root(const wchar_t keys[], keys_info_t *keys_info,
104 key_chunk_t *root, key_info_t key_info, int no_remap);
105 static int dispatch_selector(const wchar_t keys[], keys_info_t *keys_info,
106 key_info_t master_key_info, key_chunk_t *master_curr, int no_remap);
107 static int fill_key_info(const wchar_t **keys, key_info_t *key_info,
108 int prev_count, int *result);
109 static int contains_chain(key_chunk_t *root, const wchar_t *begin,
110 const wchar_t *end);
111 static int execute_next_keys(key_chunk_t *curr, const wchar_t keys[],
112 key_info_t *key_info, keys_info_t *keys_info, int has_duplicate,
113 int no_remap);
114 static int needs_waiting(const key_chunk_t *curr);
115 static int dispatch_key(key_info_t key_info, keys_info_t *keys_info,
116 key_chunk_t *curr, const wchar_t keys[]);
117 static int has_def_handler(void);
118 static default_handler def_handler(void);
119 static int execute_after_remapping(const wchar_t rhs[],
120 const wchar_t left_keys[], keys_info_t keys_info, key_info_t key_info,
121 key_chunk_t *curr);
122 static void enter_chunk(key_chunk_t *chunk);
123 static void leave_chunk(key_chunk_t *chunk);
124 static void init_keys_info(keys_info_t *keys_info, int mapped);
125 static const wchar_t * get_reg(const wchar_t *keys, int *reg);
126 static const wchar_t * get_count(const wchar_t keys[], int *count);
127 static int is_at_count(const wchar_t keys[]);
128 static int combine_counts(int count_a, int count_b);
129 static key_chunk_t * find_user_keys(const wchar_t *keys, int mode);
130 static int add_list_of_keys(key_chunk_t *root, keys_add_info_t cmds[],
131 size_t len);
132 static key_chunk_t * add_keys_inner(key_chunk_t *root, const wchar_t *keys);
133 static void list_chunk(const key_chunk_t *chunk, const wchar_t lhs[],
134 void *arg);
135 static void inc_counter(const keys_info_t *keys_info, size_t by);
136 static int is_recursive(void);
137 static int execute_mapping_handler(const key_conf_t *info,
138 key_info_t key_info, keys_info_t *keys_info);
139 static void pre_execute_mapping_handler(const keys_info_t *keys_info);
140 static void post_execute_mapping_handler(const keys_info_t *keys_info);
141 static void keys_suggest(const key_chunk_t *root, const wchar_t keys[],
142 const wchar_t prefix[], vle_keys_list_cb cb, int custom_only,
143 int fold_subkeys);
144 static void suggest_children(const key_chunk_t *chunk, const wchar_t prefix[],
145 vle_keys_list_cb cb, int fold_subkeys);
146 static void traverse_children(const key_chunk_t *chunk, const wchar_t prefix[],
147 traverse_func cb, void *param);
148 static void suggest_chunk(const key_chunk_t *chunk, const wchar_t lhs[],
149 void *arg);
150
151 void
152 vle_keys_init(int modes_count, int *key_mode_flags, vle_silence_func silence)
153 {
154 assert(key_mode_flags != NULL);
155 assert(modes_count > 0);
156 assert(silence != NULL);
157
158 max_modes = modes_count;
159 mode_flags = key_mode_flags;
160 silence_ui = silence;
161
162 builtin_cmds_root = calloc(modes_count, sizeof(*builtin_cmds_root));
163 assert(builtin_cmds_root != NULL);
164
165 user_cmds_root = calloc(modes_count, sizeof(*user_cmds_root));
166 assert(user_cmds_root != NULL);
167
168 selectors_root = calloc(modes_count, sizeof(*selectors_root));
169 assert(selectors_root != NULL);
170
171 def_handlers = calloc(modes_count, sizeof(*def_handlers));
172 assert(def_handlers != NULL);
173 }
174
175 void
176 vle_keys_reset(void)
177 {
178 free_forest(builtin_cmds_root, max_modes);
179 free_forest(user_cmds_root, max_modes);
180 free_forest(selectors_root, max_modes);
181
182 free(def_handlers);
183
184 builtin_cmds_root = NULL;
185 selectors_root = NULL;
186 user_cmds_root = NULL;
187 max_modes = 0;
188 mode_flags = NULL;
189 def_handlers = NULL;
190 }
191
192 void
193 vle_keys_user_clear(void)
194 {
195 free_forest(user_cmds_root, max_modes);
196
197 user_cmds_root = calloc(max_modes, sizeof(*user_cmds_root));
198 assert(user_cmds_root != NULL);
199 }
200
201 /* Releases array of trees of length size including trees. */
202 static void
203 free_forest(key_chunk_t *forest, size_t size)
204 {
205 size_t i;
206 for(i = 0; i < size; i++)
207 {
208 free_tree(&forest[i]);
209 }
210 free(forest);
211 }
212
213 static void
214 free_tree(key_chunk_t *root)
215 {
216 if(root->child != NULL)
217 {
218 free_tree(root->child);
219 free_chunk(root->child);
220 }
221
222 if(root->next != NULL)
223 {
224 free_tree(root->next);
225 free_chunk(root->next);
226 }
227
228 if(root->type == USER_CMD)
229 {
230 free(root->conf.data.cmd);
231 }
232 }
233
234 static void
235 free_chunk(key_chunk_t *chunk)
236 {
237 if(chunk->enters == 0)
238 {
239 free(chunk);
240 }
241 else
242 {
243 chunk->deleted = 1;
244 }
245 }
246
247 void
248 vle_keys_set_def_handler(int mode, default_handler handler)
249 {
250 def_handlers[mode] = handler;
251 }
252
253 /* This function should never be called from this module, only externally. */
254 int
255 vle_keys_exec(const wchar_t keys[])
256 {
257 return execute_keys_general_wrapper(keys, 0, 0, 0);
258 }
259
260 /* This function should never be called from this module, only externally. */
261 int
262 vle_keys_exec_no_remap(const wchar_t keys[])
263 {
264 return execute_keys_general_wrapper(keys, 0, 0, 1);
265 }
266
267 /* This function should never be called from this module, only externally. */
268 int
269 vle_keys_exec_timed_out(const wchar_t keys[])
270 {
271 return execute_keys_general_wrapper(keys, 1, 0, 0);
272 }
273
274 /* This function should never be called from this module, only externally. */
275 int
276 vle_keys_exec_timed_out_no_remap(const wchar_t keys[])
277 {
278 return execute_keys_general_wrapper(keys, 1, 0, 1);
279 }
280
281 static int
282 execute_keys_general_wrapper(const wchar_t keys[], int timed_out, int mapped,
283 int no_remap)
284 {
285 int result;
286
287 enters_counter++;
288 result = execute_keys_general(keys, timed_out, mapped, no_remap);
289 enters_counter--;
290
291 return result;
292 }
293
294 static int
295 execute_keys_general(const wchar_t keys[], int timed_out, int mapped,
296 int no_remap)
297 {
298 int result;
299 keys_info_t keys_info;
300
301 if(keys[0] == L'\0')
302 {
303 return KEYS_UNKNOWN;
304 }
305
306 init_keys_info(&keys_info, mapped);
307 keys_info.after_wait = timed_out;
308 result = dispatch_keys(keys, &keys_info, no_remap, NO_COUNT_GIVEN);
309 if(result == KEYS_UNKNOWN && def_handlers[vle_mode_get()] != NULL)
310 {
311 result = def_handlers[vle_mode_get()](keys[0]);
312 execute_keys_general(keys + 1, 0, mapped, no_remap);
313 }
314 return result;
315 }
316
317 /* Executes keys from the start of key sequence or at some offset in it.
318 * Returns error code. */
319 static int
320 dispatch_keys(const wchar_t keys[], keys_info_t *keys_info, int no_remap,
321 int prev_count)
322 {
323 const wchar_t *keys_start = keys;
324 key_info_t key_info;
325 int result;
326
327 if(fill_key_info(&keys, &key_info, prev_count, &result) != 0)
328 {
329 return result;
330 }
331
332 result = KEYS_UNKNOWN;
333 if(!no_remap)
334 {
335 result = dispatch_keys_at_root(keys, keys_info,
336 &user_cmds_root[vle_mode_get()], key_info, no_remap);
337 }
338
339 if(result == KEYS_UNKNOWN)
340 {
341 result = dispatch_keys_at_root(keys, keys_info,
342 &builtin_cmds_root[vle_mode_get()], key_info, no_remap);
343 }
344
345 if(!IS_KEYS_RET_CODE(result))
346 {
347 /* Take characters spent on count and register specification into
348 * account. */
349 inc_counter(keys_info, keys - keys_start);
350 }
351
352 return result;
353 }
354
355 /* Dispatches keys passed in using a tree of shortcuts registered in the root.
356 * Returns error code. */
357 static int
358 dispatch_keys_at_root(const wchar_t keys[], keys_info_t *keys_info,
359 key_chunk_t *root, key_info_t key_info, int no_remap)
360 {
361 key_chunk_t *curr;
362 const wchar_t *keys_start = keys;
363 int has_duplicate;
364 int result;
365
366 /* The loop finds longest match of the input (keys) among registered
367 * shortcuts. */
368 curr = root;
369 while(*keys != L'\0')
370 {
371 key_chunk_t *p;
372 int number_in_the_middle = 0;
373
374 for(p = curr->child; p != NULL && p->key < *keys; p = p->next)
375 {
376 if(p->type == BUILTIN_NIM_KEYS)
377 {
378 number_in_the_middle = 1;
379 }
380 }
381
382 if(p == NULL || p->key != *keys)
383 {
384 if(curr == root)
385 return KEYS_UNKNOWN;
386
387 for(; p != NULL; p = p->next)
388 {
389 if(p->type == BUILTIN_NIM_KEYS)
390 {
391 number_in_the_middle = 1;
392 }
393 }
394
395 if(curr->conf.followed != FOLLOWED_BY_NONE &&
396 (!number_in_the_middle || !is_at_count(keys)))
397 {
398 break;
399 }
400
401 if(number_in_the_middle)
402 {
403 int count;
404 const wchar_t *new_keys = get_count(keys, &count);
405 if(new_keys != keys)
406 {
407 key_info.count = combine_counts(key_info.count, count);
408 keys = new_keys;
409 continue;
410 }
411 }
412
413 if(curr->type == BUILTIN_WAIT_POINT)
414 {
415 return KEYS_UNKNOWN;
416 }
417
418 key_info.context = curr->conf.context;
419
420 has_duplicate = root == &user_cmds_root[vle_mode_get()] &&
421 contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys);
422 result = execute_next_keys(curr, curr->type == USER_CMD ? keys : L"",
423 &key_info, keys_info, has_duplicate, no_remap);
424 if(curr->type == USER_CMD)
425 return result;
426 if(IS_KEYS_RET_CODE(result))
427 {
428 return (result == KEYS_WAIT_SHORT) ? KEYS_UNKNOWN : result;
429 }
430 inc_counter(keys_info, keys - keys_start);
431 return execute_keys_general(keys, 0, keys_info->mapped, no_remap);
432 }
433 ++keys;
434 curr = p;
435 }
436
437 if(*keys == '\0' && curr->type != BUILTIN_WAIT_POINT &&
438 curr->children_count > 0 && curr->conf.data.handler != NULL &&
439 !keys_info->after_wait)
440 {
441 return KEYS_WAIT_SHORT;
442 }
443
444 key_info.context = curr->conf.context;
445
446 has_duplicate = root == &user_cmds_root[vle_mode_get()] &&
447 contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys);
448 result = execute_next_keys(curr, keys, &key_info, keys_info, has_duplicate,
449 no_remap);
450 if(!IS_KEYS_RET_CODE(result))
451 {
452 inc_counter(keys_info, keys - keys_start);
453 }
454 else if(*keys == '\0' && result == KEYS_UNKNOWN && curr->children_count > 0)
455 {
456 return keys_info->after_wait ? KEYS_UNKNOWN : KEYS_WAIT_SHORT;
457 }
458 return result;
459 }
460
461 /* Dispatches keys passed in as a selector followed by arbitrary other keys.
462 * Returns error code. */
463 static int
464 dispatch_selector(const wchar_t keys[], keys_info_t *keys_info,
465 key_info_t master_key_info, key_chunk_t *master_curr, int no_remap)
466 {
467 const wchar_t *keys_start = keys;
468 key_chunk_t *curr = &selectors_root[vle_mode_get()];
469 key_info_t key_info;
470 int result;
471
472 if(fill_key_info(&keys, &key_info, master_key_info.count, &result) != 0)
473 {
474 return result;
475 }
476
477 /* The loop finds longest match of the input (keys) among registered
478 * shortcuts. */
479 while(*keys != L'\0')
480 {
481 key_chunk_t *p;
482
483 for(p = curr->child; p != NULL && p->key < *keys; p = p->next)
484 {
485 /* Advance. */
486 }
487 if(p == NULL || p->key != *keys)
488 {
489 break;
490 }
491 ++keys;
492 curr = p;
493 }
494
495 /* Handle ambiguous selector. */
496 if(*keys == '\0' && curr->type != BUILTIN_WAIT_POINT &&
497 curr->children_count > 0 && curr->conf.data.handler != NULL &&
498 !keys_info->after_wait)
499 {
500 return KEYS_WAIT_SHORT;
501 }
502
503 key_info.context = curr->conf.context;
504
505 /* Execute the selector. */
506 if(curr->conf.followed == FOLLOWED_BY_MULTIKEY && keys[0] != L'\0')
507 {
508 const wchar_t mk[] = { keys[0], L'\0' };
509 result = execute_next_keys(curr, mk, &key_info, keys_info, 0, no_remap);
510 ++keys;
511 }
512 else
513 {
514 result = keys[0] == L'\0'
515 ? execute_next_keys(curr, L"", &key_info, keys_info, 0, no_remap)
516 : dispatch_key(key_info, keys_info, curr, L"");
517 }
518 if(IS_KEYS_RET_CODE(result))
519 {
520 return result;
521 }
522
523 /* We used this count in selector, so don't pass it to command. */
524 master_key_info.count = NO_COUNT_GIVEN;
525
526 /* Execute command that requested the selector. */
527 result = execute_mapping_handler(&master_curr->conf, master_key_info,
528 keys_info);
529 if(IS_KEYS_RET_CODE(result))
530 {
531 return result;
532 }
533
534 inc_counter(keys_info, keys - keys_start);
535
536 /* execute_keys_general() treats empty input as an error. */
537 if(keys[0] == L'\0')
538 {
539 return 0;
540 }
541 /* Process the rest of the line. */
542 return execute_keys_general(keys, keys_info->after_wait, 0, no_remap);
543 }
544
545 /* Fills *key_info advancing input if necessary. Returns zero on success,
546 * otherwise non-zero is returned and *result is set. */
547 static int
548 fill_key_info(const wchar_t **keys, key_info_t *key_info, int prev_count,
549 int *result)
550 {
551 *keys = get_reg(*keys, &key_info->reg);
552 if(*keys == NULL)
553 {
554 *result = KEYS_WAIT;
555 return 1;
556 }
557 if(key_info->reg == L'\x1b' || key_info->reg == L'\x03')
558 {
559 *result = 0;
560 return 1;
561 }
562
563 *keys = get_count(*keys, &key_info->count);
564 key_info->count = combine_counts(key_info->count, prev_count);
565 key_info->multi = L'\0';
566 return 0;
567 }
568
569 static int
570 contains_chain(key_chunk_t *root, const wchar_t *begin, const wchar_t *end)
571 {
572 key_chunk_t *curr;
573
574 if(begin == end)
575 return 0;
576
577 curr = root;
578 while(begin != end)
579 {
580 key_chunk_t *p;
581
582 p = curr->child;
583 while(p != NULL && p->key < *begin)
584 p = p->next;
585
586 if(p == NULL || p->key != *begin)
587 return 0;
588
589 begin++;
590 curr = p;
591 }
592 return (curr->conf.followed == FOLLOWED_BY_NONE &&
593 curr->type != BUILTIN_WAIT_POINT);
594 }
595
596 /* Handles the rest of the keys after first one has been determined (in curr).
597 * These can be: <nothing> (empty line), selector, multikey argument. Returns
598 * error code. */
599 static int
600 execute_next_keys(key_chunk_t *curr, const wchar_t keys[], key_info_t *key_info,
601 keys_info_t *keys_info, int has_duplicate, int no_remap)
602 {
603 const key_conf_t *const conf = &curr->conf;
604
605 if(*keys == L'\0')
606 {
607 int wait_point = (curr->type == BUILTIN_WAIT_POINT);
608 wait_point = wait_point || (curr->type == USER_CMD &&
609 conf->followed != FOLLOWED_BY_NONE);
610
611 if(wait_point)
612 {
613 if(!keys_info->after_wait)
614 {
615 if(needs_waiting(curr))
616 {
617 /* Wait flag on a user mapping should turn short wait into indefinite
618 * wait when there is a conflict with builtin mapping, pretending
619 * that there is no such conflict is enough to get the effect. */
620 has_duplicate = 0;
621 }
622 const int with_input = (mode_flags[vle_mode_get()] & MF_USES_INPUT);
623 return (with_input || has_duplicate) ? KEYS_WAIT_SHORT : KEYS_WAIT;
624 }
625 }
626 else if(conf->data.handler == NULL || conf->followed != FOLLOWED_BY_NONE)
627 {
628 return KEYS_UNKNOWN;
629 }
630 }
631 else if(curr->type != USER_CMD)
632 {
633 if(conf->followed == FOLLOWED_BY_MULTIKEY)
634 {
635 key_info->multi = keys[0];
636 return dispatch_key(*key_info, keys_info, curr, keys + 1);
637 }
638
639 keys_info->selector = 1;
640 return dispatch_selector(keys, keys_info, *key_info, curr, no_remap);
641 }
642
643 return dispatch_key(*key_info, keys_info, curr, keys);
644 }
645
646 /* Checks whether any child of the node has wait flag set. Returns non-zero if
647 * so, otherwise zero is returned. */
648 static int
649 needs_waiting(const key_chunk_t *curr)
650 {
651 if(curr->wait)
652 {
653 return 1;
654 }
655
656 for(curr = curr->child; curr != NULL; curr = curr->next)
657 {
658 if(needs_waiting(curr))
659 {
660 return 1;
661 }
662 }
663
664 return 0;
665 }
666
667 /* Performs action associated with the key (in curr), if any. Returns error
668 * code. */
669 static int
670 dispatch_key(key_info_t key_info, keys_info_t *keys_info, key_chunk_t *curr,
671 const wchar_t keys[])
672 {
673 const key_conf_t *const conf = &curr->conf;
674
675 if(curr->type != USER_CMD)
676 {
677 const int result = execute_mapping_handler(conf, key_info, keys_info);
678 const int finish_dispatching = result != 0
679 || *keys == L'\0'
680 || conf->followed != FOLLOWED_BY_MULTIKEY;
681 if(finish_dispatching)
682 {
683 return result;
684 }
685
686 /* Process the rest of the input after a command followed by multikey. */
687 return execute_keys_general_wrapper(keys, keys_info->after_wait, 0,
688 curr->no_remap);
689 }
690 else
691 {
692 if(curr->silent)
693 {
694 silence_ui(1);
695 }
696
697 int result = has_def_handler() ? 0 : KEYS_UNKNOWN;
698
699 /* Protect chunk from deletion while it's in use. */
700 enter_chunk(curr);
701
702 if(curr->enters == 1)
703 {
704 result = execute_after_remapping(conf->data.cmd, keys, *keys_info,
705 key_info, curr);
706 }
707 else if(has_def_handler())
708 {
709 result = def_handler()(curr->key);
710
711 if(result == 0)
712 {
713 result = execute_keys_general(keys, keys_info->after_wait, 0,
714 curr->no_remap);
715 }
716 }
717
718 if(result == KEYS_UNKNOWN && has_def_handler())
719 {
720 if(curr->enters == 1)
721 {
722 result = def_handler()(conf->data.cmd[0]);
723 enter_chunk(curr);
724 execute_keys_general(conf->data.cmd + 1, 0, 1, curr->no_remap);
725 leave_chunk(curr);
726 }
727 else
728 {
729 int i;
730 for(i = 0; conf->data.cmd[i] != '\0'; i++)
731 {
732 result = def_handler()(conf->data.cmd[i]);
733 }
734 }
735 }
736
737 if(curr->silent)
738 {
739 silence_ui(0);
740 }
741
742 /* Release the chunk, this will free it if deletion was attempted. */
743 leave_chunk(curr);
744
745 return result;
746 }
747 }
748
749 /* Checks that default handler exists for active mode. Returns non-zero if so,
750 * otherwise zero is returned. */
751 static int
752 has_def_handler(void)
753 {
754 return (def_handler() != NULL);
755 }
756
757 /* Gets default handler of active mode. Returns the handler, which is NULL when
758 * not set for active mode. */
759 static default_handler
760 def_handler(void)
761 {
762 return def_handlers[vle_mode_get()];
763 }
764
765 /* Processes remapping of a key. Returns error code. */
766 static int
767 execute_after_remapping(const wchar_t rhs[], const wchar_t left_keys[],
768 keys_info_t keys_info, key_info_t key_info, key_chunk_t *curr)
769 {
770 int result;
771 if(rhs[0] == L'\0' && left_keys[0] == L'\0')
772 {
773 /* No-operation command "executed" correctly. */
774 result = 0;
775 }
776 else if(rhs[0] == L'\0')
777 {
778 keys_info_t keys_info;
779 init_keys_info(&keys_info, 1);
780 enter_chunk(curr);
781 result = dispatch_keys(left_keys, &keys_info, curr->no_remap,
782 NO_COUNT_GIVEN);
783 leave_chunk(curr);
784 }
785 else
786 {
787 wchar_t buf[16 + wcslen(rhs) + 1 + wcslen(left_keys) + 1];
788
789 buf[0] = '\0';
790 if(key_info.reg != NO_REG_GIVEN)
791 {
792 vifm_swprintf(buf, ARRAY_LEN(buf), L"\"%c", key_info.reg);
793 }
794 if(key_info.count != NO_COUNT_GIVEN)
795 {
796 vifm_swprintf(buf + wcslen(buf), ARRAY_LEN(buf) - wcslen(buf), L"%d",
797 key_info.count);
798 }
799 wcscat(buf, rhs);
800 wcscat(buf, left_keys);
801
802 if(curr->conf.followed != FOLLOWED_BY_SELECTOR)
803 {
804 init_keys_info(&keys_info, 1);
805 }
806
807 enter_chunk(curr);
808 result = dispatch_keys(buf, &keys_info, curr->no_remap, NO_COUNT_GIVEN);
809 leave_chunk(curr);
810 }
811 return result;
812 }
813
814 /* Handles entering a chunk. Counterpart of leave_chunk(). */
815 static void
816 enter_chunk(key_chunk_t *chunk)
817 {
818 ++chunk->enters;
819 }
820
821 /* Handles leaving a chunk performing postponed chunk removal if needed.
822 * Counterpart of enter_chunk(). */
823 static void
824 leave_chunk(key_chunk_t *chunk)
825 {
826 --chunk->enters;
827
828 if(chunk->enters == 0 && chunk->deleted)
829 {
830 /* Removal of the chunk was postponed because it was in use, proceed with
831 * this now. */
832 free(chunk);
833 }
834 }
835
836 static void
837 init_keys_info(keys_info_t *keys_info, int mapped)
838 {
839 keys_info->selector = 0;
840 keys_info->count = 0;
841 keys_info->indexes = NULL;
842 keys_info->after_wait = 0;
843 keys_info->mapped = mapped;
844 keys_info->recursive = is_recursive();
845 }
846
847 static const wchar_t *
848 get_reg(const wchar_t *keys, int *reg)
849 {
850 *reg = NO_REG_GIVEN;
851 if((mode_flags[vle_mode_get()] & MF_USES_REGS) == 0)
852 {
853 return keys;
854 }
855 if(keys[0] == L'"')
856 {
857 if(keys[1] == L'\0')
858 {
859 return NULL;
860 }
861 *reg = keys[1];
862 keys += 2;
863 }
864
865 return keys;
866 }
867
868 /* Reads count of the command. Sets *count to NO_COUNT_GIVEN if there is no
869 * count in the current position. Returns pointer to a character right next to
870 * the count. */
871 static const wchar_t *
872 get_count(const wchar_t keys[], int *count)
873 {
874 if(is_at_count(keys))
875 {
876 wchar_t *ptr;
877 *count = wcstol(keys, &ptr, 10);
878 /* Handle overflow correctly. */
879 if(*count <= 0)
880 {
881 *count = INT_MAX;
882 }
883 keys = ptr;
884 }
885 else
886 {
887 *count = NO_COUNT_GIVEN;
888 }
889
890 return keys;
891 }
892
893 /* Checks keys for a count. Returns non-zero if there is count in the current
894 * position. */
895 static int
896 is_at_count(const wchar_t keys[])
897 {
898 if((mode_flags[vle_mode_get()] & MF_USES_COUNT) != 0)
899 {
900 if(keys[0] != L'0' && iswdigit(keys[0]))
901 {
902 return 1;
903 }
904 }
905 return 0;
906 }
907
908 /* Combines two counts: before command and in the middle of it. */
909 static int
910 combine_counts(int count_a, int count_b)
911 {
912 if(count_a == NO_COUNT_GIVEN)
913 {
914 return count_b;
915 }
916 else if(count_b == NO_COUNT_GIVEN)
917 {
918 return count_a;
919 }
920 else
921 {
922 return count_a*count_b;
923 }
924 }
925
926 int
927 vle_keys_user_add(const wchar_t lhs[], const wchar_t rhs[], int mode,
928 int flags)
929 {
930 key_chunk_t *curr = add_keys_inner(&user_cmds_root[mode], lhs);
931 if(curr == NULL)
932 {
933 return -1;
934 }
935
936 if(curr->type == USER_CMD)
937 {
938 free(curr->conf.data.cmd);
939 }
940
941 curr->type = USER_CMD;
942 curr->conf.data.cmd = vifm_wcsdup(rhs);
943 curr->no_remap = ((flags & KEYS_FLAG_NOREMAP) != 0);
944 curr->silent = ((flags & KEYS_FLAG_SILENT) != 0);
945 curr->wait = ((flags & KEYS_FLAG_WAIT) != 0);
946 return 0;
947 }
948
949 int
950 vle_keys_user_exists(const wchar_t keys[], int mode)
951 {
952 return find_user_keys(keys, mode) != NULL;
953 }
954
955 int
956 vle_keys_user_remove(const wchar_t keys[], int mode)
957 {
958 key_chunk_t *curr, *p;
959
960 if((curr = find_user_keys(keys, mode)) == NULL)
961 return -1;
962
963 free(curr->conf.data.cmd);
964 curr->type = BUILTIN_WAIT_POINT;
965 curr->conf.data.handler = NULL;
966
967 p = curr;
968 while(p->parent != NULL)
969 {
970 p->parent->children_count--;
971 p = p->parent;
972 }
973
974 if(curr->children_count > 0)
975 return 0;
976
977 do
978 {
979 key_chunk_t *const parent = curr->parent;
980 if(curr->prev != NULL)
981 curr->prev->next = curr->next;
982 else
983 parent->child = curr->next;
984 if(curr->next != NULL)
985 curr->next->prev = curr->prev;
986 free_chunk(curr);
987 curr = parent;
988 }
989 while(curr->parent != NULL && curr->parent->conf.data.handler == NULL &&
990 curr->parent->type == BUILTIN_WAIT_POINT &&
991 curr->parent->children_count == 0);
992
993 return 0;
994 }
995
996 static key_chunk_t *
997 find_user_keys(const wchar_t *keys, int mode)
998 {
999 key_chunk_t *curr = &user_cmds_root[mode];
1000
1001 if(*keys == L'\0')
1002 {
1003 return NULL;
1004 }
1005
1006 while(*keys != L'\0')
1007 {
1008 key_chunk_t *p = curr->child;
1009 while(p != NULL && p->key < *keys)
1010 p = p->next;
1011 if(p == NULL || p->key != *keys)
1012 return NULL;
1013 curr = p;
1014 keys++;
1015 }
1016 return (curr->type == USER_CMD) ? curr : NULL;
1017 }
1018
1019 int
1020 vle_keys_add(keys_add_info_t cmds[], size_t len, int mode)
1021 {
1022 return add_list_of_keys(&builtin_cmds_root[mode], cmds, len);
1023 }
1024
1025 int
1026 vle_keys_add_selectors(keys_add_info_t cmds[], size_t len, int mode)
1027 {
1028 return add_list_of_keys(&selectors_root[mode], cmds, len);
1029 }
1030
1031 /* Registers cmds[0 .. len-1] keys specified tree. Returns non-zero on error,
1032 * otherwise zero is returned. */
1033 static int
1034 add_list_of_keys(key_chunk_t *root, keys_add_info_t cmds[], size_t len)
1035 {
1036 int result = 0;
1037 size_t i;
1038
1039 for(i = 0U; i < len; ++i)
1040 {
1041 key_chunk_t *const curr = add_keys_inner(root, cmds[i].keys);
1042 if(curr == NULL)
1043 {
1044 result = -1;
1045 continue;
1046 }
1047
1048 curr->conf = cmds[i].info;
1049 if(curr->conf.nim)
1050 {
1051 curr->type = BUILTIN_NIM_KEYS;
1052 }
1053 else
1054 {
1055 curr->type = (curr->conf.followed == FOLLOWED_BY_NONE)
1056 ? BUILTIN_KEYS
1057 : BUILTIN_WAIT_POINT;
1058 }
1059 }
1060
1061 return result;
1062 }
1063
1064 static key_chunk_t *
1065 add_keys_inner(key_chunk_t *root, const wchar_t *keys)
1066 {
1067 key_chunk_t *curr = root;
1068 while(*keys != L'\0')
1069 {
1070 key_chunk_t *prev, *p;
1071 prev = NULL;
1072 p = curr->child;
1073 while(p != NULL && p->key < *keys)
1074 {
1075 prev = p;
1076 p = p->next;
1077 }
1078 if(p == NULL || p->key != *keys)
1079 {
1080 key_chunk_t *c = malloc(sizeof(*c));
1081 if(c == NULL)
1082 return NULL;
1083 c->key = *keys;
1084 c->type = (keys[1] == L'\0') ? BUILTIN_KEYS : BUILTIN_WAIT_POINT;
1085 c->conf.data.handler = NULL;
1086 c->conf.data.cmd = NULL;
1087 c->conf.followed = FOLLOWED_BY_NONE;
1088 c->conf.suggest = NULL;
1089 c->conf.descr = NULL;
1090 c->conf.nim = 0;
1091 c->conf.skip_suggestion = 0;
1092 c->prev = prev;
1093 c->next = p;
1094 c->child = NULL;
1095 c->parent = curr;
1096 c->children_count = 0;
1097 c->enters = 0;
1098 c->deleted = 0;
1099 c->no_remap = 1;
1100 c->silent = 0;
1101 c->wait = 0;
1102 if(prev == NULL)
1103 curr->child = c;
1104 else
1105 prev->next = c;
1106 if(p != NULL)
1107 p->prev = c;
1108
1109 if(keys[1] == L'\0')
1110 {
1111 while(curr != NULL)
1112 {
1113 curr->children_count++;
1114 curr = curr->parent;
1115 }
1116 }
1117
1118 p = c;
1119 }
1120 keys++;
1121 curr = p;
1122 }
1123 return curr;
1124 }
1125
1126 void
1127 vle_keys_list(int mode, vle_keys_list_cb cb, int user_only)
1128 {
1129 const key_chunk_t *user = &user_cmds_root[mode];
1130 const key_chunk_t *builtin = &builtin_cmds_root[mode];
1131
1132 /* Don't traverse empty tries. */
1133 if(user->children_count != 0U)
1134 {
1135 traverse_children(user, L"", &list_chunk, cb);
1136
1137 /* Put separator only when it's needed. */
1138 if(!user_only && builtin->children_count != 0U)
1139 {
1140 cb(L"", L"", "");
1141 }
1142 }
1143
1144 if(!user_only && builtin->children_count != 0U)
1145 {
1146 traverse_children(builtin, L"", &list_chunk, cb);
1147 }
1148 }
1149
1150 /* Invokes list callback (in arg) for leaf chunks. */
1151 static void
1152 list_chunk(const key_chunk_t *chunk, const wchar_t lhs[], void *arg)
1153 {
1154 if(chunk->children_count == 0 || chunk->type == USER_CMD)
1155 {
1156 const wchar_t *rhs = (chunk->type == USER_CMD) ? chunk->conf.data.cmd : L"";
1157 const char *descr = (chunk->conf.descr == NULL) ? "" : chunk->conf.descr;
1158 vle_keys_list_cb cb = arg;
1159 cb(lhs, rhs, descr);
1160 }
1161 }
1162
1163 size_t
1164 vle_keys_counter(void)
1165 {
1166 return counter;
1167 }
1168
1169 /* Increments counter if we are at the first level of key parsing recursion and
1170 * the key sequence isn't mapped. */
1171 static void
1172 inc_counter(const keys_info_t *keys_info, size_t by)
1173 {
1174 assert(enters_counter > 0);
1175
1176 if(!is_recursive())
1177 {
1178 counter += keys_info->mapped ? 0 : by;
1179 }
1180 }
1181
1182 /* Returns non-zero if current level of recursion is deeper than 1. */
1183 static int
1184 is_recursive(void)
1185 {
1186 return (enters_counter > 1);
1187 }
1188
1189 int
1190 vle_keys_inside_mapping(void)
1191 {
1192 return (inside_mapping != 0);
1193 }
1194
1195 /* Executes handler for a mapping, if any. Error or success code is
1196 * returned. */
1197 static int
1198 execute_mapping_handler(const key_conf_t *info, key_info_t key_info,
1199 keys_info_t *keys_info)
1200 {
1201 if(info->data.handler != NULL)
1202 {
1203 pre_execute_mapping_handler(keys_info);
1204 info->data.handler(key_info, keys_info);
1205 post_execute_mapping_handler(keys_info);
1206 return 0;
1207 }
1208 return KEYS_UNKNOWN;
1209 }
1210
1211 /* Pre-execution of a mapping handler callback. */
1212 static void
1213 pre_execute_mapping_handler(const keys_info_t *keys_info)
1214 {
1215 inside_mapping += keys_info->mapped != 0;
1216 assert(inside_mapping >= 0 && "Calls to pre/post funcs should be balanced");
1217 }
1218
1219 /* Post-execution of a mapping handler callback. */
1220 static void
1221 post_execute_mapping_handler(const keys_info_t *keys_info)
1222 {
1223 inside_mapping -= keys_info->mapped != 0;
1224 assert(inside_mapping >= 0 && "Calls to pre/post funcs should be balanced");
1225 }
1226
1227 void
1228 vle_keys_suggest(const wchar_t keys[], vle_keys_list_cb cb, int custom_only,
1229 int fold_subkeys)
1230 {
1231 keys_suggest(&user_cmds_root[vle_mode_get()], keys, L"key: ", cb,
1232 custom_only, fold_subkeys);
1233 keys_suggest(&builtin_cmds_root[vle_mode_get()], keys, L"key: ", cb,
1234 custom_only, fold_subkeys);
1235 }
1236
1237 /* Looks up possible continuations of keys for the given root and calls cb on
1238 * them. */
1239 static void
1240 keys_suggest(const key_chunk_t *root, const wchar_t keys[],
1241 const wchar_t prefix[], vle_keys_list_cb cb, int custom_only,
1242 int fold_subkeys)
1243 {
1244 const key_chunk_t *curr = root;
1245
1246 while(*keys != L'\0')
1247 {
1248 const key_chunk_t *p;
1249 int number_in_the_middle = 0;
1250
1251 /* Look up current key among children of current node (might be root), while
1252 * inspecting NIM as well. */
1253 for(p = curr->child; p != NULL && p->key < *keys; p = p->next)
1254 {
1255 if(p->type == BUILTIN_NIM_KEYS)
1256 {
1257 number_in_the_middle = 1;
1258 }
1259 }
1260
1261 /* Go to the next character if a match found. */
1262 if(p != NULL && p->key == *keys)
1263 {
1264 ++keys;
1265 curr = p;
1266 continue;
1267 }
1268
1269 /* No match for the first character is fatal for the lookup. */
1270 if(curr == root)
1271 {
1272 return;
1273 }
1274
1275 /* Need to inspect all children for NIM. */
1276 for(; p != NULL && !number_in_the_middle; p = p->next)
1277 {
1278 if(p->type == BUILTIN_NIM_KEYS)
1279 {
1280 number_in_the_middle = 1;
1281 }
1282 }
1283
1284 /* Give up if this isn't one of cases where next character is not presented
1285 * in the tree by design. */
1286 if(curr->conf.followed != FOLLOWED_BY_NONE &&
1287 (!number_in_the_middle || !is_at_count(keys)))
1288 {
1289 break;
1290 }
1291
1292 /* Skip over number in the middle, if any. */
1293 if(number_in_the_middle)
1294 {
1295 int count;
1296 const wchar_t *new_keys = get_count(keys, &count);
1297 if(new_keys != keys)
1298 {
1299 keys = new_keys;
1300 continue;
1301 }
1302 }
1303
1304 break;
1305 }
1306
1307 if(!custom_only && *keys == L'\0')
1308 {
1309 suggest_children(curr, prefix, cb, fold_subkeys);
1310 }
1311
1312 if(curr->type == BUILTIN_WAIT_POINT)
1313 {
1314 if(curr->conf.followed == FOLLOWED_BY_SELECTOR)
1315 {
1316 /* Suggest selectors. */
1317 keys_suggest(&selectors_root[vle_mode_get()], keys, L"sel: ", cb,
1318 custom_only, fold_subkeys);
1319 }
1320 else if(curr->conf.followed == FOLLOWED_BY_MULTIKEY)
1321 {
1322 /* Invoke optional external function to provide suggestions. */
1323 if(curr->conf.suggest != NULL)
1324 {
1325 curr->conf.suggest(cb);
1326 }
1327 }
1328 }
1329 }
1330
1331 /* Suggests children of the specified chunk. */
1332 static void
1333 suggest_children(const key_chunk_t *chunk, const wchar_t prefix[],
1334 vle_keys_list_cb cb, int fold_subkeys)
1335 {
1336 const key_chunk_t *child;
1337
1338 const size_t prefix_len = wcslen(prefix);
1339 wchar_t item[prefix_len + 1U + 1U];
1340 wcscpy(item, prefix);
1341 item[prefix_len + 1U] = L'\0';
1342
1343 for(child = chunk->child; child != NULL; child = child->next)
1344 {
1345 if(!fold_subkeys || child->children_count <= 1)
1346 {
1347 traverse_children(child, prefix, &suggest_chunk, cb);
1348 }
1349 else
1350 {
1351 char msg[64];
1352 snprintf(msg, sizeof(msg), "{ %d mappings folded }",
1353 (int)child->children_count);
1354 item[prefix_len] = child->key;
1355 cb(item, L"", msg);
1356 }
1357 }
1358 }
1359
1360 /* Visits every child of the tree and calls cb with param on it. */
1361 static void
1362 traverse_children(const key_chunk_t *chunk, const wchar_t prefix[],
1363 traverse_func cb, void *param)
1364 {
1365 const key_chunk_t *child;
1366
1367 const size_t prefix_len = wcslen(prefix);
1368 wchar_t item[prefix_len + 1U + 1U];
1369 wcscpy(item, prefix);
1370 item[prefix_len] = chunk->key;
1371 item[prefix_len + 1U] = L'\0';
1372
1373 cb(chunk, item, param);
1374
1375 for(child = chunk->child; child != NULL; child = child->next)
1376 {
1377 traverse_children(child, item, cb, param);
1378 }
1379 }
1380
1381 /* Invokes suggestion callback (in arg) for leaf chunks. */
1382 static void
1383 suggest_chunk(const key_chunk_t *chunk, const wchar_t lhs[], void *arg)
1384 {
1385 vle_keys_list_cb cb = arg;
1386
1387 if(chunk->conf.skip_suggestion)
1388 {
1389 return;
1390 }
1391
1392 if(chunk->type == USER_CMD)
1393 {
1394 cb(lhs, chunk->conf.data.cmd, "");
1395 }
1396 else if(chunk->children_count == 0 ||
1397 chunk->conf.followed != FOLLOWED_BY_NONE)
1398 {
1399 cb(lhs, L"", (chunk->conf.descr == NULL) ? "" : chunk->conf.descr);
1400 }
1401 }
1402
1403 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0: */
1404 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/engine/keys.h deleted (index 188adcc..0000000)
1 /* vifm
2 * Copyright (C) 2011 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #ifndef VIFM__ENGINE__KEYS_H__
20 #define VIFM__ENGINE__KEYS_H__
21
22 #include <stddef.h> /* size_t wchar_t */
23
24 #ifdef __cplusplus
25 extern "C"
26 {
27 #endif
28
29 enum
30 {
31 NO_COUNT_GIVEN = -1,
32 NO_REG_GIVEN = -1,
33 };
34
35 enum
36 {
37 MF_USES_REGS = 1,
38 MF_USES_COUNT = 2,
39 MF_USES_INPUT = 4,
40 };
41
42 enum
43 {
44 KEYS_UNKNOWN = -1024,
45 KEYS_WAIT = -2048,
46 KEYS_WAIT_SHORT = -4096,
47 };
48
49 /* Flags for a user key mapping. */
50 enum
51 {
52 KEYS_FLAG_NONE = 0, /* No flags. */
53 KEYS_FLAG_NOREMAP = 1, /* Ignore user mappings in RHS. */
54 KEYS_FLAG_SILENT = 2, /* Postpone screen update until mapping's end. */
55 KEYS_FLAG_WAIT = 4, /* Do not use short wait to resolve conflict against
56 builtin mapping. */
57 };
58
59 /* Checks passed in value for being among list of error codes this unit can
60 * return. */
61 #define IS_KEYS_RET_CODE(c) \
62 ({ \
63 const int tmp = (c); \
64 tmp == KEYS_UNKNOWN || tmp == KEYS_WAIT || tmp == KEYS_WAIT_SHORT; \
65 })
66
67 typedef enum
68 {
69 FOLLOWED_BY_NONE,
70 FOLLOWED_BY_SELECTOR,
71 FOLLOWED_BY_MULTIKEY,
72 }
73 FollowedBy;
74
75 /* Describes single key (command or selector) on its own. */
76 typedef struct
77 {
78 int count; /* Repeat count, may be equal NO_COUNT_GIVEN. */
79 int reg; /* Number of selected register. */
80 int multi; /* Multikey. */
81 void *context; /* User data for the key (can be NULL). */
82 }
83 key_info_t;
84
85 /* Describes sequence of keys (it can consist of a single key). This structure
86 * is shared among elements composite constructs like command+selector. */
87 typedef struct
88 {
89 int selector; /* Selector passed. */
90 int count; /* Count of selected items. */
91 int *indexes; /* Item indexes. */
92 int after_wait; /* After short timeout. */
93 int mapped; /* Not users input. */
94 int recursive; /* The key is from recursive call of execute_keys_*(...). */
95 }
96 keys_info_t;
97
98 /* Handler for builtin keys. */
99 typedef void (*vle_keys_handler)(key_info_t key_info, keys_info_t *keys_info);
100 /* Type of function invoked by vle_keys_list() and vle_keys_suggest(). rhs is
101 * provided for user-defined keys and is empty otherwise. Description is empty
102 * for user-defined keys or when not set. */
103 typedef void (*vle_keys_list_cb)(const wchar_t lhs[], const wchar_t rhs[],
104 const char descr[]);
105 /* User-provided suggestion callback for multikeys. */
106 typedef void (*vle_suggest_func)(vle_keys_list_cb cb);
107 /* User-provided callback for silencing UI. Non-zero argument makes UI more
108 * silent, zero argument makes it less silent. */
109 typedef void (*vle_silence_func)(int more);
110
111 /* Type of callback that handles all keys uncaught by shortcuts. Should return
112 * zero on success and non-zero on error. */
113 typedef int (*default_handler)(wchar_t key);
114
115 typedef struct
116 {
117 union
118 {
119 vle_keys_handler handler; /* Handler for builtin commands. */
120 wchar_t *cmd; /* Mapped value for user-defined keys. */
121 }
122 data;
123 FollowedBy followed; /* What type of key should we wait for. */
124 vle_suggest_func suggest; /* Suggestion function (can be NULL). Invoked for
125 multikeys. */
126 const char *descr; /* Brief description of the key (can be NULL). */
127 void *context; /* User data for the key (can be NULL). */
128 int nim; /* Whether additional count in the middle is
129 allowed. */
130 int skip_suggestion; /* Do not print this among suggestions. */
131 }
132 key_conf_t;
133
134 typedef struct
135 {
136 const wchar_t keys[5];
137 key_conf_t info;
138 }
139 keys_add_info_t;
140
141 /* Initializes the unit. Assumed that key_mode_flags is an array of at least
142 * modes_count items. */
143 void vle_keys_init(int modes_count, int *key_mode_flags,
144 vle_silence_func silence);
145
146 /* Frees all memory allocated by the unit and returns it to initial state. */
147 void vle_keys_reset(void);
148
149 /* Removes just user-defined keys (leaving builtin keys intact). */
150 void vle_keys_user_clear(void);
151
152 /* Set handler for unregistered keys. handler can be NULL to remove it. */
153 void vle_keys_set_def_handler(int mode, default_handler handler);
154
155 /* Process sequence of keys. Return value:
156 * - 0 - success
157 * - KEYS_*
158 * - something else from the default key handler */
159 int vle_keys_exec(const wchar_t keys[]);
160
161 /* Same as vle_keys_exec(), but disallows map processing in RHS of maps. */
162 int vle_keys_exec_no_remap(const wchar_t keys[]);
163
164 /* Same as vle_keys_exec(), but assumes that key wait timeout has expired. */
165 int vle_keys_exec_timed_out(const wchar_t keys[]);
166
167 /* Same as vle_keys_exec_no_remap(), but assumes that key wait timeout has
168 * expired. */
169 int vle_keys_exec_timed_out_no_remap(const wchar_t keys[]);
170
171 /* Registers cmds[0 .. len-1] commands for the mode. Returns non-zero on error,
172 * otherwise zero is returned. */
173 int vle_keys_add(keys_add_info_t cmds[], size_t len, int mode);
174
175 /* Registers cmds[0 .. len-1] selectors for the mode. Returns non-zero on
176 * error, otherwise zero is returned. */
177 int vle_keys_add_selectors(keys_add_info_t cmds[], size_t len, int mode);
178
179 /* Registers user key mapping. The flags parameter accepts combinations of
180 * KEYS_FLAG_*. Returns non-zero or error, otherwise zero is returned. */
181 int vle_keys_user_add(const wchar_t keys[], const wchar_t rhs[], int mode,
182 int flags);
183
184 /* Checks whether given user mapping exists. Returns non-zero if so, otherwise
185 * zero is returned. */
186 int vle_keys_user_exists(const wchar_t keys[], int mode);
187
188 /* Removes user mapping from the mode. Returns non-zero if given key sequence
189 * wasn't found. */
190 int vle_keys_user_remove(const wchar_t keys[], int mode);
191
192 /* Lists all or just user keys of the given mode with description. */
193 void vle_keys_list(int mode, vle_keys_list_cb cb, int user_only);
194
195 /* Retrieves number of keys processed so far. Clients are expected to use
196 * difference of returned values. Returns the number. */
197 size_t vle_keys_counter(void);
198
199 /* Checks whether a mapping handler is currently been executed. Returns
200 * non-zero if so, otherwise zero is returned. */
201 int vle_keys_inside_mapping(void);
202
203 /* Invokes cb for each possible keys continuation. Intended to be used on
204 * KEYS_WAIT and KEYS_WAIT_SHORT returns. The custom_only flag limits
205 * suggestions to those generated by invoking key_conf_t::suggest. The
206 * fold_subkeys enables folding of multiple keys with common prefix. */
207 void vle_keys_suggest(const wchar_t keys[], vle_keys_list_cb cb,
208 int custom_only, int fold_subkeys);
209
210 #ifdef __cplusplus
211 }
212 #endif
213
214 #endif /* VIFM__ENGINE__KEYS_H__ */
215
216 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0: */
217 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/engine/mode.c deleted (index 3e8dcf2..0000000)
1 /* vifm
2 * Copyright (C) 2014 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "mode.h"
20
21 static vle_mode_t current_mode;
22 static vle_mode_t primary_mode;
23
24 vle_mode_t
25 vle_mode_get(void)
26 {
27 return current_mode;
28 }
29
30 int
31 vle_mode_is(vle_mode_t mode)
32 {
33 return current_mode == mode;
34 }
35
36 vle_mode_t
37 vle_mode_get_primary(void)
38 {
39 return primary_mode;
40 }
41
42 int
43 vle_primary_mode_is(vle_mode_t mode)
44 {
45 return primary_mode == mode;
46 }
47
48 void
49 vle_mode_set(vle_mode_t mode, VleModeType type)
50 {
51 current_mode = mode;
52 if(type == VMT_PRIMARY)
53 {
54 primary_mode = mode;
55 }
56 }
57
58 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
59 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/engine/mode.h deleted (index e18cf20..0000000)
1 /* vifm
2 * Copyright (C) 2014 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #ifndef VIFM__ENGINE__MODE_H__
20 #define VIFM__ENGINE__MODE_H__
21
22 /* The concept:
23 *
24 * 1. Two kinds of modes: primary and secondary; defined by their interoperation
25 * in UI;
26 *
27 * 2. While secondary mode is active primary is still visible, although it does
28 * not get any input from a user.
29 *
30 * 3. Secondary mode is also the current one. There might be two generic
31 * situations:
32 *
33 * - no secondary mode, when vle_mode_get() == vle_mode_get_primary();
34 * - secondary mode present, which makes "current" and "primary" modes be
35 * different. */
36
37 #ifdef __cplusplus
38 extern "C"
39 {
40 #endif
41
42 /* Type of mode. */
43 typedef enum
44 {
45 VMT_PRIMARY, /* Host (main) mode (e.g. normal, visual). */
46 VMT_SECONDARY, /* Temporary (auxiliary) mode (e.g. command-line, dialog). */
47 }
48 VleModeType;
49
50 /* Mode identifier. It's of integer type. */
51 typedef int vle_mode_t;
52
53 /* Gets identifier of currently active mode. Returns the id. */
54 vle_mode_t vle_mode_get(void);
55
56 /* Checks that currently active mode is the mode. Returns non-zero if so,
57 * otherwise zero is returned. */
58 int vle_mode_is(vle_mode_t mode);
59
60 /* Gets identifier of currently active primary mode. Returns the id. */
61 vle_mode_t vle_mode_get_primary(void);
62
63 /* Checks that primary mode is the mode. Returns non-zero if so, otherwise zero
64 * is returned. */
65 int vle_primary_mode_is(vle_mode_t mode);
66
67 /* Sets current mode of the specified type. */
68 void vle_mode_set(vle_mode_t mode, VleModeType type);
69
70 #ifdef __cplusplus
71 }
72 #endif
73
74 #endif /* VIFM__ENGINE__MODE_H__ */
75
76 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
77 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/utils/macros.h deleted (index eed461d..0000000)
1 /* vifm
2 * Copyright (C) 2011 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #ifndef VIFM__UTILS__MACROS_H__
20 #define VIFM__UTILS__MACROS_H__
21
22 #include <stddef.h> /* size_t */
23
24 /* some useful macros */
25
26 /* for portable use of GNUC extensions (see Robert Love's book about system
27 * programming in Linux) */
28 #if __GNUC__ > 3
29 #undef inline
30 #define inline inline __attribute__ ((always_inline))
31 #define _gnuc_noinline __attribute__ ((noinline))
32 #define _gnuc_pure __attribute__ ((pure))
33 #define _gnuc_const __attribute__ ((const))
34 #define _gnuc_noreturn __attribute__ ((noreturn))
35 #define _gnuc_malloc __attribute__ ((malloc))
36 #define _gnuc_must_check __attribute__ ((must_check))
37 #define _gnuc_deprecated __attribute__ ((deprecated))
38 #define _gnuc_used __attribute__ ((used))
39 #define _gnuc_unused __attribute__ ((unused))
40 #define _gnuc_packed __attribute__ ((packed))
41 #define _gnuc_align(x) __attribute__ ((aligned(x)))
42 #define _gnuc_align_max __attribute__ ((aligned))
43 #if defined(BROKEN_SWPRINTF) || defined(__clang__)
44 #define _gnuc_printf(m, n) __attribute__ ((format(printf, (m), (n))));
45 #else
46 #define _gnuc_printf(m, n) __attribute__ ((format(gnu_printf, (m), (n))));
47 #endif
48 #define _gnuc_likely(x) __builtin_expect (!!(x), 1)
49 #define _gnuc_unlikely(x) __builtin_expect (!!(x), 0)
50 #else
51 #define _gnuc_noinline
52 #define _gnuc_pure
53 #define _gnuc_const
54 #define _gnuc_noreturn
55 #define _gnuc_malloc
56 #define _gnuc_must_check
57 #define _gnuc_deprecated
58 #define _gnuc_used
59 #define _gnuc_unused
60 #define _gnuc_packed
61 #define _gnuc_align(x)
62 #define _gnuc_align_max
63 #define _gnuc_printf(m, n)
64 #define _gnuc_likely(x) (x)
65 #define _gnuc_unlikely(x) (x)
66 #endif
67
68 #define ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
69 #define ARRAY_GUARD(x, len) \
70 typedef int x##_array_guard[(ARRAY_LEN(x) == (len)) ? 1 : -1]; \
71 /* Fake use to suppress "Unused local variable" warning. */ \
72 enum { x##_array_guard_fake_use = (size_t)(x##_array_guard*)0 }
73
74 /* Make sure nobody else defined MIN/MAX macros. */
75 #ifdef MIN
76 #undef MIN
77 #endif
78 #ifdef MAX
79 #undef MAX
80 #endif
81
82 #define MIN(a,b) ({ \
83 typeof(a) _a = (a); \
84 typeof(b) _b = (b); \
85 (_a < _b) ? _a : _b; \
86 })
87
88 #define MAX(a,b) ({ \
89 typeof(a) _a = (a); \
90 typeof(b) _b = (b); \
91 (_a > _b) ? _a : _b; \
92 })
93
94 #define DIV_ROUND_UP(a,b) ({ \
95 typeof(a) _a = (a); \
96 typeof(b) _b = (b); \
97 (_a + (_b - 1))/_b; \
98 })
99
100 #define ROUND_DOWN(a,b) ({ \
101 typeof(a) _a = (a); \
102 typeof(b) _b = (b); \
103 _a - _a%_b; \
104 })
105
106 /* Evaluates to "true" if all expressions are true. Examples:
107 * ALL(isdigit, '1', '9') == 1
108 * ALL(isdigit, 'z', '9') == 0 */
109 #define ALL(f, ...) ALL_(f, COUNT(__VA_ARGS__), __VA_ARGS__)
110 #define ALL_(f, n, ...) ALL__(f, n, __VA_ARGS__)
111 #define ALL__(f, n, ...) comb##n((f), !!, &&, __VA_ARGS__)
112
113 /* Evaluates to "true" if any of expressions is true. Examples:
114 * ANY(isdigit, 'z', '9') == 1
115 * ANY(isdigit, 'z', 'a') == 0 */
116 #define ANY(f, ...) ({ ANY_(f, COUNT(__VA_ARGS__), __VA_ARGS__); })
117 #define ANY_(f, n, ...) ANY__(f, n, __VA_ARGS__)
118 #define ANY__(f, n, ...) comb##n((f), !!, ||, __VA_ARGS__)
119
120 /* Evaluates to "true" if none of expressions is true. Examples:
121 * NONE(isdigit, 'z', 'a') == 1
122 * NONE(isdigit, '1', '9') == 0 */
123 #define NONE(f, ...) ({ NONE_(f, COUNT(__VA_ARGS__), __VA_ARGS__); })
124 #define NONE_(f, n, ...) NONE__(f, n, __VA_ARGS__)
125 #define NONE__(f, n, ...) comb##n((f), !, &&, __VA_ARGS__)
126
127 /* Checks whether v equals to any of the rest of arguments. Examples:
128 * ONE_OF(1, 1, 2) == 1
129 * ONE_OF(1, 0, 2) == 0 */
130 #define ONE_OF(v, ...) ({ ONE_OF_(v, COUNT(__VA_ARGS__), __VA_ARGS__); })
131 #define ONE_OF_(v, n, ...) ONE_OF__(v, n, __VA_ARGS__)
132 #define ONE_OF__(v, n, ...) comb##n((v) == , , ||, __VA_ARGS__)
133
134 /* Makes bit mask from a list of bits. */
135 #define MASK(...) MASK_(COUNT(__VA_ARGS__), __VA_ARGS__)
136 #define MASK_(n, ...) MASK__(n, __VA_ARGS__)
137 #define MASK__(n, ...) comb##n(<<, 1, |, __VA_ARGS__)
138
139 /* Neat trick to obtain number number of variadic arguments. */
140 #define COUNT(...) COUNT_(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
141 #define COUNT_(_1, _2, _3, _4, _5, _6, _7, _8, _9, n, ...) n
142
143 /* Helper macros to process variable arguments of macros. */
144 #define comb9(f, n, op, val, ...) (n f(val) op comb8(f, n, op, __VA_ARGS__))
145 #define comb8(f, n, op, val, ...) (n f(val) op comb7(f, n, op, __VA_ARGS__))
146 #define comb7(f, n, op, val, ...) (n f(val) op comb6(f, n, op, __VA_ARGS__))
147 #define comb6(f, n, op, val, ...) (n f(val) op comb5(f, n, op, __VA_ARGS__))
148 #define comb5(f, n, op, val, ...) (n f(val) op comb4(f, n, op, __VA_ARGS__))
149 #define comb4(f, n, op, val, ...) (n f(val) op comb3(f, n, op, __VA_ARGS__))
150 #define comb3(f, n, op, val, ...) (n f(val) op comb2(f, n, op, __VA_ARGS__))
151 #define comb2(f, n, op, val, ...) (n f(val) op comb1(f, n, op, __VA_ARGS__))
152 #define comb1(f, n, op, val) (n f(val))
153
154 #endif /* VIFM__UTILS__MACROS_H__ */
155
156 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
157 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/utils/str.c deleted (index 57094b5..0000000)
1 /* vifm
2 * Copyright (C) 2001 Ken Steen.
3 * Copyright (C) 2011 xaizek.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "str.h"
21
22 #include <ctype.h> /* tolower() isspace() */
23 #include <limits.h> /* INT_MAX INT_MIN LONG_MAX LONG_MIN */
24 #include <stdarg.h> /* va_list va_start() va_copy() va_end() */
25 #include <stddef.h> /* NULL size_t wchar_t */
26 #include <stdio.h> /* snprintf() */
27 #include <stdlib.h> /* free() malloc() mbstowcs() memmove() memset() realloc()
28 strtol() wcstombs() */
29 #include <string.h> /* strdup() strncmp() strlen() strcmp() strchr() strrchr()
30 strncpy() */
31 #include <wchar.h> /* wint_t vswprintf() */
32 #include <wctype.h> /* iswprint() iswupper() towlower() towupper() */
33
34 #include "../compat/reallocarray.h"
35 #include "macros.h"
36
37 wchar_t *
38 vifm_wcsdup(const wchar_t ws[])
39 {
40 const size_t len = wcslen(ws) + 1;
41 wchar_t * const result = reallocarray(NULL, len, sizeof(wchar_t));
42 if(result == NULL)
43 {
44 return NULL;
45 }
46 wcsncpy(result, ws, len);
47 return result;
48 }
49
50 int
51 vifm_swprintf(wchar_t str[], size_t len, const wchar_t format[], ...)
52 {
53 int result;
54 va_list ap;
55
56 va_start(ap, format);
57
58 #ifdef BROKEN_SWPRINTF
59 result = vswprintf(str, format, ap);
60 #else
61 result = vswprintf(str, len, format, ap);
62 #endif
63
64 va_end(ap);
65
66 return result;
67 }
68
69 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
70 /* vim: set cinoptions+=t0 filetype=c : */
File libs/vle/utils/str.h deleted (index ac523b6..0000000)
1 /* vifm
2 * Copyright (C) 2001 Ken Steen.
3 * Copyright (C) 2011 xaizek.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #ifndef VIFM__UTILS__STR_H__
21 #define VIFM__UTILS__STR_H__
22
23 #include <inttypes.h> /* PRIu64 */
24 #include <stddef.h> /* size_t wchar_t */
25
26 #include "macros.h"
27
28 #if defined(_WIN32) && !defined(_WIN64)
29 #ifdef BROKEN_SWPRINTF
30 #define WPRINTF_WSTR L"s"
31 #else
32 #define WPRINTF_WSTR L"ls"
33 #endif
34 #define PRINTF_ULL PRIu64
35 #else
36 #define WPRINTF_WSTR L"ls"
37 #define PRINTF_ULL "llu"
38 #endif
39
40 /* Various string functions. */
41
42 /* Duplicates a wide-character string. Returns pointer to new string or NULL on
43 * error. */
44 wchar_t * vifm_wcsdup(const wchar_t ws[]);
45
46 /* A wrapper of swprintf() functions to make its differences on various
47 * platforms transparently in other parts of the program. */
48 int vifm_swprintf(wchar_t str[], size_t len, const wchar_t format[], ...);
49
50 #endif /* VIFM__UTILS__STR_H__ */
51
52 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
53 /* vim: set cinoptions+=t0 filetype=c : */
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/pipedial

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/pipedial

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master