1256655Sjkim/******************************************************************************
2256655Sjkim *
3256655Sjkim * Module Name: acgetline - local line editing
4256655Sjkim *
5256655Sjkim *****************************************************************************/
6256655Sjkim
7256655Sjkim/*
8306536Sjkim * Copyright (C) 2000 - 2016, Intel Corp.
9256655Sjkim * All rights reserved.
10256655Sjkim *
11256655Sjkim * Redistribution and use in source and binary forms, with or without
12256655Sjkim * modification, are permitted provided that the following conditions
13256655Sjkim * are met:
14256655Sjkim * 1. Redistributions of source code must retain the above copyright
15256655Sjkim *    notice, this list of conditions, and the following disclaimer,
16256655Sjkim *    without modification.
17256655Sjkim * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18256655Sjkim *    substantially similar to the "NO WARRANTY" disclaimer below
19256655Sjkim *    ("Disclaimer") and any redistribution must be conditioned upon
20256655Sjkim *    including a substantially similar Disclaimer requirement for further
21256655Sjkim *    binary redistribution.
22256655Sjkim * 3. Neither the names of the above-listed copyright holders nor the names
23256655Sjkim *    of any contributors may be used to endorse or promote products derived
24256655Sjkim *    from this software without specific prior written permission.
25256655Sjkim *
26256655Sjkim * Alternatively, this software may be distributed under the terms of the
27256655Sjkim * GNU General Public License ("GPL") version 2 as published by the Free
28256655Sjkim * Software Foundation.
29256655Sjkim *
30256655Sjkim * NO WARRANTY
31256655Sjkim * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32256655Sjkim * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33256655Sjkim * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34256655Sjkim * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35256655Sjkim * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36256655Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37256655Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38256655Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39256655Sjkim * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40256655Sjkim * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41256655Sjkim * POSSIBILITY OF SUCH DAMAGES.
42256655Sjkim */
43256655Sjkim
44272444Sjkim#include <contrib/dev/acpica/include/acpi.h>
45272444Sjkim#include <contrib/dev/acpica/include/accommon.h>
46272444Sjkim#include <contrib/dev/acpica/include/amlcode.h>
47272444Sjkim#include <contrib/dev/acpica/include/acparser.h>
48272444Sjkim#include <contrib/dev/acpica/include/acdebug.h>
49256655Sjkim
50256655Sjkim#include <stdio.h>
51256655Sjkim
52256655Sjkim/*
53256655Sjkim * This is an os-independent implementation of line-editing services needed
54256655Sjkim * by the AcpiExec utility. It uses getchar() and putchar() and the existing
55256655Sjkim * history support provided by the AML debugger. It assumes that the terminal
56256655Sjkim * is in the correct line-editing mode such as raw and noecho. The OSL
57256655Sjkim * interface AcpiOsInitialize should do this. AcpiOsTerminate should put the
58256655Sjkim * terminal back into the original mode.
59256655Sjkim */
60256655Sjkim#define _COMPONENT          ACPI_OS_SERVICES
61256655Sjkim        ACPI_MODULE_NAME    ("acgetline")
62256655Sjkim
63256655Sjkim
64256655Sjkim/* Local prototypes */
65256655Sjkim
66256655Sjkimstatic void
67256655SjkimAcpiAcClearLine (
68256655Sjkim    UINT32                  EndOfLine,
69256655Sjkim    UINT32                  CursorPosition);
70256655Sjkim
71256655Sjkim/* Various ASCII constants */
72256655Sjkim
73256655Sjkim#define _ASCII_NUL                  0
74256655Sjkim#define _ASCII_BACKSPACE            0x08
75256655Sjkim#define _ASCII_TAB                  0x09
76256655Sjkim#define _ASCII_ESCAPE               0x1B
77256655Sjkim#define _ASCII_SPACE                0x20
78256655Sjkim#define _ASCII_LEFT_BRACKET         0x5B
79256655Sjkim#define _ASCII_DEL                  0x7F
80256655Sjkim#define _ASCII_UP_ARROW             'A'
81256655Sjkim#define _ASCII_DOWN_ARROW           'B'
82256655Sjkim#define _ASCII_RIGHT_ARROW          'C'
83256655Sjkim#define _ASCII_LEFT_ARROW           'D'
84256655Sjkim#define _ASCII_NEWLINE              '\n'
85256655Sjkim
86256655Sjkimextern UINT32               AcpiGbl_NextCmdNum;
87256655Sjkim
88256655Sjkim/* Erase a single character on the input command line */
89256655Sjkim
90256655Sjkim#define ACPI_CLEAR_CHAR() \
91256655Sjkim    putchar (_ASCII_BACKSPACE); \
92256655Sjkim    putchar (_ASCII_SPACE); \
93256655Sjkim    putchar (_ASCII_BACKSPACE);
94256655Sjkim
95256655Sjkim/* Backup cursor by Count positions */
96256655Sjkim
97256655Sjkim#define ACPI_BACKUP_CURSOR(i, Count) \
98256655Sjkim    for (i = 0; i < (Count); i++) \
99256655Sjkim        {putchar (_ASCII_BACKSPACE);}
100256655Sjkim
101256655Sjkim
102256655Sjkim/******************************************************************************
103256655Sjkim *
104256655Sjkim * FUNCTION:    AcpiAcClearLine
105256655Sjkim *
106256655Sjkim * PARAMETERS:  EndOfLine           - Current end-of-line index
107256655Sjkim *              CursorPosition      - Current cursor position within line
108256655Sjkim *
109256655Sjkim * RETURN:      None
110256655Sjkim *
111256655Sjkim * DESCRIPTION: Clear the entire command line the hard way, but probably the
112256655Sjkim *              most portable.
113256655Sjkim *
114256655Sjkim *****************************************************************************/
115256655Sjkim
116256655Sjkimstatic void
117256655SjkimAcpiAcClearLine (
118256655Sjkim    UINT32                  EndOfLine,
119256655Sjkim    UINT32                  CursorPosition)
120256655Sjkim{
121256655Sjkim    UINT32                  i;
122256655Sjkim
123256655Sjkim
124256655Sjkim    if (CursorPosition < EndOfLine)
125256655Sjkim    {
126256655Sjkim        /* Clear line from current position to end of line */
127256655Sjkim
128256655Sjkim        for (i = 0; i < (EndOfLine - CursorPosition); i++)
129256655Sjkim        {
130256655Sjkim            putchar (' ');
131256655Sjkim        }
132256655Sjkim    }
133256655Sjkim
134256655Sjkim    /* Clear the entire line */
135256655Sjkim
136256655Sjkim    for (; EndOfLine > 0; EndOfLine--)
137256655Sjkim    {
138256655Sjkim        ACPI_CLEAR_CHAR ();
139256655Sjkim    }
140256655Sjkim}
141256655Sjkim
142256655Sjkim
143256655Sjkim/******************************************************************************
144256655Sjkim *
145256655Sjkim * FUNCTION:    AcpiOsGetLine
146256655Sjkim *
147256655Sjkim * PARAMETERS:  Buffer              - Where to return the command line
148256655Sjkim *              BufferLength        - Maximum length of Buffer
149256655Sjkim *              BytesRead           - Where the actual byte count is returned
150256655Sjkim *
151256655Sjkim * RETURN:      Status and actual bytes read
152256655Sjkim *
153256655Sjkim * DESCRIPTION: Get the next input line from the terminal. NOTE: terminal
154256655Sjkim *              is expected to be in a mode that supports line-editing (raw,
155256655Sjkim *              noecho). This function is intended to be very portable. Also,
156256655Sjkim *              it uses the history support implemented in the AML debugger.
157256655Sjkim *
158256655Sjkim *****************************************************************************/
159256655Sjkim
160256655SjkimACPI_STATUS
161256655SjkimAcpiOsGetLine (
162256655Sjkim    char                    *Buffer,
163256655Sjkim    UINT32                  BufferLength,
164256655Sjkim    UINT32                  *BytesRead)
165256655Sjkim{
166256655Sjkim    char                    *NextCommand;
167256655Sjkim    UINT32                  MaxCommandIndex = AcpiGbl_NextCmdNum - 1;
168256655Sjkim    UINT32                  CurrentCommandIndex = MaxCommandIndex;
169256655Sjkim    UINT32                  PreviousCommandIndex = MaxCommandIndex;
170256655Sjkim    int                     InputChar;
171256655Sjkim    UINT32                  CursorPosition = 0;
172256655Sjkim    UINT32                  EndOfLine = 0;
173256655Sjkim    UINT32                  i;
174256655Sjkim
175256655Sjkim
176256655Sjkim    /* Always clear the line buffer before we read a new line */
177256655Sjkim
178256655Sjkim    memset (Buffer, 0, BufferLength);
179256655Sjkim
180256655Sjkim    /*
181256655Sjkim     * This loop gets one character at a time (except for esc sequences)
182256655Sjkim     * until a newline or error is detected.
183256655Sjkim     *
184256655Sjkim     * Note: Don't attempt to write terminal control ESC sequences, even
185256655Sjkim     * though it makes certain things more difficult.
186256655Sjkim     */
187256655Sjkim    while (1)
188256655Sjkim    {
189256655Sjkim        if (EndOfLine >= (BufferLength - 1))
190256655Sjkim        {
191256655Sjkim            return (AE_BUFFER_OVERFLOW);
192256655Sjkim        }
193256655Sjkim
194256655Sjkim        InputChar = getchar ();
195256655Sjkim        switch (InputChar)
196256655Sjkim        {
197256655Sjkim        default: /* This is the normal character case */
198256655Sjkim
199256655Sjkim            /* Echo the character (at EOL) and copy it to the line buffer */
200256655Sjkim
201256655Sjkim            if (EndOfLine == CursorPosition)
202256655Sjkim            {
203256655Sjkim                putchar (InputChar);
204256655Sjkim                Buffer[EndOfLine] = (char) InputChar;
205256655Sjkim
206256655Sjkim                EndOfLine++;
207256655Sjkim                CursorPosition++;
208256655Sjkim                Buffer[EndOfLine] = 0;
209256655Sjkim                continue;
210256655Sjkim            }
211256655Sjkim
212256655Sjkim            /* Insert character into the middle of the buffer */
213256655Sjkim
214256655Sjkim            memmove (&Buffer[CursorPosition + 1], &Buffer[CursorPosition],
215256655Sjkim                (EndOfLine - CursorPosition + 1));
216256655Sjkim
217256655Sjkim            Buffer [CursorPosition] = (char) InputChar;
218256655Sjkim            Buffer [EndOfLine + 1] = 0;
219256655Sjkim
220256655Sjkim            /* Display the new part of line starting at the new character */
221256655Sjkim
222256655Sjkim            fprintf (stdout, "%s", &Buffer[CursorPosition]);
223256655Sjkim
224256655Sjkim            /* Restore cursor */
225256655Sjkim
226256655Sjkim            ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition);
227256655Sjkim            CursorPosition++;
228256655Sjkim            EndOfLine++;
229256655Sjkim            continue;
230256655Sjkim
231256655Sjkim        case _ASCII_DEL: /* Backspace key */
232256655Sjkim
233256655Sjkim            if (!EndOfLine) /* Any characters on the command line? */
234256655Sjkim            {
235256655Sjkim                continue;
236256655Sjkim            }
237256655Sjkim
238256655Sjkim            if (EndOfLine == CursorPosition) /* Erase the final character */
239256655Sjkim            {
240256655Sjkim                ACPI_CLEAR_CHAR ();
241256655Sjkim                EndOfLine--;
242256655Sjkim                CursorPosition--;
243256655Sjkim                continue;
244256655Sjkim            }
245256655Sjkim
246256655Sjkim            if (!CursorPosition) /* Do not backup beyond start of line */
247256655Sjkim            {
248256655Sjkim                continue;
249256655Sjkim            }
250256655Sjkim
251256655Sjkim            /* Remove the character from the line */
252256655Sjkim
253256655Sjkim            memmove (&Buffer[CursorPosition - 1], &Buffer[CursorPosition],
254256655Sjkim                (EndOfLine - CursorPosition + 1));
255256655Sjkim
256256655Sjkim            /* Display the new part of line starting at the new character */
257256655Sjkim
258256655Sjkim            putchar (_ASCII_BACKSPACE);
259256655Sjkim            fprintf (stdout, "%s ", &Buffer[CursorPosition - 1]);
260256655Sjkim
261256655Sjkim            /* Restore cursor */
262256655Sjkim
263256655Sjkim            ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition + 1);
264256655Sjkim            EndOfLine--;
265306536Sjkim
266256655Sjkim            if (CursorPosition > 0)
267256655Sjkim            {
268256655Sjkim                CursorPosition--;
269256655Sjkim            }
270256655Sjkim            continue;
271256655Sjkim
272256655Sjkim        case _ASCII_NEWLINE: /* Normal exit case at end of command line */
273256655Sjkim        case _ASCII_NUL:
274256655Sjkim
275256655Sjkim            /* Return the number of bytes in the command line string */
276256655Sjkim
277256655Sjkim            if (BytesRead)
278256655Sjkim            {
279256655Sjkim                *BytesRead = EndOfLine;
280256655Sjkim            }
281256655Sjkim
282256655Sjkim            /* Echo, terminate string buffer, and exit */
283256655Sjkim
284256655Sjkim            putchar (InputChar);
285256655Sjkim            Buffer[EndOfLine] = 0;
286256655Sjkim            return (AE_OK);
287256655Sjkim
288256655Sjkim        case _ASCII_TAB:
289256655Sjkim
290256655Sjkim            /* Ignore */
291256655Sjkim
292256655Sjkim            continue;
293256655Sjkim
294256655Sjkim        case EOF:
295256655Sjkim
296256655Sjkim            return (AE_ERROR);
297256655Sjkim
298256655Sjkim        case _ASCII_ESCAPE:
299256655Sjkim
300256655Sjkim            /* Check for escape sequences of the form "ESC[x" */
301256655Sjkim
302256655Sjkim            InputChar = getchar ();
303256655Sjkim            if (InputChar != _ASCII_LEFT_BRACKET)
304256655Sjkim            {
305256655Sjkim                continue; /* Ignore this ESC, does not have the '[' */
306256655Sjkim            }
307256655Sjkim
308256655Sjkim            /* Get the code following the ESC [ */
309256655Sjkim
310256655Sjkim            InputChar = getchar (); /* Backup one character */
311256655Sjkim            switch (InputChar)
312256655Sjkim            {
313256655Sjkim            case _ASCII_LEFT_ARROW:
314256655Sjkim
315256655Sjkim                if (CursorPosition > 0)
316256655Sjkim                {
317256655Sjkim                    putchar (_ASCII_BACKSPACE);
318256655Sjkim                    CursorPosition--;
319256655Sjkim                }
320256655Sjkim                continue;
321256655Sjkim
322256655Sjkim            case _ASCII_RIGHT_ARROW:
323256655Sjkim                /*
324256655Sjkim                 * Move one character forward. Do this without sending
325256655Sjkim                 * ESC sequence to the terminal for max portability.
326256655Sjkim                 */
327256655Sjkim                if (CursorPosition < EndOfLine)
328256655Sjkim                {
329256655Sjkim                    /* Backup to start of line and print the entire line */
330256655Sjkim
331256655Sjkim                    ACPI_BACKUP_CURSOR (i, CursorPosition);
332256655Sjkim                    fprintf (stdout, "%s", Buffer);
333256655Sjkim
334256655Sjkim                    /* Backup to where the cursor should be */
335256655Sjkim
336256655Sjkim                    CursorPosition++;
337256655Sjkim                    ACPI_BACKUP_CURSOR (i, EndOfLine - CursorPosition);
338256655Sjkim                }
339256655Sjkim                continue;
340256655Sjkim
341256655Sjkim            case _ASCII_UP_ARROW:
342256655Sjkim
343256655Sjkim                /* If no commands available or at start of history list, ignore */
344256655Sjkim
345256655Sjkim                if (!CurrentCommandIndex)
346256655Sjkim                {
347256655Sjkim                    continue;
348256655Sjkim                }
349256655Sjkim
350256655Sjkim                /* Manage our up/down progress */
351256655Sjkim
352256655Sjkim                if (CurrentCommandIndex > PreviousCommandIndex)
353256655Sjkim                {
354256655Sjkim                    CurrentCommandIndex = PreviousCommandIndex;
355256655Sjkim                }
356256655Sjkim
357256655Sjkim                /* Get the historical command from the debugger */
358256655Sjkim
359256655Sjkim                NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex);
360256655Sjkim                if (!NextCommand)
361256655Sjkim                {
362256655Sjkim                    return (AE_ERROR);
363256655Sjkim                }
364256655Sjkim
365256655Sjkim                /* Make this the active command and echo it */
366256655Sjkim
367256655Sjkim                AcpiAcClearLine (EndOfLine, CursorPosition);
368256655Sjkim                strcpy (Buffer, NextCommand);
369256655Sjkim                fprintf (stdout, "%s", Buffer);
370256655Sjkim                EndOfLine = CursorPosition = strlen (Buffer);
371256655Sjkim
372256655Sjkim                PreviousCommandIndex = CurrentCommandIndex;
373256655Sjkim                CurrentCommandIndex--;
374256655Sjkim                continue;
375256655Sjkim
376256655Sjkim            case _ASCII_DOWN_ARROW:
377256655Sjkim
378256655Sjkim                if (!MaxCommandIndex) /* Any commands available? */
379256655Sjkim                {
380256655Sjkim                    continue;
381256655Sjkim                }
382256655Sjkim
383256655Sjkim                /* Manage our up/down progress */
384256655Sjkim
385256655Sjkim                if (CurrentCommandIndex < PreviousCommandIndex)
386256655Sjkim                {
387256655Sjkim                    CurrentCommandIndex = PreviousCommandIndex;
388256655Sjkim                }
389256655Sjkim
390256655Sjkim                /* If we are the end of the history list, output a clear new line */
391256655Sjkim
392256655Sjkim                if ((CurrentCommandIndex + 1) > MaxCommandIndex)
393256655Sjkim                {
394256655Sjkim                    AcpiAcClearLine (EndOfLine, CursorPosition);
395256655Sjkim                    EndOfLine = CursorPosition = 0;
396256655Sjkim                    PreviousCommandIndex = CurrentCommandIndex;
397256655Sjkim                    continue;
398256655Sjkim                }
399256655Sjkim
400256655Sjkim                PreviousCommandIndex = CurrentCommandIndex;
401256655Sjkim                CurrentCommandIndex++;
402256655Sjkim
403256655Sjkim                 /* Get the historical command from the debugger */
404256655Sjkim
405256655Sjkim                NextCommand = AcpiDbGetHistoryByIndex (CurrentCommandIndex);
406256655Sjkim                if (!NextCommand)
407256655Sjkim                {
408256655Sjkim                    return (AE_ERROR);
409256655Sjkim                }
410256655Sjkim
411256655Sjkim                /* Make this the active command and echo it */
412256655Sjkim
413256655Sjkim                AcpiAcClearLine (EndOfLine, CursorPosition);
414256655Sjkim                strcpy (Buffer, NextCommand);
415256655Sjkim                fprintf (stdout, "%s", Buffer);
416256655Sjkim                EndOfLine = CursorPosition = strlen (Buffer);
417256655Sjkim                continue;
418256655Sjkim
419256655Sjkim            case 0x31:
420256655Sjkim            case 0x32:
421256655Sjkim            case 0x33:
422256655Sjkim            case 0x34:
423256655Sjkim            case 0x35:
424256655Sjkim            case 0x36:
425256655Sjkim                /*
426256655Sjkim                 * Ignore the various keys like insert/delete/home/end, etc.
427256655Sjkim                 * But we must eat the final character of the ESC sequence.
428256655Sjkim                 */
429256655Sjkim                InputChar = getchar ();
430256655Sjkim                continue;
431256655Sjkim
432256655Sjkim            default:
433256655Sjkim
434256655Sjkim                /* Ignore random escape sequences that we don't care about */
435256655Sjkim
436256655Sjkim                continue;
437256655Sjkim            }
438256655Sjkim            continue;
439256655Sjkim        }
440256655Sjkim    }
441256655Sjkim}
442