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
);
569 } while (State
< Final
);
571 *Found
= ReturnFound
;
576 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
577 "Brief Description" for the .TH section as specified by Command. If the
578 command section is not found return EFI_NOT_FOUND.
580 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
582 @param[in] Handle FileHandle to read from
583 @param[in] Command name of command's section to find as entered on the
584 command line (may be a relative or absolute path or
585 be in any case: upper, lower, or mixed in numerous ways!).
586 @param[out] BriefDesc pointer to pointer to string where description goes.
587 @param[out] BriefSize pointer to size of allocated BriefDesc
588 @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
589 set if the file handle is at the 0 position.
591 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
592 @retval EFI_SUCCESS the section was found and its description stored in
593 an allocated buffer if requested.
597 ManFileFindTitleSection(
598 IN SHELL_FILE_HANDLE Handle
,
599 IN CONST CHAR16
*Command
,
600 OUT CHAR16
**BriefDesc OPTIONAL
,
601 OUT UINTN
*BriefSize OPTIONAL
,
602 IN OUT BOOLEAN
*Ascii
613 || (BriefDesc
!= NULL
&& BriefSize
== NULL
)
615 return (EFI_INVALID_PARAMETER
);
618 Status
= EFI_SUCCESS
;
622 ReadLine
= AllocateZeroPool(Size
);
623 if (ReadLine
== NULL
) {
624 return (EFI_OUT_OF_RESOURCES
);
628 // Do not pass any leading path information that may be present to IsTitleHeader().
630 Start
= StrLen(Command
);
632 && (*(Command
+ Start
- 1) != L
'\\')
633 && (*(Command
+ Start
- 1) != L
'/')
634 && (*(Command
+ Start
- 1) != L
':')) {
638 for (;!ShellFileHandleEof(Handle
);Size
= 1024) {
639 Status
= ShellFileHandleReadLine(Handle
, ReadLine
, &Size
, TRUE
, Ascii
);
641 // ignore too small of buffer...
643 if (EFI_ERROR(Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
647 Status
= EFI_NOT_FOUND
;
648 if (IsTitleHeader (Command
+Start
, ReadLine
, BriefDesc
, BriefSize
, &Found
)) {
649 Status
= Found
? EFI_SUCCESS
: EFI_NOT_FOUND
;
659 This function returns the help information for the specified command. The help text
660 will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
662 If Sections is specified, then each section name listed will be compared in a casesensitive
663 manner, to the section names described in Appendix B. If the section exists,
664 it will be appended to the returned help text. If the section does not exist, no
665 information will be returned. If Sections is NULL, then all help text information
666 available will be returned.
668 if BriefDesc is NULL, then the breif description will not be savedd seperatly,
669 but placed first in the main HelpText.
671 @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
672 @param[in] Command Points to the NULL-terminated UEFI Shell command name.
673 @param[in] Sections Points to the NULL-terminated comma-delimited
674 section names to return. If NULL, then all
675 sections will be returned.
676 @param[out] BriefDesc On return, points to a callee-allocated buffer
677 containing brief description text.
678 @param[out] HelpText On return, points to a callee-allocated buffer
679 containing all specified help text.
681 @retval EFI_SUCCESS The help text was returned.
682 @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
684 @retval EFI_INVALID_PARAMETER HelpText is NULL.
685 @retval EFI_INVALID_PARAMETER ManFileName is invalid.
686 @retval EFI_NOT_FOUND There is no help text available for Command.
691 IN CONST CHAR16
*ManFileName
,
692 IN CONST CHAR16
*Command
,
693 IN CONST CHAR16
*Sections OPTIONAL
,
694 OUT CHAR16
**BriefDesc OPTIONAL
,
695 OUT CHAR16
**HelpText
699 SHELL_FILE_HANDLE FileHandle
;
705 EFI_DEVICE_PATH_PROTOCOL
*FileDevPath
;
706 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
708 if ( ManFileName
== NULL
712 return (EFI_INVALID_PARAMETER
);
720 // See if it's in HII first
722 TempString
= ShellCommandGetCommandHelp(Command
);
723 if (TempString
!= NULL
) {
724 TempString2
= TempString
;
725 Status
= ManBufferFindTitleSection(&TempString2
, Command
, BriefDesc
, &BriefSize
);
726 if (!EFI_ERROR(Status
) && HelpText
!= NULL
){
727 Status
= ManBufferFindSections(TempString2
, Sections
, HelpText
, &HelpSize
);
731 TempString
= GetManFileName(ManFileName
);
732 if (TempString
== NULL
) {
733 return (EFI_INVALID_PARAMETER
);
736 Status
= SearchPathForFile(TempString
, &FileHandle
);
737 if (EFI_ERROR(Status
)) {
738 FileDevPath
= FileDevicePath(NULL
, TempString
);
739 DevPath
= AppendDevicePath (ShellInfoObject
.ImageDevPath
, FileDevPath
);
740 Status
= InternalOpenFileDevicePath(DevPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
741 FreePool(FileDevPath
);
745 if (!EFI_ERROR(Status
)) {
748 Status
= ManFileFindTitleSection(FileHandle
, Command
, BriefDesc
, &BriefSize
, &Ascii
);
749 if (!EFI_ERROR(Status
) && HelpText
!= NULL
){
750 Status
= ManFileFindSections(FileHandle
, Sections
, HelpText
, &HelpSize
, Ascii
);
752 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
757 if (TempString
!= NULL
) {
758 FreePool(TempString
);