1 /******************************************************************************
2  *
3  * Module Name: acgetline - local line editing
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2016, 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 MERCHANTIBILITY 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 extern UINT32               AcpiGbl_NextCmdNum;
85 
86 /* Erase a single character on the input command line */
87 
88 #define ACPI_CLEAR_CHAR() \
89     putchar (_ASCII_BACKSPACE); \
90     putchar (_ASCII_SPACE); \
91     putchar (_ASCII_BACKSPACE);
92 
93 /* Backup cursor by Count positions */
94 
95 #define ACPI_BACKUP_CURSOR(i, Count) \
96     for (i = 0; i < (Count); i++) \
97         {putchar (_ASCII_BACKSPACE);}
98 
99 
100 /******************************************************************************
101  *
102  * FUNCTION:    AcpiAcClearLine
103  *
104  * PARAMETERS:  EndOfLine           - Current end-of-line index
105  *              CursorPosition      - Current cursor position within line
106  *
107  * RETURN:      None
108  *
109  * DESCRIPTION: Clear the entire command line the hard way, but probably the
110  *              most portable.
111  *
112  *****************************************************************************/
113 
114 static void
115 AcpiAcClearLine (
116     UINT32                  EndOfLine,
117     UINT32                  CursorPosition)
118 {
119     UINT32                  i;
120 
121 
122     if (CursorPosition < EndOfLine)
123     {
124         /* Clear line from current position to end of line */
125 
126         for (i = 0; i < (EndOfLine - CursorPosition); i++)
127         {
128             putchar (' ');
129         }
130     }
131 
132     /* Clear the entire line */
133 
134     for (; EndOfLine > 0; EndOfLine--)
135     {
136         ACPI_CLEAR_CHAR ();
137     }
138 }
139 
140 
141 /******************************************************************************
142  *
143  * FUNCTION:    AcpiOsGetLine
144  *
145  * PARAMETERS:  Buffer              - Where to return the command line
146  *              BufferLength        - Maximum length of Buffer
147  *              BytesRead           - Where the actual byte count is returned
148  *
149  * RETURN:      Status and actual bytes read
150  *
151  * DESCRIPTION: Get the next input line from the terminal. NOTE: terminal
152  *              is expected to be in a mode that supports line-editing (raw,
153  *              noecho). This function is intended to be very portable. Also,
154  *              it uses the history support implemented in the AML debugger.
155  *
156  *****************************************************************************/
157 
158 ACPI_STATUS
159 AcpiOsGetLine (
160     char                    *Buffer,
161     UINT32                  BufferLength,
162     UINT32                  *BytesRead)
163 {
164     char                    *NextCommand;
165     UINT32                  MaxCommandIndex = AcpiGbl_NextCmdNum - 1;
166     UINT32                  CurrentCommandIndex = MaxCommandIndex;
167     UINT32                  PreviousCommandIndex = MaxCommandIndex;
168     int                     InputChar;
169     UINT32                  CursorPosition = 0;
170     UINT32                  EndOfLine = 0;
171     UINT32                  i;
172 
173 
174     /* Always clear the line buffer before we read a new line */
175 
176     memset (Buffer, 0, BufferLength);
177 
178     /*
179      * This loop gets one character at a time (except for esc sequences)
180      * until a newline or error is detected.
181      *
182      * Note: Don't attempt to write terminal control ESC sequences, even
183      * though it makes certain things more difficult.
184      */
185     while (1)
186     {
187         if (EndOfLine >= (BufferLength - 1))
188         {
189             return (AE_BUFFER_OVERFLOW);
190         }
191 
192         InputChar = getchar ();
193         switch (InputChar)
194         {
195         default: /* This is the normal character case */
196 
197             /* Echo the character (at EOL) and copy it to the line buffer */
198 
199             if (EndOfLine == CursorPosition)
200             {
201                 putchar (InputChar);
202                 Buffer[EndOfLine] = (char) InputChar;
203 
204                 EndOfLine++;
205                 CursorPosition++;
206                 Buffer[EndOfLine] = 0;
207                 continue;
208             }
209 
210             /* Insert character into the middle of the buffer */
211 
212             memmove (&Buffer[CursorPosition + 1], &Buffer[CursorPosition],
213                 (EndOfLine - CursorPosition + 1));
214 
215             Buffer [CursorPosition] = (char) InputChar;
216             Buffer [EndOfLine + 1] = 0;
217 
218             /* Display the new part of line starting at the new character */
219 
220             fprintf (stdout, "%s", &Buffer[CursorPosition]);
221 
222             /* Restore cursor */
223 
224             ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition);
225             CursorPosition++;
226             EndOfLine++;
227             continue;
228 
229         case _ASCII_DEL: /* Backspace key */
230 
231             if (!EndOfLine) /* Any characters on the command line? */
232             {
233                 continue;
234             }
235 
236             if (EndOfLine == CursorPosition) /* Erase the final character */
237             {
238                 ACPI_CLEAR_CHAR ();
239                 EndOfLine--;
240                 CursorPosition--;
241                 continue;
242             }
243 
244             if (!CursorPosition) /* Do not backup beyond start of line */
245             {
246                 continue;
247             }
248 
249             /* Remove the character from the line */
250 
251             memmove (&Buffer[CursorPosition - 1], &Buffer[CursorPosition],
252                 (EndOfLine - CursorPosition + 1));
253 
254             /* Display the new part of line starting at the new character */
255 
256             putchar (_ASCII_BACKSPACE);
257             fprintf (stdout, "%s ", &Buffer[CursorPosition - 1]);
258 
259             /* Restore cursor */
260 
261             ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition + 1);
262             EndOfLine--;
263 
264             if (CursorPosition > 0)
265             {
266                 CursorPosition--;
267             }
268             continue;
269 
270         case _ASCII_NEWLINE: /* Normal exit case at end of command line */
271         case _ASCII_NUL:
272 
273             /* Return the number of bytes in the command line string */
274 
275             if (BytesRead)
276             {
277                 *BytesRead = EndOfLine;
278             }
279 
280             /* Echo, terminate string buffer, and exit */
281 
282             putchar (InputChar);
283             Buffer[EndOfLine] = 0;
284             return (AE_OK);
285 
286         case _ASCII_TAB:
287 
288             /* Ignore */
289 
290             continue;
291 
292         case EOF:
293 
294             return (AE_ERROR);
295 
296         case _ASCII_ESCAPE:
297 
298             /* Check for escape sequences of the form "ESC[x" */
299 
300             InputChar = getchar ();
301             if (InputChar != _ASCII_LEFT_BRACKET)
302             {
303                 continue; /* Ignore this ESC, does not have the '[' */
304             }
305 
306             /* Get the code following the ESC [ */
307 
308             InputChar = getchar (); /* Backup one character */
309             switch (InputChar)
310             {
311             case _ASCII_LEFT_ARROW:
312 
313                 if (CursorPosition > 0)
314                 {
315                     putchar (_ASCII_BACKSPACE);
316                     CursorPosition--;
317                 }
318                 continue;
319 
320             case _ASCII_RIGHT_ARROW:
321                 /*
322                  * Move one character forward. Do this without sending
323                  * ESC sequence to the terminal for max portability.
324                  */
325                 if (CursorPosition < EndOfLine)
326                 {
327                     /* Backup to start of line and print the entire line */
328 
329                     ACPI_BACKUP_CURSOR (i, CursorPosition);
330                     fprintf (stdout, "%s", Buffer);
331 
332                     /* Backup to where the cursor should be */
333 
334                     CursorPosition++;
335                     ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition);
336                 }
337                 continue;
338 
339             case _ASCII_UP_ARROW:
340 
341                 /* If no commands available or at start of history list, ignore */
342 
343                 if (!CurrentCommandIndex)
344                 {
345                     continue;
346                 }
347 
348                 /* Manage our up/down progress */
349 
350                 if (CurrentCommandIndex > PreviousCommandIndex)
351                 {
352                     CurrentCommandIndex = PreviousCommandIndex;
353                 }
354 
355                 /* Get the historical command from the debugger */
356 
357                 NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex);
358                 if (!NextCommand)
359                 {
360                     return (AE_ERROR);
361                 }
362 
363                 /* Make this the active command and echo it */
364 
365                 AcpiAcClearLine (EndOfLine, CursorPosition);
366                 strcpy (Buffer, NextCommand);
367                 fprintf (stdout, "%s", Buffer);
368                 EndOfLine = CursorPosition = strlen (Buffer);
369 
370                 PreviousCommandIndex = CurrentCommandIndex;
371                 CurrentCommandIndex--;
372                 continue;
373 
374             case _ASCII_DOWN_ARROW:
375 
376                 if (!MaxCommandIndex) /* Any commands available? */
377                 {
378                     continue;
379                 }
380 
381                 /* Manage our up/down progress */
382 
383                 if (CurrentCommandIndex < PreviousCommandIndex)
384                 {
385                     CurrentCommandIndex = PreviousCommandIndex;
386                 }
387 
388                 /* If we are the end of the history list, output a clear new line */
389 
390                 if ((CurrentCommandIndex + 1) > MaxCommandIndex)
391                 {
392                     AcpiAcClearLine (EndOfLine, CursorPosition);
393                     EndOfLine = CursorPosition = 0;
394                     PreviousCommandIndex = CurrentCommandIndex;
395                     continue;
396                 }
397 
398                 PreviousCommandIndex = CurrentCommandIndex;
399                 CurrentCommandIndex++;
400 
401                  /* Get the historical command from the debugger */
402 
403                 NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex);
404                 if (!NextCommand)
405                 {
406                     return (AE_ERROR);
407                 }
408 
409                 /* Make this the active command and echo it */
410 
411                 AcpiAcClearLine (EndOfLine, CursorPosition);
412                 strcpy (Buffer, NextCommand);
413                 fprintf (stdout, "%s", Buffer);
414                 EndOfLine = CursorPosition = strlen (Buffer);
415                 continue;
416 
417             case 0x31:
418             case 0x32:
419             case 0x33:
420             case 0x34:
421             case 0x35:
422             case 0x36:
423                 /*
424                  * Ignore the various keys like insert/delete/home/end, etc.
425                  * But we must eat the final character of the ESC sequence.
426                  */
427                 InputChar = getchar ();
428                 continue;
429 
430             default:
431 
432                 /* Ignore random escape sequences that we don't care about */
433 
434                 continue;
435             }
436             continue;
437         }
438     }
439 }
440