]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellLib/UefiShellLib.c
add ASSERT to 2 functions and fix issue with size difference between old and new...
[mirror_edk2.git] / ShellPkg / Library / UefiShellLib / UefiShellLib.c
1 /** @file
2 Provides interface to shell functionality for shell commands and applications.
3
4 Copyright (c) 2006 - 2009, Intel Corporation<BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include <Uefi.h>
16 #include <Library/ShellLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/DevicePathLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/FileHandleLib.h>
25 #include <Library/PrintLib.h>
26 #include <Library/UefiLib.h>
27 #include <Library/HiiLib.h>
28
29 #include <Protocol/EfiShellEnvironment2.h>
30 #include <Protocol/EfiShellInterface.h>
31 #include <Protocol/EfiShell.h>
32 #include <Protocol/EfiShellParameters.h>
33 #include <Protocol/SimpleFileSystem.h>
34
35 #include "UefiShellLib.h"
36
37 #define MAX_FILE_NAME_LEN 522 // (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes)
38 #define FIND_XXXXX_FILE_BUFFER_SIZE (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN)
39
40 //
41 // This is not static since it's extern in the .h file
42 //
43 SHELL_PARAM_ITEM EmptyParamList[] = {
44 {NULL, TypeMax}
45 };
46
47 //
48 // Static file globals for the shell library
49 //
50 STATIC EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2;
51 STATIC EFI_SHELL_INTERFACE *mEfiShellInterface;
52 STATIC EFI_SHELL_PROTOCOL *mEfiShellProtocol;
53 STATIC EFI_SHELL_PARAMETERS_PROTOCOL *mEfiShellParametersProtocol;
54 STATIC EFI_HANDLE mEfiShellEnvironment2Handle;
55 STATIC FILE_HANDLE_FUNCTION_MAP FileFunctionMap;
56 STATIC UINTN mTotalParameterCount;
57 STATIC CHAR16 *mPostReplaceFormat;
58 STATIC CHAR16 *mPostReplaceFormat2;
59 /**
60 Check if a Unicode character is a hexadecimal character.
61
62 This internal function checks if a Unicode character is a
63 decimal character. The valid hexadecimal character is
64 L'0' to L'9', L'a' to L'f', or L'A' to L'F'.
65
66
67 @param Char The character to check against.
68
69 @retval TRUE If the Char is a hexadecmial character.
70 @retval FALSE If the Char is not a hexadecmial character.
71
72 **/
73 BOOLEAN
74 EFIAPI
75 ShellInternalIsHexaDecimalDigitCharacter (
76 IN CHAR16 Char
77 ) {
78 return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F') || (Char >= L'a' && Char <= L'f'));
79 }
80
81 /**
82 helper function to find ShellEnvironment2 for constructor
83 **/
84 EFI_STATUS
85 EFIAPI
86 ShellFindSE2 (
87 IN EFI_HANDLE ImageHandle
88 ) {
89 EFI_STATUS Status;
90 EFI_HANDLE *Buffer;
91 UINTN BufferSize;
92 UINTN HandleIndex;
93
94 BufferSize = 0;
95 Buffer = NULL;
96 Status = gBS->OpenProtocol(ImageHandle,
97 &gEfiShellEnvironment2Guid,
98 (VOID **)&mEfiShellEnvironment2,
99 ImageHandle,
100 NULL,
101 EFI_OPEN_PROTOCOL_GET_PROTOCOL
102 );
103 //
104 // look for the mEfiShellEnvironment2 protocol at a higher level
105 //
106 if (EFI_ERROR (Status) || !(CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid) != FALSE)){
107 //
108 // figure out how big of a buffer we need.
109 //
110 Status = gBS->LocateHandle (ByProtocol,
111 &gEfiShellEnvironment2Guid,
112 NULL, // ignored for ByProtocol
113 &BufferSize,
114 Buffer
115 );
116 //
117 // maybe it's not there???
118 //
119 if (Status == EFI_BUFFER_TOO_SMALL) {
120 Buffer = (EFI_HANDLE*)AllocatePool(BufferSize);
121 ASSERT(Buffer != NULL);
122 Status = gBS->LocateHandle (ByProtocol,
123 &gEfiShellEnvironment2Guid,
124 NULL, // ignored for ByProtocol
125 &BufferSize,
126 Buffer
127 );
128 }
129 if (!EFI_ERROR (Status)) {
130 //
131 // now parse the list of returned handles
132 //
133 Status = EFI_NOT_FOUND;
134 for (HandleIndex = 0; HandleIndex < (BufferSize/sizeof(Buffer[0])); HandleIndex++) {
135 Status = gBS->OpenProtocol(Buffer[HandleIndex],
136 &gEfiShellEnvironment2Guid,
137 (VOID **)&mEfiShellEnvironment2,
138 ImageHandle,
139 NULL,
140 EFI_OPEN_PROTOCOL_GET_PROTOCOL
141 );
142 if (CompareGuid (&mEfiShellEnvironment2->SESGuid, &gEfiShellEnvironment2ExtGuid) != FALSE) {
143 mEfiShellEnvironment2Handle = Buffer[HandleIndex];
144 Status = EFI_SUCCESS;
145 break;
146 }
147 }
148 }
149 }
150 if (Buffer != NULL) {
151 FreePool (Buffer);
152 }
153 return (Status);
154 }
155
156 EFI_STATUS
157 EFIAPI
158 ShellLibConstructorWorker (
159 IN EFI_HANDLE ImageHandle,
160 IN EFI_SYSTEM_TABLE *SystemTable
161 ) {
162 EFI_STATUS Status;
163
164 ASSERT(PcdGet16 (PcdShellLibMaxPrintBufferSize) < PcdGet32 (PcdMaximumUnicodeStringLength));
165 mPostReplaceFormat = AllocateZeroPool (PcdGet16 (PcdShellLibMaxPrintBufferSize));
166 ASSERT (mPostReplaceFormat != NULL);
167 mPostReplaceFormat2 = AllocateZeroPool (PcdGet16 (PcdShellLibMaxPrintBufferSize));
168 ASSERT (mPostReplaceFormat2 != NULL);
169
170 //
171 // Set the parameter count to an invalid number
172 //
173 mTotalParameterCount = (UINTN)(-1);
174
175 //
176 // UEFI 2.0 shell interfaces (used preferentially)
177 //
178 Status = gBS->OpenProtocol(ImageHandle,
179 &gEfiShellProtocolGuid,
180 (VOID **)&mEfiShellProtocol,
181 ImageHandle,
182 NULL,
183 EFI_OPEN_PROTOCOL_GET_PROTOCOL
184 );
185 if (EFI_ERROR(Status)) {
186 mEfiShellProtocol = NULL;
187 }
188 Status = gBS->OpenProtocol(ImageHandle,
189 &gEfiShellParametersProtocolGuid,
190 (VOID **)&mEfiShellParametersProtocol,
191 ImageHandle,
192 NULL,
193 EFI_OPEN_PROTOCOL_GET_PROTOCOL
194 );
195 if (EFI_ERROR(Status)) {
196 mEfiShellParametersProtocol = NULL;
197 }
198
199 if (mEfiShellParametersProtocol == NULL || mEfiShellProtocol == NULL) {
200 //
201 // Moved to seperate function due to complexity
202 //
203 Status = ShellFindSE2(ImageHandle);
204
205 if (EFI_ERROR(Status)) {
206 DEBUG((DEBUG_ERROR, "Status: 0x%08x\r\n", Status));
207 mEfiShellEnvironment2 = NULL;
208 }
209 Status = gBS->OpenProtocol(ImageHandle,
210 &gEfiShellInterfaceGuid,
211 (VOID **)&mEfiShellInterface,
212 ImageHandle,
213 NULL,
214 EFI_OPEN_PROTOCOL_GET_PROTOCOL
215 );
216 if (EFI_ERROR(Status)) {
217 mEfiShellInterface = NULL;
218 }
219 }
220 //
221 // only success getting 2 of either the old or new, but no 1/2 and 1/2
222 //
223 if ((mEfiShellEnvironment2 != NULL && mEfiShellInterface != NULL) ||
224 (mEfiShellProtocol != NULL && mEfiShellParametersProtocol != NULL) ) {
225 if (mEfiShellProtocol != NULL) {
226 FileFunctionMap.GetFileInfo = mEfiShellProtocol->GetFileInfo;
227 FileFunctionMap.SetFileInfo = mEfiShellProtocol->SetFileInfo;
228 FileFunctionMap.ReadFile = mEfiShellProtocol->ReadFile;
229 FileFunctionMap.WriteFile = mEfiShellProtocol->WriteFile;
230 FileFunctionMap.CloseFile = mEfiShellProtocol->CloseFile;
231 FileFunctionMap.DeleteFile = mEfiShellProtocol->DeleteFile;
232 FileFunctionMap.GetFilePosition = mEfiShellProtocol->GetFilePosition;
233 FileFunctionMap.SetFilePosition = mEfiShellProtocol->SetFilePosition;
234 FileFunctionMap.FlushFile = mEfiShellProtocol->FlushFile;
235 FileFunctionMap.GetFileSize = mEfiShellProtocol->GetFileSize;
236 } else {
237 FileFunctionMap.GetFileInfo = FileHandleGetInfo;
238 FileFunctionMap.SetFileInfo = FileHandleSetInfo;
239 FileFunctionMap.ReadFile = FileHandleRead;
240 FileFunctionMap.WriteFile = FileHandleWrite;
241 FileFunctionMap.CloseFile = FileHandleClose;
242 FileFunctionMap.DeleteFile = FileHandleDelete;
243 FileFunctionMap.GetFilePosition = FileHandleGetPosition;
244 FileFunctionMap.SetFilePosition = FileHandleSetPosition;
245 FileFunctionMap.FlushFile = FileHandleFlush;
246 FileFunctionMap.GetFileSize = FileHandleGetSize;
247 }
248 return (EFI_SUCCESS);
249 }
250 return (EFI_NOT_FOUND);
251 }
252 /**
253 Constructor for the Shell library.
254
255 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
256
257 @param ImageHandle the image handle of the process
258 @param SystemTable the EFI System Table pointer
259
260 @retval EFI_SUCCESS the initialization was complete sucessfully
261 @return others an error ocurred during initialization
262 **/
263 EFI_STATUS
264 EFIAPI
265 ShellLibConstructor (
266 IN EFI_HANDLE ImageHandle,
267 IN EFI_SYSTEM_TABLE *SystemTable
268 ) {
269
270
271 mEfiShellEnvironment2 = NULL;
272 mEfiShellProtocol = NULL;
273 mEfiShellParametersProtocol = NULL;
274 mEfiShellInterface = NULL;
275 mEfiShellEnvironment2Handle = NULL;
276 mPostReplaceFormat = NULL;
277 mPostReplaceFormat2 = NULL;
278
279 //
280 // verify that auto initialize is not set false
281 //
282 if (PcdGetBool(PcdShellLibAutoInitialize) == 0) {
283 return (EFI_SUCCESS);
284 }
285
286 return (ShellLibConstructorWorker(ImageHandle, SystemTable));
287 }
288
289 /**
290 Destructory for the library. free any resources.
291 **/
292 EFI_STATUS
293 EFIAPI
294 ShellLibDestructor (
295 IN EFI_HANDLE ImageHandle,
296 IN EFI_SYSTEM_TABLE *SystemTable
297 ) {
298 if (mEfiShellEnvironment2 != NULL) {
299 gBS->CloseProtocol(mEfiShellEnvironment2Handle==NULL?ImageHandle:mEfiShellEnvironment2Handle,
300 &gEfiShellEnvironment2Guid,
301 ImageHandle,
302 NULL);
303 mEfiShellEnvironment2 = NULL;
304 }
305 if (mEfiShellInterface != NULL) {
306 gBS->CloseProtocol(ImageHandle,
307 &gEfiShellInterfaceGuid,
308 ImageHandle,
309 NULL);
310 mEfiShellInterface = NULL;
311 }
312 if (mEfiShellProtocol != NULL) {
313 gBS->CloseProtocol(ImageHandle,
314 &gEfiShellProtocolGuid,
315 ImageHandle,
316 NULL);
317 mEfiShellProtocol = NULL;
318 }
319 if (mEfiShellParametersProtocol != NULL) {
320 gBS->CloseProtocol(ImageHandle,
321 &gEfiShellParametersProtocolGuid,
322 ImageHandle,
323 NULL);
324 mEfiShellParametersProtocol = NULL;
325 }
326 mEfiShellEnvironment2Handle = NULL;
327
328 if (mPostReplaceFormat != NULL) {
329 FreePool(mPostReplaceFormat);
330 }
331 if (mPostReplaceFormat2 != NULL) {
332 FreePool(mPostReplaceFormat2);
333 }
334 mPostReplaceFormat = NULL;
335 mPostReplaceFormat2 = NULL;
336
337 return (EFI_SUCCESS);
338 }
339
340 /**
341 This function causes the shell library to initialize itself. If the shell library
342 is already initialized it will de-initialize all the current protocol poitners and
343 re-populate them again.
344
345 When the library is used with PcdShellLibAutoInitialize set to true this function
346 will return EFI_SUCCESS and perform no actions.
347
348 This function is intended for internal access for shell commands only.
349
350 @retval EFI_SUCCESS the initialization was complete sucessfully
351
352 **/
353 EFI_STATUS
354 EFIAPI
355 ShellInitialize (
356 ) {
357 //
358 // if auto initialize is not false then skip
359 //
360 if (PcdGetBool(PcdShellLibAutoInitialize) != 0) {
361 return (EFI_SUCCESS);
362 }
363
364 //
365 // deinit the current stuff
366 //
367 ASSERT_EFI_ERROR(ShellLibDestructor(gImageHandle, gST));
368
369 //
370 // init the new stuff
371 //
372 return (ShellLibConstructorWorker(gImageHandle, gST));
373 }
374
375 /**
376 This function will retrieve the information about the file for the handle
377 specified and store it in allocated pool memory.
378
379 This function allocates a buffer to store the file's information. It is the
380 caller's responsibility to free the buffer
381
382 @param FileHandle The file handle of the file for which information is
383 being requested.
384
385 @retval NULL information could not be retrieved.
386
387 @return the information about the file
388 **/
389 EFI_FILE_INFO*
390 EFIAPI
391 ShellGetFileInfo (
392 IN EFI_FILE_HANDLE FileHandle
393 ) {
394 return (FileFunctionMap.GetFileInfo(FileHandle));
395 }
396
397 /**
398 This function will set the information about the file for the opened handle
399 specified.
400
401 @param FileHandle The file handle of the file for which information
402 is being set
403
404 @param FileInfo The infotmation to set.
405
406 @retval EFI_SUCCESS The information was set.
407 @retval EFI_UNSUPPORTED The InformationType is not known.
408 @retval EFI_NO_MEDIA The device has no medium.
409 @retval EFI_DEVICE_ERROR The device reported an error.
410 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
411 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
412 @retval EFI_ACCESS_DENIED The file was opened read only.
413 @retval EFI_VOLUME_FULL The volume is full.
414 **/
415 EFI_STATUS
416 EFIAPI
417 ShellSetFileInfo (
418 IN EFI_FILE_HANDLE FileHandle,
419 IN EFI_FILE_INFO *FileInfo
420 ) {
421 return (FileFunctionMap.SetFileInfo(FileHandle, FileInfo));
422 }
423
424 /**
425 This function will open a file or directory referenced by DevicePath.
426
427 This function opens a file with the open mode according to the file path. The
428 Attributes is valid only for EFI_FILE_MODE_CREATE.
429
430 @param FilePath on input the device path to the file. On output
431 the remaining device path.
432 @param DeviceHandle pointer to the system device handle.
433 @param FileHandle pointer to the file handle.
434 @param OpenMode the mode to open the file with.
435 @param Attributes the file's file attributes.
436
437 @retval EFI_SUCCESS The information was set.
438 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
439 @retval EFI_UNSUPPORTED Could not open the file path.
440 @retval EFI_NOT_FOUND The specified file could not be found on the
441 device or the file system could not be found on
442 the device.
443 @retval EFI_NO_MEDIA The device has no medium.
444 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
445 medium is no longer supported.
446 @retval EFI_DEVICE_ERROR The device reported an error.
447 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
448 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
449 @retval EFI_ACCESS_DENIED The file was opened read only.
450 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
451 file.
452 @retval EFI_VOLUME_FULL The volume is full.
453 **/
454 EFI_STATUS
455 EFIAPI
456 ShellOpenFileByDevicePath(
457 IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
458 OUT EFI_HANDLE *DeviceHandle,
459 OUT EFI_FILE_HANDLE *FileHandle,
460 IN UINT64 OpenMode,
461 IN UINT64 Attributes
462 ) {
463 CHAR16 *FileName;
464 EFI_STATUS Status;
465 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *EfiSimpleFileSystemProtocol;
466 EFI_FILE_HANDLE LastHandle;
467
468 //
469 // ASERT for FileHandle, FilePath, and DeviceHandle being NULL
470 //
471 ASSERT(FilePath != NULL);
472 ASSERT(FileHandle != NULL);
473 ASSERT(DeviceHandle != NULL);
474 //
475 // which shell interface should we use
476 //
477 if (mEfiShellProtocol != NULL) {
478 //
479 // use UEFI Shell 2.0 method.
480 //
481 FileName = mEfiShellProtocol->GetFilePathFromDevicePath(*FilePath);
482 if (FileName == NULL) {
483 return (EFI_INVALID_PARAMETER);
484 }
485 Status = ShellOpenFileByName(FileName, FileHandle, OpenMode, Attributes);
486 FreePool(FileName);
487 return (Status);
488 }
489
490
491 //
492 // use old shell method.
493 //
494 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid,
495 FilePath,
496 DeviceHandle);
497 if (EFI_ERROR (Status)) {
498 return Status;
499 }
500 Status = gBS->OpenProtocol(*DeviceHandle,
501 &gEfiSimpleFileSystemProtocolGuid,
502 (VOID**)&EfiSimpleFileSystemProtocol,
503 gImageHandle,
504 NULL,
505 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
506 if (EFI_ERROR (Status)) {
507 return Status;
508 }
509 Status = EfiSimpleFileSystemProtocol->OpenVolume(EfiSimpleFileSystemProtocol, FileHandle);
510 if (EFI_ERROR (Status)) {
511 FileHandle = NULL;
512 return Status;
513 }
514
515 //
516 // go down directories one node at a time.
517 //
518 while (!IsDevicePathEnd (*FilePath)) {
519 //
520 // For file system access each node should be a file path component
521 //
522 if (DevicePathType (*FilePath) != MEDIA_DEVICE_PATH ||
523 DevicePathSubType (*FilePath) != MEDIA_FILEPATH_DP
524 ) {
525 FileHandle = NULL;
526 return (EFI_INVALID_PARAMETER);
527 }
528 //
529 // Open this file path node
530 //
531 LastHandle = *FileHandle;
532 *FileHandle = NULL;
533
534 //
535 // Try to test opening an existing file
536 //
537 Status = LastHandle->Open (
538 LastHandle,
539 FileHandle,
540 ((FILEPATH_DEVICE_PATH*)*FilePath)->PathName,
541 OpenMode &~EFI_FILE_MODE_CREATE,
542 0
543 );
544
545 //
546 // see if the error was that it needs to be created
547 //
548 if ((EFI_ERROR (Status)) && (OpenMode != (OpenMode &~EFI_FILE_MODE_CREATE))) {
549 Status = LastHandle->Open (
550 LastHandle,
551 FileHandle,
552 ((FILEPATH_DEVICE_PATH*)*FilePath)->PathName,
553 OpenMode,
554 Attributes
555 );
556 }
557 //
558 // Close the last node
559 //
560 LastHandle->Close (LastHandle);
561
562 if (EFI_ERROR(Status)) {
563 return (Status);
564 }
565
566 //
567 // Get the next node
568 //
569 *FilePath = NextDevicePathNode (*FilePath);
570 }
571 return (EFI_SUCCESS);
572 }
573
574 /**
575 This function will open a file or directory referenced by filename.
576
577 If return is EFI_SUCCESS, the Filehandle is the opened file's handle;
578 otherwise, the Filehandle is NULL. The Attributes is valid only for
579 EFI_FILE_MODE_CREATE.
580
581 if FileNAme is NULL then ASSERT()
582
583 @param FileName pointer to file name
584 @param FileHandle pointer to the file handle.
585 @param OpenMode the mode to open the file with.
586 @param Attributes the file's file attributes.
587
588 @retval EFI_SUCCESS The information was set.
589 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
590 @retval EFI_UNSUPPORTED Could not open the file path.
591 @retval EFI_NOT_FOUND The specified file could not be found on the
592 device or the file system could not be found
593 on the device.
594 @retval EFI_NO_MEDIA The device has no medium.
595 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
596 medium is no longer supported.
597 @retval EFI_DEVICE_ERROR The device reported an error.
598 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
599 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
600 @retval EFI_ACCESS_DENIED The file was opened read only.
601 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
602 file.
603 @retval EFI_VOLUME_FULL The volume is full.
604 **/
605 EFI_STATUS
606 EFIAPI
607 ShellOpenFileByName(
608 IN CONST CHAR16 *FileName,
609 OUT EFI_FILE_HANDLE *FileHandle,
610 IN UINT64 OpenMode,
611 IN UINT64 Attributes
612 ) {
613 EFI_HANDLE DeviceHandle;
614 EFI_DEVICE_PATH_PROTOCOL *FilePath;
615 EFI_STATUS Status;
616 EFI_FILE_INFO *FileInfo;
617
618 //
619 // ASSERT if FileName is NULL
620 //
621 ASSERT(FileName != NULL);
622
623 if (mEfiShellProtocol != NULL) {
624 //
625 // Use UEFI Shell 2.0 method
626 //
627 Status = mEfiShellProtocol->OpenFileByName(FileName,
628 FileHandle,
629 OpenMode);
630 if (!EFI_ERROR(Status) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)){
631 FileInfo = FileFunctionMap.GetFileInfo(*FileHandle);
632 ASSERT(FileInfo != NULL);
633 FileInfo->Attribute = Attributes;
634 Status = FileFunctionMap.SetFileInfo(*FileHandle, FileInfo);
635 FreePool(FileInfo);
636 }
637 return (Status);
638 }
639 //
640 // Using EFI Shell version
641 // this means convert name to path and call that function
642 // since this will use EFI method again that will open it.
643 //
644 ASSERT(mEfiShellEnvironment2 != NULL);
645 FilePath = mEfiShellEnvironment2->NameToPath ((CHAR16*)FileName);
646 if (FileDevicePath != NULL) {
647 return (ShellOpenFileByDevicePath(&FilePath,
648 &DeviceHandle,
649 FileHandle,
650 OpenMode,
651 Attributes ));
652 }
653 return (EFI_DEVICE_ERROR);
654 }
655 /**
656 This function create a directory
657
658 If return is EFI_SUCCESS, the Filehandle is the opened directory's handle;
659 otherwise, the Filehandle is NULL. If the directory already existed, this
660 function opens the existing directory.
661
662 @param DirectoryName pointer to directory name
663 @param FileHandle pointer to the file handle.
664
665 @retval EFI_SUCCESS The information was set.
666 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
667 @retval EFI_UNSUPPORTED Could not open the file path.
668 @retval EFI_NOT_FOUND The specified file could not be found on the
669 device or the file system could not be found
670 on the device.
671 @retval EFI_NO_MEDIA The device has no medium.
672 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
673 medium is no longer supported.
674 @retval EFI_DEVICE_ERROR The device reported an error.
675 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
676 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
677 @retval EFI_ACCESS_DENIED The file was opened read only.
678 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the
679 file.
680 @retval EFI_VOLUME_FULL The volume is full.
681 @sa ShellOpenFileByName
682 **/
683 EFI_STATUS
684 EFIAPI
685 ShellCreateDirectory(
686 IN CONST CHAR16 *DirectoryName,
687 OUT EFI_FILE_HANDLE *FileHandle
688 ) {
689 if (mEfiShellProtocol != NULL) {
690 //
691 // Use UEFI Shell 2.0 method
692 //
693 return (mEfiShellProtocol->CreateFile(DirectoryName,
694 EFI_FILE_DIRECTORY,
695 FileHandle
696 ));
697 } else {
698 return (ShellOpenFileByName(DirectoryName,
699 FileHandle,
700 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
701 EFI_FILE_DIRECTORY
702 ));
703 }
704 }
705
706 /**
707 This function reads information from an opened file.
708
709 If FileHandle is not a directory, the function reads the requested number of
710 bytes from the file at the file's current position and returns them in Buffer.
711 If the read goes beyond the end of the file, the read length is truncated to the
712 end of the file. The file's current position is increased by the number of bytes
713 returned. If FileHandle is a directory, the function reads the directory entry
714 at the file's current position and returns the entry in Buffer. If the Buffer
715 is not large enough to hold the current directory entry, then
716 EFI_BUFFER_TOO_SMALL is returned and the current file position is not updated.
717 BufferSize is set to be the size of the buffer needed to read the entry. On
718 success, the current position is updated to the next directory entry. If there
719 are no more directory entries, the read returns a zero-length buffer.
720 EFI_FILE_INFO is the structure returned as the directory entry.
721
722 @param FileHandle the opened file handle
723 @param BufferSize on input the size of buffer in bytes. on return
724 the number of bytes written.
725 @param Buffer the buffer to put read data into.
726
727 @retval EFI_SUCCESS Data was read.
728 @retval EFI_NO_MEDIA The device has no media.
729 @retval EFI_DEVICE_ERROR The device reported an error.
730 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
731 @retval EFI_BUFFER_TO_SMALL Buffer is too small. ReadSize contains required
732 size.
733
734 **/
735 EFI_STATUS
736 EFIAPI
737 ShellReadFile(
738 IN EFI_FILE_HANDLE FileHandle,
739 IN OUT UINTN *BufferSize,
740 OUT VOID *Buffer
741 ) {
742 return (FileFunctionMap.ReadFile(FileHandle, BufferSize, Buffer));
743 }
744
745
746 /**
747 Write data to a file.
748
749 This function writes the specified number of bytes to the file at the current
750 file position. The current file position is advanced the actual number of bytes
751 written, which is returned in BufferSize. Partial writes only occur when there
752 has been a data error during the write attempt (such as "volume space full").
753 The file is automatically grown to hold the data if required. Direct writes to
754 opened directories are not supported.
755
756 @param FileHandle The opened file for writing
757 @param BufferSize on input the number of bytes in Buffer. On output
758 the number of bytes written.
759 @param Buffer the buffer containing data to write is stored.
760
761 @retval EFI_SUCCESS Data was written.
762 @retval EFI_UNSUPPORTED Writes to an open directory are not supported.
763 @retval EFI_NO_MEDIA The device has no media.
764 @retval EFI_DEVICE_ERROR The device reported an error.
765 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
766 @retval EFI_WRITE_PROTECTED The device is write-protected.
767 @retval EFI_ACCESS_DENIED The file was open for read only.
768 @retval EFI_VOLUME_FULL The volume is full.
769 **/
770 EFI_STATUS
771 EFIAPI
772 ShellWriteFile(
773 IN EFI_FILE_HANDLE FileHandle,
774 IN OUT UINTN *BufferSize,
775 IN VOID *Buffer
776 ) {
777 return (FileFunctionMap.WriteFile(FileHandle, BufferSize, Buffer));
778 }
779
780 /**
781 Close an open file handle.
782
783 This function closes a specified file handle. All "dirty" cached file data is
784 flushed to the device, and the file is closed. In all cases the handle is
785 closed.
786
787 @param FileHandle the file handle to close.
788
789 @retval EFI_SUCCESS the file handle was closed sucessfully.
790 **/
791 EFI_STATUS
792 EFIAPI
793 ShellCloseFile (
794 IN EFI_FILE_HANDLE *FileHandle
795 ) {
796 return (FileFunctionMap.CloseFile(*FileHandle));
797 }
798
799 /**
800 Delete a file and close the handle
801
802 This function closes and deletes a file. In all cases the file handle is closed.
803 If the file cannot be deleted, the warning code EFI_WARN_DELETE_FAILURE is
804 returned, but the handle is still closed.
805
806 @param FileHandle the file handle to delete
807
808 @retval EFI_SUCCESS the file was closed sucessfully
809 @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not
810 deleted
811 @retval INVALID_PARAMETER One of the parameters has an invalid value.
812 **/
813 EFI_STATUS
814 EFIAPI
815 ShellDeleteFile (
816 IN EFI_FILE_HANDLE *FileHandle
817 ) {
818 return (FileFunctionMap.DeleteFile(*FileHandle));
819 }
820
821 /**
822 Set the current position in a file.
823
824 This function sets the current file position for the handle to the position
825 supplied. With the exception of seeking to position 0xFFFFFFFFFFFFFFFF, only
826 absolute positioning is supported, and seeking past the end of the file is
827 allowed (a subsequent write would grow the file). Seeking to position
828 0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file.
829 If FileHandle is a directory, the only position that may be set is zero. This
830 has the effect of starting the read process of the directory entries over.
831
832 @param FileHandle The file handle on which the position is being set
833 @param Position Byte position from begining of file
834
835 @retval EFI_SUCCESS Operation completed sucessfully.
836 @retval EFI_UNSUPPORTED the seek request for non-zero is not valid on
837 directories.
838 @retval INVALID_PARAMETER One of the parameters has an invalid value.
839 **/
840 EFI_STATUS
841 EFIAPI
842 ShellSetFilePosition (
843 IN EFI_FILE_HANDLE FileHandle,
844 IN UINT64 Position
845 ) {
846 return (FileFunctionMap.SetFilePosition(FileHandle, Position));
847 }
848
849 /**
850 Gets a file's current position
851
852 This function retrieves the current file position for the file handle. For
853 directories, the current file position has no meaning outside of the file
854 system driver and as such the operation is not supported. An error is returned
855 if FileHandle is a directory.
856
857 @param FileHandle The open file handle on which to get the position.
858 @param Position Byte position from begining of file.
859
860 @retval EFI_SUCCESS the operation completed sucessfully.
861 @retval INVALID_PARAMETER One of the parameters has an invalid value.
862 @retval EFI_UNSUPPORTED the request is not valid on directories.
863 **/
864 EFI_STATUS
865 EFIAPI
866 ShellGetFilePosition (
867 IN EFI_FILE_HANDLE FileHandle,
868 OUT UINT64 *Position
869 ) {
870 return (FileFunctionMap.GetFilePosition(FileHandle, Position));
871 }
872 /**
873 Flushes data on a file
874
875 This function flushes all modified data associated with a file to a device.
876
877 @param FileHandle The file handle on which to flush data
878
879 @retval EFI_SUCCESS The data was flushed.
880 @retval EFI_NO_MEDIA The device has no media.
881 @retval EFI_DEVICE_ERROR The device reported an error.
882 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
883 @retval EFI_WRITE_PROTECTED The file or medium is write protected.
884 @retval EFI_ACCESS_DENIED The file was opened for read only.
885 **/
886 EFI_STATUS
887 EFIAPI
888 ShellFlushFile (
889 IN EFI_FILE_HANDLE FileHandle
890 ) {
891 return (FileFunctionMap.FlushFile(FileHandle));
892 }
893
894 /**
895 Retrieves the first file from a directory
896
897 This function opens a directory and gets the first file's info in the
898 directory. Caller can use ShellFindNextFile() to get other files. When
899 complete the caller is responsible for calling FreePool() on Buffer.
900
901 @param DirHandle The file handle of the directory to search
902 @param Buffer Pointer to buffer for file's information
903
904 @retval EFI_SUCCESS Found the first file.
905 @retval EFI_NOT_FOUND Cannot find the directory.
906 @retval EFI_NO_MEDIA The device has no media.
907 @retval EFI_DEVICE_ERROR The device reported an error.
908 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
909 @return Others status of ShellGetFileInfo, ShellSetFilePosition,
910 or ShellReadFile
911 **/
912 EFI_STATUS
913 EFIAPI
914 ShellFindFirstFile (
915 IN EFI_FILE_HANDLE DirHandle,
916 OUT EFI_FILE_INFO **Buffer
917 ) {
918 //
919 // pass to file handle lib
920 //
921 return (FileHandleFindFirstFile(DirHandle, Buffer));
922 }
923 /**
924 Retrieves the next file in a directory.
925
926 To use this function, caller must call the LibFindFirstFile() to get the
927 first file, and then use this function get other files. This function can be
928 called for several times to get each file's information in the directory. If
929 the call of ShellFindNextFile() got the last file in the directory, the next
930 call of this function has no file to get. *NoFile will be set to TRUE and the
931 Buffer memory will be automatically freed.
932
933 @param DirHandle the file handle of the directory
934 @param Buffer pointer to buffer for file's information
935 @param NoFile pointer to boolean when last file is found
936
937 @retval EFI_SUCCESS Found the next file, or reached last file
938 @retval EFI_NO_MEDIA The device has no media.
939 @retval EFI_DEVICE_ERROR The device reported an error.
940 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
941 **/
942 EFI_STATUS
943 EFIAPI
944 ShellFindNextFile(
945 IN EFI_FILE_HANDLE DirHandle,
946 OUT EFI_FILE_INFO *Buffer,
947 OUT BOOLEAN *NoFile
948 ) {
949 //
950 // pass to file handle lib
951 //
952 return (FileHandleFindNextFile(DirHandle, Buffer, NoFile));
953 }
954 /**
955 Retrieve the size of a file.
956
957 if FileHandle is NULL then ASSERT()
958 if Size is NULL then ASSERT()
959
960 This function extracts the file size info from the FileHandle's EFI_FILE_INFO
961 data.
962
963 @param FileHandle file handle from which size is retrieved
964 @param Size pointer to size
965
966 @retval EFI_SUCCESS operation was completed sucessfully
967 @retval EFI_DEVICE_ERROR cannot access the file
968 **/
969 EFI_STATUS
970 EFIAPI
971 ShellGetFileSize (
972 IN EFI_FILE_HANDLE FileHandle,
973 OUT UINT64 *Size
974 ) {
975 return (FileFunctionMap.GetFileSize(FileHandle, Size));
976 }
977 /**
978 Retrieves the status of the break execution flag
979
980 this function is useful to check whether the application is being asked to halt by the shell.
981
982 @retval TRUE the execution break is enabled
983 @retval FALSE the execution break is not enabled
984 **/
985 BOOLEAN
986 EFIAPI
987 ShellGetExecutionBreakFlag(
988 VOID
989 )
990 {
991 //
992 // Check for UEFI Shell 2.0 protocols
993 //
994 if (mEfiShellProtocol != NULL) {
995
996 //
997 // We are using UEFI Shell 2.0; see if the event has been triggered
998 //
999 if (gBS->CheckEvent(mEfiShellProtocol->ExecutionBreak) != EFI_SUCCESS) {
1000 return (FALSE);
1001 }
1002 return (TRUE);
1003 }
1004
1005 //
1006 // using EFI Shell; call the function to check
1007 //
1008 ASSERT(mEfiShellEnvironment2 != NULL);
1009 return (mEfiShellEnvironment2->GetExecutionBreak());
1010 }
1011 /**
1012 return the value of an environment variable
1013
1014 this function gets the value of the environment variable set by the
1015 ShellSetEnvironmentVariable function
1016
1017 @param EnvKey The key name of the environment variable.
1018
1019 @retval NULL the named environment variable does not exist.
1020 @return != NULL pointer to the value of the environment variable
1021 **/
1022 CONST CHAR16*
1023 EFIAPI
1024 ShellGetEnvironmentVariable (
1025 IN CONST CHAR16 *EnvKey
1026 )
1027 {
1028 //
1029 // Check for UEFI Shell 2.0 protocols
1030 //
1031 if (mEfiShellProtocol != NULL) {
1032 return (mEfiShellProtocol->GetEnv(EnvKey));
1033 }
1034
1035 //
1036 // ASSERT that we must have EFI shell
1037 //
1038 ASSERT(mEfiShellEnvironment2 != NULL);
1039
1040 //
1041 // using EFI Shell
1042 //
1043 return (mEfiShellEnvironment2->GetEnv((CHAR16*)EnvKey));
1044 }
1045 /**
1046 set the value of an environment variable
1047
1048 This function changes the current value of the specified environment variable. If the
1049 environment variable exists and the Value is an empty string, then the environment
1050 variable is deleted. If the environment variable exists and the Value is not an empty
1051 string, then the value of the environment variable is changed. If the environment
1052 variable does not exist and the Value is an empty string, there is no action. If the
1053 environment variable does not exist and the Value is a non-empty string, then the
1054 environment variable is created and assigned the specified value.
1055
1056 This is not supported pre-UEFI Shell 2.0.
1057
1058 @param EnvKey The key name of the environment variable.
1059 @param EnvVal The Value of the environment variable
1060 @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
1061
1062 @retval EFI_SUCCESS the operation was completed sucessfully
1063 @retval EFI_UNSUPPORTED This operation is not allowed in pre UEFI 2.0 Shell environments
1064 **/
1065 EFI_STATUS
1066 EFIAPI
1067 ShellSetEnvironmentVariable (
1068 IN CONST CHAR16 *EnvKey,
1069 IN CONST CHAR16 *EnvVal,
1070 IN BOOLEAN Volatile
1071 )
1072 {
1073 //
1074 // Check for UEFI Shell 2.0 protocols
1075 //
1076 if (mEfiShellProtocol != NULL) {
1077 return (mEfiShellProtocol->SetEnv(EnvKey, EnvVal, Volatile));
1078 }
1079
1080 //
1081 // This feature does not exist under EFI shell
1082 //
1083 return (EFI_UNSUPPORTED);
1084 }
1085 /**
1086 cause the shell to parse and execute a command line.
1087
1088 This function creates a nested instance of the shell and executes the specified
1089 command (CommandLine) with the specified environment (Environment). Upon return,
1090 the status code returned by the specified command is placed in StatusCode.
1091 If Environment is NULL, then the current environment is used and all changes made
1092 by the commands executed will be reflected in the current environment. If the
1093 Environment is non-NULL, then the changes made will be discarded.
1094 The CommandLine is executed from the current working directory on the current
1095 device.
1096
1097 EnvironmentVariables and Status are only supported for UEFI Shell 2.0.
1098 Output is only supported for pre-UEFI Shell 2.0
1099
1100 @param ImageHandle Parent image that is starting the operation
1101 @param CommandLine pointer to null terminated command line.
1102 @param Output true to display debug output. false to hide it.
1103 @param EnvironmentVariables optional pointer to array of environment variables
1104 in the form "x=y". if NULL current set is used.
1105 @param Status the status of the run command line.
1106
1107 @retval EFI_SUCCESS the operation completed sucessfully. Status
1108 contains the status code returned.
1109 @retval EFI_INVALID_PARAMETER a parameter contains an invalid value
1110 @retval EFI_OUT_OF_RESOURCES out of resources
1111 @retval EFI_UNSUPPORTED the operation is not allowed.
1112 **/
1113 EFI_STATUS
1114 EFIAPI
1115 ShellExecute (
1116 IN EFI_HANDLE *ParentHandle,
1117 IN CHAR16 *CommandLine OPTIONAL,
1118 IN BOOLEAN Output OPTIONAL,
1119 IN CHAR16 **EnvironmentVariables OPTIONAL,
1120 OUT EFI_STATUS *Status OPTIONAL
1121 )
1122 {
1123 //
1124 // Check for UEFI Shell 2.0 protocols
1125 //
1126 if (mEfiShellProtocol != NULL) {
1127 //
1128 // Call UEFI Shell 2.0 version (not using Output parameter)
1129 //
1130 return (mEfiShellProtocol->Execute(ParentHandle,
1131 CommandLine,
1132 EnvironmentVariables,
1133 Status));
1134 }
1135 //
1136 // ASSERT that we must have EFI shell
1137 //
1138 ASSERT(mEfiShellEnvironment2 != NULL);
1139 //
1140 // Call EFI Shell version (not using EnvironmentVariables or Status parameters)
1141 // Due to oddity in the EFI shell we want to dereference the ParentHandle here
1142 //
1143 return (mEfiShellEnvironment2->Execute(*ParentHandle,
1144 CommandLine,
1145 Output));
1146 }
1147 /**
1148 Retreives the current directory path
1149
1150 If the DeviceName is NULL, it returns the current device's current directory
1151 name. If the DeviceName is not NULL, it returns the current directory name
1152 on specified drive.
1153
1154 @param DeviceName the name of the drive to get directory on
1155
1156 @retval NULL the directory does not exist
1157 @return != NULL the directory
1158 **/
1159 CONST CHAR16*
1160 EFIAPI
1161 ShellGetCurrentDir (
1162 IN CHAR16 *DeviceName OPTIONAL
1163 )
1164 {
1165 //
1166 // Check for UEFI Shell 2.0 protocols
1167 //
1168 if (mEfiShellProtocol != NULL) {
1169 return (mEfiShellProtocol->GetCurDir(DeviceName));
1170 }
1171 //
1172 // ASSERT that we must have EFI shell
1173 //
1174 ASSERT(mEfiShellEnvironment2 != NULL);
1175 return (mEfiShellEnvironment2->CurDir(DeviceName));
1176 }
1177 /**
1178 sets (enabled or disabled) the page break mode
1179
1180 when page break mode is enabled the screen will stop scrolling
1181 and wait for operator input before scrolling a subsequent screen.
1182
1183 @param CurrentState TRUE to enable and FALSE to disable
1184 **/
1185 VOID
1186 EFIAPI
1187 ShellSetPageBreakMode (
1188 IN BOOLEAN CurrentState
1189 )
1190 {
1191 //
1192 // check for enabling
1193 //
1194 if (CurrentState != 0x00) {
1195 //
1196 // check for UEFI Shell 2.0
1197 //
1198 if (mEfiShellProtocol != NULL) {
1199 //
1200 // Enable with UEFI 2.0 Shell
1201 //
1202 mEfiShellProtocol->EnablePageBreak();
1203 return;
1204 } else {
1205 //
1206 // ASSERT that must have EFI Shell
1207 //
1208 ASSERT(mEfiShellEnvironment2 != NULL);
1209 //
1210 // Enable with EFI Shell
1211 //
1212 mEfiShellEnvironment2->EnablePageBreak (DEFAULT_INIT_ROW, DEFAULT_AUTO_LF);
1213 return;
1214 }
1215 } else {
1216 //
1217 // check for UEFI Shell 2.0
1218 //
1219 if (mEfiShellProtocol != NULL) {
1220 //
1221 // Disable with UEFI 2.0 Shell
1222 //
1223 mEfiShellProtocol->DisablePageBreak();
1224 return;
1225 } else {
1226 //
1227 // ASSERT that must have EFI Shell
1228 //
1229 ASSERT(mEfiShellEnvironment2 != NULL);
1230 //
1231 // Disable with EFI Shell
1232 //
1233 mEfiShellEnvironment2->DisablePageBreak ();
1234 return;
1235 }
1236 }
1237 }
1238
1239 ///
1240 /// version of EFI_SHELL_FILE_INFO struct, except has no CONST pointers.
1241 /// This allows for the struct to be populated.
1242 ///
1243 typedef struct {
1244 LIST_ENTRY Link;
1245 EFI_STATUS Status;
1246 CHAR16 *FullName;
1247 CHAR16 *FileName;
1248 EFI_FILE_HANDLE Handle;
1249 EFI_FILE_INFO *Info;
1250 } EFI_SHELL_FILE_INFO_NO_CONST;
1251
1252 /**
1253 Converts a EFI shell list of structures to the coresponding UEFI Shell 2.0 type of list.
1254
1255 if OldStyleFileList is NULL then ASSERT()
1256
1257 this function will convert a SHELL_FILE_ARG based list into a callee allocated
1258 EFI_SHELL_FILE_INFO based list. it is up to the caller to free the memory via
1259 the ShellCloseFileMetaArg function.
1260
1261 @param[in] FileList the EFI shell list type
1262 @param[in,out] ListHead the list to add to
1263
1264 @retval the resultant head of the double linked new format list;
1265 **/
1266 LIST_ENTRY*
1267 EFIAPI
1268 InternalShellConvertFileListType (
1269 IN LIST_ENTRY *FileList,
1270 IN OUT LIST_ENTRY *ListHead
1271 )
1272 {
1273 SHELL_FILE_ARG *OldInfo;
1274 LIST_ENTRY *Link;
1275 EFI_SHELL_FILE_INFO_NO_CONST *NewInfo;
1276
1277 //
1278 // ASSERTs
1279 //
1280 ASSERT(FileList != NULL);
1281 ASSERT(ListHead != NULL);
1282
1283 //
1284 // enumerate through each member of the old list and copy
1285 //
1286 for (Link = FileList->ForwardLink; Link != FileList; Link = Link->ForwardLink) {
1287 OldInfo = CR (Link, SHELL_FILE_ARG, Link, SHELL_FILE_ARG_SIGNATURE);
1288
1289 //
1290 // make sure the old list was valid
1291 //
1292 ASSERT(OldInfo != NULL);
1293 ASSERT(OldInfo->Info != NULL);
1294 ASSERT(OldInfo->FullName != NULL);
1295 ASSERT(OldInfo->FileName != NULL);
1296
1297 //
1298 // allocate a new EFI_SHELL_FILE_INFO object
1299 //
1300 NewInfo = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
1301
1302 //
1303 // copy the simple items
1304 //
1305 NewInfo->Handle = OldInfo->Handle;
1306 NewInfo->Status = OldInfo->Status;
1307
1308 // old shell checks for 0 not NULL
1309 OldInfo->Handle = 0;
1310
1311 //
1312 // allocate new space to copy strings and structure
1313 //
1314 NewInfo->FullName = AllocateZeroPool(StrSize(OldInfo->FullName));
1315 NewInfo->FileName = AllocateZeroPool(StrSize(OldInfo->FileName));
1316 NewInfo->Info = AllocateZeroPool((UINTN)OldInfo->Info->Size);
1317
1318 //
1319 // make sure all the memory allocations were sucessful
1320 //
1321 ASSERT(NewInfo->FullName != NULL);
1322 ASSERT(NewInfo->FileName != NULL);
1323 ASSERT(NewInfo->Info != NULL);
1324
1325 //
1326 // Copt the strings and structure
1327 //
1328 StrCpy(NewInfo->FullName, OldInfo->FullName);
1329 StrCpy(NewInfo->FileName, OldInfo->FileName);
1330 gBS->CopyMem (NewInfo->Info, OldInfo->Info, (UINTN)OldInfo->Info->Size);
1331
1332 //
1333 // add that to the list
1334 //
1335 InsertTailList(ListHead, &NewInfo->Link);
1336 }
1337 return (ListHead);
1338 }
1339 /**
1340 Opens a group of files based on a path.
1341
1342 This function uses the Arg to open all the matching files. Each matched
1343 file has a SHELL_FILE_ARG structure to record the file information. These
1344 structures are placed on the list ListHead. Users can get the SHELL_FILE_ARG
1345 structures from ListHead to access each file. This function supports wildcards
1346 and will process '?' and '*' as such. the list must be freed with a call to
1347 ShellCloseFileMetaArg().
1348
1349 If you are NOT appending to an existing list *ListHead must be NULL. If
1350 *ListHead is NULL then it must be callee freed.
1351
1352 @param Arg pointer to path string
1353 @param OpenMode mode to open files with
1354 @param ListHead head of linked list of results
1355
1356 @retval EFI_SUCCESS the operation was sucessful and the list head
1357 contains the list of opened files
1358 #retval EFI_UNSUPPORTED a previous ShellOpenFileMetaArg must be closed first.
1359 *ListHead is set to NULL.
1360 @return != EFI_SUCCESS the operation failed
1361
1362 @sa InternalShellConvertFileListType
1363 **/
1364 EFI_STATUS
1365 EFIAPI
1366 ShellOpenFileMetaArg (
1367 IN CHAR16 *Arg,
1368 IN UINT64 OpenMode,
1369 IN OUT EFI_SHELL_FILE_INFO **ListHead
1370 )
1371 {
1372 EFI_STATUS Status;
1373 LIST_ENTRY mOldStyleFileList;
1374
1375 //
1376 // ASSERT that Arg and ListHead are not NULL
1377 //
1378 ASSERT(Arg != NULL);
1379 ASSERT(ListHead != NULL);
1380
1381 //
1382 // Check for UEFI Shell 2.0 protocols
1383 //
1384 if (mEfiShellProtocol != NULL) {
1385 if (*ListHead == NULL) {
1386 *ListHead = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
1387 if (*ListHead == NULL) {
1388 return (EFI_OUT_OF_RESOURCES);
1389 }
1390 InitializeListHead(&((*ListHead)->Link));
1391 }
1392 Status = mEfiShellProtocol->OpenFileList(Arg,
1393 OpenMode,
1394 ListHead);
1395 if (EFI_ERROR(Status)) {
1396 mEfiShellProtocol->RemoveDupInFileList(ListHead);
1397 } else {
1398 Status = mEfiShellProtocol->RemoveDupInFileList(ListHead);
1399 }
1400 return (Status);
1401 }
1402
1403 //
1404 // ASSERT that we must have EFI shell
1405 //
1406 ASSERT(mEfiShellEnvironment2 != NULL);
1407
1408 //
1409 // make sure the list head is initialized
1410 //
1411 InitializeListHead(&mOldStyleFileList);
1412
1413 //
1414 // Get the EFI Shell list of files
1415 //
1416 Status = mEfiShellEnvironment2->FileMetaArg(Arg, &mOldStyleFileList);
1417 if (EFI_ERROR(Status)) {
1418 *ListHead = NULL;
1419 return (Status);
1420 }
1421
1422 if (*ListHead == NULL) {
1423 *ListHead = (EFI_SHELL_FILE_INFO *)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
1424 if (*ListHead == NULL) {
1425 return (EFI_OUT_OF_RESOURCES);
1426 }
1427 }
1428
1429 //
1430 // Convert that to equivalent of UEFI Shell 2.0 structure
1431 //
1432 InternalShellConvertFileListType(&mOldStyleFileList, &(*ListHead)->Link);
1433
1434 //
1435 // Free the EFI Shell version that was converted.
1436 //
1437 mEfiShellEnvironment2->FreeFileList(&mOldStyleFileList);
1438
1439 return (Status);
1440 }
1441 /**
1442 Free the linked list returned from ShellOpenFileMetaArg
1443
1444 if ListHead is NULL then ASSERT()
1445
1446 @param ListHead the pointer to free
1447
1448 @retval EFI_SUCCESS the operation was sucessful
1449 **/
1450 EFI_STATUS
1451 EFIAPI
1452 ShellCloseFileMetaArg (
1453 IN OUT EFI_SHELL_FILE_INFO **ListHead
1454 )
1455 {
1456 LIST_ENTRY *Node;
1457
1458 //
1459 // ASSERT that ListHead is not NULL
1460 //
1461 ASSERT(ListHead != NULL);
1462
1463 //
1464 // Check for UEFI Shell 2.0 protocols
1465 //
1466 if (mEfiShellProtocol != NULL) {
1467 return (mEfiShellProtocol->FreeFileList(ListHead));
1468 } else {
1469 //
1470 // Since this is EFI Shell version we need to free our internally made copy
1471 // of the list
1472 //
1473 for ( Node = GetFirstNode(&(*ListHead)->Link)
1474 ; IsListEmpty(&(*ListHead)->Link) == FALSE
1475 ; Node = GetFirstNode(&(*ListHead)->Link)) {
1476 RemoveEntryList(Node);
1477 ((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle->Close(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Handle);
1478 FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FullName);
1479 FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->FileName);
1480 FreePool(((EFI_SHELL_FILE_INFO_NO_CONST*)Node)->Info);
1481 FreePool((EFI_SHELL_FILE_INFO_NO_CONST*)Node);
1482 }
1483 return EFI_SUCCESS;
1484 }
1485 }
1486
1487 /**
1488 Find a file by searching the CWD and then the path.
1489
1490 if FileName is NULL then ASSERT.
1491
1492 if the return value is not NULL then the memory must be caller freed.
1493
1494 @param FileName Filename string.
1495
1496 @retval NULL the file was not found
1497 @return !NULL the full path to the file.
1498 **/
1499 CHAR16 *
1500 EFIAPI
1501 ShellFindFilePath (
1502 IN CONST CHAR16 *FileName
1503 )
1504 {
1505 CONST CHAR16 *Path;
1506 EFI_FILE_HANDLE Handle;
1507 EFI_STATUS Status;
1508 CHAR16 *RetVal;
1509 CHAR16 *TestPath;
1510 CONST CHAR16 *Walker;
1511 UINTN Size;
1512
1513 RetVal = NULL;
1514
1515 Path = ShellGetEnvironmentVariable(L"cwd");
1516 if (Path != NULL) {
1517 Size = StrSize(Path);
1518 Size += StrSize(FileName);
1519 TestPath = AllocateZeroPool(Size);
1520 StrCpy(TestPath, Path);
1521 StrCat(TestPath, FileName);
1522 Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0);
1523 if (!EFI_ERROR(Status)){
1524 RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0);
1525 ShellCloseFile(&Handle);
1526 FreePool(TestPath);
1527 return (RetVal);
1528 }
1529 FreePool(TestPath);
1530 }
1531 Path = ShellGetEnvironmentVariable(L"path");
1532 if (Path != NULL) {
1533 Size = StrSize(Path);
1534 Size += StrSize(FileName);
1535 TestPath = AllocateZeroPool(Size);
1536 Walker = (CHAR16*)Path;
1537 do {
1538 CopyMem(TestPath, Walker, StrSize(Walker));
1539 if (StrStr(TestPath, L";") != NULL) {
1540 *(StrStr(TestPath, L";")) = CHAR_NULL;
1541 }
1542 StrCat(TestPath, FileName);
1543 if (StrStr(Walker, L";") != NULL) {
1544 Walker = StrStr(Walker, L";") + 1;
1545 } else {
1546 Walker = NULL;
1547 }
1548 Status = ShellOpenFileByName(TestPath, &Handle, EFI_FILE_MODE_READ, 0);
1549 if (!EFI_ERROR(Status)){
1550 RetVal = StrnCatGrow(&RetVal, NULL, TestPath, 0);
1551 ShellCloseFile(&Handle);
1552 break;
1553 }
1554 } while (Walker != NULL && Walker[0] != CHAR_NULL);
1555 FreePool(TestPath);
1556 }
1557 return (RetVal);
1558 }
1559
1560 typedef struct {
1561 LIST_ENTRY Link;
1562 CHAR16 *Name;
1563 ParamType Type;
1564 CHAR16 *Value;
1565 UINTN OriginalPosition;
1566 } SHELL_PARAM_PACKAGE;
1567
1568 /**
1569 Checks the list of valid arguments and returns TRUE if the item was found. If the
1570 return value is TRUE then the type parameter is set also.
1571
1572 if CheckList is NULL then ASSERT();
1573 if Name is NULL then ASSERT();
1574 if Type is NULL then ASSERT();
1575
1576 @param Type pointer to type of parameter if it was found
1577 @param Name pointer to Name of parameter found
1578 @param CheckList List to check against
1579
1580 @retval TRUE the Parameter was found. Type is valid.
1581 @retval FALSE the Parameter was not found. Type is not valid.
1582 **/
1583 BOOLEAN
1584 EFIAPI
1585 InternalIsOnCheckList (
1586 IN CONST CHAR16 *Name,
1587 IN CONST SHELL_PARAM_ITEM *CheckList,
1588 OUT ParamType *Type
1589 ) {
1590 SHELL_PARAM_ITEM *TempListItem;
1591
1592 //
1593 // ASSERT that all 3 pointer parameters aren't NULL
1594 //
1595 ASSERT(CheckList != NULL);
1596 ASSERT(Type != NULL);
1597 ASSERT(Name != NULL);
1598
1599 //
1600 // question mark and page break mode are always supported
1601 //
1602 if ((StrCmp(Name, L"-?") == 0) ||
1603 (StrCmp(Name, L"-b") == 0)
1604 ) {
1605 return (TRUE);
1606 }
1607
1608 //
1609 // Enumerate through the list
1610 //
1611 for (TempListItem = (SHELL_PARAM_ITEM*)CheckList ; TempListItem->Name != NULL ; TempListItem++) {
1612 //
1613 // If the Type is TypeStart only check the first characters of the passed in param
1614 // If it matches set the type and return TRUE
1615 //
1616 if (TempListItem->Type == TypeStart && StrnCmp(Name, TempListItem->Name, StrLen(TempListItem->Name)) == 0) {
1617 *Type = TempListItem->Type;
1618 return (TRUE);
1619 } else if (StrCmp(Name, TempListItem->Name) == 0) {
1620 *Type = TempListItem->Type;
1621 return (TRUE);
1622 }
1623 }
1624
1625 return (FALSE);
1626 }
1627 /**
1628 Checks the string for indicators of "flag" status. this is a leading '/', '-', or '+'
1629
1630 @param Name pointer to Name of parameter found
1631
1632 @retval TRUE the Parameter is a flag.
1633 @retval FALSE the Parameter not a flag
1634 **/
1635 BOOLEAN
1636 EFIAPI
1637 InternalIsFlag (
1638 IN CONST CHAR16 *Name,
1639 IN BOOLEAN AlwaysAllowNumbers
1640 )
1641 {
1642 //
1643 // ASSERT that Name isn't NULL
1644 //
1645 ASSERT(Name != NULL);
1646
1647 //
1648 // If we accept numbers then dont return TRUE. (they will be values)
1649 //
1650 if (((Name[0] == L'-' || Name[0] == L'+') && ShellInternalIsHexaDecimalDigitCharacter(Name[1])) && AlwaysAllowNumbers == TRUE) {
1651 return (FALSE);
1652 }
1653
1654 //
1655 // If the Name has a / or - as the first character return TRUE
1656 //
1657 if ((Name[0] == L'/') ||
1658 (Name[0] == L'-') ||
1659 (Name[0] == L'+')
1660 ) {
1661 return (TRUE);
1662 }
1663 return (FALSE);
1664 }
1665
1666 /**
1667 Checks the command line arguments passed against the list of valid ones.
1668
1669 If no initialization is required, then return RETURN_SUCCESS.
1670
1671 @param CheckList pointer to list of parameters to check
1672 @param CheckPackage pointer to pointer to list checked values
1673 @param ProblemParam optional pointer to pointer to unicode string for
1674 the paramater that caused failure. If used then the
1675 caller is responsible for freeing the memory.
1676 @param AutoPageBreak will automatically set PageBreakEnabled for "b" parameter
1677 @param Argc Count of parameters in Argv
1678 @param Argv pointer to array of parameters
1679
1680 @retval EFI_SUCCESS The operation completed sucessfully.
1681 @retval EFI_OUT_OF_RESOURCES A memory allocation failed
1682 @retval EFI_INVALID_PARAMETER A parameter was invalid
1683 @retval EFI_VOLUME_CORRUPTED the command line was corrupt. an argument was
1684 duplicated. the duplicated command line argument
1685 was returned in ProblemParam if provided.
1686 @retval EFI_NOT_FOUND a argument required a value that was missing.
1687 the invalid command line argument was returned in
1688 ProblemParam if provided.
1689 **/
1690 STATIC
1691 EFI_STATUS
1692 EFIAPI
1693 InternalCommandLineParse (
1694 IN CONST SHELL_PARAM_ITEM *CheckList,
1695 OUT LIST_ENTRY **CheckPackage,
1696 OUT CHAR16 **ProblemParam OPTIONAL,
1697 IN BOOLEAN AutoPageBreak,
1698 IN CONST CHAR16 **Argv,
1699 IN UINTN Argc,
1700 IN BOOLEAN AlwaysAllowNumbers
1701 ) {
1702 UINTN LoopCounter;
1703 ParamType CurrentItemType;
1704 SHELL_PARAM_PACKAGE *CurrentItemPackage;
1705 UINTN GetItemValue;
1706 UINTN ValueSize;
1707
1708 CurrentItemPackage = NULL;
1709 mTotalParameterCount = 0;
1710 GetItemValue = 0;
1711 ValueSize = 0;
1712
1713 //
1714 // If there is only 1 item we dont need to do anything
1715 //
1716 if (Argc <= 1) {
1717 *CheckPackage = NULL;
1718 return (EFI_SUCCESS);
1719 }
1720
1721 //
1722 // ASSERTs
1723 //
1724 ASSERT(CheckList != NULL);
1725 ASSERT(Argv != NULL);
1726
1727 //
1728 // initialize the linked list
1729 //
1730 *CheckPackage = (LIST_ENTRY*)AllocateZeroPool(sizeof(LIST_ENTRY));
1731 InitializeListHead(*CheckPackage);
1732
1733 //
1734 // loop through each of the arguments
1735 //
1736 for (LoopCounter = 0 ; LoopCounter < Argc ; ++LoopCounter) {
1737 if (Argv[LoopCounter] == NULL) {
1738 //
1739 // do nothing for NULL argv
1740 //
1741 } else if (InternalIsOnCheckList(Argv[LoopCounter], CheckList, &CurrentItemType) == TRUE) {
1742 //
1743 // We might have leftover if last parameter didnt have optional value
1744 //
1745 if (GetItemValue != 0) {
1746 GetItemValue = 0;
1747 InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
1748 }
1749 //
1750 // this is a flag
1751 //
1752 CurrentItemPackage = AllocatePool(sizeof(SHELL_PARAM_PACKAGE));
1753 ASSERT(CurrentItemPackage != NULL);
1754 CurrentItemPackage->Name = AllocatePool(StrSize(Argv[LoopCounter]));
1755 ASSERT(CurrentItemPackage->Name != NULL);
1756 StrCpy(CurrentItemPackage->Name, Argv[LoopCounter]);
1757 CurrentItemPackage->Type = CurrentItemType;
1758 CurrentItemPackage->OriginalPosition = (UINTN)(-1);
1759 CurrentItemPackage->Value = NULL;
1760
1761 //
1762 // Does this flag require a value
1763 //
1764 switch (CurrentItemPackage->Type) {
1765 //
1766 // possibly trigger the next loop(s) to populate the value of this item
1767 //
1768 case TypeValue:
1769 GetItemValue = 1;
1770 ValueSize = 0;
1771 break;
1772 case TypeDoubleValue:
1773 GetItemValue = 2;
1774 ValueSize = 0;
1775 break;
1776 case TypeMaxValue:
1777 GetItemValue = (UINTN)(-1);
1778 ValueSize = 0;
1779 break;
1780 default:
1781 //
1782 // this item has no value expected; we are done
1783 //
1784 InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
1785 ASSERT(GetItemValue == 0);
1786 break;
1787 }
1788 } else if (GetItemValue != 0 && InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers) == FALSE) {
1789 ASSERT(CurrentItemPackage != NULL);
1790 //
1791 // get the item VALUE for a previous flag
1792 //
1793 CurrentItemPackage->Value = ReallocatePool(ValueSize, ValueSize + StrSize(Argv[LoopCounter]) + sizeof(CHAR16), CurrentItemPackage->Value);
1794 ASSERT(CurrentItemPackage->Value != NULL);
1795 if (ValueSize == 0) {
1796 StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);
1797 } else {
1798 StrCat(CurrentItemPackage->Value, L" ");
1799 StrCat(CurrentItemPackage->Value, Argv[LoopCounter]);
1800 }
1801 ValueSize += StrSize(Argv[LoopCounter]) + sizeof(CHAR16);
1802 GetItemValue--;
1803 if (GetItemValue == 0) {
1804 InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
1805 }
1806 } else if (InternalIsFlag(Argv[LoopCounter], AlwaysAllowNumbers) == FALSE) {
1807 //
1808 // add this one as a non-flag
1809 //
1810 CurrentItemPackage = AllocatePool(sizeof(SHELL_PARAM_PACKAGE));
1811 ASSERT(CurrentItemPackage != NULL);
1812 CurrentItemPackage->Name = NULL;
1813 CurrentItemPackage->Type = TypePosition;
1814 CurrentItemPackage->Value = AllocatePool(StrSize(Argv[LoopCounter]));
1815 ASSERT(CurrentItemPackage->Value != NULL);
1816 StrCpy(CurrentItemPackage->Value, Argv[LoopCounter]);
1817 CurrentItemPackage->OriginalPosition = mTotalParameterCount++;
1818 InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
1819 } else if (ProblemParam) {
1820 //
1821 // this was a non-recognised flag... error!
1822 //
1823 *ProblemParam = AllocatePool(StrSize(Argv[LoopCounter]));
1824 ASSERT(*ProblemParam != NULL);
1825 StrCpy(*ProblemParam, Argv[LoopCounter]);
1826 ShellCommandLineFreeVarList(*CheckPackage);
1827 *CheckPackage = NULL;
1828 return (EFI_VOLUME_CORRUPTED);
1829 } else {
1830 ShellCommandLineFreeVarList(*CheckPackage);
1831 *CheckPackage = NULL;
1832 return (EFI_VOLUME_CORRUPTED);
1833 }
1834 }
1835 if (GetItemValue != 0) {
1836 GetItemValue = 0;
1837 InsertHeadList(*CheckPackage, &CurrentItemPackage->Link);
1838 }
1839 //
1840 // support for AutoPageBreak
1841 //
1842 if (AutoPageBreak && ShellCommandLineGetFlag(*CheckPackage, L"-b")) {
1843 ShellSetPageBreakMode(TRUE);
1844 }
1845 return (EFI_SUCCESS);
1846 }
1847
1848 /**
1849 Checks the command line arguments passed against the list of valid ones.
1850 Optionally removes NULL values first.
1851
1852 If no initialization is required, then return RETURN_SUCCESS.
1853
1854 @param CheckList pointer to list of parameters to check
1855 @param CheckPackage pointer to pointer to list checked values
1856 @param ProblemParam optional pointer to pointer to unicode string for
1857 the paramater that caused failure.
1858 @param AutoPageBreak will automatically set PageBreakEnabled for "b" parameter
1859
1860 @retval EFI_SUCCESS The operation completed sucessfully.
1861 @retval EFI_OUT_OF_RESOURCES A memory allocation failed
1862 @retval EFI_INVALID_PARAMETER A parameter was invalid
1863 @retval EFI_VOLUME_CORRUPTED the command line was corrupt. an argument was
1864 duplicated. the duplicated command line argument
1865 was returned in ProblemParam if provided.
1866 @retval EFI_DEVICE_ERROR the commands contained 2 opposing arguments. one
1867 of the command line arguments was returned in
1868 ProblemParam if provided.
1869 @retval EFI_NOT_FOUND a argument required a value that was missing.
1870 the invalid command line argument was returned in
1871 ProblemParam if provided.
1872 **/
1873 EFI_STATUS
1874 EFIAPI
1875 ShellCommandLineParseEx (
1876 IN CONST SHELL_PARAM_ITEM *CheckList,
1877 OUT LIST_ENTRY **CheckPackage,
1878 OUT CHAR16 **ProblemParam OPTIONAL,
1879 IN BOOLEAN AutoPageBreak,
1880 IN BOOLEAN AlwaysAllowNumbers
1881 ) {
1882 //
1883 // ASSERT that CheckList and CheckPackage aren't NULL
1884 //
1885 ASSERT(CheckList != NULL);
1886 ASSERT(CheckPackage != NULL);
1887
1888 //
1889 // Check for UEFI Shell 2.0 protocols
1890 //
1891 if (mEfiShellParametersProtocol != NULL) {
1892 return (InternalCommandLineParse(CheckList,
1893 CheckPackage,
1894 ProblemParam,
1895 AutoPageBreak,
1896 (CONST CHAR16**) mEfiShellParametersProtocol->Argv,
1897 mEfiShellParametersProtocol->Argc,
1898 AlwaysAllowNumbers));
1899 }
1900
1901 //
1902 // ASSERT That EFI Shell is not required
1903 //
1904 ASSERT (mEfiShellInterface != NULL);
1905 return (InternalCommandLineParse(CheckList,
1906 CheckPackage,
1907 ProblemParam,
1908 AutoPageBreak,
1909 (CONST CHAR16**) mEfiShellInterface->Argv,
1910 mEfiShellInterface->Argc,
1911 AlwaysAllowNumbers));
1912 }
1913
1914 /**
1915 Frees shell variable list that was returned from ShellCommandLineParse.
1916
1917 This function will free all the memory that was used for the CheckPackage
1918 list of postprocessed shell arguments.
1919
1920 this function has no return value.
1921
1922 if CheckPackage is NULL, then return
1923
1924 @param CheckPackage the list to de-allocate
1925 **/
1926 VOID
1927 EFIAPI
1928 ShellCommandLineFreeVarList (
1929 IN LIST_ENTRY *CheckPackage
1930 ) {
1931 LIST_ENTRY *Node;
1932
1933 //
1934 // check for CheckPackage == NULL
1935 //
1936 if (CheckPackage == NULL) {
1937 return;
1938 }
1939
1940 //
1941 // for each node in the list
1942 //
1943 for ( Node = GetFirstNode(CheckPackage)
1944 ; IsListEmpty(CheckPackage) == FALSE
1945 ; Node = GetFirstNode(CheckPackage)
1946 ){
1947 //
1948 // Remove it from the list
1949 //
1950 RemoveEntryList(Node);
1951
1952 //
1953 // if it has a name free the name
1954 //
1955 if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {
1956 FreePool(((SHELL_PARAM_PACKAGE*)Node)->Name);
1957 }
1958
1959 //
1960 // if it has a value free the value
1961 //
1962 if (((SHELL_PARAM_PACKAGE*)Node)->Value != NULL) {
1963 FreePool(((SHELL_PARAM_PACKAGE*)Node)->Value);
1964 }
1965
1966 //
1967 // free the node structure
1968 //
1969 FreePool((SHELL_PARAM_PACKAGE*)Node);
1970 }
1971 //
1972 // free the list head node
1973 //
1974 FreePool(CheckPackage);
1975 }
1976 /**
1977 Checks for presence of a flag parameter
1978
1979 flag arguments are in the form of "-<Key>" or "/<Key>", but do not have a value following the key
1980
1981 if CheckPackage is NULL then return FALSE.
1982 if KeyString is NULL then ASSERT()
1983
1984 @param CheckPackage The package of parsed command line arguments
1985 @param KeyString the Key of the command line argument to check for
1986
1987 @retval TRUE the flag is on the command line
1988 @retval FALSE the flag is not on the command line
1989 **/
1990 BOOLEAN
1991 EFIAPI
1992 ShellCommandLineGetFlag (
1993 IN CONST LIST_ENTRY *CheckPackage,
1994 IN CHAR16 *KeyString
1995 ) {
1996 LIST_ENTRY *Node;
1997
1998 //
1999 // ASSERT that both CheckPackage and KeyString aren't NULL
2000 //
2001 ASSERT(KeyString != NULL);
2002
2003 //
2004 // return FALSE for no package
2005 //
2006 if (CheckPackage == NULL) {
2007 return (FALSE);
2008 }
2009
2010 //
2011 // enumerate through the list of parametrs
2012 //
2013 for ( Node = GetFirstNode(CheckPackage)
2014 ; !IsNull (CheckPackage, Node)
2015 ; Node = GetNextNode(CheckPackage, Node)
2016 ){
2017 //
2018 // If the Name matches, return TRUE (and there may be NULL name)
2019 //
2020 if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {
2021 //
2022 // If Type is TypeStart then only compare the begining of the strings
2023 //
2024 if ( ((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart
2025 && StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0
2026 ){
2027 return (TRUE);
2028 } else if (StrCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {
2029 return (TRUE);
2030 }
2031 }
2032 }
2033 return (FALSE);
2034 }
2035 /**
2036 returns value from command line argument
2037
2038 value parameters are in the form of "-<Key> value" or "/<Key> value"
2039
2040 if CheckPackage is NULL, then return NULL;
2041
2042 @param CheckPackage The package of parsed command line arguments
2043 @param KeyString the Key of the command line argument to check for
2044
2045 @retval NULL the flag is not on the command line
2046 @return !=NULL pointer to unicode string of the value
2047 **/
2048 CONST CHAR16*
2049 EFIAPI
2050 ShellCommandLineGetValue (
2051 IN CONST LIST_ENTRY *CheckPackage,
2052 IN CHAR16 *KeyString
2053 ) {
2054 LIST_ENTRY *Node;
2055
2056 //
2057 // check for CheckPackage == NULL
2058 //
2059 if (CheckPackage == NULL) {
2060 return (NULL);
2061 }
2062
2063 //
2064 // enumerate through the list of parametrs
2065 //
2066 for ( Node = GetFirstNode(CheckPackage)
2067 ; !IsNull (CheckPackage, Node)
2068 ; Node = GetNextNode(CheckPackage, Node)
2069 ){
2070 //
2071 // If the Name matches, return the value (name can be NULL)
2072 //
2073 if (((SHELL_PARAM_PACKAGE*)Node)->Name != NULL) {
2074 //
2075 // If Type is TypeStart then only compare the begining of the strings
2076 //
2077 if ( ((SHELL_PARAM_PACKAGE*)Node)->Type == TypeStart
2078 && StrnCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name, StrLen(KeyString)) == 0
2079 ){
2080 //
2081 // return the string part after the flag
2082 //
2083 return (((SHELL_PARAM_PACKAGE*)Node)->Name + StrLen(KeyString));
2084 } else if (StrCmp(KeyString, ((SHELL_PARAM_PACKAGE*)Node)->Name) == 0) {
2085 //
2086 // return the value
2087 //
2088 return (((SHELL_PARAM_PACKAGE*)Node)->Value);
2089 }
2090 }
2091 }
2092 return (NULL);
2093 }
2094 /**
2095 returns raw value from command line argument
2096
2097 raw value parameters are in the form of "value" in a specific position in the list
2098
2099 if CheckPackage is NULL, then return NULL;
2100
2101 @param CheckPackage The package of parsed command line arguments
2102 @param Position the position of the value
2103
2104 @retval NULL the flag is not on the command line
2105 @return !=NULL pointer to unicode string of the value
2106 **/
2107 CONST CHAR16*
2108 EFIAPI
2109 ShellCommandLineGetRawValue (
2110 IN CONST LIST_ENTRY *CheckPackage,
2111 IN UINT32 Position
2112 ) {
2113 LIST_ENTRY *Node;
2114
2115 //
2116 // check for CheckPackage == NULL
2117 //
2118 if (CheckPackage == NULL) {
2119 return (NULL);
2120 }
2121
2122 //
2123 // enumerate through the list of parametrs
2124 //
2125 for ( Node = GetFirstNode(CheckPackage)
2126 ; !IsNull (CheckPackage, Node)
2127 ; Node = GetNextNode(CheckPackage, Node)
2128 ){
2129 //
2130 // If the position matches, return the value
2131 //
2132 if (((SHELL_PARAM_PACKAGE*)Node)->OriginalPosition == Position) {
2133 return (((SHELL_PARAM_PACKAGE*)Node)->Value);
2134 }
2135 }
2136 return (NULL);
2137 }
2138
2139 /**
2140 returns the number of command line value parameters that were parsed.
2141
2142 this will not include flags.
2143
2144 @retval (UINTN)-1 No parsing has ocurred
2145 @return other The number of value parameters found
2146 **/
2147 UINTN
2148 EFIAPI
2149 ShellCommandLineGetCount(
2150 VOID
2151 )
2152 {
2153 return (mTotalParameterCount);
2154 }
2155
2156 /**
2157 Determins if a parameter is duplicated.
2158
2159 If Param is not NULL then it will point to a callee allocated string buffer
2160 with the parameter value if a duplicate is found.
2161
2162 If CheckPackage is NULL, then ASSERT.
2163
2164 @param[in] CheckPackage The package of parsed command line arguments.
2165 @param[out] Param Upon finding one, a pointer to the duplicated parameter.
2166
2167 @retval EFI_SUCCESS No parameters were duplicated.
2168 @retval EFI_DEVICE_ERROR A duplicate was found.
2169 **/
2170 EFI_STATUS
2171 EFIAPI
2172 ShellCommandLineCheckDuplicate (
2173 IN CONST LIST_ENTRY *CheckPackage,
2174 OUT CHAR16 **Param
2175 )
2176 {
2177 LIST_ENTRY *Node1;
2178 LIST_ENTRY *Node2;
2179
2180 ASSERT(CheckPackage != NULL);
2181
2182 for ( Node1 = GetFirstNode(CheckPackage)
2183 ; !IsNull (CheckPackage, Node1)
2184 ; Node1 = GetNextNode(CheckPackage, Node1)
2185 ){
2186 for ( Node2 = GetNextNode(CheckPackage, Node1)
2187 ; !IsNull (CheckPackage, Node2)
2188 ; Node2 = GetNextNode(CheckPackage, Node2)
2189 ){
2190 if (StrCmp(((SHELL_PARAM_PACKAGE*)Node1)->Name, ((SHELL_PARAM_PACKAGE*)Node2)->Name) == 0) {
2191 if (Param != NULL) {
2192 *Param = NULL;
2193 *Param = StrnCatGrow(Param, NULL, ((SHELL_PARAM_PACKAGE*)Node1)->Name, 0);
2194 }
2195 return (EFI_DEVICE_ERROR);
2196 }
2197 }
2198 }
2199 return (EFI_SUCCESS);
2200 }
2201
2202 /**
2203 This is a find and replace function. it will return the NewString as a copy of
2204 SourceString with each instance of FindTarget replaced with ReplaceWith.
2205
2206 If the string would grow bigger than NewSize it will halt and return error.
2207
2208 @param[in] SourceString String with source buffer
2209 @param[in,out] NewString String with resultant buffer
2210 @param[in] NewSize Size in bytes of NewString
2211 @param[in] FindTarget String to look for
2212 @param[in[ ReplaceWith String to replace FindTarget with
2213
2214 @retval EFI_INVALID_PARAMETER SourceString was NULL
2215 @retval EFI_INVALID_PARAMETER NewString was NULL
2216 @retval EFI_INVALID_PARAMETER FindTarget was NULL
2217 @retval EFI_INVALID_PARAMETER ReplaceWith was NULL
2218 @retval EFI_INVALID_PARAMETER FindTarget had length < 1
2219 @retval EFI_INVALID_PARAMETER SourceString had length < 1
2220 @retval EFI_BUFFER_TOO_SMALL NewSize was less than the minimum size to hold
2221 the new string (truncation occurred)
2222 @retval EFI_SUCCESS the string was sucessfully copied with replacement
2223 **/
2224
2225 EFI_STATUS
2226 EFIAPI
2227 CopyReplace(
2228 IN CHAR16 CONST *SourceString,
2229 IN CHAR16 *NewString,
2230 IN UINTN NewSize,
2231 IN CONST CHAR16 *FindTarget,
2232 IN CONST CHAR16 *ReplaceWith
2233 )
2234 {
2235 UINTN Size;
2236 if ( (SourceString == NULL)
2237 || (NewString == NULL)
2238 || (FindTarget == NULL)
2239 || (ReplaceWith == NULL)
2240 || (StrLen(FindTarget) < 1)
2241 || (StrLen(SourceString) < 1)
2242 ){
2243 return (EFI_INVALID_PARAMETER);
2244 }
2245 NewString = SetMem16(NewString, NewSize, CHAR_NULL);
2246 while (*SourceString != CHAR_NULL) {
2247 if (StrnCmp(SourceString, FindTarget, StrLen(FindTarget)) == 0) {
2248 SourceString += StrLen(FindTarget);
2249 Size = StrSize(NewString);
2250 if ((Size + (StrLen(ReplaceWith)*sizeof(CHAR16))) > NewSize) {
2251 return (EFI_BUFFER_TOO_SMALL);
2252 }
2253 StrCat(NewString, ReplaceWith);
2254 } else {
2255 Size = StrSize(NewString);
2256 if (Size + sizeof(CHAR16) > NewSize) {
2257 return (EFI_BUFFER_TOO_SMALL);
2258 }
2259 StrnCat(NewString, SourceString, 1);
2260 SourceString++;
2261 }
2262 }
2263 return (EFI_SUCCESS);
2264 }
2265
2266 /**
2267 Internal worker function to output a string.
2268
2269 This function will output a string to the correct StdOut.
2270
2271 @param[in] String The string to print out.
2272
2273 @retval EFI_SUCCESS The operation was sucessful.
2274 @retval !EFI_SUCCESS The operation failed.
2275 **/
2276 EFI_STATUS
2277 EFIAPI
2278 InternalPrintTo (
2279 IN CONST CHAR16 *String
2280 )
2281 {
2282 UINTN Size;
2283 Size = StrSize(String) - sizeof(CHAR16);
2284 if (mEfiShellParametersProtocol != NULL) {
2285 return (mEfiShellParametersProtocol->StdOut->Write(mEfiShellParametersProtocol->StdOut, &Size, (VOID*)String));
2286 }
2287 if (mEfiShellInterface != NULL) {
2288 //
2289 // Divide in half for old shell. Must be string length not size.
2290 //
2291 Size /= 2;
2292 return ( mEfiShellInterface->StdOut->Write(mEfiShellInterface->StdOut, &Size, (VOID*)String));
2293 }
2294 ASSERT(FALSE);
2295 return (EFI_UNSUPPORTED);
2296 }
2297
2298 /**
2299 Print at a specific location on the screen.
2300
2301 This function will move the cursor to a given screen location and print the specified string
2302
2303 If -1 is specified for either the Row or Col the current screen location for BOTH
2304 will be used.
2305
2306 if either Row or Col is out of range for the current console, then ASSERT
2307 if Format is NULL, then ASSERT
2308
2309 In addition to the standard %-based flags as supported by UefiLib Print() this supports
2310 the following additional flags:
2311 %N - Set output attribute to normal
2312 %H - Set output attribute to highlight
2313 %E - Set output attribute to error
2314 %B - Set output attribute to blue color
2315 %V - Set output attribute to green color
2316
2317 Note: The background color is controlled by the shell command cls.
2318
2319 @param[in] Row the row to print at
2320 @param[in] Col the column to print at
2321 @param[in] Format the format string
2322 @param[in] Marker the marker for the variable argument list
2323
2324 @return the number of characters printed to the screen
2325 **/
2326
2327 UINTN
2328 EFIAPI
2329 InternalShellPrintWorker(
2330 IN INT32 Col OPTIONAL,
2331 IN INT32 Row OPTIONAL,
2332 IN CONST CHAR16 *Format,
2333 VA_LIST Marker
2334 )
2335 {
2336 UINTN Return;
2337 EFI_STATUS Status;
2338 UINTN NormalAttribute;
2339 CHAR16 *ResumeLocation;
2340 CHAR16 *FormatWalker;
2341
2342 //
2343 // Back and forth each time fixing up 1 of our flags...
2344 //
2345 Status = CopyReplace(Format, mPostReplaceFormat, PcdGet16 (PcdShellLibMaxPrintBufferSize), L"%N", L"%%N");
2346 ASSERT_EFI_ERROR(Status);
2347 Status = CopyReplace(mPostReplaceFormat, mPostReplaceFormat2, PcdGet16 (PcdShellLibMaxPrintBufferSize), L"%E", L"%%E");
2348 ASSERT_EFI_ERROR(Status);
2349 Status = CopyReplace(mPostReplaceFormat2, mPostReplaceFormat, PcdGet16 (PcdShellLibMaxPrintBufferSize), L"%H", L"%%H");
2350 ASSERT_EFI_ERROR(Status);
2351 Status = CopyReplace(mPostReplaceFormat, mPostReplaceFormat2, PcdGet16 (PcdShellLibMaxPrintBufferSize), L"%B", L"%%B");
2352 ASSERT_EFI_ERROR(Status);
2353 Status = CopyReplace(mPostReplaceFormat2, mPostReplaceFormat, PcdGet16 (PcdShellLibMaxPrintBufferSize), L"%V", L"%%V");
2354 ASSERT_EFI_ERROR(Status);
2355
2356 //
2357 // Use the last buffer from replacing to print from...
2358 //
2359 Return = UnicodeVSPrint (mPostReplaceFormat2, PcdGet16 (PcdShellLibMaxPrintBufferSize), mPostReplaceFormat, Marker);
2360
2361 if (Col != -1 && Row != -1) {
2362 Status = gST->ConOut->SetCursorPosition(gST->ConOut, Col, Row);
2363 ASSERT_EFI_ERROR(Status);
2364 }
2365
2366 NormalAttribute = gST->ConOut->Mode->Attribute;
2367 FormatWalker = mPostReplaceFormat2;
2368 while (*FormatWalker != CHAR_NULL) {
2369 //
2370 // Find the next attribute change request
2371 //
2372 ResumeLocation = StrStr(FormatWalker, L"%");
2373 if (ResumeLocation != NULL) {
2374 *ResumeLocation = CHAR_NULL;
2375 }
2376 //
2377 // print the current FormatWalker string
2378 //
2379 Status = InternalPrintTo(FormatWalker);
2380 ASSERT_EFI_ERROR(Status);
2381 //
2382 // update the attribute
2383 //
2384 if (ResumeLocation != NULL) {
2385 switch (*(ResumeLocation+1)) {
2386 case (L'N'):
2387 gST->ConOut->SetAttribute(gST->ConOut, NormalAttribute);
2388 break;
2389 case (L'E'):
2390 gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_YELLOW, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));
2391 break;
2392 case (L'H'):
2393 gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_WHITE, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));
2394 break;
2395 case (L'B'):
2396 gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_BLUE, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));
2397 break;
2398 case (L'V'):
2399 gST->ConOut->SetAttribute(gST->ConOut, EFI_TEXT_ATTR(EFI_GREEN, ((NormalAttribute&(BIT4|BIT5|BIT6))>>4)));
2400 break;
2401 default:
2402 //
2403 // Print a simple '%' symbol
2404 //
2405 Status = InternalPrintTo(L"%");
2406 ASSERT_EFI_ERROR(Status);
2407 ResumeLocation = ResumeLocation - 1;
2408 break;
2409 }
2410 } else {
2411 //
2412 // reset to normal now...
2413 //
2414 gST->ConOut->SetAttribute(gST->ConOut, NormalAttribute);
2415 break;
2416 }
2417
2418 //
2419 // update FormatWalker to Resume + 2 (skip the % and the indicator)
2420 //
2421 FormatWalker = ResumeLocation + 2;
2422 }
2423
2424 return (Return);
2425 }
2426
2427 /**
2428 Print at a specific location on the screen.
2429
2430 This function will move the cursor to a given screen location and print the specified string.
2431
2432 If -1 is specified for either the Row or Col the current screen location for BOTH
2433 will be used.
2434
2435 If either Row or Col is out of range for the current console, then ASSERT.
2436 If Format is NULL, then ASSERT.
2437
2438 In addition to the standard %-based flags as supported by UefiLib Print() this supports
2439 the following additional flags:
2440 %N - Set output attribute to normal
2441 %H - Set output attribute to highlight
2442 %E - Set output attribute to error
2443 %B - Set output attribute to blue color
2444 %V - Set output attribute to green color
2445
2446 Note: The background color is controlled by the shell command cls.
2447
2448 @param[in] Row the row to print at
2449 @param[in] Col the column to print at
2450 @param[in] Format the format string
2451
2452 @return the number of characters printed to the screen
2453 **/
2454
2455 UINTN
2456 EFIAPI
2457 ShellPrintEx(
2458 IN INT32 Col OPTIONAL,
2459 IN INT32 Row OPTIONAL,
2460 IN CONST CHAR16 *Format,
2461 ...
2462 )
2463 {
2464 VA_LIST Marker;
2465 EFI_STATUS Status;
2466 VA_START (Marker, Format);
2467 Status = InternalShellPrintWorker(Col, Row, Format, Marker);
2468 VA_END(Marker);
2469 return(Status);
2470 }
2471
2472 /**
2473 Print at a specific location on the screen.
2474
2475 This function will move the cursor to a given screen location and print the specified string.
2476
2477 If -1 is specified for either the Row or Col the current screen location for BOTH
2478 will be used.
2479
2480 If either Row or Col is out of range for the current console, then ASSERT.
2481 If Format is NULL, then ASSERT.
2482
2483 In addition to the standard %-based flags as supported by UefiLib Print() this supports
2484 the following additional flags:
2485 %N - Set output attribute to normal
2486 %H - Set output attribute to highlight
2487 %E - Set output attribute to error
2488 %B - Set output attribute to blue color
2489 %V - Set output attribute to green color
2490
2491 Note: The background color is controlled by the shell command cls.
2492
2493 @param[in] Row the row to print at
2494 @param[in] Col the column to print at
2495 @param[in] HiiFormatStringId the format string Id for getting from Hii
2496 @param[in] HiiFormatHandle the format string Handle for getting from Hii
2497
2498 @return the number of characters printed to the screen
2499 **/
2500 UINTN
2501 EFIAPI
2502 ShellPrintHiiEx(
2503 IN INT32 Col OPTIONAL,
2504 IN INT32 Row OPTIONAL,
2505 IN CONST EFI_STRING_ID HiiFormatStringId,
2506 IN CONST EFI_HANDLE HiiFormatHandle,
2507 ...
2508 )
2509 {
2510 VA_LIST Marker;
2511 CHAR16 *HiiFormatString;
2512 UINTN RetVal;
2513
2514 VA_START (Marker, HiiFormatHandle);
2515 HiiFormatString = HiiGetString(HiiFormatHandle, HiiFormatStringId, NULL);
2516 ASSERT(HiiFormatString != NULL);
2517
2518 RetVal = InternalShellPrintWorker(Col, Row, HiiFormatString, Marker);
2519
2520 FreePool(HiiFormatString);
2521 VA_END(Marker);
2522
2523 return (RetVal);
2524 }
2525
2526 /**
2527 Function to determine if a given filename represents a file or a directory.
2528
2529 @param[in] DirName Path to directory to test.
2530
2531 @retval EFI_SUCCESS The Path represents a directory
2532 @retval EFI_NOT_FOUND The Path does not represent a directory
2533 @return other The path failed to open
2534 **/
2535 EFI_STATUS
2536 EFIAPI
2537 ShellIsDirectory(
2538 IN CONST CHAR16 *DirName
2539 )
2540 {
2541 EFI_STATUS Status;
2542 EFI_FILE_HANDLE Handle;
2543
2544 ASSERT(DirName != NULL);
2545
2546 Handle = NULL;
2547
2548 Status = ShellOpenFileByName(DirName, &Handle, EFI_FILE_MODE_READ, 0);
2549 if (EFI_ERROR(Status)) {
2550 return (Status);
2551 }
2552
2553 if (FileHandleIsDirectory(Handle) == EFI_SUCCESS) {
2554 ShellCloseFile(&Handle);
2555 return (EFI_SUCCESS);
2556 }
2557 ShellCloseFile(&Handle);
2558 return (EFI_NOT_FOUND);
2559 }
2560
2561 /**
2562 Function to determine if a given filename represents a file.
2563
2564 @param[in] Name Path to file to test.
2565
2566 @retval EFI_SUCCESS The Path represents a file.
2567 @retval EFI_NOT_FOUND The Path does not represent a file.
2568 @retval other The path failed to open.
2569 **/
2570 EFI_STATUS
2571 EFIAPI
2572 ShellIsFile(
2573 IN CONST CHAR16 *Name
2574 )
2575 {
2576 EFI_STATUS Status;
2577 EFI_FILE_HANDLE Handle;
2578
2579 ASSERT(Name != NULL);
2580
2581 Handle = NULL;
2582
2583 Status = ShellOpenFileByName(Name, &Handle, EFI_FILE_MODE_READ, 0);
2584 if (EFI_ERROR(Status)) {
2585 return (Status);
2586 }
2587
2588 if (FileHandleIsDirectory(Handle) != EFI_SUCCESS) {
2589 ShellCloseFile(&Handle);
2590 return (EFI_SUCCESS);
2591 }
2592 ShellCloseFile(&Handle);
2593 return (EFI_NOT_FOUND);
2594 }
2595
2596 /**
2597 Function to determine whether a string is decimal or hex representation of a number
2598 and return the number converted from the string.
2599
2600 @param[in] String String representation of a number
2601
2602 @retval all the number
2603 **/
2604 UINTN
2605 EFIAPI
2606 ShellStrToUintn(
2607 IN CONST CHAR16 *String
2608 )
2609 {
2610 CONST CHAR16 *Walker;
2611 for (Walker = String; Walker != NULL && *Walker != CHAR_NULL && *Walker == L' '; Walker = Walker + 1);
2612 if (StrnCmp(Walker, L"0x", 2) == 0 || StrnCmp(Walker, L"0X", 2) == 0){
2613 return (StrHexToUintn(Walker));
2614 }
2615 return (StrDecimalToUintn(Walker));
2616 }
2617
2618 /**
2619 Safely append with automatic string resizing given length of Destination and
2620 desired length of copy from Source.
2621
2622 append the first D characters of Source to the end of Destination, where D is
2623 the lesser of Count and the StrLen() of Source. If appending those D characters
2624 will fit within Destination (whose Size is given as CurrentSize) and
2625 still leave room for a null terminator, then those characters are appended,
2626 starting at the original terminating null of Destination, and a new terminating
2627 null is appended.
2628
2629 If appending D characters onto Destination will result in a overflow of the size
2630 given in CurrentSize the string will be grown such that the copy can be performed
2631 and CurrentSize will be updated to the new size.
2632
2633 If Source is NULL, there is nothing to append, just return the current buffer in
2634 Destination.
2635
2636 if Destination is NULL, then ASSERT()
2637 if Destination's current length (including NULL terminator) is already more then
2638 CurrentSize, then ASSERT()
2639
2640 @param[in,out] Destination The String to append onto
2641 @param[in,out] CurrentSize on call the number of bytes in Destination. On
2642 return possibly the new size (still in bytes). if NULL
2643 then allocate whatever is needed.
2644 @param[in] Source The String to append from
2645 @param[in] Count Maximum number of characters to append. if 0 then
2646 all are appended.
2647
2648 @return Destination return the resultant string.
2649 **/
2650 CHAR16*
2651 EFIAPI
2652 StrnCatGrow (
2653 IN OUT CHAR16 **Destination,
2654 IN OUT UINTN *CurrentSize,
2655 IN CONST CHAR16 *Source,
2656 IN UINTN Count
2657 )
2658 {
2659 UINTN DestinationStartSize;
2660 UINTN NewSize;
2661
2662 //
2663 // ASSERTs
2664 //
2665 ASSERT(Destination != NULL);
2666
2667 //
2668 // If there's nothing to do then just return Destination
2669 //
2670 if (Source == NULL) {
2671 return (*Destination);
2672 }
2673
2674 //
2675 // allow for un-initialized pointers, based on size being 0
2676 //
2677 if (CurrentSize != NULL && *CurrentSize == 0) {
2678 *Destination = NULL;
2679 }
2680
2681 //
2682 // allow for NULL pointers address as Destination
2683 //
2684 if (*Destination != NULL) {
2685 ASSERT(CurrentSize != 0);
2686 DestinationStartSize = StrSize(*Destination);
2687 ASSERT(DestinationStartSize <= *CurrentSize);
2688 } else {
2689 DestinationStartSize = 0;
2690 // ASSERT(*CurrentSize == 0);
2691 }
2692
2693 //
2694 // Append all of Source?
2695 //
2696 if (Count == 0) {
2697 Count = StrLen(Source);
2698 }
2699
2700 //
2701 // Test and grow if required
2702 //
2703 if (CurrentSize != NULL) {
2704 NewSize = *CurrentSize;
2705 while (NewSize < (DestinationStartSize + (Count*sizeof(CHAR16)))) {
2706 NewSize += 2 * Count * sizeof(CHAR16);
2707 }
2708 *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination);
2709 *CurrentSize = NewSize;
2710 } else {
2711 *Destination = AllocateZeroPool((Count+1)*sizeof(CHAR16));
2712 }
2713
2714 //
2715 // Now use standard StrnCat on a big enough buffer
2716 //
2717 return StrnCat(*Destination, Source, Count);
2718 }