You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1906 lines
52 KiB
1906 lines
52 KiB
/** @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>
|
|
|
|
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"
|
|
|
|
// 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;
|
|
|
|
GetEfiGlobalVariable2 (EFI_PLATFORM_LANG_VARIABLE_NAME, (VOID**)&PlatformLang, NULL);
|
|
if (PlatformLang == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (gUnicodeCollation == 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;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
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 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;
|
|
|
|
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_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 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 = 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 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;
|
|
CONST CHAR16 *CurDir;
|
|
CHAR16 *SplitCurDir;
|
|
CHAR16 *MapName;
|
|
SHELL_MAP_LIST *MapListItem;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
//
|
|
//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);
|
|
|
|
ShellCommandConsistMappingInitialize(&ConsistMappingTable);
|
|
|
|
//
|
|
// Assign new Mappings to remainders
|
|
//
|
|
for (Count = 0 ; !EFI_ERROR(Status) && HandleList[Count] != NULL && !EFI_ERROR(Status); 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;
|
|
}
|