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