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