static void console_buffer_add_string(char *buf, size_t *bytes_read, size_t *count, char *str, size_t size) { while (*count > 0 && size > 0) { buf[(*bytes_read)++] = *str; (*count)--; str++; size--; } if (size > 0) console_add_input(str, size); }
static size_t console_read(struct file *f, char *buf, size_t count) { struct console_file *console_file = (struct console_file *)f; console_lock(); console_retrieve_state(); size_t bytes_read = 0; while (console->input_buffer_head != console->input_buffer_tail && count > 0) { count--; buf[bytes_read++] = console->input_buffer[console->input_buffer_tail]; console->input_buffer_tail = (console->input_buffer_tail + 1) % MAX_INPUT; } if (console->termios.c_lflag & ICANON) { char line[MAX_CANON + 1]; /* One more for storing CR or LF */ size_t len = 0; while (count > 0) { INPUT_RECORD ir; DWORD read; ReadConsoleInputA(console->in, &ir, 1, &read); if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { switch (ir.Event.KeyEvent.wVirtualKeyCode) { case VK_RETURN: { if (!(console->termios.c_iflag & IGNCR)) line[len++] = console->termios.c_iflag & ICRNL ? '\n' : '\r'; size_t r = min(count, len); memcpy(buf + bytes_read, line, r); bytes_read += r; count -= r; if (r < len) { /* Some bytes not fit, add to input buffer */ console_add_input(line + r, len - r); } if (console->termios.c_lflag & ECHO) crnl(); goto read_done; } case VK_BACK: { if (len > 0) { len--; if (console->termios.c_lflag & ECHO) backspace(TRUE); } } default: { char ch = ir.Event.KeyEvent.uChar.AsciiChar; if (ch >= 0x20) { if (len < MAX_CANON) { line[len++] = ch; if (console->termios.c_lflag & ECHO) write_normal(&ch, 1); } } } } } } } else /* Non canonical mode */ { int vtime = console->termios.c_cc[VTIME]; int vmin = console->termios.c_cc[VMIN]; while (count > 0) { if (bytes_read > 0 && bytes_read >= vmin) break; /* If vmin > 0 and vtime == 0, it is a blocking read, otherwise we need to poll first */ if (vtime > 0 || (vmin == 0 && vtime == 0)) { if (WaitForSingleObject(console->in, vtime * 100) == WAIT_TIMEOUT) break; } INPUT_RECORD ir; DWORD read; ReadConsoleInputA(console->in, &ir, 1, &read); if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { switch (ir.Event.KeyEvent.wVirtualKeyCode) { case VK_UP: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOA" : "\x1B[A", 3); break; case VK_DOWN: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOB" : "\x1B[B", 3); break; case VK_RIGHT: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOC" : "\x1B[C", 3); break; case VK_LEFT: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOD" : "\x1B[D", 3); break; case VK_HOME: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOH" : "\x1B[H", 3); break; case VK_END: console_buffer_add_string(buf, &bytes_read, &count, console->cursor_key_mode ? "\x1BOF" : "\x1B[F", 3); break; case VK_INSERT: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[2~", 4); break; case VK_DELETE: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[3~", 4); break; case VK_PRIOR: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[5~", 4); break; case VK_NEXT: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[6~", 4); break; case VK_F1: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOP", 3); break; case VK_F2: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOQ", 3); break; case VK_F3: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOR", 3); break; case VK_F4: console_buffer_add_string(buf, &bytes_read, &count, "\x1BOS", 3); break; case VK_F5: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[15~", 5); break; case VK_F6: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[17~", 5); break; case VK_F7: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[18~", 5); break; case VK_F8: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[19~", 5); break; case VK_F9: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[20~", 5); break; case VK_F10: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[21~", 5); break; case VK_F11: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[23~", 5); break; case VK_F12: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[24~", 5); break; case VK_F13: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[25~", 5); break; case VK_F14: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[26~", 5); break; case VK_F15: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[28~", 5); break; case VK_F16: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[29~", 5); break; case VK_F17: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[31~", 5); break; case VK_F18: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[32~", 5); break; case VK_F19: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[33~", 5); break; case VK_F20: console_buffer_add_string(buf, &bytes_read, &count, "\x1B[34~", 5); break; default: { char ch = ir.Event.KeyEvent.uChar.AsciiChar; if (ch == '\r' && console->termios.c_iflag & IGNCR) break; if (ch == '\r' && console->termios.c_iflag & ICRNL) ch = '\n'; else if (ch == '\n' && console->termios.c_iflag & ICRNL) ch = '\r'; if (ch > 0) { count--; buf[bytes_read++] = ch; if (console->termios.c_lflag & ECHO) write_normal(&ch, 1); } } } } else { /* TODO: Other types of input */ } } } read_done: /* This will make the caret immediately visible */ set_pos(console->x, console->y); console_unlock(); return bytes_read; }
/* Handler for control sequencie introducer, "ESC [" */ static void control_escape_csi(char ch) { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': console->params[console->param_count] = 10 * console->params[console->param_count] + (ch - '0'); break; case ';': if (console->param_count + 1 == CONSOLE_MAX_PARAMS) log_error("Too many console parameters.\n"); else console->param_count++; break; case 'A': /* CUU */ move_up(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'B': /* CUD */ case 'e': /* VPR */ move_down(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'C': /* CUF */ case 'a': /* HPR */ move_right(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'D': /* CUB */ move_left(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'd': /* VPA */ { int y = console->params[0]? console->params[0]: 1; if (y > console->height) y = console->height; set_pos(console->x, y - 1); console->processor = NULL; break; } case 'G': /* CHA */ case '`': /* HPA */ { int x = console->params[0] ? console->params[0] : 1; if (x > console->width) x = console->width; set_pos(x - 1, console->y); console->processor = NULL; break; } case 'H': case 'f': /* Zero or one both represents the first row/column */ if (console->params[0] > 0) console->params[0]--; if (console->params[1] > 0) console->params[1]--; if (console->origin_mode) set_pos(console->params[1], console->scroll_top + console->params[0]); else set_pos(console->params[1], console->params[0]); console->processor = NULL; break; case 'h': if (console->csi_prefix == '?') for (int i = 0; i <= console->param_count; i++) change_private_mode(console->params[i], 1); else for (int i = 0; i <= console->param_count; i++) change_mode(console->params[i], 1); console->processor = NULL; break; case 'J': erase_screen(console->params[0]); console->processor = NULL; break; case 'K': erase_line(console->params[0]); console->processor = NULL; break; case 'l': if (console->csi_prefix == '?') for (int i = 0; i <= console->param_count; i++) change_private_mode(console->params[i], 0); else for (int i = 0; i <= console->param_count; i++) change_mode(console->params[i], 0); console->processor = NULL; break; case 'L': /* IL */ insert_line(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'M': /* DL */ delete_line(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case '@': /* ICH */ insert_character(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'P': /* DCH */ delete_character(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case 'c': if (console->csi_prefix == '>') /* DA2 */ { if (console->params[0] == 0) console_add_input("\x1B[>61;95;0c", 11); else log_warning("DA2 parameter is not zero.\n"); } else /* DA1 */ { if (console->params[0] == 0) log_error("DA1 not supported.\n"); else log_warning("DA1 parameter is not zero.\n"); } console->processor = NULL; break; case 'm': for (int i = 0; i <= console->param_count; i++) { switch (console->params[i]) { case 0: /* Reset */ console->bright = 0; console->reverse = 0; console->foreground = 7; console->background = 0; break; case 1: console->bright = 1; break; case 2: console->bright = 0; break; case 7: console->reverse = 1; break; case 27: console->reverse = 0; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: console->foreground = console->params[i] - 30; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: console->background = console->params[i] - 40; break; default: log_error("Unknown console attribute: %d\n", console->params[i]); } } /* Set updated text attribute */ SetConsoleTextAttribute(console->out, get_text_attribute(console)); console->processor = NULL; break; case 'r': if (console->params[0] == 0) console->params[0] = 1; if (console->params[1] == 0) console->params[1] = console->height; console->scroll_full_screen = (console->params[0] == 1 && console->params[1] == console->height); console->scroll_top = console->params[0] - 1; console->scroll_bottom = console->params[1] - 1; set_pos(0, 0); console->processor = NULL; break; case 'S': /* SU */ scroll_up(console->params[0]? console->params[0]: 1); console->processor = NULL; break; case '?': console->csi_prefix = '?'; break; case '>': console->csi_prefix = '?'; break; default: log_error("control_escape_csi(): Unhandled character %c\n", ch); console->processor = NULL; } }