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