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