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