xref: /freebsd/sys/contrib/dev/acpica/common/acgetline.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
1 /******************************************************************************
2  *
3  * Module Name: acgetline - local line editing
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2015, 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             if (CursorPosition > 0)
266             {
267                 CursorPosition--;
268             }
269             continue;
270 
271         case _ASCII_NEWLINE: /* Normal exit case at end of command line */
272         case _ASCII_NUL:
273 
274             /* Return the number of bytes in the command line string */
275 
276             if (BytesRead)
277             {
278                 *BytesRead = EndOfLine;
279             }
280 
281             /* Echo, terminate string buffer, and exit */
282 
283             putchar (InputChar);
284             Buffer[EndOfLine] = 0;
285             return (AE_OK);
286 
287         case _ASCII_TAB:
288 
289             /* Ignore */
290 
291             continue;
292 
293         case EOF:
294 
295             return (AE_ERROR);
296 
297         case _ASCII_ESCAPE:
298 
299             /* Check for escape sequences of the form "ESC[x" */
300 
301             InputChar = getchar ();
302             if (InputChar != _ASCII_LEFT_BRACKET)
303             {
304                 continue; /* Ignore this ESC, does not have the '[' */
305             }
306 
307             /* Get the code following the ESC [ */
308 
309             InputChar = getchar (); /* Backup one character */
310             switch (InputChar)
311             {
312             case _ASCII_LEFT_ARROW:
313 
314                 if (CursorPosition > 0)
315                 {
316                     putchar (_ASCII_BACKSPACE);
317                     CursorPosition--;
318                 }
319                 continue;
320 
321             case _ASCII_RIGHT_ARROW:
322                 /*
323                  * Move one character forward. Do this without sending
324                  * ESC sequence to the terminal for max portability.
325                  */
326                 if (CursorPosition < EndOfLine)
327                 {
328                     /* Backup to start of line and print the entire line */
329 
330                     ACPI_BACKUP_CURSOR (i, CursorPosition);
331                     fprintf (stdout, "%s", Buffer);
332 
333                     /* Backup to where the cursor should be */
334 
335                     CursorPosition++;
336                     ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition);
337                 }
338                 continue;
339 
340             case _ASCII_UP_ARROW:
341 
342                 /* If no commands available or at start of history list, ignore */
343 
344                 if (!CurrentCommandIndex)
345                 {
346                     continue;
347                 }
348 
349                 /* Manage our up/down progress */
350 
351                 if (CurrentCommandIndex > PreviousCommandIndex)
352                 {
353                     CurrentCommandIndex = PreviousCommandIndex;
354                 }
355 
356                 /* Get the historical command from the debugger */
357 
358                 NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex);
359                 if (!NextCommand)
360                 {
361                     return (AE_ERROR);
362                 }
363 
364                 /* Make this the active command and echo it */
365 
366                 AcpiAcClearLine (EndOfLine, CursorPosition);
367                 strcpy (Buffer, NextCommand);
368                 fprintf (stdout, "%s", Buffer);
369                 EndOfLine = CursorPosition = strlen (Buffer);
370 
371                 PreviousCommandIndex = CurrentCommandIndex;
372                 CurrentCommandIndex--;
373                 continue;
374 
375             case _ASCII_DOWN_ARROW:
376 
377                 if (!MaxCommandIndex) /* Any commands available? */
378                 {
379                     continue;
380                 }
381 
382                 /* Manage our up/down progress */
383 
384                 if (CurrentCommandIndex < PreviousCommandIndex)
385                 {
386                     CurrentCommandIndex = PreviousCommandIndex;
387                 }
388 
389                 /* If we are the end of the history list, output a clear new line */
390 
391                 if ((CurrentCommandIndex + 1) > MaxCommandIndex)
392                 {
393                     AcpiAcClearLine (EndOfLine, CursorPosition);
394                     EndOfLine = CursorPosition = 0;
395                     PreviousCommandIndex = CurrentCommandIndex;
396                     continue;
397                 }
398 
399                 PreviousCommandIndex = CurrentCommandIndex;
400                 CurrentCommandIndex++;
401 
402                  /* Get the historical command from the debugger */
403 
404                 NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex);
405                 if (!NextCommand)
406                 {
407                     return (AE_ERROR);
408                 }
409 
410                 /* Make this the active command and echo it */
411 
412                 AcpiAcClearLine (EndOfLine, CursorPosition);
413                 strcpy (Buffer, NextCommand);
414                 fprintf (stdout, "%s", Buffer);
415                 EndOfLine = CursorPosition = strlen (Buffer);
416                 continue;
417 
418             case 0x31:
419             case 0x32:
420             case 0x33:
421             case 0x34:
422             case 0x35:
423             case 0x36:
424                 /*
425                  * Ignore the various keys like insert/delete/home/end, etc.
426                  * But we must eat the final character of the ESC sequence.
427                  */
428                 InputChar = getchar ();
429                 continue;
430 
431             default:
432 
433                 /* Ignore random escape sequences that we don't care about */
434 
435                 continue;
436             }
437             continue;
438         }
439     }
440 }
441