]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLib/UefiShellLib.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / ShellPkg / Library / UefiShellLib / UefiShellLib.c
1 /** @file
2 Provides interface to shell functionality for shell commands and applications.
3
4 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
5 Copyright 2016-2018 Dell Technologies.<BR>
6 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include "UefiShellLib.h"
12 #include <Library/SortLib.h>
13 #include <Library/BaseLib.h>
14
15 //
16 // globals...
17 //
18 SHELL_PARAM_ITEM EmptyParamList[] = {
19 { NULL, TypeMax }
20 };
21 SHELL_PARAM_ITEM SfoParamList[] = {
22 { L"-sfo", TypeFlag },
23 { NULL, TypeMax }
24 };
25 EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2;
26 EFI_SHELL_INTERFACE *mEfiShellInterface;
27 EFI_SHELL_PROTOCOL *gEfiShellProtocol;
28 EFI_SHELL_PARAMETERS_PROTOCOL *gEfiShellParametersProtocol;
29 EFI_HANDLE mEfiShellEnvironment2Handle;
30 FILE_HANDLE_FUNCTION_MAP FileFunctionMap;
31 EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollationProtocol;
32
33 /**
34 Return a clean, fully-qualified version of an input path. If the return value
35 is non-NULL the caller must free the memory when it is no longer needed.
36
37 If asserts are disabled, and if the input parameter is NULL, NULL is returned.
38
39 If there is not enough memory available to create the fully-qualified path or
40 a copy of the input path, NULL is returned.
41
42 If there is no working directory, a clean copy of Path is returned.
43
44 Otherwise, the current file system or working directory (as appropriate) is
45 prepended to Path and the resulting path is cleaned and returned.
46
47 NOTE: If the input path is an empty string, then the current working directory
48 (if it exists) is returned. In other words, an empty input path is treated
49 exactly the same as ".".
50
51 @param[in] Path A pointer to some file or directory path.
52
53 @retval NULL The input path is NULL or out of memory.
54
55 @retval non-NULL A pointer to a clean, fully-qualified version of Path.
56 If there is no working directory, then a pointer to a
57 clean, but not necessarily fully-qualified version of
58 Path. The caller must free this memory when it is no
59 longer needed.
60 **/
61 CHAR16 *
62 EFIAPI
63 FullyQualifyPath (
64 IN CONST CHAR16 *Path
65 )
66 {
67 CONST CHAR16 *WorkingPath;
68 CONST CHAR16 *InputPath;
69 CHAR16 *CharPtr;
70 CHAR16 *InputFileSystem;
71 UINTN FileSystemCharCount;
72 CHAR16 *FullyQualifiedPath;
73 UINTN Size;
74
75 FullyQualifiedPath = NULL;
76
77 ASSERT (Path != NULL);
78 //
79 // Handle erroneous input when asserts are disabled.
80 //
81 if (Path == NULL) {
82 return NULL;
83 }
84
85 //
86 // In paths that contain ":", like fs0:dir/file.ext and fs2:\fqpath\file.ext,
87 // we have to consider the file system part separately from the "path" part.
88 // If there is a file system in the path, we have to get the current working
89 // directory for that file system. Then we need to use the part of the path
90 // following the ":". If a path does not contain ":", we use it as given.
91 //
92 InputPath = StrStr (Path, L":");
93 if (InputPath != NULL) {
94 InputPath++;
95 FileSystemCharCount = ((UINTN)InputPath - (UINTN)Path + sizeof (CHAR16)) / sizeof (CHAR16);
96 InputFileSystem = AllocateCopyPool (FileSystemCharCount * sizeof (CHAR16), Path);
97 if (InputFileSystem != NULL) {
98 InputFileSystem[FileSystemCharCount - 1] = CHAR_NULL;
99 }
100
101 WorkingPath = ShellGetCurrentDir (InputFileSystem);
102 SHELL_FREE_NON_NULL (InputFileSystem);
103 } else {
104 InputPath = Path;
105 WorkingPath = ShellGetEnvironmentVariable (L"cwd");
106 }
107
108 if (WorkingPath == NULL) {
109 //
110 // With no working directory, all we can do is copy and clean the input path.
111 //
112 FullyQualifiedPath = AllocateCopyPool (StrSize (Path), Path);
113 } else {
114 //
115 // Allocate space for both strings plus one more character.
116 //
117 Size = StrSize (WorkingPath) + StrSize (InputPath);
118 FullyQualifiedPath = AllocateZeroPool (Size);
119 if (FullyQualifiedPath == NULL) {
120 //
121 // Try to copy and clean just the input. No harm if not enough memory.
122 //
123 FullyQualifiedPath = AllocateCopyPool (StrSize (Path), Path);
124 } else {
125 if ((*InputPath == L'\\') || (*InputPath == L'/')) {
126 //
127 // Absolute path: start with the current working directory, then
128 // truncate the new path after the file system part.
129 //
130 StrCpyS (FullyQualifiedPath, Size/sizeof (CHAR16), WorkingPath);
131 CharPtr = StrStr (FullyQualifiedPath, L":");
132 if (CharPtr != NULL) {
133 *(CharPtr + 1) = CHAR_NULL;
134 }
135 } else {
136 //
137 // Relative path: start with the working directory and append "\".
138 //
139 StrCpyS (FullyQualifiedPath, Size/sizeof (CHAR16), WorkingPath);
140 StrCatS (FullyQualifiedPath, Size/sizeof (CHAR16), L"\\");
141 }
142
143 //
144 // Now append the absolute or relative path.
145 //
146 StrCatS (FullyQualifiedPath, Size/sizeof (CHAR16), InputPath);
147 }
148 }
149
150 PathCleanUpDirectories (FullyQualifiedPath);
151
152 return FullyQualifiedPath;
153 }
154
155 /**
156 Check if a Unicode character is a hexadecimal character.
157
158 This internal function checks if a Unicode character is a
159 numeric character. The valid hexadecimal characters are
160 L'0' to L'9', L'a' to L'f', or L'A' to L'F'.
161
162 @param Char The character to check against.
163
164 @retval TRUE If the Char is a hexadecmial character.
165 @retval FALSE If the Char is not a hexadecmial character.
166
167 **/
168 BOOLEAN
169 EFIAPI
170 ShellIsHexaDecimalDigitCharacter (
171 IN CHAR16 Char
172 )
173 {
174 return (BOOLEAN)((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f'));
175 }
176
177 /**
178 Check if a Unicode character is a decimal character.
179
180 This internal function checks if a Unicode character is a
181 decimal character. The valid characters are
182 L'0' to L'9'.
183
184
185 @param Char The character to check against.
186
187 @retval TRUE If the Char is a hexadecmial character.
188 @retval FALSE If the Char is not a hexadecmial character.
189
190 **/
191 BOOLEAN
192 EFIAPI
193 ShellIsDecimalDigitCharacter (
194 IN CHAR16 Char
195 )
196 {
197 return (BOOLEAN)(Char >= L'0' && Char <= L'9');
198 }
199
200 /**
201 Helper function to find ShellEnvironment2 for constructor.
202
203 @param[in] ImageHandle A copy of the calling image's handle.
204
205 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
206 **/
207 EFI_STATUS
208 ShellFindSE2 (
209 IN EFI_HANDLE ImageHandle
210 )
211 {
212 EFI_STATUS Status;
213 EFI_HANDLE *Buffer;
214 UINTN BufferSize;
215 UINTN HandleIndex;
216
217 BufferSize = 0;
218 Buffer = NULL;
219 Status = gBS->OpenProtocol (
220 ImageHandle,
221 &gEfiShellEnvironment2Guid,
222 (VOID **)&mEfiShellEnvironment2,
223 ImageHandle,
224 NULL,
225 EFI_OPEN_PROTOCOL_GET_PROTOCOL
226 );
227 //
228 // look for the mEfiShellEnvironment2 protocol at a higher level
229 //
230 if (EFI_ERROR (Status) || !(CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid))) {
231 //
232 // figure out how big of a buffer we need.
233 //
234 Status = gBS->LocateHandle (
235 ByProtocol,
236 &gEfiShellEnvironment2Guid,
237 NULL, // ignored for ByProtocol
238 &BufferSize,
239 Buffer
240 );
241 //
242 // maybe it's not there???
243 //
244 if (Status == EFI_BUFFER_TOO_SMALL) {
245 Buffer = (EFI_HANDLE *)AllocateZeroPool (BufferSize);
246 if (Buffer == NULL) {
247 return (EFI_OUT_OF_RESOURCES);
248 }
249
250 Status = gBS->LocateHandle (
251 ByProtocol,
252 &gEfiShellEnvironment2Guid,
253 NULL, // ignored for ByProtocol
254 &BufferSize,
255 Buffer
256 );
257 }
258
259 if (!EFI_ERROR (Status) && (Buffer != NULL)) {
260 //
261 // now parse the list of returned handles
262 //
263 Status = EFI_NOT_FOUND;
264 for (HandleIndex = 0; HandleIndex < (BufferSize/sizeof (Buffer[0])); HandleIndex++) {
265 Status = gBS->OpenProtocol (
266 Buffer[HandleIndex],
267 &gEfiShellEnvironment2Guid,
268 (VOID **)&mEfiShellEnvironment2,
269 ImageHandle,
270 NULL,
271 EFI_OPEN_PROTOCOL_GET_PROTOCOL
272 );
273 if (CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid)) {
274 mEfiShellEnvironment2Handle = Buffer[HandleIndex];
275 Status = EFI_SUCCESS;
276 break;
277 }
278 }
279 }
280 }
281
282 if (Buffer != NULL) {
283 FreePool (Buffer);
284 }
285
286 return (Status);
287 }
288
289 /**
290 Function to do most of the work of the constructor. Allows for calling
291 multiple times without complete re-initialization.
292
293 @param[in] ImageHandle A copy of the ImageHandle.
294 @param[in] SystemTable A pointer to the SystemTable for the application.
295
296 @retval EFI_SUCCESS The operationw as successful.
297 **/
298 EFI_STATUS
299 ShellLibConstructorWorker (
300 IN EFI_HANDLE ImageHandle,
301 IN EFI_SYSTEM_TABLE *SystemTable
302 )
303 {
304 EFI_STATUS Status;
305
306 if (gEfiShellProtocol == NULL) {
307 //
308 // UEFI 2.0 shell interfaces (used preferentially)
309 //
310 Status = gBS->OpenProtocol (
311 ImageHandle,
312 &gEfiShellProtocolGuid,
313 (VOID **)&gEfiShellProtocol,
314 ImageHandle,
315 NULL,
316 EFI_OPEN_PROTOCOL_GET_PROTOCOL
317 );
318 if (EFI_ERROR (Status)) {
319 //
320 // Search for the shell protocol
321 //
322 Status = gBS->LocateProtocol (
323 &gEfiShellProtocolGuid,
324 NULL,
325 (VOID **)&gEfiShellProtocol
326 );
327 if (EFI_ERROR (Status)) {
328 gEfiShellProtocol = NULL;
329 }
330 }
331 }
332
333 if (gEfiShellParametersProtocol == NULL) {
334 Status = gBS->OpenProtocol (
335 ImageHandle,
336 &gEfiShellParametersProtocolGuid,
337 (VOID **)&gEfiShellParametersProtocol,
338 ImageHandle,
339 NULL,
340 EFI_OPEN_PROTOCOL_GET_PROTOCOL
341 );
342 if (EFI_ERROR (Status)) {
343 gEfiShellParametersProtocol = NULL;
344 }
345 }
346
347 if (gEfiShellProtocol == NULL) {
348 //
349 // Moved to seperate function due to complexity
350 //
351 Status = ShellFindSE2 (ImageHandle);
352
353 if (EFI_ERROR (Status)) {
354 DEBUG ((DEBUG_ERROR, "Status: 0x%08x\r\n", Status));
355 mEfiShellEnvironment2 = NULL;
356 }
357
358 Status = gBS->OpenProtocol (
359 ImageHandle,
360 &gEfiShellInterfaceGuid,
361 (VOID **)&mEfiShellInterface,
362 ImageHandle,
363 NULL,
364 EFI_OPEN_PROTOCOL_GET_PROTOCOL
365 );
366 if (EFI_ERROR (Status)) {
367 mEfiShellInterface = NULL;
368 }
369 }
370
371 //
372 // Getting either EDK Shell's ShellEnvironment2 and ShellInterface protocol
373 // or UEFI Shell's Shell protocol.
374 // When ShellLib is linked to a driver producing DynamicCommand protocol,
375 // ShellParameters protocol is set by DynamicCommand.Handler().
376 //
377 if (((mEfiShellEnvironment2 != NULL) && (mEfiShellInterface != NULL)) ||
378 (gEfiShellProtocol != NULL)
379 )
380 {
381 if (gEfiShellProtocol != NULL) {
382 FileFunctionMap.GetFileInfo = gEfiShellProtocol->GetFileInfo;
383 FileFunctionMap.SetFileInfo = gEfiShellProtocol->SetFileInfo;
384 FileFunctionMap.ReadFile = gEfiShellProtocol->ReadFile;
385 FileFunctionMap.WriteFile = gEfiShellProtocol->WriteFile;
386 FileFunctionMap.CloseFile = gEfiShellProtocol->CloseFile;
387 FileFunctionMap.DeleteFile = gEfiShellProtocol->DeleteFile;
388 FileFunctionMap.GetFilePosition = gEfiShellProtocol->GetFilePosition;
389 FileFunctionMap.SetFilePosition = gEfiShellProtocol->SetFilePosition;
390 FileFunctionMap.FlushFile = gEfiShellProtocol->FlushFile;
391 FileFunctionMap.GetFileSize = gEfiShellProtocol->GetFileSize;
392 } else {
393 FileFunctionMap.GetFileInfo = (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo;
394 FileFunctionMap.SetFileInfo = (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo;
395 FileFunctionMap.ReadFile = (EFI_SHELL_READ_FILE)FileHandleRead;
396 FileFunctionMap.WriteFile = (EFI_SHELL_WRITE_FILE)FileHandleWrite;
397 FileFunctionMap.CloseFile = (EFI_SHELL_CLOSE_FILE)FileHandleClose;
398 FileFunctionMap.DeleteFile = (EFI_SHELL_DELETE_FILE)FileHandleDelete;
399 FileFunctionMap.GetFilePosition = (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition;
400 FileFunctionMap.SetFilePosition = (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition;
401 FileFunctionMap.FlushFile = (EFI_SHELL_FLUSH_FILE)FileHandleFlush;
402 FileFunctionMap.GetFileSize = (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize;
403 }
404
405 return (EFI_SUCCESS);
406 }
407
408 return (EFI_NOT_FOUND);
409 }
410
411 /**
412 Constructor for the Shell library.
413
414 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
415
416 @param ImageHandle the image handle of the process
417 @param SystemTable the EFI System Table pointer
418
419 @retval EFI_SUCCESS the initialization was complete sucessfully
420 @return others an error ocurred during initialization
421 **/
422 EFI_STATUS
423 EFIAPI
424 ShellLibConstructor (
425 IN EFI_HANDLE ImageHandle,
426 IN EFI_SYSTEM_TABLE *SystemTable
427 )
428 {
429 mEfiShellEnvironment2 = NULL;
430 gEfiShellProtocol = NULL;
431 gEfiShellParametersProtocol = NULL;
432 mEfiShellInterface = NULL;
433 mEfiShellEnvironment2Handle = NULL;
434 mUnicodeCollationProtocol = NULL;
435
436 //
437 // verify that auto initialize is not set false
438 //
439 if (PcdGetBool (PcdShellLibAutoInitialize) == 0) {
440 return (EFI_SUCCESS);
441 }
442
443 return (ShellLibConstructorWorker (ImageHandle, SystemTable));
444 }
445
446 /**
447 Destructor for the library. free any resources.
448
449 @param[in] ImageHandle A copy of the ImageHandle.
450 @param[in] SystemTable A pointer to the SystemTable for the application.
451
452 @retval EFI_SUCCESS The operation was successful.
453 @return An error from the CloseProtocol function.
454 **/
455 EFI_STATUS
456 EFIAPI
457 ShellLibDestructor (
458 IN EFI_HANDLE ImageHandle,
459 IN EFI_SYSTEM_TABLE *SystemTable
460 )
461 {
462 EFI_STATUS Status;
463
464 if (mEfiShellEnvironment2 != NULL) {
465 Status = gBS->CloseProtocol (
466 mEfiShellEnvironment2Handle == NULL ? ImageHandle : mEfiShellEnvironment2Handle,
467 &gEfiShellEnvironment2Guid,
468 ImageHandle,
469 NULL
470 );
471 if (!EFI_ERROR (Status)) {
472 mEfiShellEnvironment2 = NULL;
473 mEfiShellEnvironment2Handle = NULL;
474 }
475 }
476
477 if (mEfiShellInterface != NULL) {
478 Status = gBS->CloseProtocol (
479 ImageHandle,
480 &gEfiShellInterfaceGuid,
481 ImageHandle,
482 NULL
483 );
484 if (!EFI_ERROR (Status)) {
485 mEfiShellInterface = NULL;
486 }
487 }
488
489 if (gEfiShellProtocol != NULL) {
490 Status = gBS->CloseProtocol (
491 ImageHandle,
492 &gEfiShellProtocolGuid,
493 ImageHandle,
494 NULL
495 );
496 if (!EFI_ERROR (Status)) {
497 gEfiShellProtocol = NULL;
498 }
499 }
500
501 if (gEfiShellParametersProtocol != NULL) {
502 Status = gBS->CloseProtocol (
503 ImageHandle,
504 &gEfiShellParametersProtocolGuid,
505 ImageHandle,
506 NULL
507 );
508 if (!EFI_ERROR (Status)) {
509 gEfiShellParametersProtocol = NULL;
510 }
511 }
512
513 return (EFI_SUCCESS);
514 }
515
516 /**
517 This function causes the shell library to initialize itself. If the shell library
518 is already initialized it will de-initialize all the current protocol poitners and
519 re-populate them again.
520
521 When the library is used with PcdShellLibAutoInitialize set to true this function
522 will return EFI_SUCCESS and perform no actions.
523
524 This function is intended for internal access for shell commands only.
525
526 @retval EFI_SUCCESS the initialization was complete sucessfully
527
528 **/
529 EFI_STATUS
530 EFIAPI
531 ShellInitialize (
532 VOID
533 )
534 {
535 EFI_STATUS Status;
536
537 //
538 // if auto initialize is not false then skip
539 //
540 if (PcdGetBool (PcdShellLibAutoInitialize) != 0) {
541 return (EFI_SUCCESS);
542 }
543
544 //
545 // deinit the current stuff
546 //
547 Status = ShellLibDestructor (gImageHandle, gST);
548 ASSERT_EFI_ERROR (Status);
549
550 //
551 // init the new stuff
552 //
553 return (ShellLibConstructorWorker (gImageHandle, gST));
554 }
555
556 /**
557 This function will retrieve the information about the file for the handle
558 specified and store it in allocated pool memory.
559
560 This function allocates a buffer to store the file's information. It is the
561 caller's responsibility to free the buffer
562
563 @param FileHandle The file handle of the file for which information is
564 being requested.
565
566 @retval NULL information could not be retrieved.
567
568 @return the information about the file
569 **/
570 EFI_FILE_INFO *
571 EFIAPI
572 ShellGetFileInfo (
573 IN SHELL_FILE_HANDLE FileHandle
574 )
575 {
576 return (FileFunctionMap.GetFileInfo (FileHandle));
577 }
578
579 /**
580 This function sets the information about the file for the opened handle
581 specified.
582
583 @param[in] FileHandle The file handle of the file for which information
584 is being set.
585
586 @param[in] FileInfo The information to set.
587
588 @retval EFI_SUCCESS The information was set.
589 @retval EFI_INVALID_PARAMETER A parameter was out of range or invalid.
590 @retval EFI_UNSUPPORTED The FileHandle does not support FileInfo.
591 @retval EFI_NO_MEDIA The device has no medium.
592 @retval EFI_DEVICE_ERROR The device reported an error.
593 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
594 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
595 @retval EFI_ACCESS_DENIED The file was opened read only.
596 @retval EFI_VOLUME_FULL The volume is full.
597 **/
598 EFI_STATUS
599 EFIAPI
600 ShellSetFileInfo (
601 IN SHELL_FILE_HANDLE FileHandle,
602 IN EFI_FILE_INFO *FileInfo
603 )
604 {
605 return (FileFunctionMap.SetFileInfo (FileHandle, FileInfo));
606 }
607
608 /**
609 This function will open a file or directory referenced by DevicePath.
610
611 This function opens a file with the open mode according to the file path. The
612 Attributes is valid only for EFI_FILE_MODE_CREATE.
613
614 @param FilePath on input the device path to the file. On output
615 the remaining device path.
616 @param FileHandle pointer to the file handle.
617 @param OpenMode the mode to open the file with.
618 @param Attributes the file's file attributes.
619
620 @retval EFI_SUCCESS The information was set.
621 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
622 @retval EFI_UNSUPPORTED Could not open the file path.
623 @retval EFI_NOT_FOUND The specified file could not be found on the
624 device or the file system could not be found on
625 the device.
626 @retval EFI_NO_MEDIA The device has no medium.
627 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
628 medium is no longer supported.
629 @retval EFI_DEVICE_ERROR The device reported an error.
630 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
631 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
632 @retval EFI_ACCESS_DENIED The file was opened read only.
633 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
634 file.
635 @retval EFI_VOLUME_FULL The volume is full.
636 **/
637 EFI_STATUS
638 EFIAPI
639 ShellOpenFileByDevicePath (
640 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
641 OUT SHELL_FILE_HANDLE *FileHandle,
642 IN UINT64 OpenMode,
643 IN UINT64 Attributes
644 )
645 {
646 CHAR16 *FileName;
647 EFI_STATUS Status;
648 EFI_FILE_PROTOCOL *File;
649
650 if ((FilePath == NULL) || (FileHandle == NULL)) {
651 return (EFI_INVALID_PARAMETER);
652 }
653
654 //
655 // which shell interface should we use
656 //
657 if (gEfiShellProtocol != NULL) {
658 //
659 // use UEFI Shell 2.0 method.
660 //
661 FileName = gEfiShellProtocol->GetFilePathFromDevicePath (*FilePath);
662 if (FileName == NULL) {
663 return (EFI_INVALID_PARAMETER);
664 }
665
666 Status = ShellOpenFileByName (FileName, FileHandle, OpenMode, Attributes);
667 FreePool (FileName);
668 return (Status);
669 }
670
671 //
672 // use old shell method.
673 //
674 Status = EfiOpenFileByDevicePath (FilePath, &File, OpenMode, Attributes);
675 if (EFI_ERROR (Status)) {
676 return Status;
677 }
678
679 //
680 // This is a weak spot since if the undefined SHELL_FILE_HANDLE format changes this must change also!
681 //
682 *FileHandle = (VOID *)File;
683 return (EFI_SUCCESS);
684 }
685
686 /**
687 This function will open a file or directory referenced by filename.
688
689 If return is EFI_SUCCESS, the Filehandle is the opened file's handle;
690 otherwise, the Filehandle is NULL. The Attributes is valid only for
691 EFI_FILE_MODE_CREATE.
692
693 if FileName is NULL then ASSERT()
694
695 @param FileName pointer to file name
696 @param FileHandle pointer to the file handle.
697 @param OpenMode the mode to open the file with.
698 @param Attributes the file's file attributes.
699
700 @retval EFI_SUCCESS The information was set.
701 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
702 @retval EFI_UNSUPPORTED Could not open the file path.
703 @retval EFI_NOT_FOUND The specified file could not be found on the
704 device or the file system could not be found
705 on the device.
706 @retval EFI_NO_MEDIA The device has no medium.
707 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
708 medium is no longer supported.
709 @retval EFI_DEVICE_ERROR The device reported an error.
710 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
711 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
712 @retval EFI_ACCESS_DENIED The file was opened read only.
713 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
714 file.
715 @retval EFI_VOLUME_FULL The volume is full.
716 **/
717 EFI_STATUS
718 EFIAPI
719 ShellOpenFileByName (
720 IN CONST CHAR16 *FileName,
721 OUT SHELL_FILE_HANDLE *FileHandle,
722 IN UINT64 OpenMode,
723 IN UINT64 Attributes
724 )
725 {
726 EFI_DEVICE_PATH_PROTOCOL *FilePath;
727 EFI_STATUS Status;
728 EFI_FILE_INFO *FileInfo;
729 CHAR16 *FileNameCopy;
730 EFI_STATUS Status2;
731
732 //
733 // ASSERT if FileName is NULL
734 //
735 ASSERT (FileName != NULL);
736
737 if (FileName == NULL) {
738 return (EFI_INVALID_PARAMETER);
739 }
740
741 if (gEfiShellProtocol != NULL) {
742 if ((OpenMode & EFI_FILE_MODE_CREATE) == EFI_FILE_MODE_CREATE) {
743 //
744 // Create only a directory
745 //
746 if ((Attributes & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY) {
747 return ShellCreateDirectory (FileName, FileHandle);
748 }
749
750 //
751 // Create the directory to create the file in
752 //
753 FileNameCopy = AllocateCopyPool (StrSize (FileName), FileName);
754 if (FileNameCopy == NULL) {
755 return (EFI_OUT_OF_RESOURCES);
756 }
757
758 PathCleanUpDirectories (FileNameCopy);
759 if (PathRemoveLastItem (FileNameCopy)) {
760 if (!EFI_ERROR (ShellCreateDirectory (FileNameCopy, FileHandle))) {
761 ShellCloseFile (FileHandle);
762 }
763 }
764
765 SHELL_FREE_NON_NULL (FileNameCopy);
766 }
767
768 //
769 // Use UEFI Shell 2.0 method to create the file
770 //
771 Status = gEfiShellProtocol->OpenFileByName (
772 FileName,
773 FileHandle,
774 OpenMode
775 );
776 if (EFI_ERROR (Status)) {
777 return Status;
778 }
779
780 if (mUnicodeCollationProtocol == NULL) {
781 Status = gBS->LocateProtocol (&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID **)&mUnicodeCollationProtocol);
782 if (EFI_ERROR (Status)) {
783 gEfiShellProtocol->CloseFile (*FileHandle);
784 return Status;
785 }
786 }
787
788 if ((mUnicodeCollationProtocol->StriColl (mUnicodeCollationProtocol, (CHAR16 *)FileName, L"NUL") != 0) &&
789 (mUnicodeCollationProtocol->StriColl (mUnicodeCollationProtocol, (CHAR16 *)FileName, L"NULL") != 0) &&
790 !EFI_ERROR (Status) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0))
791 {
792 FileInfo = FileFunctionMap.GetFileInfo (*FileHandle);
793 ASSERT (FileInfo != NULL);
794 FileInfo->Attribute = Attributes;
795 Status2 = FileFunctionMap.SetFileInfo (*FileHandle, FileInfo);
796 FreePool (FileInfo);
797 if (EFI_ERROR (Status2)) {
798 gEfiShellProtocol->CloseFile (*FileHandle);
799 }
800
801 Status = Status2;
802 }
803
804 return (Status);
805 }
806
807 //
808 // Using EFI Shell version
809 // this means convert name to path and call that function
810 // since this will use EFI method again that will open it.
811 //
812 ASSERT (mEfiShellEnvironment2 != NULL);
813 FilePath = mEfiShellEnvironment2->NameToPath ((CHAR16 *)FileName);
814 if (FilePath != NULL) {
815 return (ShellOpenFileByDevicePath (
816 &FilePath,
817 FileHandle,
818 OpenMode,
819 Attributes
820 ));
821 }
822
823 return (EFI_DEVICE_ERROR);
824 }
825
826 /**
827 This function create a directory
828
829 If return is EFI_SUCCESS, the Filehandle is the opened directory's handle;
830 otherwise, the Filehandle is NULL. If the directory already existed, this
831 function opens the existing directory.
832
833 @param DirectoryName pointer to directory name
834 @param FileHandle pointer to the file handle.
835
836 @retval EFI_SUCCESS The information was set.
837 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
838 @retval EFI_UNSUPPORTED Could not open the file path.
839 @retval EFI_NOT_FOUND The specified file could not be found on the
840 device or the file system could not be found
841 on the device.
842 @retval EFI_NO_MEDIA The device has no medium.
843 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
844 medium is no longer supported.
845 @retval EFI_DEVICE_ERROR The device reported an error.
846 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
847 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
848 @retval EFI_ACCESS_DENIED The file was opened read only.
849 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
850 file.
851 @retval EFI_VOLUME_FULL The volume is full.
852 @sa ShellOpenFileByName
853 **/
854 EFI_STATUS
855 EFIAPI
856 ShellCreateDirectory (
857 IN CONST CHAR16 *DirectoryName,
858 OUT SHELL_FILE_HANDLE *FileHandle
859 )
860 {
861 if (gEfiShellProtocol != NULL) {
862 //
863 // Use UEFI Shell 2.0 method
864 //
865 return (gEfiShellProtocol->CreateFile (
866 DirectoryName,
867 EFI_FILE_DIRECTORY,
868 FileHandle
869 ));
870 } else {
871 return (ShellOpenFileByName (
872 DirectoryName,
873 FileHandle,
874 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
875 EFI_FILE_DIRECTORY
876 ));
877 }
878 }
879
880 /**
881 This function reads information from an opened file.
882
883 If FileHandle is not a directory, the function reads the requested number of
884 bytes from the file at the file's current position and returns them in Buffer.
885 If the read goes beyond the end of the file, the read length is truncated to the
886 end of the file. The file's current position is increased by the number of bytes
887 returned. If FileHandle is a directory, the function reads the directory entry
888 at the file's current position and returns the entry in Buffer. If the Buffer
889 is not large enough to hold the current directory entry, then
890 EFI_BUFFER_TOO_SMALL is returned and the current file position is not updated.
891 BufferSize is set to be the size of the buffer needed to read the entry. On
892 success, the current position is updated to the next directory entry. If there
893 are no more directory entries, the read returns a zero-length buffer.
894 EFI_FILE_INFO is the structure returned as the directory entry.
895
896 @param FileHandle the opened file handle
897 @param BufferSize on input the size of buffer in bytes. on return
898 the number of bytes written.
899 @param Buffer the buffer to put read data into.
900
901 @retval EFI_SUCCESS Data was read.
902 @retval EFI_NO_MEDIA The device has no media.
903 @retval EFI_DEVICE_ERROR The device reported an error.
904 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
905 @retval EFI_BUFFER_TO_SMALL Buffer is too small. ReadSize contains required
906 size.
907
908 **/
909 EFI_STATUS
910 EFIAPI
911 ShellReadFile (
912 IN SHELL_FILE_HANDLE FileHandle,
913 IN OUT UINTN *BufferSize,
914 OUT VOID *Buffer
915 )
916 {
917 return (FileFunctionMap.ReadFile (FileHandle, BufferSize, Buffer));
918 }
919
920 /**
921 Write data to a file.
922
923 This function writes the specified number of bytes to the file at the current
924 file position. The current file position is advanced the actual number of bytes
925 written, which is returned in BufferSize. Partial writes only occur when there
926 has been a data error during the write attempt (such as "volume space full").
927 The file is automatically grown to hold the data if required. Direct writes to
928 opened directories are not supported.
929
930 @param FileHandle The opened file for writing
931 @param BufferSize on input the number of bytes in Buffer. On output
932 the number of bytes written.
933 @param Buffer the buffer containing data to write is stored.
934
935 @retval EFI_SUCCESS Data was written.
936 @retval EFI_UNSUPPORTED Writes to an open directory are not supported.
937 @retval EFI_NO_MEDIA The device has no media.
938 @retval EFI_DEVICE_ERROR The device reported an error.
939 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
940 @retval EFI_WRITE_PROTECTED The device is write-protected.
941 @retval EFI_ACCESS_DENIED The file was open for read only.
942 @retval EFI_VOLUME_FULL The volume is full.
943 **/
944 EFI_STATUS
945 EFIAPI
946 ShellWriteFile (
947 IN SHELL_FILE_HANDLE FileHandle,
948 IN OUT UINTN *BufferSize,
949 IN VOID *Buffer
950 )
951 {
952 return (FileFunctionMap.WriteFile (FileHandle, BufferSize, Buffer));
953 }
954
955 /**
956 Close an open file handle.
957
958 This function closes a specified file handle. All "dirty" cached file data is
959 flushed to the device, and the file is closed. In all cases the handle is
960 closed.
961
962 @param FileHandle the file handle to close.
963
964 @retval EFI_SUCCESS the file handle was closed sucessfully.
965 **/
966 EFI_STATUS
967 EFIAPI
968 ShellCloseFile (
969 IN SHELL_FILE_HANDLE *FileHandle
970 )
971 {
972 return (FileFunctionMap.CloseFile (*FileHandle));
973 }
974
975 /**
976 Delete a file and close the handle
977
978 This function closes and deletes a file. In all cases the file handle is closed.
979 If the file cannot be deleted, the warning code EFI_WARN_DELETE_FAILURE is
980 returned, but the handle is still closed.
981
982 @param FileHandle the file handle to delete
983
984 @retval EFI_SUCCESS the file was closed sucessfully
985 @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not
986 deleted
987 @retval INVALID_PARAMETER One of the parameters has an invalid value.
988 **/
989 EFI_STATUS
990 EFIAPI
991 ShellDeleteFile (
992 IN SHELL_FILE_HANDLE *FileHandle
993 )
994 {
995 return (FileFunctionMap.DeleteFile (*FileHandle));
996 }
997
998 /**
999 Set the current position in a file.
1000
1001 This function sets the current file position for the handle to the position
1002 supplied. With the exception of seeking to position 0xFFFFFFFFFFFFFFFF, only
1003 absolute positioning is supported, and seeking past the end of the file is
1004 allowed (a subsequent write would grow the file). Seeking to position
1005 0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file.
1006 If FileHandle is a directory, the only position that may be set is zero. This
1007 has the effect of starting the read process of the directory entries over.
1008
1009 @param FileHandle The file handle on which the position is being set
1010 @param Position Byte position from begining of file
1011
1012 @retval EFI_SUCCESS Operation completed sucessfully.
1013 @retval EFI_UNSUPPORTED the seek request for non-zero is not valid on
1014 directories.
1015 @retval INVALID_PARAMETER One of the parameters has an invalid value.
1016 **/
1017 EFI_STATUS
1018 EFIAPI
1019 ShellSetFilePosition (
1020 IN SHELL_FILE_HANDLE FileHandle,
1021 IN UINT64 Position
1022 )
1023 {
1024 return (FileFunctionMap.SetFilePosition (FileHandle, Position));
1025 }
1026
1027 /**
1028 Gets a file's current position
1029
1030 This function retrieves the current file position for the file handle. For
1031 directories, the current file position has no meaning outside of the file
1032 system driver and as such the operation is not supported. An error is returned
1033 if FileHandle is a directory.
1034
1035 @param FileHandle The open file handle on which to get the position.
1036 @param Position Byte position from begining of file.
1037
1038 @retval EFI_SUCCESS the operation completed sucessfully.
1039 @retval INVALID_PARAMETER One of the parameters has an invalid value.
1040 @retval EFI_UNSUPPORTED the request is not valid on directories.
1041 **/
1042 EFI_STATUS
1043 EFIAPI
1044 ShellGetFilePosition (
1045 IN SHELL_FILE_HANDLE FileHandle,
1046 OUT UINT64 *Position
1047 )
1048 {
1049 return (FileFunctionMap.GetFilePosition (FileHandle, Position));
1050 }
1051
1052 /**
1053 Flushes data on a file
1054
1055 This function flushes all modified data associated with a file to a device.
1056
1057 @param FileHandle The file handle on which to flush data
1058
1059 @retval EFI_SUCCESS The data was flushed.
1060 @retval EFI_NO_MEDIA The device has no media.
1061 @retval EFI_DEVICE_ERROR The device reported an error.
1062 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
1063 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
1064 @retval EFI_ACCESS_DENIED The file was opened for read only.
1065 **/
1066 EFI_STATUS
1067 EFIAPI
1068 ShellFlushFile (
1069 IN SHELL_FILE_HANDLE FileHandle
1070 )
1071 {
1072 return (FileFunctionMap.FlushFile (FileHandle));
1073 }
1074
1075 /** Retrieve first entry from a directory.
1076
1077 This function takes an open directory handle and gets information from the
1078 first entry in the directory. A buffer is allocated to contain
1079 the information and a pointer to the buffer is returned in *Buffer. The
1080 caller can use ShellFindNextFile() to get subsequent directory entries.
1081
1082 The buffer will be freed by ShellFindNextFile() when the last directory
1083 entry is read. Otherwise, the caller must free the buffer, using FreePool,
1084 when finished with it.
1085
1086 @param[in] DirHandle The file handle of the directory to search.
1087 @param[out] Buffer The pointer to the buffer for the file's information.
1088
1089 @retval EFI_SUCCESS Found the first file.
1090 @retval EFI_NOT_FOUND Cannot find the directory.
1091 @retval EFI_NO_MEDIA The device has no media.
1092 @retval EFI_DEVICE_ERROR The device reported an error.
1093 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
1094 @return Others status of ShellGetFileInfo, ShellSetFilePosition,
1095 or ShellReadFile
1096 **/
1097 EFI_STATUS
1098 EFIAPI
1099 ShellFindFirstFile (
1100 IN SHELL_FILE_HANDLE DirHandle,
1101 OUT EFI_FILE_INFO **Buffer
1102 )
1103 {
1104 //
1105 // pass to file handle lib
1106 //
1107 return (FileHandleFindFirstFile (DirHandle, Buffer));
1108 }
1109
1110 /** Retrieve next entries from a directory.
1111
1112 To use this function, the caller must first call the ShellFindFirstFile()
1113 function to get the first directory entry. Subsequent directory entries are
1114 retrieved by using the ShellFindNextFile() function. This function can
1115 be called several times to get each entry from the directory. If the call of
1116 ShellFindNextFile() retrieved the last directory entry, the next call of
1117 this function will set *NoFile to TRUE and free the buffer.
1118
1119 @param[in] DirHandle The file handle of the directory.
1120 @param[out] Buffer The pointer to buffer for file's information.
1121 @param[out] NoFile The pointer to boolean when last file is found.
1122
1123 @retval EFI_SUCCESS Found the next file, or reached last file
1124 @retval EFI_NO_MEDIA The device has no media.
1125 @retval EFI_DEVICE_ERROR The device reported an error.
1126 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
1127 **/
1128 EFI_STATUS
1129 EFIAPI
1130 ShellFindNextFile (
1131 IN SHELL_FILE_HANDLE DirHandle,
1132 OUT EFI_FILE_INFO *Buffer,
1133 OUT BOOLEAN *NoFile
1134 )
1135 {
1136 //
1137 // pass to file handle lib
1138 //
1139 return (FileHandleFindNextFile (DirHandle, Buffer, NoFile));
1140 }
1141
1142 /**
1143 Retrieve the size of a file.
1144
1145 if FileHandle is NULL then ASSERT()
1146 if Size is NULL then ASSERT()
1147
1148 This function extracts the file size info from the FileHandle's EFI_FILE_INFO
1149 data.
1150
1151 @param FileHandle file handle from which size is retrieved
1152 @param Size pointer to size
1153
1154 @retval EFI_SUCCESS operation was completed sucessfully
1155 @retval EFI_DEVICE_ERROR cannot access the file
1156 **/
1157 EFI_STATUS
1158 EFIAPI
1159 ShellGetFileSize (
1160 IN SHELL_FILE_HANDLE FileHandle,
1161 OUT UINT64 *Size
1162 )
1163 {
1164 return (FileFunctionMap.GetFileSize (FileHandle, Size));
1165 }
1166
1167 /**
1168 Retrieves the status of the break execution flag
1169
1170 this function is useful to check whether the application is being asked to halt by the shell.
1171
1172 @retval TRUE the execution break is enabled
1173 @retval FALSE the execution break is not enabled
1174 **/
1175 BOOLEAN
1176 EFIAPI
1177 ShellGetExecutionBreakFlag (
1178 VOID
1179 )
1180 {
1181 //
1182 // Check for UEFI Shell 2.0 protocols
1183 //
1184 if (gEfiShellProtocol != NULL) {
1185 //
1186 // We are using UEFI Shell 2.0; see if the event has been triggered
1187 //
1188 if (gBS->CheckEvent (gEfiShellProtocol->ExecutionBreak) != EFI_SUCCESS) {
1189 return (FALSE);
1190 }
1191
1192 return (TRUE);
1193 }
1194
1195 //
1196 // using EFI Shell; call the function to check
1197 //
1198 if (mEfiShellEnvironment2 != NULL) {
1199 return (mEfiShellEnvironment2->GetExecutionBreak ());
1200 }
1201
1202 return (FALSE);
1203 }
1204
1205 /**
1206 return the value of an environment variable
1207
1208 this function gets the value of the environment variable set by the
1209 ShellSetEnvironmentVariable function
1210
1211 @param EnvKey The key name of the environment variable.
1212
1213 @retval NULL the named environment variable does not exist.
1214 @return != NULL pointer to the value of the environment variable
1215 **/
1216 CONST CHAR16 *
1217 EFIAPI
1218 ShellGetEnvironmentVariable (
1219 IN CONST CHAR16 *EnvKey
1220 )
1221 {
1222 //
1223 // Check for UEFI Shell 2.0 protocols
1224 //
1225 if (gEfiShellProtocol != NULL) {
1226 return (gEfiShellProtocol->GetEnv (EnvKey));
1227 }
1228
1229 //
1230 // Check for EFI shell
1231 //
1232 if (mEfiShellEnvironment2 != NULL) {
1233 return (mEfiShellEnvironment2->GetEnv ((CHAR16 *)EnvKey));
1234 }
1235
1236 return NULL;
1237 }
1238
1239 /**
1240 set the value of an environment variable
1241
1242 This function changes the current value of the specified environment variable. If the
1243 environment variable exists and the Value is an empty string, then the environment
1244 variable is deleted. If the environment variable exists and the Value is not an empty
1245 string, then the value of the environment variable is changed. If the environment
1246 variable does not exist and the Value is an empty string, there is no action. If the
1247 environment variable does not exist and the Value is a non-empty string, then the
1248 environment variable is created and assigned the specified value.
1249
1250 This is not supported pre-UEFI Shell 2.0.
1251
1252 @param EnvKey The key name of the environment variable.
1253 @param EnvVal The Value of the environment variable
1254 @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
1255
1256 @retval EFI_SUCCESS the operation was completed sucessfully
1257 @retval EFI_UNSUPPORTED This operation is not allowed in pre UEFI 2.0 Shell environments
1258 **/
1259 EFI_STATUS
1260 EFIAPI
1261 ShellSetEnvironmentVariable (
1262 IN CONST CHAR16 *EnvKey,
1263 IN CONST CHAR16 *EnvVal,
1264 IN BOOLEAN Volatile
1265 )
1266 {
1267 //
1268 // Check for UEFI Shell 2.0 protocols
1269 //
1270 if (gEfiShellProtocol != NULL) {
1271 return (gEfiShellProtocol->SetEnv (EnvKey, EnvVal, Volatile));
1272 }
1273
1274 //
1275 // This feature does not exist under EFI shell
1276 //
1277 return (EFI_UNSUPPORTED);
1278 }
1279
1280 /**
1281 Cause the shell to parse and execute a command line.
1282
1283 This function creates a nested instance of the shell and executes the specified
1284 command (CommandLine) with the specified environment (Environment). Upon return,
1285 the status code returned by the specified command is placed in StatusCode.
1286 If Environment is NULL, then the current environment is used and all changes made
1287 by the commands executed will be reflected in the current environment. If the
1288 Environment is non-NULL, then the changes made will be discarded.
1289 The CommandLine is executed from the current working directory on the current
1290 device.
1291
1292 The EnvironmentVariables pararemeter is ignored in a pre-UEFI Shell 2.0
1293 environment. The values pointed to by the parameters will be unchanged by the
1294 ShellExecute() function. The Output parameter has no effect in a
1295 UEFI Shell 2.0 environment.
1296
1297 @param[in] ParentHandle The parent image starting the operation.
1298 @param[in] CommandLine The pointer to a NULL terminated command line.
1299 @param[in] Output True to display debug output. False to hide it.
1300 @param[in] EnvironmentVariables Optional pointer to array of environment variables
1301 in the form "x=y". If NULL, the current set is used.
1302 @param[out] Status The status of the run command line.
1303
1304 @retval EFI_SUCCESS The operation completed sucessfully. Status
1305 contains the status code returned.
1306 @retval EFI_INVALID_PARAMETER A parameter contains an invalid value.
1307 @retval EFI_OUT_OF_RESOURCES Out of resources.
1308 @retval EFI_UNSUPPORTED The operation is not allowed.
1309 **/
1310 EFI_STATUS
1311 EFIAPI
1312 ShellExecute (
1313 IN EFI_HANDLE *ParentHandle,
1314 IN CHAR16 *CommandLine OPTIONAL,
1315 IN BOOLEAN Output OPTIONAL,
1316 IN CHAR16 **EnvironmentVariables OPTIONAL,
1317 OUT EFI_STATUS *Status OPTIONAL
1318 )
1319 {
1320 EFI_STATUS CmdStatus;
1321
1322 //
1323 // Check for UEFI Shell 2.0 protocols
1324 //
1325 if (gEfiShellProtocol != NULL) {
1326 //
1327 // Call UEFI Shell 2.0 version (not using Output parameter)
1328 //
1329 return (gEfiShellProtocol->Execute (
1330 ParentHandle,
1331 CommandLine,
1332 EnvironmentVariables,
1333 Status
1334 ));
1335 }
1336
1337 //
1338 // Check for EFI shell
1339 //
1340 if (mEfiShellEnvironment2 != NULL) {
1341 //
1342 // Call EFI Shell version.
1343 //
1344 // Due to an unfixable bug in the EdkShell implementation, we must
1345 // dereference "ParentHandle" here:
1346 //
1347 // 1. The EFI shell installs the EFI_SHELL_ENVIRONMENT2 protocol,
1348 // identified by gEfiShellEnvironment2Guid.
1349 // 2. The Execute() member function takes "ParentImageHandle" as first
1350 // parameter, with type (EFI_HANDLE*).
1351 // 3. In the EdkShell implementation, SEnvExecute() implements the
1352 // Execute() member function. It passes "ParentImageHandle" correctly to
1353 // SEnvDoExecute().
1354 // 4. SEnvDoExecute() takes the (EFI_HANDLE*), and passes it directly --
1355 // without de-referencing -- to the HandleProtocol() boot service.
1356 // 5. But HandleProtocol() takes an EFI_HANDLE.
1357 //
1358 // Therefore we must
1359 // - de-reference "ParentHandle" here, to mask the bug in
1360 // SEnvDoExecute(), and
1361 // - pass the resultant EFI_HANDLE as an (EFI_HANDLE*).
1362 //
1363 CmdStatus = (mEfiShellEnvironment2->Execute (
1364 (EFI_HANDLE *)*ParentHandle,
1365 CommandLine,
1366 Output
1367 ));
1368 //
1369 // No Status output parameter so just use the returned status
1370 //
1371 if (Status != NULL) {
1372 *Status = CmdStatus;
1373 }
1374
1375 //
1376 // If there was an error, we can't tell if it was from the command or from
1377 // the Execute() function, so we'll just assume the shell ran successfully
1378 // and the error came from the command.
1379 //
1380 return EFI_SUCCESS;
1381 }
1382
1383 return (EFI_UNSUPPORTED);
1384 }
1385
1386 /**
1387 Retreives the current directory path
1388
1389 If the DeviceName is NULL, it returns the current device's current directory
1390 name. If the DeviceName is not NULL, it returns the current directory name
1391 on specified drive.
1392
1393 Note that the current directory string should exclude the tailing backslash character.
1394
1395 @param DeviceName the name of the drive to get directory on
1396
1397 @retval NULL the directory does not exist
1398 @return != NULL the directory
1399 **/
1400 CONST CHAR16 *
1401 EFIAPI
1402 ShellGetCurrentDir (
1403 IN CHAR16 *CONST DeviceName OPTIONAL
1404 )
1405 {
1406 //
1407 // Check for UEFI Shell 2.0 protocols
1408 //
1409 if (gEfiShellProtocol != NULL) {
1410 return (gEfiShellProtocol->GetCurDir (DeviceName));
1411 }
1412
1413 //
1414 // Check for EFI shell
1415 //
1416 if (mEfiShellEnvironment2 != NULL) {
1417 return (mEfiShellEnvironment2->CurDir (DeviceName));
1418 }
1419
1420 return (NULL);
1421 }
1422
1423 /**
1424 sets (enabled or disabled) the page break mode
1425
1426 when page break mode is enabled the screen will stop scrolling
1427 and wait for operator input before scrolling a subsequent screen.
1428
1429 @param CurrentState TRUE to enable and FALSE to disable
1430 **/
1431 VOID
1432 EFIAPI
1433 ShellSetPageBreakMode (
1434 IN BOOLEAN CurrentState
1435 )
1436 {
1437 //
1438 // check for enabling
1439 //
1440 if (CurrentState != 0x00) {
1441 //
1442 // check for UEFI Shell 2.0
1443 //
1444 if (gEfiShellProtocol != NULL) {
1445 //
1446 // Enable with UEFI 2.0 Shell
1447 //
1448 gEfiShellProtocol->EnablePageBreak ();
1449 return;
1450 } else {
1451 //
1452 // Check for EFI shell
1453 //
1454 if (mEfiShellEnvironment2 != NULL) {
1455 //
1456 // Enable with EFI Shell
1457 //
1458 mEfiShellEnvironment2->EnablePageBreak (DEFAULT_INIT_ROW, DEFAULT_AUTO_LF);
1459 return;
1460 }
1461 }
1462 } else {
1463 //
1464 // check for UEFI Shell 2.0
1465 //
1466 if (gEfiShellProtocol != NULL) {
1467 //
1468 // Disable with UEFI 2.0 Shell
1469 //
1470 gEfiShellProtocol->DisablePageBreak ();
1471 return;
1472 } else {
1473 //
1474 // Check for EFI shell
1475 //
1476 if (mEfiShellEnvironment2 != NULL) {
1477 //
1478 // Disable with EFI Shell
1479 //
1480 mEfiShellEnvironment2->DisablePageBreak ();
1481 return;
1482 }
1483 }
1484 }
1485 }
1486
1487 ///
1488 /// version of EFI_SHELL_FILE_INFO struct, except has no CONST pointers.
1489 /// This allows for the struct to be populated.
1490 ///
1491 typedef struct {
1492 LIST_ENTRY Link;
1493 EFI_STATUS Status;
1494 CHAR16 *FullName;
1495 CHAR16 *FileName;
1496 SHELL_FILE_HANDLE Handle;
1497 EFI_FILE_INFO *Info;
1498 } EFI_SHELL_FILE_INFO_NO_CONST;
1499
1500 /**
1501 Converts a EFI shell list of structures to the coresponding UEFI Shell 2.0 type of list.
1502
1503 if OldStyleFileList is NULL then ASSERT()
1504
1505 this function will convert a SHELL_FILE_ARG based list into a callee allocated
1506 EFI_SHELL_FILE_INFO based list. it is up to the caller to free the memory via
1507 the ShellCloseFileMetaArg function.
1508
1509 @param[in] FileList the EFI shell list type
1510 @param[in, out] ListHead the list to add to
1511
1512 @retval the resultant head of the double linked new format list;
1513 **/
1514 LIST_ENTRY *
1515 InternalShellConvertFileListType (
1516 IN LIST_ENTRY *FileList,
1517 IN OUT LIST_ENTRY *ListHead
1518 )
1519 {
1520 SHELL_FILE_ARG *OldInfo;
1521 LIST_ENTRY *Link;
1522 EFI_SHELL_FILE_INFO_NO_CONST *NewInfo;
1523
1524 //
1525 // ASSERTs
1526 //
1527 ASSERT (FileList != NULL);
1528 ASSERT (ListHead != NULL);
1529
1530 //
1531 // enumerate through each member of the old list and copy
1532 //
1533 for (Link = FileList->ForwardLink; Link != FileList; Link = Link->ForwardLink) {
1534 OldInfo = CR (Link, SHELL_FILE_ARG, Link, SHELL_FILE_ARG_SIGNATURE);
1535 ASSERT (OldInfo != NULL);
1536
1537 //
1538 // Skip ones that failed to open...
1539 //
1540 if (OldInfo->Status != EFI_SUCCESS) {
1541 continue;
1542 }
1543
1544 //
1545 // make sure the old list was valid
1546 //
1547 ASSERT (OldInfo->Info != NULL);
1548 ASSERT (OldInfo->FullName != NULL);
1549 ASSERT (OldInfo->FileName != NULL);
1550
1551 //
1552 // allocate a new EFI_SHELL_FILE_INFO object
1553 //
1554 NewInfo = AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
1555 if (NewInfo == NULL) {
1556 ShellCloseFileMetaArg ((EFI_SHELL_FILE_INFO **)(&ListHead));
1557 ListHead = NULL;
1558 break;
1559 }
1560
1561 //
1562 // copy the simple items
1563 //
1564 NewInfo->Handle = OldInfo->Handle;
1565 NewInfo->Status = OldInfo->Status;
1566
1567 // old shell checks for 0 not NULL
1568 OldInfo->Handle = 0;
1569
1570 //
1571 // allocate new space to copy strings and structure
1572 //
1573 NewInfo->FullName = AllocateCopyPool (StrSize (OldInfo->FullName), OldInfo->FullName);
1574 NewInfo->FileName = AllocateCopyPool (StrSize (OldInfo->FileName), OldInfo->FileName);
1575 NewInfo->Info = AllocateCopyPool ((UINTN)OldInfo->Info->Size, OldInfo->Info);
1576
1577 //
1578 // make sure all the memory allocations were sucessful
1579 //
1580 if ((NULL == NewInfo->FullName) || (NewInfo->FileName == NULL) || (NewInfo->Info == NULL)) {
1581 //
1582 // Free the partially allocated new node
1583 //
1584 SHELL_FREE_NON_NULL (NewInfo->FullName);
1585 SHELL_FREE_NON_NULL (NewInfo->FileName);
1586 SHELL_FREE_NON_NULL (NewInfo->Info);
1587 SHELL_FREE_NON_NULL (NewInfo);
1588
1589 //
1590 // Free the previously converted stuff
1591 //
1592 ShellCloseFileMetaArg ((EFI_SHELL_FILE_INFO **)(&ListHead));
1593 ListHead = NULL;
1594 break;
1595 }
1596
1597 //
1598 // add that to the list
1599 //
1600 InsertTailList (ListHead, &NewInfo->Link);
1601 }
1602
1603 return (ListHead);
1604 }
1605
1606 /**
1607 Opens a group of files based on a path.
1608
1609 This function uses the Arg to open all the matching files. Each matched
1610 file has a SHELL_FILE_INFO structure to record the file information. These
1611 structures are placed on the list ListHead. Users can get the SHELL_FILE_INFO
1612 structures from ListHead to access each file. This function supports wildcards
1613 and will process '?' and '*' as such. the list must be freed with a call to
1614 ShellCloseFileMetaArg().
1615
1616 If you are NOT appending to an existing list *ListHead must be NULL. If
1617 *ListHead is NULL then it must be callee freed.
1618
1619 @param Arg pointer to path string
1620 @param OpenMode mode to open files with
1621 @param ListHead head of linked list of results
1622
1623 @retval EFI_SUCCESS the operation was sucessful and the list head
1624 contains the list of opened files
1625 @return != EFI_SUCCESS the operation failed
1626
1627 @sa InternalShellConvertFileListType
1628 **/
1629 EFI_STATUS
1630 EFIAPI
1631 ShellOpenFileMetaArg (
1632 IN CHAR16 *Arg,
1633 IN UINT64 OpenMode,
1634 IN OUT EFI_SHELL_FILE_INFO **ListHead
1635 )
1636 {
1637 EFI_STATUS Status;
1638 LIST_ENTRY mOldStyleFileList;
1639 CHAR16 *CleanFilePathStr;
1640
1641 //
1642 // ASSERT that Arg and ListHead are not NULL
1643 //
1644 ASSERT (Arg != NULL);
1645 ASSERT (ListHead != NULL);
1646
1647 CleanFilePathStr = NULL;
1648
1649 Status = InternalShellStripQuotes (Arg, &CleanFilePathStr);
1650 if (EFI_ERROR (Status)) {
1651 return Status;
1652 }
1653
1654 //
1655 // Check for UEFI Shell 2.0 protocols
1656 //
1657 if (gEfiShellProtocol != NULL) {
1658 if (*ListHead == NULL) {
1659 *ListHead = (EFI_SHELL_FILE_INFO *)AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
1660 if (*ListHead == NULL) {
1661 FreePool (CleanFilePathStr);
1662 return (EFI_OUT_OF_RESOURCES);
1663 }
1664
1665 InitializeListHead (&((*ListHead)->Link));
1666 }
1667
1668 Status = gEfiShellProtocol->OpenFileList (
1669 CleanFilePathStr,
1670 OpenMode,
1671 ListHead
1672 );
1673 if (EFI_ERROR (Status)) {
1674 gEfiShellProtocol->RemoveDupInFileList (ListHead);
1675 } else {
1676 Status = gEfiShellProtocol->RemoveDupInFileList (ListHead);
1677 }
1678
1679 if ((*ListHead != NULL) && IsListEmpty (&(*ListHead)->Link)) {
1680 FreePool (*ListHead);
1681 FreePool (CleanFilePathStr);
1682 *ListHead = NULL;
1683 return (EFI_NOT_FOUND);
1684 }
1685
1686 FreePool (CleanFilePathStr);
1687 return (Status);
1688 }
1689
1690 //
1691 // Check for EFI shell
1692 //
1693 if (mEfiShellEnvironment2 != NULL) {
1694 //
1695 // make sure the list head is initialized
1696 //
1697 InitializeListHead (&mOldStyleFileList);
1698
1699 //
1700 // Get the EFI Shell list of files
1701 //
1702 Status = mEfiShellEnvironment2->FileMetaArg (CleanFilePathStr, &mOldStyleFileList);
1703 if (EFI_ERROR (Status)) {
1704 *ListHead = NULL;
1705 FreePool (CleanFilePathStr);
1706 return (Status);
1707 }
1708
1709 if (*ListHead == NULL) {
1710 *ListHead = (EFI_SHELL_FILE_INFO *)AllocateZeroPool (sizeof (EFI_SHELL_FILE_INFO));
1711 if (*ListHead == NULL) {
1712 FreePool (CleanFilePathStr);
1713 return (EFI_OUT_OF_RESOURCES);
1714 }
1715
1716 InitializeListHead (&((*ListHead)->Link));
1717 }
1718
1719 //
1720 // Convert that to equivalent of UEFI Shell 2.0 structure
1721 //
1722 InternalShellConvertFileListType (&mOldStyleFileList, &(*ListHead)->Link);
1723
1724 //
1725 // Free the EFI Shell version that was converted.
1726 //
1727 mEfiShellEnvironment2->FreeFileList (&mOldStyleFileList);
1728
1729 if (((*ListHead)->Link.ForwardLink == (*ListHead)->Link.BackLink) && ((*ListHead)->Link.BackLink == &((*ListHead)->Link))) {
1730 FreePool (*ListHead);
1731 *ListHead = NULL;
1732 Status = EFI_NOT_FOUND;
1733 }
1734
1735 FreePool (CleanFilePathStr);
1736 return (Status);
1737 }
1738
1739 FreePool (CleanFilePathStr);
1740 return (EFI_UNSUPPORTED);
1741 }
1742
1743 /**
1744 Free the linked list returned from ShellOpenFileMetaArg.
1745
1746 if ListHead is NULL then ASSERT().
1747
1748 @param ListHead the pointer to free.
1749
1750 @retval EFI_SUCCESS the operation was sucessful.
1751 **/
1752 EFI_STATUS
1753 EFIAPI
1754 ShellCloseFileMetaArg (
1755 IN OUT EFI_SHELL_FILE_INFO **ListHead
1756 )
1757 {
1758 LIST_ENTRY *Node;
1759
1760 //
1761 // ASSERT that ListHead is not NULL
1762 //
1763 ASSERT (ListHead != NULL);
1764
1765 //
1766 // Check for UEFI Shell 2.0 protocols
1767 //
1768 if (gEfiShellProtocol != NULL) {
1769 return (gEfiShellProtocol->FreeFileList (ListHead));
1770 } else if (mEfiShellEnvironment2 != NULL) {
1771 //
1772 // Since this is EFI Shell version we need to free our internally made copy
1773 // of the list
1774 //
1775 for ( Node = GetFirstNode (&(*ListHead)->Link)
1776 ; *ListHead != NULL && !IsListEmpty (&(*ListHead)->Link)
1777 ; Node = GetFirstNode (&(*ListHead)->Link))
1778 {
1779 RemoveEntryList (Node);
1780 ((EFI_FILE_PROTOCOL *)((EFI_SHELL_FILE_INFO_NO_CONST *)Node)->Handle)->Close (((EFI_SHELL_FILE_INFO_NO_CONST *)Node)->Handle);
1781 FreePool (((EFI_SHELL_FILE_INFO_NO_CONST *)Node)->FullName);
1782 FreePool (((EFI_SHELL_FILE_INFO_NO_CONST *)Node)->FileName);
1783 FreePool (((EFI_SHELL_FILE_INFO_NO_CONST *)Node)->Info);
1784 FreePool ((EFI_SHELL_FILE_INFO_NO_CONST *)Node);
1785 }
1786
1787 SHELL_FREE_NON_NULL (*ListHead);
1788 return EFI_SUCCESS;
1789 }
1790
1791 return (EFI_UNSUPPORTED);
1792 }
1793
1794 /**
1795 Find a file by searching the CWD and then the path.
1796
1797 If FileName is NULL then ASSERT.
1798
1799 If the return value is not NULL then the memory must be caller freed.
1800
1801 @param FileName Filename string.
1802
1803 @retval NULL the file was not found
1804 @return !NULL the full path to the file.
1805 **/
1806 CHAR16 *
1807 EFIAPI
1808 ShellFindFilePath (
1809 IN CONST CHAR16 *FileName
1810 )
1811 {
1812 CONST CHAR16 *Path;
1813 SHELL_FILE_HANDLE Handle;
1814 EFI_STATUS Status;
1815 CHAR16 *RetVal;
1816 CHAR16 *TestPath;
1817 CONST CHAR16 *Walker;
1818 UINTN Size;
1819 CHAR16 *TempChar;
1820
1821 RetVal = NULL;
1822
1823 //
1824 // First make sure its not an absolute path.
1825 //
1826 Status = ShellOpenFileByName (FileName, &Handle, EFI_FILE_MODE_READ, 0);
1827 if (!EFI_ERROR (Status)) {
1828 if (FileHandleIsDirectory (Handle) != EFI_SUCCESS) {
1829 ASSERT (RetVal == NULL);
1830 RetVal = StrnCatGrow (&RetVal, NULL, FileName, 0);
1831 ShellCloseFile (&Handle);
1832 return (RetVal);
1833 } else {
1834 ShellCloseFile (&Handle);
1835 }
1836 }
1837
1838 Path = ShellGetEnvironmentVariable (L"cwd");
1839 if (Path != NULL) {
1840 Size = StrSize (Path) + sizeof (CHAR16);
1841 Size += StrSize (FileName);
1842 TestPath = AllocateZeroPool (Size);
1843 if (TestPath == NULL) {
1844 return (NULL);
1845 }
1846
1847 StrCpyS (TestPath, Size/sizeof (CHAR16), Path);
1848 StrCatS (TestPath, Size/sizeof (CHAR16), L"\\");
1849 StrCatS (TestPath, Size/sizeof (CHAR16), FileName);
1850 Status = ShellOpenFileByName (TestPath, &Handle, EFI_FILE_MODE_READ, 0);
1851 if (!EFI_ERROR (Status)) {
1852 if (FileHandleIsDirectory (Handle) != EFI_SUCCESS) {
1853 ASSERT (RetVal == NULL);
1854 RetVal = StrnCatGrow (&RetVal, NULL, TestPath, 0);
1855 ShellCloseFile (&Handle);
1856 FreePool (TestPath);
1857 return (RetVal);
1858 } else {
1859 ShellCloseFile (&Handle);
1860 }
1861 }
1862
1863 FreePool (TestPath);
1864 }
1865
1866 Path = ShellGetEnvironmentVariable (L"path");
1867 if (Path != NULL) {
1868 Size = StrSize (Path)+sizeof (CHAR16);
1869 Size += StrSize (FileName);
1870 TestPath = AllocateZeroPool (Size);
1871 if (TestPath == NULL) {
1872 return (NULL);
1873 }
1874
1875 Walker = (CHAR16 *)Path;
1876 do {
1877 CopyMem (TestPath, Walker, StrSize (Walker));
1878 if (TestPath != NULL) {
1879 TempChar = StrStr (TestPath, L";");
1880 if (TempChar != NULL) {
1881 *TempChar = CHAR_NULL;
1882 }
1883
1884 if (TestPath[StrLen (TestPath)-1] != L'\\') {
1885 StrCatS (TestPath, Size/sizeof (CHAR16), L"\\");
1886 }
1887
1888 if (FileName[0] == L'\\') {
1889 FileName++;
1890 }
1891
1892 StrCatS (TestPath, Size/sizeof (CHAR16), FileName);
1893 if (StrStr (Walker, L";") != NULL) {
1894 Walker = StrStr (Walker, L";") + 1;
1895 } else {
1896 Walker = NULL;
1897 }
1898
1899 Status = ShellOpenFileByName (TestPath, &Handle, EFI_FILE_MODE_READ, 0);
1900 if (!EFI_ERROR (Status)) {
1901 if (FileHandleIsDirectory (Handle) != EFI_SUCCESS) {
1902 ASSERT (RetVal == NULL);
1903 RetVal = StrnCatGrow (&RetVal, NULL, TestPath, 0);
1904 ShellCloseFile (&Handle);
1905 break;
1906 } else {
1907 ShellCloseFile (&Handle);
1908 }
1909 }
1910 }
1911 } while (Walker != NULL && Walker[0] != CHAR_NULL);
1912
1913 FreePool (TestPath);
1914 }
1915
1916 return (RetVal);
1917 }
1918
1919 /**
1920 Find a file by searching the CWD and then the path with a variable set of file
1921 extensions. If the file is not found it will append each extension in the list
1922 in the order provided and return the first one that is successful.
1923
1924 If FileName is NULL, then ASSERT.
1925 If FileExtension is NULL, then behavior is identical to ShellFindFilePath.
1926
1927 If the return value is not NULL then the memory must be caller freed.
1928
1929 @param[in] FileName Filename string.
1930 @param[in] FileExtension Semi-colon delimeted list of possible extensions.
1931
1932 @retval NULL The file was not found.
1933 @retval !NULL The path to the file.
1934 **/
1935 CHAR16 *
1936 EFIAPI
1937 ShellFindFilePathEx (
1938 IN CONST CHAR16 *FileName,
1939 IN CONST CHAR16 *FileExtension
1940 )
1941 {
1942 CHAR16 *TestPath;
1943 CHAR16 *RetVal;
1944 CONST CHAR16 *ExtensionWalker;
1945 UINTN Size;
1946 CHAR16 *TempChar;
1947 CHAR16 *TempChar2;
1948
1949 ASSERT (FileName != NULL);
1950 if (FileExtension == NULL) {
1951 return (ShellFindFilePath (FileName));
1952 }
1953
1954 RetVal = ShellFindFilePath (FileName);
1955 if (RetVal != NULL) {
1956 return (RetVal);
1957 }
1958
1959 Size = StrSize (FileName);
1960 Size += StrSize (FileExtension);
1961 TestPath = AllocateZeroPool (Size);
1962 if (TestPath == NULL) {
1963 return (NULL);
1964 }
1965
1966 for (ExtensionWalker = FileExtension, TempChar2 = (CHAR16 *)FileExtension; TempChar2 != NULL; ExtensionWalker = TempChar2 + 1) {
1967 StrCpyS (TestPath, Size/sizeof (CHAR16), FileName);
1968 if (ExtensionWalker != NULL) {
1969 StrCatS (TestPath, Size/sizeof (CHAR16), ExtensionWalker);
1970 }
1971
1972 TempChar = StrStr (TestPath, L";");
1973 if (TempChar != NULL) {
1974 *TempChar = CHAR_NULL;
1975 }
1976
1977 RetVal = ShellFindFilePath (TestPath);
1978 if (RetVal != NULL) {
1979 break;
1980 }
1981
1982 ASSERT (ExtensionWalker != NULL);
1983 TempChar2 = StrStr (ExtensionWalker, L";");
1984 }
1985
1986 FreePool (TestPath);
1987 return (RetVal);
1988 }
1989
1990 typedef struct {
1991 LIST_ENTRY Link;
1992 CHAR16 *Name;
1993 SHELL_PARAM_TYPE Type;
1994 CHAR16 *Value;
1995 UINTN OriginalPosition;
1996 } SHELL_PARAM_PACKAGE;
1997
1998 /**
1999 Checks the list of valid arguments and returns TRUE if the item was found. If the
2000 return value is TRUE then the type parameter is set also.
2001
2002 if CheckList is NULL then ASSERT();
2003 if Name is NULL then ASSERT();
2004 if Type is NULL then ASSERT();
2005
2006 @param Name pointer to Name of parameter found
2007 @param CheckList List to check against
2008 @param Type pointer to type of parameter if it was found
2009
2010 @retval TRUE the Parameter was found. Type is valid.
2011 @retval FALSE the Parameter was not found. Type is not valid.
2012 **/
2013 BOOLEAN
2014 InternalIsOnCheckList (
2015 IN CONST CHAR16 *Name,
2016 IN CONST SHELL_PARAM_ITEM *CheckList,
2017 OUT SHELL_PARAM_TYPE *Type
2018 )
2019 {
2020 SHELL_PARAM_ITEM *TempListItem;
2021 CHAR16 *TempString;
2022
2023 //
2024 // ASSERT that all 3 pointer parameters aren't NULL
2025 //
2026 ASSERT (CheckList != NULL);
2027 ASSERT (Type != NULL);
2028 ASSERT (Name != NULL);
2029
2030 //
2031 // question mark and page break mode are always supported
2032 //
2033 if ((StrCmp (Name, L"-?") == 0) ||
2034 (StrCmp (Name, L"-b") == 0)
2035 )
2036 {
2037 *Type = TypeFlag;
2038 return (TRUE);
2039 }
2040
2041 //
2042 // Enumerate through the list
2043 //
2044 for (TempListItem = (SHELL_PARAM_ITEM *)CheckList; TempListItem->Name != NULL; TempListItem++) {
2045 //
2046 // If the Type is TypeStart only check the first characters of the passed in param
2047 // If it matches set the type and return TRUE
2048 //
2049 if (TempListItem->Type == TypeStart) {
2050 if (StrnCmp (Name, TempListItem->Name, StrLen (TempListItem->Name)) == 0) {
2051 *Type = TempListItem->Type;
2052 return (TRUE);
2053 }
2054
2055 TempString = NULL;
2056 TempString = StrnCatGrow (&TempString, NULL, Name, StrLen (TempListItem->Name));
2057 if (TempString != NULL) {
2058 if (StringNoCaseCompare (&TempString, &TempListItem->Name) == 0) {
2059 *Type = TempListItem->Type;
2060 FreePool (TempString);
2061 return (TRUE);
2062 }
2063
2064 FreePool (TempString);
2065 }
2066 } else if (StringNoCaseCompare (&Name, &TempListItem->Name) == 0) {
2067 *Type = TempListItem->Type;
2068 return (TRUE);
2069 }
2070 }
2071
2072 return (FALSE);
2073 }
2074
2075 /**
2076 Checks the string for indicators of "flag" status. this is a leading '/', '-', or '+'
2077
2078 @param[in] Name pointer to Name of parameter found
2079 @param[in] AlwaysAllowNumbers TRUE to allow numbers, FALSE to not.
2080 @param[in] TimeNumbers TRUE to allow numbers with ":", FALSE otherwise.
2081
2082 @retval TRUE the Parameter is a flag.
2083 @retval FALSE the Parameter not a flag.
2084 **/
2085 BOOLEAN
2086 InternalIsFlag (
2087 IN CONST CHAR16 *Name,
2088 IN CONST BOOLEAN AlwaysAllowNumbers,
2089 IN CONST BOOLEAN TimeNumbers
2090 )
2091 {
2092 //
2093 // ASSERT that Name isn't NULL
2094 //
2095 ASSERT (Name != NULL);
2096
2097 //
2098 // If we accept numbers then dont return TRUE. (they will be values)
2099 //
2100 if ((((Name[0] == L'-') || (Name[0] == L'+')) && InternalShellIsHexOrDecimalNumber (Name+1, FALSE, FALSE, TimeNumbers)) && AlwaysAllowNumbers) {
2101 return (FALSE);
2102 }
2103
2104 //
2105 // If the Name has a /, +, or - as the first character return TRUE
2106 //
2107 if ((Name[0] == L'/') ||
2108 (Name[0] == L'-') ||
2109 (Name[0] == L'+')
2110 )
2111 {
2112 return (TRUE);
2113 }
2114
2115 return (FALSE);
2116 }
2117
2118 /**
2119 Checks the command line arguments passed against the list of valid ones.
2120
2121 If no initialization is required, then return RETURN_SUCCESS.
2122
2123 @param[in] CheckList pointer to list of parameters to check
2124 @param[out] CheckPackage pointer to pointer to list checked values
2125 @param[out] ProblemParam optional pointer to pointer to unicode string for
2126 the paramater that caused failure. If used then the
2127 caller is responsible for freeing the memory.
2128 @param[in] AutoPageBreak will automatically set PageBreakEnabled for "b" parameter
2129 @param[in] Argv pointer to array of parameters
2130 @param[in] Argc Count of parameters in Argv
2131 @param[in] AlwaysAllowNumbers TRUE to allow numbers always, FALSE otherwise.
2132
2133 @retval EFI_SUCCESS The operation completed sucessfully.
2134 @retval EFI_OUT_OF_RESOURCES A memory allocation failed
2135 @retval EFI_INVALID_PARAMETER A parameter was invalid
2136 @retval EFI_VOLUME_CORRUPTED the command line was corrupt. an argument was
2137 duplicated. the duplicated command line argument
2138 was returned in ProblemParam if provided.
2139 @retval EFI_NOT_FOUND a argument required a value that was missing.
2140 the invalid command line argument was returned in
2141 ProblemParam if provided.
2142 **/
2143 EFI_STATUS
2144 InternalCommandLineParse (
2145 IN CONST SHELL_PARAM_ITEM *CheckList,
2146 OUT LIST_ENTRY **CheckPackage,
2147 OUT CHAR16 **ProblemParam OPTIONAL,
2148 IN BOOLEAN AutoPageBreak,
2149 IN CONST CHAR16 **Argv,
2150 IN UINTN Argc,
2151 IN BOOLEAN AlwaysAllowNumbers
2152 )
2153 {
2154 UINTN LoopCounter;
2155 SHELL_PARAM_TYPE CurrentItemType;
2156 SHELL_PARAM_PACKAGE *CurrentItemPackage;
2157 UINTN GetItemValue;
2158 UINTN ValueSize;
2159 UINTN Count;
2160 CONST CHAR16 *TempPointer;
2161 UINTN CurrentValueSize;
2162 CHAR16 *NewValue;
2163
2164 CurrentItemPackage = NULL;
2165 GetItemValue = 0;
2166 ValueSize = 0;
2167 Count = 0;
2168
2169 //
2170 // If there is only 1 item we dont need to do anything
2171 //
2172 if (Argc < 1) {
2173 *CheckPackage = NULL;
2174 return (EFI_SUCCESS);
2175 }
2176
2177 //
2178 // ASSERTs
2179 //
2180 ASSERT (CheckList != NULL);
2181 ASSERT (Argv != NULL);
2182
2183 //
2184 // initialize the linked list
2185 //
2186 *CheckPackage = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));
2187 if (*CheckPackage == NULL) {
2188 return (EFI_OUT_OF_RESOURCES);
2189 }
2190
2191 InitializeListHead (*CheckPackage);
2192
2193 //
2194 // loop through each of the arguments
2195 //
2196 for (LoopCounter = 0; LoopCounter < Argc; ++LoopCounter) {
2197 if (Argv[LoopCounter] == NULL) {
2198 //
2199 // do nothing for NULL argv
2200 //
2201 } else if (InternalIsOnCheckList (Argv[LoopCounter], CheckList, &CurrentItemType)) {
2202 //
2203 // We might have leftover if last parameter didnt have optional value
2204 //
2205 if (GetItemValue != 0) {
2206 GetItemValue = 0;
2207 InsertHeadList (*CheckPackage, &CurrentItemPackage->Link);
2208 }
2209
2210 //
2211 // this is a flag
2212 //
2213 CurrentItemPackage = AllocateZeroPool (sizeof (SHELL_PARAM_PACKAGE));
2214 if (CurrentItemPackage == NULL) {
2215 ShellCommandLineFreeVarList (*CheckPackage);
2216 *CheckPackage = NULL;
2217 return (EFI_OUT_OF_RESOURCES);
2218 }
2219
2220 CurrentItemPackage->Name = AllocateCopyPool (StrSize (Argv[LoopCounter]), Argv[LoopCounter]);
2221 if (CurrentItemPackage->Name == NULL) {
2222 ShellCommandLineFreeVarList (*CheckPackage);
2223 *CheckPackage = NULL;
2224 return (EFI_OUT_OF_RESOURCES);
2225 }
2226
2227 CurrentItemPackage->Type = CurrentItemType;
2228 CurrentItemPackage->OriginalPosition = (UINTN)(-1);
2229 CurrentItemPackage->Value = NULL;
2230
2231 //
2232 // Does this flag require a value
2233 //
2234 switch (CurrentItemPackage->Type) {
2235 //
2236 // possibly trigger the next loop(s) to populate the value of this item
2237 //
2238 case TypeValue:
2239 case TypeTimeValue:
2240 GetItemValue = 1;
2241 ValueSize = 0;
2242 break;
2243 case TypeDoubleValue:
2244 GetItemValue = 2;
2245 ValueSize = 0;
2246 break;
2247 case TypeMaxValue:
2248 GetItemValue = (UINTN)(-1);
2249 ValueSize = 0;
2250 break;
2251 default:
2252 //
2253 // this item has no value expected; we are done
2254 //
2255 InsertHeadList (*CheckPackage, &CurrentItemPackage->Link);
2256 ASSERT (GetItemValue == 0);
2257 break;
2258 }
2259 } else if ((GetItemValue != 0) && (CurrentItemPackage != NULL) && !InternalIsFlag (Argv[LoopCounter], AlwaysAllowNumbers, (BOOLEAN)(CurrentItemPackage->Type == TypeTimeValue))) {
2260 //
2261 // get the item VALUE for a previous flag
2262 //
2263 CurrentValueSize = ValueSize + StrSize (Argv[LoopCounter]) + sizeof (CHAR16);
2264 NewValue = ReallocatePool (ValueSize, CurrentValueSize, CurrentItemPackage->Value);
2265 if (NewValue == NULL) {
2266 SHELL_FREE_NON_NULL (CurrentItemPackage->Value);
2267 SHELL_FREE_NON_NULL (CurrentItemPackage);
2268 ShellCommandLineFreeVarList (*CheckPackage);
2269 *CheckPackage = NULL;
2270 return EFI_OUT_OF_RESOURCES;
2271 }
2272
2273 CurrentItemPackage->Value = NewValue;
2274 if (ValueSize == 0) {
2275 StrCpyS (
2276 CurrentItemPackage->Value,
2277 CurrentValueSize/sizeof (CHAR16),
2278 Argv[LoopCounter]
2279 );
2280 } else {
2281 StrCatS (
2282 CurrentItemPackage->Value,
2283 CurrentValueSize/sizeof (CHAR16),
2284 L" "
2285 );
2286 StrCatS (
2287 CurrentItemPackage->Value,
2288 CurrentValueSize/sizeof (CHAR16),
2289 Argv[LoopCounter]
2290 );
2291 }
2292
2293 ValueSize += StrSize (Argv[LoopCounter]) + sizeof (CHAR16);
2294
2295 GetItemValue--;
2296 if (GetItemValue == 0) {
2297 InsertHeadList (*CheckPackage, &CurrentItemPackage->Link);
2298 }
2299 } else if (!InternalIsFlag (Argv[LoopCounter], AlwaysAllowNumbers, FALSE)) {
2300 //
2301 // add this one as a non-flag
2302 //
2303
2304 TempPointer = Argv[LoopCounter];
2305 if ( ((*TempPointer == L'^') && (*(TempPointer+1) == L'-'))
2306 || ((*TempPointer == L'^') && (*(TempPointer+1) == L'/'))
2307 || ((*TempPointer == L'^') && (*(TempPointer+1) == L'+'))
2308 )
2309 {
2310 TempPointer++;
2311 }
2312
2313 CurrentItemPackage = AllocateZeroPool (sizeof (SHELL_PARAM_PACKAGE));
2314 if (CurrentItemPackage == NULL) {
2315 ShellCommandLineFreeVarList (*CheckPackage);
2316 *CheckPackage = NULL;
2317 return (EFI_OUT_OF_RESOURCES);
2318 }
2319
2320 CurrentItemPackage->Name = NULL;
2321 CurrentItemPackage->Type = TypePosition;
2322 CurrentItemPackage->Value = AllocateCopyPool (StrSize (TempPointer), TempPointer);
2323 if (CurrentItemPackage->Value == NULL) {
2324 ShellCommandLineFreeVarList (*CheckPackage);
2325 *CheckPackage = NULL;
2326 return (EFI_OUT_OF_RESOURCES);
2327 }
2328
2329 CurrentItemPackage->OriginalPosition = Count++;
2330 InsertHeadList (*CheckPackage, &CurrentItemPackage->Link);
2331 } else {
2332 //
2333 // this was a non-recognised flag... error!
2334 //
2335 if (ProblemParam != NULL) {
2336 *ProblemParam = AllocateCopyPool (StrSize (Argv[LoopCounter]), Argv[LoopCounter]);
2337 }
2338
2339 ShellCommandLineFreeVarList (*CheckPackage);
2340 *CheckPackage = NULL;
2341 return (EFI_VOLUME_CORRUPTED);
2342 }
2343 }
2344
2345 if (GetItemValue != 0) {
2346 GetItemValue = 0;
2347 InsertHeadList (*CheckPackage, &CurrentItemPackage->Link);
2348 }
2349
2350 //
2351 // support for AutoPageBreak
2352 //
2353 if (AutoPageBreak && ShellCommandLineGetFlag (*CheckPackage, L"-b")) {
2354 ShellSetPageBreakMode (TRUE);
2355 }
2356
2357 return (EFI_SUCCESS);
2358 }
2359
2360 /**
2361 Checks the command line arguments passed against the list of valid ones.
2362 Optionally removes NULL values first.
2363
2364 If no initialization is required, then return RETURN_SUCCESS.
2365
2366 @param[in] CheckList The pointer to list of parameters to check.
2367 @param[out] CheckPackage The package of checked values.
2368 @param[out] ProblemParam Optional pointer to pointer to unicode string for
2369 the paramater that caused failure.
2370 @param[in] AutoPageBreak Will automatically set PageBreakEnabled.
2371 @param[in] AlwaysAllowNumbers Will never fail for number based flags.
2372
2373 @retval EFI_SUCCESS The operation completed sucessfully.
2374 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2375 @retval EFI_INVALID_PARAMETER A parameter was invalid.
2376 @retval EFI_VOLUME_CORRUPTED The command line was corrupt.
2377 @retval EFI_DEVICE_ERROR The commands contained 2 opposing arguments. One
2378 of the command line arguments was returned in
2379 ProblemParam if provided.
2380 @retval EFI_NOT_FOUND A argument required a value that was missing.
2381 The invalid command line argument was returned in
2382 ProblemParam if provided.
2383 **/
2384 EFI_STATUS
2385 EFIAPI
2386 ShellCommandLineParseEx (
2387 IN CONST SHELL_PARAM_ITEM *CheckList,
2388 OUT LIST_ENTRY **CheckPackage,
2389 OUT CHAR16 **ProblemParam OPTIONAL,
2390 IN BOOLEAN AutoPageBreak,
2391 IN BOOLEAN AlwaysAllowNumbers
2392 )
2393 {
2394 //
2395 // ASSERT that CheckList and CheckPackage aren't NULL
2396 //
2397 ASSERT (CheckList != NULL);
2398 ASSERT (CheckPackage != NULL);
2399
2400 //
2401 // Check for UEFI Shell 2.0 protocols
2402 //
2403 if (gEfiShellParametersProtocol != NULL) {
2404 return (InternalCommandLineParse (
2405 CheckList,
2406 CheckPackage,
2407 ProblemParam,
2408 AutoPageBreak,
2409 (CONST CHAR16 **)gEfiShellParametersProtocol->Argv,
2410 gEfiShellParametersProtocol->Argc,
2411 AlwaysAllowNumbers
2412 ));
2413 }
2414
2415 //
2416 // ASSERT That EFI Shell is not required
2417 //
2418 ASSERT (mEfiShellInterface != NULL);
2419 return (InternalCommandLineParse (
2420 CheckList,
2421 CheckPackage,
2422 ProblemParam,
2423 AutoPageBreak,
2424 (CONST CHAR16 **)mEfiShellInterface->Argv,
2425 mEfiShellInterface->Argc,
2426 AlwaysAllowNumbers
2427 ));
2428 }
2429
2430 /**
2431 Frees shell variable list that was returned from ShellCommandLineParse.
2432
2433 This function will free all the memory that was used for the CheckPackage
2434 list of postprocessed shell arguments.
2435
2436 this function has no return value.
2437
2438 if CheckPackage is NULL, then return
2439
2440 @param CheckPackage the list to de-allocate
2441 **/
2442 VOID
2443 EFIAPI
2444 ShellCommandLineFreeVarList (
2445 IN LIST_ENTRY *CheckPackage
2446 )
2447 {
2448 LIST_ENTRY *Node;
2449
2450 //
2451 // check for CheckPackage == NULL
2452 //
2453 if (CheckPackage == NULL) {
2454 return;
2455 }
2456
2457 //
2458 // for each node in the list
2459 //
2460 for ( Node = GetFirstNode (CheckPackage)
2461 ; !IsListEmpty (CheckPackage)
2462 ; Node = GetFirstNode (CheckPackage)
2463 )
2464 {
2465 //
2466 // Remove it from the list
2467 //
2468 RemoveEntryList (Node);
2469
2470 //
2471 // if it has a name free the name
2472 //
2473 if (((SHELL_PARAM_PACKAGE *)Node)->Name != NULL) {
2474 FreePool (((SHELL_PARAM_PACKAGE *)Node)->Name);
2475 }
2476
2477 //
2478 // if it has a value free the value
2479 //
2480 if (((SHELL_PARAM_PACKAGE *)Node)->Value != NULL) {
2481 FreePool (((SHELL_PARAM_PACKAGE *)Node)->Value);
2482 }
2483
2484 //
2485 // free the node structure
2486 //
2487 FreePool ((SHELL_PARAM_PACKAGE *)Node);
2488 }
2489
2490 //
2491 // free the list head node
2492 //
2493 FreePool (CheckPackage);
2494 }
2495
2496 /**
2497 Checks for presence of a flag parameter
2498
2499 flag arguments are in the form of "-<Key>" or "/<Key>", but do not have a value following the key
2500
2501 if CheckPackage is NULL then return FALSE.
2502 if KeyString is NULL then ASSERT()
2503
2504 @param CheckPackage The package of parsed command line arguments
2505 @param KeyString the Key of the command line argument to check for
2506
2507 @retval TRUE the flag is on the command line
2508 @retval FALSE the flag is not on the command line
2509 **/
2510 BOOLEAN
2511 EFIAPI
2512 ShellCommandLineGetFlag (
2513 IN CONST LIST_ENTRY *CONST CheckPackage,
2514 IN CONST CHAR16 *CONST KeyString
2515 )
2516 {
2517 LIST_ENTRY *Node;
2518 CHAR16 *TempString;
2519
2520 //
2521 // return FALSE for no package or KeyString is NULL
2522 //
2523 if ((CheckPackage == NULL) || (KeyString == NULL)) {
2524 return (FALSE);
2525 }
2526
2527 //
2528 // enumerate through the list of parametrs
2529 //
2530 for ( Node = GetFirstNode (CheckPackage)
2531 ; !IsNull (CheckPackage, Node)
2532 ; Node = GetNextNode (CheckPackage, Node)
2533 )
2534 {
2535 //
2536 // If the Name matches, return TRUE (and there may be NULL name)
2537 //
2538 if (((SHELL_PARAM_PACKAGE *)Node)->Name != NULL) {
2539 //
2540 // If Type is TypeStart then only compare the begining of the strings
2541 //
2542 if (((SHELL_PARAM_PACKAGE *)Node)->Type == TypeStart) {
2543 if (StrnCmp (KeyString, ((SHELL_PARAM_PACKAGE *)Node)->Name, StrLen (KeyString)) == 0) {
2544 return (TRUE);
2545 }
2546
2547 TempString = NULL;
2548 TempString = StrnCatGrow (&TempString, NULL, KeyString, StrLen (((SHELL_PARAM_PACKAGE *)Node)->Name));
2549 if (TempString != NULL) {
2550 if (StringNoCaseCompare (&KeyString, &((SHELL_PARAM_PACKAGE *)Node)->Name) == 0) {
2551 FreePool (TempString);
2552 return (TRUE);
2553 }
2554
2555 FreePool (TempString);
2556 }
2557 } else if (StringNoCaseCompare (&KeyString, &((SHELL_PARAM_PACKAGE *)Node)->Name) == 0) {
2558 return (TRUE);
2559 }
2560 }
2561 }
2562
2563 return (FALSE);
2564 }
2565
2566 /**
2567 Returns value from command line argument.
2568
2569 Value parameters are in the form of "-<Key> value" or "/<Key> value".
2570
2571 If CheckPackage is NULL, then return NULL.
2572
2573 @param[in] CheckPackage The package of parsed command line arguments.
2574 @param[in] KeyString The Key of the command line argument to check for.
2575
2576 @retval NULL The flag is not on the command line.
2577 @retval !=NULL The pointer to unicode string of the value.
2578 **/
2579 CONST CHAR16 *
2580 EFIAPI
2581 ShellCommandLineGetValue (
2582 IN CONST LIST_ENTRY *CheckPackage,
2583 IN CHAR16 *KeyString
2584 )
2585 {
2586 LIST_ENTRY *Node;
2587 CHAR16 *TempString;
2588
2589 //
2590 // return NULL for no package or KeyString is NULL
2591 //
2592 if ((CheckPackage == NULL) || (KeyString == NULL)) {
2593 return (NULL);
2594 }
2595
2596 //
2597 // enumerate through the list of parametrs
2598 //
2599 for ( Node = GetFirstNode (CheckPackage)
2600 ; !IsNull (CheckPackage, Node)
2601 ; Node = GetNextNode (CheckPackage, Node)
2602 )
2603 {
2604 //
2605 // If the Name matches, return TRUE (and there may be NULL name)
2606 //
2607 if (((SHELL_PARAM_PACKAGE *)Node)->Name != NULL) {
2608 //
2609 // If Type is TypeStart then only compare the begining of the strings
2610 //
2611 if (((SHELL_PARAM_PACKAGE *)Node)->Type == TypeStart) {
2612 if (StrnCmp (KeyString, ((SHELL_PARAM_PACKAGE *)Node)->Name, StrLen (KeyString)) == 0) {
2613 return (((SHELL_PARAM_PACKAGE *)Node)->Name + StrLen (KeyString));
2614 }
2615
2616 TempString = NULL;
2617 TempString = StrnCatGrow (&TempString, NULL, KeyString, StrLen (((SHELL_PARAM_PACKAGE *)Node)->Name));
2618 if (TempString != NULL) {
2619 if (StringNoCaseCompare (&KeyString, &((SHELL_PARAM_PACKAGE *)Node)->Name) == 0) {
2620 FreePool (TempString);
2621 return (((SHELL_PARAM_PACKAGE *)Node)->Name + StrLen (KeyString));
2622 }
2623
2624 FreePool (TempString);
2625 }
2626 } else if (StringNoCaseCompare (&KeyString, &((SHELL_PARAM_PACKAGE *)Node)->Name) == 0) {
2627 return (((SHELL_PARAM_PACKAGE *)Node)->Value);
2628 }
2629 }
2630 }
2631
2632 return (NULL);
2633 }
2634
2635 /**
2636 Returns raw value from command line argument.
2637
2638 Raw value parameters are in the form of "value" in a specific position in the list.
2639
2640 If CheckPackage is NULL, then return NULL.
2641
2642 @param[in] CheckPackage The package of parsed command line arguments.
2643 @param[in] Position The position of the value.
2644
2645 @retval NULL The flag is not on the command line.
2646 @retval !=NULL The pointer to unicode string of the value.
2647 **/
2648 CONST CHAR16 *
2649 EFIAPI
2650 ShellCommandLineGetRawValue (
2651 IN CONST LIST_ENTRY *CONST CheckPackage,
2652 IN UINTN Position
2653 )
2654 {
2655 LIST_ENTRY *Node;
2656
2657 //
2658 // check for CheckPackage == NULL
2659 //
2660 if (CheckPackage == NULL) {
2661 return (NULL);
2662 }
2663
2664 //
2665 // enumerate through the list of parametrs
2666 //
2667 for ( Node = GetFirstNode (CheckPackage)
2668 ; !IsNull (CheckPackage, Node)
2669 ; Node = GetNextNode (CheckPackage, Node)
2670 )
2671 {
2672 //
2673 // If the position matches, return the value
2674 //
2675 if (((SHELL_PARAM_PACKAGE *)Node)->OriginalPosition == Position) {
2676 return (((SHELL_PARAM_PACKAGE *)Node)->Value);
2677 }
2678 }
2679
2680 return (NULL);
2681 }
2682
2683 /**
2684 returns the number of command line value parameters that were parsed.
2685
2686 this will not include flags.
2687
2688 @param[in] CheckPackage The package of parsed command line arguments.
2689
2690 @retval (UINTN)-1 No parsing has ocurred
2691 @return other The number of value parameters found
2692 **/
2693 UINTN
2694 EFIAPI
2695 ShellCommandLineGetCount (
2696 IN CONST LIST_ENTRY *CheckPackage
2697 )
2698 {
2699 LIST_ENTRY *Node1;
2700 UINTN Count;
2701
2702 if (CheckPackage == NULL) {
2703 return (0);
2704 }
2705
2706 for ( Node1 = GetFirstNode (CheckPackage), Count = 0
2707 ; !IsNull (CheckPackage, Node1)
2708 ; Node1 = GetNextNode (CheckPackage, Node1)
2709 )
2710 {
2711 if (((SHELL_PARAM_PACKAGE *)Node1)->Name == NULL) {
2712 Count++;
2713 }
2714 }
2715
2716 return (Count);
2717 }
2718
2719 /**
2720 Determines if a parameter is duplicated.
2721
2722 If Param is not NULL then it will point to a callee allocated string buffer
2723 with the parameter value if a duplicate is found.
2724
2725 If CheckPackage is NULL, then ASSERT.
2726
2727 @param[in] CheckPackage The package of parsed command line arguments.
2728 @param[out] Param Upon finding one, a pointer to the duplicated parameter.
2729
2730 @retval EFI_SUCCESS No parameters were duplicated.
2731 @retval EFI_DEVICE_ERROR A duplicate was found.
2732 **/
2733 EFI_STATUS
2734 EFIAPI
2735 ShellCommandLineCheckDuplicate (
2736 IN CONST LIST_ENTRY *CheckPackage,
2737 OUT CHAR16 **Param
2738 )
2739 {
2740 LIST_ENTRY *Node1;
2741 LIST_ENTRY *Node2;
2742
2743 ASSERT (CheckPackage != NULL);
2744
2745 for ( Node1 = GetFirstNode (CheckPackage)
2746 ; !IsNull (CheckPackage, Node1)
2747 ; Node1 = GetNextNode (CheckPackage, Node1)
2748 )
2749 {
2750 for ( Node2 = GetNextNode (CheckPackage, Node1)
2751 ; !IsNull (CheckPackage, Node2)
2752 ; Node2 = GetNextNode (CheckPackage, Node2)
2753 )
2754 {
2755 if ((((SHELL_PARAM_PACKAGE *)Node1)->Name != NULL) && (((SHELL_PARAM_PACKAGE *)Node2)->Name != NULL) && (StrCmp (((SHELL_PARAM_PACKAGE *)Node1)->Name, ((SHELL_PARAM_PACKAGE *)Node2)->Name) == 0)) {
2756 if (Param != NULL) {
2757 *Param = NULL;
2758 *Param = StrnCatGrow (Param, NULL, ((SHELL_PARAM_PACKAGE *)Node1)->Name, 0);
2759 }
2760
2761 return (EFI_DEVICE_ERROR);
2762 }
2763 }
2764 }
2765
2766 return (EFI_SUCCESS);
2767 }
2768
2769 /**
2770 This is a find and replace function. Upon successful return the NewString is a copy of
2771 SourceString with each instance of FindTarget replaced with ReplaceWith.
2772
2773 If SourceString and NewString overlap the behavior is undefined.
2774
2775 If the string would grow bigger than NewSize it will halt and return error.
2776
2777 @param[in] SourceString The string with source buffer.
2778 @param[in, out] NewString The string with resultant buffer.
2779 @param[in] NewSize The size in bytes of NewString.
2780 @param[in] FindTarget The string to look for.
2781 @param[in] ReplaceWith The string to replace FindTarget with.
2782 @param[in] SkipPreCarrot If TRUE will skip a FindTarget that has a '^'
2783 immediately before it.
2784 @param[in] ParameterReplacing If TRUE will add "" around items with spaces.
2785
2786 @retval EFI_INVALID_PARAMETER SourceString was NULL.
2787 @retval EFI_INVALID_PARAMETER NewString was NULL.
2788 @retval EFI_INVALID_PARAMETER FindTarget was NULL.
2789 @retval EFI_INVALID_PARAMETER ReplaceWith was NULL.
2790 @retval EFI_INVALID_PARAMETER FindTarget had length < 1.
2791 @retval EFI_INVALID_PARAMETER SourceString had length < 1.
2792 @retval EFI_BUFFER_TOO_SMALL NewSize was less than the minimum size to hold
2793 the new string (truncation occurred).
2794 @retval EFI_SUCCESS The string was successfully copied with replacement.
2795 **/
2796 EFI_STATUS
2797 EFIAPI
2798 ShellCopySearchAndReplace (
2799 IN CHAR16 CONST *SourceString,
2800 IN OUT CHAR16 *NewString,
2801 IN UINTN NewSize,
2802 IN CONST CHAR16 *FindTarget,
2803 IN CONST CHAR16 *ReplaceWith,
2804 IN CONST BOOLEAN SkipPreCarrot,
2805 IN CONST BOOLEAN ParameterReplacing
2806 )
2807 {
2808 UINTN Size;
2809 CHAR16 *Replace;
2810
2811 if ( (SourceString == NULL)
2812 || (NewString == NULL)
2813 || (FindTarget == NULL)
2814 || (ReplaceWith == NULL)
2815 || (StrLen (FindTarget) < 1)
2816 || (StrLen (SourceString) < 1)
2817 )
2818 {
2819 return (EFI_INVALID_PARAMETER);
2820 }
2821
2822 Replace = NULL;
2823 if ((StrStr (ReplaceWith, L" ") == NULL) || !ParameterReplacing) {
2824 Replace = StrnCatGrow (&Replace, NULL, ReplaceWith, 0);
2825 } else {
2826 Replace = AllocateZeroPool (StrSize (ReplaceWith) + 2*sizeof (CHAR16));
2827 if (Replace != NULL) {
2828 UnicodeSPrint (Replace, StrSize (ReplaceWith) + 2*sizeof (CHAR16), L"\"%s\"", ReplaceWith);
2829 }
2830 }
2831
2832 if (Replace == NULL) {
2833 return (EFI_OUT_OF_RESOURCES);
2834 }
2835
2836 NewString = ZeroMem (NewString, NewSize);
2837 while (*SourceString != CHAR_NULL) {
2838 //
2839 // if we find the FindTarget and either Skip == FALSE or Skip and we
2840 // dont have a carrot do a replace...
2841 //
2842 if ( (StrnCmp (SourceString, FindTarget, StrLen (FindTarget)) == 0)
2843 && ((SkipPreCarrot && (*(SourceString-1) != L'^')) || !SkipPreCarrot)
2844 )
2845 {
2846 SourceString += StrLen (FindTarget);
2847 Size = StrSize (NewString);
2848 if ((Size + (StrLen (Replace)*sizeof (CHAR16))) > NewSize) {
2849 FreePool (Replace);
2850 return (EFI_BUFFER_TOO_SMALL);
2851 }
2852
2853 StrCatS (NewString, NewSize/sizeof (CHAR16), Replace);
2854 } else {
2855 Size = StrSize (NewString);
2856 if (Size + sizeof (CHAR16) > NewSize) {
2857 FreePool (Replace);
2858 return (EFI_BUFFER_TOO_SMALL);
2859 }
2860
2861 StrnCatS (NewString, NewSize/sizeof (CHAR16), SourceString, 1);
2862 SourceString++;
2863 }
2864 }
2865
2866 FreePool (Replace);
2867 return (EFI_SUCCESS);
2868 }
2869
2870 /**
2871 Internal worker function to output a string.
2872
2873 This function will output a string to the correct StdOut.
2874
2875 @param[in] String The string to print out.
2876
2877 @retval EFI_SUCCESS The operation was sucessful.
2878 @retval !EFI_SUCCESS The operation failed.
2879 **/
2880 EFI_STATUS
2881 InternalPrintTo (
2882 IN CONST CHAR16 *String
2883 )
2884 {
2885 UINTN Size;
2886
2887 Size = StrSize (String) - sizeof (CHAR16);
2888 if (Size == 0) {
2889 return (EFI_SUCCESS);
2890 }
2891
2892 if (gEfiShellParametersProtocol != NULL) {
2893 return (gEfiShellProtocol->WriteFile (gEfiShellParametersProtocol->StdOut, &Size, (VOID *)String));
2894 }
2895
2896 if (mEfiShellInterface != NULL) {
2897 if (mEfiShellInterface->RedirArgc == 0) {
2898 //
2899 // Divide in half for old shell. Must be string length not size.
2900 //
2901 Size /= 2; // Divide in half only when no redirection.
2902 }
2903
2904 return (mEfiShellInterface->StdOut->Write (mEfiShellInterface->StdOut, &Size, (VOID *)String));
2905 }
2906
2907 ASSERT (FALSE);
2908 return (EFI_UNSUPPORTED);
2909 }
2910
2911 /**
2912 Print at a specific location on the screen.
2913
2914 This function will move the cursor to a given screen location and print the specified string
2915
2916 If -1 is specified for either the Row or Col the current screen location for BOTH
2917 will be used.
2918
2919 if either Row or Col is out of range for the current console, then ASSERT
2920 if Format is NULL, then ASSERT
2921
2922 In addition to the standard %-based flags as supported by UefiLib Print() this supports
2923 the following additional flags:
2924 %N - Set output attribute to normal
2925 %H - Set output attribute to highlight
2926 %E - Set output attribute to error
2927 %B - Set output attribute to blue color
2928 %V - Set output attribute to green color
2929
2930 Note: The background color is controlled by the shell command cls.
2931
2932 @param[in] Col the column to print at
2933 @param[in] Row the row to print at
2934 @param[in] Format the format string
2935 @param[in] Marker the marker for the variable argument list
2936
2937 @return EFI_SUCCESS The operation was successful.
2938 @return EFI_DEVICE_ERROR The console device reported an error.
2939 **/
2940 EFI_STATUS
2941 InternalShellPrintWorker (
2942 IN INT32 Col OPTIONAL,
2943 IN INT32 Row OPTIONAL,
2944 IN CONST CHAR16 *Format,
2945 IN VA_LIST Marker
2946 )
2947 {
2948 EFI_STATUS Status;
2949 CHAR16 *ResumeLocation;
2950 CHAR16 *FormatWalker;
2951 UINTN OriginalAttribute;
2952 CHAR16 *mPostReplaceFormat;
2953 CHAR16 *mPostReplaceFormat2;
2954
2955 mPostReplaceFormat = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));
2956 mPostReplaceFormat2 = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));
2957
2958 if ((mPostReplaceFormat == NULL) || (mPostReplaceFormat2 == NULL)) {
2959 SHELL_FREE_NON_NULL (mPostReplaceFormat);
2960 SHELL_FREE_NON_NULL (mPostReplaceFormat2);
2961 return (EFI_OUT_OF_RESOURCES);
2962 }
2963
2964 Status = EFI_SUCCESS;
2965 OriginalAttribute = gST->ConOut->Mode->Attribute;
2966
2967 //
2968 // Back and forth each time fixing up 1 of our flags...
2969 //
2970 Status = ShellCopySearchAndReplace (Format, mPostReplaceFormat, PcdGet16 (PcdShellPrintBufferSize), L"%N", L"%%N", FALSE, FALSE);
2971 ASSERT_EFI_ERROR (Status);
2972 Status = ShellCopySearchAndReplace (mPostReplaceFormat, mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%E", L"%%E", FALSE, FALSE);
2973 ASSERT_EFI_ERROR (Status);
2974 Status = ShellCopySearchAndReplace (mPostReplaceFormat2, mPostReplaceFormat, PcdGet16 (PcdShellPrintBufferSize), L"%H", L"%%H", FALSE, FALSE);
2975 ASSERT_EFI_ERROR (Status);
2976 Status = ShellCopySearchAndReplace (mPostReplaceFormat, mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), L"%B", L"%%B", FALSE, FALSE);
2977 ASSERT_EFI_ERROR (Status);
2978 Status = ShellCopySearchAndReplace (mPostReplaceFormat2, mPostReplaceFormat, PcdGet16 (PcdShellPrintBufferSize), L"%V", L"%%V", FALSE, FALSE);
2979 ASSERT_EFI_ERROR (Status);
2980
2981 //
2982 // Use the last buffer from replacing to print from...
2983 //
2984 UnicodeVSPrint (mPostReplaceFormat2, PcdGet16 (PcdShellPrintBufferSize), mPostReplaceFormat, Marker);
2985
2986 if ((Col != -1) && (Row != -1)) {
2987 Status = gST->ConOut->SetCursorPosition (gST->ConOut, Col, Row);
2988 }
2989
2990 FormatWalker = mPostReplaceFormat2;
2991 while (*FormatWalker != CHAR_NULL) {
2992 //
2993 // Find the next attribute change request
2994 //
2995 ResumeLocation = StrStr (FormatWalker, L"%");
2996 if (ResumeLocation != NULL) {
2997 *ResumeLocation = CHAR_NULL;
2998 }
2999
3000 //
3001 // print the current FormatWalker string
3002 //
3003 if (StrLen (FormatWalker) > 0) {
3004 Status = InternalPrintTo (FormatWalker);
3005 if (EFI_ERROR (Status)) {
3006 break;
3007 }
3008 }
3009
3010 //
3011 // update the attribute
3012 //
3013 if (ResumeLocation != NULL) {
3014 if ((ResumeLocation != mPostReplaceFormat2) && (*(ResumeLocation-1) == L'^')) {
3015 //
3016 // Move cursor back 1 position to overwrite the ^
3017 //
3018 gST->ConOut->SetCursorPosition (gST->ConOut, gST->ConOut->Mode->CursorColumn - 1, gST->ConOut->Mode->CursorRow);
3019
3020 //
3021 // Print a simple '%' symbol
3022 //
3023 Status = InternalPrintTo (L"%");
3024 ResumeLocation = ResumeLocation - 1;
3025 } else {
3026 switch (*(ResumeLocation+1)) {
3027 case (L'N'):
3028 gST->ConOut->SetAttribute (gST->ConOut, OriginalAttribute);
3029 break;
3030 case (L'E'):
3031 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_YELLOW, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
3032 break;
3033 case (L'H'):
3034 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_WHITE, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
3035 break;
3036 case (L'B'):
3037 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTBLUE, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
3038 break;
3039 case (L'V'):
3040 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGREEN, ((OriginalAttribute&(BIT4|BIT5|BIT6))>>4)));
3041 break;
3042 default:
3043 //
3044 // Print a simple '%' symbol
3045 //
3046 Status = InternalPrintTo (L"%");
3047 if (EFI_ERROR (Status)) {
3048 break;
3049 }
3050
3051 ResumeLocation = ResumeLocation - 1;
3052 break;
3053 }
3054 }
3055 } else {
3056 //
3057 // reset to normal now...
3058 //
3059 break;
3060 }
3061
3062 //
3063 // update FormatWalker to Resume + 2 (skip the % and the indicator)
3064 //
3065 FormatWalker = ResumeLocation + 2;
3066 }
3067
3068 gST->ConOut->SetAttribute (gST->ConOut, OriginalAttribute);
3069
3070 SHELL_FREE_NON_NULL (mPostReplaceFormat);
3071 SHELL_FREE_NON_NULL (mPostReplaceFormat2);
3072 return (Status);
3073 }
3074
3075 /**
3076 Print at a specific location on the screen.
3077
3078 This function will move the cursor to a given screen location and print the specified string.
3079
3080 If -1 is specified for either the Row or Col the current screen location for BOTH
3081 will be used.
3082
3083 If either Row or Col is out of range for the current console, then ASSERT.
3084 If Format is NULL, then ASSERT.
3085
3086 In addition to the standard %-based flags as supported by UefiLib Print() this supports
3087 the following additional flags:
3088 %N - Set output attribute to normal
3089 %H - Set output attribute to highlight
3090 %E - Set output attribute to error
3091 %B - Set output attribute to blue color
3092 %V - Set output attribute to green color
3093
3094 Note: The background color is controlled by the shell command cls.
3095
3096 @param[in] Col the column to print at
3097 @param[in] Row the row to print at
3098 @param[in] Format the format string
3099 @param[in] ... The variable argument list.
3100
3101 @return EFI_SUCCESS The printing was successful.
3102 @return EFI_DEVICE_ERROR The console device reported an error.
3103 **/
3104 EFI_STATUS
3105 EFIAPI
3106 ShellPrintEx (
3107 IN INT32 Col OPTIONAL,
3108 IN INT32 Row OPTIONAL,
3109 IN CONST CHAR16 *Format,
3110 ...
3111 )
3112 {
3113 VA_LIST Marker;
3114 EFI_STATUS RetVal;
3115
3116 if (Format == NULL) {
3117 return (EFI_INVALID_PARAMETER);
3118 }
3119
3120 VA_START (Marker, Format);
3121 RetVal = InternalShellPrintWorker (Col, Row, Format, Marker);
3122 VA_END (Marker);
3123 return (RetVal);
3124 }
3125
3126 /**
3127 Print at a specific location on the screen.
3128
3129 This function will move the cursor to a given screen location and print the specified string.
3130
3131 If -1 is specified for either the Row or Col the current screen location for BOTH
3132 will be used.
3133
3134 If either Row or Col is out of range for the current console, then ASSERT.
3135 If Format is NULL, then ASSERT.
3136
3137 In addition to the standard %-based flags as supported by UefiLib Print() this supports
3138 the following additional flags:
3139 %N - Set output attribute to normal.
3140 %H - Set output attribute to highlight.
3141 %E - Set output attribute to error.
3142 %B - Set output attribute to blue color.
3143 %V - Set output attribute to green color.
3144
3145 Note: The background color is controlled by the shell command cls.
3146
3147 @param[in] Col The column to print at.
3148 @param[in] Row The row to print at.
3149 @param[in] Language The language of the string to retrieve. If this parameter
3150 is NULL, then the current platform language is used.
3151 @param[in] HiiFormatStringId The format string Id for getting from Hii.
3152 @param[in] HiiFormatHandle The format string Handle for getting from Hii.
3153 @param[in] ... The variable argument list.
3154
3155 @return EFI_SUCCESS The printing was successful.
3156 @return EFI_DEVICE_ERROR The console device reported an error.
3157 **/
3158 EFI_STATUS
3159 EFIAPI
3160 ShellPrintHiiEx (
3161 IN INT32 Col OPTIONAL,
3162 IN INT32 Row OPTIONAL,
3163 IN CONST CHAR8 *Language OPTIONAL,
3164 IN CONST EFI_STRING_ID HiiFormatStringId,
3165 IN CONST EFI_HII_HANDLE HiiFormatHandle,
3166 ...
3167 )
3168 {
3169 VA_LIST Marker;
3170 CHAR16 *HiiFormatString;
3171 EFI_STATUS RetVal;
3172
3173 RetVal = EFI_DEVICE_ERROR;
3174
3175 VA_START (Marker, HiiFormatHandle);
3176 HiiFormatString = HiiGetString (HiiFormatHandle, HiiFormatStringId, Language);
3177 if (HiiFormatString != NULL) {
3178 RetVal = InternalShellPrintWorker (Col, Row, HiiFormatString, Marker);
3179 SHELL_FREE_NON_NULL (HiiFormatString);
3180 }
3181
3182 VA_END (Marker);
3183
3184 return (RetVal);
3185 }
3186
3187 /**
3188 Function to determine if a given filename represents a file or a directory.
3189
3190 @param[in] DirName Path to directory to test.
3191
3192 @retval EFI_SUCCESS The Path represents a directory
3193 @retval EFI_NOT_FOUND The Path does not represent a directory
3194 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
3195 @return The path failed to open
3196 **/
3197 EFI_STATUS
3198 EFIAPI
3199 ShellIsDirectory (
3200 IN CONST CHAR16 *DirName
3201 )
3202 {
3203 EFI_STATUS Status;
3204 SHELL_FILE_HANDLE Handle;
3205 CHAR16 *TempLocation;
3206 CHAR16 *TempLocation2;
3207
3208 ASSERT (DirName != NULL);
3209
3210 Handle = NULL;
3211 TempLocation = NULL;
3212
3213 Status = ShellOpenFileByName (DirName, &Handle, EFI_FILE_MODE_READ, 0);
3214 if (EFI_ERROR (Status)) {
3215 //
3216 // try good logic first.
3217 //
3218 if (gEfiShellProtocol != NULL) {
3219 TempLocation = StrnCatGrow (&TempLocation, NULL, DirName, 0);
3220 if (TempLocation == NULL) {
3221 ShellCloseFile (&Handle);
3222 return (EFI_OUT_OF_RESOURCES);
3223 }
3224
3225 TempLocation2 = StrStr (TempLocation, L":");
3226 if ((TempLocation2 != NULL) && (StrLen (StrStr (TempLocation, L":")) == 2)) {
3227 *(TempLocation2+1) = CHAR_NULL;
3228 }
3229
3230 if (gEfiShellProtocol->GetDevicePathFromMap (TempLocation) != NULL) {
3231 FreePool (TempLocation);
3232 return (EFI_SUCCESS);
3233 }
3234
3235 FreePool (TempLocation);
3236 } else {
3237 //
3238 // probably a map name?!?!!?
3239 //
3240 TempLocation = StrStr (DirName, L"\\");
3241 if ((TempLocation != NULL) && (*(TempLocation+1) == CHAR_NULL)) {
3242 return (EFI_SUCCESS);
3243 }
3244 }
3245
3246 return (Status);
3247 }
3248
3249 if (FileHandleIsDirectory (Handle) == EFI_SUCCESS) {
3250 ShellCloseFile (&Handle);
3251 return (EFI_SUCCESS);
3252 }
3253
3254 ShellCloseFile (&Handle);
3255 return (EFI_NOT_FOUND);
3256 }
3257
3258 /**
3259 Function to determine if a given filename represents a file.
3260
3261 @param[in] Name Path to file to test.
3262
3263 @retval EFI_SUCCESS The Path represents a file.
3264 @retval EFI_NOT_FOUND The Path does not represent a file.
3265 @retval other The path failed to open.
3266 **/
3267 EFI_STATUS
3268 EFIAPI
3269 ShellIsFile (
3270 IN CONST CHAR16 *Name
3271 )
3272 {
3273 EFI_STATUS Status;
3274 SHELL_FILE_HANDLE Handle;
3275
3276 ASSERT (Name != NULL);
3277
3278 Handle = NULL;
3279
3280 Status = ShellOpenFileByName (Name, &Handle, EFI_FILE_MODE_READ, 0);
3281 if (EFI_ERROR (Status)) {
3282 return (Status);
3283 }
3284
3285 if (FileHandleIsDirectory (Handle) != EFI_SUCCESS) {
3286 ShellCloseFile (&Handle);
3287 return (EFI_SUCCESS);
3288 }
3289
3290 ShellCloseFile (&Handle);
3291 return (EFI_NOT_FOUND);
3292 }
3293
3294 /**
3295 Function to determine if a given filename represents a file.
3296
3297 This will search the CWD and then the Path.
3298
3299 If Name is NULL, then ASSERT.
3300
3301 @param[in] Name Path to file to test.
3302
3303 @retval EFI_SUCCESS The Path represents a file.
3304 @retval EFI_NOT_FOUND The Path does not represent a file.
3305 @retval other The path failed to open.
3306 **/
3307 EFI_STATUS
3308 EFIAPI
3309 ShellIsFileInPath (
3310 IN CONST CHAR16 *Name
3311 )
3312 {
3313 CHAR16 *NewName;
3314 EFI_STATUS Status;
3315
3316 if (!EFI_ERROR (ShellIsFile (Name))) {
3317 return (EFI_SUCCESS);
3318 }
3319
3320 NewName = ShellFindFilePath (Name);
3321 if (NewName == NULL) {
3322 return (EFI_NOT_FOUND);
3323 }
3324
3325 Status = ShellIsFile (NewName);
3326 FreePool (NewName);
3327 return (Status);
3328 }
3329
3330 /**
3331 Function return the number converted from a hex representation of a number.
3332
3333 Note: this function cannot be used when (UINTN)(-1), (0xFFFFFFFF) may be a valid
3334 result. Use ShellConvertStringToUint64 instead.
3335
3336 @param[in] String String representation of a number.
3337
3338 @return The unsigned integer result of the conversion.
3339 @retval (UINTN)(-1) An error occurred.
3340 **/
3341 UINTN
3342 EFIAPI
3343 ShellHexStrToUintn (
3344 IN CONST CHAR16 *String
3345 )
3346 {
3347 UINT64 RetVal;
3348
3349 if (!EFI_ERROR (ShellConvertStringToUint64 (String, &RetVal, TRUE, TRUE))) {
3350 return ((UINTN)RetVal);
3351 }
3352
3353 return ((UINTN)(-1));
3354 }
3355
3356 /**
3357 Function to determine whether a string is decimal or hex representation of a number
3358 and return the number converted from the string. Spaces are always skipped.
3359
3360 @param[in] String String representation of a number
3361
3362 @return the number
3363 @retval (UINTN)(-1) An error ocurred.
3364 **/
3365 UINTN
3366 EFIAPI
3367 ShellStrToUintn (
3368 IN CONST CHAR16 *String
3369 )
3370 {
3371 UINT64 RetVal;
3372 BOOLEAN Hex;
3373
3374 Hex = FALSE;
3375
3376 if (!InternalShellIsHexOrDecimalNumber (String, Hex, TRUE, FALSE)) {
3377 Hex = TRUE;
3378 }
3379
3380 if (!EFI_ERROR (ShellConvertStringToUint64 (String, &RetVal, Hex, TRUE))) {
3381 return ((UINTN)RetVal);
3382 }
3383
3384 return ((UINTN)(-1));
3385 }
3386
3387 /**
3388 Safely append with automatic string resizing given length of Destination and
3389 desired length of copy from Source.
3390
3391 append the first D characters of Source to the end of Destination, where D is
3392 the lesser of Count and the StrLen() of Source. If appending those D characters
3393 will fit within Destination (whose Size is given as CurrentSize) and
3394 still leave room for a NULL terminator, then those characters are appended,
3395 starting at the original terminating NULL of Destination, and a new terminating
3396 NULL is appended.
3397
3398 If appending D characters onto Destination will result in a overflow of the size
3399 given in CurrentSize the string will be grown such that the copy can be performed
3400 and CurrentSize will be updated to the new size.
3401
3402 If Source is NULL, there is nothing to append, just return the current buffer in
3403 Destination.
3404
3405 if Destination is NULL, then ASSERT()
3406 if Destination's current length (including NULL terminator) is already more then
3407 CurrentSize, then ASSERT()
3408
3409 @param[in, out] Destination The String to append onto
3410 @param[in, out] CurrentSize on call the number of bytes in Destination. On
3411 return possibly the new size (still in bytes). if NULL
3412 then allocate whatever is needed.
3413 @param[in] Source The String to append from
3414 @param[in] Count Maximum number of characters to append. if 0 then
3415 all are appended.
3416
3417 @return Destination return the resultant string.
3418 **/
3419 CHAR16 *
3420 EFIAPI
3421 StrnCatGrow (
3422 IN OUT CHAR16 **Destination,
3423 IN OUT UINTN *CurrentSize,
3424 IN CONST CHAR16 *Source,
3425 IN UINTN Count
3426 )
3427 {
3428 UINTN DestinationStartSize;
3429 UINTN NewSize;
3430
3431 //
3432 // ASSERTs
3433 //
3434 ASSERT (Destination != NULL);
3435
3436 //
3437 // If there's nothing to do then just return Destination
3438 //
3439 if (Source == NULL) {
3440 return (*Destination);
3441 }
3442
3443 //
3444 // allow for un-initialized pointers, based on size being 0
3445 //
3446 if ((CurrentSize != NULL) && (*CurrentSize == 0)) {
3447 *Destination = NULL;
3448 }
3449
3450 //
3451 // allow for NULL pointers address as Destination
3452 //
3453 if (*Destination != NULL) {
3454 ASSERT (CurrentSize != 0);
3455 DestinationStartSize = StrSize (*Destination);
3456 ASSERT (DestinationStartSize <= *CurrentSize);
3457 } else {
3458 DestinationStartSize = 0;
3459 // ASSERT(*CurrentSize == 0);
3460 }
3461
3462 //
3463 // Append all of Source?
3464 //
3465 if (Count == 0) {
3466 Count = StrLen (Source);
3467 }
3468
3469 //
3470 // Test and grow if required
3471 //
3472 if (CurrentSize != NULL) {
3473 NewSize = *CurrentSize;
3474 if (NewSize < DestinationStartSize + (Count * sizeof (CHAR16))) {
3475 while (NewSize < (DestinationStartSize + (Count*sizeof (CHAR16)))) {
3476 NewSize += 2 * Count * sizeof (CHAR16);
3477 }
3478
3479 *Destination = ReallocatePool (*CurrentSize, NewSize, *Destination);
3480 *CurrentSize = NewSize;
3481 }
3482 } else {
3483 NewSize = (Count+1)*sizeof (CHAR16);
3484 *Destination = AllocateZeroPool (NewSize);
3485 }
3486
3487 //
3488 // Now use standard StrnCat on a big enough buffer
3489 //
3490 if (*Destination == NULL) {
3491 return (NULL);
3492 }
3493
3494 StrnCatS (*Destination, NewSize/sizeof (CHAR16), Source, Count);
3495 return *Destination;
3496 }
3497
3498 /**
3499 Prompt the user and return the resultant answer to the requestor.
3500
3501 This function will display the requested question on the shell prompt and then
3502 wait for an appropriate answer to be input from the console.
3503
3504 if the SHELL_PROMPT_REQUEST_TYPE is SHELL_PROMPT_REQUEST_TYPE_YESNO, ShellPromptResponseTypeQuitContinue
3505 or SHELL_PROMPT_REQUEST_TYPE_YESNOCANCEL then *Response is of type SHELL_PROMPT_RESPONSE.
3506
3507 if the SHELL_PROMPT_REQUEST_TYPE is ShellPromptResponseTypeFreeform then *Response is of type
3508 CHAR16*.
3509
3510 In either case *Response must be callee freed if Response was not NULL;
3511
3512 @param Type What type of question is asked. This is used to filter the input
3513 to prevent invalid answers to question.
3514 @param Prompt Pointer to string prompt to use to request input.
3515 @param Response Pointer to Response which will be populated upon return.
3516
3517 @retval EFI_SUCCESS The operation was sucessful.
3518 @retval EFI_UNSUPPORTED The operation is not supported as requested.
3519 @retval EFI_INVALID_PARAMETER A parameter was invalid.
3520 @return other The operation failed.
3521 **/
3522 EFI_STATUS
3523 EFIAPI
3524 ShellPromptForResponse (
3525 IN SHELL_PROMPT_REQUEST_TYPE Type,
3526 IN CHAR16 *Prompt OPTIONAL,
3527 IN OUT VOID **Response OPTIONAL
3528 )
3529 {
3530 EFI_STATUS Status;
3531 EFI_INPUT_KEY Key;
3532 UINTN EventIndex;
3533 SHELL_PROMPT_RESPONSE *Resp;
3534 UINTN Size;
3535 CHAR16 *Buffer;
3536
3537 Status = EFI_UNSUPPORTED;
3538 Resp = NULL;
3539 Buffer = NULL;
3540 Size = 0;
3541 if (Type != ShellPromptResponseTypeFreeform) {
3542 Resp = (SHELL_PROMPT_RESPONSE *)AllocateZeroPool (sizeof (SHELL_PROMPT_RESPONSE));
3543 if (Resp == NULL) {
3544 if (Response != NULL) {
3545 *Response = NULL;
3546 }
3547
3548 return (EFI_OUT_OF_RESOURCES);
3549 }
3550 }
3551
3552 switch (Type) {
3553 case ShellPromptResponseTypeQuitContinue:
3554 if (Prompt != NULL) {
3555 ShellPrintEx (-1, -1, L"%s", Prompt);
3556 }
3557
3558 //
3559 // wait for valid response
3560 //
3561 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
3562 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3563 if (EFI_ERROR (Status)) {
3564 break;
3565 }
3566
3567 ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
3568 if ((Key.UnicodeChar == L'Q') || (Key.UnicodeChar == L'q')) {
3569 *Resp = ShellPromptResponseQuit;
3570 } else {
3571 *Resp = ShellPromptResponseContinue;
3572 }
3573
3574 break;
3575 case ShellPromptResponseTypeYesNoCancel:
3576 if (Prompt != NULL) {
3577 ShellPrintEx (-1, -1, L"%s", Prompt);
3578 }
3579
3580 //
3581 // wait for valid response
3582 //
3583 *Resp = ShellPromptResponseMax;
3584 while (*Resp == ShellPromptResponseMax) {
3585 if (ShellGetExecutionBreakFlag ()) {
3586 Status = EFI_ABORTED;
3587 break;
3588 }
3589
3590 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
3591 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3592 if (EFI_ERROR (Status)) {
3593 break;
3594 }
3595
3596 ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
3597 switch (Key.UnicodeChar) {
3598 case L'Y':
3599 case L'y':
3600 *Resp = ShellPromptResponseYes;
3601 break;
3602 case L'N':
3603 case L'n':
3604 *Resp = ShellPromptResponseNo;
3605 break;
3606 case L'C':
3607 case L'c':
3608 *Resp = ShellPromptResponseCancel;
3609 break;
3610 }
3611 }
3612
3613 break;
3614 case ShellPromptResponseTypeYesNoAllCancel:
3615 if (Prompt != NULL) {
3616 ShellPrintEx (-1, -1, L"%s", Prompt);
3617 }
3618
3619 //
3620 // wait for valid response
3621 //
3622 *Resp = ShellPromptResponseMax;
3623 while (*Resp == ShellPromptResponseMax) {
3624 if (ShellGetExecutionBreakFlag ()) {
3625 Status = EFI_ABORTED;
3626 break;
3627 }
3628
3629 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
3630 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3631 if (EFI_ERROR (Status)) {
3632 break;
3633 }
3634
3635 if ((Key.UnicodeChar <= 127) && (Key.UnicodeChar >= 32)) {
3636 ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
3637 }
3638
3639 switch (Key.UnicodeChar) {
3640 case L'Y':
3641 case L'y':
3642 *Resp = ShellPromptResponseYes;
3643 break;
3644 case L'N':
3645 case L'n':
3646 *Resp = ShellPromptResponseNo;
3647 break;
3648 case L'A':
3649 case L'a':
3650 *Resp = ShellPromptResponseAll;
3651 break;
3652 case L'C':
3653 case L'c':
3654 *Resp = ShellPromptResponseCancel;
3655 break;
3656 }
3657 }
3658
3659 break;
3660 case ShellPromptResponseTypeEnterContinue:
3661 case ShellPromptResponseTypeAnyKeyContinue:
3662 if (Prompt != NULL) {
3663 ShellPrintEx (-1, -1, L"%s", Prompt);
3664 }
3665
3666 //
3667 // wait for valid response
3668 //
3669 *Resp = ShellPromptResponseMax;
3670 while (*Resp == ShellPromptResponseMax) {
3671 if (ShellGetExecutionBreakFlag ()) {
3672 Status = EFI_ABORTED;
3673 break;
3674 }
3675
3676 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
3677 if (Type == ShellPromptResponseTypeEnterContinue) {
3678 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3679 if (EFI_ERROR (Status)) {
3680 break;
3681 }
3682
3683 ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
3684 if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
3685 *Resp = ShellPromptResponseContinue;
3686 break;
3687 }
3688 }
3689
3690 if (Type == ShellPromptResponseTypeAnyKeyContinue) {
3691 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3692 ASSERT_EFI_ERROR (Status);
3693 *Resp = ShellPromptResponseContinue;
3694 break;
3695 }
3696 }
3697
3698 break;
3699 case ShellPromptResponseTypeYesNo:
3700 if (Prompt != NULL) {
3701 ShellPrintEx (-1, -1, L"%s", Prompt);
3702 }
3703
3704 //
3705 // wait for valid response
3706 //
3707 *Resp = ShellPromptResponseMax;
3708 while (*Resp == ShellPromptResponseMax) {
3709 if (ShellGetExecutionBreakFlag ()) {
3710 Status = EFI_ABORTED;
3711 break;
3712 }
3713
3714 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
3715 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3716 if (EFI_ERROR (Status)) {
3717 break;
3718 }
3719
3720 ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
3721 switch (Key.UnicodeChar) {
3722 case L'Y':
3723 case L'y':
3724 *Resp = ShellPromptResponseYes;
3725 break;
3726 case L'N':
3727 case L'n':
3728 *Resp = ShellPromptResponseNo;
3729 break;
3730 }
3731 }
3732
3733 break;
3734 case ShellPromptResponseTypeFreeform:
3735 if (Prompt != NULL) {
3736 ShellPrintEx (-1, -1, L"%s", Prompt);
3737 }
3738
3739 while (1) {
3740 if (ShellGetExecutionBreakFlag ()) {
3741 Status = EFI_ABORTED;
3742 break;
3743 }
3744
3745 gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &EventIndex);
3746 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3747 if (EFI_ERROR (Status)) {
3748 break;
3749 }
3750
3751 ShellPrintEx (-1, -1, L"%c", Key.UnicodeChar);
3752 if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
3753 break;
3754 }
3755
3756 ASSERT ((Buffer == NULL && Size == 0) || (Buffer != NULL));
3757 StrnCatGrow (&Buffer, &Size, &Key.UnicodeChar, 1);
3758 }
3759
3760 break;
3761 //
3762 // This is the location to add new prompt types.
3763 // If your new type loops remember to add ExecutionBreak support.
3764 //
3765 default:
3766 ASSERT (FALSE);
3767 }
3768
3769 if (Response != NULL) {
3770 if (Resp != NULL) {
3771 *Response = Resp;
3772 } else if (Buffer != NULL) {
3773 *Response = Buffer;
3774 } else {
3775 *Response = NULL;
3776 }
3777 } else {
3778 if (Resp != NULL) {
3779 FreePool (Resp);
3780 }
3781
3782 if (Buffer != NULL) {
3783 FreePool (Buffer);
3784 }
3785 }
3786
3787 ShellPrintEx (-1, -1, L"\r\n");
3788 return (Status);
3789 }
3790
3791 /**
3792 Prompt the user and return the resultant answer to the requestor.
3793
3794 This function is the same as ShellPromptForResponse, except that the prompt is
3795 automatically pulled from HII.
3796
3797 @param Type What type of question is asked. This is used to filter the input
3798 to prevent invalid answers to question.
3799 @param[in] HiiFormatStringId The format string Id for getting from Hii.
3800 @param[in] HiiFormatHandle The format string Handle for getting from Hii.
3801 @param Response Pointer to Response which will be populated upon return.
3802
3803 @retval EFI_SUCCESS the operation was sucessful.
3804 @return other the operation failed.
3805
3806 @sa ShellPromptForResponse
3807 **/
3808 EFI_STATUS
3809 EFIAPI
3810 ShellPromptForResponseHii (
3811 IN SHELL_PROMPT_REQUEST_TYPE Type,
3812 IN CONST EFI_STRING_ID HiiFormatStringId,
3813 IN CONST EFI_HII_HANDLE HiiFormatHandle,
3814 IN OUT VOID **Response
3815 )
3816 {
3817 CHAR16 *Prompt;
3818 EFI_STATUS Status;
3819
3820 Prompt = HiiGetString (HiiFormatHandle, HiiFormatStringId, NULL);
3821 Status = ShellPromptForResponse (Type, Prompt, Response);
3822 FreePool (Prompt);
3823 return (Status);
3824 }
3825
3826 /**
3827 Function to determin if an entire string is a valid number.
3828
3829 If Hex it must be preceeded with a 0x or has ForceHex, set TRUE.
3830
3831 @param[in] String The string to evaluate.
3832 @param[in] ForceHex TRUE - always assume hex.
3833 @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going.
3834 @param[in] TimeNumbers TRUE to allow numbers with ":", FALSE otherwise.
3835
3836 @retval TRUE It is all numeric (dec/hex) characters.
3837 @retval FALSE There is a non-numeric character.
3838 **/
3839 BOOLEAN
3840 InternalShellIsHexOrDecimalNumber (
3841 IN CONST CHAR16 *String,
3842 IN CONST BOOLEAN ForceHex,
3843 IN CONST BOOLEAN StopAtSpace,
3844 IN CONST BOOLEAN TimeNumbers
3845 )
3846 {
3847 BOOLEAN Hex;
3848 BOOLEAN LeadingZero;
3849
3850 if (String == NULL) {
3851 return FALSE;
3852 }
3853
3854 //
3855 // chop off a single negative sign
3856 //
3857 if (*String == L'-') {
3858 String++;
3859 }
3860
3861 if (*String == CHAR_NULL) {
3862 return FALSE;
3863 }
3864
3865 //
3866 // chop leading zeroes
3867 //
3868 LeadingZero = FALSE;
3869 while (*String == L'0') {
3870 String++;
3871 LeadingZero = TRUE;
3872 }
3873
3874 //
3875 // allow '0x' or '0X', but not 'x' or 'X'
3876 //
3877 if ((*String == L'x') || (*String == L'X')) {
3878 if (!LeadingZero) {
3879 //
3880 // we got an x without a preceeding 0
3881 //
3882 return (FALSE);
3883 }
3884
3885 String++;
3886 Hex = TRUE;
3887 } else if (ForceHex) {
3888 Hex = TRUE;
3889 } else {
3890 Hex = FALSE;
3891 }
3892
3893 //
3894 // loop through the remaining characters and use the lib function
3895 //
3896 for ( ; *String != CHAR_NULL && !(StopAtSpace && *String == L' '); String++) {
3897 if (TimeNumbers && (String[0] == L':')) {
3898 continue;
3899 }
3900
3901 if (Hex) {
3902 if (!ShellIsHexaDecimalDigitCharacter (*String)) {
3903 return (FALSE);
3904 }
3905 } else {
3906 if (!ShellIsDecimalDigitCharacter (*String)) {
3907 return (FALSE);
3908 }
3909 }
3910 }
3911
3912 return (TRUE);
3913 }
3914
3915 /**
3916 Function to determine if a given filename exists.
3917
3918 @param[in] Name Path to test.
3919
3920 @retval EFI_SUCCESS The Path represents a file.
3921 @retval EFI_NOT_FOUND The Path does not represent a file.
3922 @retval other The path failed to open.
3923 **/
3924 EFI_STATUS
3925 EFIAPI
3926 ShellFileExists (
3927 IN CONST CHAR16 *Name
3928 )
3929 {
3930 EFI_STATUS Status;
3931 EFI_SHELL_FILE_INFO *List;
3932
3933 ASSERT (Name != NULL);
3934
3935 List = NULL;
3936 Status = ShellOpenFileMetaArg ((CHAR16 *)Name, EFI_FILE_MODE_READ, &List);
3937 if (EFI_ERROR (Status)) {
3938 return (Status);
3939 }
3940
3941 ShellCloseFileMetaArg (&List);
3942
3943 return (EFI_SUCCESS);
3944 }
3945
3946 /**
3947 Convert a Unicode character to numerical value.
3948
3949 This internal function only deal with Unicode character
3950 which maps to a valid hexadecimal ASII character, i.e.
3951 L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other
3952 Unicode character, the value returned does not make sense.
3953
3954 @param Char The character to convert.
3955
3956 @return The numerical value converted.
3957
3958 **/
3959 UINTN
3960 InternalShellHexCharToUintn (
3961 IN CHAR16 Char
3962 )
3963 {
3964 if (ShellIsDecimalDigitCharacter (Char)) {
3965 return Char - L'0';
3966 }
3967
3968 return (10 + CharToUpper (Char) - L'A');
3969 }
3970
3971 /**
3972 Convert a Null-terminated Unicode hexadecimal string to a value of type UINT64.
3973
3974 This function returns a value of type UINT64 by interpreting the contents
3975 of the Unicode string specified by String as a hexadecimal number.
3976 The format of the input Unicode string String is:
3977
3978 [spaces][zeros][x][hexadecimal digits].
3979
3980 The valid hexadecimal digit character is in the range [0-9], [a-f] and [A-F].
3981 The prefix "0x" is optional. Both "x" and "X" is allowed in "0x" prefix.
3982 If "x" appears in the input string, it must be prefixed with at least one 0.
3983 The function will ignore the pad space, which includes spaces or tab characters,
3984 before [zeros], [x] or [hexadecimal digit]. The running zero before [x] or
3985 [hexadecimal digit] will be ignored. Then, the decoding starts after [x] or the
3986 first valid hexadecimal digit. Then, the function stops at the first character that is
3987 a not a valid hexadecimal character or NULL, whichever one comes first.
3988
3989 If String has only pad spaces, then zero is returned.
3990 If String has no leading pad spaces, leading zeros or valid hexadecimal digits,
3991 then zero is returned.
3992
3993 @param[in] String A pointer to a Null-terminated Unicode string.
3994 @param[out] Value Upon a successful return the value of the conversion.
3995 @param[in] StopAtSpace FALSE to skip spaces.
3996
3997 @retval EFI_SUCCESS The conversion was successful.
3998 @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid.
3999 @retval EFI_DEVICE_ERROR An overflow occurred.
4000 **/
4001 EFI_STATUS
4002 InternalShellStrHexToUint64 (
4003 IN CONST CHAR16 *String,
4004 OUT UINT64 *Value,
4005 IN CONST BOOLEAN StopAtSpace
4006 )
4007 {
4008 UINT64 Result;
4009
4010 if ((String == NULL) || (StrSize (String) == 0) || (Value == NULL)) {
4011 return (EFI_INVALID_PARAMETER);
4012 }
4013
4014 //
4015 // Ignore the pad spaces (space or tab)
4016 //
4017 while ((*String == L' ') || (*String == L'\t')) {
4018 String++;
4019 }
4020
4021 //
4022 // Ignore leading Zeros after the spaces
4023 //
4024 while (*String == L'0') {
4025 String++;
4026 }
4027
4028 if (CharToUpper (*String) == L'X') {
4029 if (*(String - 1) != L'0') {
4030 return 0;
4031 }
4032
4033 //
4034 // Skip the 'X'
4035 //
4036 String++;
4037 }
4038
4039 Result = 0;
4040
4041 //
4042 // there is a space where there should't be
4043 //
4044 if (*String == L' ') {
4045 return (EFI_INVALID_PARAMETER);
4046 }
4047
4048 while (ShellIsHexaDecimalDigitCharacter (*String)) {
4049 //
4050 // If the Hex Number represented by String overflows according
4051 // to the range defined by UINT64, then return EFI_DEVICE_ERROR.
4052 //
4053 if (!(Result <= (RShiftU64 ((((UINT64) ~0) - InternalShellHexCharToUintn (*String)), 4)))) {
4054 // if (!(Result <= ((((UINT64) ~0) - InternalShellHexCharToUintn (*String)) >> 4))) {
4055 return (EFI_DEVICE_ERROR);
4056 }
4057
4058 Result = (LShiftU64 (Result, 4));
4059 Result += InternalShellHexCharToUintn (*String);
4060 String++;
4061
4062 //
4063 // stop at spaces if requested
4064 //
4065 if (StopAtSpace && (*String == L' ')) {
4066 break;
4067 }
4068 }
4069
4070 *Value = Result;
4071 return (EFI_SUCCESS);
4072 }
4073
4074 /**
4075 Convert a Null-terminated Unicode decimal string to a value of
4076 type UINT64.
4077
4078 This function returns a value of type UINT64 by interpreting the contents
4079 of the Unicode string specified by String as a decimal number. The format
4080 of the input Unicode string String is:
4081
4082 [spaces] [decimal digits].
4083
4084 The valid decimal digit character is in the range [0-9]. The
4085 function will ignore the pad space, which includes spaces or
4086 tab characters, before [decimal digits]. The running zero in the
4087 beginning of [decimal digits] will be ignored. Then, the function
4088 stops at the first character that is a not a valid decimal character
4089 or a Null-terminator, whichever one comes first.
4090
4091 If String has only pad spaces, then 0 is returned.
4092 If String has no pad spaces or valid decimal digits,
4093 then 0 is returned.
4094
4095 @param[in] String A pointer to a Null-terminated Unicode string.
4096 @param[out] Value Upon a successful return the value of the conversion.
4097 @param[in] StopAtSpace FALSE to skip spaces.
4098
4099 @retval EFI_SUCCESS The conversion was successful.
4100 @retval EFI_INVALID_PARAMETER A parameter was NULL or invalid.
4101 @retval EFI_DEVICE_ERROR An overflow occurred.
4102 **/
4103 EFI_STATUS
4104 InternalShellStrDecimalToUint64 (
4105 IN CONST CHAR16 *String,
4106 OUT UINT64 *Value,
4107 IN CONST BOOLEAN StopAtSpace
4108 )
4109 {
4110 UINT64 Result;
4111
4112 if ((String == NULL) || (StrSize (String) == 0) || (Value == NULL)) {
4113 return (EFI_INVALID_PARAMETER);
4114 }
4115
4116 //
4117 // Ignore the pad spaces (space or tab)
4118 //
4119 while ((*String == L' ') || (*String == L'\t')) {
4120 String++;
4121 }
4122
4123 //
4124 // Ignore leading Zeros after the spaces
4125 //
4126 while (*String == L'0') {
4127 String++;
4128 }
4129
4130 Result = 0;
4131
4132 //
4133 // Stop upon space if requested
4134 // (if the whole value was 0)
4135 //
4136 if (StopAtSpace && (*String == L' ')) {
4137 *Value = Result;
4138 return (EFI_SUCCESS);
4139 }
4140
4141 while (ShellIsDecimalDigitCharacter (*String)) {
4142 //
4143 // If the number represented by String overflows according
4144 // to the range defined by UINT64, then return EFI_DEVICE_ERROR.
4145 //
4146
4147 if (!(Result <= (DivU64x32 ((((UINT64) ~0) - (*String - L'0')), 10)))) {
4148 return (EFI_DEVICE_ERROR);
4149 }
4150
4151 Result = MultU64x32 (Result, 10) + (*String - L'0');
4152 String++;
4153
4154 //
4155 // Stop at spaces if requested
4156 //
4157 if (StopAtSpace && (*String == L' ')) {
4158 break;
4159 }
4160 }
4161
4162 *Value = Result;
4163
4164 return (EFI_SUCCESS);
4165 }
4166
4167 /**
4168 Function to verify and convert a string to its numerical value.
4169
4170 If Hex it must be preceeded with a 0x, 0X, or has ForceHex set TRUE.
4171
4172 @param[in] String The string to evaluate.
4173 @param[out] Value Upon a successful return the value of the conversion.
4174 @param[in] ForceHex TRUE - always assume hex.
4175 @param[in] StopAtSpace FALSE to skip spaces.
4176
4177 @retval EFI_SUCCESS The conversion was successful.
4178 @retval EFI_INVALID_PARAMETER String contained an invalid character.
4179 @retval EFI_NOT_FOUND String was a number, but Value was NULL.
4180 **/
4181 EFI_STATUS
4182 EFIAPI
4183 ShellConvertStringToUint64 (
4184 IN CONST CHAR16 *String,
4185 OUT UINT64 *Value,
4186 IN CONST BOOLEAN ForceHex,
4187 IN CONST BOOLEAN StopAtSpace
4188 )
4189 {
4190 UINT64 RetVal;
4191 CONST CHAR16 *Walker;
4192 EFI_STATUS Status;
4193 BOOLEAN Hex;
4194
4195 Hex = ForceHex;
4196
4197 if (!InternalShellIsHexOrDecimalNumber (String, Hex, StopAtSpace, FALSE)) {
4198 if (!Hex) {
4199 Hex = TRUE;
4200 if (!InternalShellIsHexOrDecimalNumber (String, Hex, StopAtSpace, FALSE)) {
4201 return (EFI_INVALID_PARAMETER);
4202 }
4203 } else {
4204 return (EFI_INVALID_PARAMETER);
4205 }
4206 }
4207
4208 //
4209 // Chop off leading spaces
4210 //
4211 for (Walker = String; Walker != NULL && *Walker != CHAR_NULL && *Walker == L' '; Walker++) {
4212 }
4213
4214 //
4215 // make sure we have something left that is numeric.
4216 //
4217 if ((Walker == NULL) || (*Walker == CHAR_NULL) || !InternalShellIsHexOrDecimalNumber (Walker, Hex, StopAtSpace, FALSE)) {
4218 return (EFI_INVALID_PARAMETER);
4219 }
4220
4221 //
4222 // do the conversion.
4223 //
4224 if (Hex || (StrnCmp (Walker, L"0x", 2) == 0) || (StrnCmp (Walker, L"0X", 2) == 0)) {
4225 Status = InternalShellStrHexToUint64 (Walker, &RetVal, StopAtSpace);
4226 } else {
4227 Status = InternalShellStrDecimalToUint64 (Walker, &RetVal, StopAtSpace);
4228 }
4229
4230 if ((Value == NULL) && !EFI_ERROR (Status)) {
4231 return (EFI_NOT_FOUND);
4232 }
4233
4234 if (Value != NULL) {
4235 *Value = RetVal;
4236 }
4237
4238 return (Status);
4239 }
4240
4241 /**
4242 Function to determin if an entire string is a valid number.
4243
4244 If Hex it must be preceeded with a 0x or has ForceHex, set TRUE.
4245
4246 @param[in] String The string to evaluate.
4247 @param[in] ForceHex TRUE - always assume hex.
4248 @param[in] StopAtSpace TRUE to halt upon finding a space, FALSE to keep going.
4249
4250 @retval TRUE It is all numeric (dec/hex) characters.
4251 @retval FALSE There is a non-numeric character.
4252 **/
4253 BOOLEAN
4254 EFIAPI
4255 ShellIsHexOrDecimalNumber (
4256 IN CONST CHAR16 *String,
4257 IN CONST BOOLEAN ForceHex,
4258 IN CONST BOOLEAN StopAtSpace
4259 )
4260 {
4261 if (ShellConvertStringToUint64 (String, NULL, ForceHex, StopAtSpace) == EFI_NOT_FOUND) {
4262 return (TRUE);
4263 }
4264
4265 return (FALSE);
4266 }
4267
4268 /**
4269 Function to read a single line from a SHELL_FILE_HANDLE. The \n is not included in the returned
4270 buffer. The returned buffer must be callee freed.
4271
4272 If the position upon start is 0, then the Ascii Boolean will be set. This should be
4273 maintained and not changed for all operations with the same file.
4274
4275 @param[in] Handle SHELL_FILE_HANDLE to read from.
4276 @param[in, out] Ascii Boolean value for indicating whether the file is
4277 Ascii (TRUE) or UCS2 (FALSE).
4278
4279 @return The line of text from the file.
4280 @retval NULL There was not enough memory available.
4281
4282 @sa ShellFileHandleReadLine
4283 **/
4284 CHAR16 *
4285 EFIAPI
4286 ShellFileHandleReturnLine (
4287 IN SHELL_FILE_HANDLE Handle,
4288 IN OUT BOOLEAN *Ascii
4289 )
4290 {
4291 CHAR16 *RetVal;
4292 UINTN Size;
4293 EFI_STATUS Status;
4294
4295 Size = 0;
4296 RetVal = NULL;
4297
4298 Status = ShellFileHandleReadLine (Handle, RetVal, &Size, FALSE, Ascii);
4299 if (Status == EFI_BUFFER_TOO_SMALL) {
4300 RetVal = AllocateZeroPool (Size);
4301 if (RetVal == NULL) {
4302 return (NULL);
4303 }
4304
4305 Status = ShellFileHandleReadLine (Handle, RetVal, &Size, FALSE, Ascii);
4306 }
4307
4308 if ((Status == EFI_END_OF_FILE) && (RetVal != NULL) && (*RetVal != CHAR_NULL)) {
4309 Status = EFI_SUCCESS;
4310 }
4311
4312 if (EFI_ERROR (Status) && (RetVal != NULL)) {
4313 FreePool (RetVal);
4314 RetVal = NULL;
4315 }
4316
4317 return (RetVal);
4318 }
4319
4320 /**
4321 Function to read a single line (up to but not including the \n) from a SHELL_FILE_HANDLE.
4322
4323 If the position upon start is 0, then the Ascii Boolean will be set. This should be
4324 maintained and not changed for all operations with the same file.
4325
4326 NOTE: LINES THAT ARE RETURNED BY THIS FUNCTION ARE UCS2, EVEN IF THE FILE BEING READ
4327 IS IN ASCII FORMAT.
4328
4329 @param[in] Handle SHELL_FILE_HANDLE to read from.
4330 @param[in, out] Buffer The pointer to buffer to read into. If this function
4331 returns EFI_SUCCESS, then on output Buffer will
4332 contain a UCS2 string, even if the file being
4333 read is ASCII.
4334 @param[in, out] Size On input, pointer to number of bytes in Buffer.
4335 On output, unchanged unless Buffer is too small
4336 to contain the next line of the file. In that
4337 case Size is set to the number of bytes needed
4338 to hold the next line of the file (as a UCS2
4339 string, even if it is an ASCII file).
4340 @param[in] Truncate If the buffer is large enough, this has no effect.
4341 If the buffer is is too small and Truncate is TRUE,
4342 the line will be truncated.
4343 If the buffer is is too small and Truncate is FALSE,
4344 then no read will occur.
4345
4346 @param[in, out] Ascii Boolean value for indicating whether the file is
4347 Ascii (TRUE) or UCS2 (FALSE).
4348
4349 @retval EFI_SUCCESS The operation was successful. The line is stored in
4350 Buffer.
4351 @retval EFI_END_OF_FILE There are no more lines in the file.
4352 @retval EFI_INVALID_PARAMETER Handle was NULL.
4353 @retval EFI_INVALID_PARAMETER Size was NULL.
4354 @retval EFI_BUFFER_TOO_SMALL Size was not large enough to store the line.
4355 Size was updated to the minimum space required.
4356 **/
4357 EFI_STATUS
4358 EFIAPI
4359 ShellFileHandleReadLine (
4360 IN SHELL_FILE_HANDLE Handle,
4361 IN OUT CHAR16 *Buffer,
4362 IN OUT UINTN *Size,
4363 IN BOOLEAN Truncate,
4364 IN OUT BOOLEAN *Ascii
4365 )
4366 {
4367 EFI_STATUS Status;
4368 CHAR16 CharBuffer;
4369 UINTN CharSize;
4370 UINTN CountSoFar;
4371 UINT64 OriginalFilePosition;
4372
4373 if ( (Handle == NULL)
4374 || (Size == NULL)
4375 )
4376 {
4377 return (EFI_INVALID_PARAMETER);
4378 }
4379
4380 if (Buffer == NULL) {
4381 ASSERT (*Size == 0);
4382 } else {
4383 *Buffer = CHAR_NULL;
4384 }
4385
4386 gEfiShellProtocol->GetFilePosition (Handle, &OriginalFilePosition);
4387 if (OriginalFilePosition == 0) {
4388 CharSize = sizeof (CHAR16);
4389 Status = gEfiShellProtocol->ReadFile (Handle, &CharSize, &CharBuffer);
4390 ASSERT_EFI_ERROR (Status);
4391 if (CharBuffer == gUnicodeFileTag) {
4392 *Ascii = FALSE;
4393 } else {
4394 *Ascii = TRUE;
4395 gEfiShellProtocol->SetFilePosition (Handle, OriginalFilePosition);
4396 }
4397 }
4398
4399 if (*Ascii) {
4400 CharSize = sizeof (CHAR8);
4401 } else {
4402 CharSize = sizeof (CHAR16);
4403 }
4404
4405 for (CountSoFar = 0; ; CountSoFar++) {
4406 CharBuffer = 0;
4407 Status = gEfiShellProtocol->ReadFile (Handle, &CharSize, &CharBuffer);
4408 if ( EFI_ERROR (Status)
4409 || (CharSize == 0)
4410 || ((CharBuffer == L'\n') && !(*Ascii))
4411 || ((CharBuffer == '\n') && *Ascii)
4412 )
4413 {
4414 if (CharSize == 0) {
4415 Status = EFI_END_OF_FILE;
4416 }
4417
4418 break;
4419 }
4420
4421 //
4422 // if we have space save it...
4423 //
4424 if ((CountSoFar+1)*sizeof (CHAR16) < *Size) {
4425 ASSERT (Buffer != NULL);
4426 ((CHAR16 *)Buffer)[CountSoFar] = CharBuffer;
4427 ((CHAR16 *)Buffer)[CountSoFar+1] = CHAR_NULL;
4428 }
4429 }
4430
4431 //
4432 // if we ran out of space tell when...
4433 //
4434 if ((CountSoFar+1)*sizeof (CHAR16) > *Size) {
4435 *Size = (CountSoFar+1)*sizeof (CHAR16);
4436 if (!Truncate) {
4437 gEfiShellProtocol->SetFilePosition (Handle, OriginalFilePosition);
4438 } else {
4439 DEBUG ((DEBUG_WARN, "The line was truncated in ShellFileHandleReadLine"));
4440 }
4441
4442 return (EFI_BUFFER_TOO_SMALL);
4443 }
4444
4445 while (Buffer[StrLen (Buffer)-1] == L'\r') {
4446 Buffer[StrLen (Buffer)-1] = CHAR_NULL;
4447 }
4448
4449 return (Status);
4450 }
4451
4452 /**
4453 Function to print help file / man page content in the spec from the UEFI Shell protocol GetHelpText function.
4454
4455 @param[in] CommandToGetHelpOn Pointer to a string containing the command name of help file to be printed.
4456 @param[in] SectionToGetHelpOn Pointer to the section specifier(s).
4457 @param[in] PrintCommandText If TRUE, prints the command followed by the help content, otherwise prints
4458 the help content only.
4459 @retval EFI_DEVICE_ERROR The help data format was incorrect.
4460 @retval EFI_NOT_FOUND The help data could not be found.
4461 @retval EFI_SUCCESS The operation was successful.
4462 **/
4463 EFI_STATUS
4464 EFIAPI
4465 ShellPrintHelp (
4466 IN CONST CHAR16 *CommandToGetHelpOn,
4467 IN CONST CHAR16 *SectionToGetHelpOn,
4468 IN BOOLEAN PrintCommandText
4469 )
4470 {
4471 EFI_STATUS Status;
4472 CHAR16 *OutText;
4473
4474 OutText = NULL;
4475
4476 //
4477 // Get the string to print based
4478 //
4479 Status = gEfiShellProtocol->GetHelpText (CommandToGetHelpOn, SectionToGetHelpOn, &OutText);
4480
4481 //
4482 // make sure we got a valid string
4483 //
4484 if (EFI_ERROR (Status)) {
4485 return Status;
4486 }
4487
4488 if ((OutText == NULL) || (StrLen (OutText) == 0)) {
4489 return EFI_NOT_FOUND;
4490 }
4491
4492 //
4493 // Chop off trailing stuff we dont need
4494 //
4495 while (OutText[StrLen (OutText)-1] == L'\r' || OutText[StrLen (OutText)-1] == L'\n' || OutText[StrLen (OutText)-1] == L' ') {
4496 OutText[StrLen (OutText)-1] = CHAR_NULL;
4497 }
4498
4499 //
4500 // Print this out to the console
4501 //
4502 if (PrintCommandText) {
4503 ShellPrintEx (-1, -1, L"%H%-14s%N- %s\r\n", CommandToGetHelpOn, OutText);
4504 } else {
4505 ShellPrintEx (-1, -1, L"%N%s\r\n", OutText);
4506 }
4507
4508 SHELL_FREE_NON_NULL (OutText);
4509
4510 return EFI_SUCCESS;
4511 }
4512
4513 /**
4514 Function to delete a file by name
4515
4516 @param[in] FileName Pointer to file name to delete.
4517
4518 @retval EFI_SUCCESS the file was deleted sucessfully
4519 @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not
4520 deleted
4521 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
4522 @retval EFI_NOT_FOUND The specified file could not be found on the
4523 device or the file system could not be found
4524 on the device.
4525 @retval EFI_NO_MEDIA The device has no medium.
4526 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
4527 medium is no longer supported.
4528 @retval EFI_DEVICE_ERROR The device reported an error.
4529 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
4530 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
4531 @retval EFI_ACCESS_DENIED The file was opened read only.
4532 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
4533 file.
4534 @retval other The file failed to open
4535 **/
4536 EFI_STATUS
4537 EFIAPI
4538 ShellDeleteFileByName (
4539 IN CONST CHAR16 *FileName
4540 )
4541 {
4542 EFI_STATUS Status;
4543 SHELL_FILE_HANDLE FileHandle;
4544
4545 Status = ShellFileExists (FileName);
4546
4547 if (Status == EFI_SUCCESS) {
4548 Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0x0);
4549 if (Status == EFI_SUCCESS) {
4550 Status = ShellDeleteFile (&FileHandle);
4551 }
4552 }
4553
4554 return (Status);
4555 }
4556
4557 /**
4558 Cleans off all the quotes in the string.
4559
4560 @param[in] OriginalString pointer to the string to be cleaned.
4561 @param[out] CleanString The new string with all quotes removed.
4562 Memory allocated in the function and free
4563 by caller.
4564
4565 @retval EFI_SUCCESS The operation was successful.
4566 **/
4567 EFI_STATUS
4568 InternalShellStripQuotes (
4569 IN CONST CHAR16 *OriginalString,
4570 OUT CHAR16 **CleanString
4571 )
4572 {
4573 CHAR16 *Walker;
4574
4575 if ((OriginalString == NULL) || (CleanString == NULL)) {
4576 return EFI_INVALID_PARAMETER;
4577 }
4578
4579 *CleanString = AllocateCopyPool (StrSize (OriginalString), OriginalString);
4580 if (*CleanString == NULL) {
4581 return EFI_OUT_OF_RESOURCES;
4582 }
4583
4584 for (Walker = *CleanString; Walker != NULL && *Walker != CHAR_NULL; Walker++) {
4585 if (*Walker == L'\"') {
4586 CopyMem (Walker, Walker+1, StrSize (Walker) - sizeof (Walker[0]));
4587 }
4588 }
4589
4590 return EFI_SUCCESS;
4591 }