summaryrefslogtreecommitdiff
path: root/Core/ShellPkg/Application
diff options
context:
space:
mode:
Diffstat (limited to 'Core/ShellPkg/Application')
-rw-r--r--Core/ShellPkg/Application/Shell/ConsoleLogger.c1233
-rw-r--r--Core/ShellPkg/Application/Shell/ConsoleLogger.h329
-rw-r--r--Core/ShellPkg/Application/Shell/ConsoleWrappers.c518
-rw-r--r--Core/ShellPkg/Application/Shell/ConsoleWrappers.h84
-rw-r--r--Core/ShellPkg/Application/Shell/FileHandleInternal.h68
-rw-r--r--Core/ShellPkg/Application/Shell/FileHandleWrappers.c1910
-rw-r--r--Core/ShellPkg/Application/Shell/FileHandleWrappers.h95
-rw-r--r--Core/ShellPkg/Application/Shell/Shell.c3131
-rw-r--r--Core/ShellPkg/Application/Shell/Shell.h408
-rw-r--r--Core/ShellPkg/Application/Shell/Shell.inf112
-rw-r--r--Core/ShellPkg/Application/Shell/Shell.uni58
-rw-r--r--Core/ShellPkg/Application/Shell/ShellEnvVar.c576
-rw-r--r--Core/ShellPkg/Application/Shell/ShellEnvVar.h292
-rw-r--r--Core/ShellPkg/Application/Shell/ShellManParser.c1013
-rw-r--r--Core/ShellPkg/Application/Shell/ShellManParser.h86
-rw-r--r--Core/ShellPkg/Application/Shell/ShellParametersProtocol.c1298
-rw-r--r--Core/ShellPkg/Application/Shell/ShellParametersProtocol.h194
-rw-r--r--Core/ShellPkg/Application/Shell/ShellProtocol.c3939
-rw-r--r--Core/ShellPkg/Application/Shell/ShellProtocol.h953
-rw-r--r--Core/ShellPkg/Application/ShellCTestApp/README.txt5
-rw-r--r--Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c51
-rw-r--r--Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf39
-rw-r--r--Core/ShellPkg/Application/ShellCTestApp/TestArgv.logbin0 -> 3320 bytes
-rw-r--r--Core/ShellPkg/Application/ShellCTestApp/TestArgv.nsh64
-rw-r--r--Core/ShellPkg/Application/ShellExecTestApp/SA.c38
-rw-r--r--Core/ShellPkg/Application/ShellExecTestApp/SA.inf41
-rw-r--r--Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c83
-rw-r--r--Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf42
28 files changed, 16660 insertions, 0 deletions
diff --git a/Core/ShellPkg/Application/Shell/ConsoleLogger.c b/Core/ShellPkg/Application/Shell/ConsoleLogger.c
new file mode 100644
index 0000000000..9b64303cf0
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ConsoleLogger.c
@@ -0,0 +1,1233 @@
+/** @file
+ Provides interface to shell console logger.
+
+ (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#include "Shell.h"
+
+/**
+ Install our intermediate ConOut into the system table to
+ keep a log of all the info that is displayed to the user.
+
+ @param[in] ScreensToSave Sets how many screen-worths of data to save.
+ @param[out] ConsoleInfo The object to pass into later functions.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return other The operation failed.
+
+ @sa ConsoleLoggerResetBuffers
+ @sa InstallProtocolInterface
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerInstall(
+ IN CONST UINTN ScreensToSave,
+ OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo
+ )
+{
+ EFI_STATUS Status;
+ ASSERT(ConsoleInfo != NULL);
+
+ (*ConsoleInfo) = AllocateZeroPool(sizeof(CONSOLE_LOGGER_PRIVATE_DATA));
+ if ((*ConsoleInfo) == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE;
+ (*ConsoleInfo)->OldConOut = gST->ConOut;
+ (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle;
+ (*ConsoleInfo)->Buffer = NULL;
+ (*ConsoleInfo)->BufferSize = 0;
+ (*ConsoleInfo)->OriginalStartRow = 0;
+ (*ConsoleInfo)->CurrentStartRow = 0;
+ (*ConsoleInfo)->RowsPerScreen = 0;
+ (*ConsoleInfo)->ColsPerScreen = 0;
+ (*ConsoleInfo)->Attributes = NULL;
+ (*ConsoleInfo)->AttribSize = 0;
+ (*ConsoleInfo)->ScreenCount = ScreensToSave;
+ (*ConsoleInfo)->HistoryMode.MaxMode = 1;
+ (*ConsoleInfo)->HistoryMode.Mode = 0;
+ (*ConsoleInfo)->HistoryMode.Attribute = 0;
+ (*ConsoleInfo)->HistoryMode.CursorColumn = 0;
+ (*ConsoleInfo)->HistoryMode.CursorRow = 0;
+ (*ConsoleInfo)->HistoryMode.CursorVisible = FALSE;
+ (*ConsoleInfo)->OurConOut.Reset = ConsoleLoggerReset;
+ (*ConsoleInfo)->OurConOut.OutputString = ConsoleLoggerOutputString;
+ (*ConsoleInfo)->OurConOut.TestString = ConsoleLoggerTestString;
+ (*ConsoleInfo)->OurConOut.QueryMode = ConsoleLoggerQueryMode;
+ (*ConsoleInfo)->OurConOut.SetMode = ConsoleLoggerSetMode;
+ (*ConsoleInfo)->OurConOut.SetAttribute = ConsoleLoggerSetAttribute;
+ (*ConsoleInfo)->OurConOut.ClearScreen = ConsoleLoggerClearScreen;
+ (*ConsoleInfo)->OurConOut.SetCursorPosition = ConsoleLoggerSetCursorPosition;
+ (*ConsoleInfo)->OurConOut.EnableCursor = ConsoleLoggerEnableCursor;
+ (*ConsoleInfo)->OurConOut.Mode = gST->ConOut->Mode;
+ (*ConsoleInfo)->Enabled = TRUE;
+
+ Status = ConsoleLoggerResetBuffers(*ConsoleInfo);
+ if (EFI_ERROR(Status)) {
+ SHELL_FREE_NON_NULL((*ConsoleInfo));
+ *ConsoleInfo = NULL;
+ return (Status);
+ }
+
+ Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut));
+ if (EFI_ERROR(Status)) {
+ SHELL_FREE_NON_NULL((*ConsoleInfo)->Buffer);
+ SHELL_FREE_NON_NULL((*ConsoleInfo)->Attributes);
+ SHELL_FREE_NON_NULL((*ConsoleInfo));
+ *ConsoleInfo = NULL;
+ return (Status);
+ }
+
+ gST->ConsoleOutHandle = gImageHandle;
+ gST->ConOut = &(*ConsoleInfo)->OurConOut;
+
+ return (Status);
+}
+
+/**
+ Return the system to the state it was before InstallConsoleLogger
+ was installed.
+
+ @param[in] ConsoleInfo The object from the install function.
+
+ @retval EFI_SUCCESS The operation was successful
+ @return other The operation failed. This was from UninstallProtocolInterface.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerUninstall(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ ASSERT(ConsoleInfo != NULL);
+ ASSERT(ConsoleInfo->OldConOut != NULL);
+
+ if (ConsoleInfo->Buffer != NULL) {
+ FreePool(ConsoleInfo->Buffer);
+ DEBUG_CODE(ConsoleInfo->Buffer = NULL;);
+ DEBUG_CODE(ConsoleInfo->BufferSize = 0;);
+ }
+ if (ConsoleInfo->Attributes != NULL) {
+ FreePool(ConsoleInfo->Attributes);
+ DEBUG_CODE(ConsoleInfo->Attributes = NULL;);
+ DEBUG_CODE(ConsoleInfo->AttribSize = 0;);
+ }
+
+ gST->ConsoleOutHandle = ConsoleInfo->OldConHandle;
+ gST->ConOut = ConsoleInfo->OldConOut;
+
+ return (gBS->UninstallProtocolInterface(gImageHandle, &gEfiSimpleTextOutProtocolGuid, (VOID*)&ConsoleInfo->OurConOut));
+}
+
+/**
+ Displays previously logged output back to the screen.
+
+ This will scroll the screen forwards and backwards through the log of previous
+ output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows
+ is (UINTN)(-1) then the size of the screen will be scrolled.
+
+ @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer).
+ If FALSE then the log will be displayed backwards (scroll to older).
+ @param[in] Rows Determines how many rows the log should scroll.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerDisplayHistory(
+ IN CONST BOOLEAN Forward,
+ IN CONST UINTN Rows,
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ UINTN RowChange;
+
+ ASSERT(ConsoleInfo != NULL);
+
+ //
+ // Calculate the row number change
+ //
+ switch (Rows) {
+ case ((UINTN)(-1)):
+ RowChange = ConsoleInfo->RowsPerScreen;
+ break;
+ case (0):
+ RowChange = ConsoleInfo->RowsPerScreen / 2;
+ break;
+ default:
+ RowChange = Rows;
+ break;
+ }
+
+ //
+ // Do the math for direction
+ //
+ if (Forward) {
+ if ((ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow) < RowChange) {
+ RowChange = ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow;
+ }
+ } else {
+ if (ConsoleInfo->CurrentStartRow < RowChange) {
+ RowChange = ConsoleInfo->CurrentStartRow;
+ }
+ }
+
+ //
+ // If we are already at one end or the other
+ //
+ if (RowChange == 0) {
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Clear the screen
+ //
+ ConsoleInfo->OldConOut->ClearScreen(ConsoleInfo->OldConOut);
+
+ //
+ // Set the new start row
+ //
+ if (Forward) {
+ ConsoleInfo->CurrentStartRow += RowChange;
+ } else {
+ ConsoleInfo->CurrentStartRow -= RowChange;
+ }
+
+ //
+ // Change the screen
+ //
+ return (UpdateDisplayFromHistory(ConsoleInfo));
+}
+
+/**
+ Function to return to normal output whent he scrolling is complete.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return other The operation failed. See UpdateDisplayFromHistory.
+
+ @sa UpdateDisplayFromHistory
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerStopHistory(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ ASSERT(ConsoleInfo != NULL);
+ if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Clear the screen
+ //
+ ConsoleInfo->OldConOut->ClearScreen(ConsoleInfo->OldConOut);
+
+ ConsoleInfo->CurrentStartRow = ConsoleInfo->OriginalStartRow;
+ return (UpdateDisplayFromHistory(ConsoleInfo));
+}
+
+/**
+ Updates the hidden ConOut to be displaying the correct stuff.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return other The operation failed.
+**/
+EFI_STATUS
+EFIAPI
+UpdateDisplayFromHistory(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS RetVal;
+ CHAR16 *Screen;
+ INT32 *Attributes;
+ UINTN CurrentRow;
+ CHAR16 TempCharHolder;
+ UINTN Column;
+ INT32 CurrentAttrib;
+ UINTN CurrentColumn;
+ CHAR16 *StringSegment;
+ CHAR16 *StringSegmentEnd;
+ CHAR16 StringSegmentEndChar;
+ INT32 OrigAttribute;
+
+ ASSERT(ConsoleInfo != NULL);
+ TempCharHolder = CHAR_NULL;
+ RetVal = EFI_SUCCESS;
+ OrigAttribute = ConsoleInfo->OldConOut->Mode->Attribute;
+
+ //
+ // Disable cursor visibility and move it to the top left corner
+ //
+ ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, FALSE);
+ ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, 0, 0);
+
+ Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
+ Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
+ for ( CurrentRow = 0
+ ; CurrentRow < ConsoleInfo->RowsPerScreen
+ ; CurrentRow++
+ , Screen += (ConsoleInfo->ColsPerScreen + 2)
+ , Attributes += ConsoleInfo->ColsPerScreen
+ ){
+ //
+ // dont use the last char - prevents screen scroll
+ //
+ if (CurrentRow == (ConsoleInfo->RowsPerScreen-1)){
+ TempCharHolder = Screen[ConsoleInfo->ColsPerScreen - 1];
+ Screen[ConsoleInfo->ColsPerScreen - 1] = CHAR_NULL;
+ }
+
+ for ( Column = 0
+ ; Column < ConsoleInfo->ColsPerScreen
+ ; Column++
+ ){
+ if (Screen[Column] != CHAR_NULL) {
+ CurrentAttrib = Attributes[Column];
+ CurrentColumn = Column;
+ StringSegment = &Screen[Column];
+
+ //
+ // Find the first char with a different arrribute and make that temporarily NULL
+ // so we can do fewer printout statements. (later) restore that one and we will
+ // start at that collumn on the next loop.
+ //
+ StringSegmentEndChar = CHAR_NULL;
+ for ( StringSegmentEnd = StringSegment
+ ; StringSegmentEnd != CHAR_NULL
+ ; StringSegmentEnd++
+ , Column++
+ ){
+ if (Attributes[Column] != CurrentAttrib) {
+ StringSegmentEndChar = *StringSegmentEnd;
+ *StringSegmentEnd = CHAR_NULL;
+ break;
+ }
+ } // StringSegmentEnd loop
+
+ //
+ // Now write out as much as had the same Attributes
+ //
+
+ ConsoleInfo->OldConOut->SetAttribute(ConsoleInfo->OldConOut, CurrentAttrib);
+ ConsoleInfo->OldConOut->SetCursorPosition(ConsoleInfo->OldConOut, CurrentColumn, CurrentRow);
+ Status = ConsoleInfo->OldConOut->OutputString(ConsoleInfo->OldConOut, StringSegment);
+
+ if (EFI_ERROR(Status)) {
+ ASSERT(FALSE);
+ RetVal = Status;
+ }
+
+ //
+ // If we found a change in attribute put the character back and decrement the column
+ // so when it increments it will point at that character and we will start printing
+ // a segment with that new attribute
+ //
+ if (StringSegmentEndChar != CHAR_NULL) {
+ *StringSegmentEnd = StringSegmentEndChar;
+ StringSegmentEndChar = CHAR_NULL;
+ Column--;
+ }
+ }
+ } // column for loop
+
+ //
+ // If we removed the last char and this was the last row put it back
+ //
+ if (TempCharHolder != CHAR_NULL) {
+ Screen[ConsoleInfo->ColsPerScreen - 1] = TempCharHolder;
+ TempCharHolder = CHAR_NULL;
+ }
+ } // row for loop
+
+ //
+ // If we are setting the screen back to original turn on the cursor and make it visible
+ // and set the attributes back to what they were
+ //
+ if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) {
+ ConsoleInfo->OldConOut->SetAttribute (
+ ConsoleInfo->OldConOut,
+ ConsoleInfo->HistoryMode.Attribute
+ );
+ ConsoleInfo->OldConOut->SetCursorPosition (
+ ConsoleInfo->OldConOut,
+ ConsoleInfo->HistoryMode.CursorColumn,
+ ConsoleInfo->HistoryMode.CursorRow - ConsoleInfo->OriginalStartRow
+ );
+
+ Status = ConsoleInfo->OldConOut->EnableCursor (
+ ConsoleInfo->OldConOut,
+ ConsoleInfo->HistoryMode.CursorVisible
+ );
+ if (EFI_ERROR (Status)) {
+ RetVal = Status;
+ }
+ } else {
+ ConsoleInfo->OldConOut->SetAttribute (
+ ConsoleInfo->OldConOut,
+ OrigAttribute
+ );
+ }
+
+ return (RetVal);
+}
+
+/**
+ Reset the text output device hardware and optionaly run diagnostics
+
+ @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
+ @param ExtendedVerification Indicates that a more extensive test may be performed
+
+ @retval EFI_SUCCESS The text output device was reset.
+ @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and
+ could not be reset.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerReset (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ EFI_STATUS Status;
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->Reset (ConsoleInfo->OldConOut, ExtendedVerification);
+
+ //
+ // Check that the buffers are still correct for logging
+ //
+ if (!EFI_ERROR (Status)) {
+ ConsoleLoggerResetBuffers(ConsoleInfo);
+ if (ExtendedVerification) {
+ ConsoleInfo->OriginalStartRow = 0;
+ ConsoleInfo->CurrentStartRow = 0;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Appends a string to the history buffer. If the buffer is full then the oldest
+ information in the buffer will be dropped. Information is added in a line by
+ line manner such that an empty line takes up just as much space as a full line.
+
+ @param[in] String String pointer to add.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+**/
+EFI_STATUS
+EFIAPI
+AppendStringToHistory(
+ IN CONST CHAR16 *String,
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ CONST CHAR16 *Walker;
+ UINTN CopySize;
+ UINTN PrintIndex;
+ UINTN Index;
+
+ ASSERT(ConsoleInfo != NULL);
+
+ for ( Walker = String
+ ; Walker != NULL && *Walker != CHAR_NULL
+ ; Walker++
+ ){
+ switch (*Walker) {
+ case (CHAR_BACKSPACE):
+ if (ConsoleInfo->HistoryMode.CursorColumn > 0) {
+ ConsoleInfo->HistoryMode.CursorColumn--;
+ }
+ break;
+ case (CHAR_LINEFEED):
+ if (ConsoleInfo->HistoryMode.CursorRow >= (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)) {
+ //
+ // Should never be bigger
+ //
+ ASSERT(ConsoleInfo->HistoryMode.CursorRow == (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1));
+
+ //
+ // scroll history attributes 'up' 1 row and set the last row to default attribute
+ //
+ CopySize = ConsoleInfo->ColsPerScreen
+ * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
+ * sizeof(ConsoleInfo->Attributes[0]);
+ ASSERT(CopySize < ConsoleInfo->AttribSize);
+ CopyMem(
+ ConsoleInfo->Attributes,
+ ConsoleInfo->Attributes + ConsoleInfo->ColsPerScreen,
+ CopySize
+ );
+
+ for ( Index = 0
+ ; Index < ConsoleInfo->ColsPerScreen
+ ; Index++
+ ){
+ *(ConsoleInfo->Attributes + (CopySize/sizeof(ConsoleInfo->Attributes[0])) + Index) = ConsoleInfo->HistoryMode.Attribute;
+ }
+
+ //
+ // scroll history buffer 'up' 1 row and set the last row to spaces (L' ')
+ //
+ CopySize = (ConsoleInfo->ColsPerScreen + 2)
+ * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1)
+ * sizeof(ConsoleInfo->Buffer[0]);
+ ASSERT(CopySize < ConsoleInfo->BufferSize);
+ CopyMem(
+ ConsoleInfo->Buffer,
+ ConsoleInfo->Buffer + (ConsoleInfo->ColsPerScreen + 2),
+ CopySize
+ );
+
+ //
+ // Set that last row of chars to spaces
+ //
+ SetMem16(((UINT8*)ConsoleInfo->Buffer)+CopySize, ConsoleInfo->ColsPerScreen*sizeof(CHAR16), L' ');
+ } else {
+ //
+ // we are not on the last row
+ //
+
+ //
+ // We should not be scrolling history
+ //
+ ASSERT (ConsoleInfo->OriginalStartRow == ConsoleInfo->CurrentStartRow);
+ //
+ // are we at the end of a row?
+ //
+ if (ConsoleInfo->HistoryMode.CursorRow == (INT32) (ConsoleInfo->OriginalStartRow + ConsoleInfo->RowsPerScreen - 1)) {
+ ConsoleInfo->OriginalStartRow++;
+ ConsoleInfo->CurrentStartRow++;
+ }
+ ConsoleInfo->HistoryMode.CursorRow++;
+ }
+ break;
+ case (CHAR_CARRIAGE_RETURN):
+ //
+ // Move the cursor to the beginning of the current row.
+ //
+ ConsoleInfo->HistoryMode.CursorColumn = 0;
+ break;
+ default:
+ //
+ // Acrtually print characters into the history buffer
+ //
+
+ PrintIndex = ConsoleInfo->HistoryMode.CursorRow * ConsoleInfo->ColsPerScreen + ConsoleInfo->HistoryMode.CursorColumn;
+
+ for ( // no initializer needed
+ ; ConsoleInfo->HistoryMode.CursorColumn < (INT32) ConsoleInfo->ColsPerScreen
+ ; ConsoleInfo->HistoryMode.CursorColumn++
+ , PrintIndex++
+ , Walker++
+ ){
+ if (*Walker == CHAR_NULL
+ ||*Walker == CHAR_BACKSPACE
+ ||*Walker == CHAR_LINEFEED
+ ||*Walker == CHAR_CARRIAGE_RETURN
+ ){
+ Walker--;
+ break;
+ }
+ //
+ // The buffer is 2*CursorRow more since it has that many \r\n characters at the end of each row.
+ //
+
+ ASSERT(PrintIndex + ConsoleInfo->HistoryMode.CursorRow < ConsoleInfo->BufferSize);
+ ConsoleInfo->Buffer[PrintIndex + (2*ConsoleInfo->HistoryMode.CursorRow)] = *Walker;
+ ASSERT(PrintIndex < ConsoleInfo->AttribSize);
+ ConsoleInfo->Attributes[PrintIndex] = ConsoleInfo->HistoryMode.Attribute;
+ } // for loop
+
+ //
+ // Add the carriage return and line feed at the end of the lines
+ //
+ if (ConsoleInfo->HistoryMode.CursorColumn >= (INT32)ConsoleInfo->ColsPerScreen) {
+ AppendStringToHistory(L"\r\n", ConsoleInfo);
+ Walker--;
+ }
+
+ break;
+ } // switch for character
+ } // for loop
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Worker function to handle printing the output to the screen
+ and the history buffer
+
+ @param[in] String The string to output
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ @retval EFI_SUCCESS The string was printed
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
+ the text.
+ @retval EFI_UNSUPPORTED The output device's mode is not currently in a
+ defined text mode.
+ @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
+ characters in the Unicode string could not be
+ rendered and were skipped.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerOutputStringSplit(
+ IN CONST CHAR16 *String,
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String);
+
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ return (AppendStringToHistory(String, ConsoleInfo));
+}
+
+/**
+ Function to handle page break mode.
+
+ This function will prompt for continue or break.
+
+ @retval EFI_SUCCESS Continue was choosen
+ @return other Break was choosen
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerDoPageBreak(
+ VOID
+ )
+{
+ SHELL_PROMPT_RESPONSE *Resp;
+ EFI_STATUS Status;
+
+ Resp = NULL;
+ ASSERT(ShellInfoObject.PageBreakEnabled);
+ ShellInfoObject.PageBreakEnabled = FALSE;
+ Status = ShellPromptForResponseHii(ShellPromptResponseTypeQuitContinue, STRING_TOKEN(STR_SHELL_QUIT_CONT), ShellInfoObject.HiiHandle, (VOID**)&Resp);
+ ShellInfoObject.PageBreakEnabled = TRUE;
+ ASSERT(Resp != NULL);
+ if (Resp == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+ if (EFI_ERROR(Status)) {
+ if (Resp != NULL) {
+ FreePool(Resp);
+ }
+ return (Status);
+ }
+ if (*Resp == ShellPromptResponseContinue) {
+ FreePool(Resp);
+ ShellInfoObject.ConsoleInfo->RowCounter = 0;
+// ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorRow = 0;
+// ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
+
+ return (EFI_SUCCESS);
+ } else if (*Resp == ShellPromptResponseQuit) {
+ FreePool(Resp);
+ ShellInfoObject.ConsoleInfo->Enabled = FALSE;
+ //
+ // When user wants to quit, the shell should stop running the command.
+ //
+ gBS->SignalEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
+ return (EFI_DEVICE_ERROR);
+ } else {
+ ASSERT(FALSE);
+ }
+ return (EFI_SUCCESS);
+}
+/**
+ Worker function to handle printing the output with page breaks.
+
+ @param[in] String The string to output
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ @retval EFI_SUCCESS The string was printed
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
+ the text.
+ @retval EFI_UNSUPPORTED The output device's mode is not currently in a
+ defined text mode.
+ @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
+ characters in the Unicode string could not be
+ rendered and were skipped.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerPrintWithPageBreak(
+ IN CONST CHAR16 *String,
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ CONST CHAR16 *Walker;
+ CONST CHAR16 *LineStart;
+ CHAR16 *StringCopy;
+ CHAR16 TempChar;
+
+ StringCopy = NULL;
+ StringCopy = StrnCatGrow(&StringCopy, NULL, String, 0);
+ if (StringCopy == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ for ( Walker = StringCopy
+ , LineStart = StringCopy
+ ; Walker != NULL && *Walker != CHAR_NULL
+ ; Walker++
+ ){
+ switch (*Walker) {
+ case (CHAR_BACKSPACE):
+ if (ConsoleInfo->OurConOut.Mode->CursorColumn > 0) {
+ ConsoleInfo->OurConOut.Mode->CursorColumn--;
+ }
+ break;
+ case (CHAR_LINEFEED):
+ //
+ // add a temp NULL terminator
+ //
+ TempChar = *(Walker + 1);
+ *((CHAR16*)(Walker+1)) = CHAR_NULL;
+
+ //
+ // output the string
+ //
+ ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
+
+ //
+ // restore the temp NULL terminator to it's original character
+ //
+ *((CHAR16*)(Walker+1)) = TempChar;
+
+ //
+ // Update LineStart Variable
+ //
+ LineStart = Walker + 1;
+
+ //
+ // increment row count
+ //
+ ShellInfoObject.ConsoleInfo->RowCounter++;
+ ConsoleInfo->OurConOut.Mode->CursorRow++;
+
+ break;
+ case (CHAR_CARRIAGE_RETURN):
+ //
+ // Move the cursor to the beginning of the current row.
+ //
+ ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
+ break;
+ default:
+ //
+ // increment column count
+ //
+ ConsoleInfo->OurConOut.Mode->CursorColumn++;
+ //
+ // check if that is the last column
+ //
+ if ((INTN)ConsoleInfo->ColsPerScreen == ConsoleInfo->OurConOut.Mode->CursorColumn + 1) {
+ //
+ // output a line similar to the linefeed character.
+ //
+
+ //
+ // add a temp NULL terminator
+ //
+ TempChar = *(Walker + 1);
+ *((CHAR16*)(Walker+1)) = CHAR_NULL;
+
+ //
+ // output the string
+ //
+ ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
+
+ //
+ // restore the temp NULL terminator to it's original character
+ //
+ *((CHAR16*)(Walker+1)) = TempChar;
+
+ //
+ // Update LineStart Variable
+ //
+ LineStart = Walker + 1;
+
+ //
+ // increment row count and zero the column
+ //
+ ShellInfoObject.ConsoleInfo->RowCounter++;
+ ConsoleInfo->OurConOut.Mode->CursorRow++;
+ ConsoleInfo->OurConOut.Mode->CursorColumn = 0;
+ } // last column on line
+ break;
+ } // switch for character
+
+ //
+ // check if that was the last printable row. If yes handle PageBreak mode
+ //
+ if ((ConsoleInfo->RowsPerScreen) -1 == ShellInfoObject.ConsoleInfo->RowCounter) {
+ if (EFI_ERROR(ConsoleLoggerDoPageBreak())) {
+ //
+ // We got an error which means 'break' and halt the printing
+ //
+ SHELL_FREE_NON_NULL(StringCopy);
+ return (EFI_DEVICE_ERROR);
+ }
+ }
+ } // for loop
+
+ if (LineStart != NULL && *LineStart != CHAR_NULL) {
+ ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo);
+ }
+
+ SHELL_FREE_NON_NULL(StringCopy);
+ return (EFI_SUCCESS);
+}
+
+/**
+ Write a Unicode string to the output device.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] WString The NULL-terminated Unicode string to be displayed on the output
+ device(s). All output devices must also support the Unicode
+ drawing defined in this file.
+ @retval EFI_SUCCESS The string was output to the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
+ the text.
+ @retval EFI_UNSUPPORTED The output device's mode is not currently in a
+ defined text mode.
+ @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
+ characters in the Unicode string could not be
+ rendered and were skipped.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerOutputString (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *WString
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
+ EFI_KEY_DATA KeyData;
+ UINTN EventIndex;
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
+ return (EFI_UNSUPPORTED);
+ }
+ ASSERT(ShellInfoObject.ConsoleInfo == ConsoleInfo);
+
+ Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
+ if (!EFI_ERROR (Status)) {
+ while (ShellInfoObject.HaltOutput) {
+
+ ShellInfoObject.HaltOutput = FALSE;
+ //
+ // just get some key
+ //
+ Status = gBS->WaitForEvent (1, &TxtInEx->WaitForKeyEx, &EventIndex);
+ ASSERT_EFI_ERROR (Status);
+ Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData);
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+
+ if ((KeyData.Key.UnicodeChar == L's') && (KeyData.Key.ScanCode == SCAN_NULL) &&
+ ((KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED)) ||
+ (KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_RIGHT_CONTROL_PRESSED))
+ )
+ ) {
+ ShellInfoObject.HaltOutput = TRUE;
+ }
+ }
+ }
+
+ if (!ShellInfoObject.ConsoleInfo->Enabled) {
+ return (EFI_DEVICE_ERROR);
+ } else if (ShellInfoObject.PageBreakEnabled) {
+ return (ConsoleLoggerPrintWithPageBreak(WString, ConsoleInfo));
+ } else {
+ return (ConsoleLoggerOutputStringSplit(WString, ConsoleInfo));
+ }
+}
+
+/**
+ Verifies that all characters in a Unicode string can be output to the
+ target device.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] WString The NULL-terminated Unicode string to be examined for the output
+ device(s).
+
+ @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
+ @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be
+ rendered by one or more of the output devices mapped
+ by the EFI handle.
+
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerTestString (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *WString
+ )
+{
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+ //
+ // Forward the request to the original ConOut
+ //
+ return (ConsoleInfo->OldConOut->TestString (ConsoleInfo->OldConOut, WString));
+}
+
+/**
+ Returns information for an available text mode that the output device(s)
+ supports.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ModeNumber The mode number to return information on.
+ @param[out] Columns Upon return, the number of columns in the selected geometry
+ @param[out] Rows Upon return, the number of rows in the selected geometry
+
+ @retval EFI_SUCCESS The requested mode information was returned.
+ @retval EFI_DEVICE_ERROR The device had an error and could not
+ complete the request.
+ @retval EFI_UNSUPPORTED The mode number was not valid.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerQueryMode (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN ModeNumber,
+ OUT UINTN *Columns,
+ OUT UINTN *Rows
+ )
+{
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+ //
+ // Forward the request to the original ConOut
+ //
+ return (ConsoleInfo->OldConOut->QueryMode (
+ ConsoleInfo->OldConOut,
+ ModeNumber,
+ Columns,
+ Rows
+ ));
+}
+
+/**
+ Sets the output device(s) to a specified mode.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ModeNumber The mode number to set.
+
+
+ @retval EFI_SUCCESS The requested text mode was set.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The mode number was not valid.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerSetMode (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN ModeNumber
+ )
+{
+ EFI_STATUS Status;
+
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->SetMode (ConsoleInfo->OldConOut, ModeNumber);
+
+ //
+ // Check that the buffers are still correct for logging
+ //
+ if (!EFI_ERROR (Status)) {
+ ConsoleInfo->OurConOut.Mode = ConsoleInfo->OldConOut->Mode;
+ ConsoleLoggerResetBuffers(ConsoleInfo);
+ ConsoleInfo->OriginalStartRow = 0;
+ ConsoleInfo->CurrentStartRow = 0;
+ ConsoleInfo->OurConOut.ClearScreen (&ConsoleInfo->OurConOut);
+ }
+
+ return Status;
+}
+
+/**
+ Sets the background and foreground colors for the OutputString () and
+ ClearScreen () functions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
+ bits 4..6 are the background color. All other bits are undefined
+ and must be zero. The valid Attributes are defined in this file.
+
+ @retval EFI_SUCCESS The attribute was set.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The attribute requested is not defined.
+
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerSetAttribute (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN Attribute
+ )
+{
+ EFI_STATUS Status;
+
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, Attribute);
+
+ //
+ // Record console output history
+ //
+ if (!EFI_ERROR (Status)) {
+ ConsoleInfo->HistoryMode.Attribute = (INT32) Attribute;
+ }
+
+ return Status;
+}
+
+/**
+ Clears the output device(s) display to the currently selected background
+ color.
+
+ @param[in] This Protocol instance pointer.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerClearScreen (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Screen;
+ INT32 *Attributes;
+ UINTN Row;
+ UINTN Column;
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut);
+
+ //
+ // Record console output history
+ //
+ if (!EFI_ERROR (Status)) {
+ Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow];
+ Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow];
+ for ( Row = ConsoleInfo->OriginalStartRow
+ ; Row < (ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)
+ ; Row++
+ ){
+ for ( Column = 0
+ ; Column < ConsoleInfo->ColsPerScreen
+ ; Column++
+ , Screen++
+ , Attributes++
+ ){
+ *Screen = L' ';
+ *Attributes = ConsoleInfo->OldConOut->Mode->Attribute;
+ }
+ //
+ // Skip the NULL on each column end in text buffer only
+ //
+ Screen += 2;
+ }
+ ConsoleInfo->HistoryMode.CursorColumn = 0;
+ ConsoleInfo->HistoryMode.CursorRow = 0;
+ }
+
+ return Status;
+}
+
+/**
+ Sets the current coordinates of the cursor position
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
+ @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the
+ cursor position is invalid for the current mode.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerSetCursorPosition (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN Column,
+ IN UINTN Row
+ )
+{
+ EFI_STATUS Status;
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->SetCursorPosition (
+ ConsoleInfo->OldConOut,
+ Column,
+ Row
+ );
+
+ //
+ // Record console output history
+ //
+ if (!EFI_ERROR (Status)) {
+ ConsoleInfo->HistoryMode.CursorColumn = (INT32)Column;
+ ConsoleInfo->HistoryMode.CursorRow = (INT32)(ConsoleInfo->OriginalStartRow + Row);
+ }
+
+ return Status;
+}
+
+/**
+ Makes the cursor visible or invisible
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
+ set to be invisible.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_DEVICE_ERROR The device had an error and could not complete the
+ request, or the device does not support changing
+ the cursor mode.
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerEnableCursor (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN BOOLEAN Visible
+ )
+{
+ EFI_STATUS Status;
+
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo;
+ ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This);
+ //
+ // Forward the request to the original ConOut
+ //
+ Status = ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, Visible);
+
+ //
+ // Record console output history
+ //
+ if (!EFI_ERROR (Status)) {
+ ConsoleInfo->HistoryMode.CursorVisible = Visible;
+ }
+
+ return Status;
+}
+
+/**
+ Function to update and verify that the current buffers are correct.
+
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ This will be used when a mode has changed or a reset ocurred to verify all
+ history buffers.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerResetBuffers(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ )
+{
+ EFI_STATUS Status;
+
+ if (ConsoleInfo->Buffer != NULL) {
+ FreePool(ConsoleInfo->Buffer);
+ ConsoleInfo->Buffer = NULL;
+ ConsoleInfo->BufferSize = 0;
+ }
+ if (ConsoleInfo->Attributes != NULL) {
+ FreePool(ConsoleInfo->Attributes);
+ ConsoleInfo->Attributes = NULL;
+ ConsoleInfo->AttribSize = 0;
+ }
+
+ Status = gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &ConsoleInfo->ColsPerScreen, &ConsoleInfo->RowsPerScreen);
+ if (EFI_ERROR(Status)){
+ return (Status);
+ }
+
+ ConsoleInfo->BufferSize = (ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Buffer[0]);
+ ConsoleInfo->AttribSize = ConsoleInfo->ColsPerScreen * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Attributes[0]);
+
+ ConsoleInfo->Buffer = (CHAR16*)AllocateZeroPool(ConsoleInfo->BufferSize);
+
+ if (ConsoleInfo->Buffer == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ ConsoleInfo->Attributes = (INT32*)AllocateZeroPool(ConsoleInfo->AttribSize);
+ if (ConsoleInfo->Attributes == NULL) {
+ FreePool(ConsoleInfo->Buffer);
+ ConsoleInfo->Buffer = NULL;
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE));
+
+ return (EFI_SUCCESS);
+}
diff --git a/Core/ShellPkg/Application/Shell/ConsoleLogger.h b/Core/ShellPkg/Application/Shell/ConsoleLogger.h
new file mode 100644
index 0000000000..19034c9050
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ConsoleLogger.h
@@ -0,0 +1,329 @@
+/** @file
+ Provides interface to shell console logger.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#ifndef _CONSOLE_LOGGER_HEADER_
+#define _CONSOLE_LOGGER_HEADER_
+
+#include "Shell.h"
+
+#define CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('c', 'o', 'P', 'D')
+
+typedef struct _CONSOLE_LOGGER_PRIVATE_DATA{
+ UINTN Signature;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL OurConOut; ///< the protocol we installed onto the system table
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OldConOut; ///< old protocol to reinstall upon exiting
+ EFI_HANDLE OldConHandle; ///< old protocol handle
+ UINTN ScreenCount; ///< How many screens worth of data to save
+ CHAR16 *Buffer; ///< Buffer to save data
+ UINTN BufferSize; ///< size of buffer in bytes
+
+ // start row is the top of the screen
+ UINTN OriginalStartRow; ///< What the originally visible start row was
+ UINTN CurrentStartRow; ///< what the currently visible start row is
+
+ UINTN RowsPerScreen; ///< how many rows the screen can display
+ UINTN ColsPerScreen; ///< how many columns the screen can display
+
+ INT32 *Attributes; ///< Buffer for Attribute to be saved for each character
+ UINTN AttribSize; ///< Size of Attributes in bytes
+
+ EFI_SIMPLE_TEXT_OUTPUT_MODE HistoryMode; ///< mode of the history log
+ BOOLEAN Enabled; ///< Set to FALSE when a break is requested.
+ UINTN RowCounter; ///< Initial row of each print job.
+} CONSOLE_LOGGER_PRIVATE_DATA;
+
+#define CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(a) CR (a, CONSOLE_LOGGER_PRIVATE_DATA, OurConOut, CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE)
+
+/**
+ Install our intermediate ConOut into the system table to
+ keep a log of all the info that is displayed to the user.
+
+ @param[in] ScreensToSave Sets how many screen-worths of data to save.
+ @param[out] ConsoleInfo The object to pass into later functions.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return other The operation failed.
+
+ @sa ConsoleLoggerResetBuffers
+ @sa InstallProtocolInterface
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerInstall(
+ IN CONST UINTN ScreensToSave,
+ OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo
+ );
+
+/**
+ Return the system to the state it was before InstallConsoleLogger
+ was installed.
+
+ @param[in, out] ConsoleInfo The object from the install function.
+
+ @retval EFI_SUCCESS The operation was successful
+ @return other The operation failed. This was from UninstallProtocolInterface.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerUninstall(
+ IN OUT CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ );
+
+/**
+ Displays previously logged output back to the screen.
+
+ This will scroll the screen forwards and backwards through the log of previous
+ output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows
+ is (UINTN)(-1) then the size of the screen will be scrolled.
+
+ @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer).
+ If FALSE then the log will be displayed backwards (scroll to older).
+ @param[in] Rows Determines how many rows the log should scroll.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerDisplayHistory(
+ IN CONST BOOLEAN Forward,
+ IN CONST UINTN Rows,
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ );
+
+/**
+ Function to return to normal output whent he scrolling is complete.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return other The operation failed. See UpdateDisplayFromHistory.
+
+ @sa UpdateDisplayFromHistory
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerStopHistory(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ );
+
+/**
+ Updates the hidden ConOut to be displaying the correct stuff.
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return other The operation failed.
+**/
+EFI_STATUS
+EFIAPI
+UpdateDisplayFromHistory(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ );
+
+/**
+ Reset the text output device hardware and optionaly run diagnostics
+
+ @param This Pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
+ @param ExtendedVerification Indicates that a more extensive test may be performed
+
+ @retval EFI_SUCCESS The text output device was reset.
+ @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and
+ could not be reset.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerReset (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+/**
+ Write a Unicode string to the output device.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] WString The NULL-terminated Unicode string to be displayed on the output
+ device(s). All output devices must also support the Unicode
+ drawing defined in this file.
+ @retval EFI_SUCCESS The string was output to the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
+ the text.
+ @retval EFI_UNSUPPORTED The output device's mode is not currently in a
+ defined text mode.
+ @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
+ characters in the Unicode string could not be
+ rendered and were skipped.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerOutputString(
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *WString
+ );
+
+/**
+ Verifies that all characters in a Unicode string can be output to the
+ target device.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] WString The NULL-terminated Unicode string to be examined for the output
+ device(s).
+
+ @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
+ @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be
+ rendered by one or more of the output devices mapped
+ by the EFI handle.
+
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerTestString (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *WString
+ );
+
+/**
+ Returns information for an available text mode that the output device(s)
+ supports.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ModeNumber The mode number to return information on.
+ @param[out] Columns Upon return, the number of columns in the selected geometry
+ @param[out] Rows Upon return, the number of rows in the selected geometry
+
+ @retval EFI_SUCCESS The requested mode information was returned.
+ @retval EFI_DEVICE_ERROR The device had an error and could not
+ complete the request.
+ @retval EFI_UNSUPPORTED The mode number was not valid.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerQueryMode (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN ModeNumber,
+ OUT UINTN *Columns,
+ OUT UINTN *Rows
+ );
+
+/**
+ Sets the output device(s) to a specified mode.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ModeNumber The mode number to set.
+
+
+ @retval EFI_SUCCESS The requested text mode was set.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The mode number was not valid.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerSetMode (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN ModeNumber
+ );
+
+/**
+ Sets the background and foreground colors for the OutputString () and
+ ClearScreen () functions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
+ bits 4..6 are the background color. All other bits are undefined
+ and must be zero. The valid Attributes are defined in this file.
+
+ @retval EFI_SUCCESS The attribute was set.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The attribute requested is not defined.
+
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerSetAttribute (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN Attribute
+ );
+
+/**
+ Clears the output device(s) display to the currently selected background
+ color.
+
+ @param[in] This Protocol instance pointer.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerClearScreen (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ );
+
+/**
+ Sets the current coordinates of the cursor position.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
+ @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_DEVICE_ERROR The device had an error and
+ could not complete the request.
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the
+ cursor position is invalid for the current mode.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerSetCursorPosition (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN Column,
+ IN UINTN Row
+ );
+
+/**
+ Makes the cursor visible or invisible
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
+ set to be invisible.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_DEVICE_ERROR The device had an error and could not complete the
+ request, or the device does not support changing
+ the cursor mode.
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
+
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerEnableCursor (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN BOOLEAN Visible
+ );
+
+/**
+ Function to update and verify that the current buffers are correct.
+
+ @param[in] ConsoleInfo The pointer to the instance of the console logger information.
+
+ This will be used when a mode has changed or a reset ocurred to verify all
+ history buffers.
+**/
+EFI_STATUS
+EFIAPI
+ConsoleLoggerResetBuffers(
+ IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
+ );
+
+#endif //_CONSOLE_LOGGER_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/ConsoleWrappers.c b/Core/ShellPkg/Application/Shell/ConsoleWrappers.c
new file mode 100644
index 0000000000..46af141eba
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ConsoleWrappers.c
@@ -0,0 +1,518 @@
+/** @file
+ Function definitions for shell simple text in and out on top of file handles.
+
+ (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+extern BOOLEAN AsciiRedirection;
+
+typedef struct {
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL SimpleTextIn;
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_HANDLE TheHandle;
+ UINT64 RemainingBytesOfInputFile;
+} SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
+
+typedef struct {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SimpleTextOut;
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_HANDLE TheHandle;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalSimpleTextOut;
+} SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
+
+/**
+ Event notification function for EFI_SIMPLE_TEXT_INPUT_PROTOCOL.WaitForKey event
+ Signal the event if there is key available
+
+ @param Event Indicates the event that invoke this function.
+ @param Context Indicates the calling context.
+
+**/
+VOID
+EFIAPI
+ConInWaitForKey (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gBS->SignalEvent (Event);
+}
+
+/**
+ Reset function for the fake simple text input.
+
+ @param[in] This A pointer to the SimpleTextIn structure.
+ @param[in] ExtendedVerification TRUE for extra validation, FALSE otherwise.
+
+ @retval EFI_SUCCESS The reset was successful.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextInReset(
+ IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ ReadKeyStroke function for the fake simple text input.
+
+ @param[in] This A pointer to the SimpleTextIn structure.
+ @param[in, out] Key A pointer to the Key structure to fill.
+
+ @retval EFI_SUCCESS The read was successful.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextInReadKeyStroke(
+ IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
+ IN OUT EFI_INPUT_KEY *Key
+ )
+{
+ UINTN Size;
+ UINTN CharSize;
+
+ //
+ // Verify the parameters
+ //
+ if (Key == NULL || This == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // Check if we have any characters left in the stream.
+ //
+ if (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile == 0) {
+ return (EFI_NOT_READY);
+ }
+
+ Size = sizeof(CHAR16);
+
+ if(!AsciiRedirection) {
+ CharSize = sizeof(CHAR16);
+ } else {
+ CharSize = sizeof(CHAR8);
+ }
+ //
+ // Decrement the amount of free space by Size or set to zero (for odd length files)
+ //
+ if (((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile > CharSize) {
+ ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile -= CharSize;
+ } else {
+ ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->RemainingBytesOfInputFile = 0;
+ }
+
+ Key->ScanCode = 0;
+ return (ShellInfoObject.NewEfiShellProtocol->ReadFile(
+ ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)This)->FileHandle,
+ &Size,
+ &Key->UnicodeChar));
+}
+
+/**
+ Function to create a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting input from a file.
+
+ @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
+ @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
+
+ @retval NULL There was insufficient memory available.
+ @return A pointer to the allocated protocol structure;
+**/
+EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
+EFIAPI
+CreateSimpleTextInOnFile(
+ IN SHELL_FILE_HANDLE FileHandleToUse,
+ IN EFI_HANDLE *HandleLocation
+ )
+{
+ SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ProtocolToReturn;
+ EFI_STATUS Status;
+ UINT64 CurrentPosition;
+ UINT64 FileSize;
+
+ if (HandleLocation == NULL || FileHandleToUse == NULL) {
+ return (NULL);
+ }
+
+ ProtocolToReturn = AllocateZeroPool(sizeof(SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL));
+ if (ProtocolToReturn == NULL) {
+ return (NULL);
+ }
+
+ ShellGetFileSize (FileHandleToUse, &FileSize);
+ ShellGetFilePosition(FileHandleToUse, &CurrentPosition);
+
+ //
+ // Initialize the protocol members
+ //
+ ProtocolToReturn->RemainingBytesOfInputFile = FileSize - CurrentPosition;
+ ProtocolToReturn->FileHandle = FileHandleToUse;
+ ProtocolToReturn->SimpleTextIn.Reset = FileBasedSimpleTextInReset;
+ ProtocolToReturn->SimpleTextIn.ReadKeyStroke = FileBasedSimpleTextInReadKeyStroke;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_WAIT,
+ TPL_NOTIFY,
+ ConInWaitForKey,
+ &ProtocolToReturn->SimpleTextIn,
+ &ProtocolToReturn->SimpleTextIn.WaitForKey
+ );
+
+ if (EFI_ERROR(Status)) {
+ FreePool(ProtocolToReturn);
+ return (NULL);
+ }
+ ///@todo possibly also install SimpleTextInputEx on the handle at this point.
+ Status = gBS->InstallProtocolInterface(
+ &(ProtocolToReturn->TheHandle),
+ &gEfiSimpleTextInProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &(ProtocolToReturn->SimpleTextIn));
+ if (!EFI_ERROR(Status)) {
+ *HandleLocation = ProtocolToReturn->TheHandle;
+ return ((EFI_SIMPLE_TEXT_INPUT_PROTOCOL*)ProtocolToReturn);
+ } else {
+ FreePool(ProtocolToReturn);
+ return (NULL);
+ }
+}
+
+/**
+ Function to close a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting input from a file.
+
+ @param[in] SimpleTextIn The pointer to the SimpleTextIn to close.
+
+ @retval EFI_SUCCESS The object was closed.
+**/
+EFI_STATUS
+EFIAPI
+CloseSimpleTextInOnFile(
+ IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+
+ if (SimpleTextIn == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = gBS->CloseEvent(((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL *)SimpleTextIn)->SimpleTextIn.WaitForKey);
+
+ Status1 = gBS->UninstallProtocolInterface(
+ ((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL*)SimpleTextIn)->TheHandle,
+ &gEfiSimpleTextInProtocolGuid,
+ &(((SHELL_EFI_SIMPLE_TEXT_INPUT_PROTOCOL*)SimpleTextIn)->SimpleTextIn));
+
+ FreePool(SimpleTextIn);
+ if (!EFI_ERROR(Status)) {
+ return (Status1);
+ } else {
+ return (Status);
+ }
+}
+
+/**
+ Reset the text output device hardware and optionaly run diagnostics.
+
+ @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
+ @param ExtendedVerification Indicates that a more extensive test may be performed
+
+ @retval EFI_SUCCESS The text output device was reset.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutReset (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ Verifies that all characters in a Unicode string can be output to the
+ target device.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] WString The NULL-terminated Unicode string to be examined.
+
+ @retval EFI_SUCCESS The device(s) are capable of rendering the output string.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutTestString (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *WString
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ Returns information for an available text mode that the output device(s)
+ supports.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ModeNumber The mode number to return information on.
+ @param[out] Columns Upon return, the number of columns in the selected geometry
+ @param[out] Rows Upon return, the number of rows in the selected geometry
+
+ @retval EFI_UNSUPPORTED The mode number was not valid.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutQueryMode (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN ModeNumber,
+ OUT UINTN *Columns,
+ OUT UINTN *Rows
+ )
+{
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *PassThruProtocol;
+
+ PassThruProtocol = ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)This)->OriginalSimpleTextOut;
+
+ // Pass the QueryMode call thru to the original SimpleTextOutProtocol
+ return (PassThruProtocol->QueryMode(
+ PassThruProtocol,
+ ModeNumber,
+ Columns,
+ Rows));
+}
+
+/**
+ Sets the output device(s) to a specified mode.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ModeNumber The mode number to set.
+
+ @retval EFI_UNSUPPORTED The mode number was not valid.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutSetMode (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN ModeNumber
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ Sets the background and foreground colors for the OutputString () and
+ ClearScreen () functions.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and
+ bits 4..6 are the background color. All other bits are undefined
+ and must be zero. The valid Attributes are defined in this file.
+
+ @retval EFI_SUCCESS The attribute was set.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutSetAttribute (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN Attribute
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ Clears the output device(s) display to the currently selected background
+ color.
+
+ @param[in] This Protocol instance pointer.
+
+ @retval EFI_UNSUPPORTED The output device is not in a valid text mode.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutClearScreen (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ Sets the current coordinates of the cursor position
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode
+ @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode
+
+ @retval EFI_SUCCESS The operation completed successfully.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutSetCursorPosition (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN UINTN Column,
+ IN UINTN Row
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ Makes the cursor visible or invisible
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is
+ set to be invisible.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutEnableCursor (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN BOOLEAN Visible
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ Write a Unicode string to the output device.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] WString The NULL-terminated Unicode string to be displayed on the output
+ device(s). All output devices must also support the Unicode
+ drawing defined in this file.
+ @retval EFI_SUCCESS The string was output to the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to output
+ the text.
+ @retval EFI_UNSUPPORTED The output device's mode is not currently in a
+ defined text mode.
+ @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the
+ characters in the Unicode string could not be
+ rendered and were skipped.
+**/
+EFI_STATUS
+EFIAPI
+FileBasedSimpleTextOutOutputString (
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
+ IN CHAR16 *WString
+ )
+{
+ UINTN Size;
+ Size = StrLen(WString) * sizeof(CHAR16);
+ return (ShellInfoObject.NewEfiShellProtocol->WriteFile(
+ ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *)This)->FileHandle,
+ &Size,
+ WString));
+}
+
+/**
+ Function to create a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting output from a file.
+
+ @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
+ @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
+ @param[in] OriginalProtocol The pointer to the original output protocol for pass thru of functions.
+
+ @retval NULL There was insufficient memory available.
+ @return A pointer to the allocated protocol structure;
+**/
+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
+EFIAPI
+CreateSimpleTextOutOnFile(
+ IN SHELL_FILE_HANDLE FileHandleToUse,
+ IN EFI_HANDLE *HandleLocation,
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalProtocol
+ )
+{
+ SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ProtocolToReturn;
+ EFI_STATUS Status;
+
+ if (HandleLocation == NULL || FileHandleToUse == NULL) {
+ return (NULL);
+ }
+
+ ProtocolToReturn = AllocateZeroPool(sizeof(SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL));
+ if (ProtocolToReturn == NULL) {
+ return (NULL);
+ }
+ ProtocolToReturn->FileHandle = FileHandleToUse;
+ ProtocolToReturn->OriginalSimpleTextOut = OriginalProtocol;
+ ProtocolToReturn->SimpleTextOut.Reset = FileBasedSimpleTextOutReset;
+ ProtocolToReturn->SimpleTextOut.TestString = FileBasedSimpleTextOutTestString;
+ ProtocolToReturn->SimpleTextOut.QueryMode = FileBasedSimpleTextOutQueryMode;
+ ProtocolToReturn->SimpleTextOut.SetMode = FileBasedSimpleTextOutSetMode;
+ ProtocolToReturn->SimpleTextOut.SetAttribute = FileBasedSimpleTextOutSetAttribute;
+ ProtocolToReturn->SimpleTextOut.ClearScreen = FileBasedSimpleTextOutClearScreen;
+ ProtocolToReturn->SimpleTextOut.SetCursorPosition = FileBasedSimpleTextOutSetCursorPosition;
+ ProtocolToReturn->SimpleTextOut.EnableCursor = FileBasedSimpleTextOutEnableCursor;
+ ProtocolToReturn->SimpleTextOut.OutputString = FileBasedSimpleTextOutOutputString;
+ ProtocolToReturn->SimpleTextOut.Mode = AllocateZeroPool(sizeof(EFI_SIMPLE_TEXT_OUTPUT_MODE));
+ if (ProtocolToReturn->SimpleTextOut.Mode == NULL) {
+ FreePool(ProtocolToReturn);
+ return (NULL);
+ }
+ ProtocolToReturn->SimpleTextOut.Mode->MaxMode = OriginalProtocol->Mode->MaxMode;
+ ProtocolToReturn->SimpleTextOut.Mode->Mode = OriginalProtocol->Mode->Mode;
+ ProtocolToReturn->SimpleTextOut.Mode->Attribute = OriginalProtocol->Mode->Attribute;
+ ProtocolToReturn->SimpleTextOut.Mode->CursorColumn = OriginalProtocol->Mode->CursorColumn;
+ ProtocolToReturn->SimpleTextOut.Mode->CursorRow = OriginalProtocol->Mode->CursorRow;
+ ProtocolToReturn->SimpleTextOut.Mode->CursorVisible = OriginalProtocol->Mode->CursorVisible;
+
+ Status = gBS->InstallProtocolInterface(
+ &(ProtocolToReturn->TheHandle),
+ &gEfiSimpleTextOutProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &(ProtocolToReturn->SimpleTextOut));
+ if (!EFI_ERROR(Status)) {
+ *HandleLocation = ProtocolToReturn->TheHandle;
+ return ((EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*)ProtocolToReturn);
+ } else {
+ SHELL_FREE_NON_NULL(ProtocolToReturn->SimpleTextOut.Mode);
+ SHELL_FREE_NON_NULL(ProtocolToReturn);
+ return (NULL);
+ }
+}
+
+/**
+ Function to close a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting output from a file.
+
+ @param[in] SimpleTextOut The pointer to the SimpleTextOUT to close.
+
+ @retval EFI_SUCCESS The object was closed.
+**/
+EFI_STATUS
+EFIAPI
+CloseSimpleTextOutOnFile(
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut
+ )
+{
+ EFI_STATUS Status;
+ if (SimpleTextOut == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ Status = gBS->UninstallProtocolInterface(
+ ((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*)SimpleTextOut)->TheHandle,
+ &gEfiSimpleTextOutProtocolGuid,
+ &(((SHELL_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*)SimpleTextOut)->SimpleTextOut));
+ FreePool(SimpleTextOut->Mode);
+ FreePool(SimpleTextOut);
+ return (Status);
+}
diff --git a/Core/ShellPkg/Application/Shell/ConsoleWrappers.h b/Core/ShellPkg/Application/Shell/ConsoleWrappers.h
new file mode 100644
index 0000000000..ecd99a863d
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ConsoleWrappers.h
@@ -0,0 +1,84 @@
+/** @file
+ Function definitions for shell simple text in and out on top of file handles.
+
+ (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_CONSOLE_WRAPPERS_HEADER_
+#define _SHELL_CONSOLE_WRAPPERS_HEADER_
+
+/**
+ Function to create a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting input from a file.
+
+ @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
+ @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
+
+ @retval NULL There was insufficient memory available.
+ @return A pointer to the allocated protocol structure;
+**/
+EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
+EFIAPI
+CreateSimpleTextInOnFile(
+ IN SHELL_FILE_HANDLE FileHandleToUse,
+ IN EFI_HANDLE *HandleLocation
+ );
+
+/**
+ Function to close a EFI_SIMPLE_TEXT_INPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting input from a file.
+
+ @param[in] SimpleTextIn The pointer to the SimpleTextIn to close.
+
+ @retval EFI_SUCCESS The object was closed.
+**/
+EFI_STATUS
+EFIAPI
+CloseSimpleTextInOnFile(
+ IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *SimpleTextIn
+ );
+
+/**
+ Function to create a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting output from a file.
+
+ @param[in] FileHandleToUse The pointer to the SHELL_FILE_HANDLE to use.
+ @param[in] HandleLocation The pointer of a location to copy handle with protocol to.
+ @param[in] OriginalProtocol The pointer to the original output protocol for pass thru of functions.
+
+ @retval NULL There was insufficient memory available.
+ @return A pointer to the allocated protocol structure;
+**/
+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
+EFIAPI
+CreateSimpleTextOutOnFile(
+ IN SHELL_FILE_HANDLE FileHandleToUse,
+ IN EFI_HANDLE *HandleLocation,
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *OriginalProtocol
+ );
+
+/**
+ Function to close a EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL on top of a
+ SHELL_FILE_HANDLE to support redirecting output from a file.
+
+ @param[in] SimpleTextOut The pointer to the SimpleTextOUT to close.
+
+ @retval EFI_SUCCESS The object was closed.
+**/
+EFI_STATUS
+EFIAPI
+CloseSimpleTextOutOnFile(
+ IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut
+ );
+
+#endif //_SHELL_CONSOLE_WRAPPERS_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/FileHandleInternal.h b/Core/ShellPkg/Application/Shell/FileHandleInternal.h
new file mode 100644
index 0000000000..11b490267c
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/FileHandleInternal.h
@@ -0,0 +1,68 @@
+/** @file
+ internal worker functions for FileHandleWrappers to use
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _FILE_HANDLE_INTERNAL_HEADER_
+#define _FILE_HANDLE_INTERNAL_HEADER_
+
+/**
+ Move the cursor position one character backward.
+
+ @param[in] LineLength Length of a line. Get it by calling QueryMode
+ @param[in, out] Column Current column of the cursor position
+ @param[in, out] Row Current row of the cursor position
+**/
+VOID
+EFIAPI
+MoveCursorBackward (
+ IN UINTN LineLength,
+ IN OUT UINTN *Column,
+ IN OUT UINTN *Row
+ );
+
+/**
+ Move the cursor position one character forward.
+
+ @param[in] LineLength Length of a line.
+ @param[in] TotalRow Total row of a screen
+ @param[in, out] Column Current column of the cursor position
+ @param[in, out] Row Current row of the cursor position
+**/
+VOID
+EFIAPI
+MoveCursorForward (
+ IN UINTN LineLength,
+ IN UINTN TotalRow,
+ IN OUT UINTN *Column,
+ IN OUT UINTN *Row
+ );
+
+/**
+ Prints out each previously typed command in the command list history log.
+
+ When each screen is full it will pause for a key before continuing.
+
+ @param[in] TotalCols How many columns are on the screen
+ @param[in] TotalRows How many rows are on the screen
+ @param[in] StartColumn which column to start at
+**/
+VOID
+EFIAPI
+PrintCommandHistory (
+ IN CONST UINTN TotalCols,
+ IN CONST UINTN TotalRows,
+ IN CONST UINTN StartColumn
+ );
+
+#endif //_FILE_HANDLE_INTERNAL_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/FileHandleWrappers.c b/Core/ShellPkg/Application/Shell/FileHandleWrappers.c
new file mode 100644
index 0000000000..f6a82ee3c7
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/FileHandleWrappers.c
@@ -0,0 +1,1910 @@
+/** @file
+ EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables,
+ StdIn, StdOut, StdErr, etc...).
+
+ Copyright 2016 Dell Inc.
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+#include "FileHandleInternal.h"
+
+#define MEM_WRITE_REALLOC_OVERHEAD 1024
+
+/**
+ File style interface for console (Open).
+
+ @param[in] This Ignored.
+ @param[out] NewHandle Ignored.
+ @param[in] FileName Ignored.
+ @param[in] OpenMode Ignored.
+ @param[in] Attributes Ignored.
+
+ @retval EFI_NOT_FOUND
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceOpenNotFound(
+ IN EFI_FILE_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ )
+{
+ return (EFI_NOT_FOUND);
+}
+
+/**
+ File style interface for console (Close, Delete, & Flush)
+
+ @param[in] This Ignored.
+
+ @retval EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNopGeneric(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ File style interface for console (GetPosition).
+
+ @param[in] This Ignored.
+ @param[out] Position Ignored.
+
+ @retval EFI_UNSUPPORTED
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNopGetPosition(
+ IN EFI_FILE_PROTOCOL *This,
+ OUT UINT64 *Position
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for console (SetPosition).
+
+ @param[in] This Ignored.
+ @param[in] Position Ignored.
+
+ @retval EFI_UNSUPPORTED
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNopSetPosition(
+ IN EFI_FILE_PROTOCOL *This,
+ IN UINT64 Position
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for console (GetInfo).
+
+ @param[in] This Ignored.
+ @param[in] InformationType Ignored.
+ @param[in, out] BufferSize Ignored.
+ @param[out] Buffer Ignored.
+
+ @retval EFI_UNSUPPORTED
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNopGetInfo(
+ IN EFI_FILE_PROTOCOL *This,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for console (SetInfo).
+
+ @param[in] This Ignored.
+ @param[in] InformationType Ignored.
+ @param[in] BufferSize Ignored.
+ @param[in] Buffer Ignored.
+
+ @retval EFI_UNSUPPORTED
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNopSetInfo(
+ IN EFI_FILE_PROTOCOL *This,
+ IN EFI_GUID *InformationType,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for StdOut (Write).
+
+ Writes data to the screen.
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to write.
+
+ @retval EFI_UNSUPPORTED No output console is supported.
+ @return A return value from gST->ConOut->OutputString.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceStdOutWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) {
+ return (EFI_UNSUPPORTED);
+ }
+ if (*((CHAR16 *)Buffer) == gUnicodeFileTag) {
+ return (gST->ConOut->OutputString(gST->ConOut, (CHAR16 *)Buffer + 1));
+ }
+ return (gST->ConOut->OutputString(gST->ConOut, Buffer));
+}
+
+/**
+ File style interface for StdIn (Write).
+
+ @param[in] This Ignored.
+ @param[in, out] BufferSize Ignored.
+ @param[in] Buffer Ignored.
+
+ @retval EFI_UNSUPPORTED
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceStdInWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for console StdErr (Write).
+
+ Writes error to the error output.
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to write.
+
+ @return A return value from gST->StdErr->OutputString.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceStdErrWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ return (gST->StdErr->OutputString(gST->StdErr, Buffer));
+}
+
+/**
+ File style interface for console StdOut (Read).
+
+ @param[in] This Ignored.
+ @param[in, out] BufferSize Ignored.
+ @param[out] Buffer Ignored.
+
+ @retval EFI_UNSUPPORTED
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceStdOutRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for console StdErr (Read).
+
+ @param[in] This Ignored.
+ @param[in, out] BufferSize Ignored.
+ @param[out] Buffer Ignored.
+
+ @retval EFI_UNSUPPORTED Always.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceStdErrRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ return (EFI_UNSUPPORTED);
+}
+
+/**
+ File style interface for NUL file (Read).
+
+ @param[in] This Ignored.
+ @param[in, out] BufferSize Poiner to 0 upon return.
+ @param[out] Buffer Ignored.
+
+ @retval EFI_SUCCESS Always.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNulRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ *BufferSize = 0;
+ return (EFI_SUCCESS);
+}
+
+/**
+ File style interface for NUL file (Write).
+
+ @param[in] This Ignored.
+ @param[in, out] BufferSize Ignored.
+ @param[in] Buffer Ignored.
+
+ @retval EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceNulWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ return (EFI_SUCCESS);
+}
+
+/**
+ File style interface for console (Read).
+
+ This will return a single line of input from the console.
+
+ @param This A pointer to the EFI_FILE_PROTOCOL instance that is the
+ file handle to read data from. Not used.
+ @param BufferSize On input, the size of the Buffer. On output, the amount
+ of data returned in Buffer. In both cases, the size is
+ measured in bytes.
+ @param Buffer The buffer into which the data is read.
+
+
+ @retval EFI_SUCCESS The data was read.
+ @retval EFI_NO_MEDIA The device has no medium.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted file.
+ @retval EFI_DEVICE_ERROR On entry, the current file position is beyond the end of the file.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory
+ entry. BufferSize has been updated with the size
+ needed to complete the request.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceStdInRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ CHAR16 *CurrentString;
+ BOOLEAN Done;
+ UINTN Column; // Column of current cursor
+ UINTN Row; // Row of current cursor
+ UINTN StartColumn; // Column at the beginning of the line
+ UINTN Update; // Line index for update
+ UINTN Delete; // Num of chars to delete from console after update
+ UINTN StringLen; // Total length of the line
+ UINTN StringCurPos; // Line index corresponding to the cursor
+ UINTN MaxStr; // Maximum possible line length
+ UINTN Index;
+ UINTN TotalColumn; // Num of columns in the console
+ UINTN TotalRow; // Num of rows in the console
+ UINTN SkipLength;
+ UINTN OutputLength; // Length of the update string
+ UINTN TailRow; // Row of end of line
+ UINTN TailColumn; // Column of end of line
+ EFI_INPUT_KEY Key;
+
+ BUFFER_LIST *LinePos;
+ BUFFER_LIST *NewPos;
+ BOOLEAN InScrolling;
+ EFI_STATUS Status;
+ BOOLEAN InTabScrolling; // Whether in TAB-completion state
+ EFI_SHELL_FILE_INFO *FoundFileList;
+ EFI_SHELL_FILE_INFO *TabLinePos;
+ EFI_SHELL_FILE_INFO *TempPos;
+ CHAR16 *TabStr;
+ CHAR16 *TabOutputStr;
+ BOOLEAN InQuotationMode;
+ CHAR16 *TempStr;
+ UINTN TabPos; // Start index of the string to search for TAB completion.
+ UINTN TabUpdatePos; // Start index of the string updated by TAB stroke
+// UINTN Count;
+ UINTN EventIndex;
+ CONST CHAR16 *Cwd;
+
+ //
+ // If buffer is not large enough to hold a CHAR16, return minimum buffer size
+ //
+ if (*BufferSize < sizeof (CHAR16) * 2) {
+ *BufferSize = sizeof (CHAR16) * 2;
+ return (EFI_BUFFER_TOO_SMALL);
+ }
+
+ Done = FALSE;
+ CurrentString = Buffer;
+ StringLen = 0;
+ StringCurPos = 0;
+ OutputLength = 0;
+ Update = 0;
+ Delete = 0;
+ LinePos = NewPos = (BUFFER_LIST*)(&ShellInfoObject.ViewingSettings.CommandHistory);
+ InScrolling = FALSE;
+ InTabScrolling = FALSE;
+ Status = EFI_SUCCESS;
+ TabLinePos = NULL;
+ FoundFileList = NULL;
+ TempPos = NULL;
+ TabPos = 0;
+ TabUpdatePos = 0;
+
+ //
+ // Allocate buffers
+ //
+ TabStr = AllocateZeroPool (*BufferSize);
+ if (TabStr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ TabOutputStr = AllocateZeroPool (*BufferSize);
+ if (TabOutputStr == NULL) {
+ FreePool(TabStr);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get the screen setting and the current cursor location
+ //
+ Column = StartColumn = gST->ConOut->Mode->CursorColumn;
+ Row = gST->ConOut->Mode->CursorRow;
+ gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &TotalColumn, &TotalRow);
+
+ //
+ // Limit the line length to the buffer size or the minimun size of the
+ // screen. (The smaller takes effect)
+ //
+ MaxStr = TotalColumn * (TotalRow - 1) - StartColumn;
+ if (MaxStr > *BufferSize / sizeof (CHAR16)) {
+ MaxStr = *BufferSize / sizeof (CHAR16);
+ }
+ ZeroMem (CurrentString, MaxStr * sizeof (CHAR16));
+ do {
+ //
+ // Read a key
+ //
+ gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ if (EFI_ERROR (Status)) {
+
+ if (Status == EFI_NOT_READY)
+ continue;
+
+ ZeroMem (CurrentString, MaxStr * sizeof(CHAR16));
+ StringLen = 0;
+ break;
+ }
+
+ //
+ // Press PageUp or PageDown to scroll the history screen up or down.
+ // Press any other key to quit scrolling.
+ //
+ if (Key.UnicodeChar == 0 && (Key.ScanCode == SCAN_PAGE_UP || Key.ScanCode == SCAN_PAGE_DOWN)) {
+ if (Key.ScanCode == SCAN_PAGE_UP) {
+ ConsoleLoggerDisplayHistory(FALSE, 0, ShellInfoObject.ConsoleInfo);
+ } else if (Key.ScanCode == SCAN_PAGE_DOWN) {
+ ConsoleLoggerDisplayHistory(TRUE, 0, ShellInfoObject.ConsoleInfo);
+ }
+
+ InScrolling = TRUE;
+ } else {
+ if (InScrolling) {
+ ConsoleLoggerStopHistory(ShellInfoObject.ConsoleInfo);
+ InScrolling = FALSE;
+ }
+ }
+
+ //
+ // If we are quitting TAB scrolling...
+ //
+ if (InTabScrolling && Key.UnicodeChar != CHAR_TAB) {
+ if (FoundFileList != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FoundFileList);
+ DEBUG_CODE(FoundFileList = NULL;);
+ }
+ InTabScrolling = FALSE;
+ }
+
+ switch (Key.UnicodeChar) {
+ case CHAR_CARRIAGE_RETURN:
+ //
+ // All done, print a newline at the end of the string
+ //
+ TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn;
+ TailColumn = (StringLen - StringCurPos + Column) % TotalColumn;
+ ShellPrintEx ((INT32)TailColumn, (INT32)TailRow, L"%N\n");
+ Done = TRUE;
+ break;
+
+ case CHAR_BACKSPACE:
+ if (StringCurPos != 0) {
+ //
+ // If not move back beyond string beginning, move all characters behind
+ // the current position one character forward
+ //
+ StringCurPos--;
+ Update = StringCurPos;
+ Delete = 1;
+ CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos));
+
+ //
+ // Adjust the current column and row
+ //
+ MoveCursorBackward (TotalColumn, &Column, &Row);
+ }
+ break;
+
+ case CHAR_TAB:
+ //
+ // handle auto complete of file and directory names...
+ //
+ if (InTabScrolling) {
+ ASSERT(FoundFileList != NULL);
+ ASSERT(TabLinePos != NULL);
+ TabLinePos = (EFI_SHELL_FILE_INFO*)GetNextNode(&(FoundFileList->Link), &TabLinePos->Link);
+ if (IsNull(&(FoundFileList->Link), &TabLinePos->Link)) {
+ TabLinePos = (EFI_SHELL_FILE_INFO*)GetNextNode(&(FoundFileList->Link), &TabLinePos->Link);
+ }
+ } else {
+ TabPos = 0;
+ TabUpdatePos = 0;
+ InQuotationMode = FALSE;
+ for (Index = 0; Index < StringLen; Index++) {
+ if (CurrentString[Index] == L'\"') {
+ InQuotationMode = (BOOLEAN)(!InQuotationMode);
+ }
+ if (CurrentString[Index] == L' ' && !InQuotationMode) {
+ TabPos = Index + 1;
+ TabUpdatePos = Index + 1;
+ }
+ if (CurrentString[Index] == L'\\') {
+ TabUpdatePos = Index + 1;
+ }
+ }
+ if (StrStr(CurrentString + TabPos, L":") == NULL) {
+ Cwd = ShellInfoObject.NewEfiShellProtocol->GetCurDir(NULL);
+ if (Cwd != NULL) {
+ StrnCpyS(TabStr, (*BufferSize)/sizeof(CHAR16), Cwd, (*BufferSize)/sizeof(CHAR16) - 1);
+ StrCatS(TabStr, (*BufferSize)/sizeof(CHAR16), L"\\");
+ if (TabStr[StrLen(TabStr)-1] == L'\\' && *(CurrentString + TabPos) == L'\\' ) {
+ TabStr[StrLen(TabStr)-1] = CHAR_NULL;
+ }
+ StrnCatS( TabStr,
+ (*BufferSize)/sizeof(CHAR16),
+ CurrentString + TabPos,
+ StringLen - TabPos
+ );
+ } else {
+ *TabStr = CHAR_NULL;
+ StrnCatS(TabStr, (*BufferSize)/sizeof(CHAR16), CurrentString + TabPos, StringLen - TabPos);
+ }
+ } else {
+ StrnCpyS(TabStr, (*BufferSize)/sizeof(CHAR16), CurrentString + TabPos, (*BufferSize)/sizeof(CHAR16) - 1);
+ }
+ StrnCatS(TabStr, (*BufferSize)/sizeof(CHAR16), L"*", (*BufferSize)/sizeof(CHAR16) - 1 - StrLen(TabStr));
+ FoundFileList = NULL;
+ Status = ShellInfoObject.NewEfiShellProtocol->FindFiles(TabStr, &FoundFileList);
+ for ( TempStr = CurrentString
+ ; *TempStr == L' '
+ ; TempStr++); // note the ';'... empty for loop
+ //
+ // make sure we have a list before we do anything more...
+ //
+ if (EFI_ERROR (Status) || FoundFileList == NULL) {
+ InTabScrolling = FALSE;
+ TabLinePos = NULL;
+ continue;
+ } else {
+ //
+ // enumerate through the list of files
+ //
+ for ( TempPos = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(FoundFileList->Link))
+ ; !IsNull(&FoundFileList->Link, &TempPos->Link)
+ ; TempPos = (EFI_SHELL_FILE_INFO*)GetNextNode(&(FoundFileList->Link), &(TempPos->Link))
+ ){
+ //
+ // If "cd" is typed, only directory name will be auto-complete filled
+ // in either case . and .. will be removed.
+ //
+ if ((((TempStr[0] == L'c' || TempStr[0] == L'C') &&
+ (TempStr[1] == L'd' || TempStr[1] == L'D')
+ ) && ((ShellIsDirectory(TempPos->FullName) != EFI_SUCCESS)
+ ||(StrCmp(TempPos->FileName, L".") == 0)
+ ||(StrCmp(TempPos->FileName, L"..") == 0)
+ )) || ((StrCmp(TempPos->FileName, L".") == 0)
+ ||(StrCmp(TempPos->FileName, L"..") == 0))){
+ TabLinePos = TempPos;
+ TempPos = (EFI_SHELL_FILE_INFO*)(RemoveEntryList(&(TempPos->Link))->BackLink);
+ InternalFreeShellFileInfoNode(TabLinePos);
+ }
+ }
+ if (FoundFileList != NULL && !IsListEmpty(&FoundFileList->Link)) {
+ TabLinePos = (EFI_SHELL_FILE_INFO*)GetFirstNode(&FoundFileList->Link);
+ InTabScrolling = TRUE;
+ } else {
+ ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FoundFileList);
+ }
+ }
+ }
+ break;
+
+ default:
+ if (Key.UnicodeChar >= ' ') {
+ //
+ // If we are at the buffer's end, drop the key
+ //
+ if (StringLen == MaxStr - 1 && (ShellInfoObject.ViewingSettings.InsertMode || StringCurPos == StringLen)) {
+ break;
+ }
+ //
+ // If in insert mode, make space by moving each other character 1
+ // space higher in the array
+ //
+ if (ShellInfoObject.ViewingSettings.InsertMode) {
+ CopyMem(CurrentString + StringCurPos + 1, CurrentString + StringCurPos, (StringLen - StringCurPos)*sizeof(CurrentString[0]));
+ }
+
+ CurrentString[StringCurPos] = Key.UnicodeChar;
+ Update = StringCurPos;
+
+ StringCurPos += 1;
+ OutputLength = 1;
+ }
+ break;
+
+ case 0:
+ switch (Key.ScanCode) {
+ case SCAN_DELETE:
+ //
+ // Move characters behind current position one character forward
+ //
+ if (StringLen != 0) {
+ Update = StringCurPos;
+ Delete = 1;
+ CopyMem (CurrentString + StringCurPos, CurrentString + StringCurPos + 1, sizeof (CHAR16) * (StringLen - StringCurPos));
+ }
+ break;
+
+ case SCAN_UP:
+ //
+ // Prepare to print the previous command
+ //
+ NewPos = (BUFFER_LIST*)GetPreviousNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
+ if (IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link)) {
+ NewPos = (BUFFER_LIST*)GetPreviousNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
+ }
+ break;
+
+ case SCAN_DOWN:
+ //
+ // Prepare to print the next command
+ //
+ NewPos = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
+ if (NewPos == (BUFFER_LIST*)(&ShellInfoObject.ViewingSettings.CommandHistory)) {
+ NewPos = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &LinePos->Link);
+ }
+ break;
+
+ case SCAN_LEFT:
+ //
+ // Adjust current cursor position
+ //
+ if (StringCurPos != 0) {
+ --StringCurPos;
+ MoveCursorBackward (TotalColumn, &Column, &Row);
+ }
+ break;
+
+ case SCAN_RIGHT:
+ //
+ // Adjust current cursor position
+ //
+ if (StringCurPos < StringLen) {
+ ++StringCurPos;
+ MoveCursorForward (TotalColumn, TotalRow, &Column, &Row);
+ }
+ break;
+
+ case SCAN_HOME:
+ //
+ // Move current cursor position to the beginning of the command line
+ //
+ Row -= (StringCurPos + StartColumn) / TotalColumn;
+ Column = StartColumn;
+ StringCurPos = 0;
+ break;
+
+ case SCAN_END:
+ //
+ // Move current cursor position to the end of the command line
+ //
+ TailRow = Row + (StringLen - StringCurPos + Column) / TotalColumn;
+ TailColumn = (StringLen - StringCurPos + Column) % TotalColumn;
+ Row = TailRow;
+ Column = TailColumn;
+ StringCurPos = StringLen;
+ break;
+
+ case SCAN_ESC:
+ //
+ // Prepare to clear the current command line
+ //
+ CurrentString[0] = 0;
+ Update = 0;
+ Delete = StringLen;
+ Row -= (StringCurPos + StartColumn) / TotalColumn;
+ Column = StartColumn;
+ OutputLength = 0;
+ break;
+
+ case SCAN_INSERT:
+ //
+ // Toggle the SEnvInsertMode flag
+ //
+ ShellInfoObject.ViewingSettings.InsertMode = (BOOLEAN)!ShellInfoObject.ViewingSettings.InsertMode;
+ break;
+
+ case SCAN_F7:
+ //
+ // Print command history
+ //
+ PrintCommandHistory (TotalColumn, TotalRow, 4);
+ *CurrentString = CHAR_NULL;
+ Done = TRUE;
+ break;
+ }
+ }
+
+ if (Done) {
+ break;
+ }
+
+ //
+ // If we are in auto-complete mode, we are preparing to print
+ // the next file or directory name
+ //
+ if (InTabScrolling) {
+ //
+ // Adjust the column and row to the start of TAB-completion string.
+ //
+ Column = (StartColumn + TabUpdatePos) % TotalColumn;
+ Row -= (StartColumn + StringCurPos) / TotalColumn - (StartColumn + TabUpdatePos) / TotalColumn;
+ OutputLength = StrLen (TabLinePos->FileName);
+ //
+ // if the output string contains blank space, quotation marks L'\"'
+ // should be added to the output.
+ //
+ if (StrStr(TabLinePos->FileName, L" ") != NULL){
+ TabOutputStr[0] = L'\"';
+ CopyMem (TabOutputStr + 1, TabLinePos->FileName, OutputLength * sizeof (CHAR16));
+ TabOutputStr[OutputLength + 1] = L'\"';
+ TabOutputStr[OutputLength + 2] = CHAR_NULL;
+ } else {
+ CopyMem (TabOutputStr, TabLinePos->FileName, OutputLength * sizeof (CHAR16));
+ TabOutputStr[OutputLength] = CHAR_NULL;
+ }
+ OutputLength = StrLen (TabOutputStr) < MaxStr - 1 ? StrLen (TabOutputStr) : MaxStr - 1;
+ CopyMem (CurrentString + TabUpdatePos, TabOutputStr, OutputLength * sizeof (CHAR16));
+ CurrentString[TabUpdatePos + OutputLength] = CHAR_NULL;
+ StringCurPos = TabUpdatePos + OutputLength;
+ Update = TabUpdatePos;
+ if (StringLen > TabUpdatePos + OutputLength) {
+ Delete = StringLen - TabUpdatePos - OutputLength;
+ }
+ }
+
+ //
+ // If we have a new position, we are preparing to print a previous or
+ // next command.
+ //
+ if (NewPos != (BUFFER_LIST*)(&ShellInfoObject.ViewingSettings.CommandHistory)) {
+ Column = StartColumn;
+ Row -= (StringCurPos + StartColumn) / TotalColumn;
+
+ LinePos = NewPos;
+ NewPos = (BUFFER_LIST*)(&ShellInfoObject.ViewingSettings.CommandHistory);
+
+ OutputLength = StrLen (LinePos->Buffer) < MaxStr - 1 ? StrLen (LinePos->Buffer) : MaxStr - 1;
+ CopyMem (CurrentString, LinePos->Buffer, OutputLength * sizeof (CHAR16));
+ CurrentString[OutputLength] = CHAR_NULL;
+
+ StringCurPos = OutputLength;
+
+ //
+ // Draw new input string
+ //
+ Update = 0;
+ if (StringLen > OutputLength) {
+ //
+ // If old string was longer, blank its tail
+ //
+ Delete = StringLen - OutputLength;
+ }
+ }
+ //
+ // If we need to update the output do so now
+ //
+ if (Update != (UINTN) -1) {
+ ShellPrintEx ((INT32)Column, (INT32)Row, L"%s%.*s", CurrentString + Update, Delete, L"");
+ StringLen = StrLen (CurrentString);
+
+ if (Delete != 0) {
+ SetMem (CurrentString + StringLen, Delete * sizeof (CHAR16), CHAR_NULL);
+ }
+
+ if (StringCurPos > StringLen) {
+ StringCurPos = StringLen;
+ }
+
+ Update = (UINTN) -1;
+
+ //
+ // After using print to reflect newly updates, if we're not using
+ // BACKSPACE and DELETE, we need to move the cursor position forward,
+ // so adjust row and column here.
+ //
+ if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) {
+ //
+ // Calulate row and column of the tail of current string
+ //
+ TailRow = Row + (StringLen - StringCurPos + Column + OutputLength) / TotalColumn;
+ TailColumn = (StringLen - StringCurPos + Column + OutputLength) % TotalColumn;
+
+ //
+ // If the tail of string reaches screen end, screen rolls up, so if
+ // Row does not equal TailRow, Row should be decremented
+ //
+ // (if we are recalling commands using UPPER and DOWN key, and if the
+ // old command is too long to fit the screen, TailColumn must be 79.
+ //
+ if (TailColumn == 0 && TailRow >= TotalRow && Row != TailRow) {
+ Row--;
+ }
+ //
+ // Calculate the cursor position after current operation. If cursor
+ // reaches line end, update both row and column, otherwise, only
+ // column will be changed.
+ //
+ if (Column + OutputLength >= TotalColumn) {
+ SkipLength = OutputLength - (TotalColumn - Column);
+
+ Row += SkipLength / TotalColumn + 1;
+ if (Row > TotalRow - 1) {
+ Row = TotalRow - 1;
+ }
+
+ Column = SkipLength % TotalColumn;
+ } else {
+ Column += OutputLength;
+ }
+ }
+
+ Delete = 0;
+ }
+ //
+ // Set the cursor position for this key
+ //
+ gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
+ } while (!Done);
+
+ if (CurrentString != NULL && StrLen(CurrentString) > 0) {
+ //
+ // add the line to the history buffer
+ //
+ AddLineToCommandHistory(CurrentString);
+ }
+
+ FreePool (TabStr);
+ FreePool (TabOutputStr);
+ //
+ // Return the data to the caller
+ //
+ *BufferSize = StringLen * sizeof (CHAR16);
+
+ //
+ // if this was used it should be deallocated by now...
+ // prevent memory leaks...
+ //
+ if (FoundFileList != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->FreeFileList (&FoundFileList);
+ }
+ ASSERT(FoundFileList == NULL);
+
+ return Status;
+}
+
+//
+// FILE sytle interfaces for StdIn/StdOut/StdErr
+//
+EFI_FILE_PROTOCOL FileInterfaceStdIn = {
+ EFI_FILE_REVISION,
+ FileInterfaceOpenNotFound,
+ FileInterfaceNopGeneric,
+ FileInterfaceNopGeneric,
+ FileInterfaceStdInRead,
+ FileInterfaceStdInWrite,
+ FileInterfaceNopGetPosition,
+ FileInterfaceNopSetPosition,
+ FileInterfaceNopGetInfo,
+ FileInterfaceNopSetInfo,
+ FileInterfaceNopGeneric
+};
+
+EFI_FILE_PROTOCOL FileInterfaceStdOut = {
+ EFI_FILE_REVISION,
+ FileInterfaceOpenNotFound,
+ FileInterfaceNopGeneric,
+ FileInterfaceNopGeneric,
+ FileInterfaceStdOutRead,
+ FileInterfaceStdOutWrite,
+ FileInterfaceNopGetPosition,
+ FileInterfaceNopSetPosition,
+ FileInterfaceNopGetInfo,
+ FileInterfaceNopSetInfo,
+ FileInterfaceNopGeneric
+};
+
+EFI_FILE_PROTOCOL FileInterfaceStdErr = {
+ EFI_FILE_REVISION,
+ FileInterfaceOpenNotFound,
+ FileInterfaceNopGeneric,
+ FileInterfaceNopGeneric,
+ FileInterfaceStdErrRead,
+ FileInterfaceStdErrWrite,
+ FileInterfaceNopGetPosition,
+ FileInterfaceNopSetPosition,
+ FileInterfaceNopGetInfo,
+ FileInterfaceNopSetInfo,
+ FileInterfaceNopGeneric
+};
+
+EFI_FILE_PROTOCOL FileInterfaceNulFile = {
+ EFI_FILE_REVISION,
+ FileInterfaceOpenNotFound,
+ FileInterfaceNopGeneric,
+ FileInterfaceNopGeneric,
+ FileInterfaceNulRead,
+ FileInterfaceNulWrite,
+ FileInterfaceNopGetPosition,
+ FileInterfaceNopSetPosition,
+ FileInterfaceNopGetInfo,
+ FileInterfaceNopSetInfo,
+ FileInterfaceNopGeneric
+};
+
+
+
+
+//
+// This is identical to EFI_FILE_PROTOCOL except for the additional member
+// for the name.
+//
+
+typedef struct {
+ UINT64 Revision;
+ EFI_FILE_OPEN Open;
+ EFI_FILE_CLOSE Close;
+ EFI_FILE_DELETE Delete;
+ EFI_FILE_READ Read;
+ EFI_FILE_WRITE Write;
+ EFI_FILE_GET_POSITION GetPosition;
+ EFI_FILE_SET_POSITION SetPosition;
+ EFI_FILE_GET_INFO GetInfo;
+ EFI_FILE_SET_INFO SetInfo;
+ EFI_FILE_FLUSH Flush;
+ CHAR16 Name[1];
+} EFI_FILE_PROTOCOL_ENVIRONMENT;
+//ANSI compliance helper to get size of the struct.
+#define SIZE_OF_EFI_FILE_PROTOCOL_ENVIRONMENT EFI_FIELD_OFFSET (EFI_FILE_PROTOCOL_ENVIRONMENT, Name)
+
+/**
+ File style interface for Environment Variable (Close).
+
+ Frees the memory for this object.
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+
+ @retval EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceEnvClose(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ VOID* NewBuffer;
+ UINTN NewSize;
+ EFI_STATUS Status;
+ BOOLEAN Volatile;
+
+ //
+ // Most if not all UEFI commands will have an '\r\n' at the end of any output.
+ // Since the output was redirected to a variable, it does not make sense to
+ // keep this. So, before closing, strip the trailing '\r\n' from the variable
+ // if it exists.
+ //
+ NewBuffer = NULL;
+ NewSize = 0;
+
+ Status = IsVolatileEnv (((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &Volatile);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &NewSize, NewBuffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ NewBuffer = AllocateZeroPool(NewSize + sizeof(CHAR16));
+ if (NewBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &NewSize, NewBuffer);
+ }
+
+ if (!EFI_ERROR(Status) && NewBuffer != NULL) {
+
+ if (StrSize(NewBuffer) > 6)
+ {
+ if ((((CHAR16*)NewBuffer)[(StrSize(NewBuffer)/2) - 2] == CHAR_LINEFEED)
+ && (((CHAR16*)NewBuffer)[(StrSize(NewBuffer)/2) - 3] == CHAR_CARRIAGE_RETURN)) {
+ ((CHAR16*)NewBuffer)[(StrSize(NewBuffer)/2) - 3] = CHAR_NULL;
+ }
+
+ if (Volatile) {
+ Status = SHELL_SET_ENVIRONMENT_VARIABLE_V(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, StrSize(NewBuffer), NewBuffer);
+ } else {
+ Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, StrSize(NewBuffer), NewBuffer);
+ }
+ }
+ }
+
+ SHELL_FREE_NON_NULL(NewBuffer);
+ FreePool((EFI_FILE_PROTOCOL_ENVIRONMENT*)This);
+ return (Status);
+}
+
+/**
+ File style interface for Environment Variable (Delete).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+
+ @retval The return value from FileInterfaceEnvClose().
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceEnvDelete(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ SHELL_DELETE_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name);
+ return (FileInterfaceEnvClose(This));
+}
+
+/**
+ File style interface for Environment Variable (Read).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[out] Buffer The pointer to the buffer to fill.
+
+ @retval EFI_SUCCESS The data was read.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceEnvRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ return (SHELL_GET_ENVIRONMENT_VARIABLE(
+ ((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name,
+ BufferSize,
+ Buffer));
+}
+
+/**
+ File style interface for Volatile Environment Variable (Write).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to write.
+
+ @retval EFI_SUCCESS The data was read.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceEnvVolWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ VOID* NewBuffer;
+ UINTN NewSize;
+ EFI_STATUS Status;
+
+ NewBuffer = NULL;
+ NewSize = 0;
+
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &NewSize, NewBuffer);
+ if (Status == EFI_BUFFER_TOO_SMALL){
+ NewBuffer = AllocateZeroPool(NewSize + *BufferSize + sizeof(CHAR16));
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &NewSize, NewBuffer);
+ }
+ if (!EFI_ERROR(Status) && NewBuffer != NULL) {
+ while (((CHAR16*)NewBuffer)[NewSize/2] == CHAR_NULL) {
+ //
+ // We want to overwrite the CHAR_NULL
+ //
+ NewSize -= 2;
+ }
+ CopyMem((UINT8*)NewBuffer + NewSize + 2, Buffer, *BufferSize);
+ Status = SHELL_SET_ENVIRONMENT_VARIABLE_V(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, StrSize(NewBuffer), NewBuffer);
+ FreePool(NewBuffer);
+ return (Status);
+ } else {
+ SHELL_FREE_NON_NULL(NewBuffer);
+ return (SHELL_SET_ENVIRONMENT_VARIABLE_V(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, *BufferSize, Buffer));
+ }
+}
+
+
+/**
+ File style interface for Non Volatile Environment Variable (Write).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to write.
+
+ @retval EFI_SUCCESS The data was read.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceEnvNonVolWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ VOID* NewBuffer;
+ UINTN NewSize;
+ EFI_STATUS Status;
+
+ NewBuffer = NULL;
+ NewSize = 0;
+
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &NewSize, NewBuffer);
+ if (Status == EFI_BUFFER_TOO_SMALL){
+ NewBuffer = AllocateZeroPool(NewSize + *BufferSize);
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name, &NewSize, NewBuffer);
+ }
+ if (!EFI_ERROR(Status)) {
+ CopyMem((UINT8*)NewBuffer + NewSize, Buffer, *BufferSize);
+ return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(
+ ((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name,
+ NewSize + *BufferSize,
+ NewBuffer));
+ } else {
+ return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(
+ ((EFI_FILE_PROTOCOL_ENVIRONMENT*)This)->Name,
+ *BufferSize,
+ Buffer));
+ }
+}
+
+/**
+ Creates a EFI_FILE_PROTOCOL (almost) object for using to access
+ environment variables through file operations.
+
+ @param EnvName The name of the Environment Variable to be operated on.
+
+ @retval NULL Memory could not be allocated.
+ @return other a pointer to an EFI_FILE_PROTOCOL structure
+**/
+EFI_FILE_PROTOCOL*
+EFIAPI
+CreateFileInterfaceEnv(
+ IN CONST CHAR16 *EnvName
+ )
+{
+ EFI_STATUS Status;
+ EFI_FILE_PROTOCOL_ENVIRONMENT *EnvFileInterface;
+ UINTN EnvNameSize;
+ BOOLEAN Volatile;
+
+ if (EnvName == NULL) {
+ return (NULL);
+ }
+
+ Status = IsVolatileEnv (EnvName, &Volatile);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ //
+ // Get some memory
+ //
+ EnvNameSize = StrSize(EnvName);
+ EnvFileInterface = AllocateZeroPool(sizeof(EFI_FILE_PROTOCOL_ENVIRONMENT)+EnvNameSize);
+ if (EnvFileInterface == NULL){
+ return (NULL);
+ }
+
+ //
+ // Assign the generic members
+ //
+ EnvFileInterface->Revision = EFI_FILE_REVISION;
+ EnvFileInterface->Open = FileInterfaceOpenNotFound;
+ EnvFileInterface->Close = FileInterfaceEnvClose;
+ EnvFileInterface->GetPosition = FileInterfaceNopGetPosition;
+ EnvFileInterface->SetPosition = FileInterfaceNopSetPosition;
+ EnvFileInterface->GetInfo = FileInterfaceNopGetInfo;
+ EnvFileInterface->SetInfo = FileInterfaceNopSetInfo;
+ EnvFileInterface->Flush = FileInterfaceNopGeneric;
+ EnvFileInterface->Delete = FileInterfaceEnvDelete;
+ EnvFileInterface->Read = FileInterfaceEnvRead;
+
+ CopyMem(EnvFileInterface->Name, EnvName, EnvNameSize);
+
+ //
+ // Assign the different members for Volatile and Non-Volatile variables
+ //
+ if (Volatile) {
+ EnvFileInterface->Write = FileInterfaceEnvVolWrite;
+ } else {
+ EnvFileInterface->Write = FileInterfaceEnvNonVolWrite;
+ }
+ return ((EFI_FILE_PROTOCOL *)EnvFileInterface);
+}
+
+/**
+ Move the cursor position one character backward.
+
+ @param[in] LineLength Length of a line. Get it by calling QueryMode
+ @param[in, out] Column Current column of the cursor position
+ @param[in, out] Row Current row of the cursor position
+**/
+VOID
+EFIAPI
+MoveCursorBackward (
+ IN UINTN LineLength,
+ IN OUT UINTN *Column,
+ IN OUT UINTN *Row
+ )
+{
+ //
+ // If current column is 0, move to the last column of the previous line,
+ // otherwise, just decrement column.
+ //
+ if (*Column == 0) {
+ *Column = LineLength - 1;
+ if (*Row > 0) {
+ (*Row)--;
+ }
+ return;
+ }
+ (*Column)--;
+}
+
+/**
+ Move the cursor position one character forward.
+
+ @param[in] LineLength Length of a line.
+ @param[in] TotalRow Total row of a screen
+ @param[in, out] Column Current column of the cursor position
+ @param[in, out] Row Current row of the cursor position
+**/
+VOID
+EFIAPI
+MoveCursorForward (
+ IN UINTN LineLength,
+ IN UINTN TotalRow,
+ IN OUT UINTN *Column,
+ IN OUT UINTN *Row
+ )
+{
+ //
+ // Increment Column.
+ // If this puts column past the end of the line, move to first column
+ // of the next row.
+ //
+ (*Column)++;
+ if (*Column >= LineLength) {
+ (*Column) = 0;
+ if ((*Row) < TotalRow - 1) {
+ (*Row)++;
+ }
+ }
+}
+
+/**
+ Prints out each previously typed command in the command list history log.
+
+ When each screen is full it will pause for a key before continuing.
+
+ @param[in] TotalCols How many columns are on the screen
+ @param[in] TotalRows How many rows are on the screen
+ @param[in] StartColumn which column to start at
+**/
+VOID
+EFIAPI
+PrintCommandHistory (
+ IN CONST UINTN TotalCols,
+ IN CONST UINTN TotalRows,
+ IN CONST UINTN StartColumn
+ )
+{
+ BUFFER_LIST *Node;
+ UINTN Index;
+ UINTN LineNumber;
+ UINTN LineCount;
+
+ ShellPrintEx (-1, -1, L"\n");
+ Index = 0;
+ LineNumber = 0;
+ //
+ // go through history list...
+ //
+ for ( Node = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)
+ ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link)
+ ; Node = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link)
+ ){
+ Index++;
+ LineCount = ((StrLen (Node->Buffer) + StartColumn + 1) / TotalCols) + 1;
+
+ if (LineNumber + LineCount >= TotalRows) {
+ ShellPromptForResponseHii(
+ ShellPromptResponseTypeEnterContinue,
+ STRING_TOKEN (STR_SHELL_ENTER_TO_CONT),
+ ShellInfoObject.HiiHandle,
+ NULL
+ );
+ LineNumber = 0;
+ }
+ ShellPrintEx (-1, -1, L"%2d. %s\n", Index, Node->Buffer);
+ LineNumber += LineCount;
+ }
+}
+
+
+
+
+
+
+//
+// This is identical to EFI_FILE_PROTOCOL except for the additional members
+// for the buffer, size, and position.
+//
+
+typedef struct {
+ UINT64 Revision;
+ EFI_FILE_OPEN Open;
+ EFI_FILE_CLOSE Close;
+ EFI_FILE_DELETE Delete;
+ EFI_FILE_READ Read;
+ EFI_FILE_WRITE Write;
+ EFI_FILE_GET_POSITION GetPosition;
+ EFI_FILE_SET_POSITION SetPosition;
+ EFI_FILE_GET_INFO GetInfo;
+ EFI_FILE_SET_INFO SetInfo;
+ EFI_FILE_FLUSH Flush;
+ VOID *Buffer;
+ UINT64 Position;
+ UINT64 BufferSize;
+ BOOLEAN Unicode;
+ UINT64 FileSize;
+} EFI_FILE_PROTOCOL_MEM;
+
+/**
+ File style interface for Mem (SetPosition).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[out] Position The position to set.
+
+ @retval EFI_SUCCESS The position was successfully changed.
+ @retval EFI_INVALID_PARAMETER The Position was invalid.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceMemSetPosition(
+ IN EFI_FILE_PROTOCOL *This,
+ OUT UINT64 Position
+ )
+{
+ if (Position <= ((EFI_FILE_PROTOCOL_MEM*)This)->FileSize) {
+ ((EFI_FILE_PROTOCOL_MEM*)This)->Position = Position;
+ return (EFI_SUCCESS);
+ } else {
+ return (EFI_INVALID_PARAMETER);
+ }
+}
+
+/**
+ File style interface for Mem (GetPosition).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[out] Position The pointer to the position.
+
+ @retval EFI_SUCCESS The position was retrieved.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceMemGetPosition(
+ IN EFI_FILE_PROTOCOL *This,
+ OUT UINT64 *Position
+ )
+{
+ *Position = ((EFI_FILE_PROTOCOL_MEM*)This)->Position;
+ return (EFI_SUCCESS);
+}
+
+/**
+ File style interface for Mem (Write).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to write.
+
+ @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources.
+ @retval EFI_SUCCESS The data was written.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceMemWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ CHAR8 *AsciiBuffer;
+ EFI_FILE_PROTOCOL_MEM *MemFile;
+
+ MemFile = (EFI_FILE_PROTOCOL_MEM *) This;
+ if (MemFile->Unicode) {
+ //
+ // Unicode
+ //
+ if ((UINTN)(MemFile->Position + (*BufferSize)) > (UINTN)(MemFile->BufferSize)) {
+ MemFile->Buffer = ReallocatePool((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer);
+ MemFile->BufferSize += (*BufferSize) + MEM_WRITE_REALLOC_OVERHEAD;
+ }
+ CopyMem(((UINT8*)MemFile->Buffer) + MemFile->Position, Buffer, *BufferSize);
+ MemFile->Position += (*BufferSize);
+ MemFile->FileSize = MemFile->Position;
+ return (EFI_SUCCESS);
+ } else {
+ //
+ // Ascii
+ //
+ AsciiBuffer = AllocateZeroPool(*BufferSize);
+ if (AsciiBuffer == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ AsciiSPrint(AsciiBuffer, *BufferSize, "%S", Buffer);
+ if ((UINTN)(MemFile->Position + AsciiStrSize(AsciiBuffer)) > (UINTN)(MemFile->BufferSize)) {
+ MemFile->Buffer = ReallocatePool((UINTN)(MemFile->BufferSize), (UINTN)(MemFile->BufferSize) + AsciiStrSize(AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD, MemFile->Buffer);
+ MemFile->BufferSize += AsciiStrSize(AsciiBuffer) + MEM_WRITE_REALLOC_OVERHEAD;
+ }
+ CopyMem(((UINT8*)MemFile->Buffer) + MemFile->Position, AsciiBuffer, AsciiStrSize(AsciiBuffer));
+ MemFile->Position += (*BufferSize / sizeof(CHAR16));
+ MemFile->FileSize = MemFile->Position;
+ FreePool(AsciiBuffer);
+ return (EFI_SUCCESS);
+ }
+}
+
+/**
+ File style interface for Mem (Read).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to fill.
+
+ @retval EFI_SUCCESS The data was read.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceMemRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_FILE_PROTOCOL_MEM *MemFile;
+
+ MemFile = (EFI_FILE_PROTOCOL_MEM *) This;
+ if (*BufferSize > (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position))) {
+ (*BufferSize) = (UINTN)((MemFile->FileSize) - (UINTN)(MemFile->Position));
+ }
+ CopyMem(Buffer, ((UINT8*)MemFile->Buffer) + MemFile->Position, (*BufferSize));
+ MemFile->Position = MemFile->Position + (*BufferSize);
+ return (EFI_SUCCESS);
+}
+
+/**
+ File style interface for Mem (Close).
+
+ Frees all memory associated with this object.
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+
+ @retval EFI_SUCCESS The 'file' was closed.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceMemClose(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ SHELL_FREE_NON_NULL(((EFI_FILE_PROTOCOL_MEM*)This)->Buffer);
+ SHELL_FREE_NON_NULL(This);
+ return (EFI_SUCCESS);
+}
+
+/**
+ Creates a EFI_FILE_PROTOCOL (almost) object for using to access
+ a file entirely in memory through file operations.
+
+ @param[in] Unicode Boolean value with TRUE for Unicode and FALSE for Ascii.
+
+ @retval NULL Memory could not be allocated.
+ @return other A pointer to an EFI_FILE_PROTOCOL structure.
+**/
+EFI_FILE_PROTOCOL*
+EFIAPI
+CreateFileInterfaceMem(
+ IN CONST BOOLEAN Unicode
+ )
+{
+ EFI_FILE_PROTOCOL_MEM *FileInterface;
+
+ //
+ // Get some memory
+ //
+ FileInterface = AllocateZeroPool(sizeof(EFI_FILE_PROTOCOL_MEM));
+ if (FileInterface == NULL){
+ return (NULL);
+ }
+
+ //
+ // Assign the generic members
+ //
+ FileInterface->Revision = EFI_FILE_REVISION;
+ FileInterface->Open = FileInterfaceOpenNotFound;
+ FileInterface->Close = FileInterfaceMemClose;
+ FileInterface->GetPosition = FileInterfaceMemGetPosition;
+ FileInterface->SetPosition = FileInterfaceMemSetPosition;
+ FileInterface->GetInfo = FileInterfaceNopGetInfo;
+ FileInterface->SetInfo = FileInterfaceNopSetInfo;
+ FileInterface->Flush = FileInterfaceNopGeneric;
+ FileInterface->Delete = FileInterfaceNopGeneric;
+ FileInterface->Read = FileInterfaceMemRead;
+ FileInterface->Write = FileInterfaceMemWrite;
+ FileInterface->Unicode = Unicode;
+
+ ASSERT(FileInterface->Buffer == NULL);
+ ASSERT(FileInterface->BufferSize == 0);
+ ASSERT(FileInterface->Position == 0);
+
+ if (Unicode) {
+ FileInterface->Buffer = AllocateZeroPool(sizeof(gUnicodeFileTag));
+ if (FileInterface->Buffer == NULL) {
+ FreePool (FileInterface);
+ return NULL;
+ }
+ *((CHAR16 *) (FileInterface->Buffer)) = EFI_UNICODE_BYTE_ORDER_MARK;
+ FileInterface->BufferSize = 2;
+ FileInterface->Position = 2;
+ }
+
+ return ((EFI_FILE_PROTOCOL *)FileInterface);
+}
+
+typedef struct {
+ UINT64 Revision;
+ EFI_FILE_OPEN Open;
+ EFI_FILE_CLOSE Close;
+ EFI_FILE_DELETE Delete;
+ EFI_FILE_READ Read;
+ EFI_FILE_WRITE Write;
+ EFI_FILE_GET_POSITION GetPosition;
+ EFI_FILE_SET_POSITION SetPosition;
+ EFI_FILE_GET_INFO GetInfo;
+ EFI_FILE_SET_INFO SetInfo;
+ EFI_FILE_FLUSH Flush;
+ BOOLEAN Unicode;
+ EFI_FILE_PROTOCOL *Orig;
+} EFI_FILE_PROTOCOL_FILE;
+
+/**
+ Set a files current position
+
+ @param This Protocol instance pointer.
+ @param Position Byte position from the start of the file.
+
+ @retval EFI_SUCCESS Data was written.
+ @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open.
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileSetPosition(
+ IN EFI_FILE_PROTOCOL *This,
+ IN UINT64 Position
+ )
+{
+ return ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->SetPosition(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, Position);
+}
+
+/**
+ Get a file's current position
+
+ @param This Protocol instance pointer.
+ @param Position Byte position from the start of the file.
+
+ @retval EFI_SUCCESS Data was written.
+ @retval EFI_UNSUPPORTED Seek request for non-zero is not valid on open..
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileGetPosition(
+ IN EFI_FILE_PROTOCOL *This,
+ OUT UINT64 *Position
+ )
+{
+ return ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->GetPosition(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, Position);
+}
+
+/**
+ Get information about a file.
+
+ @param This Protocol instance pointer.
+ @param InformationType Type of information to return in Buffer.
+ @param BufferSize On input size of buffer, on output amount of data in buffer.
+ @param Buffer The buffer to return data.
+
+ @retval EFI_SUCCESS Data was returned.
+ @retval EFI_UNSUPPORT InformationType is not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED The device is write protected.
+ @retval EFI_ACCESS_DENIED The file was open for read only.
+ @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in BufferSize.
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileGetInfo(
+ IN EFI_FILE_PROTOCOL *This,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ return ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->GetInfo(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, InformationType, BufferSize, Buffer);
+}
+
+/**
+ Set information about a file
+
+ @param This Protocol instance pointer.
+ @param InformationType Type of information in Buffer.
+ @param BufferSize Size of buffer.
+ @param Buffer The data to write.
+
+ @retval EFI_SUCCESS Data was returned.
+ @retval EFI_UNSUPPORT InformationType is not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED The device is write protected.
+ @retval EFI_ACCESS_DENIED The file was open for read only.
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileSetInfo(
+ IN EFI_FILE_PROTOCOL *This,
+ IN EFI_GUID *InformationType,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ return ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->SetInfo(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, InformationType, BufferSize, Buffer);
+}
+
+/**
+ Flush data back for the file handle.
+
+ @param This Protocol instance pointer.
+
+ @retval EFI_SUCCESS Data was written.
+ @retval EFI_UNSUPPORT Writes to Open directory are not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED The device is write protected.
+ @retval EFI_ACCESS_DENIED The file was open for read only.
+ @retval EFI_VOLUME_FULL The volume is full.
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileFlush(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ return ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Flush(((EFI_FILE_PROTOCOL_FILE*)This)->Orig);
+}
+
+/**
+ Read data from the file.
+
+ @param This Protocol instance pointer.
+ @param BufferSize On input size of buffer, on output amount of data in buffer.
+ @param Buffer The buffer in which data is read.
+
+ @retval EFI_SUCCESS Data was read.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_BUFFER_TO_SMALL BufferSize is too small. BufferSize contains required size.
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileRead(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ CHAR8 *AsciiStrBuffer;
+ CHAR16 *UscStrBuffer;
+ UINTN Size;
+ UINTN CharNum;
+ EFI_STATUS Status;
+ if (((EFI_FILE_PROTOCOL_FILE*)This)->Unicode) {
+ //
+ // Unicode
+ //
+ return (((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Read(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, BufferSize, Buffer));
+ } else {
+ //
+ // Ascii
+ //
+ Size = (*BufferSize) / sizeof(CHAR16);
+ AsciiStrBuffer = AllocateZeroPool(Size + sizeof(CHAR8));
+ if (AsciiStrBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ UscStrBuffer = AllocateZeroPool(*BufferSize + sizeof(CHAR16));
+ if (UscStrBuffer== NULL) {
+ SHELL_FREE_NON_NULL(AsciiStrBuffer);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = (((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Read(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, &Size, AsciiStrBuffer));
+ if (!EFI_ERROR(Status)) {
+ CharNum = UnicodeSPrint(UscStrBuffer, *BufferSize + sizeof(CHAR16), L"%a", AsciiStrBuffer);
+ if (CharNum == Size) {
+ CopyMem (Buffer, UscStrBuffer, *BufferSize);
+ } else {
+ Status = EFI_UNSUPPORTED;
+ }
+ }
+ SHELL_FREE_NON_NULL(AsciiStrBuffer);
+ SHELL_FREE_NON_NULL(UscStrBuffer);
+ return (Status);
+ }
+}
+
+/**
+ Opens a new file relative to the source file's location.
+
+ @param[in] This The protocol instance pointer.
+ @param[out] NewHandle Returns File Handle for FileName.
+ @param[in] FileName Null terminated string. "\", ".", and ".." are supported.
+ @param[in] OpenMode Open mode for file.
+ @param[in] Attributes Only used for EFI_FILE_MODE_CREATE.
+
+ @retval EFI_SUCCESS The device was opened.
+ @retval EFI_NOT_FOUND The specified file could not be found on the device.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_MEDIA_CHANGED The media has changed.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of resources.
+ @retval EFI_VOLUME_FULL The volume is full.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileOpen (
+ IN EFI_FILE_PROTOCOL *This,
+ OUT EFI_FILE_PROTOCOL **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes
+ )
+{
+ return ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Open(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, NewHandle, FileName, OpenMode, Attributes);
+}
+
+/**
+ Close and delete the file handle.
+
+ @param This Protocol instance pointer.
+
+ @retval EFI_SUCCESS The device was opened.
+ @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
+
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileDelete(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ Status = ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Delete(((EFI_FILE_PROTOCOL_FILE*)This)->Orig);
+ FreePool(This);
+ return (Status);
+}
+
+/**
+ File style interface for File (Close).
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+
+ @retval EFI_SUCCESS The file was closed.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileClose(
+ IN EFI_FILE_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ Status = ((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Close(((EFI_FILE_PROTOCOL_FILE*)This)->Orig);
+ FreePool(This);
+ return (Status);
+}
+
+/**
+ File style interface for File (Write).
+
+ If the file was opened with ASCII mode the data will be processed through
+ AsciiSPrint before writing.
+
+ @param[in] This The pointer to the EFI_FILE_PROTOCOL object.
+ @param[in, out] BufferSize Size in bytes of Buffer.
+ @param[in] Buffer The pointer to the buffer to write.
+
+ @retval EFI_SUCCESS The data was written.
+**/
+EFI_STATUS
+EFIAPI
+FileInterfaceFileWrite(
+ IN EFI_FILE_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ CHAR8 *AsciiBuffer;
+ UINTN Size;
+ EFI_STATUS Status;
+ if (((EFI_FILE_PROTOCOL_FILE*)This)->Unicode) {
+ //
+ // Unicode
+ //
+ return (((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Write(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, BufferSize, Buffer));
+ } else {
+ //
+ // Ascii
+ //
+ AsciiBuffer = AllocateZeroPool(*BufferSize);
+ AsciiSPrint(AsciiBuffer, *BufferSize, "%S", Buffer);
+ Size = AsciiStrSize(AsciiBuffer) - 1; // (we dont need the null terminator)
+ Status = (((EFI_FILE_PROTOCOL_FILE*)This)->Orig->Write(((EFI_FILE_PROTOCOL_FILE*)This)->Orig, &Size, AsciiBuffer));
+ FreePool(AsciiBuffer);
+ return (Status);
+ }
+}
+
+/**
+ Create a file interface with unicode information.
+
+ This will create a new EFI_FILE_PROTOCOL identical to the Templace
+ except that the new one has Unicode and Ascii knowledge.
+
+ @param[in] Template A pointer to the EFI_FILE_PROTOCOL object.
+ @param[in] Unicode TRUE for UCS-2, FALSE for ASCII.
+
+ @return a new EFI_FILE_PROTOCOL object to be used instead of the template.
+**/
+EFI_FILE_PROTOCOL*
+CreateFileInterfaceFile(
+ IN CONST EFI_FILE_PROTOCOL *Template,
+ IN CONST BOOLEAN Unicode
+ )
+{
+ EFI_FILE_PROTOCOL_FILE *NewOne;
+
+ NewOne = AllocateZeroPool(sizeof(EFI_FILE_PROTOCOL_FILE));
+ if (NewOne == NULL) {
+ return (NULL);
+ }
+ CopyMem(NewOne, Template, sizeof(EFI_FILE_PROTOCOL_FILE));
+ NewOne->Orig = (EFI_FILE_PROTOCOL *)Template;
+ NewOne->Unicode = Unicode;
+ NewOne->Open = FileInterfaceFileOpen;
+ NewOne->Close = FileInterfaceFileClose;
+ NewOne->Delete = FileInterfaceFileDelete;
+ NewOne->Read = FileInterfaceFileRead;
+ NewOne->Write = FileInterfaceFileWrite;
+ NewOne->GetPosition = FileInterfaceFileGetPosition;
+ NewOne->SetPosition = FileInterfaceFileSetPosition;
+ NewOne->GetInfo = FileInterfaceFileGetInfo;
+ NewOne->SetInfo = FileInterfaceFileSetInfo;
+ NewOne->Flush = FileInterfaceFileFlush;
+
+ return ((EFI_FILE_PROTOCOL *)NewOne);
+}
diff --git a/Core/ShellPkg/Application/Shell/FileHandleWrappers.h b/Core/ShellPkg/Application/Shell/FileHandleWrappers.h
new file mode 100644
index 0000000000..af133b0963
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/FileHandleWrappers.h
@@ -0,0 +1,95 @@
+/** @file
+ EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables, StdIn, StdOut, StdErr, etc...)
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_FILE_HANDLE_WRAPPERS_HEADER_
+#define _SHELL_FILE_HANDLE_WRAPPERS_HEADER_
+
+typedef struct {
+ LIST_ENTRY Link;
+ CHAR16* Buffer;
+} SHELL_LINE_LIST;
+
+typedef struct {
+ UINTN LogCount;
+ SHELL_LINE_LIST *Log;
+} SHELL_LINE_LOG;
+
+///
+/// FILE sytle interfaces for StdIn.
+///
+extern EFI_FILE_PROTOCOL FileInterfaceStdIn;
+
+///
+/// FILE sytle interfaces for StdOut.
+///
+extern EFI_FILE_PROTOCOL FileInterfaceStdOut;
+
+///
+/// FILE sytle interfaces for StdErr.
+///
+extern EFI_FILE_PROTOCOL FileInterfaceStdErr;
+
+///
+/// FILE style interface for NUL file.
+///
+extern EFI_FILE_PROTOCOL FileInterfaceNulFile;
+
+/**
+ Creates a EFI_FILE_PROTOCOL (almost) object for using to access
+ environment variables through file operations.
+
+ @param EnvName The name of the Environment Variable to be operated on.
+
+ @retval NULL Memory could not be allocated.
+ @return other a pointer to an EFI_FILE_PROTOCOL structure
+**/
+EFI_FILE_PROTOCOL*
+EFIAPI
+CreateFileInterfaceEnv(
+ CONST CHAR16 *EnvName
+ );
+
+/**
+ Creates a EFI_FILE_PROTOCOL (almost) object for using to access
+ a file entirely in memory through file operations.
+
+ @param[in] Unicode TRUE if the data is UNICODE, FALSE otherwise.
+
+ @retval NULL Memory could not be allocated.
+ @return other a pointer to an EFI_FILE_PROTOCOL structure
+**/
+EFI_FILE_PROTOCOL*
+EFIAPI
+CreateFileInterfaceMem(
+ IN CONST BOOLEAN Unicode
+ );
+
+/**
+ Creates a EFI_FILE_PROTOCOL (almost) object for using to access
+ a file entirely with unicode awareness through file operations.
+
+ @param[in] Template The pointer to the handle to start with.
+ @param[in] Unicode TRUE if the data is UNICODE, FALSE otherwise.
+
+ @retval NULL Memory could not be allocated.
+ @return other a pointer to an EFI_FILE_PROTOCOL structure
+**/
+EFI_FILE_PROTOCOL*
+CreateFileInterfaceFile(
+ IN CONST EFI_FILE_PROTOCOL *Template,
+ IN CONST BOOLEAN Unicode
+ );
+
+#endif //_SHELL_FILE_HANDLE_WRAPPERS_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/Shell.c b/Core/ShellPkg/Application/Shell/Shell.c
new file mode 100644
index 0000000000..3080a1644f
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/Shell.c
@@ -0,0 +1,3131 @@
+/** @file
+ This is THE shell (application)
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+//
+// Initialize the global structure
+//
+SHELL_INFO ShellInfoObject = {
+ NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ {
+ {{
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }},
+ 0,
+ NULL,
+ NULL
+ },
+ {{NULL, NULL}, NULL},
+ {
+ {{NULL, NULL}, NULL},
+ 0,
+ 0,
+ TRUE
+ },
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ {{NULL, NULL}, NULL, NULL},
+ {{NULL, NULL}, NULL, NULL},
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ FALSE
+};
+
+STATIC CONST CHAR16 mScriptExtension[] = L".NSH";
+STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
+STATIC CONST CHAR16 mStartupScript[] = L"startup.nsh";
+CONST CHAR16 mNoNestingEnvVarName[] = L"nonesting";
+CONST CHAR16 mNoNestingTrue[] = L"True";
+CONST CHAR16 mNoNestingFalse[] = L"False";
+
+/**
+ Cleans off leading and trailing spaces and tabs.
+
+ @param[in] String pointer to the string to trim them off.
+**/
+EFI_STATUS
+EFIAPI
+TrimSpaces(
+ IN CHAR16 **String
+ )
+{
+ ASSERT(String != NULL);
+ ASSERT(*String!= NULL);
+ //
+ // Remove any spaces and tabs at the beginning of the (*String).
+ //
+ while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {
+ CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));
+ }
+
+ //
+ // Remove any spaces and tabs at the end of the (*String).
+ //
+ while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {
+ (*String)[StrLen((*String))-1] = CHAR_NULL;
+ }
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Parse for the next instance of one string within another string. Can optionally make sure that
+ the string was not escaped (^ character) per the shell specification.
+
+ @param[in] SourceString The string to search within
+ @param[in] FindString The string to look for
+ @param[in] CheckForEscapeCharacter TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
+**/
+CHAR16*
+EFIAPI
+FindNextInstance(
+ IN CONST CHAR16 *SourceString,
+ IN CONST CHAR16 *FindString,
+ IN CONST BOOLEAN CheckForEscapeCharacter
+ )
+{
+ CHAR16 *Temp;
+ if (SourceString == NULL) {
+ return (NULL);
+ }
+ Temp = StrStr(SourceString, FindString);
+
+ //
+ // If nothing found, or we don't care about escape characters
+ //
+ if (Temp == NULL || !CheckForEscapeCharacter) {
+ return (Temp);
+ }
+
+ //
+ // If we found an escaped character, try again on the remainder of the string
+ //
+ if ((Temp > (SourceString)) && *(Temp-1) == L'^') {
+ return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);
+ }
+
+ //
+ // we found the right character
+ //
+ return (Temp);
+}
+
+/**
+ Check whether the string between a pair of % is a valid environment variable name.
+
+ @param[in] BeginPercent pointer to the first percent.
+ @param[in] EndPercent pointer to the last percent.
+
+ @retval TRUE is a valid environment variable name.
+ @retval FALSE is NOT a valid environment variable name.
+**/
+BOOLEAN
+IsValidEnvironmentVariableName(
+ IN CONST CHAR16 *BeginPercent,
+ IN CONST CHAR16 *EndPercent
+ )
+{
+ CONST CHAR16 *Walker;
+
+ Walker = NULL;
+
+ ASSERT (BeginPercent != NULL);
+ ASSERT (EndPercent != NULL);
+ ASSERT (BeginPercent < EndPercent);
+
+ if ((BeginPercent + 1) == EndPercent) {
+ return FALSE;
+ }
+
+ for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {
+ if (
+ (*Walker >= L'0' && *Walker <= L'9') ||
+ (*Walker >= L'A' && *Walker <= L'Z') ||
+ (*Walker >= L'a' && *Walker <= L'z') ||
+ (*Walker == L'_')
+ ) {
+ if (Walker == BeginPercent + 1 && (*Walker >= L'0' && *Walker <= L'9')) {
+ return FALSE;
+ } else {
+ continue;
+ }
+ } else {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ Determine if a command line contains a split operation
+
+ @param[in] CmdLine The command line to parse.
+
+ @retval TRUE CmdLine has a valid split.
+ @retval FALSE CmdLine does not have a valid split.
+**/
+BOOLEAN
+EFIAPI
+ContainsSplit(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ CONST CHAR16 *TempSpot;
+ CONST CHAR16 *FirstQuote;
+ CONST CHAR16 *SecondQuote;
+
+ FirstQuote = FindNextInstance (CmdLine, L"\"", TRUE);
+ SecondQuote = NULL;
+ TempSpot = ShellFindFirstCharacter(CmdLine, L"|", TRUE);
+
+ if (FirstQuote == NULL ||
+ TempSpot == NULL ||
+ TempSpot == CHAR_NULL ||
+ FirstQuote > TempSpot
+ ) {
+ return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
+ }
+
+ while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {
+ if (FirstQuote == NULL || FirstQuote > TempSpot) {
+ break;
+ }
+ SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);
+ if (SecondQuote == NULL) {
+ break;
+ }
+ if (SecondQuote < TempSpot) {
+ FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
+ continue;
+ } else {
+ FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
+ TempSpot = ShellFindFirstCharacter(TempSpot + 1, L"|", TRUE);
+ continue;
+ }
+ }
+
+ return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
+}
+
+/**
+ Function to start monitoring for CTRL-S using SimpleTextInputEx. This
+ feature's enabled state was not known when the shell initially launched.
+
+ @retval EFI_SUCCESS The feature is enabled.
+ @retval EFI_OUT_OF_RESOURCES There is not enough memory available.
+**/
+EFI_STATUS
+EFIAPI
+InternalEfiShellStartCtrlSMonitor(
+ VOID
+ )
+{
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
+ EFI_KEY_DATA KeyData;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol(
+ gST->ConsoleInHandle,
+ &gEfiSimpleTextInputExProtocolGuid,
+ (VOID**)&SimpleEx,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_SHELL_NO_IN_EX),
+ ShellInfoObject.HiiHandle);
+ return (EFI_SUCCESS);
+ }
+
+ KeyData.KeyState.KeyToggleState = 0;
+ KeyData.Key.ScanCode = 0;
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
+ KeyData.Key.UnicodeChar = L's';
+
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlSNotifyHandle1);
+
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
+ if (!EFI_ERROR(Status)) {
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlSNotifyHandle2);
+ }
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
+ KeyData.Key.UnicodeChar = 19;
+
+ if (!EFI_ERROR(Status)) {
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlSNotifyHandle3);
+ }
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
+ if (!EFI_ERROR(Status)) {
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlSNotifyHandle4);
+ }
+ return (Status);
+}
+
+
+
+/**
+ The entry point for the application.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+UefiMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *TempString;
+ UINTN Size;
+ EFI_HANDLE ConInHandle;
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn;
+ SPLIT_LIST *Split;
+
+ if (PcdGet8(PcdShellSupportLevel) > 3) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ //
+ // Clear the screen
+ //
+ Status = gST->ConOut->ClearScreen(gST->ConOut);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ //
+ // Populate the global structure from PCDs
+ //
+ ShellInfoObject.ImageDevPath = NULL;
+ ShellInfoObject.FileDevPath = NULL;
+ ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
+ ShellInfoObject.ViewingSettings.InsertMode = PcdGetBool(PcdShellInsertModeDefault);
+ ShellInfoObject.LogScreenCount = PcdGet8 (PcdShellScreenLogCount );
+
+ //
+ // verify we dont allow for spec violation
+ //
+ ASSERT(ShellInfoObject.LogScreenCount >= 3);
+
+ //
+ // Initialize the LIST ENTRY objects...
+ //
+ InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
+ InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
+ InitializeListHead(&ShellInfoObject.SplitList.Link);
+
+ //
+ // Check PCDs for optional features that are not implemented yet.
+ //
+ if ( PcdGetBool(PcdShellSupportOldProtocols)
+ || !FeaturePcdGet(PcdShellRequireHiiPlatform)
+ || FeaturePcdGet(PcdShellSupportFrameworkHii)
+ ) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ //
+ // turn off the watchdog timer
+ //
+ gBS->SetWatchdogTimer (0, 0, 0, NULL);
+
+ //
+ // install our console logger. This will keep a log of the output for back-browsing
+ //
+ Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
+ if (!EFI_ERROR(Status)) {
+ //
+ // Enable the cursor to be visible
+ //
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+
+ //
+ // If supporting EFI 1.1 we need to install HII protocol
+ // only do this if PcdShellRequireHiiPlatform == FALSE
+ //
+ // remove EFI_UNSUPPORTED check above when complete.
+ ///@todo add support for Framework HII
+
+ //
+ // install our (solitary) HII package
+ //
+ ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
+ if (ShellInfoObject.HiiHandle == NULL) {
+ if (PcdGetBool(PcdShellSupportFrameworkHii)) {
+ ///@todo Add our package into Framework HII
+ }
+ if (ShellInfoObject.HiiHandle == NULL) {
+ Status = EFI_NOT_STARTED;
+ goto FreeResources;
+ }
+ }
+
+ //
+ // create and install the EfiShellParametersProtocol
+ //
+ Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
+ ASSERT_EFI_ERROR(Status);
+ ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
+
+ //
+ // create and install the EfiShellProtocol
+ //
+ Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
+ ASSERT_EFI_ERROR(Status);
+ ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
+
+ //
+ // Now initialize the shell library (it requires Shell Parameters protocol)
+ //
+ Status = ShellInitialize();
+ ASSERT_EFI_ERROR(Status);
+
+ Status = CommandInit();
+ ASSERT_EFI_ERROR(Status);
+
+ Status = ShellInitEnvVarList ();
+
+ //
+ // Check the command line
+ //
+ Status = ProcessCommandLine ();
+ if (EFI_ERROR (Status)) {
+ goto FreeResources;
+ }
+
+ //
+ // If shell support level is >= 1 create the mappings and paths
+ //
+ if (PcdGet8(PcdShellSupportLevel) >= 1) {
+ Status = ShellCommandCreateInitialMappingsAndPaths();
+ }
+
+ //
+ // Set the environment variable for nesting support
+ //
+ Size = 0;
+ TempString = NULL;
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
+ //
+ // No change. require nesting in Shell Protocol Execute()
+ //
+ StrnCatGrow(&TempString,
+ &Size,
+ L"False",
+ 0);
+ } else {
+ StrnCatGrow(&TempString,
+ &Size,
+ mNoNestingTrue,
+ 0);
+ }
+ Status = InternalEfiShellSetEnv(mNoNestingEnvVarName, TempString, TRUE);
+ SHELL_FREE_NON_NULL(TempString);
+ Size = 0;
+
+ //
+ // save the device path for the loaded image and the device path for the filepath (under loaded image)
+ // These are where to look for the startup.nsh file
+ //
+ Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
+ ASSERT_EFI_ERROR(Status);
+
+ //
+ // Display the version
+ //
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
+ ShellPrintHiiEx (
+ 0,
+ gST->ConOut->Mode->CursorRow,
+ NULL,
+ STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),
+ ShellInfoObject.HiiHandle,
+ SupportLevel[PcdGet8(PcdShellSupportLevel)],
+ gEfiShellProtocol->MajorVersion,
+ gEfiShellProtocol->MinorVersion
+ );
+
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),
+ ShellInfoObject.HiiHandle,
+ (CHAR16 *) PcdGetPtr (PcdShellSupplier)
+ );
+
+ ShellPrintHiiEx (
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),
+ ShellInfoObject.HiiHandle,
+ (gST->Hdr.Revision&0xffff0000)>>16,
+ (gST->Hdr.Revision&0x0000ffff),
+ gST->FirmwareVendor,
+ gST->FirmwareRevision
+ );
+ }
+
+ //
+ // Display the mapping
+ //
+ if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
+ Status = RunCommand(L"map");
+ ASSERT_EFI_ERROR(Status);
+ }
+
+ //
+ // init all the built in alias'
+ //
+ Status = SetBuiltInAlias();
+ ASSERT_EFI_ERROR(Status);
+
+ //
+ // Initialize environment variables
+ //
+ if (ShellCommandGetProfileList() != NULL) {
+ Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);
+ ASSERT_EFI_ERROR(Status);
+ }
+
+ Size = 100;
+ TempString = AllocateZeroPool(Size);
+
+ UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));
+ Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);
+ ASSERT_EFI_ERROR(Status);
+
+ UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
+ Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);
+ ASSERT_EFI_ERROR(Status);
+
+ UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
+ Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);
+ ASSERT_EFI_ERROR(Status);
+
+ FreePool(TempString);
+
+ if (!EFI_ERROR(Status)) {
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
+ //
+ // Set up the event for CTRL-C monitoring...
+ //
+ Status = InernalEfiShellStartMonitor();
+ }
+
+ if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
+ //
+ // Set up the event for CTRL-S monitoring...
+ //
+ Status = InternalEfiShellStartCtrlSMonitor();
+ }
+
+ if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
+ //
+ // close off the gST->ConIn
+ //
+ OldConIn = gST->ConIn;
+ ConInHandle = gST->ConsoleInHandle;
+ gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
+ } else {
+ OldConIn = NULL;
+ ConInHandle = NULL;
+ }
+
+ if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
+ //
+ // process the startup script or launch the called app.
+ //
+ Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
+ }
+
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
+ //
+ // begin the UI waiting loop
+ //
+ do {
+ //
+ // clean out all the memory allocated for CONST <something> * return values
+ // between each shell prompt presentation
+ //
+ if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
+ FreeBufferList(&ShellInfoObject.BufferToFreeList);
+ }
+
+ //
+ // Reset page break back to default.
+ //
+ ShellInfoObject.PageBreakEnabled = PcdGetBool(PcdShellPageBreakDefault);
+ ASSERT (ShellInfoObject.ConsoleInfo != NULL);
+ ShellInfoObject.ConsoleInfo->Enabled = TRUE;
+ ShellInfoObject.ConsoleInfo->RowCounter = 0;
+
+ //
+ // Reset the CTRL-C event (yes we ignore the return values)
+ //
+ Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
+
+ //
+ // Display Prompt
+ //
+ Status = DoShellPrompt();
+ } while (!ShellCommandGetExit());
+ }
+ if (OldConIn != NULL && ConInHandle != NULL) {
+ CloseSimpleTextInOnFile (gST->ConIn);
+ gST->ConIn = OldConIn;
+ gST->ConsoleInHandle = ConInHandle;
+ }
+ }
+ }
+
+FreeResources:
+ //
+ // uninstall protocols / free memory / etc...
+ //
+ if (ShellInfoObject.UserBreakTimer != NULL) {
+ gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
+ DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
+ }
+ if (ShellInfoObject.ImageDevPath != NULL) {
+ FreePool(ShellInfoObject.ImageDevPath);
+ DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);
+ }
+ if (ShellInfoObject.FileDevPath != NULL) {
+ FreePool(ShellInfoObject.FileDevPath);
+ DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);
+ }
+ if (ShellInfoObject.NewShellParametersProtocol != NULL) {
+ CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);
+ DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);
+ }
+ if (ShellInfoObject.NewEfiShellProtocol != NULL){
+ if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
+ InternalEfiShellSetEnv(L"cwd", NULL, TRUE);
+ }
+ CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol);
+ DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
+ }
+
+ if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
+ FreeBufferList(&ShellInfoObject.BufferToFreeList);
+ }
+
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
+ ASSERT(FALSE); ///@todo finish this de-allocation (free SplitStdIn/Out when needed).
+
+ for ( Split = (SPLIT_LIST*)GetFirstNode (&ShellInfoObject.SplitList.Link)
+ ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)
+ ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)
+ ) {
+ RemoveEntryList (&Split->Link);
+ FreePool (Split);
+ }
+
+ DEBUG_CODE (InitializeListHead (&ShellInfoObject.SplitList.Link););
+ }
+
+ if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
+ FreePool(ShellInfoObject.ShellInitSettings.FileName);
+ DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
+ }
+
+ if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
+ FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
+ DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
+ }
+
+ if (ShellInfoObject.HiiHandle != NULL) {
+ HiiRemovePackages(ShellInfoObject.HiiHandle);
+ DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
+ }
+
+ if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
+ FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
+ }
+
+ ASSERT(ShellInfoObject.ConsoleInfo != NULL);
+ if (ShellInfoObject.ConsoleInfo != NULL) {
+ ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
+ FreePool(ShellInfoObject.ConsoleInfo);
+ DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
+ }
+
+ ShellFreeEnvVarList ();
+ ShellSetRawCmdLine (NULL);
+
+ if (ShellCommandGetExit()) {
+ return ((EFI_STATUS)ShellCommandGetExitCode());
+ }
+ return (Status);
+}
+
+/**
+ Sets all the alias' that were registered with the ShellCommandLib library.
+
+ @retval EFI_SUCCESS all init commands were run successfully.
+**/
+EFI_STATUS
+EFIAPI
+SetBuiltInAlias(
+ )
+{
+ EFI_STATUS Status;
+ CONST ALIAS_LIST *List;
+ ALIAS_LIST *Node;
+
+ //
+ // Get all the commands we want to run
+ //
+ List = ShellCommandGetInitAliasList();
+
+ //
+ // for each command in the List
+ //
+ for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
+ ; !IsNull (&List->Link, &Node->Link)
+ ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
+ ){
+ //
+ // install the alias'
+ //
+ Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Internal function to determine if 2 command names are really the same.
+
+ @param[in] Command1 The pointer to the first command name.
+ @param[in] Command2 The pointer to the second command name.
+
+ @retval TRUE The 2 command names are the same.
+ @retval FALSE The 2 command names are not the same.
+**/
+BOOLEAN
+EFIAPI
+IsCommand(
+ IN CONST CHAR16 *Command1,
+ IN CONST CHAR16 *Command2
+ )
+{
+ if (StringNoCaseCompare(&Command1, &Command2) == 0) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ Internal function to determine if a command is a script only command.
+
+ @param[in] CommandName The pointer to the command name.
+
+ @retval TRUE The command is a script only command.
+ @retval FALSE The command is not a script only command.
+**/
+BOOLEAN
+EFIAPI
+IsScriptOnlyCommand(
+ IN CONST CHAR16 *CommandName
+ )
+{
+ if (IsCommand(CommandName, L"for")
+ ||IsCommand(CommandName, L"endfor")
+ ||IsCommand(CommandName, L"if")
+ ||IsCommand(CommandName, L"else")
+ ||IsCommand(CommandName, L"endif")
+ ||IsCommand(CommandName, L"goto")) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ This function will populate the 2 device path protocol parameters based on the
+ global gImageHandle. The DevPath will point to the device path for the handle that has
+ loaded image protocol installed on it. The FilePath will point to the device path
+ for the file that was loaded.
+
+ @param[in, out] DevPath On a successful return the device path to the loaded image.
+ @param[in, out] FilePath On a successful return the device path to the file.
+
+ @retval EFI_SUCCESS The 2 device paths were successfully returned.
+ @retval other A error from gBS->HandleProtocol.
+
+ @sa HandleProtocol
+**/
+EFI_STATUS
+EFIAPI
+GetDevicePathsForImageAndFile (
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
+
+ ASSERT(DevPath != NULL);
+ ASSERT(FilePath != NULL);
+
+ Status = gBS->OpenProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID**)&LoadedImage,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = gBS->OpenProtocol (
+ LoadedImage->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID**)&ImageDevicePath,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ *DevPath = DuplicateDevicePath (ImageDevicePath);
+ *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
+ gBS->CloseProtocol(
+ LoadedImage->DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ gImageHandle,
+ NULL);
+ }
+ gBS->CloseProtocol(
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ gImageHandle,
+ NULL);
+ }
+ return (Status);
+}
+
+/**
+ Process all Uefi Shell 2.0 command line options.
+
+ see Uefi Shell 2.0 section 3.2 for full details.
+
+ the command line must resemble the following:
+
+ shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
+
+ ShellOpt-options Options which control the initialization behavior of the shell.
+ These options are read from the EFI global variable "ShellOpt"
+ and are processed before options or file-name.
+
+ options Options which control the initialization behavior of the shell.
+
+ file-name The name of a UEFI shell application or script to be executed
+ after initialization is complete. By default, if file-name is
+ specified, then -nostartup is implied. Scripts are not supported
+ by level 0.
+
+ file-name-options The command-line options that are passed to file-name when it
+ is invoked.
+
+ This will initialize the ShellInfoObject.ShellInitSettings global variable.
+
+ @retval EFI_SUCCESS The variable is initialized.
+**/
+EFI_STATUS
+EFIAPI
+ProcessCommandLine(
+ VOID
+ )
+{
+ UINTN Size;
+ UINTN LoopVar;
+ CHAR16 *CurrentArg;
+ CHAR16 *DelayValueStr;
+ UINT64 DelayValue;
+ EFI_STATUS Status;
+ EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation;
+
+ // `file-name-options` will contain arguments to `file-name` that we don't
+ // know about. This would cause ShellCommandLineParse to error, so we parse
+ // arguments manually, ignoring those after the first thing that doesn't look
+ // like a shell option (which is assumed to be `file-name`).
+
+ Status = gBS->LocateProtocol (
+ &gEfiUnicodeCollation2ProtocolGuid,
+ NULL,
+ (VOID **) &UnicodeCollation
+ );
+ if (EFI_ERROR (Status)) {
+ Status = gBS->LocateProtocol (
+ &gEfiUnicodeCollationProtocolGuid,
+ NULL,
+ (VOID **) &UnicodeCollation
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ // Set default options
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = FALSE;
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = FALSE;
+ ShellInfoObject.ShellInitSettings.Delay = 5;
+
+ //
+ // Start LoopVar at 0 to parse only optional arguments at Argv[0]
+ // and parse other parameters from Argv[1]. This is for use case that
+ // UEFI Shell boot option is created, and OptionalData is provided
+ // that starts with shell command-line options.
+ //
+ for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
+ CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];
+ if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-startup",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nostartup",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-noconsoleout",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-noconsolein",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nointerrupt",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nomap",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-noversion",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-nonest",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest = TRUE;
+ }
+ else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-delay",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = TRUE;
+ // Check for optional delay value following "-delay"
+ DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];
+ if (DelayValueStr != NULL){
+ if (*DelayValueStr == L':') {
+ DelayValueStr++;
+ }
+ if (!EFI_ERROR(ShellConvertStringToUint64 (
+ DelayValueStr,
+ &DelayValue,
+ FALSE,
+ FALSE
+ ))) {
+ ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;
+ LoopVar++;
+ }
+ }
+ } else if (UnicodeCollation->StriColl (
+ UnicodeCollation,
+ L"-_exit",
+ CurrentArg
+ ) == 0) {
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit = TRUE;
+ } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {
+ // Unrecognized option
+ ShellPrintHiiEx(-1, -1, NULL,
+ STRING_TOKEN (STR_GEN_PROBLEM),
+ ShellInfoObject.HiiHandle,
+ CurrentArg
+ );
+ return EFI_INVALID_PARAMETER;
+ } else {
+ //
+ // First argument should be Shell.efi image name
+ //
+ if (LoopVar == 0) {
+ continue;
+ }
+
+ ShellInfoObject.ShellInitSettings.FileName = AllocateCopyPool(StrSize(CurrentArg), CurrentArg);
+ if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ //
+ // We found `file-name`.
+ //
+ ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
+ LoopVar++;
+
+ // Add `file-name-options`
+ for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
+ ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
+ &Size,
+ L" ",
+ 0);
+ if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
+ SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
+ &Size,
+ gEfiShellParametersProtocol->Argv[LoopVar],
+ 0);
+ if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
+ SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ }
+ }
+ }
+
+ // "-nointerrupt" overrides "-delay"
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
+ ShellInfoObject.ShellInitSettings.Delay = 0;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Handles all interaction with the default startup script.
+
+ this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
+
+ @param ImagePath the path to the image for shell. first place to look for the startup script
+ @param FilePath the path to the file for shell. second place to look for the startup script.
+
+ @retval EFI_SUCCESS the variable is initialized.
+**/
+EFI_STATUS
+EFIAPI
+DoStartupScript(
+ IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS CalleeStatus;
+ UINTN Delay;
+ EFI_INPUT_KEY Key;
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_DEVICE_PATH_PROTOCOL *NewPath;
+ EFI_DEVICE_PATH_PROTOCOL *NamePath;
+ CHAR16 *FileStringPath;
+ CHAR16 *TempSpot;
+ UINTN NewSize;
+ CONST CHAR16 *MapName;
+
+ Key.UnicodeChar = CHAR_NULL;
+ Key.ScanCode = 0;
+ FileHandle = NULL;
+
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
+ //
+ // launch something else instead
+ //
+ NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
+ if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
+ NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
+ }
+ FileStringPath = AllocateZeroPool(NewSize);
+ if (FileStringPath == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ StrCpyS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileName);
+ if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
+ StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);
+ StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);
+ }
+ Status = RunShellCommand(FileStringPath, &CalleeStatus);
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {
+ ShellCommandRegisterExit(gEfiShellProtocol->BatchIsActive(), (UINT64)CalleeStatus);
+ }
+ FreePool(FileStringPath);
+ return (Status);
+
+ }
+
+ //
+ // for shell level 0 we do no scripts
+ // Without the Startup bit overriding we allow for nostartup to prevent scripts
+ //
+ if ( (PcdGet8(PcdShellSupportLevel) < 1)
+ || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
+ ){
+ return (EFI_SUCCESS);
+ }
+
+ gST->ConOut->EnableCursor(gST->ConOut, FALSE);
+ //
+ // print out our warning and see if they press a key
+ //
+ for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay
+ ; Delay != 0 && EFI_ERROR(Status)
+ ; Delay--
+ ){
+ ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);
+ gBS->Stall (1000000);
+ if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ }
+ }
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
+ gST->ConOut->EnableCursor(gST->ConOut, TRUE);
+
+ //
+ // ESC was pressed
+ //
+ if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Try the first location (must be file system)
+ //
+ MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
+ if (MapName != NULL) {
+ FileStringPath = NULL;
+ NewSize = 0;
+ FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
+ if (FileStringPath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ TempSpot = StrStr(FileStringPath, L";");
+ if (TempSpot != NULL) {
+ *TempSpot = CHAR_NULL;
+ }
+ FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
+ PathRemoveLastItem(FileStringPath);
+ FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
+ Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
+ FreePool(FileStringPath);
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ NamePath = FileDevicePath (NULL, mStartupScript);
+ NewPath = AppendDevicePathNode (ImagePath, NamePath);
+ FreePool(NamePath);
+
+ //
+ // Try the location
+ //
+ Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ FreePool(NewPath);
+ }
+ //
+ // If we got a file, run it
+ //
+ if (!EFI_ERROR(Status) && FileHandle != NULL) {
+ Status = RunScriptFile (mStartupScript, FileHandle, L"", ShellInfoObject.NewShellParametersProtocol);
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
+ } else {
+ FileStringPath = ShellFindFilePath(mStartupScript);
+ if (FileStringPath == NULL) {
+ //
+ // we return success since we don't need to have a startup script
+ //
+ Status = EFI_SUCCESS;
+ ASSERT(FileHandle == NULL);
+ } else {
+ Status = RunScriptFile(FileStringPath, NULL, L"", ShellInfoObject.NewShellParametersProtocol);
+ FreePool(FileStringPath);
+ }
+ }
+
+
+ return (Status);
+}
+
+/**
+ Function to perform the shell prompt looping. It will do a single prompt,
+ dispatch the result, and then return. It is expected that the caller will
+ call this function in a loop many times.
+
+ @retval EFI_SUCCESS
+ @retval RETURN_ABORTED
+**/
+EFI_STATUS
+EFIAPI
+DoShellPrompt (
+ VOID
+ )
+{
+ UINTN Column;
+ UINTN Row;
+ CHAR16 *CmdLine;
+ CONST CHAR16 *CurDir;
+ UINTN BufferSize;
+ EFI_STATUS Status;
+ LIST_ENTRY OldBufferList;
+
+ CurDir = NULL;
+
+ //
+ // Get screen setting to decide size of the command line buffer
+ //
+ gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
+ BufferSize = Column * Row * sizeof (CHAR16);
+ CmdLine = AllocateZeroPool (BufferSize);
+ if (CmdLine == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ SaveBufferList(&OldBufferList);
+ CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
+
+ //
+ // Prompt for input
+ //
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
+
+ if (CurDir != NULL && StrLen(CurDir) > 1) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
+ } else {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
+ }
+
+ //
+ // Read a line from the console
+ //
+ Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
+
+ //
+ // Null terminate the string and parse it
+ //
+ if (!EFI_ERROR (Status)) {
+ CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
+ Status = RunCommand(CmdLine);
+ }
+
+ //
+ // Done with this command
+ //
+ RestoreBufferList(&OldBufferList);
+ FreePool (CmdLine);
+ return Status;
+}
+
+/**
+ Add a buffer to the Buffer To Free List for safely returning buffers to other
+ places without risking letting them modify internal shell information.
+
+ @param Buffer Something to pass to FreePool when the shell is exiting.
+**/
+VOID*
+EFIAPI
+AddBufferToFreeList (
+ VOID *Buffer
+ )
+{
+ BUFFER_LIST *BufferListEntry;
+
+ if (Buffer == NULL) {
+ return (NULL);
+ }
+
+ BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));
+ if (BufferListEntry == NULL) {
+ return NULL;
+ }
+
+ BufferListEntry->Buffer = Buffer;
+ InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
+ return (Buffer);
+}
+
+
+/**
+ Create a new buffer list and stores the old one to OldBufferList
+
+ @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
+**/
+VOID
+SaveBufferList (
+ OUT LIST_ENTRY *OldBufferList
+ )
+{
+ CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));
+ InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
+}
+
+/**
+ Restore previous nodes into BufferToFreeList .
+
+ @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
+**/
+VOID
+RestoreBufferList (
+ IN OUT LIST_ENTRY *OldBufferList
+ )
+{
+ FreeBufferList (&ShellInfoObject.BufferToFreeList);
+ CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));
+}
+
+
+/**
+ Add a buffer to the Line History List
+
+ @param Buffer The line buffer to add.
+**/
+VOID
+EFIAPI
+AddLineToCommandHistory(
+ IN CONST CHAR16 *Buffer
+ )
+{
+ BUFFER_LIST *Node;
+ BUFFER_LIST *Walker;
+ UINT16 MaxHistoryCmdCount;
+ UINT16 Count;
+
+ Count = 0;
+ MaxHistoryCmdCount = PcdGet16(PcdShellMaxHistoryCommandCount);
+
+ if (MaxHistoryCmdCount == 0) {
+ return ;
+ }
+
+
+ Node = AllocateZeroPool(sizeof(BUFFER_LIST));
+ if (Node == NULL) {
+ return;
+ }
+
+ Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);
+ if (Node->Buffer == NULL) {
+ FreePool (Node);
+ return;
+ }
+
+ for ( Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)
+ ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
+ ; Walker = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
+ ){
+ Count++;
+ }
+ if (Count < MaxHistoryCmdCount){
+ InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
+ } else {
+ Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
+ RemoveEntryList(&Walker->Link);
+ if (Walker->Buffer != NULL) {
+ FreePool(Walker->Buffer);
+ }
+ FreePool(Walker);
+ InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
+ }
+}
+
+/**
+ Checks if a string is an alias for another command. If yes, then it replaces the alias name
+ with the correct command name.
+
+ @param[in, out] CommandString Upon entry the potential alias. Upon return the
+ command name if it was an alias. If it was not
+ an alias it will be unchanged. This function may
+ change the buffer to fit the command name.
+
+ @retval EFI_SUCCESS The name was changed.
+ @retval EFI_SUCCESS The name was not an alias.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ShellConvertAlias(
+ IN OUT CHAR16 **CommandString
+ )
+{
+ CONST CHAR16 *NewString;
+
+ NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
+ if (NewString == NULL) {
+ return (EFI_SUCCESS);
+ }
+ FreePool(*CommandString);
+ *CommandString = AllocateCopyPool(StrSize(NewString), NewString);
+ if (*CommandString == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ This function will eliminate unreplaced (and therefore non-found) environment variables.
+
+ @param[in,out] CmdLine The command line to update.
+**/
+EFI_STATUS
+EFIAPI
+StripUnreplacedEnvironmentVariables(
+ IN OUT CHAR16 *CmdLine
+ )
+{
+ CHAR16 *FirstPercent;
+ CHAR16 *FirstQuote;
+ CHAR16 *SecondPercent;
+ CHAR16 *SecondQuote;
+ CHAR16 *CurrentLocator;
+
+ for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {
+ FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);
+ FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);
+ SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;
+ if (FirstPercent == NULL || SecondPercent == NULL) {
+ //
+ // If we ever don't have 2 % we are done.
+ //
+ break;
+ }
+
+ if (FirstQuote!= NULL && FirstQuote < FirstPercent) {
+ SecondQuote = FindNextInstance(FirstQuote+1, L"\"", TRUE);
+ //
+ // Quote is first found
+ //
+
+ if (SecondQuote < FirstPercent) {
+ //
+ // restart after the pair of "
+ //
+ CurrentLocator = SecondQuote + 1;
+ } else /* FirstPercent < SecondQuote */{
+ //
+ // Restart on the first percent
+ //
+ CurrentLocator = FirstPercent;
+ }
+ continue;
+ }
+
+ if (FirstQuote == NULL || SecondPercent < FirstQuote) {
+ if (IsValidEnvironmentVariableName(FirstPercent, SecondPercent)) {
+ //
+ // We need to remove from FirstPercent to SecondPercent
+ //
+ CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));
+ //
+ // don't need to update the locator. both % characters are gone.
+ //
+ } else {
+ CurrentLocator = SecondPercent + 1;
+ }
+ continue;
+ }
+ CurrentLocator = FirstQuote;
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Function allocates a new command line and replaces all instances of environment
+ variable names that are correctly preset to their values.
+
+ If the return value is not NULL the memory must be caller freed.
+
+ @param[in] OriginalCommandLine The original command line
+
+ @retval NULL An error occurred.
+ @return The new command line with no environment variables present.
+**/
+CHAR16*
+EFIAPI
+ShellConvertVariables (
+ IN CONST CHAR16 *OriginalCommandLine
+ )
+{
+ CONST CHAR16 *MasterEnvList;
+ UINTN NewSize;
+ CHAR16 *NewCommandLine1;
+ CHAR16 *NewCommandLine2;
+ CHAR16 *Temp;
+ UINTN ItemSize;
+ CHAR16 *ItemTemp;
+ SCRIPT_FILE *CurrentScriptFile;
+ ALIAS_LIST *AliasListNode;
+
+ ASSERT(OriginalCommandLine != NULL);
+
+ ItemSize = 0;
+ NewSize = StrSize(OriginalCommandLine);
+ CurrentScriptFile = ShellCommandGetCurrentScriptFile();
+ Temp = NULL;
+
+ ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
+
+ //
+ // calculate the size required for the post-conversion string...
+ //
+ if (CurrentScriptFile != NULL) {
+ for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
+ ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ){
+ for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
+ ; Temp != NULL
+ ; Temp = StrStr(Temp+1, AliasListNode->Alias)
+ ){
+ //
+ // we need a preceding and if there is space no ^ preceding (if no space ignore)
+ //
+ if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
+ NewSize += StrSize(AliasListNode->CommandString);
+ }
+ }
+ }
+ }
+
+ for (MasterEnvList = EfiShellGetEnv(NULL)
+ ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
+ ; MasterEnvList += StrLen(MasterEnvList) + 1
+ ){
+ if (StrSize(MasterEnvList) > ItemSize) {
+ ItemSize = StrSize(MasterEnvList);
+ }
+ for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
+ ; Temp != NULL
+ ; Temp = StrStr(Temp+1, MasterEnvList)
+ ){
+ //
+ // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
+ //
+ if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
+ ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
+ NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
+ }
+ }
+ }
+
+ //
+ // now do the replacements...
+ //
+ NewCommandLine1 = AllocateCopyPool(NewSize, OriginalCommandLine);
+ NewCommandLine2 = AllocateZeroPool(NewSize);
+ ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
+ if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
+ SHELL_FREE_NON_NULL(NewCommandLine1);
+ SHELL_FREE_NON_NULL(NewCommandLine2);
+ SHELL_FREE_NON_NULL(ItemTemp);
+ return (NULL);
+ }
+ for (MasterEnvList = EfiShellGetEnv(NULL)
+ ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL
+ ; MasterEnvList += StrLen(MasterEnvList) + 1
+ ){
+ StrCpyS( ItemTemp,
+ ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
+ L"%"
+ );
+ StrCatS( ItemTemp,
+ ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
+ MasterEnvList
+ );
+ StrCatS( ItemTemp,
+ ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
+ L"%"
+ );
+ ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
+ StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
+ }
+ if (CurrentScriptFile != NULL) {
+ for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
+ ; !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ; AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
+ ){
+ ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
+ StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
+ }
+ }
+
+ //
+ // Remove non-existent environment variables
+ //
+ StripUnreplacedEnvironmentVariables(NewCommandLine1);
+
+ //
+ // Now cleanup any straggler intentionally ignored "%" characters
+ //
+ ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
+ StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
+
+ FreePool(NewCommandLine2);
+ FreePool(ItemTemp);
+
+ return (NewCommandLine1);
+}
+
+/**
+ Internal function to run a command line with pipe usage.
+
+ @param[in] CmdLine The pointer to the command line.
+ @param[in] StdIn The pointer to the Standard input.
+ @param[in] StdOut The pointer to the Standard output.
+
+ @retval EFI_SUCCESS The split command is executed successfully.
+ @retval other Some error occurs when executing the split command.
+**/
+EFI_STATUS
+EFIAPI
+RunSplitCommand(
+ IN CONST CHAR16 *CmdLine,
+ IN SHELL_FILE_HANDLE *StdIn,
+ IN SHELL_FILE_HANDLE *StdOut
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *NextCommandLine;
+ CHAR16 *OurCommandLine;
+ UINTN Size1;
+ UINTN Size2;
+ SPLIT_LIST *Split;
+ SHELL_FILE_HANDLE *TempFileHandle;
+ BOOLEAN Unicode;
+
+ ASSERT(StdOut == NULL);
+
+ ASSERT(StrStr(CmdLine, L"|") != NULL);
+
+ Status = EFI_SUCCESS;
+ NextCommandLine = NULL;
+ OurCommandLine = NULL;
+ Size1 = 0;
+ Size2 = 0;
+
+ NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
+ OurCommandLine = StrnCatGrow(&OurCommandLine , &Size2, CmdLine , StrStr(CmdLine, L"|") - CmdLine);
+
+ if (NextCommandLine == NULL || OurCommandLine == NULL) {
+ SHELL_FREE_NON_NULL(OurCommandLine);
+ SHELL_FREE_NON_NULL(NextCommandLine);
+ return (EFI_OUT_OF_RESOURCES);
+ } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
+ SHELL_FREE_NON_NULL(OurCommandLine);
+ SHELL_FREE_NON_NULL(NextCommandLine);
+ return (EFI_INVALID_PARAMETER);
+ } else if (NextCommandLine[0] == L'a' &&
+ (NextCommandLine[1] == L' ' || NextCommandLine[1] == CHAR_NULL)
+ ){
+ CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
+ while (NextCommandLine[0] == L' ') {
+ CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
+ }
+ if (NextCommandLine[0] == CHAR_NULL) {
+ SHELL_FREE_NON_NULL(OurCommandLine);
+ SHELL_FREE_NON_NULL(NextCommandLine);
+ return (EFI_INVALID_PARAMETER);
+ }
+ Unicode = FALSE;
+ } else {
+ Unicode = TRUE;
+ }
+
+
+ //
+ // make a SPLIT_LIST item and add to list
+ //
+ Split = AllocateZeroPool(sizeof(SPLIT_LIST));
+ if (Split == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Split->SplitStdIn = StdIn;
+ Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
+ ASSERT(Split->SplitStdOut != NULL);
+ InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
+
+ Status = RunCommand(OurCommandLine);
+
+ //
+ // move the output from the first to the in to the second.
+ //
+ TempFileHandle = Split->SplitStdOut;
+ if (Split->SplitStdIn == StdIn) {
+ Split->SplitStdOut = NULL;
+ } else {
+ Split->SplitStdOut = Split->SplitStdIn;
+ }
+ Split->SplitStdIn = TempFileHandle;
+ ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
+
+ if (!EFI_ERROR(Status)) {
+ Status = RunCommand(NextCommandLine);
+ }
+
+ //
+ // remove the top level from the ScriptList
+ //
+ ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
+ RemoveEntryList(&Split->Link);
+
+ //
+ // Note that the original StdIn is now the StdOut...
+ //
+ if (Split->SplitStdOut != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
+ }
+ if (Split->SplitStdIn != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
+ FreePool (Split->SplitStdIn);
+ }
+
+ FreePool(Split);
+ FreePool(NextCommandLine);
+ FreePool(OurCommandLine);
+
+ return (Status);
+}
+
+/**
+ Take the original command line, substitute any variables, free
+ the original string, return the modified copy.
+
+ @param[in] CmdLine pointer to the command line to update.
+
+ @retval EFI_SUCCESS the function was successful.
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ShellSubstituteVariables(
+ IN CHAR16 **CmdLine
+ )
+{
+ CHAR16 *NewCmdLine;
+ NewCmdLine = ShellConvertVariables(*CmdLine);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ if (NewCmdLine == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ *CmdLine = NewCmdLine;
+ return (EFI_SUCCESS);
+}
+
+/**
+ Take the original command line, substitute any alias in the first group of space delimited characters, free
+ the original string, return the modified copy.
+
+ @param[in] CmdLine pointer to the command line to update.
+
+ @retval EFI_SUCCESS the function was successful.
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ShellSubstituteAliases(
+ IN CHAR16 **CmdLine
+ )
+{
+ CHAR16 *NewCmdLine;
+ CHAR16 *CommandName;
+ EFI_STATUS Status;
+ UINTN PostAliasSize;
+ ASSERT(CmdLine != NULL);
+ ASSERT(*CmdLine!= NULL);
+
+
+ CommandName = NULL;
+ if (StrStr((*CmdLine), L" ") == NULL){
+ StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);
+ } else {
+ StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));
+ }
+
+ //
+ // This cannot happen 'inline' since the CmdLine can need extra space.
+ //
+ NewCmdLine = NULL;
+ if (!ShellCommandIsCommandOnList(CommandName)) {
+ //
+ // Convert via alias
+ //
+ Status = ShellConvertAlias(&CommandName);
+ if (EFI_ERROR(Status)){
+ return (Status);
+ }
+ PostAliasSize = 0;
+ NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);
+ if (NewCmdLine == NULL) {
+ SHELL_FREE_NON_NULL(CommandName);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);
+ if (NewCmdLine == NULL) {
+ SHELL_FREE_NON_NULL(CommandName);
+ SHELL_FREE_NON_NULL(*CmdLine);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ } else {
+ NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);
+ }
+
+ SHELL_FREE_NON_NULL(*CmdLine);
+ SHELL_FREE_NON_NULL(CommandName);
+
+ //
+ // re-assign the passed in double pointer to point to our newly allocated buffer
+ //
+ *CmdLine = NewCmdLine;
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Takes the Argv[0] part of the command line and determine the meaning of it.
+
+ @param[in] CmdName pointer to the command line to update.
+
+ @retval Internal_Command The name is an internal command.
+ @retval File_Sys_Change the name is a file system change.
+ @retval Script_File_Name the name is a NSH script file.
+ @retval Unknown_Invalid the name is unknown.
+ @retval Efi_Application the name is an application (.EFI).
+**/
+SHELL_OPERATION_TYPES
+EFIAPI
+GetOperationType(
+ IN CONST CHAR16 *CmdName
+ )
+{
+ CHAR16* FileWithPath;
+ CONST CHAR16* TempLocation;
+ CONST CHAR16* TempLocation2;
+
+ FileWithPath = NULL;
+ //
+ // test for an internal command.
+ //
+ if (ShellCommandIsCommandOnList(CmdName)) {
+ return (Internal_Command);
+ }
+
+ //
+ // Test for file system change request. anything ending with first : and cant have spaces.
+ //
+ if (CmdName[(StrLen(CmdName)-1)] == L':') {
+ if ( StrStr(CmdName, L" ") != NULL
+ || StrLen(StrStr(CmdName, L":")) > 1
+ ) {
+ return (Unknown_Invalid);
+ }
+ return (File_Sys_Change);
+ }
+
+ //
+ // Test for a file
+ //
+ if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {
+ //
+ // See if that file has a script file extension
+ //
+ if (StrLen(FileWithPath) > 4) {
+ TempLocation = FileWithPath+StrLen(FileWithPath)-4;
+ TempLocation2 = mScriptExtension;
+ if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {
+ SHELL_FREE_NON_NULL(FileWithPath);
+ return (Script_File_Name);
+ }
+ }
+
+ //
+ // Was a file, but not a script. we treat this as an application.
+ //
+ SHELL_FREE_NON_NULL(FileWithPath);
+ return (Efi_Application);
+ }
+
+ SHELL_FREE_NON_NULL(FileWithPath);
+ //
+ // No clue what this is... return invalid flag...
+ //
+ return (Unknown_Invalid);
+}
+
+/**
+ Determine if the first item in a command line is valid.
+
+ @param[in] CmdLine The command line to parse.
+
+ @retval EFI_SUCCESS The item is valid.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+ @retval EFI_NOT_FOUND The operation type is unknown or invalid.
+**/
+EFI_STATUS
+EFIAPI
+IsValidSplit(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ CHAR16 *Temp;
+ CHAR16 *FirstParameter;
+ CHAR16 *TempWalker;
+ EFI_STATUS Status;
+
+ Temp = NULL;
+
+ Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);
+ if (Temp == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ FirstParameter = StrStr(Temp, L"|");
+ if (FirstParameter != NULL) {
+ *FirstParameter = CHAR_NULL;
+ }
+
+ FirstParameter = NULL;
+
+ //
+ // Process the command line
+ //
+ Status = ProcessCommandLineToFinal(&Temp);
+
+ if (!EFI_ERROR(Status)) {
+ FirstParameter = AllocateZeroPool(StrSize(CmdLine));
+ if (FirstParameter == NULL) {
+ SHELL_FREE_NON_NULL(Temp);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ TempWalker = (CHAR16*)Temp;
+ if (!EFI_ERROR (ShellGetNextParameter (&TempWalker, FirstParameter, StrSize(CmdLine), TRUE))) {
+ if (GetOperationType(FirstParameter) == Unknown_Invalid) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
+ SetLastError(SHELL_NOT_FOUND);
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ }
+
+ SHELL_FREE_NON_NULL(Temp);
+ SHELL_FREE_NON_NULL(FirstParameter);
+ return Status;
+}
+
+/**
+ Determine if a command line contains with a split contains only valid commands.
+
+ @param[in] CmdLine The command line to parse.
+
+ @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
+ @retval EFI_ABORTED CmdLine has at least one invalid command or application.
+**/
+EFI_STATUS
+EFIAPI
+VerifySplit(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ CONST CHAR16 *TempSpot;
+ EFI_STATUS Status;
+
+ //
+ // If this was the only item, then get out
+ //
+ if (!ContainsSplit(CmdLine)) {
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Verify up to the pipe or end character
+ //
+ Status = IsValidSplit(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ //
+ // recurse to verify the next item
+ //
+ TempSpot = ShellFindFirstCharacter(CmdLine, L"|", TRUE) + 1;
+ if (*TempSpot == L'a' &&
+ (*(TempSpot + 1) == L' ' || *(TempSpot + 1) == CHAR_NULL)
+ ) {
+ // If it's an ASCII pipe '|a'
+ TempSpot += 1;
+ }
+
+ return (VerifySplit(TempSpot));
+}
+
+/**
+ Process a split based operation.
+
+ @param[in] CmdLine pointer to the command line to process
+
+ @retval EFI_SUCCESS The operation was successful
+ @return an error occurred.
+**/
+EFI_STATUS
+EFIAPI
+ProcessNewSplitCommandLine(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ SPLIT_LIST *Split;
+ EFI_STATUS Status;
+
+ Status = VerifySplit(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ Split = NULL;
+
+ //
+ // are we in an existing split???
+ //
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
+ Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
+ }
+
+ if (Split == NULL) {
+ Status = RunSplitCommand(CmdLine, NULL, NULL);
+ } else {
+ Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);
+ }
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
+ }
+ return (Status);
+}
+
+/**
+ Handle a request to change the current file system.
+
+ @param[in] CmdLine The passed in command line.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+EFIAPI
+ChangeMappedDrive(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ EFI_STATUS Status;
+ Status = EFI_SUCCESS;
+
+ //
+ // make sure we are the right operation
+ //
+ ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);
+
+ //
+ // Call the protocol API to do the work
+ //
+ Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);
+
+ //
+ // Report any errors
+ //
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
+ }
+
+ return (Status);
+}
+
+/**
+ Reprocess the command line to direct all -? to the help command.
+
+ if found, will add "help" as argv[0], and move the rest later.
+
+ @param[in,out] CmdLine pointer to the command line to update
+**/
+EFI_STATUS
+EFIAPI
+DoHelpUpdate(
+ IN OUT CHAR16 **CmdLine
+ )
+{
+ CHAR16 *CurrentParameter;
+ CHAR16 *Walker;
+ CHAR16 *NewCommandLine;
+ EFI_STATUS Status;
+ UINTN NewCmdLineSize;
+
+ Status = EFI_SUCCESS;
+
+ CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));
+ if (CurrentParameter == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ Walker = *CmdLine;
+ while(Walker != NULL && *Walker != CHAR_NULL) {
+ if (!EFI_ERROR (ShellGetNextParameter (&Walker, CurrentParameter, StrSize(*CmdLine), TRUE))) {
+ if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {
+ CurrentParameter[0] = L' ';
+ CurrentParameter[1] = L' ';
+ NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine);
+ NewCommandLine = AllocateZeroPool(NewCmdLineSize);
+ if (NewCommandLine == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ //
+ // We know the space is sufficient since we just calculated it.
+ //
+ StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5);
+ StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine));
+ SHELL_FREE_NON_NULL(*CmdLine);
+ *CmdLine = NewCommandLine;
+ break;
+ }
+ }
+ }
+
+ SHELL_FREE_NON_NULL(CurrentParameter);
+
+ return (Status);
+}
+
+/**
+ Function to update the shell variable "lasterror".
+
+ @param[in] ErrorCode the error code to put into lasterror.
+**/
+EFI_STATUS
+EFIAPI
+SetLastError(
+ IN CONST SHELL_STATUS ErrorCode
+ )
+{
+ CHAR16 LeString[19];
+ if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);
+ } else {
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);
+ }
+ DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
+ InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
+
+ @param[in,out] CmdLine pointer to the command line to update
+
+ @retval EFI_SUCCESS The operation was successful
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+ @return some other error occurred
+**/
+EFI_STATUS
+EFIAPI
+ProcessCommandLineToFinal(
+ IN OUT CHAR16 **CmdLine
+ )
+{
+ EFI_STATUS Status;
+ TrimSpaces(CmdLine);
+
+ Status = ShellSubstituteAliases(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ TrimSpaces(CmdLine);
+
+ Status = ShellSubstituteVariables(CmdLine);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+ ASSERT (*CmdLine != NULL);
+
+ TrimSpaces(CmdLine);
+
+ //
+ // update for help parsing
+ //
+ if (StrStr(*CmdLine, L"?") != NULL) {
+ //
+ // This may do nothing if the ? does not indicate help.
+ // Save all the details for in the API below.
+ //
+ Status = DoHelpUpdate(CmdLine);
+ }
+
+ TrimSpaces(CmdLine);
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Run an internal shell command.
+
+ This API will update the shell's environment since these commands are libraries.
+
+ @param[in] CmdLine the command line to run.
+ @param[in] FirstParameter the first parameter on the command line
+ @param[in] ParamProtocol the shell parameters protocol pointer
+ @param[out] CommandStatus the status from the command line.
+
+ @retval EFI_SUCCESS The command was completed.
+ @retval EFI_ABORTED The command's operation was aborted.
+**/
+EFI_STATUS
+EFIAPI
+RunInternalCommand(
+ IN CONST CHAR16 *CmdLine,
+ IN CHAR16 *FirstParameter,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
+ OUT EFI_STATUS *CommandStatus
+)
+{
+ EFI_STATUS Status;
+ UINTN Argc;
+ CHAR16 **Argv;
+ SHELL_STATUS CommandReturnedStatus;
+ BOOLEAN LastError;
+ CHAR16 *Walker;
+ CHAR16 *NewCmdLine;
+
+ NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);
+ if (NewCmdLine == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
+ if (*Walker == L'^' && *(Walker+1) == L'#') {
+ CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
+ }
+ }
+
+ //
+ // get the argc and argv updated for internal commands
+ //
+ Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);
+ if (!EFI_ERROR(Status)) {
+ //
+ // Run the internal command.
+ //
+ Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);
+
+ if (!EFI_ERROR(Status)) {
+ if (CommandStatus != NULL) {
+ if (CommandReturnedStatus != SHELL_SUCCESS) {
+ *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);
+ } else {
+ *CommandStatus = EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Update last error status.
+ // some commands do not update last error.
+ //
+ if (LastError) {
+ SetLastError(CommandReturnedStatus);
+ }
+
+ //
+ // Pass thru the exitcode from the app.
+ //
+ if (ShellCommandGetExit()) {
+ //
+ // An Exit was requested ("exit" command), pass its value up.
+ //
+ Status = CommandReturnedStatus;
+ } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {
+ //
+ // Always abort when a script only command fails for any reason
+ //
+ Status = EFI_ABORTED;
+ } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {
+ //
+ // Abort when in a script and a command aborted
+ //
+ Status = EFI_ABORTED;
+ }
+ }
+ }
+
+ //
+ // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
+ // This is safe even if the update API failed. In this case, it may be a no-op.
+ //
+ RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
+
+ //
+ // If a script is running and the command is not a script only command, then
+ // change return value to success so the script won't halt (unless aborted).
+ //
+ // Script only commands have to be able halt the script since the script will
+ // not operate if they are failing.
+ //
+ if ( ShellCommandGetCurrentScriptFile() != NULL
+ && !IsScriptOnlyCommand(FirstParameter)
+ && Status != EFI_ABORTED
+ ) {
+ Status = EFI_SUCCESS;
+ }
+
+ FreePool (NewCmdLine);
+ return (Status);
+}
+
+/**
+ Function to run the command or file.
+
+ @param[in] Type the type of operation being run.
+ @param[in] CmdLine the command line to run.
+ @param[in] FirstParameter the first parameter on the command line
+ @param[in] ParamProtocol the shell parameters protocol pointer
+ @param[out] CommandStatus the status from the command line.
+
+ @retval EFI_SUCCESS The command was completed.
+ @retval EFI_ABORTED The command's operation was aborted.
+**/
+EFI_STATUS
+EFIAPI
+RunCommandOrFile(
+ IN SHELL_OPERATION_TYPES Type,
+ IN CONST CHAR16 *CmdLine,
+ IN CHAR16 *FirstParameter,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
+ OUT EFI_STATUS *CommandStatus
+)
+{
+ EFI_STATUS Status;
+ EFI_STATUS StartStatus;
+ CHAR16 *CommandWithPath;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ SHELL_STATUS CalleeExitStatus;
+
+ Status = EFI_SUCCESS;
+ CommandWithPath = NULL;
+ DevPath = NULL;
+ CalleeExitStatus = SHELL_INVALID_PARAMETER;
+
+ switch (Type) {
+ case Internal_Command:
+ Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol, CommandStatus);
+ break;
+ case Script_File_Name:
+ case Efi_Application:
+ //
+ // Process a fully qualified path
+ //
+ if (StrStr(FirstParameter, L":") != NULL) {
+ ASSERT (CommandWithPath == NULL);
+ if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {
+ CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);
+ }
+ }
+
+ //
+ // Process a relative path and also check in the path environment variable
+ //
+ if (CommandWithPath == NULL) {
+ CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);
+ }
+
+ //
+ // This should be impossible now.
+ //
+ ASSERT(CommandWithPath != NULL);
+
+ //
+ // Make sure that path is not just a directory (or not found)
+ //
+ if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
+ SetLastError(SHELL_NOT_FOUND);
+ }
+ switch (Type) {
+ case Script_File_Name:
+ Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);
+ break;
+ case Efi_Application:
+ //
+ // Get the device path of the application image
+ //
+ DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
+ if (DevPath == NULL){
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ //
+ // Execute the device path
+ //
+ Status = InternalShellExecuteDevicePath(
+ &gImageHandle,
+ DevPath,
+ CmdLine,
+ NULL,
+ &StartStatus
+ );
+
+ SHELL_FREE_NON_NULL(DevPath);
+
+ if(EFI_ERROR (Status)) {
+ CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
+ } else {
+ CalleeExitStatus = (SHELL_STATUS) StartStatus;
+ }
+
+ if (CommandStatus != NULL) {
+ *CommandStatus = CalleeExitStatus;
+ }
+
+ //
+ // Update last error status.
+ //
+ // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
+ SetLastError(CalleeExitStatus);
+ break;
+ default:
+ //
+ // Do nothing.
+ //
+ break;
+ }
+ break;
+ default:
+ //
+ // Do nothing.
+ //
+ break;
+ }
+
+ SHELL_FREE_NON_NULL(CommandWithPath);
+
+ return (Status);
+}
+
+/**
+ Function to setup StdIn, StdErr, StdOut, and then run the command or file.
+
+ @param[in] Type the type of operation being run.
+ @param[in] CmdLine the command line to run.
+ @param[in] FirstParameter the first parameter on the command line.
+ @param[in] ParamProtocol the shell parameters protocol pointer
+ @param[out] CommandStatus the status from the command line.
+
+ @retval EFI_SUCCESS The command was completed.
+ @retval EFI_ABORTED The command's operation was aborted.
+**/
+EFI_STATUS
+EFIAPI
+SetupAndRunCommandOrFile(
+ IN SHELL_OPERATION_TYPES Type,
+ IN CHAR16 *CmdLine,
+ IN CHAR16 *FirstParameter,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol,
+ OUT EFI_STATUS *CommandStatus
+)
+{
+ EFI_STATUS Status;
+ SHELL_FILE_HANDLE OriginalStdIn;
+ SHELL_FILE_HANDLE OriginalStdOut;
+ SHELL_FILE_HANDLE OriginalStdErr;
+ SYSTEM_TABLE_INFO OriginalSystemTableInfo;
+ CONST SCRIPT_FILE *ConstScriptFile;
+
+ //
+ // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
+ //
+ Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
+
+ //
+ // The StdIn, StdOut, and StdErr are set up.
+ // Now run the command, script, or application
+ //
+ if (!EFI_ERROR(Status)) {
+ TrimSpaces(&CmdLine);
+ Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);
+ }
+
+ //
+ // Now print errors
+ //
+ if (EFI_ERROR(Status)) {
+ ConstScriptFile = ShellCommandGetCurrentScriptFile();
+ if (ConstScriptFile == NULL || ConstScriptFile->CurrentCommand == NULL) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
+ } else {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID*)(Status), ConstScriptFile->CurrentCommand->Line);
+ }
+ }
+
+ //
+ // put back the original StdIn, StdOut, and StdErr
+ //
+ RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
+
+ return (Status);
+}
+
+/**
+ Function will process and run a command line.
+
+ This will determine if the command line represents an internal shell
+ command or dispatch an external application.
+
+ @param[in] CmdLine The command line to parse.
+ @param[out] CommandStatus The status from the command line.
+
+ @retval EFI_SUCCESS The command was completed.
+ @retval EFI_ABORTED The command's operation was aborted.
+**/
+EFI_STATUS
+EFIAPI
+RunShellCommand(
+ IN CONST CHAR16 *CmdLine,
+ OUT EFI_STATUS *CommandStatus
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *CleanOriginal;
+ CHAR16 *FirstParameter;
+ CHAR16 *TempWalker;
+ SHELL_OPERATION_TYPES Type;
+ CHAR16 *OldCmdLine;
+
+ ASSERT(CmdLine != NULL);
+ if (StrLen(CmdLine) == 0) {
+ return (EFI_SUCCESS);
+ }
+
+ Status = EFI_SUCCESS;
+ FirstParameter = NULL;
+ CleanOriginal = NULL;
+ OldCmdLine = NULL;
+
+ CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
+ if (CleanOriginal == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ TrimSpaces(&CleanOriginal);
+
+ //
+ // NULL out comments (leveraged from RunScriptFileHandle() ).
+ // The # character on a line is used to denote that all characters on the same line
+ // and to the right of the # are to be ignored by the shell.
+ // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
+ //
+ for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {
+ if (*TempWalker == L'^') {
+ if (*(TempWalker + 1) == L'#') {
+ TempWalker++;
+ }
+ } else if (*TempWalker == L'#') {
+ *TempWalker = CHAR_NULL;
+ }
+ }
+
+ TrimSpaces(&CleanOriginal);
+
+ //
+ // Handle case that passed in command line is just 1 or more " " characters.
+ //
+ if (StrLen (CleanOriginal) == 0) {
+ Status = EFI_SUCCESS;
+ goto Done;
+ }
+
+ Status = ProcessCommandLineToFinal(&CleanOriginal);
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ OldCmdLine = ShellGetRawCmdLine ();
+ ShellSetRawCmdLine (CleanOriginal);
+
+ //
+ // We don't do normal processing with a split command line (output from one command input to another)
+ //
+ if (ContainsSplit(CleanOriginal)) {
+ Status = ProcessNewSplitCommandLine(CleanOriginal);
+ goto Done;
+ }
+
+ //
+ // We need the first parameter information so we can determine the operation type
+ //
+ FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));
+ if (FirstParameter == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+ TempWalker = CleanOriginal;
+ if (!EFI_ERROR (ShellGetNextParameter (&TempWalker, FirstParameter, StrSize(CleanOriginal), TRUE))) {
+ //
+ // Depending on the first parameter we change the behavior
+ //
+ switch (Type = GetOperationType(FirstParameter)) {
+ case File_Sys_Change:
+ Status = ChangeMappedDrive (FirstParameter);
+ break;
+ case Internal_Command:
+ case Script_File_Name:
+ case Efi_Application:
+ Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);
+ break;
+ default:
+ //
+ // Whatever was typed, it was invalid.
+ //
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
+ SetLastError(SHELL_NOT_FOUND);
+ break;
+ }
+ } else {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
+ SetLastError(SHELL_NOT_FOUND);
+ }
+
+Done:
+ ShellSetRawCmdLine (OldCmdLine);
+ SHELL_FREE_NON_NULL (OldCmdLine);
+ SHELL_FREE_NON_NULL (CleanOriginal);
+ SHELL_FREE_NON_NULL (FirstParameter);
+
+ return (Status);
+}
+
+/**
+ Function will process and run a command line.
+
+ This will determine if the command line represents an internal shell
+ command or dispatch an external application.
+
+ @param[in] CmdLine The command line to parse.
+
+ @retval EFI_SUCCESS The command was completed.
+ @retval EFI_ABORTED The command's operation was aborted.
+**/
+EFI_STATUS
+EFIAPI
+RunCommand(
+ IN CONST CHAR16 *CmdLine
+ )
+{
+ return (RunShellCommand(CmdLine, NULL));
+}
+
+
+STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
+/**
+ Function determines if the CommandName COULD be a valid command. It does not determine whether
+ this is a valid command. It only checks for invalid characters.
+
+ @param[in] CommandName The name to check
+
+ @retval TRUE CommandName could be a command name
+ @retval FALSE CommandName could not be a valid command name
+**/
+BOOLEAN
+EFIAPI
+IsValidCommandName(
+ IN CONST CHAR16 *CommandName
+ )
+{
+ UINTN Count;
+ if (CommandName == NULL) {
+ ASSERT(FALSE);
+ return (FALSE);
+ }
+ for ( Count = 0
+ ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
+ ; Count++
+ ){
+ if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/**
+ Function to process a NSH script file via SHELL_FILE_HANDLE.
+
+ @param[in] Handle The handle to the already opened file.
+ @param[in] Name The name of the script file.
+
+ @retval EFI_SUCCESS the script completed successfully
+**/
+EFI_STATUS
+EFIAPI
+RunScriptFileHandle (
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Name
+ )
+{
+ EFI_STATUS Status;
+ SCRIPT_FILE *NewScriptFile;
+ UINTN LoopVar;
+ UINTN PrintBuffSize;
+ CHAR16 *CommandLine;
+ CHAR16 *CommandLine2;
+ CHAR16 *CommandLine3;
+ SCRIPT_COMMAND_LIST *LastCommand;
+ BOOLEAN Ascii;
+ BOOLEAN PreScriptEchoState;
+ BOOLEAN PreCommandEchoState;
+ CONST CHAR16 *CurDir;
+ UINTN LineCount;
+ CHAR16 LeString[50];
+ LIST_ENTRY OldBufferList;
+
+ ASSERT(!ShellCommandGetScriptExit());
+
+ PreScriptEchoState = ShellCommandGetEchoState();
+ PrintBuffSize = PcdGet16(PcdShellPrintBufferSize);
+
+ NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
+ if (NewScriptFile == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ //
+ // Set up the name
+ //
+ ASSERT(NewScriptFile->ScriptName == NULL);
+ NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
+ if (NewScriptFile->ScriptName == NULL) {
+ DeleteScriptFileStruct(NewScriptFile);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ //
+ // Save the parameters (used to replace %0 to %9 later on)
+ //
+ NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
+ if (NewScriptFile->Argc != 0) {
+ NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
+ if (NewScriptFile->Argv == NULL) {
+ DeleteScriptFileStruct(NewScriptFile);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
+ ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
+ NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
+ if (NewScriptFile->Argv[LoopVar] == NULL) {
+ DeleteScriptFileStruct(NewScriptFile);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ }
+ } else {
+ NewScriptFile->Argv = NULL;
+ }
+
+ InitializeListHead(&NewScriptFile->CommandList);
+ InitializeListHead(&NewScriptFile->SubstList);
+
+ //
+ // Now build the list of all script commands.
+ //
+ LineCount = 0;
+ while(!ShellFileHandleEof(Handle)) {
+ CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
+ LineCount++;
+ if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {
+ SHELL_FREE_NON_NULL(CommandLine);
+ continue;
+ }
+ NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
+ if (NewScriptFile->CurrentCommand == NULL) {
+ SHELL_FREE_NON_NULL(CommandLine);
+ DeleteScriptFileStruct(NewScriptFile);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ NewScriptFile->CurrentCommand->Cl = CommandLine;
+ NewScriptFile->CurrentCommand->Data = NULL;
+ NewScriptFile->CurrentCommand->Line = LineCount;
+
+ InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
+ }
+
+ //
+ // Add this as the topmost script file
+ //
+ ShellCommandSetNewScript (NewScriptFile);
+
+ //
+ // Now enumerate through the commands and run each one.
+ //
+ CommandLine = AllocateZeroPool(PrintBuffSize);
+ if (CommandLine == NULL) {
+ DeleteScriptFileStruct(NewScriptFile);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ CommandLine2 = AllocateZeroPool(PrintBuffSize);
+ if (CommandLine2 == NULL) {
+ FreePool(CommandLine);
+ DeleteScriptFileStruct(NewScriptFile);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
+ ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
+ ; // conditional increment in the body of the loop
+ ){
+ ASSERT(CommandLine2 != NULL);
+ StrnCpyS( CommandLine2,
+ PrintBuffSize/sizeof(CHAR16),
+ NewScriptFile->CurrentCommand->Cl,
+ PrintBuffSize/sizeof(CHAR16) - 1
+ );
+
+ SaveBufferList(&OldBufferList);
+
+ //
+ // NULL out comments
+ //
+ for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
+ if (*CommandLine3 == L'^') {
+ if ( *(CommandLine3+1) == L':') {
+ CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
+ } else if (*(CommandLine3+1) == L'#') {
+ CommandLine3++;
+ }
+ } else if (*CommandLine3 == L'#') {
+ *CommandLine3 = CHAR_NULL;
+ }
+ }
+
+ if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
+ //
+ // Due to variability in starting the find and replace action we need to have both buffers the same.
+ //
+ StrnCpyS( CommandLine,
+ PrintBuffSize/sizeof(CHAR16),
+ CommandLine2,
+ PrintBuffSize/sizeof(CHAR16) - 1
+ );
+
+ //
+ // Remove the %0 to %9 from the command line (if we have some arguments)
+ //
+ if (NewScriptFile->Argv != NULL) {
+ switch (NewScriptFile->Argc) {
+ default:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 9:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 8:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 7:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 6:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 5:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 4:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 3:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 2:
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ case 1:
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ break;
+ case 0:
+ break;
+ }
+ }
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);
+ Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);
+
+ StrnCpyS( CommandLine2,
+ PrintBuffSize/sizeof(CHAR16),
+ CommandLine,
+ PrintBuffSize/sizeof(CHAR16) - 1
+ );
+
+ LastCommand = NewScriptFile->CurrentCommand;
+
+ for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
+
+ if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
+ //
+ // This line is a goto target / label
+ //
+ } else {
+ if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
+ if (CommandLine3[0] == L'@') {
+ //
+ // We need to save the current echo state
+ // and disable echo for just this command.
+ //
+ PreCommandEchoState = ShellCommandGetEchoState();
+ ShellCommandSetEchoState(FALSE);
+ Status = RunCommand(CommandLine3+1);
+
+ //
+ // If command was "@echo -off" or "@echo -on" then don't restore echo state
+ //
+ if (StrCmp (L"@echo -off", CommandLine3) != 0 &&
+ StrCmp (L"@echo -on", CommandLine3) != 0) {
+ //
+ // Now restore the pre-'@' echo state.
+ //
+ ShellCommandSetEchoState(PreCommandEchoState);
+ }
+ } else {
+ if (ShellCommandGetEchoState()) {
+ CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
+ if (CurDir != NULL && StrLen(CurDir) > 1) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
+ } else {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
+ }
+ ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
+ }
+ Status = RunCommand(CommandLine3);
+ }
+ }
+
+ if (ShellCommandGetScriptExit()) {
+ //
+ // ShellCommandGetExitCode() always returns a UINT64
+ //
+ UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());
+ DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
+ InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
+
+ ShellCommandRegisterExit(FALSE, 0);
+ Status = EFI_SUCCESS;
+ RestoreBufferList(&OldBufferList);
+ break;
+ }
+ if (ShellGetExecutionBreakFlag()) {
+ RestoreBufferList(&OldBufferList);
+ break;
+ }
+ if (EFI_ERROR(Status)) {
+ RestoreBufferList(&OldBufferList);
+ break;
+ }
+ if (ShellCommandGetExit()) {
+ RestoreBufferList(&OldBufferList);
+ break;
+ }
+ }
+ //
+ // If that commend did not update the CurrentCommand then we need to advance it...
+ //
+ if (LastCommand == NewScriptFile->CurrentCommand) {
+ NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
+ if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
+ NewScriptFile->CurrentCommand->Reset = TRUE;
+ }
+ }
+ } else {
+ NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
+ if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
+ NewScriptFile->CurrentCommand->Reset = TRUE;
+ }
+ }
+ RestoreBufferList(&OldBufferList);
+ }
+
+
+ FreePool(CommandLine);
+ FreePool(CommandLine2);
+ ShellCommandSetNewScript (NULL);
+
+ //
+ // Only if this was the last script reset the state.
+ //
+ if (ShellCommandGetCurrentScriptFile()==NULL) {
+ ShellCommandSetEchoState(PreScriptEchoState);
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Function to process a NSH script file.
+
+ @param[in] ScriptPath Pointer to the script file name (including file system path).
+ @param[in] Handle the handle of the script file already opened.
+ @param[in] CmdLine the command line to run.
+ @param[in] ParamProtocol the shell parameters protocol pointer
+
+ @retval EFI_SUCCESS the script completed successfully
+**/
+EFI_STATUS
+EFIAPI
+RunScriptFile (
+ IN CONST CHAR16 *ScriptPath,
+ IN SHELL_FILE_HANDLE Handle OPTIONAL,
+ IN CONST CHAR16 *CmdLine,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol
+ )
+{
+ EFI_STATUS Status;
+ SHELL_FILE_HANDLE FileHandle;
+ UINTN Argc;
+ CHAR16 **Argv;
+
+ if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // get the argc and argv updated for scripts
+ //
+ Status = UpdateArgcArgv(ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);
+ if (!EFI_ERROR(Status)) {
+
+ if (Handle == NULL) {
+ //
+ // open the file
+ //
+ Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ if (!EFI_ERROR(Status)) {
+ //
+ // run it
+ //
+ Status = RunScriptFileHandle(FileHandle, ScriptPath);
+
+ //
+ // now close the file
+ //
+ ShellCloseFile(&FileHandle);
+ }
+ } else {
+ Status = RunScriptFileHandle(Handle, ScriptPath);
+ }
+ }
+
+ //
+ // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
+ // This is safe even if the update API failed. In this case, it may be a no-op.
+ //
+ RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
+
+ return (Status);
+}
+
diff --git a/Core/ShellPkg/Application/Shell/Shell.h b/Core/ShellPkg/Application/Shell/Shell.h
new file mode 100644
index 0000000000..a34c91ad1e
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/Shell.h
@@ -0,0 +1,408 @@
+/** @file
+ function definitions for internal to shell functions.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_INTERNAL_HEADER_
+#define _SHELL_INTERNAL_HEADER_
+
+#include <Uefi.h>
+#include <ShellBase.h>
+
+#include <Guid/ShellVariableGuid.h>
+#include <Guid/ShellAliasGuid.h>
+
+#include <Protocol/LoadedImage.h>
+#include <Protocol/SimpleTextOut.h>
+#include <Protocol/EfiShell.h>
+#include <Protocol/EfiShellInterface.h>
+#include <Protocol/EfiShellEnvironment2.h>
+#include <Protocol/EfiShellParameters.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/HiiPackageList.h>
+
+#include <Library/BaseLib.h>
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/ShellCommandLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/ShellLib.h>
+#include <Library/SortLib.h>
+#include <Library/HiiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/HandleParsingLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/UefiHiiServicesLib.h>
+
+#include "ShellParametersProtocol.h"
+#include "ShellProtocol.h"
+#include "ShellEnvVar.h"
+#include "ConsoleLogger.h"
+#include "ShellManParser.h"
+#include "ConsoleWrappers.h"
+#include "FileHandleWrappers.h"
+
+extern CONST CHAR16 mNoNestingEnvVarName[];
+extern CONST CHAR16 mNoNestingTrue[];
+extern CONST CHAR16 mNoNestingFalse[];
+
+typedef struct {
+ LIST_ENTRY Link; ///< Standard linked list handler.
+ SHELL_FILE_HANDLE *SplitStdOut; ///< ConsoleOut for use in the split.
+ SHELL_FILE_HANDLE *SplitStdIn; ///< ConsoleIn for use in the split.
+} SPLIT_LIST;
+
+typedef struct {
+ UINT32 Startup:1; ///< Was "-startup" found on command line.
+ UINT32 NoStartup:1; ///< Was "-nostartup" found on command line.
+ UINT32 NoConsoleOut:1; ///< Was "-noconsoleout" found on command line.
+ UINT32 NoConsoleIn:1; ///< Was "-noconsolein" found on command line.
+ UINT32 NoInterrupt:1; ///< Was "-nointerrupt" found on command line.
+ UINT32 NoMap:1; ///< Was "-nomap" found on command line.
+ UINT32 NoVersion:1; ///< Was "-noversion" found on command line.
+ UINT32 Delay:1; ///< Was "-delay[:n] found on command line
+ UINT32 Exit:1; ///< Was "-_exit" found on command line
+ UINT32 NoNest:1; ///< Was "-nonest" found on command line
+ UINT32 Reserved:7; ///< Extra bits
+} SHELL_BITS;
+
+typedef union {
+ SHELL_BITS Bits;
+ UINT16 AllBits;
+} SHELL_BIT_UNION;
+
+typedef struct {
+ SHELL_BIT_UNION BitUnion;
+ UINTN Delay; ///< Seconds of delay default:5.
+ CHAR16 *FileName; ///< Filename to run upon successful initialization.
+ CHAR16 *FileOptions; ///< Options to pass to FileName.
+} SHELL_INIT_SETTINGS;
+
+typedef struct {
+ BUFFER_LIST CommandHistory;
+ UINTN VisibleRowNumber;
+ UINTN OriginalVisibleRowNumber;
+ BOOLEAN InsertMode; ///< Is the current typing mode insert (FALSE = overwrite).
+} SHELL_VIEWING_SETTINGS;
+
+typedef struct {
+ EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParametersProtocol;
+ EFI_SHELL_PROTOCOL *NewEfiShellProtocol;
+ BOOLEAN PageBreakEnabled;
+ BOOLEAN RootShellInstance;
+ SHELL_INIT_SETTINGS ShellInitSettings;
+ BUFFER_LIST BufferToFreeList; ///< List of buffers that were returned to the user to free.
+ SHELL_VIEWING_SETTINGS ViewingSettings;
+ EFI_HII_HANDLE HiiHandle; ///< Handle from HiiLib.
+ UINTN LogScreenCount; ///< How many screens of log information to save.
+ EFI_EVENT UserBreakTimer; ///< Timer event for polling for CTRL-C.
+ EFI_DEVICE_PATH_PROTOCOL *ImageDevPath; ///< DevicePath for ourselves.
+ EFI_DEVICE_PATH_PROTOCOL *FileDevPath; ///< DevicePath for ourselves.
+ CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; ///< Pointer for ConsoleInformation.
+ EFI_SHELL_PARAMETERS_PROTOCOL *OldShellParameters; ///< old shell parameters to reinstall upon exiting.
+ SHELL_PROTOCOL_HANDLE_LIST OldShellList; ///< List of other instances to reinstall when closing.
+ SPLIT_LIST SplitList; ///< List of Splits in FILO stack.
+ VOID *CtrlCNotifyHandle1; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlCNotifyHandle2; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlCNotifyHandle3; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlCNotifyHandle4; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlSNotifyHandle1; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlSNotifyHandle2; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlSNotifyHandle3; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ VOID *CtrlSNotifyHandle4; ///< The NotifyHandle returned from SimpleTextInputEx.RegisterKeyNotify.
+ BOOLEAN HaltOutput; ///< TRUE to start a CTRL-S halt.
+} SHELL_INFO;
+
+#pragma pack(1)
+///
+/// HII specific Vendor Device Path definition.
+///
+typedef struct {
+ VENDOR_DEVICE_PATH VendorDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} SHELL_MAN_HII_VENDOR_DEVICE_PATH;
+#pragma pack()
+
+extern SHELL_INFO ShellInfoObject;
+
+/**
+ Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
+
+ @param[in,out] CmdLine pointer to the command line to update
+
+ @retval EFI_SUCCESS The operation was successful
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+ @return some other error occurred
+**/
+EFI_STATUS
+EFIAPI
+ProcessCommandLineToFinal(
+ IN OUT CHAR16 **CmdLine
+ );
+
+/**
+ Function to update the shell variable "lasterror".
+
+ @param[in] ErrorCode the error code to put into lasterror
+**/
+EFI_STATUS
+EFIAPI
+SetLastError(
+ IN CONST SHELL_STATUS ErrorCode
+ );
+
+/**
+ Sets all the alias' that were registered with the ShellCommandLib library.
+
+ @retval EFI_SUCCESS all init commands were run successfully.
+**/
+EFI_STATUS
+EFIAPI
+SetBuiltInAlias(
+ VOID
+ );
+
+/**
+ This function will populate the 2 device path protocol parameters based on the
+ global gImageHandle. the DevPath will point to the device path for the handle that has
+ loaded image protocol installed on it. the FilePath will point to the device path
+ for the file that was loaded.
+
+ @param[in, out] DevPath on a successful return the device path to the loaded image
+ @param[in, out] FilePath on a successful return the device path to the file
+
+ @retval EFI_SUCCESS the 2 device paths were successfully returned.
+ @return other a error from gBS->HandleProtocol
+
+ @sa HandleProtocol
+**/
+EFI_STATUS
+EFIAPI
+GetDevicePathsForImageAndFile (
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
+ );
+
+/**
+ Process all Uefi Shell 2.0 command line options.
+
+ see Uefi Shell 2.0 section 3.2 for full details.
+
+ the command line should resemble the following:
+
+ shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
+
+ ShellOpt options Options which control the initialization behavior of the shell.
+ These options are read from the EFI global variable "ShellOpt"
+ and are processed before options or file-name.
+
+ options Options which control the initialization behavior of the shell.
+
+ file-name The name of a UEFI shell application or script to be executed
+ after initialization is complete. By default, if file-name is
+ specified, then -nostartup is implied. Scripts are not supported
+ by level 0.
+
+ file-nameoptions The command-line options that are passed to file-name when it
+ is invoked.
+
+ This will initialize the ShellInitSettings global variable.
+
+ @retval EFI_SUCCESS the variable is initialized.
+**/
+EFI_STATUS
+EFIAPI
+ProcessCommandLine(
+ VOID
+ );
+
+/**
+ Handles all interaction with the default startup script.
+
+ this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
+
+ @param[in] ImagePath The path to the image for shell. The first place to look for the startup script.
+ @param[in] FilePath The path to the file for shell. The second place to look for the startup script.
+
+ @retval EFI_SUCCESS The variable is initialized.
+**/
+EFI_STATUS
+EFIAPI
+DoStartupScript(
+ IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ );
+
+/**
+ Function to perform the shell prompt looping. It will do a single prompt,
+ dispatch the result, and then return. It is expected that the caller will
+ call this function in a loop many times.
+
+ @retval EFI_SUCCESS
+ @retval RETURN_ABORTED
+**/
+EFI_STATUS
+EFIAPI
+DoShellPrompt (
+ VOID
+ );
+
+/**
+ Add a buffer to the Buffer To Free List for safely returning buffers to other
+ places without risking letting them modify internal shell information.
+
+ @param Buffer Something to pass to FreePool when the shell is exiting.
+**/
+VOID*
+EFIAPI
+AddBufferToFreeList(
+ VOID *Buffer
+ );
+
+/**
+ Add a buffer to the Command History List.
+
+ @param Buffer[in] The line buffer to add.
+**/
+VOID
+EFIAPI
+AddLineToCommandHistory(
+ IN CONST CHAR16 *Buffer
+ );
+
+/**
+ Function will process and run a command line.
+
+ This will determine if the command line represents an internal shell command or dispatch an external application.
+
+ @param[in] CmdLine the command line to parse
+
+ @retval EFI_SUCCESS the command was completed
+ @retval EFI_ABORTED the command's operation was aborted
+**/
+EFI_STATUS
+EFIAPI
+RunCommand(
+ IN CONST CHAR16 *CmdLine
+ );
+
+/**
+ Function will process and run a command line.
+
+ This will determine if the command line represents an internal shell
+ command or dispatch an external application.
+
+ @param[in] CmdLine The command line to parse.
+ @param[out] CommandStatus The status from the command line.
+
+ @retval EFI_SUCCESS The command was completed.
+ @retval EFI_ABORTED The command's operation was aborted.
+**/
+EFI_STATUS
+EFIAPI
+RunShellCommand(
+ IN CONST CHAR16 *CmdLine,
+ OUT EFI_STATUS *CommandStatus
+ );
+
+/**
+ Function determines if the CommandName COULD be a valid command. It does not determine whether
+ this is a valid command. It only checks for invalid characters.
+
+ @param[in] CommandName The name to check
+
+ @retval TRUE CommandName could be a command name
+ @retval FALSE CommandName could not be a valid command name
+**/
+BOOLEAN
+EFIAPI
+IsValidCommandName(
+ IN CONST CHAR16 *CommandName
+ );
+
+/**
+ Function to process a NSH script file via SHELL_FILE_HANDLE.
+
+ @param[in] Handle The handle to the already opened file.
+ @param[in] Name The name of the script file.
+
+ @retval EFI_SUCCESS the script completed successfully
+**/
+EFI_STATUS
+EFIAPI
+RunScriptFileHandle (
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Name
+ );
+
+/**
+ Function to process a NSH script file.
+
+ @param[in] ScriptPath Pointer to the script file name (including file system path).
+ @param[in] Handle the handle of the script file already opened.
+ @param[in] CmdLine the command line to run.
+ @param[in] ParamProtocol the shell parameters protocol pointer
+
+ @retval EFI_SUCCESS the script completed successfully
+**/
+EFI_STATUS
+EFIAPI
+RunScriptFile (
+ IN CONST CHAR16 *ScriptPath,
+ IN SHELL_FILE_HANDLE Handle OPTIONAL,
+ IN CONST CHAR16 *CmdLine,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol
+ );
+
+/**
+ Cleans off leading and trailing spaces and tabs.
+
+ @param[in] String pointer to the string to trim them off.
+**/
+EFI_STATUS
+EFIAPI
+TrimSpaces(
+ IN CHAR16 **String
+ );
+
+/**
+
+ Create a new buffer list and stores the old one to OldBufferList
+
+ @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
+**/
+VOID
+SaveBufferList (
+ OUT LIST_ENTRY *OldBufferList
+ );
+
+/**
+ Restore previous nodes into BufferToFreeList .
+
+ @param OldBufferList The temporary list head used to store the nodes in BufferToFreeList.
+**/
+VOID
+RestoreBufferList (
+ IN OUT LIST_ENTRY *OldBufferList
+ );
+
+
+
+#endif //_SHELL_INTERNAL_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/Shell.inf b/Core/ShellPkg/Application/Shell/Shell.inf
new file mode 100644
index 0000000000..a1b6a24e87
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/Shell.inf
@@ -0,0 +1,112 @@
+## @file
+# This is the shell application
+#
+# (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
+# Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = Shell
+ FILE_GUID = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = UefiMain
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ Shell.c
+ Shell.h
+ ShellParametersProtocol.c
+ ShellParametersProtocol.h
+ ShellProtocol.c
+ ShellProtocol.h
+ FileHandleWrappers.c
+ FileHandleWrappers.h
+ FileHandleInternal.h
+ ShellEnvVar.c
+ ShellEnvVar.h
+ ShellManParser.c
+ ShellManParser.h
+ Shell.uni
+ ConsoleLogger.c
+ ConsoleLogger.h
+ ConsoleWrappers.c
+ ConsoleWrappers.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ ShellPkg/ShellPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ UefiApplicationEntryPoint
+ UefiLib
+ DebugLib
+ MemoryAllocationLib
+ ShellCommandLib
+ UefiRuntimeServicesTableLib
+ UefiBootServicesTableLib
+ DevicePathLib
+ BaseMemoryLib
+ PcdLib
+ FileHandleLib
+ PrintLib
+ HiiLib
+ SortLib
+ HandleParsingLib
+ UefiHiiServicesLib
+
+[Guids]
+ gShellVariableGuid ## SOMETIMES_CONSUMES ## GUID
+ gShellAliasGuid ## SOMETIMES_CONSUMES ## GUID
+ gShellAliasGuid ## SOMETIMES_PRODUCES ## GUID
+
+[Protocols]
+ gEfiShellProtocolGuid ## PRODUCES
+ ## SOMETIMES_CONSUMES
+ gEfiShellParametersProtocolGuid ## PRODUCES
+ ## SOMETIMES_CONSUMES
+
+# gEfiShellEnvironment2Guid ## SOMETIMES_PRODUCES
+# gEfiShellInterfaceGuid ## SOMETIMES_PRODUCES
+
+ gEfiSimpleTextInputExProtocolGuid ## CONSUMES
+ gEfiSimpleTextInProtocolGuid ## CONSUMES
+ gEfiSimpleTextOutProtocolGuid ## CONSUMES
+ gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiLoadedImageProtocolGuid ## CONSUMES
+ gEfiComponentName2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiUnicodeCollation2ProtocolGuid ## CONSUMES
+ gEfiDevicePathProtocolGuid ## CONSUMES
+ gEfiHiiPackageListProtocolGuid ## SOMETIMES_PRODUCES
+
+[Pcd]
+ gEfiShellPkgTokenSpaceGuid.PcdShellSupportLevel ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellSupportOldProtocols ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellRequireHiiPlatform ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellSupportFrameworkHii ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellPageBreakDefault ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellInsertModeDefault ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellScreenLogCount ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellMapNameLength ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellPrintBufferSize ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellForceConsole ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellSupplier ## CONSUMES
+ gEfiShellPkgTokenSpaceGuid.PcdShellMaxHistoryCommandCount ## CONSUMES
diff --git a/Core/ShellPkg/Application/Shell/Shell.uni b/Core/ShellPkg/Application/Shell/Shell.uni
new file mode 100644
index 0000000000..ef69f891d2
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/Shell.uni
@@ -0,0 +1,58 @@
+// *++
+//
+// (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
+// Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved. <BR>
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+// Module Name:
+//
+// ShellStrings.uni
+//
+// Abstract:
+//
+// String definitions for the main UEFI Shell 2.0 application
+//
+// Revision History:
+//
+// --*/
+
+/=#
+
+#langdef en-US "English"
+
+#string STR_SHELL_CURDIR #language en-US "%E%s\> %N"
+#string STR_GEN_PROBLEM #language en-US "The argument '%B%s%N' is incorrect.\r\n"
+#string STR_SHELL_SHELL #language en-US "%EShell> %N"
+
+#string STR_SHELL_STARTUP_QUESTION #language en-US "%NPress %HESC%N in %d seconds to skip %Estartup.nsh%N or any other key to continue."
+
+#string STR_SHELL_ENTER_TO_CONT #language en-US "%NPress ENTER to continue:%E"
+#string STR_SHELL_QUIT_CONT #language en-US "%NPress ENTER to continue or 'Q' break:%E"
+
+#string STR_SHELL_NOT_FOUND #language en-US "%N'%E%s%N' is not recognized as an internal or external command, operable program, or script file.\r\n"
+
+#string STR_SHELL_CRLF #language en-US "\r\n"
+
+#string STR_SHELL_ERROR #language en-US "%NCommand Error Status: %r\r\n"
+#string STR_SHELL_ERROR_SCRIPT #language en-US "%NScript Error Status: %r (line number %d)\r\n"
+
+#string STR_SHELL_INVALID_MAPPING #language en-US "%N'%B%s%N' is not a valid mapping.\r\n"
+#string STR_SHELL_INVALID_SPLIT #language en-US "Invalid use of pipe (%B|%N).\r\n"
+
+#string STR_SHELL_INVALID_REDIR #language en-US "Unable to redirect file.\r\n"
+#string STR_SHELL_REDUNDA_REDIR #language en-US "Redundant redirection specified.\r\n"
+
+#string STR_VER_OUTPUT_MAIN_SHELL #language en-US "UEFI %s Shell v%d.%d\r\n"
+#string STR_VER_OUTPUT_MAIN_SUPPLIER #language en-US "%s\r\n"
+#string STR_VER_OUTPUT_MAIN_UEFI #language en-US "UEFI v%d.%02d (%s, 0x%08x)\r\n"
+
+#string STR_SHELL_NO_IN_EX #language en-US "No SimpleTextInputEx was found. CTRL-based features are not usable.\r\n"
+
+#string STR_SHELL_IMAGE_NOT_APP #language en-US "The image is not an application.\r\n"
+
diff --git a/Core/ShellPkg/Application/Shell/ShellEnvVar.c b/Core/ShellPkg/Application/Shell/ShellEnvVar.c
new file mode 100644
index 0000000000..77bf599eab
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellEnvVar.c
@@ -0,0 +1,576 @@
+/** @file
+ function declarations for shell environment functions.
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+#define INIT_NAME_BUFFER_SIZE 128
+#define INIT_DATA_BUFFER_SIZE 1024
+
+//
+// The list is used to cache the environment variables.
+//
+ENV_VAR_LIST gShellEnvVarList;
+
+/**
+ Reports whether an environment variable is Volatile or Non-Volatile.
+
+ @param EnvVarName The name of the environment variable in question
+ @param Volatile Return TRUE if the environment variable is volatile
+
+ @retval EFI_SUCCESS The volatile attribute is returned successfully
+ @retval others Some errors happened.
+**/
+EFI_STATUS
+IsVolatileEnv (
+ IN CONST CHAR16 *EnvVarName,
+ OUT BOOLEAN *Volatile
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ VOID *Buffer;
+ UINT32 Attribs;
+
+ ASSERT (Volatile != NULL);
+
+ Size = 0;
+ Buffer = NULL;
+
+ //
+ // get the variable
+ //
+ Status = gRT->GetVariable((CHAR16*)EnvVarName,
+ &gShellVariableGuid,
+ &Attribs,
+ &Size,
+ Buffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Buffer = AllocateZeroPool(Size);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = gRT->GetVariable((CHAR16*)EnvVarName,
+ &gShellVariableGuid,
+ &Attribs,
+ &Size,
+ Buffer);
+ FreePool(Buffer);
+ }
+ //
+ // not found means volatile
+ //
+ if (Status == EFI_NOT_FOUND) {
+ *Volatile = TRUE;
+ return EFI_SUCCESS;
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // check for the Non Volatile bit
+ //
+ *Volatile = !(BOOLEAN) ((Attribs & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE);
+ return EFI_SUCCESS;
+}
+
+/**
+ free function for ENV_VAR_LIST objects.
+
+ @param[in] List The pointer to pointer to list.
+**/
+VOID
+EFIAPI
+FreeEnvironmentVariableList(
+ IN LIST_ENTRY *List
+ )
+{
+ ENV_VAR_LIST *Node;
+
+ ASSERT (List != NULL);
+ if (List == NULL) {
+ return;
+ }
+
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(List)
+ ; !IsListEmpty(List)
+ ; Node = (ENV_VAR_LIST*)GetFirstNode(List)
+ ){
+ ASSERT(Node != NULL);
+ RemoveEntryList(&Node->Link);
+ if (Node->Key != NULL) {
+ FreePool(Node->Key);
+ }
+ if (Node->Val != NULL) {
+ FreePool(Node->Val);
+ }
+ FreePool(Node);
+ }
+}
+
+/**
+ Creates a list of all Shell-Guid-based environment variables.
+
+ @param[in, out] ListHead The pointer to pointer to LIST ENTRY object for
+ storing this list.
+
+ @retval EFI_SUCCESS the list was created sucessfully.
+**/
+EFI_STATUS
+EFIAPI
+GetEnvironmentVariableList(
+ IN OUT LIST_ENTRY *ListHead
+ )
+{
+ CHAR16 *VariableName;
+ UINTN NameSize;
+ UINTN NameBufferSize;
+ EFI_STATUS Status;
+ EFI_GUID Guid;
+ UINTN ValSize;
+ UINTN ValBufferSize;
+ ENV_VAR_LIST *VarList;
+
+ if (ListHead == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = EFI_SUCCESS;
+
+ ValBufferSize = INIT_DATA_BUFFER_SIZE;
+ NameBufferSize = INIT_NAME_BUFFER_SIZE;
+ VariableName = AllocateZeroPool(NameBufferSize);
+ if (VariableName == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ *VariableName = CHAR_NULL;
+
+ while (!EFI_ERROR(Status)) {
+ NameSize = NameBufferSize;
+ Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
+ if (Status == EFI_NOT_FOUND){
+ Status = EFI_SUCCESS;
+ break;
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
+ SHELL_FREE_NON_NULL(VariableName);
+ VariableName = AllocateZeroPool(NameBufferSize);
+ if (VariableName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ NameSize = NameBufferSize;
+ Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
+ }
+
+ if (!EFI_ERROR(Status) && CompareGuid(&Guid, &gShellVariableGuid)){
+ VarList = AllocateZeroPool(sizeof(ENV_VAR_LIST));
+ if (VarList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ ValSize = ValBufferSize;
+ VarList->Val = AllocateZeroPool(ValSize);
+ if (VarList->Val == NULL) {
+ SHELL_FREE_NON_NULL(VarList);
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(VariableName, &VarList->Atts, &ValSize, VarList->Val);
+ if (Status == EFI_BUFFER_TOO_SMALL){
+ ValBufferSize = ValSize > ValBufferSize * 2 ? ValSize : ValBufferSize * 2;
+ SHELL_FREE_NON_NULL (VarList->Val);
+ VarList->Val = AllocateZeroPool(ValBufferSize);
+ if (VarList->Val == NULL) {
+ SHELL_FREE_NON_NULL(VarList);
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ ValSize = ValBufferSize;
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(VariableName, &VarList->Atts, &ValSize, VarList->Val);
+ }
+ if (!EFI_ERROR(Status)) {
+ VarList->Key = AllocateCopyPool(StrSize(VariableName), VariableName);
+ if (VarList->Key == NULL) {
+ SHELL_FREE_NON_NULL(VarList->Val);
+ SHELL_FREE_NON_NULL(VarList);
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ InsertTailList(ListHead, &VarList->Link);
+ }
+ } else {
+ SHELL_FREE_NON_NULL(VarList->Val);
+ SHELL_FREE_NON_NULL(VarList);
+ }
+ } // if (VarList == NULL) ... else ...
+ } // compare guid
+ } // while
+ SHELL_FREE_NON_NULL (VariableName);
+
+ if (EFI_ERROR(Status)) {
+ FreeEnvironmentVariableList(ListHead);
+ }
+
+ return (Status);
+}
+
+/**
+ Sets a list of all Shell-Guid-based environment variables. this will
+ also eliminate all existing shell environment variables (even if they
+ are not on the list).
+
+ This function will also deallocate the memory from List.
+
+ @param[in] ListHead The pointer to LIST_ENTRY from
+ GetShellEnvVarList().
+
+ @retval EFI_SUCCESS the list was Set sucessfully.
+**/
+EFI_STATUS
+EFIAPI
+SetEnvironmentVariableList(
+ IN LIST_ENTRY *ListHead
+ )
+{
+ ENV_VAR_LIST VarList;
+ ENV_VAR_LIST *Node;
+ EFI_STATUS Status;
+ UINTN Size;
+
+ InitializeListHead(&VarList.Link);
+
+ //
+ // Delete all the current environment variables
+ //
+ Status = GetEnvironmentVariableList(&VarList.Link);
+ ASSERT_EFI_ERROR(Status);
+
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&VarList.Link)
+ ; !IsNull(&VarList.Link, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&VarList.Link, &Node->Link)
+ ){
+ if (Node->Key != NULL) {
+ Status = SHELL_DELETE_ENVIRONMENT_VARIABLE(Node->Key);
+ }
+ ASSERT_EFI_ERROR(Status);
+ }
+
+ FreeEnvironmentVariableList(&VarList.Link);
+
+ //
+ // set all the variables fron the list
+ //
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(ListHead)
+ ; !IsNull(ListHead, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(ListHead, &Node->Link)
+ ){
+ Size = StrSize(Node->Val);
+ if (Node->Atts & EFI_VARIABLE_NON_VOLATILE) {
+ Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV(Node->Key, Size, Node->Val);
+ } else {
+ Status = SHELL_SET_ENVIRONMENT_VARIABLE_V (Node->Key, Size, Node->Val);
+ }
+ ASSERT_EFI_ERROR(Status);
+ }
+ FreeEnvironmentVariableList(ListHead);
+
+ return (Status);
+}
+
+/**
+ sets a list of all Shell-Guid-based environment variables.
+
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+
+ @sa SetEnvironmentVariableList
+**/
+EFI_STATUS
+EFIAPI
+SetEnvironmentVariables(
+ IN CONST CHAR16 **Environment
+ )
+{
+ CONST CHAR16 *CurrentString;
+ UINTN CurrentCount;
+ ENV_VAR_LIST *VarList;
+ ENV_VAR_LIST *Node;
+
+ VarList = NULL;
+
+ if (Environment == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // Build a list identical to the ones used for get/set list functions above
+ //
+ for ( CurrentCount = 0
+ ;
+ ; CurrentCount++
+ ){
+ CurrentString = Environment[CurrentCount];
+ if (CurrentString == NULL) {
+ break;
+ }
+ ASSERT(StrStr(CurrentString, L"=") != NULL);
+ Node = AllocateZeroPool(sizeof(ENV_VAR_LIST));
+ if (Node == NULL) {
+ SetEnvironmentVariableList(&VarList->Link);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ Node->Key = AllocateZeroPool((StrStr(CurrentString, L"=") - CurrentString + 1) * sizeof(CHAR16));
+ if (Node->Key == NULL) {
+ SHELL_FREE_NON_NULL(Node);
+ SetEnvironmentVariableList(&VarList->Link);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ //
+ // Copy the string into the Key, leaving the last character allocated as NULL to terminate
+ //
+ StrnCpyS( Node->Key,
+ StrStr(CurrentString, L"=") - CurrentString + 1,
+ CurrentString,
+ StrStr(CurrentString, L"=") - CurrentString
+ );
+
+ //
+ // ValueSize = TotalSize - already removed size - size for '=' + size for terminator (the last 2 items cancel each other)
+ //
+ Node->Val = AllocateCopyPool(StrSize(CurrentString) - StrSize(Node->Key), CurrentString + StrLen(Node->Key) + 1);
+ if (Node->Val == NULL) {
+ SHELL_FREE_NON_NULL(Node->Key);
+ SHELL_FREE_NON_NULL(Node);
+ SetEnvironmentVariableList(&VarList->Link);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ Node->Atts = EFI_VARIABLE_BOOTSERVICE_ACCESS;
+
+ if (VarList == NULL) {
+ VarList = AllocateZeroPool(sizeof(ENV_VAR_LIST));
+ if (VarList == NULL) {
+ SHELL_FREE_NON_NULL(Node->Key);
+ SHELL_FREE_NON_NULL(Node->Val);
+ SHELL_FREE_NON_NULL(Node);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ InitializeListHead(&VarList->Link);
+ }
+ InsertTailList(&VarList->Link, &Node->Link);
+
+ } // for loop
+
+ //
+ // set this new list as the set of all environment variables.
+ // this function also frees the memory and deletes all pre-existing
+ // shell-guid based environment variables.
+ //
+ return (SetEnvironmentVariableList(&VarList->Link));
+}
+
+/**
+ Find an environment variable in the gShellEnvVarList.
+
+ @param Key The name of the environment variable.
+ @param Value The value of the environment variable, the buffer
+ shoule be freed by the caller.
+ @param ValueSize The size in bytes of the environment variable
+ including the tailing CHAR_NELL.
+ @param Atts The attributes of the variable.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_NOT_FOUND The environment variable is not found in
+ gShellEnvVarList.
+
+**/
+EFI_STATUS
+ShellFindEnvVarInList (
+ IN CONST CHAR16 *Key,
+ OUT CHAR16 **Value,
+ OUT UINTN *ValueSize,
+ OUT UINT32 *Atts OPTIONAL
+ )
+{
+ ENV_VAR_LIST *Node;
+
+ if (Key == NULL || Value == NULL || ValueSize == NULL) {
+ return SHELL_INVALID_PARAMETER;
+ }
+
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
+ ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
+ ){
+ if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
+ *Value = AllocateCopyPool(StrSize(Node->Val), Node->Val);
+ *ValueSize = StrSize(Node->Val);
+ if (Atts != NULL) {
+ *Atts = Node->Atts;
+ }
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Add an environment variable into gShellEnvVarList.
+
+ @param Key The name of the environment variable.
+ @param Value The value of environment variable.
+ @param ValueSize The size in bytes of the environment variable
+ including the tailing CHAR_NULL
+ @param Atts The attributes of the variable.
+
+ @retval EFI_SUCCESS The environment variable was added to list successfully.
+ @retval others Some errors happened.
+
+**/
+EFI_STATUS
+ShellAddEnvVarToList (
+ IN CONST CHAR16 *Key,
+ IN CONST CHAR16 *Value,
+ IN UINTN ValueSize,
+ IN UINT32 Atts
+ )
+{
+ ENV_VAR_LIST *Node;
+ CHAR16 *LocalKey;
+ CHAR16 *LocalValue;
+
+ if (Key == NULL || Value == NULL || ValueSize == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ LocalValue = AllocateCopyPool (ValueSize, Value);
+ if (LocalValue == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Update the variable value if it exists in gShellEnvVarList.
+ //
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
+ ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
+ ){
+ if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
+ Node->Atts = Atts;
+ SHELL_FREE_NON_NULL(Node->Val);
+ Node->Val = LocalValue;
+ return EFI_SUCCESS;
+ }
+ }
+
+ //
+ // If the environment varialbe key doesn't exist in list just insert
+ // a new node.
+ //
+ LocalKey = AllocateCopyPool (StrSize(Key), Key);
+ if (LocalKey == NULL) {
+ FreePool (LocalValue);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Node = (ENV_VAR_LIST*)AllocateZeroPool (sizeof(ENV_VAR_LIST));
+ if (Node == NULL) {
+ FreePool (LocalKey);
+ FreePool (LocalValue);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Node->Key = LocalKey;
+ Node->Val = LocalValue;
+ Node->Atts = Atts;
+ InsertTailList(&gShellEnvVarList.Link, &Node->Link);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Remove a specified environment variable in gShellEnvVarList.
+
+ @param Key The name of the environment variable.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_NOT_FOUND The environment variable is not found in
+ gShellEnvVarList.
+**/
+EFI_STATUS
+ShellRemvoeEnvVarFromList (
+ IN CONST CHAR16 *Key
+ )
+{
+ ENV_VAR_LIST *Node;
+
+ if (Key == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
+ ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
+ ){
+ if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
+ SHELL_FREE_NON_NULL(Node->Key);
+ SHELL_FREE_NON_NULL(Node->Val);
+ RemoveEntryList(&Node->Link);
+ SHELL_FREE_NON_NULL(Node);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
+ variables.
+
+**/
+EFI_STATUS
+ShellInitEnvVarList (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ InitializeListHead(&gShellEnvVarList.Link);
+ Status = GetEnvironmentVariableList (&gShellEnvVarList.Link);
+
+ return Status;
+}
+
+/**
+ Destructe the gShellEnvVarList.
+
+**/
+VOID
+ShellFreeEnvVarList (
+ VOID
+ )
+{
+ FreeEnvironmentVariableList (&gShellEnvVarList.Link);
+ InitializeListHead(&gShellEnvVarList.Link);
+
+ return;
+}
+
diff --git a/Core/ShellPkg/Application/Shell/ShellEnvVar.h b/Core/ShellPkg/Application/Shell/ShellEnvVar.h
new file mode 100644
index 0000000000..f05ef2bd66
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellEnvVar.h
@@ -0,0 +1,292 @@
+/** @file
+ function definitions for shell environment functions.
+
+ the following includes are required:
+//#include <Guid/ShellVariableGuid.h>
+//#include <Library/UefiRuntimeServicesTableLib.h>
+
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_ENVIRONMENT_VARIABLE_HEADER_
+#define _SHELL_ENVIRONMENT_VARIABLE_HEADER_
+
+typedef struct {
+ LIST_ENTRY Link;
+ CHAR16 *Key;
+ CHAR16 *Val;
+ UINT32 Atts;
+} ENV_VAR_LIST;
+
+//
+// The list is used to cache the environment variables.
+//
+extern ENV_VAR_LIST gShellEnvVarList;
+
+
+/**
+ Reports whether an environment variable is Volatile or Non-Volatile.
+
+ @param EnvVarName The name of the environment variable in question
+ @param Volatile Return TRUE if the environment variable is volatile
+
+ @retval EFI_SUCCESS The volatile attribute is returned successfully
+ @retval others Some errors happened.
+**/
+EFI_STATUS
+IsVolatileEnv (
+ IN CONST CHAR16 *EnvVarName,
+ OUT BOOLEAN *Volatile
+ );
+
+/**
+ Delete a Non-Violatile environment variable.
+
+ This will use the Runtime Services call SetVariable to remove a non-violatile variable.
+
+ @param EnvVarName The name of the environment variable in question
+
+ @retval EFI_SUCCESS The variable was deleted sucessfully
+ @retval other An error ocurred
+ @sa SetVariable
+**/
+#define SHELL_DELETE_ENVIRONMENT_VARIABLE(EnvVarName) \
+ (gRT->SetVariable((CHAR16*)EnvVarName, \
+ &gShellVariableGuid, \
+ 0, \
+ 0, \
+ NULL))
+
+/**
+ Set a Non-Violatile environment variable.
+
+ This will use the Runtime Services call SetVariable to set a non-violatile variable.
+
+ @param EnvVarName The name of the environment variable in question
+ @param BufferSize UINTN size of Buffer
+ @param Buffer Pointer to value to set variable to
+
+ @retval EFI_SUCCESS The variable was changed sucessfully
+ @retval other An error ocurred
+ @sa SetVariable
+**/
+#define SHELL_SET_ENVIRONMENT_VARIABLE_NV(EnvVarName,BufferSize,Buffer) \
+ (gRT->SetVariable((CHAR16*)EnvVarName, \
+ &gShellVariableGuid, \
+ EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, \
+ BufferSize, \
+ (VOID*)Buffer))
+
+/**
+ Get an environment variable.
+
+ This will use the Runtime Services call GetVariable to get a variable.
+
+ @param EnvVarName The name of the environment variable in question
+ @param BufferSize Pointer to the UINTN size of Buffer
+ @param Buffer Pointer buffer to get variable value into
+
+ @retval EFI_SUCCESS The variable's value was retrieved sucessfully
+ @retval other An error ocurred
+ @sa SetVariable
+**/
+#define SHELL_GET_ENVIRONMENT_VARIABLE(EnvVarName,BufferSize,Buffer) \
+ (gRT->GetVariable((CHAR16*)EnvVarName, \
+ &gShellVariableGuid, \
+ 0, \
+ BufferSize, \
+ Buffer))
+
+/**
+ Get an environment variable.
+
+ This will use the Runtime Services call GetVariable to get a variable.
+
+ @param EnvVarName The name of the environment variable in question
+ @param Atts Pointer to the UINT32 for attributes (or NULL)
+ @param BufferSize Pointer to the UINTN size of Buffer
+ @param Buffer Pointer buffer to get variable value into
+
+ @retval EFI_SUCCESS The variable's value was retrieved sucessfully
+ @retval other An error ocurred
+ @sa SetVariable
+**/
+#define SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(EnvVarName,Atts,BufferSize,Buffer) \
+ (gRT->GetVariable((CHAR16*)EnvVarName, \
+ &gShellVariableGuid, \
+ Atts, \
+ BufferSize, \
+ Buffer))
+
+/**
+ Set a Violatile environment variable.
+
+ This will use the Runtime Services call SetVariable to set a violatile variable.
+
+ @param EnvVarName The name of the environment variable in question
+ @param BufferSize UINTN size of Buffer
+ @param Buffer Pointer to value to set variable to
+
+ @retval EFI_SUCCESS The variable was changed sucessfully
+ @retval other An error ocurred
+ @sa SetVariable
+**/
+#define SHELL_SET_ENVIRONMENT_VARIABLE_V(EnvVarName,BufferSize,Buffer) \
+ (gRT->SetVariable((CHAR16*)EnvVarName, \
+ &gShellVariableGuid, \
+ EFI_VARIABLE_BOOTSERVICE_ACCESS, \
+ BufferSize, \
+ (VOID*)Buffer))
+
+/**
+ Creates a list of all Shell-Guid-based environment variables.
+
+ @param[in, out] List The pointer to pointer to LIST_ENTRY object for
+ storing this list.
+
+ @retval EFI_SUCCESS the list was created sucessfully.
+**/
+EFI_STATUS
+EFIAPI
+GetEnvironmentVariableList(
+ IN OUT LIST_ENTRY *List
+ );
+
+/**
+ Sets a list of all Shell-Guid-based environment variables. this will
+ also eliminate all pre-existing shell environment variables (even if they
+ are not on the list).
+
+ This function will also deallocate the memory from List.
+
+ @param[in] List The pointer to LIST_ENTRY from
+ GetShellEnvVarList().
+
+ @retval EFI_SUCCESS The list was Set sucessfully.
+**/
+EFI_STATUS
+EFIAPI
+SetEnvironmentVariableList(
+ IN LIST_ENTRY *List
+ );
+
+/**
+ sets all Shell-Guid-based environment variables. this will
+ also eliminate all pre-existing shell environment variables (even if they
+ are not on the list).
+
+ @param[in] Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+
+ @sa SetEnvironmentVariableList
+**/
+EFI_STATUS
+EFIAPI
+SetEnvironmentVariables(
+ IN CONST CHAR16 **Environment
+ );
+
+/**
+ free function for ENV_VAR_LIST objects.
+
+ @param[in] List The pointer to pointer to list.
+**/
+VOID
+EFIAPI
+FreeEnvironmentVariableList(
+ IN LIST_ENTRY *List
+ );
+
+/**
+ Find an environment variable in the gShellEnvVarList.
+
+ @param Key The name of the environment variable.
+ @param Value The value of the environment variable, the buffer
+ shoule be freed by the caller.
+ @param ValueSize The size in bytes of the environment variable
+ including the tailing CHAR_NULL.
+ @param Atts The attributes of the variable.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_NOT_FOUND The environment variable is not found in
+ gShellEnvVarList.
+
+**/
+EFI_STATUS
+ShellFindEnvVarInList (
+ IN CONST CHAR16 *Key,
+ OUT CHAR16 **Value,
+ OUT UINTN *ValueSize,
+ OUT UINT32 *Atts OPTIONAL
+ );
+
+/**
+ Add an environment variable into gShellEnvVarList.
+
+ @param Key The name of the environment variable.
+ @param Value The value of environment variable.
+ @param ValueSize The size in bytes of the environment variable
+ including the tailing CHAR_NULL
+ @param Atts The attributes of the variable.
+
+ @retval EFI_SUCCESS The environment variable was added to list successfully.
+ @retval others Some errors happened.
+
+**/
+EFI_STATUS
+ShellAddEnvVarToList (
+ IN CONST CHAR16 *Key,
+ IN CONST CHAR16 *Value,
+ IN UINTN ValueSize,
+ IN UINT32 Atts
+ );
+
+/**
+ Remove a specified environment variable in gShellEnvVarList.
+
+ @param Key The name of the environment variable.
+
+ @retval EFI_SUCCESS The command executed successfully.
+ @retval EFI_NOT_FOUND The environment variable is not found in
+ gShellEnvVarList.
+**/
+EFI_STATUS
+ShellRemvoeEnvVarFromList (
+ IN CONST CHAR16 *Key
+ );
+
+/**
+ Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
+ variables.
+
+**/
+EFI_STATUS
+ShellInitEnvVarList (
+ VOID
+ );
+
+/**
+ Destructe the gShellEnvVarList.
+
+**/
+VOID
+ShellFreeEnvVarList (
+ VOID
+ );
+
+#endif //_SHELL_ENVIRONMENT_VARIABLE_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/ShellManParser.c b/Core/ShellPkg/Application/Shell/ShellManParser.c
new file mode 100644
index 0000000000..948759fe46
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellManParser.c
@@ -0,0 +1,1013 @@
+/** @file
+ Provides interface to shell MAN file parser.
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ Copyright 2015 Dell Inc.
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+#define SHELL_MAN_HII_GUID \
+{ \
+ 0xf62ccd0c, 0x2449, 0x453c, { 0x8a, 0xcb, 0x8c, 0xc5, 0x7c, 0xf0, 0x2a, 0x97 } \
+}
+
+EFI_HII_HANDLE mShellManHiiHandle = NULL;
+EFI_HANDLE mShellManDriverHandle = NULL;
+
+
+SHELL_MAN_HII_VENDOR_DEVICE_PATH mShellManHiiDevicePath = {
+ {
+ {
+ HARDWARE_DEVICE_PATH,
+ HW_VENDOR_DP,
+ {
+ (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+ (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+ }
+ },
+ SHELL_MAN_HII_GUID
+ },
+ {
+ END_DEVICE_PATH_TYPE,
+ END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ {
+ (UINT8) (END_DEVICE_PATH_LENGTH),
+ (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
+ }
+ }
+};
+
+
+/**
+ Convert a Unicode character to upper case only if
+ it maps to a valid small-case ASCII character.
+
+ This internal function only deal with Unicode character
+ which maps to a valid small-case ASCII character, i.e.
+ L'a' to L'z'. For other Unicode character, the input character
+ is returned directly.
+
+ @param Char The character to convert.
+
+ @retval LowerCharacter If the Char is with range L'a' to L'z'.
+ @retval Unchanged Otherwise.
+
+**/
+CHAR16
+EFIAPI
+InternalShellCharToUpper (
+ IN CHAR16 Char
+ );
+
+/**
+ Verifies that the filename has .EFI on the end.
+
+ allocates a new buffer and copies the name (appending .EFI if necessary).
+ Caller to free the buffer.
+
+ @param[in] NameString original name string
+
+ @return the new filename with .efi as the extension.
+**/
+CHAR16 *
+EFIAPI
+GetExecuatableFileName (
+ IN CONST CHAR16 *NameString
+ )
+{
+ CHAR16 *Buffer;
+ CHAR16 *SuffixStr;
+ if (NameString == NULL) {
+ return (NULL);
+ }
+
+ //
+ // Fix the file name
+ //
+ if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".efi"), L".efi", StrLen(L".efi"))==0) {
+ Buffer = AllocateCopyPool(StrSize(NameString), NameString);
+ } else if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".man"), L".man", StrLen(L".man"))==0) {
+ Buffer = AllocateCopyPool(StrSize(NameString), NameString);
+ if (Buffer != NULL) {
+ SuffixStr = Buffer+StrLen(Buffer)-StrLen(L".man");
+ StrnCpyS (SuffixStr, StrSize(L".man")/sizeof(CHAR16), L".efi", StrLen(L".efi"));
+ }
+ } else {
+ Buffer = AllocateZeroPool(StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16));
+ if (Buffer != NULL) {
+ StrnCpyS( Buffer,
+ (StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
+ NameString,
+ StrLen(NameString)
+ );
+ StrnCatS( Buffer,
+ (StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
+ L".efi",
+ StrLen(L".efi")
+ );
+ }
+ }
+ return (Buffer);
+
+}
+
+/**
+ Verifies that the filename has .MAN on the end.
+
+ allocates a new buffer and copies the name (appending .MAN if necessary)
+
+ ASSERT if ManFileName is NULL
+
+ @param[in] ManFileName original filename
+
+ @return the new filename with .man as the extension.
+**/
+CHAR16 *
+EFIAPI
+GetManFileName(
+ IN CONST CHAR16 *ManFileName
+ )
+{
+ CHAR16 *Buffer;
+ if (ManFileName == NULL) {
+ return (NULL);
+ }
+ //
+ // Fix the file name
+ //
+ if (StrnCmp(ManFileName+StrLen(ManFileName)-4, L".man", 4)==0) {
+ Buffer = AllocateCopyPool(StrSize(ManFileName), ManFileName);
+ } else {
+ Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16));
+ if (Buffer != NULL) {
+ StrnCpyS( Buffer,
+ (StrSize(ManFileName) + 4*sizeof(CHAR16))/sizeof(CHAR16),
+ ManFileName,
+ StrLen(ManFileName)
+ );
+ StrnCatS( Buffer,
+ (StrSize(ManFileName) + 4*sizeof(CHAR16))/sizeof(CHAR16),
+ L".man",
+ 4
+ );
+ }
+ }
+ return (Buffer);
+}
+
+/**
+ Search the path environment variable for possible locations and test for
+ which one contains a man file with the name specified. If a valid file is found
+ stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
+
+ @param[in] FileName Name of the file to find and open.
+ @param[out] Handle Pointer to the handle of the found file. The
+ value of this is undefined for return values
+ except EFI_SUCCESS.
+
+ @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
+ @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
+ @retval EFI_NOT_FOUND The file was not found.
+**/
+EFI_STATUS
+EFIAPI
+SearchPathForFile(
+ IN CONST CHAR16 *FileName,
+ OUT SHELL_FILE_HANDLE *Handle
+ )
+{
+ CHAR16 *FullFileName;
+ EFI_STATUS Status;
+
+ if ( FileName == NULL
+ || Handle == NULL
+ || StrLen(FileName) == 0
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ FullFileName = ShellFindFilePath(FileName);
+ if (FullFileName == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // now open that file
+ //
+ Status = EfiShellOpenFileByName(FullFileName, Handle, EFI_FILE_MODE_READ);
+ FreePool(FullFileName);
+
+ return (Status);
+}
+
+/**
+ parses through Buffer (which is MAN file formatted) and returns the
+ detailed help for any sub section specified in the comma seperated list of
+ sections provided. If the end of the file or a .TH section is found then
+ return.
+
+ Upon a sucessful return the caller is responsible to free the memory in *HelpText
+
+ @param[in] Buffer Buffer to read from
+ @param[in] Sections name of command's sub sections to find
+ @param[in] HelpText pointer to pointer to string where text goes.
+ @param[in] HelpSize pointer to size of allocated HelpText (may be updated)
+
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+ @retval EFI_SUCCESS the section was found and its description sotred in
+ an alloceted buffer.
+**/
+EFI_STATUS
+EFIAPI
+ManBufferFindSections(
+ IN CONST CHAR16 *Buffer,
+ IN CONST CHAR16 *Sections,
+ IN CHAR16 **HelpText,
+ IN UINTN *HelpSize
+ )
+{
+ EFI_STATUS Status;
+ CONST CHAR16 *CurrentLocation;
+ BOOLEAN CurrentlyReading;
+ CHAR16 *SectionName;
+ UINTN SectionLen;
+ BOOLEAN Found;
+ CHAR16 *TempString;
+ CHAR16 *TempString2;
+
+ if ( Buffer == NULL
+ || HelpText == NULL
+ || HelpSize == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = EFI_SUCCESS;
+ CurrentlyReading = FALSE;
+ Found = FALSE;
+
+ for (CurrentLocation = Buffer,TempString = NULL
+ ; CurrentLocation != NULL && *CurrentLocation != CHAR_NULL
+ ; CurrentLocation=StrStr(CurrentLocation, L"\r\n"),TempString = NULL
+ ){
+ while(CurrentLocation[0] == L'\r' || CurrentLocation[0] == L'\n') {
+ CurrentLocation++;
+ }
+ if (CurrentLocation[0] == L'#') {
+ //
+ // Skip comment lines
+ //
+ continue;
+ }
+ if (StrnCmp(CurrentLocation, L".TH", 3) == 0) {
+ //
+ // we hit the end of this commands section so stop.
+ //
+ break;
+ }
+ if (StrnCmp(CurrentLocation, L".SH ", 4) == 0) {
+ if (Sections == NULL) {
+ CurrentlyReading = TRUE;
+ continue;
+ } else if (CurrentlyReading) {
+ CurrentlyReading = FALSE;
+ }
+ CurrentLocation += 4;
+ //
+ // is this a section we want to read in?
+ //
+ if (StrLen(CurrentLocation)!=0) {
+ TempString2 = StrStr(CurrentLocation, L" ");
+ TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\r"));
+ TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n"));
+ ASSERT(TempString == NULL);
+ TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation);
+ if (TempString == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ SectionName = TempString;
+ SectionLen = StrLen(SectionName);
+ SectionName = StrStr(Sections, SectionName);
+ if (SectionName == NULL) {
+ SHELL_FREE_NON_NULL(TempString);
+ continue;
+ }
+ if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
+ CurrentlyReading = TRUE;
+ }
+ }
+ } else if (CurrentlyReading) {
+ Found = TRUE;
+ if (StrLen(CurrentLocation)!=0) {
+ TempString2 = StrStr(CurrentLocation, L"\r");
+ TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n"));
+ ASSERT(TempString == NULL);
+ TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation);
+ if (TempString == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ //
+ // copy and save the current line.
+ //
+ ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
+ StrnCatGrow (HelpText, HelpSize, TempString, 0);
+ if (HelpText == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
+ if (HelpText == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ }
+ }
+ SHELL_FREE_NON_NULL(TempString);
+ }
+ SHELL_FREE_NON_NULL(TempString);
+ if (!Found && !EFI_ERROR(Status)) {
+ return (EFI_NOT_FOUND);
+ }
+ return (Status);
+}
+
+/**
+ parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
+ detailed help for any sub section specified in the comma seperated list of
+ sections provided. If the end of the file or a .TH section is found then
+ return.
+
+ Upon a sucessful return the caller is responsible to free the memory in *HelpText
+
+ @param[in] Handle FileHandle to read from
+ @param[in] Sections name of command's sub sections to find
+ @param[out] HelpText pointer to pointer to string where text goes.
+ @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
+ @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
+
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+ @retval EFI_SUCCESS the section was found and its description sotred in
+ an alloceted buffer.
+**/
+EFI_STATUS
+EFIAPI
+ManFileFindSections(
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Sections,
+ OUT CHAR16 **HelpText,
+ OUT UINTN *HelpSize,
+ IN BOOLEAN Ascii
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *ReadLine;
+ UINTN Size;
+ BOOLEAN CurrentlyReading;
+ CHAR16 *SectionName;
+ UINTN SectionLen;
+ BOOLEAN Found;
+
+ if ( Handle == NULL
+ || HelpText == NULL
+ || HelpSize == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = EFI_SUCCESS;
+ CurrentlyReading = FALSE;
+ Size = 1024;
+ Found = FALSE;
+
+ ReadLine = AllocateZeroPool(Size);
+ if (ReadLine == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ for (;!ShellFileHandleEof(Handle);Size = 1024) {
+ Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, &Ascii);
+ if (ReadLine[0] == L'#') {
+ //
+ // Skip comment lines
+ //
+ continue;
+ }
+ //
+ // ignore too small of buffer...
+ //
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Status = EFI_SUCCESS;
+ }
+ if (EFI_ERROR(Status)) {
+ break;
+ } else if (StrnCmp(ReadLine, L".TH", 3) == 0) {
+ //
+ // we hit the end of this commands section so stop.
+ //
+ break;
+ } else if (StrnCmp(ReadLine, L".SH", 3) == 0) {
+ if (Sections == NULL) {
+ CurrentlyReading = TRUE;
+ continue;
+ }
+ //
+ // we found a section
+ //
+ if (CurrentlyReading) {
+ CurrentlyReading = FALSE;
+ }
+ //
+ // is this a section we want to read in?
+ //
+ for ( SectionName = ReadLine + 3
+ ; *SectionName == L' '
+ ; SectionName++);
+ SectionLen = StrLen(SectionName);
+ SectionName = StrStr(Sections, SectionName);
+ if (SectionName == NULL) {
+ continue;
+ }
+ if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
+ CurrentlyReading = TRUE;
+ }
+ } else if (CurrentlyReading) {
+ Found = TRUE;
+ //
+ // copy and save the current line.
+ //
+ ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
+ StrnCatGrow (HelpText, HelpSize, ReadLine, 0);
+ StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
+ }
+ }
+ FreePool(ReadLine);
+ if (!Found && !EFI_ERROR(Status)) {
+ return (EFI_NOT_FOUND);
+ }
+ return (Status);
+}
+
+/**
+ parses through the MAN file formatted Buffer and returns the
+ "Brief Description" for the .TH section as specified by Command. If the
+ command section is not found return EFI_NOT_FOUND.
+
+ Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
+
+ @param[in] Buffer Buffer to read from
+ @param[in] Command name of command's section to find
+ @param[in] BriefDesc pointer to pointer to string where description goes.
+ @param[in] BriefSize pointer to size of allocated BriefDesc
+
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+ @retval EFI_SUCCESS the section was found and its description sotred in
+ an alloceted buffer.
+**/
+EFI_STATUS
+EFIAPI
+ManBufferFindTitleSection(
+ IN CHAR16 **Buffer,
+ IN CONST CHAR16 *Command,
+ IN CHAR16 **BriefDesc,
+ IN UINTN *BriefSize
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *TitleString;
+ CHAR16 *TitleEnd;
+ CHAR16 *CurrentLocation;
+ UINTN TitleLength;
+ UINTN Start;
+ CONST CHAR16 StartString[] = L".TH ";
+ CONST CHAR16 EndString[] = L" 0 ";
+
+ if ( Buffer == NULL
+ || Command == NULL
+ || (BriefDesc != NULL && BriefSize == NULL)
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Do not pass any leading path information that may be present to IsTitleHeader().
+ //
+ Start = StrLen(Command);
+ while ((Start != 0)
+ && (*(Command + Start - 1) != L'\\')
+ && (*(Command + Start - 1) != L'/')
+ && (*(Command + Start - 1) != L':')) {
+ --Start;
+ }
+
+ //
+ // more characters for StartString and EndString
+ //
+ TitleLength = StrSize(Command + Start) + (StrLen(StartString) + StrLen(EndString)) * sizeof(CHAR16);
+ TitleString = AllocateZeroPool(TitleLength);
+ if (TitleString == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ StrCpyS(TitleString, TitleLength/sizeof(CHAR16), StartString);
+ StrCatS(TitleString, TitleLength/sizeof(CHAR16), Command + Start);
+ StrCatS(TitleString, TitleLength/sizeof(CHAR16), EndString);
+
+ CurrentLocation = StrStr(*Buffer, TitleString);
+ if (CurrentLocation == NULL){
+ Status = EFI_NOT_FOUND;
+ } else {
+ //
+ // we found it so copy out the rest of the line into BriefDesc
+ // After skipping any spaces or zeroes
+ //
+ for (CurrentLocation += StrLen(TitleString)
+ ; *CurrentLocation == L' ' || *CurrentLocation == L'0' || *CurrentLocation == L'1' || *CurrentLocation == L'\"'
+ ; CurrentLocation++);
+
+ TitleEnd = StrStr(CurrentLocation, L"\"");
+ if (TitleEnd == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ if (BriefDesc != NULL) {
+ *BriefSize = StrSize(TitleEnd);
+ *BriefDesc = AllocateZeroPool(*BriefSize);
+ if (*BriefDesc == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ StrnCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), CurrentLocation, TitleEnd-CurrentLocation);
+ }
+ }
+
+ for (CurrentLocation = TitleEnd
+ ; *CurrentLocation != L'\n'
+ ; CurrentLocation++);
+ for (
+ ; *CurrentLocation == L' ' || *CurrentLocation == L'\n' || *CurrentLocation == L'\r'
+ ; CurrentLocation++);
+ *Buffer = CurrentLocation;
+ }
+ }
+
+ FreePool(TitleString);
+ return (Status);
+}
+
+/**
+ Parses a line from a MAN file to see if it is the Title Header. If it is, then
+ if the "Brief Description" is desired, allocate a buffer for it and return a
+ copy. Upon a sucessful return the caller is responsible to free the memory in
+ *BriefDesc
+
+ Uses a simple state machine that allows "unlimited" whitespace before and after the
+ ".TH", compares Command and the MAN file commnd name without respect to case, and
+ allows "unlimited" whitespace and '0' and '1' characters before the Short Description.
+ The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
+ where group 1 is the Command Name and group 2 is the Short Description.
+
+ @param[in] Command name of command whose MAN file we think Line came from
+ @param[in] Line Pointer to a line from the MAN file
+ @param[out] BriefDesc pointer to pointer to string where description goes.
+ @param[out] BriefSize pointer to size of allocated BriefDesc
+ @param[out] Found TRUE if the Title Header was found and it belongs to Command
+
+ @retval TRUE Line contained the Title Header
+ @retval FALSE Line did not contain the Title Header
+**/
+BOOLEAN
+IsTitleHeader(
+ IN CONST CHAR16 *Command,
+ IN CHAR16 *Line,
+ OUT CHAR16 **BriefDesc OPTIONAL,
+ OUT UINTN *BriefSize OPTIONAL,
+ OUT BOOLEAN *Found
+ )
+{
+ // The states of a simple state machine used to recognize a title header line
+ // and to extract the Short Description, if desired.
+ typedef enum {
+ LookForThMacro, LookForCommandName, CompareCommands, GetBriefDescription, Final
+ } STATEVALUES;
+
+ STATEVALUES State;
+ UINTN CommandIndex; // Indexes Command as we compare its chars to the MAN file.
+ BOOLEAN ReturnValue; // TRUE if this the Title Header line of *some* MAN file.
+ BOOLEAN ReturnFound; // TRUE if this the Title Header line of *the desired* MAN file.
+
+ ReturnValue = FALSE;
+ ReturnFound = FALSE;
+ CommandIndex = 0;
+ State = LookForThMacro;
+
+ do {
+
+ if (*Line == L'\0') {
+ break;
+ }
+
+ switch (State) {
+
+ // Handle "^\s*.TH\s"
+ // Go to state LookForCommandName if the title header macro is present; otherwise,
+ // eat white space. If we see something other than white space, this is not a
+ // title header line.
+ case LookForThMacro:
+ if (StrnCmp (L".TH ", Line, 4) == 0 || StrnCmp (L".TH\t", Line, 4) == 0) {
+ Line += 4;
+ State = LookForCommandName;
+ }
+ else if (*Line == L' ' || *Line == L'\t') {
+ Line++;
+ }
+ else {
+ State = Final;
+ }
+ break;
+
+ // Handle "\s*"
+ // Eat any "extra" whitespace after the title header macro (we have already seen
+ // at least one white space character). Go to state CompareCommands when a
+ // non-white space is seen.
+ case LookForCommandName:
+ if (*Line == L' ' || *Line == L'\t') {
+ Line++;
+ }
+ else {
+ ReturnValue = TRUE; // This is *some* command's title header line.
+ State = CompareCommands;
+ // Do not increment Line; it points to the first character of the command
+ // name on the title header line.
+ }
+ break;
+
+ // Handle "(\S)\s"
+ // Compare Command to the title header command name, ignoring case. When we
+ // reach the end of the command (i.e. we see white space), the next state
+ // depends on whether the caller wants a copy of the Brief Description.
+ case CompareCommands:
+ if (*Line == L' ' || *Line == L'\t') {
+ ReturnFound = TRUE; // This is the desired command's title header line.
+ State = (BriefDesc == NULL) ? Final : GetBriefDescription;
+ }
+ else if (InternalShellCharToUpper (*Line) != InternalShellCharToUpper (*(Command + CommandIndex++))) {
+ State = Final;
+ }
+ Line++;
+ break;
+
+ // Handle "[\s01]*(.*)$"
+ // Skip whitespace, '0', and '1' characters, if any, prior to the brief description.
+ // Return the description to the caller.
+ case GetBriefDescription:
+ if (*Line != L' ' && *Line != L'\t' && *Line != L'0' && *Line != L'1') {
+ *BriefSize = StrSize(Line);
+ *BriefDesc = AllocateZeroPool(*BriefSize);
+ if (*BriefDesc != NULL) {
+ StrCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), Line);
+ }
+ State = Final;
+ }
+ Line++;
+ break;
+
+ default:
+ break;
+ }
+
+ } while (State < Final);
+
+ *Found = ReturnFound;
+ return ReturnValue;
+}
+
+/**
+ parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
+ "Brief Description" for the .TH section as specified by Command. If the
+ command section is not found return EFI_NOT_FOUND.
+
+ Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
+
+ @param[in] Handle FileHandle to read from
+ @param[in] Command name of command's section to find as entered on the
+ command line (may be a relative or absolute path or
+ be in any case: upper, lower, or mixed in numerous ways!).
+ @param[out] BriefDesc pointer to pointer to string where description goes.
+ @param[out] BriefSize pointer to size of allocated BriefDesc
+ @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
+ set if the file handle is at the 0 position.
+
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+ @retval EFI_SUCCESS the section was found and its description stored in
+ an allocated buffer if requested.
+**/
+EFI_STATUS
+EFIAPI
+ManFileFindTitleSection(
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Command,
+ OUT CHAR16 **BriefDesc OPTIONAL,
+ OUT UINTN *BriefSize OPTIONAL,
+ IN OUT BOOLEAN *Ascii
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *ReadLine;
+ UINTN Size;
+ BOOLEAN Found;
+ UINTN Start;
+
+ if ( Handle == NULL
+ || Command == NULL
+ || (BriefDesc != NULL && BriefSize == NULL)
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = EFI_SUCCESS;
+ Size = 1024;
+ Found = FALSE;
+
+ ReadLine = AllocateZeroPool(Size);
+ if (ReadLine == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ //
+ // Do not pass any leading path information that may be present to IsTitleHeader().
+ //
+ Start = StrLen(Command);
+ while ((Start != 0)
+ && (*(Command + Start - 1) != L'\\')
+ && (*(Command + Start - 1) != L'/')
+ && (*(Command + Start - 1) != L':')) {
+ --Start;
+ }
+
+ for (;!ShellFileHandleEof(Handle);Size = 1024) {
+ Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, Ascii);
+ //
+ // ignore too small of buffer...
+ //
+ if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ break;
+ }
+
+ Status = EFI_NOT_FOUND;
+ if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, &Found)) {
+ Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND;
+ break;
+ }
+ }
+
+ FreePool(ReadLine);
+ return (Status);
+}
+
+/**
+ This function returns the help information for the specified command. The help text
+ will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
+
+ If Sections is specified, then each section name listed will be compared in a casesensitive
+ manner, to the section names described in Appendix B. If the section exists,
+ it will be appended to the returned help text. If the section does not exist, no
+ information will be returned. If Sections is NULL, then all help text information
+ available will be returned.
+
+ if BriefDesc is NULL, then the breif description will not be savedd seperatly,
+ but placed first in the main HelpText.
+
+ @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
+ @param[in] Command Points to the NULL-terminated UEFI Shell command name.
+ @param[in] Sections Points to the NULL-terminated comma-delimited
+ section names to return. If NULL, then all
+ sections will be returned.
+ @param[out] BriefDesc On return, points to a callee-allocated buffer
+ containing brief description text.
+ @param[out] HelpText On return, points to a callee-allocated buffer
+ containing all specified help text.
+
+ @retval EFI_SUCCESS The help text was returned.
+ @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
+ returned help text.
+ @retval EFI_INVALID_PARAMETER HelpText is NULL.
+ @retval EFI_INVALID_PARAMETER ManFileName is invalid.
+ @retval EFI_NOT_FOUND There is no help text available for Command.
+**/
+EFI_STATUS
+EFIAPI
+ProcessManFile(
+ IN CONST CHAR16 *ManFileName,
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Sections OPTIONAL,
+ OUT CHAR16 **BriefDesc OPTIONAL,
+ OUT CHAR16 **HelpText
+ )
+{
+ CHAR16 *TempString;
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_HANDLE CmdFileImgHandle;
+ EFI_STATUS Status;
+ UINTN HelpSize;
+ UINTN BriefSize;
+ UINTN StringIdWalker;
+ BOOLEAN Ascii;
+ CHAR16 *TempString2;
+ CHAR16 *CmdFileName;
+ CHAR16 *CmdFilePathName;
+ CHAR16 *StringBuff;
+ EFI_DEVICE_PATH_PROTOCOL *FileDevPath;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
+
+ if ( ManFileName == NULL
+ || Command == NULL
+ || HelpText == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ HelpSize = 0;
+ BriefSize = 0;
+ StringIdWalker = 0;
+ TempString = NULL;
+ Ascii = FALSE;
+ CmdFileName = NULL;
+ CmdFilePathName = NULL;
+ CmdFileImgHandle = NULL;
+ StringBuff = NULL;
+ PackageListHeader = NULL;
+ FileDevPath = NULL;
+ DevPath = NULL;
+
+ //
+ // See if it's in HII first
+ //
+ TempString = ShellCommandGetCommandHelp(Command);
+ if (TempString != NULL) {
+ TempString2 = TempString;
+ Status = ManBufferFindTitleSection(&TempString2, Command, BriefDesc, &BriefSize);
+ if (!EFI_ERROR(Status) && HelpText != NULL){
+ Status = ManBufferFindSections(TempString2, Sections, HelpText, &HelpSize);
+ }
+ } else {
+ //
+ // If the image is a external app, check .MAN file first.
+ //
+ FileHandle = NULL;
+ TempString = GetManFileName(ManFileName);
+ if (TempString == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = SearchPathForFile(TempString, &FileHandle);
+ if (EFI_ERROR(Status)) {
+ FileDevPath = FileDevicePath(NULL, TempString);
+ DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
+ Status = InternalOpenFileDevicePath(DevPath, &FileHandle, EFI_FILE_MODE_READ, 0);
+ SHELL_FREE_NON_NULL(FileDevPath);
+ SHELL_FREE_NON_NULL(DevPath);
+ }
+
+ if (!EFI_ERROR(Status)) {
+ HelpSize = 0;
+ BriefSize = 0;
+ Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
+ if (!EFI_ERROR(Status) && HelpText != NULL){
+ Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii);
+ }
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
+ if (!EFI_ERROR(Status)) {
+ //
+ // Get help text from .MAN file success.
+ //
+ goto Done;
+ }
+ }
+
+ //
+ // Load the app image to check EFI_HII_PACKAGE_LIST_PROTOCOL.
+ //
+ CmdFileName = GetExecuatableFileName(TempString);
+ if (CmdFileName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+ //
+ // If the file in CWD then use the file name, else use the full
+ // path name.
+ //
+ CmdFilePathName = ShellFindFilePath(CmdFileName);
+ if (CmdFilePathName == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+ DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CmdFilePathName);
+ Status = gBS->LoadImage(FALSE, gImageHandle, DevPath, NULL, 0, &CmdFileImgHandle);
+ if(EFI_ERROR(Status)) {
+ *HelpText = NULL;
+ goto Done;
+ }
+ Status = gBS->OpenProtocol(
+ CmdFileImgHandle,
+ &gEfiHiiPackageListProtocolGuid,
+ (VOID**)&PackageListHeader,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if(EFI_ERROR(Status)) {
+ *HelpText = NULL;
+ goto Done;
+ }
+
+ //
+ // If get package list on image handle, install it on HiiDatabase.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &mShellManDriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mShellManHiiDevicePath
+ );
+ if (EFI_ERROR(Status)) {
+ goto Done;
+ }
+
+ Status = gHiiDatabase->NewPackageList (
+ gHiiDatabase,
+ PackageListHeader,
+ mShellManDriverHandle,
+ &mShellManHiiHandle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ StringIdWalker = 1;
+ do {
+ SHELL_FREE_NON_NULL(StringBuff);
+ if (BriefDesc != NULL) {
+ SHELL_FREE_NON_NULL(*BriefDesc);
+ }
+ StringBuff = HiiGetString (mShellManHiiHandle, (EFI_STRING_ID)StringIdWalker, NULL);
+ if (StringBuff == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+ TempString2 = StringBuff;
+ Status = ManBufferFindTitleSection(&TempString2, Command, BriefDesc, &BriefSize);
+ if (!EFI_ERROR(Status) && HelpText != NULL){
+ Status = ManBufferFindSections(TempString2, Sections, HelpText, &HelpSize);
+ }
+ if (!EFI_ERROR(Status)){
+ //
+ // Found what we need and return
+ //
+ goto Done;
+ }
+
+ StringIdWalker += 1;
+ } while (StringIdWalker < 0xFFFF && StringBuff != NULL);
+
+ }
+
+Done:
+ if (mShellManDriverHandle != NULL) {
+ gBS->UninstallProtocolInterface (
+ mShellManDriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mShellManHiiDevicePath
+ );
+ mShellManDriverHandle = NULL;
+ }
+
+ if (mShellManHiiHandle != NULL) {
+ HiiRemovePackages (mShellManHiiHandle);
+ mShellManHiiHandle = NULL;
+ }
+
+ if (CmdFileImgHandle != NULL) {
+ Status = gBS->UnloadImage (CmdFileImgHandle);
+ }
+
+ SHELL_FREE_NON_NULL(StringBuff);
+ SHELL_FREE_NON_NULL(TempString);
+ SHELL_FREE_NON_NULL(CmdFileName);
+ SHELL_FREE_NON_NULL(CmdFilePathName);
+ SHELL_FREE_NON_NULL(FileDevPath);
+ SHELL_FREE_NON_NULL(DevPath);
+
+ return (Status);
+}
+
diff --git a/Core/ShellPkg/Application/Shell/ShellManParser.h b/Core/ShellPkg/Application/Shell/ShellManParser.h
new file mode 100644
index 0000000000..3807eec9b2
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellManParser.h
@@ -0,0 +1,86 @@
+/** @file
+ Provides interface to shell MAN file parser.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_MAN_FILE_PARSER_HEADER_
+#define _SHELL_MAN_FILE_PARSER_HEADER_
+
+/**
+ This function returns the help information for the specified command. The help text
+ will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
+
+ If Sections is specified, then each section name listed will be compared in a casesensitive
+ manner, to the section names described in Appendix B. If the section exists,
+ it will be appended to the returned help text. If the section does not exist, no
+ information will be returned. If Sections is NULL, then all help text information
+ available will be returned.
+
+ if BriefDesc is NULL, then the breif description will not be savedd seperatly,
+ but placed first in the main HelpText.
+
+ @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
+ @param[in] Command Points to the NULL-terminated UEFI Shell command name.
+ @param[in] Sections Points to the NULL-terminated comma-delimited
+ section names to return. If NULL, then all
+ sections will be returned.
+ @param[out] BriefDesc On return, points to a callee-allocated buffer
+ containing brief description text.
+ @param[out] HelpText On return, points to a callee-allocated buffer
+ containing all specified help text.
+
+ @retval EFI_SUCCESS The help text was returned.
+ @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
+ returned help text.
+ @retval EFI_INVALID_PARAMETER HelpText is NULL
+ @retval EFI_NOT_FOUND There is no help text available for Command.
+**/
+EFI_STATUS
+EFIAPI
+ProcessManFile(
+ IN CONST CHAR16 *ManFileName,
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Sections OPTIONAL,
+ OUT CHAR16 **BriefDesc,
+ OUT CHAR16 **HelpText
+ );
+
+/**
+ parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
+ detailed help for any sub section specified in the comma seperated list of
+ sections provided. If the end of the file or a .TH section is found then
+ return.
+
+ Upon a sucessful return the caller is responsible to free the memory in *HelpText
+
+ @param[in] Handle FileHandle to read from
+ @param[in] Sections name of command's sub sections to find
+ @param[out] HelpText pointer to pointer to string where text goes.
+ @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
+ @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
+
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+ @retval EFI_SUCCESS the section was found and its description sotred in
+ an alloceted buffer.
+**/
+EFI_STATUS
+EFIAPI
+ManFileFindSections(
+ IN SHELL_FILE_HANDLE Handle,
+ IN CONST CHAR16 *Sections,
+ OUT CHAR16 **HelpText,
+ OUT UINTN *HelpSize,
+ IN BOOLEAN Ascii
+ );
+
+#endif //_SHELL_MAN_FILE_PARSER_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/ShellParametersProtocol.c b/Core/ShellPkg/Application/Shell/ShellParametersProtocol.c
new file mode 100644
index 0000000000..0785902b16
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellParametersProtocol.c
@@ -0,0 +1,1298 @@
+/** @file
+ Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation,
+ manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL.
+
+ Copyright (C) 2014, Red Hat, Inc.
+ (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+BOOLEAN AsciiRedirection = FALSE;
+
+/**
+ Function to populate Argc and Argv.
+
+ This function parses the CommandLine and divides it into standard C style Argc/Argv
+ parameters for inclusion in EFI_SHELL_PARAMETERS_PROTOCOL. this supports space
+ delimited and quote surrounded parameter definition.
+
+ All special character processing (alias, environment variable, redirection,
+ etc... must be complete before calling this API.
+
+ @param[in] CommandLine String of command line to parse
+ @param[in] StripQuotation if TRUE then strip the quotation marks surrounding
+ the parameters.
+ @param[in, out] Argv pointer to array of strings; one for each parameter
+ @param[in, out] Argc pointer to number of strings in Argv array
+
+ @return EFI_SUCCESS the operation was sucessful
+ @return EFI_OUT_OF_RESOURCES a memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ParseCommandLineToArgs(
+ IN CONST CHAR16 *CommandLine,
+ IN BOOLEAN StripQuotation,
+ IN OUT CHAR16 ***Argv,
+ IN OUT UINTN *Argc
+ )
+{
+ UINTN Count;
+ CHAR16 *TempParameter;
+ CHAR16 *Walker;
+ CHAR16 *NewParam;
+ CHAR16 *NewCommandLine;
+ UINTN Size;
+ EFI_STATUS Status;
+
+ ASSERT(Argc != NULL);
+ ASSERT(Argv != NULL);
+
+ if (CommandLine == NULL || StrLen(CommandLine)==0) {
+ (*Argc) = 0;
+ (*Argv) = NULL;
+ return (EFI_SUCCESS);
+ }
+
+ NewCommandLine = AllocateCopyPool(StrSize(CommandLine), CommandLine);
+ if (NewCommandLine == NULL){
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ TrimSpaces(&NewCommandLine);
+ Size = StrSize(NewCommandLine);
+ TempParameter = AllocateZeroPool(Size);
+ if (TempParameter == NULL) {
+ SHELL_FREE_NON_NULL(NewCommandLine);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ for ( Count = 0
+ , Walker = (CHAR16*)NewCommandLine
+ ; Walker != NULL && *Walker != CHAR_NULL
+ ; Count++
+ ) {
+ if (EFI_ERROR (ShellGetNextParameter (&Walker, TempParameter, Size, TRUE))) {
+ break;
+ }
+ }
+
+ //
+ // lets allocate the pointer array
+ //
+ (*Argv) = AllocateZeroPool((Count)*sizeof(CHAR16*));
+ if (*Argv == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ *Argc = 0;
+ Walker = (CHAR16*)NewCommandLine;
+ while(Walker != NULL && *Walker != CHAR_NULL) {
+ SetMem16(TempParameter, Size, CHAR_NULL);
+ if (EFI_ERROR (ShellGetNextParameter (&Walker, TempParameter, Size, StripQuotation))) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+
+ NewParam = AllocateCopyPool(StrSize(TempParameter), TempParameter);
+ if (NewParam == NULL){
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+ ((CHAR16**)(*Argv))[(*Argc)] = NewParam;
+ (*Argc)++;
+ }
+ ASSERT(Count >= (*Argc));
+ Status = EFI_SUCCESS;
+
+Done:
+ SHELL_FREE_NON_NULL(TempParameter);
+ SHELL_FREE_NON_NULL(NewCommandLine);
+ return (Status);
+}
+
+/**
+ creates a new EFI_SHELL_PARAMETERS_PROTOCOL instance and populates it and then
+ installs it on our handle and if there is an existing version of the protocol
+ that one is cached for removal later.
+
+ @param[in, out] NewShellParameters on a successful return, a pointer to pointer
+ to the newly installed interface.
+ @param[in, out] RootShellInstance on a successful return, pointer to boolean.
+ TRUE if this is the root shell instance.
+
+ @retval EFI_SUCCESS the operation completed successfully.
+ @return other the operation failed.
+ @sa ReinstallProtocolInterface
+ @sa InstallProtocolInterface
+ @sa ParseCommandLineToArgs
+**/
+EFI_STATUS
+EFIAPI
+CreatePopulateInstallShellParametersProtocol (
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL **NewShellParameters,
+ IN OUT BOOLEAN *RootShellInstance
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ CHAR16 *FullCommandLine;
+ UINTN Size;
+
+ Size = 0;
+ FullCommandLine = NULL;
+ LoadedImage = NULL;
+
+ //
+ // Assert for valid parameters
+ //
+ ASSERT(NewShellParameters != NULL);
+ ASSERT(RootShellInstance != NULL);
+
+ //
+ // See if we have a shell parameters placed on us
+ //
+ Status = gBS->OpenProtocol (
+ gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID **) &ShellInfoObject.OldShellParameters,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ //
+ // if we don't then we must be the root shell (error is expected)
+ //
+ if (EFI_ERROR (Status)) {
+ *RootShellInstance = TRUE;
+ }
+
+ //
+ // Allocate the new structure
+ //
+ *NewShellParameters = AllocateZeroPool(sizeof(EFI_SHELL_PARAMETERS_PROTOCOL));
+ if ((*NewShellParameters) == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ //
+ // get loaded image protocol
+ //
+ Status = gBS->OpenProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImage,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR(Status);
+ //
+ // Build the full command line
+ //
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(L"ShellOpt", &Size, FullCommandLine);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FullCommandLine = AllocateZeroPool(Size + LoadedImage->LoadOptionsSize);
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(L"ShellOpt", &Size, FullCommandLine);
+ }
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // no parameters via environment... ok
+ //
+ } else {
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+ }
+ if (Size == 0 && LoadedImage->LoadOptionsSize != 0) {
+ ASSERT(FullCommandLine == NULL);
+ //
+ // Now we need to include a NULL terminator in the size.
+ //
+ Size = LoadedImage->LoadOptionsSize + sizeof(FullCommandLine[0]);
+ FullCommandLine = AllocateZeroPool(Size);
+ }
+ if (FullCommandLine != NULL) {
+ CopyMem (FullCommandLine, LoadedImage->LoadOptions, LoadedImage->LoadOptionsSize);
+ //
+ // Populate Argc and Argv
+ //
+ Status = ParseCommandLineToArgs(FullCommandLine,
+ TRUE,
+ &(*NewShellParameters)->Argv,
+ &(*NewShellParameters)->Argc);
+
+ FreePool(FullCommandLine);
+
+ ASSERT_EFI_ERROR(Status);
+ } else {
+ (*NewShellParameters)->Argv = NULL;
+ (*NewShellParameters)->Argc = 0;
+ }
+
+ //
+ // Populate the 3 faked file systems...
+ //
+ if (*RootShellInstance) {
+ (*NewShellParameters)->StdIn = &FileInterfaceStdIn;
+ (*NewShellParameters)->StdOut = &FileInterfaceStdOut;
+ (*NewShellParameters)->StdErr = &FileInterfaceStdErr;
+ Status = gBS->InstallProtocolInterface(&gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ (VOID*)(*NewShellParameters));
+ } else {
+ //
+ // copy from the existing ones
+ //
+ (*NewShellParameters)->StdIn = ShellInfoObject.OldShellParameters->StdIn;
+ (*NewShellParameters)->StdOut = ShellInfoObject.OldShellParameters->StdOut;
+ (*NewShellParameters)->StdErr = ShellInfoObject.OldShellParameters->StdErr;
+ Status = gBS->ReinstallProtocolInterface(gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID*)ShellInfoObject.OldShellParameters,
+ (VOID*)(*NewShellParameters));
+ }
+
+ return (Status);
+}
+
+/**
+ frees all memory used by createion and installation of shell parameters protocol
+ and if there was an old version installed it will restore that one.
+
+ @param NewShellParameters the interface of EFI_SHELL_PARAMETERS_PROTOCOL that is
+ being cleaned up.
+
+ @retval EFI_SUCCESS the cleanup was successful
+ @return other the cleanup failed
+ @sa ReinstallProtocolInterface
+ @sa UninstallProtocolInterface
+**/
+EFI_STATUS
+EFIAPI
+CleanUpShellParametersProtocol (
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParameters
+ )
+{
+ EFI_STATUS Status;
+ UINTN LoopCounter;
+
+ //
+ // If the old exists we need to restore it
+ //
+ if (ShellInfoObject.OldShellParameters != NULL) {
+ Status = gBS->ReinstallProtocolInterface(gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID*)NewShellParameters,
+ (VOID*)ShellInfoObject.OldShellParameters);
+ DEBUG_CODE(ShellInfoObject.OldShellParameters = NULL;);
+ } else {
+ //
+ // No old one, just uninstall us...
+ //
+ Status = gBS->UninstallProtocolInterface(gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID*)NewShellParameters);
+ }
+ if (NewShellParameters->Argv != NULL) {
+ for ( LoopCounter = 0
+ ; LoopCounter < NewShellParameters->Argc
+ ; LoopCounter++
+ ){
+ FreePool(NewShellParameters->Argv[LoopCounter]);
+ }
+ FreePool(NewShellParameters->Argv);
+ }
+ FreePool(NewShellParameters);
+ return (Status);
+}
+
+/**
+ Determin if a file name represents a unicode file.
+
+ @param[in] FileName Pointer to the filename to open.
+
+ @retval EFI_SUCCESS The file is a unicode file.
+ @return An error upon failure.
+**/
+EFI_STATUS
+EFIAPI
+IsUnicodeFile(
+ IN CONST CHAR16 *FileName
+ )
+{
+ SHELL_FILE_HANDLE Handle;
+ EFI_STATUS Status;
+ UINT64 OriginalFilePosition;
+ UINTN CharSize;
+ CHAR16 CharBuffer;
+
+ Status = gEfiShellProtocol->OpenFileByName(FileName, &Handle, EFI_FILE_MODE_READ);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+ gEfiShellProtocol->GetFilePosition(Handle, &OriginalFilePosition);
+ gEfiShellProtocol->SetFilePosition(Handle, 0);
+ CharSize = sizeof(CHAR16);
+ Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer);
+ if (EFI_ERROR(Status) || CharBuffer != gUnicodeFileTag) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ }
+ gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
+ gEfiShellProtocol->CloseFile(Handle);
+ return (Status);
+}
+
+/**
+ Strips out quotes sections of a string.
+
+ All of the characters between quotes is replaced with spaces.
+
+ @param[in, out] TheString A pointer to the string to update.
+**/
+VOID
+EFIAPI
+StripQuotes (
+ IN OUT CHAR16 *TheString
+ )
+{
+ BOOLEAN RemoveNow;
+
+ for (RemoveNow = FALSE ; TheString != NULL && *TheString != CHAR_NULL ; TheString++) {
+ if (*TheString == L'^' && *(TheString + 1) == L'\"') {
+ TheString++;
+ } else if (*TheString == L'\"') {
+ RemoveNow = (BOOLEAN)!RemoveNow;
+ } else if (RemoveNow) {
+ *TheString = L' ';
+ }
+ }
+}
+
+/**
+ Calcualte the 32-bit CRC in a EFI table using the service provided by the
+ gRuntime service.
+
+ @param Hdr Pointer to an EFI standard header
+
+**/
+VOID
+CalculateEfiHdrCrc (
+ IN OUT EFI_TABLE_HEADER *Hdr
+ )
+{
+ UINT32 Crc;
+
+ Hdr->CRC32 = 0;
+
+ //
+ // If gBS->CalculateCrce32 () == CoreEfiNotAvailableYet () then
+ // Crc will come back as zero if we set it to zero here
+ //
+ Crc = 0;
+ gBS->CalculateCrc32 ((UINT8 *)Hdr, Hdr->HeaderSize, &Crc);
+ Hdr->CRC32 = Crc;
+}
+
+/**
+ Fix a string to only have the file name, removing starting at the first space of whatever is quoted.
+
+ @param[in] FileName The filename to start with.
+
+ @retval NULL FileName was invalid.
+ @return The modified FileName.
+**/
+CHAR16*
+EFIAPI
+FixFileName (
+ IN CHAR16 *FileName
+ )
+{
+ CHAR16 *Copy;
+ CHAR16 *TempLocation;
+
+ if (FileName == NULL) {
+ return (NULL);
+ }
+
+ if (FileName[0] == L'\"') {
+ Copy = FileName+1;
+ if ((TempLocation = StrStr(Copy , L"\"")) != NULL) {
+ TempLocation[0] = CHAR_NULL;
+ }
+ } else {
+ Copy = FileName;
+ while(Copy[0] == L' ') {
+ Copy++;
+ }
+ if ((TempLocation = StrStr(Copy , L" ")) != NULL) {
+ TempLocation[0] = CHAR_NULL;
+ }
+ }
+
+ if (Copy[0] == CHAR_NULL) {
+ return (NULL);
+ }
+
+ return (Copy);
+}
+
+/**
+ Fix a string to only have the environment variable name, removing starting at the first space of whatever is quoted and removing the leading and trailing %.
+
+ @param[in] FileName The filename to start with.
+
+ @retval NULL FileName was invalid.
+ @return The modified FileName.
+**/
+CHAR16*
+EFIAPI
+FixVarName (
+ IN CHAR16 *FileName
+ )
+{
+ CHAR16 *Copy;
+ CHAR16 *TempLocation;
+
+ Copy = FileName;
+
+ if (FileName[0] == L'%') {
+ Copy = FileName+1;
+ if ((TempLocation = StrStr(Copy , L"%")) != NULL) {
+ TempLocation[0] = CHAR_NULL;
+ }
+ }
+
+ return (FixFileName(Copy));
+}
+
+/**
+ Remove the unicode file tag from the begining of the file buffer since that will not be
+ used by StdIn.
+
+ @param[in] Handle Pointer to the handle of the file to be processed.
+
+ @retval EFI_SUCCESS The unicode file tag has been moved successfully.
+**/
+EFI_STATUS
+EFIAPI
+RemoveFileTag(
+ IN SHELL_FILE_HANDLE *Handle
+ )
+{
+ UINTN CharSize;
+ CHAR16 CharBuffer;
+
+ CharSize = sizeof(CHAR16);
+ CharBuffer = 0;
+ gEfiShellProtocol->ReadFile(*Handle, &CharSize, &CharBuffer);
+ if (CharBuffer != gUnicodeFileTag) {
+ gEfiShellProtocol->SetFilePosition(*Handle, 0);
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Write the unicode file tag to the specified file.
+
+ It is the caller's responsibility to ensure that
+ ShellInfoObject.NewEfiShellProtocol has been initialized before calling this
+ function.
+
+ @param[in] FileHandle The file to write the unicode file tag to.
+
+ @return Status code from ShellInfoObject.NewEfiShellProtocol->WriteFile.
+**/
+EFI_STATUS
+WriteFileTag (
+ IN SHELL_FILE_HANDLE FileHandle
+ )
+{
+ CHAR16 FileTag;
+ UINTN Size;
+ EFI_STATUS Status;
+
+ FileTag = gUnicodeFileTag;
+ Size = sizeof FileTag;
+ Status = ShellInfoObject.NewEfiShellProtocol->WriteFile (FileHandle, &Size,
+ &FileTag);
+ ASSERT (EFI_ERROR (Status) || Size == sizeof FileTag);
+ return Status;
+}
+
+
+/**
+ Funcion will replace the current StdIn and StdOut in the ShellParameters protocol
+ structure by parsing NewCommandLine. The current values are returned to the
+ user.
+
+ This will also update the system table.
+
+ @param[in, out] ShellParameters Pointer to parameter structure to modify.
+ @param[in] NewCommandLine The new command line to parse and use.
+ @param[out] OldStdIn Pointer to old StdIn.
+ @param[out] OldStdOut Pointer to old StdOut.
+ @param[out] OldStdErr Pointer to old StdErr.
+ @param[out] SystemTableInfo Pointer to old system table information.
+
+ @retval EFI_SUCCESS Operation was sucessful, Argv and Argc are valid.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+UpdateStdInStdOutStdErr(
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN CHAR16 *NewCommandLine,
+ OUT SHELL_FILE_HANDLE *OldStdIn,
+ OUT SHELL_FILE_HANDLE *OldStdOut,
+ OUT SHELL_FILE_HANDLE *OldStdErr,
+ OUT SYSTEM_TABLE_INFO *SystemTableInfo
+ )
+{
+ CHAR16 *CommandLineCopy;
+ CHAR16 *CommandLineWalker;
+ CHAR16 *StdErrFileName;
+ CHAR16 *StdOutFileName;
+ CHAR16 *StdInFileName;
+ CHAR16 *StdInVarName;
+ CHAR16 *StdOutVarName;
+ CHAR16 *StdErrVarName;
+ EFI_STATUS Status;
+ SHELL_FILE_HANDLE TempHandle;
+ UINT64 FileSize;
+ BOOLEAN OutUnicode;
+ BOOLEAN InUnicode;
+ BOOLEAN ErrUnicode;
+ BOOLEAN OutAppend;
+ BOOLEAN ErrAppend;
+ UINTN Size;
+ SPLIT_LIST *Split;
+ CHAR16 *FirstLocation;
+ BOOLEAN Volatile;
+
+ OutUnicode = TRUE;
+ InUnicode = TRUE;
+ AsciiRedirection = FALSE;
+ ErrUnicode = TRUE;
+ StdInVarName = NULL;
+ StdOutVarName = NULL;
+ StdErrVarName = NULL;
+ StdErrFileName = NULL;
+ StdInFileName = NULL;
+ StdOutFileName = NULL;
+ ErrAppend = FALSE;
+ OutAppend = FALSE;
+ CommandLineCopy = NULL;
+ FirstLocation = NULL;
+
+ if (ShellParameters == NULL || SystemTableInfo == NULL || OldStdIn == NULL || OldStdOut == NULL || OldStdErr == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ SystemTableInfo->ConIn = gST->ConIn;
+ SystemTableInfo->ConInHandle = gST->ConsoleInHandle;
+ SystemTableInfo->ConOut = gST->ConOut;
+ SystemTableInfo->ConOutHandle = gST->ConsoleOutHandle;
+ SystemTableInfo->ErrOut = gST->StdErr;
+ SystemTableInfo->ErrOutHandle = gST->StandardErrorHandle;
+ *OldStdIn = ShellParameters->StdIn;
+ *OldStdOut = ShellParameters->StdOut;
+ *OldStdErr = ShellParameters->StdErr;
+
+ if (NewCommandLine == NULL) {
+ return (EFI_SUCCESS);
+ }
+
+ CommandLineCopy = StrnCatGrow(&CommandLineCopy, NULL, NewCommandLine, 0);
+ if (CommandLineCopy == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ Status = EFI_SUCCESS;
+ Split = NULL;
+ FirstLocation = CommandLineCopy + StrLen(CommandLineCopy);
+
+ StripQuotes(CommandLineCopy);
+
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
+ Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
+ if (Split != NULL && Split->SplitStdIn != NULL) {
+ ShellParameters->StdIn = Split->SplitStdIn;
+ }
+ if (Split != NULL && Split->SplitStdOut != NULL) {
+ ShellParameters->StdOut = Split->SplitStdOut;
+ }
+ }
+
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>>v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 12, L' ');
+ StdErrVarName = CommandLineWalker += 6;
+ ErrAppend = TRUE;
+ if (StrStr(CommandLineWalker, L" 2>>v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>>v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 12, L' ');
+ StdOutVarName = CommandLineWalker += 6;
+ OutAppend = TRUE;
+ if (StrStr(CommandLineWalker, L" 1>>v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ } else if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >>v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ StdOutVarName = CommandLineWalker += 5;
+ OutAppend = TRUE;
+ if (StrStr(CommandLineWalker, L" >>v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ } else if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ StdOutVarName = CommandLineWalker += 4;
+ OutAppend = FALSE;
+ if (StrStr(CommandLineWalker, L" >v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>>a ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 12, L' ');
+ StdOutFileName = CommandLineWalker += 6;
+ OutAppend = TRUE;
+ OutUnicode = FALSE;
+ if (StrStr(CommandLineWalker, L" 1>>a ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>> ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 5;
+ OutAppend = TRUE;
+ }
+ if (StrStr(CommandLineWalker, L" 1>> ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >> ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 4;
+ OutAppend = TRUE;
+ }
+ if (StrStr(CommandLineWalker, L" >> ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >>a ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 5;
+ OutAppend = TRUE;
+ OutUnicode = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" >>a ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>a ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 5;
+ OutAppend = FALSE;
+ OutUnicode = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" 1>a ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >a ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 4;
+ OutAppend = FALSE;
+ OutUnicode = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" >a ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>> ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdErrFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdErrFileName = CommandLineWalker += 5;
+ ErrAppend = TRUE;
+ }
+ if (StrStr(CommandLineWalker, L" 2>> ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdErrVarName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdErrVarName = CommandLineWalker += 5;
+ ErrAppend = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" 2>v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdOutVarName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutVarName = CommandLineWalker += 5;
+ OutAppend = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" 1>v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>a ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 10, L' ');
+ if (StdErrFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdErrFileName = CommandLineWalker += 5;
+ ErrAppend = FALSE;
+ ErrUnicode = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" 2>a ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2> ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ if (StdErrFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdErrFileName = CommandLineWalker += 4;
+ ErrAppend = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" 2> ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1> ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 4;
+ OutAppend = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" 1> ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" > ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 6, L' ');
+ if (StdOutFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdOutFileName = CommandLineWalker += 3;
+ OutAppend = FALSE;
+ }
+ if (StrStr(CommandLineWalker, L" > ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" < ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 6, L' ');
+ if (StdInFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdInFileName = CommandLineWalker += 3;
+ }
+ if (StrStr(CommandLineWalker, L" < ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" <a ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ if (StdInFileName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdInFileName = CommandLineWalker += 4;
+ InUnicode = FALSE;
+ AsciiRedirection = TRUE;
+ }
+ if (StrStr(CommandLineWalker, L" <a ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" <v ")) != NULL) {
+ FirstLocation = MIN(CommandLineWalker, FirstLocation);
+ SetMem16(CommandLineWalker, 8, L' ');
+ if (StdInVarName != NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ StdInVarName = CommandLineWalker += 4;
+ }
+ if (StrStr(CommandLineWalker, L" <v ") != NULL) {
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ //
+ // re-populate the string to support any filenames that were in quotes.
+ //
+ StrnCpyS(CommandLineCopy, StrSize(CommandLineCopy)/sizeof(CHAR16), NewCommandLine, StrLen(NewCommandLine));
+
+ if (FirstLocation != CommandLineCopy + StrLen(CommandLineCopy)
+ && ((UINTN)(FirstLocation - CommandLineCopy) < StrLen(NewCommandLine))
+ ){
+ *(NewCommandLine + (UINTN)(FirstLocation - CommandLineCopy)) = CHAR_NULL;
+ }
+
+ if (!EFI_ERROR(Status)) {
+
+ if (StdErrFileName != NULL) {
+ if ((StdErrFileName = FixFileName(StdErrFileName)) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ if (StdOutFileName != NULL) {
+ if ((StdOutFileName = FixFileName(StdOutFileName)) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ if (StdInFileName != NULL) {
+ if ((StdInFileName = FixFileName(StdInFileName)) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ if (StdErrVarName != NULL) {
+ if ((StdErrVarName = FixVarName(StdErrVarName)) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ if (StdOutVarName != NULL) {
+ if ((StdOutVarName = FixVarName(StdOutVarName)) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+ if (StdInVarName != NULL) {
+ if ((StdInVarName = FixVarName(StdInVarName)) == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Verify not the same and not duplicating something from a split
+ //
+ if (
+ //
+ // Check that no 2 filenames are the same
+ //
+ (StdErrFileName != NULL && StdOutFileName!= NULL && StringNoCaseCompare(&StdErrFileName, &StdOutFileName) == 0)
+ ||(StdErrFileName != NULL && StdInFileName != NULL && StringNoCaseCompare(&StdErrFileName, &StdInFileName ) == 0)
+ ||(StdOutFileName != NULL && StdInFileName != NULL && StringNoCaseCompare(&StdOutFileName, &StdInFileName ) == 0)
+ //
+ // Check that no 2 variable names are the same
+ //
+ ||(StdErrVarName != NULL && StdInVarName != NULL && StringNoCaseCompare(&StdErrVarName , &StdInVarName ) == 0)
+ ||(StdOutVarName != NULL && StdInVarName != NULL && StringNoCaseCompare(&StdOutVarName , &StdInVarName ) == 0)
+ ||(StdErrVarName != NULL && StdOutVarName != NULL && StringNoCaseCompare(&StdErrVarName , &StdOutVarName ) == 0)
+ //
+ // When a split (using | operator) is in place some are not allowed
+ //
+ ||(Split != NULL && Split->SplitStdIn != NULL && (StdInVarName != NULL || StdInFileName != NULL))
+ ||(Split != NULL && Split->SplitStdOut != NULL && (StdOutVarName != NULL || StdOutFileName != NULL))
+ //
+ // Check that nothing is trying to be output to 2 locations.
+ //
+ ||(StdErrFileName != NULL && StdErrVarName != NULL)
+ ||(StdOutFileName != NULL && StdOutVarName != NULL)
+ ||(StdInFileName != NULL && StdInVarName != NULL)
+ //
+ // Check for no volatile environment variables
+ //
+ ||(StdErrVarName != NULL && !EFI_ERROR (IsVolatileEnv (StdErrVarName, &Volatile)) && !Volatile)
+ ||(StdOutVarName != NULL && !EFI_ERROR (IsVolatileEnv (StdOutVarName, &Volatile)) && !Volatile)
+ //
+ // Cant redirect during a reconnect operation.
+ //
+ ||(StrStr(NewCommandLine, L"connect -r") != NULL
+ && (StdOutVarName != NULL || StdOutFileName != NULL || StdErrFileName != NULL || StdErrVarName != NULL))
+ //
+ // Check that filetypes (Unicode/Ascii) do not change during an append
+ //
+ ||(StdOutFileName != NULL && OutUnicode && OutAppend && (!EFI_ERROR(ShellFileExists(StdOutFileName)) && EFI_ERROR(IsUnicodeFile(StdOutFileName))))
+ ||(StdErrFileName != NULL && ErrUnicode && ErrAppend && (!EFI_ERROR(ShellFileExists(StdErrFileName)) && EFI_ERROR(IsUnicodeFile(StdErrFileName))))
+ ||(StdOutFileName != NULL && !OutUnicode && OutAppend && (!EFI_ERROR(ShellFileExists(StdOutFileName)) && !EFI_ERROR(IsUnicodeFile(StdOutFileName))))
+ ||(StdErrFileName != NULL && !ErrUnicode && ErrAppend && (!EFI_ERROR(ShellFileExists(StdErrFileName)) && !EFI_ERROR(IsUnicodeFile(StdErrFileName))))
+ ){
+ Status = EFI_INVALID_PARAMETER;
+ ShellParameters->StdIn = *OldStdIn;
+ ShellParameters->StdOut = *OldStdOut;
+ ShellParameters->StdErr = *OldStdErr;
+ } else if (!EFI_ERROR(Status)){
+ //
+ // Open the Std<Whatever> and we should not have conflicts here...
+ //
+
+ //
+ // StdErr to a file
+ //
+ if (StdErrFileName != NULL) {
+ if (!ErrAppend) {
+ //
+ // delete existing file.
+ //
+ ShellInfoObject.NewEfiShellProtocol->DeleteFileByName(StdErrFileName);
+ }
+ Status = ShellOpenFileByName(StdErrFileName, &TempHandle, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE,0);
+ if (!ErrAppend && ErrUnicode && !EFI_ERROR(Status)) {
+ Status = WriteFileTag (TempHandle);
+ }
+ if (!ErrUnicode && !EFI_ERROR(Status)) {
+ TempHandle = CreateFileInterfaceFile(TempHandle, FALSE);
+ ASSERT(TempHandle != NULL);
+ }
+ if (!EFI_ERROR(Status)) {
+ ShellParameters->StdErr = TempHandle;
+ gST->StdErr = CreateSimpleTextOutOnFile(TempHandle, &gST->StandardErrorHandle, gST->StdErr);
+ }
+ }
+
+ //
+ // StdOut to a file
+ //
+ if (!EFI_ERROR(Status) && StdOutFileName != NULL) {
+ if (!OutAppend) {
+ //
+ // delete existing file.
+ //
+ ShellInfoObject.NewEfiShellProtocol->DeleteFileByName(StdOutFileName);
+ }
+ Status = ShellOpenFileByName(StdOutFileName, &TempHandle, EFI_FILE_MODE_WRITE|EFI_FILE_MODE_READ|EFI_FILE_MODE_CREATE,0);
+ if (TempHandle == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ if (StrStr(StdOutFileName, L"NUL")==StdOutFileName) {
+ //no-op
+ } else if (!OutAppend && OutUnicode && !EFI_ERROR(Status)) {
+ Status = WriteFileTag (TempHandle);
+ } else if (OutAppend) {
+ Status = ShellInfoObject.NewEfiShellProtocol->GetFileSize(TempHandle, &FileSize);
+ if (!EFI_ERROR(Status)) {
+ //
+ // When appending to a new unicode file, write the file tag.
+ // Otherwise (ie. when appending to a new ASCII file, or an
+ // existent file with any encoding), just seek to the end.
+ //
+ Status = (FileSize == 0 && OutUnicode) ?
+ WriteFileTag (TempHandle) :
+ ShellInfoObject.NewEfiShellProtocol->SetFilePosition (
+ TempHandle,
+ FileSize);
+ }
+ }
+ if (!OutUnicode && !EFI_ERROR(Status)) {
+ TempHandle = CreateFileInterfaceFile(TempHandle, FALSE);
+ ASSERT(TempHandle != NULL);
+ }
+ if (!EFI_ERROR(Status)) {
+ ShellParameters->StdOut = TempHandle;
+ gST->ConOut = CreateSimpleTextOutOnFile(TempHandle, &gST->ConsoleOutHandle, gST->ConOut);
+ }
+ }
+ }
+
+ //
+ // StdOut to a var
+ //
+ if (!EFI_ERROR(Status) && StdOutVarName != NULL) {
+ if (!OutAppend) {
+ //
+ // delete existing variable.
+ //
+ SHELL_SET_ENVIRONMENT_VARIABLE_V(StdOutVarName, 0, L"");
+ }
+ TempHandle = CreateFileInterfaceEnv(StdOutVarName);
+ ASSERT(TempHandle != NULL);
+ ShellParameters->StdOut = TempHandle;
+ gST->ConOut = CreateSimpleTextOutOnFile(TempHandle, &gST->ConsoleOutHandle, gST->ConOut);
+ }
+
+ //
+ // StdErr to a var
+ //
+ if (!EFI_ERROR(Status) && StdErrVarName != NULL) {
+ if (!ErrAppend) {
+ //
+ // delete existing variable.
+ //
+ SHELL_SET_ENVIRONMENT_VARIABLE_V(StdErrVarName, 0, L"");
+ }
+ TempHandle = CreateFileInterfaceEnv(StdErrVarName);
+ ASSERT(TempHandle != NULL);
+ ShellParameters->StdErr = TempHandle;
+ gST->StdErr = CreateSimpleTextOutOnFile(TempHandle, &gST->StandardErrorHandle, gST->StdErr);
+ }
+
+ //
+ // StdIn from a var
+ //
+ if (!EFI_ERROR(Status) && StdInVarName != NULL) {
+ TempHandle = CreateFileInterfaceEnv(StdInVarName);
+ if (TempHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ if (!InUnicode) {
+ TempHandle = CreateFileInterfaceFile(TempHandle, FALSE);
+ }
+ Size = 0;
+ if (TempHandle == NULL || ((EFI_FILE_PROTOCOL*)TempHandle)->Read(TempHandle, &Size, NULL) != EFI_BUFFER_TOO_SMALL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ ShellParameters->StdIn = TempHandle;
+ gST->ConIn = CreateSimpleTextInOnFile(TempHandle, &gST->ConsoleInHandle);
+ }
+ }
+ }
+
+ //
+ // StdIn from a file
+ //
+ if (!EFI_ERROR(Status) && StdInFileName != NULL) {
+ Status = ShellOpenFileByName(
+ StdInFileName,
+ &TempHandle,
+ EFI_FILE_MODE_READ,
+ 0);
+ if (!EFI_ERROR(Status)) {
+ if (!InUnicode) {
+ //
+ // Create the ASCII->Unicode conversion layer
+ //
+ TempHandle = CreateFileInterfaceFile(TempHandle, FALSE);
+ }
+ ShellParameters->StdIn = TempHandle;
+ gST->ConIn = CreateSimpleTextInOnFile(TempHandle, &gST->ConsoleInHandle);
+ }
+ }
+ }
+ }
+ FreePool(CommandLineCopy);
+
+ CalculateEfiHdrCrc(&gST->Hdr);
+
+ if (gST->ConIn == NULL ||gST->ConOut == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);
+ } else if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);
+ }
+
+ return (Status);
+}
+
+/**
+ Funcion will replace the current StdIn and StdOut in the ShellParameters protocol
+ structure with StdIn and StdOut. The current values are de-allocated.
+
+ @param[in, out] ShellParameters Pointer to parameter structure to modify.
+ @param[in] OldStdIn Pointer to old StdIn.
+ @param[in] OldStdOut Pointer to old StdOut.
+ @param[in] OldStdErr Pointer to old StdErr.
+ @param[in] SystemTableInfo Pointer to old system table information.
+**/
+EFI_STATUS
+EFIAPI
+RestoreStdInStdOutStdErr (
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN SHELL_FILE_HANDLE *OldStdIn,
+ IN SHELL_FILE_HANDLE *OldStdOut,
+ IN SHELL_FILE_HANDLE *OldStdErr,
+ IN SYSTEM_TABLE_INFO *SystemTableInfo
+ )
+{
+ SPLIT_LIST *Split;
+
+ if (ShellParameters == NULL
+ ||OldStdIn == NULL
+ ||OldStdOut == NULL
+ ||OldStdErr == NULL
+ ||SystemTableInfo == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
+ Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
+ } else {
+ Split = NULL;
+ }
+ if (ShellParameters->StdIn != *OldStdIn) {
+ if ((Split != NULL && Split->SplitStdIn != ShellParameters->StdIn) || Split == NULL) {
+ gEfiShellProtocol->CloseFile(ShellParameters->StdIn);
+ }
+ ShellParameters->StdIn = *OldStdIn;
+ }
+ if (ShellParameters->StdOut != *OldStdOut) {
+ if ((Split != NULL && Split->SplitStdOut != ShellParameters->StdOut) || Split == NULL) {
+ gEfiShellProtocol->CloseFile(ShellParameters->StdOut);
+ }
+ ShellParameters->StdOut = *OldStdOut;
+ }
+ if (ShellParameters->StdErr != *OldStdErr) {
+ gEfiShellProtocol->CloseFile(ShellParameters->StdErr);
+ ShellParameters->StdErr = *OldStdErr;
+ }
+
+ if (gST->ConIn != SystemTableInfo->ConIn) {
+ CloseSimpleTextInOnFile(gST->ConIn);
+ gST->ConIn = SystemTableInfo->ConIn;
+ gST->ConsoleInHandle = SystemTableInfo->ConInHandle;
+ }
+ if (gST->ConOut != SystemTableInfo->ConOut) {
+ CloseSimpleTextOutOnFile(gST->ConOut);
+ gST->ConOut = SystemTableInfo->ConOut;
+ gST->ConsoleOutHandle = SystemTableInfo->ConOutHandle;
+ }
+ if (gST->StdErr != SystemTableInfo->ErrOut) {
+ CloseSimpleTextOutOnFile(gST->StdErr);
+ gST->StdErr = SystemTableInfo->ErrOut;
+ gST->StandardErrorHandle = SystemTableInfo->ErrOutHandle;
+ }
+
+ CalculateEfiHdrCrc(&gST->Hdr);
+
+ return (EFI_SUCCESS);
+}
+/**
+ Funcion will replace the current Argc and Argv in the ShellParameters protocol
+ structure by parsing NewCommandLine. The current values are returned to the
+ user.
+
+ If OldArgv or OldArgc is NULL then that value is not returned.
+
+ @param[in, out] ShellParameters Pointer to parameter structure to modify.
+ @param[in] NewCommandLine The new command line to parse and use.
+ @param[in] Type The type of operation.
+ @param[out] OldArgv Pointer to old list of parameters.
+ @param[out] OldArgc Pointer to old number of items in Argv list.
+
+ @retval EFI_SUCCESS Operation was sucessful, Argv and Argc are valid.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+UpdateArgcArgv(
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN CONST CHAR16 *NewCommandLine,
+ IN SHELL_OPERATION_TYPES Type,
+ OUT CHAR16 ***OldArgv OPTIONAL,
+ OUT UINTN *OldArgc OPTIONAL
+ )
+{
+ BOOLEAN StripParamQuotation;
+
+ ASSERT(ShellParameters != NULL);
+ StripParamQuotation = TRUE;
+
+ if (OldArgc != NULL) {
+ *OldArgc = ShellParameters->Argc;
+ }
+ if (OldArgc != NULL) {
+ *OldArgv = ShellParameters->Argv;
+ }
+
+ if (Type == Script_File_Name) {
+ StripParamQuotation = FALSE;
+ }
+
+ return ParseCommandLineToArgs( NewCommandLine,
+ StripParamQuotation,
+ &(ShellParameters->Argv),
+ &(ShellParameters->Argc)
+ );
+}
+
+/**
+ Funcion will replace the current Argc and Argv in the ShellParameters protocol
+ structure with Argv and Argc. The current values are de-allocated and the
+ OldArgv must not be deallocated by the caller.
+
+ @param[in, out] ShellParameters pointer to parameter structure to modify
+ @param[in] OldArgv pointer to old list of parameters
+ @param[in] OldArgc pointer to old number of items in Argv list
+**/
+VOID
+EFIAPI
+RestoreArgcArgv(
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN CHAR16 ***OldArgv,
+ IN UINTN *OldArgc
+ )
+{
+ UINTN LoopCounter;
+ ASSERT(ShellParameters != NULL);
+ ASSERT(OldArgv != NULL);
+ ASSERT(OldArgc != NULL);
+
+ if (ShellParameters->Argv != NULL) {
+ for ( LoopCounter = 0
+ ; LoopCounter < ShellParameters->Argc
+ ; LoopCounter++
+ ){
+ FreePool(ShellParameters->Argv[LoopCounter]);
+ }
+ FreePool(ShellParameters->Argv);
+ }
+ ShellParameters->Argv = *OldArgv;
+ *OldArgv = NULL;
+ ShellParameters->Argc = *OldArgc;
+ *OldArgc = 0;
+}
diff --git a/Core/ShellPkg/Application/Shell/ShellParametersProtocol.h b/Core/ShellPkg/Application/Shell/ShellParametersProtocol.h
new file mode 100644
index 0000000000..1205d92c19
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellParametersProtocol.h
@@ -0,0 +1,194 @@
+/** @file
+ Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation,
+ manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL.
+
+ Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_
+#define _SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_
+
+#include "Shell.h"
+
+typedef enum {
+ Internal_Command,
+ Script_File_Name,
+ Efi_Application,
+ File_Sys_Change,
+ Unknown_Invalid
+} SHELL_OPERATION_TYPES;
+
+/**
+ creates a new EFI_SHELL_PARAMETERS_PROTOCOL instance and populates it and then
+ installs it on our handle and if there is an existing version of the protocol
+ that one is cached for removal later.
+
+ @param[in, out] NewShellParameters on a successful return, a pointer to pointer
+ to the newly installed interface.
+ @param[in, out] RootShellInstance on a successful return, pointer to boolean.
+ TRUE if this is the root shell instance.
+
+ @retval EFI_SUCCESS the operation completed successfully.
+ @return other the operation failed.
+ @sa ReinstallProtocolInterface
+ @sa InstallProtocolInterface
+ @sa ParseCommandLineToArgs
+**/
+EFI_STATUS
+EFIAPI
+CreatePopulateInstallShellParametersProtocol (
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL **NewShellParameters,
+ IN OUT BOOLEAN *RootShellInstance
+ );
+
+/**
+ frees all memory used by createion and installation of shell parameters protocol
+ and if there was an old version installed it will restore that one.
+
+ @param NewShellParameters the interface of EFI_SHELL_PARAMETERS_PROTOCOL that is
+ being cleaned up.
+
+ @retval EFI_SUCCESS the cleanup was successful
+ @return other the cleanup failed
+ @sa ReinstallProtocolInterface
+ @sa UninstallProtocolInterface
+**/
+EFI_STATUS
+EFIAPI
+CleanUpShellParametersProtocol (
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *NewShellParameters
+ );
+
+/**
+ Funcion will replace the current Argc and Argv in the ShellParameters protocol
+ structure by parsing NewCommandLine. The current values are returned to the
+ user.
+
+ @param[in, out] ShellParameters pointer to parameter structure to modify
+ @param[in] NewCommandLine the new command line to parse and use
+ @param[in] Type the type of operation.
+ @param[out] OldArgv pointer to old list of parameters
+ @param[out] OldArgc pointer to old number of items in Argv list
+
+ @retval EFI_SUCCESS operation was sucessful, Argv and Argc are valid
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+UpdateArgcArgv(
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN CONST CHAR16 *NewCommandLine,
+ IN SHELL_OPERATION_TYPES Type,
+ OUT CHAR16 ***OldArgv,
+ OUT UINTN *OldArgc
+ );
+
+/**
+ Funcion will replace the current Argc and Argv in the ShellParameters protocol
+ structure with Argv and Argc. The current values are de-allocated and the
+ OldArgv must not be deallocated by the caller.
+
+ @param[in, out] ShellParameters pointer to parameter structure to modify
+ @param[in] OldArgv pointer to old list of parameters
+ @param[in] OldArgc pointer to old number of items in Argv list
+**/
+VOID
+EFIAPI
+RestoreArgcArgv(
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN CHAR16 ***OldArgv,
+ IN UINTN *OldArgc
+ );
+
+typedef struct {
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
+ EFI_HANDLE ConInHandle;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
+ EFI_HANDLE ConOutHandle;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ErrOut;
+ EFI_HANDLE ErrOutHandle;
+} SYSTEM_TABLE_INFO;
+
+/**
+ Funcion will replace the current StdIn and StdOut in the ShellParameters protocol
+ structure by parsing NewCommandLine. The current values are returned to the
+ user.
+
+ This will also update the system table.
+
+ @param[in, out] ShellParameters Pointer to parameter structure to modify.
+ @param[in] NewCommandLine The new command line to parse and use.
+ @param[out] OldStdIn Pointer to old StdIn.
+ @param[out] OldStdOut Pointer to old StdOut.
+ @param[out] OldStdErr Pointer to old StdErr.
+ @param[out] SystemTableInfo Pointer to old system table information.
+
+ @retval EFI_SUCCESS Operation was sucessful, Argv and Argc are valid.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+UpdateStdInStdOutStdErr(
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN CHAR16 *NewCommandLine,
+ OUT SHELL_FILE_HANDLE *OldStdIn,
+ OUT SHELL_FILE_HANDLE *OldStdOut,
+ OUT SHELL_FILE_HANDLE *OldStdErr,
+ OUT SYSTEM_TABLE_INFO *SystemTableInfo
+ );
+
+/**
+ Funcion will replace the current StdIn and StdOut in the ShellParameters protocol
+ structure with StdIn and StdOut. The current values are de-allocated.
+
+ @param[in, out] ShellParameters Pointer to parameter structure to modify.
+ @param[in] OldStdIn Pointer to old StdIn.
+ @param[in] OldStdOut Pointer to old StdOut.
+ @param[in] OldStdErr Pointer to old StdErr.
+ @param[in] SystemTableInfo Pointer to old system table information.
+**/
+EFI_STATUS
+EFIAPI
+RestoreStdInStdOutStdErr (
+ IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN SHELL_FILE_HANDLE *OldStdIn,
+ IN SHELL_FILE_HANDLE *OldStdOut,
+ IN SHELL_FILE_HANDLE *OldStdErr,
+ IN SYSTEM_TABLE_INFO *SystemTableInfo
+ );
+
+/**
+ function to populate Argc and Argv.
+
+ This function parses the CommandLine and divides it into standard C style Argc/Argv
+ parameters for inclusion in EFI_SHELL_PARAMETERS_PROTOCOL. this supports space
+ delimited and quote surrounded parameter definition.
+
+ @param[in] CommandLine String of command line to parse
+ @param[in] StripQuotation if TRUE then strip the quotation marks surrounding
+ the parameters.
+ @param[in, out] Argv pointer to array of strings; one for each parameter
+ @param[in, out] Argc pointer to number of strings in Argv array
+
+ @return EFI_SUCCESS the operation was sucessful
+ @return EFI_OUT_OF_RESOURCES a memory allocation failed.
+**/
+EFI_STATUS
+EFIAPI
+ParseCommandLineToArgs(
+ IN CONST CHAR16 *CommandLine,
+ IN BOOLEAN StripQuotation,
+ IN OUT CHAR16 ***Argv,
+ IN OUT UINTN *Argc
+ );
+
+#endif //_SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_
+
diff --git a/Core/ShellPkg/Application/Shell/ShellProtocol.c b/Core/ShellPkg/Application/Shell/ShellProtocol.c
new file mode 100644
index 0000000000..55a1e43f06
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellProtocol.c
@@ -0,0 +1,3939 @@
+/** @file
+ Member functions of EFI_SHELL_PROTOCOL and functions for creation,
+ manipulation, and initialization of EFI_SHELL_PROTOCOL.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Shell.h"
+
+#define INIT_NAME_BUFFER_SIZE 128
+
+/**
+ Close an open file handle.
+
+ This function closes a specified file handle. All "dirty" cached file data is
+ flushed to the device, and the file is closed. In all cases the handle is
+ closed.
+
+ @param[in] FileHandle The file handle to close.
+
+ @retval EFI_SUCCESS The file handle was closed successfully.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellClose (
+ IN SHELL_FILE_HANDLE FileHandle
+ )
+{
+ ShellFileHandleRemove(FileHandle);
+ return (FileHandleClose(ConvertShellHandleToEfiFileProtocol(FileHandle)));
+}
+
+/**
+ Internal worker to determine whether there is a BlockIo somewhere
+ upon the device path specified.
+
+ @param[in] DevicePath The device path to test.
+
+ @retval TRUE gEfiBlockIoProtocolGuid was installed on a handle with this device path
+ @retval FALSE gEfiBlockIoProtocolGuid was not found.
+**/
+BOOLEAN
+EFIAPI
+InternalShellProtocolIsBlockIoPresent(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+
+ Handle = NULL;
+
+ DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
+ Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);
+
+ if ((Handle != NULL) && (!EFI_ERROR(Status))) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ Internal worker to determine whether there is a file system somewhere
+ upon the device path specified.
+
+ @param[in] DevicePath The device path to test.
+
+ @retval TRUE gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
+ @retval FALSE gEfiSimpleFileSystemProtocolGuid was not found.
+**/
+BOOLEAN
+EFIAPI
+InternalShellProtocolIsSimpleFileSystemPresent(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+
+ Handle = NULL;
+
+ DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
+
+ if ((Handle != NULL) && (!EFI_ERROR(Status))) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ Internal worker debug helper function to print out maps as they are added.
+
+ @param[in] Mapping string mapping that has been added
+ @param[in] DevicePath pointer to device path that has been mapped.
+
+ @retval EFI_SUCCESS the operation was successful.
+ @return other an error ocurred
+
+ @sa LocateHandle
+ @sa OpenProtocol
+**/
+EFI_STATUS
+EFIAPI
+InternalShellProtocolDebugPrintMessage (
+ IN CONST CHAR16 *Mapping,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Temp;
+
+ Status = EFI_SUCCESS;
+ DEBUG_CODE_BEGIN();
+
+ if (Mapping != NULL) {
+ DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping));
+ }
+ Temp = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
+ DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp));
+ FreePool(Temp);
+
+ DEBUG_CODE_END();
+ return (Status);
+}
+
+/**
+ This function creates a mapping for a device path.
+
+ If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.
+
+ @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping,
+ then the mapping will be deleted.
+ @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':'
+
+ @retval EFI_SUCCESS Mapping created or deleted successfully.
+ @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the
+ boot service function LocateDevicePath().
+ @retval EFI_ACCESS_DENIED The mapping is a built-in alias.
+ @retval EFI_INVALID_PARAMETER Mapping was NULL
+ @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
+ @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
+ @retval EFI_NOT_FOUND There was no mapping found to delete
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetMap(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
+ IN CONST CHAR16 *Mapping
+ )
+{
+ EFI_STATUS Status;
+ SHELL_MAP_LIST *MapListNode;
+
+ if (Mapping == NULL){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (Mapping[StrLen(Mapping)-1] != ':') {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // Delete the mapping
+ //
+ if (DevicePath == NULL) {
+ if (IsListEmpty(&gShellMapList.Link)) {
+ return (EFI_NOT_FOUND);
+ }
+ for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &MapListNode->Link)
+ ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)
+ ){
+ if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {
+ RemoveEntryList(&MapListNode->Link);
+ SHELL_FREE_NON_NULL(MapListNode->DevicePath);
+ SHELL_FREE_NON_NULL(MapListNode->MapName);
+ SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
+ FreePool(MapListNode);
+ return (EFI_SUCCESS);
+ }
+ } // for loop
+
+ //
+ // We didnt find one to delete
+ //
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // make sure this is a valid to add device path
+ //
+ ///@todo add BlockIo to this test...
+ if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)
+ && !InternalShellProtocolIsBlockIoPresent(DevicePath)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // First make sure there is no old mapping
+ //
+ Status = EfiShellSetMap(NULL, Mapping);
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
+ return (Status);
+ }
+
+ //
+ // now add the new one.
+ //
+ Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);
+
+ return(Status);
+}
+
+/**
+ Gets the device path from the mapping.
+
+ This function gets the device path associated with a mapping.
+
+ @param Mapping A pointer to the mapping
+
+ @retval !=NULL Pointer to the device path that corresponds to the
+ device mapping. The returned pointer does not need
+ to be freed.
+ @retval NULL There is no device path associated with the
+ specified mapping.
+**/
+CONST EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiShellGetDevicePathFromMap(
+ IN CONST CHAR16 *Mapping
+ )
+{
+ SHELL_MAP_LIST *MapListItem;
+ CHAR16 *NewName;
+ UINTN Size;
+
+ NewName = NULL;
+ Size = 0;
+
+ StrnCatGrow(&NewName, &Size, Mapping, 0);
+ if (Mapping[StrLen(Mapping)-1] != L':') {
+ StrnCatGrow(&NewName, &Size, L":", 0);
+ }
+
+ MapListItem = ShellCommandFindMapItem(NewName);
+
+ FreePool(NewName);
+
+ if (MapListItem != NULL) {
+ return (MapListItem->DevicePath);
+ }
+ return(NULL);
+}
+
+/**
+ Gets the mapping(s) that most closely matches the device path.
+
+ This function gets the mapping which corresponds to the device path *DevicePath. If
+ there is no exact match, then the mapping which most closely matches *DevicePath
+ is returned, and *DevicePath is updated to point to the remaining portion of the
+ device path. If there is an exact match, the mapping is returned and *DevicePath
+ points to the end-of-device-path node.
+
+ If there are multiple map names they will be semi-colon seperated in the
+ NULL-terminated string.
+
+ @param DevicePath On entry, points to a device path pointer. On
+ exit, updates the pointer to point to the
+ portion of the device path after the mapping.
+
+ @retval NULL No mapping was found.
+ @return !=NULL Pointer to NULL-terminated mapping. The buffer
+ is callee allocated and should be freed by the caller.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetMapFromDevicePath(
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ SHELL_MAP_LIST *Node;
+ CHAR16 *PathForReturn;
+ UINTN PathSize;
+// EFI_HANDLE PathHandle;
+// EFI_HANDLE MapHandle;
+// EFI_STATUS Status;
+// EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+// EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
+
+ if (DevicePath == NULL || *DevicePath == NULL) {
+ return (NULL);
+ }
+
+ PathForReturn = NULL;
+ PathSize = 0;
+
+ for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &Node->Link)
+ ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
+ ){
+ //
+ // check for exact match
+ //
+ if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {
+ ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
+ if (PathSize != 0) {
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
+ }
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
+ }
+ }
+ if (PathForReturn != NULL) {
+ while (!IsDevicePathEndType (*DevicePath)) {
+ *DevicePath = NextDevicePathNode (*DevicePath);
+ }
+ SetDevicePathEndNode (*DevicePath);
+ }
+/*
+ ///@todo finish code for inexact matches.
+ if (PathForReturn == NULL) {
+ PathSize = 0;
+
+ DevicePathCopy = DuplicateDevicePath(*DevicePath);
+ ASSERT(DevicePathCopy != NULL);
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
+ ASSERT_EFI_ERROR(Status);
+ //
+ // check each of the device paths we have to get the root of the path for consist mappings
+ //
+ for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &Node->Link)
+ ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
+ ){
+ if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
+ continue;
+ }
+ MapPathCopy = DuplicateDevicePath(Node->DevicePath);
+ ASSERT(MapPathCopy != NULL);
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
+ if (MapHandle == PathHandle) {
+
+ *DevicePath = DevicePathCopy;
+
+ MapPathCopy = NULL;
+ DevicePathCopy = NULL;
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
+ break;
+ }
+ }
+ //
+ // now add on the non-consistent mappings
+ //
+ for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &Node->Link)
+ ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
+ ){
+ if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
+ continue;
+ }
+ MapPathCopy = Node->DevicePath;
+ ASSERT(MapPathCopy != NULL);
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
+ if (MapHandle == PathHandle) {
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
+ break;
+ }
+ }
+ }
+*/
+
+ return (AddBufferToFreeList(PathForReturn));
+}
+
+/**
+ Converts a device path to a file system-style path.
+
+ This function converts a device path to a file system path by replacing part, or all, of
+ the device path with the file-system mapping. If there are more than one application
+ file system mappings, the one that most closely matches Path will be used.
+
+ @param Path The pointer to the device path
+
+ @retval NULL the device path could not be found.
+ @return all The pointer of the NULL-terminated file path. The path
+ is callee-allocated and should be freed by the caller.
+**/
+CHAR16 *
+EFIAPI
+EfiShellGetFilePathFromDevicePath(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+ EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
+ SHELL_MAP_LIST *MapListItem;
+ CHAR16 *PathForReturn;
+ UINTN PathSize;
+ EFI_HANDLE PathHandle;
+ EFI_HANDLE MapHandle;
+ EFI_STATUS Status;
+ FILEPATH_DEVICE_PATH *FilePath;
+ FILEPATH_DEVICE_PATH *AlignedNode;
+
+ PathForReturn = NULL;
+ PathSize = 0;
+
+ DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;
+ ASSERT(DevicePathCopy != NULL);
+ if (DevicePathCopy == NULL) {
+ return (NULL);
+ }
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
+
+ if (EFI_ERROR(Status)) {
+ return (NULL);
+ }
+ //
+ // check each of the device paths we have to get the root of the path
+ //
+ for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &MapListItem->Link)
+ ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)
+ ){
+ MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;
+ ASSERT(MapPathCopy != NULL);
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
+ if (MapHandle == PathHandle) {
+ ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);
+ //
+ // go through all the remaining nodes in the device path
+ //
+ for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy
+ ; !IsDevicePathEnd (&FilePath->Header)
+ ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)
+ ){
+ //
+ // If any node is not a file path node, then the conversion can not be completed
+ //
+ if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||
+ (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {
+ FreePool(PathForReturn);
+ return NULL;
+ }
+
+ //
+ // append the path part onto the filepath.
+ //
+ ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
+
+ AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath);
+ if (AlignedNode == NULL) {
+ FreePool (PathForReturn);
+ return NULL;
+ }
+
+ // File Path Device Path Nodes 'can optionally add a "\" separator to
+ // the beginning and/or the end of the Path Name string.'
+ // (UEFI Spec 2.4 section 9.3.6.4).
+ // If necessary, add a "\", but otherwise don't
+ // (This is specified in the above section, and also implied by the
+ // UEFI Shell spec section 3.7)
+ if ((PathSize != 0) &&
+ (PathForReturn != NULL) &&
+ (PathForReturn[PathSize - 1] != L'\\') &&
+ (AlignedNode->PathName[0] != L'\\')) {
+ PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1);
+ }
+
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, AlignedNode->PathName, 0);
+ FreePool(AlignedNode);
+ } // for loop of remaining nodes
+ }
+ if (PathForReturn != NULL) {
+ break;
+ }
+ } // for loop of paths to check
+ return(PathForReturn);
+}
+
+/**
+ Converts a file system style name to a device path.
+
+ This function converts a file system style name to a device path, by replacing any
+ mapping references to the associated device path.
+
+ @param[in] Path The pointer to the path.
+
+ @return The pointer of the file path. The file path is callee
+ allocated and should be freed by the caller.
+ @retval NULL The path could not be found.
+ @retval NULL There was not enough available memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiShellGetDevicePathFromFilePath(
+ IN CONST CHAR16 *Path
+ )
+{
+ CHAR16 *MapName;
+ CHAR16 *NewPath;
+ CONST CHAR16 *Cwd;
+ UINTN Size;
+ CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopyForFree;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathForReturn;
+ EFI_HANDLE Handle;
+ EFI_STATUS Status;
+
+ if (Path == NULL) {
+ return (NULL);
+ }
+
+ MapName = NULL;
+ NewPath = NULL;
+
+ if (StrStr(Path, L":") == NULL) {
+ Cwd = EfiShellGetCurDir(NULL);
+ if (Cwd == NULL) {
+ return (NULL);
+ }
+ Size = StrSize(Cwd) + StrSize(Path);
+ NewPath = AllocateZeroPool(Size);
+ if (NewPath == NULL) {
+ return (NULL);
+ }
+ StrCpyS(NewPath, Size/sizeof(CHAR16), Cwd);
+ StrCatS(NewPath, Size/sizeof(CHAR16), L"\\");
+ if (*Path == L'\\') {
+ Path++;
+ while (PathRemoveLastItem(NewPath)) ;
+ }
+ StrCatS(NewPath, Size/sizeof(CHAR16), Path);
+ DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);
+ FreePool(NewPath);
+ return (DevicePathForReturn);
+ }
+
+ Size = 0;
+ //
+ // find the part before (but including) the : for the map name
+ //
+ ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));
+ MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));
+ if (MapName == NULL || MapName[StrLen(MapName)-1] != L':') {
+ return (NULL);
+ }
+
+ //
+ // look up the device path in the map
+ //
+ DevicePath = EfiShellGetDevicePathFromMap(MapName);
+ if (DevicePath == NULL) {
+ //
+ // Must have been a bad Mapname
+ //
+ return (NULL);
+ }
+
+ //
+ // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)
+ //
+ DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath(DevicePath);
+ if (DevicePathCopy == NULL) {
+ FreePool(MapName);
+ return (NULL);
+ }
+
+ //
+ // get the handle
+ //
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
+ if (EFI_ERROR(Status)) {
+ if (DevicePathCopyForFree != NULL) {
+ FreePool(DevicePathCopyForFree);
+ }
+ FreePool(MapName);
+ return (NULL);
+ }
+
+ //
+ // build the full device path
+ //
+ if (*(Path+StrLen(MapName)+1) == CHAR_NULL) {
+ DevicePathForReturn = FileDevicePath(Handle, L"\\");
+ } else {
+ DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName));
+ }
+
+ FreePool(MapName);
+ if (DevicePathCopyForFree != NULL) {
+ FreePool(DevicePathCopyForFree);
+ }
+
+ return (DevicePathForReturn);
+}
+
+/**
+ Gets the name of the device specified by the device handle.
+
+ This function gets the user-readable name of the device specified by the device
+ handle. If no user-readable name could be generated, then *BestDeviceName will be
+ NULL and EFI_NOT_FOUND will be returned.
+
+ If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
+ device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
+ DeviceHandle.
+
+ If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
+ device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
+ If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
+ EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
+ EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
+
+ @param DeviceHandle The handle of the device.
+ @param Flags Determines the possible sources of component names.
+ Valid bits are:
+ EFI_DEVICE_NAME_USE_COMPONENT_NAME
+ EFI_DEVICE_NAME_USE_DEVICE_PATH
+ @param Language A pointer to the language specified for the device
+ name, in the same format as described in the UEFI
+ specification, Appendix M
+ @param BestDeviceName On return, points to the callee-allocated NULL-
+ terminated name of the device. If no device name
+ could be found, points to NULL. The name must be
+ freed by the caller...
+
+ @retval EFI_SUCCESS Get the name successfully.
+ @retval EFI_NOT_FOUND Fail to get the device name.
+ @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
+ @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
+ @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetDeviceName(
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
+ IN CHAR8 *Language,
+ OUT CHAR16 **BestDeviceName
+ )
+{
+ EFI_STATUS Status;
+ EFI_COMPONENT_NAME2_PROTOCOL *CompName2;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE *HandleList;
+ UINTN HandleCount;
+ UINTN LoopVar;
+ CHAR16 *DeviceNameToReturn;
+ CHAR8 *Lang;
+ UINTN ParentControllerCount;
+ EFI_HANDLE *ParentControllerBuffer;
+ UINTN ParentDriverCount;
+ EFI_HANDLE *ParentDriverBuffer;
+
+ if (BestDeviceName == NULL ||
+ DeviceHandle == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // make sure one of the 2 supported bits is on
+ //
+ if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&
+ ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ DeviceNameToReturn = NULL;
+ *BestDeviceName = NULL;
+ HandleList = NULL;
+ HandleCount = 0;
+ Lang = NULL;
+
+ if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {
+ Status = ParseHandleDatabaseByRelationship(
+ NULL,
+ DeviceHandle,
+ HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,
+ &HandleCount,
+ &HandleList);
+ for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){
+ //
+ // Go through those handles until we get one that passes for GetComponentName
+ //
+ Status = gBS->OpenProtocol(
+ HandleList[LoopVar],
+ &gEfiComponentName2ProtocolGuid,
+ (VOID**)&CompName2,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ Status = gBS->OpenProtocol(
+ HandleList[LoopVar],
+ &gEfiComponentNameProtocolGuid,
+ (VOID**)&CompName2,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ }
+
+ if (EFI_ERROR(Status)) {
+ continue;
+ }
+ Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
+ Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
+ FreePool(Lang);
+ Lang = NULL;
+ if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
+ break;
+ }
+ }
+ if (HandleList != NULL) {
+ FreePool(HandleList);
+ }
+
+ //
+ // Now check the parent controller using this as the child.
+ //
+ if (DeviceNameToReturn == NULL){
+ PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);
+ for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) {
+ PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);
+ for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) {
+ //
+ // try using that driver's component name with controller and our driver as the child.
+ //
+ Status = gBS->OpenProtocol(
+ ParentDriverBuffer[HandleCount],
+ &gEfiComponentName2ProtocolGuid,
+ (VOID**)&CompName2,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ Status = gBS->OpenProtocol(
+ ParentDriverBuffer[HandleCount],
+ &gEfiComponentNameProtocolGuid,
+ (VOID**)&CompName2,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ }
+
+ if (EFI_ERROR(Status)) {
+ continue;
+ }
+ Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
+ Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);
+ FreePool(Lang);
+ Lang = NULL;
+ if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
+ break;
+ }
+
+
+
+ }
+ SHELL_FREE_NON_NULL(ParentDriverBuffer);
+ if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
+ break;
+ }
+ }
+ SHELL_FREE_NON_NULL(ParentControllerBuffer);
+ }
+ //
+ // dont return on fail since we will try device path if that bit is on
+ //
+ if (DeviceNameToReturn != NULL){
+ ASSERT(BestDeviceName != NULL);
+ StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);
+ return (EFI_SUCCESS);
+ }
+ }
+ if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
+ Status = gBS->OpenProtocol(
+ DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID**)&DevicePath,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (!EFI_ERROR(Status)) {
+ //
+ // use device path to text on the device path
+ //
+ *BestDeviceName = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
+ return (EFI_SUCCESS);
+ }
+ }
+ //
+ // none of the selected bits worked.
+ //
+ return (EFI_NOT_FOUND);
+}
+
+/**
+ Opens the root directory of a device on a handle
+
+ This function opens the root directory of a device and returns a file handle to it.
+
+ @param DeviceHandle The handle of the device that contains the volume.
+ @param FileHandle On exit, points to the file handle corresponding to the root directory on the
+ device.
+
+ @retval EFI_SUCCESS Root opened successfully.
+ @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
+ could not be opened.
+ @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
+ @retval EFI_DEVICE_ERROR The device had an error
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenRootByHandle(
+ IN EFI_HANDLE DeviceHandle,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
+ EFI_FILE_PROTOCOL *RealFileHandle;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+
+ //
+ // get the simple file system interface
+ //
+ Status = gBS->OpenProtocol(DeviceHandle,
+ &gEfiSimpleFileSystemProtocolGuid,
+ (VOID**)&SimpleFileSystem,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ return (EFI_NOT_FOUND);
+ }
+
+ Status = gBS->OpenProtocol(DeviceHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID**)&DevPath,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ return (EFI_NOT_FOUND);
+ }
+ //
+ // Open the root volume now...
+ //
+ Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &RealFileHandle);
+ *FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath));
+ return (Status);
+}
+
+/**
+ Opens the root directory of a device.
+
+ This function opens the root directory of a device and returns a file handle to it.
+
+ @param DevicePath Points to the device path corresponding to the device where the
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
+ @param FileHandle On exit, points to the file handle corresponding to the root directory on the
+ device.
+
+ @retval EFI_SUCCESS Root opened successfully.
+ @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
+ could not be opened.
+ @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
+ @retval EFI_DEVICE_ERROR The device had an error
+ @retval EFI_INVALID_PARAMETER FileHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenRoot(
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+
+ if (FileHandle == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // find the handle of the device with that device handle and the file system
+ //
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
+ &DevicePath,
+ &Handle);
+ if (EFI_ERROR(Status)) {
+ return (EFI_NOT_FOUND);
+ }
+
+ return (EfiShellOpenRootByHandle(Handle, FileHandle));
+}
+
+/**
+ Returns whether any script files are currently being processed.
+
+ @retval TRUE There is at least one script file active.
+ @retval FALSE No script files are active now.
+
+**/
+BOOLEAN
+EFIAPI
+EfiShellBatchIsActive (
+ VOID
+ )
+{
+ if (ShellCommandGetCurrentScriptFile() == NULL) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/**
+ Worker function to open a file based on a device path. this will open the root
+ of the volume and then traverse down to the file itself.
+
+ @param DevicePath Device Path of the file.
+ @param FileHandle Pointer to the file upon a successful return.
+ @param OpenMode mode to open file in.
+ @param Attributes the File Attributes to use when creating a new file.
+
+ @retval EFI_SUCCESS the file is open and FileHandle is valid
+ @retval EFI_UNSUPPORTED the device path cotained non-path elements
+ @retval other an error ocurred.
+**/
+EFI_STATUS
+EFIAPI
+InternalOpenFileDevicePath(
+ IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ FILEPATH_DEVICE_PATH *FilePathNode;
+ EFI_HANDLE Handle;
+ SHELL_FILE_HANDLE ShellHandle;
+ EFI_FILE_PROTOCOL *Handle1;
+ EFI_FILE_PROTOCOL *Handle2;
+ FILEPATH_DEVICE_PATH *AlignedNode;
+
+ if (FileHandle == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = NULL;
+ Handle1 = NULL;
+ Handle2 = NULL;
+ Handle = NULL;
+ ShellHandle = NULL;
+ FilePathNode = NULL;
+ AlignedNode = NULL;
+
+ Status = EfiShellOpenRoot(DevicePath, &ShellHandle);
+
+ if (!EFI_ERROR(Status)) {
+ Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);
+ if (Handle1 != NULL) {
+ //
+ // chop off the begining part before the file system part...
+ //
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
+ &DevicePath,
+ &Handle);
+ if (!EFI_ERROR(Status)) {
+ //
+ // To access as a file system, the file path should only
+ // contain file path components. Follow the file path nodes
+ // and find the target file
+ //
+ for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath
+ ; !IsDevicePathEnd (&FilePathNode->Header)
+ ; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header)
+ ){
+ SHELL_FREE_NON_NULL(AlignedNode);
+ AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode);
+ //
+ // For file system access each node should be a file path component
+ //
+ if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
+ DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP
+ ) {
+ Status = EFI_UNSUPPORTED;
+ break;
+ }
+
+ //
+ // Open this file path node
+ //
+ Handle2 = Handle1;
+ Handle1 = NULL;
+
+ //
+ // if this is the last node in the DevicePath always create (if that was requested).
+ //
+ if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {
+ Status = Handle2->Open (
+ Handle2,
+ &Handle1,
+ AlignedNode->PathName,
+ OpenMode,
+ Attributes
+ );
+ } else {
+
+ //
+ // This is not the last node and we dont want to 'create' existing
+ // directory entries...
+ //
+
+ //
+ // open without letting it create
+ // prevents error on existing files/directories
+ //
+ Status = Handle2->Open (
+ Handle2,
+ &Handle1,
+ AlignedNode->PathName,
+ OpenMode &~EFI_FILE_MODE_CREATE,
+ Attributes
+ );
+ //
+ // if above failed now open and create the 'item'
+ // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)
+ //
+ if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
+ Status = Handle2->Open (
+ Handle2,
+ &Handle1,
+ AlignedNode->PathName,
+ OpenMode,
+ Attributes
+ );
+ }
+ }
+ //
+ // Close the last node
+ //
+ ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);
+
+ //
+ // If there's been an error, stop
+ //
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ } // for loop
+ }
+ }
+ }
+ SHELL_FREE_NON_NULL(AlignedNode);
+ if (EFI_ERROR(Status)) {
+ if (Handle1 != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1);
+ }
+ } else {
+ *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));
+ }
+ return (Status);
+}
+
+/**
+ Creates a file or directory by name.
+
+ This function creates an empty new file or directory with the specified attributes and
+ returns the new file's handle. If the file already exists and is read-only, then
+ EFI_INVALID_PARAMETER will be returned.
+
+ If the file already existed, it is truncated and its attributes updated. If the file is
+ created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
+
+ If the file name begins with >v, then the file handle which is returned refers to the
+ shell environment variable with the specified name. If the shell environment variable
+ already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
+
+ @param FileName Pointer to NULL-terminated file path
+ @param FileAttribs The new file's attrbiutes. the different attributes are
+ described in EFI_FILE_PROTOCOL.Open().
+ @param FileHandle On return, points to the created file handle or directory's handle
+
+ @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED could not open the file path
+ @retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not
+ file the file system on the device.
+ @retval EFI_NO_MEDIA the device has no medium.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported.
+ @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
+ the DirName.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected.
+ @retval EFI_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
+ @retval EFI_VOLUME_FULL The volume is full.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellCreateFile(
+ IN CONST CHAR16 *FileName,
+ IN UINT64 FileAttribs,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+ BOOLEAN Volatile;
+
+ //
+ // Is this for an environment variable
+ // do we start with >v
+ //
+ if (StrStr(FileName, L">v") == FileName) {
+ Status = IsVolatileEnv (FileName + 2, &Volatile);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (!Volatile) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = CreateFileInterfaceEnv(FileName+2);
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // We are opening a regular file.
+ //
+ DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
+ if (DevicePath == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+
+ Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs);
+ FreePool(DevicePath);
+
+ return(Status);
+}
+
+/**
+ Register a GUID and a localized human readable name for it.
+
+ If Guid is not assigned a name, then assign GuidName to Guid. This list of GUID
+ names must be used whenever a shell command outputs GUID information.
+
+ This function is only available when the major and minor versions in the
+ EfiShellProtocol are greater than or equal to 2 and 1, respectively.
+
+ @param[in] Guid A pointer to the GUID being registered.
+ @param[in] GuidName A pointer to the localized name for the GUID being registered.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_INVALID_PARAMETER Guid was NULL.
+ @retval EFI_INVALID_PARAMETER GuidName was NULL.
+ @retval EFI_ACCESS_DENIED Guid already is assigned a name.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellRegisterGuidName(
+ IN CONST EFI_GUID *Guid,
+ IN CONST CHAR16 *GuidName
+ )
+{
+ return (AddNewGuidNameMapping(Guid, GuidName, NULL));
+}
+
+/**
+ Opens a file or a directory by file name.
+
+ This function opens the specified file in the specified OpenMode and returns a file
+ handle.
+ If the file name begins with >v, then the file handle which is returned refers to the
+ shell environment variable with the specified name. If the shell environment variable
+ exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
+ EFI_INVALID_PARAMETER is returned.
+
+ If the file name is >i, then the file handle which is returned refers to the standard
+ input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is >o, then the file handle which is returned refers to the standard
+ output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is >e, then the file handle which is returned refers to the standard
+ error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is NUL, then the file handle that is returned refers to the standard NUL
+ file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
+ returned.
+
+ If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
+ FileHandle is NULL.
+
+ @param FileName Points to the NULL-terminated UCS-2 encoded file name.
+ @param FileHandle On return, points to the file handle.
+ @param OpenMode File open mode. Either EFI_FILE_MODE_READ or
+ EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
+ Specification.
+ @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
+ @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL.
+ @retval EFI_NOT_FOUND The specified file could not be found on the device or the file
+ system could not be found on the device. FileHandle is NULL.
+ @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported. FileHandle is NULL.
+ @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
+ the FileName. FileHandle is NULL.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected. FileHandle is NULL.
+ @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle
+ is NULL.
+ @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenFileByName(
+ IN CONST CHAR16 *FileName,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+ BOOLEAN Volatile;
+
+ *FileHandle = NULL;
+
+ //
+ // Is this for StdIn
+ //
+ if (StrCmp(FileName, L">i") == 0) {
+ //
+ // make sure not writing to StdIn
+ //
+ if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
+ ASSERT(*FileHandle != NULL);
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for StdOut
+ //
+ if (StrCmp(FileName, L">o") == 0) {
+ //
+ // make sure not writing to StdIn
+ //
+ if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = &FileInterfaceStdOut;
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for NUL file
+ //
+ if (StrCmp(FileName, L"NUL") == 0) {
+ *FileHandle = &FileInterfaceNulFile;
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for StdErr
+ //
+ if (StrCmp(FileName, L">e") == 0) {
+ //
+ // make sure not writing to StdIn
+ //
+ if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = &FileInterfaceStdErr;
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for an environment variable
+ // do we start with >v
+ //
+ if (StrStr(FileName, L">v") == FileName) {
+ Status = IsVolatileEnv (FileName + 2, &Volatile);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (!Volatile &&
+ ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = CreateFileInterfaceEnv(FileName+2);
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // We are opening a regular file.
+ //
+ DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
+// DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath););
+ if (DevicePath == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // Copy the device path, open the file, then free the memory
+ //
+ Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
+ FreePool(DevicePath);
+
+ return(Status);
+}
+
+/**
+ Deletes the file specified by the file name.
+
+ This function deletes a file.
+
+ @param FileName Points to the NULL-terminated file name.
+
+ @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed.
+ @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
+ @sa EfiShellCreateFile
+**/
+EFI_STATUS
+EFIAPI
+EfiShellDeleteFileByName(
+ IN CONST CHAR16 *FileName
+ )
+{
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_STATUS Status;
+
+ FileHandle = NULL;
+
+ //
+ // get a handle to the file
+ //
+ Status = EfiShellCreateFile(FileName,
+ 0,
+ &FileHandle);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+ //
+ // now delete the file
+ //
+ ShellFileHandleRemove(FileHandle);
+ return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));
+}
+
+/**
+ Disables the page break output mode.
+**/
+VOID
+EFIAPI
+EfiShellDisablePageBreak (
+ VOID
+ )
+{
+ ShellInfoObject.PageBreakEnabled = FALSE;
+}
+
+/**
+ Enables the page break output mode.
+**/
+VOID
+EFIAPI
+EfiShellEnablePageBreak (
+ VOID
+ )
+{
+ ShellInfoObject.PageBreakEnabled = TRUE;
+}
+
+/**
+ internal worker function to load and run an image via device path.
+
+ @param ParentImageHandle A handle of the image that is executing the specified
+ command line.
+ @param DevicePath device path of the file to execute
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+
+ @param[out] StartImageStatus Returned status from gBS->StartImage.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+**/
+EFI_STATUS
+EFIAPI
+InternalShellExecuteDevicePath(
+ IN CONST EFI_HANDLE *ParentImageHandle,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CONST CHAR16 *CommandLine OPTIONAL,
+ IN CONST CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StartImageStatus OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS StartStatus;
+ EFI_STATUS CleanupStatus;
+ EFI_HANDLE NewHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ LIST_ENTRY OrigEnvs;
+ EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
+ CHAR16 *ImagePath;
+ UINTN Index;
+ CHAR16 *Walker;
+ CHAR16 *NewCmdLine;
+
+ if (ParentImageHandle == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ InitializeListHead(&OrigEnvs);
+ ZeroMem(&ShellParamsProtocol, sizeof(EFI_SHELL_PARAMETERS_PROTOCOL));
+
+ NewHandle = NULL;
+
+ NewCmdLine = AllocateCopyPool (StrSize (CommandLine), CommandLine);
+ if (NewCmdLine == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
+ if (*Walker == L'^' && *(Walker+1) == L'#') {
+ CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
+ }
+ }
+
+ //
+ // Load the image with:
+ // FALSE - not from boot manager and NULL, 0 being not already in memory
+ //
+ Status = gBS->LoadImage(
+ FALSE,
+ *ParentImageHandle,
+ (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
+ NULL,
+ 0,
+ &NewHandle);
+
+ if (EFI_ERROR(Status)) {
+ if (NewHandle != NULL) {
+ gBS->UnloadImage(NewHandle);
+ }
+ FreePool (NewCmdLine);
+ return (Status);
+ }
+ Status = gBS->OpenProtocol(
+ NewHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID**)&LoadedImage,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (!EFI_ERROR(Status)) {
+ //
+ // If the image is not an app abort it.
+ //
+ if (LoadedImage->ImageCodeType != EfiLoaderCode){
+ ShellPrintHiiEx(
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_SHELL_IMAGE_NOT_APP),
+ ShellInfoObject.HiiHandle
+ );
+ goto UnloadImage;
+ }
+
+ ASSERT(LoadedImage->LoadOptionsSize == 0);
+ if (NewCmdLine != NULL) {
+ LoadedImage->LoadOptionsSize = (UINT32)StrSize(NewCmdLine);
+ LoadedImage->LoadOptions = (VOID*)NewCmdLine;
+ }
+
+ //
+ // Save our current environment settings for later restoration if necessary
+ //
+ if (Environment != NULL) {
+ Status = GetEnvironmentVariableList(&OrigEnvs);
+ if (!EFI_ERROR(Status)) {
+ Status = SetEnvironmentVariables(Environment);
+ }
+ }
+
+ //
+ // Initialize and install a shell parameters protocol on the image.
+ //
+ ShellParamsProtocol.StdIn = ShellInfoObject.NewShellParametersProtocol->StdIn;
+ ShellParamsProtocol.StdOut = ShellInfoObject.NewShellParametersProtocol->StdOut;
+ ShellParamsProtocol.StdErr = ShellInfoObject.NewShellParametersProtocol->StdErr;
+ Status = UpdateArgcArgv(&ShellParamsProtocol, NewCmdLine, Efi_Application, NULL, NULL);
+ ASSERT_EFI_ERROR(Status);
+ //
+ // Replace Argv[0] with the full path of the binary we're executing:
+ // If the command line was "foo", the binary might be called "foo.efi".
+ // "The first entry in [Argv] is always the full file path of the
+ // executable" - UEFI Shell Spec section 2.3
+ //
+ ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
+ // The image we're executing isn't necessarily in a filesystem - it might
+ // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
+ // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
+ if (ImagePath != NULL) {
+ if (ShellParamsProtocol.Argv == NULL) {
+ // Command line was empty or null.
+ // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
+ ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
+ if (ShellParamsProtocol.Argv == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto UnloadImage;
+ }
+ ShellParamsProtocol.Argc = 1;
+ } else {
+ // Free the string UpdateArgcArgv put in Argv[0];
+ FreePool (ShellParamsProtocol.Argv[0]);
+ }
+ ShellParamsProtocol.Argv[0] = ImagePath;
+ }
+
+ Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
+ ASSERT_EFI_ERROR(Status);
+
+ ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
+
+ //
+ // now start the image and if the caller wanted the return code pass it to them...
+ //
+ if (!EFI_ERROR(Status)) {
+ StartStatus = gBS->StartImage(
+ NewHandle,
+ 0,
+ NULL
+ );
+ if (StartImageStatus != NULL) {
+ *StartImageStatus = StartStatus;
+ }
+
+ CleanupStatus = gBS->UninstallProtocolInterface(
+ NewHandle,
+ &gEfiShellParametersProtocolGuid,
+ &ShellParamsProtocol
+ );
+ ASSERT_EFI_ERROR(CleanupStatus);
+
+ goto FreeAlloc;
+ }
+
+UnloadImage:
+ // Unload image - We should only get here if we didn't call StartImage
+ gBS->UnloadImage (NewHandle);
+
+FreeAlloc:
+ // Free Argv (Allocated in UpdateArgcArgv)
+ if (ShellParamsProtocol.Argv != NULL) {
+ for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) {
+ if (ShellParamsProtocol.Argv[Index] != NULL) {
+ FreePool (ShellParamsProtocol.Argv[Index]);
+ }
+ }
+ FreePool (ShellParamsProtocol.Argv);
+ }
+ }
+
+ // Restore environment variables
+ if (!IsListEmpty(&OrigEnvs)) {
+ CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
+ ASSERT_EFI_ERROR (CleanupStatus);
+ }
+
+ FreePool (NewCmdLine);
+
+ return(Status);
+}
+
+/**
+ internal worker function to load and run an image in the current shell.
+
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+
+ @param[out] StartImageStatus Returned status from the command line.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+**/
+EFI_STATUS
+EFIAPI
+InternalShellExecute(
+ IN CONST CHAR16 *CommandLine OPTIONAL,
+ IN CONST CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StartImageStatus OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS CleanupStatus;
+ LIST_ENTRY OrigEnvs;
+
+ InitializeListHead(&OrigEnvs);
+
+ //
+ // Save our current environment settings for later restoration if necessary
+ //
+ if (Environment != NULL) {
+ Status = GetEnvironmentVariableList(&OrigEnvs);
+ if (!EFI_ERROR(Status)) {
+ Status = SetEnvironmentVariables(Environment);
+ } else {
+ return Status;
+ }
+ }
+
+ Status = RunShellCommand(CommandLine, StartImageStatus);
+
+ // Restore environment variables
+ if (!IsListEmpty(&OrigEnvs)) {
+ CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
+ ASSERT_EFI_ERROR (CleanupStatus);
+ }
+
+ return(Status);
+}
+
+/**
+ Determine if the UEFI Shell is currently running with nesting enabled or disabled.
+
+ @retval FALSE nesting is required
+ @retval other nesting is enabled
+**/
+STATIC
+BOOLEAN
+EFIAPI
+NestingEnabled(
+)
+{
+ EFI_STATUS Status;
+ CHAR16 *Temp;
+ CHAR16 *Temp2;
+ UINTN TempSize;
+ BOOLEAN RetVal;
+
+ RetVal = TRUE;
+ Temp = NULL;
+ Temp2 = NULL;
+
+ if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
+ TempSize = 0;
+ Temp = NULL;
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(mNoNestingEnvVarName, &TempSize, Temp);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Temp = AllocateZeroPool(TempSize + sizeof(CHAR16));
+ if (Temp != NULL) {
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(mNoNestingEnvVarName, &TempSize, Temp);
+ }
+ }
+ Temp2 = StrnCatGrow(&Temp2, NULL, mNoNestingTrue, 0);
+ if (Temp != NULL && Temp2 != NULL && StringNoCaseCompare(&Temp, &Temp2) == 0) {
+ //
+ // Use the no nesting method.
+ //
+ RetVal = FALSE;
+ }
+ }
+
+ SHELL_FREE_NON_NULL(Temp);
+ SHELL_FREE_NON_NULL(Temp2);
+ return (RetVal);
+}
+
+/**
+ Execute the command line.
+
+ This function creates a nested instance of the shell and executes the specified
+ command (CommandLine) with the specified environment (Environment). Upon return,
+ the status code returned by the specified command is placed in StatusCode.
+
+ If Environment is NULL, then the current environment is used and all changes made
+ by the commands executed will be reflected in the current environment. If the
+ Environment is non-NULL, then the changes made will be discarded.
+
+ The CommandLine is executed from the current working directory on the current
+ device.
+
+ @param ParentImageHandle A handle of the image that is executing the specified
+ command line.
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+ @param StatusCode Points to the status code returned by the CommandLine.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+ @retval EFI_UNSUPPORTED The support level required for this function is not present.
+
+ @sa InternalShellExecuteDevicePath
+**/
+EFI_STATUS
+EFIAPI
+EfiShellExecute(
+ IN EFI_HANDLE *ParentImageHandle,
+ IN CHAR16 *CommandLine OPTIONAL,
+ IN CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StatusCode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Temp;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ UINTN Size;
+
+ if ((PcdGet8(PcdShellSupportLevel) < 1)) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ if (NestingEnabled()) {
+ DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
+
+ DEBUG_CODE_BEGIN();
+ Temp = ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);
+ FreePool(Temp);
+ Temp = ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);
+ FreePool(Temp);
+ Temp = ConvertDevicePathToText(DevPath, TRUE, TRUE);
+ FreePool(Temp);
+ DEBUG_CODE_END();
+
+ Temp = NULL;
+ Size = 0;
+ ASSERT((Temp == NULL && Size == 0) || (Temp != NULL));
+ StrnCatGrow(&Temp, &Size, L"Shell.efi -_exit ", 0);
+ StrnCatGrow(&Temp, &Size, CommandLine, 0);
+
+ Status = InternalShellExecuteDevicePath(
+ ParentImageHandle,
+ DevPath,
+ Temp,
+ (CONST CHAR16**)Environment,
+ StatusCode);
+
+ //
+ // de-allocate and return
+ //
+ FreePool(DevPath);
+ FreePool(Temp);
+ } else {
+ Status = InternalShellExecute(
+ (CONST CHAR16*)CommandLine,
+ (CONST CHAR16**)Environment,
+ StatusCode);
+ }
+
+ return(Status);
+}
+
+/**
+ Utility cleanup function for EFI_SHELL_FILE_INFO objects.
+
+ 1) frees all pointers (non-NULL)
+ 2) Closes the SHELL_FILE_HANDLE
+
+ @param FileListNode pointer to the list node to free
+**/
+VOID
+EFIAPI
+InternalFreeShellFileInfoNode(
+ IN EFI_SHELL_FILE_INFO *FileListNode
+ )
+{
+ if (FileListNode->Info != NULL) {
+ FreePool((VOID*)FileListNode->Info);
+ }
+ if (FileListNode->FileName != NULL) {
+ FreePool((VOID*)FileListNode->FileName);
+ }
+ if (FileListNode->FullName != NULL) {
+ FreePool((VOID*)FileListNode->FullName);
+ }
+ if (FileListNode->Handle != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);
+ }
+ FreePool(FileListNode);
+}
+/**
+ Frees the file list.
+
+ This function cleans up the file list and any related data structures. It has no
+ impact on the files themselves.
+
+ @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is
+ defined in OpenFileList()
+
+ @retval EFI_SUCCESS Free the file list successfully.
+ @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFreeFileList(
+ IN EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_SHELL_FILE_INFO *ShellFileListItem;
+
+ if (FileList == NULL || *FileList == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
+ ; !IsListEmpty(&(*FileList)->Link)
+ ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
+ ){
+ RemoveEntryList(&ShellFileListItem->Link);
+ InternalFreeShellFileInfoNode(ShellFileListItem);
+ }
+ InternalFreeShellFileInfoNode(*FileList);
+ *FileList = NULL;
+ return(EFI_SUCCESS);
+}
+
+/**
+ Deletes the duplicate file names files in the given file list.
+
+ This function deletes the reduplicate files in the given file list.
+
+ @param FileList A pointer to the first entry in the file list.
+
+ @retval EFI_SUCCESS Always success.
+ @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
+**/
+EFI_STATUS
+EFIAPI
+EfiShellRemoveDupInFileList(
+ IN EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_SHELL_FILE_INFO *ShellFileListItem;
+ EFI_SHELL_FILE_INFO *ShellFileListItem2;
+ EFI_SHELL_FILE_INFO *TempNode;
+
+ if (FileList == NULL || *FileList == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
+ ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
+ ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
+ ){
+ for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
+ ; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link)
+ ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link)
+ ){
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)ShellFileListItem->FullName,
+ (CHAR16*)ShellFileListItem2->FullName) == 0
+ ){
+ TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode(
+ &(*FileList)->Link,
+ &ShellFileListItem2->Link
+ );
+ RemoveEntryList(&ShellFileListItem2->Link);
+ InternalFreeShellFileInfoNode(ShellFileListItem2);
+ // Set ShellFileListItem2 to PreviousNode so we don't access Freed
+ // memory in GetNextNode in the loop expression above.
+ ShellFileListItem2 = TempNode;
+ }
+ }
+ }
+ return (EFI_SUCCESS);
+}
+
+//
+// This is the same structure as the external version, but it has no CONST qualifiers.
+//
+typedef struct {
+ LIST_ENTRY Link; ///< Linked list members.
+ EFI_STATUS Status; ///< Status of opening the file. Valid only if Handle != NULL.
+ CHAR16 *FullName; ///< Fully qualified filename.
+ CHAR16 *FileName; ///< name of this file.
+ SHELL_FILE_HANDLE Handle; ///< Handle for interacting with the opened file or NULL if closed.
+ EFI_FILE_INFO *Info; ///< Pointer to the FileInfo struct for this file or NULL.
+} EFI_SHELL_FILE_INFO_NO_CONST;
+
+/**
+ Allocates and duplicates a EFI_SHELL_FILE_INFO node.
+
+ @param[in] Node The node to copy from.
+ @param[in] Save TRUE to set Node->Handle to NULL, FALSE otherwise.
+
+ @retval NULL a memory allocation error ocurred
+ @return != NULL a pointer to the new node
+**/
+EFI_SHELL_FILE_INFO*
+EFIAPI
+InternalDuplicateShellFileInfo(
+ IN EFI_SHELL_FILE_INFO *Node,
+ IN BOOLEAN Save
+ )
+{
+ EFI_SHELL_FILE_INFO_NO_CONST *NewNode;
+
+ //
+ // try to confirm that the objects are in sync
+ //
+ ASSERT(sizeof(EFI_SHELL_FILE_INFO_NO_CONST) == sizeof(EFI_SHELL_FILE_INFO));
+
+ NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
+ if (NewNode == NULL) {
+ return (NULL);
+ }
+ NewNode->FullName = AllocateCopyPool(StrSize(Node->FullName), Node->FullName);
+ NewNode->FileName = AllocateCopyPool(StrSize(Node->FileName), Node->FileName);
+ NewNode->Info = AllocateCopyPool((UINTN)Node->Info->Size, Node->Info);
+ if ( NewNode->FullName == NULL
+ || NewNode->FileName == NULL
+ || NewNode->Info == NULL
+ ){
+ SHELL_FREE_NON_NULL(NewNode->FullName);
+ SHELL_FREE_NON_NULL(NewNode->FileName);
+ SHELL_FREE_NON_NULL(NewNode->Info);
+ SHELL_FREE_NON_NULL(NewNode);
+ return(NULL);
+ }
+ NewNode->Status = Node->Status;
+ NewNode->Handle = Node->Handle;
+ if (!Save) {
+ Node->Handle = NULL;
+ }
+
+ return((EFI_SHELL_FILE_INFO*)NewNode);
+}
+
+/**
+ Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation
+ failed it will return NULL.
+
+ @param[in] BasePath the Path to prepend onto filename for FullPath
+ @param[in] Status Status member initial value.
+ @param[in] FileName FileName member initial value.
+ @param[in] Handle Handle member initial value.
+ @param[in] Info Info struct to copy.
+
+ @retval NULL An error ocurred.
+ @return a pointer to the newly allocated structure.
+**/
+EFI_SHELL_FILE_INFO *
+EFIAPI
+CreateAndPopulateShellFileInfo(
+ IN CONST CHAR16 *BasePath,
+ IN CONST EFI_STATUS Status,
+ IN CONST CHAR16 *FileName,
+ IN CONST SHELL_FILE_HANDLE Handle,
+ IN CONST EFI_FILE_INFO *Info
+ )
+{
+ EFI_SHELL_FILE_INFO *ShellFileListItem;
+ CHAR16 *TempString;
+ UINTN Size;
+
+ TempString = NULL;
+ Size = 0;
+
+ ShellFileListItem = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
+ if (ShellFileListItem == NULL) {
+ return (NULL);
+ }
+ if (Info != NULL && Info->Size != 0) {
+ ShellFileListItem->Info = AllocateZeroPool((UINTN)Info->Size);
+ if (ShellFileListItem->Info == NULL) {
+ FreePool(ShellFileListItem);
+ return (NULL);
+ }
+ CopyMem(ShellFileListItem->Info, Info, (UINTN)Info->Size);
+ } else {
+ ShellFileListItem->Info = NULL;
+ }
+ if (FileName != NULL) {
+ ASSERT(TempString == NULL);
+ ShellFileListItem->FileName = StrnCatGrow(&TempString, 0, FileName, 0);
+ if (ShellFileListItem->FileName == NULL) {
+ FreePool(ShellFileListItem->Info);
+ FreePool(ShellFileListItem);
+ return (NULL);
+ }
+ } else {
+ ShellFileListItem->FileName = NULL;
+ }
+ Size = 0;
+ TempString = NULL;
+ if (BasePath != NULL) {
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
+ if (TempString == NULL) {
+ FreePool((VOID*)ShellFileListItem->FileName);
+ SHELL_FREE_NON_NULL(ShellFileListItem->Info);
+ FreePool(ShellFileListItem);
+ return (NULL);
+ }
+ }
+ if (ShellFileListItem->FileName != NULL) {
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ TempString = StrnCatGrow(&TempString, &Size, ShellFileListItem->FileName, 0);
+ if (TempString == NULL) {
+ FreePool((VOID*)ShellFileListItem->FileName);
+ FreePool(ShellFileListItem->Info);
+ FreePool(ShellFileListItem);
+ return (NULL);
+ }
+ }
+
+ TempString = PathCleanUpDirectories(TempString);
+
+ ShellFileListItem->FullName = TempString;
+ ShellFileListItem->Status = Status;
+ ShellFileListItem->Handle = Handle;
+
+ return (ShellFileListItem);
+}
+
+/**
+ Find all files in a specified directory.
+
+ @param FileDirHandle Handle of the directory to search.
+ @param FileList On return, points to the list of files in the directory
+ or NULL if there are no files in the directory.
+
+ @retval EFI_SUCCESS File information was returned successfully.
+ @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_NO_MEDIA The device media is not present.
+ @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
+ @return An error from FileHandleGetFileName().
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFindFilesInDir(
+ IN SHELL_FILE_HANDLE FileDirHandle,
+ OUT EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_SHELL_FILE_INFO *ShellFileList;
+ EFI_SHELL_FILE_INFO *ShellFileListItem;
+ EFI_FILE_INFO *FileInfo;
+ EFI_STATUS Status;
+ BOOLEAN NoFile;
+ CHAR16 *TempString;
+ CHAR16 *BasePath;
+ UINTN Size;
+ CHAR16 *TempSpot;
+
+ BasePath = NULL;
+ Status = FileHandleGetFileName(FileDirHandle, &BasePath);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ if (ShellFileHandleGetPath(FileDirHandle) != NULL) {
+ TempString = NULL;
+ Size = 0;
+ TempString = StrnCatGrow(&TempString, &Size, ShellFileHandleGetPath(FileDirHandle), 0);
+ if (TempString == NULL) {
+ SHELL_FREE_NON_NULL(BasePath);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ TempSpot = StrStr(TempString, L";");
+
+ if (TempSpot != NULL) {
+ *TempSpot = CHAR_NULL;
+ }
+
+ TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
+ if (TempString == NULL) {
+ SHELL_FREE_NON_NULL(BasePath);
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ SHELL_FREE_NON_NULL(BasePath);
+ BasePath = TempString;
+ }
+
+ NoFile = FALSE;
+ ShellFileList = NULL;
+ ShellFileListItem = NULL;
+ FileInfo = NULL;
+ Status = EFI_SUCCESS;
+
+
+ for ( Status = FileHandleFindFirstFile(FileDirHandle, &FileInfo)
+ ; !EFI_ERROR(Status) && !NoFile
+ ; Status = FileHandleFindNextFile(FileDirHandle, FileInfo, &NoFile)
+ ){
+ if (ShellFileList == NULL) {
+ ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
+ if (ShellFileList == NULL) {
+ SHELL_FREE_NON_NULL (BasePath);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ InitializeListHead(&ShellFileList->Link);
+ }
+ //
+ // allocate a new EFI_SHELL_FILE_INFO and populate it...
+ //
+ ShellFileListItem = CreateAndPopulateShellFileInfo(
+ BasePath,
+ EFI_SUCCESS, // success since we didnt fail to open it...
+ FileInfo->FileName,
+ NULL, // no handle since not open
+ FileInfo);
+ if (ShellFileListItem == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ //
+ // Free resources outside the loop.
+ //
+ break;
+ }
+ InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);
+ }
+ if (EFI_ERROR(Status)) {
+ EfiShellFreeFileList(&ShellFileList);
+ *FileList = NULL;
+ } else {
+ *FileList = ShellFileList;
+ }
+ SHELL_FREE_NON_NULL(BasePath);
+ return(Status);
+}
+
+/**
+ Get the GUID value from a human readable name.
+
+ If GuidName is a known GUID name, then update Guid to have the correct value for
+ that GUID.
+
+ This function is only available when the major and minor versions in the
+ EfiShellProtocol are greater than or equal to 2 and 1, respectively.
+
+ @param[in] GuidName A pointer to the localized name for the GUID being queried.
+ @param[out] Guid A pointer to the GUID structure to be filled in.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_INVALID_PARAMETER Guid was NULL.
+ @retval EFI_INVALID_PARAMETER GuidName was NULL.
+ @retval EFI_NOT_FOUND GuidName is not a known GUID Name.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetGuidFromName(
+ IN CONST CHAR16 *GuidName,
+ OUT EFI_GUID *Guid
+ )
+{
+ EFI_GUID *NewGuid;
+ EFI_STATUS Status;
+
+ if (Guid == NULL || GuidName == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Status = GetGuidFromStringName(GuidName, NULL, &NewGuid);
+
+ if (!EFI_ERROR(Status)) {
+ CopyGuid(NewGuid, Guid);
+ }
+
+ return (Status);
+}
+
+/**
+ Get the human readable name for a GUID from the value.
+
+ If Guid is assigned a name, then update *GuidName to point to the name. The callee
+ should not modify the value.
+
+ This function is only available when the major and minor versions in the
+ EfiShellProtocol are greater than or equal to 2 and 1, respectively.
+
+ @param[in] Guid A pointer to the GUID being queried.
+ @param[out] GuidName A pointer to a pointer the localized to name for the GUID being requested
+
+ @retval EFI_SUCCESS The operation was successful.
+ @retval EFI_INVALID_PARAMETER Guid was NULL.
+ @retval EFI_INVALID_PARAMETER GuidName was NULL.
+ @retval EFI_NOT_FOUND Guid is not assigned a name.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetGuidName(
+ IN CONST EFI_GUID *Guid,
+ OUT CONST CHAR16 **GuidName
+ )
+{
+ CHAR16 *Name;
+
+ if (Guid == NULL || GuidName == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ Name = GetStringNameFromGuid(Guid, NULL);
+ if (Name == NULL || StrLen(Name) == 0) {
+ SHELL_FREE_NON_NULL(Name);
+ return (EFI_NOT_FOUND);
+ }
+
+ *GuidName = AddBufferToFreeList(Name);
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ Updates a file name to be preceeded by the mapped drive name
+
+ @param[in] BasePath the Mapped drive name to prepend
+ @param[in, out] Path pointer to pointer to the file name to update.
+
+ @retval EFI_SUCCESS
+ @retval EFI_OUT_OF_RESOURCES
+**/
+EFI_STATUS
+EFIAPI
+UpdateFileName(
+ IN CONST CHAR16 *BasePath,
+ IN OUT CHAR16 **Path
+ )
+{
+ CHAR16 *Path2;
+ UINTN Path2Size;
+
+ Path2Size = 0;
+ Path2 = NULL;
+
+ ASSERT(Path != NULL);
+ ASSERT(*Path != NULL);
+ ASSERT(BasePath != NULL);
+
+ //
+ // convert a local path to an absolute path
+ //
+ if (StrStr(*Path, L":") == NULL) {
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, BasePath, 0);
+ if (Path2 == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0);
+ if (Path2 == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ }
+
+ FreePool(*Path);
+ (*Path) = Path2;
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ If FileHandle is a directory then the function reads from FileHandle and reads in
+ each of the FileInfo structures. If one of them matches the Pattern's first
+ "level" then it opens that handle and calls itself on that handle.
+
+ If FileHandle is a file and matches all of the remaining Pattern (which would be
+ on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
+
+ Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call
+ FreeFileList with FileList.
+
+ @param[in] FilePattern The FilePattern to check against.
+ @param[in] UnicodeCollation The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure
+ @param[in] FileHandle The FileHandle to start with
+ @param[in, out] FileList pointer to pointer to list of found files.
+ @param[in] ParentNode The node for the parent. Same file as identified by HANDLE.
+ @param[in] MapName The file system name this file is on.
+
+ @retval EFI_SUCCESS all files were found and the FileList contains a list.
+ @retval EFI_NOT_FOUND no files were found
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed
+**/
+EFI_STATUS
+EFIAPI
+ShellSearchHandle(
+ IN CONST CHAR16 *FilePattern,
+ IN EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
+ IN SHELL_FILE_HANDLE FileHandle,
+ IN OUT EFI_SHELL_FILE_INFO **FileList,
+ IN CONST EFI_SHELL_FILE_INFO *ParentNode OPTIONAL,
+ IN CONST CHAR16 *MapName
+ )
+{
+ EFI_STATUS Status;
+ CONST CHAR16 *NextFilePatternStart;
+ CHAR16 *CurrentFilePattern;
+ EFI_SHELL_FILE_INFO *ShellInfo;
+ EFI_SHELL_FILE_INFO *ShellInfoNode;
+ EFI_SHELL_FILE_INFO *NewShellNode;
+ EFI_FILE_INFO *FileInfo;
+ BOOLEAN Directory;
+ CHAR16 *NewFullName;
+ UINTN Size;
+
+ if ( FilePattern == NULL
+ || UnicodeCollation == NULL
+ || FileList == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ ShellInfo = NULL;
+ CurrentFilePattern = NULL;
+
+ if (*FilePattern == L'\\') {
+ FilePattern++;
+ }
+
+ for( NextFilePatternStart = FilePattern
+ ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
+ ; NextFilePatternStart++);
+
+ CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));
+ if (CurrentFilePattern == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ StrnCpyS(CurrentFilePattern, NextFilePatternStart-FilePattern+1, FilePattern, NextFilePatternStart-FilePattern);
+
+ if (CurrentFilePattern[0] == CHAR_NULL
+ &&NextFilePatternStart[0] == CHAR_NULL
+ ){
+ //
+ // we want the parent or root node (if no parent)
+ //
+ if (ParentNode == NULL) {
+ //
+ // We want the root node. create the node.
+ //
+ FileInfo = FileHandleGetInfo(FileHandle);
+ NewShellNode = CreateAndPopulateShellFileInfo(
+ MapName,
+ EFI_SUCCESS,
+ L"\\",
+ FileHandle,
+ FileInfo
+ );
+ SHELL_FREE_NON_NULL(FileInfo);
+ } else {
+ //
+ // Add the current parameter FileHandle to the list, then end...
+ //
+ NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);
+ }
+ if (NewShellNode == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ NewShellNode->Handle = NULL;
+ if (*FileList == NULL) {
+ *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
+ InitializeListHead(&((*FileList)->Link));
+ }
+
+ //
+ // Add to the returning to use list
+ //
+ InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
+
+ Status = EFI_SUCCESS;
+ }
+ } else {
+ Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo);
+
+ if (!EFI_ERROR(Status)){
+ if (StrStr(NextFilePatternStart, L"\\") != NULL){
+ Directory = TRUE;
+ } else {
+ Directory = FALSE;
+ }
+ for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link)
+ ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)
+ ; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link)
+ ){
+ if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){
+ if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) {
+ Size = StrSize(ShellInfoNode->FullName);
+ Size += StrSize(MapName) + sizeof(CHAR16);
+ NewFullName = AllocateZeroPool(Size);
+ if (NewFullName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ StrCpyS(NewFullName, Size/sizeof(CHAR16), MapName);
+ StrCatS(NewFullName, Size/sizeof(CHAR16), ShellInfoNode->FullName+1);
+ FreePool((VOID*)ShellInfoNode->FullName);
+ ShellInfoNode->FullName = NewFullName;
+ }
+ }
+ if (Directory && !EFI_ERROR(Status) && ShellInfoNode->FullName != NULL && ShellInfoNode->FileName != NULL){
+ //
+ // should be a directory
+ //
+
+ //
+ // don't open the . and .. directories
+ //
+ if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)
+ && (StrCmp(ShellInfoNode->FileName, L"..") != 0)
+ ){
+ //
+ //
+ //
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ //
+ // Open the directory since we need that handle in the next recursion.
+ //
+ ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);
+
+ //
+ // recurse with the next part of the pattern
+ //
+ Status = ShellSearchHandle(NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);
+ EfiShellClose(ShellInfoNode->Handle);
+ ShellInfoNode->Handle = NULL;
+ }
+ } else if (!EFI_ERROR(Status)) {
+ //
+ // should be a file
+ //
+
+ //
+ // copy the information we need into a new Node
+ //
+ NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);
+ if (NewShellNode == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ if (*FileList == NULL) {
+ *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
+ InitializeListHead(&((*FileList)->Link));
+ }
+
+ //
+ // Add to the returning to use list
+ //
+ InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ EfiShellFreeFileList(&ShellInfo);
+ } else {
+ Status = EfiShellFreeFileList(&ShellInfo);
+ }
+ }
+ }
+
+ FreePool(CurrentFilePattern);
+ return (Status);
+}
+
+/**
+ Find files that match a specified pattern.
+
+ This function searches for all files and directories that match the specified
+ FilePattern. The FilePattern can contain wild-card characters. The resulting file
+ information is placed in the file list FileList.
+
+ Wildcards are processed
+ according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
+
+ The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
+ field is set to NULL.
+
+ if *FileList is not NULL then it must be a pre-existing and properly initialized list.
+
+ @param FilePattern Points to a NULL-terminated shell file path, including wildcards.
+ @param FileList On return, points to the start of a file list containing the names
+ of all matching files or else points to NULL if no matching files
+ were found. only on a EFI_SUCCESS return will; this be non-NULL.
+
+ @retval EFI_SUCCESS Files found. FileList is a valid list.
+ @retval EFI_NOT_FOUND No files found.
+ @retval EFI_NO_MEDIA The device has no media
+ @retval EFI_DEVICE_ERROR The device reported an error
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFindFiles(
+ IN CONST CHAR16 *FilePattern,
+ OUT EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *PatternCopy;
+ CHAR16 *PatternCurrentLocation;
+ EFI_DEVICE_PATH_PROTOCOL *RootDevicePath;
+ SHELL_FILE_HANDLE RootFileHandle;
+ CHAR16 *MapName;
+ UINTN Count;
+
+ if ( FilePattern == NULL
+ || FileList == NULL
+ || StrStr(FilePattern, L":") == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ Status = EFI_SUCCESS;
+ RootDevicePath = NULL;
+ RootFileHandle = NULL;
+ MapName = NULL;
+ PatternCopy = AllocateCopyPool(StrSize(FilePattern), FilePattern);
+ if (PatternCopy == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+
+ PatternCopy = PathCleanUpDirectories(PatternCopy);
+
+ Count = StrStr(PatternCopy, L":") - PatternCopy;
+ Count += 2;
+
+ ASSERT(MapName == NULL);
+ MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);
+ if (MapName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ RootDevicePath = EfiShellGetDevicePathFromFilePath(PatternCopy);
+ if (RootDevicePath == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ Status = EfiShellOpenRoot(RootDevicePath, &RootFileHandle);
+ if (!EFI_ERROR(Status)) {
+ for ( PatternCurrentLocation = PatternCopy
+ ; *PatternCurrentLocation != ':'
+ ; PatternCurrentLocation++);
+ PatternCurrentLocation++;
+ Status = ShellSearchHandle(PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);
+ EfiShellClose(RootFileHandle);
+ }
+ FreePool(RootDevicePath);
+ }
+ }
+
+ SHELL_FREE_NON_NULL(PatternCopy);
+ SHELL_FREE_NON_NULL(MapName);
+
+ return(Status);
+}
+
+/**
+ Opens the files that match the path specified.
+
+ This function opens all of the files specified by Path. Wildcards are processed
+ according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
+ matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
+
+ @param Path A pointer to the path string.
+ @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or
+ EFI_FILE_MODE_WRITE.
+ @param FileList Points to the start of a list of files opened.
+
+ @retval EFI_SUCCESS Create the file list successfully.
+ @return Others Can't create the file list.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenFileList(
+ IN CHAR16 *Path,
+ IN UINT64 OpenMode,
+ IN OUT EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_STATUS Status;
+ EFI_SHELL_FILE_INFO *ShellFileListItem;
+ CHAR16 *Path2;
+ UINTN Path2Size;
+ CONST CHAR16 *CurDir;
+ BOOLEAN Found;
+
+ PathCleanUpDirectories(Path);
+
+ Path2Size = 0;
+ Path2 = NULL;
+
+ if (FileList == NULL || *FileList == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (*Path == L'.' && *(Path+1) == L'\\') {
+ Path+=2;
+ }
+
+ //
+ // convert a local path to an absolute path
+ //
+ if (StrStr(Path, L":") == NULL) {
+ CurDir = EfiShellGetCurDir(NULL);
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, CurDir, 0);
+ StrnCatGrow(&Path2, &Path2Size, L"\\", 0);
+ if (*Path == L'\\') {
+ Path++;
+ while (PathRemoveLastItem(Path2)) ;
+ }
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, Path, 0);
+ } else {
+ ASSERT(Path2 == NULL);
+ StrnCatGrow(&Path2, NULL, Path, 0);
+ }
+
+ PathCleanUpDirectories (Path2);
+
+ //
+ // do the search
+ //
+ Status = EfiShellFindFiles(Path2, FileList);
+
+ FreePool(Path2);
+
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ Found = FALSE;
+ //
+ // We had no errors so open all the files (that are not already opened...)
+ //
+ for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
+ ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
+ ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
+ ){
+ if (ShellFileListItem->Status == 0 && ShellFileListItem->Handle == NULL) {
+ ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);
+ Found = TRUE;
+ }
+ }
+
+ if (!Found) {
+ return (EFI_NOT_FOUND);
+ }
+ return(EFI_SUCCESS);
+}
+
+/**
+ Gets the environment variable and Attributes, or list of environment variables. Can be
+ used instead of GetEnv().
+
+ This function returns the current value of the specified environment variable and
+ the Attributes. If no variable name was specified, then all of the known
+ variables will be returned.
+
+ @param[in] Name A pointer to the environment variable name. If Name is NULL,
+ then the function will return all of the defined shell
+ environment variables. In the case where multiple environment
+ variables are being returned, each variable will be terminated
+ by a NULL, and the list will be terminated by a double NULL.
+ @param[out] Attributes If not NULL, a pointer to the returned attributes bitmask for
+ the environment variable. In the case where Name is NULL, and
+ multiple environment variables are being returned, Attributes
+ is undefined.
+
+ @retval NULL The environment variable doesn't exist.
+ @return A non-NULL value points to the variable's value. The returned
+ pointer does not need to be freed by the caller.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetEnvEx(
+ IN CONST CHAR16 *Name,
+ OUT UINT32 *Attributes OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ VOID *Buffer;
+ UINTN Size;
+ ENV_VAR_LIST *Node;
+ CHAR16 *CurrentWriteLocation;
+
+ Size = 0;
+ Buffer = NULL;
+
+ if (Name == NULL) {
+
+ //
+ // Build the semi-colon delimited list. (2 passes)
+ //
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
+ ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
+ ){
+ ASSERT(Node->Key != NULL);
+ Size += StrSize(Node->Key);
+ }
+
+ Size += 2*sizeof(CHAR16);
+
+ Buffer = AllocateZeroPool(Size);
+ if (Buffer == NULL) {
+ return (NULL);
+ }
+ CurrentWriteLocation = (CHAR16*)Buffer;
+
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
+ ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
+ ){
+ ASSERT(Node->Key != NULL);
+ StrCpyS( CurrentWriteLocation,
+ (Size)/sizeof(CHAR16) - (CurrentWriteLocation - ((CHAR16*)Buffer)),
+ Node->Key
+ );
+ CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
+ }
+
+ } else {
+ //
+ // We are doing a specific environment variable
+ //
+ Status = ShellFindEnvVarInList(Name, (CHAR16**)&Buffer, &Size, Attributes);
+
+ if (EFI_ERROR(Status)){
+ //
+ // get the size we need for this EnvVariable
+ //
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate the space and recall the get function
+ //
+ Buffer = AllocateZeroPool(Size);
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
+ }
+ //
+ // we didnt get it (might not exist)
+ // free the memory if we allocated any and return NULL
+ //
+ if (EFI_ERROR(Status)) {
+ if (Buffer != NULL) {
+ FreePool(Buffer);
+ }
+ return (NULL);
+ } else {
+ //
+ // If we did not find the environment variable in the gShellEnvVarList
+ // but get it from UEFI variable storage successfully then we need update
+ // the gShellEnvVarList.
+ //
+ ShellFreeEnvVarList ();
+ Status = ShellInitEnvVarList ();
+ ASSERT (Status == EFI_SUCCESS);
+ }
+ }
+ }
+
+ //
+ // return the buffer
+ //
+ return (AddBufferToFreeList(Buffer));
+}
+
+/**
+ Gets either a single or list of environment variables.
+
+ If name is not NULL then this function returns the current value of the specified
+ environment variable.
+
+ If Name is NULL, then a list of all environment variable names is returned. Each is a
+ NULL terminated string with a double NULL terminating the list.
+
+ @param Name A pointer to the environment variable name. If
+ Name is NULL, then the function will return all
+ of the defined shell environment variables. In
+ the case where multiple environment variables are
+ being returned, each variable will be terminated by
+ a NULL, and the list will be terminated by a double
+ NULL.
+
+ @retval !=NULL A pointer to the returned string.
+ The returned pointer does not need to be freed by the caller.
+
+ @retval NULL The environment variable doesn't exist or there are
+ no environment variables.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetEnv(
+ IN CONST CHAR16 *Name
+ )
+{
+ return (EfiShellGetEnvEx(Name, NULL));
+}
+
+/**
+ Internal variable setting function. Allows for setting of the read only variables.
+
+ @param Name Points to the NULL-terminated environment variable name.
+ @param Value Points to the NULL-terminated environment variable value. If the value is an
+ empty string then the environment variable is deleted.
+ @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
+
+ @retval EFI_SUCCESS The environment variable was successfully updated.
+**/
+EFI_STATUS
+EFIAPI
+InternalEfiShellSetEnv(
+ IN CONST CHAR16 *Name,
+ IN CONST CHAR16 *Value,
+ IN BOOLEAN Volatile
+ )
+{
+ EFI_STATUS Status;
+
+ if (Value == NULL || StrLen(Value) == 0) {
+ Status = SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
+ if (!EFI_ERROR(Status)) {
+ ShellRemvoeEnvVarFromList(Name);
+ }
+ } else {
+ SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
+ Status = ShellAddEnvVarToList(
+ Name, Value, StrSize(Value),
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE)
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = Volatile
+ ? SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value)
+ : SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value);
+ if (EFI_ERROR (Status)) {
+ ShellRemvoeEnvVarFromList(Name);
+ }
+ }
+ }
+ return Status;
+}
+
+/**
+ Sets the environment variable.
+
+ This function changes the current value of the specified environment variable. If the
+ environment variable exists and the Value is an empty string, then the environment
+ variable is deleted. If the environment variable exists and the Value is not an empty
+ string, then the value of the environment variable is changed. If the environment
+ variable does not exist and the Value is an empty string, there is no action. If the
+ environment variable does not exist and the Value is a non-empty string, then the
+ environment variable is created and assigned the specified value.
+
+ For a description of volatile and non-volatile environment variables, see UEFI Shell
+ 2.0 specification section 3.6.1.
+
+ @param Name Points to the NULL-terminated environment variable name.
+ @param Value Points to the NULL-terminated environment variable value. If the value is an
+ empty string then the environment variable is deleted.
+ @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
+
+ @retval EFI_SUCCESS The environment variable was successfully updated.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetEnv(
+ IN CONST CHAR16 *Name,
+ IN CONST CHAR16 *Value,
+ IN BOOLEAN Volatile
+ )
+{
+ if (Name == NULL || *Name == CHAR_NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ //
+ // Make sure we dont 'set' a predefined read only variable
+ //
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"cwd") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"Lasterror") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"profiles") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"uefishellsupport") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"uefishellversion") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"uefiversion") == 0
+ ||(!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest &&
+ gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ (CHAR16*)mNoNestingEnvVarName) == 0)
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ return (InternalEfiShellSetEnv(Name, Value, Volatile));
+}
+
+/**
+ Returns the current directory on the specified device.
+
+ If FileSystemMapping is NULL, it returns the current working directory. If the
+ FileSystemMapping is not NULL, it returns the current directory associated with the
+ FileSystemMapping. In both cases, the returned name includes the file system
+ mapping (i.e. fs0:\current-dir).
+
+ Note that the current directory string should exclude the tailing backslash character.
+
+ @param FileSystemMapping A pointer to the file system mapping. If NULL,
+ then the current working directory is returned.
+
+ @retval !=NULL The current directory.
+ @retval NULL Current directory does not exist.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetCurDir(
+ IN CONST CHAR16 *FileSystemMapping OPTIONAL
+ )
+{
+ CHAR16 *PathToReturn;
+ UINTN Size;
+ SHELL_MAP_LIST *MapListItem;
+ if (!IsListEmpty(&gShellMapList.Link)) {
+ //
+ // if parameter is NULL, use current
+ //
+ if (FileSystemMapping == NULL) {
+ return (EfiShellGetEnv(L"cwd"));
+ } else {
+ Size = 0;
+ PathToReturn = NULL;
+ MapListItem = ShellCommandFindMapItem(FileSystemMapping);
+ if (MapListItem != NULL) {
+ ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
+ PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);
+ PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
+ }
+ }
+ return (AddBufferToFreeList(PathToReturn));
+ } else {
+ return (NULL);
+ }
+}
+
+/**
+ Changes the current directory on the specified device.
+
+ If the FileSystem is NULL, and the directory Dir does not contain a file system's
+ mapped name, this function changes the current working directory.
+
+ If the FileSystem is NULL and the directory Dir contains a mapped name, then the
+ current file system and the current directory on that file system are changed.
+
+ If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
+ system.
+
+ If FileSystem is not NULL and Dir is not NULL, then this function changes the current
+ directory on the specified file system.
+
+ If the current working directory or the current working file system is changed then the
+ %cwd% environment variable will be updated
+
+ Note that the current directory string should exclude the tailing backslash character.
+
+ @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working
+ directory is changed.
+ @param Dir Points to the NULL-terminated directory on the device specified by FileSystem.
+
+ @retval EFI_SUCCESS The operation was sucessful
+ @retval EFI_NOT_FOUND The file system could not be found
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetCurDir(
+ IN CONST CHAR16 *FileSystem OPTIONAL,
+ IN CONST CHAR16 *Dir
+ )
+{
+ CHAR16 *MapName;
+ SHELL_MAP_LIST *MapListItem;
+ UINTN Size;
+ EFI_STATUS Status;
+ CHAR16 *TempString;
+ CHAR16 *DirectoryName;
+ UINTN TempLen;
+
+ Size = 0;
+ MapName = NULL;
+ MapListItem = NULL;
+ TempString = NULL;
+ DirectoryName = NULL;
+
+ if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (IsListEmpty(&gShellMapList.Link)){
+ return (EFI_NOT_FOUND);
+ }
+
+ DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);
+ ASSERT(DirectoryName != NULL);
+
+ PathCleanUpDirectories(DirectoryName);
+
+ if (FileSystem == NULL) {
+ //
+ // determine the file system mapping to use
+ //
+ if (StrStr(DirectoryName, L":") != NULL) {
+ ASSERT(MapName == NULL);
+ MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));
+ }
+ //
+ // find the file system mapping's entry in the list
+ // or use current
+ //
+ if (MapName != NULL) {
+ MapListItem = ShellCommandFindMapItem(MapName);
+
+ //
+ // make that the current file system mapping
+ //
+ if (MapListItem != NULL) {
+ gShellCurDir = MapListItem;
+ }
+ } else {
+ MapListItem = gShellCurDir;
+ }
+
+ if (MapListItem == NULL) {
+ FreePool (DirectoryName);
+ SHELL_FREE_NON_NULL(MapName);
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // now update the MapListItem's current directory
+ //
+ if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {
+ FreePool(MapListItem->CurrentDirectoryPath);
+ MapListItem->CurrentDirectoryPath = NULL;
+ }
+ if (MapName != NULL) {
+ TempLen = StrLen(MapName);
+ if (TempLen != StrLen(DirectoryName)) {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);
+ }
+ FreePool (MapName);
+ } else {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
+ }
+ if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ if (MapListItem->CurrentDirectoryPath != NULL) {
+ MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
+ }
+ }
+ } else {
+ //
+ // cant have a mapping in the directory...
+ //
+ if (StrStr(DirectoryName, L":") != NULL) {
+ FreePool (DirectoryName);
+ return (EFI_INVALID_PARAMETER);
+ }
+ //
+ // FileSystem != NULL
+ //
+ MapListItem = ShellCommandFindMapItem(FileSystem);
+ if (MapListItem == NULL) {
+ FreePool (DirectoryName);
+ return (EFI_INVALID_PARAMETER);
+ }
+// gShellCurDir = MapListItem;
+ if (DirectoryName != NULL) {
+ //
+ // change current dir on that file system
+ //
+
+ if (MapListItem->CurrentDirectoryPath != NULL) {
+ FreePool(MapListItem->CurrentDirectoryPath);
+ DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);
+ }
+// ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+// MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
+ if (MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
+ }
+ }
+ }
+ FreePool (DirectoryName);
+ //
+ // if updated the current directory then update the environment variable
+ //
+ if (MapListItem == gShellCurDir) {
+ Size = 0;
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
+ Status = InternalEfiShellSetEnv(L"cwd", TempString, TRUE);
+ FreePool(TempString);
+ return (Status);
+ }
+ return(EFI_SUCCESS);
+}
+
+/**
+ Return help information about a specific command.
+
+ This function returns the help information for the specified command. The help text
+ can be internal to the shell or can be from a UEFI Shell manual page.
+
+ If Sections is specified, then each section name listed will be compared in a casesensitive
+ manner, to the section names described in Appendix B. If the section exists,
+ it will be appended to the returned help text. If the section does not exist, no
+ information will be returned. If Sections is NULL, then all help text information
+ available will be returned.
+
+ @param Command Points to the NULL-terminated UEFI Shell command name.
+ @param Sections Points to the NULL-terminated comma-delimited
+ section names to return. If NULL, then all
+ sections will be returned.
+ @param HelpText On return, points to a callee-allocated buffer
+ containing all specified help text.
+
+ @retval EFI_SUCCESS The help text was returned.
+ @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
+ returned help text.
+ @retval EFI_INVALID_PARAMETER HelpText is NULL
+ @retval EFI_NOT_FOUND There is no help text available for Command.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetHelpText(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Sections OPTIONAL,
+ OUT CHAR16 **HelpText
+ )
+{
+ CONST CHAR16 *ManFileName;
+ CHAR16 *FixCommand;
+ EFI_STATUS Status;
+
+ ASSERT(HelpText != NULL);
+ FixCommand = NULL;
+
+ ManFileName = ShellCommandGetManFileNameHandler(Command);
+
+ if (ManFileName != NULL) {
+ return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));
+ } else {
+ if ((StrLen(Command)> 4)
+ && (Command[StrLen(Command)-1] == L'i' || Command[StrLen(Command)-1] == L'I')
+ && (Command[StrLen(Command)-2] == L'f' || Command[StrLen(Command)-2] == L'F')
+ && (Command[StrLen(Command)-3] == L'e' || Command[StrLen(Command)-3] == L'E')
+ && (Command[StrLen(Command)-4] == L'.')
+ ) {
+ FixCommand = AllocateZeroPool(StrSize(Command) - 4 * sizeof (CHAR16));
+ if (FixCommand == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ StrnCpyS( FixCommand,
+ (StrSize(Command) - 4 * sizeof (CHAR16))/sizeof(CHAR16),
+ Command,
+ StrLen(Command)-4
+ );
+ Status = ProcessManFile(FixCommand, FixCommand, Sections, NULL, HelpText);
+ FreePool(FixCommand);
+ return Status;
+ } else {
+ return (ProcessManFile(Command, Command, Sections, NULL, HelpText));
+ }
+ }
+}
+
+/**
+ Gets the enable status of the page break output mode.
+
+ User can use this function to determine current page break mode.
+
+ @retval TRUE The page break output mode is enabled.
+ @retval FALSE The page break output mode is disabled.
+**/
+BOOLEAN
+EFIAPI
+EfiShellGetPageBreak(
+ VOID
+ )
+{
+ return(ShellInfoObject.PageBreakEnabled);
+}
+
+/**
+ Judges whether the active shell is the root shell.
+
+ This function makes the user to know that whether the active Shell is the root shell.
+
+ @retval TRUE The active Shell is the root Shell.
+ @retval FALSE The active Shell is NOT the root Shell.
+**/
+BOOLEAN
+EFIAPI
+EfiShellIsRootShell(
+ VOID
+ )
+{
+ return(ShellInfoObject.RootShellInstance);
+}
+
+/**
+ function to return a semi-colon delimeted list of all alias' in the current shell
+
+ up to caller to free the memory.
+
+ @retval NULL No alias' were found
+ @retval NULL An error ocurred getting alias'
+ @return !NULL a list of all alias'
+**/
+CHAR16 *
+EFIAPI
+InternalEfiShellGetListAlias(
+ )
+{
+
+ EFI_STATUS Status;
+ EFI_GUID Guid;
+ CHAR16 *VariableName;
+ UINTN NameSize;
+ UINTN NameBufferSize;
+ CHAR16 *RetVal;
+ UINTN RetSize;
+
+ NameBufferSize = INIT_NAME_BUFFER_SIZE;
+ VariableName = AllocateZeroPool(NameBufferSize);
+ RetSize = 0;
+ RetVal = NULL;
+
+ if (VariableName == NULL) {
+ return (NULL);
+ }
+
+ VariableName[0] = CHAR_NULL;
+
+ while (TRUE) {
+ NameSize = NameBufferSize;
+ Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
+ if (Status == EFI_NOT_FOUND){
+ break;
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
+ SHELL_FREE_NON_NULL(VariableName);
+ VariableName = AllocateZeroPool(NameBufferSize);
+ if (VariableName == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ SHELL_FREE_NON_NULL(RetVal);
+ RetVal = NULL;
+ break;
+ }
+
+ NameSize = NameBufferSize;
+ Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
+ }
+
+ if (EFI_ERROR (Status)) {
+ SHELL_FREE_NON_NULL(RetVal);
+ RetVal = NULL;
+ break;
+ }
+
+ if (CompareGuid(&Guid, &gShellAliasGuid)){
+ ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
+ RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);
+ RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);
+ } // compare guid
+ } // while
+ SHELL_FREE_NON_NULL(VariableName);
+
+ return (RetVal);
+}
+
+/**
+ Convert a null-terminated unicode string, in-place, to all lowercase.
+ Then return it.
+
+ @param Str The null-terminated string to be converted to all lowercase.
+
+ @return The null-terminated string converted into all lowercase.
+**/
+CHAR16 *
+ToLower (
+ CHAR16 *Str
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Str[Index] != L'\0'; Index++) {
+ if (Str[Index] >= L'A' && Str[Index] <= L'Z') {
+ Str[Index] -= (CHAR16)(L'A' - L'a');
+ }
+ }
+ return Str;
+}
+
+/**
+ This function returns the command associated with a alias or a list of all
+ alias'.
+
+ @param[in] Alias Points to the NULL-terminated shell alias.
+ If this parameter is NULL, then all
+ aliases will be returned in ReturnedData.
+ @param[out] Volatile upon return of a single command if TRUE indicates
+ this is stored in a volatile fashion. FALSE otherwise.
+
+ @return If Alias is not NULL, it will return a pointer to
+ the NULL-terminated command for that alias.
+ If Alias is NULL, ReturnedData points to a ';'
+ delimited list of alias (e.g.
+ ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
+ @retval NULL an error ocurred
+ @retval NULL Alias was not a valid Alias
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetAlias(
+ IN CONST CHAR16 *Alias,
+ OUT BOOLEAN *Volatile OPTIONAL
+ )
+{
+ CHAR16 *RetVal;
+ UINTN RetSize;
+ UINT32 Attribs;
+ EFI_STATUS Status;
+ CHAR16 *AliasLower;
+ CHAR16 *AliasVal;
+
+ // Convert to lowercase to make aliases case-insensitive
+ if (Alias != NULL) {
+ AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
+ if (AliasLower == NULL) {
+ return NULL;
+ }
+ ToLower (AliasLower);
+
+ if (Volatile == NULL) {
+ GetVariable2 (AliasLower, &gShellAliasGuid, (VOID **)&AliasVal, NULL);
+ FreePool(AliasLower);
+ return (AddBufferToFreeList(AliasVal));
+ }
+ RetSize = 0;
+ RetVal = NULL;
+ Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ RetVal = AllocateZeroPool(RetSize);
+ Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
+ }
+ if (EFI_ERROR(Status)) {
+ if (RetVal != NULL) {
+ FreePool(RetVal);
+ }
+ FreePool(AliasLower);
+ return (NULL);
+ }
+ if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
+ *Volatile = FALSE;
+ } else {
+ *Volatile = TRUE;
+ }
+
+ FreePool (AliasLower);
+ return (AddBufferToFreeList(RetVal));
+ }
+ return (AddBufferToFreeList(InternalEfiShellGetListAlias()));
+}
+
+/**
+ Changes a shell command alias.
+
+ This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
+
+ this function does not check for built in alias'.
+
+ @param[in] Command Points to the NULL-terminated shell command or existing alias.
+ @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
+ Command refers to an alias, that alias will be deleted.
+ @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
+ Alias being set will be stored in a non-volatile fashion.
+
+ @retval EFI_SUCCESS Alias created or deleted successfully.
+ @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
+**/
+EFI_STATUS
+EFIAPI
+InternalSetAlias(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias,
+ IN BOOLEAN Volatile
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *AliasLower;
+
+ // Convert to lowercase to make aliases case-insensitive
+ if (Alias != NULL) {
+ AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
+ if (AliasLower == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ ToLower (AliasLower);
+ } else {
+ AliasLower = NULL;
+ }
+
+ //
+ // We must be trying to remove one if Alias is NULL
+ //
+ if (Alias == NULL) {
+ //
+ // remove an alias (but passed in COMMAND parameter)
+ //
+ Status = (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL));
+ } else {
+ //
+ // Add and replace are the same
+ //
+
+ // We dont check the error return on purpose since the variable may not exist.
+ gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL);
+
+ Status = (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command));
+ }
+
+ if (Alias != NULL) {
+ FreePool (AliasLower);
+ }
+ return Status;
+}
+
+/**
+ Changes a shell command alias.
+
+ This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
+
+
+ @param[in] Command Points to the NULL-terminated shell command or existing alias.
+ @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
+ Command refers to an alias, that alias will be deleted.
+ @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If
+ FALSE and the alias already exists, then the existing alias is unchanged and
+ EFI_ACCESS_DENIED is returned.
+ @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
+ Alias being set will be stored in a non-volatile fashion.
+
+ @retval EFI_SUCCESS Alias created or deleted successfully.
+ @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
+ @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to
+ FALSE.
+ @retval EFI_INVALID_PARAMETER Command is null or the empty string.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetAlias(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias,
+ IN BOOLEAN Replace,
+ IN BOOLEAN Volatile
+ )
+{
+ if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {
+ //
+ // cant set over a built in alias
+ //
+ return (EFI_ACCESS_DENIED);
+ } else if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {
+ //
+ // Command is null or empty
+ //
+ return (EFI_INVALID_PARAMETER);
+ } else if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {
+ //
+ // Alias already exists, Replace not set
+ //
+ return (EFI_ACCESS_DENIED);
+ } else {
+ return (InternalSetAlias(Command, Alias, Volatile));
+ }
+}
+
+// Pure FILE_HANDLE operations are passed to FileHandleLib
+// these functions are indicated by the *
+EFI_SHELL_PROTOCOL mShellProtocol = {
+ EfiShellExecute,
+ EfiShellGetEnv,
+ EfiShellSetEnv,
+ EfiShellGetAlias,
+ EfiShellSetAlias,
+ EfiShellGetHelpText,
+ EfiShellGetDevicePathFromMap,
+ EfiShellGetMapFromDevicePath,
+ EfiShellGetDevicePathFromFilePath,
+ EfiShellGetFilePathFromDevicePath,
+ EfiShellSetMap,
+ EfiShellGetCurDir,
+ EfiShellSetCurDir,
+ EfiShellOpenFileList,
+ EfiShellFreeFileList,
+ EfiShellRemoveDupInFileList,
+ EfiShellBatchIsActive,
+ EfiShellIsRootShell,
+ EfiShellEnablePageBreak,
+ EfiShellDisablePageBreak,
+ EfiShellGetPageBreak,
+ EfiShellGetDeviceName,
+ (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo, //*
+ (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo, //*
+ EfiShellOpenFileByName,
+ EfiShellClose,
+ EfiShellCreateFile,
+ (EFI_SHELL_READ_FILE)FileHandleRead, //*
+ (EFI_SHELL_WRITE_FILE)FileHandleWrite, //*
+ (EFI_SHELL_DELETE_FILE)FileHandleDelete, //*
+ EfiShellDeleteFileByName,
+ (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, //*
+ (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, //*
+ (EFI_SHELL_FLUSH_FILE)FileHandleFlush, //*
+ EfiShellFindFiles,
+ EfiShellFindFilesInDir,
+ (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize, //*
+ EfiShellOpenRoot,
+ EfiShellOpenRootByHandle,
+ NULL,
+ SHELL_MAJOR_VERSION,
+ SHELL_MINOR_VERSION,
+
+ // New for UEFI Shell 2.1
+ EfiShellRegisterGuidName,
+ EfiShellGetGuidName,
+ EfiShellGetGuidFromName,
+ EfiShellGetEnvEx
+};
+
+/**
+ Function to create and install on the current handle.
+
+ Will overwrite any existing ShellProtocols in the system to be sure that
+ the current shell is in control.
+
+ This must be removed via calling CleanUpShellProtocol().
+
+ @param[in, out] NewShell The pointer to the pointer to the structure
+ to install.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return An error from LocateHandle, CreateEvent, or other core function.
+**/
+EFI_STATUS
+EFIAPI
+CreatePopulateInstallShellProtocol (
+ IN OUT EFI_SHELL_PROTOCOL **NewShell
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ EFI_HANDLE *Buffer;
+ UINTN HandleCounter;
+ SHELL_PROTOCOL_HANDLE_LIST *OldProtocolNode;
+ EFI_SHELL_PROTOCOL *OldShell;
+
+ if (NewShell == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ BufferSize = 0;
+ Buffer = NULL;
+ OldProtocolNode = NULL;
+ InitializeListHead(&ShellInfoObject.OldShellList.Link);
+
+ //
+ // Initialize EfiShellProtocol object...
+ //
+ Status = gBS->CreateEvent(0,
+ 0,
+ NULL,
+ NULL,
+ &mShellProtocol.ExecutionBreak);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ //
+ // Get the size of the buffer we need.
+ //
+ Status = gBS->LocateHandle(ByProtocol,
+ &gEfiShellProtocolGuid,
+ NULL,
+ &BufferSize,
+ Buffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate and recall with buffer of correct size
+ //
+ Buffer = AllocateZeroPool(BufferSize);
+ if (Buffer == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ Status = gBS->LocateHandle(ByProtocol,
+ &gEfiShellProtocolGuid,
+ NULL,
+ &BufferSize,
+ Buffer);
+ if (EFI_ERROR(Status)) {
+ FreePool(Buffer);
+ return (Status);
+ }
+ //
+ // now overwrite each of them, but save the info to restore when we end.
+ //
+ for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) {
+ Status = gBS->OpenProtocol(Buffer[HandleCounter],
+ &gEfiShellProtocolGuid,
+ (VOID **) &OldShell,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR(Status)) {
+ OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST));
+ if (OldProtocolNode == NULL) {
+ if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) {
+ CleanUpShellProtocol (&mShellProtocol);
+ }
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+ //
+ // reinstall over the old one...
+ //
+ OldProtocolNode->Handle = Buffer[HandleCounter];
+ OldProtocolNode->Interface = OldShell;
+ Status = gBS->ReinstallProtocolInterface(
+ OldProtocolNode->Handle,
+ &gEfiShellProtocolGuid,
+ OldProtocolNode->Interface,
+ (VOID*)(&mShellProtocol));
+ if (!EFI_ERROR(Status)) {
+ //
+ // we reinstalled sucessfully. log this so we can reverse it later.
+ //
+
+ //
+ // add to the list for subsequent...
+ //
+ InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
+ }
+ }
+ }
+ FreePool(Buffer);
+ } else if (Status == EFI_NOT_FOUND) {
+ ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link));
+ //
+ // no one else published yet. just publish it ourselves.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &gImageHandle,
+ &gEfiShellProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ (VOID*)(&mShellProtocol));
+ }
+
+ if (PcdGetBool(PcdShellSupportOldProtocols)){
+ ///@todo support ShellEnvironment2
+ ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
+ }
+
+ if (!EFI_ERROR(Status)) {
+ *NewShell = &mShellProtocol;
+ }
+ return (Status);
+}
+
+/**
+ Opposite of CreatePopulateInstallShellProtocol.
+
+ Free all memory and restore the system to the state it was in before calling
+ CreatePopulateInstallShellProtocol.
+
+ @param[in, out] NewShell The pointer to the new shell protocol structure.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+CleanUpShellProtocol (
+ IN OUT EFI_SHELL_PROTOCOL *NewShell
+ )
+{
+ SHELL_PROTOCOL_HANDLE_LIST *Node2;
+
+ //
+ // if we need to restore old protocols...
+ //
+ if (!IsListEmpty(&ShellInfoObject.OldShellList.Link)) {
+ for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *) GetFirstNode (&ShellInfoObject.OldShellList.Link)
+ ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
+ ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *) GetFirstNode (&ShellInfoObject.OldShellList.Link)
+ ) {
+ RemoveEntryList (&Node2->Link);
+ gBS->ReinstallProtocolInterface (Node2->Handle, &gEfiShellProtocolGuid, NewShell, Node2->Interface);
+ FreePool (Node2);
+ }
+ } else {
+ //
+ // no need to restore
+ //
+ gBS->UninstallProtocolInterface (gImageHandle, &gEfiShellProtocolGuid, NewShell);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Cleanup the shell environment.
+
+ @param[in, out] NewShell The pointer to the new shell protocol structure.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+CleanUpShellEnvironment (
+ IN OUT EFI_SHELL_PROTOCOL *NewShell
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
+
+ CleanUpShellProtocol (NewShell);
+
+ Status = gBS->CloseEvent(NewShell->ExecutionBreak);
+ NewShell->ExecutionBreak = NULL;
+
+ Status = gBS->OpenProtocol(
+ gST->ConsoleInHandle,
+ &gEfiSimpleTextInputExProtocolGuid,
+ (VOID**)&SimpleEx,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (!EFI_ERROR (Status)) {
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle1);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle2);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle3);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle4);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle1);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle2);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle3);
+ Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle4);
+ }
+ return (Status);
+}
+
+/**
+ Notification function for keystrokes.
+
+ @param[in] KeyData The key that was pressed.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+EFIAPI
+NotificationFunction(
+ IN EFI_KEY_DATA *KeyData
+ )
+{
+ if ( ((KeyData->Key.UnicodeChar == L'c') &&
+ (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) ||
+ (KeyData->Key.UnicodeChar == 3)
+ ){
+ if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
+ return (EFI_UNSUPPORTED);
+ }
+ return (gBS->SignalEvent(ShellInfoObject.NewEfiShellProtocol->ExecutionBreak));
+ } else if ((KeyData->Key.UnicodeChar == L's') &&
+ (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))
+ ){
+ ShellInfoObject.HaltOutput = TRUE;
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Function to start monitoring for CTRL-C using SimpleTextInputEx. This
+ feature's enabled state was not known when the shell initially launched.
+
+ @retval EFI_SUCCESS The feature is enabled.
+ @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
+**/
+EFI_STATUS
+EFIAPI
+InernalEfiShellStartMonitor(
+ VOID
+ )
+{
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
+ EFI_KEY_DATA KeyData;
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol(
+ gST->ConsoleInHandle,
+ &gEfiSimpleTextInputExProtocolGuid,
+ (VOID**)&SimpleEx,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status)) {
+ ShellPrintHiiEx(
+ -1,
+ -1,
+ NULL,
+ STRING_TOKEN (STR_SHELL_NO_IN_EX),
+ ShellInfoObject.HiiHandle);
+ return (EFI_SUCCESS);
+ }
+
+ if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ KeyData.KeyState.KeyToggleState = 0;
+ KeyData.Key.ScanCode = 0;
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
+ KeyData.Key.UnicodeChar = L'c';
+
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlCNotifyHandle1);
+
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
+ if (!EFI_ERROR(Status)) {
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlCNotifyHandle2);
+ }
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
+ KeyData.Key.UnicodeChar = 3;
+ if (!EFI_ERROR(Status)) {
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlCNotifyHandle3);
+ }
+ KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
+ if (!EFI_ERROR(Status)) {
+ Status = SimpleEx->RegisterKeyNotify(
+ SimpleEx,
+ &KeyData,
+ NotificationFunction,
+ &ShellInfoObject.CtrlCNotifyHandle4);
+ }
+ return (Status);
+}
+
diff --git a/Core/ShellPkg/Application/Shell/ShellProtocol.h b/Core/ShellPkg/Application/Shell/ShellProtocol.h
new file mode 100644
index 0000000000..4f701cb749
--- /dev/null
+++ b/Core/ShellPkg/Application/Shell/ShellProtocol.h
@@ -0,0 +1,953 @@
+/** @file
+ Member functions of EFI_SHELL_PROTOCOL and functions for creation,
+ manipulation, and initialization of EFI_SHELL_PROTOCOL.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _SHELL_PROTOCOL_HEADER_
+#define _SHELL_PROTOCOL_HEADER_
+
+#include "Shell.h"
+
+typedef struct {
+ LIST_ENTRY Link;
+ EFI_SHELL_PROTOCOL *Interface;
+ EFI_HANDLE Handle;
+} SHELL_PROTOCOL_HANDLE_LIST;
+
+// flags values...
+#define SHELL_MAP_FLAGS_CONSIST BIT1
+
+/**
+ Function to create and install on the current handle.
+
+ Will overwrite any existing ShellProtocols in the system to be sure that
+ the current shell is in control.
+
+ This must be removed via calling CleanUpShellProtocol().
+
+ @param[in, out] NewShell The pointer to the pointer to the structure
+ to install.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return An error from LocateHandle, CreateEvent, or other core function.
+**/
+EFI_STATUS
+EFIAPI
+CreatePopulateInstallShellProtocol (
+ IN OUT EFI_SHELL_PROTOCOL **NewShell
+ );
+
+/**
+ Opposite of CreatePopulateInstallShellProtocol.
+
+ Free all memory and restore the system to the state it was in before calling
+ CreatePopulateInstallShellProtocol.
+
+ @param[in, out] NewShell The pointer to the new shell protocol structure.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+CleanUpShellProtocol (
+ IN OUT EFI_SHELL_PROTOCOL *NewShell
+ );
+
+/**
+ Cleanup the shell environment.
+
+ @param[in, out] NewShell The pointer to the new shell protocol structure.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+CleanUpShellEnvironment (
+ IN OUT EFI_SHELL_PROTOCOL *NewShell
+ );
+
+/**
+ This function creates a mapping for a device path.
+
+ @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping,
+ then the mapping will be deleted.
+ @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':'
+
+ @retval EFI_SUCCESS Mapping created or deleted successfully.
+ @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the
+ boot service function LocateDevicePath().
+ @retval EFI_ACCESS_DENIED The mapping is a built-in alias.
+ @retval EFI_INVALID_PARAMETER Mapping was NULL
+ @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
+ @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
+ @retval EFI_NOT_FOUND There was no mapping found to delete
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetMap(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
+ IN CONST CHAR16 *Mapping
+ );
+
+/**
+ Gets the device path from the mapping.
+
+ This function gets the device path associated with a mapping.
+
+ @param Mapping A pointer to the mapping
+
+ @retval !=NULL Pointer to the device path that corresponds to the
+ device mapping. The returned pointer does not need
+ to be freed.
+ @retval NULL There is no device path associated with the
+ specified mapping.
+**/
+CONST EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiShellGetDevicePathFromMap(
+ IN CONST CHAR16 *Mapping
+ );
+
+/**
+ Gets the mapping that most closely matches the device path.
+
+ This function gets the mapping which corresponds to the device path *DevicePath. If
+ there is no exact match, then the mapping which most closely matches *DevicePath
+ is returned, and *DevicePath is updated to point to the remaining portion of the
+ device path. If there is an exact match, the mapping is returned and *DevicePath
+ points to the end-of-device-path node.
+
+ @param DevicePath On entry, points to a device path pointer. On
+ exit, updates the pointer to point to the
+ portion of the device path after the mapping.
+
+ @retval NULL No mapping was found.
+ @return !=NULL Pointer to NULL-terminated mapping. The buffer
+ is callee allocated and should be freed by the caller.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetMapFromDevicePath(
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Converts a device path to a file system-style path.
+
+ This function converts a device path to a file system path by replacing part, or all, of
+ the device path with the file-system mapping. If there are more than one application
+ file system mappings, the one that most closely matches Path will be used.
+
+ @param Path The pointer to the device path
+
+ @retval NULL the device path could not be found.
+ @return all The pointer of the NULL-terminated file path. The path
+ is callee-allocated and should be freed by the caller.
+**/
+CHAR16 *
+EFIAPI
+EfiShellGetFilePathFromDevicePath(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
+ );
+
+/**
+ Converts a file system style name to a device path.
+
+ This function converts a file system style name to a device path, by replacing any
+ mapping references to the associated device path.
+
+ @param Path the pointer to the path
+
+ @return all The pointer of the file path. The file path is callee
+ allocated and should be freed by the caller.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiShellGetDevicePathFromFilePath(
+ IN CONST CHAR16 *Path
+ );
+
+/**
+ Gets the name of the device specified by the device handle.
+
+ This function gets the user-readable name of the device specified by the device
+ handle. If no user-readable name could be generated, then *BestDeviceName will be
+ NULL and EFI_NOT_FOUND will be returned.
+
+ If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
+ device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
+ DeviceHandle.
+
+ If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
+ device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
+ If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
+ EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
+ EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
+
+ @param DeviceHandle The handle of the device.
+ @param Flags Determines the possible sources of component names.
+ Valid bits are:
+ EFI_DEVICE_NAME_USE_COMPONENT_NAME
+ EFI_DEVICE_NAME_USE_DEVICE_PATH
+ @param Language A pointer to the language specified for the device
+ name, in the same format as described in the UEFI
+ specification, Appendix M
+ @param BestDeviceName On return, points to the callee-allocated NULL-
+ terminated name of the device. If no device name
+ could be found, points to NULL. The name must be
+ freed by the caller...
+
+ @retval EFI_SUCCESS Get the name successfully.
+ @retval EFI_NOT_FOUND Fail to get the device name.
+ @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
+ @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
+ @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetDeviceName(
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
+ IN CHAR8 *Language,
+ OUT CHAR16 **BestDeviceName
+ );
+
+/**
+ Opens the root directory of a device on a handle
+
+ This function opens the root directory of a device and returns a file handle to it.
+
+ @param DeviceHandle The handle of the device that contains the volume.
+ @param FileHandle On exit, points to the file handle corresponding to the root directory on the
+ device.
+
+ @retval EFI_SUCCESS Root opened successfully.
+ @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
+ could not be opened.
+ @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
+ @retval EFI_DEVICE_ERROR The device had an error
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenRootByHandle(
+ IN EFI_HANDLE DeviceHandle,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ );
+
+/**
+ Opens the root directory of a device.
+
+ This function opens the root directory of a device and returns a file handle to it.
+
+ @param DevicePath Points to the device path corresponding to the device where the
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
+ @param FileHandle On exit, points to the file handle corresponding to the root directory on the
+ device.
+
+ @retval EFI_SUCCESS Root opened successfully.
+ @retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
+ could not be opened.
+ @retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted.
+ @retval EFI_DEVICE_ERROR The device had an error
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenRoot(
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ );
+
+/**
+ Returns whether any script files are currently being processed.
+
+ @retval TRUE There is at least one script file active.
+ @retval FALSE No script files are active now.
+
+**/
+BOOLEAN
+EFIAPI
+EfiShellBatchIsActive (
+ VOID
+ );
+
+/**
+ Worker function to open a file based on a device path. this will open the root
+ of the volume and then traverse down to the file itself.
+
+ @param DevicePath2 Device Path of the file
+ @param FileHandle Pointer to the file upon a successful return
+ @param OpenMode mode to open file in.
+ @param Attributes the File Attributes to use when creating a new file
+
+ @retval EFI_SUCCESS the file is open and FileHandle is valid
+ @retval EFI_UNSUPPORTED the device path cotained non-path elements
+ @retval other an error ocurred.
+**/
+EFI_STATUS
+EFIAPI
+InternalOpenFileDevicePath(
+ IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath2,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes OPTIONAL
+ );
+
+/**
+ Creates a file or directory by name.
+
+ This function creates an empty new file or directory with the specified attributes and
+ returns the new file's handle. If the file already exists and is read-only, then
+ EFI_INVALID_PARAMETER will be returned.
+
+ If the file already existed, it is truncated and its attributes updated. If the file is
+ created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
+
+ If the file name begins with >v, then the file handle which is returned refers to the
+ shell environment variable with the specified name. If the shell environment variable
+ already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
+
+ @param FileName Pointer to NULL-terminated file path
+ @param FileAttribs The new file's attrbiutes. the different attributes are
+ described in EFI_FILE_PROTOCOL.Open().
+ @param FileHandle On return, points to the created file handle or directory's handle
+
+ @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED could not open the file path
+ @retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not
+ file the file system on the device.
+ @retval EFI_NO_MEDIA the device has no medium.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported.
+ @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
+ the DirName.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected.
+ @retval EFI_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
+ @retval EFI_VOLUME_FULL The volume is full.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellCreateFile(
+ IN CONST CHAR16 *FileName,
+ IN UINT64 FileAttribs,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ );
+
+/**
+ Opens a file or a directory by file name.
+
+ This function opens the specified file in the specified OpenMode and returns a file
+ handle.
+ If the file name begins with >v, then the file handle which is returned refers to the
+ shell environment variable with the specified name. If the shell environment variable
+ exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
+ EFI_INVALID_PARAMETER is returned.
+
+ If the file name is >i, then the file handle which is returned refers to the standard
+ input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is >o, then the file handle which is returned refers to the standard
+ output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is >e, then the file handle which is returned refers to the standard
+ error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is NUL, then the file handle that is returned refers to the standard NUL
+ file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
+ returned.
+
+ If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
+ FileHandle is NULL.
+
+ @param FileName Points to the NULL-terminated UCS-2 encoded file name.
+ @param FileHandle On return, points to the file handle.
+ @param OpenMode File open mode. Either EFI_FILE_MODE_READ or
+ EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
+ Specification.
+ @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
+ @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL.
+ @retval EFI_NOT_FOUND The specified file could not be found on the device or the file
+ system could not be found on the device. FileHandle is NULL.
+ @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported. FileHandle is NULL.
+ @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
+ the FileName. FileHandle is NULL.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected. FileHandle is NULL.
+ @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle
+ is NULL.
+ @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenFileByName(
+ IN CONST CHAR16 *FileName,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode
+ );
+
+/**
+ Deletes the file specified by the file name.
+
+ This function deletes a file.
+
+ @param FileName Points to the NULL-terminated file name.
+
+ @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed.
+ @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
+ @sa EfiShellCreateFile
+ @sa FileHandleDelete
+**/
+EFI_STATUS
+EFIAPI
+EfiShellDeleteFileByName(
+ IN CONST CHAR16 *FileName
+ );
+
+/**
+ Disables the page break output mode.
+**/
+VOID
+EFIAPI
+EfiShellDisablePageBreak (
+ VOID
+ );
+
+/**
+ Enables the page break output mode.
+**/
+VOID
+EFIAPI
+EfiShellEnablePageBreak (
+ VOID
+ );
+
+/**
+ internal worker function to run a command via Device Path
+
+ @param ParentImageHandle A handle of the image that is executing the specified
+ command line.
+ @param DevicePath device path of the file to execute
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+ @param[out] StartImageStatus Returned status from gBS->StartImage.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+**/
+EFI_STATUS
+EFIAPI
+InternalShellExecuteDevicePath(
+ IN CONST EFI_HANDLE *ParentImageHandle,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CONST CHAR16 *CommandLine OPTIONAL,
+ IN CONST CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StartImageStatus OPTIONAL
+ );
+
+/**
+ Execute the command line.
+
+ This function creates a nested instance of the shell and executes the specified
+ command (CommandLine) with the specified environment (Environment). Upon return,
+ the status code returned by the specified command is placed in StatusCode.
+
+ If Environment is NULL, then the current environment is used and all changes made
+ by the commands executed will be reflected in the current environment. If the
+ Environment is non-NULL, then the changes made will be discarded.
+
+ The CommandLine is executed from the current working directory on the current
+ device.
+
+ @param ParentImageHandle A handle of the image that is executing the specified
+ command line.
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+ @param StatusCode Points to the status code returned by the command.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellExecute(
+ IN EFI_HANDLE *ParentImageHandle,
+ IN CHAR16 *CommandLine OPTIONAL,
+ IN CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StatusCode OPTIONAL
+ );
+
+/**
+ Utility cleanup function for EFI_SHELL_FILE_INFO objects.
+
+ 1) frees all pointers (non-NULL)
+ 2) Closes the SHELL_FILE_HANDLE
+
+ @param FileListNode pointer to the list node to free
+**/
+VOID
+EFIAPI
+FreeShellFileInfoNode(
+ IN EFI_SHELL_FILE_INFO *FileListNode
+ );
+
+/**
+ Frees the file list.
+
+ This function cleans up the file list and any related data structures. It has no
+ impact on the files themselves.
+
+ @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is
+ defined in OpenFileList()
+
+ @retval EFI_SUCCESS Free the file list successfully.
+ @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFreeFileList(
+ IN EFI_SHELL_FILE_INFO **FileList
+ );
+
+/**
+ Deletes the duplicate file names files in the given file list.
+
+ This function deletes the reduplicate files in the given file list.
+
+ @param FileList A pointer to the first entry in the file list.
+
+ @retval EFI_SUCCESS Always success.
+ @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
+**/
+EFI_STATUS
+EFIAPI
+EfiShellRemoveDupInFileList(
+ IN EFI_SHELL_FILE_INFO **FileList
+ );
+
+/**
+ Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation
+ failed it will return NULL.
+
+ @param[in] BasePath the Path to prepend onto filename for FullPath
+ @param[in] Status Status member initial value.
+ @param[in] FileName FileName member initial value.
+ @param[in] Handle Handle member initial value.
+ @param[in] Info Info struct to copy.
+
+**/
+EFI_SHELL_FILE_INFO *
+EFIAPI
+CreateAndPopulateShellFileInfo(
+ IN CONST CHAR16 *BasePath,
+ IN CONST EFI_STATUS Status,
+ IN CONST CHAR16 *FileName,
+ IN CONST SHELL_FILE_HANDLE Handle,
+ IN CONST EFI_FILE_INFO *Info
+ );
+
+/**
+ Find all files in a specified directory.
+
+ @param FileDirHandle Handle of the directory to search.
+ @param FileList On return, points to the list of files in the directory
+ or NULL if there are no files in the directory.
+
+ @retval EFI_SUCCESS File information was returned successfully.
+ @retval EFI_VOLUME_CORRUPTED The file system structures have been corrupted.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_NO_MEDIA The device media is not present.
+ @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFindFilesInDir(
+ IN SHELL_FILE_HANDLE FileDirHandle,
+ OUT EFI_SHELL_FILE_INFO **FileList
+ );
+
+/**
+ Find files that match a specified pattern.
+
+ This function searches for all files and directories that match the specified
+ FilePattern. The FilePattern can contain wild-card characters. The resulting file
+ information is placed in the file list FileList.
+
+ Wildcards are processed
+ according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
+
+ The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
+ field is set to NULL.
+
+ if *FileList is not NULL then it must be a pre-existing and properly initialized list.
+
+ @param FilePattern Points to a NULL-terminated shell file path, including wildcards.
+ @param FileList On return, points to the start of a file list containing the names
+ of all matching files or else points to NULL if no matching files
+ were found. only on a EFI_SUCCESS return will; this be non-NULL.
+
+ @retval EFI_SUCCESS Files found. FileList is a valid list.
+ @retval EFI_NOT_FOUND No files found.
+ @retval EFI_NO_MEDIA The device has no media
+ @retval EFI_DEVICE_ERROR The device reported an error
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFindFiles(
+ IN CONST CHAR16 *FilePattern,
+ OUT EFI_SHELL_FILE_INFO **FileList
+ );
+
+/**
+ Opens the files that match the path specified.
+
+ This function opens all of the files specified by Path. Wildcards are processed
+ according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
+ matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
+
+ @param Path A pointer to the path string.
+ @param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or
+ EFI_FILE_MODE_WRITE.
+ @param FileList Points to the start of a list of files opened.
+
+ @retval EFI_SUCCESS Create the file list successfully.
+ @return Others Can't create the file list.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenFileList(
+ IN CHAR16 *Path,
+ IN UINT64 OpenMode,
+ IN OUT EFI_SHELL_FILE_INFO **FileList
+ );
+
+/**
+ Gets the environment variable.
+
+ This function returns the current value of the specified environment variable.
+
+ @param Name A pointer to the environment variable name
+
+ @retval !=NULL The environment variable's value. The returned
+ pointer does not need to be freed by the caller.
+ @retval NULL The environment variable doesn't exist.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetEnv(
+ IN CONST CHAR16 *Name
+ );
+
+/**
+ Sets the environment variable.
+
+ This function changes the current value of the specified environment variable. If the
+ environment variable exists and the Value is an empty string, then the environment
+ variable is deleted. If the environment variable exists and the Value is not an empty
+ string, then the value of the environment variable is changed. If the environment
+ variable does not exist and the Value is an empty string, there is no action. If the
+ environment variable does not exist and the Value is a non-empty string, then the
+ environment variable is created and assigned the specified value.
+
+ For a description of volatile and non-volatile environment variables, see UEFI Shell
+ 2.0 specification section 3.6.1.
+
+ @param Name Points to the NULL-terminated environment variable name.
+ @param Value Points to the NULL-terminated environment variable value. If the value is an
+ empty string then the environment variable is deleted.
+ @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
+
+ @retval EFI_SUCCESS The environment variable was successfully updated.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetEnv(
+ IN CONST CHAR16 *Name,
+ IN CONST CHAR16 *Value,
+ IN BOOLEAN Volatile
+ );
+
+/**
+ Returns the current directory on the specified device.
+
+ If FileSystemMapping is NULL, it returns the current working directory. If the
+ FileSystemMapping is not NULL, it returns the current directory associated with the
+ FileSystemMapping. In both cases, the returned name includes the file system
+ mapping (i.e. fs0:\current-dir).
+
+ @param FileSystemMapping A pointer to the file system mapping. If NULL,
+ then the current working directory is returned.
+
+ @retval !=NULL The current directory.
+ @retval NULL Current directory does not exist.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetCurDir(
+ IN CONST CHAR16 *FileSystemMapping OPTIONAL
+ );
+
+/**
+ Changes the current directory on the specified device.
+
+ If the FileSystem is NULL, and the directory Dir does not contain a file system's
+ mapped name, this function changes the current working directory. If FileSystem is
+ NULL and the directory Dir contains a mapped name, then the current file system and
+ the current directory on that file system are changed.
+
+ If FileSystem is not NULL, and Dir is NULL, then this changes the current working file
+ system.
+
+ If FileSystem is not NULL and Dir is not NULL, then this function changes the current
+ directory on the specified file system.
+
+ If the current working directory or the current working file system is changed then the
+ %cwd% environment variable will be updated
+
+ @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working
+ directory is changed.
+ @param Dir Points to the NULL-terminated directory on the device specified by FileSystem.
+
+ @retval EFI_SUCCESS The operation was sucessful
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetCurDir(
+ IN CONST CHAR16 *FileSystem OPTIONAL,
+ IN CONST CHAR16 *Dir
+ );
+
+/**
+ Return help information about a specific command.
+
+ This function returns the help information for the specified command. The help text
+ can be internal to the shell or can be from a UEFI Shell manual page.
+
+ If Sections is specified, then each section name listed will be compared in a casesensitive
+ manner, to the section names described in Appendix B. If the section exists,
+ it will be appended to the returned help text. If the section does not exist, no
+ information will be returned. If Sections is NULL, then all help text information
+ available will be returned.
+
+ @param Command Points to the NULL-terminated UEFI Shell command name.
+ @param Sections Points to the NULL-terminated comma-delimited
+ section names to return. If NULL, then all
+ sections will be returned.
+ @param HelpText On return, points to a callee-allocated buffer
+ containing all specified help text.
+
+ @retval EFI_SUCCESS The help text was returned.
+ @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
+ returned help text.
+ @retval EFI_INVALID_PARAMETER HelpText is NULL
+ @retval EFI_NOT_FOUND There is no help text available for Command.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetHelpText(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Sections OPTIONAL,
+ OUT CHAR16 **HelpText
+ );
+
+/**
+ Gets the enable status of the page break output mode.
+
+ User can use this function to determine current page break mode.
+
+ @retval TRUE The page break output mode is enabled
+ @retval FALSE The page break output mode is disabled
+**/
+BOOLEAN
+EFIAPI
+EfiShellGetPageBreak(
+ VOID
+ );
+
+/**
+ Judges whether the active shell is the root shell.
+
+ This function makes the user to know that whether the active Shell is the root shell.
+
+ @retval TRUE The active Shell is the root Shell.
+ @retval FALSE The active Shell is NOT the root Shell.
+**/
+BOOLEAN
+EFIAPI
+EfiShellIsRootShell(
+ VOID
+ );
+
+/**
+ This function returns the command associated with a alias or a list of all
+ alias'.
+
+ @param[in] Command Points to the NULL-terminated shell alias.
+ If this parameter is NULL, then all
+ aliases will be returned in ReturnedData.
+ @param[out] Volatile upon return of a single command if TRUE indicates
+ this is stored in a volatile fashion. FALSE otherwise.
+ @return If Alias is not NULL, it will return a pointer to
+ the NULL-terminated command for that alias.
+ If Alias is NULL, ReturnedData points to a ';'
+ delimited list of alias (e.g.
+ ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
+ @retval NULL an error ocurred
+ @retval NULL Alias was not a valid Alias
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetAlias(
+ IN CONST CHAR16 *Command,
+ OUT BOOLEAN *Volatile OPTIONAL
+ );
+
+/**
+ Changes a shell command alias.
+
+ This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
+
+ this function does not check for built in alias'.
+
+ @param[in] Command Points to the NULL-terminated shell command or existing alias.
+ @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
+ Command refers to an alias, that alias will be deleted.
+ @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
+ Alias being set will be stored in a non-volatile fashion.
+
+ @retval EFI_SUCCESS Alias created or deleted successfully.
+ @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
+**/
+EFI_STATUS
+EFIAPI
+InternalSetAlias(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias OPTIONAL,
+ IN BOOLEAN Volatile
+ );
+
+/**
+ Changes a shell command alias.
+
+ This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
+
+
+ @param[in] Command Points to the NULL-terminated shell command or existing alias.
+ @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
+ Command refers to an alias, that alias will be deleted.
+ @param[in] Replace If TRUE and the alias already exists, then the existing alias will be replaced. If
+ FALSE and the alias already exists, then the existing alias is unchanged and
+ EFI_ACCESS_DENIED is returned.
+ @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
+ Alias being set will be stored in a non-volatile fashion.
+
+ @retval EFI_SUCCESS Alias created or deleted successfully.
+ @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
+ @retval EFI_ACCESS_DENIED The alias is a built-in alias or already existed and Replace was set to
+ FALSE.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetAlias(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias OPTIONAL,
+ IN BOOLEAN Replace,
+ IN BOOLEAN Volatile
+ );
+
+/**
+ Utility cleanup function for EFI_SHELL_FILE_INFO objects.
+
+ 1) frees all pointers (non-NULL)
+ 2) Closes the SHELL_FILE_HANDLE
+
+ @param FileListNode pointer to the list node to free
+**/
+VOID
+EFIAPI
+InternalFreeShellFileInfoNode(
+ IN EFI_SHELL_FILE_INFO *FileListNode
+ );
+
+/**
+ Internal variable setting function. Allows for setting of the read only variables.
+
+ @param Name Points to the NULL-terminated environment variable name.
+ @param Value Points to the NULL-terminated environment variable value. If the value is an
+ empty string then the environment variable is deleted.
+ @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
+
+ @retval EFI_SUCCESS The environment variable was successfully updated.
+**/
+EFI_STATUS
+EFIAPI
+InternalEfiShellSetEnv(
+ IN CONST CHAR16 *Name,
+ IN CONST CHAR16 *Value,
+ IN BOOLEAN Volatile
+ );
+
+/**
+ Function to start monitoring for CTRL-C using SimpleTextInputEx. This
+ feature's enabled state was not known when the shell initially launched.
+
+ @retval EFI_SUCCESS The feature is enabled.
+ @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
+**/
+EFI_STATUS
+EFIAPI
+InernalEfiShellStartMonitor(
+ VOID
+ );
+
+/**
+ Notification function for keystrokes.
+
+ @param[in] KeyData The key that was pressed.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+EFIAPI
+NotificationFunction(
+ IN EFI_KEY_DATA *KeyData
+ );
+#endif //_SHELL_PROTOCOL_HEADER_
+
diff --git a/Core/ShellPkg/Application/ShellCTestApp/README.txt b/Core/ShellPkg/Application/ShellCTestApp/README.txt
new file mode 100644
index 0000000000..7814bb8386
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellCTestApp/README.txt
@@ -0,0 +1,5 @@
+TestArgv.nsh is a very simple shell script to test how the interpreter parses
+the parameters. It uses ShellCTestApp.efi to dump the parameters passed from the
+intepreter.
+
+TestArgv.log is the desired output created using "TestArgv.nsh > TestArgv.log". \ No newline at end of file
diff --git a/Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c b/Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c
new file mode 100644
index 0000000000..08c830cfb7
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.c
@@ -0,0 +1,51 @@
+/** @file
+ This is a test application that demonstrates how to use the C-style entry point
+ for a shell application.
+
+ Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ShellCEntryLib.h>
+
+/**
+ UEFI application entry point which has an interface similar to a
+ standard C main function.
+
+ The ShellCEntryLib library instance wrappers the actual UEFI application
+ entry point and calls this ShellAppMain function.
+
+ @param[in] Argc The number of items in Argv.
+ @param[in] Argv Array of pointers to strings.
+
+ @retval 0 The application exited normally.
+ @retval Other An error occurred.
+
+**/
+INTN
+EFIAPI
+ShellAppMain (
+ IN UINTN Argc,
+ IN CHAR16 **Argv
+ )
+{
+ UINTN Index;
+ if (Argc == 1) {
+ Print (L"Argv[1] = NULL\n");
+ }
+ for (Index = 1; Index < Argc; Index++) {
+ Print(L"Argv[%d]: \"%s\"\n", Index, Argv[Index]);
+ }
+
+ return 0;
+}
diff --git a/Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf b/Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf
new file mode 100644
index 0000000000..4839b9fd97
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellCTestApp/ShellCTestApp.inf
@@ -0,0 +1,39 @@
+## @file
+# This is the shell application
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = ShellCTestApp
+ FILE_GUID = 7a6ca3b8-ee1b-489c-b300-24544a7bd418
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = ShellCEntryLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ ShellCTestApp.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+ ShellCEntryLib
+ UefiLib \ No newline at end of file
diff --git a/Core/ShellPkg/Application/ShellCTestApp/TestArgv.log b/Core/ShellPkg/Application/ShellCTestApp/TestArgv.log
new file mode 100644
index 0000000000..e76781ea0e
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellCTestApp/TestArgv.log
Binary files differ
diff --git a/Core/ShellPkg/Application/ShellCTestApp/TestArgv.nsh b/Core/ShellPkg/Application/ShellCTestApp/TestArgv.nsh
new file mode 100644
index 0000000000..013ac123ec
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellCTestApp/TestArgv.nsh
@@ -0,0 +1,64 @@
+#/** @file
+# This is a very simple shell script to test how the interpreter parses the parameters.
+#
+# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#**/
+echo -on
+set Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA ValueOfGuid
+set Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580 ^#
+set Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC ^"
+set Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE
+alias ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE ShellCTestApp
+
+#
+# '^' should escape all special characters (including space)
+# but has no impact to non-special characters
+#
+ShellCTestApp ^^
+ShellCTestApp ^#
+ShellCTestApp ^%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA%
+ShellCTestApp ^"
+ShellCTestApp ^ 1
+ShellCTestApp ^
+ShellCTestApp ^1
+ShellCTestApp ^^^"
+ShellCTestApp ^^^
+
+#
+# '#' should be processed before %% replacement, and inside '"'
+#
+ShellCTestApp #%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA%
+#ShellCTestApp "#"
+ShellCTestApp %Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580%
+
+#
+# '%' should be processed before grouping parameters
+#
+ShellCTestApp "%Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA% 2%Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC%
+
+#
+# alias should be processed after %% replacement
+#
+%Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE%
+
+#
+# '"' should be stripped, space inside '"' should be kept,
+#
+ShellCTestApp "p 1"
+ShellCTestApp "p"1
+ShellCTestApp "p 1"e"x"""
+
+set -d Var_EFCF356F_228C_47C2_AD0C_3B5DAC9A8CFA
+set -d Sharp_E8528E46_A008_4221_8DE0_D5AB42A9C580
+set -d Quote_E95DEE8B_E3AA_4155_9ED5_6916394104FC
+set -d Var_ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE
+alias -d ShellCTestApp_EE6E8BC6_71A6_44A5_BED3_D8F901105CDE
+echo -off \ No newline at end of file
diff --git a/Core/ShellPkg/Application/ShellExecTestApp/SA.c b/Core/ShellPkg/Application/ShellExecTestApp/SA.c
new file mode 100644
index 0000000000..80531d09e9
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellExecTestApp/SA.c
@@ -0,0 +1,38 @@
+/** @file
+ This is a simple shell application
+
+ Copyright (c) 2008 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi.h>
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiLib.h>
+
+/**
+ as the real entry point for the application.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The entry point is executed successfully.
+ @retval other Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+UefiMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ Print(L"ShellExecute - Pass");
+ return EFI_SUCCESS;
+}
diff --git a/Core/ShellPkg/Application/ShellExecTestApp/SA.inf b/Core/ShellPkg/Application/ShellExecTestApp/SA.inf
new file mode 100644
index 0000000000..86e3f7b197
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellExecTestApp/SA.inf
@@ -0,0 +1,41 @@
+## @file
+# Sample UEFI Application Reference EDKII Module
+#
+# This is a simple shell application
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = EmptyApplication
+ FILE_GUID = 8F7D7B1D-0E1C-4c98-B12E-4EC99C4081AC
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = UefiMain
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ SA.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ UefiApplicationEntryPoint
+ UefiLib
+
diff --git a/Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c b/Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c
new file mode 100644
index 0000000000..6bdd237e8d
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.c
@@ -0,0 +1,83 @@
+/** @file
+ This is a test application that demonstrates how to use the sorting functions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ShellCEntryLib.h>
+#include <Library/SortLib.h>
+
+/**
+ Test comparator.
+
+ @param[in] b1 The first INTN
+ @param[in] b2 The other INTN
+
+ @retval 0 They are the same.
+ @retval -1 b1 is less than b2
+ @retval 1 b1 is greater then b2
+**/
+INTN
+EFIAPI
+Test(CONST VOID *b1, CONST VOID *b2)
+{
+ if (*(INTN*)b1 == *(INTN*)b2) {
+ return (0);
+ }
+ if (*(INTN*)b1 < *(INTN*)b2) {
+ return(-1);
+ }
+ return (1);
+}
+
+/**
+ UEFI application entry point which has an interface similar to a
+ standard C main function.
+
+ The ShellCEntryLib library instance wrappers the actual UEFI application
+ entry point and calls this ShellAppMain function.
+
+ @param Argc Argument count
+ @param Argv The parsed arguments
+
+ @retval 0 The application exited normally.
+ @retval Other An error occurred.
+
+**/
+INTN
+EFIAPI
+ShellAppMain (
+ IN UINTN Argc,
+ IN CHAR16 **Argv
+ )
+{
+ INTN Array[10];
+
+ Array[0] = 2;
+ Array[1] = 3;
+ Array[2] = 4;
+ Array[3] = 1;
+ Array[4] = 5;
+ Array[5] = 6;
+ Array[6] = 7;
+ Array[7] = 8;
+ Array[8] = 1;
+ Array[9] = 5;
+
+ Print(L"Array = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\r\n", Array[0],Array[1],Array[2],Array[3],Array[4],Array[5],Array[6],Array[7],Array[8],Array[9]);
+ PerformQuickSort(Array, 10, sizeof(INTN), Test);
+ Print(L"POST-SORT\r\n");
+ Print(L"Array = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\r\n", Array[0],Array[1],Array[2],Array[3],Array[4],Array[5],Array[6],Array[7],Array[8],Array[9]);
+ return 0;
+}
diff --git a/Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf b/Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf
new file mode 100644
index 0000000000..70130c1c06
--- /dev/null
+++ b/Core/ShellPkg/Application/ShellSortTestApp/ShellSortTestApp.inf
@@ -0,0 +1,42 @@
+## @file
+# This is the shell sorting testing application
+#
+# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = ShellSortTestApp
+ FILE_GUID = 079E8E98-AE93-4b9a-8A71-1DC869F23E09
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = ShellCEntryLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ ShellSortTestApp.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ ShellPkg/ShellPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ ShellCEntryLib
+ UefiLib
+ SortLib
+