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