Prepare v2023.10
[platform/kernel/u-boot.git] / common / cli_getch.c
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
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  */
21 enum cli_esc_state_t {
22         ESC_REJECT,
23         ESC_SAVE,
24         ESC_CONVERTED
25 };
26
27 void 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  */
40 static 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 ichar;
133 }
134
135 int 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) {
143                 if (cch->emitting) {
144                         if (cch->emit_upto < cch->esc_len)
145                                 return cch->esc_save[cch->emit_upto++];
146                         cch->emit_upto = 0;
147                         cch->emitting = false;
148                         cch->esc_len = 0;
149                 }
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;
179                         ichar = 0;
180                         break;
181                 case ESC_REJECT:
182                         /*
183                          * invalid escape sequence, start returning the
184                          * characters in it
185                          */
186                         cch->esc_save[cch->esc_len++] = ichar;
187                         ichar = cch->esc_save[cch->emit_upto++];
188                         cch->emitting = true;
189                         return ichar;
190                 case ESC_CONVERTED:
191                         /* valid escape sequence, return the resulting char */
192                         cch->esc_len = 0;
193                         break;
194                 }
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 }