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 */ 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 else 50 act = ESC_CONVERTED; 51 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 134 return ichar; 135} 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) { 145 if (cch->emitting) { 146 if (cch->emit_upto < cch->esc_len) 147 return cch->esc_save[cch->emit_upto++]; 148 cch->emit_upto = 0; 149 cch->emitting = false; 150 cch->esc_len = 0; 151 } 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; 181 ichar = 0; 182 break; 183 case ESC_REJECT: 184 /* 185 * invalid escape sequence, start returning the 186 * characters in it 187 */ 188 cch->esc_save[cch->esc_len++] = ichar; 189 ichar = cch->esc_save[cch->emit_upto++]; 190 cch->emitting = true; 191 return ichar; 192 case ESC_CONVERTED: 193 /* valid escape sequence, return the resulting char */ 194 cch->esc_len = 0; 195 break; 196 } 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} 212