2 Provides interface to shell MAN file parser.
4 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
5 Copyright 2015 Dell Inc.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 CHAR16 EFIAPI
InternalShellCharToUpper (IN CHAR16 Char
);
21 Verifies that the filename has .MAN on the end.
23 allocates a new buffer and copies the name (appending .MAN if necessary)
25 ASSERT if ManFileName is NULL
27 @param[in] ManFileName original filename
29 @return the new filename with .man as the extension.
34 IN CONST CHAR16
*ManFileName
38 if (ManFileName
== NULL
) {
44 if (StrnCmp(ManFileName
+StrLen(ManFileName
)-4, L
".man", 4)==0) {
45 Buffer
= AllocateCopyPool(StrSize(ManFileName
), ManFileName
);
47 Buffer
= AllocateZeroPool(StrSize(ManFileName
) + 4*sizeof(CHAR16
));
50 (StrSize(ManFileName
) + 4*sizeof(CHAR16
))/sizeof(CHAR16
),
55 (StrSize(ManFileName
) + 4*sizeof(CHAR16
))/sizeof(CHAR16
),
65 Search the path environment variable for possible locations and test for
66 which one contains a man file with the name specified. If a valid file is found
67 stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
69 @param[in] FileName Name of the file to find and open.
70 @param[out] Handle Pointer to the handle of the found file. The
71 value of this is undefined for return values
74 @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
75 @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
76 @retval EFI_NOT_FOUND The file was not found.
81 IN CONST CHAR16
*FileName
,
82 OUT SHELL_FILE_HANDLE
*Handle
90 || StrLen(FileName
) == 0
92 return (EFI_INVALID_PARAMETER
);
95 FullFileName
= ShellFindFilePath(FileName
);
96 if (FullFileName
== NULL
) {
97 return (EFI_NOT_FOUND
);
101 // now open that file
103 Status
= EfiShellOpenFileByName(FullFileName
, Handle
, EFI_FILE_MODE_READ
);
104 FreePool(FullFileName
);
110 parses through Buffer (which is MAN file formatted) and returns the
111 detailed help for any sub section specified in the comma seperated list of
112 sections provided. If the end of the file or a .TH section is found then
115 Upon a sucessful return the caller is responsible to free the memory in *HelpText
117 @param[in] Buffer Buffer to read from
118 @param[in] Sections name of command's sub sections to find
119 @param[in] HelpText pointer to pointer to string where text goes.
120 @param[in] HelpSize pointer to size of allocated HelpText (may be updated)
122 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
123 @retval EFI_SUCCESS the section was found and its description sotred in
128 ManBufferFindSections(
129 IN CONST CHAR16
*Buffer
,
130 IN CONST CHAR16
*Sections
,
131 IN CHAR16
**HelpText
,
136 CONST CHAR16
*CurrentLocation
;
137 BOOLEAN CurrentlyReading
;
148 return (EFI_INVALID_PARAMETER
);
151 Status
= EFI_SUCCESS
;
152 CurrentlyReading
= FALSE
;
155 for (CurrentLocation
= Buffer
,TempString
= NULL
156 ; CurrentLocation
!= NULL
&& *CurrentLocation
!= CHAR_NULL
157 ; CurrentLocation
=StrStr(CurrentLocation
, L
"\r\n"),TempString
= NULL
159 while(CurrentLocation
[0] == L
'\r' || CurrentLocation
[0] == L
'\n') {
162 if (CurrentLocation
[0] == L
'#') {
164 // Skip comment lines
168 if (StrnCmp(CurrentLocation
, L
".TH", 3) == 0) {
170 // we hit the end of this commands section so stop.
174 if (StrnCmp(CurrentLocation
, L
".SH ", 4) == 0) {
175 if (Sections
== NULL
) {
176 CurrentlyReading
= TRUE
;
178 } else if (CurrentlyReading
) {
179 CurrentlyReading
= FALSE
;
181 CurrentLocation
+= 4;
183 // is this a section we want to read in?
185 if (StrLen(CurrentLocation
)!=0) {
186 TempString2
= StrStr(CurrentLocation
, L
" ");
187 TempString2
= MIN(TempString2
, StrStr(CurrentLocation
, L
"\r"));
188 TempString2
= MIN(TempString2
, StrStr(CurrentLocation
, L
"\n"));
189 ASSERT(TempString
== NULL
);
190 TempString
= StrnCatGrow(&TempString
, NULL
, CurrentLocation
, TempString2
==NULL
?0:TempString2
- CurrentLocation
);
191 if (TempString
== NULL
) {
192 Status
= EFI_OUT_OF_RESOURCES
;
195 SectionName
= TempString
;
196 SectionLen
= StrLen(SectionName
);
197 SectionName
= StrStr(Sections
, SectionName
);
198 if (SectionName
== NULL
) {
201 if (*(SectionName
+ SectionLen
) == CHAR_NULL
|| *(SectionName
+ SectionLen
) == L
',') {
202 CurrentlyReading
= TRUE
;
205 } else if (CurrentlyReading
) {
207 if (StrLen(CurrentLocation
)!=0) {
208 TempString2
= StrStr(CurrentLocation
, L
"\r");
209 TempString2
= MIN(TempString2
, StrStr(CurrentLocation
, L
"\n"));
210 ASSERT(TempString
== NULL
);
211 TempString
= StrnCatGrow(&TempString
, NULL
, CurrentLocation
, TempString2
==NULL
?0:TempString2
- CurrentLocation
);
212 if (TempString
== NULL
) {
213 Status
= EFI_OUT_OF_RESOURCES
;
217 // copy and save the current line.
219 ASSERT((*HelpText
== NULL
&& *HelpSize
== 0) || (*HelpText
!= NULL
));
220 StrnCatGrow (HelpText
, HelpSize
, TempString
, 0);
221 if (HelpText
== NULL
) {
222 Status
= EFI_OUT_OF_RESOURCES
;
225 StrnCatGrow (HelpText
, HelpSize
, L
"\r\n", 0);
226 if (HelpText
== NULL
) {
227 Status
= EFI_OUT_OF_RESOURCES
;
232 SHELL_FREE_NON_NULL(TempString
);
234 if (!Found
&& !EFI_ERROR(Status
)) {
235 return (EFI_NOT_FOUND
);
241 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
242 detailed help for any sub section specified in the comma seperated list of
243 sections provided. If the end of the file or a .TH section is found then
246 Upon a sucessful return the caller is responsible to free the memory in *HelpText
248 @param[in] Handle FileHandle to read from
249 @param[in] Sections name of command's sub sections to find
250 @param[out] HelpText pointer to pointer to string where text goes.
251 @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
252 @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
254 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
255 @retval EFI_SUCCESS the section was found and its description sotred in
261 IN SHELL_FILE_HANDLE Handle
,
262 IN CONST CHAR16
*Sections
,
263 OUT CHAR16
**HelpText
,
271 BOOLEAN CurrentlyReading
;
280 return (EFI_INVALID_PARAMETER
);
283 Status
= EFI_SUCCESS
;
284 CurrentlyReading
= FALSE
;
288 ReadLine
= AllocateZeroPool(Size
);
289 if (ReadLine
== NULL
) {
290 return (EFI_OUT_OF_RESOURCES
);
293 for (;!ShellFileHandleEof(Handle
);Size
= 1024) {
294 Status
= ShellFileHandleReadLine(Handle
, ReadLine
, &Size
, TRUE
, &Ascii
);
295 if (ReadLine
[0] == L
'#') {
297 // Skip comment lines
302 // ignore too small of buffer...
304 if (Status
== EFI_BUFFER_TOO_SMALL
) {
305 Status
= EFI_SUCCESS
;
307 if (EFI_ERROR(Status
)) {
309 } else if (StrnCmp(ReadLine
, L
".TH", 3) == 0) {
311 // we hit the end of this commands section so stop.
314 } else if (StrnCmp(ReadLine
, L
".SH", 3) == 0) {
315 if (Sections
== NULL
) {
316 CurrentlyReading
= TRUE
;
320 // we found a section
322 if (CurrentlyReading
) {
323 CurrentlyReading
= FALSE
;
326 // is this a section we want to read in?
328 for ( SectionName
= ReadLine
+ 3
329 ; *SectionName
== L
' '
331 SectionLen
= StrLen(SectionName
);
332 SectionName
= StrStr(Sections
, SectionName
);
333 if (SectionName
== NULL
) {
336 if (*(SectionName
+ SectionLen
) == CHAR_NULL
|| *(SectionName
+ SectionLen
) == L
',') {
337 CurrentlyReading
= TRUE
;
339 } else if (CurrentlyReading
) {
342 // copy and save the current line.
344 ASSERT((*HelpText
== NULL
&& *HelpSize
== 0) || (*HelpText
!= NULL
));
345 StrnCatGrow (HelpText
, HelpSize
, ReadLine
, 0);
346 StrnCatGrow (HelpText
, HelpSize
, L
"\r\n", 0);
350 if (!Found
&& !EFI_ERROR(Status
)) {
351 return (EFI_NOT_FOUND
);
357 parses through the MAN file formatted Buffer and returns the
358 "Brief Description" for the .TH section as specified by Command. If the
359 command section is not found return EFI_NOT_FOUND.
361 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
363 @param[in] Handle Buffer to read from
364 @param[in] Command name of command's section to find
365 @param[in] BriefDesc pointer to pointer to string where description goes.
366 @param[in] BriefSize pointer to size of allocated BriefDesc
368 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
369 @retval EFI_SUCCESS the section was found and its description sotred in
374 ManBufferFindTitleSection(
376 IN CONST CHAR16
*Command
,
377 IN CHAR16
**BriefDesc
,
384 CHAR16
*CurrentLocation
;
386 CONST CHAR16 StartString
[] = L
".TH ";
387 CONST CHAR16 EndString
[] = L
" 0 ";
391 || (BriefDesc
!= NULL
&& BriefSize
== NULL
)
393 return (EFI_INVALID_PARAMETER
);
396 Status
= EFI_SUCCESS
;
399 // more characters for StartString and EndString
401 TitleLength
= StrSize(Command
) + (StrLen(StartString
) + StrLen(EndString
)) * sizeof(CHAR16
);
402 TitleString
= AllocateZeroPool(TitleLength
);
403 if (TitleString
== NULL
) {
404 return (EFI_OUT_OF_RESOURCES
);
406 StrCpyS(TitleString
, TitleLength
/sizeof(CHAR16
), StartString
);
407 StrCatS(TitleString
, TitleLength
/sizeof(CHAR16
), Command
);
408 StrCatS(TitleString
, TitleLength
/sizeof(CHAR16
), EndString
);
410 CurrentLocation
= StrStr(*Buffer
, TitleString
);
411 if (CurrentLocation
== NULL
){
412 Status
= EFI_NOT_FOUND
;
415 // we found it so copy out the rest of the line into BriefDesc
416 // After skipping any spaces or zeroes
418 for (CurrentLocation
+= StrLen(TitleString
)
419 ; *CurrentLocation
== L
' ' || *CurrentLocation
== L
'0' || *CurrentLocation
== L
'1' || *CurrentLocation
== L
'\"'
420 ; CurrentLocation
++);
422 TitleEnd
= StrStr(CurrentLocation
, L
"\"");
423 if (TitleEnd
== NULL
) {
424 Status
= EFI_DEVICE_ERROR
;
426 if (BriefDesc
!= NULL
) {
427 *BriefSize
= StrSize(TitleEnd
);
428 *BriefDesc
= AllocateZeroPool(*BriefSize
);
429 if (*BriefDesc
== NULL
) {
430 Status
= EFI_OUT_OF_RESOURCES
;
432 StrnCpyS(*BriefDesc
, (*BriefSize
)/sizeof(CHAR16
), CurrentLocation
, TitleEnd
-CurrentLocation
);
436 for (CurrentLocation
= TitleEnd
437 ; *CurrentLocation
!= L
'\n'
438 ; CurrentLocation
++);
440 ; *CurrentLocation
== L
' ' || *CurrentLocation
== L
'\n' || *CurrentLocation
== L
'\r'
441 ; CurrentLocation
++);
442 *Buffer
= CurrentLocation
;
446 FreePool(TitleString
);
451 Parses a line from a MAN file to see if it is the Title Header. If it is, then
452 if the "Brief Description" is desired, allocate a buffer for it and return a
453 copy. Upon a sucessful return the caller is responsible to free the memory in
456 Uses a simple state machine that allows "unlimited" whitespace before and after the
457 ".TH", compares Command and the MAN file commnd name without respect to case, and
458 allows "unlimited" whitespace and '0' and '1' characters before the Short Description.
459 The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
460 where group 1 is the Command Name and group 2 is the Short Description.
462 @param[in] Command name of command whose MAN file we think Line came from
463 @param[in] Line Pointer to a line from the MAN file
464 @param[out] BriefDesc pointer to pointer to string where description goes.
465 @param[out] BriefSize pointer to size of allocated BriefDesc
466 @param[out] Found TRUE if the Title Header was found and it belongs to Command
468 @retval TRUE Line contained the Title Header
469 @retval FALSE Line did not contain the Title Header
473 IN CONST CHAR16
*Command
,
475 OUT CHAR16
**BriefDesc OPTIONAL
,
476 OUT UINTN
*BriefSize OPTIONAL
,
480 // The states of a simple state machine used to recognize a title header line
481 // and to extract the Short Description, if desired.
483 LookForThMacro
, LookForCommandName
, CompareCommands
, GetBriefDescription
, Final
487 UINTN CommandIndex
; // Indexes Command as we compare its chars to the MAN file.
488 BOOLEAN ReturnValue
; // TRUE if this the Title Header line of *some* MAN file.
489 BOOLEAN ReturnFound
; // TRUE if this the Title Header line of *the desired* MAN file.
494 State
= LookForThMacro
;
498 if (*Line
== L
'\0') {
504 // Handle "^\s*.TH\s"
505 // Go to state LookForCommandName if the title header macro is present; otherwise,
506 // eat white space. If we see something other than white space, this is not a
507 // title header line.
509 if (StrnCmp (L
".TH ", Line
, 4) == 0 || StrnCmp (L
".TH\t", Line
, 4) == 0) {
511 State
= LookForCommandName
;
513 else if (*Line
== L
' ' || *Line
== L
'\t') {
522 // Eat any "extra" whitespace after the title header macro (we have already seen
523 // at least one white space character). Go to state CompareCommands when a
524 // non-white space is seen.
525 case LookForCommandName
:
526 if (*Line
== L
' ' || *Line
== L
'\t') {
530 ReturnValue
= TRUE
; // This is *some* command's title header line.
531 State
= CompareCommands
;
532 // Do not increment Line; it points to the first character of the command
533 // name on the title header line.
538 // Compare Command to the title header command name, ignoring case. When we
539 // reach the end of the command (i.e. we see white space), the next state
540 // depends on whether the caller wants a copy of the Brief Description.
541 case CompareCommands
:
542 if (*Line
== L
' ' || *Line
== L
'\t') {
543 ReturnFound
= TRUE
; // This is the desired command's title header line.
544 State
= (BriefDesc
== NULL
) ? Final
: GetBriefDescription
;
546 else if (InternalShellCharToUpper (*Line
) != InternalShellCharToUpper (*(Command
+ CommandIndex
++))) {
552 // Handle "[\s01]*(.*)$"
553 // Skip whitespace, '0', and '1' characters, if any, prior to the brief description.
554 // Return the description to the caller.
555 case GetBriefDescription
:
556 if (*Line
!= L
' ' && *Line
!= L
'\t' && *Line
!= L
'0' && *Line
!= L
'1') {
557 *BriefSize
= StrSize(Line
);
558 *BriefDesc
= AllocateZeroPool(*BriefSize
);
559 if (*BriefDesc
!= NULL
) {
560 StrCpyS(*BriefDesc
, (*BriefSize
)/sizeof(CHAR16
), Line
);
571 } while (State
< Final
);
573 *Found
= ReturnFound
;
578 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
579 "Brief Description" for the .TH section as specified by Command. If the
580 command section is not found return EFI_NOT_FOUND.
582 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
584 @param[in] Handle FileHandle to read from
585 @param[in] Command name of command's section to find as entered on the
586 command line (may be a relative or absolute path or
587 be in any case: upper, lower, or mixed in numerous ways!).
588 @param[out] BriefDesc pointer to pointer to string where description goes.
589 @param[out] BriefSize pointer to size of allocated BriefDesc
590 @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
591 set if the file handle is at the 0 position.
593 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
594 @retval EFI_SUCCESS the section was found and its description stored in
595 an allocated buffer if requested.
599 ManFileFindTitleSection(
600 IN SHELL_FILE_HANDLE Handle
,
601 IN CONST CHAR16
*Command
,
602 OUT CHAR16
**BriefDesc OPTIONAL
,
603 OUT UINTN
*BriefSize OPTIONAL
,
604 IN OUT BOOLEAN
*Ascii
615 || (BriefDesc
!= NULL
&& BriefSize
== NULL
)
617 return (EFI_INVALID_PARAMETER
);
620 Status
= EFI_SUCCESS
;
624 ReadLine
= AllocateZeroPool(Size
);
625 if (ReadLine
== NULL
) {
626 return (EFI_OUT_OF_RESOURCES
);
630 // Do not pass any leading path information that may be present to IsTitleHeader().
632 Start
= StrLen(Command
);
634 && (*(Command
+ Start
- 1) != L
'\\')
635 && (*(Command
+ Start
- 1) != L
'/')
636 && (*(Command
+ Start
- 1) != L
':')) {
640 for (;!ShellFileHandleEof(Handle
);Size
= 1024) {
641 Status
= ShellFileHandleReadLine(Handle
, ReadLine
, &Size
, TRUE
, Ascii
);
643 // ignore too small of buffer...
645 if (EFI_ERROR(Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
649 Status
= EFI_NOT_FOUND
;
650 if (IsTitleHeader (Command
+Start
, ReadLine
, BriefDesc
, BriefSize
, &Found
)) {
651 Status
= Found
? EFI_SUCCESS
: EFI_NOT_FOUND
;
661 This function returns the help information for the specified command. The help text
662 will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
664 If Sections is specified, then each section name listed will be compared in a casesensitive
665 manner, to the section names described in Appendix B. If the section exists,
666 it will be appended to the returned help text. If the section does not exist, no
667 information will be returned. If Sections is NULL, then all help text information
668 available will be returned.
670 if BriefDesc is NULL, then the breif description will not be savedd seperatly,
671 but placed first in the main HelpText.
673 @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
674 @param[in] Command Points to the NULL-terminated UEFI Shell command name.
675 @param[in] Sections Points to the NULL-terminated comma-delimited
676 section names to return. If NULL, then all
677 sections will be returned.
678 @param[out] BriefDesc On return, points to a callee-allocated buffer
679 containing brief description text.
680 @param[out] HelpText On return, points to a callee-allocated buffer
681 containing all specified help text.
683 @retval EFI_SUCCESS The help text was returned.
684 @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
686 @retval EFI_INVALID_PARAMETER HelpText is NULL.
687 @retval EFI_INVALID_PARAMETER ManFileName is invalid.
688 @retval EFI_NOT_FOUND There is no help text available for Command.
693 IN CONST CHAR16
*ManFileName
,
694 IN CONST CHAR16
*Command
,
695 IN CONST CHAR16
*Sections OPTIONAL
,
696 OUT CHAR16
**BriefDesc OPTIONAL
,
697 OUT CHAR16
**HelpText
701 SHELL_FILE_HANDLE FileHandle
;
707 EFI_DEVICE_PATH_PROTOCOL
*FileDevPath
;
708 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
710 if ( ManFileName
== NULL
714 return (EFI_INVALID_PARAMETER
);
722 // See if it's in HII first
724 TempString
= ShellCommandGetCommandHelp(Command
);
725 if (TempString
!= NULL
) {
726 TempString2
= TempString
;
727 Status
= ManBufferFindTitleSection(&TempString2
, Command
, BriefDesc
, &BriefSize
);
728 if (!EFI_ERROR(Status
) && HelpText
!= NULL
){
729 Status
= ManBufferFindSections(TempString2
, Sections
, HelpText
, &HelpSize
);
733 TempString
= GetManFileName(ManFileName
);
734 if (TempString
== NULL
) {
735 return (EFI_INVALID_PARAMETER
);
738 Status
= SearchPathForFile(TempString
, &FileHandle
);
739 if (EFI_ERROR(Status
)) {
740 FileDevPath
= FileDevicePath(NULL
, TempString
);
741 DevPath
= AppendDevicePath (ShellInfoObject
.ImageDevPath
, FileDevPath
);
742 Status
= InternalOpenFileDevicePath(DevPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
743 FreePool(FileDevPath
);
747 if (!EFI_ERROR(Status
)) {
750 Status
= ManFileFindTitleSection(FileHandle
, Command
, BriefDesc
, &BriefSize
, &Ascii
);
751 if (!EFI_ERROR(Status
) && HelpText
!= NULL
){
752 Status
= ManFileFindSections(FileHandle
, Sections
, HelpText
, &HelpSize
, Ascii
);
754 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
759 if (TempString
!= NULL
) {
760 FreePool(TempString
);