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