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