ShellPkg: Add checking for memory allocation and pointer returns from functions.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file\r
2   This is THE shell (application)\r
3 \r
4   Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
5   This program and the accompanying materials\r
6   are licensed and made available under the terms and conditions of the BSD License\r
7   which accompanies this distribution.  The full text of the license may be found at\r
8   http://opensource.org/licenses/bsd-license.php\r
9 \r
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "Shell.h"\r
16 \r
17 //\r
18 // Initialize the global structure\r
19 //\r
20 SHELL_INFO ShellInfoObject = {\r
21   NULL,\r
22   NULL,\r
23   FALSE,\r
24   FALSE,\r
25   {\r
26     0,\r
27     0,\r
28     0,\r
29     0,\r
30     0,\r
31     0,\r
32     0,\r
33     0,\r
34     0,\r
35     0,\r
36     NULL,\r
37     NULL\r
38   },\r
39   {0,0},\r
40   {\r
41     {0,0},\r
42     0,\r
43     0,\r
44     TRUE\r
45   },\r
46   NULL,\r
47   0,\r
48   NULL,\r
49   NULL,\r
50   NULL,\r
51   NULL,\r
52   NULL,\r
53   {0,0,NULL,NULL},\r
54   {0,0},\r
55   NULL,\r
56   NULL,\r
57   NULL,\r
58   NULL,\r
59   FALSE\r
60 };\r
61 \r
62 STATIC CONST CHAR16 mScriptExtension[]      = L".NSH";\r
63 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
64 STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";\r
65 \r
66 /**\r
67   Function to start monitoring for CTRL-S using SimpleTextInputEx.  This \r
68   feature's enabled state was not known when the shell initially launched.\r
69 \r
70   @retval EFI_SUCCESS           The feature is enabled.\r
71   @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.\r
72 **/\r
73 EFI_STATUS\r
74 EFIAPI\r
75 InternalEfiShellStartCtrlSMonitor(\r
76   VOID\r
77   )\r
78 {\r
79   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;\r
80   EFI_KEY_DATA                      KeyData;\r
81   EFI_STATUS                        Status;\r
82 \r
83   Status = gBS->OpenProtocol(\r
84     gST->ConsoleInHandle,\r
85     &gEfiSimpleTextInputExProtocolGuid,\r
86     (VOID**)&SimpleEx,\r
87     gImageHandle,\r
88     NULL,\r
89     EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
90   if (EFI_ERROR(Status)) {\r
91     ShellPrintHiiEx(\r
92       -1, \r
93       -1, \r
94       NULL,\r
95       STRING_TOKEN (STR_SHELL_NO_IN_EX),\r
96       ShellInfoObject.HiiHandle);\r
97     return (EFI_SUCCESS);\r
98   }\r
99 \r
100   KeyData.KeyState.KeyToggleState = 0;\r
101   KeyData.Key.ScanCode            = 0;\r
102   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
103   KeyData.Key.UnicodeChar         = L's';\r
104 \r
105   Status = SimpleEx->RegisterKeyNotify(\r
106     SimpleEx,\r
107     &KeyData,\r
108     NotificationFunction,\r
109     &ShellInfoObject.CtrlSNotifyHandle1);\r
110   \r
111   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
112   if (!EFI_ERROR(Status)) {\r
113     Status = SimpleEx->RegisterKeyNotify(\r
114       SimpleEx,\r
115       &KeyData,\r
116       NotificationFunction,\r
117       &ShellInfoObject.CtrlSNotifyHandle2);\r
118   }\r
119   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
120   KeyData.Key.UnicodeChar         = 19;\r
121 \r
122   if (!EFI_ERROR(Status)) {\r
123     Status = SimpleEx->RegisterKeyNotify(\r
124       SimpleEx,\r
125       &KeyData,\r
126       NotificationFunction,\r
127       &ShellInfoObject.CtrlSNotifyHandle2);\r
128   }  \r
129   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
130   if (!EFI_ERROR(Status)) {\r
131     Status = SimpleEx->RegisterKeyNotify(\r
132       SimpleEx,\r
133       &KeyData,\r
134       NotificationFunction,\r
135       &ShellInfoObject.CtrlSNotifyHandle2);\r
136   }\r
137   return (Status);\r
138 }\r
139 \r
140 \r
141 \r
142 /**\r
143   The entry point for the application.\r
144 \r
145   @param[in] ImageHandle    The firmware allocated handle for the EFI image.\r
146   @param[in] SystemTable    A pointer to the EFI System Table.\r
147 \r
148   @retval EFI_SUCCESS       The entry point is executed successfully.\r
149   @retval other             Some error occurs when executing this entry point.\r
150 \r
151 **/\r
152 EFI_STATUS\r
153 EFIAPI\r
154 UefiMain (\r
155   IN EFI_HANDLE        ImageHandle,\r
156   IN EFI_SYSTEM_TABLE  *SystemTable\r
157   )\r
158 {\r
159   EFI_STATUS                      Status;\r
160   CHAR16                          *TempString;\r
161   UINTN                           Size;\r
162   EFI_HANDLE                      ConInHandle;\r
163   EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;\r
164 \r
165   if (PcdGet8(PcdShellSupportLevel) > 3) {\r
166     return (EFI_UNSUPPORTED);\r
167   }\r
168 \r
169   //\r
170   // Clear the screen\r
171   //\r
172   Status = gST->ConOut->ClearScreen(gST->ConOut);\r
173   if (EFI_ERROR(Status)) {\r
174     return (Status);\r
175   }\r
176 \r
177   //\r
178   // Populate the global structure from PCDs\r
179   //\r
180   ShellInfoObject.ImageDevPath                = NULL;\r
181   ShellInfoObject.FileDevPath                 = NULL;\r
182   ShellInfoObject.PageBreakEnabled            = PcdGetBool(PcdShellPageBreakDefault);\r
183   ShellInfoObject.ViewingSettings.InsertMode  = PcdGetBool(PcdShellInsertModeDefault);\r
184   ShellInfoObject.LogScreenCount              = PcdGet8   (PcdShellScreenLogCount  );\r
185 \r
186   //\r
187   // verify we dont allow for spec violation\r
188   //\r
189   ASSERT(ShellInfoObject.LogScreenCount >= 3);\r
190 \r
191   //\r
192   // Initialize the LIST ENTRY objects...\r
193   //\r
194   InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);\r
195   InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
196   InitializeListHead(&ShellInfoObject.SplitList.Link);\r
197 \r
198   //\r
199   // Check PCDs for optional features that are not implemented yet.\r
200   //\r
201   if (   PcdGetBool(PcdShellSupportOldProtocols)\r
202       || !FeaturePcdGet(PcdShellRequireHiiPlatform)\r
203       || FeaturePcdGet(PcdShellSupportFrameworkHii)\r
204    ) {\r
205     return (EFI_UNSUPPORTED);\r
206   }\r
207 \r
208   //\r
209   // turn off the watchdog timer\r
210   //\r
211   gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
212 \r
213   //\r
214   // install our console logger.  This will keep a log of the output for back-browsing\r
215   //\r
216   Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);\r
217   if (!EFI_ERROR(Status)) {\r
218     //\r
219     // Enable the cursor to be visible\r
220     //\r
221     gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
222 \r
223     //\r
224     // If supporting EFI 1.1 we need to install HII protocol\r
225     // only do this if PcdShellRequireHiiPlatform == FALSE\r
226     //\r
227     // remove EFI_UNSUPPORTED check above when complete.\r
228     ///@todo add support for Framework HII\r
229 \r
230     //\r
231     // install our (solitary) HII package\r
232     //\r
233     ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);\r
234     if (ShellInfoObject.HiiHandle == NULL) {\r
235       if (PcdGetBool(PcdShellSupportFrameworkHii)) {\r
236         ///@todo Add our package into Framework HII\r
237       }\r
238       if (ShellInfoObject.HiiHandle == NULL) {\r
239         return (EFI_NOT_STARTED);\r
240       }\r
241     }\r
242 \r
243     //\r
244     // create and install the EfiShellParametersProtocol\r
245     //\r
246     Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);\r
247     ASSERT_EFI_ERROR(Status);\r
248     ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);\r
249 \r
250     //\r
251     // create and install the EfiShellProtocol\r
252     //\r
253     Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);\r
254     ASSERT_EFI_ERROR(Status);\r
255     ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);\r
256 \r
257     //\r
258     // Now initialize the shell library (it requires Shell Parameters protocol)\r
259     //\r
260     Status = ShellInitialize();\r
261     ASSERT_EFI_ERROR(Status);\r
262 \r
263     Status = CommandInit();\r
264     ASSERT_EFI_ERROR(Status);\r
265 \r
266     //\r
267     // Check the command line\r
268     //\r
269     Status = ProcessCommandLine();\r
270 \r
271     //\r
272     // If shell support level is >= 1 create the mappings and paths\r
273     //\r
274     if (PcdGet8(PcdShellSupportLevel) >= 1) {\r
275       Status = ShellCommandCreateInitialMappingsAndPaths();\r
276     }\r
277 \r
278     //\r
279     // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
280     // These are where to look for the startup.nsh file\r
281     //\r
282     Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);\r
283     ASSERT_EFI_ERROR(Status);\r
284 \r
285     //\r
286     // Display the version\r
287     //\r
288     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {\r
289       ShellPrintHiiEx (\r
290         0,\r
291         gST->ConOut->Mode->CursorRow,\r
292         NULL,\r
293         STRING_TOKEN (STR_VER_OUTPUT_MAIN),\r
294         ShellInfoObject.HiiHandle,\r
295         SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
296         gEfiShellProtocol->MajorVersion,\r
297         gEfiShellProtocol->MinorVersion,\r
298         (gST->Hdr.Revision&0xffff0000)>>16,\r
299         (gST->Hdr.Revision&0x0000ffff),\r
300         gST->FirmwareVendor,\r
301         gST->FirmwareRevision\r
302        );\r
303     }\r
304 \r
305     //\r
306     // Display the mapping\r
307     //\r
308     if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
309       Status = RunCommand(L"map");\r
310       ASSERT_EFI_ERROR(Status);\r
311     }\r
312 \r
313     //\r
314     // init all the built in alias'\r
315     //\r
316     Status = SetBuiltInAlias();\r
317     ASSERT_EFI_ERROR(Status);\r
318 \r
319     //\r
320     // Initialize environment variables\r
321     //\r
322     if (ShellCommandGetProfileList() != NULL) {\r
323       Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);\r
324       ASSERT_EFI_ERROR(Status);\r
325     }\r
326 \r
327     Size        = 100;\r
328     TempString  = AllocateZeroPool(Size);\r
329 \r
330     UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));\r
331     Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);\r
332     ASSERT_EFI_ERROR(Status);\r
333 \r
334     UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);\r
335     Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);\r
336     ASSERT_EFI_ERROR(Status);\r
337 \r
338     UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);\r
339     Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);\r
340     ASSERT_EFI_ERROR(Status);\r
341 \r
342     FreePool(TempString);\r
343 \r
344     if (!EFI_ERROR(Status)) {\r
345       if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
346         //\r
347         // Set up the event for CTRL-C monitoring...\r
348         //\r
349         Status = InernalEfiShellStartMonitor();\r
350       }\r
351 \r
352       if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
353         //\r
354         // Set up the event for CTRL-S monitoring...\r
355         //\r
356         Status = InternalEfiShellStartCtrlSMonitor();\r
357       }\r
358 \r
359       if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
360         //\r
361         // close off the gST->ConIn\r
362         //\r
363         OldConIn      = gST->ConIn;\r
364         ConInHandle   = gST->ConsoleInHandle;\r
365         gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);\r
366       } else {\r
367         OldConIn      = NULL;\r
368         ConInHandle   = NULL;\r
369       }\r
370 \r
371       if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {\r
372         //\r
373         // process the startup script or launch the called app.\r
374         //\r
375         Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
376       }\r
377 \r
378       if (!ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
379         //\r
380         // begin the UI waiting loop\r
381         //\r
382         do {\r
383           //\r
384           // clean out all the memory allocated for CONST <something> * return values\r
385           // between each shell prompt presentation\r
386           //\r
387           if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
388             FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
389           }\r
390 \r
391           //\r
392           // Reset page break back to default.\r
393           //\r
394           ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);\r
395           ShellInfoObject.ConsoleInfo->Enabled    = TRUE;\r
396           ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
397 \r
398           //\r
399           // Reset the CTRL-C event (yes we ignore the return values)\r
400           //\r
401           Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
402 \r
403           //\r
404           // Display Prompt\r
405           //\r
406           Status = DoShellPrompt();\r
407         } while (!ShellCommandGetExit());\r
408       }\r
409       if (OldConIn != NULL && ConInHandle != NULL) {\r
410         CloseSimpleTextInOnFile (gST->ConIn);\r
411         gST->ConIn            = OldConIn;\r
412         gST->ConsoleInHandle  = ConInHandle;\r
413       }\r
414     }\r
415   }\r
416 \r
417   //\r
418   // uninstall protocols / free memory / etc...\r
419   //\r
420   if (ShellInfoObject.UserBreakTimer != NULL) {\r
421     gBS->CloseEvent(ShellInfoObject.UserBreakTimer);\r
422     DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);\r
423   }\r
424   if (ShellInfoObject.ImageDevPath != NULL) {\r
425     FreePool(ShellInfoObject.ImageDevPath);\r
426     DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);\r
427   }\r
428   if (ShellInfoObject.FileDevPath != NULL) {\r
429     FreePool(ShellInfoObject.FileDevPath);\r
430     DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);\r
431   }\r
432   if (ShellInfoObject.NewShellParametersProtocol != NULL) {\r
433     CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);\r
434     DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);\r
435   }\r
436   if (ShellInfoObject.NewEfiShellProtocol != NULL){\r
437     if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
438       ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);\r
439     }\r
440     CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);\r
441     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
442   }\r
443 \r
444   if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
445     FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
446   }\r
447 \r
448   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){\r
449     ASSERT(FALSE); ///@todo finish this de-allocation.\r
450   }\r
451 \r
452   if (ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
453     FreePool(ShellInfoObject.ShellInitSettings.FileName);\r
454     DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);\r
455   }\r
456 \r
457   if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
458     FreePool(ShellInfoObject.ShellInitSettings.FileOptions);\r
459     DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);\r
460   }\r
461 \r
462   if (ShellInfoObject.HiiHandle != NULL) {\r
463     HiiRemovePackages(ShellInfoObject.HiiHandle);\r
464     DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);\r
465   }\r
466 \r
467   if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){\r
468     FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);\r
469   }\r
470 \r
471   ASSERT(ShellInfoObject.ConsoleInfo != NULL);\r
472   if (ShellInfoObject.ConsoleInfo != NULL) {\r
473     ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);\r
474     FreePool(ShellInfoObject.ConsoleInfo);\r
475     DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
476   }\r
477 \r
478   if (ShellCommandGetExit()) {\r
479     return ((EFI_STATUS)ShellCommandGetExitCode());\r
480   }\r
481   return (Status);\r
482 }\r
483 \r
484 /**\r
485   Sets all the alias' that were registered with the ShellCommandLib library.\r
486 \r
487   @retval EFI_SUCCESS           all init commands were run sucessfully.\r
488 **/\r
489 EFI_STATUS\r
490 EFIAPI\r
491 SetBuiltInAlias(\r
492   )\r
493 {\r
494   EFI_STATUS          Status;\r
495   CONST ALIAS_LIST    *List;\r
496   ALIAS_LIST          *Node;\r
497 \r
498   //\r
499   // Get all the commands we want to run\r
500   //\r
501   List = ShellCommandGetInitAliasList();\r
502 \r
503   //\r
504   // for each command in the List\r
505   //\r
506   for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)\r
507       ; !IsNull (&List->Link, &Node->Link)\r
508       ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)\r
509    ){\r
510     //\r
511     // install the alias'\r
512     //\r
513     Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);\r
514     ASSERT_EFI_ERROR(Status);\r
515   }\r
516   return (EFI_SUCCESS);\r
517 }\r
518 \r
519 /**\r
520   Internal function to determine if 2 command names are really the same.\r
521 \r
522   @param[in] Command1       The pointer to the first command name.\r
523   @param[in] Command2       The pointer to the second command name.\r
524 \r
525   @retval TRUE              The 2 command names are the same.\r
526   @retval FALSE             The 2 command names are not the same.\r
527 **/\r
528 BOOLEAN\r
529 EFIAPI\r
530 IsCommand(\r
531   IN CONST CHAR16 *Command1,\r
532   IN CONST CHAR16 *Command2\r
533   )\r
534 {\r
535   if (StringNoCaseCompare(&Command1, &Command2) == 0) {\r
536     return (TRUE);\r
537   }\r
538   return (FALSE);\r
539 }\r
540 \r
541 /**\r
542   Internal function to determine if a command is a script only command.\r
543 \r
544   @param[in] CommandName    The pointer to the command name.\r
545 \r
546   @retval TRUE              The command is a script only command.\r
547   @retval FALSE             The command is not a script only command.\r
548 **/\r
549 BOOLEAN\r
550 EFIAPI\r
551 IsScriptOnlyCommand(\r
552   IN CONST CHAR16 *CommandName\r
553   )\r
554 {\r
555   if (IsCommand(CommandName, L"for")\r
556     ||IsCommand(CommandName, L"endfor")\r
557     ||IsCommand(CommandName, L"if")\r
558     ||IsCommand(CommandName, L"else")\r
559     ||IsCommand(CommandName, L"endif")\r
560     ||IsCommand(CommandName, L"goto")) {\r
561     return (TRUE);\r
562   }\r
563   return (FALSE);\r
564 }\r
565 \r
566 /**\r
567   This function will populate the 2 device path protocol parameters based on the\r
568   global gImageHandle.  The DevPath will point to the device path for the handle that has\r
569   loaded image protocol installed on it.  The FilePath will point to the device path\r
570   for the file that was loaded.\r
571 \r
572   @param[in, out] DevPath       On a sucessful return the device path to the loaded image.\r
573   @param[in, out] FilePath      On a sucessful return the device path to the file.\r
574 \r
575   @retval EFI_SUCCESS           The 2 device paths were sucessfully returned.\r
576   @retval other                 A error from gBS->HandleProtocol.\r
577 \r
578   @sa HandleProtocol\r
579 **/\r
580 EFI_STATUS\r
581 EFIAPI\r
582 GetDevicePathsForImageAndFile (\r
583   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,\r
584   IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath\r
585   )\r
586 {\r
587   EFI_STATUS                Status;\r
588   EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
589   EFI_DEVICE_PATH_PROTOCOL  *ImageDevicePath;\r
590 \r
591   ASSERT(DevPath  != NULL);\r
592   ASSERT(FilePath != NULL);\r
593 \r
594   Status = gBS->OpenProtocol (\r
595                 gImageHandle,\r
596                 &gEfiLoadedImageProtocolGuid,\r
597                 (VOID**)&LoadedImage,\r
598                 gImageHandle,\r
599                 NULL,\r
600                 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
601                );\r
602   if (!EFI_ERROR (Status)) {\r
603     Status = gBS->OpenProtocol (\r
604                   LoadedImage->DeviceHandle,\r
605                   &gEfiDevicePathProtocolGuid,\r
606                   (VOID**)&ImageDevicePath,\r
607                   gImageHandle,\r
608                   NULL,\r
609                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
610                  );\r
611     if (!EFI_ERROR (Status)) {\r
612       *DevPath  = DuplicateDevicePath (ImageDevicePath);\r
613       *FilePath = DuplicateDevicePath (LoadedImage->FilePath);\r
614       gBS->CloseProtocol(\r
615                   LoadedImage->DeviceHandle,\r
616                   &gEfiDevicePathProtocolGuid,\r
617                   gImageHandle,\r
618                   NULL);\r
619     }\r
620     gBS->CloseProtocol(\r
621                 gImageHandle,\r
622                 &gEfiLoadedImageProtocolGuid,\r
623                 gImageHandle,\r
624                 NULL);\r
625   }\r
626   return (Status);\r
627 }\r
628 \r
629 STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {\r
630   {L"-nostartup",     TypeFlag},\r
631   {L"-startup",       TypeFlag},\r
632   {L"-noconsoleout",  TypeFlag},\r
633   {L"-noconsolein",   TypeFlag},\r
634   {L"-nointerrupt",   TypeFlag},\r
635   {L"-nomap",         TypeFlag},\r
636   {L"-noversion",     TypeFlag},\r
637   {L"-startup",       TypeFlag},\r
638   {L"-delay",         TypeValue},\r
639   {NULL, TypeMax}\r
640   };\r
641 \r
642 /**\r
643   Process all Uefi Shell 2.0 command line options.\r
644 \r
645   see Uefi Shell 2.0 section 3.2 for full details.\r
646 \r
647   the command line must resemble the following:\r
648 \r
649   shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]\r
650 \r
651   ShellOpt-options  Options which control the initialization behavior of the shell.\r
652                     These options are read from the EFI global variable "ShellOpt"\r
653                     and are processed before options or file-name.\r
654 \r
655   options           Options which control the initialization behavior of the shell.\r
656 \r
657   file-name         The name of a UEFI shell application or script to be executed\r
658                     after initialization is complete. By default, if file-name is\r
659                     specified, then -nostartup is implied. Scripts are not supported\r
660                     by level 0.\r
661 \r
662   file-name-options The command-line options that are passed to file-name when it\r
663                     is invoked.\r
664 \r
665   This will initialize the ShellInfoObject.ShellInitSettings global variable.\r
666 \r
667   @retval EFI_SUCCESS           The variable is initialized.\r
668 **/\r
669 EFI_STATUS\r
670 EFIAPI\r
671 ProcessCommandLine(\r
672   VOID\r
673   )\r
674 {\r
675   EFI_STATUS    Status;\r
676   LIST_ENTRY    *Package;\r
677   UINTN         Size;\r
678   CONST CHAR16  *TempConst;\r
679   UINTN         Count;\r
680   UINTN         LoopVar;\r
681   CHAR16        *ProblemParam;\r
682   UINT64        Intermediate;\r
683 \r
684   Package       = NULL;\r
685   ProblemParam  = NULL;\r
686   \r
687   Status = ShellCommandLineParse (mShellParamList, &Package, NULL, FALSE);\r
688 \r
689   Count = 1;\r
690   Size = 0;\r
691   TempConst = ShellCommandLineGetRawValue(Package, Count++);\r
692   if (TempConst != NULL && StrLen(TempConst)) {\r
693     ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(TempConst));\r
694     if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
695       return (EFI_OUT_OF_RESOURCES);\r
696     }\r
697     StrCpy(ShellInfoObject.ShellInitSettings.FileName, TempConst);\r
698     ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
699     for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
700       if (StrCmp(gEfiShellParametersProtocol->Argv[LoopVar], ShellInfoObject.ShellInitSettings.FileName)==0) {\r
701         LoopVar++;\r
702         //\r
703         // We found the file... add the rest of the params...\r
704         //\r
705         for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
706           ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
707           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
708                       &Size,\r
709                       L" ",\r
710                       0);\r
711           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
712             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
713             return (EFI_OUT_OF_RESOURCES);\r
714           }\r
715           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
716                       &Size,\r
717                       gEfiShellParametersProtocol->Argv[LoopVar],\r
718                       0);\r
719           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
720             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
721             return (EFI_OUT_OF_RESOURCES);\r
722           }\r
723         }\r
724       }\r
725     }\r
726   } else {\r
727     ShellCommandLineFreeVarList(Package);\r
728     Package = NULL;\r
729     Status = ShellCommandLineParse (mShellParamList, &Package, &ProblemParam, FALSE);\r
730     if (EFI_ERROR(Status)) {\r
731       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), ShellInfoObject.HiiHandle, ProblemParam);\r
732       FreePool(ProblemParam);\r
733       ShellCommandLineFreeVarList(Package);\r
734       return (EFI_INVALID_PARAMETER);\r
735     }\r
736   }\r
737 \r
738   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = ShellCommandLineGetFlag(Package, L"-startup");\r
739   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = ShellCommandLineGetFlag(Package, L"-nostartup");\r
740   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = ShellCommandLineGetFlag(Package, L"-noconsoleout");\r
741   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = ShellCommandLineGetFlag(Package, L"-noconsolein");\r
742   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = ShellCommandLineGetFlag(Package, L"-nointerrupt");\r
743   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = ShellCommandLineGetFlag(Package, L"-nomap");\r
744   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = ShellCommandLineGetFlag(Package, L"-noversion");\r
745   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = ShellCommandLineGetFlag(Package, L"-delay");\r
746 \r
747   ShellInfoObject.ShellInitSettings.Delay = 5;\r
748 \r
749   if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
750     ShellInfoObject.ShellInitSettings.Delay = 0;\r
751   } else if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay) {\r
752     TempConst = ShellCommandLineGetValue(Package, L"-delay");\r
753     if (TempConst != NULL && *TempConst == L':') {\r
754       TempConst++;\r
755     }\r
756     if (TempConst != NULL && !EFI_ERROR(ShellConvertStringToUint64(TempConst, &Intermediate, FALSE, FALSE))) {\r
757       ShellInfoObject.ShellInitSettings.Delay = (UINTN)Intermediate;\r
758     }\r
759   }\r
760   ShellCommandLineFreeVarList(Package);\r
761 \r
762   return (Status);\r
763 }\r
764 \r
765 /**\r
766   Handles all interaction with the default startup script.\r
767 \r
768   this will check that the correct command line parameters were passed, handle the delay, and then start running the script.\r
769 \r
770   @param ImagePath              the path to the image for shell.  first place to look for the startup script\r
771   @param FilePath               the path to the file for shell.  second place to look for the startup script.\r
772 \r
773   @retval EFI_SUCCESS           the variable is initialized.\r
774 **/\r
775 EFI_STATUS\r
776 EFIAPI\r
777 DoStartupScript(\r
778   EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
779   EFI_DEVICE_PATH_PROTOCOL *FilePath\r
780   )\r
781 {\r
782   EFI_STATUS                    Status;\r
783   UINTN                         Delay;\r
784   EFI_INPUT_KEY                 Key;\r
785   SHELL_FILE_HANDLE             FileHandle;\r
786   EFI_DEVICE_PATH_PROTOCOL      *NewPath;\r
787   EFI_DEVICE_PATH_PROTOCOL      *NamePath;\r
788   CHAR16                        *FileStringPath;\r
789   CHAR16                        *TempSpot;\r
790   UINTN                         NewSize;\r
791   CONST CHAR16                  *MapName;\r
792 \r
793   Key.UnicodeChar = CHAR_NULL;\r
794   Key.ScanCode    = 0;\r
795   FileHandle      = NULL;\r
796 \r
797   if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
798     //\r
799     // launch something else instead\r
800     //\r
801     NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);\r
802     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
803       NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);\r
804     }\r
805     FileStringPath = AllocateZeroPool(NewSize);\r
806     if (FileStringPath == NULL) {\r
807       return (EFI_OUT_OF_RESOURCES);\r
808     }\r
809     StrCpy(FileStringPath, ShellInfoObject.ShellInitSettings.FileName);\r
810     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
811       StrCat(FileStringPath, L" ");\r
812       StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions);\r
813     }\r
814     Status = RunCommand(FileStringPath);\r
815     FreePool(FileStringPath);\r
816     return (Status);\r
817 \r
818   }\r
819 \r
820   //\r
821   // for shell level 0 we do no scripts\r
822   // Without the Startup bit overriding we allow for nostartup to prevent scripts\r
823   //\r
824   if ( (PcdGet8(PcdShellSupportLevel) < 1)\r
825     || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)\r
826    ){\r
827     return (EFI_SUCCESS);\r
828   }\r
829 \r
830   //\r
831   // print out our warning and see if they press a key\r
832   //\r
833   for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay * 10\r
834       ; Delay != 0 && EFI_ERROR(Status)\r
835       ; Delay--\r
836      ){\r
837     ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);\r
838     gBS->Stall (100000);\r
839     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
840       Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
841     }\r
842   }\r
843   ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);\r
844 \r
845   //\r
846   // ESC was pressed\r
847   //\r
848   if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {\r
849     return (EFI_SUCCESS);\r
850   }\r
851 \r
852   //\r
853   // Try the first location (must be file system)\r
854   //\r
855   MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);\r
856   if (MapName != NULL) {\r
857     FileStringPath = NULL;\r
858     NewSize = 0;\r
859     FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);\r
860     if (FileStringPath == NULL) {\r
861       Status = EFI_OUT_OF_RESOURCES;\r
862     } else {\r
863       TempSpot = StrStr(FileStringPath, L";");\r
864       if (TempSpot != NULL) {\r
865         *TempSpot = CHAR_NULL;\r
866       }\r
867       FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
868       PathRemoveLastItem(FileStringPath);\r
869       FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
870       Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
871       FreePool(FileStringPath);\r
872     }\r
873   }\r
874   if (EFI_ERROR(Status)) {\r
875     NamePath = FileDevicePath (NULL, mStartupScript);\r
876     NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
877     FreePool(NamePath);\r
878 \r
879     //\r
880     // Try the location\r
881     //\r
882     Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
883     FreePool(NewPath);\r
884   }\r
885   //\r
886   // If we got a file, run it\r
887   //\r
888   if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
889     Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
890     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
891   } else {\r
892     FileStringPath = ShellFindFilePath(mStartupScript);\r
893     if (FileStringPath == NULL) {\r
894       //\r
895       // we return success since we dont need to have a startup script\r
896       //\r
897       Status = EFI_SUCCESS;\r
898       ASSERT(FileHandle == NULL);\r
899     } else {\r
900       Status = RunScriptFile(FileStringPath);\r
901       FreePool(FileStringPath);\r
902     }\r
903   }\r
904 \r
905 \r
906   return (Status);\r
907 }\r
908 \r
909 /**\r
910   Function to perform the shell prompt looping.  It will do a single prompt,\r
911   dispatch the result, and then return.  It is expected that the caller will\r
912   call this function in a loop many times.\r
913 \r
914   @retval EFI_SUCCESS\r
915   @retval RETURN_ABORTED\r
916 **/\r
917 EFI_STATUS\r
918 EFIAPI\r
919 DoShellPrompt (\r
920   VOID\r
921   )\r
922 {\r
923   UINTN         Column;\r
924   UINTN         Row;\r
925   CHAR16        *CmdLine;\r
926   CONST CHAR16  *CurDir;\r
927   UINTN         BufferSize;\r
928   EFI_STATUS    Status;\r
929 \r
930   CurDir  = NULL;\r
931 \r
932   //\r
933   // Get screen setting to decide size of the command line buffer\r
934   //\r
935   gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);\r
936   BufferSize  = Column * Row * sizeof (CHAR16);\r
937   CmdLine     = AllocateZeroPool (BufferSize);\r
938   if (CmdLine == NULL) {\r
939     return EFI_OUT_OF_RESOURCES;\r
940   }\r
941 \r
942   CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
943 \r
944   //\r
945   // Prompt for input\r
946   //\r
947   gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);\r
948 \r
949   if (CurDir != NULL && StrLen(CurDir) > 1) {\r
950     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
951   } else {\r
952     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
953   }\r
954 \r
955   //\r
956   // Read a line from the console\r
957   //\r
958   Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);\r
959 \r
960   //\r
961   // Null terminate the string and parse it\r
962   //\r
963   if (!EFI_ERROR (Status)) {\r
964     CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
965     Status = RunCommand(CmdLine);\r
966   }\r
967 \r
968   //\r
969   // Done with this command\r
970   //\r
971   FreePool (CmdLine);\r
972   return Status;\r
973 }\r
974 \r
975 /**\r
976   Add a buffer to the Buffer To Free List for safely returning buffers to other\r
977   places without risking letting them modify internal shell information.\r
978 \r
979   @param Buffer   Something to pass to FreePool when the shell is exiting.\r
980 **/\r
981 VOID*\r
982 EFIAPI\r
983 AddBufferToFreeList(\r
984   VOID *Buffer\r
985   )\r
986 {\r
987   BUFFER_LIST   *BufferListEntry;\r
988 \r
989   if (Buffer == NULL) {\r
990     return (NULL);\r
991   }\r
992 \r
993   BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));\r
994   ASSERT(BufferListEntry != NULL);\r
995   BufferListEntry->Buffer = Buffer;\r
996   InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
997   return (Buffer);\r
998 }\r
999 \r
1000 /**\r
1001   Add a buffer to the Line History List\r
1002 \r
1003   @param Buffer     The line buffer to add.\r
1004 **/\r
1005 VOID\r
1006 EFIAPI\r
1007 AddLineToCommandHistory(\r
1008   IN CONST CHAR16 *Buffer\r
1009   )\r
1010 {\r
1011   BUFFER_LIST *Node;\r
1012 \r
1013   Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1014   ASSERT(Node != NULL);\r
1015   Node->Buffer = AllocateZeroPool(StrSize(Buffer));\r
1016   ASSERT(Node->Buffer != NULL);\r
1017   StrCpy(Node->Buffer, Buffer);\r
1018 \r
1019   InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1020 }\r
1021 \r
1022 /**\r
1023   Checks if a string is an alias for another command.  If yes, then it replaces the alias name\r
1024   with the correct command name.\r
1025 \r
1026   @param[in, out] CommandString    Upon entry the potential alias.  Upon return the\r
1027                                    command name if it was an alias.  If it was not\r
1028                                    an alias it will be unchanged.  This function may\r
1029                                    change the buffer to fit the command name.\r
1030 \r
1031   @retval EFI_SUCCESS             The name was changed.\r
1032   @retval EFI_SUCCESS             The name was not an alias.\r
1033   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.\r
1034 **/\r
1035 EFI_STATUS\r
1036 EFIAPI\r
1037 ShellConvertAlias(\r
1038   IN OUT CHAR16 **CommandString\r
1039   )\r
1040 {\r
1041   CONST CHAR16  *NewString;\r
1042 \r
1043   NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);\r
1044   if (NewString == NULL) {\r
1045     return (EFI_SUCCESS);\r
1046   }\r
1047   FreePool(*CommandString);\r
1048   *CommandString = AllocateZeroPool(StrSize(NewString));\r
1049   if (*CommandString == NULL) {\r
1050     return (EFI_OUT_OF_RESOURCES);\r
1051   }\r
1052   StrCpy(*CommandString, NewString);\r
1053   return (EFI_SUCCESS);\r
1054 }\r
1055 \r
1056 /**\r
1057   Function allocates a new command line and replaces all instances of environment\r
1058   variable names that are correctly preset to their values.\r
1059 \r
1060   If the return value is not NULL the memory must be caller freed.\r
1061 \r
1062   @param[in] OriginalCommandLine    The original command line\r
1063 \r
1064   @retval NULL                      An error ocurred.\r
1065   @return                           The new command line with no environment variables present.\r
1066 **/\r
1067 CHAR16*\r
1068 EFIAPI\r
1069 ShellConvertVariables (\r
1070   IN CONST CHAR16 *OriginalCommandLine\r
1071   )\r
1072 {\r
1073   CONST CHAR16        *MasterEnvList;\r
1074   UINTN               NewSize;\r
1075   CHAR16              *NewCommandLine1;\r
1076   CHAR16              *NewCommandLine2;\r
1077   CHAR16              *Temp;\r
1078   UINTN               ItemSize;\r
1079   CHAR16              *ItemTemp;\r
1080   SCRIPT_FILE         *CurrentScriptFile;\r
1081   ALIAS_LIST          *AliasListNode;\r
1082 \r
1083   ASSERT(OriginalCommandLine != NULL);\r
1084 \r
1085   ItemSize          = 0;\r
1086   NewSize           = StrSize(OriginalCommandLine);\r
1087   CurrentScriptFile = ShellCommandGetCurrentScriptFile();\r
1088   Temp              = NULL;\r
1089 \r
1090   ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?\r
1091 \r
1092   //\r
1093   // calculate the size required for the post-conversion string...\r
1094   //\r
1095   if (CurrentScriptFile != NULL) {\r
1096     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1097       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1098       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1099    ){\r
1100       for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)\r
1101         ;  Temp != NULL\r
1102         ;  Temp = StrStr(Temp+1, AliasListNode->Alias)\r
1103        ){\r
1104         //\r
1105         // we need a preceeding and if there is space no ^ preceeding (if no space ignore)\r
1106         //\r
1107         if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {\r
1108           NewSize += StrSize(AliasListNode->CommandString);\r
1109         }\r
1110       }\r
1111     }\r
1112   }\r
1113 \r
1114   for (MasterEnvList = EfiShellGetEnv(NULL)\r
1115     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1116     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
1117    ){\r
1118     if (StrSize(MasterEnvList) > ItemSize) {\r
1119       ItemSize = StrSize(MasterEnvList);\r
1120     }\r
1121     for (Temp = StrStr(OriginalCommandLine, MasterEnvList)\r
1122       ;  Temp != NULL\r
1123       ;  Temp = StrStr(Temp+1, MasterEnvList)\r
1124      ){\r
1125       //\r
1126       // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)\r
1127       //\r
1128       if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&\r
1129         ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {\r
1130         NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));\r
1131       }\r
1132     }\r
1133   }\r
1134 \r
1135   //\r
1136   // Quick out if none were found...\r
1137   //\r
1138   if (NewSize == StrSize(OriginalCommandLine)) {\r
1139     ASSERT(Temp == NULL);\r
1140     Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);\r
1141     return (Temp);\r
1142   }\r
1143 \r
1144   //\r
1145   // now do the replacements...\r
1146   //\r
1147   NewCommandLine1 = AllocateZeroPool(NewSize);\r
1148   NewCommandLine2 = AllocateZeroPool(NewSize);\r
1149   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
1150   if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
1151     SHELL_FREE_NON_NULL(NewCommandLine1);\r
1152     SHELL_FREE_NON_NULL(NewCommandLine2);\r
1153     SHELL_FREE_NON_NULL(ItemTemp);\r
1154     return (NULL);\r
1155   }\r
1156   StrCpy(NewCommandLine1, OriginalCommandLine);\r
1157   for (MasterEnvList = EfiShellGetEnv(NULL)\r
1158     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1159     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
1160    ){\r
1161     StrCpy(ItemTemp, L"%");\r
1162     StrCat(ItemTemp, MasterEnvList);\r
1163     StrCat(ItemTemp, L"%");\r
1164     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
1165     StrCpy(NewCommandLine1, NewCommandLine2);\r
1166   }\r
1167   if (CurrentScriptFile != NULL) {\r
1168     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1169       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1170       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1171    ){\r
1172     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
1173     StrCpy(NewCommandLine1, NewCommandLine2);\r
1174     }\r
1175   }\r
1176 \r
1177   FreePool(NewCommandLine2);\r
1178   FreePool(ItemTemp);\r
1179 \r
1180   return (NewCommandLine1);\r
1181 }\r
1182 \r
1183 /**\r
1184   Internal function to run a command line with pipe usage.\r
1185 \r
1186   @param[in] CmdLine        The pointer to the command line.\r
1187   @param[in] StdIn          The pointer to the Standard input.\r
1188   @param[in] StdOut         The pointer to the Standard output.\r
1189 \r
1190   @retval EFI_SUCCESS       The split command is executed successfully.\r
1191   @retval other             Some error occurs when executing the split command.\r
1192 **/\r
1193 EFI_STATUS\r
1194 EFIAPI\r
1195 RunSplitCommand(\r
1196   IN CONST CHAR16             *CmdLine,\r
1197   IN       SHELL_FILE_HANDLE  *StdIn,\r
1198   IN       SHELL_FILE_HANDLE  *StdOut\r
1199   )\r
1200 {\r
1201   EFI_STATUS        Status;\r
1202   CHAR16            *NextCommandLine;\r
1203   CHAR16            *OurCommandLine;\r
1204   UINTN             Size1;\r
1205   UINTN             Size2;\r
1206   SPLIT_LIST        *Split;\r
1207   SHELL_FILE_HANDLE *TempFileHandle;\r
1208   BOOLEAN           Unicode;\r
1209 \r
1210   ASSERT(StdOut == NULL);\r
1211 \r
1212   ASSERT(StrStr(CmdLine, L"|") != NULL);\r
1213 \r
1214   Status          = EFI_SUCCESS;\r
1215   NextCommandLine = NULL;\r
1216   OurCommandLine  = NULL;\r
1217   Size1           = 0;\r
1218   Size2           = 0;\r
1219 \r
1220   NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
1221   OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);\r
1222 \r
1223   if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
1224     SHELL_FREE_NON_NULL(OurCommandLine);\r
1225     SHELL_FREE_NON_NULL(NextCommandLine);\r
1226     return (EFI_OUT_OF_RESOURCES);\r
1227   }\r
1228   if (NextCommandLine[0] != CHAR_NULL &&\r
1229       NextCommandLine[0] == L'a' &&\r
1230       NextCommandLine[1] == L' '\r
1231      ){\r
1232     CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
1233     Unicode = FALSE;\r
1234   } else {\r
1235     Unicode = TRUE;\r
1236   }\r
1237 \r
1238 \r
1239   //\r
1240   // make a SPLIT_LIST item and add to list\r
1241   //\r
1242   Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
1243   ASSERT(Split != NULL);\r
1244   Split->SplitStdIn   = StdIn;\r
1245   Split->SplitStdOut  = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
1246   ASSERT(Split->SplitStdOut != NULL);\r
1247   InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
1248 \r
1249   ASSERT(StrStr(OurCommandLine, L"|") == NULL);\r
1250   Status = RunCommand(OurCommandLine);\r
1251 \r
1252   //\r
1253   // move the output from the first to the in to the second.\r
1254   //\r
1255   TempFileHandle      = Split->SplitStdOut;\r
1256   if (Split->SplitStdIn == StdIn) {\r
1257     Split->SplitStdOut = NULL;\r
1258   } else {\r
1259     Split->SplitStdOut  = Split->SplitStdIn;\r
1260   }\r
1261   Split->SplitStdIn   = TempFileHandle;\r
1262   ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
1263 \r
1264   if (!EFI_ERROR(Status)) {\r
1265     Status = RunCommand(NextCommandLine);\r
1266   }\r
1267 \r
1268   //\r
1269   // remove the top level from the ScriptList\r
1270   //\r
1271   ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
1272   RemoveEntryList(&Split->Link);\r
1273 \r
1274   //\r
1275   // Note that the original StdIn is now the StdOut...\r
1276   //\r
1277   if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {\r
1278     ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));\r
1279   }\r
1280   if (Split->SplitStdIn != NULL) {\r
1281     ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));\r
1282   }\r
1283 \r
1284   FreePool(Split);\r
1285   FreePool(NextCommandLine);\r
1286   FreePool(OurCommandLine);\r
1287 \r
1288   return (Status);\r
1289 }\r
1290 \r
1291 /**\r
1292   Function will process and run a command line.\r
1293 \r
1294   This will determine if the command line represents an internal shell \r
1295   command or dispatch an external application.\r
1296 \r
1297   @param[in] CmdLine      The command line to parse.\r
1298 \r
1299   @retval EFI_SUCCESS     The command was completed.\r
1300   @retval EFI_ABORTED     The command's operation was aborted.\r
1301 **/\r
1302 EFI_STATUS\r
1303 EFIAPI\r
1304 RunCommand(\r
1305   IN CONST CHAR16   *CmdLine\r
1306   )\r
1307 {\r
1308   EFI_STATUS                Status;\r
1309   CHAR16                    *CommandName;\r
1310   SHELL_STATUS              ShellStatus;\r
1311   UINTN                     Argc;\r
1312   CHAR16                    **Argv;\r
1313   BOOLEAN                   LastError;\r
1314   CHAR16                    LeString[11];\r
1315   CHAR16                    *PostAliasCmdLine;\r
1316   UINTN                     PostAliasSize;\r
1317   CHAR16                    *PostVariableCmdLine;\r
1318   CHAR16                    *CommandWithPath;\r
1319   CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
1320   CONST CHAR16              *TempLocation;\r
1321   CONST CHAR16              *TempLocation2;\r
1322   SHELL_FILE_HANDLE         OriginalStdIn;\r
1323   SHELL_FILE_HANDLE         OriginalStdOut;\r
1324   SHELL_FILE_HANDLE         OriginalStdErr;\r
1325   SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
1326   CHAR16                    *TempLocation3;\r
1327   UINTN                     Count;\r
1328   UINTN                     Count2;\r
1329   CHAR16                    *CleanOriginal;\r
1330   SPLIT_LIST                *Split;\r
1331 \r
1332   ASSERT(CmdLine != NULL);\r
1333   if (StrLen(CmdLine) == 0) {\r
1334     return (EFI_SUCCESS);\r
1335   }\r
1336 \r
1337   CommandName         = NULL;\r
1338   PostVariableCmdLine = NULL;\r
1339   PostAliasCmdLine    = NULL;\r
1340   CommandWithPath     = NULL;\r
1341   DevPath             = NULL;\r
1342   Status              = EFI_SUCCESS;\r
1343   CleanOriginal       = NULL;\r
1344   Split               = NULL;\r
1345 \r
1346   CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
1347   if (CleanOriginal == NULL) {\r
1348     return (EFI_OUT_OF_RESOURCES);\r
1349   }\r
1350   while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {\r
1351     CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;\r
1352   }\r
1353   while (CleanOriginal[0] == L' ') {\r
1354     CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));\r
1355   }\r
1356 \r
1357   CommandName = NULL;\r
1358   if (StrStr(CleanOriginal, L" ") == NULL){\r
1359     StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);\r
1360   } else {\r
1361     StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);\r
1362   }\r
1363 \r
1364   ASSERT(PostAliasCmdLine == NULL);\r
1365   if (!ShellCommandIsCommandOnList(CommandName)) {\r
1366     //\r
1367     // Convert via alias\r
1368     //\r
1369     Status = ShellConvertAlias(&CommandName);\r
1370     PostAliasSize = 0;\r
1371     PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);\r
1372     PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);\r
1373     ASSERT_EFI_ERROR(Status);\r
1374   } else {\r
1375     PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);\r
1376   }\r
1377 \r
1378   if (CleanOriginal != NULL) {\r
1379     FreePool(CleanOriginal);\r
1380     CleanOriginal = NULL;\r
1381   }\r
1382 \r
1383   if (CommandName != NULL) {\r
1384     FreePool(CommandName);\r
1385     CommandName = NULL;\r
1386   }\r
1387 \r
1388   PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);\r
1389 \r
1390   //\r
1391   // we can now free the modified by alias command line\r
1392   //\r
1393   if (PostAliasCmdLine != NULL) {\r
1394     FreePool(PostAliasCmdLine);\r
1395     PostAliasCmdLine = NULL;\r
1396   }\r
1397 \r
1398   if (PostVariableCmdLine == NULL) {\r
1399     return (EFI_OUT_OF_RESOURCES);\r
1400   }\r
1401 \r
1402   while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
1403     PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
1404   }\r
1405   while (PostVariableCmdLine[0] == L' ') {\r
1406     CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
1407   }\r
1408 \r
1409   //\r
1410   // We dont do normal processing with a split command line (output from one command input to another)\r
1411   //\r
1412   TempLocation3 = NULL;\r
1413   if (StrStr(PostVariableCmdLine, L"|") != NULL) {\r
1414     for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {\r
1415       if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {\r
1416         TempLocation3++;\r
1417       } else if (*TempLocation3 == L'|') {\r
1418         break;\r
1419       }\r
1420     }\r
1421   }\r
1422   if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {\r
1423     //\r
1424     // are we in an existing split???\r
1425     //\r
1426     if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
1427       Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
1428     }\r
1429 \r
1430     if (Split == NULL) {\r
1431       Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);\r
1432     } else {\r
1433       Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
1434     }\r
1435   } else {\r
1436 \r
1437     //\r
1438     // If this is a mapped drive change handle that...\r
1439     //\r
1440     if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {\r
1441       Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);\r
1442       if (EFI_ERROR(Status)) {\r
1443         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
1444       }\r
1445       FreePool(PostVariableCmdLine);\r
1446       return (Status);\r
1447     }\r
1448 \r
1449     ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...\r
1450     ///      We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...\r
1451 \r
1452 \r
1453 \r
1454     Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
1455     if (EFI_ERROR(Status)) {\r
1456       if (Status == EFI_NOT_FOUND) {\r
1457         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);\r
1458       } else {\r
1459         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
1460       }\r
1461     } else {\r
1462       while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
1463         PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
1464       }\r
1465       while (PostVariableCmdLine[0] == L' ') {\r
1466         CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
1467       }\r
1468 \r
1469       //\r
1470       // get the argc and argv updated for internal commands\r
1471       //\r
1472       Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);\r
1473       ASSERT_EFI_ERROR(Status);\r
1474 \r
1475       for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {\r
1476         if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]\r
1477         ||  (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)\r
1478           ) {\r
1479           //\r
1480           // We need to redo the arguments since a parameter was -?\r
1481           // move them all down 1 to the end, then up one then replace the first with help\r
1482           //\r
1483           FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);\r
1484           ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;\r
1485           for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {\r
1486             ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];\r
1487           }\r
1488           ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;\r
1489           for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {\r
1490             ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];\r
1491           }\r
1492           ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;\r
1493           ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);\r
1494           break;\r
1495         }\r
1496       }\r
1497 \r
1498       //\r
1499       // command or file?\r
1500       //\r
1501       if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
1502         //\r
1503         // Run the command (which was converted if it was an alias)\r
1504         //\r
1505         if (!EFI_ERROR(Status))  {\r
1506           Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
1507           ASSERT_EFI_ERROR(Status);\r
1508           UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);\r
1509           DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1510           if (LastError) {\r
1511             InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
1512           }\r
1513           //\r
1514           // Pass thru the exitcode from the app.\r
1515           //\r
1516           if (ShellCommandGetExit()) {\r
1517             Status = ShellStatus;\r
1518           } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
1519             Status = EFI_ABORTED;\r
1520           }\r
1521         }\r
1522       } else {\r
1523         //\r
1524         // run an external file (or script)\r
1525         //\r
1526         if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {\r
1527           ASSERT (CommandWithPath == NULL);\r
1528           if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {\r
1529             CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
1530           }\r
1531         }\r
1532         if (CommandWithPath == NULL) {\r
1533           CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);\r
1534         }\r
1535         if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
1536           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
1537         } else {\r
1538           //\r
1539           // Check if it's a NSH (script) file.\r
1540           //\r
1541           TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
1542           TempLocation2 = mScriptExtension;\r
1543           if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
1544             Status = RunScriptFile (CommandWithPath);\r
1545           } else {\r
1546             DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
1547             ASSERT(DevPath != NULL);\r
1548             Status = InternalShellExecuteDevicePath(\r
1549               &gImageHandle,\r
1550               DevPath,\r
1551               PostVariableCmdLine,\r
1552               NULL,\r
1553               NULL\r
1554              );\r
1555           }\r
1556         }\r
1557       }\r
1558       CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
1559 \r
1560       RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
1561 \r
1562       RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
1563     }\r
1564     if (CommandName != NULL) {\r
1565       if (ShellCommandGetCurrentScriptFile() != NULL &&  !IsScriptOnlyCommand(CommandName)) {\r
1566         //\r
1567         // if this is NOT a scipt only command return success so the script won't quit.\r
1568         // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
1569         //\r
1570         Status = EFI_SUCCESS;\r
1571       }\r
1572     }\r
1573   }\r
1574 \r
1575   SHELL_FREE_NON_NULL(CommandName);\r
1576   SHELL_FREE_NON_NULL(CommandWithPath);\r
1577   SHELL_FREE_NON_NULL(PostVariableCmdLine);\r
1578 \r
1579   return (Status);\r
1580 }\r
1581 \r
1582 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};\r
1583 /**\r
1584   Function determins if the CommandName COULD be a valid command.  It does not determine whether\r
1585   this is a valid command.  It only checks for invalid characters.\r
1586 \r
1587   @param[in] CommandName    The name to check\r
1588 \r
1589   @retval TRUE              CommandName could be a command name\r
1590   @retval FALSE             CommandName could not be a valid command name\r
1591 **/\r
1592 BOOLEAN\r
1593 EFIAPI\r
1594 IsValidCommandName(\r
1595   IN CONST CHAR16     *CommandName\r
1596   )\r
1597 {\r
1598   UINTN Count;\r
1599   if (CommandName == NULL) {\r
1600     ASSERT(FALSE);\r
1601     return (FALSE);\r
1602   }\r
1603   for ( Count = 0\r
1604       ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])\r
1605       ; Count++\r
1606      ){\r
1607     if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {\r
1608       return (FALSE);\r
1609     }\r
1610   }\r
1611   return (TRUE);\r
1612 }\r
1613 \r
1614 /**\r
1615   Function to process a NSH script file via SHELL_FILE_HANDLE.\r
1616 \r
1617   @param[in] Handle             The handle to the already opened file.\r
1618   @param[in] Name               The name of the script file.\r
1619 \r
1620   @retval EFI_SUCCESS           the script completed sucessfully\r
1621 **/\r
1622 EFI_STATUS\r
1623 EFIAPI\r
1624 RunScriptFileHandle (\r
1625   IN SHELL_FILE_HANDLE  Handle,\r
1626   IN CONST CHAR16       *Name\r
1627   )\r
1628 {\r
1629   EFI_STATUS          Status;\r
1630   SCRIPT_FILE         *NewScriptFile;\r
1631   UINTN               LoopVar;\r
1632   CHAR16              *CommandLine;\r
1633   CHAR16              *CommandLine2;\r
1634   CHAR16              *CommandLine3;\r
1635   SCRIPT_COMMAND_LIST *LastCommand;\r
1636   BOOLEAN             Ascii;\r
1637   BOOLEAN             PreScriptEchoState;\r
1638   BOOLEAN             PreCommandEchoState;\r
1639   CONST CHAR16        *CurDir;\r
1640   UINTN               LineCount;\r
1641   CHAR16              LeString[50];\r
1642 \r
1643   ASSERT(!ShellCommandGetScriptExit());\r
1644 \r
1645   PreScriptEchoState = ShellCommandGetEchoState();\r
1646 \r
1647   NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
1648   if (NewScriptFile == NULL) {\r
1649     return (EFI_OUT_OF_RESOURCES);\r
1650   }\r
1651 \r
1652   //\r
1653   // Set up the name\r
1654   //\r
1655   ASSERT(NewScriptFile->ScriptName == NULL);\r
1656   NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);\r
1657   if (NewScriptFile->ScriptName == NULL) {\r
1658     DeleteScriptFileStruct(NewScriptFile);\r
1659     return (EFI_OUT_OF_RESOURCES);\r
1660   }\r
1661 \r
1662   //\r
1663   // Save the parameters (used to replace %0 to %9 later on)\r
1664   //\r
1665   NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
1666   if (NewScriptFile->Argc != 0) {\r
1667     NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));\r
1668     if (NewScriptFile->Argv == NULL) {\r
1669       DeleteScriptFileStruct(NewScriptFile);\r
1670       return (EFI_OUT_OF_RESOURCES);\r
1671     }\r
1672     for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
1673       ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
1674       NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
1675       if (NewScriptFile->Argv[LoopVar] == NULL) {\r
1676         DeleteScriptFileStruct(NewScriptFile);\r
1677         return (EFI_OUT_OF_RESOURCES);\r
1678       }\r
1679     }\r
1680   } else {\r
1681     NewScriptFile->Argv = NULL;\r
1682   }\r
1683 \r
1684   InitializeListHead(&NewScriptFile->CommandList);\r
1685   InitializeListHead(&NewScriptFile->SubstList);\r
1686 \r
1687   //\r
1688   // Now build the list of all script commands.\r
1689   //\r
1690   LineCount = 0;\r
1691   while(!ShellFileHandleEof(Handle)) {\r
1692     CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
1693     LineCount++;\r
1694     if (CommandLine == NULL || StrLen(CommandLine) == 0) {\r
1695       continue;\r
1696     }\r
1697     NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
1698     if (NewScriptFile->CurrentCommand == NULL) {\r
1699       DeleteScriptFileStruct(NewScriptFile);\r
1700       return (EFI_OUT_OF_RESOURCES);\r
1701     }\r
1702 \r
1703     NewScriptFile->CurrentCommand->Cl   = CommandLine;\r
1704     NewScriptFile->CurrentCommand->Data = NULL;\r
1705     NewScriptFile->CurrentCommand->Line = LineCount;\r
1706 \r
1707     InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1708   }\r
1709 \r
1710   //\r
1711   // Add this as the topmost script file\r
1712   //\r
1713   ShellCommandSetNewScript (NewScriptFile);\r
1714 \r
1715   //\r
1716   // Now enumerate through the commands and run each one.\r
1717   //\r
1718   CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
1719   if (CommandLine == NULL) {\r
1720     DeleteScriptFileStruct(NewScriptFile);\r
1721     return (EFI_OUT_OF_RESOURCES);\r
1722   }\r
1723   CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
1724   if (CommandLine2 == NULL) {\r
1725     FreePool(CommandLine);\r
1726     DeleteScriptFileStruct(NewScriptFile);\r
1727     return (EFI_OUT_OF_RESOURCES);\r
1728   }\r
1729 \r
1730   for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)\r
1731       ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
1732       ; // conditional increment in the body of the loop\r
1733   ){\r
1734     ASSERT(CommandLine2 != NULL);\r
1735     StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);\r
1736 \r
1737     //\r
1738     // NULL out comments\r
1739     //\r
1740     for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {\r
1741       if (*CommandLine3 == L'^') {\r
1742         if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {\r
1743           CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));\r
1744         }\r
1745       } else if (*CommandLine3 == L'#') {\r
1746         *CommandLine3 = CHAR_NULL;\r
1747       }\r
1748     }\r
1749 \r
1750     if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {\r
1751       //\r
1752       // Due to variability in starting the find and replace action we need to have both buffers the same.\r
1753       //\r
1754       StrCpy(CommandLine, CommandLine2);\r
1755 \r
1756       //\r
1757       // Remove the %0 to %9 from the command line (if we have some arguments)\r
1758       //\r
1759       if (NewScriptFile->Argv != NULL) {\r
1760         switch (NewScriptFile->Argc) {\r
1761           default:\r
1762             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);\r
1763             ASSERT_EFI_ERROR(Status);\r
1764           case 9:\r
1765             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);\r
1766             ASSERT_EFI_ERROR(Status);\r
1767           case 8:\r
1768             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);\r
1769             ASSERT_EFI_ERROR(Status);\r
1770           case 7:\r
1771             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);\r
1772             ASSERT_EFI_ERROR(Status);\r
1773           case 6:\r
1774             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);\r
1775             ASSERT_EFI_ERROR(Status);\r
1776           case 5:\r
1777             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);\r
1778             ASSERT_EFI_ERROR(Status);\r
1779           case 4:\r
1780             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);\r
1781             ASSERT_EFI_ERROR(Status);\r
1782           case 3:\r
1783             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);\r
1784             ASSERT_EFI_ERROR(Status);\r
1785           case 2:\r
1786             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);\r
1787             ASSERT_EFI_ERROR(Status);\r
1788           case 1:\r
1789             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);\r
1790             ASSERT_EFI_ERROR(Status);\r
1791             break;\r
1792           case 0:\r
1793             break;\r
1794         }\r
1795       }\r
1796       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);\r
1797       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);\r
1798       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);\r
1799       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);\r
1800       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);\r
1801       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);\r
1802       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);\r
1803       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);\r
1804       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);\r
1805 \r
1806       StrCpy(CommandLine2, CommandLine);\r
1807 \r
1808       LastCommand = NewScriptFile->CurrentCommand;\r
1809 \r
1810       for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);\r
1811 \r
1812       if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {\r
1813         //\r
1814         // This line is a goto target / label\r
1815         //\r
1816       } else {\r
1817         if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {\r
1818           if (ShellCommandGetEchoState()) {\r
1819             CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
1820             if (CurDir != NULL && StrLen(CurDir) > 1) {\r
1821               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
1822             } else {\r
1823               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
1824             }\r
1825             ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
1826           }\r
1827           if (CommandLine3[0] == L'@') {\r
1828             //\r
1829             // We need to save the current echo state\r
1830             // and disable echo for just this command.\r
1831             //\r
1832             PreCommandEchoState = ShellCommandGetEchoState();\r
1833             ShellCommandSetEchoState(FALSE);\r
1834             Status = RunCommand(CommandLine3+1);\r
1835 \r
1836             //\r
1837             // Now restore the pre-'@' echo state.\r
1838             //\r
1839             ShellCommandSetEchoState(PreCommandEchoState);\r
1840           } else {\r
1841             Status = RunCommand(CommandLine3);\r
1842           }\r
1843         }\r
1844 \r
1845         if (ShellCommandGetScriptExit()) {\r
1846           UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%Lx", ShellCommandGetExitCode());\r
1847           DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1848           InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
1849 \r
1850           ShellCommandRegisterExit(FALSE, 0);\r
1851           Status = EFI_SUCCESS;\r
1852           break;\r
1853         }\r
1854         if (EFI_ERROR(Status)) {\r
1855           break;\r
1856         }\r
1857         if (ShellCommandGetExit()) {\r
1858           break;\r
1859         }\r
1860       }\r
1861       //\r
1862       // If that commend did not update the CurrentCommand then we need to advance it...\r
1863       //\r
1864       if (LastCommand == NewScriptFile->CurrentCommand) {\r
1865         NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1866         if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
1867           NewScriptFile->CurrentCommand->Reset = TRUE;\r
1868         }\r
1869       }\r
1870     } else {\r
1871       NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1872       if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
1873         NewScriptFile->CurrentCommand->Reset = TRUE;\r
1874       }\r
1875     }\r
1876   }\r
1877 \r
1878 \r
1879   FreePool(CommandLine);\r
1880   FreePool(CommandLine2);\r
1881   ShellCommandSetNewScript (NULL);\r
1882 \r
1883   //\r
1884   // Only if this was the last script reset the state.\r
1885   //\r
1886   if (ShellCommandGetCurrentScriptFile()==NULL) {\r
1887     ShellCommandSetEchoState(PreScriptEchoState);\r
1888   }\r
1889   return (EFI_SUCCESS);\r
1890 }\r
1891 \r
1892 /**\r
1893   Function to process a NSH script file.\r
1894 \r
1895   @param[in] ScriptPath         Pointer to the script file name (including file system path).\r
1896 \r
1897   @retval EFI_SUCCESS           the script completed sucessfully\r
1898 **/\r
1899 EFI_STATUS\r
1900 EFIAPI\r
1901 RunScriptFile (\r
1902   IN CONST CHAR16 *ScriptPath\r
1903   )\r
1904 {\r
1905   EFI_STATUS          Status;\r
1906   SHELL_FILE_HANDLE   FileHandle;\r
1907 \r
1908   if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
1909     return (EFI_INVALID_PARAMETER);\r
1910   }\r
1911 \r
1912   Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
1913   if (EFI_ERROR(Status)) {\r
1914     return (Status);\r
1915   }\r
1916 \r
1917   Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
1918 \r
1919   ShellCloseFile(&FileHandle);\r
1920 \r
1921   return (Status);\r
1922 }\r