blob: 87c23edcf4b4b640135f55f3ebf502e57970c13d [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
132 return act == ESC_CONVERTED ? ichar : 0;
133}
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 Glassb08e9d42023-01-06 08:52:20 -0600148 }
149 return 0;
150 } else if (ichar == -ETIMEDOUT) {
151 /*
152 * If we are in an escape sequence but nothing has followed the
153 * Escape character, then the user probably just pressed the
154 * Escape key. Return it and clear the sequence.
155 */
156 if (cch->esc_len) {
157 cch->esc_len = 0;
158 return '\e';
159 }
160
161 /* Otherwise there is nothing to return */
162 return 0;
163 }
164
165 if (ichar == '\n' || ichar == '\r')
166 return '\n';
167
168 /* handle standard linux xterm esc sequences for arrow key, etc. */
169 if (cch->esc_len != 0) {
170 enum cli_esc_state_t act;
171
172 ichar = cli_ch_esc(cch, ichar, &act);
173
174 switch (act) {
175 case ESC_SAVE:
176 /* save this character and return nothing */
177 cch->esc_save[cch->esc_len++] = ichar;
Simon Glass32bab0e2023-01-06 08:52:26 -0600178 ichar = 0;
179 break;
Simon Glassb08e9d42023-01-06 08:52:20 -0600180 case ESC_REJECT:
181 /*
182 * invalid escape sequence, start returning the
183 * characters in it
184 */
185 cch->esc_save[cch->esc_len++] = ichar;
Simon Glass32bab0e2023-01-06 08:52:26 -0600186 ichar = cch->esc_save[cch->emit_upto++];
187 cch->emitting = true;
188 break;
Simon Glassb08e9d42023-01-06 08:52:20 -0600189 case ESC_CONVERTED:
190 /* valid escape sequence, return the resulting char */
191 cch->esc_len = 0;
Simon Glass32bab0e2023-01-06 08:52:26 -0600192 break;
Simon Glassb08e9d42023-01-06 08:52:20 -0600193 }
194 }
195
196 if (ichar == '\e') {
197 if (!cch->esc_len) {
198 cch->esc_save[cch->esc_len] = ichar;
199 cch->esc_len = 1;
200 } else {
201 puts("impossible condition #876\n");
202 cch->esc_len = 0;
203 }
204 return 0;
205 }
206
207 return ichar;
208}