1 /****************************************************************************** 2 * 3 * Module Name: acgetline - local line editing 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2022, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include "acpi.h" 45 #include "accommon.h" 46 #include "amlcode.h" 47 #include "acparser.h" 48 #include "acdebug.h" 49 50 /* 51 * This is an os-independent implementation of line-editing services needed 52 * by the AcpiExec utility. It uses getchar() and putchar() and the existing 53 * history support provided by the AML debugger. It assumes that the terminal 54 * is in the correct line-editing mode such as raw and noecho. The OSL 55 * interface AcpiOsInitialize should do this. AcpiOsTerminate should put the 56 * terminal back into the original mode. 57 */ 58 #define _COMPONENT ACPI_OS_SERVICES 59 ACPI_MODULE_NAME ("acgetline") 60 61 62 /* Local prototypes */ 63 64 static void 65 AcpiAcClearLine ( 66 UINT32 EndOfLine, 67 UINT32 CursorPosition); 68 69 /* Various ASCII constants */ 70 71 #define _ASCII_NUL 0 72 #define _ASCII_BACKSPACE 0x08 73 #define _ASCII_TAB 0x09 74 #define _ASCII_ESCAPE 0x1B 75 #define _ASCII_SPACE 0x20 76 #define _ASCII_LEFT_BRACKET 0x5B 77 #define _ASCII_DEL 0x7F 78 #define _ASCII_UP_ARROW 'A' 79 #define _ASCII_DOWN_ARROW 'B' 80 #define _ASCII_RIGHT_ARROW 'C' 81 #define _ASCII_LEFT_ARROW 'D' 82 #define _ASCII_NEWLINE '\n' 83 84 /* Erase a single character on the input command line */ 85 86 #define ACPI_CLEAR_CHAR() \ 87 putchar (_ASCII_BACKSPACE); \ 88 putchar (_ASCII_SPACE); \ 89 putchar (_ASCII_BACKSPACE); 90 91 /* Backup cursor by Count positions */ 92 93 #define ACPI_BACKUP_CURSOR(i, Count) \ 94 for (i = 0; i < (Count); i++) \ 95 {putchar (_ASCII_BACKSPACE);} 96 97 98 /****************************************************************************** 99 * 100 * FUNCTION: AcpiAcClearLine 101 * 102 * PARAMETERS: EndOfLine - Current end-of-line index 103 * CursorPosition - Current cursor position within line 104 * 105 * RETURN: None 106 * 107 * DESCRIPTION: Clear the entire command line the hard way, but probably the 108 * most portable. 109 * 110 *****************************************************************************/ 111 112 static void 113 AcpiAcClearLine ( 114 UINT32 EndOfLine, 115 UINT32 CursorPosition) 116 { 117 UINT32 i; 118 119 120 if (CursorPosition < EndOfLine) 121 { 122 /* Clear line from current position to end of line */ 123 124 for (i = 0; i < (EndOfLine - CursorPosition); i++) 125 { 126 putchar (' '); 127 } 128 } 129 130 /* Clear the entire line */ 131 132 for (; EndOfLine > 0; EndOfLine--) 133 { 134 ACPI_CLEAR_CHAR (); 135 } 136 } 137 138 139 /****************************************************************************** 140 * 141 * FUNCTION: AcpiOsGetLine 142 * 143 * PARAMETERS: Buffer - Where to return the command line 144 * BufferLength - Maximum length of Buffer 145 * BytesRead - Where the actual byte count is returned 146 * 147 * RETURN: Status and actual bytes read 148 * 149 * DESCRIPTION: Get the next input line from the terminal. NOTE: terminal 150 * is expected to be in a mode that supports line-editing (raw, 151 * noecho). This function is intended to be very portable. Also, 152 * it uses the history support implemented in the AML debugger. 153 * 154 *****************************************************************************/ 155 156 ACPI_STATUS 157 AcpiOsGetLine ( 158 char *Buffer, 159 UINT32 BufferLength, 160 UINT32 *BytesRead) 161 { 162 char *NextCommand; 163 UINT32 MaxCommandIndex = AcpiGbl_NextCmdNum - 1; 164 UINT32 CurrentCommandIndex = MaxCommandIndex; 165 UINT32 PreviousCommandIndex = MaxCommandIndex; 166 int InputChar; 167 UINT32 CursorPosition = 0; 168 UINT32 EndOfLine = 0; 169 UINT32 i; 170 171 172 /* Always clear the line buffer before we read a new line */ 173 174 memset (Buffer, 0, BufferLength); 175 176 /* 177 * This loop gets one character at a time (except for esc sequences) 178 * until a newline or error is detected. 179 * 180 * Note: Don't attempt to write terminal control ESC sequences, even 181 * though it makes certain things more difficult. 182 */ 183 while (1) 184 { 185 if (EndOfLine >= (BufferLength - 1)) 186 { 187 return (AE_BUFFER_OVERFLOW); 188 } 189 190 InputChar = getchar (); 191 switch (InputChar) 192 { 193 default: /* This is the normal character case */ 194 195 /* Echo the character (at EOL) and copy it to the line buffer */ 196 197 if (EndOfLine == CursorPosition) 198 { 199 putchar (InputChar); 200 Buffer[EndOfLine] = (char) InputChar; 201 202 EndOfLine++; 203 CursorPosition++; 204 Buffer[EndOfLine] = 0; 205 continue; 206 } 207 208 /* Insert character into the middle of the buffer */ 209 210 memmove (&Buffer[CursorPosition + 1], &Buffer[CursorPosition], 211 (EndOfLine - CursorPosition + 1)); 212 213 Buffer [CursorPosition] = (char) InputChar; 214 Buffer [EndOfLine + 1] = 0; 215 216 /* Display the new part of line starting at the new character */ 217 218 fprintf (stdout, "%s", &Buffer[CursorPosition]); 219 220 /* Restore cursor */ 221 222 ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition); 223 CursorPosition++; 224 EndOfLine++; 225 continue; 226 227 case _ASCII_DEL: /* Backspace key */ 228 229 if (!EndOfLine) /* Any characters on the command line? */ 230 { 231 continue; 232 } 233 234 if (EndOfLine == CursorPosition) /* Erase the final character */ 235 { 236 ACPI_CLEAR_CHAR (); 237 EndOfLine--; 238 CursorPosition--; 239 continue; 240 } 241 242 if (!CursorPosition) /* Do not backup beyond start of line */ 243 { 244 continue; 245 } 246 247 /* Remove the character from the line */ 248 249 memmove (&Buffer[CursorPosition - 1], &Buffer[CursorPosition], 250 (EndOfLine - CursorPosition + 1)); 251 252 /* Display the new part of line starting at the new character */ 253 254 putchar (_ASCII_BACKSPACE); 255 fprintf (stdout, "%s ", &Buffer[CursorPosition - 1]); 256 257 /* Restore cursor */ 258 259 ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition + 1); 260 EndOfLine--; 261 262 if (CursorPosition > 0) 263 { 264 CursorPosition--; 265 } 266 continue; 267 268 case _ASCII_NEWLINE: /* Normal exit case at end of command line */ 269 case _ASCII_NUL: 270 271 /* Return the number of bytes in the command line string */ 272 273 if (BytesRead) 274 { 275 *BytesRead = EndOfLine; 276 } 277 278 /* Echo, terminate string buffer, and exit */ 279 280 putchar (InputChar); 281 Buffer[EndOfLine] = 0; 282 return (AE_OK); 283 284 case _ASCII_TAB: 285 286 /* Ignore */ 287 288 continue; 289 290 case EOF: 291 292 return (AE_ERROR); 293 294 case _ASCII_ESCAPE: 295 296 /* Check for escape sequences of the form "ESC[x" */ 297 298 InputChar = getchar (); 299 if (InputChar != _ASCII_LEFT_BRACKET) 300 { 301 continue; /* Ignore this ESC, does not have the '[' */ 302 } 303 304 /* Get the code following the ESC [ */ 305 306 InputChar = getchar (); /* Backup one character */ 307 switch (InputChar) 308 { 309 case _ASCII_LEFT_ARROW: 310 311 if (CursorPosition > 0) 312 { 313 putchar (_ASCII_BACKSPACE); 314 CursorPosition--; 315 } 316 continue; 317 318 case _ASCII_RIGHT_ARROW: 319 /* 320 * Move one character forward. Do this without sending 321 * ESC sequence to the terminal for max portability. 322 */ 323 if (CursorPosition < EndOfLine) 324 { 325 /* Backup to start of line and print the entire line */ 326 327 ACPI_BACKUP_CURSOR (i, CursorPosition); 328 fprintf (stdout, "%s", Buffer); 329 330 /* Backup to where the cursor should be */ 331 332 CursorPosition++; 333 ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition); 334 } 335 continue; 336 337 case _ASCII_UP_ARROW: 338 339 /* If no commands available or at start of history list, ignore */ 340 341 if (!CurrentCommandIndex) 342 { 343 continue; 344 } 345 346 /* Manage our up/down progress */ 347 348 if (CurrentCommandIndex > PreviousCommandIndex) 349 { 350 CurrentCommandIndex = PreviousCommandIndex; 351 } 352 353 /* Get the historical command from the debugger */ 354 355 NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex); 356 if (!NextCommand) 357 { 358 return (AE_ERROR); 359 } 360 361 /* Make this the active command and echo it */ 362 363 AcpiAcClearLine (EndOfLine, CursorPosition); 364 strcpy (Buffer, NextCommand); 365 fprintf (stdout, "%s", Buffer); 366 EndOfLine = CursorPosition = strlen (Buffer); 367 368 PreviousCommandIndex = CurrentCommandIndex; 369 CurrentCommandIndex--; 370 continue; 371 372 case _ASCII_DOWN_ARROW: 373 374 if (!MaxCommandIndex) /* Any commands available? */ 375 { 376 continue; 377 } 378 379 /* Manage our up/down progress */ 380 381 if (CurrentCommandIndex < PreviousCommandIndex) 382 { 383 CurrentCommandIndex = PreviousCommandIndex; 384 } 385 386 /* If we are the end of the history list, output a clear new line */ 387 388 if ((CurrentCommandIndex + 1) > MaxCommandIndex) 389 { 390 AcpiAcClearLine (EndOfLine, CursorPosition); 391 EndOfLine = CursorPosition = 0; 392 PreviousCommandIndex = CurrentCommandIndex; 393 continue; 394 } 395 396 PreviousCommandIndex = CurrentCommandIndex; 397 CurrentCommandIndex++; 398 399 /* Get the historical command from the debugger */ 400 401 NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex); 402 if (!NextCommand) 403 { 404 return (AE_ERROR); 405 } 406 407 /* Make this the active command and echo it */ 408 409 AcpiAcClearLine (EndOfLine, CursorPosition); 410 strcpy (Buffer, NextCommand); 411 fprintf (stdout, "%s", Buffer); 412 EndOfLine = CursorPosition = strlen (Buffer); 413 continue; 414 415 case 0x31: 416 case 0x32: 417 case 0x33: 418 case 0x34: 419 case 0x35: 420 case 0x36: 421 /* 422 * Ignore the various keys like insert/delete/home/end, etc. 423 * But we must eat the final character of the ESC sequence. 424 */ 425 (void) getchar (); 426 continue; 427 428 default: 429 430 /* Ignore random escape sequences that we don't care about */ 431 432 continue; 433 } 434 continue; 435 } 436 } 437 } 438