ShellPkg: Add checking for memory allocation and pointer returns from functions.
[mirror_edk2.git] / ShellPkg / Application / Shell / ShellProtocol.c
1 /** @file\r
2   Member functions of EFI_SHELL_PROTOCOL and functions for creation,\r
3   manipulation, and initialization of EFI_SHELL_PROTOCOL.\r
4 \r
5   Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
6   This program and the accompanying materials\r
7   are licensed and made available under the terms and conditions of the BSD License\r
8   which accompanies this distribution.  The full text of the license may be found at\r
9   http://opensource.org/licenses/bsd-license.php\r
10 \r
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 \r
14 **/\r
15 \r
16 #include "Shell.h"\r
17 #include <Library/FileHandleLib.h>\r
18 \r
19 /**\r
20   Close an open file handle.\r
21 \r
22   This function closes a specified file handle. All "dirty" cached file data is\r
23   flushed to the device, and the file is closed. In all cases the handle is\r
24   closed.\r
25 \r
26   @param[in] FileHandle           The file handle to close.\r
27 \r
28   @retval EFI_SUCCESS             The file handle was closed successfully.\r
29 **/\r
30 EFI_STATUS\r
31 EFIAPI\r
32 EfiShellClose (\r
33   IN SHELL_FILE_HANDLE            FileHandle\r
34   )\r
35 {\r
36   ShellFileHandleRemove(FileHandle);\r
37   return (FileHandleClose(ConvertShellHandleToEfiFileProtocol(FileHandle)));\r
38 }\r
39 \r
40 /**\r
41   Internal worker to determine whether there is a BlockIo somewhere\r
42   upon the device path specified.\r
43 \r
44   @param[in] DevicePath    The device path to test.\r
45 \r
46   @retval TRUE      gEfiBlockIoProtocolGuid was installed on a handle with this device path\r
47   @retval FALSE     gEfiBlockIoProtocolGuid was not found.\r
48 **/\r
49 BOOLEAN\r
50 EFIAPI\r
51 InternalShellProtocolIsBlockIoPresent(\r
52   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
53   )\r
54 {\r
55   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;\r
56   EFI_STATUS                Status;\r
57   EFI_HANDLE                Handle;\r
58 \r
59   Handle = NULL;\r
60 \r
61   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;\r
62   Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);\r
63 \r
64   if ((Handle != NULL) && (!EFI_ERROR(Status))) {\r
65     return (TRUE);\r
66   }\r
67   return (FALSE);\r
68 }\r
69 \r
70 /**\r
71   Internal worker to determine whether there is a file system somewhere\r
72   upon the device path specified.\r
73 \r
74   @param[in] DevicePath    The device path to test.\r
75 \r
76   @retval TRUE      gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path\r
77   @retval FALSE     gEfiSimpleFileSystemProtocolGuid was not found.\r
78 **/\r
79 BOOLEAN\r
80 EFIAPI\r
81 InternalShellProtocolIsSimpleFileSystemPresent(\r
82   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
83   )\r
84 {\r
85   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;\r
86   EFI_STATUS                Status;\r
87   EFI_HANDLE                Handle;\r
88 \r
89   Handle = NULL;\r
90 \r
91   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;\r
92   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);\r
93 \r
94   if ((Handle != NULL) && (!EFI_ERROR(Status))) {\r
95     return (TRUE);\r
96   }\r
97   return (FALSE);\r
98 }\r
99 \r
100 /**\r
101   Internal worker debug helper function to print out maps as they are added.\r
102 \r
103   @param[in] Mapping        string mapping that has been added\r
104   @param[in] DevicePath     pointer to device path that has been mapped.\r
105 \r
106   @retval EFI_SUCCESS   the operation was successful.\r
107   @return other         an error ocurred\r
108 \r
109   @sa LocateHandle\r
110   @sa OpenProtocol\r
111 **/\r
112 EFI_STATUS\r
113 EFIAPI\r
114 InternalShellProtocolDebugPrintMessage (\r
115   IN CONST CHAR16                   *Mapping,\r
116   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
117   )\r
118 {\r
119   EFI_DEVICE_PATH_TO_TEXT_PROTOCOL  *DevicePathToText;\r
120   EFI_STATUS                        Status;\r
121   CHAR16                            *Temp;\r
122 \r
123   Status = EFI_SUCCESS;\r
124   DEBUG_CODE_BEGIN();\r
125   DevicePathToText = NULL;\r
126 \r
127   Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid,\r
128                                NULL,\r
129                                (VOID**)&DevicePathToText);\r
130   if (Mapping != NULL) {\r
131     DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping));\r
132   }\r
133   if (!EFI_ERROR(Status)) {\r
134     if (DevicePath != NULL) {\r
135       Temp = DevicePathToText->ConvertDevicePathToText(DevicePath, TRUE, TRUE);\r
136       DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp));\r
137       FreePool(Temp);\r
138     }\r
139   }\r
140   DEBUG_CODE_END();\r
141   return (Status);\r
142 }\r
143 \r
144 /**\r
145   This function creates a mapping for a device path.\r
146 \r
147   If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.\r
148 \r
149   @param DevicePath             Points to the device path. If this is NULL and Mapping points to a valid mapping,\r
150                                 then the mapping will be deleted.\r
151   @param Mapping                Points to the NULL-terminated mapping for the device path.  Must end with a ':'\r
152 \r
153   @retval EFI_SUCCESS           Mapping created or deleted successfully.\r
154   @retval EFI_NO_MAPPING        There is no handle that corresponds exactly to DevicePath. See the\r
155                                 boot service function LocateDevicePath().\r
156   @retval EFI_ACCESS_DENIED     The mapping is a built-in alias.\r
157   @retval EFI_INVALID_PARAMETER Mapping was NULL\r
158   @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'\r
159   @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.\r
160   @retval EFI_NOT_FOUND         There was no mapping found to delete\r
161   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed\r
162 **/\r
163 EFI_STATUS\r
164 EFIAPI\r
165 EfiShellSetMap(\r
166   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,\r
167   IN CONST CHAR16 *Mapping\r
168   )\r
169 {\r
170   EFI_STATUS      Status;\r
171   SHELL_MAP_LIST  *MapListNode;\r
172 \r
173   if (Mapping == NULL){\r
174     return (EFI_INVALID_PARAMETER);\r
175   }\r
176 \r
177   if (Mapping[StrLen(Mapping)-1] != ':') {\r
178     return (EFI_INVALID_PARAMETER);\r
179   }\r
180 \r
181   //\r
182   // Delete the mapping\r
183   //\r
184   if (DevicePath == NULL) {\r
185     if (IsListEmpty(&gShellMapList.Link)) {\r
186       return (EFI_NOT_FOUND);\r
187     }\r
188     for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)\r
189         ; !IsNull(&gShellMapList.Link, &MapListNode->Link)\r
190         ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)\r
191        ){\r
192           if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {\r
193             RemoveEntryList(&MapListNode->Link);\r
194             FreePool(MapListNode);\r
195             return (EFI_SUCCESS);\r
196           }\r
197     } // for loop\r
198 \r
199     //\r
200     // We didnt find one to delete\r
201     //\r
202     return (EFI_NOT_FOUND);\r
203   }\r
204 \r
205   //\r
206   // make sure this is a valid to add device path\r
207   //\r
208   ///@todo add BlockIo to this test...\r
209   if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)\r
210     && !InternalShellProtocolIsBlockIoPresent(DevicePath)) {\r
211     return (EFI_INVALID_PARAMETER);\r
212   }\r
213 \r
214   //\r
215   // First make sure there is no old mapping\r
216   //\r
217   Status = EfiShellSetMap(NULL, Mapping);\r
218   if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {\r
219     return (Status);\r
220   }\r
221 \r
222   //\r
223   // now add the new one.\r
224   //\r
225   Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);\r
226 \r
227   return(Status);\r
228 }\r
229 \r
230 /**\r
231   Gets the device path from the mapping.\r
232 \r
233   This function gets the device path associated with a mapping.\r
234 \r
235   @param Mapping                A pointer to the mapping\r
236 \r
237   @retval !=NULL                Pointer to the device path that corresponds to the\r
238                                 device mapping. The returned pointer does not need\r
239                                 to be freed.\r
240   @retval NULL                  There is no device path associated with the\r
241                                 specified mapping.\r
242 **/\r
243 CONST EFI_DEVICE_PATH_PROTOCOL *\r
244 EFIAPI\r
245 EfiShellGetDevicePathFromMap(\r
246   IN CONST CHAR16 *Mapping\r
247   )\r
248 {\r
249   SHELL_MAP_LIST  *MapListItem;\r
250   CHAR16          *NewName;\r
251   UINTN           Size;\r
252 \r
253   NewName = NULL;\r
254   Size    = 0;\r
255 \r
256   StrnCatGrow(&NewName, &Size, Mapping, 0);\r
257   if (Mapping[StrLen(Mapping)-1] != L':') {\r
258     StrnCatGrow(&NewName, &Size, L":", 0);\r
259   }\r
260 \r
261   MapListItem = ShellCommandFindMapItem(NewName);\r
262 \r
263   FreePool(NewName);\r
264 \r
265   if (MapListItem != NULL) {\r
266     return (MapListItem->DevicePath);\r
267   }\r
268   return(NULL);\r
269 }\r
270 \r
271 /**\r
272   Gets the mapping(s) that most closely matches the device path.\r
273 \r
274   This function gets the mapping which corresponds to the device path *DevicePath. If\r
275   there is no exact match, then the mapping which most closely matches *DevicePath\r
276   is returned, and *DevicePath is updated to point to the remaining portion of the\r
277   device path. If there is an exact match, the mapping is returned and *DevicePath\r
278   points to the end-of-device-path node.\r
279 \r
280   If there are multiple map names they will be semi-colon seperated in the\r
281   NULL-terminated string.\r
282 \r
283   @param DevicePath             On entry, points to a device path pointer. On\r
284                                 exit, updates the pointer to point to the\r
285                                 portion of the device path after the mapping.\r
286 \r
287   @retval NULL                  No mapping was found.\r
288   @return !=NULL                Pointer to NULL-terminated mapping. The buffer\r
289                                 is callee allocated and should be freed by the caller.\r
290 **/\r
291 CONST CHAR16 *\r
292 EFIAPI\r
293 EfiShellGetMapFromDevicePath(\r
294   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
295   )\r
296 {\r
297   SHELL_MAP_LIST              *Node;\r
298   CHAR16                      *PathForReturn;\r
299   UINTN                       PathSize;\r
300 //  EFI_HANDLE                  PathHandle;\r
301 //  EFI_HANDLE                  MapHandle;\r
302 //  EFI_STATUS                  Status;\r
303 //  EFI_DEVICE_PATH_PROTOCOL    *DevicePathCopy;\r
304 //  EFI_DEVICE_PATH_PROTOCOL    *MapPathCopy;\r
305 \r
306   if (DevicePath == NULL || *DevicePath == NULL) {\r
307     return (NULL);\r
308   }\r
309 \r
310   PathForReturn = NULL;\r
311   PathSize      = 0;\r
312 \r
313   for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)\r
314       ; !IsNull(&gShellMapList.Link, &Node->Link)\r
315       ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)\r
316      ){\r
317     //\r
318     // check for exact match\r
319     //\r
320     if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {\r
321       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));\r
322       if (PathSize != 0) {\r
323         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);\r
324       }\r
325       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);\r
326     }\r
327   }\r
328   if (PathForReturn != NULL) {\r
329     while (!IsDevicePathEndType (*DevicePath)) {\r
330       *DevicePath = NextDevicePathNode (*DevicePath);\r
331     }\r
332     SetDevicePathEndNode (*DevicePath);\r
333   }\r
334 /*\r
335   ///@todo finish code for inexact matches.\r
336   if (PathForReturn == NULL) {\r
337     PathSize = 0;\r
338 \r
339     DevicePathCopy = DuplicateDevicePath(*DevicePath);\r
340     ASSERT(DevicePathCopy != NULL);\r
341     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);\r
342     ASSERT_EFI_ERROR(Status);\r
343     //\r
344     //  check each of the device paths we have to get the root of the path for consist mappings\r
345     //\r
346     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)\r
347         ; !IsNull(&gShellMapList.Link, &Node->Link)\r
348         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)\r
349        ){\r
350       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {\r
351         continue;\r
352       }\r
353       MapPathCopy = DuplicateDevicePath(Node->DevicePath);\r
354       ASSERT(MapPathCopy != NULL);\r
355       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);\r
356       if (MapHandle == PathHandle) {\r
357 \r
358         *DevicePath = DevicePathCopy;\r
359 \r
360         MapPathCopy = NULL;\r
361         DevicePathCopy = NULL;\r
362         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);\r
363         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);\r
364         break;\r
365       }\r
366     }\r
367     //\r
368     // now add on the non-consistent mappings\r
369     //\r
370     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)\r
371         ; !IsNull(&gShellMapList.Link, &Node->Link)\r
372         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)\r
373        ){\r
374       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {\r
375         continue;\r
376       }\r
377       MapPathCopy = Node->DevicePath;\r
378       ASSERT(MapPathCopy != NULL);\r
379       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);\r
380       if (MapHandle == PathHandle) {\r
381         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);\r
382         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);\r
383         break;\r
384       }\r
385     }\r
386   }\r
387 */\r
388 \r
389   return (AddBufferToFreeList(PathForReturn));\r
390 }\r
391 \r
392 /**\r
393   Converts a device path to a file system-style path.\r
394 \r
395   This function converts a device path to a file system path by replacing part, or all, of\r
396   the device path with the file-system mapping. If there are more than one application\r
397   file system mappings, the one that most closely matches Path will be used.\r
398 \r
399   @param Path                   The pointer to the device path\r
400 \r
401   @retval NULL                  the device path could not be found.\r
402   @return all                   The pointer of the NULL-terminated file path. The path\r
403                                 is callee-allocated and should be freed by the caller.\r
404 **/\r
405 CHAR16 *\r
406 EFIAPI\r
407 EfiShellGetFilePathFromDevicePath(\r
408   IN CONST EFI_DEVICE_PATH_PROTOCOL *Path\r
409   )\r
410 {\r
411   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;\r
412   EFI_DEVICE_PATH_PROTOCOL        *MapPathCopy;\r
413   SHELL_MAP_LIST                  *MapListItem;\r
414   CHAR16                          *PathForReturn;\r
415   UINTN                           PathSize;\r
416   EFI_HANDLE                      PathHandle;\r
417   EFI_HANDLE                      MapHandle;\r
418   EFI_STATUS                      Status;\r
419   FILEPATH_DEVICE_PATH            *FilePath;\r
420   FILEPATH_DEVICE_PATH            *AlignedNode;\r
421 \r
422   PathForReturn = NULL;\r
423   PathSize = 0;\r
424 \r
425   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;\r
426   ASSERT(DevicePathCopy != NULL);\r
427   if (DevicePathCopy == NULL) {\r
428     return (NULL);\r
429   }\r
430   ///@todo BlockIo?\r
431   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);\r
432 \r
433   if (EFI_ERROR(Status)) {\r
434     return (NULL);\r
435   }\r
436   //\r
437   //  check each of the device paths we have to get the root of the path\r
438   //\r
439   for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)\r
440       ; !IsNull(&gShellMapList.Link, &MapListItem->Link)\r
441       ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)\r
442      ){\r
443     MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;\r
444     ASSERT(MapPathCopy != NULL);\r
445     ///@todo BlockIo?\r
446     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);\r
447     if (MapHandle == PathHandle) {\r
448       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));\r
449       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);\r
450       //\r
451       // go through all the remaining nodes in the device path\r
452       //\r
453       for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy\r
454           ; !IsDevicePathEnd (&FilePath->Header)\r
455           ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)\r
456          ){\r
457         //\r
458         // all the rest should be file path nodes\r
459         //\r
460         if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||\r
461             (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {\r
462           FreePool(PathForReturn);\r
463           PathForReturn = NULL;\r
464           ASSERT(FALSE);\r
465         } else {\r
466           //\r
467           // append the path part onto the filepath.\r
468           //\r
469           ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));\r
470           PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L"\\", 1);\r
471 \r
472           AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath);\r
473           PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, AlignedNode->PathName, 0);\r
474           FreePool(AlignedNode);\r
475         }\r
476       } // for loop of remaining nodes\r
477     }\r
478     if (PathForReturn != NULL) {\r
479       break;\r
480     }\r
481   } // for loop of paths to check\r
482   return(PathForReturn);\r
483 }\r
484 \r
485 /**\r
486   Converts a file system style name to a device path.\r
487 \r
488   This function converts a file system style name to a device path, by replacing any\r
489   mapping references to the associated device path.\r
490 \r
491   @param[in] Path               The pointer to the path.\r
492 \r
493   @return                       The pointer of the file path. The file path is callee\r
494                                 allocated and should be freed by the caller.\r
495   @retval NULL                  The path could not be found.\r
496   @retval NULL                  There was not enough available memory.\r
497 **/\r
498 EFI_DEVICE_PATH_PROTOCOL *\r
499 EFIAPI\r
500 EfiShellGetDevicePathFromFilePath(\r
501   IN CONST CHAR16 *Path\r
502   )\r
503 {\r
504   CHAR16                          *MapName;\r
505   CHAR16                          *NewPath;\r
506   CONST CHAR16                    *Cwd;\r
507   UINTN                           Size;\r
508   CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
509   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopy;\r
510   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopyForFree;\r
511   EFI_DEVICE_PATH_PROTOCOL        *DevicePathForReturn;\r
512   EFI_HANDLE                      Handle;\r
513   EFI_STATUS                      Status;\r
514 \r
515   if (Path == NULL) {\r
516     return (NULL);\r
517   }\r
518 \r
519   MapName = NULL;\r
520   NewPath = NULL;\r
521 \r
522   if (StrStr(Path, L":") == NULL) {\r
523     Cwd = EfiShellGetCurDir(NULL);\r
524     if (Cwd == NULL) {\r
525       return (NULL);\r
526     }\r
527     Size = StrSize(Cwd);\r
528     Size += StrSize(Path);\r
529     NewPath = AllocateZeroPool(Size);\r
530     if (NewPath == NULL) {\r
531       return (NULL);\r
532     }\r
533     StrCpy(NewPath, Cwd);\r
534     if (*Path == L'\\') {\r
535       Path++;\r
536       while (PathRemoveLastItem(NewPath)) ;\r
537     }\r
538     StrCat(NewPath, Path);\r
539     DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);\r
540     FreePool(NewPath);\r
541     return (DevicePathForReturn);\r
542   }\r
543 \r
544   Size = 0;\r
545   //\r
546   // find the part before (but including) the : for the map name\r
547   //\r
548   ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));\r
549   MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));\r
550   if (MapName == NULL || MapName[StrLen(MapName)-1] != L':') {\r
551     return (NULL);\r
552   }\r
553 \r
554   //\r
555   // look up the device path in the map\r
556   //\r
557   DevicePath = EfiShellGetDevicePathFromMap(MapName);\r
558   if (DevicePath == NULL) {\r
559     //\r
560     // Must have been a bad Mapname\r
561     //\r
562     return (NULL);\r
563   }\r
564 \r
565   //\r
566   // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)\r
567   //\r
568   DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath(DevicePath);\r
569   if (DevicePathCopy == NULL) {\r
570     FreePool(MapName);\r
571     return (NULL);\r
572   }\r
573 \r
574   //\r
575   // get the handle\r
576   //\r
577   ///@todo BlockIo?\r
578   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);\r
579   if (EFI_ERROR(Status)) {\r
580     if (DevicePathCopyForFree != NULL) {\r
581       FreePool(DevicePathCopyForFree);\r
582     }\r
583     FreePool(MapName);\r
584     return (NULL);\r
585   }\r
586 \r
587   //\r
588   // build the full device path\r
589   //\r
590   if (*(Path+StrLen(MapName)+1) == CHAR_NULL) {\r
591     DevicePathForReturn = FileDevicePath(Handle, L"\\");\r
592   } else {\r
593     DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName));\r
594   }\r
595 \r
596   FreePool(MapName);\r
597   if (DevicePathCopyForFree != NULL) {\r
598     FreePool(DevicePathCopyForFree);\r
599   }\r
600 \r
601   return (DevicePathForReturn);\r
602 }\r
603 \r
604 /**\r
605   Gets the name of the device specified by the device handle.\r
606 \r
607   This function gets the user-readable name of the device specified by the device\r
608   handle. If no user-readable name could be generated, then *BestDeviceName will be\r
609   NULL and EFI_NOT_FOUND will be returned.\r
610 \r
611   If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the\r
612   device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on\r
613   DeviceHandle.\r
614 \r
615   If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the\r
616   device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.\r
617   If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and\r
618   EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then\r
619   EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.\r
620 \r
621   @param DeviceHandle           The handle of the device.\r
622   @param Flags                  Determines the possible sources of component names.\r
623                                 Valid bits are:\r
624                                   EFI_DEVICE_NAME_USE_COMPONENT_NAME\r
625                                   EFI_DEVICE_NAME_USE_DEVICE_PATH\r
626   @param Language               A pointer to the language specified for the device\r
627                                 name, in the same format as described in the UEFI\r
628                                 specification, Appendix M\r
629   @param BestDeviceName         On return, points to the callee-allocated NULL-\r
630                                 terminated name of the device. If no device name\r
631                                 could be found, points to NULL. The name must be\r
632                                 freed by the caller...\r
633 \r
634   @retval EFI_SUCCESS           Get the name successfully.\r
635   @retval EFI_NOT_FOUND         Fail to get the device name.\r
636   @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.\r
637   @retval EFI_INVALID_PARAMETER BestDeviceName was NULL\r
638   @retval EFI_INVALID_PARAMETER DeviceHandle was NULL\r
639 **/\r
640 EFI_STATUS\r
641 EFIAPI\r
642 EfiShellGetDeviceName(\r
643   IN EFI_HANDLE DeviceHandle,\r
644   IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,\r
645   IN CHAR8 *Language,\r
646   OUT CHAR16 **BestDeviceName\r
647   )\r
648 {\r
649   EFI_STATUS                        Status;\r
650   EFI_COMPONENT_NAME2_PROTOCOL      *CompName2;\r
651   EFI_DEVICE_PATH_TO_TEXT_PROTOCOL  *DevicePathToText;\r
652   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;\r
653   EFI_HANDLE                        *HandleList;\r
654   UINTN                             HandleCount;\r
655   UINTN                             LoopVar;\r
656   CHAR16                            *DeviceNameToReturn;\r
657   CHAR8                             *Lang;\r
658   CHAR8                             *TempChar;\r
659 \r
660   UINTN                             ParentControllerCount;\r
661   EFI_HANDLE                        *ParentControllerBuffer;\r
662   UINTN                             ParentDriverCount;\r
663   EFI_HANDLE                        *ParentDriverBuffer;\r
664 \r
665   if (BestDeviceName == NULL ||\r
666       DeviceHandle   == NULL\r
667      ){\r
668     return (EFI_INVALID_PARAMETER);\r
669   }\r
670 \r
671   //\r
672   // make sure one of the 2 supported bits is on\r
673   //\r
674   if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&\r
675       ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) {\r
676     return (EFI_INVALID_PARAMETER);\r
677   }\r
678 \r
679   DeviceNameToReturn  = NULL;\r
680   *BestDeviceName     = NULL;\r
681   HandleList          = NULL;\r
682   HandleCount         = 0;\r
683   Lang                = NULL;\r
684 \r
685   if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {\r
686     Status = ParseHandleDatabaseByRelationship(\r
687       NULL,\r
688       DeviceHandle,\r
689       HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,\r
690       &HandleCount,\r
691       &HandleList);\r
692     for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){\r
693       //\r
694       // Go through those handles until we get one that passes for GetComponentName\r
695       //\r
696       Status = gBS->OpenProtocol(\r
697         HandleList[LoopVar],\r
698         &gEfiComponentName2ProtocolGuid,\r
699         (VOID**)&CompName2,\r
700         gImageHandle,\r
701         NULL,\r
702         EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
703       if (EFI_ERROR(Status)) {\r
704         Status = gBS->OpenProtocol(\r
705           HandleList[LoopVar],\r
706           &gEfiComponentNameProtocolGuid,\r
707           (VOID**)&CompName2,\r
708           gImageHandle,\r
709           NULL,\r
710           EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
711       }\r
712 \r
713       if (EFI_ERROR(Status)) {\r
714         continue;\r
715       }\r
716       if (Language == NULL) {\r
717         Lang = AllocateZeroPool(AsciiStrSize(CompName2->SupportedLanguages));\r
718         if (Lang == NULL) {\r
719           return (EFI_OUT_OF_RESOURCES);\r
720         }\r
721         AsciiStrCpy(Lang, CompName2->SupportedLanguages);\r
722         TempChar = AsciiStrStr(Lang, ";");\r
723         if (TempChar != NULL){\r
724           *TempChar = CHAR_NULL;\r
725         }\r
726       } else {\r
727         Lang = AllocateZeroPool(AsciiStrSize(Language));\r
728         if (Lang == NULL) {\r
729           return (EFI_OUT_OF_RESOURCES);\r
730         }\r
731         AsciiStrCpy(Lang, Language);\r
732       }\r
733       Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);\r
734       FreePool(Lang);\r
735       Lang = NULL;\r
736       if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {\r
737         break;\r
738       }\r
739     }\r
740     if (HandleList != NULL) {\r
741       FreePool(HandleList);\r
742     }\r
743 \r
744     //\r
745     // Now check the parent controller using this as the child.\r
746     //\r
747     if (DeviceNameToReturn == NULL){\r
748       PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);\r
749       for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) {\r
750         PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);\r
751         for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) {\r
752           //\r
753           // try using that driver's component name with controller and our driver as the child.\r
754           //\r
755           Status = gBS->OpenProtocol(\r
756             ParentDriverBuffer[HandleCount],\r
757             &gEfiComponentName2ProtocolGuid,\r
758             (VOID**)&CompName2,\r
759             gImageHandle,\r
760             NULL,\r
761             EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
762           if (EFI_ERROR(Status)) {\r
763             Status = gBS->OpenProtocol(\r
764               ParentDriverBuffer[HandleCount],\r
765               &gEfiComponentNameProtocolGuid,\r
766               (VOID**)&CompName2,\r
767               gImageHandle,\r
768               NULL,\r
769               EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
770           }\r
771 \r
772           if (EFI_ERROR(Status)) {\r
773             continue;\r
774           }\r
775           if (Language == NULL) {\r
776             Lang = AllocateZeroPool(AsciiStrSize(CompName2->SupportedLanguages));\r
777             if (Lang == NULL) {\r
778               return (EFI_OUT_OF_RESOURCES);\r
779             }\r
780             AsciiStrCpy(Lang, CompName2->SupportedLanguages);\r
781             TempChar = AsciiStrStr(Lang, ";");\r
782             if (TempChar != NULL){\r
783               *TempChar = CHAR_NULL;\r
784             }\r
785           } else {\r
786             Lang = AllocateZeroPool(AsciiStrSize(Language));\r
787             if (Lang == NULL) {\r
788               return (EFI_OUT_OF_RESOURCES);\r
789             }\r
790             AsciiStrCpy(Lang, Language);\r
791           }\r
792           Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);\r
793           FreePool(Lang);\r
794           Lang = NULL;\r
795           if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {\r
796             break;\r
797           }\r
798 \r
799 \r
800 \r
801         }\r
802         SHELL_FREE_NON_NULL(ParentDriverBuffer);\r
803         if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {\r
804           break;\r
805         }\r
806       }\r
807       SHELL_FREE_NON_NULL(ParentControllerBuffer);\r
808     }\r
809     //\r
810     // dont return on fail since we will try device path if that bit is on\r
811     //\r
812     if (DeviceNameToReturn != NULL){\r
813       ASSERT(BestDeviceName != NULL);\r
814       StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);\r
815       return (EFI_SUCCESS);\r
816     }\r
817   }\r
818   if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {\r
819     Status = gBS->LocateProtocol(\r
820       &gEfiDevicePathToTextProtocolGuid,\r
821       NULL,\r
822       (VOID**)&DevicePathToText);\r
823     //\r
824     // we now have the device path to text protocol\r
825     //\r
826     if (!EFI_ERROR(Status)) {\r
827       Status = gBS->OpenProtocol(\r
828         DeviceHandle,\r
829         &gEfiDevicePathProtocolGuid,\r
830         (VOID**)&DevicePath,\r
831         gImageHandle,\r
832         NULL,\r
833         EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
834       if (!EFI_ERROR(Status)) {\r
835         //\r
836         // use device path to text on the device path\r
837         //\r
838         *BestDeviceName = DevicePathToText->ConvertDevicePathToText(DevicePath, TRUE, TRUE);\r
839         return (EFI_SUCCESS);\r
840       }\r
841     }\r
842   }\r
843   //\r
844   // none of the selected bits worked.\r
845   //\r
846   return (EFI_NOT_FOUND);\r
847 }\r
848 \r
849 /**\r
850   Opens the root directory of a device on a handle\r
851 \r
852   This function opens the root directory of a device and returns a file handle to it.\r
853 \r
854   @param DeviceHandle           The handle of the device that contains the volume.\r
855   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the\r
856                                 device.\r
857 \r
858   @retval EFI_SUCCESS           Root opened successfully.\r
859   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory\r
860                                 could not be opened.\r
861   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.\r
862   @retval EFI_DEVICE_ERROR      The device had an error\r
863 **/\r
864 EFI_STATUS\r
865 EFIAPI\r
866 EfiShellOpenRootByHandle(\r
867   IN EFI_HANDLE DeviceHandle,\r
868   OUT SHELL_FILE_HANDLE *FileHandle\r
869   )\r
870 {\r
871   EFI_STATUS                      Status;\r
872   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;\r
873   EFI_FILE_PROTOCOL               *RealFileHandle;\r
874   EFI_DEVICE_PATH_PROTOCOL        *DevPath;\r
875 \r
876   //\r
877   // get the simple file system interface\r
878   //\r
879   Status = gBS->OpenProtocol(DeviceHandle,\r
880                              &gEfiSimpleFileSystemProtocolGuid,\r
881                              (VOID**)&SimpleFileSystem,\r
882                              gImageHandle,\r
883                              NULL,\r
884                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
885   if (EFI_ERROR(Status)) {\r
886     return (EFI_NOT_FOUND);\r
887   }\r
888 \r
889   Status = gBS->OpenProtocol(DeviceHandle,\r
890                              &gEfiDevicePathProtocolGuid,\r
891                              (VOID**)&DevPath,\r
892                              gImageHandle,\r
893                              NULL,\r
894                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
895   if (EFI_ERROR(Status)) {\r
896     return (EFI_NOT_FOUND);\r
897   }\r
898   //\r
899   // Open the root volume now...\r
900   //\r
901   Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &RealFileHandle);\r
902   *FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath));\r
903   return (Status);\r
904 }\r
905 \r
906 /**\r
907   Opens the root directory of a device.\r
908 \r
909   This function opens the root directory of a device and returns a file handle to it.\r
910 \r
911   @param DevicePath             Points to the device path corresponding to the device where the\r
912                                 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.\r
913   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the\r
914                                 device.\r
915 \r
916   @retval EFI_SUCCESS           Root opened successfully.\r
917   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory\r
918                                 could not be opened.\r
919   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.\r
920   @retval EFI_DEVICE_ERROR      The device had an error\r
921   @retval EFI_INVALID_PARAMETER FileHandle is NULL.\r
922 **/\r
923 EFI_STATUS\r
924 EFIAPI\r
925 EfiShellOpenRoot(\r
926   IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
927   OUT SHELL_FILE_HANDLE *FileHandle\r
928   )\r
929 {\r
930   EFI_STATUS Status;\r
931   EFI_HANDLE Handle;\r
932 \r
933   if (FileHandle == NULL) {\r
934     return (EFI_INVALID_PARAMETER);\r
935   }\r
936 \r
937   //\r
938   // find the handle of the device with that device handle and the file system\r
939   //\r
940   ///@todo BlockIo?\r
941   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,\r
942                                  &DevicePath,\r
943                                  &Handle);\r
944   if (EFI_ERROR(Status)) {\r
945     return (EFI_NOT_FOUND);\r
946   }\r
947 \r
948   return (EfiShellOpenRootByHandle(Handle, FileHandle));\r
949 }\r
950 \r
951 /**\r
952   Returns whether any script files are currently being processed.\r
953 \r
954   @retval TRUE                 There is at least one script file active.\r
955   @retval FALSE                No script files are active now.\r
956 \r
957 **/\r
958 BOOLEAN\r
959 EFIAPI\r
960 EfiShellBatchIsActive (\r
961   VOID\r
962   )\r
963 {\r
964   if (ShellCommandGetCurrentScriptFile() == NULL) {\r
965     return (FALSE);\r
966   }\r
967   return (TRUE);\r
968 }\r
969 \r
970 /**\r
971   Worker function to open a file based on a device path.  this will open the root\r
972   of the volume and then traverse down to the file itself.\r
973 \r
974   @param DevicePath               Device Path of the file.\r
975   @param FileHandle               Pointer to the file upon a successful return.\r
976   @param OpenMode                 mode to open file in.\r
977   @param Attributes               the File Attributes to use when creating a new file.\r
978 \r
979   @retval EFI_SUCCESS             the file is open and FileHandle is valid\r
980   @retval EFI_UNSUPPORTED         the device path cotained non-path elements\r
981   @retval other                   an error ocurred.\r
982 **/\r
983 EFI_STATUS\r
984 EFIAPI\r
985 InternalOpenFileDevicePath(\r
986   IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
987   OUT SHELL_FILE_HANDLE           *FileHandle,\r
988   IN UINT64                       OpenMode,\r
989   IN UINT64                       Attributes OPTIONAL\r
990   )\r
991 {\r
992   EFI_STATUS                      Status;\r
993   FILEPATH_DEVICE_PATH            *FilePathNode;\r
994   EFI_HANDLE                      Handle;\r
995   SHELL_FILE_HANDLE               ShellHandle;\r
996   EFI_FILE_PROTOCOL               *Handle1;\r
997   EFI_FILE_PROTOCOL               *Handle2;\r
998   EFI_DEVICE_PATH_PROTOCOL        *DpCopy;\r
999   FILEPATH_DEVICE_PATH            *AlignedNode;\r
1000 \r
1001   if (FileHandle == NULL) {\r
1002     return (EFI_INVALID_PARAMETER);\r
1003   }\r
1004   *FileHandle   = NULL;\r
1005   Handle1       = NULL;\r
1006   Handle2       = NULL;\r
1007   Handle        = NULL;\r
1008   DpCopy        = DevicePath;\r
1009   ShellHandle   = NULL;\r
1010   FilePathNode  = NULL;\r
1011   AlignedNode   = NULL;\r
1012 \r
1013   Status = EfiShellOpenRoot(DevicePath, &ShellHandle);\r
1014 \r
1015   if (!EFI_ERROR(Status)) {\r
1016     Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);\r
1017     if (Handle1 != NULL) {\r
1018       //\r
1019       // chop off the begining part before the file system part...\r
1020       //\r
1021       ///@todo BlockIo?\r
1022       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,\r
1023                                      &DevicePath,\r
1024                                      &Handle);\r
1025         if (!EFI_ERROR(Status)) {\r
1026         //\r
1027         // To access as a file system, the file path should only\r
1028         // contain file path components.  Follow the file path nodes\r
1029         // and find the target file\r
1030         //\r
1031         for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath\r
1032             ; !IsDevicePathEnd (&FilePathNode->Header)\r
1033             ; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header)\r
1034            ){\r
1035           SHELL_FREE_NON_NULL(AlignedNode);\r
1036           AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode);\r
1037           //\r
1038           // For file system access each node should be a file path component\r
1039           //\r
1040           if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||\r
1041               DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP\r
1042              ) {\r
1043             Status = EFI_UNSUPPORTED;\r
1044             break;\r
1045           }\r
1046 \r
1047           //\r
1048           // Open this file path node\r
1049           //\r
1050           Handle2 = Handle1;\r
1051           Handle1 = NULL;\r
1052 \r
1053           //\r
1054           // if this is the last node in the DevicePath always create (if that was requested).\r
1055           //\r
1056           if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {\r
1057             Status = Handle2->Open (\r
1058                                   Handle2,\r
1059                                   &Handle1,\r
1060                                   AlignedNode->PathName,\r
1061                                   OpenMode,\r
1062                                   Attributes\r
1063                                  );\r
1064           } else {\r
1065 \r
1066             //\r
1067             //  This is not the last node and we dont want to 'create' existing\r
1068             //  directory entries...\r
1069             //\r
1070 \r
1071             //\r
1072             // open without letting it create\r
1073             // prevents error on existing files/directories\r
1074             //\r
1075             Status = Handle2->Open (\r
1076                                   Handle2,\r
1077                                   &Handle1,\r
1078                                   AlignedNode->PathName,\r
1079                                   OpenMode &~EFI_FILE_MODE_CREATE,\r
1080                                   Attributes\r
1081                                  );\r
1082             //\r
1083             // if above failed now open and create the 'item'\r
1084             // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)\r
1085             //\r
1086             if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {\r
1087               Status = Handle2->Open (\r
1088                                     Handle2,\r
1089                                     &Handle1,\r
1090                                     AlignedNode->PathName,\r
1091                                     OpenMode,\r
1092                                     Attributes\r
1093                                    );\r
1094             }\r
1095           }\r
1096           //\r
1097           // Close the last node\r
1098           //\r
1099           ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);\r
1100 \r
1101           //\r
1102           // If there's been an error, stop\r
1103           //\r
1104           if (EFI_ERROR (Status)) {\r
1105             break;\r
1106           }\r
1107         } // for loop\r
1108       }\r
1109     }\r
1110   }\r
1111   SHELL_FREE_NON_NULL(AlignedNode);\r
1112   if (EFI_ERROR(Status)) {\r
1113     if (Handle1 != NULL) {\r
1114       ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1);\r
1115     }\r
1116   } else {\r
1117     *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));\r
1118   }\r
1119   return (Status);\r
1120 }\r
1121 \r
1122 /**\r
1123   Creates a file or directory by name.\r
1124 \r
1125   This function creates an empty new file or directory with the specified attributes and\r
1126   returns the new file's handle. If the file already exists and is read-only, then\r
1127   EFI_INVALID_PARAMETER will be returned.\r
1128 \r
1129   If the file already existed, it is truncated and its attributes updated. If the file is\r
1130   created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.\r
1131 \r
1132   If the file name begins with >v, then the file handle which is returned refers to the\r
1133   shell environment variable with the specified name. If the shell environment variable\r
1134   already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.\r
1135 \r
1136   @param FileName           Pointer to NULL-terminated file path\r
1137   @param FileAttribs        The new file's attrbiutes.  the different attributes are\r
1138                             described in EFI_FILE_PROTOCOL.Open().\r
1139   @param FileHandle         On return, points to the created file handle or directory's handle\r
1140 \r
1141   @retval EFI_SUCCESS       The file was opened.  FileHandle points to the new file's handle.\r
1142   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.\r
1143   @retval EFI_UNSUPPORTED   could not open the file path\r
1144   @retval EFI_NOT_FOUND     the specified file could not be found on the devide, or could not\r
1145                             file the file system on the device.\r
1146   @retval EFI_NO_MEDIA      the device has no medium.\r
1147   @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no\r
1148                             longer supported.\r
1149   @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according\r
1150                             the DirName.\r
1151   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
1152   @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write\r
1153                             when the media is write-protected.\r
1154   @retval EFI_ACCESS_DENIED The service denied access to the file.\r
1155   @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.\r
1156   @retval EFI_VOLUME_FULL   The volume is full.\r
1157 **/\r
1158 EFI_STATUS\r
1159 EFIAPI\r
1160 EfiShellCreateFile(\r
1161   IN CONST CHAR16       *FileName,\r
1162   IN UINT64             FileAttribs,\r
1163   OUT SHELL_FILE_HANDLE *FileHandle\r
1164   )\r
1165 {\r
1166   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
1167   EFI_STATUS                Status;\r
1168 \r
1169   //\r
1170   // Is this for an environment variable\r
1171   // do we start with >v\r
1172   //\r
1173   if (StrStr(FileName, L">v") == FileName) {\r
1174     if (!IsVolatileEnv(FileName+2)) {\r
1175       return (EFI_INVALID_PARAMETER);\r
1176     }\r
1177     *FileHandle = CreateFileInterfaceEnv(FileName+2);\r
1178     return (EFI_SUCCESS);\r
1179   }\r
1180 \r
1181   //\r
1182   // We are opening a regular file.\r
1183   //\r
1184   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);\r
1185   if (DevicePath == NULL) {\r
1186     return (EFI_NOT_FOUND);\r
1187   }\r
1188 \r
1189   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs); // 0 = no specific file attributes\r
1190   FreePool(DevicePath);\r
1191 \r
1192   return(Status);\r
1193 }\r
1194 \r
1195 /**\r
1196   Opens a file or a directory by file name.\r
1197 \r
1198   This function opens the specified file in the specified OpenMode and returns a file\r
1199   handle.\r
1200   If the file name begins with >v, then the file handle which is returned refers to the\r
1201   shell environment variable with the specified name. If the shell environment variable\r
1202   exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then\r
1203   EFI_INVALID_PARAMETER is returned.\r
1204 \r
1205   If the file name is >i, then the file handle which is returned refers to the standard\r
1206   input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER\r
1207   is returned.\r
1208 \r
1209   If the file name is >o, then the file handle which is returned refers to the standard\r
1210   output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER\r
1211   is returned.\r
1212 \r
1213   If the file name is >e, then the file handle which is returned refers to the standard\r
1214   error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER\r
1215   is returned.\r
1216 \r
1217   If the file name is NUL, then the file handle that is returned refers to the standard NUL\r
1218   file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is\r
1219   returned.\r
1220 \r
1221   If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the\r
1222   FileHandle is NULL.\r
1223 \r
1224   @param FileName               Points to the NULL-terminated UCS-2 encoded file name.\r
1225   @param FileHandle             On return, points to the file handle.\r
1226   @param OpenMode               File open mode. Either EFI_FILE_MODE_READ or\r
1227                                 EFI_FILE_MODE_WRITE from section 12.4 of the UEFI\r
1228                                 Specification.\r
1229   @retval EFI_SUCCESS           The file was opened. FileHandle has the opened file's handle.\r
1230   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.\r
1231   @retval EFI_UNSUPPORTED       Could not open the file path. FileHandle is NULL.\r
1232   @retval EFI_NOT_FOUND         The specified file could not be found on the device or the file\r
1233                                 system could not be found on the device. FileHandle is NULL.\r
1234   @retval EFI_NO_MEDIA          The device has no medium. FileHandle is NULL.\r
1235   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the medium is no\r
1236                                 longer supported. FileHandle is NULL.\r
1237   @retval EFI_DEVICE_ERROR      The device reported an error or can't get the file path according\r
1238                                 the FileName. FileHandle is NULL.\r
1239   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted. FileHandle is NULL.\r
1240   @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a file for write\r
1241                                 when the media is write-protected. FileHandle is NULL.\r
1242   @retval EFI_ACCESS_DENIED     The service denied access to the file. FileHandle is NULL.\r
1243   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the file. FileHandle\r
1244                                 is NULL.\r
1245   @retval EFI_VOLUME_FULL       The volume is full. FileHandle is NULL.\r
1246 **/\r
1247 EFI_STATUS\r
1248 EFIAPI\r
1249 EfiShellOpenFileByName(\r
1250   IN CONST CHAR16       *FileName,\r
1251   OUT SHELL_FILE_HANDLE *FileHandle,\r
1252   IN UINT64             OpenMode\r
1253   )\r
1254 {\r
1255   EFI_DEVICE_PATH_PROTOCOL        *DevicePath;\r
1256   EFI_STATUS                      Status;\r
1257 \r
1258   *FileHandle = NULL;\r
1259 \r
1260   //\r
1261   // Is this for StdIn\r
1262   //\r
1263   if (StrCmp(FileName, L">i") == 0) {\r
1264     //\r
1265     // make sure not writing to StdIn\r
1266     //\r
1267     if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {\r
1268       return (EFI_INVALID_PARAMETER);\r
1269     }\r
1270     *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;\r
1271     ASSERT(*FileHandle != NULL);\r
1272     return (EFI_SUCCESS);\r
1273   }\r
1274 \r
1275   //\r
1276   // Is this for StdOut\r
1277   //\r
1278   if (StrCmp(FileName, L">o") == 0) {\r
1279     //\r
1280     // make sure not writing to StdIn\r
1281     //\r
1282     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {\r
1283       return (EFI_INVALID_PARAMETER);\r
1284     }\r
1285     *FileHandle = &FileInterfaceStdOut;\r
1286     return (EFI_SUCCESS);\r
1287   }\r
1288 \r
1289   //\r
1290   // Is this for NUL file\r
1291   //\r
1292   if (StrCmp(FileName, L"NUL") == 0) {\r
1293     *FileHandle = &FileInterfaceNulFile;\r
1294     return (EFI_SUCCESS);\r
1295   }\r
1296 \r
1297   //\r
1298   // Is this for StdErr\r
1299   //\r
1300   if (StrCmp(FileName, L">e") == 0) {\r
1301     //\r
1302     // make sure not writing to StdIn\r
1303     //\r
1304     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {\r
1305       return (EFI_INVALID_PARAMETER);\r
1306     }\r
1307     *FileHandle = &FileInterfaceStdErr;\r
1308     return (EFI_SUCCESS);\r
1309   }\r
1310 \r
1311   //\r
1312   // Is this for an environment variable\r
1313   // do we start with >v\r
1314   //\r
1315   if (StrStr(FileName, L">v") == FileName) {\r
1316     if (!IsVolatileEnv(FileName+2) &&\r
1317         ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {\r
1318       return (EFI_INVALID_PARAMETER);\r
1319     }\r
1320     *FileHandle = CreateFileInterfaceEnv(FileName+2);\r
1321     return (EFI_SUCCESS);\r
1322   }\r
1323 \r
1324   //\r
1325   // We are opening a regular file.\r
1326   //\r
1327   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);\r
1328 //  DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath););\r
1329   if (DevicePath == NULL) {\r
1330     return (EFI_NOT_FOUND);\r
1331   }\r
1332 \r
1333   //\r
1334   // Copy the device path, open the file, then free the memory\r
1335   //\r
1336   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes\r
1337   FreePool(DevicePath);\r
1338 \r
1339   return(Status);\r
1340 }\r
1341 \r
1342 /**\r
1343   Deletes the file specified by the file name.\r
1344 \r
1345   This function deletes a file.\r
1346 \r
1347   @param FileName                 Points to the NULL-terminated file name.\r
1348 \r
1349   @retval EFI_SUCCESS             The file was closed and deleted, and the handle was closed.\r
1350   @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.\r
1351   @sa EfiShellCreateFile\r
1352 **/\r
1353 EFI_STATUS\r
1354 EFIAPI\r
1355 EfiShellDeleteFileByName(\r
1356   IN CONST CHAR16 *FileName\r
1357   )\r
1358 {\r
1359   SHELL_FILE_HANDLE FileHandle;\r
1360   EFI_STATUS        Status;\r
1361 \r
1362   //\r
1363   // get a handle to the file\r
1364   //\r
1365   Status = EfiShellCreateFile(FileName,\r
1366                               0,\r
1367                               &FileHandle);\r
1368   if (EFI_ERROR(Status)) {\r
1369     return (Status);\r
1370   }\r
1371   //\r
1372   // now delete the file\r
1373   //\r
1374   return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));\r
1375 }\r
1376 \r
1377 /**\r
1378   Disables the page break output mode.\r
1379 **/\r
1380 VOID\r
1381 EFIAPI\r
1382 EfiShellDisablePageBreak (\r
1383   VOID\r
1384   )\r
1385 {\r
1386   ShellInfoObject.PageBreakEnabled = FALSE;\r
1387 }\r
1388 \r
1389 /**\r
1390   Enables the page break output mode.\r
1391 **/\r
1392 VOID\r
1393 EFIAPI\r
1394 EfiShellEnablePageBreak (\r
1395   VOID\r
1396   )\r
1397 {\r
1398   ShellInfoObject.PageBreakEnabled = TRUE;\r
1399 }\r
1400 \r
1401 /**\r
1402   internal worker function to load and run an image via device path.\r
1403 \r
1404   @param ParentImageHandle  A handle of the image that is executing the specified\r
1405                             command line.\r
1406   @param DevicePath         device path of the file to execute\r
1407   @param CommandLine        Points to the NULL-terminated UCS-2 encoded string\r
1408                             containing the command line. If NULL then the command-\r
1409                             line will be empty.\r
1410   @param Environment        Points to a NULL-terminated array of environment\r
1411                             variables with the format 'x=y', where x is the\r
1412                             environment variable name and y is the value. If this\r
1413                             is NULL, then the current shell environment is used.\r
1414   @param StatusCode         Points to the status code returned by the command.\r
1415 \r
1416   @retval EFI_SUCCESS       The command executed successfully. The  status code\r
1417                             returned by the command is pointed to by StatusCode.\r
1418   @retval EFI_INVALID_PARAMETER The parameters are invalid.\r
1419   @retval EFI_OUT_OF_RESOURCES Out of resources.\r
1420   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.\r
1421 **/\r
1422 EFI_STATUS\r
1423 EFIAPI\r
1424 InternalShellExecuteDevicePath(\r
1425   IN CONST EFI_HANDLE               *ParentImageHandle,\r
1426   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
1427   IN CONST CHAR16                   *CommandLine OPTIONAL,\r
1428   IN CONST CHAR16                   **Environment OPTIONAL,\r
1429   OUT EFI_STATUS                    *StatusCode OPTIONAL\r
1430   )\r
1431 {\r
1432   EFI_STATUS                    Status;\r
1433   EFI_HANDLE                    NewHandle;\r
1434   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;\r
1435   LIST_ENTRY                    OrigEnvs;\r
1436   EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;\r
1437 \r
1438   if (ParentImageHandle == NULL) {\r
1439     return (EFI_INVALID_PARAMETER);\r
1440   }\r
1441 \r
1442   InitializeListHead(&OrigEnvs);\r
1443 \r
1444   NewHandle = NULL;\r
1445 \r
1446   //\r
1447   // Load the image with:\r
1448   // FALSE - not from boot manager and NULL, 0 being not already in memory\r
1449   //\r
1450   Status = gBS->LoadImage(\r
1451     FALSE,\r
1452     *ParentImageHandle,\r
1453     (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,\r
1454     NULL,\r
1455     0,\r
1456     &NewHandle);\r
1457 \r
1458   if (EFI_ERROR(Status)) {\r
1459     if (NewHandle != NULL) {\r
1460       gBS->UnloadImage(NewHandle);\r
1461     }\r
1462     return (Status);\r
1463   }\r
1464   Status = gBS->OpenProtocol(\r
1465     NewHandle,\r
1466     &gEfiLoadedImageProtocolGuid,\r
1467     (VOID**)&LoadedImage,\r
1468     gImageHandle,\r
1469     NULL,\r
1470     EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
1471 \r
1472   if (!EFI_ERROR(Status)) {\r
1473     ASSERT(LoadedImage->LoadOptionsSize == 0);\r
1474     if (CommandLine != NULL) {\r
1475       LoadedImage->LoadOptionsSize  = (UINT32)StrSize(CommandLine);\r
1476       LoadedImage->LoadOptions      = (VOID*)CommandLine;\r
1477     }\r
1478 \r
1479     //\r
1480     // Save our current environment settings for later restoration if necessary\r
1481     //\r
1482     if (Environment != NULL) {\r
1483       Status = GetEnvironmentVariableList(&OrigEnvs);\r
1484       if (!EFI_ERROR(Status)) {\r
1485         Status = SetEnvironmentVariables(Environment);\r
1486       }\r
1487     }\r
1488 \r
1489     //\r
1490     // Initialize and install a shell parameters protocol on the image.\r
1491     //\r
1492     ShellParamsProtocol.StdIn   = ShellInfoObject.NewShellParametersProtocol->StdIn;\r
1493     ShellParamsProtocol.StdOut  = ShellInfoObject.NewShellParametersProtocol->StdOut;\r
1494     ShellParamsProtocol.StdErr  = ShellInfoObject.NewShellParametersProtocol->StdErr;\r
1495     Status = UpdateArgcArgv(&ShellParamsProtocol, CommandLine, NULL, NULL);\r
1496     ASSERT_EFI_ERROR(Status);\r
1497     Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);\r
1498     ASSERT_EFI_ERROR(Status);\r
1499 \r
1500     ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)\r
1501 \r
1502     //\r
1503     // now start the image and if the caller wanted the return code pass it to them...\r
1504     //\r
1505     if (!EFI_ERROR(Status)) {\r
1506       if (StatusCode != NULL) {\r
1507         *StatusCode = gBS->StartImage(NewHandle, NULL, NULL);\r
1508       } else {\r
1509         Status      = gBS->StartImage(NewHandle, NULL, NULL);\r
1510       }\r
1511     }\r
1512 \r
1513     //\r
1514     // Cleanup (and dont overwrite errors)\r
1515     //\r
1516     if (EFI_ERROR(Status)) {\r
1517       gBS->UninstallProtocolInterface(NewHandle, &gEfiShellParametersProtocolGuid, &ShellParamsProtocol);\r
1518     } else {\r
1519       Status = gBS->UninstallProtocolInterface(NewHandle, &gEfiShellParametersProtocolGuid, &ShellParamsProtocol);\r
1520       ASSERT_EFI_ERROR(Status);\r
1521     }\r
1522   }\r
1523 \r
1524   if (!IsListEmpty(&OrigEnvs)) {\r
1525     if (EFI_ERROR(Status)) {\r
1526       SetEnvironmentVariableList(&OrigEnvs);\r
1527     } else {\r
1528       Status = SetEnvironmentVariableList(&OrigEnvs);\r
1529     }\r
1530   }\r
1531 \r
1532   return(Status);\r
1533 }\r
1534 /**\r
1535   Execute the command line.\r
1536 \r
1537   This function creates a nested instance of the shell and executes the specified\r
1538   command (CommandLine) with the specified environment (Environment). Upon return,\r
1539   the status code returned by the specified command is placed in StatusCode.\r
1540 \r
1541   If Environment is NULL, then the current environment is used and all changes made\r
1542   by the commands executed will be reflected in the current environment. If the\r
1543   Environment is non-NULL, then the changes made will be discarded.\r
1544 \r
1545   The CommandLine is executed from the current working directory on the current\r
1546   device.\r
1547 \r
1548   @param ParentImageHandle  A handle of the image that is executing the specified\r
1549                             command line.\r
1550   @param CommandLine        Points to the NULL-terminated UCS-2 encoded string\r
1551                             containing the command line. If NULL then the command-\r
1552                             line will be empty.\r
1553   @param Environment        Points to a NULL-terminated array of environment\r
1554                             variables with the format 'x=y', where x is the\r
1555                             environment variable name and y is the value. If this\r
1556                             is NULL, then the current shell environment is used.\r
1557   @param StatusCode         Points to the status code returned by the command.\r
1558 \r
1559   @retval EFI_SUCCESS       The command executed successfully. The  status code\r
1560                             returned by the command is pointed to by StatusCode.\r
1561   @retval EFI_INVALID_PARAMETER The parameters are invalid.\r
1562   @retval EFI_OUT_OF_RESOURCES Out of resources.\r
1563   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.\r
1564   @retval EFI_UNSUPPORTED   The support level required for this function is not present.\r
1565 \r
1566   @sa InternalShellExecuteDevicePath\r
1567 **/\r
1568 EFI_STATUS\r
1569 EFIAPI\r
1570 EfiShellExecute(\r
1571   IN EFI_HANDLE *ParentImageHandle,\r
1572   IN CHAR16 *CommandLine OPTIONAL,\r
1573   IN CHAR16 **Environment OPTIONAL,\r
1574   OUT EFI_STATUS *StatusCode OPTIONAL\r
1575   )\r
1576 {\r
1577   EFI_STATUS                Status;\r
1578   CHAR16                    *Temp;\r
1579   EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
1580   UINTN                     Size;\r
1581 \r
1582   if ((PcdGet8(PcdShellSupportLevel) < 1)) {\r
1583     return (EFI_UNSUPPORTED);\r
1584   }\r
1585 \r
1586   DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
1587 \r
1588   DEBUG_CODE_BEGIN();\r
1589   Temp = gDevPathToText->ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);\r
1590   FreePool(Temp);\r
1591   Temp = gDevPathToText->ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);\r
1592   FreePool(Temp);\r
1593   Temp = gDevPathToText->ConvertDevicePathToText(DevPath, TRUE, TRUE);\r
1594   FreePool(Temp);\r
1595   DEBUG_CODE_END();\r
1596 \r
1597   Temp = NULL;\r
1598   Size = 0;\r
1599   ASSERT((Temp == NULL && Size == 0) || (Temp != NULL));\r
1600   StrnCatGrow(&Temp, &Size, L"Shell.efi ", 0);\r
1601   StrnCatGrow(&Temp, &Size, CommandLine, 0);\r
1602 \r
1603   Status = InternalShellExecuteDevicePath(\r
1604     ParentImageHandle,\r
1605     DevPath,\r
1606     Temp,\r
1607     (CONST CHAR16**)Environment,\r
1608     StatusCode);\r
1609 \r
1610   //\r
1611   // de-allocate and return\r
1612   //\r
1613   FreePool(DevPath);\r
1614   FreePool(Temp);\r
1615   return(Status);\r
1616 }\r
1617 \r
1618 /**\r
1619   Utility cleanup function for EFI_SHELL_FILE_INFO objects.\r
1620 \r
1621   1) frees all pointers (non-NULL)\r
1622   2) Closes the SHELL_FILE_HANDLE\r
1623 \r
1624   @param FileListNode     pointer to the list node to free\r
1625 **/\r
1626 VOID\r
1627 EFIAPI\r
1628 InternalFreeShellFileInfoNode(\r
1629   IN EFI_SHELL_FILE_INFO *FileListNode\r
1630   )\r
1631 {\r
1632   if (FileListNode->Info != NULL) {\r
1633     FreePool((VOID*)FileListNode->Info);\r
1634   }\r
1635   if (FileListNode->FileName != NULL) {\r
1636     FreePool((VOID*)FileListNode->FileName);\r
1637   }\r
1638   if (FileListNode->FullName != NULL) {\r
1639     FreePool((VOID*)FileListNode->FullName);\r
1640   }\r
1641   if (FileListNode->Handle != NULL) {\r
1642     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);\r
1643   }\r
1644   FreePool(FileListNode);\r
1645 }\r
1646 /**\r
1647   Frees the file list.\r
1648 \r
1649   This function cleans up the file list and any related data structures. It has no\r
1650   impact on the files themselves.\r
1651 \r
1652   @param FileList               The file list to free. Type EFI_SHELL_FILE_INFO is\r
1653                                 defined in OpenFileList()\r
1654 \r
1655   @retval EFI_SUCCESS           Free the file list successfully.\r
1656   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;\r
1657 **/\r
1658 EFI_STATUS\r
1659 EFIAPI\r
1660 EfiShellFreeFileList(\r
1661   IN EFI_SHELL_FILE_INFO **FileList\r
1662   )\r
1663 {\r
1664   EFI_SHELL_FILE_INFO *ShellFileListItem;\r
1665 \r
1666   if (FileList == NULL || *FileList == NULL) {\r
1667     return (EFI_INVALID_PARAMETER);\r
1668   }\r
1669 \r
1670   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)\r
1671       ; !IsListEmpty(&(*FileList)->Link)\r
1672       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)\r
1673      ){\r
1674     RemoveEntryList(&ShellFileListItem->Link);\r
1675     InternalFreeShellFileInfoNode(ShellFileListItem);\r
1676   }\r
1677   return(EFI_SUCCESS);\r
1678 }\r
1679 \r
1680 /**\r
1681   Deletes the duplicate file names files in the given file list.\r
1682 \r
1683   This function deletes the reduplicate files in the given file list.\r
1684 \r
1685   @param FileList               A pointer to the first entry in the file list.\r
1686 \r
1687   @retval EFI_SUCCESS           Always success.\r
1688   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;\r
1689 **/\r
1690 EFI_STATUS\r
1691 EFIAPI\r
1692 EfiShellRemoveDupInFileList(\r
1693   IN EFI_SHELL_FILE_INFO **FileList\r
1694   )\r
1695 {\r
1696   EFI_SHELL_FILE_INFO *ShellFileListItem;\r
1697   EFI_SHELL_FILE_INFO *ShellFileListItem2;\r
1698 \r
1699   if (FileList == NULL || *FileList == NULL) {\r
1700     return (EFI_INVALID_PARAMETER);\r
1701   }\r
1702   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)\r
1703       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)\r
1704       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)\r
1705      ){\r
1706     for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)\r
1707         ; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link)\r
1708         ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link)\r
1709        ){\r
1710       if (gUnicodeCollation->StriColl(\r
1711             gUnicodeCollation,\r
1712             (CHAR16*)ShellFileListItem->FullName,\r
1713             (CHAR16*)ShellFileListItem2->FullName) == 0\r
1714          ){\r
1715         RemoveEntryList(&ShellFileListItem2->Link);\r
1716         InternalFreeShellFileInfoNode(ShellFileListItem2);\r
1717       }\r
1718     }\r
1719   }\r
1720   return (EFI_SUCCESS);\r
1721 }\r
1722 /**\r
1723   Allocates and duplicates a EFI_SHELL_FILE_INFO node.\r
1724 \r
1725   @param[in] Node     The node to copy from.\r
1726   @param[in] Save     TRUE to set Node->Handle to NULL, FALSE otherwise.\r
1727 \r
1728   @retval NULL        a memory allocation error ocurred\r
1729   @return != NULL     a pointer to the new node\r
1730 **/\r
1731 EFI_SHELL_FILE_INFO*\r
1732 EFIAPI\r
1733 InternalDuplicateShellFileInfo(\r
1734   IN       EFI_SHELL_FILE_INFO *Node,\r
1735   IN BOOLEAN                   Save\r
1736   )\r
1737 {\r
1738   EFI_SHELL_FILE_INFO *NewNode;\r
1739 \r
1740   NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));\r
1741   if (NewNode == NULL) {\r
1742     return (NULL);\r
1743   }\r
1744   NewNode->FullName = AllocateZeroPool(StrSize(Node->FullName));\r
1745 \r
1746   NewNode->FileName = AllocateZeroPool(StrSize(Node->FileName));\r
1747   NewNode->Info     = AllocateZeroPool((UINTN)Node->Info->Size);\r
1748   if ( NewNode->FullName == NULL\r
1749     || NewNode->FileName == NULL\r
1750     || NewNode->Info == NULL\r
1751    ){\r
1752     return(NULL);\r
1753   }\r
1754   NewNode->Status = Node->Status;\r
1755   NewNode->Handle = Node->Handle;\r
1756   if (!Save) {\r
1757     Node->Handle = NULL;\r
1758   }\r
1759   StrCpy((CHAR16*)NewNode->FullName, Node->FullName);\r
1760   StrCpy((CHAR16*)NewNode->FileName, Node->FileName);\r
1761   CopyMem(NewNode->Info, Node->Info, (UINTN)Node->Info->Size);\r
1762 \r
1763   return(NewNode);\r
1764 }\r
1765 \r
1766 /**\r
1767   Allocates and populates a EFI_SHELL_FILE_INFO structure.  if any memory operation\r
1768   failed it will return NULL.\r
1769 \r
1770   @param[in] BasePath         the Path to prepend onto filename for FullPath\r
1771   @param[in] Status           Status member initial value.\r
1772   @param[in] FullName         FullName member initial value.\r
1773   @param[in] FileName         FileName member initial value.\r
1774   @param[in] Handle           Handle member initial value.\r
1775   @param[in] Info             Info struct to copy.\r
1776 \r
1777   @retval NULL                An error ocurred.\r
1778   @return                     a pointer to the newly allocated structure.\r
1779 **/\r
1780 EFI_SHELL_FILE_INFO *\r
1781 EFIAPI\r
1782 CreateAndPopulateShellFileInfo(\r
1783   IN CONST CHAR16 *BasePath,\r
1784   IN CONST EFI_STATUS Status,\r
1785   IN CONST CHAR16 *FullName,\r
1786   IN CONST CHAR16 *FileName,\r
1787   IN CONST SHELL_FILE_HANDLE Handle,\r
1788   IN CONST EFI_FILE_INFO *Info\r
1789   )\r
1790 {\r
1791   EFI_SHELL_FILE_INFO *ShellFileListItem;\r
1792   CHAR16              *TempString;\r
1793   UINTN               Size;\r
1794 \r
1795   TempString = NULL;\r
1796   Size = 0;\r
1797 \r
1798   ShellFileListItem = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));\r
1799   if (ShellFileListItem == NULL) {\r
1800     return (NULL);\r
1801   }\r
1802   if (Info != NULL) {\r
1803     ShellFileListItem->Info = AllocateZeroPool((UINTN)Info->Size);\r
1804     if (ShellFileListItem->Info == NULL) {\r
1805       FreePool(ShellFileListItem);\r
1806       return (NULL);\r
1807     }\r
1808     CopyMem(ShellFileListItem->Info, Info, (UINTN)Info->Size);\r
1809   } else {\r
1810     ShellFileListItem->Info = NULL;\r
1811   }\r
1812   if (FileName != NULL) {\r
1813     ASSERT(TempString == NULL);\r
1814     ShellFileListItem->FileName = StrnCatGrow(&TempString, 0, FileName, 0);\r
1815     if (ShellFileListItem->FileName == NULL) {\r
1816       FreePool(ShellFileListItem->Info);\r
1817       FreePool(ShellFileListItem);\r
1818       return (NULL);\r
1819     }\r
1820   } else {\r
1821     ShellFileListItem->FileName = NULL;\r
1822   }\r
1823   Size = 0;\r
1824   TempString = NULL;\r
1825   if (BasePath != NULL) {\r
1826     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));\r
1827     TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);\r
1828     if (TempString == NULL) {\r
1829       FreePool((VOID*)ShellFileListItem->FileName);\r
1830       FreePool(ShellFileListItem->Info);\r
1831       FreePool(ShellFileListItem);\r
1832       return (NULL);\r
1833     }\r
1834   }\r
1835   if (ShellFileListItem->FileName != NULL) {\r
1836     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));\r
1837     TempString = StrnCatGrow(&TempString, &Size, ShellFileListItem->FileName, 0);\r
1838     if (TempString == NULL) {\r
1839       FreePool((VOID*)ShellFileListItem->FileName);\r
1840       FreePool(ShellFileListItem->Info);\r
1841       FreePool(ShellFileListItem);\r
1842       return (NULL);\r
1843     }\r
1844   }\r
1845 \r
1846   ShellFileListItem->FullName = TempString;\r
1847   ShellFileListItem->Status   = Status;\r
1848   ShellFileListItem->Handle   = Handle;\r
1849 \r
1850   return (ShellFileListItem);\r
1851 }\r
1852 \r
1853 /**\r
1854   Find all files in a specified directory.\r
1855 \r
1856   @param FileDirHandle          Handle of the directory to search.\r
1857   @param FileList               On return, points to the list of files in the directory\r
1858                                 or NULL if there are no files in the directory.\r
1859 \r
1860   @retval EFI_SUCCESS           File information was returned successfully.\r
1861   @retval EFI_VOLUME_CORRUPTED  The file system structures have been corrupted.\r
1862   @retval EFI_DEVICE_ERROR      The device reported an error.\r
1863   @retval EFI_NO_MEDIA          The device media is not present.\r
1864   @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.\r
1865   @return                       An error from FileHandleGetFileName().\r
1866 **/\r
1867 EFI_STATUS\r
1868 EFIAPI\r
1869 EfiShellFindFilesInDir(\r
1870   IN SHELL_FILE_HANDLE FileDirHandle,\r
1871   OUT EFI_SHELL_FILE_INFO **FileList\r
1872   )\r
1873 {\r
1874   EFI_SHELL_FILE_INFO       *ShellFileList;\r
1875   EFI_SHELL_FILE_INFO       *ShellFileListItem;\r
1876   EFI_FILE_INFO             *FileInfo;\r
1877   EFI_STATUS                Status;\r
1878   BOOLEAN                   NoFile;\r
1879   CHAR16                    *TempString;\r
1880   CHAR16                    *BasePath;\r
1881   UINTN                     Size;\r
1882   CHAR16                    *TempSpot;\r
1883 \r
1884   Status = FileHandleGetFileName(FileDirHandle, &BasePath);\r
1885   if (EFI_ERROR(Status)) {\r
1886     return (Status);\r
1887   }\r
1888 \r
1889   if (ShellFileHandleGetPath(FileDirHandle) != NULL) {\r
1890     TempString        = NULL;\r
1891     Size              = 0;\r
1892     TempString        = StrnCatGrow(&TempString, &Size, ShellFileHandleGetPath(FileDirHandle), 0);\r
1893     if (TempString == NULL) {\r
1894       return (EFI_OUT_OF_RESOURCES);\r
1895     }\r
1896     TempSpot          = StrStr(TempString, L";");\r
1897 \r
1898     if (TempSpot != NULL) {\r
1899       *TempSpot = CHAR_NULL;\r
1900     }\r
1901 \r
1902     TempString        = StrnCatGrow(&TempString, &Size, BasePath, 0);\r
1903     if (TempString == NULL) {\r
1904       return (EFI_OUT_OF_RESOURCES);\r
1905     }\r
1906     BasePath          = TempString;\r
1907   }\r
1908 \r
1909   NoFile            = FALSE;\r
1910   ShellFileList     = NULL;\r
1911   ShellFileListItem = NULL;\r
1912   FileInfo          = NULL;\r
1913   Status            = EFI_SUCCESS;\r
1914 \r
1915 \r
1916   for ( Status = FileHandleFindFirstFile(FileDirHandle, &FileInfo)\r
1917       ; !EFI_ERROR(Status) && !NoFile\r
1918       ; Status = FileHandleFindNextFile(FileDirHandle, FileInfo, &NoFile)\r
1919      ){\r
1920     TempString  = NULL;\r
1921     Size        = 0;\r
1922     //\r
1923     // allocate a new EFI_SHELL_FILE_INFO and populate it...\r
1924     //\r
1925     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));\r
1926     TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);\r
1927     TempString = StrnCatGrow(&TempString, &Size, FileInfo->FileName, 0);\r
1928     ShellFileListItem = CreateAndPopulateShellFileInfo(\r
1929       BasePath,\r
1930       EFI_SUCCESS, // success since we didnt fail to open it...\r
1931       TempString,\r
1932       FileInfo->FileName,\r
1933       NULL, // no handle since not open\r
1934       FileInfo);\r
1935 \r
1936     if (ShellFileList == NULL) {\r
1937       ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));\r
1938       ASSERT(ShellFileList != NULL);\r
1939       InitializeListHead(&ShellFileList->Link);\r
1940     }\r
1941     InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);\r
1942   }\r
1943   if (EFI_ERROR(Status)) {\r
1944     EfiShellFreeFileList(&ShellFileList);\r
1945     *FileList = NULL;\r
1946   } else {\r
1947     *FileList = ShellFileList;\r
1948   }\r
1949   SHELL_FREE_NON_NULL(BasePath);\r
1950   return(Status);\r
1951 }\r
1952 \r
1953 /**\r
1954   Updates a file name to be preceeded by the mapped drive name\r
1955 \r
1956   @param[in] BasePath      the Mapped drive name to prepend\r
1957   @param[in, out] Path     pointer to pointer to the file name to update.\r
1958 \r
1959   @retval EFI_SUCCESS\r
1960   @retval EFI_OUT_OF_RESOURCES\r
1961 **/\r
1962 EFI_STATUS\r
1963 EFIAPI\r
1964 UpdateFileName(\r
1965   IN CONST CHAR16 *BasePath,\r
1966   IN OUT CHAR16   **Path\r
1967   )\r
1968 {\r
1969   CHAR16              *Path2;\r
1970   UINTN               Path2Size;\r
1971 \r
1972   Path2Size = 0;\r
1973   Path2 = NULL;\r
1974 \r
1975   ASSERT(Path      != NULL);\r
1976   ASSERT(*Path     != NULL);\r
1977   ASSERT(BasePath  != NULL);\r
1978 \r
1979   //\r
1980   // convert a local path to an absolute path\r
1981   //\r
1982   if (StrStr(*Path, L":") == NULL) {\r
1983     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));\r
1984     StrnCatGrow(&Path2, &Path2Size, BasePath, 0);\r
1985     if (Path2 == NULL) {\r
1986       return (EFI_OUT_OF_RESOURCES);\r
1987     }\r
1988     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));\r
1989     StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0);\r
1990     if (Path2 == NULL) {\r
1991       return (EFI_OUT_OF_RESOURCES);\r
1992     }\r
1993   }\r
1994 \r
1995   FreePool(*Path);\r
1996   (*Path) = Path2;\r
1997 \r
1998   return (EFI_SUCCESS);\r
1999 }\r
2000 \r
2001 /**\r
2002   If FileHandle is a directory then the function reads from FileHandle and reads in\r
2003   each of the FileInfo structures.  If one of them matches the Pattern's first\r
2004   "level" then it opens that handle and calls itself on that handle.\r
2005 \r
2006   If FileHandle is a file and matches all of the remaining Pattern (which would be\r
2007   on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.\r
2008 \r
2009   Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call\r
2010   FreeFileList with FileList.\r
2011 \r
2012   @param[in] FilePattern         The FilePattern to check against.\r
2013   @param[in] UnicodeCollation    The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure\r
2014   @param[in] FileHandle          The FileHandle to start with\r
2015   @param[in, out] FileList       pointer to pointer to list of found files.\r
2016   @param[in] ParentNode          The node for the parent. Same file as identified by HANDLE.\r
2017   @param[in] MapName             The file system name this file is on.\r
2018 \r
2019   @retval EFI_SUCCESS           all files were found and the FileList contains a list.\r
2020   @retval EFI_NOT_FOUND         no files were found\r
2021   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed\r
2022 **/\r
2023 EFI_STATUS\r
2024 EFIAPI\r
2025 ShellSearchHandle(\r
2026   IN     CONST CHAR16                         *FilePattern,\r
2027   IN           EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,\r
2028   IN           SHELL_FILE_HANDLE              FileHandle,\r
2029   IN OUT       EFI_SHELL_FILE_INFO            **FileList,\r
2030   IN     CONST EFI_SHELL_FILE_INFO            *ParentNode OPTIONAL,\r
2031   IN     CONST CHAR16                         *MapName\r
2032   )\r
2033 {\r
2034   EFI_STATUS          Status;\r
2035   CONST CHAR16        *NextFilePatternStart;\r
2036   CHAR16              *CurrentFilePattern;\r
2037   EFI_SHELL_FILE_INFO *ShellInfo;\r
2038   EFI_SHELL_FILE_INFO *ShellInfoNode;\r
2039   EFI_SHELL_FILE_INFO *NewShellNode;\r
2040   BOOLEAN             Directory;\r
2041   CHAR16              *NewFullName;\r
2042   UINTN               Size;\r
2043 \r
2044   if ( FilePattern      == NULL\r
2045     || UnicodeCollation == NULL\r
2046     || FileList         == NULL\r
2047    ){\r
2048     return (EFI_INVALID_PARAMETER);\r
2049   }\r
2050   ShellInfo = NULL;\r
2051   CurrentFilePattern = NULL;\r
2052 \r
2053   if (*FilePattern == L'\\') {\r
2054     FilePattern++;\r
2055   }\r
2056 \r
2057   for( NextFilePatternStart = FilePattern\r
2058      ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'\r
2059      ; NextFilePatternStart++);\r
2060 \r
2061   CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));\r
2062   ASSERT(CurrentFilePattern != NULL);\r
2063   StrnCpy(CurrentFilePattern, FilePattern, NextFilePatternStart-FilePattern);\r
2064 \r
2065   if (CurrentFilePattern[0]   == CHAR_NULL\r
2066     &&NextFilePatternStart[0] == CHAR_NULL\r
2067    ){\r
2068     //\r
2069     // Add the current parameter FileHandle to the list, then end...\r
2070     //\r
2071     if (ParentNode == NULL) {\r
2072       Status = EFI_INVALID_PARAMETER;\r
2073     } else {\r
2074       NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);\r
2075       if (NewShellNode == NULL) {\r
2076         Status = EFI_OUT_OF_RESOURCES;\r
2077       } else {\r
2078         NewShellNode->Handle = NULL;\r
2079         if (*FileList == NULL) {\r
2080           *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));\r
2081           InitializeListHead(&((*FileList)->Link));\r
2082         }\r
2083 \r
2084         //\r
2085         // Add to the returning to use list\r
2086         //\r
2087         InsertTailList(&(*FileList)->Link, &NewShellNode->Link);\r
2088 \r
2089         Status = EFI_SUCCESS;\r
2090       }\r
2091     }\r
2092   } else {\r
2093     Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo);\r
2094 \r
2095     if (!EFI_ERROR(Status)){\r
2096       if (StrStr(NextFilePatternStart, L"\\") != NULL){\r
2097         Directory = TRUE;\r
2098       } else {\r
2099         Directory = FALSE;\r
2100       }\r
2101       for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link)\r
2102           ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)\r
2103           ; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link)\r
2104          ){\r
2105         if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){\r
2106           if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) {\r
2107             Size = StrSize(ShellInfoNode->FullName);\r
2108             Size += StrSize(MapName) + sizeof(CHAR16);\r
2109             NewFullName = AllocateZeroPool(Size);\r
2110             if (NewFullName == NULL) {\r
2111               Status = EFI_OUT_OF_RESOURCES;\r
2112             } else {\r
2113               StrCpy(NewFullName, MapName);\r
2114               StrCat(NewFullName, ShellInfoNode->FullName+1);\r
2115               FreePool((VOID*)ShellInfoNode->FullName);\r
2116               ShellInfoNode->FullName = NewFullName;\r
2117             }\r
2118           }\r
2119           if (Directory && !EFI_ERROR(Status) && ShellInfoNode->FullName != NULL && ShellInfoNode->FileName != NULL){\r
2120             //\r
2121             // should be a directory\r
2122             //\r
2123 \r
2124             //\r
2125             // don't open the . and .. directories\r
2126             //\r
2127             if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)\r
2128               && (StrCmp(ShellInfoNode->FileName, L"..") != 0)\r
2129              ){\r
2130               //\r
2131               //\r
2132               //\r
2133               if (EFI_ERROR(Status)) {\r
2134                 break;\r
2135               }\r
2136               //\r
2137               // Open the directory since we need that handle in the next recursion.\r
2138               //\r
2139               ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);\r
2140 \r
2141               //\r
2142               // recurse with the next part of the pattern\r
2143               //\r
2144               Status = ShellSearchHandle(NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);\r
2145             }\r
2146           } else if (!EFI_ERROR(Status)) {\r
2147             //\r
2148             // should be a file\r
2149             //\r
2150 \r
2151             //\r
2152             // copy the information we need into a new Node\r
2153             //\r
2154             NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);\r
2155             ASSERT(NewShellNode != NULL);\r
2156             if (NewShellNode == NULL) {\r
2157               Status = EFI_OUT_OF_RESOURCES;\r
2158             }\r
2159             if (*FileList == NULL) {\r
2160               *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));\r
2161               InitializeListHead(&((*FileList)->Link));\r
2162             }\r
2163 \r
2164             //\r
2165             // Add to the returning to use list\r
2166             //\r
2167             InsertTailList(&(*FileList)->Link, &NewShellNode->Link);\r
2168           }\r
2169         }\r
2170         if (EFI_ERROR(Status)) {\r
2171           break;\r
2172         }\r
2173       }\r
2174       if (EFI_ERROR(Status)) {\r
2175         EfiShellFreeFileList(&ShellInfo);\r
2176       } else {\r
2177         Status = EfiShellFreeFileList(&ShellInfo);\r
2178       }\r
2179     }\r
2180   }\r
2181 \r
2182   FreePool(CurrentFilePattern);\r
2183   return (Status);\r
2184 }\r
2185 \r
2186 /**\r
2187   Find files that match a specified pattern.\r
2188 \r
2189   This function searches for all files and directories that match the specified\r
2190   FilePattern. The FilePattern can contain wild-card characters. The resulting file\r
2191   information is placed in the file list FileList.\r
2192 \r
2193   Wildcards are processed\r
2194   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.\r
2195 \r
2196   The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo\r
2197   field is set to NULL.\r
2198 \r
2199   if *FileList is not NULL then it must be a pre-existing and properly initialized list.\r
2200 \r
2201   @param FilePattern      Points to a NULL-terminated shell file path, including wildcards.\r
2202   @param FileList         On return, points to the start of a file list containing the names\r
2203                           of all matching files or else points to NULL if no matching files\r
2204                           were found.  only on a EFI_SUCCESS return will; this be non-NULL.\r
2205 \r
2206   @retval EFI_SUCCESS           Files found.  FileList is a valid list.\r
2207   @retval EFI_NOT_FOUND         No files found.\r
2208   @retval EFI_NO_MEDIA          The device has no media\r
2209   @retval EFI_DEVICE_ERROR      The device reported an error\r
2210   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted\r
2211 **/\r
2212 EFI_STATUS\r
2213 EFIAPI\r
2214 EfiShellFindFiles(\r
2215   IN CONST CHAR16 *FilePattern,\r
2216   OUT EFI_SHELL_FILE_INFO **FileList\r
2217   )\r
2218 {\r
2219   EFI_STATUS                      Status;\r
2220   CHAR16                          *PatternCopy;\r
2221   CHAR16                          *PatternCurrentLocation;\r
2222   EFI_DEVICE_PATH_PROTOCOL        *RootDevicePath;\r
2223   SHELL_FILE_HANDLE               RootFileHandle;\r
2224   CHAR16                          *MapName;\r
2225   UINTN                           Count;\r
2226 \r
2227   if ( FilePattern      == NULL\r
2228     || FileList         == NULL\r
2229     || StrStr(FilePattern, L":") == NULL\r
2230    ){\r
2231     return (EFI_INVALID_PARAMETER);\r
2232   }\r
2233   Status = EFI_SUCCESS;\r
2234   RootDevicePath = NULL;\r
2235   RootFileHandle = NULL;\r
2236   MapName        = NULL;\r
2237   PatternCopy = AllocateZeroPool(StrSize(FilePattern));\r
2238   if (PatternCopy == NULL) {\r
2239     return (EFI_OUT_OF_RESOURCES);\r
2240   }\r
2241   StrCpy(PatternCopy, FilePattern);\r
2242 \r
2243   PatternCopy = PathCleanUpDirectories(PatternCopy);\r
2244 \r
2245   Count = StrStr(PatternCopy, L":") - PatternCopy;\r
2246   Count += 2;\r
2247 \r
2248   ASSERT(MapName == NULL);\r
2249   MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);\r
2250   if (MapName == NULL) {\r
2251     Status = EFI_OUT_OF_RESOURCES;\r
2252   }\r
2253 \r
2254   if (!EFI_ERROR(Status)) {\r
2255     RootDevicePath = EfiShellGetDevicePathFromFilePath(PatternCopy);\r
2256     if (RootDevicePath == NULL) {\r
2257       Status = EFI_INVALID_PARAMETER;\r
2258     } else {\r
2259       Status = EfiShellOpenRoot(RootDevicePath, &RootFileHandle);\r
2260       if (!EFI_ERROR(Status)) {\r
2261         for ( PatternCurrentLocation = PatternCopy\r
2262             ; *PatternCurrentLocation != ':'\r
2263             ; PatternCurrentLocation++);\r
2264         PatternCurrentLocation++;\r
2265         Status = ShellSearchHandle(PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);\r
2266       }\r
2267       FreePool(RootDevicePath);\r
2268     }\r
2269   }\r
2270 \r
2271   SHELL_FREE_NON_NULL(PatternCopy);\r
2272   SHELL_FREE_NON_NULL(MapName);\r
2273 \r
2274   return(Status);\r
2275 }\r
2276 \r
2277 /**\r
2278   Opens the files that match the path specified.\r
2279 \r
2280   This function opens all of the files specified by Path. Wildcards are processed\r
2281   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each\r
2282   matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.\r
2283 \r
2284   @param Path                   A pointer to the path string.\r
2285   @param OpenMode               Specifies the mode used to open each file, EFI_FILE_MODE_READ or\r
2286                                 EFI_FILE_MODE_WRITE.\r
2287   @param FileList               Points to the start of a list of files opened.\r
2288 \r
2289   @retval EFI_SUCCESS           Create the file list successfully.\r
2290   @return Others                Can't create the file list.\r
2291 **/\r
2292 EFI_STATUS\r
2293 EFIAPI\r
2294 EfiShellOpenFileList(\r
2295   IN CHAR16 *Path,\r
2296   IN UINT64 OpenMode,\r
2297   IN OUT EFI_SHELL_FILE_INFO **FileList\r
2298   )\r
2299 {\r
2300   EFI_STATUS Status;\r
2301   EFI_SHELL_FILE_INFO *ShellFileListItem;\r
2302   CHAR16              *Path2;\r
2303   UINTN               Path2Size;\r
2304   CONST CHAR16        *CurDir;\r
2305   BOOLEAN             Found;\r
2306 \r
2307   PathCleanUpDirectories(Path);\r
2308 \r
2309   Path2Size     = 0;\r
2310   Path2         = NULL;\r
2311 \r
2312   if (FileList == NULL || *FileList == NULL) {\r
2313     return (EFI_INVALID_PARAMETER);\r
2314   }\r
2315 \r
2316   if (*Path == L'.' && *(Path+1) == L'\\') {\r
2317     Path+=2;\r
2318   }\r
2319 \r
2320   //\r
2321   // convert a local path to an absolute path\r
2322   //\r
2323   if (StrStr(Path, L":") == NULL) {\r
2324     CurDir = EfiShellGetCurDir(NULL);\r
2325     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));\r
2326     StrnCatGrow(&Path2, &Path2Size, CurDir, 0);\r
2327     if (*Path == L'\\') {\r
2328       Path++;\r
2329       while (PathRemoveLastItem(Path2)) ;\r
2330     }\r
2331     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));\r
2332     StrnCatGrow(&Path2, &Path2Size, Path, 0);\r
2333   } else {\r
2334     ASSERT(Path2 == NULL);\r
2335     StrnCatGrow(&Path2, NULL, Path, 0);\r
2336   }\r
2337 \r
2338   PathCleanUpDirectories (Path2);\r
2339 \r
2340   //\r
2341   // do the search\r
2342   //\r
2343   Status = EfiShellFindFiles(Path2, FileList);\r
2344 \r
2345   FreePool(Path2);\r
2346 \r
2347   if (EFI_ERROR(Status)) {\r
2348     return (Status);\r
2349   }\r
2350 \r
2351   Found = FALSE;\r
2352   //\r
2353   // We had no errors so open all the files (that are not already opened...)\r
2354   //\r
2355   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)\r
2356       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)\r
2357       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)\r
2358      ){\r
2359     if (ShellFileListItem->Status == 0 && ShellFileListItem->Handle == NULL) {\r
2360       ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);\r
2361       Found = TRUE;\r
2362     }\r
2363   }\r
2364 \r
2365   if (!Found) {\r
2366     return (EFI_NOT_FOUND);\r
2367   }\r
2368   return(EFI_SUCCESS);\r
2369 }\r
2370 \r
2371 /**\r
2372   This function updated with errata.\r
2373 \r
2374   Gets either a single or list of environment variables.\r
2375 \r
2376   If name is not NULL then this function returns the current value of the specified\r
2377   environment variable.\r
2378 \r
2379   If Name is NULL, then a list of all environment variable names is returned.  Each is a\r
2380   NULL terminated string with a double NULL terminating the list.\r
2381 \r
2382   @param Name                   A pointer to the environment variable name.  If\r
2383                                 Name is NULL, then the function will return all\r
2384                                 of the defined shell environment variables.  In\r
2385                                 the case where multiple environment variables are\r
2386                                 being returned, each variable will be terminated by\r
2387                                 a NULL, and the list will be terminated by a double\r
2388                                 NULL.\r
2389 \r
2390   @return !=NULL                A pointer to the returned string.\r
2391                                 The returned pointer does not need to be freed by the caller.\r
2392 \r
2393   @retval NULL                  The environment variable doesn't exist or there are\r
2394                                 no environment variables.\r
2395 **/\r
2396 CONST CHAR16 *\r
2397 EFIAPI\r
2398 EfiShellGetEnv(\r
2399   IN CONST CHAR16 *Name\r
2400   )\r
2401 {\r
2402   EFI_STATUS  Status;\r
2403   VOID        *Buffer;\r
2404   UINTN       Size;\r
2405   LIST_ENTRY  List;\r
2406   ENV_VAR_LIST *Node;\r
2407   CHAR16      *CurrentWriteLocation;\r
2408 \r
2409   Size = 0;\r
2410   Buffer = NULL;\r
2411 \r
2412   if (Name == NULL) {\r
2413     //\r
2414     // Get all our environment variables\r
2415     //\r
2416     InitializeListHead(&List);\r
2417     Status = GetEnvironmentVariableList(&List);\r
2418     if (EFI_ERROR(Status)){\r
2419       return (NULL);\r
2420     }\r
2421 \r
2422     //\r
2423     // Build the semi-colon delimited list. (2 passes)\r
2424     //\r
2425     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)\r
2426       ; !IsNull(&List, &Node->Link)\r
2427       ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)\r
2428      ){\r
2429       ASSERT(Node->Key != NULL);\r
2430       Size += StrSize(Node->Key);\r
2431     }\r
2432 \r
2433     Size += 2*sizeof(CHAR16);\r
2434 \r
2435     Buffer = AllocateZeroPool(Size);\r
2436     if (Buffer == NULL) {\r
2437       if (!IsListEmpty (&List)) {\r
2438         FreeEnvironmentVariableList(&List);\r
2439       }\r
2440       return (NULL);\r
2441     }\r
2442     CurrentWriteLocation = (CHAR16*)Buffer;\r
2443 \r
2444     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)\r
2445       ; !IsNull(&List, &Node->Link)\r
2446       ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)\r
2447      ){\r
2448       ASSERT(Node->Key != NULL);\r
2449       StrCpy(CurrentWriteLocation, Node->Key);\r
2450       CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;\r
2451     }\r
2452 \r
2453     //\r
2454     // Free the list...\r
2455     //\r
2456     if (!IsListEmpty (&List)) {\r
2457       FreeEnvironmentVariableList(&List);\r
2458     }\r
2459   } else {\r
2460     //\r
2461     // We are doing a specific environment variable\r
2462     //\r
2463 \r
2464     //\r
2465     // get the size we need for this EnvVariable\r
2466     //\r
2467     Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer);\r
2468     if (Status == EFI_BUFFER_TOO_SMALL) {\r
2469       //\r
2470       // Allocate the space and recall the get function\r
2471       //\r
2472       Buffer = AllocateZeroPool(Size);\r
2473       ASSERT(Buffer != NULL);\r
2474       Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer);\r
2475     }\r
2476     //\r
2477     // we didnt get it (might not exist)\r
2478     // free the memory if we allocated any and return NULL\r
2479     //\r
2480     if (EFI_ERROR(Status)) {\r
2481       if (Buffer != NULL) {\r
2482         FreePool(Buffer);\r
2483       }\r
2484       return (NULL);\r
2485     }\r
2486   }\r
2487 \r
2488   //\r
2489   // return the buffer\r
2490   //\r
2491   return (AddBufferToFreeList(Buffer));\r
2492 }\r
2493 \r
2494 /**\r
2495   Internal variable setting function.  Allows for setting of the read only variables.\r
2496 \r
2497   @param Name                   Points to the NULL-terminated environment variable name.\r
2498   @param Value                  Points to the NULL-terminated environment variable value. If the value is an\r
2499                                 empty string then the environment variable is deleted.\r
2500   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).\r
2501 \r
2502   @retval EFI_SUCCESS           The environment variable was successfully updated.\r
2503 **/\r
2504 EFI_STATUS\r
2505 EFIAPI\r
2506 InternalEfiShellSetEnv(\r
2507   IN CONST CHAR16 *Name,\r
2508   IN CONST CHAR16 *Value,\r
2509   IN BOOLEAN Volatile\r
2510   )\r
2511 {\r
2512   if (Value == NULL || StrLen(Value) == 0) {\r
2513     return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name));\r
2514   } else {\r
2515     SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);\r
2516     if (Volatile) {\r
2517       return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value));\r
2518     } else {\r
2519       return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value));\r
2520     }\r
2521   }\r
2522 }\r
2523 \r
2524 /**\r
2525   Sets the environment variable.\r
2526 \r
2527   This function changes the current value of the specified environment variable. If the\r
2528   environment variable exists and the Value is an empty string, then the environment\r
2529   variable is deleted. If the environment variable exists and the Value is not an empty\r
2530   string, then the value of the environment variable is changed. If the environment\r
2531   variable does not exist and the Value is an empty string, there is no action. If the\r
2532   environment variable does not exist and the Value is a non-empty string, then the\r
2533   environment variable is created and assigned the specified value.\r
2534 \r
2535   For a description of volatile and non-volatile environment variables, see UEFI Shell\r
2536   2.0 specification section 3.6.1.\r
2537 \r
2538   @param Name                   Points to the NULL-terminated environment variable name.\r
2539   @param Value                  Points to the NULL-terminated environment variable value. If the value is an\r
2540                                 empty string then the environment variable is deleted.\r
2541   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).\r
2542 \r
2543   @retval EFI_SUCCESS           The environment variable was successfully updated.\r
2544 **/\r
2545 EFI_STATUS\r
2546 EFIAPI\r
2547 EfiShellSetEnv(\r
2548   IN CONST CHAR16 *Name,\r
2549   IN CONST CHAR16 *Value,\r
2550   IN BOOLEAN Volatile\r
2551   )\r
2552 {\r
2553   if (Name == NULL || *Name == CHAR_NULL) {\r
2554     return (EFI_INVALID_PARAMETER);\r
2555   }\r
2556   //\r
2557   // Make sure we dont 'set' a predefined read only variable\r
2558   //\r
2559   if (gUnicodeCollation->StriColl(\r
2560         gUnicodeCollation,\r
2561         (CHAR16*)Name,\r
2562         L"cwd") == 0\r
2563     ||gUnicodeCollation->StriColl(\r
2564         gUnicodeCollation,\r
2565         (CHAR16*)Name,\r
2566         L"Lasterror") == 0\r
2567     ||gUnicodeCollation->StriColl(\r
2568         gUnicodeCollation,\r
2569         (CHAR16*)Name,\r
2570         L"profiles") == 0\r
2571     ||gUnicodeCollation->StriColl(\r
2572         gUnicodeCollation,\r
2573         (CHAR16*)Name,\r
2574         L"uefishellsupport") == 0\r
2575     ||gUnicodeCollation->StriColl(\r
2576         gUnicodeCollation,\r
2577         (CHAR16*)Name,\r
2578         L"uefishellversion") == 0\r
2579     ||gUnicodeCollation->StriColl(\r
2580         gUnicodeCollation,\r
2581         (CHAR16*)Name,\r
2582         L"uefiversion") == 0\r
2583        ){\r
2584     return (EFI_INVALID_PARAMETER);\r
2585   }\r
2586   return (InternalEfiShellSetEnv(Name, Value, Volatile));\r
2587 }\r
2588 \r
2589 /**\r
2590   Returns the current directory on the specified device.\r
2591 \r
2592   If FileSystemMapping is NULL, it returns the current working directory. If the\r
2593   FileSystemMapping is not NULL, it returns the current directory associated with the\r
2594   FileSystemMapping. In both cases, the returned name includes the file system\r
2595   mapping (i.e. fs0:\current-dir).\r
2596 \r
2597   @param FileSystemMapping      A pointer to the file system mapping. If NULL,\r
2598                                 then the current working directory is returned.\r
2599 \r
2600   @retval !=NULL                The current directory.\r
2601   @retval NULL                  Current directory does not exist.\r
2602 **/\r
2603 CONST CHAR16 *\r
2604 EFIAPI\r
2605 EfiShellGetCurDir(\r
2606   IN CONST CHAR16 *FileSystemMapping OPTIONAL\r
2607   )\r
2608 {\r
2609   CHAR16  *PathToReturn;\r
2610   UINTN   Size;\r
2611   SHELL_MAP_LIST *MapListItem;\r
2612   if (!IsListEmpty(&gShellMapList.Link)) {\r
2613     //\r
2614     // if parameter is NULL, use current\r
2615     //\r
2616     if (FileSystemMapping == NULL) {\r
2617       return (EfiShellGetEnv(L"cwd"));\r
2618     } else {\r
2619       Size = 0;\r
2620       PathToReturn = NULL;\r
2621       MapListItem = ShellCommandFindMapItem(FileSystemMapping);\r
2622       if (MapListItem != NULL) {\r
2623         ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));\r
2624         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);\r
2625         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);\r
2626       }\r
2627     }\r
2628     return (AddBufferToFreeList(PathToReturn));\r
2629   } else {\r
2630     return (NULL);\r
2631   }\r
2632 }\r
2633 \r
2634 /**\r
2635   Changes the current directory on the specified device.\r
2636 \r
2637   If the FileSystem is NULL, and the directory Dir does not contain a file system's\r
2638   mapped name, this function changes the current working directory.\r
2639 \r
2640   If the FileSystem is NULL and the directory Dir contains a mapped name, then the\r
2641   current file system and the current directory on that file system are changed.\r
2642 \r
2643   If FileSystem is NULL, and Dir is not NULL, then this changes the current working file\r
2644   system.\r
2645 \r
2646   If FileSystem is not NULL and Dir is not NULL, then this function changes the current\r
2647   directory on the specified file system.\r
2648 \r
2649   If the current working directory or the current working file system is changed then the\r
2650   %cwd% environment variable will be updated\r
2651 \r
2652   @param FileSystem             A pointer to the file system's mapped name. If NULL, then the current working\r
2653                                 directory is changed.\r
2654   @param Dir                    Points to the NULL-terminated directory on the device specified by FileSystem.\r
2655 \r
2656   @retval EFI_SUCCESS           The operation was sucessful\r
2657   @retval EFI_NOT_FOUND         The file system could not be found\r
2658 **/\r
2659 EFI_STATUS\r
2660 EFIAPI\r
2661 EfiShellSetCurDir(\r
2662   IN CONST CHAR16 *FileSystem OPTIONAL,\r
2663   IN CONST CHAR16 *Dir\r
2664   )\r
2665 {\r
2666   CHAR16          *MapName;\r
2667   SHELL_MAP_LIST  *MapListItem;\r
2668   UINTN           Size;\r
2669   EFI_STATUS      Status;\r
2670   CHAR16          *TempString;\r
2671   CHAR16          *DirectoryName;\r
2672   UINTN           TempLen;\r
2673 \r
2674   Size          = 0;\r
2675   MapName       = NULL;\r
2676   MapListItem   = NULL;\r
2677   TempString    = NULL;\r
2678   DirectoryName = NULL;\r
2679 \r
2680   if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) {\r
2681     return (EFI_INVALID_PARAMETER);\r
2682   }\r
2683 \r
2684   if (IsListEmpty(&gShellMapList.Link)){\r
2685     return (EFI_NOT_FOUND);\r
2686   }\r
2687 \r
2688   DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);\r
2689   ASSERT(DirectoryName != NULL);\r
2690 \r
2691   PathCleanUpDirectories(DirectoryName);\r
2692 \r
2693   if (FileSystem == NULL) {\r
2694     //\r
2695     // determine the file system mapping to use\r
2696     //\r
2697     if (StrStr(DirectoryName, L":") != NULL) {\r
2698       ASSERT(MapName == NULL);\r
2699       MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));\r
2700     }\r
2701     //\r
2702     // find the file system mapping's entry in the list\r
2703     // or use current\r
2704     //\r
2705     if (MapName != NULL) {\r
2706       MapListItem = ShellCommandFindMapItem(MapName);\r
2707 \r
2708       //\r
2709       // make that the current file system mapping\r
2710       //\r
2711       if (MapListItem != NULL) {\r
2712         gShellCurDir = MapListItem;\r
2713       }\r
2714     } else {\r
2715       MapListItem = gShellCurDir;\r
2716     }\r
2717 \r
2718     if (MapListItem == NULL) {\r
2719       return (EFI_NOT_FOUND);\r
2720     }\r
2721 \r
2722     //\r
2723     // now update the MapListItem's current directory\r
2724     //\r
2725     if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {\r
2726       FreePool(MapListItem->CurrentDirectoryPath);\r
2727       MapListItem->CurrentDirectoryPath = NULL;\r
2728     }\r
2729     if (MapName != NULL) {\r
2730       TempLen = StrLen(MapName);\r
2731       if (TempLen != StrLen(DirectoryName)) {\r
2732         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2733         MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);\r
2734       }\r
2735     } else {\r
2736       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2737       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);\r
2738     }\r
2739     if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {\r
2740       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2741       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);\r
2742     }\r
2743   } else {\r
2744     //\r
2745     // cant have a mapping in the directory...\r
2746     //\r
2747     if (StrStr(DirectoryName, L":") != NULL) {\r
2748       return (EFI_INVALID_PARAMETER);\r
2749     }\r
2750     //\r
2751     // FileSystem != NULL\r
2752     //\r
2753     MapListItem = ShellCommandFindMapItem(FileSystem);\r
2754     if (MapListItem == NULL) {\r
2755       return (EFI_INVALID_PARAMETER);\r
2756     }\r
2757 //    gShellCurDir = MapListItem;\r
2758     if (DirectoryName != NULL) {\r
2759       //\r
2760       // change current dir on that file system\r
2761       //\r
2762 \r
2763       if (MapListItem->CurrentDirectoryPath != NULL) {\r
2764         FreePool(MapListItem->CurrentDirectoryPath);\r
2765         DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);\r
2766       }\r
2767 //      ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2768 //      MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);\r
2769       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2770       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);\r
2771       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2772       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);\r
2773       if (MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') {\r
2774         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));\r
2775         MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);\r
2776       }\r
2777     }\r
2778   }\r
2779   //\r
2780   // if updated the current directory then update the environment variable\r
2781   //\r
2782   if (MapListItem == gShellCurDir) {\r
2783     Size = 0;\r
2784     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));\r
2785     StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);\r
2786     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));\r
2787     StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);\r
2788     Status =  InternalEfiShellSetEnv(L"cwd", TempString, TRUE);\r
2789     FreePool(TempString);\r
2790     return (Status);\r
2791   }\r
2792   return(EFI_SUCCESS);\r
2793 }\r
2794 \r
2795 /**\r
2796   Return help information about a specific command.\r
2797 \r
2798   This function returns the help information for the specified command. The help text\r
2799   can be internal to the shell or can be from a UEFI Shell manual page.\r
2800 \r
2801   If Sections is specified, then each section name listed will be compared in a casesensitive\r
2802   manner, to the section names described in Appendix B. If the section exists,\r
2803   it will be appended to the returned help text. If the section does not exist, no\r
2804   information will be returned. If Sections is NULL, then all help text information\r
2805   available will be returned.\r
2806 \r
2807   @param Command                Points to the NULL-terminated UEFI Shell command name.\r
2808   @param Sections               Points to the NULL-terminated comma-delimited\r
2809                                 section names to return. If NULL, then all\r
2810                                 sections will be returned.\r
2811   @param HelpText               On return, points to a callee-allocated buffer\r
2812                                 containing all specified help text.\r
2813 \r
2814   @retval EFI_SUCCESS           The help text was returned.\r
2815   @retval EFI_OUT_OF_RESOURCES  The necessary buffer could not be allocated to hold the\r
2816                                 returned help text.\r
2817   @retval EFI_INVALID_PARAMETER HelpText is NULL\r
2818   @retval EFI_NOT_FOUND         There is no help text available for Command.\r
2819 **/\r
2820 EFI_STATUS\r
2821 EFIAPI\r
2822 EfiShellGetHelpText(\r
2823   IN CONST CHAR16 *Command,\r
2824   IN CONST CHAR16 *Sections OPTIONAL,\r
2825   OUT CHAR16 **HelpText\r
2826   )\r
2827 {\r
2828   CONST CHAR16  *ManFileName;\r
2829 \r
2830   ASSERT(HelpText != NULL);\r
2831 \r
2832   ManFileName = ShellCommandGetManFileNameHandler(Command);\r
2833 \r
2834   if (ManFileName != NULL) {\r
2835     return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));\r
2836   } else {\r
2837     return (ProcessManFile(Command, Command, Sections, NULL, HelpText));\r
2838   }\r
2839 }\r
2840 \r
2841 /**\r
2842   Gets the enable status of the page break output mode.\r
2843 \r
2844   User can use this function to determine current page break mode.\r
2845 \r
2846   @retval TRUE                  The page break output mode is enabled.\r
2847   @retval FALSE                 The page break output mode is disabled.\r
2848 **/\r
2849 BOOLEAN\r
2850 EFIAPI\r
2851 EfiShellGetPageBreak(\r
2852   VOID\r
2853   )\r
2854 {\r
2855   return(ShellInfoObject.PageBreakEnabled);\r
2856 }\r
2857 \r
2858 /**\r
2859   Judges whether the active shell is the root shell.\r
2860 \r
2861   This function makes the user to know that whether the active Shell is the root shell.\r
2862 \r
2863   @retval TRUE                  The active Shell is the root Shell.\r
2864   @retval FALSE                 The active Shell is NOT the root Shell.\r
2865 **/\r
2866 BOOLEAN\r
2867 EFIAPI\r
2868 EfiShellIsRootShell(\r
2869   VOID\r
2870   )\r
2871 {\r
2872   return(ShellInfoObject.RootShellInstance);\r
2873 }\r
2874 \r
2875 /**\r
2876   function to return a semi-colon delimeted list of all alias' in the current shell\r
2877 \r
2878   up to caller to free the memory.\r
2879 \r
2880   @retval NULL    No alias' were found\r
2881   @retval NULL    An error ocurred getting alias'\r
2882   @return !NULL   a list of all alias'\r
2883 **/\r
2884 CHAR16 *\r
2885 EFIAPI\r
2886 InternalEfiShellGetListAlias(\r
2887   )\r
2888 {\r
2889   UINT64            MaxStorSize;\r
2890   UINT64            RemStorSize;\r
2891   UINT64            MaxVarSize;\r
2892   EFI_STATUS        Status;\r
2893   EFI_GUID          Guid;\r
2894   CHAR16            *VariableName;\r
2895   UINTN             NameSize;\r
2896   CHAR16            *RetVal;\r
2897   UINTN             RetSize;\r
2898   CHAR16            *Alias;\r
2899 \r
2900   Status = gRT->QueryVariableInfo(EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, &MaxStorSize, &RemStorSize, &MaxVarSize);\r
2901   ASSERT_EFI_ERROR(Status);\r
2902 \r
2903   VariableName  = AllocateZeroPool((UINTN)MaxVarSize);\r
2904   RetSize       = 0;\r
2905   RetVal        = NULL;\r
2906 \r
2907   if (VariableName == NULL) {\r
2908     return (NULL);\r
2909   }\r
2910 \r
2911   VariableName[0] = CHAR_NULL;\r
2912 \r
2913   while (TRUE) {\r
2914     NameSize = (UINTN)MaxVarSize;\r
2915     Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);\r
2916     if (Status == EFI_NOT_FOUND){\r
2917       break;\r
2918     }\r
2919     ASSERT_EFI_ERROR(Status);\r
2920     if (EFI_ERROR(Status)) {\r
2921       break;\r
2922     }\r
2923     if (CompareGuid(&Guid, &gShellAliasGuid)){\r
2924       Alias = GetVariable(VariableName, &gShellAliasGuid);\r
2925       ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));\r
2926       RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);\r
2927       RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);\r
2928     } // compare guid\r
2929   } // while\r
2930   FreePool(VariableName);\r
2931 \r
2932   return (RetVal);\r
2933 }\r
2934 \r
2935 /**\r
2936   This function returns the command associated with a alias or a list of all\r
2937   alias'.\r
2938 \r
2939   @param[in] Alias              Points to the NULL-terminated shell alias.\r
2940                                 If this parameter is NULL, then all\r
2941                                 aliases will be returned in ReturnedData.\r
2942   @param[out] Volatile          upon return of a single command if TRUE indicates\r
2943                                 this is stored in a volatile fashion.  FALSE otherwise.\r
2944 \r
2945   @return                       If Alias is not NULL, it will return a pointer to\r
2946                                 the NULL-terminated command for that alias.\r
2947                                 If Alias is NULL, ReturnedData points to a ';'\r
2948                                 delimited list of alias (e.g.\r
2949                                 ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.\r
2950   @retval NULL                  an error ocurred\r
2951   @retval NULL                  Alias was not a valid Alias\r
2952 **/\r
2953 CONST CHAR16 *\r
2954 EFIAPI\r
2955 EfiShellGetAlias(\r
2956   IN  CONST CHAR16 *Alias,\r
2957   OUT BOOLEAN      *Volatile OPTIONAL\r
2958   )\r
2959 {\r
2960   CHAR16      *RetVal;\r
2961   UINTN       RetSize;\r
2962   UINT32      Attribs;\r
2963   EFI_STATUS  Status;\r
2964 \r
2965   if (Alias != NULL) {\r
2966     if (Volatile == NULL) {\r
2967       return (AddBufferToFreeList(GetVariable((CHAR16*)Alias, &gShellAliasGuid)));\r
2968     }\r
2969     RetSize = 0;\r
2970     RetVal = NULL;\r
2971     Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal);\r
2972     if (Status == EFI_BUFFER_TOO_SMALL) {\r
2973       RetVal = AllocateZeroPool(RetSize);\r
2974       Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal);\r
2975     }\r
2976     if (EFI_ERROR(Status)) {\r
2977       if (RetVal != NULL) {\r
2978         FreePool(RetVal);\r
2979       }\r
2980       return (NULL);\r
2981     }\r
2982     if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {\r
2983       *Volatile = FALSE;\r
2984     } else {\r
2985       *Volatile = TRUE;\r
2986     }\r
2987 \r
2988     return (AddBufferToFreeList(RetVal));\r
2989   }\r
2990   return (AddBufferToFreeList(InternalEfiShellGetListAlias()));\r
2991 }\r
2992 \r
2993 /**\r
2994   Changes a shell command alias.\r
2995 \r
2996   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.\r
2997 \r
2998   this function does not check for built in alias'.\r
2999 \r
3000   @param[in] Command            Points to the NULL-terminated shell command or existing alias.\r
3001   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and\r
3002                                 Command refers to an alias, that alias will be deleted.\r
3003   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the\r
3004                                 Alias being set will be stored in a non-volatile fashion.\r
3005 \r
3006   @retval EFI_SUCCESS           Alias created or deleted successfully.\r
3007   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found\r
3008 **/\r
3009 EFI_STATUS\r
3010 EFIAPI\r
3011 InternalSetAlias(\r
3012   IN CONST CHAR16 *Command,\r
3013   IN CONST CHAR16 *Alias,\r
3014   IN BOOLEAN Volatile\r
3015   )\r
3016 {\r
3017   //\r
3018   // We must be trying to remove one if Alias is NULL\r
3019   //\r
3020   if (Alias == NULL) {\r
3021     //\r
3022     // remove an alias (but passed in COMMAND parameter)\r
3023     //\r
3024     return (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL));\r
3025   } else {\r
3026     //\r
3027     // Add and replace are the same\r
3028     //\r
3029 \r
3030     // We dont check the error return on purpose since the variable may not exist.\r
3031     gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL);\r
3032 \r
3033     return (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command));\r
3034   }\r
3035 }\r
3036 \r
3037 /**\r
3038   Changes a shell command alias.\r
3039 \r
3040   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.\r
3041 \r
3042 \r
3043   @param[in] Command            Points to the NULL-terminated shell command or existing alias.\r
3044   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and\r
3045                                 Command refers to an alias, that alias will be deleted.\r
3046   @param[in] Replace            If TRUE and the alias already exists, then the existing alias will be replaced. If\r
3047                                 FALSE and the alias already exists, then the existing alias is unchanged and\r
3048                                 EFI_ACCESS_DENIED is returned.\r
3049   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the\r
3050                                 Alias being set will be stored in a non-volatile fashion.\r
3051 \r
3052   @retval EFI_SUCCESS           Alias created or deleted successfully.\r
3053   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found\r
3054   @retval EFI_ACCESS_DENIED     The alias is a built-in alias or already existed and Replace was set to\r
3055                                 FALSE.\r
3056 **/\r
3057 EFI_STATUS\r
3058 EFIAPI\r
3059 EfiShellSetAlias(\r
3060   IN CONST CHAR16 *Command,\r
3061   IN CONST CHAR16 *Alias,\r
3062   IN BOOLEAN Replace,\r
3063   IN BOOLEAN Volatile\r
3064   )\r
3065 {\r
3066   //\r
3067   // cant set over a built in alias\r
3068   //\r
3069   if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {\r
3070     return (EFI_ACCESS_DENIED);\r
3071   }\r
3072   if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {\r
3073     return (EFI_INVALID_PARAMETER);\r
3074   }\r
3075 \r
3076   if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {\r
3077     return (EFI_ACCESS_DENIED);\r
3078   }\r
3079 \r
3080   return (InternalSetAlias(Command, Alias, Volatile));\r
3081 }\r
3082 \r
3083 // Pure FILE_HANDLE operations are passed to FileHandleLib\r
3084 // these functions are indicated by the *\r
3085 EFI_SHELL_PROTOCOL         mShellProtocol = {\r
3086   EfiShellExecute,\r
3087   EfiShellGetEnv,\r
3088   EfiShellSetEnv,\r
3089   EfiShellGetAlias,\r
3090   EfiShellSetAlias,\r
3091   EfiShellGetHelpText,\r
3092   EfiShellGetDevicePathFromMap,\r
3093   EfiShellGetMapFromDevicePath,\r
3094   EfiShellGetDevicePathFromFilePath,\r
3095   EfiShellGetFilePathFromDevicePath,\r
3096   EfiShellSetMap,\r
3097   EfiShellGetCurDir,\r
3098   EfiShellSetCurDir,\r
3099   EfiShellOpenFileList,\r
3100   EfiShellFreeFileList,\r
3101   EfiShellRemoveDupInFileList,\r
3102   EfiShellBatchIsActive,\r
3103   EfiShellIsRootShell,\r
3104   EfiShellEnablePageBreak,\r
3105   EfiShellDisablePageBreak,\r
3106   EfiShellGetPageBreak,\r
3107   EfiShellGetDeviceName,\r