1 /******************************************************************************
   2  *
   3  * Module Name: acgetline - local line editing
   4  *
   5  *****************************************************************************/
   6 
   7 /*
   8  * Copyright (C) 2000 - 2014, 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 #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 }