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