| /** @file | |
| Provides interface to shell MAN file parser. | |
| Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "Shell.h" | |
| /** | |
| Verifies that the filename has .MAN on the end. | |
| allocates a new buffer and copies the name (appending .MAN if necessary) | |
| ASSERT if ManFileName is NULL | |
| @param[in] ManFileName original filename | |
| @return the new filename with .man as the extension. | |
| **/ | |
| CHAR16 * | |
| EFIAPI | |
| GetManFileName( | |
| IN CONST CHAR16 *ManFileName | |
| ) | |
| { | |
| CHAR16 *Buffer; | |
| if (ManFileName == NULL) { | |
| return (NULL); | |
| } | |
| // | |
| // Fix the file name | |
| // | |
| if (StrnCmp(ManFileName+StrLen(ManFileName)-4, L".man", 4)==0) { | |
| Buffer = AllocateZeroPool(StrSize(ManFileName)); | |
| if (Buffer != NULL) { | |
| StrCpy(Buffer, ManFileName); | |
| } | |
| } else { | |
| Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16)); | |
| if (Buffer != NULL) { | |
| StrCpy(Buffer, ManFileName); | |
| StrCat(Buffer, L".man"); | |
| } | |
| } | |
| return (Buffer); | |
| } | |
| /** | |
| Search the path environment variable for possible locations and test for | |
| which one contains a man file with the name specified. If a valid file is found | |
| stop searching and return the (opened) SHELL_FILE_HANDLE for that file. | |
| @param[in] FileName Name of the file to find and open. | |
| @param[out] Handle Pointer to the handle of the found file. The | |
| value of this is undefined for return values | |
| except EFI_SUCCESS. | |
| @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE | |
| @retval EFI_INVALID_PARAMETER A parameter had an invalid value. | |
| @retval EFI_NOT_FOUND The file was not found. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SearchPathForFile( | |
| IN CONST CHAR16 *FileName, | |
| OUT SHELL_FILE_HANDLE *Handle | |
| ) | |
| { | |
| CHAR16 *FullFileName; | |
| EFI_STATUS Status; | |
| if ( FileName == NULL | |
| || Handle == NULL | |
| || StrLen(FileName) == 0 | |
| ){ | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| FullFileName = ShellFindFilePath(FileName); | |
| if (FullFileName == NULL) { | |
| return (EFI_NOT_FOUND); | |
| } | |
| // | |
| // now open that file | |
| // | |
| Status = EfiShellOpenFileByName(FullFileName, Handle, EFI_FILE_MODE_READ); | |
| FreePool(FullFileName); | |
| return (Status); | |
| } | |
| /** | |
| parses through Buffer (which is MAN file formatted) and returns the | |
| detailed help for any sub section specified in the comma seperated list of | |
| sections provided. If the end of the file or a .TH section is found then | |
| return. | |
| Upon a sucessful return the caller is responsible to free the memory in *HelpText | |
| @param[in] Buffer Buffer to read from | |
| @param[in] Sections name of command's sub sections to find | |
| @param[in] HelpText pointer to pointer to string where text goes. | |
| @param[in] HelpSize pointer to size of allocated HelpText (may be updated) | |
| @retval EFI_OUT_OF_RESOURCES a memory allocation failed. | |
| @retval EFI_SUCCESS the section was found and its description sotred in | |
| an alloceted buffer. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ManBufferFindSections( | |
| IN CONST CHAR16 *Buffer, | |
| IN CONST CHAR16 *Sections, | |
| IN CHAR16 **HelpText, | |
| IN UINTN *HelpSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CONST CHAR16 *CurrentLocation; | |
| BOOLEAN CurrentlyReading; | |
| CHAR16 *SectionName; | |
| UINTN SectionLen; | |
| BOOLEAN Found; | |
| CHAR16 *TempString; | |
| CHAR16 *TempString2; | |
| if ( Buffer == NULL | |
| || HelpText == NULL | |
| || HelpSize == NULL | |
| ){ | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| Status = EFI_SUCCESS; | |
| CurrentlyReading = FALSE; | |
| Found = FALSE; | |
| for (CurrentLocation = Buffer,TempString = NULL | |
| ; CurrentLocation != NULL && *CurrentLocation != CHAR_NULL | |
| ; CurrentLocation=StrStr(CurrentLocation, L"\r\n"),TempString = NULL | |
| ){ | |
| while(CurrentLocation[0] == L'\r' || CurrentLocation[0] == L'\n') { | |
| CurrentLocation++; | |
| } | |
| if (CurrentLocation[0] == L'#') { | |
| // | |
| // Skip comment lines | |
| // | |
| continue; | |
| } | |
| if (StrnCmp(CurrentLocation, L".TH", 3) == 0) { | |
| // | |
| // we hit the end of this commands section so stop. | |
| // | |
| break; | |
| } | |
| if (StrnCmp(CurrentLocation, L".SH ", 4) == 0) { | |
| if (Sections == NULL) { | |
| CurrentlyReading = TRUE; | |
| continue; | |
| } else if (CurrentlyReading) { | |
| CurrentlyReading = FALSE; | |
| } | |
| CurrentLocation += 4; | |
| // | |
| // is this a section we want to read in? | |
| // | |
| if (StrLen(CurrentLocation)!=0) { | |
| TempString2 = StrStr(CurrentLocation, L" "); | |
| TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\r")); | |
| TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n")); | |
| ASSERT(TempString == NULL); | |
| TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation); | |
| if (TempString == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| SectionName = TempString; | |
| SectionLen = StrLen(SectionName); | |
| SectionName = StrStr(Sections, SectionName); | |
| if (SectionName == NULL) { | |
| continue; | |
| } | |
| if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') { | |
| CurrentlyReading = TRUE; | |
| } | |
| } | |
| } else if (CurrentlyReading) { | |
| Found = TRUE; | |
| if (StrLen(CurrentLocation)!=0) { | |
| TempString2 = StrStr(CurrentLocation, L"\r"); | |
| TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n")); | |
| ASSERT(TempString == NULL); | |
| TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation); | |
| if (TempString == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| // | |
| // copy and save the current line. | |
| // | |
| ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL)); | |
| StrnCatGrow (HelpText, HelpSize, TempString, 0); | |
| if (HelpText == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| StrnCatGrow (HelpText, HelpSize, L"\r\n", 0); | |
| if (HelpText == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| } | |
| } | |
| SHELL_FREE_NON_NULL(TempString); | |
| } | |
| if (!Found && !EFI_ERROR(Status)) { | |
| return (EFI_NOT_FOUND); | |
| } | |
| return (Status); | |
| } | |
| /** | |
| parses through the MAN file specified by SHELL_FILE_HANDLE and returns the | |
| detailed help for any sub section specified in the comma seperated list of | |
| sections provided. If the end of the file or a .TH section is found then | |
| return. | |
| Upon a sucessful return the caller is responsible to free the memory in *HelpText | |
| @param[in] Handle FileHandle to read from | |
| @param[in] Sections name of command's sub sections to find | |
| @param[out] HelpText pointer to pointer to string where text goes. | |
| @param[out] HelpSize pointer to size of allocated HelpText (may be updated) | |
| @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise. | |
| @retval EFI_OUT_OF_RESOURCES a memory allocation failed. | |
| @retval EFI_SUCCESS the section was found and its description sotred in | |
| an alloceted buffer. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ManFileFindSections( | |
| IN SHELL_FILE_HANDLE Handle, | |
| IN CONST CHAR16 *Sections, | |
| OUT CHAR16 **HelpText, | |
| OUT UINTN *HelpSize, | |
| IN BOOLEAN Ascii | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CHAR16 *ReadLine; | |
| UINTN Size; | |
| BOOLEAN CurrentlyReading; | |
| CHAR16 *SectionName; | |
| UINTN SectionLen; | |
| BOOLEAN Found; | |
| if ( Handle == NULL | |
| || HelpText == NULL | |
| || HelpSize == NULL | |
| ){ | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| Status = EFI_SUCCESS; | |
| CurrentlyReading = FALSE; | |
| Size = 1024; | |
| Found = FALSE; | |
| ReadLine = AllocateZeroPool(Size); | |
| if (ReadLine == NULL) { | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| for (;!ShellFileHandleEof(Handle);Size = 1024) { | |
| Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, &Ascii); | |
| if (ReadLine[0] == L'#') { | |
| // | |
| // Skip comment lines | |
| // | |
| continue; | |
| } | |
| // | |
| // ignore too small of buffer... | |
| // | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| Status = EFI_SUCCESS; | |
| } | |
| if (EFI_ERROR(Status)) { | |
| break; | |
| } else if (StrnCmp(ReadLine, L".TH", 3) == 0) { | |
| // | |
| // we hit the end of this commands section so stop. | |
| // | |
| break; | |
| } else if (StrnCmp(ReadLine, L".SH", 3) == 0) { | |
| if (Sections == NULL) { | |
| CurrentlyReading = TRUE; | |
| continue; | |
| } | |
| // | |
| // we found a section | |
| // | |
| if (CurrentlyReading) { | |
| CurrentlyReading = FALSE; | |
| } | |
| // | |
| // is this a section we want to read in? | |
| // | |
| for ( SectionName = ReadLine + 3 | |
| ; *SectionName == L' ' | |
| ; SectionName++); | |
| SectionLen = StrLen(SectionName); | |
| SectionName = StrStr(Sections, SectionName); | |
| if (SectionName == NULL) { | |
| continue; | |
| } | |
| if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') { | |
| CurrentlyReading = TRUE; | |
| } | |
| } else if (CurrentlyReading) { | |
| Found = TRUE; | |
| // | |
| // copy and save the current line. | |
| // | |
| ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL)); | |
| StrnCatGrow (HelpText, HelpSize, ReadLine, 0); | |
| StrnCatGrow (HelpText, HelpSize, L"\r\n", 0); | |
| } | |
| } | |
| FreePool(ReadLine); | |
| if (!Found && !EFI_ERROR(Status)) { | |
| return (EFI_NOT_FOUND); | |
| } | |
| return (Status); | |
| } | |
| /** | |
| parses through the MAN file formatted Buffer and returns the | |
| "Brief Description" for the .TH section as specified by Command. If the | |
| command section is not found return EFI_NOT_FOUND. | |
| Upon a sucessful return the caller is responsible to free the memory in *BriefDesc | |
| @param[in] Handle Buffer to read from | |
| @param[in] Command name of command's section to find | |
| @param[in] BriefDesc pointer to pointer to string where description goes. | |
| @param[in] BriefSize pointer to size of allocated BriefDesc | |
| @retval EFI_OUT_OF_RESOURCES a memory allocation failed. | |
| @retval EFI_SUCCESS the section was found and its description sotred in | |
| an alloceted buffer. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ManBufferFindTitleSection( | |
| IN CHAR16 **Buffer, | |
| IN CONST CHAR16 *Command, | |
| IN CHAR16 **BriefDesc, | |
| IN UINTN *BriefSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CHAR16 *TitleString; | |
| CHAR16 *TitleEnd; | |
| CHAR16 *CurrentLocation; | |
| if ( Buffer == NULL | |
| || Command == NULL | |
| || (BriefDesc != NULL && BriefSize == NULL) | |
| ){ | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| Status = EFI_SUCCESS; | |
| TitleString = AllocateZeroPool((7*sizeof(CHAR16)) + StrSize(Command)); | |
| if (TitleString == NULL) { | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| StrCpy(TitleString, L".TH "); | |
| StrCat(TitleString, Command); | |
| StrCat(TitleString, L" 0 "); | |
| CurrentLocation = StrStr(*Buffer, TitleString); | |
| if (CurrentLocation == NULL){ | |
| Status = EFI_NOT_FOUND; | |
| } else { | |
| // | |
| // we found it so copy out the rest of the line into BriefDesc | |
| // After skipping any spaces or zeroes | |
| // | |
| for (CurrentLocation += StrLen(TitleString) | |
| ; *CurrentLocation == L' ' || *CurrentLocation == L'0' || *CurrentLocation == L'1' || *CurrentLocation == L'\"' | |
| ; CurrentLocation++); | |
| TitleEnd = StrStr(CurrentLocation, L"\""); | |
| if (TitleEnd == NULL) { | |
| Status = EFI_DEVICE_ERROR; | |
| } else { | |
| if (BriefDesc != NULL) { | |
| *BriefSize = StrSize(TitleEnd); | |
| *BriefDesc = AllocateZeroPool(*BriefSize); | |
| if (*BriefDesc == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| } else { | |
| StrnCpy(*BriefDesc, CurrentLocation, TitleEnd-CurrentLocation); | |
| } | |
| } | |
| for (CurrentLocation = TitleEnd | |
| ; *CurrentLocation != L'\n' | |
| ; CurrentLocation++); | |
| for ( | |
| ; *CurrentLocation == L' ' || *CurrentLocation == L'\n' || *CurrentLocation == L'\r' | |
| ; CurrentLocation++); | |
| *Buffer = CurrentLocation; | |
| } | |
| } | |
| FreePool(TitleString); | |
| return (Status); | |
| } | |
| /** | |
| parses through the MAN file specified by SHELL_FILE_HANDLE and returns the | |
| "Brief Description" for the .TH section as specified by Command. if the | |
| command section is not found return EFI_NOT_FOUND. | |
| Upon a sucessful return the caller is responsible to free the memory in *BriefDesc | |
| @param[in] Handle FileHandle to read from | |
| @param[in] Command name of command's section to find | |
| @param[out] BriefDesc pointer to pointer to string where description goes. | |
| @param[out] BriefSize pointer to size of allocated BriefDesc | |
| @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be | |
| set if the file handle is at the 0 position. | |
| @retval EFI_OUT_OF_RESOURCES a memory allocation failed. | |
| @retval EFI_SUCCESS the section was found and its description sotred in | |
| an alloceted buffer. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ManFileFindTitleSection( | |
| IN SHELL_FILE_HANDLE Handle, | |
| IN CONST CHAR16 *Command, | |
| OUT CHAR16 **BriefDesc OPTIONAL, | |
| OUT UINTN *BriefSize OPTIONAL, | |
| IN OUT BOOLEAN *Ascii | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CHAR16 *TitleString; | |
| CHAR16 *ReadLine; | |
| UINTN Size; | |
| CHAR16 *TitleEnd; | |
| UINTN TitleLen; | |
| BOOLEAN Found; | |
| if ( Handle == NULL | |
| || Command == NULL | |
| || (BriefDesc != NULL && BriefSize == NULL) | |
| ){ | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| Status = EFI_SUCCESS; | |
| Size = 1024; | |
| Found = FALSE; | |
| ReadLine = AllocateZeroPool(Size); | |
| if (ReadLine == NULL) { | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| TitleString = AllocateZeroPool((4*sizeof(CHAR16)) + StrSize(Command)); | |
| if (TitleString == NULL) { | |
| FreePool(ReadLine); | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| StrCpy(TitleString, L".TH "); | |
| StrCat(TitleString, Command); | |
| TitleLen = StrLen(TitleString); | |
| for (;!ShellFileHandleEof(Handle);Size = 1024) { | |
| Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, Ascii); | |
| if (ReadLine[0] == L'#') { | |
| // | |
| // Skip comment lines | |
| // | |
| continue; | |
| } | |
| // | |
| // ignore too small of buffer... | |
| // | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| Status = EFI_SUCCESS; | |
| } | |
| if (EFI_ERROR(Status)) { | |
| break; | |
| } | |
| if (StrnCmp(ReadLine, TitleString, TitleLen) == 0) { | |
| Found = TRUE; | |
| // | |
| // we found it so copy out the rest of the line into BriefDesc | |
| // After skipping any spaces or zeroes | |
| // | |
| for ( TitleEnd = ReadLine+TitleLen | |
| ; *TitleEnd == L' ' || *TitleEnd == L'0' || *TitleEnd == L'1' | |
| ; TitleEnd++); | |
| if (BriefDesc != NULL) { | |
| *BriefSize = StrSize(TitleEnd); | |
| *BriefDesc = AllocateZeroPool(*BriefSize); | |
| if (*BriefDesc == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| StrCpy(*BriefDesc, TitleEnd); | |
| } | |
| break; | |
| } | |
| } | |
| FreePool(ReadLine); | |
| FreePool(TitleString); | |
| if (!Found && !EFI_ERROR(Status)) { | |
| return (EFI_NOT_FOUND); | |
| } | |
| return (Status); | |
| } | |
| /** | |
| This function returns the help information for the specified command. The help text | |
| will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B) | |
| If Sections is specified, then each section name listed will be compared in a casesensitive | |
| manner, to the section names described in Appendix B. If the section exists, | |
| it will be appended to the returned help text. If the section does not exist, no | |
| information will be returned. If Sections is NULL, then all help text information | |
| available will be returned. | |
| if BriefDesc is NULL, then the breif description will not be savedd seperatly, | |
| but placed first in the main HelpText. | |
| @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name. | |
| @param[in] Command Points to the NULL-terminated UEFI Shell command name. | |
| @param[in] Sections Points to the NULL-terminated comma-delimited | |
| section names to return. If NULL, then all | |
| sections will be returned. | |
| @param[out] BriefDesc On return, points to a callee-allocated buffer | |
| containing brief description text. | |
| @param[out] HelpText On return, points to a callee-allocated buffer | |
| containing all specified help text. | |
| @retval EFI_SUCCESS The help text was returned. | |
| @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the | |
| returned help text. | |
| @retval EFI_INVALID_PARAMETER HelpText is NULL. | |
| @retval EFI_INVALID_PARAMETER ManFileName is invalid. | |
| @retval EFI_NOT_FOUND There is no help text available for Command. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ProcessManFile( | |
| IN CONST CHAR16 *ManFileName, | |
| IN CONST CHAR16 *Command, | |
| IN CONST CHAR16 *Sections OPTIONAL, | |
| OUT CHAR16 **BriefDesc OPTIONAL, | |
| OUT CHAR16 **HelpText | |
| ) | |
| { | |
| CHAR16 *TempString; | |
| SHELL_FILE_HANDLE FileHandle; | |
| EFI_STATUS Status; | |
| UINTN HelpSize; | |
| UINTN BriefSize; | |
| BOOLEAN Ascii; | |
| CHAR16 *TempString2; | |
| EFI_DEVICE_PATH_PROTOCOL *FileDevPath; | |
| EFI_DEVICE_PATH_PROTOCOL *DevPath; | |
| if ( ManFileName == NULL | |
| || Command == NULL | |
| || HelpText == NULL | |
| ){ | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| HelpSize = 0; | |
| BriefSize = 0; | |
| TempString = NULL; | |
| // | |
| // See if it's in HII first | |
| // | |
| TempString = ShellCommandGetCommandHelp(Command); | |
| if (TempString != NULL) { | |
| TempString2 = TempString; | |
| Status = ManBufferFindTitleSection(&TempString2, Command, BriefDesc, &BriefSize); | |
| if (!EFI_ERROR(Status) && HelpText != NULL){ | |
| Status = ManBufferFindSections(TempString2, Sections, HelpText, &HelpSize); | |
| } | |
| } else { | |
| FileHandle = NULL; | |
| TempString = GetManFileName(ManFileName); | |
| if (TempString == NULL) { | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| Status = SearchPathForFile(TempString, &FileHandle); | |
| if (EFI_ERROR(Status)) { | |
| FileDevPath = FileDevicePath(NULL, TempString); | |
| DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath); | |
| Status = InternalOpenFileDevicePath(DevPath, &FileHandle, EFI_FILE_MODE_READ, 0); | |
| FreePool(FileDevPath); | |
| FreePool(DevPath); | |
| } | |
| if (!EFI_ERROR(Status)) { | |
| HelpSize = 0; | |
| BriefSize = 0; | |
| Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii); | |
| if (!EFI_ERROR(Status) && HelpText != NULL){ | |
| Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii); | |
| } | |
| ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle); | |
| } else { | |
| *HelpText = NULL; | |
| } | |
| } | |
| if (TempString != NULL) { | |
| FreePool(TempString); | |
| } | |
| return (Status); | |
| } |