/* 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;
}
}