From a405b86d274d32b92f69842bfb9a1ab143128f57 Mon Sep 17 00:00:00 2001 From: jcarsey Date: Tue, 14 Sep 2010 05:18:09 +0000 Subject: udk2010.up2.shell initial release. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10874 6f19259b-4bc3-4df7-8a09-765794883524 --- ShellPkg/Application/Shell/ConsoleLogger.c | 1151 ++++++++ ShellPkg/Application/Shell/ConsoleLogger.h | 341 +++ ShellPkg/Application/Shell/FileHandleInternal.h | 68 + ShellPkg/Application/Shell/FileHandleWrappers.c | 1563 ++++++++++ ShellPkg/Application/Shell/FileHandleWrappers.h | 95 + ShellPkg/Application/Shell/Shell.c | 1707 +++++++++++ ShellPkg/Application/Shell/Shell.h | 292 ++ ShellPkg/Application/Shell/Shell.inf | 105 + ShellPkg/Application/Shell/Shell.uni | Bin 0 -> 3868 bytes ShellPkg/Application/Shell/ShellEnvVar.c | 326 ++ ShellPkg/Application/Shell/ShellEnvVar.h | 210 ++ ShellPkg/Application/Shell/ShellManParser.c | 615 ++++ ShellPkg/Application/Shell/ShellManParser.h | 86 + .../Application/Shell/ShellParametersProtocol.c | 949 ++++++ .../Application/Shell/ShellParametersProtocol.h | 208 ++ ShellPkg/Application/Shell/ShellProtocol.c | 3122 ++++++++++++++++++++ ShellPkg/Application/Shell/ShellProtocol.h | 950 ++++++ ShellPkg/Application/ShellLibTestApp/sa3.c | 2 +- 18 files changed, 11789 insertions(+), 1 deletion(-) create mode 100644 ShellPkg/Application/Shell/ConsoleLogger.c create mode 100644 ShellPkg/Application/Shell/ConsoleLogger.h create mode 100644 ShellPkg/Application/Shell/FileHandleInternal.h create mode 100644 ShellPkg/Application/Shell/FileHandleWrappers.c create mode 100644 ShellPkg/Application/Shell/FileHandleWrappers.h create mode 100644 ShellPkg/Application/Shell/Shell.c create mode 100644 ShellPkg/Application/Shell/Shell.h create mode 100644 ShellPkg/Application/Shell/Shell.inf create mode 100644 ShellPkg/Application/Shell/Shell.uni create mode 100644 ShellPkg/Application/Shell/ShellEnvVar.c create mode 100644 ShellPkg/Application/Shell/ShellEnvVar.h create mode 100644 ShellPkg/Application/Shell/ShellManParser.c create mode 100644 ShellPkg/Application/Shell/ShellManParser.h create mode 100644 ShellPkg/Application/Shell/ShellParametersProtocol.c create mode 100644 ShellPkg/Application/Shell/ShellParametersProtocol.h create mode 100644 ShellPkg/Application/Shell/ShellProtocol.c create mode 100644 ShellPkg/Application/Shell/ShellProtocol.h (limited to 'ShellPkg/Application') diff --git a/ShellPkg/Application/Shell/ConsoleLogger.c b/ShellPkg/Application/Shell/ConsoleLogger.c new file mode 100644 index 0000000..4b0d94d --- /dev/null +++ b/ShellPkg/Application/Shell/ConsoleLogger.c @@ -0,0 +1,1151 @@ +/** @file + Provides interface to shell console logger. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 "ConsoleLogger.h" +#include "Shell.h" + +STATIC CONST CHAR16 mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL }; + +/** + 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 = AllocatePool(sizeof(CONSOLE_LOGGER_PRIVATE_DATA)); + ASSERT(ConsoleInfo != NULL); + + (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE; + (*ConsoleInfo)->OldConOut = NULL; + (*ConsoleInfo)->OldConHandle = NULL; + (*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 = NULL; + (*ConsoleInfo)->Enabled = TRUE; + + Status = ConsoleLoggerResetBuffers(*ConsoleInfo); + if (EFI_ERROR(Status)) { + return (Status); + } + + Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut)); + + (*ConsoleInfo)->OldConOut = gST->ConOut; + (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle; + + gST->ConsoleOutHandle = gImageHandle; + gST->ConOut = &(*ConsoleInfo)->OurConOut; + + return (Status); +} + +/** + 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 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); + } + 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; + + ASSERT(ConsoleInfo != NULL); + TempCharHolder = CHAR_NULL; + RetVal = EFI_SUCCESS; + + // + // 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; + } + } + + 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); + } + + 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)) + 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; + return (EFI_SUCCESS); + } else if (*Resp == ShellPromptResponseQuit) { + FreePool(Resp); + ShellInfoObject.ConsoleInfo->Enabled = FALSE; + 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 CHAR16 *String, + IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo + ) +{ + CONST CHAR16 *Walker; + CONST CHAR16 *LineStart; + CHAR16 TempChar; + + for ( Walker = String + , LineStart = String + ; 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; + + // + // 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 + // + return (EFI_DEVICE_ERROR); + } + } + } // for loop + + if (LineStart != NULL && *LineStart != CHAR_NULL) { + ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); + } + + 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 + ) +{ + CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; + ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); + ASSERT(ShellInfoObject.ConsoleInfo == ConsoleInfo); + 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)) { + ConsoleLoggerResetBuffers(ConsoleInfo); + } + + 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; + 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 + 1) * 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++; + } + 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; + 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); + } + + ConsoleInfo->OurConOut.Mode = gST->ConOut->Mode; + ConsoleInfo->OldConOut = gST->ConOut; + CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE)); + + return (EFI_SUCCESS); +} diff --git a/ShellPkg/Application/Shell/ConsoleLogger.h b/ShellPkg/Application/Shell/ConsoleLogger.h new file mode 100644 index 0000000..847c64c --- /dev/null +++ b/ShellPkg/Application/Shell/ConsoleLogger.h @@ -0,0 +1,341 @@ +/** @file + Provides interface to shell console logger. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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/ShellPkg/Application/Shell/FileHandleInternal.h b/ShellPkg/Application/Shell/FileHandleInternal.h new file mode 100644 index 0000000..ec55a6e --- /dev/null +++ b/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.
+ 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/ShellPkg/Application/Shell/FileHandleWrappers.c b/ShellPkg/Application/Shell/FileHandleWrappers.c new file mode 100644 index 0000000..02e42e3 --- /dev/null +++ b/ShellPkg/Application/Shell/FileHandleWrappers.c @@ -0,0 +1,1563 @@ +/** @file + EFI_FILE_PROTOCOL wrappers for other items (Like Environment Variables, + StdIn, StdOut, StdErr, etc...). + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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" + +/** + 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); + } else { + return (gST->ConOut->OutputString(gST->ConOut, Buffer)); + } +} + +/** + File style interface for StdIn (Write). + + @param[in] This Ignored. + @param[in] 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 +**/ +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 Ignored. + @param[in] Buffer Ignored. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +FileInterfaceNulRead( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + 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)) { + continue; + } + + // + // 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) { + StrCpy(TabStr, Cwd); + if (TabStr[StrLen(TabStr)-1] == L'\\' && *(CurrentString + TabPos) == L'\\' ) { + TabStr[StrLen(TabStr)-1] = CHAR_NULL; + } + StrnCat(TabStr, CurrentString + TabPos, (StringLen - TabPos) * sizeof (CHAR16)); + } else { + StrCpy(TabStr, L""); + StrnCat(TabStr, CurrentString + TabPos, (StringLen - TabPos) * sizeof (CHAR16)); + } + } else { + StrCpy(TabStr, CurrentString + TabPos); + } + StrCat(TabStr, L"*"); + FoundFileList = NULL; +// TabStr = CleanPath(TabStr); + 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 { + FreePool(FoundFileList); + FoundFileList = NULL; + } + } + } + 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... + // + ASSERT(FoundFileList == NULL); + + return EFI_SUCCESS; +} + +// +// 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 + ) +{ + FreePool((EFI_FILE_PROTOCOL_ENVIRONMENT*)This); + return (EFI_SUCCESS); +} + +/** + 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_FILE_PROTOCOL_ENVIRONMENT *EnvFileInterface; + + if (EnvName == NULL) { + return (NULL); + } + + // + // Get some memory + // + EnvFileInterface = AllocateZeroPool(sizeof(EFI_FILE_PROTOCOL_ENVIRONMENT)+StrSize(EnvName)); + 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; + + StrCpy(EnvFileInterface->Name, EnvName); + + // + // Assign the different members for Volatile and Non-Volatile variables + // + if (IsVolatileEnv(EnvName)) { + 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; +} 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)->BufferSize) { + ((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_SUCCESS The data was written. +**/ +EFI_STATUS +EFIAPI +FileInterfaceMemWrite( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + CHAR8 *AsciiBuffer; + if (((EFI_FILE_PROTOCOL_MEM*)This)->Unicode) { + // + // Unicode + // + if ((UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->Position + (*BufferSize)) > (UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize)) { + ((EFI_FILE_PROTOCOL_MEM*)This)->Buffer = ReallocatePool((UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize), (UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize) + (*BufferSize) + 10, ((EFI_FILE_PROTOCOL_MEM*)This)->Buffer); + ((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize += (*BufferSize) + 10; + } + CopyMem(((UINT8*)((EFI_FILE_PROTOCOL_MEM*)This)->Buffer) + ((EFI_FILE_PROTOCOL_MEM*)This)->Position, Buffer, *BufferSize); + ((EFI_FILE_PROTOCOL_MEM*)This)->Position += (*BufferSize); + return (EFI_SUCCESS); + } else { + // + // Ascii + // + AsciiBuffer = AllocatePool(*BufferSize); + AsciiSPrint(AsciiBuffer, *BufferSize, "%S", Buffer); + if ((UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->Position + AsciiStrSize(AsciiBuffer)) > (UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize)) { + ((EFI_FILE_PROTOCOL_MEM*)This)->Buffer = ReallocatePool((UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize), (UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize) + AsciiStrSize(AsciiBuffer) + 10, ((EFI_FILE_PROTOCOL_MEM*)This)->Buffer); + ((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize += AsciiStrSize(AsciiBuffer) + 10; + } + CopyMem(((UINT8*)((EFI_FILE_PROTOCOL_MEM*)This)->Buffer) + ((EFI_FILE_PROTOCOL_MEM*)This)->Position, AsciiBuffer, AsciiStrSize(AsciiBuffer)); + ((EFI_FILE_PROTOCOL_MEM*)This)->Position += AsciiStrSize(AsciiBuffer); + 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 + ) +{ + if (*BufferSize > (UINTN)((((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize) - (UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->Position))) { + (*BufferSize) = (UINTN)((((EFI_FILE_PROTOCOL_MEM*)This)->BufferSize) - (UINTN)(((EFI_FILE_PROTOCOL_MEM*)This)->Position)); + } + CopyMem(Buffer, ((UINT8*)((EFI_FILE_PROTOCOL_MEM*)This)->Buffer) + ((EFI_FILE_PROTOCOL_MEM*)This)->Position, (*BufferSize)); + ((EFI_FILE_PROTOCOL_MEM*)This)->Position = ((EFI_FILE_PROTOCOL_MEM*)This)->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((EFI_FILE_PROTOCOL_MEM*)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); + + 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; + +/** + 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_FILE_PROTOCOL_FILE*)This)->Orig->Close(((EFI_FILE_PROTOCOL_FILE*)This)->Orig); + FreePool(This); + return (EFI_SUCCESS); +} + +/** + 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] 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 = AllocatePool(*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 = AllocatePool(sizeof(EFI_FILE_PROTOCOL_FILE)); + CopyMem(NewOne, Template, sizeof(EFI_FILE_PROTOCOL_FILE)); + NewOne->Orig = (EFI_FILE_PROTOCOL *)Template; + NewOne->Unicode = Unicode; + NewOne->Close = FileInterfaceFileClose; + NewOne->Write = FileInterfaceFileWrite; + + return ((EFI_FILE_PROTOCOL *)NewOne); +} diff --git a/ShellPkg/Application/Shell/FileHandleWrappers.h b/ShellPkg/Application/Shell/FileHandleWrappers.h new file mode 100644 index 0000000..af133b0 --- /dev/null +++ b/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.
+ 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/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c new file mode 100644 index 0000000..3401bc0 --- /dev/null +++ b/ShellPkg/Application/Shell/Shell.c @@ -0,0 +1,1707 @@ +/** @file + This is THE shell (application) + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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, + NULL, + NULL + }, + {0,0}, + { + {0,0}, + 0, + 0, + TRUE + }, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + {0,0,NULL,NULL}, + {0,0}, +}; + +STATIC CONST CHAR16 mScriptExtension[] = L".NSH"; +STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI"; + +/** + 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_INPUT_KEY Key; + +// gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Calling\r\n"); +// +// Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); +// +// gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Done\r\n"); +// gBS->Stall (1000000); + + if (PcdGet8(PcdShellSupportLevel) > 3) { + return (EFI_UNSUPPORTED); + } + + // + // Clear the screen + // + Status = gST->ConOut->ClearScreen(gST->ConOut); + ASSERT_EFI_ERROR(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); + ASSERT_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) { + return (EFI_NOT_STARTED); + } + } + + // + // 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); + + // + // Check the command line + // + Status = ProcessCommandLine(); + + // + // If shell support level is >= 1 create the mappings and paths + // + if (PcdGet8(PcdShellSupportLevel) >= 1) { + Status = ShellCommandCreateInitialMappingsAndPaths(); + } + + // + // 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), + ShellInfoObject.HiiHandle, + SupportLevel[PcdGet8(PcdShellSupportLevel)], + gEfiShellProtocol->MajorVersion, + gEfiShellProtocol->MinorVersion, + (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... + // + + ///@todo add support for using SimpleInputEx here + // if SimpleInputEx is not available display a warning. + } + + if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) { + // + // process the startup script or launch the called app. + // + Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); + } + + if ((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 * 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); + ShellInfoObject.ConsoleInfo->Enabled = TRUE; + ShellInfoObject.ConsoleInfo->RowCounter = 0; + + // + // Display Prompt + // + Status = DoShellPrompt(); + } while (!ShellCommandGetExit()); + } + } + // + // uninstall protocols / free memory / etc... + // + if (ShellInfoObject.UserBreakTimer != NULL) { + gBS->CloseEvent(ShellInfoObject.UserBreakTimer); + DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;); + } + + if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){ + ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE); + } + + 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){ + CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol); + DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;); + } + + if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){ + FreeBufferList(&ShellInfoObject.BufferToFreeList); + } + + if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){ + ASSERT(FALSE); + } + + 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;); + } + + return (Status); +} + +/** + Sets all the alias' that were registered with the ShellCommandLib library. + + @retval EFI_SUCCESS all init commands were run sucessfully. +**/ +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 sucessful return the device path to the loaded image. + @param[in,out] FilePath On a sucessful return the device path to the file. + + @retval EFI_SUCCESS The 2 device paths were sucessfully 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( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + gImageHandle, + NULL); + } + return (Status); +} + +STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = { + {L"-nostartup", TypeFlag}, + {L"-startup", TypeFlag}, + {L"-noconsoleout", TypeFlag}, + {L"-noconsolein", TypeFlag}, + {L"-nointerrupt", TypeFlag}, + {L"-nomap", TypeFlag}, + {L"-noversion", TypeFlag}, + {L"-startup", TypeFlag}, + {L"-delay", TypeValue}, + {NULL, TypeMax} + }; +/** + 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 + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Package; + UINTN Size; + CONST CHAR16 *TempConst; + UINTN Count; + UINTN LoopVar; + CHAR16 *ProblemParam; + + Package = NULL; + ProblemParam = NULL; + + Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE); + + Count = 1; + Size = 0; + TempConst = ShellCommandLineGetRawValue(Package, Count++); + if (TempConst != NULL && StrLen(TempConst)) { + ShellInfoObject.ShellInitSettings.FileName = AllocatePool(StrSize(TempConst)); + StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1; + for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) { + if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) { + LoopVar++; + // + // We found the file... add the rest of the params... + // + for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) { + ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL)); + StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions, + &Size, + L" ", + 0); + StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions, + &Size, + gEfiShellParametersProtocol->Argv[LoopVar], + 0); + } + } + } + } else { + ShellCommandLineFreeVarList(Package); + Package = NULL; + Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam); + FreePool(ProblemParam); + ShellCommandLineFreeVarList(Package); + return (EFI_INVALID_PARAMETER); + } + } + + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup = ShellCommandLineGetFlag(Package, L"-startup"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = ShellCommandLineGetFlag(Package, L"-nostartup"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn = ShellCommandLineGetFlag(Package, L"-noconsolein"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt = ShellCommandLineGetFlag(Package, L"-nointerrupt"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap = ShellCommandLineGetFlag(Package, L"-nomap"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion = ShellCommandLineGetFlag(Package, L"-noversion"); + ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay = ShellCommandLineGetFlag(Package, L"-delay"); + + if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) { + TempConst = ShellCommandLineGetValue(Package, L"-delay"); + if (TempConst != NULL) { + ShellInfoObject.ShellInitSettings.Delay = StrDecimalToUintn (TempConst); + } else { + ShellInfoObject.ShellInitSettings.Delay = 5; + } + } else { + ShellInfoObject.ShellInitSettings.Delay = 5; + } + + ShellCommandLineFreeVarList(Package); + + return (Status); +} + +/** + 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( + EFI_DEVICE_PATH_PROTOCOL *ImagePath, + EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + UINTN Delay; + EFI_INPUT_KEY Key; + SHELL_FILE_HANDLE FileHandle; + EFI_DEVICE_PATH_PROTOCOL *NewPath; + EFI_DEVICE_PATH_PROTOCOL *NamePath; + CHAR16 *FileStringPath; + UINTN NewSize; + + 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); + StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName); + if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) { + StrCat (FileStringPath, L" "); + StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions); + } + Status = RunCommand(FileStringPath); + 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); + } + + // + // print out our warning and see if they press a key + // + for ( Status = EFI_UNSUPPORTED, Delay = (ShellInfoObject.ShellInitSettings.Delay * 10) + ; Delay > 0 && EFI_ERROR(Status) + ; Delay-- + ){ + ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10); + gBS->Stall (100000); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + } + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle); + + // + // ESC was pressed + // + if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) { + return (EFI_SUCCESS); + } + + NamePath = FileDevicePath (NULL, L"startup.nsh"); + // + // Try the first location + // + NewPath = AppendDevicePathNode (ImagePath, NamePath); + Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(Status)) { + // + // Try the second location + // + FreePool(NewPath); + NewPath = AppendDevicePathNode (FilePath , NamePath); + Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0); + } + + // + // If we got a file, run it + // + if (!EFI_ERROR(Status)) { + Status = RunScriptFileHandle (FileHandle, L"startup.nsh"); + ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle); + } else { + // + // we return success since we dont need to have a startup script + // + Status = EFI_SUCCESS; + ASSERT(FileHandle == NULL); + } + + FreePool(NamePath); + FreePool(NewPath); + + 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; + + 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; + } + + 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 + // + 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)); + ASSERT(BufferListEntry != NULL); + BufferListEntry->Buffer = Buffer; + InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link); + return (Buffer); +} + +/** + 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; + + Node = AllocateZeroPool(sizeof(BUFFER_LIST)); + ASSERT(Node != NULL); + Node->Buffer = AllocateZeroPool(StrSize(Buffer)); + ASSERT(Node->Buffer != NULL); + StrCpy(Node->Buffer, Buffer); + + 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 = AllocatePool(StrSize(NewString)); + if (*CommandString == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + StrCpy(*CommandString, NewString); + 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 ocurred. + @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 preceeding and if there is space no ^ preceeding (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 preceeding and following % and if there is space no ^ preceeding (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)); + } + } + } + + // + // Quick out if none were found... + // + if (NewSize == StrSize(OriginalCommandLine)) { + ASSERT(Temp == NULL); + Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0); + return (Temp); + } + + // + // now do the replacements... + // + NewCommandLine1 = AllocateZeroPool(NewSize); + NewCommandLine2 = AllocateZeroPool(NewSize); + ItemTemp = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16))); + StrCpy(NewCommandLine1, OriginalCommandLine); + for (MasterEnvList = EfiShellGetEnv(NULL) + ; MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL + ; MasterEnvList += StrLen(MasterEnvList) + 1 + ){ + StrCpy(ItemTemp, L"%"); + StrCat(ItemTemp, MasterEnvList); + StrCat(ItemTemp, L"%"); + ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE); + StrCpy(NewCommandLine1, 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); + StrCpy(NewCommandLine1, 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[0] != CHAR_NULL && + NextCommandLine[0] == L'a' && + NextCommandLine[1] == L' ' + ){ + CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0])); + Unicode = FALSE; + } else { + Unicode = TRUE; + } + + + // + // make a SPLIT_LIST item and add to list + // + Split = AllocateZeroPool(sizeof(SPLIT_LIST)); + ASSERT(Split != NULL); + Split->SplitStdIn = StdIn; + Split->SplitStdOut = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL); + ASSERT(Split->SplitStdOut != NULL); + InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link); + + ASSERT(StrStr(OurCommandLine, L"|") == NULL); + 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 && Split->SplitStdOut != StdIn) { + ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut)); + } + if (Split->SplitStdIn != NULL) { + ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn)); + } + + FreePool(Split); + FreePool(NextCommandLine); + FreePool(OurCommandLine); + + 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 + ) +{ + EFI_STATUS Status; + CHAR16 *CommandName; + SHELL_STATUS ShellStatus; + UINTN Argc; + CHAR16 **Argv; + BOOLEAN LastError; + CHAR16 LeString[11]; + CHAR16 *PostAliasCmdLine; + UINTN PostAliasSize; + CHAR16 *PostVariableCmdLine; + CHAR16 *CommandWithPath; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + CONST CHAR16 *TempLocation; + CONST CHAR16 *TempLocation2; + SHELL_FILE_HANDLE OriginalStdIn; + SHELL_FILE_HANDLE OriginalStdOut; + SHELL_FILE_HANDLE OriginalStdErr; + CHAR16 *TempLocation3; + UINTN Count; + UINTN Count2; + CHAR16 *CleanOriginal; + SPLIT_LIST *Split; + + ASSERT(CmdLine != NULL); + if (StrLen(CmdLine) == 0) { + return (EFI_SUCCESS); + } + + CommandName = NULL; + PostVariableCmdLine = NULL; + PostAliasCmdLine = NULL; + CommandWithPath = NULL; + DevPath = NULL; + Status = EFI_SUCCESS; + CleanOriginal = NULL; + Split = NULL; + + CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0); + while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') { + CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL; + } + while (CleanOriginal[0] == L' ') { + CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0])); + } + + CommandName = NULL; + if (StrStr(CleanOriginal, L" ") == NULL){ + StrnCatGrow(&CommandName, NULL, CleanOriginal, 0); + } else { + StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal); + } + + ASSERT(PostAliasCmdLine == NULL); + if (!ShellCommandIsCommandOnList(CommandName)) { + // + // Convert via alias + // + Status = ShellConvertAlias(&CommandName); + PostAliasSize = 0; + PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0); + PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0); + ASSERT_EFI_ERROR(Status); + } else { + PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0); + } + + if (CleanOriginal != NULL) { + FreePool(CleanOriginal); + CleanOriginal = NULL; + } + + if (CommandName != NULL) { + FreePool(CommandName); + CommandName = NULL; + } + + PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine); + + // + // we can now free the modified by alias command line + // + if (PostAliasCmdLine != NULL) { + FreePool(PostAliasCmdLine); + PostAliasCmdLine = NULL; + } + + while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') { + PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL; + } + while (PostVariableCmdLine[0] == L' ') { + CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0])); + } + + // + // We dont do normal processing with a split command line (output from one command input to another) + // + TempLocation3 = NULL; + if (StrStr(PostVariableCmdLine, L"|") != NULL) { + for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) { + if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') { + TempLocation3++; + } else if (*TempLocation3 == L'|') { + break; + } + } + } + if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) { + // + // are we in an existing split??? + // + if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) { + Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link); + } + + if (Split == NULL) { + Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL); + } else { + Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut); + } + } else { + + // + // If this is a mapped drive change handle that... + // + if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) { + Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine); + } + FreePool(PostVariableCmdLine); + return (Status); + } + + ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file... + /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files... + + + + Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr); + if (EFI_ERROR(Status)) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle); + } else { + // + // remove the < and/or > from the command line now + // + for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) { + if (*TempLocation3 == L'^') { + if (*(TempLocation3+1) == L'<' || *(TempLocation3+1) == L'>') { + CopyMem(TempLocation3, TempLocation3+1, StrSize(TempLocation3) - sizeof(TempLocation3[0])); + } + } else if (*TempLocation3 == L'>') { + *TempLocation3 = CHAR_NULL; + } else if ((*TempLocation3 == L'1' || *TempLocation3 == L'2')&&(*(TempLocation3+1) == L'>')) { + *TempLocation3 = CHAR_NULL; + } + } + + while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') { + PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL; + } + while (PostVariableCmdLine[0] == L' ') { + CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0])); + } + + // + // get the argc and argv updated for internal commands + // + Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc); + ASSERT_EFI_ERROR(Status); + + for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) { + if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count] + || (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL) + ) { + // + // We need to redo the arguments since a parameter was -? + // move them all down 1 to the end, then up one then replace the first with help + // + FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]); + ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL; + for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) { + ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1]; + } + ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL; + for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) { + ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1]; + } + ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL; + ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0); + break; + } + } + + // + // command or file? + // + if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) { + // + // Run the command (which was converted if it was an alias) + // + if (!EFI_ERROR(Status)) { + Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError); + ASSERT_EFI_ERROR(Status); + UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus); + DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE);); + if (LastError) { + InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE); + } + // + // Pass thru the exitcode from the app. + // + if (ShellCommandGetExit()) { + Status = ShellStatus; + } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) { + Status = EFI_ABORTED; + } + } + } else { + // + // run an external file (or script) + // + if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) { + ASSERT (CommandWithPath == NULL); + if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) { + CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0); + } + } + if (CommandWithPath == NULL) { + CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions); + } + if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) { + ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]); + } else { + // + // Check if it's a NSH (script) file. + // + TempLocation = CommandWithPath+StrLen(CommandWithPath)-4; + TempLocation2 = mScriptExtension; + if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) { + Status = RunScriptFile (CommandWithPath); + } else { + DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath); + ASSERT(DevPath != NULL); + Status = InternalShellExecuteDevicePath( + &gImageHandle, + DevPath, + PostVariableCmdLine, + NULL, + NULL + ); + } + } + } + CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0); + + RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc); + + RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr); + } + if (CommandName != NULL) { + if (ShellCommandGetCurrentScriptFile() != NULL && !IsScriptOnlyCommand(CommandName)) { + // + // if this is NOT a scipt only command return success so the script won't quit. + // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...) + // + Status = EFI_SUCCESS; + } + } + } + + SHELL_FREE_NON_NULL(CommandName); + SHELL_FREE_NON_NULL(CommandWithPath); + SHELL_FREE_NON_NULL(PostVariableCmdLine); + SHELL_FREE_NON_NULL(DevPath); + + return (Status); +} + +STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002}; +/** + Function determins 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 sucessfully +**/ +EFI_STATUS +EFIAPI +RunScriptFileHandle ( + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Name + ) +{ + EFI_STATUS Status; + SCRIPT_FILE *NewScriptFile; + UINTN LoopVar; + CHAR16 *CommandLine; + CHAR16 *CommandLine2; + CHAR16 *CommandLine3; + SCRIPT_COMMAND_LIST *LastCommand; + BOOLEAN Ascii; + BOOLEAN PreScriptEchoState; + CONST CHAR16 *CurDir; + UINTN LineCount; + + ASSERT(!ShellCommandGetScriptExit()); + + PreScriptEchoState = ShellCommandGetEchoState(); + + NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE)); + ASSERT(NewScriptFile != NULL); + + // + // Set up the name + // + ASSERT(NewScriptFile->ScriptName == NULL); + NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0); + + // + // 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*)); + ASSERT(NewScriptFile->Argv != NULL); + 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); + } + } 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) { + continue; + } + NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST)); + ASSERT(NewScriptFile->CurrentCommand != NULL); + + 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 = AllocatePool(PcdGet16(PcdShellPrintBufferSize)); + CommandLine2 = AllocatePool(PcdGet16(PcdShellPrintBufferSize)); + + for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList) + ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link) + ; // conditional increment in the body of the loop + ){ + StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl); + + // + // NULL out comments + // + for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) { + if (*CommandLine3 == L'^') { + if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') { + CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0])); + } + } 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. + // + StrCpy(CommandLine, CommandLine2); + + // + // 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, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 9: + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 8: + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 7: + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 6: + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 5: + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 4: + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 3: + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 2: + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + case 1: + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE); + ASSERT_EFI_ERROR(Status); + break; + case 0: + break; + } + } + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine, CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE); + Status = ShellCopySearchAndReplace(CommandLine2, CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE); + + StrCpy(CommandLine2, CommandLine); + + LastCommand = NewScriptFile->CurrentCommand; + + for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++); + + if (CommandLine3[0] == L':' ) { + // + // This line is a goto target / label + // + } else { + if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) { + 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()) { + ShellCommandRegisterExit(FALSE); + Status = EFI_SUCCESS; + break; + } + if (EFI_ERROR(Status)) { + break; + } + if (ShellCommandGetExit()) { + 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; + } + } + } + + ShellCommandSetEchoState(PreScriptEchoState); + + FreePool(CommandLine); + FreePool(CommandLine2); + ShellCommandSetNewScript (NULL); + return (EFI_SUCCESS); +} + +/** + Function to process a NSH script file. + + @param[in] ScriptPath Pointer to the script file name (including file system path). + + @retval EFI_SUCCESS the script completed sucessfully +**/ +EFI_STATUS +EFIAPI +RunScriptFile ( + IN CONST CHAR16 *ScriptPath + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE FileHandle; + + if (ShellIsFile(ScriptPath) != EFI_SUCCESS) { + return (EFI_INVALID_PARAMETER); + } + + Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(Status)) { + return (Status); + } + + Status = RunScriptFileHandle(FileHandle, ScriptPath); + + ShellCloseFile(&FileHandle); + + return (Status); +} diff --git a/ShellPkg/Application/Shell/Shell.h b/ShellPkg/Application/Shell/Shell.h new file mode 100644 index 0000000..6f165e5 --- /dev/null +++ b/ShellPkg/Application/Shell/Shell.h @@ -0,0 +1,292 @@ +/** @file + function definitions for internal to shell functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ShellParametersProtocol.h" +#include "ShellProtocol.h" +#include "ShellEnvVar.h" +#include "ConsoleLogger.h" +#include "ShellManParser.h" + +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 Reserved:8; ///< 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. +} SHELL_INFO; + +extern SHELL_INFO ShellInfoObject; + +/** + Sets all the alias' that were registered with the ShellCommandLib library. + + @retval EFI_SUCCESS all init commands were run sucessfully. +**/ +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 sucessful return the device path to the loaded image + @param[in,out] FilePath on a sucessful return the device path to the file + + @retval EFI_SUCCESS the 2 device paths were sucessfully 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 determins 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 sucessfully +**/ +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). + + @retval EFI_SUCCESS the script completed sucessfully +**/ +EFI_STATUS +EFIAPI +RunScriptFile ( + IN CONST CHAR16 *ScriptPath + ); + + +#endif //_SHELL_INTERNAL_HEADER_ + diff --git a/ShellPkg/Application/Shell/Shell.inf b/ShellPkg/Application/Shell/Shell.inf new file mode 100644 index 0000000..c50e560 --- /dev/null +++ b/ShellPkg/Application/Shell/Shell.inf @@ -0,0 +1,105 @@ +## @file +# This is the shell application +# +# Copyright (c) 2009-2010, Intel Corporation. All rights reserved.
+# +# 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 + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + MdeModulePkg/MdeModulePkg.dec + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + +[LibraryClasses] + BaseLib + UefiApplicationEntryPoint + UefiLib + DebugLib + MemoryAllocationLib + ShellCommandLib + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + DevicePathLib + BaseMemoryLib + PcdLib + FileHandleLib + PrintLib + HiiLib + SortLib + HandleParsingLib + +[Guids] + gShellVariableGuid # ALWAYS_CONSUMED + gShellMapGuid # ALWAYS_CONSUMED + gShellAliasGuid # ALWAYS_CONSUMED + +[Protocols] + gEfiShellProtocolGuid # ALWAYS_PRODUCED + gEfiShellParametersProtocolGuid # ALWAYS_PRODUCED + gEfiShellEnvironment2Guid # SOMETIMES_PRODUCED + gEfiShellInterfaceGuid # SOMETIMES_PRODUCED + + gEfiLoadedImageProtocolGuid # ALWAYS_CONSUMED + gEfiSimpleTextInputExProtocolGuid # ALWAYS_CONSUMED + gEfiSimpleTextOutProtocolGuid # ALWAYS_CONSUMED + gEfiSimpleFileSystemProtocolGuid # ALWAYS_CONSUMED + gEfiLoadedImageProtocolGuid # ALWAYS_CONSUMED + gEfiComponentName2ProtocolGuid # ALWAYS_CONSUMED + gEfiUnicodeCollation2ProtocolGuid # ALWAYS_CONSUMED + gEfiDevicePathProtocolGuid # ALWAYS_CONSUMED + gEfiDevicePathToTextProtocolGuid # ALWAYS_CONSUMED + +[Pcd] + gEfiShellPkgTokenSpaceGuid.PcdShellSupportLevel # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellSupportOldProtocols # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellRequireHiiPlatform # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellSupportFrameworkHii # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellPageBreakDefault # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellInsertModeDefault # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellScreenLogCount # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellMapNameLength # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellPrintBufferSize # ALWAYS_CONSUMED + gEfiShellPkgTokenSpaceGuid.PcdShellForceConsole # ALWAYS_CONSUMED + diff --git a/ShellPkg/Application/Shell/Shell.uni b/ShellPkg/Application/Shell/Shell.uni new file mode 100644 index 0000000..82a87ae Binary files /dev/null and b/ShellPkg/Application/Shell/Shell.uni differ diff --git a/ShellPkg/Application/Shell/ShellEnvVar.c b/ShellPkg/Application/Shell/ShellEnvVar.c new file mode 100644 index 0000000..ff353c4 --- /dev/null +++ b/ShellPkg/Application/Shell/ShellEnvVar.c @@ -0,0 +1,326 @@ +/** @file + function declarations for shell environment functions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 + +#include + +#include +#include +#include +#include +#include + +#include "ShellEnvVar.h" + + +/** + Reports whether an environment variable is Volatile or Non-Volatile. + + @param EnvVarName The name of the environment variable in question + + @retval TRUE This environment variable is Volatile + @retval FALSE This environment variable is NON-Volatile +**/ +BOOLEAN +EFIAPI +IsVolatileEnv ( + IN CONST CHAR16 *EnvVarName + ) +{ + EFI_STATUS Status; + UINTN Size; + VOID *Buffer; + UINT32 Attribs; + + Size = 0; + Buffer = NULL; + + // + // get the variable + // + Status = gRT->GetVariable((CHAR16*)EnvVarName, + &gShellVariableGuid, + &Attribs, + &Size, + Buffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + Buffer = AllocatePool(Size); + ASSERT(Buffer != NULL); + Status = gRT->GetVariable((CHAR16*)EnvVarName, + &gShellVariableGuid, + &Attribs, + &Size, + Buffer); + FreePool(Buffer); + } + // + // not found means volatile + // + if (Status == EFI_NOT_FOUND) { + return (TRUE); + } + ASSERT_EFI_ERROR(Status); + + // + // check for the Non Volatile bit + // + if ((Attribs & EFI_VARIABLE_NON_VOLATILE) == EFI_VARIABLE_NON_VOLATILE) { + return (FALSE); + } + + // + // everything else is volatile + // + return (TRUE); +} + +/** + 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; + UINT64 MaxStorSize; + UINT64 RemStorSize; + UINT64 MaxVarSize; + EFI_STATUS Status; + EFI_GUID Guid; + UINTN ValSize; + ENV_VAR_LIST *VarList; + + ASSERT(ListHead != NULL); + + Status = gRT->QueryVariableInfo(EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, &MaxStorSize, &RemStorSize, &MaxVarSize); + ASSERT_EFI_ERROR(Status); + + NameSize = (UINTN)MaxVarSize; + VariableName = AllocatePool(NameSize); + StrCpy(VariableName, L""); + + while (TRUE) { + NameSize = (UINTN)MaxVarSize; + Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid); + if (Status == EFI_NOT_FOUND){ + Status = EFI_SUCCESS; + break; + } + ASSERT_EFI_ERROR(Status); + if (EFI_ERROR(Status)) { + break; + } + if (CompareGuid(&Guid, &gShellVariableGuid)){ + VarList = AllocateZeroPool(sizeof(ENV_VAR_LIST)); + ValSize = 0; + Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(VariableName, &VarList->Atts, &ValSize, VarList->Val); + if (Status == EFI_BUFFER_TOO_SMALL){ + VarList->Val = AllocatePool(ValSize); + ASSERT(VarList->Val != NULL); + Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(VariableName, &VarList->Atts, &ValSize, VarList->Val); + } + ASSERT_EFI_ERROR(Status); + VarList->Key = AllocatePool(StrSize(VariableName)); + ASSERT(VarList->Key != NULL); + StrCpy(VarList->Key, VariableName); + + InsertTailList(ListHead, &VarList->Link); + } // compare guid + } // while + FreePool(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; + UINTN NewSize; + + 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 = AllocatePool(sizeof(ENV_VAR_LIST)); + ASSERT(Node != NULL); + Node->Key = AllocateZeroPool((StrStr(CurrentString, L"=") - CurrentString + 1) * sizeof(CHAR16)); + ASSERT(Node->Key != NULL); + StrnCpy(Node->Key, CurrentString, StrStr(CurrentString, L"=") - CurrentString); + NewSize = StrSize(CurrentString); + NewSize -= StrLen(Node->Key) - 1; + Node->Val = AllocateZeroPool(NewSize); + ASSERT(Node->Val != NULL); + StrCpy(Node->Val, CurrentString + StrLen(Node->Key) + 1); + Node->Atts = EFI_VARIABLE_BOOTSERVICE_ACCESS; + + if (VarList == NULL) { + VarList = AllocateZeroPool(sizeof(ENV_VAR_LIST)); + ASSERT(VarList != NULL); + 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)); +} diff --git a/ShellPkg/Application/Shell/ShellEnvVar.h b/ShellPkg/Application/Shell/ShellEnvVar.h new file mode 100644 index 0000000..2778959 --- /dev/null +++ b/ShellPkg/Application/Shell/ShellEnvVar.h @@ -0,0 +1,210 @@ +/** @file + function definitions for shell environment functions. + + the following includes are required: +//#include +//#include + + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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; + +/** + Reports whether an environment variable is Volatile or Non-Volatile + + This will use the Runtime Services call GetVariable to to search for the variable. + + @param EnvVarName The name of the environment variable in question + + @retval TRUE This environment variable is Volatile + @retval FALSE This environment variable is NON-Volatile +**/ +BOOLEAN +EFIAPI +IsVolatileEnv ( + IN CONST CHAR16 *EnvVarName + ); + +/** + 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 + ); + +#endif //_SHELL_ENVIRONMENT_VARIABLE_HEADER_ + diff --git a/ShellPkg/Application/Shell/ShellManParser.c b/ShellPkg/Application/Shell/ShellManParser.c new file mode 100644 index 0000000..fe9facb --- /dev/null +++ b/ShellPkg/Application/Shell/ShellManParser.c @@ -0,0 +1,615 @@ +/** @file + Provides interface to shell MAN file parser. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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" + +/** + 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; + ASSERT(ManFileName != NULL); + // + // Fix the file name + // + if (StrnCmp(ManFileName+StrLen(ManFileName)-4, L".man", 4)==0) { + Buffer = AllocateZeroPool(StrSize(ManFileName)); + StrCpy(Buffer, ManFileName); + } else { + Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16)); + StrCpy(Buffer, ManFileName); + StrCat(Buffer, L".man"); + } + 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); + SectionName = TempString; + 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; + 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); + // + // copy and save the current line. + // + ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL)); + StrnCatGrow (HelpText, HelpSize, TempString, 0); + StrnCatGrow (HelpText, HelpSize, L"\r\n", 0); + } + } + 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] Handle 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; + + if ( Buffer == NULL + || Command == NULL + || (BriefDesc != NULL && BriefSize == NULL) + ){ + return (EFI_INVALID_PARAMETER); + } + + Status = EFI_SUCCESS; + + TitleString = AllocatePool((7*sizeof(CHAR16)) + StrSize(Command)); + if (TitleString == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + StrCpy(TitleString, L".TH "); + StrCat(TitleString, Command); + StrCat(TitleString, L" 0 "); + + 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"\""); + ASSERT(TitleEnd != NULL); + if (BriefDesc != NULL) { + *BriefSize = StrSize(TitleEnd); + *BriefDesc = AllocateZeroPool(*BriefSize); + if (*BriefDesc == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + StrnCpy(*BriefDesc, 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 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 + @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 sotred in + an alloceted buffer. +**/ +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 *TitleString; + CHAR16 *ReadLine; + UINTN Size; + CHAR16 *TitleEnd; + UINTN TitleLen; + BOOLEAN Found; + + 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); + } + + TitleString = AllocatePool((4*sizeof(CHAR16)) + StrSize(Command)); + if (TitleString == NULL) { + FreePool(ReadLine); + return (EFI_OUT_OF_RESOURCES); + } + StrCpy(TitleString, L".TH "); + StrCat(TitleString, Command); + TitleLen = StrLen(TitleString); + 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; + } + if (StrnCmp(ReadLine, TitleString, TitleLen) == 0) { + Found = TRUE; + // + // we found it so copy out the rest of the line into BriefDesc + // After skipping any spaces or zeroes + // + for ( TitleEnd = ReadLine+TitleLen + ; *TitleEnd == L' ' || *TitleEnd == L'0' || *TitleEnd == L'1' + ; TitleEnd++); + if (BriefDesc != NULL) { + *BriefSize = StrSize(TitleEnd); + *BriefDesc = AllocateZeroPool(*BriefSize); + if (*BriefDesc == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + StrCpy(*BriefDesc, TitleEnd); + } + break; + } + } + FreePool(ReadLine); + FreePool(TitleString); + if (!Found && !EFI_ERROR(Status)) { + return (EFI_NOT_FOUND); + } + 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_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_STATUS Status; + UINTN HelpSize; + UINTN BriefSize; + BOOLEAN Ascii; + CHAR16 *TempString2; + EFI_DEVICE_PATH_PROTOCOL *FileDevPath; + EFI_DEVICE_PATH_PROTOCOL *DevPath; + + if ( ManFileName == NULL + || Command == NULL + || HelpText == NULL + ){ + return (EFI_INVALID_PARAMETER); + } + + HelpSize = 0; + BriefSize = 0; + TempString = 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 { + FileHandle = NULL; + TempString = GetManFileName(ManFileName); + + 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); + FreePool(FileDevPath); + FreePool(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); + } else { + *HelpText = NULL; + } + } + if (TempString != NULL) { + FreePool(TempString); + } + + return (Status); +} diff --git a/ShellPkg/Application/Shell/ShellManParser.h b/ShellPkg/Application/Shell/ShellManParser.h new file mode 100644 index 0000000..3807eec --- /dev/null +++ b/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.
+ 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/ShellPkg/Application/Shell/ShellParametersProtocol.c b/ShellPkg/Application/Shell/ShellParametersProtocol.c new file mode 100644 index 0000000..914853f --- /dev/null +++ b/ShellPkg/Application/Shell/ShellParametersProtocol.c @@ -0,0 +1,949 @@ +/** @file + Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 "ShellParametersProtocol.h" + +/** + return the next parameter from a command line string; + + This function moves the next parameter from Walker into TempParameter and moves + Walker up past that parameter for recursive calling. When the final parameter + is moved *Walker will be set to NULL; + + Temp Parameter must be large enough to hold the parameter before calling this + function. + + @param[in,out] Walker pointer to string of command line. Adjusted to + reminaing command line on return + @param[in,out] TempParameter pointer to string of command line item extracted. + +**/ +VOID +EFIAPI +GetNextParameter( + CHAR16 **Walker, + CHAR16 **TempParameter + ) +{ + CHAR16 *NextDelim; + CHAR16 *TempLoc; + + ASSERT(Walker != NULL); + ASSERT(*Walker != NULL); + ASSERT(TempParameter != NULL); + ASSERT(*TempParameter != NULL); + + // + // make sure we dont have any leading spaces + // + while ((*Walker)[0] == L' ') { + (*Walker)++; + } + + // + // make sure we still have some params now... + // + if (StrLen(*Walker) == 0) { + ASSERT((*Walker)[0] == CHAR_NULL); + *Walker = NULL; + return; + } + + // + // we have a quoted parameter + // could be the last parameter, but SHOULD have a trailing quote + // + if ((*Walker)[0] == L'\"') { + NextDelim = NULL; + for (TempLoc = *Walker + 1 ; TempLoc != NULL && *TempLoc != CHAR_NULL ; TempLoc++) { + if (*TempLoc == L'^' && *(TempLoc+1) == L'^') { + TempLoc++; + } else if (*TempLoc == L'^' && *(TempLoc+1) == L'\"') { + TempLoc++; + } else if (*TempLoc == L'^' && *(TempLoc+1) == L'|') { + TempLoc++; + } else if (*TempLoc == L'^') { + *TempLoc = L' '; + } else if (*TempLoc == L'\"') { + NextDelim = TempLoc; + break; + } + } + + if (NextDelim - ((*Walker)+1) == 0) { + // + // found "" + // + StrCpy(*TempParameter, L""); + *Walker = NextDelim + 1; + } else if (NextDelim != NULL) { + StrnCpy(*TempParameter, (*Walker)+1, NextDelim - ((*Walker)+1)); + *Walker = NextDelim + 1; + } else { + // + // last one... someone forgot the training quote! + // + StrCpy(*TempParameter, *Walker); + *Walker = NULL; + } + for (TempLoc = *TempParameter ; TempLoc != NULL && *TempLoc != CHAR_NULL ; TempLoc++) { + if ((*TempLoc == L'^' && *(TempLoc+1) == L'^') + || (*TempLoc == L'^' && *(TempLoc+1) == L'|') + || (*TempLoc == L'^' && *(TempLoc+1) == L'\"') + ){ + CopyMem(TempLoc, TempLoc+1, StrSize(TempLoc) - sizeof(TempLoc[0])); + } + } + } else { + // + // we have a regular parameter (no quote) OR + // we have the final parameter (no trailing space) + // + NextDelim = StrStr((*Walker), L" "); + if (NextDelim != NULL) { + StrnCpy(*TempParameter, *Walker, NextDelim - (*Walker)); + (*TempParameter)[NextDelim - (*Walker)] = CHAR_NULL; + *Walker = NextDelim+1; + } else { + // + // last one. + // + StrCpy(*TempParameter, *Walker); + *Walker = NULL; + } + for (NextDelim = *TempParameter ; NextDelim != NULL && *NextDelim != CHAR_NULL ; NextDelim++) { + if (*NextDelim == L'^' && *(NextDelim+1) == L'^') { + CopyMem(NextDelim, NextDelim+1, StrSize(NextDelim) - sizeof(NextDelim[0])); + } else if (*NextDelim == L'^') { + *NextDelim = L' '; + } + } + while ((*TempParameter)[StrLen(*TempParameter)-1] == L' ') { + (*TempParameter)[StrLen(*TempParameter)-1] = CHAR_NULL; + } + while ((*TempParameter)[0] == L' ') { + CopyMem(*TempParameter, (*TempParameter)+1, StrSize(*TempParameter) - sizeof((*TempParameter)[0])); + } + } + return; +} + +/** + 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,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 OUT CHAR16 ***Argv, + IN OUT UINTN *Argc + ) +{ + UINTN Count; + CHAR16 *TempParameter; + CHAR16 *Walker; + CHAR16 *NewParam; + UINTN Size; + + ASSERT(Argc != NULL); + ASSERT(Argv != NULL); + + if (CommandLine == NULL || StrLen(CommandLine)==0) { + (*Argc) = 0; + (*Argv) = NULL; + return (EFI_SUCCESS); + } + + Size = StrSize(CommandLine); + TempParameter = AllocateZeroPool(Size); + if (TempParameter == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + for ( Count = 0 + , Walker = (CHAR16*)CommandLine + ; Walker != NULL && *Walker != CHAR_NULL + ; GetNextParameter(&Walker, &TempParameter) + , Count++ + ); + +/* Count = 0; + Walker = (CHAR16*)CommandLine; + while(Walker != NULL) { + GetNextParameter(&Walker, &TempParameter); + Count++; + } +*/ + // + // lets allocate the pointer array + // + (*Argv) = AllocateZeroPool((Count)*sizeof(CHAR16*)); + if (*Argv == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + *Argc = 0; + Walker = (CHAR16*)CommandLine; + while(Walker != NULL && *Walker != CHAR_NULL) { + SetMem16(TempParameter, Size, CHAR_NULL); + GetNextParameter(&Walker, &TempParameter); + NewParam = AllocateZeroPool(StrSize(TempParameter)); + ASSERT(NewParam != NULL); + StrCpy(NewParam, TempParameter); + ((CHAR16**)(*Argv))[(*Argc)] = NewParam; + (*Argc)++; + } + ASSERT(Count >= (*Argc)); + return (EFI_SUCCESS); +} + +/** + 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)); + ASSERT(NewShellParameters != NULL); + + // + // 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 { + ASSERT_EFI_ERROR(Status); + } + if (Size == 0 && LoadedImage->LoadOptionsSize != 0) { + // + // Now we need to include a NULL terminator in the size. + // + Size = LoadedImage->LoadOptionsSize + sizeof(FullCommandLine[0]); + FullCommandLine = AllocateZeroPool(Size); + } + if (LoadedImage->LoadOptionsSize != 0){ + StrCpy(FullCommandLine, LoadedImage->LoadOptions); + } + if (FullCommandLine != NULL) { + // + // Populate Argc and Argv + // + Status = ParseCommandLineToArgs(FullCommandLine, + &(*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); +} + +/** + Funcion will replace the current StdIn and StdOut in the ShellParameters protocol + structure by parsing NewCommandLine. The current values are returned to the + user. + + If OldStdIn or OldStdOut 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[out] OldStdIn Pointer to old StdIn. + @param[out] OldStdOut Pointer to old StdOut. + @param[out] OldStdErr Pointer to old StdErr. + + @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 CONST CHAR16 *NewCommandLine, + OUT SHELL_FILE_HANDLE *OldStdIn, + OUT SHELL_FILE_HANDLE *OldStdOut, + OUT SHELL_FILE_HANDLE *OldStdErr + ) +{ + 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; + CHAR16 TagBuffer[2]; + SPLIT_LIST *Split; + + ASSERT(ShellParameters != NULL); + OutUnicode = TRUE; + InUnicode = TRUE; + ErrUnicode = TRUE; + StdInVarName = NULL; + StdOutVarName = NULL; + StdErrVarName = NULL; + StdErrFileName = NULL; + StdInFileName = NULL; + StdOutFileName = NULL; + ErrAppend = FALSE; + OutAppend = FALSE; + CommandLineCopy = NULL; + + if (OldStdIn != NULL) { + *OldStdIn = ShellParameters->StdIn; + } + if (OldStdOut != NULL) { + *OldStdOut = ShellParameters->StdOut; + } + if (OldStdErr != NULL) { + *OldStdErr = ShellParameters->StdErr; + } + + if (NewCommandLine == NULL) { + return (EFI_SUCCESS); + } + + CommandLineCopy = StrnCatGrow(&CommandLineCopy, NULL, NewCommandLine, 0); + Status = EFI_SUCCESS; + Split = NULL; + + 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) { + StdErrVarName = CommandLineWalker += 6; + ErrAppend = TRUE; + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>>v ")) != NULL) { + StdOutVarName = CommandLineWalker += 6; + OutAppend = TRUE; + } else if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >>v ")) != NULL) { + StdOutVarName = CommandLineWalker += 5; + OutAppend = TRUE; + } else if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >v ")) != NULL) { + StdOutVarName = CommandLineWalker += 4; + OutAppend = FALSE; + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>>a ")) != NULL) { + StdOutFileName = CommandLineWalker += 6; + OutAppend = TRUE; + OutUnicode = FALSE; + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>> ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 5; + OutAppend = TRUE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >> ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 4; + OutAppend = TRUE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >>a ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 5; + OutAppend = TRUE; + OutUnicode = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>a ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 5; + OutAppend = FALSE; + OutUnicode = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" >a ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 4; + OutAppend = FALSE; + OutUnicode = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>> ")) != NULL) { + if (StdErrFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrFileName = CommandLineWalker += 5; + ErrAppend = TRUE; + } + } + + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>v ")) != NULL) { + if (StdErrVarName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrVarName = CommandLineWalker += 5; + ErrAppend = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1>v ")) != NULL) { + if (StdOutVarName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutVarName = CommandLineWalker += 5; + OutAppend = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2>a ")) != NULL) { + if (StdErrFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrFileName = CommandLineWalker += 5; + ErrAppend = FALSE; + ErrUnicode = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 2> ")) != NULL) { + if (StdErrFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdErrFileName = CommandLineWalker += 4; + ErrAppend = FALSE; + } + } + + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" 1> ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 4; + OutAppend = FALSE; + } + } + + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" > ")) != NULL) { + if (StdOutFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdOutFileName = CommandLineWalker += 3; + OutAppend = FALSE; + } + } + + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" < ")) != NULL) { + if (StdInFileName != NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + StdInFileName = CommandLineWalker += 3; + OutAppend = FALSE; + } + } + if (!EFI_ERROR(Status) && (CommandLineWalker = StrStr(CommandLineCopy, L" SplitStdIn != NULL && (StdInVarName != NULL || StdInFileName != NULL)) + ||(Split != NULL && Split->SplitStdOut != NULL && (StdOutVarName != NULL || StdOutFileName != NULL)) + ||(StdErrFileName != NULL && StdErrVarName != NULL) + ||(StdOutFileName != NULL && StdOutVarName != NULL) + ||(StdInFileName != NULL && StdInVarName != NULL) + ||(StdErrVarName != NULL && !IsVolatileEnv(StdErrVarName)) + ||(StdOutVarName != NULL && !IsVolatileEnv(StdOutVarName)) + ){ + Status = EFI_INVALID_PARAMETER; + } else { + // + // Open the Std 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); + ASSERT(TempHandle != NULL); + if (!ErrAppend && ErrUnicode && !EFI_ERROR(Status)) { + // + // Write out the UnicodeFileTag + // + Size = sizeof(CHAR16); + TagBuffer[0] = UnicodeFileTag; + TagBuffer[1] = CHAR_NULL; + ShellInfoObject.NewEfiShellProtocol->WriteFile(TempHandle, &Size, TagBuffer); + } + if (!ErrUnicode && !EFI_ERROR(Status)) { + TempHandle = CreateFileInterfaceFile(TempHandle, FALSE); + ASSERT(TempHandle != NULL); + } + if (!EFI_ERROR(Status)) { + ShellParameters->StdErr = TempHandle; + } + } + + // + // 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); + ASSERT(TempHandle != NULL); + if (!OutAppend && OutUnicode && !EFI_ERROR(Status)) { + // + // Write out the UnicodeFileTag + // + Size = sizeof(CHAR16); + TagBuffer[0] = UnicodeFileTag; + TagBuffer[1] = CHAR_NULL; + ShellInfoObject.NewEfiShellProtocol->WriteFile(TempHandle, &Size, TagBuffer); + } else if (OutAppend) { + // + // Move to end of file + // + Status = ShellInfoObject.NewEfiShellProtocol->GetFileSize(TempHandle, &FileSize); + if (!EFI_ERROR(Status)) { + Status = ShellInfoObject.NewEfiShellProtocol->SetFilePosition(TempHandle, FileSize); + } + } + if (!OutUnicode && !EFI_ERROR(Status)) { + TempHandle = CreateFileInterfaceFile(TempHandle, FALSE); + ASSERT(TempHandle != NULL); + } + if (!EFI_ERROR(Status)) { + ShellParameters->StdOut = TempHandle; + } + } + + // + // 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); + if (!OutUnicode) { + TempHandle = CreateFileInterfaceFile(TempHandle, FALSE); + ASSERT(TempHandle != NULL); + } + ShellParameters->StdOut = TempHandle; + } + + // + // 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); + if (!ErrUnicode) { + TempHandle = CreateFileInterfaceFile(TempHandle, FALSE); + ASSERT(TempHandle != NULL); + } + ShellParameters->StdErr = TempHandle; + } + + // + // StdIn from a var + // + if (!EFI_ERROR(Status) && StdInVarName != NULL) { + TempHandle = CreateFileInterfaceEnv(StdInVarName); + if (!InUnicode) { + TempHandle = CreateFileInterfaceFile(TempHandle, FALSE); + } + Size = 0; + ASSERT(TempHandle != NULL); + if (((EFI_FILE_PROTOCOL*)TempHandle)->Read(TempHandle, &Size, NULL) != EFI_BUFFER_TOO_SMALL) { + Status = EFI_INVALID_PARAMETER; + } else { + ShellParameters->StdIn = TempHandle; + } + } + + // + // StdIn from a file + // + if (!EFI_ERROR(Status) && StdInFileName != NULL) { + Status = ShellOpenFileByName( + StdInFileName, + &TempHandle, + EFI_FILE_MODE_READ, + 0); + if (!InUnicode && !EFI_ERROR(Status)) { + TempHandle = CreateFileInterfaceFile(TempHandle, FALSE); + } + if (!EFI_ERROR(Status)) { + ShellParameters->StdIn = TempHandle; + } + } + } + } + FreePool(CommandLineCopy); + 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[out] OldStdIn Pointer to old StdIn. + @param[out] OldStdOut Pointer to old StdOut. + @param[out] OldStdErr Pointer to old StdErr. +**/ +EFI_STATUS +EFIAPI +RestoreStdInStdOutStdErr ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + OUT SHELL_FILE_HANDLE *OldStdIn OPTIONAL, + OUT SHELL_FILE_HANDLE *OldStdOut OPTIONAL, + OUT SHELL_FILE_HANDLE *OldStdErr OPTIONAL + ) +{ + SPLIT_LIST *Split; + if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) { + Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link); + } else { + Split = NULL; + } + if (OldStdIn != NULL && ShellParameters->StdIn != *OldStdIn) { + if ((Split != NULL && Split->SplitStdIn != ShellParameters->StdIn) || Split == NULL) { + gEfiShellProtocol->CloseFile(ShellParameters->StdIn); + } + ShellParameters->StdIn = OldStdIn==NULL?NULL:*OldStdIn; + } + if (OldStdOut != NULL && ShellParameters->StdOut != *OldStdOut) { + if ((Split != NULL && Split->SplitStdOut != ShellParameters->StdOut) || Split == NULL) { + gEfiShellProtocol->CloseFile(ShellParameters->StdOut); + } + ShellParameters->StdOut = OldStdOut==NULL?NULL:*OldStdOut; + } + 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[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, + OUT CHAR16 ***OldArgv OPTIONAL, + OUT UINTN *OldArgc OPTIONAL + ) +{ + ASSERT(ShellParameters != NULL); + + if (OldArgc != NULL) { + *OldArgc = ShellParameters->Argc; + } + if (OldArgc != NULL) { + *OldArgv = ShellParameters->Argv; + } + + return (ParseCommandLineToArgs(NewCommandLine, &(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/ShellPkg/Application/Shell/ShellParametersProtocol.h b/ShellPkg/Application/Shell/ShellParametersProtocol.h new file mode 100644 index 0000000..3a5fc30 --- /dev/null +++ b/ShellPkg/Application/Shell/ShellParametersProtocol.h @@ -0,0 +1,208 @@ +/** @file + Member functions of EFI_SHELL_PARAMETERS_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PARAMETERS_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ShellEnvVar.h" +#include "FileHandleWrappers.h" +#include "Shell.h" + +/** + 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[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, + 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 + ); + +/** + Funcion will replace the current StdIn and StdOut in the ShellParameters protocol + structure by parsing NewCommandLine. The current values are returned to the + user. + + If OldStdIn or OldStdOut 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[out] OldStdIn Pointer to old StdIn. + @param[out] OldStdOut Pointer to old StdOut. + @param[out] OldStdErr Pointer to old StdErr. + + @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 CONST CHAR16 *NewCommandLine, + OUT SHELL_FILE_HANDLE *OldStdIn OPTIONAL, + OUT SHELL_FILE_HANDLE *OldStdOut OPTIONAL, + OUT SHELL_FILE_HANDLE *OldStdErr OPTIONAL + ); + +/** + 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[out] OldStdIn Pointer to old StdIn. + @param[out] OldStdOut Pointer to old StdOut. + @param[out] OldStdErr Pointer to old StdErr. +**/ +EFI_STATUS +EFIAPI +RestoreStdInStdOutStdErr ( + IN OUT EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters, + OUT SHELL_FILE_HANDLE *OldStdIn OPTIONAL, + OUT SHELL_FILE_HANDLE *OldStdOut OPTIONAL, + OUT SHELL_FILE_HANDLE *OldStdErr OPTIONAL + ); + +/** + 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,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 OUT CHAR16 ***Argv, + IN OUT UINTN *Argc + ); + +/** + return the next parameter from a command line string; + + This function moves the next parameter from Walker into TempParameter and moves + Walker up past that parameter for recursive calling. When the final parameter + is moved *Walker will be set to NULL; + + Temp Parameter must be large enough to hold the parameter before calling this + function. + + @param[in,out] Walker pointer to string of command line. Adjusted to + reminaing command line on return + @param[in,out] TempParameter pointer to string of command line item extracted. + +**/ +VOID +EFIAPI +GetNextParameter( + CHAR16 **Walker, + CHAR16 **TempParameter + ); + +#endif //_SHELL_PARAMETERS_PROTOCOL_PROVIDER_HEADER_ + diff --git a/ShellPkg/Application/Shell/ShellProtocol.c b/ShellPkg/Application/Shell/ShellProtocol.c new file mode 100644 index 0000000..214693f --- /dev/null +++ b/ShellPkg/Application/Shell/ShellProtocol.c @@ -0,0 +1,3122 @@ +/** @file + Member functions of EFI_SHELL_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 + +/** + 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(FileHandle)); +} + +/** + 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_DEVICE_PATH_TO_TEXT_PROTOCOL *DevicePathToText; + EFI_STATUS Status; + CHAR16 *Temp; + + Status = EFI_SUCCESS; + DEBUG_CODE_BEGIN(); + DevicePathToText = NULL; + + Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, + NULL, + (VOID**)&DevicePathToText); + if (Mapping != NULL) { + DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping)); + } + if (!EFI_ERROR(Status)) { + if (DevicePath != NULL) { + Temp = DevicePathToText->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); + 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)) { + 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; + + 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) + ){ + // + // all the rest should be file path nodes + // + if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) || + (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) { + FreePool(PathForReturn); + PathForReturn = NULL; + ASSERT(FALSE); + } else { + // + // append the path part onto the filepath. + // + ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); + PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L"\\", 1); + PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, FilePath->PathName, 0); + } + } // 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 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 + ) +{ + 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; + + MapName = NULL; + ASSERT(Path != NULL); + + if (StrStr(Path, L":") == NULL) { + Cwd = EfiShellGetCurDir(NULL); + if (Cwd == NULL) { + return (NULL); + } + Size = StrSize(Cwd); + Size += StrSize(Path); + NewPath = AllocateZeroPool(Size); + ASSERT(NewPath != NULL); + StrCpy(NewPath, Cwd); + if (NewPath[StrLen(NewPath)-1] == Path[0] == (CHAR16)L'\\') { + ((CHAR16*)NewPath)[StrLen(NewPath)-1] = CHAR_NULL; + } + StrCat(NewPath, 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[StrLen(MapName)-1] != L':') { + ASSERT(FALSE); + 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) { + ASSERT(FALSE); + 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 + // + DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName)+1); + + 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_TO_TEXT_PROTOCOL *DevicePathToText; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_HANDLE *HandleList; + UINTN HandleCount; + UINTN LoopVar; + CHAR16 *DeviceNameToReturn; + CHAR8 *Lang; + CHAR8 *TempChar; + + 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; + } + if (Language == NULL) { + Lang = AllocatePool(AsciiStrSize(CompName2->SupportedLanguages)); + AsciiStrCpy(Lang, CompName2->SupportedLanguages); + TempChar = AsciiStrStr(Lang, ";"); + if (TempChar != NULL){ + *TempChar = CHAR_NULL; + } + } else { + Lang = AllocatePool(AsciiStrSize(Language)); + AsciiStrCpy(Lang, Language); + } + Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn); + FreePool(Lang); + Lang = NULL; + if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) { + break; + } + } + if (HandleList != NULL) { + FreePool(HandleList); + } + if (DeviceNameToReturn != NULL){ + ASSERT(BestDeviceName == NULL); + StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0); + return (EFI_SUCCESS); + } + // + // dont return on fail since we will try device path if that bit is on + // + } + if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) { + Status = gBS->LocateProtocol( + &gEfiDevicePathToTextProtocolGuid, + NULL, + (VOID**)&DevicePathToText); + // + // we now have the device path to text protocol + // + if (!EFI_ERROR(Status)) { + 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 = DevicePathToText->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 +**/ +EFI_STATUS +EFIAPI +EfiShellOpenRoot( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT SHELL_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + // + // 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; + EFI_DEVICE_PATH_PROTOCOL *DpCopy; + + ASSERT(FileHandle != NULL); + *FileHandle = NULL; + Handle1 = NULL; + DpCopy = DevicePath; + + Status = EfiShellOpenRoot(DevicePath, &ShellHandle); + + if (!EFI_ERROR(Status)) { + Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle); + // + // 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) + ){ + // + // 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, + FilePathNode->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, + FilePathNode->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, + FilePathNode->PathName, + OpenMode, + Attributes + ); + } + } + // + // Close the last node + // + Handle2->Close (Handle2); + + // + // If there's been an error, stop + // + if (EFI_ERROR (Status)) { + break; + } + } // for loop + } + } + if (EFI_ERROR(Status)) { + if (Handle1 != NULL) { + Handle1->Close(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; + + // + // Is this for an environment variable + // do we start with >v + // + if (StrStr(FileName, L">v") == FileName) { + if (!IsVolatileEnv(FileName+2)) { + 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); // 0 = no specific file attributes + FreePool(DevicePath); + + return(Status); +} + +/** + 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; + + *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) { + if (!IsVolatileEnv(FileName+2) && + ((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; + + // + // get a handle to the file + // + Status = EfiShellCreateFile(FileName, + 0, + &FileHandle); + if (EFI_ERROR(Status)) { + return (Status); + } + // + // now delete the file + // + 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 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 +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 *StatusCode OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE NewHandle; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + LIST_ENTRY OrigEnvs; + EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol; + + if (ParentImageHandle == NULL) { + return (EFI_INVALID_PARAMETER); + } + + InitializeListHead(&OrigEnvs); + + NewHandle = NULL; + + // + // 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); + } + return (Status); + } + Status = gBS->OpenProtocol( + NewHandle, + &gEfiLoadedImageProtocolGuid, + (VOID**)&LoadedImage, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!EFI_ERROR(Status)) { + ASSERT(LoadedImage->LoadOptionsSize == 0); + if (CommandLine != NULL) { + LoadedImage->LoadOptionsSize = (UINT32)StrSize(CommandLine); + LoadedImage->LoadOptions = (VOID*)CommandLine; + } + + // + // 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, CommandLine, NULL, NULL); + ASSERT_EFI_ERROR(Status); + 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)) { + if (StatusCode != NULL) { + *StatusCode = gBS->StartImage(NewHandle, NULL, NULL); + } else { + Status = gBS->StartImage(NewHandle, NULL, NULL); + } + } + + // + // Cleanup (and dont overwrite errors) + // + if (EFI_ERROR(Status)) { + gBS->UninstallProtocolInterface(NewHandle, &gEfiShellParametersProtocolGuid, &ShellParamsProtocol); + } else { + Status = gBS->UninstallProtocolInterface(NewHandle, &gEfiShellParametersProtocolGuid, &ShellParamsProtocol); + ASSERT_EFI_ERROR(Status); + } + } + + if (!IsListEmpty(&OrigEnvs)) { + if (EFI_ERROR(Status)) { + SetEnvironmentVariableList(&OrigEnvs); + } else { + Status = SetEnvironmentVariableList(&OrigEnvs); + } + } + + return(Status); +} +/** + 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. + @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); + } + + DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); + + DEBUG_CODE_BEGIN(); + Temp = gDevPathToText->ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE); + FreePool(Temp); + Temp = gDevPathToText->ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE); + FreePool(Temp); + Temp = gDevPathToText->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 ", 0); + StrnCatGrow(&Temp, &Size, CommandLine, 0); + + Status = InternalShellExecuteDevicePath( + ParentImageHandle, + DevPath, + Temp, + (CONST CHAR16**)Environment, + StatusCode); + + // + // de-allocate and return + // + FreePool(DevPath); + FreePool(Temp); + 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); + } + 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; + + 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 + ){ + RemoveEntryList(&ShellFileListItem2->Link); + InternalFreeShellFileInfoNode(ShellFileListItem2); + } + } + } + return (EFI_SUCCESS); +} +/** + 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 *NewNode; + + NewNode = AllocatePool(sizeof(EFI_SHELL_FILE_INFO)); + if (NewNode == NULL) { + return (NULL); + } + NewNode->FullName = AllocateZeroPool(StrSize(Node->FullName)); + + NewNode->FileName = AllocateZeroPool(StrSize(Node->FileName)); + NewNode->Info = AllocatePool((UINTN)Node->Info->Size); + if ( NewNode->FullName == NULL + || NewNode->FileName == NULL + || NewNode->Info == NULL + ){ + return(NULL); + } + NewNode->Status = Node->Status; + NewNode->Handle = Node->Handle; + if (!Save) { + Node->Handle = NULL; + } + StrCpy((CHAR16*)NewNode->FullName, Node->FullName); + StrCpy((CHAR16*)NewNode->FileName, Node->FileName); + CopyMem(NewNode->Info, Node->Info, (UINTN)Node->Info->Size); + + return(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] FullName FullName 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 *FullName, + 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) { + 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); + FreePool(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); + } + } + + 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; + + 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); + TempSpot = StrStr(TempString, L";"); + + if (TempSpot != NULL) { + *TempSpot = CHAR_NULL; + } + + TempString = StrnCatGrow(&TempString, &Size, BasePath, 0); + 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) + ){ + TempString = NULL; + Size = 0; + // + // allocate a new EFI_SHELL_FILE_INFO and populate it... + // + ASSERT((TempString == NULL && Size == 0) || (TempString != NULL)); + TempString = StrnCatGrow(&TempString, &Size, BasePath, 0); + TempString = StrnCatGrow(&TempString, &Size, FileInfo->FileName, 0); + ShellFileListItem = CreateAndPopulateShellFileInfo( + BasePath, + EFI_SUCCESS, // success since we didnt fail to open it... + TempString, + FileInfo->FileName, + NULL, // no handle since not open + FileInfo); + + if (ShellFileList == NULL) { + ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); + ASSERT(ShellFileList != NULL); + InitializeListHead(&ShellFileList->Link); + } + InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link); + } + if (EFI_ERROR(Status)) { + EfiShellFreeFileList(&ShellFileList); + *FileList = NULL; + } else { + *FileList = ShellFileList; + } + SHELL_FREE_NON_NULL(BasePath); + return(Status); +} + +/** + 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. + + if FileList is NULL, then ASSERT + if FilePattern is NULL, then ASSERT + if UnicodeCollation is NULL, then ASSERT + if FileHandle is NULL, then ASSERT + + 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. + + @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 + ) +{ + EFI_STATUS Status; + CONST CHAR16 *NextFilePatternStart; + CHAR16 *CurrentFilePattern; + EFI_SHELL_FILE_INFO *ShellInfo; + EFI_SHELL_FILE_INFO *ShellInfoNode; + EFI_SHELL_FILE_INFO *NewShellNode; + BOOLEAN Directory; + + 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)); + ASSERT(CurrentFilePattern != NULL); + StrnCpy(CurrentFilePattern, FilePattern, NextFilePatternStart-FilePattern); + + if (CurrentFilePattern[0] == CHAR_NULL + &&NextFilePatternStart[0] == CHAR_NULL + ){ + // + // Add the current parameter FileHandle to the list, then end... + // + if (ParentNode == NULL) { + Status = EFI_INVALID_PARAMETER; + } else { + NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE); + if (NewShellNode == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } else { + NewShellNode->Handle = NULL; + if (*FileList == NULL) { + *FileList = AllocatePool(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 (Directory){ + // + // should be a directory + // + + // + // don't open the . and .. directories + // + if ( (StrCmp(ShellInfoNode->FileName, L".") != 0) + && (StrCmp(ShellInfoNode->FileName, L"..") != 0) + ){ + // + // + // + ASSERT_EFI_ERROR(Status); + 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); + } + } else { + // + // should be a file + // + + // + // copy the information we need into a new Node + // + NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE); + ASSERT(NewShellNode != NULL); + if (NewShellNode == NULL) { + Status = EFI_OUT_OF_RESOURCES; + } + if (*FileList == NULL) { + *FileList = AllocatePool(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 = AllocatePool(StrSize(FilePattern)); + if (PatternCopy == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + StrCpy(PatternCopy, FilePattern); + + PatternCopy = CleanPath(PatternCopy); + + Count = StrStr(PatternCopy, L":") - PatternCopy; + Count += 2; + + ASSERT(MapName == NULL); + MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count); + + if (!EFI_ERROR(Status)) { + 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); + } + FreePool(RootDevicePath); + } + } + + if (PatternCopy != NULL) { + FreePool(PatternCopy); + } + if (MapName != NULL) { + FreePool(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; + + ShellCommandCleanPath(Path); + + Path2Size = 0; + Path2 = NULL; + + ASSERT(FileList != NULL); + ASSERT(*FileList != NULL); + + if (*Path == L'.' && *(Path+1) == L'\\') { + Path++; + } + + // + // 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); + if (*Path == L'\\') { + Path++; + } + ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL)); + StrnCatGrow(&Path2, &Path2Size, Path, 0); + } else { + ASSERT(Path2 == NULL); + StrnCatGrow(&Path2, NULL, Path, 0); + } + + CleanPath (Path2); + + // + // do the search + // + Status = EfiShellFindFiles(Path2, FileList); + + FreePool(Path2); + + if (EFI_ERROR(Status)) { + return (Status); + } + + // + // 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); + } + } + + return(EFI_SUCCESS); +} + +/** + This function updated with errata. + + 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. + + @return !=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 + ) +{ + EFI_STATUS Status; + VOID *Buffer; + UINTN Size; + LIST_ENTRY List; + ENV_VAR_LIST *Node; + CHAR16 *CurrentWriteLocation; + + Size = 0; + Buffer = NULL; + + if (Name == NULL) { + // + // Get all our environment variables + // + InitializeListHead(&List); + Status = GetEnvironmentVariableList(&List); + if (EFI_ERROR(Status)){ + return (NULL); + } + + // + // Build the semi-colon delimited list. (2 passes) + // + for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List) + ; !IsNull(&List, &Node->Link) + ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link) + ){ + ASSERT(Node->Key != NULL); + Size += StrSize(Node->Key); + } + + Size += 2*sizeof(CHAR16); + + Buffer = AllocateZeroPool(Size); + CurrentWriteLocation = (CHAR16*)Buffer; + + for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List) + ; !IsNull(&List, &Node->Link) + ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link) + ){ + ASSERT(Node->Key != NULL); + StrCpy(CurrentWriteLocation, Node->Key); + CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1; + } + + // + // Free the list... + // + FreeEnvironmentVariableList(&List); + } else { + // + // We are doing a specific environment variable + // + + // + // get the size we need for this EnvVariable + // + Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer); + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate the space and recall the get function + // + Buffer = AllocateZeroPool(Size); + ASSERT(Buffer != NULL); + Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &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); + } + } + + // + // return the buffer + // + return (AddBufferToFreeList(Buffer)); +} + +/** + 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 + ) +{ + if (Value == NULL || StrLen(Value) == 0) { + return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name)); + } else { + SHELL_DELETE_ENVIRONMENT_VARIABLE(Name); + if (Volatile) { + return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value)); + } else { + return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value)); + } + } +} + +/** + 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 + ){ + 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). + + @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 + + @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) { + return (EFI_INVALID_PARAMETER); + } + + if (IsListEmpty(&gShellMapList.Link)){ + return (EFI_NOT_FOUND); + } + + DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0); + ASSERT(DirectoryName != NULL); + + CleanPath(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) { + 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); + } + } 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)); + MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0); + } + } else { + // + // cant have a mapping in the directory... + // + if (StrStr(DirectoryName, L":") != NULL) { + return (EFI_INVALID_PARAMETER); + } + // + // FileSystem != NULL + // + MapListItem = ShellCommandFindMapItem(FileSystem); + if (MapListItem == NULL) { + 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[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') { + ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL)); + MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0); + } + } + } + // + // 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; + + ASSERT(HelpText != NULL); + + ManFileName = ShellCommandGetManFileNameHandler(Command); + + if (ManFileName != NULL) { + return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText)); + } 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( + ) +{ + UINT64 MaxStorSize; + UINT64 RemStorSize; + UINT64 MaxVarSize; + EFI_STATUS Status; + EFI_GUID Guid; + CHAR16 *VariableName; + UINTN NameSize; + CHAR16 *RetVal; + UINTN RetSize; + CHAR16 *Alias; + + Status = gRT->QueryVariableInfo(EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, &MaxStorSize, &RemStorSize, &MaxVarSize); + ASSERT_EFI_ERROR(Status); + + VariableName = AllocateZeroPool((UINTN)MaxVarSize); + RetSize = 0; + RetVal = NULL; + + VariableName[0] = CHAR_NULL; + + while (TRUE) { + NameSize = (UINTN)MaxVarSize; + Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid); + if (Status == EFI_NOT_FOUND){ + break; + } + ASSERT_EFI_ERROR(Status); + if (EFI_ERROR(Status)) { + break; + } + if (CompareGuid(&Guid, &gShellAliasGuid)){ + Alias = GetVariable(VariableName, &gShellAliasGuid); + ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL)); + RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0); + RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0); + } // compare guid + } // while + FreePool(VariableName); + + return (RetVal); +} + +/** + 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; + + if (Alias != NULL) { + if (Volatile == NULL) { + return (AddBufferToFreeList(GetVariable((CHAR16*)Alias, &gShellAliasGuid))); + } + RetSize = 0; + RetVal = NULL; + Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal); + if (Status == EFI_BUFFER_TOO_SMALL) { + RetVal = AllocateZeroPool(RetSize); + Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal); + } + if (EFI_ERROR(Status)) { + if (RetVal != NULL) { + FreePool(RetVal); + } + return (NULL); + } + if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) { + *Volatile = FALSE; + } else { + *Volatile = TRUE; + } + + 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 + ) +{ + // + // We must be trying to remove one if Alias is NULL + // + if (Alias == NULL) { + // + // remove an alias (but passed in COMMAND parameter) + // + return (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); + + return (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command)); + } +} + +/** + 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, + IN BOOLEAN Replace, + IN BOOLEAN Volatile + ) +{ + // + // cant set over a built in alias + // + if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) { + return (EFI_ACCESS_DENIED); + } + if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) { + return (EFI_INVALID_PARAMETER); + } + + if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) { + return (EFI_ACCESS_DENIED); + } + + 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 +}; + +/** + 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; + + BufferSize = 0; + Buffer = NULL; + OldProtocolNode = NULL; + InitializeListHead(&ShellInfoObject.OldShellList.Link); + + ASSERT(NewShell != NULL); + + // + // Initialize EfiShellProtocol object... + // + *NewShell = &mShellProtocol; + Status = gBS->CreateEvent(0, + 0, + NULL, + NULL, + &mShellProtocol.ExecutionBreak); + ASSERT_EFI_ERROR(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); + ASSERT(Buffer != NULL); + Status = gBS->LocateHandle(ByProtocol, + &gEfiShellProtocolGuid, + NULL, + &BufferSize, + Buffer); + ASSERT_EFI_ERROR(Status); + // + // now overwrite each of them, but save the info to restore when we end. + // + for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) { + OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST)); + ASSERT(OldProtocolNode != NULL); + Status = gBS->OpenProtocol(Buffer[HandleCounter], + &gEfiShellProtocolGuid, + (VOID **) &(OldProtocolNode->Interface), + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR(Status)) { + // + // reinstall over the old one... + // + OldProtocolNode->Handle = Buffer[HandleCounter]; + Status = gBS->ReinstallProtocolInterface( + OldProtocolNode->Handle, + &gEfiShellProtocolGuid, + OldProtocolNode->Interface, + (VOID*)(*NewShell)); + 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*)(*NewShell)); + } + + if (PcdGetBool(PcdShellSupportOldProtocols)){ + ///@todo support ShellEnvironment2 + ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also? + } + + 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 +EFIAPI +CleanUpShellProtocol ( + IN OUT EFI_SHELL_PROTOCOL *NewShell + ) +{ + EFI_STATUS Status; + 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); + Status = gBS->ReinstallProtocolInterface(Node2->Handle, + &gEfiShellProtocolGuid, + NewShell, + Node2->Interface); + ASSERT_EFI_ERROR(Status); + FreePool(Node2); + } + } else { + // + // no need to restore + // + Status = gBS->UninstallProtocolInterface(gImageHandle, + &gEfiShellProtocolGuid, + NewShell); + ASSERT_EFI_ERROR(Status); + } + Status = gBS->CloseEvent(NewShell->ExecutionBreak); + + return (Status); +} + + diff --git a/ShellPkg/Application/Shell/ShellProtocol.h b/ShellPkg/Application/Shell/ShellProtocol.h new file mode 100644 index 0000000..d03a382 --- /dev/null +++ b/ShellPkg/Application/Shell/ShellProtocol.h @@ -0,0 +1,950 @@ +/** @file + Member functions of EFI_SHELL_PROTOCOL and functions for creation, + manipulation, and initialization of EFI_SHELL_PROTOCOL. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ 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 +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FileHandleWrappers.h" +#include "ShellEnvVar.h" +#include "ShellManParser.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 +EFIAPI +CleanUpShellProtocol ( + 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 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 +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 *StatusCode 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] FullName FullName 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 *FullName, + 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 + + @return !=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 + ); + +#endif //_SHELL_PROTOCOL_HEADER_ + diff --git a/ShellPkg/Application/ShellLibTestApp/sa3.c b/ShellPkg/Application/ShellLibTestApp/sa3.c index e3813c9..6418a6c 100644 --- a/ShellPkg/Application/ShellLibTestApp/sa3.c +++ b/ShellPkg/Application/ShellLibTestApp/sa3.c @@ -52,7 +52,7 @@ UefiMain ( IN EFI_SYSTEM_TABLE *SystemTable ) { - EFI_FILE_HANDLE FileHandle; + SHELL_FILE_HANDLE FileHandle; EFI_STATUS Status; CHAR16 FileName[100]; UINTN BufferSize; -- cgit v1.1