]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/ShellManParser.c
ShellPkg: Support finding help message embedded in resource section.
[mirror_edk2.git] / ShellPkg / Application / Shell / ShellManParser.c
1 /** @file
2 Provides interface to shell MAN file parser.
3
4 Copyright (c) 2009 - 2016, 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 EFIAPI
67 InternalShellCharToUpper (
68 IN CHAR16 Char
69 );
70
71 /**
72 Verifies that the filename has .EFI on the end.
73
74 allocates a new buffer and copies the name (appending .EFI if necessary).
75 Caller to free the buffer.
76
77 @param[in] NameString original name string
78
79 @return the new filename with .efi as the extension.
80 **/
81 CHAR16 *
82 EFIAPI
83 GetExecuatableFileName (
84 IN CONST CHAR16 *NameString
85 )
86 {
87 CHAR16 *Buffer;
88 CHAR16 *SuffixStr;
89 if (NameString == NULL) {
90 return (NULL);
91 }
92
93 //
94 // Fix the file name
95 //
96 if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".efi"), L".efi", StrLen(L".efi"))==0) {
97 Buffer = AllocateCopyPool(StrSize(NameString), NameString);
98 } else if (StrnCmp(NameString+StrLen(NameString)-StrLen(L".man"), L".man", StrLen(L".man"))==0) {
99 Buffer = AllocateCopyPool(StrSize(NameString), NameString);
100 if (Buffer != NULL) {
101 SuffixStr = Buffer+StrLen(Buffer)-StrLen(L".man");
102 StrnCpyS (SuffixStr, StrSize(L".man")/sizeof(CHAR16), L".efi", StrLen(L".efi"));
103 }
104 } else {
105 Buffer = AllocateZeroPool(StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16));
106 if (Buffer != NULL) {
107 StrnCpyS( Buffer,
108 (StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
109 NameString,
110 StrLen(NameString)
111 );
112 StrnCatS( Buffer,
113 (StrSize(NameString) + StrLen(L".efi")*sizeof(CHAR16))/sizeof(CHAR16),
114 L".efi",
115 StrLen(L".efi")
116 );
117 }
118 }
119 return (Buffer);
120
121 }
122
123 /**
124 Verifies that the filename has .MAN on the end.
125
126 allocates a new buffer and copies the name (appending .MAN if necessary)
127
128 ASSERT if ManFileName is NULL
129
130 @param[in] ManFileName original filename
131
132 @return the new filename with .man as the extension.
133 **/
134 CHAR16 *
135 EFIAPI
136 GetManFileName(
137 IN CONST CHAR16 *ManFileName
138 )
139 {
140 CHAR16 *Buffer;
141 if (ManFileName == NULL) {
142 return (NULL);
143 }
144 //
145 // Fix the file name
146 //
147 if (StrnCmp(ManFileName+StrLen(ManFileName)-4, L".man", 4)==0) {
148 Buffer = AllocateCopyPool(StrSize(ManFileName), ManFileName);
149 } else {
150 Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16));
151 if (Buffer != NULL) {
152 StrnCpyS( Buffer,
153 (StrSize(ManFileName) + 4*sizeof(CHAR16))/sizeof(CHAR16),
154 ManFileName,
155 StrLen(ManFileName)
156 );
157 StrnCatS( Buffer,
158 (StrSize(ManFileName) + 4*sizeof(CHAR16))/sizeof(CHAR16),
159 L".man",
160 4
161 );
162 }
163 }
164 return (Buffer);
165 }
166
167 /**
168 Search the path environment variable for possible locations and test for
169 which one contains a man file with the name specified. If a valid file is found
170 stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
171
172 @param[in] FileName Name of the file to find and open.
173 @param[out] Handle Pointer to the handle of the found file. The
174 value of this is undefined for return values
175 except EFI_SUCCESS.
176
177 @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
178 @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
179 @retval EFI_NOT_FOUND The file was not found.
180 **/
181 EFI_STATUS
182 EFIAPI
183 SearchPathForFile(
184 IN CONST CHAR16 *FileName,
185 OUT SHELL_FILE_HANDLE *Handle
186 )
187 {
188 CHAR16 *FullFileName;
189 EFI_STATUS Status;
190
191 if ( FileName == NULL
192 || Handle == NULL
193 || StrLen(FileName) == 0
194 ){
195 return (EFI_INVALID_PARAMETER);
196 }
197
198 FullFileName = ShellFindFilePath(FileName);
199 if (FullFileName == NULL) {
200 return (EFI_NOT_FOUND);
201 }
202
203 //
204 // now open that file
205 //
206 Status = EfiShellOpenFileByName(FullFileName, Handle, EFI_FILE_MODE_READ);
207 FreePool(FullFileName);
208
209 return (Status);
210 }
211
212 /**
213 parses through Buffer (which is MAN file formatted) and returns the
214 detailed help for any sub section specified in the comma seperated list of
215 sections provided. If the end of the file or a .TH section is found then
216 return.
217
218 Upon a sucessful return the caller is responsible to free the memory in *HelpText
219
220 @param[in] Buffer Buffer to read from
221 @param[in] Sections name of command's sub sections to find
222 @param[in] HelpText pointer to pointer to string where text goes.
223 @param[in] HelpSize pointer to size of allocated HelpText (may be updated)
224
225 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
226 @retval EFI_SUCCESS the section was found and its description sotred in
227 an alloceted buffer.
228 **/
229 EFI_STATUS
230 EFIAPI
231 ManBufferFindSections(
232 IN CONST CHAR16 *Buffer,
233 IN CONST CHAR16 *Sections,
234 IN CHAR16 **HelpText,
235 IN UINTN *HelpSize
236 )
237 {
238 EFI_STATUS Status;
239 CONST CHAR16 *CurrentLocation;
240 BOOLEAN CurrentlyReading;
241 CHAR16 *SectionName;
242 UINTN SectionLen;
243 BOOLEAN Found;
244 CHAR16 *TempString;
245 CHAR16 *TempString2;
246
247 if ( Buffer == NULL
248 || HelpText == NULL
249 || HelpSize == NULL
250 ){
251 return (EFI_INVALID_PARAMETER);
252 }
253
254 Status = EFI_SUCCESS;
255 CurrentlyReading = FALSE;
256 Found = FALSE;
257
258 for (CurrentLocation = Buffer,TempString = NULL
259 ; CurrentLocation != NULL && *CurrentLocation != CHAR_NULL
260 ; CurrentLocation=StrStr(CurrentLocation, L"\r\n"),TempString = NULL
261 ){
262 while(CurrentLocation[0] == L'\r' || CurrentLocation[0] == L'\n') {
263 CurrentLocation++;
264 }
265 if (CurrentLocation[0] == L'#') {
266 //
267 // Skip comment lines
268 //
269 continue;
270 }
271 if (StrnCmp(CurrentLocation, L".TH", 3) == 0) {
272 //
273 // we hit the end of this commands section so stop.
274 //
275 break;
276 }
277 if (StrnCmp(CurrentLocation, L".SH ", 4) == 0) {
278 if (Sections == NULL) {
279 CurrentlyReading = TRUE;
280 continue;
281 } else if (CurrentlyReading) {
282 CurrentlyReading = FALSE;
283 }
284 CurrentLocation += 4;
285 //
286 // is this a section we want to read in?
287 //
288 if (StrLen(CurrentLocation)!=0) {
289 TempString2 = StrStr(CurrentLocation, L" ");
290 TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\r"));
291 TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n"));
292 ASSERT(TempString == NULL);
293 TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation);
294 if (TempString == NULL) {
295 Status = EFI_OUT_OF_RESOURCES;
296 break;
297 }
298 SectionName = TempString;
299 SectionLen = StrLen(SectionName);
300 SectionName = StrStr(Sections, SectionName);
301 if (SectionName == NULL) {
302 SHELL_FREE_NON_NULL(TempString);
303 continue;
304 }
305 if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
306 CurrentlyReading = TRUE;
307 }
308 }
309 } else if (CurrentlyReading) {
310 Found = TRUE;
311 if (StrLen(CurrentLocation)!=0) {
312 TempString2 = StrStr(CurrentLocation, L"\r");
313 TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n"));
314 ASSERT(TempString == NULL);
315 TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation);
316 if (TempString == NULL) {
317 Status = EFI_OUT_OF_RESOURCES;
318 break;
319 }
320 //
321 // copy and save the current line.
322 //
323 ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
324 StrnCatGrow (HelpText, HelpSize, TempString, 0);
325 if (HelpText == NULL) {
326 Status = EFI_OUT_OF_RESOURCES;
327 break;
328 }
329 StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
330 if (HelpText == NULL) {
331 Status = EFI_OUT_OF_RESOURCES;
332 break;
333 }
334 }
335 }
336 SHELL_FREE_NON_NULL(TempString);
337 }
338 SHELL_FREE_NON_NULL(TempString);
339 if (!Found && !EFI_ERROR(Status)) {
340 return (EFI_NOT_FOUND);
341 }
342 return (Status);
343 }
344
345 /**
346 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
347 detailed help for any sub section specified in the comma seperated list of
348 sections provided. If the end of the file or a .TH section is found then
349 return.
350
351 Upon a sucessful return the caller is responsible to free the memory in *HelpText
352
353 @param[in] Handle FileHandle to read from
354 @param[in] Sections name of command's sub sections to find
355 @param[out] HelpText pointer to pointer to string where text goes.
356 @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
357 @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
358
359 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
360 @retval EFI_SUCCESS the section was found and its description sotred in
361 an alloceted buffer.
362 **/
363 EFI_STATUS
364 EFIAPI
365 ManFileFindSections(
366 IN SHELL_FILE_HANDLE Handle,
367 IN CONST CHAR16 *Sections,
368 OUT CHAR16 **HelpText,
369 OUT UINTN *HelpSize,
370 IN BOOLEAN Ascii
371 )
372 {
373 EFI_STATUS Status;
374 CHAR16 *ReadLine;
375 UINTN Size;
376 BOOLEAN CurrentlyReading;
377 CHAR16 *SectionName;
378 UINTN SectionLen;
379 BOOLEAN Found;
380
381 if ( Handle == NULL
382 || HelpText == NULL
383 || HelpSize == NULL
384 ){
385 return (EFI_INVALID_PARAMETER);
386 }
387
388 Status = EFI_SUCCESS;
389 CurrentlyReading = FALSE;
390 Size = 1024;
391 Found = FALSE;
392
393 ReadLine = AllocateZeroPool(Size);
394 if (ReadLine == NULL) {
395 return (EFI_OUT_OF_RESOURCES);
396 }
397
398 for (;!ShellFileHandleEof(Handle);Size = 1024) {
399 Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, &Ascii);
400 if (ReadLine[0] == L'#') {
401 //
402 // Skip comment lines
403 //
404 continue;
405 }
406 //
407 // ignore too small of buffer...
408 //
409 if (Status == EFI_BUFFER_TOO_SMALL) {
410 Status = EFI_SUCCESS;
411 }
412 if (EFI_ERROR(Status)) {
413 break;
414 } else if (StrnCmp(ReadLine, L".TH", 3) == 0) {
415 //
416 // we hit the end of this commands section so stop.
417 //
418 break;
419 } else if (StrnCmp(ReadLine, L".SH", 3) == 0) {
420 if (Sections == NULL) {
421 CurrentlyReading = TRUE;
422 continue;
423 }
424 //
425 // we found a section
426 //
427 if (CurrentlyReading) {
428 CurrentlyReading = FALSE;
429 }
430 //
431 // is this a section we want to read in?
432 //
433 for ( SectionName = ReadLine + 3
434 ; *SectionName == L' '
435 ; SectionName++);
436 SectionLen = StrLen(SectionName);
437 SectionName = StrStr(Sections, SectionName);
438 if (SectionName == NULL) {
439 continue;
440 }
441 if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
442 CurrentlyReading = TRUE;
443 }
444 } else if (CurrentlyReading) {
445 Found = TRUE;
446 //
447 // copy and save the current line.
448 //
449 ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
450 StrnCatGrow (HelpText, HelpSize, ReadLine, 0);
451 StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
452 }
453 }
454 FreePool(ReadLine);
455 if (!Found && !EFI_ERROR(Status)) {
456 return (EFI_NOT_FOUND);
457 }
458 return (Status);
459 }
460
461 /**
462 parses through the MAN file formatted Buffer and returns the
463 "Brief Description" for the .TH section as specified by Command. If the
464 command section is not found return EFI_NOT_FOUND.
465
466 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
467
468 @param[in] Buffer Buffer to read from
469 @param[in] Command name of command's section to find
470 @param[in] BriefDesc pointer to pointer to string where description goes.
471 @param[in] BriefSize pointer to size of allocated BriefDesc
472
473 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
474 @retval EFI_SUCCESS the section was found and its description sotred in
475 an alloceted buffer.
476 **/
477 EFI_STATUS
478 EFIAPI
479 ManBufferFindTitleSection(
480 IN CHAR16 **Buffer,
481 IN CONST CHAR16 *Command,
482 IN CHAR16 **BriefDesc,
483 IN UINTN *BriefSize
484 )
485 {
486 EFI_STATUS Status;
487 CHAR16 *TitleString;
488 CHAR16 *TitleEnd;
489 CHAR16 *CurrentLocation;
490 UINTN TitleLength;
491 UINTN Start;
492 CONST CHAR16 StartString[] = L".TH ";
493 CONST CHAR16 EndString[] = L" 0 ";
494
495 if ( Buffer == NULL
496 || Command == NULL
497 || (BriefDesc != NULL && BriefSize == NULL)
498 ){
499 return (EFI_INVALID_PARAMETER);
500 }
501
502 Status = EFI_SUCCESS;
503
504 //
505 // Do not pass any leading path information that may be present to IsTitleHeader().
506 //
507 Start = StrLen(Command);
508 while ((Start != 0)
509 && (*(Command + Start - 1) != L'\\')
510 && (*(Command + Start - 1) != L'/')
511 && (*(Command + Start - 1) != L':')) {
512 --Start;
513 }
514
515 //
516 // more characters for StartString and EndString
517 //
518 TitleLength = StrSize(Command + Start) + (StrLen(StartString) + StrLen(EndString)) * sizeof(CHAR16);
519 TitleString = AllocateZeroPool(TitleLength);
520 if (TitleString == NULL) {
521 return (EFI_OUT_OF_RESOURCES);
522 }
523 StrCpyS(TitleString, TitleLength/sizeof(CHAR16), StartString);
524 StrCatS(TitleString, TitleLength/sizeof(CHAR16), Command + Start);
525 StrCatS(TitleString, TitleLength/sizeof(CHAR16), EndString);
526
527 CurrentLocation = StrStr(*Buffer, TitleString);
528 if (CurrentLocation == NULL){
529 Status = EFI_NOT_FOUND;
530 } else {
531 //
532 // we found it so copy out the rest of the line into BriefDesc
533 // After skipping any spaces or zeroes
534 //
535 for (CurrentLocation += StrLen(TitleString)
536 ; *CurrentLocation == L' ' || *CurrentLocation == L'0' || *CurrentLocation == L'1' || *CurrentLocation == L'\"'
537 ; CurrentLocation++);
538
539 TitleEnd = StrStr(CurrentLocation, L"\"");
540 if (TitleEnd == NULL) {
541 Status = EFI_DEVICE_ERROR;
542 } else {
543 if (BriefDesc != NULL) {
544 *BriefSize = StrSize(TitleEnd);
545 *BriefDesc = AllocateZeroPool(*BriefSize);
546 if (*BriefDesc == NULL) {
547 Status = EFI_OUT_OF_RESOURCES;
548 } else {
549 StrnCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), CurrentLocation, TitleEnd-CurrentLocation);
550 }
551 }
552
553 for (CurrentLocation = TitleEnd
554 ; *CurrentLocation != L'\n'
555 ; CurrentLocation++);
556 for (
557 ; *CurrentLocation == L' ' || *CurrentLocation == L'\n' || *CurrentLocation == L'\r'
558 ; CurrentLocation++);
559 *Buffer = CurrentLocation;
560 }
561 }
562
563 FreePool(TitleString);
564 return (Status);
565 }
566
567 /**
568 Parses a line from a MAN file to see if it is the Title Header. If it is, then
569 if the "Brief Description" is desired, allocate a buffer for it and return a
570 copy. Upon a sucessful return the caller is responsible to free the memory in
571 *BriefDesc
572
573 Uses a simple state machine that allows "unlimited" whitespace before and after the
574 ".TH", compares Command and the MAN file commnd name without respect to case, and
575 allows "unlimited" whitespace and '0' and '1' characters before the Short Description.
576 The PCRE regex describing this functionality is: ^\s*\.TH\s+(\S)\s[\s01]*(.*)$
577 where group 1 is the Command Name and group 2 is the Short Description.
578
579 @param[in] Command name of command whose MAN file we think Line came from
580 @param[in] Line Pointer to a line from the MAN file
581 @param[out] BriefDesc pointer to pointer to string where description goes.
582 @param[out] BriefSize pointer to size of allocated BriefDesc
583 @param[out] Found TRUE if the Title Header was found and it belongs to Command
584
585 @retval TRUE Line contained the Title Header
586 @retval FALSE Line did not contain the Title Header
587 **/
588 BOOLEAN
589 IsTitleHeader(
590 IN CONST CHAR16 *Command,
591 IN CHAR16 *Line,
592 OUT CHAR16 **BriefDesc OPTIONAL,
593 OUT UINTN *BriefSize OPTIONAL,
594 OUT BOOLEAN *Found
595 )
596 {
597 // The states of a simple state machine used to recognize a title header line
598 // and to extract the Short Description, if desired.
599 typedef enum {
600 LookForThMacro, LookForCommandName, CompareCommands, GetBriefDescription, Final
601 } STATEVALUES;
602
603 STATEVALUES State;
604 UINTN CommandIndex; // Indexes Command as we compare its chars to the MAN file.
605 BOOLEAN ReturnValue; // TRUE if this the Title Header line of *some* MAN file.
606 BOOLEAN ReturnFound; // TRUE if this the Title Header line of *the desired* MAN file.
607
608 ReturnValue = FALSE;
609 ReturnFound = FALSE;
610 CommandIndex = 0;
611 State = LookForThMacro;
612
613 do {
614
615 if (*Line == L'\0') {
616 break;
617 }
618
619 switch (State) {
620
621 // Handle "^\s*.TH\s"
622 // Go to state LookForCommandName if the title header macro is present; otherwise,
623 // eat white space. If we see something other than white space, this is not a
624 // title header line.
625 case LookForThMacro:
626 if (StrnCmp (L".TH ", Line, 4) == 0 || StrnCmp (L".TH\t", Line, 4) == 0) {
627 Line += 4;
628 State = LookForCommandName;
629 }
630 else if (*Line == L' ' || *Line == L'\t') {
631 Line++;
632 }
633 else {
634 State = Final;
635 }
636 break;
637
638 // Handle "\s*"
639 // Eat any "extra" whitespace after the title header macro (we have already seen
640 // at least one white space character). Go to state CompareCommands when a
641 // non-white space is seen.
642 case LookForCommandName:
643 if (*Line == L' ' || *Line == L'\t') {
644 Line++;
645 }
646 else {
647 ReturnValue = TRUE; // This is *some* command's title header line.
648 State = CompareCommands;
649 // Do not increment Line; it points to the first character of the command
650 // name on the title header line.
651 }
652 break;
653
654 // Handle "(\S)\s"
655 // Compare Command to the title header command name, ignoring case. When we
656 // reach the end of the command (i.e. we see white space), the next state
657 // depends on whether the caller wants a copy of the Brief Description.
658 case CompareCommands:
659 if (*Line == L' ' || *Line == L'\t') {
660 ReturnFound = TRUE; // This is the desired command's title header line.
661 State = (BriefDesc == NULL) ? Final : GetBriefDescription;
662 }
663 else if (InternalShellCharToUpper (*Line) != InternalShellCharToUpper (*(Command + CommandIndex++))) {
664 State = Final;
665 }
666 Line++;
667 break;
668
669 // Handle "[\s01]*(.*)$"
670 // Skip whitespace, '0', and '1' characters, if any, prior to the brief description.
671 // Return the description to the caller.
672 case GetBriefDescription:
673 if (*Line != L' ' && *Line != L'\t' && *Line != L'0' && *Line != L'1') {
674 *BriefSize = StrSize(Line);
675 *BriefDesc = AllocateZeroPool(*BriefSize);
676 if (*BriefDesc != NULL) {
677 StrCpyS(*BriefDesc, (*BriefSize)/sizeof(CHAR16), Line);
678 }
679 State = Final;
680 }
681 Line++;
682 break;
683
684 default:
685 break;
686 }
687
688 } while (State < Final);
689
690 *Found = ReturnFound;
691 return ReturnValue;
692 }
693
694 /**
695 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
696 "Brief Description" for the .TH section as specified by Command. If the
697 command section is not found return EFI_NOT_FOUND.
698
699 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
700
701 @param[in] Handle FileHandle to read from
702 @param[in] Command name of command's section to find as entered on the
703 command line (may be a relative or absolute path or
704 be in any case: upper, lower, or mixed in numerous ways!).
705 @param[out] BriefDesc pointer to pointer to string where description goes.
706 @param[out] BriefSize pointer to size of allocated BriefDesc
707 @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
708 set if the file handle is at the 0 position.
709
710 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
711 @retval EFI_SUCCESS the section was found and its description stored in
712 an allocated buffer if requested.
713 **/
714 EFI_STATUS
715 EFIAPI
716 ManFileFindTitleSection(
717 IN SHELL_FILE_HANDLE Handle,
718 IN CONST CHAR16 *Command,
719 OUT CHAR16 **BriefDesc OPTIONAL,
720 OUT UINTN *BriefSize OPTIONAL,
721 IN OUT BOOLEAN *Ascii
722 )
723 {
724 EFI_STATUS Status;
725 CHAR16 *ReadLine;
726 UINTN Size;
727 BOOLEAN Found;
728 UINTN Start;
729
730 if ( Handle == NULL
731 || Command == NULL
732 || (BriefDesc != NULL && BriefSize == NULL)
733 ){
734 return (EFI_INVALID_PARAMETER);
735 }
736
737 Status = EFI_SUCCESS;
738 Size = 1024;
739 Found = FALSE;
740
741 ReadLine = AllocateZeroPool(Size);
742 if (ReadLine == NULL) {
743 return (EFI_OUT_OF_RESOURCES);
744 }
745
746 //
747 // Do not pass any leading path information that may be present to IsTitleHeader().
748 //
749 Start = StrLen(Command);
750 while ((Start != 0)
751 && (*(Command + Start - 1) != L'\\')
752 && (*(Command + Start - 1) != L'/')
753 && (*(Command + Start - 1) != L':')) {
754 --Start;
755 }
756
757 for (;!ShellFileHandleEof(Handle);Size = 1024) {
758 Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, Ascii);
759 //
760 // ignore too small of buffer...
761 //
762 if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
763 break;
764 }
765
766 Status = EFI_NOT_FOUND;
767 if (IsTitleHeader (Command+Start, ReadLine, BriefDesc, BriefSize, &Found)) {
768 Status = Found ? EFI_SUCCESS : EFI_NOT_FOUND;
769 break;
770 }
771 }
772
773 FreePool(ReadLine);
774 return (Status);
775 }
776
777 /**
778 This function returns the help information for the specified command. The help text
779 will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
780
781 If Sections is specified, then each section name listed will be compared in a casesensitive
782 manner, to the section names described in Appendix B. If the section exists,
783 it will be appended to the returned help text. If the section does not exist, no
784 information will be returned. If Sections is NULL, then all help text information
785 available will be returned.
786
787 if BriefDesc is NULL, then the breif description will not be savedd seperatly,
788 but placed first in the main HelpText.
789
790 @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
791 @param[in] Command Points to the NULL-terminated UEFI Shell command name.
792 @param[in] Sections Points to the NULL-terminated comma-delimited
793 section names to return. If NULL, then all
794 sections will be returned.
795 @param[out] BriefDesc On return, points to a callee-allocated buffer
796 containing brief description text.
797 @param[out] HelpText On return, points to a callee-allocated buffer
798 containing all specified help text.
799
800 @retval EFI_SUCCESS The help text was returned.
801 @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
802 returned help text.
803 @retval EFI_INVALID_PARAMETER HelpText is NULL.
804 @retval EFI_INVALID_PARAMETER ManFileName is invalid.
805 @retval EFI_NOT_FOUND There is no help text available for Command.
806 **/
807 EFI_STATUS
808 EFIAPI
809 ProcessManFile(
810 IN CONST CHAR16 *ManFileName,
811 IN CONST CHAR16 *Command,
812 IN CONST CHAR16 *Sections OPTIONAL,
813 OUT CHAR16 **BriefDesc OPTIONAL,
814 OUT CHAR16 **HelpText
815 )
816 {
817 CHAR16 *TempString;
818 SHELL_FILE_HANDLE FileHandle;
819 EFI_HANDLE CmdFileImgHandle;
820 EFI_STATUS Status;
821 UINTN HelpSize;
822 UINTN BriefSize;
823 UINTN StringIdWalker;
824 BOOLEAN Ascii;
825 CHAR16 *TempString2;
826 CHAR16 *CmdFileName;
827 CHAR16 *CmdFilePathName;
828 CHAR16 *StringBuff;
829 EFI_DEVICE_PATH_PROTOCOL *FileDevPath;
830 EFI_DEVICE_PATH_PROTOCOL *DevPath;
831 EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
832
833 if ( ManFileName == NULL
834 || Command == NULL
835 || HelpText == NULL
836 ){
837 return (EFI_INVALID_PARAMETER);
838 }
839
840 HelpSize = 0;
841 BriefSize = 0;
842 StringIdWalker = 0;
843 TempString = NULL;
844 Ascii = FALSE;
845 CmdFileName = NULL;
846 CmdFilePathName = NULL;
847 CmdFileImgHandle = NULL;
848 StringBuff = NULL;
849 PackageListHeader = NULL;
850 FileDevPath = NULL;
851 DevPath = NULL;
852
853 //
854 // See if it's in HII first
855 //
856 TempString = ShellCommandGetCommandHelp(Command);
857 if (TempString != NULL) {
858 TempString2 = TempString;
859 Status = ManBufferFindTitleSection(&TempString2, Command, BriefDesc, &BriefSize);
860 if (!EFI_ERROR(Status) && HelpText != NULL){
861 Status = ManBufferFindSections(TempString2, Sections, HelpText, &HelpSize);
862 }
863 } else {
864 //
865 // If the image is a external app, check .MAN file first.
866 //
867 FileHandle = NULL;
868 TempString = GetManFileName(ManFileName);
869 if (TempString == NULL) {
870 return (EFI_INVALID_PARAMETER);
871 }
872
873 Status = SearchPathForFile(TempString, &FileHandle);
874 if (EFI_ERROR(Status)) {
875 FileDevPath = FileDevicePath(NULL, TempString);
876 DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
877 Status = InternalOpenFileDevicePath(DevPath, &FileHandle, EFI_FILE_MODE_READ, 0);
878 SHELL_FREE_NON_NULL(FileDevPath);
879 SHELL_FREE_NON_NULL(DevPath);
880 }
881
882 if (!EFI_ERROR(Status)) {
883 HelpSize = 0;
884 BriefSize = 0;
885 Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
886 if (!EFI_ERROR(Status) && HelpText != NULL){
887 Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii);
888 }
889 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
890 if (!EFI_ERROR(Status)) {
891 //
892 // Get help text from .MAN file success.
893 //
894 goto Done;
895 }
896 }
897
898 //
899 // Load the app image to check EFI_HII_PACKAGE_LIST_PROTOCOL.
900 //
901 CmdFileName = GetExecuatableFileName(TempString);
902 if (CmdFileName == NULL) {
903 Status = EFI_OUT_OF_RESOURCES;
904 goto Done;
905 }
906 //
907 // If the file in CWD then use the file name, else use the full
908 // path name.
909 //
910 CmdFilePathName = ShellFindFilePath(CmdFileName);
911 if (CmdFilePathName == NULL) {
912 Status = EFI_NOT_FOUND;
913 goto Done;
914 }
915 DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CmdFilePathName);
916 Status = gBS->LoadImage(FALSE, gImageHandle, DevPath, NULL, 0, &CmdFileImgHandle);
917 if(EFI_ERROR(Status)) {
918 *HelpText = NULL;
919 goto Done;
920 }
921 Status = gBS->OpenProtocol(
922 CmdFileImgHandle,
923 &gEfiHiiPackageListProtocolGuid,
924 (VOID**)&PackageListHeader,
925 gImageHandle,
926 NULL,
927 EFI_OPEN_PROTOCOL_GET_PROTOCOL
928 );
929 if(EFI_ERROR(Status)) {
930 *HelpText = NULL;
931 goto Done;
932 }
933
934 //
935 // If get package list on image handle, install it on HiiDatabase.
936 //
937 Status = gBS->InstallProtocolInterface (
938 &mShellManDriverHandle,
939 &gEfiDevicePathProtocolGuid,
940 EFI_NATIVE_INTERFACE,
941 &mShellManHiiDevicePath
942 );
943 if (EFI_ERROR(Status)) {
944 goto Done;
945 }
946
947 Status = gHiiDatabase->NewPackageList (
948 gHiiDatabase,
949 PackageListHeader,
950 mShellManDriverHandle,
951 &mShellManHiiHandle
952 );
953 if (EFI_ERROR (Status)) {
954 goto Done;
955 }
956
957 StringIdWalker = 1;
958 do {
959 SHELL_FREE_NON_NULL(StringBuff);
960 if (BriefDesc != NULL) {
961 SHELL_FREE_NON_NULL(*BriefDesc);
962 }
963 StringBuff = HiiGetString (mShellManHiiHandle, (EFI_STRING_ID)StringIdWalker, NULL);
964 if (StringBuff == NULL) {
965 Status = EFI_NOT_FOUND;
966 goto Done;
967 }
968 TempString2 = StringBuff;
969 Status = ManBufferFindTitleSection(&TempString2, Command, BriefDesc, &BriefSize);
970 if (!EFI_ERROR(Status) && HelpText != NULL){
971 Status = ManBufferFindSections(TempString2, Sections, HelpText, &HelpSize);
972 }
973 if (!EFI_ERROR(Status)){
974 //
975 // Found what we need and return
976 //
977 goto Done;
978 }
979
980 StringIdWalker += 1;
981 } while (StringIdWalker < 0xFFFF && StringBuff != NULL);
982
983 }
984
985 Done:
986 if (mShellManDriverHandle != NULL) {
987 gBS->UninstallProtocolInterface (
988 mShellManDriverHandle,
989 &gEfiDevicePathProtocolGuid,
990 &mShellManHiiDevicePath
991 );
992 mShellManDriverHandle = NULL;
993 }
994
995 if (mShellManHiiHandle != NULL) {
996 HiiRemovePackages (mShellManHiiHandle);
997 mShellManHiiHandle = NULL;
998 }
999
1000 if (CmdFileImgHandle != NULL) {
1001 Status = gBS->UnloadImage (CmdFileImgHandle);
1002 }
1003
1004 SHELL_FREE_NON_NULL(StringBuff);
1005 SHELL_FREE_NON_NULL(TempString);
1006 SHELL_FREE_NON_NULL(CmdFileName);
1007 SHELL_FREE_NON_NULL(CmdFilePathName);
1008 SHELL_FREE_NON_NULL(FileDevPath);
1009 SHELL_FREE_NON_NULL(DevPath);
1010
1011 return (Status);
1012 }
1013