edit.c 19 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. /*
  2. * Command line editing and history
  3. * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include "includes.h"
  15. #include <termios.h>
  16. #include "common.h"
  17. #include "eloop.h"
  18. #include "edit.h"
  19. #define CMD_BUF_LEN 256
  20. static char cmdbuf[CMD_BUF_LEN];
  21. static int cmdbuf_pos = 0;
  22. static int cmdbuf_len = 0;
  23. #define CMD_HISTORY_LEN 20
  24. static char history_buf[CMD_HISTORY_LEN][CMD_BUF_LEN];
  25. static int history_pos = 0;
  26. static int history_current = 0;
  27. static void *edit_cb_ctx;
  28. static void (*edit_cmd_cb)(void *ctx, char *cmd);
  29. static void (*edit_eof_cb)(void *ctx);
  30. static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
  31. NULL;
  32. static struct termios prevt, newt;
  33. #define CLEAR_END_LINE "\e[K"
  34. void edit_clear_line(void)
  35. {
  36. int i;
  37. putchar('\r');
  38. for (i = 0; i < cmdbuf_len + 2; i++)
  39. putchar(' ');
  40. }
  41. static void move_start(void)
  42. {
  43. cmdbuf_pos = 0;
  44. edit_redraw();
  45. }
  46. static void move_end(void)
  47. {
  48. cmdbuf_pos = cmdbuf_len;
  49. edit_redraw();
  50. }
  51. static void move_left(void)
  52. {
  53. if (cmdbuf_pos > 0) {
  54. cmdbuf_pos--;
  55. edit_redraw();
  56. }
  57. }
  58. static void move_right(void)
  59. {
  60. if (cmdbuf_pos < cmdbuf_len) {
  61. cmdbuf_pos++;
  62. edit_redraw();
  63. }
  64. }
  65. static void move_word_left(void)
  66. {
  67. while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
  68. cmdbuf_pos--;
  69. while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
  70. cmdbuf_pos--;
  71. edit_redraw();
  72. }
  73. static void move_word_right(void)
  74. {
  75. while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
  76. cmdbuf_pos++;
  77. while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
  78. cmdbuf_pos++;
  79. edit_redraw();
  80. }
  81. static void delete_left(void)
  82. {
  83. if (cmdbuf_pos == 0)
  84. return;
  85. edit_clear_line();
  86. os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
  87. cmdbuf_len - cmdbuf_pos);
  88. cmdbuf_pos--;
  89. cmdbuf_len--;
  90. edit_redraw();
  91. }
  92. static void delete_current(void)
  93. {
  94. if (cmdbuf_pos == cmdbuf_len)
  95. return;
  96. edit_clear_line();
  97. os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
  98. cmdbuf_len - cmdbuf_pos);
  99. cmdbuf_len--;
  100. edit_redraw();
  101. }
  102. static void delete_word(void)
  103. {
  104. edit_clear_line();
  105. while (cmdbuf_len > 0 && cmdbuf[cmdbuf_len - 1] == ' ')
  106. cmdbuf_len--;
  107. while (cmdbuf_len > 0 && cmdbuf[cmdbuf_len - 1] != ' ')
  108. cmdbuf_len--;
  109. if (cmdbuf_pos > cmdbuf_len)
  110. cmdbuf_pos = cmdbuf_len;
  111. edit_redraw();
  112. }
  113. static void clear_left(void)
  114. {
  115. if (cmdbuf_pos == 0)
  116. return;
  117. edit_clear_line();
  118. os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
  119. cmdbuf_len -= cmdbuf_pos;
  120. cmdbuf_pos = 0;
  121. edit_redraw();
  122. }
  123. static void clear_right(void)
  124. {
  125. if (cmdbuf_pos == cmdbuf_len)
  126. return;
  127. edit_clear_line();
  128. cmdbuf_len = cmdbuf_pos;
  129. edit_redraw();
  130. }
  131. static void history_add(const char *str)
  132. {
  133. int prev;
  134. if (str[0] == '\0')
  135. return;
  136. if (history_pos == 0)
  137. prev = CMD_HISTORY_LEN - 1;
  138. else
  139. prev = history_pos - 1;
  140. if (os_strcmp(history_buf[prev], str) == 0)
  141. return;
  142. os_strlcpy(history_buf[history_pos], str, CMD_BUF_LEN);
  143. history_pos++;
  144. if (history_pos == CMD_HISTORY_LEN)
  145. history_pos = 0;
  146. history_current = history_pos;
  147. }
  148. static void history_prev(void)
  149. {
  150. int pos;
  151. if (history_current == (history_pos + 1) % CMD_HISTORY_LEN)
  152. return;
  153. pos = history_current;
  154. if (history_current == history_pos && cmdbuf_len) {
  155. cmdbuf[cmdbuf_len] = '\0';
  156. history_add(cmdbuf);
  157. }
  158. if (pos > 0)
  159. pos--;
  160. else
  161. pos = CMD_HISTORY_LEN - 1;
  162. if (history_buf[pos][0] == '\0')
  163. return;
  164. history_current = pos;
  165. edit_clear_line();
  166. cmdbuf_len = cmdbuf_pos = os_strlen(history_buf[history_current]);
  167. os_memcpy(cmdbuf, history_buf[history_current], cmdbuf_len);
  168. edit_redraw();
  169. }
  170. static void history_next(void)
  171. {
  172. if (history_current == history_pos)
  173. return;
  174. history_current++;
  175. if (history_current == CMD_HISTORY_LEN)
  176. history_current = 0;
  177. edit_clear_line();
  178. cmdbuf_len = cmdbuf_pos = os_strlen(history_buf[history_current]);
  179. os_memcpy(cmdbuf, history_buf[history_current], cmdbuf_len);
  180. edit_redraw();
  181. }
  182. static void history_debug_dump(void)
  183. {
  184. int p;
  185. edit_clear_line();
  186. printf("\r");
  187. p = (history_pos + 1) % CMD_HISTORY_LEN;
  188. for (;;) {
  189. printf("[%d%s%s] %s\n",
  190. p, p == history_current ? "C" : "",
  191. p == history_pos ? "P" : "", history_buf[p]);
  192. if (p == history_pos)
  193. break;
  194. p++;
  195. if (p == CMD_HISTORY_LEN)
  196. p = 0;
  197. }
  198. edit_redraw();
  199. }
  200. static void insert_char(int c)
  201. {
  202. if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
  203. return;
  204. if (cmdbuf_len == cmdbuf_pos) {
  205. cmdbuf[cmdbuf_pos++] = c;
  206. cmdbuf_len++;
  207. putchar(c);
  208. fflush(stdout);
  209. } else {
  210. os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
  211. cmdbuf_len - cmdbuf_pos);
  212. cmdbuf[cmdbuf_pos++] = c;
  213. cmdbuf_len++;
  214. edit_redraw();
  215. }
  216. }
  217. static void process_cmd(void)
  218. {
  219. if (cmdbuf_len == 0) {
  220. printf("\n> ");
  221. fflush(stdout);
  222. return;
  223. }
  224. printf("\n");
  225. cmdbuf[cmdbuf_len] = '\0';
  226. history_add(cmdbuf);
  227. cmdbuf_pos = 0;
  228. cmdbuf_len = 0;
  229. edit_cmd_cb(edit_cb_ctx, cmdbuf);
  230. printf("> ");
  231. fflush(stdout);
  232. }
  233. static void free_completions(char **c)
  234. {
  235. int i;
  236. if (c == NULL)
  237. return;
  238. for (i = 0; c[i]; i++)
  239. os_free(c[i]);
  240. os_free(c);
  241. }
  242. static int filter_strings(char **c, char *str, size_t len)
  243. {
  244. int i, j;
  245. for (i = 0, j = 0; c[j]; j++) {
  246. if (os_strncasecmp(c[j], str, len) == 0) {
  247. if (i != j) {
  248. c[i] = c[j];
  249. c[j] = NULL;
  250. }
  251. i++;
  252. } else {
  253. os_free(c[j]);
  254. c[j] = NULL;
  255. }
  256. }
  257. c[i] = NULL;
  258. return i;
  259. }
  260. static int common_len(const char *a, const char *b)
  261. {
  262. int len = 0;
  263. while (a[len] && a[len] == b[len])
  264. len++;
  265. return len;
  266. }
  267. static int max_common_length(char **c)
  268. {
  269. int len, i;
  270. len = os_strlen(c[0]);
  271. for (i = 1; c[i]; i++) {
  272. int same = common_len(c[0], c[i]);
  273. if (same < len)
  274. len = same;
  275. }
  276. return len;
  277. }
  278. static int cmp_str(const void *a, const void *b)
  279. {
  280. return os_strcmp(* (const char **) a, * (const char **) b);
  281. }
  282. static void complete(int list)
  283. {
  284. char **c;
  285. int i, len, count;
  286. int start, end;
  287. int room, plen, add_space;
  288. if (edit_completion_cb == NULL)
  289. return;
  290. cmdbuf[cmdbuf_len] = '\0';
  291. c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
  292. if (c == NULL)
  293. return;
  294. end = cmdbuf_pos;
  295. start = end;
  296. while (start > 0 && cmdbuf[start - 1] != ' ')
  297. start--;
  298. plen = end - start;
  299. count = filter_strings(c, &cmdbuf[start], plen);
  300. if (count == 0) {
  301. free_completions(c);
  302. return;
  303. }
  304. len = max_common_length(c);
  305. if (len <= plen && count > 1) {
  306. if (list) {
  307. qsort(c, count, sizeof(char *), cmp_str);
  308. edit_clear_line();
  309. printf("\r");
  310. for (i = 0; c[i]; i++)
  311. printf("%s%s", i > 0 ? " " : "", c[i]);
  312. printf("\n");
  313. edit_redraw();
  314. }
  315. free_completions(c);
  316. return;
  317. }
  318. len -= plen;
  319. room = sizeof(cmdbuf) - 1 - cmdbuf_len;
  320. if (room < len)
  321. len = room;
  322. add_space = count == 1 && len < room;
  323. os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
  324. cmdbuf_len - cmdbuf_pos);
  325. os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
  326. if (add_space)
  327. cmdbuf[cmdbuf_pos + len] = ' ';
  328. cmdbuf_pos += len + add_space;
  329. cmdbuf_len += len + add_space;
  330. edit_redraw();
  331. free_completions(c);
  332. }
  333. enum edit_key_code {
  334. EDIT_KEY_NONE = 256,
  335. EDIT_KEY_TAB,
  336. EDIT_KEY_UP,
  337. EDIT_KEY_DOWN,
  338. EDIT_KEY_RIGHT,
  339. EDIT_KEY_LEFT,
  340. EDIT_KEY_ENTER,
  341. EDIT_KEY_BACKSPACE,
  342. EDIT_KEY_INSERT,
  343. EDIT_KEY_DELETE,
  344. EDIT_KEY_HOME,
  345. EDIT_KEY_END,
  346. EDIT_KEY_PAGE_UP,
  347. EDIT_KEY_PAGE_DOWN,
  348. EDIT_KEY_F1,
  349. EDIT_KEY_F2,
  350. EDIT_KEY_F3,
  351. EDIT_KEY_F4,
  352. EDIT_KEY_F5,
  353. EDIT_KEY_F6,
  354. EDIT_KEY_F7,
  355. EDIT_KEY_F8,
  356. EDIT_KEY_F9,
  357. EDIT_KEY_F10,
  358. EDIT_KEY_F11,
  359. EDIT_KEY_F12,
  360. EDIT_KEY_CTRL_UP,
  361. EDIT_KEY_CTRL_DOWN,
  362. EDIT_KEY_CTRL_RIGHT,
  363. EDIT_KEY_CTRL_LEFT,
  364. EDIT_KEY_CTRL_A,
  365. EDIT_KEY_CTRL_B,
  366. EDIT_KEY_CTRL_D,
  367. EDIT_KEY_CTRL_E,
  368. EDIT_KEY_CTRL_F,
  369. EDIT_KEY_CTRL_G,
  370. EDIT_KEY_CTRL_H,
  371. EDIT_KEY_CTRL_J,
  372. EDIT_KEY_CTRL_K,
  373. EDIT_KEY_CTRL_L,
  374. EDIT_KEY_CTRL_N,
  375. EDIT_KEY_CTRL_O,
  376. EDIT_KEY_CTRL_P,
  377. EDIT_KEY_CTRL_R,
  378. EDIT_KEY_CTRL_T,
  379. EDIT_KEY_CTRL_U,
  380. EDIT_KEY_CTRL_V,
  381. EDIT_KEY_CTRL_W,
  382. EDIT_KEY_ALT_UP,
  383. EDIT_KEY_ALT_DOWN,
  384. EDIT_KEY_ALT_RIGHT,
  385. EDIT_KEY_ALT_LEFT,
  386. EDIT_KEY_SHIFT_UP,
  387. EDIT_KEY_SHIFT_DOWN,
  388. EDIT_KEY_SHIFT_RIGHT,
  389. EDIT_KEY_SHIFT_LEFT,
  390. EDIT_KEY_ALT_SHIFT_UP,
  391. EDIT_KEY_ALT_SHIFT_DOWN,
  392. EDIT_KEY_ALT_SHIFT_RIGHT,
  393. EDIT_KEY_ALT_SHIFT_LEFT,
  394. EDIT_KEY_EOF
  395. };
  396. static void show_esc_buf(const char *esc_buf, char c, int i)
  397. {
  398. edit_clear_line();
  399. printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
  400. edit_redraw();
  401. }
  402. static enum edit_key_code esc_seq_to_key1_no(char last)
  403. {
  404. switch (last) {
  405. case 'A':
  406. return EDIT_KEY_UP;
  407. case 'B':
  408. return EDIT_KEY_DOWN;
  409. case 'C':
  410. return EDIT_KEY_RIGHT;
  411. case 'D':
  412. return EDIT_KEY_LEFT;
  413. default:
  414. return EDIT_KEY_NONE;
  415. }
  416. }
  417. static enum edit_key_code esc_seq_to_key1_shift(char last)
  418. {
  419. switch (last) {
  420. case 'A':
  421. return EDIT_KEY_SHIFT_UP;
  422. case 'B':
  423. return EDIT_KEY_SHIFT_DOWN;
  424. case 'C':
  425. return EDIT_KEY_SHIFT_RIGHT;
  426. case 'D':
  427. return EDIT_KEY_SHIFT_LEFT;
  428. default:
  429. return EDIT_KEY_NONE;
  430. }
  431. }
  432. static enum edit_key_code esc_seq_to_key1_alt(char last)
  433. {
  434. switch (last) {
  435. case 'A':
  436. return EDIT_KEY_ALT_UP;
  437. case 'B':
  438. return EDIT_KEY_ALT_DOWN;
  439. case 'C':
  440. return EDIT_KEY_ALT_RIGHT;
  441. case 'D':
  442. return EDIT_KEY_ALT_LEFT;
  443. default:
  444. return EDIT_KEY_NONE;
  445. }
  446. }
  447. static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
  448. {
  449. switch (last) {
  450. case 'A':
  451. return EDIT_KEY_ALT_SHIFT_UP;
  452. case 'B':
  453. return EDIT_KEY_ALT_SHIFT_DOWN;
  454. case 'C':
  455. return EDIT_KEY_ALT_SHIFT_RIGHT;
  456. case 'D':
  457. return EDIT_KEY_ALT_SHIFT_LEFT;
  458. default:
  459. return EDIT_KEY_NONE;
  460. }
  461. }
  462. static enum edit_key_code esc_seq_to_key1_ctrl(char last)
  463. {
  464. switch (last) {
  465. case 'A':
  466. return EDIT_KEY_CTRL_UP;
  467. case 'B':
  468. return EDIT_KEY_CTRL_DOWN;
  469. case 'C':
  470. return EDIT_KEY_CTRL_RIGHT;
  471. case 'D':
  472. return EDIT_KEY_CTRL_LEFT;
  473. default:
  474. return EDIT_KEY_NONE;
  475. }
  476. }
  477. static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
  478. {
  479. /* ESC-[<param1>;<param2><last> */
  480. if (param1 < 0 && param2 < 0)
  481. return esc_seq_to_key1_no(last);
  482. if (param1 == 1 && param2 == 2)
  483. return esc_seq_to_key1_shift(last);
  484. if (param1 == 1 && param2 == 3)
  485. return esc_seq_to_key1_alt(last);
  486. if (param1 == 1 && param2 == 4)
  487. return esc_seq_to_key1_alt_shift(last);
  488. if (param1 == 1 && param2 == 5)
  489. return esc_seq_to_key1_ctrl(last);
  490. if (param2 < 0) {
  491. if (last != '~')
  492. return EDIT_KEY_NONE;
  493. switch (param1) {
  494. case 2:
  495. return EDIT_KEY_INSERT;
  496. case 3:
  497. return EDIT_KEY_DELETE;
  498. case 5:
  499. return EDIT_KEY_PAGE_UP;
  500. case 6:
  501. return EDIT_KEY_PAGE_DOWN;
  502. case 15:
  503. return EDIT_KEY_F5;
  504. case 17:
  505. return EDIT_KEY_F6;
  506. case 18:
  507. return EDIT_KEY_F7;
  508. case 19:
  509. return EDIT_KEY_F8;
  510. case 20:
  511. return EDIT_KEY_F9;
  512. case 21:
  513. return EDIT_KEY_F10;
  514. case 23:
  515. return EDIT_KEY_F11;
  516. case 24:
  517. return EDIT_KEY_F12;
  518. }
  519. }
  520. return EDIT_KEY_NONE;
  521. }
  522. static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
  523. {
  524. /* ESC-O<param1>;<param2><last> */
  525. if (param1 >= 0 || param2 >= 0)
  526. return EDIT_KEY_NONE;
  527. switch (last) {
  528. case 'F':
  529. return EDIT_KEY_END;
  530. case 'H':
  531. return EDIT_KEY_HOME;
  532. case 'P':
  533. return EDIT_KEY_F1;
  534. case 'Q':
  535. return EDIT_KEY_F2;
  536. case 'R':
  537. return EDIT_KEY_F3;
  538. case 'S':
  539. return EDIT_KEY_F4;
  540. default:
  541. return EDIT_KEY_NONE;
  542. }
  543. }
  544. static enum edit_key_code esc_seq_to_key(char *seq)
  545. {
  546. char last, *pos;
  547. int param1 = -1, param2 = -1;
  548. enum edit_key_code ret = EDIT_KEY_NONE;
  549. for (pos = seq; *pos; pos++)
  550. last = *pos;
  551. if (seq[1] >= '0' && seq[1] <= '9') {
  552. param1 = atoi(&seq[1]);
  553. pos = os_strchr(seq, ';');
  554. if (pos)
  555. param2 = atoi(pos + 1);
  556. }
  557. if (seq[0] == '[')
  558. ret = esc_seq_to_key1(param1, param2, last);
  559. else if (seq[0] == 'O')
  560. ret = esc_seq_to_key2(param1, param2, last);
  561. if (ret != EDIT_KEY_NONE)
  562. return ret;
  563. edit_clear_line();
  564. printf("\rUnknown escape sequence '%s'\n", seq);
  565. edit_redraw();
  566. return EDIT_KEY_NONE;
  567. }
  568. static enum edit_key_code edit_read_key(int sock)
  569. {
  570. int c;
  571. unsigned char buf[1];
  572. int res;
  573. static int esc = -1;
  574. static char esc_buf[7];
  575. res = read(sock, buf, 1);
  576. if (res < 0)
  577. perror("read");
  578. if (res <= 0)
  579. return EDIT_KEY_EOF;
  580. c = buf[0];
  581. if (esc >= 0) {
  582. if (c == 27 /* ESC */) {
  583. esc = 0;
  584. return EDIT_KEY_NONE;
  585. }
  586. if (esc == 6) {
  587. show_esc_buf(esc_buf, c, 0);
  588. esc = -1;
  589. } else {
  590. esc_buf[esc++] = c;
  591. esc_buf[esc] = '\0';
  592. }
  593. }
  594. if (esc == 1) {
  595. if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
  596. show_esc_buf(esc_buf, c, 1);
  597. esc = -1;
  598. return EDIT_KEY_NONE;
  599. } else
  600. return EDIT_KEY_NONE; /* Escape sequence continues */
  601. }
  602. if (esc > 1) {
  603. if ((c >= '0' && c <= '9') || c == ';')
  604. return EDIT_KEY_NONE; /* Escape sequence continues */
  605. if (c == '~' || (c >= 'A' && c <= 'Z')) {
  606. esc = -1;
  607. return esc_seq_to_key(esc_buf);
  608. }
  609. show_esc_buf(esc_buf, c, 2);
  610. esc = -1;
  611. return EDIT_KEY_NONE;
  612. }
  613. switch (c) {
  614. case 1:
  615. return EDIT_KEY_CTRL_A;
  616. case 2:
  617. return EDIT_KEY_CTRL_B;
  618. case 4:
  619. return EDIT_KEY_CTRL_D;
  620. case 5:
  621. return EDIT_KEY_CTRL_E;
  622. case 6:
  623. return EDIT_KEY_CTRL_F;
  624. case 7:
  625. return EDIT_KEY_CTRL_G;
  626. case 8:
  627. return EDIT_KEY_CTRL_H;
  628. case 9:
  629. return EDIT_KEY_TAB;
  630. case 10:
  631. return EDIT_KEY_CTRL_J;
  632. case 13: /* CR */
  633. return EDIT_KEY_ENTER;
  634. case 11:
  635. return EDIT_KEY_CTRL_K;
  636. case 12:
  637. return EDIT_KEY_CTRL_L;
  638. case 14:
  639. return EDIT_KEY_CTRL_N;
  640. case 15:
  641. return EDIT_KEY_CTRL_O;
  642. case 16:
  643. return EDIT_KEY_CTRL_P;
  644. case 18:
  645. return EDIT_KEY_CTRL_R;
  646. case 20:
  647. return EDIT_KEY_CTRL_T;
  648. case 21:
  649. return EDIT_KEY_CTRL_U;
  650. case 22:
  651. return EDIT_KEY_CTRL_V;
  652. case 23:
  653. return EDIT_KEY_CTRL_W;
  654. case 27: /* ESC */
  655. esc = 0;
  656. return EDIT_KEY_NONE;
  657. case 127:
  658. return EDIT_KEY_BACKSPACE;
  659. default:
  660. return c;
  661. }
  662. }
  663. static char search_buf[21];
  664. static int search_skip;
  665. static char * search_find(void)
  666. {
  667. int pos = history_pos;
  668. size_t len = os_strlen(search_buf);
  669. int skip = search_skip;
  670. if (len == 0)
  671. return NULL;
  672. for (;;) {
  673. if (pos == 0)
  674. pos = CMD_HISTORY_LEN - 1;
  675. else
  676. pos--;
  677. if (pos == history_pos) {
  678. search_skip = 0;
  679. return NULL;
  680. }
  681. if (os_strstr(history_buf[pos], search_buf)) {
  682. if (skip == 0)
  683. return history_buf[pos];
  684. skip--;
  685. }
  686. }
  687. }
  688. static void search_redraw(void)
  689. {
  690. char *match = search_find();
  691. printf("\rsearch '%s': %s" CLEAR_END_LINE,
  692. search_buf, match ? match : "");
  693. printf("\rsearch '%s", search_buf);
  694. fflush(stdout);
  695. }
  696. static void search_start(void)
  697. {
  698. edit_clear_line();
  699. search_buf[0] = '\0';
  700. search_skip = 0;
  701. search_redraw();
  702. }
  703. static void search_clear(void)
  704. {
  705. search_redraw();
  706. printf("\r" CLEAR_END_LINE);
  707. }
  708. static void search_stop(void)
  709. {
  710. char *match = search_find();
  711. search_buf[0] = '\0';
  712. search_clear();
  713. if (match) {
  714. os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
  715. cmdbuf_len = os_strlen(cmdbuf);
  716. cmdbuf_pos = cmdbuf_len;
  717. }
  718. edit_redraw();
  719. }
  720. static void search_cancel(void)
  721. {
  722. search_buf[0] = '\0';
  723. search_clear();
  724. edit_redraw();
  725. }
  726. static void search_backspace(void)
  727. {
  728. size_t len;
  729. len = os_strlen(search_buf);
  730. if (len == 0)
  731. return;
  732. search_buf[len - 1] = '\0';
  733. search_skip = 0;
  734. search_redraw();
  735. }
  736. static void search_next(void)
  737. {
  738. search_skip++;
  739. search_find();
  740. search_redraw();
  741. }
  742. static void search_char(char c)
  743. {
  744. size_t len;
  745. len = os_strlen(search_buf);
  746. if (len == sizeof(search_buf) - 1)
  747. return;
  748. search_buf[len] = c;
  749. search_buf[len + 1] = '\0';
  750. search_skip = 0;
  751. search_redraw();
  752. }
  753. static enum edit_key_code search_key(enum edit_key_code c)
  754. {
  755. switch (c) {
  756. case EDIT_KEY_ENTER:
  757. case EDIT_KEY_CTRL_J:
  758. case EDIT_KEY_LEFT:
  759. case EDIT_KEY_RIGHT:
  760. case EDIT_KEY_HOME:
  761. case EDIT_KEY_END:
  762. case EDIT_KEY_CTRL_A:
  763. case EDIT_KEY_CTRL_E:
  764. search_stop();
  765. return c;
  766. case EDIT_KEY_DOWN:
  767. case EDIT_KEY_UP:
  768. search_cancel();
  769. return EDIT_KEY_EOF;
  770. case EDIT_KEY_CTRL_H:
  771. case EDIT_KEY_BACKSPACE:
  772. search_backspace();
  773. break;
  774. case EDIT_KEY_CTRL_R:
  775. search_next();
  776. break;
  777. default:
  778. if (c >= 32 && c <= 255)
  779. search_char(c);
  780. break;
  781. }
  782. return EDIT_KEY_NONE;
  783. }
  784. static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
  785. {
  786. static int last_tab = 0;
  787. static int search = 0;
  788. enum edit_key_code c;
  789. c = edit_read_key(sock);
  790. if (search) {
  791. c = search_key(c);
  792. if (c == EDIT_KEY_NONE)
  793. return;
  794. search = 0;
  795. if (c == EDIT_KEY_EOF)
  796. return;
  797. }
  798. if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
  799. last_tab = 0;
  800. switch (c) {
  801. case EDIT_KEY_NONE:
  802. break;
  803. case EDIT_KEY_EOF:
  804. edit_eof_cb(edit_cb_ctx);
  805. break;
  806. case EDIT_KEY_TAB:
  807. complete(last_tab);
  808. last_tab = 1;
  809. break;
  810. case EDIT_KEY_UP:
  811. case EDIT_KEY_CTRL_P:
  812. history_prev();
  813. break;
  814. case EDIT_KEY_DOWN:
  815. case EDIT_KEY_CTRL_N:
  816. history_next();
  817. break;
  818. case EDIT_KEY_RIGHT:
  819. case EDIT_KEY_CTRL_F:
  820. move_right();
  821. break;
  822. case EDIT_KEY_LEFT:
  823. case EDIT_KEY_CTRL_B:
  824. move_left();
  825. break;
  826. case EDIT_KEY_CTRL_RIGHT:
  827. move_word_right();
  828. break;
  829. case EDIT_KEY_CTRL_LEFT:
  830. move_word_left();
  831. break;
  832. case EDIT_KEY_DELETE:
  833. delete_current();
  834. break;
  835. case EDIT_KEY_END:
  836. move_end();
  837. break;
  838. case EDIT_KEY_HOME:
  839. case EDIT_KEY_CTRL_A:
  840. move_start();
  841. break;
  842. case EDIT_KEY_F2:
  843. history_debug_dump();
  844. break;
  845. case EDIT_KEY_CTRL_D:
  846. if (cmdbuf_len > 0) {
  847. delete_current();
  848. return;
  849. }
  850. printf("\n");
  851. edit_eof_cb(edit_cb_ctx);
  852. break;
  853. case EDIT_KEY_CTRL_E:
  854. move_end();
  855. break;
  856. case EDIT_KEY_CTRL_H:
  857. case EDIT_KEY_BACKSPACE:
  858. delete_left();
  859. break;
  860. case EDIT_KEY_ENTER:
  861. case EDIT_KEY_CTRL_J:
  862. process_cmd();
  863. break;
  864. case EDIT_KEY_CTRL_K:
  865. clear_right();
  866. break;
  867. case EDIT_KEY_CTRL_L:
  868. edit_clear_line();
  869. edit_redraw();
  870. break;
  871. case EDIT_KEY_CTRL_R:
  872. search = 1;
  873. search_start();
  874. break;
  875. case EDIT_KEY_CTRL_U:
  876. clear_left();
  877. break;
  878. case EDIT_KEY_CTRL_W:
  879. delete_word();
  880. break;
  881. default:
  882. if (c >= 32 && c <= 255)
  883. insert_char(c);
  884. break;
  885. }
  886. }
  887. int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
  888. void (*eof_cb)(void *ctx),
  889. void *ctx)
  890. {
  891. os_memset(history_buf, 0, sizeof(history_buf));
  892. edit_cb_ctx = ctx;
  893. edit_cmd_cb = cmd_cb;
  894. edit_eof_cb = eof_cb;
  895. tcgetattr(STDIN_FILENO, &prevt);
  896. newt = prevt;
  897. newt.c_lflag &= ~(ICANON | ECHO);
  898. tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  899. eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
  900. printf("> ");
  901. fflush(stdout);
  902. return 0;
  903. }
  904. void edit_deinit(void)
  905. {
  906. eloop_unregister_read_sock(STDIN_FILENO);
  907. tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
  908. }
  909. void edit_redraw(void)
  910. {
  911. char tmp;
  912. cmdbuf[cmdbuf_len] = '\0';
  913. printf("\r> %s", cmdbuf);
  914. if (cmdbuf_pos != cmdbuf_len) {
  915. tmp = cmdbuf[cmdbuf_pos];
  916. cmdbuf[cmdbuf_pos] = '\0';
  917. printf("\r> %s", cmdbuf);
  918. cmdbuf[cmdbuf_pos] = tmp;
  919. }
  920. fflush(stdout);
  921. }
  922. void edit_set_filter_history_cb(int (*cb)(void *ctx, const char *cmd))
  923. {
  924. }
  925. void edit_set_completion_cb(char ** (*cb)(void *ctx, const char *cmd, int pos))
  926. {
  927. edit_completion_cb = cb;
  928. }