summaryrefslogtreecommitdiff
path: root/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c')
-rw-r--r--ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c1511
1 files changed, 1511 insertions, 0 deletions
diff --git a/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
new file mode 100644
index 0000000000..5a3739ff91
--- /dev/null
+++ b/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c
@@ -0,0 +1,1511 @@
+/** @file
+ Provides interface to shell internal functions for shell commands.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "UefiShellCommandLib.h"
+
+/// The tag for use in identifying UNICODE files.
+/// If the file is UNICODE, the first 16 bits of the file will equal this value.
+enum {
+ UnicodeFileTag = 0xFEFF
+};
+
+// STATIC local variables
+STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList;
+STATIC SCRIPT_FILE_LIST mScriptList;
+STATIC ALIAS_LIST mAliasList;
+STATIC BOOLEAN mEchoState;
+STATIC BOOLEAN mExitRequested;
+STATIC BOOLEAN mExitScript;
+STATIC CHAR16 *mProfileList;
+STATIC UINTN mProfileListSize;
+STATIC UINTN mFsMaxCount = 0;
+STATIC UINTN mBlkMaxCount = 0;
+STATIC BUFFER_LIST mFileHandleList;
+
+// global variables required by library class.
+EFI_SHELL_PROTOCOL *gEfiShellProtocol = NULL;
+EFI_SHELL_PARAMETERS_PROTOCOL *gEfiShellParametersProtocol = NULL;
+EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
+EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *gDevPathToText = NULL;
+SHELL_MAP_LIST gShellMapList;
+SHELL_MAP_LIST *gShellCurDir = NULL;
+
+CONST CHAR16* SupportLevel[] = {
+ L"Minimal",
+ L"Scripting",
+ L"Basic",
+ L"Interactive"
+};
+
+/**
+ Function to make sure that the global protocol pointers are valid.
+ must be called after constructor before accessing the pointers.
+**/
+EFI_STATUS
+EFIAPI
+CommandInit(
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ if (gEfiShellParametersProtocol == NULL) {
+ Status = gBS->OpenProtocol(gImageHandle,
+ &gEfiShellParametersProtocolGuid,
+ (VOID **)&gEfiShellParametersProtocol,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR(Status)) {
+ return (EFI_DEVICE_ERROR);
+ }
+ }
+ if (gEfiShellProtocol == NULL) {
+ Status = gBS->LocateProtocol(&gEfiShellProtocolGuid, NULL, (VOID**)&gEfiShellProtocol);
+ if (EFI_ERROR(Status)) {
+ return (EFI_DEVICE_ERROR);
+ }
+ }
+ if (gUnicodeCollation == NULL) {
+ Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
+ if (EFI_ERROR(Status)) {
+ return (EFI_DEVICE_ERROR);
+ }
+ }
+ if (gDevPathToText == NULL) {
+ Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID**)&gDevPathToText);
+ if (EFI_ERROR(Status)) {
+ return (EFI_DEVICE_ERROR);
+ }
+ }
+ return (EFI_SUCCESS);
+}
+
+/**
+ Constructor for the Shell Command library.
+
+ Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
+
+ @param ImageHandle the image handle of the process
+ @param SystemTable the EFI System Table pointer
+
+ @retval EFI_SUCCESS the initialization was complete sucessfully
+**/
+RETURN_STATUS
+EFIAPI
+ShellCommandLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ InitializeListHead(&gShellMapList.Link);
+ InitializeListHead(&mCommandList.Link);
+ InitializeListHead(&mAliasList.Link);
+ InitializeListHead(&mScriptList.Link);
+ InitializeListHead(&mFileHandleList.Link);
+ mEchoState = TRUE;
+
+ mExitRequested = FALSE;
+ mExitScript = FALSE;
+ mProfileListSize = 0;
+ mProfileList = NULL;
+
+ if (gUnicodeCollation == NULL) {
+ Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
+ if (EFI_ERROR(Status)) {
+ return (EFI_DEVICE_ERROR);
+ }
+ }
+
+ return (RETURN_SUCCESS);
+}
+
+/**
+ Destructor for the library. free any resources.
+
+ @param ImageHandle the image handle of the process
+ @param SystemTable the EFI System Table pointer
+
+ @retval RETURN_SUCCESS this function always returns success
+**/
+RETURN_STATUS
+EFIAPI
+ShellCommandLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
+ COMMAND_LIST *Node2;
+ SCRIPT_FILE_LIST *Node3;
+ SHELL_MAP_LIST *MapNode;
+ //
+ // enumerate throught the list and free all the memory
+ //
+ while (!IsListEmpty (&mCommandList.Link)) {
+ Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link);
+ RemoveEntryList(&Node->Link);
+ SHELL_FREE_NON_NULL(Node->CommandString);
+ FreePool(Node);
+ DEBUG_CODE(Node = NULL;);
+ }
+
+ //
+ // enumerate through the init command list and free all memory
+ //
+ while (!IsListEmpty (&mAliasList.Link)) {
+ Node2 = (COMMAND_LIST *)GetFirstNode(&mAliasList.Link);
+ RemoveEntryList(&Node2->Link);
+ SHELL_FREE_NON_NULL(Node2->CommandString);
+ FreePool(Node2);
+ DEBUG_CODE(Node2 = NULL;);
+ }
+
+ //
+ // enumerate throught the list and free all the memory
+ //
+ while (!IsListEmpty (&mScriptList.Link)) {
+ Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
+ RemoveEntryList(&Node3->Link);
+ DeleteScriptFileStruct(Node3->Data);
+ FreePool(Node3);
+ }
+
+ //
+ // enumerate throught the mappings list and free all the memory
+ //
+ if (!IsListEmpty(&gShellMapList.Link)) {
+ for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsListEmpty (&gShellMapList.Link)
+ ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ){
+ ASSERT(MapNode != NULL);
+ RemoveEntryList(&MapNode->Link);
+ SHELL_FREE_NON_NULL(MapNode->DevicePath);
+ SHELL_FREE_NON_NULL(MapNode->MapName);
+ SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath);
+ FreePool(MapNode);
+ }
+ }
+ if (!IsListEmpty(&mFileHandleList.Link)){
+ FreeBufferList(&mFileHandleList);
+ }
+
+ if (mProfileList != NULL) {
+ FreePool(mProfileList);
+ }
+
+ return (RETURN_SUCCESS);
+}
+
+/**
+ Checks if a command is already on the list.
+
+ @param[in] CommandString The command string to check for on the list.
+**/
+BOOLEAN
+EFIAPI
+ShellCommandIsCommandOnList (
+ IN CONST CHAR16 *CommandString
+ )
+{
+ SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
+
+ //
+ // assert for NULL parameter
+ //
+ ASSERT(CommandString != NULL);
+
+ //
+ // check for the command
+ //
+ for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
+ ; !IsNull(&mCommandList.Link, &Node->Link)
+ ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
+ ){
+ ASSERT(Node->CommandString != NULL);
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)CommandString,
+ Node->CommandString) == 0
+ ){
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/**
+ Get the help text for a command.
+
+ @param[in] CommandString The command name.
+
+ @retval NULL No help text was found.
+ @return String of help text. Caller reuiqred to free.
+**/
+CHAR16*
+EFIAPI
+ShellCommandGetCommandHelp (
+ IN CONST CHAR16 *CommandString
+ )
+{
+ SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
+
+ //
+ // assert for NULL parameter
+ //
+ ASSERT(CommandString != NULL);
+
+ //
+ // check for the command
+ //
+ for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
+ ; !IsNull(&mCommandList.Link, &Node->Link)
+ ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
+ ){
+ ASSERT(Node->CommandString != NULL);
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)CommandString,
+ Node->CommandString) == 0
+ ){
+ return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL));
+ }
+ }
+ return (NULL);
+}
+
+/**
+ Registers handlers of type SHELL_RUN_COMMAND and
+ SHELL_GET_MAN_FILENAME for each shell command.
+
+ If the ShellSupportLevel is greater than the value of the
+ PcdShellSupportLevel then return RETURN_UNSUPPORTED.
+
+ Registers the handlers specified by GetHelpInfoHandler and CommandHandler
+ with the command specified by CommandString. If the command named by
+ CommandString has already been registered, then return
+ RETURN_ALREADY_STARTED.
+
+ If there are not enough resources available to register the handlers then
+ RETURN_OUT_OF_RESOURCES is returned.
+
+ If CommandString is NULL, then ASSERT().
+ If GetHelpInfoHandler is NULL, then ASSERT().
+ If CommandHandler is NULL, then ASSERT().
+ If ProfileName is NULL, then ASSERT().
+
+ @param[in] CommandString Pointer to the command name. This is the
+ name to look for on the command line in
+ the shell.
+ @param[in] CommandHandler Pointer to a function that runs the
+ specified command.
+ @param[in] GetManFileName Pointer to a function that provides man
+ filename.
+ @param[in] ShellMinSupportLevel minimum Shell Support Level which has this
+ function.
+ @param[in] ProfileName profile name to require for support of this
+ function.
+ @param[in] CanAffectLE indicates whether this command's return value
+ can change the LASTERROR environment variable.
+ @param[in] HiiHandle Handle of this command's HII entry.
+ @param[in] ManFormatHelp HII locator for the help text.
+
+ @retval RETURN_SUCCESS The handlers were registered.
+ @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
+ register the shell command.
+ @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the
+ currently allowed support level.
+ @retval RETURN_ALREADY_STARTED The CommandString represents a command that
+ is already registered. Only 1 handler set for
+ a given command is allowed.
+ @sa SHELL_GET_MAN_FILENAME
+ @sa SHELL_RUN_COMMAND
+**/
+RETURN_STATUS
+EFIAPI
+ShellCommandRegisterCommandName (
+ IN CONST CHAR16 *CommandString,
+ IN SHELL_RUN_COMMAND CommandHandler,
+ IN SHELL_GET_MAN_FILENAME GetManFileName,
+ IN UINT32 ShellMinSupportLevel,
+ IN CONST CHAR16 *ProfileName,
+ IN CONST BOOLEAN CanAffectLE,
+ IN CONST EFI_HANDLE HiiHandle,
+ IN CONST EFI_STRING_ID ManFormatHelp
+ )
+{
+ SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
+
+ //
+ // ASSERTs for NULL parameters
+ //
+ ASSERT(CommandString != NULL);
+ ASSERT(GetManFileName != NULL);
+ ASSERT(CommandHandler != NULL);
+ ASSERT(ProfileName != NULL);
+
+ //
+ // check for shell support level
+ //
+ if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) {
+ return (RETURN_UNSUPPORTED);
+ }
+
+ //
+ // check for already on the list
+ //
+ if (ShellCommandIsCommandOnList(CommandString)) {
+ return (RETURN_ALREADY_STARTED);
+ }
+
+ //
+ // allocate memory for new struct
+ //
+ Node = AllocatePool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));
+ ASSERT(Node != NULL);
+ Node->CommandString = AllocatePool(StrSize(CommandString));
+ ASSERT(Node->CommandString != NULL);
+
+ //
+ // populate the new struct
+ //
+ StrCpy(Node->CommandString, CommandString);
+
+ Node->GetManFileName = GetManFileName;
+ Node->CommandHandler = CommandHandler;
+ Node->LastError = CanAffectLE;
+ Node->HiiHandle = HiiHandle;
+ Node->ManFormatHelp = ManFormatHelp;
+
+ if ( StrLen(ProfileName)>0
+ && ((mProfileList != NULL
+ && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL)
+ ){
+ ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
+ if (mProfileList == NULL) {
+ //
+ // If this is the first make a leading ';'
+ //
+ StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
+ }
+ StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0);
+ StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
+ }
+
+ //
+ // add the new struct to the list
+ //
+ InsertTailList (&mCommandList.Link, &Node->Link);
+
+ return (RETURN_SUCCESS);
+}
+
+/**
+ Function to get the current Profile string.
+
+ @retval NULL There are no installed profiles.
+ @return A semi-colon delimited list of profiles.
+**/
+CONST CHAR16 *
+EFIAPI
+ShellCommandGetProfileList (
+ VOID
+ )
+{
+ return (mProfileList);
+}
+
+/**
+ Checks if a command string has been registered for CommandString and if so it runs
+ the previously registered handler for that command with the command line.
+
+ If CommandString is NULL, then ASSERT().
+
+ If Sections is specified, then each section name listed will be compared in a casesensitive
+ manner, to the section names described in Appendix B UEFI Shell 2.0 spec. 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 Sections pointer to string representing which section to get help on.
+
+ @param[in] CommandString Pointer to the command name. This is the name
+ found on the command line in the shell.
+ @param[in,out] RetVal Pointer to the return vaule from the command handler.
+
+ @param[in,out] CanAffectLE indicates whether this command's return value
+ needs to be placed into LASTERROR environment variable.
+
+ @retval RETURN_SUCCESS The handler was run.
+ @retval RETURN_NOT_FOUND The CommandString did not match a registered
+ command name.
+ @sa SHELL_RUN_COMMAND
+**/
+RETURN_STATUS
+EFIAPI
+ShellCommandRunCommandHandler (
+ IN CONST CHAR16 *CommandString,
+ IN OUT SHELL_STATUS *RetVal,
+ IN OUT BOOLEAN *CanAffectLE OPTIONAL
+ )
+{
+ SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
+
+ //
+ // assert for NULL parameters
+ //
+ ASSERT(CommandString != NULL);
+
+ //
+ // check for the command
+ //
+ for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
+ ; !IsNull(&mCommandList.Link, &Node->Link)
+ ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
+ ){
+ ASSERT(Node->CommandString != NULL);
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)CommandString,
+ Node->CommandString) == 0
+ ){
+ if (CanAffectLE != NULL) {
+ *CanAffectLE = Node->LastError;
+ }
+ if (RetVal != NULL) {
+ *RetVal = Node->CommandHandler(NULL, gST);
+ } else {
+ Node->CommandHandler(NULL, gST);
+ }
+ return (RETURN_SUCCESS);
+ }
+ }
+ return (RETURN_NOT_FOUND);
+}
+
+/**
+ Checks if a command string has been registered for CommandString and if so it
+ returns the MAN filename specified for that command.
+
+ If CommandString is NULL, then ASSERT().
+
+ @param[in] CommandString Pointer to the command name. This is the name
+ found on the command line in the shell.\
+
+ @retval NULL the commandString was not a registered command.
+ @return other the name of the MAN file.
+ @sa SHELL_GET_MAN_FILENAME
+**/
+CONST CHAR16*
+EFIAPI
+ShellCommandGetManFileNameHandler (
+ IN CONST CHAR16 *CommandString
+ )
+{
+ SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
+
+ //
+ // assert for NULL parameters
+ //
+ ASSERT(CommandString != NULL);
+
+ //
+ // check for the command
+ //
+ for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
+ ; !IsNull(&mCommandList.Link, &Node->Link)
+ ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
+ ){
+ ASSERT(Node->CommandString != NULL);
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)CommandString,
+ Node->CommandString) == 0
+ ){
+ return (Node->GetManFileName());
+ }
+ }
+ return (NULL);
+}
+
+/**
+ Get the list of all available shell internal commands. This is a linked list
+ (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
+ list functions. do not modify the values.
+
+ @return a Linked list of all available shell commands.
+**/
+CONST COMMAND_LIST*
+EFIAPI
+ShellCommandGetCommandList (
+ )
+{
+ return ((COMMAND_LIST*)(&mCommandList));
+}
+
+/**
+ Registers aliases to be set as part of the initialization of the shell application.
+
+ If Command is NULL, then ASSERT().
+ If Alias is NULL, then ASSERT().
+
+ @param[in] Command Pointer to the Command
+ @param[in] Alias Pointer to Alias
+
+ @retval RETURN_SUCCESS The handlers were registered.
+ @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
+ register the shell command.
+**/
+RETURN_STATUS
+EFIAPI
+ShellCommandRegisterAlias (
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias
+ )
+{
+ ALIAS_LIST *Node;
+
+ //
+ // Asserts for NULL
+ //
+ ASSERT(Command != NULL);
+ ASSERT(Alias != NULL);
+
+ //
+ // allocate memory for new struct
+ //
+ Node = AllocatePool(sizeof(ALIAS_LIST));
+ ASSERT(Node != NULL);
+ Node->CommandString = AllocatePool(StrSize(Command));
+ Node->Alias = AllocatePool(StrSize(Alias));
+ ASSERT(Node->CommandString != NULL);
+ ASSERT(Node->Alias != NULL);
+
+ //
+ // populate the new struct
+ //
+ StrCpy(Node->CommandString, Command);
+ StrCpy(Node->Alias , Alias );
+
+ //
+ // add the new struct to the list
+ //
+ InsertTailList (&mAliasList.Link, &Node->Link);
+
+ return (RETURN_SUCCESS);
+}
+
+/**
+ Get the list of all shell alias commands. This is a linked list
+ (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
+ list functions. do not modify the values.
+
+ @return a Linked list of all requested shell alias'.
+**/
+CONST ALIAS_LIST*
+EFIAPI
+ShellCommandGetInitAliasList (
+ VOID
+ )
+{
+ return (&mAliasList);
+}
+
+/**
+ Determine if a given alias is on the list of built in alias'
+
+ @param[in] Alias The alias to test for
+
+ @retval TRUE The alias is a built in alias
+ @retval FALSE The alias is not a built in alias
+**/
+BOOLEAN
+EFIAPI
+ShellCommandIsOnAliasList(
+ IN CONST CHAR16 *Alias
+ )
+{
+ ALIAS_LIST *Node;
+
+ //
+ // assert for NULL parameter
+ //
+ ASSERT(Alias != NULL);
+
+ //
+ // check for the Alias
+ //
+ for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link)
+ ; !IsNull(&mAliasList.Link, &Node->Link)
+ ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link)
+ ){
+ ASSERT(Node->CommandString != NULL);
+ ASSERT(Node->Alias != NULL);
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Alias,
+ Node->CommandString) == 0
+ ){
+ return (TRUE);
+ }
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Alias,
+ Node->Alias) == 0
+ ){
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/**
+ Function to determine current state of ECHO. Echo determins if lines from scripts
+ and ECHO commands are enabled.
+
+ @retval TRUE Echo is currently enabled
+ @retval FALSE Echo is currently disabled
+**/
+BOOLEAN
+EFIAPI
+ShellCommandGetEchoState(
+ VOID
+ )
+{
+ return (mEchoState);
+}
+
+/**
+ Function to set current state of ECHO. Echo determins if lines from scripts
+ and ECHO commands are enabled.
+
+ If State is TRUE, Echo will be enabled.
+ If State is FALSE, Echo will be disabled.
+**/
+VOID
+EFIAPI
+ShellCommandSetEchoState(
+ IN BOOLEAN State
+ )
+{
+ mEchoState = State;
+}
+
+/**
+ Indicate that the current shell or script should exit.
+
+ @param[in] ScriptOnly TRUE if only exiting a script, FALSE othrwise.
+**/
+VOID
+EFIAPI
+ShellCommandRegisterExit (
+ IN BOOLEAN ScriptOnly
+ )
+{
+ mExitRequested = (BOOLEAN)(!mExitRequested);
+ if (mExitRequested) {
+ mExitScript = ScriptOnly;
+ } else {
+ mExitScript = FALSE;
+ }
+}
+
+/**
+ Retrieve the Exit indicator.
+
+ @retval TRUE Exit was indicated.
+ @retval FALSE Exis was not indicated.
+**/
+BOOLEAN
+EFIAPI
+ShellCommandGetExit (
+ VOID
+ )
+{
+ return (mExitRequested);
+}
+
+/**
+ Retrieve the Exit script indicator.
+
+ If ShellCommandGetExit returns FALSE than the return from this is undefined.
+
+ @retval TRUE ScriptOnly was indicated.
+ @retval FALSE ScriptOnly was not indicated.
+**/
+BOOLEAN
+EFIAPI
+ShellCommandGetScriptExit (
+ VOID
+ )
+{
+ return (mExitScript);
+}
+
+/**
+ Function to cleanup all memory from a SCRIPT_FILE structure.
+
+ @param[in] Script The pointer to the structure to cleanup.
+**/
+VOID
+EFIAPI
+DeleteScriptFileStruct (
+ IN SCRIPT_FILE *Script
+ )
+{
+ UINT8 LoopVar;
+ ASSERT(Script != NULL);
+ ASSERT(Script->ScriptName != NULL);
+ for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) {
+ FreePool(Script->Argv[LoopVar]);
+ }
+ if (Script->Argv != NULL) {
+ FreePool(Script->Argv);
+ }
+ Script->CurrentCommand = NULL;
+ while (!IsListEmpty (&Script->CommandList)) {
+ Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList);
+ if (Script->CurrentCommand != NULL) {
+ RemoveEntryList(&Script->CurrentCommand->Link);
+ if (Script->CurrentCommand->Cl != NULL) {
+ FreePool(Script->CurrentCommand->Cl);
+ }
+ if (Script->CurrentCommand->Data != NULL) {
+ FreePool(Script->CurrentCommand->Data);
+ }
+ FreePool(Script->CurrentCommand);
+ }
+ }
+ FreePool(Script->ScriptName);
+ FreePool(Script);
+}
+
+/**
+ Function to return a pointer to the currently running script file object.
+
+ @retval NULL A script file is not currently running.
+ @return A pointer to the current script file object.
+**/
+SCRIPT_FILE*
+EFIAPI
+ShellCommandGetCurrentScriptFile (
+ VOID
+ )
+{
+ SCRIPT_FILE_LIST *List;
+ if (IsListEmpty (&mScriptList.Link)) {
+ return (NULL);
+ }
+ List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link));
+ return (List->Data);
+}
+
+/**
+ Function to set a new script as the currently running one.
+
+ This function will correctly stack and unstack nested scripts.
+
+ @param[in] Script Pointer to new script information structure. if NULL
+ will remove and de-allocate the top-most Script structure.
+
+ @return A pointer to the current running script file after this
+ change. NULL if removing the final script.
+**/
+SCRIPT_FILE*
+EFIAPI
+ShellCommandSetNewScript (
+ IN SCRIPT_FILE *Script OPTIONAL
+ )
+{
+ SCRIPT_FILE_LIST *Node;
+ if (Script == NULL) {
+ if (IsListEmpty (&mScriptList.Link)) {
+ ASSERT(FALSE);
+ return (NULL);
+ }
+ Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
+ RemoveEntryList(&Node->Link);
+ DeleteScriptFileStruct(Node->Data);
+ FreePool(Node);
+ } else {
+ Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST));
+ Node->Data = Script;
+ InsertHeadList(&mScriptList.Link, &Node->Link);
+ }
+ return (ShellCommandGetCurrentScriptFile());
+}
+
+/**
+ Function to generate the next default mapping name.
+
+ If the return value is not NULL then it must be callee freed.
+
+ @param Type What kind of mapping name to make.
+
+ @retval NULL a memory allocation failed.
+ @return a new map name string
+**/
+CHAR16*
+EFIAPI
+ShellCommandCreateNewMappingName(
+ IN CONST SHELL_MAPPING_TYPE Type
+ )
+{
+ CHAR16 *String;
+ ASSERT(Type < MappingTypeMax);
+
+ String = NULL;
+
+ String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0]));
+ UnicodeSPrint(
+ String,
+ PcdGet8(PcdShellMapNameLength) * sizeof(String[0]),
+ Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:",
+ Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++);
+
+ return (String);
+}
+
+/**
+ Function to add a map node to the list of map items and update the "path" environment variable (optionally).
+
+ If Path is TRUE (during initialization only), the path environment variable will also be updated to include
+ default paths on the new map name...
+
+ Path should be FALSE when this function is called from the protocol SetMap function.
+
+ @param[in] Name The human readable mapped name.
+ @param[in] DevicePath The Device Path for this map.
+ @param[in] Flags The Flags attribute for this map item.
+ @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
+
+ @retval EFI_SUCCESS The addition was sucessful.
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
+ @retval EFI_INVALID_PARAMETER A parameter was invalid.
+**/
+EFI_STATUS
+EFIAPI
+ShellCommandAddMapItemAndUpdatePath(
+ IN CONST CHAR16 *Name,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CONST UINT64 Flags,
+ IN CONST BOOLEAN Path
+ )
+{
+ EFI_STATUS Status;
+ SHELL_MAP_LIST *MapListNode;
+ CONST CHAR16 *OriginalPath;
+ CHAR16 *NewPath;
+ UINTN NewPathSize;
+
+ NewPathSize = 0;
+ NewPath = NULL;
+ OriginalPath = NULL;
+ Status = EFI_SUCCESS;
+
+ MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST));
+ if (MapListNode == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ MapListNode->Flags = Flags;
+ MapListNode->MapName = AllocateZeroPool(StrSize(Name));
+ MapListNode->DevicePath = DuplicateDevicePath(DevicePath);
+ if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ StrCpy(MapListNode->MapName, Name);
+ InsertTailList(&gShellMapList.Link, &MapListNode->Link);
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ if (MapListNode != NULL) {
+ if (MapListNode->DevicePath != NULL) {
+ FreePool(MapListNode->DevicePath);
+ }
+ if (MapListNode->MapName != NULL) {
+ FreePool(MapListNode->MapName);
+ }
+ FreePool(MapListNode);
+ }
+ } else if (Path) {
+ //
+ // Since there was no error and Path was TRUE
+ // Now add the correct path for that mapping
+ //
+ OriginalPath = gEfiShellProtocol->GetEnv(L"path");
+ ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
+ if (OriginalPath != NULL) {
+ StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0);
+ } else {
+ StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0);
+ }
+ StrnCatGrow(&NewPath, &NewPathSize, L";", 0);
+ StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
+ StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
+ StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
+ StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
+ StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
+ StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0);
+
+ Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ FreePool(NewPath);
+ }
+ return (Status);
+}
+
+/**
+ Creates the default map names for each device path in the system with
+ a protocol depending on the Type.
+
+ Creates the consistent map names for each device path in the system with
+ a protocol depending on the Type.
+
+ Note: This will reset all mappings in the system("map -r").
+
+ Also sets up the default path environment variable if Type is FileSystem.
+
+ @retval EFI_SUCCESS All map names were created sucessfully.
+ @retval EFI_NOT_FOUND No protocols were found in the system.
+ @return Error returned from gBS->LocateHandle().
+
+ @sa LocateHandle
+**/
+EFI_STATUS
+EFIAPI
+ShellCommandCreateInitialMappingsAndPaths(
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleList;
+ UINTN Count;
+ EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
+ CHAR16 *NewDefaultName;
+ CHAR16 *NewConsistName;
+ EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
+ SHELL_MAP_LIST *MapListNode;
+
+ HandleList = NULL;
+
+ //
+ // Reset the static members back to zero
+ //
+ mFsMaxCount = 0;
+ mBlkMaxCount = 0;
+
+ gEfiShellProtocol->SetEnv(L"path", L"", TRUE);
+
+ //
+ // First empty out the existing list.
+ //
+ if (!IsListEmpty(&gShellMapList.Link)) {
+ for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsListEmpty(&gShellMapList.Link)
+ ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ){
+ RemoveEntryList(&MapListNode->Link);
+ FreePool(MapListNode);
+ } // for loop
+ }
+
+ //
+ // Find each handle with Simple File System
+ //
+ HandleList = GetHandleListByPotocol(&gEfiSimpleFileSystemProtocolGuid);
+ if (HandleList != NULL) {
+ //
+ // Do a count of the handles
+ //
+ for (Count = 0 ; HandleList[Count] != NULL ; Count++);
+
+ //
+ // Get all Device Paths
+ //
+ DevicePathList = AllocatePool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
+ ASSERT(DevicePathList != NULL);
+
+ for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
+ DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
+ }
+
+ //
+ // Sort all DevicePaths
+ //
+ PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
+
+ ShellCommandConsistMappingInitialize(&ConsistMappingTable);
+ //
+ // Assign new Mappings to all...
+ //
+ for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
+ //
+ // Get default name first
+ //
+ NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
+ ASSERT(NewDefaultName != NULL);
+ Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE);
+ ASSERT_EFI_ERROR(Status);
+ FreePool(NewDefaultName);
+
+ //
+ // Now do consistent name
+ //
+ NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
+ if (NewConsistName != NULL) {
+ Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ FreePool(NewConsistName);
+ }
+ }
+
+ ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
+
+ SHELL_FREE_NON_NULL(HandleList);
+ SHELL_FREE_NON_NULL(DevicePathList);
+
+ HandleList = NULL;
+ } else {
+ Count = (UINTN)-1;
+ }
+
+ //
+ // Find each handle with Block Io
+ //
+ HandleList = GetHandleListByPotocol(&gEfiBlockIoProtocolGuid);
+ if (HandleList != NULL) {
+ for (Count = 0 ; HandleList[Count] != NULL ; Count++);
+
+ //
+ // Get all Device Paths
+ //
+ DevicePathList = AllocatePool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
+ ASSERT(DevicePathList != NULL);
+
+ for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
+ DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
+ }
+
+ //
+ // Sort all DevicePaths
+ //
+ PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
+
+ //
+ // Assign new Mappings to all...
+ //
+ for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
+ //
+ // Get default name first
+ //
+ NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo);
+ ASSERT(NewDefaultName != NULL);
+ Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE);
+ ASSERT_EFI_ERROR(Status);
+ FreePool(NewDefaultName);
+ }
+
+ SHELL_FREE_NON_NULL(HandleList);
+ SHELL_FREE_NON_NULL(DevicePathList);
+ } else if (Count == (UINTN)-1) {
+ return (EFI_NOT_FOUND);
+ }
+
+ return (EFI_SUCCESS);
+}
+
+CHAR16*
+EFIAPI
+ShellCommandCleanPath (
+ IN OUT CHAR16 *Path
+ )
+{
+ CHAR16 *Path2;
+
+ for (Path2 = Path ; Path2 != NULL && *Path2 != CHAR_NULL ; Path2++) {
+ if (*Path2 == L'/') {
+ *Path2 = L'\\';
+ }
+ }
+
+ return (Path);
+}
+
+/**
+ Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
+
+ @param[in] Handle The SHELL_FILE_HANDLE to convert.
+
+ @return a EFI_FILE_PROTOCOL* representing the same file.
+**/
+EFI_FILE_PROTOCOL*
+EFIAPI
+ConvertShellHandleToEfiFileProtocol(
+ IN CONST SHELL_FILE_HANDLE Handle
+ )
+{
+ return ((EFI_FILE_PROTOCOL*)(Handle));
+}
+
+/**
+ Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
+
+ @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert.
+ @param[in] Path The path to the file for verification.
+
+ @return a SHELL_FILE_HANDLE representing the same file.
+**/
+SHELL_FILE_HANDLE
+EFIAPI
+ConvertEfiFileProtocolToShellHandle(
+ IN CONST EFI_FILE_PROTOCOL *Handle,
+ IN CONST CHAR16 *Path
+ )
+{
+ SHELL_COMMAND_FILE_HANDLE *Buffer;
+ BUFFER_LIST *NewNode;
+
+ if (Path != NULL) {
+ Buffer = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE));
+ ASSERT(Buffer != NULL);
+ NewNode = AllocatePool(sizeof(BUFFER_LIST));
+ ASSERT(NewNode != NULL);
+ Buffer->FileHandle = (EFI_FILE_PROTOCOL*)Handle;
+ Buffer->Path = StrnCatGrow(&Buffer->Path, NULL, Path, 0);
+ NewNode->Buffer = Buffer;
+
+ InsertHeadList(&mFileHandleList.Link, &NewNode->Link);
+ }
+ return ((SHELL_FILE_HANDLE)(Handle));
+}
+
+/**
+ Find the path that was logged with the specified SHELL_FILE_HANDLE.
+
+ @param[in] Handle The SHELL_FILE_HANDLE to query on.
+
+ @return A pointer to the path for the file.
+**/
+CONST CHAR16*
+EFIAPI
+ShellFileHandleGetPath(
+ IN CONST SHELL_FILE_HANDLE Handle
+ )
+{
+ BUFFER_LIST *Node;
+
+ for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
+ ; !IsNull(&mFileHandleList.Link, &Node->Link)
+ ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
+ ){
+ if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
+ return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
+ }
+ }
+ return (NULL);
+}
+
+/**
+ Remove a SHELL_FILE_HANDLE frmo the list of SHELL_FILE_HANDLES.
+
+ @param[in] Handle The SHELL_FILE_HANDLE to remove.
+
+ @retval TRUE The item was removed.
+ @retval FALSE The item was not found.
+**/
+BOOLEAN
+EFIAPI
+ShellFileHandleRemove(
+ IN CONST SHELL_FILE_HANDLE Handle
+ )
+{
+ BUFFER_LIST *Node;
+
+ for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
+ ; !IsNull(&mFileHandleList.Link, &Node->Link)
+ ; Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
+ ){
+ if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
+ SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
+ RemoveEntryList(&Node->Link);
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/**
+ Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
+
+ This will NOT work on directories.
+
+ If Handle is NULL, then ASSERT.
+
+ @param[in] Handle the file handle
+
+ @retval TRUE the position is at the end of the file
+ @retval FALSE the position is not at the end of the file
+**/
+BOOLEAN
+EFIAPI
+ShellFileHandleEof(
+ IN SHELL_FILE_HANDLE Handle
+ )
+{
+ EFI_FILE_INFO *Info;
+ UINT64 Pos;
+ BOOLEAN RetVal;
+
+ //
+ // ASSERT if Handle is NULL
+ //
+ ASSERT(Handle != NULL);
+
+ gEfiShellProtocol->GetFilePosition(Handle, &Pos);
+ Info = gEfiShellProtocol->GetFileInfo (Handle);
+ ASSERT(Info != NULL);
+ gEfiShellProtocol->SetFilePosition(Handle, Pos);
+
+ if (Info == NULL) {
+ return (FALSE);
+ }
+
+ if (Pos == Info->FileSize) {
+ RetVal = TRUE;
+ } else {
+ RetVal = FALSE;
+ }
+
+ FreePool (Info);
+
+ return (RetVal);
+}
+
+/**
+ Function to read a single line from a SHELL_FILE_HANDLE. The \n is not included in the returned
+ buffer. The returned buffer must be callee freed.
+
+ If the position upon start is 0, then the Ascii Boolean will be set. This should be
+ maintained and not changed for all operations with the same file.
+
+ @param[in] Handle SHELL_FILE_HANDLE to read from.
+ @param[in,out] Ascii Boolean value for indicating whether the file is
+ Ascii (TRUE) or UCS2 (FALSE).
+
+ @return The line of text from the file.
+
+ @sa ShellFileHandleReadLine
+**/
+CHAR16*
+EFIAPI
+ShellFileHandleReturnLine(
+ IN SHELL_FILE_HANDLE Handle,
+ IN OUT BOOLEAN *Ascii
+ )
+{
+ CHAR16 *RetVal;
+ UINTN Size;
+ EFI_STATUS Status;
+
+ Size = 0;
+ RetVal = NULL;
+
+ Status = ShellFileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ RetVal = AllocatePool(Size);
+ Status = ShellFileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
+ }
+ ASSERT_EFI_ERROR(Status);
+ if (EFI_ERROR(Status) && (RetVal != NULL)) {
+ FreePool(RetVal);
+ RetVal = NULL;
+ }
+ return (RetVal);
+}
+
+/**
+ Function to read a single line (up to but not including the \n) from a SHELL_FILE_HANDLE.
+
+ If the position upon start is 0, then the Ascii Boolean will be set. This should be
+ maintained and not changed for all operations with the same file.
+
+ @param[in] Handle SHELL_FILE_HANDLE to read from.
+ @param[in,out] Buffer The pointer to buffer to read into.
+ @param[in,out] Size The pointer to number of bytes in Buffer.
+ @param[in] Truncate If the buffer is large enough, this has no effect.
+ If the buffer is is too small and Truncate is TRUE,
+ the line will be truncated.
+ If the buffer is is too small and Truncate is FALSE,
+ then no read will occur.
+
+ @param[in,out] Ascii Boolean value for indicating whether the file is
+ Ascii (TRUE) or UCS2 (FALSE).
+
+ @retval EFI_SUCCESS The operation was successful. The line is stored in
+ Buffer.
+ @retval EFI_INVALID_PARAMETER Handle was NULL.
+ @retval EFI_INVALID_PARAMETER Size was NULL.
+ @retval EFI_BUFFER_TOO_SMALL Size was not large enough to store the line.
+ Size was updated to the minimum space required.
+ @sa ShellFileHandleRead
+**/
+EFI_STATUS
+EFIAPI
+ShellFileHandleReadLine(
+ IN SHELL_FILE_HANDLE Handle,
+ IN OUT CHAR16 *Buffer,
+ IN OUT UINTN *Size,
+ IN BOOLEAN Truncate,
+ IN OUT BOOLEAN *Ascii
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 CharBuffer;
+ UINTN CharSize;
+ UINTN CountSoFar;
+ UINT64 OriginalFilePosition;
+
+
+ if (Handle == NULL
+ ||Size == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ if (Buffer == NULL) {
+ ASSERT(*Size == 0);
+ } else {
+ *Buffer = CHAR_NULL;
+ }
+ gEfiShellProtocol->GetFilePosition(Handle, &OriginalFilePosition);
+ if (OriginalFilePosition == 0) {
+ CharSize = sizeof(CHAR16);
+ Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer);
+ ASSERT_EFI_ERROR(Status);
+ if (CharBuffer == UnicodeFileTag) {
+ *Ascii = FALSE;
+ } else {
+ *Ascii = TRUE;
+ gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
+ }
+ }
+
+ for (CountSoFar = 0;;CountSoFar++){
+ CharBuffer = 0;
+ if (*Ascii) {
+ CharSize = sizeof(CHAR8);
+ } else {
+ CharSize = sizeof(CHAR16);
+ }
+ Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer);
+ if ( EFI_ERROR(Status)
+ || CharSize == 0
+ || (CharBuffer == L'\n' && !(*Ascii))
+ || (CharBuffer == '\n' && *Ascii)
+ ){
+ break;
+ }
+ //
+ // if we have space save it...
+ //
+ if ((CountSoFar+1)*sizeof(CHAR16) < *Size){
+ ASSERT(Buffer != NULL);
+ ((CHAR16*)Buffer)[CountSoFar] = CharBuffer;
+ ((CHAR16*)Buffer)[CountSoFar+1] = CHAR_NULL;
+ }
+ }
+
+ //
+ // if we ran out of space tell when...
+ //
+ if ((CountSoFar+1)*sizeof(CHAR16) > *Size){
+ *Size = (CountSoFar+1)*sizeof(CHAR16);
+ if (!Truncate) {
+ gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition);
+ } else {
+ DEBUG((DEBUG_WARN, "The line was truncated in ShellFileHandleReadLine"));
+ }
+ return (EFI_BUFFER_TOO_SMALL);
+ }
+ while(Buffer[StrLen(Buffer)-1] == L'\r') {
+ Buffer[StrLen(Buffer)-1] = CHAR_NULL;
+ }
+
+ return (Status);
+}
+/**
+ Frees any BUFFER_LIST defined type.
+**/
+VOID
+EFIAPI
+FreeBufferList (
+ IN BUFFER_LIST *List
+ )
+{
+ BUFFER_LIST *BufferListEntry;
+
+ if (List == NULL){
+ return;
+ }
+ //
+ // enumerate through the buffer list and free all memory
+ //
+ for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
+ ; !IsListEmpty (&List->Link)
+ ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
+ ){
+ RemoveEntryList(&BufferListEntry->Link);
+ ASSERT(BufferListEntry->Buffer != NULL);
+ if (BufferListEntry->Buffer != NULL) {
+ FreePool(BufferListEntry->Buffer);
+ }
+ FreePool(BufferListEntry);
+ }
+}
+
+/**
+ Chops off last directory or file entry in a path leaving the trailing slash
+
+ @param[in,out] Path
+
+ @retval FALSE No directory was found to chop off.
+ @retval TRUE A directory was chopped off.
+**/
+BOOLEAN
+EFIAPI
+ChopLastSlash(
+ IN OUT CHAR16 *PathToReturn
+ )
+{
+ CHAR16 *Walker;
+ CHAR16 *LastSlash = NULL;
+ //
+ // get directory name from path... ('chop' off extra)
+ //
+ for ( Walker = PathToReturn
+ ; Walker != NULL && *Walker != CHAR_NULL
+ ; Walker++
+ ){
+ if (*Walker == L'\\' && *(Walker + 1) != CHAR_NULL) {
+ LastSlash = Walker+1;
+ }
+ }
+ if (LastSlash != NULL) {
+ *LastSlash = CHAR_NULL;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+