/* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "input.h" #include "output.h" #include "tty.h" #include "terminal.h" #include "line.h" #include "misc.h" #include #include #include enum { ST_STANDARD, ST_QUOTED, ST_META, ST_CTLX, ST_ESC1, ST_ESC1_1, ST_ESC1_2, /* ESC [ sequences */ ST_ESC2 /* ESC O sequences */ }; static int read_char() { unsigned char c; if (read(fileno(rl_instream), &c, 1) != 1) return EOF; return c; } int read_cmd() { int state; int n; int c; state = ST_STANDARD; n = 0; while (1) { if ((c = read_char()) == EOF) { if (line_end == 0) return make_cmd(CM_EOF); return make_cmd(CM_ACCEPT_LINE); } if (state == ST_QUOTED) /* C-q, C-v */ return make_cmd_char(CM_INSERT_CHAR, c); if (tty_isig) { /* emulation des signaux du terminal */ if (c == tty_intr) return make_cmd(CM_INTERRUPT); if (c == tty_quit || c == tty_susp) /* laisse l'etat inchange */ continue; } switch (state) { case ST_STANDARD: if (isascii(c) && iscntrl(c)) { switch (c) { case ctrl('@'): return make_cmd(CM_SET_MARK); case ctrl('A'): return make_cmd(CM_MOVE_START); case ctrl('B'): return make_cmd(CM_MOVE_BW_CHAR); case ctrl('D'): if (line_end == 0) return make_cmd(CM_EOF); return make_cmd(CM_DELETE_CHAR); case ctrl('E'): return make_cmd(CM_MOVE_END); case ctrl('F'): return make_cmd(CM_MOVE_FW_CHAR); case ctrl('G'): goto abort; case '\b': /* ^H */ return make_cmd(CM_BW_DELETE_CHAR); case '\t': /* ^I */ return make_cmd_char(CM_INSERT_CHAR, '\t'); case '\n': /* ^J */ return make_cmd(CM_ACCEPT_LINE); case '\v': /* ^K */ return make_cmd(CM_KILL_LINE); case '\f': /* ^L */ return make_cmd(CM_CLEAR_SCREEN); case '\r': /* ^M */ return make_cmd(CM_ACCEPT_LINE); case ctrl('N'): return make_cmd(CM_NEXT_HIST); case ctrl('P'): return make_cmd(CM_PREV_HIST); case ctrl('Q'): state = ST_QUOTED; continue; case ctrl('U'): return make_cmd(CM_BW_KILL_LINE); case ctrl('V'): state = ST_QUOTED; continue; case ctrl('W'): return make_cmd(CM_BW_KILL_WORD_WSP); case ctrl('X'): state = ST_CTLX; continue; case ctrl('Y'): return make_cmd(CM_YANK); case '\e': /* ^[ */ state = ST_META; continue; case RUBOUT: return make_cmd(CM_BW_DELETE_CHAR); } break; } return make_cmd_char(CM_INSERT_CHAR, c); case ST_META: /* ESC (Meta) */ if (isascii(c) && isupper(c)) c = tolower(c); switch (c) { case ctrl('G'): goto abort; case '\b': /* ^H */ return make_cmd(CM_BW_KILL_WORD); case '\t': /* ^I */ return make_cmd_char(CM_INSERT_CHAR, '\t'); case ctrl('R'): return make_cmd(CM_REVERT_LINE); case ' ': return make_cmd(CM_SET_MARK); case '<': return make_cmd(CM_BEGIN_HIST); case '>': return make_cmd(CM_END_HIST); case 'O': state = ST_ESC2; continue; case 'b': return make_cmd(CM_MOVE_BW_WORD); case 'd': return make_cmd(CM_KILL_WORD); case 'f': return make_cmd(CM_MOVE_FW_WORD); case 'r': return make_cmd(CM_REVERT_LINE); case 'y': return make_cmd(CM_YANK_POP); case '[': state = ST_ESC1; continue; case RUBOUT: return make_cmd(CM_BW_KILL_WORD); } break; case ST_CTLX: /* C-x */ if (isascii(c) && isupper(c)) c = tolower(c); switch (c) { case ctrl('G'): goto abort; case ctrl('X'): return make_cmd(CM_EXCHANGE_POINT_AND_MARK); case 'r': return make_cmd(CM_REFRESH_LINE); case RUBOUT: return make_cmd(CM_BW_KILL_LINE); } break; case ST_ESC1: /* "\e[" (escape sequence) */ switch (c) { case 'A': /* UpArrow (ANSI) */ return make_cmd(CM_PREV_HIST); case 'B': /* DownArrow (ANSI) */ return make_cmd(CM_NEXT_HIST); case 'C': /* RightArrow (ANSI) */ return make_cmd(CM_MOVE_FW_CHAR); case 'D': /* LeftArrow (ANSI) */ return make_cmd(CM_MOVE_BW_CHAR); case 'F': /* End (ANSI) */ return make_cmd(CM_MOVE_END); case 'H': /* Home (ANSI) */ return make_cmd(CM_MOVE_START); case '[': state = ST_ESC1_1; continue; } if (isascii(c) && isdigit(c)) { n = c - '0'; state = ST_ESC1_2; continue; } break; case ST_ESC1_1: /* "\e[[" */ break; case ST_ESC1_2: /* "\e[{num} */ if (isascii(c) && isdigit(c)) { n = n * 10 + (c - '0'); state = ST_ESC1_2; continue; } if (c == '~') { switch (n) { case 1: /* Home (Linux) */ return make_cmd(CM_MOVE_START); case 2: /* Insert (VT200, Linux) */ return make_cmd(CM_TOOGLE_OVWRT_MODE); case 3: /* Delete (VT200, Linux) */ return make_cmd(CM_DELETE_CHAR); case 4: /* End (Linux) */ return make_cmd(CM_MOVE_END); case 5: /* PageUp (VT200, Linux) */ return make_cmd(CM_PREV_HIST); case 6: /* PageDown (VT200, Linux) */ return make_cmd(CM_NEXT_HIST); } break; } break; case ST_ESC2: /* "\eO" (escape sequence) */ switch (c) { case 'A': /* UpArrow (keypad) */ return make_cmd(CM_PREV_HIST); case 'B': /* DownArrow (keypad) */ return make_cmd(CM_NEXT_HIST); case 'C': /* RightArrow (keypad) */ return make_cmd(CM_MOVE_FW_CHAR); case 'D': /* LeftArrow (keypad) */ return make_cmd(CM_MOVE_BW_CHAR); case 'F': /* End (keypad) */ return make_cmd(CM_MOVE_END); case 'H': /* Home (keypad) */ return make_cmd(CM_MOVE_START); } break; } abort: ding(); state = ST_STANDARD; } }