From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../ShellPkg/Library/UefiShellLib/UefiShellLib.c | 4392 ++++++++++++++++++++ .../ShellPkg/Library/UefiShellLib/UefiShellLib.h | 90 + .../ShellPkg/Library/UefiShellLib/UefiShellLib.inf | 65 + 3 files changed, 4547 insertions(+) create mode 100644 roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.c create mode 100644 roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.h create mode 100644 roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.inf (limited to 'roms/edk2/ShellPkg/Library/UefiShellLib') diff --git a/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.c b/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.c new file mode 100644 index 000000000..c4749941a --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.c @@ -0,0 +1,4392 @@ +/** @file + Provides interface to shell functionality for shell commands and applications. + + (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright 2016-2018 Dell Technologies.
+ Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "UefiShellLib.h" +#include +#include + +// +// globals... +// +SHELL_PARAM_ITEM EmptyParamList[] = { + {NULL, TypeMax} + }; +SHELL_PARAM_ITEM SfoParamList[] = { + {L"-sfo", TypeFlag}, + {NULL, TypeMax} + }; +EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2; +EFI_SHELL_INTERFACE *mEfiShellInterface; +EFI_SHELL_PROTOCOL *gEfiShellProtocol; +EFI_SHELL_PARAMETERS_PROTOCOL *gEfiShellParametersProtocol; +EFI_HANDLE mEfiShellEnvironment2Handle; +FILE_HANDLE_FUNCTION_MAP FileFunctionMap; +EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollationProtocol; + +/** + Return a clean, fully-qualified version of an input path. If the return value + is non-NULL the caller must free the memory when it is no longer needed. + + If asserts are disabled, and if the input parameter is NULL, NULL is returned. + + If there is not enough memory available to create the fully-qualified path or + a copy of the input path, NULL is returned. + + If there is no working directory, a clean copy of Path is returned. + + Otherwise, the current file system or working directory (as appropriate) is + prepended to Path and the resulting path is cleaned and returned. + + NOTE: If the input path is an empty string, then the current working directory + (if it exists) is returned. In other words, an empty input path is treated + exactly the same as ".". + + @param[in] Path A pointer to some file or directory path. + + @retval NULL The input path is NULL or out of memory. + + @retval non-NULL A pointer to a clean, fully-qualified version of Path. + If there is no working directory, then a pointer to a + clean, but not necessarily fully-qualified version of + Path. The caller must free this memory when it is no + longer needed. +**/ +CHAR16* +EFIAPI +FullyQualifyPath( + IN CONST CHAR16 *Path + ) +{ + CONST CHAR16 *WorkingPath; + CONST CHAR16 *InputPath; + CHAR16 *CharPtr; + CHAR16 *InputFileSystem; + UINTN FileSystemCharCount; + CHAR16 *FullyQualifiedPath; + UINTN Size; + + FullyQualifiedPath = NULL; + + ASSERT(Path != NULL); + // + // Handle erroneous input when asserts are disabled. + // + if (Path == NULL) { + return NULL; + } + // + // In paths that contain ":", like fs0:dir/file.ext and fs2:\fqpath\file.ext, + // we have to consider the file system part separately from the "path" part. + // If there is a file system in the path, we have to get the current working + // directory for that file system. Then we need to use the part of the path + // following the ":". If a path does not contain ":", we use it as given. + // + InputPath = StrStr(Path, L":"); + if (InputPath != NULL) { + InputPath++; + FileSystemCharCount = ((UINTN)InputPath - (UINTN)Path + sizeof(CHAR16)) / sizeof(CHAR16); + InputFileSystem = AllocateCopyPool(FileSystemCharCount * sizeof(CHAR16), Path); + if (InputFileSystem != NULL) { + InputFileSystem[FileSystemCharCount - 1] = CHAR_NULL; + } + WorkingPath = ShellGetCurrentDir(InputFileSystem); + SHELL_FREE_NON_NULL(InputFileSystem); + } else { + InputPath = Path; + WorkingPath = ShellGetEnvironmentVariable(L"cwd"); + } + + if (WorkingPath == NULL) { + // + // With no working directory, all we can do is copy and clean the input path. + // + FullyQualifiedPath = AllocateCopyPool(StrSize(Path), Path); + } else { + // + // Allocate space for both strings plus one more character. + // + Size = StrSize(WorkingPath) + StrSize(InputPath); + FullyQualifiedPath = AllocateZeroPool(Size); + if (FullyQualifiedPath == NULL) { + // + // Try to copy and clean just the input. No harm if not enough memory. + // + FullyQualifiedPath = AllocateCopyPool(StrSize(Path), Path); + } else { + if (*InputPath == L'\\' || *InputPath == L'/') { + // + // Absolute path: start with the current working directory, then + // truncate the new path after the file system part. + // + StrCpyS(FullyQualifiedPath, Size/sizeof(CHAR16), WorkingPath); + CharPtr = StrStr(FullyQualifiedPath, L":"); + if (CharPtr != NULL) { + *(CharPtr + 1) = CHAR_NULL; + } + } else { + // + // Relative path: start with the working directory and append "\". + // + StrCpyS(FullyQualifiedPath, Size/sizeof(CHAR16), WorkingPath); + StrCatS(FullyQualifiedPath, Size/sizeof(CHAR16), L"\\"); + } + // + // Now append the absolute or relative path. + // + StrCatS(FullyQualifiedPath, Size/sizeof(CHAR16), InputPath); + } + } + + PathCleanUpDirectories(FullyQualifiedPath); + + return FullyQualifiedPath; +} + +/** + Check if a Unicode character is a hexadecimal character. + + This internal function checks if a Unicode character is a + numeric character. The valid hexadecimal characters are + L'0' to L'9', L'a' to L'f', or L'A' to L'F'. + + @param Char The character to check against. + + @retval TRUE If the Char is a hexadecmial character. + @retval FALSE If the Char is not a hexadecmial character. + +**/ +BOOLEAN +EFIAPI +ShellIsHexaDecimalDigitCharacter ( + IN CHAR16 Char + ) +{ + return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f')); +} + +/** + Check if a Unicode character is a decimal character. + + This internal function checks if a Unicode character is a + decimal character. The valid characters are + L'0' to L'9'. + + + @param Char The character to check against. + + @retval TRUE If the Char is a hexadecmial character. + @retval FALSE If the Char is not a hexadecmial character. + +**/ +BOOLEAN +EFIAPI +ShellIsDecimalDigitCharacter ( + IN CHAR16 Char + ) +{ + return (BOOLEAN) (Char >= L'0' && Char <= L'9'); +} + +/** + Helper function to find ShellEnvironment2 for constructor. + + @param[in] ImageHandle A copy of the calling image's handle. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. +**/ +EFI_STATUS +ShellFindSE2 ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Buffer; + UINTN BufferSize; + UINTN HandleIndex; + + BufferSize = 0; + Buffer = NULL; + Status = gBS->OpenProtocol(ImageHandle, + &gEfiShellEnvironment2Guid, + (VOID **)&mEfiShellEnvironment2, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + // + // look for the mEfiShellEnvironment2 protocol at a higher level + // + if (EFI_ERROR (Status) || !(CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid))){ + // + // figure out how big of a buffer we need. + // + Status = gBS->LocateHandle (ByProtocol, + &gEfiShellEnvironment2Guid, + NULL, // ignored for ByProtocol + &BufferSize, + Buffer + ); + // + // maybe it's not there??? + // + if (Status == EFI_BUFFER_TOO_SMALL) { + Buffer = (EFI_HANDLE*)AllocateZeroPool(BufferSize); + if (Buffer == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + Status = gBS->LocateHandle (ByProtocol, + &gEfiShellEnvironment2Guid, + NULL, // ignored for ByProtocol + &BufferSize, + Buffer + ); + } + if (!EFI_ERROR (Status) && Buffer != NULL) { + // + // now parse the list of returned handles + // + Status = EFI_NOT_FOUND; + for (HandleIndex = 0; HandleIndex < (BufferSize/sizeof(Buffer[0])); HandleIndex++) { + Status = gBS->OpenProtocol(Buffer[HandleIndex], + &gEfiShellEnvironment2Guid, + (VOID **)&mEfiShellEnvironment2, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid)) { + mEfiShellEnvironment2Handle = Buffer[HandleIndex]; + Status = EFI_SUCCESS; + break; + } + } + } + } + if (Buffer != NULL) { + FreePool (Buffer); + } + return (Status); +} + +/** + Function to do most of the work of the constructor. Allows for calling + multiple times without complete re-initialization. + + @param[in] ImageHandle A copy of the ImageHandle. + @param[in] SystemTable A pointer to the SystemTable for the application. + + @retval EFI_SUCCESS The operationw as successful. +**/ +EFI_STATUS +ShellLibConstructorWorker ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (gEfiShellProtocol == NULL) { + // + // UEFI 2.0 shell interfaces (used preferentially) + // + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiShellProtocolGuid, + (VOID **)&gEfiShellProtocol, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Search for the shell protocol + // + Status = gBS->LocateProtocol ( + &gEfiShellProtocolGuid, + NULL, + (VOID **)&gEfiShellProtocol + ); + if (EFI_ERROR (Status)) { + gEfiShellProtocol = NULL; + } + } + } + + if (gEfiShellParametersProtocol == NULL) { + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID **)&gEfiShellParametersProtocol, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + gEfiShellParametersProtocol = NULL; + } + } + + if (gEfiShellProtocol == NULL) { + // + // Moved to seperate function due to complexity + // + Status = ShellFindSE2(ImageHandle); + + if (EFI_ERROR(Status)) { + DEBUG((DEBUG_ERROR, "Status: 0x%08x\r\n", Status)); + mEfiShellEnvironment2 = NULL; + } + Status = gBS->OpenProtocol(ImageHandle, + &gEfiShellInterfaceGuid, + (VOID **)&mEfiShellInterface, + ImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR(Status)) { + mEfiShellInterface = NULL; + } + } + + // + // Getting either EDK Shell's ShellEnvironment2 and ShellInterface protocol + // or UEFI Shell's Shell protocol. + // When ShellLib is linked to a driver producing DynamicCommand protocol, + // ShellParameters protocol is set by DynamicCommand.Handler(). + // + if ((mEfiShellEnvironment2 != NULL && mEfiShellInterface != NULL) || + (gEfiShellProtocol != NULL) + ) { + if (gEfiShellProtocol != NULL) { + FileFunctionMap.GetFileInfo = gEfiShellProtocol->GetFileInfo; + FileFunctionMap.SetFileInfo = gEfiShellProtocol->SetFileInfo; + FileFunctionMap.ReadFile = gEfiShellProtocol->ReadFile; + FileFunctionMap.WriteFile = gEfiShellProtocol->WriteFile; + FileFunctionMap.CloseFile = gEfiShellProtocol->CloseFile; + FileFunctionMap.DeleteFile = gEfiShellProtocol->DeleteFile; + FileFunctionMap.GetFilePosition = gEfiShellProtocol->GetFilePosition; + FileFunctionMap.SetFilePosition = gEfiShellProtocol->SetFilePosition; + FileFunctionMap.FlushFile = gEfiShellProtocol->FlushFile; + FileFunctionMap.GetFileSize = gEfiShellProtocol->GetFileSize; + } else { + FileFunctionMap.GetFileInfo = (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo; + FileFunctionMap.SetFileInfo = (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo; + FileFunctionMap.ReadFile = (EFI_SHELL_READ_FILE)FileHandleRead; + FileFunctionMap.WriteFile = (EFI_SHELL_WRITE_FILE)FileHandleWrite; + FileFunctionMap.CloseFile = (EFI_SHELL_CLOSE_FILE)FileHandleClose; + FileFunctionMap.DeleteFile = (EFI_SHELL_DELETE_FILE)FileHandleDelete; + FileFunctionMap.GetFilePosition = (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition; + FileFunctionMap.SetFilePosition = (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition; + FileFunctionMap.FlushFile = (EFI_SHELL_FLUSH_FILE)FileHandleFlush; + FileFunctionMap.GetFileSize = (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize; + } + return (EFI_SUCCESS); + } + return (EFI_NOT_FOUND); +} +/** + Constructor for the Shell 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 others an error ocurred during initialization +**/ +EFI_STATUS +EFIAPI +ShellLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + mEfiShellEnvironment2 = NULL; + gEfiShellProtocol = NULL; + gEfiShellParametersProtocol = NULL; + mEfiShellInterface = NULL; + mEfiShellEnvironment2Handle = NULL; + mUnicodeCollationProtocol = NULL; + + // + // verify that auto initialize is not set false + // + if (PcdGetBool(PcdShellLibAutoInitialize) == 0) { + return (EFI_SUCCESS); + } + + return (ShellLibConstructorWorker(ImageHandle, SystemTable)); +} + +/** + Destructor for the library. free any resources. + + @param[in] ImageHandle A copy of the ImageHandle. + @param[in] SystemTable A pointer to the SystemTable for the application. + + @retval EFI_SUCCESS The operation was successful. + @return An error from the CloseProtocol function. +**/ +EFI_STATUS +EFIAPI +ShellLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (mEfiShellEnvironment2 != NULL) { + Status = gBS->CloseProtocol(mEfiShellEnvironment2Handle==NULL?ImageHandle:mEfiShellEnvironment2Handle, + &gEfiShellEnvironment2Guid, + ImageHandle, + NULL); + if (!EFI_ERROR (Status)) { + mEfiShellEnvironment2 = NULL; + mEfiShellEnvironment2Handle = NULL; + } + } + if (mEfiShellInterface != NULL) { + Status = gBS->CloseProtocol(ImageHandle, + &gEfiShellInterfaceGuid, + ImageHandle, + NULL); + if (!EFI_ERROR (Status)) { + mEfiShellInterface = NULL; + } + } + if (gEfiShellProtocol != NULL) { + Status = gBS->CloseProtocol(ImageHandle, + &gEfiShellProtocolGuid, + ImageHandle, + NULL); + if (!EFI_ERROR (Status)) { + gEfiShellProtocol = NULL; + } + } + if (gEfiShellParametersProtocol != NULL) { + Status = gBS->CloseProtocol(ImageHandle, + &gEfiShellParametersProtocolGuid, + ImageHandle, + NULL); + if (!EFI_ERROR (Status)) { + gEfiShellParametersProtocol = NULL; + } + } + + return (EFI_SUCCESS); +} + +/** + This function causes the shell library to initialize itself. If the shell library + is already initialized it will de-initialize all the current protocol poitners and + re-populate them again. + + When the library is used with PcdShellLibAutoInitialize set to true this function + will return EFI_SUCCESS and perform no actions. + + This function is intended for internal access for shell commands only. + + @retval EFI_SUCCESS the initialization was complete sucessfully + +**/ +EFI_STATUS +EFIAPI +ShellInitialize ( + VOID + ) +{ + EFI_STATUS Status; + + // + // if auto initialize is not false then skip + // + if (PcdGetBool(PcdShellLibAutoInitialize) != 0) { + return (EFI_SUCCESS); + } + + // + // deinit the current stuff + // + Status = ShellLibDestructor (gImageHandle, gST); + ASSERT_EFI_ERROR (Status); + + // + // init the new stuff + // + return (ShellLibConstructorWorker(gImageHandle, gST)); +} + +/** + This function will retrieve the information about the file for the handle + specified and store it in allocated pool memory. + + This function allocates a buffer to store the file's information. It is the + caller's responsibility to free the buffer + + @param FileHandle The file handle of the file for which information is + being requested. + + @retval NULL information could not be retrieved. + + @return the information about the file +**/ +EFI_FILE_INFO* +EFIAPI +ShellGetFileInfo ( + IN SHELL_FILE_HANDLE FileHandle + ) +{ + return (FileFunctionMap.GetFileInfo(FileHandle)); +} + +/** + This function sets the information about the file for the opened handle + specified. + + @param[in] FileHandle The file handle of the file for which information + is being set. + + @param[in] FileInfo The information to set. + + @retval EFI_SUCCESS The information was set. + @retval EFI_INVALID_PARAMETER A parameter was out of range or invalid. + @retval EFI_UNSUPPORTED The FileHandle does not support FileInfo. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +ShellSetFileInfo ( + IN SHELL_FILE_HANDLE FileHandle, + IN EFI_FILE_INFO *FileInfo + ) +{ + return (FileFunctionMap.SetFileInfo(FileHandle, FileInfo)); +} + + /** + This function will open a file or directory referenced by DevicePath. + + This function opens a file with the open mode according to the file path. The + Attributes is valid only for EFI_FILE_MODE_CREATE. + + @param FilePath on input the device path to the file. On output + the remaining device path. + @param FileHandle pointer to the file handle. + @param OpenMode the mode to open the file with. + @param Attributes the file's file attributes. + + @retval EFI_SUCCESS The information was set. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_UNSUPPORTED Could not open the file path. + @retval EFI_NOT_FOUND The specified file could not be found on the + device or the file system could not be found on + the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the + medium is no longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the + file. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +ShellOpenFileByDevicePath( + IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath, + OUT SHELL_FILE_HANDLE *FileHandle, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + CHAR16 *FileName; + EFI_STATUS Status; + EFI_FILE_PROTOCOL *File; + + if (FilePath == NULL || FileHandle == NULL) { + return (EFI_INVALID_PARAMETER); + } + + // + // which shell interface should we use + // + if (gEfiShellProtocol != NULL) { + // + // use UEFI Shell 2.0 method. + // + FileName = gEfiShellProtocol->GetFilePathFromDevicePath(*FilePath); + if (FileName == NULL) { + return (EFI_INVALID_PARAMETER); + } + Status = ShellOpenFileByName(FileName, FileHandle, OpenMode, Attributes); + FreePool(FileName); + return (Status); + } + + + // + // use old shell method. + // + Status = EfiOpenFileByDevicePath (FilePath, &File, OpenMode, Attributes); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // This is a weak spot since if the undefined SHELL_FILE_HANDLE format changes this must change also! + // + *FileHandle = (VOID*)File; + return (EFI_SUCCESS); +} + +/** + This function will open a file or directory referenced by filename. + + If return is EFI_SUCCESS, the Filehandle is the opened file's handle; + otherwise, the Filehandle is NULL. The Attributes is valid only for + EFI_FILE_MODE_CREATE. + + if FileName is NULL then ASSERT() + + @param FileName pointer to file name + @param FileHandle pointer to the file handle. + @param OpenMode the mode to open the file with. + @param Attributes the file's file attributes. + + @retval EFI_SUCCESS The information was set. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_UNSUPPORTED Could not open the file path. + @retval EFI_NOT_FOUND The specified file could not be found on the + device or the file system could not be found + on the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the + medium is no longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the + file. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +ShellOpenFileByName( + IN CONST CHAR16 *FileName, + OUT SHELL_FILE_HANDLE *FileHandle, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + CHAR16 *FileNameCopy; + EFI_STATUS Status2; + + // + // ASSERT if FileName is NULL + // + ASSERT(FileName != NULL); + + if (FileName == NULL) { + return (EFI_INVALID_PARAMETER); + } + + if (gEfiShellProtocol != NULL) { + if ((OpenMode & EFI_FILE_MODE_CREATE) == EFI_FILE_MODE_CREATE) { + + // + // Create only a directory + // + if ((Attributes & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY) { + return ShellCreateDirectory(FileName, FileHandle); + } + + // + // Create the directory to create the file in + // + FileNameCopy = AllocateCopyPool (StrSize (FileName), FileName); + if (FileNameCopy == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + PathCleanUpDirectories (FileNameCopy); + if (PathRemoveLastItem (FileNameCopy)) { + if (!EFI_ERROR(ShellCreateDirectory (FileNameCopy, FileHandle))) { + ShellCloseFile (FileHandle); + } + } + SHELL_FREE_NON_NULL (FileNameCopy); + } + + // + // Use UEFI Shell 2.0 method to create the file + // + Status = gEfiShellProtocol->OpenFileByName(FileName, + FileHandle, + OpenMode); + if (EFI_ERROR(Status)) { + return Status; + } + + if (mUnicodeCollationProtocol == NULL) { + Status = gBS->LocateProtocol (&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&mUnicodeCollationProtocol); + if (EFI_ERROR (Status)) { + gEfiShellProtocol->CloseFile (*FileHandle); + return Status; + } + } + + if ((mUnicodeCollationProtocol->StriColl (mUnicodeCollationProtocol, (CHAR16*)FileName, L"NUL") != 0) && + (mUnicodeCollationProtocol->StriColl (mUnicodeCollationProtocol, (CHAR16*)FileName, L"NULL") != 0) && + !EFI_ERROR(Status) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)){ + FileInfo = FileFunctionMap.GetFileInfo(*FileHandle); + ASSERT(FileInfo != NULL); + FileInfo->Attribute = Attributes; + Status2 = FileFunctionMap.SetFileInfo(*FileHandle, FileInfo); + FreePool(FileInfo); + if (EFI_ERROR (Status2)) { + gEfiShellProtocol->CloseFile(*FileHandle); + } + Status = Status2; + } + return (Status); + } + // + // Using EFI Shell version + // this means convert name to path and call that function + // since this will use EFI method again that will open it. + // + ASSERT(mEfiShellEnvironment2 != NULL); + FilePath = mEfiShellEnvironment2->NameToPath ((CHAR16*)FileName); + if (FilePath != NULL) { + return (ShellOpenFileByDevicePath(&FilePath, + FileHandle, + OpenMode, + Attributes)); + } + return (EFI_DEVICE_ERROR); +} +/** + This function create a directory + + If return is EFI_SUCCESS, the Filehandle is the opened directory's handle; + otherwise, the Filehandle is NULL. If the directory already existed, this + function opens the existing directory. + + @param DirectoryName pointer to directory name + @param FileHandle pointer to the file handle. + + @retval EFI_SUCCESS The information was set. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_UNSUPPORTED Could not open the file path. + @retval EFI_NOT_FOUND The specified file could not be found on the + device or the file system could not be found + on the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the + medium is no longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the + file. + @retval EFI_VOLUME_FULL The volume is full. + @sa ShellOpenFileByName +**/ +EFI_STATUS +EFIAPI +ShellCreateDirectory( + IN CONST CHAR16 *DirectoryName, + OUT SHELL_FILE_HANDLE *FileHandle + ) +{ + if (gEfiShellProtocol != NULL) { + // + // Use UEFI Shell 2.0 method + // + return (gEfiShellProtocol->CreateFile(DirectoryName, + EFI_FILE_DIRECTORY, + FileHandle + )); + } else { + return (ShellOpenFileByName(DirectoryName, + FileHandle, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, + EFI_FILE_DIRECTORY + )); + } +} + +/** + This function reads information from an opened file. + + If FileHandle is not a directory, the function reads the requested number of + bytes from the file at the file's current position and returns them in Buffer. + If the read goes beyond the end of the file, the read length is truncated to the + end of the file. The file's current position is increased by the number of bytes + returned. If FileHandle is a directory, the function reads the directory entry + at the file's current position and returns the entry in Buffer. If the Buffer + is not large enough to hold the current directory entry, then + EFI_BUFFER_TOO_SMALL is returned and the current file position is not updated. + BufferSize is set to be the size of the buffer needed to read the entry. On + success, the current position is updated to the next directory entry. If there + are no more directory entries, the read returns a zero-length buffer. + EFI_FILE_INFO is the structure returned as the directory entry. + + @param FileHandle the opened file handle + @param BufferSize on input the size of buffer in bytes. on return + the number of bytes written. + @param Buffer the buffer to put read data into. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TO_SMALL Buffer is too small. ReadSize contains required + size. + +**/ +EFI_STATUS +EFIAPI +ShellReadFile( + IN SHELL_FILE_HANDLE FileHandle, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + return (FileFunctionMap.ReadFile(FileHandle, BufferSize, Buffer)); +} + + +/** + Write data to a file. + + This function writes the specified number of bytes to the file at the current + file position. The current file position is advanced the actual number of bytes + written, which is returned in BufferSize. Partial writes only occur when there + has been a data error during the write attempt (such as "volume space full"). + The file is automatically grown to hold the data if required. Direct writes to + opened directories are not supported. + + @param FileHandle The opened file for writing + @param BufferSize on input the number of bytes in Buffer. On output + the number of bytes written. + @param Buffer the buffer containing data to write is stored. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Writes to an open directory are not supported. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The device is write-protected. + @retval EFI_ACCESS_DENIED The file was open for read only. + @retval EFI_VOLUME_FULL The volume is full. +**/ +EFI_STATUS +EFIAPI +ShellWriteFile( + IN SHELL_FILE_HANDLE FileHandle, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + return (FileFunctionMap.WriteFile(FileHandle, BufferSize, Buffer)); +} + +/** + Close an open file handle. + + This function closes a specified file handle. All "dirty" cached file data is + flushed to the device, and the file is closed. In all cases the handle is + closed. + +@param FileHandle the file handle to close. + +@retval EFI_SUCCESS the file handle was closed sucessfully. +**/ +EFI_STATUS +EFIAPI +ShellCloseFile ( + IN SHELL_FILE_HANDLE *FileHandle + ) +{ + return (FileFunctionMap.CloseFile(*FileHandle)); +} + +/** + Delete a file and close the handle + + This function closes and deletes a file. In all cases the file handle is closed. + If the file cannot be deleted, the warning code EFI_WARN_DELETE_FAILURE is + returned, but the handle is still closed. + + @param FileHandle the file handle to delete + + @retval EFI_SUCCESS the file was closed sucessfully + @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not + deleted + @retval INVALID_PARAMETER One of the parameters has an invalid value. +**/ +EFI_STATUS +EFIAPI +ShellDeleteFile ( + IN SHELL_FILE_HANDLE *FileHandle + ) +{ + return (FileFunctionMap.DeleteFile(*FileHandle)); +} + +/** + Set the current position in a file. + + This function sets the current file position for the handle to the position + supplied. With the exception of seeking to position 0xFFFFFFFFFFFFFFFF, only + absolute positioning is supported, and seeking past the end of the file is + allowed (a subsequent write would grow the file). Seeking to position + 0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file. + If FileHandle is a directory, the only position that may be set is zero. This + has the effect of starting the read process of the directory entries over. + + @param FileHandle The file handle on which the position is being set + @param Position Byte position from begining of file + + @retval EFI_SUCCESS Operation completed sucessfully. + @retval EFI_UNSUPPORTED the seek request for non-zero is not valid on + directories. + @retval INVALID_PARAMETER One of the parameters has an invalid value. +**/ +EFI_STATUS +EFIAPI +ShellSetFilePosition ( + IN SHELL_FILE_HANDLE FileHandle, + IN UINT64 Position + ) +{ + return (FileFunctionMap.SetFilePosition(FileHandle, Position)); +} + +/** + Gets a file's current position + + This function retrieves the current file position for the file handle. For + directories, the current file position has no meaning outside of the file + system driver and as such the operation is not supported. An error is returned + if FileHandle is a directory. + + @param FileHandle The open file handle on which to get the position. + @param Position Byte position from begining of file. + + @retval EFI_SUCCESS the operation completed sucessfully. + @retval INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_UNSUPPORTED the request is not valid on directories. +**/ +EFI_STATUS +EFIAPI +ShellGetFilePosition ( + IN SHELL_FILE_HANDLE FileHandle, + OUT UINT64 *Position + ) +{ + return (FileFunctionMap.GetFilePosition(FileHandle, Position)); +} +/** + Flushes data on a file + + This function flushes all modified data associated with a file to a device. + + @param FileHandle The file handle on which to flush data + + @retval EFI_SUCCESS The data was flushed. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write protected. + @retval EFI_ACCESS_DENIED The file was opened for read only. +**/ +EFI_STATUS +EFIAPI +ShellFlushFile ( + IN SHELL_FILE_HANDLE FileHandle + ) +{ + return (FileFunctionMap.FlushFile(FileHandle)); +} + +/** Retrieve first entry from a directory. + + This function takes an open directory handle and gets information from the + first entry in the directory. A buffer is allocated to contain + the information and a pointer to the buffer is returned in *Buffer. The + caller can use ShellFindNextFile() to get subsequent directory entries. + + The buffer will be freed by ShellFindNextFile() when the last directory + entry is read. Otherwise, the caller must free the buffer, using FreePool, + when finished with it. + + @param[in] DirHandle The file handle of the directory to search. + @param[out] Buffer The pointer to the buffer for the file's information. + + @retval EFI_SUCCESS Found the first file. + @retval EFI_NOT_FOUND Cannot find the directory. + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @return Others status of ShellGetFileInfo, ShellSetFilePosition, + or ShellReadFile +**/ +EFI_STATUS +EFIAPI +ShellFindFirstFile ( + IN SHELL_FILE_HANDLE DirHandle, + OUT EFI_FILE_INFO **Buffer + ) +{ + // + // pass to file handle lib + // + return (FileHandleFindFirstFile(DirHandle, Buffer)); +} +/** Retrieve next entries from a directory. + + To use this function, the caller must first call the ShellFindFirstFile() + function to get the first directory entry. Subsequent directory entries are + retrieved by using the ShellFindNextFile() function. This function can + be called several times to get each entry from the directory. If the call of + ShellFindNextFile() retrieved the last directory entry, the next call of + this function will set *NoFile to TRUE and free the buffer. + + @param[in] DirHandle The file handle of the directory. + @param[out] Buffer The pointer to buffer for file's information. + @param[out] NoFile The pointer to boolean when last file is found. + + @retval EFI_SUCCESS Found the next file, or reached last file + @retval EFI_NO_MEDIA The device has no media. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. +**/ +EFI_STATUS +EFIAPI +ShellFindNextFile( + IN SHELL_FILE_HANDLE DirHandle, + OUT EFI_FILE_INFO *Buffer, + OUT BOOLEAN *NoFile + ) +{ + // + // pass to file handle lib + // + return (FileHandleFindNextFile(DirHandle, Buffer, NoFile)); +} +/** + Retrieve the size of a file. + + if FileHandle is NULL then ASSERT() + if Size is NULL then ASSERT() + + This function extracts the file size info from the FileHandle's EFI_FILE_INFO + data. + + @param FileHandle file handle from which size is retrieved + @param Size pointer to size + + @retval EFI_SUCCESS operation was completed sucessfully + @retval EFI_DEVICE_ERROR cannot access the file +**/ +EFI_STATUS +EFIAPI +ShellGetFileSize ( + IN SHELL_FILE_HANDLE FileHandle, + OUT UINT64 *Size + ) +{ + return (FileFunctionMap.GetFileSize(FileHandle, Size)); +} +/** + Retrieves the status of the break execution flag + + this function is useful to check whether the application is being asked to halt by the shell. + + @retval TRUE the execution break is enabled + @retval FALSE the execution break is not enabled +**/ +BOOLEAN +EFIAPI +ShellGetExecutionBreakFlag( + VOID + ) +{ + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + + // + // We are using UEFI Shell 2.0; see if the event has been triggered + // + if (gBS->CheckEvent(gEfiShellProtocol->ExecutionBreak) != EFI_SUCCESS) { + return (FALSE); + } + return (TRUE); + } + + // + // using EFI Shell; call the function to check + // + if (mEfiShellEnvironment2 != NULL) { + return (mEfiShellEnvironment2->GetExecutionBreak()); + } + + return (FALSE); +} +/** + return the value of an environment variable + + this function gets the value of the environment variable set by the + ShellSetEnvironmentVariable function + + @param EnvKey The key name of the environment variable. + + @retval NULL the named environment variable does not exist. + @return != NULL pointer to the value of the environment variable +**/ +CONST CHAR16* +EFIAPI +ShellGetEnvironmentVariable ( + IN CONST CHAR16 *EnvKey + ) +{ + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + return (gEfiShellProtocol->GetEnv(EnvKey)); + } + + // + // Check for EFI shell + // + if (mEfiShellEnvironment2 != NULL) { + return (mEfiShellEnvironment2->GetEnv((CHAR16*)EnvKey)); + } + + return NULL; +} +/** + set the value of an environment variable + +This function changes the current value of the specified environment variable. If the +environment variable exists and the Value is an empty string, then the environment +variable is deleted. If the environment variable exists and the Value is not an empty +string, then the value of the environment variable is changed. If the environment +variable does not exist and the Value is an empty string, there is no action. If the +environment variable does not exist and the Value is a non-empty string, then the +environment variable is created and assigned the specified value. + + This is not supported pre-UEFI Shell 2.0. + + @param EnvKey The key name of the environment variable. + @param EnvVal The Value of the environment variable + @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE). + + @retval EFI_SUCCESS the operation was completed sucessfully + @retval EFI_UNSUPPORTED This operation is not allowed in pre UEFI 2.0 Shell environments +**/ +EFI_STATUS +EFIAPI +ShellSetEnvironmentVariable ( + IN CONST CHAR16 *EnvKey, + IN CONST CHAR16 *EnvVal, + IN BOOLEAN Volatile + ) +{ + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + return (gEfiShellProtocol->SetEnv(EnvKey, EnvVal, Volatile)); + } + + // + // This feature does not exist under EFI shell + // + return (EFI_UNSUPPORTED); +} + +/** + Cause the shell to parse and execute a command line. + + This function creates a nested instance of the shell and executes the specified + command (CommandLine) with the specified environment (Environment). Upon return, + the status code returned by the specified command is placed in StatusCode. + If Environment is NULL, then the current environment is used and all changes made + by the commands executed will be reflected in the current environment. If the + Environment is non-NULL, then the changes made will be discarded. + The CommandLine is executed from the current working directory on the current + device. + + The EnvironmentVariables pararemeter is ignored in a pre-UEFI Shell 2.0 + environment. The values pointed to by the parameters will be unchanged by the + ShellExecute() function. The Output parameter has no effect in a + UEFI Shell 2.0 environment. + + @param[in] ParentHandle The parent image starting the operation. + @param[in] CommandLine The pointer to a NULL terminated command line. + @param[in] Output True to display debug output. False to hide it. + @param[in] EnvironmentVariables Optional pointer to array of environment variables + in the form "x=y". If NULL, the current set is used. + @param[out] Status The status of the run command line. + + @retval EFI_SUCCESS The operation completed sucessfully. Status + contains the status code returned. + @retval EFI_INVALID_PARAMETER A parameter contains an invalid value. + @retval EFI_OUT_OF_RESOURCES Out of resources. + @retval EFI_UNSUPPORTED The operation is not allowed. +**/ +EFI_STATUS +EFIAPI +ShellExecute ( + IN EFI_HANDLE *ParentHandle, + IN CHAR16 *CommandLine OPTIONAL, + IN BOOLEAN Output OPTIONAL, + IN CHAR16 **EnvironmentVariables OPTIONAL, + OUT EFI_STATUS *Status OPTIONAL + ) +{ + EFI_STATUS CmdStatus; + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + // + // Call UEFI Shell 2.0 version (not using Output parameter) + // + return (gEfiShellProtocol->Execute(ParentHandle, + CommandLine, + EnvironmentVariables, + Status)); + } + + // + // Check for EFI shell + // + if (mEfiShellEnvironment2 != NULL) { + // + // Call EFI Shell version. + // + // Due to an unfixable bug in the EdkShell implementation, we must + // dereference "ParentHandle" here: + // + // 1. The EFI shell installs the EFI_SHELL_ENVIRONMENT2 protocol, + // identified by gEfiShellEnvironment2Guid. + // 2. The Execute() member function takes "ParentImageHandle" as first + // parameter, with type (EFI_HANDLE*). + // 3. In the EdkShell implementation, SEnvExecute() implements the + // Execute() member function. It passes "ParentImageHandle" correctly to + // SEnvDoExecute(). + // 4. SEnvDoExecute() takes the (EFI_HANDLE*), and passes it directly -- + // without de-referencing -- to the HandleProtocol() boot service. + // 5. But HandleProtocol() takes an EFI_HANDLE. + // + // Therefore we must + // - de-reference "ParentHandle" here, to mask the bug in + // SEnvDoExecute(), and + // - pass the resultant EFI_HANDLE as an (EFI_HANDLE*). + // + CmdStatus = (mEfiShellEnvironment2->Execute((EFI_HANDLE *)*ParentHandle, + CommandLine, + Output)); + // + // No Status output parameter so just use the returned status + // + if (Status != NULL) { + *Status = CmdStatus; + } + // + // If there was an error, we can't tell if it was from the command or from + // the Execute() function, so we'll just assume the shell ran successfully + // and the error came from the command. + // + return EFI_SUCCESS; + } + + return (EFI_UNSUPPORTED); +} + +/** + Retreives the current directory path + + If the DeviceName is NULL, it returns the current device's current directory + name. If the DeviceName is not NULL, it returns the current directory name + on specified drive. + + Note that the current directory string should exclude the tailing backslash character. + + @param DeviceName the name of the drive to get directory on + + @retval NULL the directory does not exist + @return != NULL the directory +**/ +CONST CHAR16* +EFIAPI +ShellGetCurrentDir ( + IN CHAR16 * CONST DeviceName OPTIONAL + ) +{ + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + return (gEfiShellProtocol->GetCurDir(DeviceName)); + } + + // + // Check for EFI shell + // + if (mEfiShellEnvironment2 != NULL) { + return (mEfiShellEnvironment2->CurDir(DeviceName)); + } + + return (NULL); +} +/** + sets (enabled or disabled) the page break mode + + when page break mode is enabled the screen will stop scrolling + and wait for operator input before scrolling a subsequent screen. + + @param CurrentState TRUE to enable and FALSE to disable +**/ +VOID +EFIAPI +ShellSetPageBreakMode ( + IN BOOLEAN CurrentState + ) +{ + // + // check for enabling + // + if (CurrentState != 0x00) { + // + // check for UEFI Shell 2.0 + // + if (gEfiShellProtocol != NULL) { + // + // Enable with UEFI 2.0 Shell + // + gEfiShellProtocol->EnablePageBreak(); + return; + } else { + // + // Check for EFI shell + // + if (mEfiShellEnvironment2 != NULL) { + // + // Enable with EFI Shell + // + mEfiShellEnvironment2->EnablePageBreak (DEFAULT_INIT_ROW, DEFAULT_AUTO_LF); + return; + } + } + } else { + // + // check for UEFI Shell 2.0 + // + if (gEfiShellProtocol != NULL) { + // + // Disable with UEFI 2.0 Shell + // + gEfiShellProtocol->DisablePageBreak(); + return; + } else { + // + // Check for EFI shell + // + if (mEfiShellEnvironment2 != NULL) { + // + // Disable with EFI Shell + // + mEfiShellEnvironment2->DisablePageBreak (); + return; + } + } + } +} + +/// +/// version of EFI_SHELL_FILE_INFO struct, except has no CONST pointers. +/// This allows for the struct to be populated. +/// +typedef struct { + LIST_ENTRY Link; + EFI_STATUS Status; + CHAR16 *FullName; + CHAR16 *FileName; + SHELL_FILE_HANDLE Handle; + EFI_FILE_INFO *Info; +} EFI_SHELL_FILE_INFO_NO_CONST; + +/** + Converts a EFI shell list of structures to the coresponding UEFI Shell 2.0 type of list. + + if OldStyleFileList is NULL then ASSERT() + + this function will convert a SHELL_FILE_ARG based list into a callee allocated + EFI_SHELL_FILE_INFO based list. it is up to the caller to free the memory via + the ShellCloseFileMetaArg function. + + @param[in] FileList the EFI shell list type + @param[in, out] ListHead the list to add to + + @retval the resultant head of the double linked new format list; +**/ +LIST_ENTRY* +InternalShellConvertFileListType ( + IN LIST_ENTRY *FileList, + IN OUT LIST_ENTRY *ListHead + ) +{ + SHELL_FILE_ARG *OldInfo; + LIST_ENTRY *Link; + EFI_SHELL_FILE_INFO_NO_CONST *NewInfo; + + // + // ASSERTs + // + ASSERT(FileList != NULL); + ASSERT(ListHead != NULL); + + // + // enumerate through each member of the old list and copy + // + for (Link = FileList->ForwardLink; Link != FileList; Link = Link->ForwardLink) { + OldInfo = CR (Link, SHELL_FILE_ARG, Link, SHELL_FILE_ARG_SIGNATURE); + ASSERT(OldInfo != NULL); + + // + // Skip ones that failed to open... + // + if (OldInfo->Status != EFI_SUCCESS) { + continue; + } + + // + // make sure the old list was valid + // + ASSERT(OldInfo->Info != NULL); + ASSERT(OldInfo->FullName != NULL); + ASSERT(OldInfo->FileName != NULL); + + // + // allocate a new EFI_SHELL_FILE_INFO object + // + NewInfo = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); + if (NewInfo == NULL) { + ShellCloseFileMetaArg((EFI_SHELL_FILE_INFO**)(&ListHead)); + ListHead = NULL; + break; + } + + // + // copy the simple items + // + NewInfo->Handle = OldInfo->Handle; + NewInfo->Status = OldInfo->Status; + + // old shell checks for 0 not NULL + OldInfo->Handle = 0; + + // + // allocate new space to copy strings and structure + // + NewInfo->FullName = AllocateCopyPool(StrSize(OldInfo->FullName), OldInfo->FullName); + NewInfo->FileName = AllocateCopyPool(StrSize(OldInfo->FileName), OldInfo->FileName); + NewInfo->Info = AllocateCopyPool((UINTN)OldInfo->Info->Size, OldInfo->Info); + + // + // make sure all the memory allocations were sucessful + // + if (NULL == NewInfo->FullName || NewInfo->FileName == NULL || NewInfo->Info == NULL) { + // + // Free the partially allocated new node + // + SHELL_FREE_NON_NULL(NewInfo->FullName); + SHELL_FREE_NON_NULL(NewInfo->FileName); + SHELL_FREE_NON_NULL(NewInfo->Info); + SHELL_FREE_NON_NULL(NewInfo); + + // + // Free the previously converted stuff + // + ShellCloseFileMetaArg((EFI_SHELL_FILE_INFO**)(&ListHead)); + ListHead = NULL; + break; + } + + // + // add that to the list + // + InsertTailList(ListHead, &NewInfo->Link); + } + return (ListHead); +} +/** + Opens a group of files based on a path. + + This function uses the Arg to open all the matching files. Each matched + file has a SHELL_FILE_INFO structure to record the file information. These + structures are placed on the list ListHead. Users can get the SHELL_FILE_INFO + structures from ListHead to access each file. This function supports wildcards + and will process '?' and '*' as such. the list must be freed with a call to + ShellCloseFileMetaArg(). + + If you are NOT appending to an existing list *ListHead must be NULL. If + *ListHead is NULL then it must be callee freed. + + @param Arg pointer to path string + @param OpenMode mode to open files with + @param ListHead head of linked list of results + + @retval EFI_SUCCESS the operation was sucessful and the list head + contains the list of opened files + @return != EFI_SUCCESS the operation failed + + @sa InternalShellConvertFileListType +**/ +EFI_STATUS +EFIAPI +ShellOpenFileMetaArg ( + IN CHAR16 *Arg, + IN UINT64 OpenMode, + IN OUT EFI_SHELL_FILE_INFO **ListHead + ) +{ + EFI_STATUS Status; + LIST_ENTRY mOldStyleFileList; + CHAR16 *CleanFilePathStr; + + // + // ASSERT that Arg and ListHead are not NULL + // + ASSERT(Arg != NULL); + ASSERT(ListHead != NULL); + + CleanFilePathStr = NULL; + + Status = InternalShellStripQuotes (Arg, &CleanFilePathStr); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + if (*ListHead == NULL) { + *ListHead = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); + if (*ListHead == NULL) { + FreePool(CleanFilePathStr); + return (EFI_OUT_OF_RESOURCES); + } + InitializeListHead(&((*ListHead)->Link)); + } + Status = gEfiShellProtocol->OpenFileList(CleanFilePathStr, + OpenMode, + ListHead); + if (EFI_ERROR(Status)) { + gEfiShellProtocol->RemoveDupInFileList(ListHead); + } else { + Status = gEfiShellProtocol->RemoveDupInFileList(ListHead); + } + if (*ListHead != NULL && IsListEmpty(&(*ListHead)->Link)) { + FreePool(*ListHead); + FreePool(CleanFilePathStr); + *ListHead = NULL; + return (EFI_NOT_FOUND); + } + FreePool(CleanFilePathStr); + return (Status); + } + + // + // Check for EFI shell + // + if (mEfiShellEnvironment2 != NULL) { + // + // make sure the list head is initialized + // + InitializeListHead(&mOldStyleFileList); + + // + // Get the EFI Shell list of files + // + Status = mEfiShellEnvironment2->FileMetaArg(CleanFilePathStr, &mOldStyleFileList); + if (EFI_ERROR(Status)) { + *ListHead = NULL; + FreePool(CleanFilePathStr); + return (Status); + } + + if (*ListHead == NULL) { + *ListHead = (EFI_SHELL_FILE_INFO *)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); + if (*ListHead == NULL) { + FreePool(CleanFilePathStr); + return (EFI_OUT_OF_RESOURCES); + } + InitializeListHead(&((*ListHead)->Link)); + } + + // + // Convert that to equivalent of UEFI Shell 2.0 structure + // + InternalShellConvertFileListType(&mOldStyleFileList, &(*ListHead)->Link); + + // + // Free the EFI Shell version that was converted. + // + mEfiShellEnvironment2->FreeFileList(&mOldStyleFileList); + + if ((*ListHead)->Link.ForwardLink == (*ListHead)->Link.BackLink && (*ListHead)->Link.BackLink == &((*ListHead)->Link)) { + FreePool(*ListHead); + *ListHead = NULL; + Status = EFI_NOT_FOUND; + } + FreePool(CleanFilePathStr); + return (Status); + } + + FreePool(CleanFilePathStr); + return (EFI_UNSUPPORTED); +} +/** + Free the linked list returned from ShellOpenFileMetaArg. + + if ListHead is NULL then ASSERT(). + + @param ListHead the pointer to free. + + @retval EFI_SUCCESS the operation was sucessful. +**/ +EFI_STATUS +EFIAPI +ShellCloseFileMetaArg ( + IN OUT EFI_SHELL_FILE_INFO **ListHead + ) +{ + LIST_ENTRY *Node; + + // + // ASSERT that ListHead is not NULL + // + ASSERT(ListHead != NULL); + + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellProtocol != NULL) { + return (gEfiShellProtocol->FreeFileList(ListHead)); + } else if (mEfiShellEnvironment2 != NULL) { + // + // Since this is EFI Shell version we need to free our internally made copy + // of the list + // + for ( Node = GetFirstNode(&(*ListHead)->Link) + ; *ListHead != NULL && !IsListEmpty(&(*ListHead)->Link) + ; Node = GetFirstNode(&(*ListHead)->Link)) { + RemoveEntryList(Node); + ((EFI_FILE_PROTOCOL*)((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle)->Close(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle); + FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FullName); + FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FileName); + FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Info); + FreePool((EFI_SHELL_FILE_INFO_NO_CONST*)Node); + } + SHELL_FREE_NON_NULL(*ListHead); + return EFI_SUCCESS; + } + + return (EFI_UNSUPPORTED); +} + +/** + Find a file by searching the CWD and then the path. + + If FileName is NULL then ASSERT. + + If the return value is not NULL then the memory must be caller freed. + + @param FileName Filename string. + + @retval NULL the file was not found + @return !NULL the full path to the file. +**/ +CHAR16 * +EFIAPI +ShellFindFilePath ( + IN CONST CHAR16 *FileName + ) +{ + CONST CHAR16 *Path; + SHELL_FILE_HANDLE Handle; + EFI_STATUS Status; + CHAR16 *RetVal; + CHAR16 *TestPath; + CONST CHAR16 *Walker; + UINTN Size; + CHAR16 *TempChar; + + RetVal = NULL; + + // + // First make sure its not an absolute path. + // + Status = ShellOpenFileByName(FileName, &Handle, EFI_FILE_MODE_READ, 0); + if (!EFI_ERROR(Status)){ + if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) { + ASSERT(RetVal == NULL); + RetVal = StrnCatGrow(&RetVal, NULL, FileName, 0); + ShellCloseFile(&Handle); + return (RetVal); + } else { + ShellCloseFile(&Handle); + } + } + + Path = ShellGetEnvironmentVariable(L"cwd"); + if (Path != NULL) { + Size = StrSize(Path) + sizeof(CHAR16); + Size += StrSize(FileName); + TestPath = AllocateZeroPool(Size); + if (TestPath == NULL) { + return (NULL); + } + StrCpyS(TestPath, Size/sizeof(CHAR16), Path); + StrCatS(TestPath, Size/sizeof(CHAR16), L"\\"); + StrCatS(TestPath, Size/sizeof(CHAR16), FileName); + Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0); + if (!EFI_ERROR(Status)){ + if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) { + ASSERT(RetVal == NULL); + RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0); + ShellCloseFile(&Handle); + FreePool(TestPath); + return (RetVal); + } else { + ShellCloseFile(&Handle); + } + } + FreePool(TestPath); + } + Path = ShellGetEnvironmentVariable(L"path"); + if (Path != NULL) { + Size = StrSize(Path)+sizeof(CHAR16); + Size += StrSize(FileName); + TestPath = AllocateZeroPool(Size); + if (TestPath == NULL) { + return (NULL); + } + Walker = (CHAR16*)Path; + do { + CopyMem(TestPath, Walker, StrSize(Walker)); + if (TestPath != NULL) { + TempChar = StrStr(TestPath, L";"); + if (TempChar != NULL) { + *TempChar = CHAR_NULL; + } + if (TestPath[StrLen(TestPath)-1] != L'\\') { + StrCatS(TestPath, Size/sizeof(CHAR16), L"\\"); + } + if (FileName[0] == L'\\') { + FileName++; + } + StrCatS(TestPath, Size/sizeof(CHAR16), FileName); + if (StrStr(Walker, L";") != NULL) { + Walker = StrStr(Walker, L";") + 1; + } else { + Walker = NULL; + } + Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0); + if (!EFI_ERROR(Status)){ + if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) { + ASSERT(RetVal == NULL); + RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0); + ShellCloseFile(&Handle); + break; + } else { + ShellCloseFile(&Handle); + } + } + } + } while (Walker != NULL && Walker[0] != CHAR_NULL); + FreePool(TestPath); + } + return (RetVal); +} + +/** + Find a file by searching the CWD and then the path with a variable set of file + extensions. If the file is not found it will append each extension in the list + in the order provided and return the first one that is successful. + + If FileName is NULL, then ASSERT. + If FileExtension is NULL, then behavior is identical to ShellFindFilePath. + + If the return value is not NULL then the memory must be caller freed. + + @param[in] FileName Filename string. + @param[in] FileExtension Semi-colon delimeted list of possible extensions. + + @retval NULL The file was not found. + @retval !NULL The path to the file. +**/ +CHAR16 * +EFIAPI +ShellFindFilePathEx ( + IN CONST CHAR16 *FileName, + IN CONST CHAR16 *FileExtension + ) +{ + CHAR16 *TestPath; + CHAR16 *RetVal; + CONST CHAR16 *ExtensionWalker; + UINTN Size; + CHAR16 *TempChar; + CHAR16 *TempChar2; + + ASSERT(FileName != NULL); + if (FileExtension == NULL) { + return (ShellFindFilePath(FileName)); + } + RetVal = ShellFindFilePath(FileName); + if (RetVal != NULL) { + return (RetVal); + } + Size = StrSize(FileName); + Size += StrSize(FileExtension); + TestPath = AllocateZeroPool(Size); + if (TestPath == NULL) { + return (NULL); + } + for (ExtensionWalker = FileExtension, TempChar2 = (CHAR16*)FileExtension; TempChar2 != NULL ; ExtensionWalker = TempChar2 + 1){ + StrCpyS(TestPath, Size/sizeof(CHAR16), FileName); + if (ExtensionWalker != NULL) { + StrCatS(TestPath, Size/sizeof(CHAR16), ExtensionWalker); + } + TempChar = StrStr(TestPath, L";"); + if (TempChar != NULL) { + *TempChar = CHAR_NULL; + } + RetVal = ShellFindFilePath(TestPath); + if (RetVal != NULL) { + break; + } + ASSERT(ExtensionWalker != NULL); + TempChar2 = StrStr(ExtensionWalker, L";"); + } + FreePool(TestPath); + return (RetVal); +} + +typedef struct { + LIST_ENTRY Link; + CHAR16 *Name; + SHELL_PARAM_TYPE Type; + CHAR16 *Value; + UINTN OriginalPosition; +} SHELL_PARAM_PACKAGE; + +/** + Checks the list of valid arguments and returns TRUE if the item was found. If the + return value is TRUE then the type parameter is set also. + + if CheckList is NULL then ASSERT(); + if Name is NULL then ASSERT(); + if Type is NULL then ASSERT(); + + @param Name pointer to Name of parameter found + @param CheckList List to check against + @param Type pointer to type of parameter if it was found + + @retval TRUE the Parameter was found. Type is valid. + @retval FALSE the Parameter was not found. Type is not valid. +**/ +BOOLEAN +InternalIsOnCheckList ( + IN CONST CHAR16 *Name, + IN CONST SHELL_PARAM_ITEM *CheckList, + OUT SHELL_PARAM_TYPE *Type + ) +{ + SHELL_PARAM_ITEM *TempListItem; + CHAR16 *TempString; + + // + // ASSERT that all 3 pointer parameters aren't NULL + // + ASSERT(CheckList != NULL); + ASSERT(Type != NULL); + ASSERT(Name != NULL); + + // + // question mark and page break mode are always supported + // + if ((StrCmp(Name, L"-?") == 0) || + (StrCmp(Name, L"-b") == 0) + ) { + *Type = TypeFlag; + return (TRUE); + } + + // + // Enumerate through the list + // + for (TempListItem = (SHELL_PARAM_ITEM*)CheckList ; TempListItem->Name != NULL ; TempListItem++) { + // + // If the Type is TypeStart only check the first characters of the passed in param + // If it matches set the type and return TRUE + // + if (TempListItem->Type == TypeStart) { + if (StrnCmp(Name, TempListItem->Name, StrLen(TempListItem->Name)) == 0) { + *Type = TempListItem->Type; + return (TRUE); + } + TempString = NULL; + TempString = StrnCatGrow(&TempString, NULL, Name, StrLen(TempListItem->Name)); + if (TempString != NULL) { + if (StringNoCaseCompare(&TempString, &TempListItem->Name) == 0) { + *Type = TempListItem->Type; + FreePool(TempString); + return (TRUE); + } + FreePool(TempString); + } + } else if (StringNoCaseCompare(&Name, &TempListItem->Name) == 0) { + *Type = TempListItem->Type; + return (TRUE); + } + } + + return (FALSE); +} +/** + Checks the string for indicators of "flag" status. this is a leading '/', '-', or '+' + + @param[in] Name pointer to Name of parameter found + @param[in] AlwaysAllowNumbers TRUE to allow numbers, FALSE to not. + @param[in] TimeNumbers TRUE to allow numbers with ":", FALSE otherwise. + + @retval TRUE the Parameter is a flag. + @retval FALSE the Parameter not a flag. +**/ +BOOLEAN +InternalIsFlag ( + IN CONST CHAR16 *Name, + IN CONST BOOLEAN AlwaysAllowNumbers, + IN CONST BOOLEAN TimeNumbers + ) +{ + // + // ASSERT that Name isn't NULL + // + ASSERT(Name != NULL); + + // + // If we accept numbers then dont return TRUE. (they will be values) + // + if (((Name[0] == L'-' || Name[0] == L'+') && InternalShellIsHexOrDecimalNumber(Name+1, FALSE, FALSE, TimeNumbers)) && AlwaysAllowNumbers) { + return (FALSE); + } + + // + // If the Name has a /, +, or - as the first character return TRUE + // + if ((Name[0] == L'/') || + (Name[0] == L'-') || + (Name[0] == L'+') + ) { + return (TRUE); + } + return (FALSE); +} + +/** + Checks the command line arguments passed against the list of valid ones. + + If no initialization is required, then return RETURN_SUCCESS. + + @param[in] CheckList pointer to list of parameters to check + @param[out] CheckPackage pointer to pointer to list checked values + @param[out] ProblemParam optional pointer to pointer to unicode string for + the paramater that caused failure. If used then the + caller is responsible for freeing the memory. + @param[in] AutoPageBreak will automatically set PageBreakEnabled for "b" parameter + @param[in] Argv pointer to array of parameters + @param[in] Argc Count of parameters in Argv + @param[in] AlwaysAllowNumbers TRUE to allow numbers always, FALSE otherwise. + + @retval EFI_SUCCESS The operation completed sucessfully. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed + @retval EFI_INVALID_PARAMETER A parameter was invalid + @retval EFI_VOLUME_CORRUPTED the command line was corrupt. an argument was + duplicated. the duplicated command line argument + was returned in ProblemParam if provided. + @retval EFI_NOT_FOUND a argument required a value that was missing. + the invalid command line argument was returned in + ProblemParam if provided. +**/ +EFI_STATUS +InternalCommandLineParse ( + IN CONST SHELL_PARAM_ITEM *CheckList, + OUT LIST_ENTRY **CheckPackage, + OUT CHAR16 **ProblemParam OPTIONAL, + IN BOOLEAN AutoPageBreak, + IN CONST CHAR16 **Argv, + IN UINTN Argc, + IN BOOLEAN AlwaysAllowNumbers + ) +{ + UINTN LoopCounter; + SHELL_PARAM_TYPE CurrentItemType; + SHELL_PARAM_PACKAGE *CurrentItemPackage; + UINTN GetItemValue; + UINTN ValueSize; + UINTN Count; + CONST CHAR16 *TempPointer; + UINTN CurrentValueSize; + CHAR16 *NewValue; + + CurrentItemPackage = NULL; + GetItemValue = 0; + ValueSize = 0; + Count = 0; + + // + // If there is only 1 item we dont need to do anything + // + if (Argc < 1) { + *CheckPackage = NULL; + return (EFI_SUCCESS); + } + + // + // ASSERTs + // + ASSERT(CheckList != NULL); + ASSERT(Argv != NULL); + + // + // initialize the linked list + // + *CheckPackage = (LIST_ENTRY*)AllocateZeroPool(sizeof(LIST_ENTRY)); + if (*CheckPackage == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + + InitializeListHead(*CheckPackage); + + // + // loop through each of the arguments + // + for (LoopCounter = 0 ; LoopCounter < Argc ; ++LoopCounter) { + if (Argv[LoopCounter] == NULL) { + // + // do nothing for NULL argv + // + } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType)) { + // + // We might have leftover if last parameter didnt have optional value + // + if (GetItemValue != 0) { + GetItemValue = 0; + InsertHeadList(*CheckPackage, &CurrentItemPackage->Link); + } + // + // this is a flag + // + CurrentItemPackage = AllocateZeroPool(sizeof(SHELL_PARAM_PACKAGE)); + if (CurrentItemPackage == NULL) { + ShellCommandLineFreeVarList(*CheckPackage); + *CheckPackage = NULL; + return (EFI_OUT_OF_RESOURCES); + } + CurrentItemPackage->Name = AllocateCopyPool(StrSize(Argv[LoopCounter]), Argv[LoopCounter]); + if (CurrentItemPackage->Name == NULL) { + ShellCommandLineFreeVarList(*CheckPackage); + *CheckPackage = NULL; + return (EFI_OUT_OF_RESOURCES); + } + CurrentItemPackage->Type = CurrentItemType; + CurrentItemPackage->OriginalPosition = (UINTN)(-1); + CurrentItemPackage->Value = NULL; + + // + // Does this flag require a value + // + switch (CurrentItemPackage->Type) { + // + // possibly trigger the next loop(s) to populate the value of this item + // + case TypeValue: + case TypeTimeValue: + GetItemValue = 1; + ValueSize = 0; + break; + case TypeDoubleValue: + GetItemValue = 2; + ValueSize = 0; + break; + case TypeMaxValue: + GetItemValue = (UINTN)(-1); + ValueSize = 0; + break; + default: + // + // this item has no value expected; we are done + // + InsertHeadList(*CheckPackage, &CurrentItemPackage->Link); + ASSERT(GetItemValue == 0); + break; + } + } else if (GetItemValue != 0 && CurrentItemPackage != NULL && !InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers, (BOOLEAN)(CurrentItemPackage->Type == TypeTimeValue))) { + // + // get the item VALUE for a previous flag + // + CurrentValueSize = ValueSize + StrSize(Argv[LoopCounter]) + sizeof(CHAR16); + NewValue = ReallocatePool(ValueSize, CurrentValueSize, CurrentItemPackage->Value); + if (NewValue == NULL) { + SHELL_FREE_NON_NULL (CurrentItemPackage->Value); + SHELL_FREE_NON_NULL (CurrentItemPackage); + ShellCommandLineFreeVarList (*CheckPackage); + *CheckPackage = NULL; + return EFI_OUT_OF_RESOURCES; + } + CurrentItemPackage->Value = NewValue; + if (ValueSize == 0) { + StrCpyS( CurrentItemPackage->Value, + CurrentValueSize/sizeof(CHAR16), + Argv[LoopCounter] + ); + } else { + StrCatS( CurrentItemPackage->Value, + CurrentValueSize/sizeof(CHAR16), + L" " + ); + StrCatS( CurrentItemPackage->Value, + CurrentValueSize/sizeof(CHAR16), + Argv[LoopCounter] + ); + } + ValueSize += StrSize(Argv[LoopCounter]) + sizeof(CHAR16); + + GetItemValue--; + if (GetItemValue == 0) { + InsertHeadList(*CheckPackage, &CurrentItemPackage->Link); + } + } else if (!InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers, FALSE)){ + // + // add this one as a non-flag + // + + TempPointer = Argv[LoopCounter]; + if ((*TempPointer == L'^' && *(TempPointer+1) == L'-') + || (*TempPointer == L'^' && *(TempPointer+1) == L'/') + || (*TempPointer == L'^' && *(TempPointer+1) == L'+') + ){ + TempPointer++; + } + CurrentItemPackage = AllocateZeroPool(sizeof(SHELL_PARAM_PACKAGE)); + if (CurrentItemPackage == NULL) { + ShellCommandLineFreeVarList(*CheckPackage); + *CheckPackage = NULL; + return (EFI_OUT_OF_RESOURCES); + } + CurrentItemPackage->Name = NULL; + CurrentItemPackage->Type = TypePosition; + CurrentItemPackage->Value = AllocateCopyPool(StrSize(TempPointer), TempPointer); + if (CurrentItemPackage->Value == NULL) { + ShellCommandLineFreeVarList(*CheckPackage); + *CheckPackage = NULL; + return (EFI_OUT_OF_RESOURCES); + } + CurrentItemPackage->OriginalPosition = Count++; + InsertHeadList(*CheckPackage, &CurrentItemPackage->Link); + } else { + // + // this was a non-recognised flag... error! + // + if (ProblemParam != NULL) { + *ProblemParam = AllocateCopyPool(StrSize(Argv[LoopCounter]), Argv[LoopCounter]); + } + ShellCommandLineFreeVarList(*CheckPackage); + *CheckPackage = NULL; + return (EFI_VOLUME_CORRUPTED); + } + } + if (GetItemValue != 0) { + GetItemValue = 0; + InsertHeadList(*CheckPackage, &CurrentItemPackage->Link); + } + // + // support for AutoPageBreak + // + if (AutoPageBreak && ShellCommandLineGetFlag(*CheckPackage, L"-b")) { + ShellSetPageBreakMode(TRUE); + } + return (EFI_SUCCESS); +} + +/** + Checks the command line arguments passed against the list of valid ones. + Optionally removes NULL values first. + + If no initialization is required, then return RETURN_SUCCESS. + + @param[in] CheckList The pointer to list of parameters to check. + @param[out] CheckPackage The package of checked values. + @param[out] ProblemParam Optional pointer to pointer to unicode string for + the paramater that caused failure. + @param[in] AutoPageBreak Will automatically set PageBreakEnabled. + @param[in] AlwaysAllowNumbers Will never fail for number based flags. + + @retval EFI_SUCCESS The operation completed sucessfully. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_VOLUME_CORRUPTED The command line was corrupt. + @retval EFI_DEVICE_ERROR The commands contained 2 opposing arguments. One + of the command line arguments was returned in + ProblemParam if provided. + @retval EFI_NOT_FOUND A argument required a value that was missing. + The invalid command line argument was returned in + ProblemParam if provided. +**/ +EFI_STATUS +EFIAPI +ShellCommandLineParseEx ( + IN CONST SHELL_PARAM_ITEM *CheckList, + OUT LIST_ENTRY **CheckPackage, + OUT CHAR16 **ProblemParam OPTIONAL, + IN BOOLEAN AutoPageBreak, + IN BOOLEAN AlwaysAllowNumbers + ) +{ + // + // ASSERT that CheckList and CheckPackage aren't NULL + // + ASSERT(CheckList != NULL); + ASSERT(CheckPackage != NULL); + + // + // Check for UEFI Shell 2.0 protocols + // + if (gEfiShellParametersProtocol != NULL) { + return (InternalCommandLineParse(CheckList, + CheckPackage, + ProblemParam, + AutoPageBreak, + (CONST CHAR16**) gEfiShellParametersProtocol->Argv, + gEfiShellParametersProtocol->Argc, + AlwaysAllowNumbers)); + } + + // + // ASSERT That EFI Shell is not required + // + ASSERT (mEfiShellInterface != NULL); + return (InternalCommandLineParse(CheckList, + CheckPackage, + ProblemParam, + AutoPageBreak, + (CONST CHAR16**) mEfiShellInterface->Argv, + mEfiShellInterface->Argc, + AlwaysAllowNumbers)); +} + +/** + Frees shell variable list that was returned from ShellCommandLineParse. + + This function will free all the memory that was used for the CheckPackage + list of postprocessed shell arguments. + + this function has no return value. + + if CheckPackage is NULL, then return + + @param CheckPackage the list to de-allocate + **/ +VOID +EFIAPI +ShellCommandLineFreeVarList ( + IN LIST_ENTRY *CheckPackage + ) +{ + LIST_ENTRY *Node; + + // + // check for CheckPackage == NULL + // + if (CheckPackage == NULL) { + return; + } + + // + // for each node in the list + // + for ( Node = GetFirstNode(CheckPackage) + ; !IsListEmpty(CheckPackage) + ; Node = GetFirstNode(CheckPackage) + ){ + // + // Remove it from the list + // + RemoveEntryList(Node); + + // + // if it has a name free the name + // + if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) { + FreePool(((SHELL_PARAM_PACKAGE*)Node)->Name); + } + + // + // if it has a value free the value + // + if (((SHELL_PARAM_PACKAGE*)Node)->Value != NULL) { + FreePool(((SHELL_PARAM_PACKAGE*)Node)->Value); + } + + // + // free the node structure + // + FreePool((SHELL_PARAM_PACKAGE*)Node); + } + // + // free the list head node + // + FreePool(CheckPackage); +} +/** + Checks for presence of a flag parameter + + flag arguments are in the form of "-" or "/", but do not have a value following the key + + if CheckPackage is NULL then return FALSE. + if KeyString is NULL then ASSERT() + + @param CheckPackage The package of parsed command line arguments + @param KeyString the Key of the command line argument to check for + + @retval TRUE the flag is on the command line + @retval FALSE the flag is not on the command line + **/ +BOOLEAN +EFIAPI +ShellCommandLineGetFlag ( + IN CONST LIST_ENTRY * CONST CheckPackage, + IN CONST CHAR16 * CONST KeyString + ) +{ + LIST_ENTRY *Node; + CHAR16 *TempString; + + // + // return FALSE for no package or KeyString is NULL + // + if (CheckPackage == NULL || KeyString == NULL) { + return (FALSE); + } + + // + // enumerate through the list of parametrs + // + for ( Node = GetFirstNode(CheckPackage) + ; !IsNull (CheckPackage, Node) + ; Node = GetNextNode(CheckPackage, Node) + ){ + // + // If the Name matches, return TRUE (and there may be NULL name) + // + if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) { + // + // If Type is TypeStart then only compare the begining of the strings + // + if (((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart) { + if (StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0) { + return (TRUE); + } + TempString = NULL; + TempString = StrnCatGrow(&TempString, NULL, KeyString, StrLen(((SHELL_PARAM_PACKAGE*)Node)->Name)); + if (TempString != NULL) { + if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) { + FreePool(TempString); + return (TRUE); + } + FreePool(TempString); + } + } else if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) { + return (TRUE); + } + } + } + return (FALSE); +} +/** + Returns value from command line argument. + + Value parameters are in the form of "- value" or "/ value". + + If CheckPackage is NULL, then return NULL. + + @param[in] CheckPackage The package of parsed command line arguments. + @param[in] KeyString The Key of the command line argument to check for. + + @retval NULL The flag is not on the command line. + @retval !=NULL The pointer to unicode string of the value. +**/ +CONST CHAR16* +EFIAPI +ShellCommandLineGetValue ( + IN CONST LIST_ENTRY *CheckPackage, + IN CHAR16 *KeyString + ) +{ + LIST_ENTRY *Node; + CHAR16 *TempString; + + // + // return NULL for no package or KeyString is NULL + // + if (CheckPackage == NULL || KeyString == NULL) { + return (NULL); + } + + // + // enumerate through the list of parametrs + // + for ( Node = GetFirstNode(CheckPackage) + ; !IsNull (CheckPackage, Node) + ; Node = GetNextNode(CheckPackage, Node) + ){ + // + // If the Name matches, return TRUE (and there may be NULL name) + // + if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) { + // + // If Type is TypeStart then only compare the begining of the strings + // + if (((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart) { + if (StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0) { + return (((SHELL_PARAM_PACKAGE*)Node)->Name + StrLen(KeyString)); + } + TempString = NULL; + TempString = StrnCatGrow(&TempString, NULL, KeyString, StrLen(((SHELL_PARAM_PACKAGE*)Node)->Name)); + if (TempString != NULL) { + if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) { + FreePool(TempString); + return (((SHELL_PARAM_PACKAGE*)Node)->Name + StrLen(KeyString)); + } + FreePool(TempString); + } + } else if (StringNoCaseCompare(&KeyString, &((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) { + return (((SHELL_PARAM_PACKAGE*)Node)->Value); + } + } + } + return (NULL); +} + +/** + Returns raw value from command line argument. + + Raw value parameters are in the form of "value" in a specific position in the list. + + If CheckPackage is NULL, then return NULL. + + @param[in] CheckPackage The package of parsed command line arguments. + @param[in] Position The position of the value. + + @retval NULL The flag is not on the command line. + @retval !=NULL The pointer to unicode string of the value. + **/ +CONST CHAR16* +EFIAPI +ShellCommandLineGetRawValue ( + IN CONST LIST_ENTRY * CONST CheckPackage, + IN UINTN Position + ) +{ + LIST_ENTRY *Node; + + // + // check for CheckPackage == NULL + // + if (CheckPackage == NULL) { + return (NULL); + } + + // + // enumerate through the list of parametrs + // + for ( Node = GetFirstNode(CheckPackage) + ; !IsNull (CheckPackage, Node) + ; Node = GetNextNode(CheckPackage, Node) + ){ + // + // If the position matches, return the value + // + if (((SHELL_PARAM_PACKAGE*)Node)->OriginalPosition == Position) { + return (((SHELL_PARAM_PACKAGE*)Node)->Value); + } + } + return (NULL); +} + +/** + returns the number of command line value parameters that were parsed. + + this will not include flags. + + @param[in] CheckPackage The package of parsed command line arguments. + + @retval (UINTN)-1 No parsing has ocurred + @return other The number of value parameters found +**/ +UINTN +EFIAPI +ShellCommandLineGetCount( + IN CONST LIST_ENTRY *CheckPackage + ) +{ + LIST_ENTRY *Node1; + UINTN Count; + + if (CheckPackage == NULL) { + return (0); + } + for ( Node1 = GetFirstNode(CheckPackage), Count = 0 + ; !IsNull (CheckPackage, Node1) + ; Node1 = GetNextNode(CheckPackage, Node1) + ){ + if (((SHELL_PARAM_PACKAGE*)Node1)->Name == NULL) { + Count++; + } + } + return (Count); +} + +/** + Determines if a parameter is duplicated. + + If Param is not NULL then it will point to a callee allocated string buffer + with the parameter value if a duplicate is found. + + If CheckPackage is NULL, then ASSERT. + + @param[in] CheckPackage The package of parsed command line arguments. + @param[out] Param Upon finding one, a pointer to the duplicated parameter. + + @retval EFI_SUCCESS No parameters were duplicated. + @retval EFI_DEVICE_ERROR A duplicate was found. + **/ +EFI_STATUS +EFIAPI +ShellCommandLineCheckDuplicate ( + IN CONST LIST_ENTRY *CheckPackage, + OUT CHAR16 **Param + ) +{ + LIST_ENTRY *Node1; + LIST_ENTRY *Node2; + + ASSERT(CheckPackage != NULL); + + for ( Node1 = GetFirstNode(CheckPackage) + ; !IsNull (CheckPackage, Node1) + ; Node1 = GetNextNode(CheckPackage, Node1) + ){ + for ( Node2 = GetNextNode(CheckPackage, Node1) + ; !IsNull (CheckPackage, Node2) + ; Node2 = GetNextNode(CheckPackage, Node2) + ){ + if ((((SHELL_PARAM_PACKAGE*)Node1)->Name != NULL) && (((SHELL_PARAM_PACKAGE*)Node2)->Name != NULL) && StrCmp(((SHELL_PARAM_PACKAGE*)Node1)->Name, ((SHELL_PARAM_PACKAGE*)Node2)->Name) == 0) { + if (Param != NULL) { + *Param = NULL; + *Param = StrnCatGrow(Param, NULL, ((SHELL_PARAM_PACKAGE*)Node1)->Name, 0); + } + return (EFI_DEVICE_ERROR); + } + } + } + return (EFI_SUCCESS); +} + +/** + This is a find and replace function. Upon successful return the NewString is a copy of + SourceString with each instance of FindTarget replaced with ReplaceWith. + + If SourceString and NewString overlap the behavior is undefined. + + If the string would grow bigger than NewSize it will halt and return error. + + @param[in] SourceString The string with source buffer. + @param[in, out] NewString The string with resultant buffer. + @param[in] NewSize The size in bytes of NewString. + @param[in] FindTarget The string to look for. + @param[in] ReplaceWith The string to replace FindTarget with. + @param[in] SkipPreCarrot If TRUE will skip a FindTarget that has a '^' + immediately before it. + @param[in] ParameterReplacing If TRUE will add "" around items with spaces. + + @retval EFI_INVALID_PARAMETER SourceString was NULL. + @retval EFI_INVALID_PARAMETER NewString was NULL. + @retval EFI_INVALID_PARAMETER FindTarget was NULL. + @retval EFI_INVALID_PARAMETER ReplaceWith was NULL. + @retval EFI_INVALID_PARAMETER FindTarget had length < 1. + @retval EFI_INVALID_PARAMETER SourceString had length < 1. + @retval EFI_BUFFER_TOO_SMALL NewSize was less than the minimum size to hold + the new string (truncation occurred). + @retval EFI_SUCCESS The string was successfully copied with replacement. +**/ +EFI_STATUS +EFIAPI +ShellCopySearchAndReplace( + IN CHAR16 CONST *SourceString, + IN OUT CHAR16 *NewString, + IN UINTN NewSize, + IN CONST CHAR16 *FindTarget, + IN CONST CHAR16 *ReplaceWith, + IN CONST BOOLEAN SkipPreCarrot, + IN CONST BOOLEAN ParameterReplacing + ) +{ + UINTN Size; + CHAR16 *Replace; + + if ( (SourceString == NULL) + || (NewString == NULL) + || (FindTarget == NULL) + || (ReplaceWith == NULL) + || (StrLen(FindTarget) < 1) + || (StrLen(SourceString) < 1) + ){ + return (EFI_INVALID_PARAMETER); + } + Replace = NULL; + if (StrStr(ReplaceWith, L" ") == NULL || !ParameterReplacing) { + Replace = StrnCatGrow(&Replace, NULL, ReplaceWith, 0); + } else { + Replace = AllocateZeroPool(StrSize(ReplaceWith) + 2*sizeof(CHAR16)); + if (Replace != NULL) { + UnicodeSPrint(Replace, StrSize(ReplaceWith) + 2*sizeof(CHAR16), L"\"%s\"", ReplaceWith); + } + } + if (Replace == NULL) { + return (EFI_OUT_OF_RESOURCES); + } + NewString = ZeroMem(NewString, NewSize); + while (*SourceString != CHAR_NULL) { + // + // if we find the FindTarget and either Skip == FALSE or Skip and we + // dont have a carrot do a replace... + // + if (StrnCmp(SourceString, FindTarget, StrLen(FindTarget)) == 0 + && ((SkipPreCarrot && *(SourceString-1) != L'^') || !SkipPreCarrot) + ){ + SourceString += StrLen(FindTarget); + Size = StrSize(NewString); + if ((Size + (StrLen(Replace)*sizeof(CHAR16))) > NewSize) { + FreePool(Replace); + return (EFI_BUFFER_TOO_SMALL); + } + StrCatS(NewString, NewSize/sizeof(CHAR16), Replace); + } else { + Size = StrSize(NewString); + if (Size + sizeof(CHAR16) > NewSize) { + FreePool(Replace); + return (EFI_BUFFER_TOO_SMALL); + } + StrnCatS(NewString, NewSize/sizeof(CHAR16), SourceString, 1); + SourceString++; + } + } + FreePool(Replace); + return (EFI_SUCCESS); +} + +/** + Internal worker function to output a string. + + This function will output a string to the correct StdOut. + + @param[in] String The string to print out. + + @retval EFI_SUCCESS The operation was sucessful. + @retval !EFI_SUCCESS The operation failed. +**/ +EFI_STATUS +InternalPrintTo ( + IN CONST CHAR16 *String + ) +{ + UINTN Size; + Size = StrSize(String) - sizeof(CHAR16); + if (Size == 0) { + return (EFI_SUCCESS); + } + if (gEfiShellParametersProtocol != NULL) { + return (gEfiShellProtocol->WriteFile(gEfiShellParametersProtocol->StdOut, &Size, (VOID*)String)); + } + if (mEfiShellInterface != NULL) { + if (mEfiShellInterface->RedirArgc == 0) { + // + // Divide in half for old shell. Must be string length not size. + // + Size /=2; // Divide in half only when no redirection. + } + return (mEfiShellInterface->StdOut->Write(mEfiShellInterface->StdOut, &Size, (VOID*)String)); + } + ASSERT(FALSE); + return (EFI_UNSUPPORTED); +} + +/** + Print at a specific location on the screen. + + This function will move the cursor to a given screen location and print the specified string + + If -1 is specified for either the Row or Col the current screen location for BOTH + will be used. + + if either Row or Col is out of range for the current console, then ASSERT + if Format is NULL, then ASSERT + + In addition to the standard %-based flags as supported by UefiLib Print() this supports + the following additional flags: + %N - Set output attribute to normal + %H - Set output attribute to highlight + %E - Set output attribute to error + %B - Set output attribute to blue color + %V - Set output attribute to green color + + Note: The background color is controlled by the shell command cls. + + @param[in] Col the column to print at + @param[in] Row the row to print at + @param[in] Format the format string + @param[in] Marker the marker for the variable argument list + + @return EFI_SUCCESS The operation was successful. + @return EFI_DEVICE_ERROR The console device reported an error. +**/ +EFI_STATUS +InternalShellPrintWorker( + IN INT32 Col OPTIONAL, + IN INT32 Row OPTIONAL, + IN CONST CHAR16 *Format, + IN VA_LIST Marker + ) +{ + EFI_STATUS Status; + CHAR16 *ResumeLocation; + CHAR16 *FormatWalker; + UINTN OriginalAttribute; + CHAR16 *mPostReplaceFormat; + CHAR16 *mPostReplaceFormat2; + + mPostReplaceFormat = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize)); + mPostReplaceFormat2 = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize)); + + if (mPostReplaceFormat == NULL || mPostReplaceFormat2 == NULL) { + SHELL_FREE_NON_NULL(mPostReplaceFormat); + SHELL_FREE_NON_NULL(mPostReplaceFormat2); + return (EFI_OUT_OF_RESOURCES); + } + + Status = EFI_SUCCESS; + OriginalAttribute = gST->ConOut->Mode->Attribute; + + // + // Back and forth each time fixing up 1 of our flags... + // + Status = ShellCopySearchAndReplace(Format, mPostReplaceFormat, PcdGet16 (PcdShellPrintBufferSize), L"%N", L"%%N", FALSE, FALSE); + ASSERT_EFI_ERROR(Status); + Status = ShellCopySearchAndReplace(mPostReplaceFormat, mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%E", L"%%E", FALSE, FALSE); + ASSERT_EFI_ERROR(Status); + Status = ShellCopySearchAndReplace(mPostReplaceFormat2, mPostReplaceFormat, PcdGet16 (PcdShellPrintBufferSize), L"%H", L"%%H", FALSE, FALSE); + ASSERT_EFI_ERROR(Status); + Status = ShellCopySearchAndReplace(mPostReplaceFormat, mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%B", L"%%B", FALSE, FALSE); + ASSERT_EFI_ERROR(Status); + Status = ShellCopySearchAndReplace(mPostReplaceFormat2, mPostReplaceFormat, PcdGet16 (PcdShellPrintBufferSize), L"%V", L"%%V", FALSE, FALSE); + ASSERT_EFI_ERROR(Status); + + // + // Use the last buffer from replacing to print from... + // + UnicodeVSPrint (mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), mPostReplaceFormat, Marker); + + if (Col != -1 && Row != -1) { + Status = gST->ConOut->SetCursorPosition(gST->ConOut, Col, Row); + } + + FormatWalker = mPostReplaceFormat2; + while (*FormatWalker != CHAR_NULL) { + // + // Find the next attribute change request + // + ResumeLocation = StrStr(FormatWalker, L"%"); + if (ResumeLocation != NULL) { + *ResumeLocation = CHAR_NULL; + } + // + // print the current FormatWalker string + // + if (StrLen(FormatWalker)>0) { + Status = InternalPrintTo(FormatWalker); + if (EFI_ERROR(Status)) { + break; + } + } + + // + // update the attribute + // + if (ResumeLocation != NULL) { + if ((ResumeLocation != mPostReplaceFormat2) && (*(ResumeLocation-1) == L'^')) { + // + // Move cursor back 1 position to overwrite the ^ + // + gST->ConOut->SetCursorPosition(gST->ConOut, gST->ConOut->Mode->CursorColumn - 1, gST->ConOut->Mode->CursorRow); + + // + // Print a simple '%' symbol + // + Status = InternalPrintTo(L"%"); + ResumeLocation = ResumeLocation - 1; + } else { + switch (*(ResumeLocation+1)) { + case (L'N'): + gST->ConOut->SetAttribute(gST->ConOut, OriginalAttribute); + break; + case (L'E'): + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_YELLOW, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))); + break; + case (L'H'): + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_WHITE, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))); + break; + case (L'B'): + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_LIGHTBLUE, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))); + break; + case (L'V'): + gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_LIGHTGREEN, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4))); + break; + default: + // + // Print a simple '%' symbol + // + Status = InternalPrintTo(L"%"); + if (EFI_ERROR(Status)) { + break; + } + ResumeLocation = ResumeLocation - 1; + break; + } + } + } else { + // + // reset to normal now... + // + break; + } + + // + // update FormatWalker to Resume + 2 (skip the % and the indicator) + // + FormatWalker = ResumeLocation + 2; + } + + gST->ConOut->SetAttribute(gST->ConOut, OriginalAttribute); + + SHELL_FREE_NON_NULL(mPostReplaceFormat); + SHELL_FREE_NON_NULL(mPostReplaceFormat2); + return (Status); +} + +/** + Print at a specific location on the screen. + + This function will move the cursor to a given screen location and print the specified string. + + If -1 is specified for either the Row or Col the current screen location for BOTH + will be used. + + If either Row or Col is out of range for the current console, then ASSERT. + If Format is NULL, then ASSERT. + + In addition to the standard %-based flags as supported by UefiLib Print() this supports + the following additional flags: + %N - Set output attribute to normal + %H - Set output attribute to highlight + %E - Set output attribute to error + %B - Set output attribute to blue color + %V - Set output attribute to green color + + Note: The background color is controlled by the shell command cls. + + @param[in] Col the column to print at + @param[in] Row the row to print at + @param[in] Format the format string + @param[in] ... The variable argument list. + + @return EFI_SUCCESS The printing was successful. + @return EFI_DEVICE_ERROR The console device reported an error. +**/ +EFI_STATUS +EFIAPI +ShellPrintEx( + IN INT32 Col OPTIONAL, + IN INT32 Row OPTIONAL, + IN CONST CHAR16 *Format, + ... + ) +{ + VA_LIST Marker; + EFI_STATUS RetVal; + if (Format == NULL) { + return (EFI_INVALID_PARAMETER); + } + VA_START (Marker, Format); + RetVal = InternalShellPrintWorker(Col, Row, Format, Marker); + VA_END(Marker); + return(RetVal); +} + +/** + Print at a specific location on the screen. + + This function will move the cursor to a given screen location and print the specified string. + + If -1 is specified for either the Row or Col the current screen location for BOTH + will be used. + + If either Row or Col is out of range for the current console, then ASSERT. + If Format is NULL, then ASSERT. + + In addition to the standard %-based flags as supported by UefiLib Print() this supports + the following additional flags: + %N - Set output attribute to normal. + %H - Set output attribute to highlight. + %E - Set output attribute to error. + %B - Set output attribute to blue color. + %V - Set output attribute to green color. + + Note: The background color is controlled by the shell command cls. + + @param[in] Col The column to print at. + @param[in] Row The row to print at. + @param[in] Language The language of the string to retrieve. If this parameter + is NULL, then the current platform language is used. + @param[in] HiiFormatStringId The format string Id for getting from Hii. + @param[in] HiiFormatHandle The format string Handle for getting from Hii. + @param[in] ... The variable argument list. + + @return EFI_SUCCESS The printing was successful. + @return EFI_DEVICE_ERROR The console device reported an error. +**/ +EFI_STATUS +EFIAPI +ShellPrintHiiEx( + IN INT32 Col OPTIONAL, + IN INT32 Row OPTIONAL, + IN CONST CHAR8 *Language OPTIONAL, + IN CONST EFI_STRING_ID HiiFormatStringId, + IN CONST EFI_HII_HANDLE HiiFormatHandle, + ... + ) +{ + VA_LIST Marker; + CHAR16 *HiiFormatString; + EFI_STATUS RetVal; + + RetVal = EFI_DEVICE_ERROR; + + VA_START (Marker, HiiFormatHandle); + HiiFormatString = HiiGetString(HiiFormatHandle, HiiFormatStringId, Language); + if (HiiFormatString != NULL) { + RetVal = InternalShellPrintWorker (Col, Row, HiiFormatString, Marker); + SHELL_FREE_NON_NULL (HiiFormatString); + } + VA_END(Marker); + + return (RetVal); +} + +/** + Function to determine if a given filename represents a file or a directory. + + @param[in] DirName Path to directory to test. + + @retval EFI_SUCCESS The Path represents a directory + @retval EFI_NOT_FOUND The Path does not represent a directory + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @return The path failed to open +**/ +EFI_STATUS +EFIAPI +ShellIsDirectory( + IN CONST CHAR16 *DirName + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE Handle; + CHAR16 *TempLocation; + CHAR16 *TempLocation2; + + ASSERT(DirName != NULL); + + Handle = NULL; + TempLocation = NULL; + + Status = ShellOpenFileByName(DirName, &Handle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(Status)) { + // + // try good logic first. + // + if (gEfiShellProtocol != NULL) { + TempLocation = StrnCatGrow(&TempLocation, NULL, DirName, 0); + if (TempLocation == NULL) { + ShellCloseFile(&Handle); + return (EFI_OUT_OF_RESOURCES); + } + TempLocation2 = StrStr(TempLocation, L":"); + if (TempLocation2 != NULL && StrLen(StrStr(TempLocation, L":")) == 2) { + *(TempLocation2+1) = CHAR_NULL; + } + if (gEfiShellProtocol->GetDevicePathFromMap(TempLocation) != NULL) { + FreePool(TempLocation); + return (EFI_SUCCESS); + } + FreePool(TempLocation); + } else { + // + // probably a map name?!?!!? + // + TempLocation = StrStr(DirName, L"\\"); + if (TempLocation != NULL && *(TempLocation+1) == CHAR_NULL) { + return (EFI_SUCCESS); + } + } + return (Status); + } + + if (FileHandleIsDirectory(Handle) == EFI_SUCCESS) { + ShellCloseFile(&Handle); + return (EFI_SUCCESS); + } + ShellCloseFile(&Handle); + return (EFI_NOT_FOUND); +} + +/** + Function to determine if a given filename represents a file. + + @param[in] Name Path to file to test. + + @retval EFI_SUCCESS The Path represents a file. + @retval EFI_NOT_FOUND The Path does not represent a file. + @retval other The path failed to open. +**/ +EFI_STATUS +EFIAPI +ShellIsFile( + IN CONST CHAR16 *Name + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE Handle; + + ASSERT(Name != NULL); + + Handle = NULL; + + Status = ShellOpenFileByName(Name, &Handle, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(Status)) { + return (Status); + } + + if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) { + ShellCloseFile(&Handle); + return (EFI_SUCCESS); + } + ShellCloseFile(&Handle); + return (EFI_NOT_FOUND); +} + +/** + Function to determine if a given filename represents a file. + + This will search the CWD and then the Path. + + If Name is NULL, then ASSERT. + + @param[in] Name Path to file to test. + + @retval EFI_SUCCESS The Path represents a file. + @retval EFI_NOT_FOUND The Path does not represent a file. + @retval other The path failed to open. +**/ +EFI_STATUS +EFIAPI +ShellIsFileInPath( + IN CONST CHAR16 *Name + ) +{ + CHAR16 *NewName; + EFI_STATUS Status; + + if (!EFI_ERROR(ShellIsFile(Name))) { + return (EFI_SUCCESS); + } + + NewName = ShellFindFilePath(Name); + if (NewName == NULL) { + return (EFI_NOT_FOUND); + } + Status = ShellIsFile(NewName); + FreePool(NewName); + return (Status); +} + +/** + Function return the number converted from a hex representation of a number. + + Note: this function cannot be used when (UINTN)(-1), (0xFFFFFFFF) may be a valid + result. Use ShellConvertStringToUint64 instead. + + @param[in] String String representation of a number. + + @return The unsigned integer result of the conversion. + @retval (UINTN)(-1) An error occurred. +**/ +UINTN +EFIAPI +ShellHexStrToUintn( + IN CONST CHAR16 *String + ) +{ + UINT64 RetVal; + + if (!EFI_ERROR(ShellConvertStringToUint64(String, &RetVal, TRUE, TRUE))) { + return ((UINTN)RetVal); + } + + return ((UINTN)(-1)); +} + +/** + Function to determine whether a string is decimal or hex representation of a number + and return the number converted from the string. Spaces are always skipped. + + @param[in] String String representation of a number + + @return the number + @retval (UINTN)(-1) An error ocurred. +**/ +UINTN +EFIAPI +ShellStrToUintn( + IN CONST CHAR16 *String + ) +{ + UINT64 RetVal; + BOOLEAN Hex; + + Hex = FALSE; + + if (!InternalShellIsHexOrDecimalNumber(String, Hex, TRUE, FALSE)) { + Hex = TRUE; + } + + if (!EFI_ERROR(ShellConvertStringToUint64(String, &RetVal, Hex, TRUE))) { + return ((UINTN)RetVal); + } + return ((UINTN)(-1)); +} + +/** + Safely append with automatic string resizing given length of Destination and + desired length of copy from Source. + + append the first D characters of Source to the end of Destination, where D is + the lesser of Count and the StrLen() of Source. If appending those D characters + will fit within Destination (whose Size is given as CurrentSize) and + still leave room for a NULL terminator, then those characters are appended, + starting at the original terminating NULL of Destination, and a new terminating + NULL is appended. + + If appending D characters onto Destination will result in a overflow of the size + given in CurrentSize the string will be grown such that the copy can be performed + and CurrentSize will be updated to the new size. + + If Source is NULL, there is nothing to append, just return the current buffer in + Destination. + + if Destination is NULL, then ASSERT() + if Destination's current length (including NULL terminator) is already more then + CurrentSize, then ASSERT() + + @param[in, out] Destination The String to append onto + @param[in, out] CurrentSize on call the number of bytes in Destination. On + return possibly the new size (still in bytes). if NULL + then allocate whatever is needed. + @param[in] Source The String to append from + @param[in] Count Maximum number of characters to append. if 0 then + all are appended. + + @return Destination return the resultant string. +**/ +CHAR16* +EFIAPI +StrnCatGrow ( + IN OUT CHAR16 **Destination, + IN OUT UINTN *CurrentSize, + IN CONST CHAR16 *Source, + IN UINTN Count + ) +{ + UINTN DestinationStartSize; + UINTN NewSize; + + // + // ASSERTs + // + ASSERT(Destination != NULL); + + // + // If there's nothing to do then just return Destination + // + if (Source == NULL) { + return (*Destination); + } + + // + // allow for un-initialized pointers, based on size being 0 + // + if (CurrentSize != NULL && *CurrentSize == 0) { + *Destination = NULL; + } + + // + // allow for NULL pointers address as Destination + // + if (*Destination != NULL) { + ASSERT(CurrentSize != 0); + DestinationStartSize = StrSize(*Destination); + ASSERT(DestinationStartSize <= *CurrentSize); + } else { + DestinationStartSize = 0; +// ASSERT(*CurrentSize == 0); + } + + // + // Append all of Source? + // + if (Count == 0) { + Count = StrLen(Source); + } + + // + // Test and grow if required + // + if (CurrentSize != NULL) { + NewSize = *CurrentSize; + if (NewSize < DestinationStartSize + (Count * sizeof(CHAR16))) { + while (NewSize < (DestinationStartSize + (Count*sizeof(CHAR16)))) { + NewSize += 2 * Count * sizeof(CHAR16); + } + *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination); + *CurrentSize = NewSize; + } + } else { + NewSize = (Count+1)*sizeof(CHAR16); + *Destination = AllocateZeroPool(NewSize); + } + + // + // Now use standard StrnCat on a big enough buffer + // + if (*Destination == NULL) { + return (NULL); + } + + StrnCatS(*Destination, NewSize/sizeof(CHAR16), Source, Count); + return *Destination; +} + +/** + Prompt the user and return the resultant answer to the requestor. + + This function will display the requested question on the shell prompt and then + wait for an appropriate answer to be input from the console. + + if the SHELL_PROMPT_REQUEST_TYPE is SHELL_PROMPT_REQUEST_TYPE_YESNO, ShellPromptResponseTypeQuitContinue + or SHELL_PROMPT_REQUEST_TYPE_YESNOCANCEL then *Response is of type SHELL_PROMPT_RESPONSE. + + if the SHELL_PROMPT_REQUEST_TYPE is ShellPromptResponseTypeFreeform then *Response is of type + CHAR16*. + + In either case *Response must be callee freed if Response was not NULL; + + @param Type What type of question is asked. This is used to filter the input + to prevent invalid answers to question. + @param Prompt Pointer to string prompt to use to request input. + @param Response Pointer to Response which will be populated upon return. + + @retval EFI_SUCCESS The operation was sucessful. + @retval EFI_UNSUPPORTED The operation is not supported as requested. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @return other The operation failed. +**/ +EFI_STATUS +EFIAPI +ShellPromptForResponse ( + IN SHELL_PROMPT_REQUEST_TYPE Type, + IN CHAR16 *Prompt OPTIONAL, + IN OUT VOID **Response OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + UINTN EventIndex; + SHELL_PROMPT_RESPONSE *Resp; + UINTN Size; + CHAR16 *Buffer; + + Status = EFI_UNSUPPORTED; + Resp = NULL; + Buffer = NULL; + Size = 0; + if (Type != ShellPromptResponseTypeFreeform) { + Resp = (SHELL_PROMPT_RESPONSE*)AllocateZeroPool(sizeof(SHELL_PROMPT_RESPONSE)); + if (Resp == NULL) { + if (Response != NULL) { + *Response = NULL; + } + return (EFI_OUT_OF_RESOURCES); + } + } + + switch(Type) { + case ShellPromptResponseTypeQuitContinue: + if (Prompt != NULL) { + ShellPrintEx(-1, -1, L"%s", Prompt); + } + // + // wait for valid response + // + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR(Status)) { + break; + } + ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar); + if (Key.UnicodeChar == L'Q' || Key.UnicodeChar ==L'q') { + *Resp = ShellPromptResponseQuit; + } else { + *Resp = ShellPromptResponseContinue; + } + break; + case ShellPromptResponseTypeYesNoCancel: + if (Prompt != NULL) { + ShellPrintEx(-1, -1, L"%s", Prompt); + } + // + // wait for valid response + // + *Resp = ShellPromptResponseMax; + while (*Resp == ShellPromptResponseMax) { + if (ShellGetExecutionBreakFlag()) { + Status = EFI_ABORTED; + break; + } + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR(Status)) { + break; + } + ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar); + switch (Key.UnicodeChar) { + case L'Y': + case L'y': + *Resp = ShellPromptResponseYes; + break; + case L'N': + case L'n': + *Resp = ShellPromptResponseNo; + break; + case L'C': + case L'c': + *Resp = ShellPromptResponseCancel; + break; + } + } + break; + case ShellPromptResponseTypeYesNoAllCancel: + if (Prompt != NULL) { + ShellPrintEx(-1, -1, L"%s", Prompt); + } + // + // wait for valid response + // + *Resp = ShellPromptResponseMax; + while (*Resp == ShellPromptResponseMax) { + if (ShellGetExecutionBreakFlag()) { + Status = EFI_ABORTED; + break; + } + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR(Status)) { + break; + } + + if (Key.UnicodeChar <= 127 && Key.UnicodeChar >= 32) { + ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar); + } + + switch (Key.UnicodeChar) { + case L'Y': + case L'y': + *Resp = ShellPromptResponseYes; + break; + case L'N': + case L'n': + *Resp = ShellPromptResponseNo; + break; + case L'A': + case L'a': + *Resp = ShellPromptResponseAll; + break; + case L'C': + case L'c': + *Resp = ShellPromptResponseCancel; + break; + } + } + break; + case ShellPromptResponseTypeEnterContinue: + case ShellPromptResponseTypeAnyKeyContinue: + if (Prompt != NULL) { + ShellPrintEx(-1, -1, L"%s", Prompt); + } + // + // wait for valid response + // + *Resp = ShellPromptResponseMax; + while (*Resp == ShellPromptResponseMax) { + if (ShellGetExecutionBreakFlag()) { + Status = EFI_ABORTED; + break; + } + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + if (Type == ShellPromptResponseTypeEnterContinue) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR(Status)) { + break; + } + ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar); + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + *Resp = ShellPromptResponseContinue; + break; + } + } + if (Type == ShellPromptResponseTypeAnyKeyContinue) { + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + ASSERT_EFI_ERROR(Status); + *Resp = ShellPromptResponseContinue; + break; + } + } + break; + case ShellPromptResponseTypeYesNo: + if (Prompt != NULL) { + ShellPrintEx(-1, -1, L"%s", Prompt); + } + // + // wait for valid response + // + *Resp = ShellPromptResponseMax; + while (*Resp == ShellPromptResponseMax) { + if (ShellGetExecutionBreakFlag()) { + Status = EFI_ABORTED; + break; + } + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR(Status)) { + break; + } + ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar); + switch (Key.UnicodeChar) { + case L'Y': + case L'y': + *Resp = ShellPromptResponseYes; + break; + case L'N': + case L'n': + *Resp = ShellPromptResponseNo; + break; + } + } + break; + case ShellPromptResponseTypeFreeform: + if (Prompt != NULL) { + ShellPrintEx(-1, -1, L"%s", Prompt); + } + while(1) { + if (ShellGetExecutionBreakFlag()) { + Status = EFI_ABORTED; + break; + } + gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex); + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (EFI_ERROR(Status)) { + break; + } + ShellPrintEx(-1, -1, L"%c", Key.UnicodeChar); + if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) { + break; + } + ASSERT((Buffer == NULL && Size == 0) || (Buffer != NULL)); + StrnCatGrow(&Buffer, &Size, &Key.UnicodeChar, 1); + } + break; + // + // This is the location to add new prompt types. + // If your new type loops remember to add ExecutionBreak support. + // + default: + ASSERT(FALSE); + } + + if (Response != NULL) { + if (Resp != NULL) { + *Response = Resp; + } else if (Buffer != NULL) { + *Response = Buffer; + } else { + *Response = NULL; + } + } else { + if (Resp != NULL) { + FreePool(Resp); + } + if (Buffer != NULL) { + FreePool(Buffer); + } + } + + ShellPrintEx(-1, -1, L"\r\n"); + return (Status); +} + +/** + Prompt the user and return the resultant answer to the requestor. + + This function is the same as ShellPromptForResponse, except that the prompt is + automatically pulled from HII. + + @param Type What type of question is asked. This is used to filter the input + to prevent invalid answers to question. + @param[in] HiiFormatStringId The format string Id for getting from Hii. + @param[in] HiiFormatHandle The format string Handle for getting from Hii. + @param Response Pointer to Response which will be populated upon return. + + @retval EFI_SUCCESS the operation was sucessful. + @return other the operation failed. + + @sa ShellPromptForResponse +**/ +EFI_STATUS +EFIAPI +ShellPromptForResponseHii ( + IN SHELL_PROMPT_REQUEST_TYPE Type, + IN CONST EFI_STRING_ID HiiFormatStringId, + IN CONST EFI_HII_HANDLE HiiFormatHandle, + IN OUT VOID **Response + ) +{ + CHAR16 *Prompt; + EFI_STATUS Status; + + Prompt = HiiGetString(HiiFormatHandle, HiiFormatStringId, NULL); + Status = ShellPromptForResponse(Type, Prompt, Response); + FreePool(Prompt); + return (Status); +} + +/** + Function to determin if an entire string is a valid number. + + If Hex it must be preceeded with a 0x or has ForceHex, set TRUE. + + @param[in] String The string to evaluate. + @param[in] ForceHex TRUE - always assume hex. + @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. + @param[in] TimeNumbers TRUE to allow numbers with ":", FALSE otherwise. + + @retval TRUE It is all numeric (dec/hex) characters. + @retval FALSE There is a non-numeric character. +**/ +BOOLEAN +InternalShellIsHexOrDecimalNumber ( + IN CONST CHAR16 *String, + IN CONST BOOLEAN ForceHex, + IN CONST BOOLEAN StopAtSpace, + IN CONST BOOLEAN TimeNumbers + ) +{ + BOOLEAN Hex; + BOOLEAN LeadingZero; + + if (String == NULL) { + return FALSE; + } + + // + // chop off a single negative sign + // + if (*String == L'-') { + String++; + } + + if (*String == CHAR_NULL) { + return FALSE; + } + + // + // chop leading zeroes + // + LeadingZero = FALSE; + while(*String == L'0'){ + String++; + LeadingZero = TRUE; + } + // + // allow '0x' or '0X', but not 'x' or 'X' + // + if (*String == L'x' || *String == L'X') { + if (!LeadingZero) { + // + // we got an x without a preceeding 0 + // + return (FALSE); + } + String++; + Hex = TRUE; + } else if (ForceHex) { + Hex = TRUE; + } else { + Hex = FALSE; + } + + // + // loop through the remaining characters and use the lib function + // + for ( ; *String != CHAR_NULL && !(StopAtSpace && *String == L' ') ; String++){ + if (TimeNumbers && (String[0] == L':')) { + continue; + } + if (Hex) { + if (!ShellIsHexaDecimalDigitCharacter(*String)) { + return (FALSE); + } + } else { + if (!ShellIsDecimalDigitCharacter(*String)) { + return (FALSE); + } + } + } + + return (TRUE); +} + +/** + Function to determine if a given filename exists. + + @param[in] Name Path to test. + + @retval EFI_SUCCESS The Path represents a file. + @retval EFI_NOT_FOUND The Path does not represent a file. + @retval other The path failed to open. +**/ +EFI_STATUS +EFIAPI +ShellFileExists( + IN CONST CHAR16 *Name + ) +{ + EFI_STATUS Status; + EFI_SHELL_FILE_INFO *List; + + ASSERT(Name != NULL); + + List = NULL; + Status = ShellOpenFileMetaArg((CHAR16*)Name, EFI_FILE_MODE_READ, &List); + if (EFI_ERROR(Status)) { + return (Status); + } + + ShellCloseFileMetaArg(&List); + + return (EFI_SUCCESS); +} + +/** + Convert a Unicode character to numerical value. + + This internal function only deal with Unicode character + which maps to a valid hexadecimal ASII character, i.e. + L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other + Unicode character, the value returned does not make sense. + + @param Char The character to convert. + + @return The numerical value converted. + +**/ +UINTN +InternalShellHexCharToUintn ( + IN CHAR16 Char + ) +{ + if (ShellIsDecimalDigitCharacter (Char)) { + return Char - L'0'; + } + + return (10 + CharToUpper (Char) - L'A'); +} + +/** + Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64. + + This function returns a value of type UINT64 by interpreting the contents + of the Unicode string specified by String as a hexadecimal number. + The format of the input Unicode string String is: + + [spaces][zeros][x][hexadecimal digits]. + + The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F]. + The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix. + If "x" appears in the input string, it must be prefixed with at least one 0. + The function will ignore the pad space, which includes spaces or tab characters, + before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or + [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the + first valid hexadecimal digit. Then, the function stops at the first character that is + a not a valid hexadecimal character or NULL, whichever one comes first. + + If String has only pad spaces, then zero is returned. + If String has no leading pad spaces, leading zeros or valid hexadecimal digits, + then zero is returned. + + @param[in] String A pointer to a Null-terminated Unicode string. + @param[out] Value Upon a successful return the value of the conversion. + @param[in] StopAtSpace FALSE to skip spaces. + + @retval EFI_SUCCESS The conversion was successful. + @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. + @retval EFI_DEVICE_ERROR An overflow occurred. +**/ +EFI_STATUS +InternalShellStrHexToUint64 ( + IN CONST CHAR16 *String, + OUT UINT64 *Value, + IN CONST BOOLEAN StopAtSpace + ) +{ + UINT64 Result; + + if (String == NULL || StrSize(String) == 0 || Value == NULL) { + return (EFI_INVALID_PARAMETER); + } + + // + // Ignore the pad spaces (space or tab) + // + while ((*String == L' ') || (*String == L'\t')) { + String++; + } + + // + // Ignore leading Zeros after the spaces + // + while (*String == L'0') { + String++; + } + + if (CharToUpper (*String) == L'X') { + if (*(String - 1) != L'0') { + return 0; + } + // + // Skip the 'X' + // + String++; + } + + Result = 0; + + // + // there is a space where there should't be + // + if (*String == L' ') { + return (EFI_INVALID_PARAMETER); + } + + while (ShellIsHexaDecimalDigitCharacter (*String)) { + // + // If the Hex Number represented by String overflows according + // to the range defined by UINT64, then return EFI_DEVICE_ERROR. + // + if (!(Result <= (RShiftU64((((UINT64) ~0) - InternalShellHexCharToUintn (*String)), 4)))) { +// if (!(Result <= ((((UINT64) ~0) - InternalShellHexCharToUintn (*String)) >> 4))) { + return (EFI_DEVICE_ERROR); + } + + Result = (LShiftU64(Result, 4)); + Result += InternalShellHexCharToUintn (*String); + String++; + + // + // stop at spaces if requested + // + if (StopAtSpace && *String == L' ') { + break; + } + } + + *Value = Result; + return (EFI_SUCCESS); +} + +/** + Convert a Null-terminated Unicode decimal string to a value of + type UINT64. + + This function returns a value of type UINT64 by interpreting the contents + of the Unicode string specified by String as a decimal number. The format + of the input Unicode string String is: + + [spaces] [decimal digits]. + + The valid decimal digit character is in the range [0-9]. The + function will ignore the pad space, which includes spaces or + tab characters, before [decimal digits]. The running zero in the + beginning of [decimal digits] will be ignored. Then, the function + stops at the first character that is a not a valid decimal character + or a Null-terminator, whichever one comes first. + + If String has only pad spaces, then 0 is returned. + If String has no pad spaces or valid decimal digits, + then 0 is returned. + + @param[in] String A pointer to a Null-terminated Unicode string. + @param[out] Value Upon a successful return the value of the conversion. + @param[in] StopAtSpace FALSE to skip spaces. + + @retval EFI_SUCCESS The conversion was successful. + @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid. + @retval EFI_DEVICE_ERROR An overflow occurred. +**/ +EFI_STATUS +InternalShellStrDecimalToUint64 ( + IN CONST CHAR16 *String, + OUT UINT64 *Value, + IN CONST BOOLEAN StopAtSpace + ) +{ + UINT64 Result; + + if (String == NULL || StrSize (String) == 0 || Value == NULL) { + return (EFI_INVALID_PARAMETER); + } + + // + // Ignore the pad spaces (space or tab) + // + while ((*String == L' ') || (*String == L'\t')) { + String++; + } + + // + // Ignore leading Zeros after the spaces + // + while (*String == L'0') { + String++; + } + + Result = 0; + + // + // Stop upon space if requested + // (if the whole value was 0) + // + if (StopAtSpace && *String == L' ') { + *Value = Result; + return (EFI_SUCCESS); + } + + while (ShellIsDecimalDigitCharacter (*String)) { + // + // If the number represented by String overflows according + // to the range defined by UINT64, then return EFI_DEVICE_ERROR. + // + + if (!(Result <= (DivU64x32((((UINT64) ~0) - (*String - L'0')),10)))) { + return (EFI_DEVICE_ERROR); + } + + Result = MultU64x32(Result, 10) + (*String - L'0'); + String++; + + // + // Stop at spaces if requested + // + if (StopAtSpace && *String == L' ') { + break; + } + } + + *Value = Result; + + return (EFI_SUCCESS); +} + +/** + Function to verify and convert a string to its numerical value. + + If Hex it must be preceeded with a 0x, 0X, or has ForceHex set TRUE. + + @param[in] String The string to evaluate. + @param[out] Value Upon a successful return the value of the conversion. + @param[in] ForceHex TRUE - always assume hex. + @param[in] StopAtSpace FALSE to skip spaces. + + @retval EFI_SUCCESS The conversion was successful. + @retval EFI_INVALID_PARAMETER String contained an invalid character. + @retval EFI_NOT_FOUND String was a number, but Value was NULL. +**/ +EFI_STATUS +EFIAPI +ShellConvertStringToUint64( + IN CONST CHAR16 *String, + OUT UINT64 *Value, + IN CONST BOOLEAN ForceHex, + IN CONST BOOLEAN StopAtSpace + ) +{ + UINT64 RetVal; + CONST CHAR16 *Walker; + EFI_STATUS Status; + BOOLEAN Hex; + + Hex = ForceHex; + + if (!InternalShellIsHexOrDecimalNumber(String, Hex, StopAtSpace, FALSE)) { + if (!Hex) { + Hex = TRUE; + if (!InternalShellIsHexOrDecimalNumber(String, Hex, StopAtSpace, FALSE)) { + return (EFI_INVALID_PARAMETER); + } + } else { + return (EFI_INVALID_PARAMETER); + } + } + + // + // Chop off leading spaces + // + for (Walker = String; Walker != NULL && *Walker != CHAR_NULL && *Walker == L' '; Walker++); + + // + // make sure we have something left that is numeric. + // + if (Walker == NULL || *Walker == CHAR_NULL || !InternalShellIsHexOrDecimalNumber(Walker, Hex, StopAtSpace, FALSE)) { + return (EFI_INVALID_PARAMETER); + } + + // + // do the conversion. + // + if (Hex || StrnCmp(Walker, L"0x", 2) == 0 || StrnCmp(Walker, L"0X", 2) == 0){ + Status = InternalShellStrHexToUint64(Walker, &RetVal, StopAtSpace); + } else { + Status = InternalShellStrDecimalToUint64(Walker, &RetVal, StopAtSpace); + } + + if (Value == NULL && !EFI_ERROR(Status)) { + return (EFI_NOT_FOUND); + } + + if (Value != NULL) { + *Value = RetVal; + } + + return (Status); +} + +/** + Function to determin if an entire string is a valid number. + + If Hex it must be preceeded with a 0x or has ForceHex, set TRUE. + + @param[in] String The string to evaluate. + @param[in] ForceHex TRUE - always assume hex. + @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. + + @retval TRUE It is all numeric (dec/hex) characters. + @retval FALSE There is a non-numeric character. +**/ +BOOLEAN +EFIAPI +ShellIsHexOrDecimalNumber ( + IN CONST CHAR16 *String, + IN CONST BOOLEAN ForceHex, + IN CONST BOOLEAN StopAtSpace + ) +{ + if (ShellConvertStringToUint64(String, NULL, ForceHex, StopAtSpace) == EFI_NOT_FOUND) { + return (TRUE); + } + return (FALSE); +} + +/** + Function to read a single line from a SHELL_FILE_HANDLE. The \n is not included in the returned + buffer. The returned buffer must be callee freed. + + If the position upon start is 0, then the Ascii Boolean will be set. This should be + maintained and not changed for all operations with the same file. + + @param[in] Handle SHELL_FILE_HANDLE to read from. + @param[in, out] Ascii Boolean value for indicating whether the file is + Ascii (TRUE) or UCS2 (FALSE). + + @return The line of text from the file. + @retval NULL There was not enough memory available. + + @sa ShellFileHandleReadLine +**/ +CHAR16* +EFIAPI +ShellFileHandleReturnLine( + IN SHELL_FILE_HANDLE Handle, + IN OUT BOOLEAN *Ascii + ) +{ + CHAR16 *RetVal; + UINTN Size; + EFI_STATUS Status; + + Size = 0; + RetVal = NULL; + + Status = ShellFileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii); + if (Status == EFI_BUFFER_TOO_SMALL) { + RetVal = AllocateZeroPool(Size); + if (RetVal == NULL) { + return (NULL); + } + Status = ShellFileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii); + + } + if (Status == EFI_END_OF_FILE && RetVal != NULL && *RetVal != CHAR_NULL) { + Status = EFI_SUCCESS; + } + if (EFI_ERROR(Status) && (RetVal != NULL)) { + FreePool(RetVal); + RetVal = NULL; + } + return (RetVal); +} + +/** + Function to read a single line (up to but not including the \n) from a SHELL_FILE_HANDLE. + + If the position upon start is 0, then the Ascii Boolean will be set. This should be + maintained and not changed for all operations with the same file. + + NOTE: LINES THAT ARE RETURNED BY THIS FUNCTION ARE UCS2, EVEN IF THE FILE BEING READ + IS IN ASCII FORMAT. + + @param[in] Handle SHELL_FILE_HANDLE to read from. + @param[in, out] Buffer The pointer to buffer to read into. If this function + returns EFI_SUCCESS, then on output Buffer will + contain a UCS2 string, even if the file being + read is ASCII. + @param[in, out] Size On input, pointer to number of bytes in Buffer. + On output, unchanged unless Buffer is too small + to contain the next line of the file. In that + case Size is set to the number of bytes needed + to hold the next line of the file (as a UCS2 + string, even if it is an ASCII file). + @param[in] Truncate If the buffer is large enough, this has no effect. + If the buffer is is too small and Truncate is TRUE, + the line will be truncated. + If the buffer is is too small and Truncate is FALSE, + then no read will occur. + + @param[in, out] Ascii Boolean value for indicating whether the file is + Ascii (TRUE) or UCS2 (FALSE). + + @retval EFI_SUCCESS The operation was successful. The line is stored in + Buffer. + @retval EFI_END_OF_FILE There are no more lines in the file. + @retval EFI_INVALID_PARAMETER Handle was NULL. + @retval EFI_INVALID_PARAMETER Size was NULL. + @retval EFI_BUFFER_TOO_SMALL Size was not large enough to store the line. + Size was updated to the minimum space required. +**/ +EFI_STATUS +EFIAPI +ShellFileHandleReadLine( + IN SHELL_FILE_HANDLE Handle, + IN OUT CHAR16 *Buffer, + IN OUT UINTN *Size, + IN BOOLEAN Truncate, + IN OUT BOOLEAN *Ascii + ) +{ + EFI_STATUS Status; + CHAR16 CharBuffer; + UINTN CharSize; + UINTN CountSoFar; + UINT64 OriginalFilePosition; + + + if (Handle == NULL + ||Size == NULL + ){ + return (EFI_INVALID_PARAMETER); + } + if (Buffer == NULL) { + ASSERT(*Size == 0); + } else { + *Buffer = CHAR_NULL; + } + gEfiShellProtocol->GetFilePosition(Handle, &OriginalFilePosition); + if (OriginalFilePosition == 0) { + CharSize = sizeof(CHAR16); + Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer); + ASSERT_EFI_ERROR(Status); + if (CharBuffer == gUnicodeFileTag) { + *Ascii = FALSE; + } else { + *Ascii = TRUE; + gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition); + } + } + + if (*Ascii) { + CharSize = sizeof(CHAR8); + } else { + CharSize = sizeof(CHAR16); + } + for (CountSoFar = 0;;CountSoFar++){ + CharBuffer = 0; + Status = gEfiShellProtocol->ReadFile(Handle, &CharSize, &CharBuffer); + if ( EFI_ERROR(Status) + || CharSize == 0 + || (CharBuffer == L'\n' && !(*Ascii)) + || (CharBuffer == '\n' && *Ascii) + ){ + if (CharSize == 0) { + Status = EFI_END_OF_FILE; + } + break; + } + // + // if we have space save it... + // + if ((CountSoFar+1)*sizeof(CHAR16) < *Size){ + ASSERT(Buffer != NULL); + ((CHAR16*)Buffer)[CountSoFar] = CharBuffer; + ((CHAR16*)Buffer)[CountSoFar+1] = CHAR_NULL; + } + } + + // + // if we ran out of space tell when... + // + if ((CountSoFar+1)*sizeof(CHAR16) > *Size){ + *Size = (CountSoFar+1)*sizeof(CHAR16); + if (!Truncate) { + gEfiShellProtocol->SetFilePosition(Handle, OriginalFilePosition); + } else { + DEBUG((DEBUG_WARN, "The line was truncated in ShellFileHandleReadLine")); + } + return (EFI_BUFFER_TOO_SMALL); + } + while(Buffer[StrLen(Buffer)-1] == L'\r') { + Buffer[StrLen(Buffer)-1] = CHAR_NULL; + } + + return (Status); +} + +/** + Function to print help file / man page content in the spec from the UEFI Shell protocol GetHelpText function. + + @param[in] CommandToGetHelpOn Pointer to a string containing the command name of help file to be printed. + @param[in] SectionToGetHelpOn Pointer to the section specifier(s). + @param[in] PrintCommandText If TRUE, prints the command followed by the help content, otherwise prints + the help content only. + @retval EFI_DEVICE_ERROR The help data format was incorrect. + @retval EFI_NOT_FOUND The help data could not be found. + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +EFIAPI +ShellPrintHelp ( + IN CONST CHAR16 *CommandToGetHelpOn, + IN CONST CHAR16 *SectionToGetHelpOn, + IN BOOLEAN PrintCommandText + ) +{ + EFI_STATUS Status; + CHAR16 *OutText; + + OutText = NULL; + + // + // Get the string to print based + // + Status = gEfiShellProtocol->GetHelpText (CommandToGetHelpOn, SectionToGetHelpOn, &OutText); + + // + // make sure we got a valid string + // + if (EFI_ERROR(Status)){ + return Status; + } + if (OutText == NULL || StrLen(OutText) == 0) { + return EFI_NOT_FOUND; + } + + // + // Chop off trailing stuff we dont need + // + while (OutText[StrLen(OutText)-1] == L'\r' || OutText[StrLen(OutText)-1] == L'\n' || OutText[StrLen(OutText)-1] == L' ') { + OutText[StrLen(OutText)-1] = CHAR_NULL; + } + + // + // Print this out to the console + // + if (PrintCommandText) { + ShellPrintEx(-1, -1, L"%H%-14s%N- %s\r\n", CommandToGetHelpOn, OutText); + } else { + ShellPrintEx(-1, -1, L"%N%s\r\n", OutText); + } + + SHELL_FREE_NON_NULL(OutText); + + return EFI_SUCCESS; +} + +/** + Function to delete a file by name + + @param[in] FileName Pointer to file name to delete. + + @retval EFI_SUCCESS the file was deleted sucessfully + @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not + deleted + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_NOT_FOUND The specified file could not be found on the + device or the file system could not be found + on the device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the + medium is no longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the + file. + @retval other The file failed to open +**/ +EFI_STATUS +EFIAPI +ShellDeleteFileByName( + IN CONST CHAR16 *FileName + ) +{ + EFI_STATUS Status; + SHELL_FILE_HANDLE FileHandle; + + Status = ShellFileExists(FileName); + + if (Status == EFI_SUCCESS){ + Status = ShellOpenFileByName(FileName, &FileHandle, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0x0); + if (Status == EFI_SUCCESS){ + Status = ShellDeleteFile(&FileHandle); + } + } + + return(Status); + +} + +/** + Cleans off all the quotes in the string. + + @param[in] OriginalString pointer to the string to be cleaned. + @param[out] CleanString The new string with all quotes removed. + Memory allocated in the function and free + by caller. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +InternalShellStripQuotes ( + IN CONST CHAR16 *OriginalString, + OUT CHAR16 **CleanString + ) +{ + CHAR16 *Walker; + + if (OriginalString == NULL || CleanString == NULL) { + return EFI_INVALID_PARAMETER; + } + + *CleanString = AllocateCopyPool (StrSize (OriginalString), OriginalString); + if (*CleanString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Walker = *CleanString; Walker != NULL && *Walker != CHAR_NULL ; Walker++) { + if (*Walker == L'\"') { + CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0])); + } + } + + return EFI_SUCCESS; +} + diff --git a/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.h b/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.h new file mode 100644 index 000000000..919ae83e1 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.h @@ -0,0 +1,90 @@ +/** @file + Provides interface to shell functionality for shell commands and applications. + + (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _UEFI_SHELL_LIB_INTERNAL_H_ +#define _UEFI_SHELL_LIB_INTERNAL_H_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + EFI_SHELL_GET_FILE_INFO GetFileInfo; + EFI_SHELL_SET_FILE_INFO SetFileInfo; + EFI_SHELL_READ_FILE ReadFile; + EFI_SHELL_WRITE_FILE WriteFile; + EFI_SHELL_CLOSE_FILE CloseFile; + EFI_SHELL_DELETE_FILE DeleteFile; + EFI_SHELL_GET_FILE_POSITION GetFilePosition; + EFI_SHELL_SET_FILE_POSITION SetFilePosition; + EFI_SHELL_FLUSH_FILE FlushFile; + EFI_SHELL_GET_FILE_SIZE GetFileSize; +} FILE_HANDLE_FUNCTION_MAP; + +/** + Function to determin if an entire string is a valid number. + + If Hex it must be preceeded with a 0x or has ForceHex, set TRUE. + + @param[in] String The string to evaluate. + @param[in] ForceHex TRUE - always assume hex. + @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going. + @param[in] TimeNumbers TRUE to allow numbers with ":", FALSE otherwise. + + @retval TRUE It is all numeric (dec/hex) characters. + @retval FALSE There is a non-numeric character. +**/ +BOOLEAN +InternalShellIsHexOrDecimalNumber ( + IN CONST CHAR16 *String, + IN CONST BOOLEAN ForceHex, + IN CONST BOOLEAN StopAtSpace, + IN CONST BOOLEAN TimeNumbers + ); + +/** + Cleans off all the quotes in the string. + + @param[in] OriginalString pointer to the string to be cleaned. + @param[out] CleanString The new string with all quotes removed. + Memory allocated in the function and free + by caller. + + @retval EFI_SUCCESS The operation was successful. +**/ +EFI_STATUS +InternalShellStripQuotes ( + IN CONST CHAR16 *OriginalString, + OUT CHAR16 **CleanString + ); + + +#endif + diff --git a/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.inf b/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.inf new file mode 100644 index 000000000..1eeee70a3 --- /dev/null +++ b/roms/edk2/ShellPkg/Library/UefiShellLib/UefiShellLib.inf @@ -0,0 +1,65 @@ +## @file +# Provides interface to shell functionality for shell commands and applications. +# +# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = UefiShellLib + FILE_GUID = 449D0F00-2148-4a43-9836-F10B3980ECF5 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.2 + LIBRARY_CLASS = ShellLib|UEFI_APPLICATION UEFI_DRIVER DXE_RUNTIME_DRIVER DXE_DRIVER + CONSTRUCTOR = ShellLibConstructor + DESTRUCTOR = ShellLibDestructor + +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources.common] + UefiShellLib.c + UefiShellLib.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + DevicePathLib + BaseLib + BaseMemoryLib + DebugLib + FileHandleLib + PrintLib + UefiLib + HiiLib + SortLib + +[Protocols] + gEfiUnicodeCollation2ProtocolGuid ## CONSUMES + + # shell 2.0 + gEfiShellProtocolGuid ## SOMETIMES_CONSUMES + gEfiShellParametersProtocolGuid ## SOMETIMES_CONSUMES + + # 'old' shell + gEfiShellEnvironment2Guid ## SOMETIMES_CONSUMES + gEfiShellInterfaceGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## GUID + gEfiShellEnvironment2ExtGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd.common] + gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize ## CONSUMES + gEfiShellPkgTokenSpaceGuid.PcdShellPrintBufferSize ## CONSUMES -- cgit