]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Application/Shell/ShellManParser.c
ShellPkg: Add checking for memory allocation and pointer returns from functions.
[mirror_edk2.git] / ShellPkg / Application / Shell / ShellManParser.c
1 /** @file
2 Provides interface to shell MAN file parser.
3
4 Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Shell.h"
16
17 /**
18 Verifies that the filename has .MAN on the end.
19
20 allocates a new buffer and copies the name (appending .MAN if necessary)
21
22 ASSERT if ManFileName is NULL
23
24 @param[in] ManFileName original filename
25
26 @return the new filename with .man as the extension.
27 **/
28 CHAR16 *
29 EFIAPI
30 GetManFileName(
31 IN CONST CHAR16 *ManFileName
32 )
33 {
34 CHAR16 *Buffer;
35 if (ManFileName == NULL) {
36 return (NULL);
37 }
38 //
39 // Fix the file name
40 //
41 if (StrnCmp(ManFileName+StrLen(ManFileName)-4, L".man", 4)==0) {
42 Buffer = AllocateZeroPool(StrSize(ManFileName));
43 if (Buffer != NULL) {
44 StrCpy(Buffer, ManFileName);
45 }
46 } else {
47 Buffer = AllocateZeroPool(StrSize(ManFileName) + 4*sizeof(CHAR16));
48 if (Buffer != NULL) {
49 StrCpy(Buffer, ManFileName);
50 StrCat(Buffer, L".man");
51 }
52 }
53 return (Buffer);
54 }
55
56 /**
57 Search the path environment variable for possible locations and test for
58 which one contains a man file with the name specified. If a valid file is found
59 stop searching and return the (opened) SHELL_FILE_HANDLE for that file.
60
61 @param[in] FileName Name of the file to find and open.
62 @param[out] Handle Pointer to the handle of the found file. The
63 value of this is undefined for return values
64 except EFI_SUCCESS.
65
66 @retval EFI_SUCCESS The file was found. Handle is a valid SHELL_FILE_HANDLE
67 @retval EFI_INVALID_PARAMETER A parameter had an invalid value.
68 @retval EFI_NOT_FOUND The file was not found.
69 **/
70 EFI_STATUS
71 EFIAPI
72 SearchPathForFile(
73 IN CONST CHAR16 *FileName,
74 OUT SHELL_FILE_HANDLE *Handle
75 )
76 {
77 CHAR16 *FullFileName;
78 EFI_STATUS Status;
79
80 if ( FileName == NULL
81 || Handle == NULL
82 || StrLen(FileName) == 0
83 ){
84 return (EFI_INVALID_PARAMETER);
85 }
86
87 FullFileName = ShellFindFilePath(FileName);
88 if (FullFileName == NULL) {
89 return (EFI_NOT_FOUND);
90 }
91
92 //
93 // now open that file
94 //
95 Status = EfiShellOpenFileByName(FullFileName, Handle, EFI_FILE_MODE_READ);
96 FreePool(FullFileName);
97
98 return (Status);
99 }
100
101 /**
102 parses through Buffer (which is MAN file formatted) and returns the
103 detailed help for any sub section specified in the comma seperated list of
104 sections provided. If the end of the file or a .TH section is found then
105 return.
106
107 Upon a sucessful return the caller is responsible to free the memory in *HelpText
108
109 @param[in] Buffer Buffer to read from
110 @param[in] Sections name of command's sub sections to find
111 @param[in] HelpText pointer to pointer to string where text goes.
112 @param[in] HelpSize pointer to size of allocated HelpText (may be updated)
113
114 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
115 @retval EFI_SUCCESS the section was found and its description sotred in
116 an alloceted buffer.
117 **/
118 EFI_STATUS
119 EFIAPI
120 ManBufferFindSections(
121 IN CONST CHAR16 *Buffer,
122 IN CONST CHAR16 *Sections,
123 IN CHAR16 **HelpText,
124 IN UINTN *HelpSize
125 )
126 {
127 EFI_STATUS Status;
128 CONST CHAR16 *CurrentLocation;
129 BOOLEAN CurrentlyReading;
130 CHAR16 *SectionName;
131 UINTN SectionLen;
132 BOOLEAN Found;
133 CHAR16 *TempString;
134 CHAR16 *TempString2;
135
136 if ( Buffer == NULL
137 || HelpText == NULL
138 || HelpSize == NULL
139 ){
140 return (EFI_INVALID_PARAMETER);
141 }
142
143 Status = EFI_SUCCESS;
144 CurrentlyReading = FALSE;
145 Found = FALSE;
146
147 for (CurrentLocation = Buffer,TempString = NULL
148 ; CurrentLocation != NULL && *CurrentLocation != CHAR_NULL
149 ; CurrentLocation=StrStr(CurrentLocation, L"\r\n"),TempString = NULL
150 ){
151 while(CurrentLocation[0] == L'\r' || CurrentLocation[0] == L'\n') {
152 CurrentLocation++;
153 }
154 if (CurrentLocation[0] == L'#') {
155 //
156 // Skip comment lines
157 //
158 continue;
159 }
160 if (StrnCmp(CurrentLocation, L".TH", 3) == 0) {
161 //
162 // we hit the end of this commands section so stop.
163 //
164 break;
165 }
166 if (StrnCmp(CurrentLocation, L".SH ", 4) == 0) {
167 if (Sections == NULL) {
168 CurrentlyReading = TRUE;
169 continue;
170 } else if (CurrentlyReading) {
171 CurrentlyReading = FALSE;
172 }
173 CurrentLocation += 4;
174 //
175 // is this a section we want to read in?
176 //
177 if (StrLen(CurrentLocation)!=0) {
178 TempString2 = StrStr(CurrentLocation, L" ");
179 TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\r"));
180 TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n"));
181 ASSERT(TempString == NULL);
182 TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation);
183 if (TempString == NULL) {
184 Status = EFI_OUT_OF_RESOURCES;
185 break;
186 }
187 SectionName = TempString;
188 SectionLen = StrLen(SectionName);
189 SectionName = StrStr(Sections, SectionName);
190 if (SectionName == NULL) {
191 continue;
192 }
193 if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
194 CurrentlyReading = TRUE;
195 }
196 }
197 } else if (CurrentlyReading) {
198 Found = TRUE;
199 if (StrLen(CurrentLocation)!=0) {
200 TempString2 = StrStr(CurrentLocation, L"\r");
201 TempString2 = MIN(TempString2, StrStr(CurrentLocation, L"\n"));
202 ASSERT(TempString == NULL);
203 TempString = StrnCatGrow(&TempString, NULL, CurrentLocation, TempString2==NULL?0:TempString2 - CurrentLocation);
204 if (TempString == NULL) {
205 Status = EFI_OUT_OF_RESOURCES;
206 break;
207 }
208 //
209 // copy and save the current line.
210 //
211 ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
212 StrnCatGrow (HelpText, HelpSize, TempString, 0);
213 if (HelpText == NULL) {
214 Status = EFI_OUT_OF_RESOURCES;
215 break;
216 }
217 StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
218 if (HelpText == NULL) {
219 Status = EFI_OUT_OF_RESOURCES;
220 break;
221 }
222 }
223 }
224 SHELL_FREE_NON_NULL(TempString);
225 }
226 if (!Found && !EFI_ERROR(Status)) {
227 return (EFI_NOT_FOUND);
228 }
229 return (Status);
230 }
231
232 /**
233 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
234 detailed help for any sub section specified in the comma seperated list of
235 sections provided. If the end of the file or a .TH section is found then
236 return.
237
238 Upon a sucessful return the caller is responsible to free the memory in *HelpText
239
240 @param[in] Handle FileHandle to read from
241 @param[in] Sections name of command's sub sections to find
242 @param[out] HelpText pointer to pointer to string where text goes.
243 @param[out] HelpSize pointer to size of allocated HelpText (may be updated)
244 @param[in] Ascii TRUE if the file is ASCII, FALSE otherwise.
245
246 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
247 @retval EFI_SUCCESS the section was found and its description sotred in
248 an alloceted buffer.
249 **/
250 EFI_STATUS
251 EFIAPI
252 ManFileFindSections(
253 IN SHELL_FILE_HANDLE Handle,
254 IN CONST CHAR16 *Sections,
255 OUT CHAR16 **HelpText,
256 OUT UINTN *HelpSize,
257 IN BOOLEAN Ascii
258 )
259 {
260 EFI_STATUS Status;
261 CHAR16 *ReadLine;
262 UINTN Size;
263 BOOLEAN CurrentlyReading;
264 CHAR16 *SectionName;
265 UINTN SectionLen;
266 BOOLEAN Found;
267
268 if ( Handle == NULL
269 || HelpText == NULL
270 || HelpSize == NULL
271 ){
272 return (EFI_INVALID_PARAMETER);
273 }
274
275 Status = EFI_SUCCESS;
276 CurrentlyReading = FALSE;
277 Size = 1024;
278 Found = FALSE;
279
280 ReadLine = AllocateZeroPool(Size);
281 if (ReadLine == NULL) {
282 return (EFI_OUT_OF_RESOURCES);
283 }
284
285 for (;!ShellFileHandleEof(Handle);Size = 1024) {
286 Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, &Ascii);
287 if (ReadLine[0] == L'#') {
288 //
289 // Skip comment lines
290 //
291 continue;
292 }
293 //
294 // ignore too small of buffer...
295 //
296 if (Status == EFI_BUFFER_TOO_SMALL) {
297 Status = EFI_SUCCESS;
298 }
299 if (EFI_ERROR(Status)) {
300 break;
301 } else if (StrnCmp(ReadLine, L".TH", 3) == 0) {
302 //
303 // we hit the end of this commands section so stop.
304 //
305 break;
306 } else if (StrnCmp(ReadLine, L".SH", 3) == 0) {
307 if (Sections == NULL) {
308 CurrentlyReading = TRUE;
309 continue;
310 }
311 //
312 // we found a section
313 //
314 if (CurrentlyReading) {
315 CurrentlyReading = FALSE;
316 }
317 //
318 // is this a section we want to read in?
319 //
320 for ( SectionName = ReadLine + 3
321 ; *SectionName == L' '
322 ; SectionName++);
323 SectionLen = StrLen(SectionName);
324 SectionName = StrStr(Sections, SectionName);
325 if (SectionName == NULL) {
326 continue;
327 }
328 if (*(SectionName + SectionLen) == CHAR_NULL || *(SectionName + SectionLen) == L',') {
329 CurrentlyReading = TRUE;
330 }
331 } else if (CurrentlyReading) {
332 Found = TRUE;
333 //
334 // copy and save the current line.
335 //
336 ASSERT((*HelpText == NULL && *HelpSize == 0) || (*HelpText != NULL));
337 StrnCatGrow (HelpText, HelpSize, ReadLine, 0);
338 StrnCatGrow (HelpText, HelpSize, L"\r\n", 0);
339 }
340 }
341 FreePool(ReadLine);
342 if (!Found && !EFI_ERROR(Status)) {
343 return (EFI_NOT_FOUND);
344 }
345 return (Status);
346 }
347
348 /**
349 parses through the MAN file formatted Buffer and returns the
350 "Brief Description" for the .TH section as specified by Command. If the
351 command section is not found return EFI_NOT_FOUND.
352
353 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
354
355 @param[in] Handle Buffer to read from
356 @param[in] Command name of command's section to find
357 @param[in] BriefDesc pointer to pointer to string where description goes.
358 @param[in] BriefSize pointer to size of allocated BriefDesc
359
360 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
361 @retval EFI_SUCCESS the section was found and its description sotred in
362 an alloceted buffer.
363 **/
364 EFI_STATUS
365 EFIAPI
366 ManBufferFindTitleSection(
367 IN CHAR16 **Buffer,
368 IN CONST CHAR16 *Command,
369 IN CHAR16 **BriefDesc,
370 IN UINTN *BriefSize
371 )
372 {
373 EFI_STATUS Status;
374 CHAR16 *TitleString;
375 CHAR16 *TitleEnd;
376 CHAR16 *CurrentLocation;
377
378 if ( Buffer == NULL
379 || Command == NULL
380 || (BriefDesc != NULL && BriefSize == NULL)
381 ){
382 return (EFI_INVALID_PARAMETER);
383 }
384
385 Status = EFI_SUCCESS;
386
387 TitleString = AllocateZeroPool((7*sizeof(CHAR16)) + StrSize(Command));
388 if (TitleString == NULL) {
389 return (EFI_OUT_OF_RESOURCES);
390 }
391 StrCpy(TitleString, L".TH ");
392 StrCat(TitleString, Command);
393 StrCat(TitleString, L" 0 ");
394
395 CurrentLocation = StrStr(*Buffer, TitleString);
396 if (CurrentLocation == NULL){
397 Status = EFI_NOT_FOUND;
398 } else {
399 //
400 // we found it so copy out the rest of the line into BriefDesc
401 // After skipping any spaces or zeroes
402 //
403 for (CurrentLocation += StrLen(TitleString)
404 ; *CurrentLocation == L' ' || *CurrentLocation == L'0' || *CurrentLocation == L'1' || *CurrentLocation == L'\"'
405 ; CurrentLocation++);
406
407 TitleEnd = StrStr(CurrentLocation, L"\"");
408 if (TitleEnd == NULL) {
409 Status = EFI_DEVICE_ERROR;
410 } else {
411 if (BriefDesc != NULL) {
412 *BriefSize = StrSize(TitleEnd);
413 *BriefDesc = AllocateZeroPool(*BriefSize);
414 if (*BriefDesc == NULL) {
415 Status = EFI_OUT_OF_RESOURCES;
416 } else {
417 StrnCpy(*BriefDesc, CurrentLocation, TitleEnd-CurrentLocation);
418 }
419 }
420
421 for (CurrentLocation = TitleEnd
422 ; *CurrentLocation != L'\n'
423 ; CurrentLocation++);
424 for (
425 ; *CurrentLocation == L' ' || *CurrentLocation == L'\n' || *CurrentLocation == L'\r'
426 ; CurrentLocation++);
427 *Buffer = CurrentLocation;
428 }
429 }
430
431 FreePool(TitleString);
432 return (Status);
433 }
434
435 /**
436 parses through the MAN file specified by SHELL_FILE_HANDLE and returns the
437 "Brief Description" for the .TH section as specified by Command. if the
438 command section is not found return EFI_NOT_FOUND.
439
440 Upon a sucessful return the caller is responsible to free the memory in *BriefDesc
441
442 @param[in] Handle FileHandle to read from
443 @param[in] Command name of command's section to find
444 @param[out] BriefDesc pointer to pointer to string where description goes.
445 @param[out] BriefSize pointer to size of allocated BriefDesc
446 @param[in, out] Ascii TRUE if the file is ASCII, FALSE otherwise, will be
447 set if the file handle is at the 0 position.
448
449 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
450 @retval EFI_SUCCESS the section was found and its description sotred in
451 an alloceted buffer.
452 **/
453 EFI_STATUS
454 EFIAPI
455 ManFileFindTitleSection(
456 IN SHELL_FILE_HANDLE Handle,
457 IN CONST CHAR16 *Command,
458 OUT CHAR16 **BriefDesc OPTIONAL,
459 OUT UINTN *BriefSize OPTIONAL,
460 IN OUT BOOLEAN *Ascii
461 )
462 {
463 EFI_STATUS Status;
464 CHAR16 *TitleString;
465 CHAR16 *ReadLine;
466 UINTN Size;
467 CHAR16 *TitleEnd;
468 UINTN TitleLen;
469 BOOLEAN Found;
470
471 if ( Handle == NULL
472 || Command == NULL
473 || (BriefDesc != NULL && BriefSize == NULL)
474 ){
475 return (EFI_INVALID_PARAMETER);
476 }
477
478 Status = EFI_SUCCESS;
479 Size = 1024;
480 Found = FALSE;
481
482 ReadLine = AllocateZeroPool(Size);
483 if (ReadLine == NULL) {
484 return (EFI_OUT_OF_RESOURCES);
485 }
486
487 TitleString = AllocateZeroPool((4*sizeof(CHAR16)) + StrSize(Command));
488 if (TitleString == NULL) {
489 FreePool(ReadLine);
490 return (EFI_OUT_OF_RESOURCES);
491 }
492 StrCpy(TitleString, L".TH ");
493 StrCat(TitleString, Command);
494 TitleLen = StrLen(TitleString);
495 for (;!ShellFileHandleEof(Handle);Size = 1024) {
496 Status = ShellFileHandleReadLine(Handle, ReadLine, &Size, TRUE, Ascii);
497 if (ReadLine[0] == L'#') {
498 //
499 // Skip comment lines
500 //
501 continue;
502 }
503 //
504 // ignore too small of buffer...
505 //
506 if (Status == EFI_BUFFER_TOO_SMALL) {
507 Status = EFI_SUCCESS;
508 }
509 if (EFI_ERROR(Status)) {
510 break;
511 }
512 if (StrnCmp(ReadLine, TitleString, TitleLen) == 0) {
513 Found = TRUE;
514 //
515 // we found it so copy out the rest of the line into BriefDesc
516 // After skipping any spaces or zeroes
517 //
518 for ( TitleEnd = ReadLine+TitleLen
519 ; *TitleEnd == L' ' || *TitleEnd == L'0' || *TitleEnd == L'1'
520 ; TitleEnd++);
521 if (BriefDesc != NULL) {
522 *BriefSize = StrSize(TitleEnd);
523 *BriefDesc = AllocateZeroPool(*BriefSize);
524 if (*BriefDesc == NULL) {
525 Status = EFI_OUT_OF_RESOURCES;
526 break;
527 }
528 StrCpy(*BriefDesc, TitleEnd);
529 }
530 break;
531 }
532 }
533 FreePool(ReadLine);
534 FreePool(TitleString);
535 if (!Found && !EFI_ERROR(Status)) {
536 return (EFI_NOT_FOUND);
537 }
538 return (Status);
539 }
540
541 /**
542 This function returns the help information for the specified command. The help text
543 will be parsed from a UEFI Shell manual page. (see UEFI Shell 2.0 Appendix B)
544
545 If Sections is specified, then each section name listed will be compared in a casesensitive
546 manner, to the section names described in Appendix B. If the section exists,
547 it will be appended to the returned help text. If the section does not exist, no
548 information will be returned. If Sections is NULL, then all help text information
549 available will be returned.
550
551 if BriefDesc is NULL, then the breif description will not be savedd seperatly,
552 but placed first in the main HelpText.
553
554 @param[in] ManFileName Points to the NULL-terminated UEFI Shell MAN file name.
555 @param[in] Command Points to the NULL-terminated UEFI Shell command name.
556 @param[in] Sections Points to the NULL-terminated comma-delimited
557 section names to return. If NULL, then all
558 sections will be returned.
559 @param[out] BriefDesc On return, points to a callee-allocated buffer
560 containing brief description text.
561 @param[out] HelpText On return, points to a callee-allocated buffer
562 containing all specified help text.
563
564 @retval EFI_SUCCESS The help text was returned.
565 @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
566 returned help text.
567 @retval EFI_INVALID_PARAMETER HelpText is NULL.
568 @retval EFI_INVALID_PARAMETER ManFileName is invalid.
569 @retval EFI_NOT_FOUND There is no help text available for Command.
570 **/
571 EFI_STATUS
572 EFIAPI
573 ProcessManFile(
574 IN CONST CHAR16 *ManFileName,
575 IN CONST CHAR16 *Command,
576 IN CONST CHAR16 *Sections OPTIONAL,
577 OUT CHAR16 **BriefDesc OPTIONAL,
578 OUT CHAR16 **HelpText
579 )
580 {
581 CHAR16 *TempString;
582 SHELL_FILE_HANDLE FileHandle;
583 EFI_STATUS Status;
584 UINTN HelpSize;
585 UINTN BriefSize;
586 BOOLEAN Ascii;
587 CHAR16 *TempString2;
588 EFI_DEVICE_PATH_PROTOCOL *FileDevPath;
589 EFI_DEVICE_PATH_PROTOCOL *DevPath;
590
591 if ( ManFileName == NULL
592 || Command == NULL
593 || HelpText == NULL
594 ){
595 return (EFI_INVALID_PARAMETER);
596 }
597
598 HelpSize = 0;
599 BriefSize = 0;
600 TempString = NULL;
601 //
602 // See if it's in HII first
603 //
604 TempString = ShellCommandGetCommandHelp(Command);
605 if (TempString != NULL) {
606 TempString2 = TempString;
607 Status = ManBufferFindTitleSection(&TempString2, Command, BriefDesc, &BriefSize);
608 if (!EFI_ERROR(Status) && HelpText != NULL){
609 Status = ManBufferFindSections(TempString2, Sections, HelpText, &HelpSize);
610 }
611 } else {
612 FileHandle = NULL;
613 TempString = GetManFileName(ManFileName);
614 if (TempString == NULL) {
615 return (EFI_INVALID_PARAMETER);
616 }
617
618 Status = SearchPathForFile(TempString, &FileHandle);
619 if (EFI_ERROR(Status)) {
620 FileDevPath = FileDevicePath(NULL, TempString);
621 DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, FileDevPath);
622 Status = InternalOpenFileDevicePath(DevPath, &FileHandle, EFI_FILE_MODE_READ, 0);
623 FreePool(FileDevPath);
624 FreePool(DevPath);
625 }
626
627 if (!EFI_ERROR(Status)) {
628 HelpSize = 0;
629 BriefSize = 0;
630 Status = ManFileFindTitleSection(FileHandle, Command, BriefDesc, &BriefSize, &Ascii);
631 if (!EFI_ERROR(Status) && HelpText != NULL){
632 Status = ManFileFindSections(FileHandle, Sections, HelpText, &HelpSize, Ascii);
633 }
634 ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
635 } else {
636 *HelpText = NULL;
637 }
638 }
639 if (TempString != NULL) {
640 FreePool(TempString);
641 }
642
643 return (Status);
644 }