blob: 0ee790877748125007bc77ea14e363bb15d2cb98 [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;
Yurii Monakov2dd86b92023-10-10 11:16:39 +030049 else
50 act = ESC_CONVERTED;
Simon Glassb08e9d42023-01-06 08:52:20 -060051 break;
52 case 2:
53 switch (ichar) {
54 case 'D': /* <- key */
55 ichar = CTL_CH('b');
56 act = ESC_CONVERTED;
57 break; /* pass off to ^B handler */
58 case 'C': /* -> key */
59 ichar = CTL_CH('f');
60 act = ESC_CONVERTED;
61 break; /* pass off to ^F handler */
62 case 'H': /* Home key */
63 ichar = CTL_CH('a');
64 act = ESC_CONVERTED;
65 break; /* pass off to ^A handler */
66 case 'F': /* End key */
67 ichar = CTL_CH('e');
68 act = ESC_CONVERTED;
69 break; /* pass off to ^E handler */
70 case 'A': /* up arrow */
71 ichar = CTL_CH('p');
72 act = ESC_CONVERTED;
73 break; /* pass off to ^P handler */
74 case 'B': /* down arrow */
75 ichar = CTL_CH('n');
76 act = ESC_CONVERTED;
77 break; /* pass off to ^N handler */
78 case '1':
79 case '2':
80 case '3':
81 case '4':
82 case '7':
83 case '8':
84 if (cch->esc_save[1] == '[') {
85 /* see if next character is ~ */
86 act = ESC_SAVE;
87 }
88 break;
89 }
90 break;
91 case 3:
92 switch (ichar) {
93 case '~':
94 switch (cch->esc_save[2]) {
95 case '3': /* Delete key */
96 ichar = CTL_CH('d');
97 act = ESC_CONVERTED;
98 break; /* pass to ^D handler */
99 case '1': /* Home key */
100 case '7':
101 ichar = CTL_CH('a');
102 act = ESC_CONVERTED;
103 break; /* pass to ^A handler */
104 case '4': /* End key */
105 case '8':
106 ichar = CTL_CH('e');
107 act = ESC_CONVERTED;
108 break; /* pass to ^E handler */
109 }
110 break;
111 case '0':
112 if (cch->esc_save[2] == '2')
113 act = ESC_SAVE;
114 break;
115 }
116 break;
117 case 4:
118 switch (ichar) {
119 case '0':
120 case '1':
121 act = ESC_SAVE;
122 break; /* bracketed paste */
123 }
124 break;
125 case 5:
126 if (ichar == '~') { /* bracketed paste */
127 ichar = 0;
128 act = ESC_CONVERTED;
129 }
130 }
131
132 *actp = act;
133
Simon Glass17b45e62023-03-28 08:34:13 +1300134 return ichar;
Simon Glassb08e9d42023-01-06 08:52:20 -0600135}
136
137int cli_ch_process(struct cli_ch_state *cch, int ichar)
138{
139 /*
140 * ichar=0x0 when error occurs in U-Boot getchar() or when the caller
141 * wants to check if there are more characters saved in the escape
142 * sequence
143 */
144 if (!ichar) {
Simon Glass32bab0e2023-01-06 08:52:26 -0600145 if (cch->emitting) {
Simon Glassb08e9d42023-01-06 08:52:20 -0600146 if (cch->emit_upto < cch->esc_len)
147 return cch->esc_save[cch->emit_upto++];
148 cch->emit_upto = 0;
Simon Glass32bab0e2023-01-06 08:52:26 -0600149 cch->emitting = false;
Simon Glass17b45e62023-03-28 08:34:13 +1300150 cch->esc_len = 0;
Simon Glassb08e9d42023-01-06 08:52:20 -0600151 }
152 return 0;
153 } else if (ichar == -ETIMEDOUT) {
154 /*
155 * If we are in an escape sequence but nothing has followed the
156 * Escape character, then the user probably just pressed the
157 * Escape key. Return it and clear the sequence.
158 */
159 if (cch->esc_len) {
160 cch->esc_len = 0;
161 return '\e';
162 }
163
164 /* Otherwise there is nothing to return */
165 return 0;
166 }
167
168 if (ichar == '\n' || ichar == '\r')
169 return '\n';
170
171 /* handle standard linux xterm esc sequences for arrow key, etc. */
172 if (cch->esc_len != 0) {
173 enum cli_esc_state_t act;
174
175 ichar = cli_ch_esc(cch, ichar, &act);
176
177 switch (act) {
178 case ESC_SAVE:
179 /* save this character and return nothing */
180 cch->esc_save[cch->esc_len++] = ichar;
Simon Glass32bab0e2023-01-06 08:52:26 -0600181 ichar = 0;
182 break;
Simon Glassb08e9d42023-01-06 08:52:20 -0600183 case ESC_REJECT:
184 /*
185 * invalid escape sequence, start returning the
186 * characters in it
187 */
188 cch->esc_save[cch->esc_len++] = ichar;
Simon Glass32bab0e2023-01-06 08:52:26 -0600189 ichar = cch->esc_save[cch->emit_upto++];
190 cch->emitting = true;
Simon Glass17b45e62023-03-28 08:34:13 +1300191 return ichar;
Simon Glassb08e9d42023-01-06 08:52:20 -0600192 case ESC_CONVERTED:
193 /* valid escape sequence, return the resulting char */
194 cch->esc_len = 0;
Simon Glass32bab0e2023-01-06 08:52:26 -0600195 break;
Simon Glassb08e9d42023-01-06 08:52:20 -0600196 }
197 }
198
199 if (ichar == '\e') {
200 if (!cch->esc_len) {
201 cch->esc_save[cch->esc_len] = ichar;
202 cch->esc_len = 1;
203 } else {
204 puts("impossible condition #876\n");
205 cch->esc_len = 0;
206 }
207 return 0;
208 }
209
210 return ichar;
211}