cli_getch.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * (C) Copyright 2000
  4. * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  5. *
  6. * Copyright 2022 Google LLC
  7. */
  8. #include <common.h>
  9. #include <cli.h>
  10. /**
  11. * enum cli_esc_state_t - indicates what to do with an escape character
  12. *
  13. * @ESC_REJECT: Invalid escape sequence, so the esc_save[] characters are
  14. * returned from each subsequent call to cli_ch_esc()
  15. * @ESC_SAVE: Character should be saved in esc_save until we have another one
  16. * @ESC_CONVERTED: Escape sequence has been completed and the resulting
  17. * character is available
  18. */
  19. enum cli_esc_state_t {
  20. ESC_REJECT,
  21. ESC_SAVE,
  22. ESC_CONVERTED
  23. };
  24. void cli_ch_init(struct cli_ch_state *cch)
  25. {
  26. memset(cch, '\0', sizeof(*cch));
  27. }
  28. /**
  29. * cli_ch_esc() - Process a character in an ongoing escape sequence
  30. *
  31. * @cch: State information
  32. * @ichar: Character to process
  33. * @actp: Returns the action to take
  34. * Returns: Output character if *actp is ESC_CONVERTED, else 0
  35. */
  36. static int cli_ch_esc(struct cli_ch_state *cch, int ichar,
  37. enum cli_esc_state_t *actp)
  38. {
  39. enum cli_esc_state_t act = ESC_REJECT;
  40. switch (cch->esc_len) {
  41. case 1:
  42. if (ichar == '[' || ichar == 'O')
  43. act = ESC_SAVE;
  44. break;
  45. case 2:
  46. switch (ichar) {
  47. case 'D': /* <- key */
  48. ichar = CTL_CH('b');
  49. act = ESC_CONVERTED;
  50. break; /* pass off to ^B handler */
  51. case 'C': /* -> key */
  52. ichar = CTL_CH('f');
  53. act = ESC_CONVERTED;
  54. break; /* pass off to ^F handler */
  55. case 'H': /* Home key */
  56. ichar = CTL_CH('a');
  57. act = ESC_CONVERTED;
  58. break; /* pass off to ^A handler */
  59. case 'F': /* End key */
  60. ichar = CTL_CH('e');
  61. act = ESC_CONVERTED;
  62. break; /* pass off to ^E handler */
  63. case 'A': /* up arrow */
  64. ichar = CTL_CH('p');
  65. act = ESC_CONVERTED;
  66. break; /* pass off to ^P handler */
  67. case 'B': /* down arrow */
  68. ichar = CTL_CH('n');
  69. act = ESC_CONVERTED;
  70. break; /* pass off to ^N handler */
  71. case '1':
  72. case '2':
  73. case '3':
  74. case '4':
  75. case '7':
  76. case '8':
  77. if (cch->esc_save[1] == '[') {
  78. /* see if next character is ~ */
  79. act = ESC_SAVE;
  80. }
  81. break;
  82. }
  83. break;
  84. case 3:
  85. switch (ichar) {
  86. case '~':
  87. switch (cch->esc_save[2]) {
  88. case '3': /* Delete key */
  89. ichar = CTL_CH('d');
  90. act = ESC_CONVERTED;
  91. break; /* pass to ^D handler */
  92. case '1': /* Home key */
  93. case '7':
  94. ichar = CTL_CH('a');
  95. act = ESC_CONVERTED;
  96. break; /* pass to ^A handler */
  97. case '4': /* End key */
  98. case '8':
  99. ichar = CTL_CH('e');
  100. act = ESC_CONVERTED;
  101. break; /* pass to ^E handler */
  102. }
  103. break;
  104. case '0':
  105. if (cch->esc_save[2] == '2')
  106. act = ESC_SAVE;
  107. break;
  108. }
  109. break;
  110. case 4:
  111. switch (ichar) {
  112. case '0':
  113. case '1':
  114. act = ESC_SAVE;
  115. break; /* bracketed paste */
  116. }
  117. break;
  118. case 5:
  119. if (ichar == '~') { /* bracketed paste */
  120. ichar = 0;
  121. act = ESC_CONVERTED;
  122. }
  123. }
  124. *actp = act;
  125. return ichar;
  126. }
  127. int cli_ch_process(struct cli_ch_state *cch, int ichar)
  128. {
  129. /*
  130. * ichar=0x0 when error occurs in U-Boot getchar() or when the caller
  131. * wants to check if there are more characters saved in the escape
  132. * sequence
  133. */
  134. if (!ichar) {
  135. if (cch->emitting) {
  136. if (cch->emit_upto < cch->esc_len)
  137. return cch->esc_save[cch->emit_upto++];
  138. cch->emit_upto = 0;
  139. cch->emitting = false;
  140. cch->esc_len = 0;
  141. }
  142. return 0;
  143. } else if (ichar == -ETIMEDOUT) {
  144. /*
  145. * If we are in an escape sequence but nothing has followed the
  146. * Escape character, then the user probably just pressed the
  147. * Escape key. Return it and clear the sequence.
  148. */
  149. if (cch->esc_len) {
  150. cch->esc_len = 0;
  151. return '\e';
  152. }
  153. /* Otherwise there is nothing to return */
  154. return 0;
  155. }
  156. if (ichar == '\n' || ichar == '\r')
  157. return '\n';
  158. /* handle standard linux xterm esc sequences for arrow key, etc. */
  159. if (cch->esc_len != 0) {
  160. enum cli_esc_state_t act;
  161. ichar = cli_ch_esc(cch, ichar, &act);
  162. switch (act) {
  163. case ESC_SAVE:
  164. /* save this character and return nothing */
  165. cch->esc_save[cch->esc_len++] = ichar;
  166. ichar = 0;
  167. break;
  168. case ESC_REJECT:
  169. /*
  170. * invalid escape sequence, start returning the
  171. * characters in it
  172. */
  173. cch->esc_save[cch->esc_len++] = ichar;
  174. ichar = cch->esc_save[cch->emit_upto++];
  175. cch->emitting = true;
  176. return ichar;
  177. case ESC_CONVERTED:
  178. /* valid escape sequence, return the resulting char */
  179. cch->esc_len = 0;
  180. break;
  181. }
  182. }
  183. if (ichar == '\e') {
  184. if (!cch->esc_len) {
  185. cch->esc_save[cch->esc_len] = ichar;
  186. cch->esc_len = 1;
  187. } else {
  188. puts("impossible condition #876\n");
  189. cch->esc_len = 0;
  190. }
  191. return 0;
  192. }
  193. return ichar;
  194. }