Last active
November 7, 2017 21:18
-
-
Save parkovski/c0af657c8371760e89c6a82febbd4f84 to your computer and use it in GitHub Desktop.
Windows ANSI line editor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
class ModeRestore { | |
HANDLE in, out; | |
DWORD inmode, outmode; | |
public: | |
ModeRestore(HANDLE in, DWORD inmode, HANDLE out, DWORD outmode) : in(in), out(out) { | |
DWORD written; | |
if (GetConsoleMode(in, &this->inmode)) { | |
SetConsoleMode(in, inmode); | |
} else { | |
this->inmode = 0; | |
} | |
if (GetConsoleMode(out, &this->outmode)) { | |
SetConsoleMode(out, outmode); | |
} else { | |
this->outmode = 0; | |
} | |
} | |
~ModeRestore() { | |
if (inmode) { | |
SetConsoleMode(in, inmode); | |
} | |
if (outmode) { | |
SetConsoleMode(out, outmode); | |
} | |
} | |
}; | |
int main() { | |
DWORD written; | |
auto out = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, 3, 0, 3, 0, 0); | |
if (out == INVALID_HANDLE_VALUE) { | |
return 1; | |
} | |
auto in = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, 3, 0, 3, 0, 0); | |
if (in == INVALID_HANDLE_VALUE) { | |
WriteFile(out, "no input\n", 9, &written, nullptr); | |
return 1; | |
} | |
ModeRestore moderestore(in, 0x204, out, 0xF); | |
SetConsoleCtrlHandler(nullptr, TRUE); | |
char esc[16]; | |
int esc_idx = 0; | |
int cursor; | |
auto handle_esc = [&]() -> bool { | |
if (esc_idx == 1) { | |
return true; | |
} | |
if (esc[1] != '[') { | |
return false; | |
} | |
if (esc_idx == 2) { | |
return true; | |
} | |
if (esc[2] == 'C' || esc[2] == 'D') { | |
// ^[[C, ^[[D = right/left | |
if (esc[2] == 'C') { | |
++cursor; | |
} else { | |
if (--cursor < 0) { cursor = 0; } | |
} | |
char m[] = { '\x1b', '[', 0, 0, 'G' }; | |
m[2] = '0' + cursor / 10; | |
m[3] = '0' + cursor % 10; | |
WriteFile(out, m, 5, &written, nullptr); | |
} else if (esc[2] == 'A') { | |
// ^[[A = up | |
WriteFile(out, "\x1b[S", 3, &written, nullptr); | |
} else if (esc[2] == 'B') { | |
// ^[[B = down | |
WriteFile(out, "\x1b[T", 3, &written, nullptr); | |
} | |
esc_idx = 0; | |
return true; | |
}; | |
int line = 0; | |
auto handle_char = [&](char c) { | |
if (esc_idx > 0) { | |
// Continue escape sequence | |
esc[esc_idx++] = c; | |
if (handle_esc()) { | |
return; | |
} | |
} else if (c == 0x1b) { | |
// Begin escape sequence | |
esc[0] = 0x1b; | |
esc_idx = 1; | |
} else if (c == 8 || c == 0x7f) { | |
// Backspace | |
char m[] = { '\x1b', '[', 0, 0, 'G', ' ', '\x1b', '[', 0, 0, 'G' }; | |
if (--cursor < 0) { cursor = 0; } | |
m[2] = '0' + cursor / 10; | |
m[3] = '0' + cursor % 10; | |
m[8] = '0' + cursor / 10; | |
m[9] = '0' + cursor % 10; | |
WriteFile(out, m, 11, &written, nullptr); | |
} else if (c == 11) { // ^K | |
// Clear the screen | |
WriteFile(out, "\x1b[2J\x1b[1;0H> \x1b[3G", 16, &written, nullptr); | |
cursor = 3; | |
line = 1; | |
} else if (c >= 0 && c < 0x20) { | |
// Echo the control sequence | |
char m[2] = { '^', 0 }; | |
char *s = "@ABCDEFGHI\0KL\0NOPQRSTUVWXYZ[\\]^_"; | |
if ((m[1] = s[c]) == 0) { | |
return; | |
} | |
cursor += 2; | |
WriteFile(out, m, 2, &written, nullptr); | |
} else { | |
// Echo the character | |
++cursor; | |
WriteFile(out, &c, 1, &written, nullptr); | |
} | |
}; | |
auto next_line = [&]() { | |
// Query cursor position | |
WriteFile(out, "\x1b[6n", 4, &written, nullptr); | |
int stage = 0; | |
int nl = 0, nc = 0; | |
while (true) { | |
char c; | |
DWORD read; | |
ReadFile(in, &c, 1, &read, nullptr); | |
if (stage == 0 && c == 0x1b) { | |
stage = 1; | |
continue; | |
} | |
if (stage == 1 && c == '[') { | |
stage = 2; | |
continue; | |
} | |
if (stage == 2) { | |
if (c >= '0' && c <= '9') { | |
nl = nl * 10 + (c - '0'); | |
} else if (c == ';') { | |
stage = 3; | |
} else { | |
break; | |
} | |
continue; | |
} | |
if (stage == 3) { | |
if (c >= '0' && c <= '9') { | |
nc = nc * 10 + (c - '0'); | |
continue; | |
} else if (c == 'R') { | |
line = nl + 1; | |
} | |
} | |
break; | |
} | |
cursor = 3; | |
char m[] = { '\x1b', '[', 0, 0, ';', '0', 'H', '>', ' ', '\x1b', '[', '3', 'G' }; | |
m[2] = '0' + line / 10; | |
m[3] = '0' + line % 10; | |
WriteFile(out, m, 13, &written, nullptr); | |
}; | |
WriteFile(out, "> ", 2, &written, nullptr); | |
cursor = 3; | |
while (true) { | |
char c; | |
DWORD read; | |
while (true) { | |
ReadFile(in, &c, 1, &read, nullptr); | |
if (read == 0) { return 2; } | |
handle_char(c); | |
if (c == 4) { return 0; } | |
if (c == 0xA || c == 0xD) { | |
next_line(); | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment