readline.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. * QEMU readline utility
  3. *
  4. * Copyright (c) 2003-2004 Fabrice Bellard
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include "qemu/readline.h"
  26. #include "qemu/ctype.h"
  27. #include "qemu/cutils.h"
  28. #define IS_NORM 0
  29. #define IS_ESC 1
  30. #define IS_CSI 2
  31. #define IS_SS3 3
  32. void readline_show_prompt(ReadLineState *rs)
  33. {
  34. rs->printf_func(rs->opaque, "%s", rs->prompt);
  35. rs->flush_func(rs->opaque);
  36. rs->last_cmd_buf_index = 0;
  37. rs->last_cmd_buf_size = 0;
  38. rs->esc_state = IS_NORM;
  39. }
  40. /* update the displayed command line */
  41. static void readline_update(ReadLineState *rs)
  42. {
  43. int i, delta, len;
  44. if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
  45. memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
  46. for (i = 0; i < rs->last_cmd_buf_index; i++) {
  47. rs->printf_func(rs->opaque, "\033[D");
  48. }
  49. rs->cmd_buf[rs->cmd_buf_size] = '\0';
  50. if (rs->read_password) {
  51. len = strlen(rs->cmd_buf);
  52. for (i = 0; i < len; i++) {
  53. rs->printf_func(rs->opaque, "*");
  54. }
  55. } else {
  56. rs->printf_func(rs->opaque, "%s", rs->cmd_buf);
  57. }
  58. rs->printf_func(rs->opaque, "\033[K");
  59. memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
  60. rs->last_cmd_buf_size = rs->cmd_buf_size;
  61. rs->last_cmd_buf_index = rs->cmd_buf_size;
  62. }
  63. if (rs->cmd_buf_index != rs->last_cmd_buf_index) {
  64. delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
  65. if (delta > 0) {
  66. for (i = 0; i < delta; i++) {
  67. rs->printf_func(rs->opaque, "\033[C");
  68. }
  69. } else {
  70. delta = -delta;
  71. for (i = 0; i < delta; i++) {
  72. rs->printf_func(rs->opaque, "\033[D");
  73. }
  74. }
  75. rs->last_cmd_buf_index = rs->cmd_buf_index;
  76. }
  77. rs->flush_func(rs->opaque);
  78. }
  79. static void readline_insert_char(ReadLineState *rs, int ch)
  80. {
  81. if (rs->cmd_buf_index < READLINE_CMD_BUF_SIZE) {
  82. memmove(rs->cmd_buf + rs->cmd_buf_index + 1,
  83. rs->cmd_buf + rs->cmd_buf_index,
  84. rs->cmd_buf_size - rs->cmd_buf_index);
  85. rs->cmd_buf[rs->cmd_buf_index] = ch;
  86. rs->cmd_buf_size++;
  87. rs->cmd_buf_index++;
  88. }
  89. }
  90. static void readline_backward_char(ReadLineState *rs)
  91. {
  92. if (rs->cmd_buf_index > 0) {
  93. rs->cmd_buf_index--;
  94. }
  95. }
  96. static void readline_forward_char(ReadLineState *rs)
  97. {
  98. if (rs->cmd_buf_index < rs->cmd_buf_size) {
  99. rs->cmd_buf_index++;
  100. }
  101. }
  102. static void readline_delete_char(ReadLineState *rs)
  103. {
  104. if (rs->cmd_buf_index < rs->cmd_buf_size) {
  105. memmove(rs->cmd_buf + rs->cmd_buf_index,
  106. rs->cmd_buf + rs->cmd_buf_index + 1,
  107. rs->cmd_buf_size - rs->cmd_buf_index - 1);
  108. rs->cmd_buf_size--;
  109. }
  110. }
  111. static void readline_backspace(ReadLineState *rs)
  112. {
  113. if (rs->cmd_buf_index > 0) {
  114. readline_backward_char(rs);
  115. readline_delete_char(rs);
  116. }
  117. }
  118. static void readline_backword(ReadLineState *rs)
  119. {
  120. int start;
  121. if (rs->cmd_buf_index == 0 || rs->cmd_buf_index > rs->cmd_buf_size) {
  122. return;
  123. }
  124. start = rs->cmd_buf_index - 1;
  125. /* find first word (backwards) */
  126. while (start > 0) {
  127. if (!qemu_isspace(rs->cmd_buf[start])) {
  128. break;
  129. }
  130. --start;
  131. }
  132. /* find first space (backwards) */
  133. while (start > 0) {
  134. if (qemu_isspace(rs->cmd_buf[start])) {
  135. ++start;
  136. break;
  137. }
  138. --start;
  139. }
  140. /* remove word */
  141. if (start < rs->cmd_buf_index) {
  142. memmove(rs->cmd_buf + start,
  143. rs->cmd_buf + rs->cmd_buf_index,
  144. rs->cmd_buf_size - rs->cmd_buf_index);
  145. rs->cmd_buf_size -= rs->cmd_buf_index - start;
  146. rs->cmd_buf_index = start;
  147. }
  148. }
  149. static void readline_bol(ReadLineState *rs)
  150. {
  151. rs->cmd_buf_index = 0;
  152. }
  153. static void readline_eol(ReadLineState *rs)
  154. {
  155. rs->cmd_buf_index = rs->cmd_buf_size;
  156. }
  157. static void readline_up_char(ReadLineState *rs)
  158. {
  159. int idx;
  160. if (rs->hist_entry == 0) {
  161. return;
  162. }
  163. if (rs->hist_entry == -1) {
  164. /* Find latest entry */
  165. for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
  166. if (rs->history[idx] == NULL) {
  167. break;
  168. }
  169. }
  170. rs->hist_entry = idx;
  171. }
  172. rs->hist_entry--;
  173. if (rs->hist_entry >= 0) {
  174. pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
  175. rs->history[rs->hist_entry]);
  176. rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
  177. }
  178. }
  179. static void readline_down_char(ReadLineState *rs)
  180. {
  181. if (rs->hist_entry == -1) {
  182. return;
  183. }
  184. if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
  185. rs->history[++rs->hist_entry] != NULL) {
  186. pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
  187. rs->history[rs->hist_entry]);
  188. } else {
  189. rs->cmd_buf[0] = 0;
  190. rs->hist_entry = -1;
  191. }
  192. rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
  193. }
  194. static void readline_hist_add(ReadLineState *rs, const char *cmdline)
  195. {
  196. char *hist_entry, *new_entry;
  197. int idx;
  198. if (cmdline[0] == '\0') {
  199. return;
  200. }
  201. new_entry = NULL;
  202. if (rs->hist_entry != -1) {
  203. /* We were editing an existing history entry: replace it */
  204. hist_entry = rs->history[rs->hist_entry];
  205. idx = rs->hist_entry;
  206. if (strcmp(hist_entry, cmdline) == 0) {
  207. goto same_entry;
  208. }
  209. }
  210. /* Search cmdline in history buffers */
  211. for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
  212. hist_entry = rs->history[idx];
  213. if (hist_entry == NULL) {
  214. break;
  215. }
  216. if (strcmp(hist_entry, cmdline) == 0) {
  217. same_entry:
  218. new_entry = hist_entry;
  219. /* Put this entry at the end of history */
  220. memmove(&rs->history[idx], &rs->history[idx + 1],
  221. (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *));
  222. rs->history[READLINE_MAX_CMDS - 1] = NULL;
  223. for (; idx < READLINE_MAX_CMDS; idx++) {
  224. if (rs->history[idx] == NULL) {
  225. break;
  226. }
  227. }
  228. break;
  229. }
  230. }
  231. if (idx == READLINE_MAX_CMDS) {
  232. /* Need to get one free slot */
  233. g_free(rs->history[0]);
  234. memmove(rs->history, &rs->history[1],
  235. (READLINE_MAX_CMDS - 1) * sizeof(char *));
  236. rs->history[READLINE_MAX_CMDS - 1] = NULL;
  237. idx = READLINE_MAX_CMDS - 1;
  238. }
  239. if (new_entry == NULL) {
  240. new_entry = g_strdup(cmdline);
  241. }
  242. rs->history[idx] = new_entry;
  243. rs->hist_entry = -1;
  244. }
  245. /* completion support */
  246. void readline_add_completion(ReadLineState *rs, const char *str)
  247. {
  248. if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
  249. int i;
  250. for (i = 0; i < rs->nb_completions; i++) {
  251. if (!strcmp(rs->completions[i], str)) {
  252. return;
  253. }
  254. }
  255. rs->completions[rs->nb_completions++] = g_strdup(str);
  256. }
  257. }
  258. void readline_set_completion_index(ReadLineState *rs, int index)
  259. {
  260. rs->completion_index = index;
  261. }
  262. static int completion_comp(const void *a, const void *b)
  263. {
  264. return strcmp(*(const char **) a, *(const char **) b);
  265. }
  266. static void readline_completion(ReadLineState *rs)
  267. {
  268. int len, i, j, max_width, nb_cols, max_prefix;
  269. char *cmdline;
  270. rs->nb_completions = 0;
  271. cmdline = g_strndup(rs->cmd_buf, rs->cmd_buf_index);
  272. rs->completion_finder(rs->opaque, cmdline);
  273. g_free(cmdline);
  274. /* no completion found */
  275. if (rs->nb_completions <= 0) {
  276. return;
  277. }
  278. if (rs->nb_completions == 1) {
  279. len = strlen(rs->completions[0]);
  280. for (i = rs->completion_index; i < len; i++) {
  281. readline_insert_char(rs, rs->completions[0][i]);
  282. }
  283. /* extra space for next argument. XXX: make it more generic */
  284. if (len > 0 && rs->completions[0][len - 1] != '/') {
  285. readline_insert_char(rs, ' ');
  286. }
  287. } else {
  288. qsort(rs->completions, rs->nb_completions, sizeof(char *),
  289. completion_comp);
  290. rs->printf_func(rs->opaque, "\n");
  291. max_width = 0;
  292. max_prefix = 0;
  293. for (i = 0; i < rs->nb_completions; i++) {
  294. len = strlen(rs->completions[i]);
  295. if (i == 0) {
  296. max_prefix = len;
  297. } else {
  298. if (len < max_prefix) {
  299. max_prefix = len;
  300. }
  301. for (j = 0; j < max_prefix; j++) {
  302. if (rs->completions[i][j] != rs->completions[0][j]) {
  303. max_prefix = j;
  304. }
  305. }
  306. }
  307. if (len > max_width) {
  308. max_width = len;
  309. }
  310. }
  311. if (max_prefix > 0)
  312. for (i = rs->completion_index; i < max_prefix; i++) {
  313. readline_insert_char(rs, rs->completions[0][i]);
  314. }
  315. max_width += 2;
  316. if (max_width < 10) {
  317. max_width = 10;
  318. } else if (max_width > 80) {
  319. max_width = 80;
  320. }
  321. nb_cols = 80 / max_width;
  322. j = 0;
  323. for (i = 0; i < rs->nb_completions; i++) {
  324. rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]);
  325. if (++j == nb_cols || i == (rs->nb_completions - 1)) {
  326. rs->printf_func(rs->opaque, "\n");
  327. j = 0;
  328. }
  329. }
  330. readline_show_prompt(rs);
  331. }
  332. for (i = 0; i < rs->nb_completions; i++) {
  333. g_free(rs->completions[i]);
  334. }
  335. }
  336. static void readline_clear_screen(ReadLineState *rs)
  337. {
  338. rs->printf_func(rs->opaque, "\033[2J\033[1;1H");
  339. readline_show_prompt(rs);
  340. }
  341. /* return true if command handled */
  342. void readline_handle_byte(ReadLineState *rs, int ch)
  343. {
  344. switch (rs->esc_state) {
  345. case IS_NORM:
  346. switch (ch) {
  347. case 1:
  348. readline_bol(rs);
  349. break;
  350. case 4:
  351. readline_delete_char(rs);
  352. break;
  353. case 5:
  354. readline_eol(rs);
  355. break;
  356. case 9:
  357. readline_completion(rs);
  358. break;
  359. case 12:
  360. readline_clear_screen(rs);
  361. break;
  362. case 10:
  363. case 13:
  364. rs->cmd_buf[rs->cmd_buf_size] = '\0';
  365. if (!rs->read_password) {
  366. readline_hist_add(rs, rs->cmd_buf);
  367. }
  368. rs->printf_func(rs->opaque, "\n");
  369. rs->cmd_buf_index = 0;
  370. rs->cmd_buf_size = 0;
  371. rs->last_cmd_buf_index = 0;
  372. rs->last_cmd_buf_size = 0;
  373. rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque);
  374. break;
  375. case 23:
  376. /* ^W */
  377. readline_backword(rs);
  378. break;
  379. case 27:
  380. rs->esc_state = IS_ESC;
  381. break;
  382. case 127:
  383. case 8:
  384. readline_backspace(rs);
  385. break;
  386. case 155:
  387. rs->esc_state = IS_CSI;
  388. break;
  389. default:
  390. if (ch >= 32) {
  391. readline_insert_char(rs, ch);
  392. }
  393. break;
  394. }
  395. break;
  396. case IS_ESC:
  397. if (ch == '[') {
  398. rs->esc_state = IS_CSI;
  399. rs->esc_param = 0;
  400. } else if (ch == 'O') {
  401. rs->esc_state = IS_SS3;
  402. rs->esc_param = 0;
  403. } else {
  404. rs->esc_state = IS_NORM;
  405. }
  406. break;
  407. case IS_CSI:
  408. switch (ch) {
  409. case 'A':
  410. case 'F':
  411. readline_up_char(rs);
  412. break;
  413. case 'B':
  414. case 'E':
  415. readline_down_char(rs);
  416. break;
  417. case 'D':
  418. readline_backward_char(rs);
  419. break;
  420. case 'C':
  421. readline_forward_char(rs);
  422. break;
  423. case '0' ... '9':
  424. rs->esc_param = rs->esc_param * 10 + (ch - '0');
  425. goto the_end;
  426. case '~':
  427. switch (rs->esc_param) {
  428. case 1:
  429. readline_bol(rs);
  430. break;
  431. case 3:
  432. readline_delete_char(rs);
  433. break;
  434. case 4:
  435. readline_eol(rs);
  436. break;
  437. }
  438. break;
  439. default:
  440. break;
  441. }
  442. rs->esc_state = IS_NORM;
  443. the_end:
  444. break;
  445. case IS_SS3:
  446. switch (ch) {
  447. case 'F':
  448. readline_eol(rs);
  449. break;
  450. case 'H':
  451. readline_bol(rs);
  452. break;
  453. }
  454. rs->esc_state = IS_NORM;
  455. break;
  456. }
  457. readline_update(rs);
  458. }
  459. void readline_start(ReadLineState *rs, const char *prompt, int read_password,
  460. ReadLineFunc *readline_func, void *opaque)
  461. {
  462. pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
  463. rs->readline_func = readline_func;
  464. rs->readline_opaque = opaque;
  465. rs->read_password = read_password;
  466. readline_restart(rs);
  467. }
  468. void readline_restart(ReadLineState *rs)
  469. {
  470. rs->cmd_buf_index = 0;
  471. rs->cmd_buf_size = 0;
  472. }
  473. const char *readline_get_history(ReadLineState *rs, unsigned int index)
  474. {
  475. if (index >= READLINE_MAX_CMDS) {
  476. return NULL;
  477. }
  478. return rs->history[index];
  479. }
  480. void readline_free(ReadLineState *rs)
  481. {
  482. int i;
  483. if (!rs) {
  484. return;
  485. }
  486. for (i = 0; i < READLINE_MAX_CMDS; i++) {
  487. g_free(rs->history[i]);
  488. }
  489. g_free(rs);
  490. }
  491. ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
  492. ReadLineFlushFunc *flush_func,
  493. void *opaque,
  494. ReadLineCompletionFunc *completion_finder)
  495. {
  496. ReadLineState *rs = g_new0(ReadLineState, 1);
  497. rs->hist_entry = -1;
  498. rs->opaque = opaque;
  499. rs->printf_func = printf_func;
  500. rs->flush_func = flush_func;
  501. rs->completion_finder = completion_finder;
  502. return rs;
  503. }