terminal.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. // SPDX-License-Identifier: Apache-2.0
  2. /*
  3. * Copyright (C) 2013 Intel Corporation
  4. *
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include <stdbool.h>
  10. #include <termios.h>
  11. #include <stdlib.h>
  12. #include "terminal.h"
  13. #include "history.h"
  14. /*
  15. * Character sequences recognized by code in this file
  16. * Leading ESC 0x1B is not included
  17. */
  18. #define SEQ_INSERT "[2~"
  19. #define SEQ_DELETE "[3~"
  20. #define SEQ_HOME "OH"
  21. #define SEQ_END "OF"
  22. #define SEQ_PGUP "[5~"
  23. #define SEQ_PGDOWN "[6~"
  24. #define SEQ_LEFT "[D"
  25. #define SEQ_RIGHT "[C"
  26. #define SEQ_UP "[A"
  27. #define SEQ_DOWN "[B"
  28. #define SEQ_STAB "[Z"
  29. #define SEQ_M_n "n"
  30. #define SEQ_M_p "p"
  31. #define SEQ_CLEFT "[1;5D"
  32. #define SEQ_CRIGHT "[1;5C"
  33. #define SEQ_CUP "[1;5A"
  34. #define SEQ_CDOWN "[1;5B"
  35. #define SEQ_SLEFT "[1;2D"
  36. #define SEQ_SRIGHT "[1;2C"
  37. #define SEQ_SUP "[1;2A"
  38. #define SEQ_SDOWN "[1;2B"
  39. #define SEQ_MLEFT "[1;3D"
  40. #define SEQ_MRIGHT "[1;3C"
  41. #define SEQ_MUP "[1;3A"
  42. #define SEQ_MDOWN "[1;3B"
  43. #define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k }
  44. struct ansii_sequence {
  45. int code;
  46. const char *sequence;
  47. };
  48. /* Table connects single int key codes with character sequences */
  49. static const struct ansii_sequence ansii_sequnces[] = {
  50. KEY_SEQUENCE(INSERT),
  51. KEY_SEQUENCE(DELETE),
  52. KEY_SEQUENCE(HOME),
  53. KEY_SEQUENCE(END),
  54. KEY_SEQUENCE(PGUP),
  55. KEY_SEQUENCE(PGDOWN),
  56. KEY_SEQUENCE(LEFT),
  57. KEY_SEQUENCE(RIGHT),
  58. KEY_SEQUENCE(UP),
  59. KEY_SEQUENCE(DOWN),
  60. KEY_SEQUENCE(CLEFT),
  61. KEY_SEQUENCE(CRIGHT),
  62. KEY_SEQUENCE(CUP),
  63. KEY_SEQUENCE(CDOWN),
  64. KEY_SEQUENCE(SLEFT),
  65. KEY_SEQUENCE(SRIGHT),
  66. KEY_SEQUENCE(SUP),
  67. KEY_SEQUENCE(SDOWN),
  68. KEY_SEQUENCE(MLEFT),
  69. KEY_SEQUENCE(MRIGHT),
  70. KEY_SEQUENCE(MUP),
  71. KEY_SEQUENCE(MDOWN),
  72. KEY_SEQUENCE(STAB),
  73. KEY_SEQUENCE(M_p),
  74. KEY_SEQUENCE(M_n),
  75. { 0, NULL }
  76. };
  77. #define KEY_SEQUNCE_NOT_FINISHED -1
  78. #define KEY_C_C 3
  79. #define KEY_C_D 4
  80. #define KEY_C_L 12
  81. #define isseqence(c) ((c) == 0x1B)
  82. /*
  83. * Number of characters that consist of ANSI sequence
  84. * Should not be less then longest string in ansi_sequences
  85. */
  86. #define MAX_ASCII_SEQUENCE 10
  87. static char current_sequence[MAX_ASCII_SEQUENCE];
  88. static int current_sequence_len = -1;
  89. /* single line typed by user goes here */
  90. static char line_buf[LINE_BUF_MAX];
  91. /* index of cursor in input line */
  92. static int line_buf_ix = 0;
  93. /* current length of input line */
  94. static int line_len = 0;
  95. /* line index used for fetching lines from history */
  96. static int line_index = 0;
  97. static char prompt_buf[10] = "> ";
  98. static const char *const noprompt = "";
  99. static const char *current_prompt = prompt_buf;
  100. static const char *prompt = prompt_buf;
  101. /*
  102. * Moves cursor to right or left
  103. *
  104. * n - positive - moves cursor right
  105. * n - negative - moves cursor left
  106. */
  107. static void terminal_move_cursor(int n)
  108. {
  109. if (n < 0) {
  110. for (; n < 0; n++)
  111. putchar('\b');
  112. } else if (n > 0) {
  113. printf("%*s", n, line_buf + line_buf_ix);
  114. }
  115. }
  116. /* Draw command line */
  117. void terminal_draw_command_line(void)
  118. {
  119. /*
  120. * this needs to be checked here since line_buf is not cleared
  121. * before parsing event though line_len and line_buf_ix are
  122. */
  123. if (line_len > 0)
  124. printf("%s%s", prompt, line_buf);
  125. else
  126. printf("%s", prompt);
  127. /* move cursor to it's place */
  128. terminal_move_cursor(line_buf_ix - line_len);
  129. }
  130. /* inserts string into command line at cursor position */
  131. void terminal_insert_into_command_line(const char *p)
  132. {
  133. int len = strlen(p);
  134. if (line_len == line_buf_ix) {
  135. strcat(line_buf, p);
  136. printf("%s", p);
  137. line_len = line_len + len;
  138. line_buf_ix = line_len;
  139. } else {
  140. memmove(line_buf + line_buf_ix + len,
  141. line_buf + line_buf_ix, line_len - line_buf_ix + 1);
  142. memmove(line_buf + line_buf_ix, p, len);
  143. printf("%s", line_buf + line_buf_ix);
  144. line_buf_ix += len;
  145. line_len += len;
  146. terminal_move_cursor(line_buf_ix - line_len);
  147. }
  148. }
  149. /* Prints string and redraws command line */
  150. int terminal_print(const char *format, ...)
  151. {
  152. va_list args;
  153. int ret;
  154. va_start(args, format);
  155. ret = terminal_vprint(format, args);
  156. va_end(args);
  157. return ret;
  158. }
  159. /* Prints string and redraws command line */
  160. int terminal_vprint(const char *format, va_list args)
  161. {
  162. int ret;
  163. printf("\r%*s\r", (int) line_len + 1, " ");
  164. ret = vprintf(format, args);
  165. terminal_draw_command_line();
  166. fflush(stdout);
  167. return ret;
  168. }
  169. /*
  170. * Call this when text in line_buf was changed
  171. * and line needs to be redrawn
  172. */
  173. static void terminal_line_replaced(void)
  174. {
  175. int len = strlen(line_buf);
  176. /* line is shorter that previous */
  177. if (len < line_len) {
  178. /* if new line is shorter move cursor to end of new end */
  179. while (line_buf_ix > len) {
  180. putchar('\b');
  181. line_buf_ix--;
  182. }
  183. /* If cursor was not at the end, move it to the end */
  184. if (line_buf_ix < line_len)
  185. printf("%.*s", line_len - line_buf_ix,
  186. line_buf + line_buf_ix);
  187. /* over write end of previous line */
  188. while (line_len >= len++)
  189. putchar(' ');
  190. }
  191. /* draw new line */
  192. printf("\r%s%s", prompt, line_buf);
  193. /* set up indexes to new line */
  194. line_len = strlen(line_buf);
  195. line_buf_ix = line_len;
  196. fflush(stdout);
  197. }
  198. static void terminal_clear_line(void)
  199. {
  200. line_buf[0] = '\0';
  201. terminal_line_replaced();
  202. }
  203. static void terminal_clear_screen(void)
  204. {
  205. line_buf[0] = '\0';
  206. line_buf_ix = 0;
  207. line_len = 0;
  208. printf("\x1b[2J\x1b[1;1H%s", prompt);
  209. }
  210. static void terminal_delete_char(void)
  211. {
  212. /* delete character under cursor if not at the very end */
  213. if (line_buf_ix >= line_len)
  214. return;
  215. /*
  216. * Prepare buffer with one character missing
  217. * trailing 0 is moved
  218. */
  219. line_len--;
  220. memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1,
  221. line_len - line_buf_ix + 1);
  222. /* print rest of line from current cursor position */
  223. printf("%s \b", line_buf + line_buf_ix);
  224. /* move back cursor */
  225. terminal_move_cursor(line_buf_ix - line_len);
  226. }
  227. /*
  228. * Function tries to replace current line with specified line in history
  229. * new_line_index - new line to show, -1 to show oldest
  230. */
  231. static void terminal_get_line_from_history(int new_line_index)
  232. {
  233. new_line_index = history_get_line(new_line_index,
  234. line_buf, LINE_BUF_MAX);
  235. if (new_line_index >= 0) {
  236. terminal_line_replaced();
  237. line_index = new_line_index;
  238. }
  239. }
  240. /*
  241. * Function searches history back or forward for command line that starts
  242. * with characters up to cursor position
  243. *
  244. * back - true - searches backward
  245. * back - false - searches forward (more recent commands)
  246. */
  247. static void terminal_match_hitory(bool back)
  248. {
  249. char buf[line_buf_ix + 1];
  250. int line;
  251. int matching_line = -1;
  252. int dir = back ? 1 : -1;
  253. line = line_index + dir;
  254. while (matching_line == -1 && line >= 0) {
  255. int new_line_index;
  256. new_line_index = history_get_line(line, buf, line_buf_ix + 1);
  257. if (new_line_index < 0)
  258. break;
  259. if (0 == strncmp(line_buf, buf, line_buf_ix))
  260. matching_line = line;
  261. line += dir;
  262. }
  263. if (matching_line >= 0) {
  264. int pos = line_buf_ix;
  265. terminal_get_line_from_history(matching_line);
  266. /* move back to cursor position to original place */
  267. line_buf_ix = pos;
  268. terminal_move_cursor(pos - line_len);
  269. }
  270. }
  271. /*
  272. * Converts terminal character sequences to single value representing
  273. * keyboard keys
  274. */
  275. static int terminal_convert_sequence(int c)
  276. {
  277. int i;
  278. /* Not in sequence yet? */
  279. if (current_sequence_len == -1) {
  280. /* Is ansi sequence detected by 0x1B ? */
  281. if (isseqence(c)) {
  282. current_sequence_len++;
  283. return KEY_SEQUNCE_NOT_FINISHED;
  284. }
  285. return c;
  286. }
  287. /* Inside sequence */
  288. current_sequence[current_sequence_len++] = c;
  289. current_sequence[current_sequence_len] = '\0';
  290. for (i = 0; ansii_sequnces[i].code; ++i) {
  291. /* Matches so far? */
  292. if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence,
  293. current_sequence_len))
  294. continue;
  295. /* Matches as a whole? */
  296. if (ansii_sequnces[i].sequence[current_sequence_len] == 0) {
  297. current_sequence_len = -1;
  298. return ansii_sequnces[i].code;
  299. }
  300. /* partial match (not whole sequence yet) */
  301. return KEY_SEQUNCE_NOT_FINISHED;
  302. }
  303. terminal_print("ansi char 0x%X %c\n", c);
  304. /*
  305. * Sequence does not match
  306. * mark that no in sequence any more, return char
  307. */
  308. current_sequence_len = -1;
  309. return c;
  310. }
  311. typedef void (*terminal_action)(int c, line_callback process_line);
  312. #define TERMINAL_ACTION(n) \
  313. static void n(int c, void (*process_line)(char *line))
  314. TERMINAL_ACTION(terminal_action_null)
  315. {
  316. }
  317. /* Mapping between keys and function */
  318. typedef struct {
  319. int key;
  320. terminal_action func;
  321. } KeyAction;
  322. int action_keys[] = {
  323. KEY_SEQUNCE_NOT_FINISHED,
  324. KEY_LEFT,
  325. KEY_RIGHT,
  326. KEY_HOME,
  327. KEY_END,
  328. KEY_DELETE,
  329. KEY_CLEFT,
  330. KEY_CRIGHT,
  331. KEY_SUP,
  332. KEY_SDOWN,
  333. KEY_UP,
  334. KEY_DOWN,
  335. KEY_BACKSPACE,
  336. KEY_INSERT,
  337. KEY_PGUP,
  338. KEY_PGDOWN,
  339. KEY_CUP,
  340. KEY_CDOWN,
  341. KEY_SLEFT,
  342. KEY_SRIGHT,
  343. KEY_MLEFT,
  344. KEY_MRIGHT,
  345. KEY_MUP,
  346. KEY_MDOWN,
  347. KEY_STAB,
  348. KEY_M_n,
  349. KEY_M_p,
  350. KEY_C_C,
  351. KEY_C_D,
  352. KEY_C_L,
  353. '\t',
  354. '\r',
  355. '\n',
  356. };
  357. #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
  358. /*
  359. * current_actions holds all recognizable kes and actions for them
  360. * additional element (index 0) is used for default action
  361. */
  362. static KeyAction current_actions[NELEM(action_keys) + 1];
  363. /* KeyAction comparator by key, for qsort and bsearch */
  364. static int KeyActionKeyCompare(const void *a, const void *b)
  365. {
  366. return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key;
  367. }
  368. /* Find action by key, NULL if no action for this key */
  369. static KeyAction *terminal_get_action(int key)
  370. {
  371. KeyAction a = { .key = key };
  372. return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a),
  373. KeyActionKeyCompare);
  374. }
  375. /* Sets new set of actions to use */
  376. static void terminal_set_actions(const KeyAction *actions)
  377. {
  378. int i;
  379. /* Make map with empty function for every key */
  380. for (i = 0; i < NELEM(action_keys); ++i) {
  381. /*
  382. * + 1 due to 0 index reserved for default action that is
  383. * called for non mapped key
  384. */
  385. current_actions[i + 1].key = action_keys[i];
  386. current_actions[i + 1].func = terminal_action_null;
  387. }
  388. /* Sort action from 1 (index 0 - default action) */
  389. qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction),
  390. KeyActionKeyCompare);
  391. /* Set default action (first in array) */
  392. current_actions[0] = *actions++;
  393. /* Copy rest of actions into their places */
  394. for (; actions->key; ++actions) {
  395. KeyAction *place = terminal_get_action(actions->key);
  396. if (place)
  397. place->func = actions->func;
  398. }
  399. }
  400. TERMINAL_ACTION(terminal_action_left)
  401. {
  402. /* if not at the beginning move to previous character */
  403. if (line_buf_ix <= 0)
  404. return;
  405. line_buf_ix--;
  406. terminal_move_cursor(-1);
  407. }
  408. TERMINAL_ACTION(terminal_action_right)
  409. {
  410. /*
  411. * If not at the end, just print current character
  412. * and modify position
  413. */
  414. if (line_buf_ix < line_len)
  415. putchar(line_buf[line_buf_ix++]);
  416. }
  417. TERMINAL_ACTION(terminal_action_home)
  418. {
  419. /* move to beginning of line and update position */
  420. printf("\r%s", prompt);
  421. line_buf_ix = 0;
  422. }
  423. TERMINAL_ACTION(terminal_action_end)
  424. {
  425. /* if not at the end of line */
  426. if (line_buf_ix < line_len) {
  427. /* print everything from cursor */
  428. printf("%s", line_buf + line_buf_ix);
  429. /* just modify current position */
  430. line_buf_ix = line_len;
  431. }
  432. }
  433. TERMINAL_ACTION(terminal_action_del)
  434. {
  435. terminal_delete_char();
  436. }
  437. TERMINAL_ACTION(terminal_action_word_left)
  438. {
  439. int old_pos;
  440. /*
  441. * Move by word left
  442. *
  443. * Are we at the beginning of line?
  444. */
  445. if (line_buf_ix <= 0)
  446. return;
  447. old_pos = line_buf_ix;
  448. line_buf_ix--;
  449. /* skip spaces left */
  450. while (line_buf_ix && isspace(line_buf[line_buf_ix]))
  451. line_buf_ix--;
  452. /* skip all non spaces to the left */
  453. while (line_buf_ix > 0 &&
  454. !isspace(line_buf[line_buf_ix - 1]))
  455. line_buf_ix--;
  456. /* move cursor to new position */
  457. terminal_move_cursor(line_buf_ix - old_pos);
  458. }
  459. TERMINAL_ACTION(terminal_action_word_right)
  460. {
  461. int old_pos;
  462. /*
  463. * Move by word right
  464. *
  465. * are we at the end of line?
  466. */
  467. if (line_buf_ix >= line_len)
  468. return;
  469. old_pos = line_buf_ix;
  470. /* skip all spaces */
  471. while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix]))
  472. line_buf_ix++;
  473. /* skip all non spaces */
  474. while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix]))
  475. line_buf_ix++;
  476. /*
  477. * Move cursor to right by printing text
  478. * between old cursor and new
  479. */
  480. if (line_buf_ix > old_pos)
  481. printf("%.*s", (int) (line_buf_ix - old_pos),
  482. line_buf + old_pos);
  483. }
  484. TERMINAL_ACTION(terminal_action_history_begin)
  485. {
  486. terminal_get_line_from_history(-1);
  487. }
  488. TERMINAL_ACTION(terminal_action_history_end)
  489. {
  490. if (line_index > 0)
  491. terminal_get_line_from_history(0);
  492. }
  493. TERMINAL_ACTION(terminal_action_history_up)
  494. {
  495. terminal_get_line_from_history(line_index + 1);
  496. }
  497. TERMINAL_ACTION(terminal_action_history_down)
  498. {
  499. if (line_index > 0)
  500. terminal_get_line_from_history(line_index - 1);
  501. }
  502. TERMINAL_ACTION(terminal_action_tab)
  503. {
  504. /* tab processing */
  505. process_tab(line_buf, line_buf_ix);
  506. }
  507. TERMINAL_ACTION(terminal_action_backspace)
  508. {
  509. if (line_buf_ix <= 0)
  510. return;
  511. if (line_buf_ix == line_len) {
  512. printf("\b \b");
  513. line_len = --line_buf_ix;
  514. line_buf[line_len] = 0;
  515. } else {
  516. putchar('\b');
  517. line_buf_ix--;
  518. line_len--;
  519. memmove(line_buf + line_buf_ix,
  520. line_buf + line_buf_ix + 1,
  521. line_len - line_buf_ix + 1);
  522. printf("%s \b", line_buf + line_buf_ix);
  523. terminal_move_cursor(line_buf_ix - line_len);
  524. }
  525. }
  526. TERMINAL_ACTION(terminal_action_find_history_forward)
  527. {
  528. /* Search history forward */
  529. terminal_match_hitory(false);
  530. }
  531. TERMINAL_ACTION(terminal_action_find_history_backward)
  532. {
  533. /* Search history forward */
  534. terminal_match_hitory(true);
  535. }
  536. TERMINAL_ACTION(terminal_action_ctrl_c)
  537. {
  538. terminal_clear_line();
  539. }
  540. TERMINAL_ACTION(terminal_action_ctrl_d)
  541. {
  542. if (line_len > 0) {
  543. terminal_delete_char();
  544. } else {
  545. puts("");
  546. exit(0);
  547. }
  548. }
  549. TERMINAL_ACTION(terminal_action_clear_screen)
  550. {
  551. terminal_clear_screen();
  552. }
  553. TERMINAL_ACTION(terminal_action_enter)
  554. {
  555. /*
  556. * On new line add line to history
  557. * forget history position
  558. */
  559. history_add_line(line_buf);
  560. line_len = 0;
  561. line_buf_ix = 0;
  562. line_index = -1;
  563. /* print new line */
  564. putchar(c);
  565. prompt = noprompt;
  566. process_line(line_buf);
  567. /* clear current line */
  568. line_buf[0] = '\0';
  569. prompt = current_prompt;
  570. printf("%s", prompt);
  571. }
  572. TERMINAL_ACTION(terminal_action_default)
  573. {
  574. char str[2] = { c, 0 };
  575. if (!isprint(c))
  576. /*
  577. * TODO: remove this print once all meaningful sequences
  578. * are identified
  579. */
  580. printf("char-0x%02x\n", c);
  581. else if (line_buf_ix < LINE_BUF_MAX - 1)
  582. terminal_insert_into_command_line(str);
  583. }
  584. /* Callback to call when user hit enter during prompt for */
  585. static line_callback prompt_callback;
  586. static KeyAction normal_actions[] = {
  587. { 0, terminal_action_default },
  588. { KEY_LEFT, terminal_action_left },
  589. { KEY_RIGHT, terminal_action_right },
  590. { KEY_HOME, terminal_action_home },
  591. { KEY_END, terminal_action_end },
  592. { KEY_DELETE, terminal_action_del },
  593. { KEY_CLEFT, terminal_action_word_left },
  594. { KEY_CRIGHT, terminal_action_word_right },
  595. { KEY_SUP, terminal_action_history_begin },
  596. { KEY_SDOWN, terminal_action_history_end },
  597. { KEY_UP, terminal_action_history_up },
  598. { KEY_DOWN, terminal_action_history_down },
  599. { '\t', terminal_action_tab },
  600. { KEY_BACKSPACE, terminal_action_backspace },
  601. { KEY_M_n, terminal_action_find_history_forward },
  602. { KEY_M_p, terminal_action_find_history_backward },
  603. { KEY_C_C, terminal_action_ctrl_c },
  604. { KEY_C_D, terminal_action_ctrl_d },
  605. { KEY_C_L, terminal_action_clear_screen },
  606. { '\r', terminal_action_enter },
  607. { '\n', terminal_action_enter },
  608. { 0, NULL },
  609. };
  610. TERMINAL_ACTION(terminal_action_answer)
  611. {
  612. putchar(c);
  613. terminal_set_actions(normal_actions);
  614. /* Restore default prompt */
  615. current_prompt = prompt_buf;
  616. /* No prompt for prints */
  617. prompt = noprompt;
  618. line_buf_ix = 0;
  619. line_len = 0;
  620. /* Call user function with what was typed */
  621. prompt_callback(line_buf);
  622. line_buf[0] = 0;
  623. /* promot_callback could change current_prompt */
  624. prompt = current_prompt;
  625. printf("%s", prompt);
  626. }
  627. TERMINAL_ACTION(terminal_action_prompt_ctrl_c)
  628. {
  629. printf("^C\n");
  630. line_buf_ix = 0;
  631. line_len = 0;
  632. line_buf[0] = 0;
  633. current_prompt = prompt_buf;
  634. prompt = current_prompt;
  635. terminal_set_actions(normal_actions);
  636. printf("%s", prompt);
  637. }
  638. static KeyAction prompt_actions[] = {
  639. { 0, terminal_action_default },
  640. { KEY_LEFT, terminal_action_left },
  641. { KEY_RIGHT, terminal_action_right },
  642. { KEY_HOME, terminal_action_home },
  643. { KEY_END, terminal_action_end },
  644. { KEY_DELETE, terminal_action_del },
  645. { KEY_CLEFT, terminal_action_word_left },
  646. { KEY_CRIGHT, terminal_action_word_right },
  647. { KEY_BACKSPACE, terminal_action_backspace },
  648. { KEY_C_C, terminal_action_prompt_ctrl_c },
  649. { KEY_C_D, terminal_action_ctrl_d },
  650. { '\r', terminal_action_answer },
  651. { '\n', terminal_action_answer },
  652. { 0, NULL },
  653. };
  654. void terminal_process_char(int c, line_callback process_line)
  655. {
  656. KeyAction *a;
  657. c = terminal_convert_sequence(c);
  658. /* Get action for this key */
  659. a = terminal_get_action(c);
  660. /* No action found, get default one */
  661. if (a == NULL)
  662. a = &current_actions[0];
  663. a->func(c, process_line);
  664. fflush(stdout);
  665. }
  666. void terminal_prompt_for(const char *s, line_callback process_line)
  667. {
  668. current_prompt = s;
  669. if (prompt != noprompt) {
  670. prompt = s;
  671. terminal_clear_line();
  672. }
  673. prompt_callback = process_line;
  674. terminal_set_actions(prompt_actions);
  675. }
  676. static struct termios origianl_tios;
  677. static void terminal_cleanup(void)
  678. {
  679. tcsetattr(0, TCSANOW, &origianl_tios);
  680. }
  681. void terminal_setup(void)
  682. {
  683. struct termios tios;
  684. terminal_set_actions(normal_actions);
  685. tcgetattr(0, &origianl_tios);
  686. tios = origianl_tios;
  687. /*
  688. * Turn off echo since all editing is done by hand,
  689. * Ctrl-c handled internally
  690. */
  691. tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK);
  692. tcsetattr(0, TCSANOW, &tios);
  693. /* Restore terminal at exit */
  694. atexit(terminal_cleanup);
  695. printf("%s", prompt);
  696. fflush(stdout);
  697. }