]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/ShellManParser.c
ShellPkg/help: Fix "-?" may not show manual sometimes
[mirror_edk2.git] / ShellPkg / Application / Shell / ShellManParser.c
1 /** @file
2 Provides interface to shell MAN file parser.
3
4 Copyright (c) 2009 - 2018, 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
10
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.
13
14 **/
15
16 #include "Shell.h"
17
18 #define SHELL_MAN_HII_GUID \
19 { \
20 0xf62ccd0c, 0x2449, 0x453c, { 0x8a, 0xcb, 0x8c, 0xc5, 0x7c, 0xf0, 0x2a, 0x97 } \
21 }
22
23 EFI_HII_HANDLE mShellManHiiHandle = NULL;
24 EFI_HANDLE mShellManDriverHandle = NULL;
25
26
27 SHELL_MAN_HII_VENDOR_DEVICE_PATH mShellManHiiDevicePath = {
28 {
29 {
30 HARDWARE_DEVICE_PATH,
31 HW_VENDOR_DP,
32 {
33 (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
34 (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
35 }
36 },
37 SHELL_MAN_HII_GUID
38 },
39 {
40 END_DEVICE_PATH_TYPE,
41 END_ENTIRE_DEVICE_PATH_SUBTYPE,
42 {
43 (UINT8) (END_DEVICE_PATH_LENGTH),
44 (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
45 }
46 }
47 };
48
49
50 /**
51 Convert a Unicode character to upper case only if
52 it maps to a valid small-case ASCII character.
53
54 This internal function only deal with Unicode character
55 which maps to a valid small-case ASCII character, i.e.
56 L'a' to L'z'. For other Unicode character, the input character
57 is returned directly.
58
59 @param Char The character to convert.
60
61 @retval LowerCharacter If the Char is with range L'a' to L'z'.
62 @retval Unchanged Otherwise.
63
64 **/
65 CHAR16
66 InternalShellCharToUpper (
67 IN CHAR16 Char
68 );
69
70 /**
71 Verifies that the filename has .EFI on the end.
72
73 allocates a new buffer and copies the name (appending .EFI if necessary).
74 Caller to free the buffer.
75
76 @param[in] NameString original name string
77
78 @return the new filename with .efi as the extension.
79 **/
80 CHAR16 *
81 GetExecuatableFileName (
82 IN CONST CHAR16 *NameString
83 )
84 {
85 CHAR16 *Buffer;
86 CHAR16 *SuffixStr;
87 if (NameString == NULL) {
88 return (NULL);
89 }
90
91 //
92 // Fix the file name
93 //
94 if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".efi"), L".efi", StrLen(L".efi"))==0) {
95 Buffer = AllocateCopyPool(StrSize(NameString), NameString);
96 } else if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".man"), L".man", StrLen(L".man"))==0) {
97 Buffer = AllocateCopyPool(StrSize(NameString), NameString);
98 if (Buffer != NULL) {
99 SuffixStr = Buffer+StrLen(Buffer)-StrLen(L".man");
100 StrnCpyS (SuffixStr, StrSize(L".man")/sizeof(CHAR16), L".efi", StrLen(L".efi"));
101 }
102 } else {
103 Buffer = AllocateZeroPool(StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16));
104 if (Buffer != NULL) {
105 StrnCpyS( Buffer,
106 (StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
107 NameString,
108 StrLen(NameString)
109 );
110 StrnCatS( Buffer,
111 (StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
112 L".efi",
113 StrLen(L".efi")
114 );
115 }
116 }
117 return (Buffer);
118
119 }
120
121 /**
122 Verifies that the filename has .MAN on the end.
123
124 allocates a new buffer and copies the name (appending .MAN if necessary)
125
126 ASSERT if ManFileName is NULL
127
128 @param[in] ManFileName original filename
129
130 @return the new filename with .man as the extension.
131 **/
132 CHAR16 *
133 GetManFileName(
134 IN CONST CHAR16 *ManFileName
135 )
136 {
137 CHAR16 *Buffer;
138 if (ManFileName == NULL) {
139 return (NULL);
140 }
141 //
142 // Fix the file name
143 //
144 if (StrnCmp(ManFileName+StrLen(ManFileName)-4, L".man", 4)==0) {
145 Buffer = AllocateCopyPool(StrSize(ManFileName), ManFileName);
146 } else {
147 Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16));
148 if (Buffer != NULL) {
149 StrnCpyS( Buffer,
150 (StrSize(ManFileName) + 4*sizeof(CHAR16))/sizeof(CHAR16),
151 ManFileName,
152 StrLen(ManFileName)
153 );
154 StrnCatS( Buffer,
155 (StrSize(ManFileName) + 4*sizeof(CHAR16))/sizeof(CHAR16),
156 L".man",
157 4
158 );
159 }
160 }
161 return (Buffer);
162 }
163
164 /**
165 Search the path environment variable for possible locations and test for
166 which one contains a man file with the name specified. If a valid file is found
167 stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
168
169 @param[in] FileName Name of the file to find and open.
170 @param[out] Handle Pointer to the handle of the found file. The
171 value of this is undefined for return values
172 except EFI_SUCCESS.
173
174 @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
175 @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
176 @retval EFI_NOT_FOUND The file was not found.
177 **/
178 EFI_STATUS
179 SearchPathForFile(
180 IN CONST CHAR16 *FileName,
181 OUT SHELL_FILE_HANDLE *Handle
182 )
183 {
184 CHAR16 *FullFileName;
185 EFI_STATUS Status;
186
187 if ( FileName == NULL
188 || Handle == NULL
189 || StrLen(FileName) == 0
190 ){
191 return (EFI_INVALID_PARAMETER);
192 }
193
194 FullFileName = ShellFindFilePath(FileName);
195 if (FullFileName == NULL) {
196 return (EFI_NOT_FOUND);
197 }
198
199 //
200 // now open that file
201 //
202 Status = EfiShellOpenFileByName(FullFileName, Handle, EFI_FILE_MODE_READ);
203 FreePool(FullFileName);
204
205 return (Status);
206 }
207
208 /**
209 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
210 detailed help for any sub section specified in the comma seperated list of
211 sections provided. If the end of the file or a .TH section is found then
212 return.
213
214 Upon a sucessful return the caller is responsible to free the memory in *HelpText
215
216 @param[in] Handle FileHandle to read from
217 @param[in] Sections name of command's sub sections to find
218 @param[out] HelpText pointer to pointer to string where text goes.
219 @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
220 @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
221
222 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
223 @retval EFI_SUCCESS the section was found and its description sotred in
224 an alloceted buffer.
225 **/
226 EFI_STATUS
227 ManFileFindSections(
228 IN SHELL_FILE_HANDLE Handle,
229 IN CONST CHAR16 *Sections,
230 OUT CHAR16 **HelpText,
231 OUT UINTN *HelpSize,
232 IN BOOLEAN Ascii
233 )
234 {
235 EFI_STATUS Status;
236 CHAR16 *ReadLine;
237 UINTN Size;
238 BOOLEAN CurrentlyReading;
239 CHAR16 *SectionName;
240 UINTN SectionLen;
241 BOOLEAN Found;
242
243 if ( Handle == NULL
244 || HelpText == NULL
245 || HelpSize == NULL
246 ){
247 return (EFI_INVALID_PARAMETER);
248 }
249
250 Status = EFI_SUCCESS;
251 CurrentlyReading = FALSE;
252 Size = 1024;
253 Found = FALSE;
254
255 ReadLine = AllocateZeroPool(Size);
256 if (ReadLine == NULL) {
257 return (EFI_OUT_OF_RESOURCES);
258 }
259
260 for (;!ShellFileHandleEof(Handle);Size = 1024) {
261 Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, &Ascii);
262 if (ReadLine[0] == L'#') {
263 //
264 // Skip comment lines
265 //
266 continue;
267 }
268 //
269 // ignore too small of buffer...
270 //
271 if (Status == EFI_BUFFER_TOO_SMALL) {
272 Status = EFI_SUCCESS;
273 }
274 if (EFI_ERROR(Status)) {
275 break;
276 } else if (StrnCmp(ReadLine, L".TH", 3) == 0) {
277 //
278 // we hit the end of this commands section so stop.
279 //
280 break;
281 } else if (StrnCmp(ReadLine, L".SH", 3) == 0) {
282 if (Sections == NULL) {
283 CurrentlyReading = TRUE;
284 continue;
285 }
286 //
287 // we found a section
288 //
289 if (CurrentlyReading) {
290 CurrentlyReading = FALSE;
291 }
292 //
293 // is this a section we want to read in?
294 //
295 for ( SectionName = ReadLine + 3
296 ; *SectionName == L' '
297 ; SectionName++);
298 SectionLen = StrLen(SectionName);
299 SectionName = StrStr(Sections, SectionName);
300 if (SectionName == NULL) {
301 continue;
302 }
303 if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
304 CurrentlyReading = TRUE;
305 }
306 } else if (CurrentlyReading) {
307 Found = TRUE;
308 //
309 // copy and save the current line.
310 //
311 ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
312 StrnCatGrow (HelpText, HelpSize, ReadLine, 0);
313 StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
314 }
315 }
316 FreePool(ReadLine);
317 if (!Found && !EFI_ERROR(Status)) {
318 return (EFI_NOT_FOUND);
319 }
320 return (Status);
321 }
322
323 /**
324 Parses a line from a MAN file to see if it is the Title Header. If it is, then
325 if the "Brief Description" is desired, allocate a buffer for it and return a
326 copy. Upon a sucessful return the caller is responsible to free the memory in
327 *BriefDesc
328
329 Uses a simple state machine that allows "unlimited" whitespace before and after the
330 ".TH", compares Command and the MAN file commnd name without respect to case, and
331 allows "unlimited" whitespace and '0' and '1' characters before the Short Description.
332 The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
333 where group 1 is the Command Name and group 2 is the Short Description.
334
335 @param[in] Command name of command whose MAN file we think Line came from
336 @param[in] Line Pointer to a line from the MAN file
337 @param[out] BriefDesc pointer to pointer to string where description goes.
338 @param[out] BriefSize pointer to size of allocated BriefDesc
339 @param[out] Found TRUE if the Title Header was found and it belongs to Command
340
341 @retval TRUE Line contained the Title Header
342 @retval FALSE Line did not contain the Title Header
343 **/
344 BOOLEAN
345 IsTitleHeader(
346 IN CONST CHAR16 *Command,
347 IN CHAR16 *Line,
348 OUT CHAR16 **BriefDesc OPTIONAL,
349 OUT UINTN *BriefSize OPTIONAL,
350 OUT BOOLEAN *Found
351 )
352 {
353 // The states of a simple state machine used to recognize a title header line
354 // and to extract the Short Description, if desired.
355 typedef enum {
356 LookForThMacro, LookForCommandName, CompareCommands, GetBriefDescription, Final
357 } STATEVALUES;
358
359 STATEVALUES State;
360 UINTN CommandIndex; // Indexes Command as we compare its chars to the MAN file.
361 BOOLEAN ReturnValue; // TRUE if this the Title Header line of *some* MAN file.
362 BOOLEAN ReturnFound; // TRUE if this the Title Header line of *the desired* MAN file.
363
364 ReturnValue = FALSE;
365 ReturnFound = FALSE;
366 CommandIndex = 0;
367 State = LookForThMacro;
368
369 do {
370
371 if (*Line == L'\0') {
372 break;
373 }
374
375 switch (State) {
376
377 // Handle "^\s*.TH\s"
378 // Go to state LookForCommandName if the title header macro is present; otherwise,
379 // eat white space. If we see something other than white space, this is not a
380 // title header line.
381 case LookForThMacro:
382 if (StrnCmp (L".TH ", Line, 4) == 0 || StrnCmp (L".TH\t", Line, 4) == 0) {
383 Line += 4;
384 State = LookForCommandName;
385 }
386 else if (*Line == L' ' || *Line == L'\t') {
387 Line++;
388 }
389 else {
390 State = Final;
391 }
392 break;
393
394 // Handle "\s*"
395 // Eat any "extra" whitespace after the title header macro (we have already seen
396 // at least one white space character). Go to state CompareCommands when a
397 // non-white space is seen.
398 case LookForCommandName:
399 if (*Line == L' ' || *Line == L'\t') {
400 Line++;
401 }
402 else {
403 ReturnValue = TRUE; // This is *some* command's title header line.
404 State = CompareCommands;
405 // Do not increment Line; it points to the first character of the command
406 // name on the title header line.
407 }
408 break;
409
410 // Handle "(\S)\s"
411 // Compare Command to the title header command name, ignoring case. When we
412 // reach the end of the command (i.e. we see white space), the next state
413 // depends on whether the caller wants a copy of the Brief Description.
414 case CompareCommands:
415 if (*Line == L' ' || *Line == L'\t') {
416 ReturnFound = TRUE; // This is the desired command's title header line.
417 State = (BriefDesc == NULL) ? Final : GetBriefDescription;
418 }
419 else if (InternalShellCharToUpper (*Line) != InternalShellCharToUpper (*(Command + CommandIndex++))) {
420 State = Final;
421 }
422 Line++;
423 break;
424
425 // Handle "[\s01]*(.*)$"
426 // Skip whitespace, '0', and '1' characters, if any, prior to the brief description.
427 // Return the description to the caller.
428 case GetBriefDescription:
429 if (*Line != L' ' && *Line != L'\t' && *Line != L'0' && *Line != L'1') {
430 *BriefSize = StrSize(Line);
431 *BriefDesc = AllocateZeroPool(*BriefSize);
432 if (*BriefDesc != NULL) {
433 StrCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), Line);
434 }
435 State = Final;
436 }
437 Line++;
438 break;
439
440 default:
441 break;
442 }
443
444 } while (State < Final);
445
446 *Found = ReturnFound;
447 return ReturnValue;
448 }
449
450 /**
451 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
452 "Brief Description" for the .TH section as specified by Command. If the
453 command section is not found return EFI_NOT_FOUND.
454
455 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
456
457 @param[in] Handle FileHandle to read from
458 @param[in] Command name of command's section to find as entered on the
459 command line (may be a relative or absolute path or
460 be in any case: upper, lower, or mixed in numerous ways!).
461 @param[out] BriefDesc pointer to pointer to string where description goes.
462 @param[out] BriefSize pointer to size of allocated BriefDesc
463 @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
464 set if the file handle is at the 0 position.
465
466 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
467 @retval EFI_SUCCESS the section was found and its description stored in
468 an allocated buffer if requested.
469 **/
470 EFI_STATUS
471 ManFileFindTitleSection(
472 IN SHELL_FILE_HANDLE Handle,
473 IN CONST CHAR16 *Command,
474 OUT CHAR16 **BriefDesc OPTIONAL,
475 OUT UINTN *BriefSize OPTIONAL,
476 IN OUT BOOLEAN *Ascii
477 )
478 {
479 EFI_STATUS Status;
480 CHAR16 *ReadLine;
481 UINTN Size;
482 BOOLEAN Found;
483 UINTN Start;
484
485 if ( Handle == NULL
486 || Command == NULL
487 || (BriefDesc != NULL && BriefSize == NULL)
488 ){
489 return (EFI_INVALID_PARAMETER);
490 }
491
492 Status = EFI_SUCCESS;
493 Size = 1024;
494 Found = FALSE;
495
496 ReadLine = AllocateZeroPool(Size);
497 if (ReadLine == NULL) {
498 return (EFI_OUT_OF_RESOURCES);
499 }
500
501 //
502 // Do not pass any leading path information that may be present to IsTitleHeader().
503 //
504 Start = StrLen(Command);
505 while ((Start != 0)
506 && (*(Command + Start - 1) != L'\\')
507 && (*(Command + Start - 1) != L'/')
508 && (*(Command + Start - 1) != L':')) {
509 --Start;
510 }
511
512 for (;!ShellFileHandleEof(Handle);Size = 1024) {
513 Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, Ascii);
514 //
515 // ignore too small of buffer...
516 //
517 if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
518 break;
519 }
520
521 Status = EFI_NOT_FOUND;
522 if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, &Found)) {
523 Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND;
524 break;
525 }
526 }
527
528 FreePool(ReadLine);
529 return (Status);
530 }
531
532 /**
533 This function returns the help information for the specified command. The help text
534 will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
535
536 If Sections is specified, then each section name listed will be compared in a casesensitive
537 manner, to the section names described in Appendix B. If the section exists,
538 it will be appended to the returned help text. If the section does not exist, no
539 information will be returned. If Sections is NULL, then all help text information
540 available will be returned.
541
542 if BriefDesc is NULL, then the breif description will not be savedd seperatly,
543 but placed first in the main HelpText.
544
545 @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
546 @param[in] Command Points to the NULL-terminated UEFI Shell command name.
547 @param[in] Sections Points to the NULL-terminated comma-delimited
548 section names to return. If NULL, then all
549 sections will be returned.
550 @param[out] BriefDesc On return, points to a callee-allocated buffer
551 containing brief description text.
552 @param[out] HelpText On return, points to a callee-allocated buffer
553 containing all specified help text.
554
555 @retval EFI_SUCCESS The help text was returned.
556 @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
557 returned help text.
558 @retval EFI_INVALID_PARAMETER HelpText is NULL.
559 @retval EFI_INVALID_PARAMETER ManFileName is invalid.
560 @retval EFI_NOT_FOUND There is no help text available for Command.
561 **/
562 EFI_STATUS
563 ProcessManFile(
564 IN CONST CHAR16 *ManFileName,
565 IN CONST CHAR16 *Command,
566 IN CONST CHAR16 *Sections OPTIONAL,
567 OUT CHAR16 **BriefDesc OPTIONAL,
568 OUT CHAR16 **HelpText
569 )
570 {
571 CHAR16 *TempString;
572 SHELL_FILE_HANDLE FileHandle;
573 EFI_HANDLE CmdFileImgHandle;
574 EFI_STATUS Status;
575 UINTN HelpSize;
576 UINTN BriefSize;
577 UINTN StringIdWalker;
578 BOOLEAN Ascii;
579 CHAR16 *CmdFileName;
580 CHAR16 *CmdFilePathName;
581 EFI_DEVICE_PATH_PROTOCOL *FileDevPath;
582 EFI_DEVICE_PATH_PROTOCOL *DevPath;
583 EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
584
585 if ( ManFileName == NULL
586 || Command == NULL
587 || HelpText == NULL
588 ){
589 return (EFI_INVALID_PARAMETER);
590 }
591
592 HelpSize = 0;
593 BriefSize = 0;
594 StringIdWalker = 0;
595 TempString = NULL;
596 Ascii = FALSE;
597 CmdFileName = NULL;
598 CmdFilePathName = NULL;
599 CmdFileImgHandle = NULL;
600 PackageListHeader = NULL;
601 FileDevPath = NULL;
602 DevPath = NULL;
603
604 //
605 // See if it's in HII first
606 //
607 TempString = ShellCommandGetCommandHelp(Command);
608 if (TempString != NULL) {
609 FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL);
610 HelpSize = StrLen (TempString) * sizeof (CHAR16);
611 ShellWriteFile (FileHandle, &HelpSize, TempString);
612 ShellSetFilePosition (FileHandle, 0);
613 HelpSize = 0;
614 BriefSize = 0;
615 Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
616 if (!EFI_ERROR(Status) && HelpText != NULL){
617 Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii);
618 }
619 ShellCloseFile (&FileHandle);
620 } else {
621 //
622 // If the image is a external app, check .MAN file first.
623 //
624 FileHandle = NULL;
625 TempString = GetManFileName(ManFileName);
626 if (TempString == NULL) {
627 return (EFI_INVALID_PARAMETER);
628 }
629
630 Status = SearchPathForFile(TempString, &FileHandle);
631 if (EFI_ERROR(Status)) {
632 FileDevPath = FileDevicePath(NULL, TempString);
633 DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
634 Status = InternalOpenFileDevicePath(DevPath, &FileHandle, EFI_FILE_MODE_READ, 0);
635 SHELL_FREE_NON_NULL(FileDevPath);
636 SHELL_FREE_NON_NULL(DevPath);
637 }
638
639 if (!EFI_ERROR(Status)) {
640 HelpSize = 0;
641 BriefSize = 0;
642 Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
643 if (!EFI_ERROR(Status) && HelpText != NULL){
644 Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii);
645 }
646 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
647 if (!EFI_ERROR(Status)) {
648 //
649 // Get help text from .MAN file success.
650 //
651 goto Done;
652 }
653 }
654
655 //
656 // Load the app image to check EFI_HII_PACKAGE_LIST_PROTOCOL.
657 //
658 CmdFileName = GetExecuatableFileName(TempString);
659 if (CmdFileName == NULL) {
660 Status = EFI_OUT_OF_RESOURCES;
661 goto Done;
662 }
663 //
664 // If the file in CWD then use the file name, else use the full
665 // path name.
666 //
667 CmdFilePathName = ShellFindFilePath(CmdFileName);
668 if (CmdFilePathName == NULL) {
669 Status = EFI_NOT_FOUND;
670 goto Done;
671 }
672 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CmdFilePathName);
673 Status = gBS->LoadImage(FALSE, gImageHandle, DevPath, NULL, 0, &CmdFileImgHandle);
674 if(EFI_ERROR(Status)) {
675 *HelpText = NULL;
676 goto Done;
677 }
678 Status = gBS->OpenProtocol(
679 CmdFileImgHandle,
680 &gEfiHiiPackageListProtocolGuid,
681 (VOID**)&PackageListHeader,
682 gImageHandle,
683 NULL,
684 EFI_OPEN_PROTOCOL_GET_PROTOCOL
685 );
686 if(EFI_ERROR(Status)) {
687 *HelpText = NULL;
688 goto Done;
689 }
690
691 //
692 // If get package list on image handle, install it on HiiDatabase.
693 //
694 Status = gBS->InstallProtocolInterface (
695 &mShellManDriverHandle,
696 &gEfiDevicePathProtocolGuid,
697 EFI_NATIVE_INTERFACE,
698 &mShellManHiiDevicePath
699 );
700 if (EFI_ERROR(Status)) {
701 goto Done;
702 }
703
704 Status = gHiiDatabase->NewPackageList (
705 gHiiDatabase,
706 PackageListHeader,
707 mShellManDriverHandle,
708 &mShellManHiiHandle
709 );
710 if (EFI_ERROR (Status)) {
711 goto Done;
712 }
713
714 StringIdWalker = 1;
715 do {
716 SHELL_FREE_NON_NULL(TempString);
717 if (BriefDesc != NULL) {
718 SHELL_FREE_NON_NULL(*BriefDesc);
719 }
720 TempString = HiiGetString (mShellManHiiHandle, (EFI_STRING_ID)StringIdWalker, NULL);
721 if (TempString == NULL) {
722 Status = EFI_NOT_FOUND;
723 goto Done;
724 }
725 FileHandle = ConvertEfiFileProtocolToShellHandle (CreateFileInterfaceMem (TRUE), NULL);
726 HelpSize = StrLen (TempString) * sizeof (CHAR16);
727 ShellWriteFile (FileHandle, &HelpSize, TempString);
728 ShellSetFilePosition (FileHandle, 0);
729 HelpSize = 0;
730 BriefSize = 0;
731 Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
732 if (!EFI_ERROR(Status) && HelpText != NULL){
733 Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii);
734 }
735 ShellCloseFile (&FileHandle);
736 if (!EFI_ERROR(Status)){
737 //
738 // Found what we need and return
739 //
740 goto Done;
741 }
742
743 StringIdWalker += 1;
744 } while (StringIdWalker < 0xFFFF && TempString != NULL);
745
746 }
747
748 Done:
749 if (mShellManDriverHandle != NULL) {
750 gBS->UninstallProtocolInterface (
751 mShellManDriverHandle,
752 &gEfiDevicePathProtocolGuid,
753 &mShellManHiiDevicePath
754 );
755 mShellManDriverHandle = NULL;
756 }
757
758 if (mShellManHiiHandle != NULL) {
759 HiiRemovePackages (mShellManHiiHandle);
760 mShellManHiiHandle = NULL;
761 }
762
763 if (CmdFileImgHandle != NULL) {
764 Status = gBS->UnloadImage (CmdFileImgHandle);
765 }
766
767 SHELL_FREE_NON_NULL(TempString);
768 SHELL_FREE_NON_NULL(CmdFileName);
769 SHELL_FREE_NON_NULL(CmdFilePathName);
770 SHELL_FREE_NON_NULL(FileDevPath);
771 SHELL_FREE_NON_NULL(DevPath);
772
773 return (Status);
774 }
775