mirror of https://github.com/acidanthera/audk.git
2329 lines
65 KiB
C
2329 lines
65 KiB
C
/** @file
|
|
Provides interface to shell internal functions for shell commands.
|
|
|
|
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
|
|
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "UefiShellCommandLib.h"
|
|
|
|
// 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 UINT64 mExitCode;
|
|
STATIC BOOLEAN mExitScript;
|
|
STATIC CHAR16 *mProfileList;
|
|
STATIC UINTN mProfileListSize;
|
|
STATIC UINTN mFsMaxCount = 0;
|
|
STATIC UINTN mBlkMaxCount = 0;
|
|
STATIC BUFFER_LIST mFileHandleList;
|
|
|
|
STATIC CONST CHAR8 Hex[] = {
|
|
'0',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'7',
|
|
'8',
|
|
'9',
|
|
'A',
|
|
'B',
|
|
'C',
|
|
'D',
|
|
'E',
|
|
'F'
|
|
};
|
|
|
|
// global variables required by library class.
|
|
EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
|
|
SHELL_MAP_LIST gShellMapList;
|
|
SHELL_MAP_LIST *gShellCurMapping = 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
|
|
)
|
|
{
|
|
UINTN NumHandles;
|
|
EFI_HANDLE *Handles;
|
|
EFI_UNICODE_COLLATION_PROTOCOL *Uc;
|
|
CHAR8 *BestLanguage;
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
CHAR8 *PlatformLang;
|
|
|
|
if (gUnicodeCollation == NULL) {
|
|
GetEfiGlobalVariable2 (EFI_PLATFORM_LANG_VARIABLE_NAME, (VOID **)&PlatformLang, NULL);
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiUnicodeCollation2ProtocolGuid,
|
|
NULL,
|
|
&NumHandles,
|
|
&Handles
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
NumHandles = 0;
|
|
Handles = NULL;
|
|
}
|
|
|
|
for (Index = 0; Index < NumHandles; Index++) {
|
|
//
|
|
// Open Unicode Collation Protocol
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Handles[Index],
|
|
&gEfiUnicodeCollation2ProtocolGuid,
|
|
(VOID **)&Uc,
|
|
gImageHandle,
|
|
NULL,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Without clue provided use the first Unicode Collation2 protocol.
|
|
// This may happen when PlatformLang is NULL or when no installed Unicode
|
|
// Collation2 protocol instance supports PlatformLang.
|
|
//
|
|
if (gUnicodeCollation == NULL) {
|
|
gUnicodeCollation = Uc;
|
|
}
|
|
|
|
if (PlatformLang == NULL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Find the best matching matching language from the supported languages
|
|
// of Unicode Collation2 protocol.
|
|
//
|
|
BestLanguage = GetBestLanguage (
|
|
Uc->SupportedLanguages,
|
|
FALSE,
|
|
PlatformLang,
|
|
NULL
|
|
);
|
|
if (BestLanguage != NULL) {
|
|
FreePool (BestLanguage);
|
|
gUnicodeCollation = Uc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Handles != NULL) {
|
|
FreePool (Handles);
|
|
}
|
|
|
|
if (PlatformLang != NULL) {
|
|
FreePool (PlatformLang);
|
|
}
|
|
}
|
|
|
|
return (gUnicodeCollation == NULL) ? EFI_UNSUPPORTED : 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 successfully
|
|
**/
|
|
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;
|
|
|
|
Status = CommandInit ();
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return (RETURN_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
Frees list of file handles.
|
|
|
|
@param[in] List The list to free.
|
|
**/
|
|
VOID
|
|
FreeFileHandleList (
|
|
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);
|
|
SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)(BufferListEntry->Buffer))->Path);
|
|
SHELL_FREE_NON_NULL (BufferListEntry->Buffer);
|
|
SHELL_FREE_NON_NULL (BufferListEntry);
|
|
}
|
|
}
|
|
|
|
/**
|
|
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;
|
|
ALIAS_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 alias list and free all memory
|
|
//
|
|
while (!IsListEmpty (&mAliasList.Link)) {
|
|
Node2 = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link);
|
|
RemoveEntryList (&Node2->Link);
|
|
SHELL_FREE_NON_NULL (Node2->CommandString);
|
|
SHELL_FREE_NON_NULL (Node2->Alias);
|
|
SHELL_FREE_NON_NULL (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)) {
|
|
FreeFileHandleList (&mFileHandleList);
|
|
}
|
|
|
|
if (mProfileList != NULL) {
|
|
FreePool (mProfileList);
|
|
}
|
|
|
|
gUnicodeCollation = NULL;
|
|
gShellCurMapping = NULL;
|
|
|
|
return (RETURN_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
Find a dynamic command protocol instance given a command name string.
|
|
|
|
@param CommandString the command name string
|
|
|
|
@return instance the command protocol instance, if dynamic command instance found
|
|
@retval NULL no dynamic command protocol instance found for name
|
|
**/
|
|
CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
|
|
ShellCommandFindDynamicCommand (
|
|
IN CONST CHAR16 *CommandString
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *CommandHandleList;
|
|
EFI_HANDLE *NextCommand;
|
|
EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
|
|
|
|
CommandHandleList = GetHandleListByProtocol (&gEfiShellDynamicCommandProtocolGuid);
|
|
if (CommandHandleList == NULL) {
|
|
//
|
|
// not found or out of resources
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
|
|
Status = gBS->HandleProtocol (
|
|
*NextCommand,
|
|
&gEfiShellDynamicCommandProtocolGuid,
|
|
(VOID **)&DynamicCommand
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
if (gUnicodeCollation->StriColl (
|
|
gUnicodeCollation,
|
|
(CHAR16 *)CommandString,
|
|
(CHAR16 *)DynamicCommand->CommandName
|
|
) == 0
|
|
)
|
|
{
|
|
FreePool (CommandHandleList);
|
|
return (DynamicCommand);
|
|
}
|
|
}
|
|
|
|
FreePool (CommandHandleList);
|
|
return (NULL);
|
|
}
|
|
|
|
/**
|
|
Checks if a command exists as a dynamic command protocol instance
|
|
|
|
@param[in] CommandString The command string to check for on the list.
|
|
**/
|
|
BOOLEAN
|
|
ShellCommandDynamicCommandExists (
|
|
IN CONST CHAR16 *CommandString
|
|
)
|
|
{
|
|
return (BOOLEAN)((ShellCommandFindDynamicCommand (CommandString) != NULL));
|
|
}
|
|
|
|
/**
|
|
Checks if a command is already on the internal command list.
|
|
|
|
@param[in] CommandString The command string to check for on the list.
|
|
**/
|
|
BOOLEAN
|
|
ShellCommandIsCommandOnInternalList (
|
|
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);
|
|
}
|
|
|
|
/**
|
|
Checks if a command exists, either internally or through the dynamic command protocol.
|
|
|
|
@param[in] CommandString The command string to check for on the list.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
ShellCommandIsCommandOnList (
|
|
IN CONST CHAR16 *CommandString
|
|
)
|
|
{
|
|
if (ShellCommandIsCommandOnInternalList (CommandString)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return ShellCommandDynamicCommandExists (CommandString);
|
|
}
|
|
|
|
/**
|
|
Get the help text for a dynamic command.
|
|
|
|
@param[in] CommandString The command name.
|
|
|
|
@retval NULL No help text was found.
|
|
@return String of help text. Caller required to free.
|
|
**/
|
|
CHAR16 *
|
|
ShellCommandGetDynamicCommandHelp (
|
|
IN CONST CHAR16 *CommandString
|
|
)
|
|
{
|
|
EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
|
|
|
|
DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString);
|
|
if (DynamicCommand == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
//
|
|
// TODO: how to get proper language?
|
|
//
|
|
return DynamicCommand->GetHelp (DynamicCommand, "en");
|
|
}
|
|
|
|
/**
|
|
Get the help text for an internal command.
|
|
|
|
@param[in] CommandString The command name.
|
|
|
|
@retval NULL No help text was found.
|
|
@return String of help text. Caller reuiqred to free.
|
|
**/
|
|
CHAR16 *
|
|
ShellCommandGetInternalCommandHelp (
|
|
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);
|
|
}
|
|
|
|
/**
|
|
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
|
|
)
|
|
{
|
|
CHAR16 *HelpStr;
|
|
|
|
HelpStr = ShellCommandGetInternalCommandHelp (CommandString);
|
|
|
|
if (HelpStr == NULL) {
|
|
HelpStr = ShellCommandGetDynamicCommandHelp (CommandString);
|
|
}
|
|
|
|
return HelpStr;
|
|
}
|
|
|
|
/**
|
|
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_HII_HANDLE HiiHandle,
|
|
IN CONST EFI_STRING_ID ManFormatHelp
|
|
)
|
|
{
|
|
SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
|
|
SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
|
|
SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
|
|
INTN LexicalMatchValue;
|
|
|
|
//
|
|
// Initialize local variables.
|
|
//
|
|
Command = NULL;
|
|
PrevCommand = NULL;
|
|
LexicalMatchValue = 0;
|
|
|
|
//
|
|
// 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 = AllocateZeroPool (sizeof (SHELL_COMMAND_INTERNAL_LIST_ENTRY));
|
|
if (Node == NULL) {
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Node->CommandString = AllocateCopyPool (StrSize (CommandString), CommandString);
|
|
if (Node->CommandString == NULL) {
|
|
FreePool (Node);
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Insert a new entry on top of the list
|
|
//
|
|
InsertHeadList (&mCommandList.Link, &Node->Link);
|
|
|
|
//
|
|
// Move a new registered command to its sorted ordered location in the list
|
|
//
|
|
for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
|
|
PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
|
|
; !IsNull (&mCommandList.Link, &Command->Link)
|
|
; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link))
|
|
{
|
|
//
|
|
// Get Lexical Comparison Value between PrevCommand and Command list entry
|
|
//
|
|
LexicalMatchValue = gUnicodeCollation->StriColl (
|
|
gUnicodeCollation,
|
|
PrevCommand->CommandString,
|
|
Command->CommandString
|
|
);
|
|
|
|
//
|
|
// Swap PrevCommand and Command list entry if PrevCommand list entry
|
|
// is alphabetically greater than Command list entry
|
|
//
|
|
if (LexicalMatchValue > 0) {
|
|
Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)SwapListEntries (&PrevCommand->Link, &Command->Link);
|
|
} else if (LexicalMatchValue < 0) {
|
|
//
|
|
// PrevCommand entry is lexically lower than Command entry
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
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[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;
|
|
EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
//
|
|
// An internal command was not found, try to find a dynamic command
|
|
//
|
|
DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString);
|
|
if (DynamicCommand != NULL) {
|
|
if (RetVal != NULL) {
|
|
*RetVal = DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
|
|
} else {
|
|
DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
|
|
}
|
|
|
|
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.
|
|
|
|
@param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise.
|
|
|
|
@return a Linked list of all available shell commands.
|
|
**/
|
|
CONST COMMAND_LIST *
|
|
EFIAPI
|
|
ShellCommandGetCommandList (
|
|
IN CONST BOOLEAN Sort
|
|
)
|
|
{
|
|
// if (!Sort) {
|
|
// return ((COMMAND_LIST*)(&mCommandList));
|
|
// }
|
|
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;
|
|
ALIAS_LIST *CommandAlias;
|
|
ALIAS_LIST *PrevCommandAlias;
|
|
INTN LexicalMatchValue;
|
|
|
|
//
|
|
// Asserts for NULL
|
|
//
|
|
ASSERT (Command != NULL);
|
|
ASSERT (Alias != NULL);
|
|
|
|
//
|
|
// allocate memory for new struct
|
|
//
|
|
Node = AllocateZeroPool (sizeof (ALIAS_LIST));
|
|
if (Node == NULL) {
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Node->CommandString = AllocateCopyPool (StrSize (Command), Command);
|
|
if (Node->CommandString == NULL) {
|
|
FreePool (Node);
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Node->Alias = AllocateCopyPool (StrSize (Alias), Alias);
|
|
if (Node->Alias == NULL) {
|
|
FreePool (Node->CommandString);
|
|
FreePool (Node);
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
InsertHeadList (&mAliasList.Link, &Node->Link);
|
|
|
|
//
|
|
// Move a new pre-defined registered alias to its sorted ordered location in the list
|
|
//
|
|
for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
|
|
PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
|
|
; !IsNull (&mAliasList.Link, &CommandAlias->Link)
|
|
; CommandAlias = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &CommandAlias->Link))
|
|
{
|
|
//
|
|
// Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
|
|
//
|
|
LexicalMatchValue = gUnicodeCollation->StriColl (
|
|
gUnicodeCollation,
|
|
PrevCommandAlias->Alias,
|
|
CommandAlias->Alias
|
|
);
|
|
|
|
//
|
|
// Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
|
|
// is alphabetically greater than CommandAlias list entry
|
|
//
|
|
if (LexicalMatchValue > 0) {
|
|
CommandAlias = (ALIAS_LIST *)SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
|
|
} else if (LexicalMatchValue < 0) {
|
|
//
|
|
// PrevCommandAlias entry is lexically lower than CommandAlias entry
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 determines 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 determines 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.
|
|
|
|
@param[in] State How to set echo.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
ShellCommandSetEchoState (
|
|
IN BOOLEAN State
|
|
)
|
|
{
|
|
mEchoState = State;
|
|
}
|
|
|
|
/**
|
|
Indicate that the current shell or script should exit.
|
|
|
|
@param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise.
|
|
@param[in] ErrorCode The 64 bit error code to return.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
ShellCommandRegisterExit (
|
|
IN BOOLEAN ScriptOnly,
|
|
IN CONST UINT64 ErrorCode
|
|
)
|
|
{
|
|
mExitRequested = (BOOLEAN)(!mExitRequested);
|
|
if (mExitRequested) {
|
|
mExitScript = ScriptOnly;
|
|
} else {
|
|
mExitScript = FALSE;
|
|
}
|
|
|
|
mExitCode = ErrorCode;
|
|
}
|
|
|
|
/**
|
|
Retrieve the Exit indicator.
|
|
|
|
@retval TRUE Exit was indicated.
|
|
@retval FALSE Exis was not indicated.
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
ShellCommandGetExit (
|
|
VOID
|
|
)
|
|
{
|
|
return (mExitRequested);
|
|
}
|
|
|
|
/**
|
|
Retrieve the Exit code.
|
|
|
|
If ShellCommandGetExit returns FALSE than the return from this is undefined.
|
|
|
|
@return the value passed into RegisterExit.
|
|
**/
|
|
UINT64
|
|
EFIAPI
|
|
ShellCommandGetExitCode (
|
|
VOID
|
|
)
|
|
{
|
|
return (mExitCode);
|
|
}
|
|
|
|
/**
|
|
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;
|
|
|
|
if (Script == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (LoopVar = 0; LoopVar < Script->Argc; LoopVar++) {
|
|
SHELL_FREE_NON_NULL (Script->Argv[LoopVar]);
|
|
}
|
|
|
|
if (Script->Argv != NULL) {
|
|
SHELL_FREE_NON_NULL (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) {
|
|
SHELL_FREE_NON_NULL (Script->CurrentCommand->Cl);
|
|
}
|
|
|
|
if (Script->CurrentCommand->Data != NULL) {
|
|
SHELL_FREE_NON_NULL (Script->CurrentCommand->Data);
|
|
}
|
|
|
|
SHELL_FREE_NON_NULL (Script->CurrentCommand);
|
|
}
|
|
}
|
|
|
|
SHELL_FREE_NON_NULL (Script->ScriptName);
|
|
SHELL_FREE_NON_NULL (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)) {
|
|
return (NULL);
|
|
}
|
|
|
|
Node = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link);
|
|
RemoveEntryList (&Node->Link);
|
|
DeleteScriptFileStruct (Node->Data);
|
|
FreePool (Node);
|
|
} else {
|
|
Node = AllocateZeroPool (sizeof (SCRIPT_FILE_LIST));
|
|
if (Node == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
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 successful.
|
|
@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 = AllocateCopyPool (StrSize (Name), Name);
|
|
MapListNode->DevicePath = DuplicateDevicePath (DevicePath);
|
|
if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
} else {
|
|
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);
|
|
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 successfully.
|
|
@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;
|
|
CONST CHAR16 *CurDir;
|
|
CHAR16 *SplitCurDir;
|
|
CHAR16 *MapName;
|
|
SHELL_MAP_LIST *MapListItem;
|
|
|
|
ConsistMappingTable = NULL;
|
|
SplitCurDir = NULL;
|
|
MapName = NULL;
|
|
MapListItem = NULL;
|
|
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);
|
|
SHELL_FREE_NON_NULL (MapListNode->DevicePath);
|
|
SHELL_FREE_NON_NULL (MapListNode->MapName);
|
|
SHELL_FREE_NON_NULL (MapListNode->CurrentDirectoryPath);
|
|
FreePool (MapListNode);
|
|
} // for loop
|
|
}
|
|
|
|
//
|
|
// Find each handle with Simple File System
|
|
//
|
|
HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid);
|
|
if (HandleList != NULL) {
|
|
//
|
|
// Do a count of the handles
|
|
//
|
|
for (Count = 0; HandleList[Count] != NULL; Count++) {
|
|
}
|
|
|
|
//
|
|
// Get all Device Paths
|
|
//
|
|
DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
|
|
if (DevicePathList == NULL) {
|
|
SHELL_FREE_NON_NULL (HandleList);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Count = 0; HandleList[Count] != NULL; Count++) {
|
|
DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
|
|
}
|
|
|
|
//
|
|
// Sort all DevicePaths
|
|
//
|
|
PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
|
|
|
|
if (!EFI_ERROR (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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ConsistMappingTable != NULL) {
|
|
ShellCommandConsistMappingUnInitialize (ConsistMappingTable);
|
|
}
|
|
|
|
SHELL_FREE_NON_NULL (HandleList);
|
|
SHELL_FREE_NON_NULL (DevicePathList);
|
|
|
|
HandleList = NULL;
|
|
|
|
//
|
|
// gShellCurMapping point to node of current file system in the gShellMapList. When reset all mappings,
|
|
// all nodes in the gShellMapList will be free. Then gShellCurMapping will be a dangling pointer, So,
|
|
// after created new mappings, we should reset the gShellCurMapping pointer back to node of current file system.
|
|
//
|
|
if (gShellCurMapping != NULL) {
|
|
gShellCurMapping = NULL;
|
|
CurDir = gEfiShellProtocol->GetEnv (L"cwd");
|
|
if (CurDir != NULL) {
|
|
MapName = AllocateCopyPool (StrSize (CurDir), CurDir);
|
|
if (MapName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
SplitCurDir = StrStr (MapName, L":");
|
|
if (SplitCurDir == NULL) {
|
|
SHELL_FREE_NON_NULL (MapName);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*(SplitCurDir + 1) = CHAR_NULL;
|
|
MapListItem = ShellCommandFindMapItem (MapName);
|
|
if (MapListItem != NULL) {
|
|
gShellCurMapping = MapListItem;
|
|
}
|
|
|
|
SHELL_FREE_NON_NULL (MapName);
|
|
}
|
|
}
|
|
} else {
|
|
Count = (UINTN)-1;
|
|
}
|
|
|
|
//
|
|
// Find each handle with Block Io
|
|
//
|
|
HandleList = GetHandleListByProtocol (&gEfiBlockIoProtocolGuid);
|
|
if (HandleList != NULL) {
|
|
for (Count = 0; HandleList[Count] != NULL; Count++) {
|
|
}
|
|
|
|
//
|
|
// Get all Device Paths
|
|
//
|
|
DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
|
|
if (DevicePathList == NULL) {
|
|
SHELL_FREE_NON_NULL (HandleList);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
Add mappings for any devices without one. Do not change any existing maps.
|
|
|
|
@retval EFI_SUCCESS The operation was successful.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ShellCommandUpdateMapping (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleList;
|
|
UINTN Count;
|
|
EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
|
|
CHAR16 *NewDefaultName;
|
|
CHAR16 *NewConsistName;
|
|
EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
|
|
|
|
HandleList = NULL;
|
|
Status = EFI_SUCCESS;
|
|
|
|
//
|
|
// remove mappings that represent removed devices.
|
|
//
|
|
|
|
//
|
|
// Find each handle with Simple File System
|
|
//
|
|
HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid);
|
|
if (HandleList != NULL) {
|
|
//
|
|
// Do a count of the handles
|
|
//
|
|
for (Count = 0; HandleList[Count] != NULL; Count++) {
|
|
}
|
|
|
|
//
|
|
// Get all Device Paths
|
|
//
|
|
DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
|
|
if (DevicePathList == NULL) {
|
|
return (EFI_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
for (Count = 0; HandleList[Count] != NULL; Count++) {
|
|
DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
|
|
}
|
|
|
|
//
|
|
// Sort all DevicePaths
|
|
//
|
|
PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
|
|
|
|
Status = ShellCommandConsistMappingInitialize (&ConsistMappingTable);
|
|
|
|
//
|
|
// Assign new Mappings to remainders
|
|
//
|
|
for (Count = 0; !EFI_ERROR (Status) && HandleList[Count] != NULL; Count++) {
|
|
//
|
|
// Skip ones that already have
|
|
//
|
|
if (gEfiShellProtocol->GetMapFromDevicePath (&DevicePathList[Count]) != NULL) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get default name
|
|
//
|
|
NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem);
|
|
if (NewDefaultName == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Call shell protocol SetMap function now...
|
|
//
|
|
Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewDefaultName);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Now do consistent name
|
|
//
|
|
NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable);
|
|
if (NewConsistName != NULL) {
|
|
Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewConsistName);
|
|
FreePool (NewConsistName);
|
|
}
|
|
}
|
|
|
|
FreePool (NewDefaultName);
|
|
}
|
|
|
|
ShellCommandConsistMappingUnInitialize (ConsistMappingTable);
|
|
SHELL_FREE_NON_NULL (HandleList);
|
|
SHELL_FREE_NON_NULL (DevicePathList);
|
|
|
|
HandleList = NULL;
|
|
} else {
|
|
Count = (UINTN)-1;
|
|
}
|
|
|
|
//
|
|
// Do it all over again for gEfiBlockIoProtocolGuid
|
|
//
|
|
|
|
return (Status);
|
|
}
|
|
|
|
/**
|
|
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.
|
|
@retval NULL There was not enough memory.
|
|
**/
|
|
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));
|
|
if (Buffer == NULL) {
|
|
return (NULL);
|
|
}
|
|
|
|
NewNode = AllocateZeroPool (sizeof (BUFFER_LIST));
|
|
if (NewNode == NULL) {
|
|
SHELL_FREE_NON_NULL (Buffer);
|
|
return (NULL);
|
|
}
|
|
|
|
Buffer->FileHandle = (EFI_FILE_PROTOCOL *)Handle;
|
|
Buffer->Path = StrnCatGrow (&Buffer->Path, NULL, Path, 0);
|
|
if (Buffer->Path == NULL) {
|
|
SHELL_FREE_NON_NULL (NewNode);
|
|
SHELL_FREE_NON_NULL (Buffer);
|
|
return (NULL);
|
|
}
|
|
|
|
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 from 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)) {
|
|
RemoveEntryList (&Node->Link);
|
|
SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
|
|
SHELL_FREE_NON_NULL (Node->Buffer);
|
|
SHELL_FREE_NON_NULL (Node);
|
|
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);
|
|
gEfiShellProtocol->SetFilePosition (Handle, Pos);
|
|
|
|
if (Info == NULL) {
|
|
return (FALSE);
|
|
}
|
|
|
|
if (Pos == Info->FileSize) {
|
|
RetVal = TRUE;
|
|
} else {
|
|
RetVal = FALSE;
|
|
}
|
|
|
|
FreePool (Info);
|
|
|
|
return (RetVal);
|
|
}
|
|
|
|
/**
|
|
Frees any BUFFER_LIST defined type.
|
|
|
|
@param[in] List The BUFFER_LIST object to free.
|
|
**/
|
|
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);
|
|
if (BufferListEntry->Buffer != NULL) {
|
|
FreePool (BufferListEntry->Buffer);
|
|
}
|
|
|
|
FreePool (BufferListEntry);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Dump some hexadecimal data to the screen.
|
|
|
|
@param[in] Indent How many spaces to indent the output.
|
|
@param[in] Offset The offset of the printing.
|
|
@param[in] DataSize The size in bytes of UserData.
|
|
@param[in] UserData The data to print out.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DumpHex (
|
|
IN UINTN Indent,
|
|
IN UINTN Offset,
|
|
IN UINTN DataSize,
|
|
IN VOID *UserData
|
|
)
|
|
{
|
|
UINT8 *Data;
|
|
|
|
CHAR8 Val[50];
|
|
|
|
CHAR8 Str[20];
|
|
|
|
UINT8 TempByte;
|
|
UINTN Size;
|
|
UINTN Index;
|
|
|
|
Data = UserData;
|
|
while (DataSize != 0) {
|
|
Size = 16;
|
|
if (Size > DataSize) {
|
|
Size = DataSize;
|
|
}
|
|
|
|
for (Index = 0; Index < Size; Index += 1) {
|
|
TempByte = Data[Index];
|
|
Val[Index * 3 + 0] = Hex[TempByte >> 4];
|
|
Val[Index * 3 + 1] = Hex[TempByte & 0xF];
|
|
Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
|
|
Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > '~') ? '.' : TempByte);
|
|
}
|
|
|
|
Val[Index * 3] = 0;
|
|
Str[Index] = 0;
|
|
ShellPrintEx (-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
|
|
|
|
Data += Size;
|
|
Offset += Size;
|
|
DataSize -= Size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Dump HEX data into buffer.
|
|
|
|
@param[in] Buffer HEX data to be dumped in Buffer.
|
|
@param[in] Indent How many spaces to indent the output.
|
|
@param[in] Offset The offset of the printing.
|
|
@param[in] DataSize The size in bytes of UserData.
|
|
@param[in] UserData The data to print out.
|
|
**/
|
|
CHAR16 *
|
|
EFIAPI
|
|
CatSDumpHex (
|
|
IN CHAR16 *Buffer,
|
|
IN UINTN Indent,
|
|
IN UINTN Offset,
|
|
IN UINTN DataSize,
|
|
IN VOID *UserData
|
|
)
|
|
{
|
|
UINT8 *Data;
|
|
UINT8 TempByte;
|
|
UINTN Size;
|
|
UINTN Index;
|
|
CHAR8 Val[50];
|
|
CHAR8 Str[20];
|
|
CHAR16 *RetVal;
|
|
CHAR16 *TempRetVal;
|
|
|
|
Data = UserData;
|
|
RetVal = Buffer;
|
|
while (DataSize != 0) {
|
|
Size = 16;
|
|
if (Size > DataSize) {
|
|
Size = DataSize;
|
|
}
|
|
|
|
for (Index = 0; Index < Size; Index += 1) {
|
|
TempByte = Data[Index];
|
|
Val[Index * 3 + 0] = Hex[TempByte >> 4];
|
|
Val[Index * 3 + 1] = Hex[TempByte & 0xF];
|
|
Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
|
|
Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
|
|
}
|
|
|
|
Val[Index * 3] = 0;
|
|
Str[Index] = 0;
|
|
TempRetVal = CatSPrint (RetVal, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
|
|
SHELL_FREE_NON_NULL (RetVal);
|
|
RetVal = TempRetVal;
|
|
|
|
Data += Size;
|
|
Offset += Size;
|
|
DataSize -= Size;
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
/**
|
|
ORDERED_COLLECTION_USER_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
|
|
|
|
@param[in] Unique1AsVoid The first SHELL_SORT_UNIQUE_NAME object (Unique1),
|
|
passed in as a pointer-to-VOID.
|
|
|
|
@param[in] Unique2AsVoid The second SHELL_SORT_UNIQUE_NAME object (Unique2),
|
|
passed in as a pointer-to-VOID.
|
|
|
|
@retval <0 If Unique1 compares less than Unique2.
|
|
|
|
@retval 0 If Unique1 compares equal to Unique2.
|
|
|
|
@retval >0 If Unique1 compares greater than Unique2.
|
|
**/
|
|
STATIC
|
|
INTN
|
|
EFIAPI
|
|
UniqueNameCompare (
|
|
IN CONST VOID *Unique1AsVoid,
|
|
IN CONST VOID *Unique2AsVoid
|
|
)
|
|
{
|
|
CONST SHELL_SORT_UNIQUE_NAME *Unique1;
|
|
CONST SHELL_SORT_UNIQUE_NAME *Unique2;
|
|
|
|
Unique1 = Unique1AsVoid;
|
|
Unique2 = Unique2AsVoid;
|
|
|
|
//
|
|
// We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
|
|
//
|
|
return gUnicodeCollation->StriColl (
|
|
gUnicodeCollation,
|
|
(CHAR16 *)Unique1->Alias,
|
|
(CHAR16 *)Unique2->Alias
|
|
);
|
|
}
|
|
|
|
/**
|
|
ORDERED_COLLECTION_KEY_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
|
|
|
|
@param[in] UniqueAliasAsVoid The CHAR16 string UniqueAlias, passed in as a
|
|
pointer-to-VOID.
|
|
|
|
@param[in] UniqueAsVoid The SHELL_SORT_UNIQUE_NAME object (Unique),
|
|
passed in as a pointer-to-VOID.
|
|
|
|
@retval <0 If UniqueAlias compares less than Unique->Alias.
|
|
|
|
@retval 0 If UniqueAlias compares equal to Unique->Alias.
|
|
|
|
@retval >0 If UniqueAlias compares greater than Unique->Alias.
|
|
**/
|
|
STATIC
|
|
INTN
|
|
EFIAPI
|
|
UniqueNameAliasCompare (
|
|
IN CONST VOID *UniqueAliasAsVoid,
|
|
IN CONST VOID *UniqueAsVoid
|
|
)
|
|
{
|
|
CONST CHAR16 *UniqueAlias;
|
|
CONST SHELL_SORT_UNIQUE_NAME *Unique;
|
|
|
|
UniqueAlias = UniqueAliasAsVoid;
|
|
Unique = UniqueAsVoid;
|
|
|
|
//
|
|
// We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
|
|
//
|
|
return gUnicodeCollation->StriColl (
|
|
gUnicodeCollation,
|
|
(CHAR16 *)UniqueAlias,
|
|
(CHAR16 *)Unique->Alias
|
|
);
|
|
}
|
|
|
|
/**
|
|
Sort an EFI_SHELL_FILE_INFO list, optionally moving duplicates to a separate
|
|
list.
|
|
|
|
@param[in,out] FileList The list of EFI_SHELL_FILE_INFO objects to sort.
|
|
|
|
If FileList is NULL on input, then FileList is
|
|
considered an empty, hence already sorted, list.
|
|
|
|
Otherwise, if (*FileList) is NULL on input, then
|
|
EFI_INVALID_PARAMETER is returned.
|
|
|
|
Otherwise, the caller is responsible for having
|
|
initialized (*FileList)->Link with
|
|
InitializeListHead(). No other fields in the
|
|
(**FileList) head element are accessed by this
|
|
function.
|
|
|
|
On output, (*FileList) is sorted according to Order.
|
|
If Duplicates is NULL on input, then duplicate
|
|
elements are preserved, sorted stably, on
|
|
(*FileList). If Duplicates is not NULL on input,
|
|
then duplicates are moved (stably sorted) to the
|
|
new, dynamically allocated (*Duplicates) list.
|
|
|
|
@param[out] Duplicates If Duplicates is NULL on input, (*FileList) will be
|
|
a monotonically ordered list on output, with
|
|
duplicates stably sorted.
|
|
|
|
If Duplicates is not NULL on input, (*FileList) will
|
|
be a strictly monotonically oredered list on output,
|
|
with duplicates separated (stably sorted) to
|
|
(*Duplicates). All fields except Link will be
|
|
zero-initialized in the (**Duplicates) head element.
|
|
If no duplicates exist, then (*Duplicates) is set to
|
|
NULL on output.
|
|
|
|
@param[in] Order Determines the comparison operation between
|
|
EFI_SHELL_FILE_INFO objects.
|
|
|
|
@retval EFI_INVALID_PARAMETER (UINTN)Order is greater than or equal to
|
|
(UINTN)ShellSortFileListMax. Neither the
|
|
(*FileList) nor the (*Duplicates) list has
|
|
been modified.
|
|
|
|
@retval EFI_INVALID_PARAMETER (*FileList) was NULL on input. Neither the
|
|
(*FileList) nor the (*Duplicates) list has
|
|
been modified.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Memory allocation failed. Neither the
|
|
(*FileList) nor the (*Duplicates) list has
|
|
been modified.
|
|
|
|
@retval EFI_SUCCESS Sorting successful, including the case when
|
|
FileList is NULL on input.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ShellSortFileList (
|
|
IN OUT EFI_SHELL_FILE_INFO **FileList,
|
|
OUT EFI_SHELL_FILE_INFO **Duplicates OPTIONAL,
|
|
IN SHELL_SORT_FILE_LIST Order
|
|
)
|
|
{
|
|
LIST_ENTRY *FilesHead;
|
|
ORDERED_COLLECTION *Sort;
|
|
LIST_ENTRY *FileEntry;
|
|
EFI_SHELL_FILE_INFO *FileInfo;
|
|
SHELL_SORT_UNIQUE_NAME *Unique;
|
|
EFI_STATUS Status;
|
|
EFI_SHELL_FILE_INFO *Dupes;
|
|
LIST_ENTRY *NextFileEntry;
|
|
CONST CHAR16 *Alias;
|
|
ORDERED_COLLECTION_ENTRY *SortEntry;
|
|
LIST_ENTRY *TargetFileList;
|
|
ORDERED_COLLECTION_ENTRY *NextSortEntry;
|
|
VOID *UniqueAsVoid;
|
|
|
|
if ((UINTN)Order >= (UINTN)ShellSortFileListMax) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (FileList == NULL) {
|
|
//
|
|
// FileList is considered empty, hence already sorted, with no duplicates.
|
|
//
|
|
if (Duplicates != NULL) {
|
|
*Duplicates = NULL;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (*FileList == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
FilesHead = &(*FileList)->Link;
|
|
|
|
//
|
|
// Collect all the unique names.
|
|
//
|
|
Sort = OrderedCollectionInit (UniqueNameCompare, UniqueNameAliasCompare);
|
|
if (Sort == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
BASE_LIST_FOR_EACH (FileEntry, FilesHead) {
|
|
FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
|
|
|
|
//
|
|
// Try to record the name of this file as a unique name.
|
|
//
|
|
Unique = AllocatePool (sizeof (*Unique));
|
|
if (Unique == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UninitSort;
|
|
}
|
|
|
|
Unique->Alias = ((Order == ShellSortFileListByFileName) ?
|
|
FileInfo->FileName :
|
|
FileInfo->FullName);
|
|
InitializeListHead (&Unique->SameNameList);
|
|
|
|
Status = OrderedCollectionInsert (Sort, NULL, Unique);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Only two errors are possible: memory allocation failed, or this name
|
|
// has been encountered before. In either case, the
|
|
// SHELL_SORT_UNIQUE_NAME object being constructed has to be released.
|
|
//
|
|
FreePool (Unique);
|
|
//
|
|
// Memory allocation failure is fatal, while having seen the same name
|
|
// before is normal.
|
|
//
|
|
if (Status == EFI_OUT_OF_RESOURCES) {
|
|
goto UninitSort;
|
|
}
|
|
|
|
ASSERT (Status == EFI_ALREADY_STARTED);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set Dupes to suppress incorrect compiler/analyzer warnings.
|
|
//
|
|
Dupes = NULL;
|
|
|
|
//
|
|
// If separation of duplicates has been requested, allocate the list for
|
|
// them.
|
|
//
|
|
if (Duplicates != NULL) {
|
|
Dupes = AllocateZeroPool (sizeof (*Dupes));
|
|
if (Dupes == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UninitSort;
|
|
}
|
|
|
|
InitializeListHead (&Dupes->Link);
|
|
}
|
|
|
|
//
|
|
// No memory allocation beyond this point; thus, no chance to fail. We can
|
|
// now migrate the EFI_SHELL_FILE_INFO objects from (*FileList) to Sort.
|
|
//
|
|
BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, FilesHead) {
|
|
FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
|
|
//
|
|
// Look up the SHELL_SORT_UNIQUE_NAME that matches FileInfo's name.
|
|
//
|
|
Alias = ((Order == ShellSortFileListByFileName) ?
|
|
FileInfo->FileName :
|
|
FileInfo->FullName);
|
|
SortEntry = OrderedCollectionFind (Sort, Alias);
|
|
ASSERT (SortEntry != NULL);
|
|
Unique = OrderedCollectionUserStruct (SortEntry);
|
|
//
|
|
// Move FileInfo from (*FileList) to the end of the list of files whose
|
|
// names all compare identical to FileInfo's name.
|
|
//
|
|
RemoveEntryList (&FileInfo->Link);
|
|
InsertTailList (&Unique->SameNameList, &FileInfo->Link);
|
|
}
|
|
|
|
//
|
|
// All EFI_SHELL_FILE_INFO objects originally in (*FileList) have been
|
|
// distributed to Sort. Now migrate them back to (*FileList), advancing in
|
|
// unique name order.
|
|
//
|
|
for (SortEntry = OrderedCollectionMin (Sort);
|
|
SortEntry != NULL;
|
|
SortEntry = OrderedCollectionNext (SortEntry))
|
|
{
|
|
Unique = OrderedCollectionUserStruct (SortEntry);
|
|
//
|
|
// The first FileInfo encountered for each unique name goes back on
|
|
// (*FileList) unconditionally. Further FileInfo instances for the same
|
|
// unique name -- that is, duplicates -- are either returned to (*FileList)
|
|
// or separated, dependent on the caller's request.
|
|
//
|
|
TargetFileList = FilesHead;
|
|
BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, &Unique->SameNameList) {
|
|
RemoveEntryList (FileEntry);
|
|
InsertTailList (TargetFileList, FileEntry);
|
|
if (Duplicates != NULL) {
|
|
TargetFileList = &Dupes->Link;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We're done. If separation of duplicates has been requested, output the
|
|
// list of duplicates -- and free that list at once, if it's empty (i.e., if
|
|
// no duplicates have been found).
|
|
//
|
|
if (Duplicates != NULL) {
|
|
if (IsListEmpty (&Dupes->Link)) {
|
|
FreePool (Dupes);
|
|
*Duplicates = NULL;
|
|
} else {
|
|
*Duplicates = Dupes;
|
|
}
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
//
|
|
// Fall through.
|
|
//
|
|
UninitSort:
|
|
for (SortEntry = OrderedCollectionMin (Sort);
|
|
SortEntry != NULL;
|
|
SortEntry = NextSortEntry)
|
|
{
|
|
NextSortEntry = OrderedCollectionNext (SortEntry);
|
|
OrderedCollectionDelete (Sort, SortEntry, &UniqueAsVoid);
|
|
Unique = UniqueAsVoid;
|
|
ASSERT (IsListEmpty (&Unique->SameNameList));
|
|
FreePool (Unique);
|
|
}
|
|
|
|
OrderedCollectionUninit (Sort);
|
|
|
|
return Status;
|
|
}
|