614c1e9595d3d02132750a1c3550a82c02db47d8
[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     TempSpot = StrStr(FileStringPath, L";");\r
861     if (TempSpot != NULL) {\r
862       *TempSpot = CHAR_NULL;\r
863     }\r
864     FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);\r
865     PathRemoveLastItem(FileStringPath);\r
866     FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);\r
867     Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);\r
868     FreePool(FileStringPath);\r
869   }\r
870   if (EFI_ERROR(Status)) {\r
871     NamePath = FileDevicePath (NULL, mStartupScript);\r
872     NewPath = AppendDevicePathNode (ImagePath, NamePath);\r
873     FreePool(NamePath);\r
874 \r
875     //\r
876     // Try the location\r
877     //\r
878     Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
879     FreePool(NewPath);\r
880   }\r
881   //\r
882   // If we got a file, run it\r
883   //\r
884   if (!EFI_ERROR(Status) && FileHandle != NULL) {\r
885     Status = RunScriptFileHandle (FileHandle, mStartupScript);\r
886     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);\r
887   } else {\r
888     FileStringPath = ShellFindFilePath(mStartupScript);\r
889     if (FileStringPath == NULL) {\r
890       //\r
891       // we return success since we dont need to have a startup script\r
892       //\r
893       Status = EFI_SUCCESS;\r
894       ASSERT(FileHandle == NULL);\r
895     } else {\r
896       Status = RunScriptFile(FileStringPath);\r
897       FreePool(FileStringPath);\r
898     }\r
899   }\r
900 \r
901 \r
902   return (Status);\r
903 }\r
904 \r
905 /**\r
906   Function to perform the shell prompt looping.  It will do a single prompt,\r
907   dispatch the result, and then return.  It is expected that the caller will\r
908   call this function in a loop many times.\r
909 \r
910   @retval EFI_SUCCESS\r
911   @retval RETURN_ABORTED\r
912 **/\r
913 EFI_STATUS\r
914 EFIAPI\r
915 DoShellPrompt (\r
916   VOID\r
917   )\r
918 {\r
919   UINTN         Column;\r
920   UINTN         Row;\r
921   CHAR16        *CmdLine;\r
922   CONST CHAR16  *CurDir;\r
923   UINTN         BufferSize;\r
924   EFI_STATUS    Status;\r
925 \r
926   CurDir  = NULL;\r
927 \r
928   //\r
929   // Get screen setting to decide size of the command line buffer\r
930   //\r
931   gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);\r
932   BufferSize  = Column * Row * sizeof (CHAR16);\r
933   CmdLine     = AllocateZeroPool (BufferSize);\r
934   if (CmdLine == NULL) {\r
935     return EFI_OUT_OF_RESOURCES;\r
936   }\r
937 \r
938   CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
939 \r
940   //\r
941   // Prompt for input\r
942   //\r
943   gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);\r
944 \r
945   if (CurDir != NULL && StrLen(CurDir) > 1) {\r
946     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
947   } else {\r
948     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
949   }\r
950 \r
951   //\r
952   // Read a line from the console\r
953   //\r
954   Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);\r
955 \r
956   //\r
957   // Null terminate the string and parse it\r
958   //\r
959   if (!EFI_ERROR (Status)) {\r
960     CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
961     Status = RunCommand(CmdLine);\r
962   }\r
963 \r
964   //\r
965   // Done with this command\r
966   //\r
967   FreePool (CmdLine);\r
968   return Status;\r
969 }\r
970 \r
971 /**\r
972   Add a buffer to the Buffer To Free List for safely returning buffers to other\r
973   places without risking letting them modify internal shell information.\r
974 \r
975   @param Buffer   Something to pass to FreePool when the shell is exiting.\r
976 **/\r
977 VOID*\r
978 EFIAPI\r
979 AddBufferToFreeList(\r
980   VOID *Buffer\r
981   )\r
982 {\r
983   BUFFER_LIST   *BufferListEntry;\r
984 \r
985   if (Buffer == NULL) {\r
986     return (NULL);\r
987   }\r
988 \r
989   BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));\r
990   ASSERT(BufferListEntry != NULL);\r
991   BufferListEntry->Buffer = Buffer;\r
992   InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
993   return (Buffer);\r
994 }\r
995 \r
996 /**\r
997   Add a buffer to the Line History List\r
998 \r
999   @param Buffer     The line buffer to add.\r
1000 **/\r
1001 VOID\r
1002 EFIAPI\r
1003 AddLineToCommandHistory(\r
1004   IN CONST CHAR16 *Buffer\r
1005   )\r
1006 {\r
1007   BUFFER_LIST *Node;\r
1008 \r
1009   Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1010   ASSERT(Node != NULL);\r
1011   Node->Buffer = AllocateZeroPool(StrSize(Buffer));\r
1012   ASSERT(Node->Buffer != NULL);\r
1013   StrCpy(Node->Buffer, Buffer);\r
1014 \r
1015   InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1016 }\r
1017 \r
1018 /**\r
1019   Checks if a string is an alias for another command.  If yes, then it replaces the alias name\r
1020   with the correct command name.\r
1021 \r
1022   @param[in, out] CommandString    Upon entry the potential alias.  Upon return the\r
1023                                    command name if it was an alias.  If it was not\r
1024                                    an alias it will be unchanged.  This function may\r
1025                                    change the buffer to fit the command name.\r
1026 \r
1027   @retval EFI_SUCCESS             The name was changed.\r
1028   @retval EFI_SUCCESS             The name was not an alias.\r
1029   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.\r
1030 **/\r
1031 EFI_STATUS\r
1032 EFIAPI\r
1033 ShellConvertAlias(\r
1034   IN OUT CHAR16 **CommandString\r
1035   )\r
1036 {\r
1037   CONST CHAR16  *NewString;\r
1038 \r
1039   NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);\r
1040   if (NewString == NULL) {\r
1041     return (EFI_SUCCESS);\r
1042   }\r
1043   FreePool(*CommandString);\r
1044   *CommandString = AllocateZeroPool(StrSize(NewString));\r
1045   if (*CommandString == NULL) {\r
1046     return (EFI_OUT_OF_RESOURCES);\r
1047   }\r
1048   StrCpy(*CommandString, NewString);\r
1049   return (EFI_SUCCESS);\r
1050 }\r
1051 \r
1052 /**\r
1053   Function allocates a new command line and replaces all instances of environment\r
1054   variable names that are correctly preset to their values.\r
1055 \r
1056   If the return value is not NULL the memory must be caller freed.\r
1057 \r
1058   @param[in] OriginalCommandLine    The original command line\r
1059 \r
1060   @retval NULL                      An error ocurred.\r
1061   @return                           The new command line with no environment variables present.\r
1062 **/\r
1063 CHAR16*\r
1064 EFIAPI\r
1065 ShellConvertVariables (\r
1066   IN CONST CHAR16 *OriginalCommandLine\r
1067   )\r
1068 {\r
1069   CONST CHAR16        *MasterEnvList;\r
1070   UINTN               NewSize;\r
1071   CHAR16              *NewCommandLine1;\r
1072   CHAR16              *NewCommandLine2;\r
1073   CHAR16              *Temp;\r
1074   UINTN               ItemSize;\r
1075   CHAR16              *ItemTemp;\r
1076   SCRIPT_FILE         *CurrentScriptFile;\r
1077   ALIAS_LIST          *AliasListNode;\r
1078 \r
1079   ASSERT(OriginalCommandLine != NULL);\r
1080 \r
1081   ItemSize          = 0;\r
1082   NewSize           = StrSize(OriginalCommandLine);\r
1083   CurrentScriptFile = ShellCommandGetCurrentScriptFile();\r
1084   Temp              = NULL;\r
1085 \r
1086   ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?\r
1087 \r
1088   //\r
1089   // calculate the size required for the post-conversion string...\r
1090   //\r
1091   if (CurrentScriptFile != NULL) {\r
1092     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1093       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1094       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1095    ){\r
1096       for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)\r
1097         ;  Temp != NULL\r
1098         ;  Temp = StrStr(Temp+1, AliasListNode->Alias)\r
1099        ){\r
1100         //\r
1101         // we need a preceeding and if there is space no ^ preceeding (if no space ignore)\r
1102         //\r
1103         if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {\r
1104           NewSize += StrSize(AliasListNode->CommandString);\r
1105         }\r
1106       }\r
1107     }\r
1108   }\r
1109 \r
1110   for (MasterEnvList = EfiShellGetEnv(NULL)\r
1111     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1112     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
1113    ){\r
1114     if (StrSize(MasterEnvList) > ItemSize) {\r
1115       ItemSize = StrSize(MasterEnvList);\r
1116     }\r
1117     for (Temp = StrStr(OriginalCommandLine, MasterEnvList)\r
1118       ;  Temp != NULL\r
1119       ;  Temp = StrStr(Temp+1, MasterEnvList)\r
1120      ){\r
1121       //\r
1122       // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)\r
1123       //\r
1124       if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&\r
1125         ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {\r
1126         NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));\r
1127       }\r
1128     }\r
1129   }\r
1130 \r
1131   //\r
1132   // Quick out if none were found...\r
1133   //\r
1134   if (NewSize == StrSize(OriginalCommandLine)) {\r
1135     ASSERT(Temp == NULL);\r
1136     Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);\r
1137     return (Temp);\r
1138   }\r
1139 \r
1140   //\r
1141   // now do the replacements...\r
1142   //\r
1143   NewCommandLine1 = AllocateZeroPool(NewSize);\r
1144   NewCommandLine2 = AllocateZeroPool(NewSize);\r
1145   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
1146   if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
1147     SHELL_FREE_NON_NULL(NewCommandLine1);\r
1148     SHELL_FREE_NON_NULL(NewCommandLine2);\r
1149     SHELL_FREE_NON_NULL(ItemTemp);\r
1150     return (NULL);\r
1151   }\r
1152   StrCpy(NewCommandLine1, OriginalCommandLine);\r
1153   for (MasterEnvList = EfiShellGetEnv(NULL)\r
1154     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1155     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
1156    ){\r
1157     StrCpy(ItemTemp, L"%");\r
1158     StrCat(ItemTemp, MasterEnvList);\r
1159     StrCat(ItemTemp, L"%");\r
1160     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
1161     StrCpy(NewCommandLine1, NewCommandLine2);\r
1162   }\r
1163   if (CurrentScriptFile != NULL) {\r
1164     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1165       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1166       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1167    ){\r
1168     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
1169     StrCpy(NewCommandLine1, NewCommandLine2);\r
1170     }\r
1171   }\r
1172 \r
1173   FreePool(NewCommandLine2);\r
1174   FreePool(ItemTemp);\r
1175 \r
1176   return (NewCommandLine1);\r
1177 }\r
1178 \r
1179 /**\r
1180   Internal function to run a command line with pipe usage.\r
1181 \r
1182   @param[in] CmdLine        The pointer to the command line.\r
1183   @param[in] StdIn          The pointer to the Standard input.\r
1184   @param[in] StdOut         The pointer to the Standard output.\r
1185 \r
1186   @retval EFI_SUCCESS       The split command is executed successfully.\r
1187   @retval other             Some error occurs when executing the split command.\r
1188 **/\r
1189 EFI_STATUS\r
1190 EFIAPI\r
1191 RunSplitCommand(\r
1192   IN CONST CHAR16             *CmdLine,\r
1193   IN       SHELL_FILE_HANDLE  *StdIn,\r
1194   IN       SHELL_FILE_HANDLE  *StdOut\r
1195   )\r
1196 {\r
1197   EFI_STATUS        Status;\r
1198   CHAR16            *NextCommandLine;\r
1199   CHAR16            *OurCommandLine;\r
1200   UINTN             Size1;\r
1201   UINTN             Size2;\r
1202   SPLIT_LIST        *Split;\r
1203   SHELL_FILE_HANDLE *TempFileHandle;\r
1204   BOOLEAN           Unicode;\r
1205 \r
1206   ASSERT(StdOut == NULL);\r
1207 \r
1208   ASSERT(StrStr(CmdLine, L"|") != NULL);\r
1209 \r
1210   Status          = EFI_SUCCESS;\r
1211   NextCommandLine = NULL;\r
1212   OurCommandLine  = NULL;\r
1213   Size1           = 0;\r
1214   Size2           = 0;\r
1215 \r
1216   NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
1217   OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);\r
1218   if (NextCommandLine[0] != CHAR_NULL &&\r
1219       NextCommandLine[0] == L'a' &&\r
1220       NextCommandLine[1] == L' '\r
1221      ){\r
1222     CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
1223     Unicode = FALSE;\r
1224   } else {\r
1225     Unicode = TRUE;\r
1226   }\r
1227 \r
1228 \r
1229   //\r
1230   // make a SPLIT_LIST item and add to list\r
1231   //\r
1232   Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
1233   ASSERT(Split != NULL);\r
1234   Split->SplitStdIn   = StdIn;\r
1235   Split->SplitStdOut  = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
1236   ASSERT(Split->SplitStdOut != NULL);\r
1237   InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
1238 \r
1239   ASSERT(StrStr(OurCommandLine, L"|") == NULL);\r
1240   Status = RunCommand(OurCommandLine);\r
1241 \r
1242   //\r
1243   // move the output from the first to the in to the second.\r
1244   //\r
1245   TempFileHandle      = Split->SplitStdOut;\r
1246   if (Split->SplitStdIn == StdIn) {\r
1247     Split->SplitStdOut = NULL;\r
1248   } else {\r
1249     Split->SplitStdOut  = Split->SplitStdIn;\r
1250   }\r
1251   Split->SplitStdIn   = TempFileHandle;\r
1252   ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);\r
1253 \r
1254   if (!EFI_ERROR(Status)) {\r
1255     Status = RunCommand(NextCommandLine);\r
1256   }\r
1257 \r
1258   //\r
1259   // remove the top level from the ScriptList\r
1260   //\r
1261   ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
1262   RemoveEntryList(&Split->Link);\r
1263 \r
1264   //\r
1265   // Note that the original StdIn is now the StdOut...\r
1266   //\r
1267   if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {\r
1268     ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));\r
1269   }\r
1270   if (Split->SplitStdIn != NULL) {\r
1271     ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));\r
1272   }\r
1273 \r
1274   FreePool(Split);\r
1275   FreePool(NextCommandLine);\r
1276   FreePool(OurCommandLine);\r
1277 \r
1278   return (Status);\r
1279 }\r
1280 \r
1281 /**\r
1282   Function will process and run a command line.\r
1283 \r
1284   This will determine if the command line represents an internal shell \r
1285   command or dispatch an external application.\r
1286 \r
1287   @param[in] CmdLine      The command line to parse.\r
1288 \r
1289   @retval EFI_SUCCESS     The command was completed.\r
1290   @retval EFI_ABORTED     The command's operation was aborted.\r
1291 **/\r
1292 EFI_STATUS\r
1293 EFIAPI\r
1294 RunCommand(\r
1295   IN CONST CHAR16   *CmdLine\r
1296   )\r
1297 {\r
1298   EFI_STATUS                Status;\r
1299   CHAR16                    *CommandName;\r
1300   SHELL_STATUS              ShellStatus;\r
1301   UINTN                     Argc;\r
1302   CHAR16                    **Argv;\r
1303   BOOLEAN                   LastError;\r
1304   CHAR16                    LeString[11];\r
1305   CHAR16                    *PostAliasCmdLine;\r
1306   UINTN                     PostAliasSize;\r
1307   CHAR16                    *PostVariableCmdLine;\r
1308   CHAR16                    *CommandWithPath;\r
1309   CONST EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
1310   CONST CHAR16              *TempLocation;\r
1311   CONST CHAR16              *TempLocation2;\r
1312   SHELL_FILE_HANDLE         OriginalStdIn;\r
1313   SHELL_FILE_HANDLE         OriginalStdOut;\r
1314   SHELL_FILE_HANDLE         OriginalStdErr;\r
1315   SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
1316   CHAR16                    *TempLocation3;\r
1317   UINTN                     Count;\r
1318   UINTN                     Count2;\r
1319   CHAR16                    *CleanOriginal;\r
1320   SPLIT_LIST                *Split;\r
1321 \r
1322   ASSERT(CmdLine != NULL);\r
1323   if (StrLen(CmdLine) == 0) {\r
1324     return (EFI_SUCCESS);\r
1325   }\r
1326 \r
1327   CommandName         = NULL;\r
1328   PostVariableCmdLine = NULL;\r
1329   PostAliasCmdLine    = NULL;\r
1330   CommandWithPath     = NULL;\r
1331   DevPath             = NULL;\r
1332   Status              = EFI_SUCCESS;\r
1333   CleanOriginal       = NULL;\r
1334   Split               = NULL;\r
1335 \r
1336   CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
1337   while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {\r
1338     CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;\r
1339   }\r
1340   while (CleanOriginal[0] == L' ') {\r
1341     CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));\r
1342   }\r
1343 \r
1344   CommandName = NULL;\r
1345   if (StrStr(CleanOriginal, L" ") == NULL){\r
1346     StrnCatGrow(&CommandName, NULL, CleanOriginal, 0);\r
1347   } else {\r
1348     StrnCatGrow(&CommandName, NULL, CleanOriginal, StrStr(CleanOriginal, L" ") - CleanOriginal);\r
1349   }\r
1350 \r
1351   ASSERT(PostAliasCmdLine == NULL);\r
1352   if (!ShellCommandIsCommandOnList(CommandName)) {\r
1353     //\r
1354     // Convert via alias\r
1355     //\r
1356     Status = ShellConvertAlias(&CommandName);\r
1357     PostAliasSize = 0;\r
1358     PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, CommandName, 0);\r
1359     PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, &PostAliasSize, StrStr(CleanOriginal, L" "), 0);\r
1360     ASSERT_EFI_ERROR(Status);\r
1361   } else {\r
1362     PostAliasCmdLine = StrnCatGrow(&PostAliasCmdLine, NULL, CleanOriginal, 0);\r
1363   }\r
1364 \r
1365   if (CleanOriginal != NULL) {\r
1366     FreePool(CleanOriginal);\r
1367     CleanOriginal = NULL;\r
1368   }\r
1369 \r
1370   if (CommandName != NULL) {\r
1371     FreePool(CommandName);\r
1372     CommandName = NULL;\r
1373   }\r
1374 \r
1375   PostVariableCmdLine = ShellConvertVariables(PostAliasCmdLine);\r
1376 \r
1377   //\r
1378   // we can now free the modified by alias command line\r
1379   //\r
1380   if (PostAliasCmdLine != NULL) {\r
1381     FreePool(PostAliasCmdLine);\r
1382     PostAliasCmdLine = NULL;\r
1383   }\r
1384 \r
1385   if (PostVariableCmdLine == NULL) {\r
1386     return (EFI_OUT_OF_RESOURCES);\r
1387   }\r
1388 \r
1389   while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
1390     PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
1391   }\r
1392   while (PostVariableCmdLine[0] == L' ') {\r
1393     CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
1394   }\r
1395 \r
1396   //\r
1397   // We dont do normal processing with a split command line (output from one command input to another)\r
1398   //\r
1399   TempLocation3 = NULL;\r
1400   if (StrStr(PostVariableCmdLine, L"|") != NULL) {\r
1401     for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {\r
1402       if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {\r
1403         TempLocation3++;\r
1404       } else if (*TempLocation3 == L'|') {\r
1405         break;\r
1406       }\r
1407     }\r
1408   }\r
1409   if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {\r
1410     //\r
1411     // are we in an existing split???\r
1412     //\r
1413     if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
1414       Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
1415     }\r
1416 \r
1417     if (Split == NULL) {\r
1418       Status = RunSplitCommand(PostVariableCmdLine, NULL, NULL);\r
1419     } else {\r
1420       Status = RunSplitCommand(PostVariableCmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
1421     }\r
1422   } else {\r
1423 \r
1424     //\r
1425     // If this is a mapped drive change handle that...\r
1426     //\r
1427     if (PostVariableCmdLine[(StrLen(PostVariableCmdLine)-1)] == L':' && StrStr(PostVariableCmdLine, L" ") == NULL) {\r
1428       Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, PostVariableCmdLine);\r
1429       if (EFI_ERROR(Status)) {\r
1430         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, PostVariableCmdLine);\r
1431       }\r
1432       FreePool(PostVariableCmdLine);\r
1433       return (Status);\r
1434     }\r
1435 \r
1436     ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...\r
1437     ///      We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...\r
1438 \r
1439 \r
1440 \r
1441     Status = UpdateStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
1442     if (EFI_ERROR(Status)) {\r
1443       if (Status == EFI_NOT_FOUND) {\r
1444         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR), ShellInfoObject.HiiHandle);\r
1445       } else {\r
1446         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
1447       }\r
1448     } else {\r
1449       while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
1450         PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
1451       }\r
1452       while (PostVariableCmdLine[0] == L' ') {\r
1453         CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
1454       }\r
1455 \r
1456       //\r
1457       // get the argc and argv updated for internal commands\r
1458       //\r
1459       Status = UpdateArgcArgv(ShellInfoObject.NewShellParametersProtocol, PostVariableCmdLine, &Argv, &Argc);\r
1460       ASSERT_EFI_ERROR(Status);\r
1461 \r
1462       for (Count = 0 ; Count < ShellInfoObject.NewShellParametersProtocol->Argc ; Count++) {\r
1463         if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[Count], L"-?") == ShellInfoObject.NewShellParametersProtocol->Argv[Count]\r
1464         ||  (ShellInfoObject.NewShellParametersProtocol->Argv[0][0] == L'?' && ShellInfoObject.NewShellParametersProtocol->Argv[0][1] == CHAR_NULL)\r
1465           ) {\r
1466           //\r
1467           // We need to redo the arguments since a parameter was -?\r
1468           // move them all down 1 to the end, then up one then replace the first with help\r
1469           //\r
1470           FreePool(ShellInfoObject.NewShellParametersProtocol->Argv[Count]);\r
1471           ShellInfoObject.NewShellParametersProtocol->Argv[Count] = NULL;\r
1472           for (Count2 = Count ; (Count2 + 1) < ShellInfoObject.NewShellParametersProtocol->Argc ; Count2++) {\r
1473             ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2+1];\r
1474           }\r
1475           ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = NULL;\r
1476           for (Count2 = ShellInfoObject.NewShellParametersProtocol->Argc -1 ; Count2 > 0 ; Count2--) {\r
1477             ShellInfoObject.NewShellParametersProtocol->Argv[Count2] = ShellInfoObject.NewShellParametersProtocol->Argv[Count2-1];\r
1478           }\r
1479           ShellInfoObject.NewShellParametersProtocol->Argv[0] = NULL;\r
1480           ShellInfoObject.NewShellParametersProtocol->Argv[0] = StrnCatGrow(&ShellInfoObject.NewShellParametersProtocol->Argv[0], NULL, L"help", 0);\r
1481           break;\r
1482         }\r
1483       }\r
1484 \r
1485       //\r
1486       // command or file?\r
1487       //\r
1488       if (ShellCommandIsCommandOnList(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
1489         //\r
1490         // Run the command (which was converted if it was an alias)\r
1491         //\r
1492         if (!EFI_ERROR(Status))  {\r
1493           Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
1494           ASSERT_EFI_ERROR(Status);\r
1495           UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);\r
1496           DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1497           if (LastError) {\r
1498             InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
1499           }\r
1500           //\r
1501           // Pass thru the exitcode from the app.\r
1502           //\r
1503           if (ShellCommandGetExit()) {\r
1504             Status = ShellStatus;\r
1505           } else if (ShellStatus != 0 && IsScriptOnlyCommand(ShellInfoObject.NewShellParametersProtocol->Argv[0])) {\r
1506             Status = EFI_ABORTED;\r
1507           }\r
1508         }\r
1509       } else {\r
1510         //\r
1511         // run an external file (or script)\r
1512         //\r
1513         if (StrStr(ShellInfoObject.NewShellParametersProtocol->Argv[0], L":") != NULL) {\r
1514           ASSERT (CommandWithPath == NULL);\r
1515           if (ShellIsFile(ShellInfoObject.NewShellParametersProtocol->Argv[0]) == EFI_SUCCESS) {\r
1516             CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
1517           }\r
1518         }\r
1519         if (CommandWithPath == NULL) {\r
1520           CommandWithPath = ShellFindFilePathEx(ShellInfoObject.NewShellParametersProtocol->Argv[0], mExecutableExtensions);\r
1521         }\r
1522         if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
1523           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
1524         } else {\r
1525           //\r
1526           // Check if it's a NSH (script) file.\r
1527           //\r
1528           TempLocation = CommandWithPath+StrLen(CommandWithPath)-4;\r
1529           TempLocation2 = mScriptExtension;\r
1530           if ((StrLen(CommandWithPath) > 4) && (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0)) {\r
1531             Status = RunScriptFile (CommandWithPath);\r
1532           } else {\r
1533             DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
1534             ASSERT(DevPath != NULL);\r
1535             Status = InternalShellExecuteDevicePath(\r
1536               &gImageHandle,\r
1537               DevPath,\r
1538               PostVariableCmdLine,\r
1539               NULL,\r
1540               NULL\r
1541              );\r
1542           }\r
1543         }\r
1544       }\r
1545       CommandName = StrnCatGrow(&CommandName, NULL, ShellInfoObject.NewShellParametersProtocol->Argv[0], 0);\r
1546 \r
1547       RestoreArgcArgv(ShellInfoObject.NewShellParametersProtocol, &Argv, &Argc);\r
1548 \r
1549       RestoreStdInStdOutStdErr(ShellInfoObject.NewShellParametersProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
1550     }\r
1551     if (CommandName != NULL) {\r
1552       if (ShellCommandGetCurrentScriptFile() != NULL &&  !IsScriptOnlyCommand(CommandName)) {\r
1553         //\r
1554         // if this is NOT a scipt only command return success so the script won't quit.\r
1555         // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)\r
1556         //\r
1557         Status = EFI_SUCCESS;\r
1558       }\r
1559     }\r
1560   }\r
1561 \r
1562   SHELL_FREE_NON_NULL(CommandName);\r
1563   SHELL_FREE_NON_NULL(CommandWithPath);\r
1564   SHELL_FREE_NON_NULL(PostVariableCmdLine);\r
1565 \r
1566   return (Status);\r
1567 }\r
1568 \r
1569 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};\r
1570 /**\r
1571   Function determins if the CommandName COULD be a valid command.  It does not determine whether\r
1572   this is a valid command.  It only checks for invalid characters.\r
1573 \r
1574   @param[in] CommandName    The name to check\r
1575 \r
1576   @retval TRUE              CommandName could be a command name\r
1577   @retval FALSE             CommandName could not be a valid command name\r
1578 **/\r
1579 BOOLEAN\r
1580 EFIAPI\r
1581 IsValidCommandName(\r
1582   IN CONST CHAR16     *CommandName\r
1583   )\r
1584 {\r
1585   UINTN Count;\r
1586   if (CommandName == NULL) {\r
1587     ASSERT(FALSE);\r
1588     return (FALSE);\r
1589   }\r
1590   for ( Count = 0\r
1591       ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])\r
1592       ; Count++\r
1593      ){\r
1594     if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {\r
1595       return (FALSE);\r
1596     }\r
1597   }\r
1598   return (TRUE);\r
1599 }\r
1600 \r
1601 /**\r
1602   Function to process a NSH script file via SHELL_FILE_HANDLE.\r
1603 \r
1604   @param[in] Handle             The handle to the already opened file.\r
1605   @param[in] Name               The name of the script file.\r
1606 \r
1607   @retval EFI_SUCCESS           the script completed sucessfully\r
1608 **/\r
1609 EFI_STATUS\r
1610 EFIAPI\r
1611 RunScriptFileHandle (\r
1612   IN SHELL_FILE_HANDLE  Handle,\r
1613   IN CONST CHAR16       *Name\r
1614   )\r
1615 {\r
1616   EFI_STATUS          Status;\r
1617   SCRIPT_FILE         *NewScriptFile;\r
1618   UINTN               LoopVar;\r
1619   CHAR16              *CommandLine;\r
1620   CHAR16              *CommandLine2;\r
1621   CHAR16              *CommandLine3;\r
1622   SCRIPT_COMMAND_LIST *LastCommand;\r
1623   BOOLEAN             Ascii;\r
1624   BOOLEAN             PreScriptEchoState;\r
1625   BOOLEAN             PreCommandEchoState;\r
1626   CONST CHAR16        *CurDir;\r
1627   UINTN               LineCount;\r
1628   CHAR16              LeString[50];\r
1629 \r
1630   ASSERT(!ShellCommandGetScriptExit());\r
1631 \r
1632   PreScriptEchoState = ShellCommandGetEchoState();\r
1633 \r
1634   NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
1635   if (NewScriptFile == NULL) {\r
1636     return (EFI_OUT_OF_RESOURCES);\r
1637   }\r
1638 \r
1639   //\r
1640   // Set up the name\r
1641   //\r
1642   ASSERT(NewScriptFile->ScriptName == NULL);\r
1643   NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);\r
1644   if (NewScriptFile->ScriptName == NULL) {\r
1645     DeleteScriptFileStruct(NewScriptFile);\r
1646     return (EFI_OUT_OF_RESOURCES);\r
1647   }\r
1648 \r
1649   //\r
1650   // Save the parameters (used to replace %0 to %9 later on)\r
1651   //\r
1652   NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
1653   if (NewScriptFile->Argc != 0) {\r
1654     NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));\r
1655     if (NewScriptFile->Argv == NULL) {\r
1656       DeleteScriptFileStruct(NewScriptFile);\r
1657       return (EFI_OUT_OF_RESOURCES);\r
1658     }\r
1659     for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
1660       ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
1661       NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
1662       if (NewScriptFile->Argv[LoopVar] == NULL) {\r
1663         DeleteScriptFileStruct(NewScriptFile);\r
1664         return (EFI_OUT_OF_RESOURCES);\r
1665       }\r
1666     }\r
1667   } else {\r
1668     NewScriptFile->Argv = NULL;\r
1669   }\r
1670 \r
1671   InitializeListHead(&NewScriptFile->CommandList);\r
1672   InitializeListHead(&NewScriptFile->SubstList);\r
1673 \r
1674   //\r
1675   // Now build the list of all script commands.\r
1676   //\r
1677   LineCount = 0;\r
1678   while(!ShellFileHandleEof(Handle)) {\r
1679     CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
1680     LineCount++;\r
1681     if (CommandLine == NULL || StrLen(CommandLine) == 0) {\r
1682       continue;\r
1683     }\r
1684     NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
1685     if (NewScriptFile->CurrentCommand == NULL) {\r
1686       DeleteScriptFileStruct(NewScriptFile);\r
1687       return (EFI_OUT_OF_RESOURCES);\r
1688     }\r
1689 \r
1690     NewScriptFile->CurrentCommand->Cl   = CommandLine;\r
1691     NewScriptFile->CurrentCommand->Data = NULL;\r
1692     NewScriptFile->CurrentCommand->Line = LineCount;\r
1693 \r
1694     InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1695   }\r
1696 \r
1697   //\r
1698   // Add this as the topmost script file\r
1699   //\r
1700   ShellCommandSetNewScript (NewScriptFile);\r
1701 \r
1702   //\r
1703   // Now enumerate through the commands and run each one.\r
1704   //\r
1705   CommandLine = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
1706   if (CommandLine == NULL) {\r
1707     DeleteScriptFileStruct(NewScriptFile);\r
1708     return (EFI_OUT_OF_RESOURCES);\r
1709   }\r
1710   CommandLine2 = AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize));\r
1711   if (CommandLine2 == NULL) {\r
1712     FreePool(CommandLine);\r
1713     DeleteScriptFileStruct(NewScriptFile);\r
1714     return (EFI_OUT_OF_RESOURCES);\r
1715   }\r
1716 \r
1717   for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)\r
1718       ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
1719       ; // conditional increment in the body of the loop\r
1720   ){\r
1721     ASSERT(CommandLine2 != NULL);\r
1722     StrCpy(CommandLine2, NewScriptFile->CurrentCommand->Cl);\r
1723 \r
1724     //\r
1725     // NULL out comments\r
1726     //\r
1727     for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {\r
1728       if (*CommandLine3 == L'^') {\r
1729         if (*(CommandLine3+1) == L'#' || *(CommandLine3+1) == L':') {\r
1730           CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));\r
1731         }\r
1732       } else if (*CommandLine3 == L'#') {\r
1733         *CommandLine3 = CHAR_NULL;\r
1734       }\r
1735     }\r
1736 \r
1737     if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {\r
1738       //\r
1739       // Due to variability in starting the find and replace action we need to have both buffers the same.\r
1740       //\r
1741       StrCpy(CommandLine, CommandLine2);\r
1742 \r
1743       //\r
1744       // Remove the %0 to %9 from the command line (if we have some arguments)\r
1745       //\r
1746       if (NewScriptFile->Argv != NULL) {\r
1747         switch (NewScriptFile->Argc) {\r
1748           default:\r
1749             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", NewScriptFile->Argv[9], FALSE, TRUE);\r
1750             ASSERT_EFI_ERROR(Status);\r
1751           case 9:\r
1752             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", NewScriptFile->Argv[8], FALSE, TRUE);\r
1753             ASSERT_EFI_ERROR(Status);\r
1754           case 8:\r
1755             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", NewScriptFile->Argv[7], FALSE, TRUE);\r
1756             ASSERT_EFI_ERROR(Status);\r
1757           case 7:\r
1758             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", NewScriptFile->Argv[6], FALSE, TRUE);\r
1759             ASSERT_EFI_ERROR(Status);\r
1760           case 6:\r
1761             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", NewScriptFile->Argv[5], FALSE, TRUE);\r
1762             ASSERT_EFI_ERROR(Status);\r
1763           case 5:\r
1764             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", NewScriptFile->Argv[4], FALSE, TRUE);\r
1765             ASSERT_EFI_ERROR(Status);\r
1766           case 4:\r
1767             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", NewScriptFile->Argv[3], FALSE, TRUE);\r
1768             ASSERT_EFI_ERROR(Status);\r
1769           case 3:\r
1770             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", NewScriptFile->Argv[2], FALSE, TRUE);\r
1771             ASSERT_EFI_ERROR(Status);\r
1772           case 2:\r
1773             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", NewScriptFile->Argv[1], FALSE, TRUE);\r
1774             ASSERT_EFI_ERROR(Status);\r
1775           case 1:\r
1776             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%0", NewScriptFile->Argv[0], FALSE, TRUE);\r
1777             ASSERT_EFI_ERROR(Status);\r
1778             break;\r
1779           case 0:\r
1780             break;\r
1781         }\r
1782       }\r
1783       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%1", L"\"\"", FALSE, FALSE);\r
1784       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%2", L"\"\"", FALSE, FALSE);\r
1785       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%3", L"\"\"", FALSE, FALSE);\r
1786       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%4", L"\"\"", FALSE, FALSE);\r
1787       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%5", L"\"\"", FALSE, FALSE);\r
1788       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%6", L"\"\"", FALSE, FALSE);\r
1789       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%7", L"\"\"", FALSE, FALSE);\r
1790       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PcdGet16 (PcdShellPrintBufferSize), L"%8", L"\"\"", FALSE, FALSE);\r
1791       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PcdGet16 (PcdShellPrintBufferSize), L"%9", L"\"\"", FALSE, FALSE);\r
1792 \r
1793       StrCpy(CommandLine2, CommandLine);\r
1794 \r
1795       LastCommand = NewScriptFile->CurrentCommand;\r
1796 \r
1797       for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);\r
1798 \r
1799       if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {\r
1800         //\r
1801         // This line is a goto target / label\r
1802         //\r
1803       } else {\r
1804         if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {\r
1805           if (ShellCommandGetEchoState()) {\r
1806             CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
1807             if (CurDir != NULL && StrLen(CurDir) > 1) {\r
1808               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
1809             } else {\r
1810               ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
1811             }\r
1812             ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
1813           }\r
1814           if (CommandLine3[0] == L'@') {\r
1815             //\r
1816             // We need to save the current echo state\r
1817             // and disable echo for just this command.\r
1818             //\r
1819             PreCommandEchoState = ShellCommandGetEchoState();\r
1820             ShellCommandSetEchoState(FALSE);\r
1821             Status = RunCommand(CommandLine3+1);\r
1822 \r
1823             //\r
1824             // Now restore the pre-'@' echo state.\r
1825             //\r
1826             ShellCommandSetEchoState(PreCommandEchoState);\r
1827           } else {\r
1828             Status = RunCommand(CommandLine3);\r
1829           }\r
1830         }\r
1831 \r
1832         if (ShellCommandGetScriptExit()) {\r
1833           UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%Lx", ShellCommandGetExitCode());\r
1834           DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
1835           InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
1836 \r
1837           ShellCommandRegisterExit(FALSE, 0);\r
1838           Status = EFI_SUCCESS;\r
1839           break;\r
1840         }\r
1841         if (EFI_ERROR(Status)) {\r
1842           break;\r
1843         }\r
1844         if (ShellCommandGetExit()) {\r
1845           break;\r
1846         }\r
1847       }\r
1848       //\r
1849       // If that commend did not update the CurrentCommand then we need to advance it...\r
1850       //\r
1851       if (LastCommand == NewScriptFile->CurrentCommand) {\r
1852         NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1853         if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
1854           NewScriptFile->CurrentCommand->Reset = TRUE;\r
1855         }\r
1856       }\r
1857     } else {\r
1858       NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
1859       if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
1860         NewScriptFile->CurrentCommand->Reset = TRUE;\r
1861       }\r
1862     }\r
1863   }\r
1864 \r
1865 \r
1866   FreePool(CommandLine);\r
1867   FreePool(CommandLine2);\r
1868   ShellCommandSetNewScript (NULL);\r
1869 \r
1870   //\r
1871   // Only if this was the last script reset the state.\r
1872   //\r
1873   if (ShellCommandGetCurrentScriptFile()==NULL) {\r
1874     ShellCommandSetEchoState(PreScriptEchoState);\r
1875   }\r
1876   return (EFI_SUCCESS);\r
1877 }\r
1878 \r
1879 /**\r
1880   Function to process a NSH script file.\r
1881 \r
1882   @param[in] ScriptPath         Pointer to the script file name (including file system path).\r
1883 \r
1884   @retval EFI_SUCCESS           the script completed sucessfully\r
1885 **/\r
1886 EFI_STATUS\r
1887 EFIAPI\r
1888 RunScriptFile (\r
1889   IN CONST CHAR16 *ScriptPath\r
1890   )\r
1891 {\r
1892   EFI_STATUS          Status;\r
1893   SHELL_FILE_HANDLE   FileHandle;\r
1894 \r
1895   if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
1896     return (EFI_INVALID_PARAMETER);\r
1897   }\r
1898 \r
1899   Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
1900   if (EFI_ERROR(Status)) {\r
1901     return (Status);\r
1902   }\r
1903 \r
1904   Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
1905 \r
1906   ShellCloseFile(&FileHandle);\r
1907 \r
1908   return (Status);\r
1909 }\r