1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
// SPDX-License-Identifier: BSL-1.0
#include <string.h>
#include <functional>
#include <fstream>
#include <iostream>
typedef bool _Bool;
#include "../termpaint_input.h"
#ifndef BUNDLED_PICOJSON
#include "picojson.h"
#else
#include "../third-party/picojson.h"
#endif
#ifndef BUNDLED_DOCOPT
#include "docopt/docopt.h"
#else
#include "../third-party/docopt/docopt.h"
#endif
#ifndef BUNDLED_FMT
#include "fmt/format.h"
#else
#include "../third-party/format.h"
#endif
using jarray = picojson::value::array;
using jobject = picojson::value::object;
template <typename Result, typename... Args>
Result wrapper(void* state, Args... args) {
using FnType = std::function<Result(Args...)>;
FnType& w = *reinterpret_cast<FnType*>(state);
return w(args...);
}
template <typename TYPE1, typename Result, typename... Args>
void wrap(void (*set_cb)(TYPE1 *ctx, Result (*cb)(void *user_data, Args...), void *user_data), TYPE1 *ctx, std::function<Result(Args...)> &fn) {
void *state = reinterpret_cast<void*>(&fn);
set_cb(ctx, &wrapper<Result, Args...>, state);
}
bool parses_as_one(const char *input) {
enum { START, OK, ERROR } state = START;
std::function<_Bool(const char*, unsigned, _Bool overflow)> callback = [input, &state] (const char *data, unsigned length, _Bool overflow) -> _Bool {
(void)overflow;
if (state == START) {
if (length == strlen(input) && memcmp(input, data, length) == 0) {
state = OK;
} else {
state = ERROR;
}
} else if (state == OK) {
state = ERROR;
}
return true;
};
termpaint_input *input_ctx = termpaint_input_new();
wrap(termpaint_input_set_raw_filter_cb, input_ctx, callback);
termpaint_input_add_data(input_ctx, input, strlen(input));
return state == OK;
}
void die(std::string msg) {
std::cout << msg << std::endl;
exit(1);
}
void check_tok(std::string filename) {
std::ifstream istrm(filename);
while (true) {
std::string instr, rawstr;
istrm >> instr;
if (instr.empty()) {
break;
}
rawstr = instr;
while (rawstr.find("\\e") != std::string::npos)
rawstr.replace(rawstr.find("\\e"), 2, "\033");
if (!parses_as_one(rawstr.c_str())) {
puts(instr.c_str());
}
}
}
static int hexToInt(char input) {
if ('0' <= input && input <= '9') {
return input - '0';
} else if ('a' <= input && input <= 'f') {
return input - 'a' + 10;
} else if ('A' <= input && input <= 'F') {
return input - 'A' + 10;
} else {
die("file contains unparsable hex values");
}
return -1;
}
void update_interpretation(std::string filename) {
picojson::value rootval;
{
std::ifstream istrm(filename, std::ios::binary);
istrm >> rootval;
if (istrm.fail()) {
die(fmt::format("Error while reading '{}': {}", filename, picojson::get_last_error()));
}
}
jarray newSequences;
for (const auto &caseval : rootval.get<jobject>()["sequences"].get<jarray>()) {
jobject caseobj = caseval.get<jobject>();
std::string keyId = caseobj["keyId"].get<std::string>();
if (!caseobj.count("raw")) {
newSequences.emplace_back(caseobj);
continue;
}
std::string rawInputHex = caseobj["raw"].get<std::string>();
std::string rawInput;
for (size_t i=0; i < rawInputHex.size(); i+=2) {
unsigned char ch;
ch = (hexToInt(rawInputHex[i]) << 4) + hexToInt(rawInputHex[i+1]);
rawInput.push_back(static_cast<char>(ch));
}
jobject result;
result.emplace("keyId", keyId);
result.emplace("raw", rawInputHex);
enum { START, GOT_EVENT, GOT_SYNC } state = START;
std::function<void(termpaint_event* event)> event_callback
= [&] (termpaint_event* event) -> void {
bool wasSync = event->type == TERMPAINT_EV_KEY && event->key.atom == termpaint_input_i_resync();
if (state == START) {
if (wasSync) {
die(fmt::format("Unable to interpret value for key '{}'. Seems to start with sync", keyId));
}
if (event->type == TERMPAINT_EV_UNKNOWN) {
result.emplace("type", "unknown");
} else if (event->type == TERMPAINT_EV_CHAR) {
std::string modifiers;
modifiers += (event->c.modifier & TERMPAINT_MOD_SHIFT) ? "S" : " ";
modifiers += (event->c.modifier & TERMPAINT_MOD_ALT) ? "A" : " ";
modifiers += (event->c.modifier & TERMPAINT_MOD_CTRL) ? "C" : " ";
result.emplace("type", "char");
result.emplace("mod", modifiers);
result.emplace("chars", std::string(event->c.string, event->c.length));
} else if (event->type == TERMPAINT_EV_KEY) {
std::string modifiers;
modifiers += (event->key.modifier & TERMPAINT_MOD_SHIFT) ? "S" : " ";
modifiers += (event->key.modifier & TERMPAINT_MOD_ALT) ? "A" : " ";
modifiers += (event->key.modifier & TERMPAINT_MOD_CTRL) ? "C" : " ";
result.emplace("type", "key");
result.emplace("mod", modifiers);
result.emplace("key", event->key.atom);
} else {
die(fmt::format("Unable to interpret value for key '{}'. Event type was {}", keyId, event->type));
}
state = GOT_EVENT;
} else if (state == GOT_EVENT) {
if (!wasSync) {
die(fmt::format("Unable to interpret value for key '{}'. Parses as multiple keys", keyId));
}
state = GOT_SYNC;
} else {
die(fmt::format("Unable to interpret value for key '{}'. State was {}", keyId, (int)state));
}
};
termpaint_input *input_ctx = termpaint_input_new();
wrap(termpaint_input_set_event_cb, input_ctx, event_callback);
termpaint_input_add_data(input_ctx, rawInput.data(), rawInput.size());
if (termpaint_input_peek_buffer_length(input_ctx)) {
termpaint_input_add_data(input_ctx, "\033[0n", 4);
}
newSequences.emplace_back(result);
}
rootval.get<jobject>()["sequences"] = picojson::value(newSequences);
std::ofstream outputfile(filename, std::ios::binary);
rootval.serialize(std::ostream_iterator<char>(outputfile), true);
}
static const char usage[] =
R"(Manual checking tool for libtermpaint.
Usage:
mcheck tok <file>
mcheck update-interpretation <file>
mcheck (-h | --help)
)";
int main(int argc, const char** argv)
{
std::map<std::string, docopt::value> args = docopt::docopt(usage, { argv + 1, argv + argc });
if (args["tok"].asBool()) {
check_tok(args["<file>"].asString());
}
if (args["update-interpretation"].asBool()) {
update_interpretation(args["<file>"].asString());
}
return 0;
}
|