825 lines
18 KiB
C
825 lines
18 KiB
C
|
/*
|
||
|
* Copyright (C) 2013 Intel Corporation
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <termios.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "terminal.h"
|
||
|
#include "history.h"
|
||
|
|
||
|
/*
|
||
|
* Character sequences recognized by code in this file
|
||
|
* Leading ESC 0x1B is not included
|
||
|
*/
|
||
|
#define SEQ_INSERT "[2~"
|
||
|
#define SEQ_DELETE "[3~"
|
||
|
#define SEQ_HOME "OH"
|
||
|
#define SEQ_END "OF"
|
||
|
#define SEQ_PGUP "[5~"
|
||
|
#define SEQ_PGDOWN "[6~"
|
||
|
#define SEQ_LEFT "[D"
|
||
|
#define SEQ_RIGHT "[C"
|
||
|
#define SEQ_UP "[A"
|
||
|
#define SEQ_DOWN "[B"
|
||
|
#define SEQ_STAB "[Z"
|
||
|
#define SEQ_M_n "n"
|
||
|
#define SEQ_M_p "p"
|
||
|
#define SEQ_CLEFT "[1;5D"
|
||
|
#define SEQ_CRIGHT "[1;5C"
|
||
|
#define SEQ_CUP "[1;5A"
|
||
|
#define SEQ_CDOWN "[1;5B"
|
||
|
#define SEQ_SLEFT "[1;2D"
|
||
|
#define SEQ_SRIGHT "[1;2C"
|
||
|
#define SEQ_SUP "[1;2A"
|
||
|
#define SEQ_SDOWN "[1;2B"
|
||
|
#define SEQ_MLEFT "[1;3D"
|
||
|
#define SEQ_MRIGHT "[1;3C"
|
||
|
#define SEQ_MUP "[1;3A"
|
||
|
#define SEQ_MDOWN "[1;3B"
|
||
|
|
||
|
#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k }
|
||
|
struct ansii_sequence {
|
||
|
int code;
|
||
|
const char *sequence;
|
||
|
};
|
||
|
|
||
|
/* Table connects single int key codes with character sequences */
|
||
|
static const struct ansii_sequence ansii_sequnces[] = {
|
||
|
KEY_SEQUENCE(INSERT),
|
||
|
KEY_SEQUENCE(DELETE),
|
||
|
KEY_SEQUENCE(HOME),
|
||
|
KEY_SEQUENCE(END),
|
||
|
KEY_SEQUENCE(PGUP),
|
||
|
KEY_SEQUENCE(PGDOWN),
|
||
|
KEY_SEQUENCE(LEFT),
|
||
|
KEY_SEQUENCE(RIGHT),
|
||
|
KEY_SEQUENCE(UP),
|
||
|
KEY_SEQUENCE(DOWN),
|
||
|
KEY_SEQUENCE(CLEFT),
|
||
|
KEY_SEQUENCE(CRIGHT),
|
||
|
KEY_SEQUENCE(CUP),
|
||
|
KEY_SEQUENCE(CDOWN),
|
||
|
KEY_SEQUENCE(SLEFT),
|
||
|
KEY_SEQUENCE(SRIGHT),
|
||
|
KEY_SEQUENCE(SUP),
|
||
|
KEY_SEQUENCE(SDOWN),
|
||
|
KEY_SEQUENCE(MLEFT),
|
||
|
KEY_SEQUENCE(MRIGHT),
|
||
|
KEY_SEQUENCE(MUP),
|
||
|
KEY_SEQUENCE(MDOWN),
|
||
|
KEY_SEQUENCE(STAB),
|
||
|
KEY_SEQUENCE(M_p),
|
||
|
KEY_SEQUENCE(M_n),
|
||
|
{ 0, NULL }
|
||
|
};
|
||
|
|
||
|
#define KEY_SEQUNCE_NOT_FINISHED -1
|
||
|
#define KEY_C_C 3
|
||
|
#define KEY_C_D 4
|
||
|
#define KEY_C_L 12
|
||
|
|
||
|
#define isseqence(c) ((c) == 0x1B)
|
||
|
|
||
|
/*
|
||
|
* Number of characters that consist of ANSI sequence
|
||
|
* Should not be less then longest string in ansi_sequences
|
||
|
*/
|
||
|
#define MAX_ASCII_SEQUENCE 10
|
||
|
|
||
|
static char current_sequence[MAX_ASCII_SEQUENCE];
|
||
|
static int current_sequence_len = -1;
|
||
|
|
||
|
/* single line typed by user goes here */
|
||
|
static char line_buf[LINE_BUF_MAX];
|
||
|
/* index of cursor in input line */
|
||
|
static int line_buf_ix = 0;
|
||
|
/* current length of input line */
|
||
|
static int line_len = 0;
|
||
|
|
||
|
/* line index used for fetching lines from history */
|
||
|
static int line_index = 0;
|
||
|
|
||
|
static char prompt_buf[10] = "> ";
|
||
|
static const char *const noprompt = "";
|
||
|
static const char *current_prompt = prompt_buf;
|
||
|
static const char *prompt = prompt_buf;
|
||
|
/*
|
||
|
* Moves cursor to right or left
|
||
|
*
|
||
|
* n - positive - moves cursor right
|
||
|
* n - negative - moves cursor left
|
||
|
*/
|
||
|
static void terminal_move_cursor(int n)
|
||
|
{
|
||
|
if (n < 0) {
|
||
|
for (; n < 0; n++)
|
||
|
putchar('\b');
|
||
|
} else if (n > 0) {
|
||
|
printf("%*s", n, line_buf + line_buf_ix);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Draw command line */
|
||
|
void terminal_draw_command_line(void)
|
||
|
{
|
||
|
/*
|
||
|
* this needs to be checked here since line_buf is not cleared
|
||
|
* before parsing event though line_len and line_buf_ix are
|
||
|
*/
|
||
|
if (line_len > 0)
|
||
|
printf("%s%s", prompt, line_buf);
|
||
|
else
|
||
|
printf("%s", prompt);
|
||
|
|
||
|
/* move cursor to it's place */
|
||
|
terminal_move_cursor(line_buf_ix - line_len);
|
||
|
}
|
||
|
|
||
|
/* inserts string into command line at cursor position */
|
||
|
void terminal_insert_into_command_line(const char *p)
|
||
|
{
|
||
|
int len = strlen(p);
|
||
|
|
||
|
if (line_len == line_buf_ix) {
|
||
|
strcat(line_buf, p);
|
||
|
printf("%s", p);
|
||
|
line_len = line_len + len;
|
||
|
line_buf_ix = line_len;
|
||
|
} else {
|
||
|
memmove(line_buf + line_buf_ix + len,
|
||
|
line_buf + line_buf_ix, line_len - line_buf_ix + 1);
|
||
|
memmove(line_buf + line_buf_ix, p, len);
|
||
|
printf("%s", line_buf + line_buf_ix);
|
||
|
line_buf_ix += len;
|
||
|
line_len += len;
|
||
|
terminal_move_cursor(line_buf_ix - line_len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Prints string and redraws command line */
|
||
|
int terminal_print(const char *format, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
int ret;
|
||
|
|
||
|
va_start(args, format);
|
||
|
|
||
|
ret = terminal_vprint(format, args);
|
||
|
|
||
|
va_end(args);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Prints string and redraws command line */
|
||
|
int terminal_vprint(const char *format, va_list args)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
printf("\r%*s\r", (int) line_len + 1, " ");
|
||
|
|
||
|
ret = vprintf(format, args);
|
||
|
|
||
|
terminal_draw_command_line();
|
||
|
|
||
|
fflush(stdout);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Call this when text in line_buf was changed
|
||
|
* and line needs to be redrawn
|
||
|
*/
|
||
|
static void terminal_line_replaced(void)
|
||
|
{
|
||
|
int len = strlen(line_buf);
|
||
|
|
||
|
/* line is shorter that previous */
|
||
|
if (len < line_len) {
|
||
|
/* if new line is shorter move cursor to end of new end */
|
||
|
while (line_buf_ix > len) {
|
||
|
putchar('\b');
|
||
|
line_buf_ix--;
|
||
|
}
|
||
|
|
||
|
/* If cursor was not at the end, move it to the end */
|
||
|
if (line_buf_ix < line_len)
|
||
|
printf("%.*s", line_len - line_buf_ix,
|
||
|
line_buf + line_buf_ix);
|
||
|
/* over write end of previous line */
|
||
|
while (line_len >= len++)
|
||
|
putchar(' ');
|
||
|
}
|
||
|
|
||
|
/* draw new line */
|
||
|
printf("\r%s%s", prompt, line_buf);
|
||
|
/* set up indexes to new line */
|
||
|
line_len = strlen(line_buf);
|
||
|
line_buf_ix = line_len;
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
|
||
|
static void terminal_clear_line(void)
|
||
|
{
|
||
|
line_buf[0] = '\0';
|
||
|
terminal_line_replaced();
|
||
|
}
|
||
|
|
||
|
static void terminal_clear_screen(void)
|
||
|
{
|
||
|
line_buf[0] = '\0';
|
||
|
line_buf_ix = 0;
|
||
|
line_len = 0;
|
||
|
|
||
|
printf("\x1b[2J\x1b[1;1H%s", prompt);
|
||
|
}
|
||
|
|
||
|
static void terminal_delete_char(void)
|
||
|
{
|
||
|
/* delete character under cursor if not at the very end */
|
||
|
if (line_buf_ix >= line_len)
|
||
|
return;
|
||
|
/*
|
||
|
* Prepare buffer with one character missing
|
||
|
* trailing 0 is moved
|
||
|
*/
|
||
|
line_len--;
|
||
|
memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1,
|
||
|
line_len - line_buf_ix + 1);
|
||
|
/* print rest of line from current cursor position */
|
||
|
printf("%s \b", line_buf + line_buf_ix);
|
||
|
/* move back cursor */
|
||
|
terminal_move_cursor(line_buf_ix - line_len);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function tries to replace current line with specified line in history
|
||
|
* new_line_index - new line to show, -1 to show oldest
|
||
|
*/
|
||
|
static void terminal_get_line_from_history(int new_line_index)
|
||
|
{
|
||
|
new_line_index = history_get_line(new_line_index,
|
||
|
line_buf, LINE_BUF_MAX);
|
||
|
|
||
|
if (new_line_index >= 0) {
|
||
|
terminal_line_replaced();
|
||
|
line_index = new_line_index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function searches history back or forward for command line that starts
|
||
|
* with characters up to cursor position
|
||
|
*
|
||
|
* back - true - searches backward
|
||
|
* back - false - searches forward (more recent commands)
|
||
|
*/
|
||
|
static void terminal_match_hitory(bool back)
|
||
|
{
|
||
|
char buf[line_buf_ix + 1];
|
||
|
int line;
|
||
|
int matching_line = -1;
|
||
|
int dir = back ? 1 : -1;
|
||
|
|
||
|
line = line_index + dir;
|
||
|
while (matching_line == -1 && line >= 0) {
|
||
|
int new_line_index;
|
||
|
|
||
|
new_line_index = history_get_line(line, buf, line_buf_ix + 1);
|
||
|
if (new_line_index < 0)
|
||
|
break;
|
||
|
|
||
|
if (0 == strncmp(line_buf, buf, line_buf_ix))
|
||
|
matching_line = line;
|
||
|
line += dir;
|
||
|
}
|
||
|
|
||
|
if (matching_line >= 0) {
|
||
|
int pos = line_buf_ix;
|
||
|
terminal_get_line_from_history(matching_line);
|
||
|
/* move back to cursor position to original place */
|
||
|
line_buf_ix = pos;
|
||
|
terminal_move_cursor(pos - line_len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Converts terminal character sequences to single value representing
|
||
|
* keyboard keys
|
||
|
*/
|
||
|
static int terminal_convert_sequence(int c)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* Not in sequence yet? */
|
||
|
if (current_sequence_len == -1) {
|
||
|
/* Is ansi sequence detected by 0x1B ? */
|
||
|
if (isseqence(c)) {
|
||
|
current_sequence_len++;
|
||
|
return KEY_SEQUNCE_NOT_FINISHED;
|
||
|
}
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/* Inside sequence */
|
||
|
current_sequence[current_sequence_len++] = c;
|
||
|
current_sequence[current_sequence_len] = '\0';
|
||
|
for (i = 0; ansii_sequnces[i].code; ++i) {
|
||
|
/* Matches so far? */
|
||
|
if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence,
|
||
|
current_sequence_len))
|
||
|
continue;
|
||
|
|
||
|
/* Matches as a whole? */
|
||
|
if (ansii_sequnces[i].sequence[current_sequence_len] == 0) {
|
||
|
current_sequence_len = -1;
|
||
|
return ansii_sequnces[i].code;
|
||
|
}
|
||
|
|
||
|
/* partial match (not whole sequence yet) */
|
||
|
return KEY_SEQUNCE_NOT_FINISHED;
|
||
|
}
|
||
|
|
||
|
terminal_print("ansi char 0x%X %c\n", c);
|
||
|
/*
|
||
|
* Sequence does not match
|
||
|
* mark that no in sequence any more, return char
|
||
|
*/
|
||
|
current_sequence_len = -1;
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
typedef void (*terminal_action)(int c, line_callback process_line);
|
||
|
|
||
|
#define TERMINAL_ACTION(n) \
|
||
|
static void n(int c, void (*process_line)(char *line))
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_null)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/* Mapping between keys and function */
|
||
|
typedef struct {
|
||
|
int key;
|
||
|
terminal_action func;
|
||
|
} KeyAction;
|
||
|
|
||
|
int action_keys[] = {
|
||
|
KEY_SEQUNCE_NOT_FINISHED,
|
||
|
KEY_LEFT,
|
||
|
KEY_RIGHT,
|
||
|
KEY_HOME,
|
||
|
KEY_END,
|
||
|
KEY_DELETE,
|
||
|
KEY_CLEFT,
|
||
|
KEY_CRIGHT,
|
||
|
KEY_SUP,
|
||
|
KEY_SDOWN,
|
||
|
KEY_UP,
|
||
|
KEY_DOWN,
|
||
|
KEY_BACKSPACE,
|
||
|
KEY_INSERT,
|
||
|
KEY_PGUP,
|
||
|
KEY_PGDOWN,
|
||
|
KEY_CUP,
|
||
|
KEY_CDOWN,
|
||
|
KEY_SLEFT,
|
||
|
KEY_SRIGHT,
|
||
|
KEY_MLEFT,
|
||
|
KEY_MRIGHT,
|
||
|
KEY_MUP,
|
||
|
KEY_MDOWN,
|
||
|
KEY_STAB,
|
||
|
KEY_M_n,
|
||
|
KEY_M_p,
|
||
|
KEY_C_C,
|
||
|
KEY_C_D,
|
||
|
KEY_C_L,
|
||
|
'\t',
|
||
|
'\r',
|
||
|
'\n',
|
||
|
};
|
||
|
|
||
|
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
||
|
|
||
|
/*
|
||
|
* current_actions holds all recognizable kes and actions for them
|
||
|
* additional element (index 0) is used for default action
|
||
|
*/
|
||
|
static KeyAction current_actions[NELEM(action_keys) + 1];
|
||
|
|
||
|
/* KeyAction comparator by key, for qsort and bsearch */
|
||
|
static int KeyActionKeyCompare(const void *a, const void *b)
|
||
|
{
|
||
|
return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key;
|
||
|
}
|
||
|
|
||
|
/* Find action by key, NULL if no action for this key */
|
||
|
static KeyAction *terminal_get_action(int key)
|
||
|
{
|
||
|
KeyAction a = { .key = key };
|
||
|
|
||
|
return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a),
|
||
|
KeyActionKeyCompare);
|
||
|
}
|
||
|
|
||
|
/* Sets new set of actions to use */
|
||
|
static void terminal_set_actions(const KeyAction *actions)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* Make map with empty function for every key */
|
||
|
for (i = 0; i < NELEM(action_keys); ++i) {
|
||
|
/*
|
||
|
* + 1 due to 0 index reserved for default action that is
|
||
|
* called for non mapped key
|
||
|
*/
|
||
|
current_actions[i + 1].key = action_keys[i];
|
||
|
current_actions[i + 1].func = terminal_action_null;
|
||
|
}
|
||
|
|
||
|
/* Sort action from 1 (index 0 - default action) */
|
||
|
qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction),
|
||
|
KeyActionKeyCompare);
|
||
|
/* Set default action (first in array) */
|
||
|
current_actions[0] = *actions++;
|
||
|
|
||
|
/* Copy rest of actions into their places */
|
||
|
for (; actions->key; ++actions) {
|
||
|
KeyAction *place = terminal_get_action(actions->key);
|
||
|
|
||
|
if (place)
|
||
|
place->func = actions->func;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_left)
|
||
|
{
|
||
|
/* if not at the beginning move to previous character */
|
||
|
if (line_buf_ix <= 0)
|
||
|
return;
|
||
|
line_buf_ix--;
|
||
|
terminal_move_cursor(-1);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_right)
|
||
|
{
|
||
|
/*
|
||
|
* If not at the end, just print current character
|
||
|
* and modify position
|
||
|
*/
|
||
|
if (line_buf_ix < line_len)
|
||
|
putchar(line_buf[line_buf_ix++]);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_home)
|
||
|
{
|
||
|
/* move to beginning of line and update position */
|
||
|
printf("\r%s", prompt);
|
||
|
line_buf_ix = 0;
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_end)
|
||
|
{
|
||
|
/* if not at the end of line */
|
||
|
if (line_buf_ix < line_len) {
|
||
|
/* print everything from cursor */
|
||
|
printf("%s", line_buf + line_buf_ix);
|
||
|
/* just modify current position */
|
||
|
line_buf_ix = line_len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_del)
|
||
|
{
|
||
|
terminal_delete_char();
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_word_left)
|
||
|
{
|
||
|
int old_pos;
|
||
|
/*
|
||
|
* Move by word left
|
||
|
*
|
||
|
* Are we at the beginning of line?
|
||
|
*/
|
||
|
if (line_buf_ix <= 0)
|
||
|
return;
|
||
|
|
||
|
old_pos = line_buf_ix;
|
||
|
line_buf_ix--;
|
||
|
/* skip spaces left */
|
||
|
while (line_buf_ix && isspace(line_buf[line_buf_ix]))
|
||
|
line_buf_ix--;
|
||
|
|
||
|
/* skip all non spaces to the left */
|
||
|
while (line_buf_ix > 0 &&
|
||
|
!isspace(line_buf[line_buf_ix - 1]))
|
||
|
line_buf_ix--;
|
||
|
|
||
|
/* move cursor to new position */
|
||
|
terminal_move_cursor(line_buf_ix - old_pos);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_word_right)
|
||
|
{
|
||
|
int old_pos;
|
||
|
/*
|
||
|
* Move by word right
|
||
|
*
|
||
|
* are we at the end of line?
|
||
|
*/
|
||
|
if (line_buf_ix >= line_len)
|
||
|
return;
|
||
|
|
||
|
old_pos = line_buf_ix;
|
||
|
/* skip all spaces */
|
||
|
while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix]))
|
||
|
line_buf_ix++;
|
||
|
|
||
|
/* skip all non spaces */
|
||
|
while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix]))
|
||
|
line_buf_ix++;
|
||
|
/*
|
||
|
* Move cursor to right by printing text
|
||
|
* between old cursor and new
|
||
|
*/
|
||
|
if (line_buf_ix > old_pos)
|
||
|
printf("%.*s", (int) (line_buf_ix - old_pos),
|
||
|
line_buf + old_pos);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_history_begin)
|
||
|
{
|
||
|
terminal_get_line_from_history(-1);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_history_end)
|
||
|
{
|
||
|
if (line_index > 0)
|
||
|
terminal_get_line_from_history(0);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_history_up)
|
||
|
{
|
||
|
terminal_get_line_from_history(line_index + 1);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_history_down)
|
||
|
{
|
||
|
if (line_index > 0)
|
||
|
terminal_get_line_from_history(line_index - 1);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_tab)
|
||
|
{
|
||
|
/* tab processing */
|
||
|
process_tab(line_buf, line_buf_ix);
|
||
|
}
|
||
|
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_backspace)
|
||
|
{
|
||
|
if (line_buf_ix <= 0)
|
||
|
return;
|
||
|
|
||
|
if (line_buf_ix == line_len) {
|
||
|
printf("\b \b");
|
||
|
line_len = --line_buf_ix;
|
||
|
line_buf[line_len] = 0;
|
||
|
} else {
|
||
|
putchar('\b');
|
||
|
line_buf_ix--;
|
||
|
line_len--;
|
||
|
memmove(line_buf + line_buf_ix,
|
||
|
line_buf + line_buf_ix + 1,
|
||
|
line_len - line_buf_ix + 1);
|
||
|
printf("%s \b", line_buf + line_buf_ix);
|
||
|
terminal_move_cursor(line_buf_ix - line_len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_find_history_forward)
|
||
|
{
|
||
|
/* Search history forward */
|
||
|
terminal_match_hitory(false);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_find_history_backward)
|
||
|
{
|
||
|
/* Search history forward */
|
||
|
terminal_match_hitory(true);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_ctrl_c)
|
||
|
{
|
||
|
terminal_clear_line();
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_ctrl_d)
|
||
|
{
|
||
|
if (line_len > 0) {
|
||
|
terminal_delete_char();
|
||
|
} else {
|
||
|
puts("");
|
||
|
exit(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_clear_screen)
|
||
|
{
|
||
|
terminal_clear_screen();
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_enter)
|
||
|
{
|
||
|
/*
|
||
|
* On new line add line to history
|
||
|
* forget history position
|
||
|
*/
|
||
|
history_add_line(line_buf);
|
||
|
line_len = 0;
|
||
|
line_buf_ix = 0;
|
||
|
line_index = -1;
|
||
|
/* print new line */
|
||
|
putchar(c);
|
||
|
prompt = noprompt;
|
||
|
process_line(line_buf);
|
||
|
/* clear current line */
|
||
|
line_buf[0] = '\0';
|
||
|
prompt = current_prompt;
|
||
|
printf("%s", prompt);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_default)
|
||
|
{
|
||
|
char str[2] = { c, 0 };
|
||
|
|
||
|
if (!isprint(c))
|
||
|
/*
|
||
|
* TODO: remove this print once all meaningful sequences
|
||
|
* are identified
|
||
|
*/
|
||
|
printf("char-0x%02x\n", c);
|
||
|
else if (line_buf_ix < LINE_BUF_MAX - 1)
|
||
|
terminal_insert_into_command_line(str);
|
||
|
}
|
||
|
|
||
|
/* Callback to call when user hit enter during prompt for */
|
||
|
static line_callback prompt_callback;
|
||
|
|
||
|
static KeyAction normal_actions[] = {
|
||
|
{ 0, terminal_action_default },
|
||
|
{ KEY_LEFT, terminal_action_left },
|
||
|
{ KEY_RIGHT, terminal_action_right },
|
||
|
{ KEY_HOME, terminal_action_home },
|
||
|
{ KEY_END, terminal_action_end },
|
||
|
{ KEY_DELETE, terminal_action_del },
|
||
|
{ KEY_CLEFT, terminal_action_word_left },
|
||
|
{ KEY_CRIGHT, terminal_action_word_right },
|
||
|
{ KEY_SUP, terminal_action_history_begin },
|
||
|
{ KEY_SDOWN, terminal_action_history_end },
|
||
|
{ KEY_UP, terminal_action_history_up },
|
||
|
{ KEY_DOWN, terminal_action_history_down },
|
||
|
{ '\t', terminal_action_tab },
|
||
|
{ KEY_BACKSPACE, terminal_action_backspace },
|
||
|
{ KEY_M_n, terminal_action_find_history_forward },
|
||
|
{ KEY_M_p, terminal_action_find_history_backward },
|
||
|
{ KEY_C_C, terminal_action_ctrl_c },
|
||
|
{ KEY_C_D, terminal_action_ctrl_d },
|
||
|
{ KEY_C_L, terminal_action_clear_screen },
|
||
|
{ '\r', terminal_action_enter },
|
||
|
{ '\n', terminal_action_enter },
|
||
|
{ 0, NULL },
|
||
|
};
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_answer)
|
||
|
{
|
||
|
putchar(c);
|
||
|
|
||
|
terminal_set_actions(normal_actions);
|
||
|
/* Restore default prompt */
|
||
|
current_prompt = prompt_buf;
|
||
|
|
||
|
/* No prompt for prints */
|
||
|
prompt = noprompt;
|
||
|
line_buf_ix = 0;
|
||
|
line_len = 0;
|
||
|
/* Call user function with what was typed */
|
||
|
prompt_callback(line_buf);
|
||
|
|
||
|
line_buf[0] = 0;
|
||
|
/* promot_callback could change current_prompt */
|
||
|
prompt = current_prompt;
|
||
|
|
||
|
printf("%s", prompt);
|
||
|
}
|
||
|
|
||
|
TERMINAL_ACTION(terminal_action_prompt_ctrl_c)
|
||
|
{
|
||
|
printf("^C\n");
|
||
|
line_buf_ix = 0;
|
||
|
line_len = 0;
|
||
|
line_buf[0] = 0;
|
||
|
|
||
|
current_prompt = prompt_buf;
|
||
|
prompt = current_prompt;
|
||
|
terminal_set_actions(normal_actions);
|
||
|
|
||
|
printf("%s", prompt);
|
||
|
}
|
||
|
|
||
|
static KeyAction prompt_actions[] = {
|
||
|
{ 0, terminal_action_default },
|
||
|
{ KEY_LEFT, terminal_action_left },
|
||
|
{ KEY_RIGHT, terminal_action_right },
|
||
|
{ KEY_HOME, terminal_action_home },
|
||
|
{ KEY_END, terminal_action_end },
|
||
|
{ KEY_DELETE, terminal_action_del },
|
||
|
{ KEY_CLEFT, terminal_action_word_left },
|
||
|
{ KEY_CRIGHT, terminal_action_word_right },
|
||
|
{ KEY_BACKSPACE, terminal_action_backspace },
|
||
|
{ KEY_C_C, terminal_action_prompt_ctrl_c },
|
||
|
{ KEY_C_D, terminal_action_ctrl_d },
|
||
|
{ '\r', terminal_action_answer },
|
||
|
{ '\n', terminal_action_answer },
|
||
|
{ 0, NULL },
|
||
|
};
|
||
|
|
||
|
void terminal_process_char(int c, line_callback process_line)
|
||
|
{
|
||
|
KeyAction *a;
|
||
|
|
||
|
c = terminal_convert_sequence(c);
|
||
|
|
||
|
/* Get action for this key */
|
||
|
a = terminal_get_action(c);
|
||
|
|
||
|
/* No action found, get default one */
|
||
|
if (a == NULL)
|
||
|
a = ¤t_actions[0];
|
||
|
|
||
|
a->func(c, process_line);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
|
||
|
void terminal_prompt_for(const char *s, line_callback process_line)
|
||
|
{
|
||
|
current_prompt = s;
|
||
|
if (prompt != noprompt) {
|
||
|
prompt = s;
|
||
|
terminal_clear_line();
|
||
|
}
|
||
|
prompt_callback = process_line;
|
||
|
terminal_set_actions(prompt_actions);
|
||
|
}
|
||
|
|
||
|
static struct termios origianl_tios;
|
||
|
|
||
|
static void terminal_cleanup(void)
|
||
|
{
|
||
|
tcsetattr(0, TCSANOW, &origianl_tios);
|
||
|
}
|
||
|
|
||
|
void terminal_setup(void)
|
||
|
{
|
||
|
struct termios tios;
|
||
|
|
||
|
terminal_set_actions(normal_actions);
|
||
|
|
||
|
tcgetattr(0, &origianl_tios);
|
||
|
tios = origianl_tios;
|
||
|
|
||
|
/*
|
||
|
* Turn off echo since all editing is done by hand,
|
||
|
* Ctrl-c handled internally
|
||
|
*/
|
||
|
tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK);
|
||
|
tcsetattr(0, TCSANOW, &tios);
|
||
|
|
||
|
/* Restore terminal at exit */
|
||
|
atexit(terminal_cleanup);
|
||
|
|
||
|
printf("%s", prompt);
|
||
|
fflush(stdout);
|
||
|
}
|