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