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