Skip to content

Instantly share code, notes, and snippets.

@parkovski
Last active November 7, 2017 21:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parkovski/c0af657c8371760e89c6a82febbd4f84 to your computer and use it in GitHub Desktop.
Save parkovski/c0af657c8371760e89c6a82febbd4f84 to your computer and use it in GitHub Desktop.
Windows ANSI line editor
#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