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