text: /*/###########################################################################
  //**
  //**  Copyright  (C) 1996-97 Intel Corporation. All rights reserved. 
  //**
  //** The information and source code contained herein is the exclusive 
  //** property of Intel Corporation and may not be disclosed, examined
  //** from the company.
  //**
  *//* ########################################################################### */
  
  #include "EfiShell.h"
  #include "doskey.h" 
  
  #define isprint(a) ((a) >= ' ')
  
  CHAR16 *DosKeyInsert (DosKey_t *DosKey);
  CHAR16 *DosKeyPreviousCurrent (DosKey_t *DosKey);
  CHAR16 *DosKeyGetCommandLine (DosKey_t *DosKey);
  
  
  #define MAX_LINE        256
  #define MAX_HISTORY     20
  
  
  typedef struct {
      UINTN           Signature;
      LIST_ENTRY      Link;
      CHAR16          Buffer[MAX_LINE];
  } INPUT_LINE;
  
  
  /* 
   *  Globals
   */
  
  
  static BOOLEAN      ShellEnvInsertMode;
  static LIST_ENTRY   *ShellEnvCurrentLine;
  static LIST_ENTRY   ShellEnvLineHistory;
  static UINTN        ShellEnvNoHistory;
  
  
  VOID
  DosKeyDelete (
      IN OUT  DosKey_t *DosKey
      )
  {                                                   
      INTN NewEnd;
      
      if (DosKey->Start != DosKey->End) {
          NewEnd = (DosKey->End==0)?(MAX_HISTORY-1):(DosKey->End - 1);
          if (DosKey->Current == NewEnd) {
              DosKey->Current = (DosKey->Current==0)?(MAX_HISTORY-1):(DosKey->Current - 1);
          }
          DosKey->End = NewEnd;
      }
  }                            
  
  CHAR16 *
  DosKeyInsert (
      IN OUT  DosKey_t *DosKey
      )
  {                                                       
      INTN     Next;
      INTN     Data;        
      INTN     i;
      
      Data = DosKey->End;
      Next = ((DosKey->End + 1) % MAX_HISTORY);
      if (DosKey->Start == Next) {
          /*  Wrap case */
          DosKey->Start = ((DosKey->Start + 1) % MAX_HISTORY);
      }
      DosKey->End = Next;
      for (i=0; i<MAX_CMDLINE; i++) {
          DosKey->Buffer[Data][i] = '\0';
      }
      DosKey->Current = Data;
      return (&(DosKey->Buffer[Data][0]));
  }
  
  CHAR16 *
  DosKeyPreviousCurrent (
      IN OUT DosKey_t *DosKey
  ) 
  {
      INTN Next;
      
      Next = (DosKey->Current==0)?(MAX_HISTORY-1):(DosKey->Current - 1);
      if (DosKey->Start < DosKey->End) {
          if ((Next >= DosKey->Start) && (Next != (MAX_HISTORY-1))) {
              DosKey->Current = Next;
          } 
      } else if (DosKey->Start > DosKey->End){
          /*  Allways a Full Buffer  */
          if (Next != DosKey->End) {
              DosKey->Current = Next; 
          }
      } else { 
          /*  No Data */
      }
      return (&(DosKey->Buffer[DosKey->Current][0]));
  }
  
  CHAR16 *
  DosKeyNextCurrent (
      IN OUT  DosKey_t *DosKey
      ) 
  {
      INTN Next;
      
      Next = ((DosKey->Current + 1) % MAX_HISTORY);
      if (DosKey->Start < DosKey->End) {
          if (Next != DosKey->End) {
              DosKey->Current = Next;
          } 
      } else if (DosKey->Start > DosKey->End){
          /*  Allways a Full Buffer  */
          if (Next != DosKey->Start) {
              DosKey->Current = Next; 
          }
      } else { 
          /*  No Data */
      }
      return (&(DosKey->Buffer[DosKey->Current][0]));
  }
  
  VOID
  PrintDosKeyBuffer(
      IN OUT  DosKey_t *DosKey
      )
  {
      INTN i;  
      INTN Index;
  
      Index = 1;
      if (DosKey->Start < DosKey->End) {  
          for (i = DosKey->End - 1; i >= DosKey->Start; i--) {
              if (DosKey->Buffer[i][0] != '\0') {
                  Print (L"\n%2d:%2d: %s",Index++, i, DosKey->Buffer[i]);
              } else {
                  Print (L"\n  :%2d:",i);
              }
          }
      } else if (DosKey->Start > DosKey->End) {
          for (i = DosKey->End -1; i >= 0; i--) {
              if (DosKey->Buffer[i][0] != '\0') {
                  Print (L"\n%2d:%2d: %s",Index++, i, DosKey->Buffer[i]);
              } else {
                  Print (L"\n  :%2d:",i);
              }
          }
          for (i = (MAX_HISTORY-1); i >= DosKey->Start; i--) {
              if (DosKey->Buffer[i][0] != '\0') {
                  Print(L"\n%2d:%2d: %s",Index++, i, DosKey->Buffer[i]);
              } else {
                  Print(L"\n  :%2d:",i);
              }
          }
      } else /* if (DosKey->Start == DosKey->End) */{
      }
  }
  
  CHAR16 *
  ShellEnvReadLine (
  
  
  DosKeyGetCommandLine (
      IN DosKey_t     *DosKey
      )
  { 
      CHAR16                          Str[MAX_CMDLINE];
      CHAR16                          *CommandLine;
      BOOLEAN                         Done;
      UINTN                           Column, Row;
      UINTN                           Update, Delete;
      UINTN                           Len, StrPos, MaxStr;
      UINTN                           Index;
  
      EFI_INPUT_KEY                   Key;
      SIMPLE_TEXT_OUTPUT_INTERFACE    *ConOut;
      SIMPLE_INPUT_INTERFACE          *ConIn;
  
  
      ConOut = ST->ConOut;
      ConIn = ST->ConIn;
  
      /* 
       *  Get input fields location
       */
  
      Column = ConOut->CursorColumn;
      Row = ConOut->CursorRow;
      ConOut->QueryMode (ConOut, ConOut->Mode, &MaxStr, &Index);
  
      /*  bugbug: for now wrapping is not handled */
      MaxStr = MaxStr - Column;
      if (MaxStr > MAX_CMDLINE) {
          MaxStr = MAX_CMDLINE;
      }
  
          
      /* 
       *  Set new input
       */
  
      CommandLine = DosKeyInsert(DosKey);
      SetMem(Str, sizeof(Str), 0x00);
      Update = 0;
      Delete = 0;
  
      Done = FALSE;
      do {
          /* 
           *  If we need to update the output do so now
           */
  
          if (Update != -1) {
              PrintAt (Column+Update, Row, L"%s%.*s", Str + Update, Delete, L"");
              Len = StrLen (Str);
  
              if (Delete) {
                  SetMem(Str+Len, Delete * sizeof(CHAR16), 0x00);
              }
  
              if (StrPos > Len) {
                  StrPos = Len;
              }
  
              Update = -1;
              Delete = 0;
          }
  
          /* 
           *  Set the cursor position for this key
           */
  
          ConOut->SetCursorPosition (ConOut, Column+StrPos, Row);
  
          /* 
           *  Read the key
           */
  
          ConIn->ReadKeyStroke(ConIn, &Key);
  
          switch (Key.UnicodeChar) {
          case CHAR_CARRIAGE_RETURN:
              /* 
               *  All done, print a newline at the end of the string
               */
  
              PrintAt (Column+Len, Row, L"\n");
              if (*Str == 0) {
                  DosKeyDelete(DosKey);
              }
              Done = TRUE;
              break;
  
          case CHAR_BACKSPACE:
              if (StrPos) {
                  StrPos -= 1;
                  Update = StrPos;
                  Delete = 1;
                  CopyMem (Str+StrPos, Str+StrPos+1, sizeof(CHAR16) * (Len-StrPos));
              }
              break;
  
          default:
              if (isprint(Key.UnicodeChar)) {
                  /*  If we are at the buffer's end, drop the key */
                  if (Len == MaxStr-1 && 
                      (DosKey->InsertMode || StrPos == Len)) {
                      break;
                  }
  
                  if (DosKey->InsertMode) {
                      for (Index=Len; Index > StrPos; Index -= 1) {
                          Str[Index] = Str[Index-1];
                      }
                  }
  
                  Str[StrPos] = Key.UnicodeChar;
                  Update = StrPos;
                  StrPos += 1;
              }
              break;
  
          case 0:
              switch (Key.ScanCode) {
              case SCAN_DELETE:
                  if (StrLen) {
                      Update = StrPos;
                      Delete = 1;
                      CopyMem (Str+StrPos, Str+StrPos+1, sizeof(CHAR16) * (Len-StrPos));
                  }
                  break;
  
              case SCAN_UP:
                  StrCpy(Str, DosKeyPreviousCurrent(DosKey));
  
                  Index = Len;                /*  Save old len */
                  Len = StrLen(Str);          /*  Get new len */
                  StrPos = Len;
                  Update = 0;                 /*  draw new input string */
                  if (Index > Len) {
                      Delete = Index - Len;   /*  if old string was longer, blank it */
                  }
                  break;
  
              case SCAN_DOWN:
                  StrCpy(Str, DosKeyNextCurrent(DosKey));
  
                  Index = Len;                /*  Save old len */
                  Len = StrLen(Str);          /*  Get new len */
                  StrPos = Len;
                  Update = 0;                 /*  draw new input string */
                  if (Index > Len) {
                      Delete = Index - Len;   /*  if old string was longer, blank it */
                  }
  
                  break;
  
              case SCAN_LEFT:
                  if (StrPos) {
                      StrPos -= 1;
                  }
                  break;
  
              case SCAN_RIGHT:
                  if (StrPos < Len) {
                      StrPos += 1;
                  }
                  break;
  
              case SCAN_HOME:
                  StrPos = 0;
                  break;
  
              case SCAN_END:
                  StrPos = Len;
                  break;
  
              case SCAN_ESC:
                  Str[0] = 0;
                  Update = 0;
                  Delete = Len;
                  break;
  
              case SCAN_INSERT:
                  DosKey->InsertMode = !DosKey->InsertMode;
                  break;
  
              case SCAN_F7:
                  DosKeyDelete(DosKey);
                  PrintDosKeyBuffer(DosKey);
                  *Str = 0;
                  Done = TRUE;    
                  break;
              }       
          }
      } while (!Done);
  
      StrCpy (CommandLine, Str);
      return (CommandLine);
  }
  
  VOID
  RemoveFirstCharFromString(
      IN OUT  CHAR16  *Str
      )
  {
      UINTN   Length;
      CHAR16  *NewData;
  
      NewData = Str + 1;
      Length = StrLen(NewData);
      while (Length-- != 0) {
          *Str++ = *NewData++;
      }
      *Str = 0;
  }
  