1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120 |
- #include "terminal.h"
- #include "utils.h"
- #include <ctype.h>
- #include <fcntl.h>
- #include <fstream>
- #include <iomanip>
- #include <iostream>
- #include <map>
- #include <pty.h>
- #include <sstream>
- #include <stdio.h>
- #include <stdlib.h> // random()
- #include <string.h>
- #include <string>
- #include <strings.h> // strcasecmp
- #include <sys/select.h>
- #include <sys/wait.h>
- #include <termios.h>
- #include <time.h>
- #include <unistd.h>
- /*
- `Tip the DoorMan`
- DoorMan is a BBS door manager that handles translating UTF-8 to CP437 for
- using ncurses based programs as BBS doors.
- We might start with using the quite excellent iconv library for our
- conversions, but I believe I'll eventually switch to using a map of some sort
- to do the process. (Calling a library over and over again to convert the same
- chars to the same thing over and over again just sounds "wrong".)
- -NOXLATE (No Translation, leave UTF-8 as-is)
- -ICONV (Use ICONV)
- (Default will be using our own table.)
- We will also need to convert the user input (which will be a BBS Terminal
- program) into whatever Linux expects from users. Function keys, arrow keys,
- etc.
- -NOTERM (Don't translate the terminal input)
- Logging will be a huge requirement to figure much of this out!
- -NOLOG (Disable logging)
- We'll want to optionally capture/eat Ctrl-C, because most programs quit on
- that.
- -NOC (No Ctrl-C, not the default)
- Anything not matching here is considered to be the program + arguments.
- */
- /*
- "FEATURES"
- We don't understand color code 96/97. (Bright white/bright cyan). Maybe try
- mapping those back to the original non-bright colors?
- There's a possibility of missing keys (if they aren't all received in one
- "packet"). ?
- Unicode double lines are definitely missing! Yikes!
- Anything else that exists in unicode that maps over to high ASCII.
- https://en.wikipedia.org/wiki/Code_page_437
- I'm not sure, but it almost looks like it has the CP437 char and then the
- unicode char below it. That might be exactly what I need!
- https://jrgraphix.net/r/Unicode/2500-257F
- http://xahlee.info/comp/unicode_drawing_shapes.html
- Still need to finish up function keys.
- still having issues with chars (mc, pageup/pagedown -- garbled chars, that looks
- like missing unicode multi-byte issues)
- */
- enum TRANSLATE { NONE, ICONV, INTERNAL } translate = NONE;
- /*
- https://softwareengineering.stackexchange.com/questions/141973/how-do-you-achieve-a-numeric-versioning-scheme-with-git
- Use tags to mark commits with version numbers:
- git tag -a v2.5 -m 'Version 2.5'
- Push tags upstream—this is not done by default:
- git push --tags
- Then use the describe command:
- git describe --tags --long
- */
- std::string version = "0.0.0"; // DMVERSION;
- // #include <signal.h> // handle Ctrl-C/SIGINT
- /* Log level guideline:
- * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected.
- * Process can't continue and must be terminated.
- * Example: division by zero, unexpected modifications from other thread.
- * - ZF_LOG_ERROR - happened something possible, but highly unexpected. The
- * process is able to recover and continue execution.
- * Example: out of memory (could also be FATAL if not handled properly).
- * - ZF_LOG_WARN - happened something that *usually* should not happen and
- * significantly changes application behavior for some period of time.
- * Example: configuration file not found, auth error.
- * - ZF_LOG_INFO - happened significant life cycle event or major state
- * transition.
- * Example: app started, user logged in.
- * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the
- * execution path. Usually disabled in release builds.
- * - ZF_LOG_VERBOSE - all other events. Usually disabled in release builds.
- *
- * *Ideally*, log file of debugged, well tested, production ready application
- * should be empty or very small. Choosing a right log level is as important as
- * providing short and self descriptive log message.
- */
- /*
- #define ZF_LOG_VERBOSE 1
- #define ZF_LOG_DEBUG 2
- #define ZF_LOG_INFO 3
- #define ZF_LOG_WARN 4
- #define ZF_LOG_ERROR 5
- #define ZF_LOG_FATAL 6
- */
- // When debugging low-level, use this:
- // #define ZF_LOG_LEVEL ZF_LOG_VERBOSE
- // Except this doesn't work. It needs to be anywere the
- // zf_log.h is included.
- // LOGGING with file output
- #include "zf_log.h"
- FILE *g_log_file;
- static void file_output_callback(const zf_log_message *msg, void *arg) {
- (void)arg;
- *msg->p = '\n';
- fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file);
- fflush(g_log_file);
- }
- static void file_output_close(void) { fclose(g_log_file); }
- static int file_output_open(const char *const log_path) {
- g_log_file = fopen(log_path, "a");
- if (!g_log_file) {
- ZF_LOGW("Failed to open log file %s", log_path);
- return 0;
- }
- atexit(file_output_close);
- zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
- return 1;
- }
- void log_flush(void) { fflush(g_log_file); }
- // END LOGGING
- /*
- This is done. :D My buffering system works with stack'em.
- TO FIX: Stop using c strings, must use char * buffer + int length.
- MAY CONTAIN NULL VALUES.
- Rework some things here.
- Here's the "plan":
- if buffer is EMPTY:
- time_idle = 1;
- // setup for "random timeout value mess"
- // we're in luck! The last parameter is time interval/timeout. :D
- timeout.tv_sec = 10; // randrange(10-25)
- timeout.tv_usec = 0;
- NOT EMPTY:
- // we're in luck! The last parameter is time interval/timeout. :D
- timeout.tv_sec = 0;
- timeout.tv_usec = 10; // Wild Guess Here? Maybe higher, maybe
- lower? time_idle = 0;
- ON READ:
- read/append to current buffer.
- We can't use nulls -- what if they are using ZModem, there's nulls in
- the file! Look for trailing / the very last "\r\n".
- (I could mangle/chunk it line by line. But I'm not sure I'd need to do
- that.)
- Optional "mangle" buffer up to that very point -- and send up to that
- point.
- Option #2: Maybe we send everything if program has been running for
- under 20 seconds. This would allow the ANSI detect to not get screwed up by
- this new idea.
- ON TIMEOUT:
- if time_idle:
- Activate funny harry timeout events.
- else:
- Ok, we *STILL* haven't received any more characters into the buffer --
- even after waiting. (Maybe we haven't waited long enough?)
- send the pending information in the buffer and clear it out.
- Maybe this is a prompt, and there won't be a \r\n.
- This allows for cleaner process of "lines" of buffer. We shouldn't break
- in the midDLE OF A WORD. Downside is that we sit on buffer contents a
- little while / some amount of time -- which will add some lag to prompts
- showing up.
- (LAG? Are you kidding?)
- ZModem:
- start: "rz^M**"...
- 05-12 18:12:15.916 >> rz^M**^XB00000000000000^M<8A>^Q
- 05-12 18:12:15.928 << **\x18B0100000023be50\r\n\x11
- 05-12 18:12:15.928 >> *^XC^D
- 05-12 18:12:15.939 << **\x18B0900000000a87c\r\n\x11
- 05-12 18:12:15.940 >> *^XC
- # Start of PK zipfile.
- 05-12 18:12:15.941 >> PK^C^D^T
- end:
- 05-12 18:26:38.700 << **\x18B0100000023be50\r\n\x11
- 05-12 18:26:38.700 >> **^XB0823a77600344c^M<8A>
- 05-12 18:26:38.711 << **\x18B0800000000022d\r\n
- 05-12 18:26:38.712 >> OO^MESC[0m
- */
- // TODO: Get everything above this -- into another file.
- /*
- void display_line(const char *line) {
- char input[1024];
- char output[1024];
- strcpy(input, line);
- converter.convert(input, output, sizeof(output));
- printf("%s\n", output);
- }
- */
- void terminal_output(TRANSLATE xlate, std::string &buffer) {
- /*
- The current problem is this. The unichoad characters are getting spilt by
- buffer breaks.
- */
- static Terminal term;
- // static IConv converter("UTF-8", "CP437");
- // static IConv converter("CP437", "UTF-8"); // TO, FROM
- ZF_LOGV_MEM(buffer.data(), buffer.size(), "Buffer now:");
- ZF_LOGD("Buffer: %s", repr(buffer.c_str()));
- if (xlate == NONE) {
- // no translation
- write(STDOUT_FILENO, buffer.data(), buffer.size());
- buffer.clear();
- } else {
- std::string ansi;
- int dcs_mode = term.dcs();
- // The Hunt for Red Unichoad
- // https://en.wikipedia.org/wiki/UTF-8
- /*
- Number
- of bytes First
- code point Last
- code point Byte 1 Byte 2 Byte 3 Byte 4
- 1 U+0000 U+007F 0xxxxxxx
- 2 U+0080 U+07FF 110xxxxx 10xxxxxx
- 3 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
- 4 U+10000 U+10FFFF[18] 11110xxx 10xxxxxx 10xxxxxx
- 10xxxxxx
- */
- int length = buffer.size();
- int save = 0; // default to saving zero characters.
- char uchoad;
- // 1 char searches:
- if (length >= 1) {
- uchoad = buffer[length - 1];
- if (unicode_len(uchoad))
- save = 1;
- }
- if (length >= 2) {
- uchoad = buffer[length - 2];
- if (unicode_len(uchoad) > 2)
- save = 2;
- }
- // We'll probably never see any of these, but it's here if we need it.
- if (length >= 3) {
- uchoad = buffer[length - 3];
- if (unicode_len(uchoad) > 3)
- save = 3;
- }
- std::string saved;
- if (save) {
- ZF_LOGE("Saving %d chars", save);
- ZF_LOGV_MEM(buffer.data(), buffer.size(), "PRE-SAVE:");
- saved = buffer.substr(length - save, save);
- buffer = buffer.substr(0, length - save);
- ZF_LOGV_MEM(buffer.data(), buffer.size(), "After Save:");
- ZF_LOGV_MEM(saved.data(), saved.size(), "SAVED:");
- }
- // TO FIX: Pull chars out, and look up in the map. Don't brute force your
- // way through all of the times.
- static std::map<std::string, int> utf8cp437 = {
- {"\xe2\x98\xba", 1},
- {"\xe2\x98\xbb", 2},
- {"\xe2\x99\xa5", 3},
- {"\xe2\x99\xa6", 4},
- {"\xe2\x99\xa3", 5},
- {"\xe2\x99\xa0", 6},
- // {"\xe2\x80\xa2", 7}, {"\xe2\x97\x98", 8},
- {"\xe2\x97\x8b", 9},
- //{"\xe2\x97\x99", 0x0a},
- {"\xe2\x99\x82", 0x0b},
- {"\xe2\x99\x80", 0x0c},
- //{"\xe2\x99\xaa", 0x0d},
- {"\xe2\x99\xab", 0x0e},
- {"\xe2\x98\xbc", 0x0f},
- {"\xe2\x96\xba", 0x10},
- {"\xe2\x97\x84", 0x11},
- {"\xe2\x86\x95", 0x12},
- {"\xe2\x80\xbc", 0x13},
- {"\xc2\xb6", 0x14},
- {"\xc2\xa7", 0x15},
- {"\xe2\x96\xac", 0x16},
- {"\xe2\x86\xa8", 0x17},
- {"\xe2\x86\x91", 0x18},
- {"\xe2\x86\x93", 0x19},
- {"\xe2\x86\x92", 0x1a},
- {"\xe2\x86\x90", 0x1b},
- {"\xe2\x88\x9f", 0x1c},
- {"\xe2\x86\x94", 0x1d},
- {"\xe2\x96\xb2", 0x1e},
- {"\xe2\x96\xbc", 0x1f},
- {"\xe2\x8c\x82", 0x7f},
- {"\xc3\x87", 0x80},
- {"\xc3\xbc", 0x81},
- {"\xc3\xa9", 0x82},
- {"\xc3\xa2", 0x83},
- {"\xc3\xa4", 0x84},
- {"\xc3\xa0", 0x85},
- {"\xc3\xa5", 0x86},
- {"\xc3\xa7", 0x87},
- {"\xc3\xaa", 0x88},
- {"\xc3\xab", 0x89},
- {"\xc3\xa8", 0x8a},
- {"\xc3\xaf", 0x8b},
- {"\xc3\xae", 0x8c},
- {"\xc3\xac", 0x8d},
- {"\xc3\x84", 0x8e},
- {"\xc3\x85", 0x8f},
- {"\xc3\x89", 0x90},
- {"\xc3\xa6", 0x91},
- {"\xc3\x86", 0x92},
- {"\xc3\xb4", 0x93},
- {"\xc3\xb6", 0x94},
- {"\xc3\xb2", 0x95},
- {"\xc3\xbb", 0x96},
- {"\xc3\xb9", 0x97},
- {"\xc3\xbf", 0x98},
- {"\xc3\x96", 0x99},
- {"\xc3\x9c", 0x9a},
- {"\xc2\xa2", 0x9b},
- {"\xc2\xa3", 0x9c},
- {"\xc2\xa5", 0x9d},
- {"\xe2\x82\xa7", 0x9e},
- {"\xc6\x92", 0x9f},
- {"\xc3\xa1", 0xa0},
- {"\xc3\xad", 0xa1},
- {"\xc3\xb3", 0xa2},
- {"\xc3\xba", 0xa3},
- {"\xc3\xb1", 0xa4},
- {"\xc3\x91", 0xa5},
- {"\xc2\xaa", 0xa6},
- {"\xc2\xba", 0xa7},
- {"\xc2\xbf", 0xa8},
- {"\xe2\x8c\x90", 0xa9},
- {"\xc2\xac", 0xaa},
- {"\xc2\xbd", 0xab},
- {"\xc2\xbc", 0xac},
- {"\xc2\xa1", 0xad},
- {"\xc2\xab", 0xae},
- {"\xc2\xbb", 0xaf},
- {"\xe2\x96\x91", 0xb0},
- {"\xe2\x96\x92", 0xb1},
- {"\xe2\x96\x93", 0xb2},
- {"\xe2\x94\x82", 0xb3},
- {"\xe2\x94\xa4", 0xb4},
- {"\xe2\x95\xa1", 0xb5},
- {"\xe2\x95\xa2", 0xb6},
- {"\xe2\x95\x96", 0xb7},
- {"\xe2\x95\x95", 0xb8},
- {"\xe2\x95\xa3", 0xb9},
- {"\xe2\x95\x91", 0xba},
- {"\xe2\x95\x97", 0xbb},
- {"\xe2\x95\x9d", 0xbc},
- {"\xe2\x95\x9c", 0xbd},
- {"\xe2\x95\x9b", 0xbe},
- {"\xe2\x94\x90", 0xbf},
- {"\xe2\x94\x94", 0xc0},
- {"\xe2\x94\xb4", 0xc1},
- {"\xe2\x94\xac", 0xc2},
- {"\xe2\x94\x9c", 0xc3},
- {"\xe2\x94\x80", 0xc4},
- {"\xe2\x94\xbc", 0xc5},
- {"\xe2\x95\x9e", 0xc6},
- {"\xe2\x95\x9f", 0xc7},
- {"\xe2\x95\x9a", 0xc8},
- {"\xe2\x95\x94", 0xc9},
- {"\xe2\x95\xa9", 0xca},
- {"\xe2\x95\xa6", 0xcb},
- {"\xe2\x95\xa0", 0xcc},
- {"\xe2\x95\x90", 0xcd},
- {"\xe2\x95\xac", 0xce},
- {"\xe2\x95\xa7", 0xcf},
- {"\xe2\x95\xa8", 0xd0},
- {"\xe2\x95\xa4", 0xd1},
- {"\xe2\x95\xa5", 0xd2},
- {"\xe2\x95\x99", 0xd3},
- {"\xe2\x95\x98", 0xd4},
- {"\xe2\x95\x92", 0xd5},
- {"\xe2\x95\x93", 0xd6},
- {"\xe2\x95\xab", 0xd7},
- {"\xe2\x95\xaa", 0xd8},
- {"\xe2\x94\x98", 0xd9},
- {"\xe2\x94\x8c", 0xda},
- {"\xe2\x96\x88", 0xdb},
- {"\xe2\x96\x84", 0xdc},
- {"\xe2\x96\x8c", 0xdd},
- {"\xe2\x96\x90", 0xde},
- {"\xe2\x96\x80", 0xdf},
- {"\xce\xb1", 0xe0},
- {"\xc3\x9f", 0xe1},
- {"\xce\x93", 0xe2},
- {"\xcf\x80", 0xe3},
- {"\xce\xa3", 0xe4},
- {"\xcf\x83", 0xe5},
- {"\xc2\xb5", 0xe6},
- {"\xcf\x84", 0xe7},
- {"\xce\xa6", 0xe8},
- {"\xce\x98", 0xe9},
- {"\xce\xa9", 0xea},
- {"\xce\xb4", 0xeb},
- {"\xe2\x88\x9e", 0xec},
- {"\xcf\x86", 0xed},
- {"\xce\xb5", 0xee},
- {"\xe2\x88\xa9", 0xef},
- {"\xe2\x89\xa1", 0xf0},
- {"\xc2\xb1", 0xf1},
- {"\xe2\x89\xa5", 0xf2},
- {"\xe2\x89\xa4", 0xf3},
- {"\xe2\x8c\xa0", 0xf4},
- {"\xe2\x8c\xa1", 0xf5},
- {"\xc3\xb7", 0xf6},
- {"\xe2\x89\x88", 0xf7},
- {"\xc2\xb0", 0xf8},
- {"\xe2\x88\x99", 0xf9},
- {"\xc2\xb7", 0xfa},
- {"\xe2\x88\x9a", 0xfb},
- {"\xe2\x81\xbf", 0xfc},
- {"\xc2\xb2", 0xfd},
- {"\xe2\x96\xa0", 0xfe},
- {"\xc2\xa0", 0xff}};
- int c = 0;
- size_t pos;
- std::vector<char> keys = {'\xc2', '\xc3', '\xce', '\xcf', '\xe2'};
- for (auto ikey = keys.begin(); ikey != keys.end(); ++ikey) {
- size_t spos = 0;
- while ((pos = buffer.find(*ikey, spos)) != std::string::npos) {
- char uchoad = buffer[pos];
- int ulen = unicode_len(uchoad);
- std::string ustr = buffer.substr(pos, ulen);
- ZF_LOGE_MEM(ustr.data(), ustr.length(), "unicode %d : (@%ld)", ulen,
- pos);
- auto upos = utf8cp437.find(ustr.c_str());
- if (upos != utf8cp437.end()) {
- std::string to(1, (char)upos->second);
- while (replace(buffer, ustr, to)) {
- c++;
- }
- spos = 0;
- } else {
- ZF_LOGE("failed to locate.");
- spos = pos + 1;
- }
- }
- }
- if (c) {
- ZF_LOGE("vector/map Replaced %d", c);
- ZF_LOGV_MEM(buffer.data(), buffer.size(), "After Replace:");
- }
- /*
- if ((buffer.find('\xe2') != std::string::npos) ||
- (buffer.find('\xc2') != std::string::npos) ||
- (buffer.find('\xc3') != std::string::npos) ||
- (buffer.find('\xce') != std::string::npos) ||
- (buffer.find('\xcf') != std::string::npos)) {
- c = 0;
- for (auto it = utf8cp437.begin(); it != utf8cp437.end(); ++it) {
- while (replace(buffer, it->first, std::string(1, char(it->second))))
- { c++;
- }
- }
- if (c) {
- ZF_LOGE("Replaced %d", c);
- ZF_LOGV_MEM(buffer.data(), buffer.size(), "After Replace:");
- }
- }
- */
- // Convert bright to bold.
- while (replace(buffer, "\x1b[90m", "\x1b[1;30m")) {
- };
- while (replace(buffer, "\x1b[91m", "\x1b[1;31m")) {
- };
- while (replace(buffer, "\x1b[92m", "\x1b[1;32m")) {
- };
- while (replace(buffer, "\x1b[93m", "\x1b[1;33m")) {
- };
- while (replace(buffer, "\x1b[94m", "\x1b[1;34m")) {
- };
- while (replace(buffer, "\x1b[95m", "\x1b[1;35m")) {
- };
- while (replace(buffer, "\x1b[96m", "\x1b[1;36m")) {
- };
- while (replace(buffer, "\x1b[97m", "\x1b[1;37m")) {
- };
- // Search for DCS mode, and translate the line characters.
- for (auto iter = buffer.begin(); iter != buffer.end(); ++iter) {
- char c = *iter;
- Terminal::termchar tc = term.putchar(c);
- if (tc.in_ansi) {
- ansi.append(1, c);
- if (tc.ansi == Terminal::ANSI_TYPE::START)
- continue;
- // Ok, the ansi command is completed.
- if (tc.ansi ==
- Terminal::ANSI_TYPE::DCS) { // Terminal::ANSI_TYPE::DCS) {
- // Ok, EAT this command! it's worthless to the terminal we're talking
- // to.
- dcs_mode = term.dcs();
- }
- } else {
- // Ok, we're not in any ANSI mode.
- /*
- ┌ ─ ┬ ┐
- │ │ │ │
- ├ ─ ┼ ┤
- └ ─ ┴ ┘
- We cheat here. Instead of converting to unicode, we convert to CP437
- chars.
- */
- if (dcs_mode == 0) {
- switch (c) {
- case 'j':
- c = '\xd9'; // '┘';
- break;
- case 'k':
- c = '\xbf'; // '┐';
- break;
- case 'l':
- c = '\xda'; // '┌';
- break;
- case 'm':
- c = '\xc0'; // '└';
- break;
- case 'n':
- c = '\xc5'; // '┼';
- break;
- case 'q':
- c = '\xc4'; // '─';
- break;
- case 't':
- c = '\xc3'; // '├';
- break;
- case 'u':
- c = '\xb4'; // '┤';
- break;
- case 'v':
- c = '\xc1'; // '┴';
- break;
- case 'w':
- c = '\xc2'; // '┬';
- break;
- case 'x':
- c = '\xb3'; // '│';
- break;
- default:
- ZF_LOGE("DCS(0): I don't have translation for [%c]", c);
- }
- ZF_LOGE("DCS(0) Converted [%c] to [%c]", *iter, c);
- *iter = c;
- }
- }
- }
- write(STDOUT_FILENO, buffer.data(), buffer.size());
- buffer.clear();
- buffer.insert(0, saved);
- saved.clear();
- }
- }
- void help(void) {
- printf("Usage:\n");
- printf("Translate (default to internal)\n");
- printf("\t-NOXLATE\tNo translation\n");
- printf("\t-ICONV\tUse ICONV library\n");
- printf("\t-NOLOG\tNo logging\n");
- printf("\t-NOC\tDon't allow Ctrl-C\n");
- printf("\t-NOTERM\tDon't Translate Keys\n");
- printf("\t-LOGTIME\tAdd Hours/Minutes to logfile name\n");
- }
- int main(int argc, char *argv[]) {
- int master;
- pid_t pid;
- // init_harry();
- srandom(time(NULL));
- /*
- -NOXLATE (No Translation, leave UTF-8 as-is)
- -ICONV (Use ICONV)
- -NOTERM (Don't translate the terminal input)
- -NOLOG (Disable logging)
- -NOC (No Ctrl-C, not the default)
- */
- translate =
- ICONV; // Default to internal translation map (well, not initially.)
- int CATCH_CTRLC = 0;
- int NO_LOGGING = 0;
- int TERM_XLATE = 1;
- int LOG_TIME = 0;
- int x;
- for (x = 1; x < argc; x++) {
- if (strcasecmp("-NOXLATE", argv[x]) == 0) {
- translate = NONE;
- continue;
- }
- if (strcasecmp("-ICONV", argv[x]) == 0) {
- translate = ICONV;
- continue;
- }
- if (strcasecmp("-NOC", argv[x]) == 0) {
- CATCH_CTRLC = 1;
- continue;
- }
- if (strcasecmp("-NOLOG", argv[x]) == 0) {
- NO_LOGGING = 1;
- continue;
- }
- if (strcasecmp("-NOTERM", argv[x]) == 0) {
- TERM_XLATE = 0;
- continue;
- }
- if (strcasecmp("-LOGTIME", argv[x]) == 0) {
- LOG_TIME = 1;
- continue;
- }
- if ((strcasecmp("-H", argv[x]) == 0) || (strcmp("-?", argv[x]) == 0)) {
- // display help information.
- help();
- return 0;
- }
- if (argv[x][0] == '-') {
- printf("I don't understand arg %s\n", argv[x]);
- help();
- return 0;
- }
- break;
- }
- if (x == argc) {
- printf("No command to run found.\n");
- help();
- return 0;
- }
- std::string logfile;
- {
- std::ostringstream buffer;
- time_t now = time(NULL);
- struct tm *tmp;
- tmp = gmtime(&now);
- // tmp->tm_mon
- buffer << "doorman-" << tmp->tm_year + 1900 << "-" << std::setfill('0')
- << std::setw(2) << tmp->tm_mon + 1 << "-" << std::setfill('0')
- << std::setw(2) << tmp->tm_mday;
- if (LOG_TIME) {
- buffer << "-" << std::setw(2) << tmp->tm_hour << std::setw(2)
- << tmp->tm_min;
- }
- buffer << ".log";
- logfile = buffer.str();
- }
- if (NO_LOGGING) {
- zf_log_set_output_level(ZF_LOG_NONE);
- } else {
- if (!file_output_open((const char *)logfile.c_str()))
- return 2;
- };
- ZF_LOGE("DoorMan %s", version.c_str());
- // Build the new command to run here
- char *args[20]; // max 20 args
- char *target_exec = argv[x];
- ZF_LOGE("Target: (%d) [%s]", x, target_exec);
- // build new args list
- args[0] = target_exec;
- // We have the target, skip to the next arg
- if (x < argc)
- ++x;
- int ax = 1;
- for (; x < argc; x++) {
- args[ax] = argv[x];
- ZF_LOGD("ARG %d : %s", ax, args[ax]);
- ax++;
- };
- // null term the list
- args[ax] = NULL;
- pid = forkpty(&master, NULL, NULL, NULL);
- // impossible to fork
- if (pid < 0) {
- return 1;
- }
- // child
- else if (pid == 0) {
- // This has been done up above.
- /*
- char *args[20]; // max 20 args
- int x;
- char new_exec[] = TARGET;
- // build new args list
- args[0] = new_exec;
- for (x = 1; x < argc; x++) {
- args[x] = argv[x];
- };
- // null term the list
- args[x] = NULL;
- */
- // run TARGET, run!
- /*
- for (x = 0; args[x] != nullptr; x++) {
- ZF_LOGD("%d : %s", x, args[x]);
- }
- */
- execvp(target_exec, args);
- }
- // parent
- else {
- struct termios tios, orig1;
- struct timeval timeout;
- ZF_LOGD("starting");
- tcgetattr(master, &tios);
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON);
- /*
- tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- tios.c_oflag &= ~(OPOST);
- */
- tcsetattr(master, TCSAFLUSH, &tios);
- tcgetattr(1, &orig1);
- tios = orig1;
- /*
- tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- tios.c_oflag &= ~(OPOST);
- */
- tios.c_iflag &= ~(ICRNL | IXON | ISTRIP | BRKINT);
- tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- tios.c_oflag &= ~(OPOST);
- tios.c_cflag |= (CS8);
- // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
- tcsetattr(1, TCSAFLUSH, &tios);
- /*
- This doesn't need to be static -- because it is part of
- main. Once main ends, we're done.
- */
- #define BSIZE 128
- std::string buffer;
- buffer.reserve(BSIZE);
- for (;;) {
- // define estruturas para o select, que serve para verificar qual
- // se tornou "pronto pra uso"
- fd_set read_fd;
- fd_set write_fd;
- fd_set except_fd;
- // inicializa as estruturas
- FD_ZERO(&read_fd);
- FD_ZERO(&write_fd);
- FD_ZERO(&except_fd);
- // atribui o descritor master, obtido pelo forkpty, ao read_fd
- FD_SET(master, &read_fd);
- // atribui o stdin ao read_fd
- FD_SET(STDIN_FILENO, &read_fd);
- // o descritor tem que ser unico para o programa, a documentacao
- // recomenda um calculo entre os descritores sendo usados + 1
- timeout.tv_sec = 60;
- timeout.tv_usec = 0;
- // if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0)
- // {
- if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0) {
- ZF_LOGI("TIMEOUT");
- // This means timeout!
- }
- // read_fd esta atribuido com read_fd?
- if (FD_ISSET(master, &read_fd)) {
- // leia o que bc esta mandando
- // ZF_LOGD("read (%d) %d bytes", size, BSIZE - size);
- char read_buffer[BSIZE + 1];
- int total;
- // We may adjust this later on (adjusting read length).
- if ((total = read(master, read_buffer, BSIZE)) != -1) {
- // Ok, we've read more into the buffer.
- ZF_LOGV("Read %d bytes", total);
- buffer.append(read_buffer, total);
- terminal_output(translate, buffer);
- } else {
- ZF_LOGE("read = -1, break/exit");
- break;
- }
- }
- // read_fd esta atribuido com a entrada padrao?
- if (FD_ISSET(STDIN_FILENO, &read_fd)) {
- // leia a entrada padrao
- char input[BSIZE];
- int r = read(STDIN_FILENO, &input, BSIZE);
- std::string input_str(input, r);
- // input[r] = 0;
- // F11 didn't work, it's a "fullscreen" key.
- static const char *func_keys[12] = {"\x1bOP",
- "\x1bOQ",
- "\x1bOR",
- "\x1bOS",
- "\x1b[15\x7e",
- "\x1b[17\x7e",
- "\x1b[18\x7e",
- "\x1b[19\x7e",
- "\x1b[20\x7e",
- "\x1b[21\x7e",
- "",
- "\x1b[24]x7e"};
- // left right up down ins del home end PgUp PgDown
- static const char *dir_keys[] = {
- "\x1bOD", "\x1bOC", "\x1bOA", "\x1bOB", "\x1b[2\x7e",
- "\x1b[3\x7e", "\x1bOH", "\x1b[F", "\x1b[5\x7e", "\x1b[6\x7e"};
- // e escreva no bc
- ZF_LOGI_MEM(input_str.data(), input_str.size(), "INPUT <<");
- if (CATCH_CTRLC) {
- remove_all(input_str, '\x03');
- }
- if (TERM_XLATE) {
- // We're going to try it without any buffer or understanding of ANSI
- // codes.
- int c = 0;
- while (replace(input_str, "\x1b[A", dir_keys[2])) { // OA
- c++;
- }
- while (replace(input_str, "\x1b[B", dir_keys[3])) { // OB
- c++;
- }
- while (replace(input_str, "\x1b[C", dir_keys[1])) { // OC
- c++;
- }
- while (replace(input_str, "\x1b[D", dir_keys[0])) { // OD
- c++;
- }
- while (replace(input_str, "\x1b[U", dir_keys[9])) { // [6~
- c++;
- }
- while (replace(input_str, "\x1b[V", dir_keys[8])) { // [5~
- c++;
- }
- while (replace(input_str, "\x1b[H", dir_keys[6])) { // OH
- c++;
- }
- while (replace(input_str, "\x1b[K", dir_keys[7])) { // OF
- c++;
- }
- while (replace(input_str, "\x1b[@", dir_keys[4])) { // [2~
- c++;
- }
- /* F1-F5 are "broken". They all map to this.
- while (replace(input_str, "\x1b[1", "\x1bOR")) { // F?
- c++;
- }
- */
- while (replace(input_str, "\r\n", "\r")) {
- c++;
- }
- // NON-Doorways, DEL = 0x7f
- // Terminal DEL = "\x1b[3\7e"
- while (replace(input_str, std::string("\x7f"), dir_keys[5])) {
- c++;
- }
- // DOORWAYS mode:
- // Future Fix: Find char(0) pos, get next char, look those 2 chars up
- // in a map.
- if (input_str.find(char(0)) != std::string::npos) {
- // We found a null character, TIAS
- ZF_LOGE("null found. Start doorways replace mode.");
- using namespace std::string_literals;
- // F1-F12
- while (replace(input_str, std::string("\x00\x3b"s),
- std::string(func_keys[0]))) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x3c"s), func_keys[1])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x3d"s), func_keys[2])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x3e"s), func_keys[3])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x3f"s), func_keys[4])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x40"s), func_keys[5])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x41"s), func_keys[6])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x42"s), func_keys[7])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x43"s), func_keys[8])) {
- c++;
- }
- while (replace(input_str, std::string("\x00\x44"s), func_keys[9])) {
- c++;
- }
- /*
- while (replace(input_str, std::string("\x00\x85"s), func_keys[10]))
- { // F11 c++;
- } */
- while (
- replace(input_str, std::string("\x00\x86"s), func_keys[11])) {
- c++;
- }
- // Delete = 0x7f (not here)
- // Left, Right, Up, Down, Insert, Home, End, PgUp, PgDwn
- while (replace(input_str, std::string("\x00\x4b"s),
- dir_keys[0])) { // Left
- c++;
- }
- while (replace(input_str, std::string("\x00\x4d"s),
- dir_keys[1])) { // Right
- c++;
- }
- while (replace(input_str, std::string("\x00\x48"s),
- dir_keys[2])) { // Up
- c++;
- }
- while (replace(input_str, std::string("\x00\x50"s),
- dir_keys[3])) { // Down
- c++;
- }
- while (replace(input_str, std::string("\x00\x52"s),
- dir_keys[4])) { // Ins
- c++;
- }
- // Delete is 0x7f (no null)
- while (replace(input_str, std::string("\x00\x47"s),
- dir_keys[6])) { // Home
- c++;
- }
- while (replace(input_str, std::string("\x00\x4f"s),
- dir_keys[7])) { // End
- c++;
- }
- while (replace(input_str, std::string("\x00\x49"s),
- dir_keys[8])) { // Page Up
- c++;
- }
- while (replace(input_str, std::string("\x00\x51"s),
- dir_keys[9])) { // Page Down
- c++;
- }
- if (c) {
- ZF_LOGE_MEM(input_str.data(), input_str.size(),
- "Input (%d changed):", c);
- }
- }
- ZF_LOGD_MEM(input_str.data(), input_str.size(),
- "Write Input String:");
- // write(master, &input, r);
- write(master, input_str.data(), input_str.size());
- // This is INPUT from the USER
- // ZF_LOGI_MEM( input, strlen(input), "<< ");
- }
- }
- }
- // Ok, we get to EXIT, but the ... program is still open/running. Why?
- // I think this is a netcat problem. It can't tell that the program has
- // been closed, so it just flips out.
- // FIXED with pipexec
- /*
- close(master);
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- int wstate;
- int rc = waitpid(pid, &wstate, 0);
- ZF_LOGE("waitpid %d (%d) = %d", rc, pid, wstate);
- */
- // Restore terminal
- tcsetattr(1, TCSAFLUSH, &orig1);
- ZF_LOGD("exit");
- }
- return 0;
- }
|