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