/** @file | |
Provides interface to shell MAN file parser. | |
Copyright (c) 2009 - 2014, 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 = AllocateCopyPool(StrSize(ManFileName), ManFileName); | |
} else { | |
Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16)); | |
if (Buffer != NULL) { | |
StrnCpy(Buffer, ManFileName, StrLen(ManFileName)); | |
StrnCat(Buffer, L".man", 4); | |
} | |
} | |
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; | |
UINTN TitleLength; | |
CONST CHAR16 StartString[] = L".TH "; | |
CONST CHAR16 EndString[] = L" 0 "; | |
if ( Buffer == NULL | |
|| Command == NULL | |
|| (BriefDesc != NULL && BriefSize == NULL) | |
){ | |
return (EFI_INVALID_PARAMETER); | |
} | |
Status = EFI_SUCCESS; | |
// | |
// more characters for StartString and EndString | |
// | |
TitleLength = StrSize(Command) + (StrLen(StartString) + StrLen(EndString)) * sizeof(CHAR16); | |
TitleString = AllocateZeroPool(TitleLength); | |
if (TitleString == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
StrnCpy(TitleString, StartString, TitleLength/sizeof(CHAR16) - 1); | |
StrnCat(TitleString, Command, TitleLength/sizeof(CHAR16) - 1 - StrLen(TitleString)); | |
StrnCat(TitleString, EndString, TitleLength/sizeof(CHAR16) - 1 - StrLen(TitleString)); | |
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; | |
UINTN TitleSize; | |
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); | |
} | |
TitleSize = (4*sizeof(CHAR16)) + StrSize(Command); | |
TitleString = AllocateZeroPool(TitleSize); | |
if (TitleString == NULL) { | |
FreePool(ReadLine); | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
StrnCpy(TitleString, L".TH ", TitleSize/sizeof(CHAR16) - 1); | |
StrnCat(TitleString, Command, TitleSize/sizeof(CHAR16) - 1 - StrLen(TitleString)); | |
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; | |
} | |
StrnCpy(*BriefDesc, TitleEnd, (*BriefSize)/sizeof(CHAR16) - 1); | |
} | |
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; | |
Ascii = FALSE; | |
// | |
// 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); | |
} |