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