1 /******************************************************************************
2 *
3 * Module Name: acgetline - local line editing
4 *
5 *****************************************************************************/
6
7 /*
8 * Copyright (C) 2000 - 2023, 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
AcpiAcClearLine(UINT32 EndOfLine,UINT32 CursorPosition)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
AcpiOsGetLine(char * Buffer,UINT32 BufferLength,UINT32 * BytesRead)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