| /** @file | |
| Provides interface to shell functionality for shell commands and applications. | |
| (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
| Copyright 2016-2018 Dell Technologies.<BR> | |
| Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
| Copyright (C) 2023, Apple Inc. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "UefiShellLib.h" | |
| #include <Library/SortLib.h> | |
| #include <Library/BaseLib.h> | |
| // | |
| // 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 successfully | |
| @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 pointers 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 successfully | |
| **/ | |
| 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 successfully. | |
| **/ | |
| 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 successfully | |
| @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 beginning of file | |
| @retval EFI_SUCCESS Operation completed successfully. | |
| @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 beginning of file. | |
| @retval EFI_SUCCESS the operation completed successfully. | |
| @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 successfully | |
| @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 successfully | |
| @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 successfully. 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 successful | |
| // | |
| 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 successful 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 successful. | |
| **/ | |
| 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 successfully. | |
| @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 successfully. | |
| @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 "-<Key>" or "/<Key>", 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 "-<Key> value" or "/<Key> 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 successful. | |
| @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 (PcdGet32 (PcdShellPrintBufferSize)); | |
| mPostReplaceFormat2 = AllocateZeroPool (PcdGet32 (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, PcdGet32 (PcdShellPrintBufferSize), L"%N", L"%%N", FALSE, FALSE); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = ShellCopySearchAndReplace (mPostReplaceFormat, mPostReplaceFormat2, PcdGet32 (PcdShellPrintBufferSize), L"%E", L"%%E", FALSE, FALSE); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = ShellCopySearchAndReplace (mPostReplaceFormat2, mPostReplaceFormat, PcdGet32 (PcdShellPrintBufferSize), L"%H", L"%%H", FALSE, FALSE); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = ShellCopySearchAndReplace (mPostReplaceFormat, mPostReplaceFormat2, PcdGet32 (PcdShellPrintBufferSize), L"%B", L"%%B", FALSE, FALSE); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = ShellCopySearchAndReplace (mPostReplaceFormat2, mPostReplaceFormat, PcdGet32 (PcdShellPrintBufferSize), L"%V", L"%%V", FALSE, FALSE); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Use the last buffer from replacing to print from... | |
| // | |
| UnicodeVSPrint (mPostReplaceFormat2, PcdGet32 (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 successful. | |
| @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) { | |
| ShellPrintDefaultEx (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; | |
| } | |
| ShellPrintDefaultEx (L"%c", Key.UnicodeChar); | |
| if ((Key.UnicodeChar == L'Q') || (Key.UnicodeChar == L'q')) { | |
| *Resp = ShellPromptResponseQuit; | |
| } else { | |
| *Resp = ShellPromptResponseContinue; | |
| } | |
| break; | |
| case ShellPromptResponseTypeYesNoCancel: | |
| if (Prompt != NULL) { | |
| ShellPrintDefaultEx (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; | |
| } | |
| ShellPrintDefaultEx (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) { | |
| ShellPrintDefaultEx (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)) { | |
| ShellPrintDefaultEx (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) { | |
| ShellPrintDefaultEx (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; | |
| } | |
| ShellPrintDefaultEx (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) { | |
| ShellPrintDefaultEx (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; | |
| } | |
| ShellPrintDefaultEx (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) { | |
| ShellPrintDefaultEx (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; | |
| } | |
| ShellPrintDefaultEx (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); | |
| } | |
| } | |
| ShellPrintDefaultEx (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 successful. | |
| @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); | |
| if (Prompt != NULL) { | |
| 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; | |
| } | |
| if ((*String == CHAR_NULL) && LeadingZero) { | |
| return (TRUE); | |
| } | |
| // | |
| // 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; | |
| BOOLEAN LeadingZero; | |
| if ((String == NULL) || (*String == CHAR_NULL) || (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 | |
| // | |
| LeadingZero = FALSE; | |
| while (*String == L'0') { | |
| String++; | |
| LeadingZero = TRUE; | |
| } | |
| if (CharToUpper (*String) == L'X') { | |
| if (!LeadingZero) { | |
| return 0; | |
| } | |
| // | |
| // Skip the 'X' | |
| // | |
| String++; | |
| // | |
| // there is a space where there should't be | |
| // | |
| if (*String == L' ') { | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| } | |
| Result = 0; | |
| 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) || (*String == CHAR_NULL) || (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 (EFI_ERROR (Status)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (Value == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| *Value = RetVal; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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 BufferLength; | |
| 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); | |
| } | |
| BufferLength = StrLen (Buffer); | |
| while ((BufferLength != 0) && (Buffer[--BufferLength] == L'\r')) { | |
| Buffer[BufferLength] = 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) { | |
| ShellPrintDefaultEx (L"%H%-14s%N- %s\r\n", CommandToGetHelpOn, OutText); | |
| } else { | |
| ShellPrintDefaultEx (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; | |
| } |