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