ShellPkg: add array index check for shell delay option
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
1 /** @file\r
2   This is THE shell (application)\r
3 \r
4   Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
5   (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<BR>\r
6   Copyright 2015-2018 Dell Technologies.<BR>\r
7   This program and the accompanying materials\r
8   are licensed and made available under the terms and conditions of the BSD License\r
9   which accompanies this distribution.  The full text of the license may be found at\r
10   http://opensource.org/licenses/bsd-license.php\r
11 \r
12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14 \r
15 **/\r
16 \r
17 #include "Shell.h"\r
18 \r
19 //\r
20 // Initialize the global structure\r
21 //\r
22 SHELL_INFO ShellInfoObject = {\r
23   NULL,\r
24   NULL,\r
25   FALSE,\r
26   FALSE,\r
27   {\r
28     {{\r
29       0,\r
30       0,\r
31       0,\r
32       0,\r
33       0,\r
34       0,\r
35       0,\r
36       0,\r
37       0,\r
38       0\r
39     }},\r
40     0,\r
41     NULL,\r
42     NULL\r
43   },\r
44   {{NULL, NULL}, NULL},\r
45   {\r
46     {{NULL, NULL}, NULL},\r
47     0,\r
48     0,\r
49     TRUE\r
50   },\r
51   NULL,\r
52   0,\r
53   NULL,\r
54   NULL,\r
55   NULL,\r
56   NULL,\r
57   NULL,\r
58   {{NULL, NULL}, NULL, NULL},\r
59   {{NULL, NULL}, NULL, NULL},\r
60   NULL,\r
61   NULL,\r
62   NULL,\r
63   NULL,\r
64   NULL,\r
65   NULL,\r
66   NULL,\r
67   NULL,\r
68   FALSE\r
69 };\r
70 \r
71 STATIC CONST CHAR16 mScriptExtension[]      = L".NSH";\r
72 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
73 STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";\r
74 CONST CHAR16 mNoNestingEnvVarName[]         = L"nonesting";\r
75 CONST CHAR16 mNoNestingTrue[]               = L"True";\r
76 CONST CHAR16 mNoNestingFalse[]              = L"False";\r
77 \r
78 /**\r
79   Cleans off leading and trailing spaces and tabs.\r
80 \r
81   @param[in] String pointer to the string to trim them off.\r
82 **/\r
83 EFI_STATUS\r
84 TrimSpaces(\r
85   IN CHAR16 **String\r
86   )\r
87 {\r
88   ASSERT(String != NULL);\r
89   ASSERT(*String!= NULL);\r
90   //\r
91   // Remove any spaces and tabs at the beginning of the (*String).\r
92   //\r
93   while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {\r
94     CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));\r
95   }\r
96 \r
97   //\r
98   // Remove any spaces and tabs at the end of the (*String).\r
99   //\r
100   while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {\r
101     (*String)[StrLen((*String))-1] = CHAR_NULL;\r
102   }\r
103 \r
104   return (EFI_SUCCESS);\r
105 }\r
106 \r
107 /**\r
108   Parse for the next instance of one string within another string. Can optionally make sure that\r
109   the string was not escaped (^ character) per the shell specification.\r
110 \r
111   @param[in] SourceString             The string to search within\r
112   @param[in] FindString               The string to look for\r
113   @param[in] CheckForEscapeCharacter  TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances\r
114 **/\r
115 CHAR16*\r
116 FindNextInstance(\r
117   IN CONST CHAR16   *SourceString,\r
118   IN CONST CHAR16   *FindString,\r
119   IN CONST BOOLEAN  CheckForEscapeCharacter\r
120   )\r
121 {\r
122   CHAR16 *Temp;\r
123   if (SourceString == NULL) {\r
124     return (NULL);\r
125   }\r
126   Temp = StrStr(SourceString, FindString);\r
127 \r
128   //\r
129   // If nothing found, or we don't care about escape characters\r
130   //\r
131   if (Temp == NULL || !CheckForEscapeCharacter) {\r
132     return (Temp);\r
133   }\r
134 \r
135   //\r
136   // If we found an escaped character, try again on the remainder of the string\r
137   //\r
138   if ((Temp > (SourceString)) && *(Temp-1) == L'^') {\r
139     return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);\r
140   }\r
141 \r
142   //\r
143   // we found the right character\r
144   //\r
145   return (Temp);\r
146 }\r
147 \r
148 /**\r
149   Check whether the string between a pair of % is a valid environment variable name.\r
150 \r
151   @param[in] BeginPercent       pointer to the first percent.\r
152   @param[in] EndPercent          pointer to the last percent.\r
153 \r
154   @retval TRUE                          is a valid environment variable name.\r
155   @retval FALSE                         is NOT a valid environment variable name.\r
156 **/\r
157 BOOLEAN\r
158 IsValidEnvironmentVariableName(\r
159   IN CONST CHAR16     *BeginPercent,\r
160   IN CONST CHAR16     *EndPercent\r
161   )\r
162 {\r
163   CONST CHAR16    *Walker;\r
164 \r
165   Walker = NULL;\r
166 \r
167   ASSERT (BeginPercent != NULL);\r
168   ASSERT (EndPercent != NULL);\r
169   ASSERT (BeginPercent < EndPercent);\r
170 \r
171   if ((BeginPercent + 1) == EndPercent) {\r
172     return FALSE;\r
173   }\r
174 \r
175   for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {\r
176     if (\r
177         (*Walker >= L'0' && *Walker <= L'9') ||\r
178         (*Walker >= L'A' && *Walker <= L'Z') ||\r
179         (*Walker >= L'a' && *Walker <= L'z') ||\r
180         (*Walker == L'_')\r
181       ) {\r
182       if (Walker == BeginPercent + 1 && (*Walker >= L'0' && *Walker <= L'9')) {\r
183         return FALSE;\r
184       } else {\r
185         continue;\r
186       }\r
187     } else {\r
188       return FALSE;\r
189     }\r
190   }\r
191 \r
192   return TRUE;\r
193 }\r
194 \r
195 /**\r
196   Determine if a command line contains a split operation\r
197 \r
198   @param[in] CmdLine      The command line to parse.\r
199 \r
200   @retval TRUE            CmdLine has a valid split.\r
201   @retval FALSE           CmdLine does not have a valid split.\r
202 **/\r
203 BOOLEAN\r
204 ContainsSplit(\r
205   IN CONST CHAR16 *CmdLine\r
206   )\r
207 {\r
208   CONST CHAR16 *TempSpot;\r
209   CONST CHAR16 *FirstQuote;\r
210   CONST CHAR16 *SecondQuote;\r
211 \r
212   FirstQuote    = FindNextInstance (CmdLine, L"\"", TRUE);\r
213   SecondQuote   = NULL;\r
214   TempSpot      = FindFirstCharacter(CmdLine, L"|", L'^');\r
215 \r
216   if (FirstQuote == NULL    ||\r
217       TempSpot == NULL      ||\r
218       TempSpot == CHAR_NULL ||\r
219       FirstQuote > TempSpot\r
220       ) {\r
221     return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
222   }\r
223 \r
224   while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {\r
225     if (FirstQuote == NULL || FirstQuote > TempSpot) {\r
226       break;\r
227     }\r
228     SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);\r
229     if (SecondQuote == NULL) {\r
230       break;\r
231     }\r
232     if (SecondQuote < TempSpot) {\r
233       FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);\r
234       continue;\r
235     } else {\r
236       FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);\r
237       TempSpot = FindFirstCharacter(TempSpot + 1, L"|", L'^');\r
238       continue;\r
239     }\r
240   }\r
241 \r
242   return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));\r
243 }\r
244 \r
245 /**\r
246   Function to start monitoring for CTRL-S using SimpleTextInputEx.  This\r
247   feature's enabled state was not known when the shell initially launched.\r
248 \r
249   @retval EFI_SUCCESS           The feature is enabled.\r
250   @retval EFI_OUT_OF_RESOURCES  There is not enough memory available.\r
251 **/\r
252 EFI_STATUS\r
253 InternalEfiShellStartCtrlSMonitor(\r
254   VOID\r
255   )\r
256 {\r
257   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;\r
258   EFI_KEY_DATA                      KeyData;\r
259   EFI_STATUS                        Status;\r
260 \r
261   Status = gBS->OpenProtocol(\r
262     gST->ConsoleInHandle,\r
263     &gEfiSimpleTextInputExProtocolGuid,\r
264     (VOID**)&SimpleEx,\r
265     gImageHandle,\r
266     NULL,\r
267     EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
268   if (EFI_ERROR(Status)) {\r
269     ShellPrintHiiEx(\r
270       -1,\r
271       -1,\r
272       NULL,\r
273       STRING_TOKEN (STR_SHELL_NO_IN_EX),\r
274       ShellInfoObject.HiiHandle);\r
275     return (EFI_SUCCESS);\r
276   }\r
277 \r
278   KeyData.KeyState.KeyToggleState = 0;\r
279   KeyData.Key.ScanCode            = 0;\r
280   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
281   KeyData.Key.UnicodeChar         = L's';\r
282 \r
283   Status = SimpleEx->RegisterKeyNotify(\r
284     SimpleEx,\r
285     &KeyData,\r
286     NotificationFunction,\r
287     &ShellInfoObject.CtrlSNotifyHandle1);\r
288 \r
289   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
290   if (!EFI_ERROR(Status)) {\r
291     Status = SimpleEx->RegisterKeyNotify(\r
292       SimpleEx,\r
293       &KeyData,\r
294       NotificationFunction,\r
295       &ShellInfoObject.CtrlSNotifyHandle2);\r
296   }\r
297   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;\r
298   KeyData.Key.UnicodeChar         = 19;\r
299 \r
300   if (!EFI_ERROR(Status)) {\r
301     Status = SimpleEx->RegisterKeyNotify(\r
302       SimpleEx,\r
303       &KeyData,\r
304       NotificationFunction,\r
305       &ShellInfoObject.CtrlSNotifyHandle3);\r
306   }\r
307   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
308   if (!EFI_ERROR(Status)) {\r
309     Status = SimpleEx->RegisterKeyNotify(\r
310       SimpleEx,\r
311       &KeyData,\r
312       NotificationFunction,\r
313       &ShellInfoObject.CtrlSNotifyHandle4);\r
314   }\r
315   return (Status);\r
316 }\r
317 \r
318 \r
319 \r
320 /**\r
321   The entry point for the application.\r
322 \r
323   @param[in] ImageHandle    The firmware allocated handle for the EFI image.\r
324   @param[in] SystemTable    A pointer to the EFI System Table.\r
325 \r
326   @retval EFI_SUCCESS       The entry point is executed successfully.\r
327   @retval other             Some error occurs when executing this entry point.\r
328 \r
329 **/\r
330 EFI_STATUS\r
331 EFIAPI\r
332 UefiMain (\r
333   IN EFI_HANDLE        ImageHandle,\r
334   IN EFI_SYSTEM_TABLE  *SystemTable\r
335   )\r
336 {\r
337   EFI_STATUS                      Status;\r
338   CHAR16                          *TempString;\r
339   UINTN                           Size;\r
340   EFI_HANDLE                      ConInHandle;\r
341   EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;\r
342   SPLIT_LIST                      *Split;\r
343 \r
344   if (PcdGet8(PcdShellSupportLevel) > 3) {\r
345     return (EFI_UNSUPPORTED);\r
346   }\r
347 \r
348   //\r
349   // Clear the screen\r
350   //\r
351   Status = gST->ConOut->ClearScreen(gST->ConOut);\r
352   if (EFI_ERROR(Status)) {\r
353     return (Status);\r
354   }\r
355 \r
356   //\r
357   // Populate the global structure from PCDs\r
358   //\r
359   ShellInfoObject.ImageDevPath                = NULL;\r
360   ShellInfoObject.FileDevPath                 = NULL;\r
361   ShellInfoObject.PageBreakEnabled            = PcdGetBool(PcdShellPageBreakDefault);\r
362   ShellInfoObject.ViewingSettings.InsertMode  = PcdGetBool(PcdShellInsertModeDefault);\r
363   ShellInfoObject.LogScreenCount              = PcdGet8   (PcdShellScreenLogCount  );\r
364 \r
365   //\r
366   // verify we dont allow for spec violation\r
367   //\r
368   ASSERT(ShellInfoObject.LogScreenCount >= 3);\r
369 \r
370   //\r
371   // Initialize the LIST ENTRY objects...\r
372   //\r
373   InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);\r
374   InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
375   InitializeListHead(&ShellInfoObject.SplitList.Link);\r
376 \r
377   //\r
378   // Check PCDs for optional features that are not implemented yet.\r
379   //\r
380   if (   PcdGetBool(PcdShellSupportOldProtocols)\r
381       || !FeaturePcdGet(PcdShellRequireHiiPlatform)\r
382       || FeaturePcdGet(PcdShellSupportFrameworkHii)\r
383    ) {\r
384     return (EFI_UNSUPPORTED);\r
385   }\r
386 \r
387   //\r
388   // turn off the watchdog timer\r
389   //\r
390   gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
391 \r
392   //\r
393   // install our console logger.  This will keep a log of the output for back-browsing\r
394   //\r
395   Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);\r
396   if (!EFI_ERROR(Status)) {\r
397     //\r
398     // Enable the cursor to be visible\r
399     //\r
400     gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
401 \r
402     //\r
403     // If supporting EFI 1.1 we need to install HII protocol\r
404     // only do this if PcdShellRequireHiiPlatform == FALSE\r
405     //\r
406     // remove EFI_UNSUPPORTED check above when complete.\r
407     ///@todo add support for Framework HII\r
408 \r
409     //\r
410     // install our (solitary) HII package\r
411     //\r
412     ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);\r
413     if (ShellInfoObject.HiiHandle == NULL) {\r
414       if (PcdGetBool(PcdShellSupportFrameworkHii)) {\r
415         ///@todo Add our package into Framework HII\r
416       }\r
417       if (ShellInfoObject.HiiHandle == NULL) {\r
418         Status = EFI_NOT_STARTED;\r
419         goto FreeResources;\r
420       }\r
421     }\r
422 \r
423     //\r
424     // create and install the EfiShellParametersProtocol\r
425     //\r
426     Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);\r
427     ASSERT_EFI_ERROR(Status);\r
428     ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);\r
429 \r
430     //\r
431     // create and install the EfiShellProtocol\r
432     //\r
433     Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);\r
434     ASSERT_EFI_ERROR(Status);\r
435     ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);\r
436 \r
437     //\r
438     // Now initialize the shell library (it requires Shell Parameters protocol)\r
439     //\r
440     Status = ShellInitialize();\r
441     ASSERT_EFI_ERROR(Status);\r
442 \r
443     Status = CommandInit();\r
444     ASSERT_EFI_ERROR(Status);\r
445 \r
446     Status = ShellInitEnvVarList ();\r
447 \r
448     //\r
449     // Check the command line\r
450     //\r
451     Status = ProcessCommandLine ();\r
452     if (EFI_ERROR (Status)) {\r
453       goto FreeResources;\r
454     }\r
455 \r
456     //\r
457     // If shell support level is >= 1 create the mappings and paths\r
458     //\r
459     if (PcdGet8(PcdShellSupportLevel) >= 1) {\r
460       Status = ShellCommandCreateInitialMappingsAndPaths();\r
461     }\r
462 \r
463     //\r
464     // Set the environment variable for nesting support\r
465     //\r
466     Size = 0;\r
467     TempString = NULL;\r
468     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {\r
469       //\r
470       // No change.  require nesting in Shell Protocol Execute()\r
471       //\r
472       StrnCatGrow(&TempString,\r
473                   &Size,\r
474                   L"False",\r
475                   0);\r
476     } else {\r
477       StrnCatGrow(&TempString,\r
478                   &Size,\r
479                   mNoNestingTrue,\r
480                   0);\r
481     }\r
482     Status = InternalEfiShellSetEnv(mNoNestingEnvVarName, TempString, TRUE);\r
483     SHELL_FREE_NON_NULL(TempString);\r
484     Size = 0;\r
485 \r
486     //\r
487     // save the device path for the loaded image and the device path for the filepath (under loaded image)\r
488     // These are where to look for the startup.nsh file\r
489     //\r
490     Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);\r
491     ASSERT_EFI_ERROR(Status);\r
492 \r
493     //\r
494     // Display the version\r
495     //\r
496     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {\r
497       ShellPrintHiiEx (\r
498         0,\r
499         gST->ConOut->Mode->CursorRow,\r
500         NULL,\r
501         STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),\r
502         ShellInfoObject.HiiHandle,\r
503         SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
504         gEfiShellProtocol->MajorVersion,\r
505         gEfiShellProtocol->MinorVersion\r
506        );\r
507 \r
508       ShellPrintHiiEx (\r
509         -1,\r
510         -1,\r
511         NULL,\r
512         STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),\r
513         ShellInfoObject.HiiHandle,\r
514         (CHAR16 *) PcdGetPtr (PcdShellSupplier)\r
515        );\r
516 \r
517       ShellPrintHiiEx (\r
518         -1,\r
519         -1,\r
520         NULL,\r
521         STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),\r
522         ShellInfoObject.HiiHandle,\r
523         (gST->Hdr.Revision&0xffff0000)>>16,\r
524         (gST->Hdr.Revision&0x0000ffff),\r
525         gST->FirmwareVendor,\r
526         gST->FirmwareRevision\r
527        );\r
528     }\r
529 \r
530     //\r
531     // Display the mapping\r
532     //\r
533     if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {\r
534       Status = RunCommand(L"map");\r
535       ASSERT_EFI_ERROR(Status);\r
536     }\r
537 \r
538     //\r
539     // init all the built in alias'\r
540     //\r
541     Status = SetBuiltInAlias();\r
542     ASSERT_EFI_ERROR(Status);\r
543 \r
544     //\r
545     // Initialize environment variables\r
546     //\r
547     if (ShellCommandGetProfileList() != NULL) {\r
548       Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);\r
549       ASSERT_EFI_ERROR(Status);\r
550     }\r
551 \r
552     Size        = 100;\r
553     TempString  = AllocateZeroPool(Size);\r
554 \r
555     UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));\r
556     Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);\r
557     ASSERT_EFI_ERROR(Status);\r
558 \r
559     UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);\r
560     Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);\r
561     ASSERT_EFI_ERROR(Status);\r
562 \r
563     UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);\r
564     Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);\r
565     ASSERT_EFI_ERROR(Status);\r
566 \r
567     FreePool(TempString);\r
568 \r
569     if (!EFI_ERROR(Status)) {\r
570       if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
571         //\r
572         // Set up the event for CTRL-C monitoring...\r
573         //\r
574         Status = InernalEfiShellStartMonitor();\r
575       }\r
576 \r
577       if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
578         //\r
579         // Set up the event for CTRL-S monitoring...\r
580         //\r
581         Status = InternalEfiShellStartCtrlSMonitor();\r
582       }\r
583 \r
584       if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
585         //\r
586         // close off the gST->ConIn\r
587         //\r
588         OldConIn      = gST->ConIn;\r
589         ConInHandle   = gST->ConsoleInHandle;\r
590         gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);\r
591       } else {\r
592         OldConIn      = NULL;\r
593         ConInHandle   = NULL;\r
594       }\r
595 \r
596       if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {\r
597         //\r
598         // process the startup script or launch the called app.\r
599         //\r
600         Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
601       }\r
602 \r
603       if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
604         //\r
605         // begin the UI waiting loop\r
606         //\r
607         do {\r
608           //\r
609           // clean out all the memory allocated for CONST <something> * return values\r
610           // between each shell prompt presentation\r
611           //\r
612           if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
613             FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
614           }\r
615 \r
616           //\r
617           // Reset page break back to default.\r
618           //\r
619           ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);\r
620           ASSERT (ShellInfoObject.ConsoleInfo != NULL);\r
621           ShellInfoObject.ConsoleInfo->Enabled    = TRUE;\r
622           ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
623 \r
624           //\r
625           // Display Prompt\r
626           //\r
627           Status = DoShellPrompt();\r
628         } while (!ShellCommandGetExit());\r
629       }\r
630       if (OldConIn != NULL && ConInHandle != NULL) {\r
631         CloseSimpleTextInOnFile (gST->ConIn);\r
632         gST->ConIn            = OldConIn;\r
633         gST->ConsoleInHandle  = ConInHandle;\r
634       }\r
635     }\r
636   }\r
637 \r
638 FreeResources:\r
639   //\r
640   // uninstall protocols / free memory / etc...\r
641   //\r
642   if (ShellInfoObject.UserBreakTimer != NULL) {\r
643     gBS->CloseEvent(ShellInfoObject.UserBreakTimer);\r
644     DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);\r
645   }\r
646   if (ShellInfoObject.ImageDevPath != NULL) {\r
647     FreePool(ShellInfoObject.ImageDevPath);\r
648     DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);\r
649   }\r
650   if (ShellInfoObject.FileDevPath != NULL) {\r
651     FreePool(ShellInfoObject.FileDevPath);\r
652     DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);\r
653   }\r
654   if (ShellInfoObject.NewShellParametersProtocol != NULL) {\r
655     CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);\r
656     DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);\r
657   }\r
658   if (ShellInfoObject.NewEfiShellProtocol != NULL){\r
659     if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
660       InternalEfiShellSetEnv(L"cwd", NULL, TRUE);\r
661     }\r
662     CleanUpShellEnvironment (ShellInfoObject.NewEfiShellProtocol);\r
663     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
664   }\r
665 \r
666   if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){\r
667     FreeBufferList(&ShellInfoObject.BufferToFreeList);\r
668   }\r
669 \r
670   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){\r
671     ASSERT(FALSE); ///@todo finish this de-allocation (free SplitStdIn/Out when needed).\r
672 \r
673     for ( Split = (SPLIT_LIST*)GetFirstNode (&ShellInfoObject.SplitList.Link)\r
674         ; !IsNull (&ShellInfoObject.SplitList.Link, &Split->Link)\r
675         ; Split = (SPLIT_LIST *)GetNextNode (&ShellInfoObject.SplitList.Link, &Split->Link)\r
676      ) {\r
677       RemoveEntryList (&Split->Link);\r
678       FreePool (Split);\r
679     }\r
680 \r
681     DEBUG_CODE (InitializeListHead (&ShellInfoObject.SplitList.Link););\r
682   }\r
683 \r
684   if (ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
685     FreePool(ShellInfoObject.ShellInitSettings.FileName);\r
686     DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);\r
687   }\r
688 \r
689   if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
690     FreePool(ShellInfoObject.ShellInitSettings.FileOptions);\r
691     DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);\r
692   }\r
693 \r
694   if (ShellInfoObject.HiiHandle != NULL) {\r
695     HiiRemovePackages(ShellInfoObject.HiiHandle);\r
696     DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);\r
697   }\r
698 \r
699   if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){\r
700     FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);\r
701   }\r
702 \r
703   ASSERT(ShellInfoObject.ConsoleInfo != NULL);\r
704   if (ShellInfoObject.ConsoleInfo != NULL) {\r
705     ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);\r
706     FreePool(ShellInfoObject.ConsoleInfo);\r
707     DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);\r
708   }\r
709 \r
710   ShellFreeEnvVarList ();\r
711 \r
712   if (ShellCommandGetExit()) {\r
713     return ((EFI_STATUS)ShellCommandGetExitCode());\r
714   }\r
715   return (Status);\r
716 }\r
717 \r
718 /**\r
719   Sets all the alias' that were registered with the ShellCommandLib library.\r
720 \r
721   @retval EFI_SUCCESS           all init commands were run successfully.\r
722 **/\r
723 EFI_STATUS\r
724 SetBuiltInAlias(\r
725   VOID\r
726   )\r
727 {\r
728   EFI_STATUS          Status;\r
729   CONST ALIAS_LIST    *List;\r
730   ALIAS_LIST          *Node;\r
731 \r
732   //\r
733   // Get all the commands we want to run\r
734   //\r
735   List = ShellCommandGetInitAliasList();\r
736 \r
737   //\r
738   // for each command in the List\r
739   //\r
740   for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)\r
741       ; !IsNull (&List->Link, &Node->Link)\r
742       ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)\r
743    ){\r
744     //\r
745     // install the alias'\r
746     //\r
747     Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);\r
748     ASSERT_EFI_ERROR(Status);\r
749   }\r
750   return (EFI_SUCCESS);\r
751 }\r
752 \r
753 /**\r
754   Internal function to determine if 2 command names are really the same.\r
755 \r
756   @param[in] Command1       The pointer to the first command name.\r
757   @param[in] Command2       The pointer to the second command name.\r
758 \r
759   @retval TRUE              The 2 command names are the same.\r
760   @retval FALSE             The 2 command names are not the same.\r
761 **/\r
762 BOOLEAN\r
763 IsCommand(\r
764   IN CONST CHAR16 *Command1,\r
765   IN CONST CHAR16 *Command2\r
766   )\r
767 {\r
768   if (StringNoCaseCompare(&Command1, &Command2) == 0) {\r
769     return (TRUE);\r
770   }\r
771   return (FALSE);\r
772 }\r
773 \r
774 /**\r
775   Internal function to determine if a command is a script only command.\r
776 \r
777   @param[in] CommandName    The pointer to the command name.\r
778 \r
779   @retval TRUE              The command is a script only command.\r
780   @retval FALSE             The command is not a script only command.\r
781 **/\r
782 BOOLEAN\r
783 IsScriptOnlyCommand(\r
784   IN CONST CHAR16 *CommandName\r
785   )\r
786 {\r
787   if (IsCommand(CommandName, L"for")\r
788     ||IsCommand(CommandName, L"endfor")\r
789     ||IsCommand(CommandName, L"if")\r
790     ||IsCommand(CommandName, L"else")\r
791     ||IsCommand(CommandName, L"endif")\r
792     ||IsCommand(CommandName, L"goto")) {\r
793     return (TRUE);\r
794   }\r
795   return (FALSE);\r
796 }\r
797 \r
798 /**\r
799   This function will populate the 2 device path protocol parameters based on the\r
800   global gImageHandle.  The DevPath will point to the device path for the handle that has\r
801   loaded image protocol installed on it.  The FilePath will point to the device path\r
802   for the file that was loaded.\r
803 \r
804   @param[in, out] DevPath       On a successful return the device path to the loaded image.\r
805   @param[in, out] FilePath      On a successful return the device path to the file.\r
806 \r
807   @retval EFI_SUCCESS           The 2 device paths were successfully returned.\r
808   @retval other                 A error from gBS->HandleProtocol.\r
809 \r
810   @sa HandleProtocol\r
811 **/\r
812 EFI_STATUS\r
813 GetDevicePathsForImageAndFile (\r
814   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,\r
815   IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath\r
816   )\r
817 {\r
818   EFI_STATUS                Status;\r
819   EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
820   EFI_DEVICE_PATH_PROTOCOL  *ImageDevicePath;\r
821 \r
822   ASSERT(DevPath  != NULL);\r
823   ASSERT(FilePath != NULL);\r
824 \r
825   Status = gBS->OpenProtocol (\r
826                 gImageHandle,\r
827                 &gEfiLoadedImageProtocolGuid,\r
828                 (VOID**)&LoadedImage,\r
829                 gImageHandle,\r
830                 NULL,\r
831                 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
832                );\r
833   if (!EFI_ERROR (Status)) {\r
834     Status = gBS->OpenProtocol (\r
835                   LoadedImage->DeviceHandle,\r
836                   &gEfiDevicePathProtocolGuid,\r
837                   (VOID**)&ImageDevicePath,\r
838                   gImageHandle,\r
839                   NULL,\r
840                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
841                  );\r
842     if (!EFI_ERROR (Status)) {\r
843       *DevPath  = DuplicateDevicePath (ImageDevicePath);\r
844       *FilePath = DuplicateDevicePath (LoadedImage->FilePath);\r
845       gBS->CloseProtocol(\r
846                   LoadedImage->DeviceHandle,\r
847                   &gEfiDevicePathProtocolGuid,\r
848                   gImageHandle,\r
849                   NULL);\r
850     }\r
851     gBS->CloseProtocol(\r
852                 gImageHandle,\r
853                 &gEfiLoadedImageProtocolGuid,\r
854                 gImageHandle,\r
855                 NULL);\r
856   }\r
857   return (Status);\r
858 }\r
859 \r
860 /**\r
861   Process all Uefi Shell 2.0 command line options.\r
862 \r
863   see Uefi Shell 2.0 section 3.2 for full details.\r
864 \r
865   the command line must resemble the following:\r
866 \r
867   shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]\r
868 \r
869   ShellOpt-options  Options which control the initialization behavior of the shell.\r
870                     These options are read from the EFI global variable "ShellOpt"\r
871                     and are processed before options or file-name.\r
872 \r
873   options           Options which control the initialization behavior of the shell.\r
874 \r
875   file-name         The name of a UEFI shell application or script to be executed\r
876                     after initialization is complete. By default, if file-name is\r
877                     specified, then -nostartup is implied. Scripts are not supported\r
878                     by level 0.\r
879 \r
880   file-name-options The command-line options that are passed to file-name when it\r
881                     is invoked.\r
882 \r
883   This will initialize the ShellInfoObject.ShellInitSettings global variable.\r
884 \r
885   @retval EFI_SUCCESS           The variable is initialized.\r
886 **/\r
887 EFI_STATUS\r
888 ProcessCommandLine(\r
889   VOID\r
890   )\r
891 {\r
892   UINTN                           Size;\r
893   UINTN                           LoopVar;\r
894   CHAR16                          *CurrentArg;\r
895   CHAR16                          *DelayValueStr;\r
896   UINT64                          DelayValue;\r
897   EFI_STATUS                      Status;\r
898   EFI_UNICODE_COLLATION_PROTOCOL  *UnicodeCollation;\r
899 \r
900   // `file-name-options` will contain arguments to `file-name` that we don't\r
901   // know about. This would cause ShellCommandLineParse to error, so we parse\r
902   // arguments manually, ignoring those after the first thing that doesn't look\r
903   // like a shell option (which is assumed to be `file-name`).\r
904 \r
905   Status = gBS->LocateProtocol (\r
906                   &gEfiUnicodeCollation2ProtocolGuid,\r
907                   NULL,\r
908                   (VOID **) &UnicodeCollation\r
909                   );\r
910   if (EFI_ERROR (Status)) {\r
911     Status = gBS->LocateProtocol (\r
912                     &gEfiUnicodeCollationProtocolGuid,\r
913                     NULL,\r
914                     (VOID **) &UnicodeCollation\r
915                     );\r
916     if (EFI_ERROR (Status)) {\r
917       return Status;\r
918     }\r
919   }\r
920 \r
921   // Set default options\r
922   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = FALSE;\r
923   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = FALSE;\r
924   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;\r
925   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = FALSE;\r
926   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = FALSE;\r
927   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = FALSE;\r
928   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = FALSE;\r
929   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = FALSE;\r
930   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = FALSE;\r
931   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest       = FALSE;\r
932   ShellInfoObject.ShellInitSettings.Delay = 5;\r
933 \r
934   //\r
935   // Start LoopVar at 0 to parse only optional arguments at Argv[0]\r
936   // and parse other parameters from Argv[1].  This is for use case that\r
937   // UEFI Shell boot option is created, and OptionalData is provided\r
938   // that starts with shell command-line options.\r
939   //\r
940   for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
941     CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];\r
942     if (UnicodeCollation->StriColl (\r
943                             UnicodeCollation,\r
944                             L"-startup",\r
945                             CurrentArg\r
946                             ) == 0) {\r
947       ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = TRUE;\r
948     }\r
949     else if (UnicodeCollation->StriColl (\r
950                                  UnicodeCollation,\r
951                                  L"-nostartup",\r
952                                  CurrentArg\r
953                                  ) == 0) {\r
954       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = TRUE;\r
955     }\r
956     else if (UnicodeCollation->StriColl (\r
957                                  UnicodeCollation,\r
958                                  L"-noconsoleout",\r
959                                  CurrentArg\r
960                                  ) == 0) {\r
961       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;\r
962     }\r
963     else if (UnicodeCollation->StriColl (\r
964                                  UnicodeCollation,\r
965                                  L"-noconsolein",\r
966                                  CurrentArg\r
967                                  ) == 0) {\r
968       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = TRUE;\r
969     }\r
970     else if (UnicodeCollation->StriColl (\r
971                                  UnicodeCollation,\r
972                                  L"-nointerrupt",\r
973                                  CurrentArg\r
974                                  ) == 0) {\r
975       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = TRUE;\r
976     }\r
977     else if (UnicodeCollation->StriColl (\r
978                                  UnicodeCollation,\r
979                                  L"-nomap",\r
980                                  CurrentArg\r
981                                  ) == 0) {\r
982       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = TRUE;\r
983     }\r
984     else if (UnicodeCollation->StriColl (\r
985                                  UnicodeCollation,\r
986                                  L"-noversion",\r
987                                  CurrentArg\r
988                                  ) == 0) {\r
989       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = TRUE;\r
990     }\r
991     else if (UnicodeCollation->StriColl (\r
992                                  UnicodeCollation,\r
993                                  L"-nonest",\r
994                                  CurrentArg\r
995                                  ) == 0) {\r
996       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest       = TRUE;\r
997     }\r
998     else if (UnicodeCollation->StriColl (\r
999                                  UnicodeCollation,\r
1000                                  L"-delay",\r
1001                                  CurrentArg\r
1002                                  ) == 0) {\r
1003       ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = TRUE;\r
1004       // Check for optional delay value following "-delay"\r
1005       if ((LoopVar + 1) >= gEfiShellParametersProtocol->Argc) {\r
1006         DelayValueStr = NULL;\r
1007       } else {\r
1008         DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];\r
1009       }\r
1010       if (DelayValueStr != NULL){\r
1011         if (*DelayValueStr == L':') {\r
1012           DelayValueStr++;\r
1013         }\r
1014         if (!EFI_ERROR(ShellConvertStringToUint64 (\r
1015                         DelayValueStr,\r
1016                         &DelayValue,\r
1017                         FALSE,\r
1018                         FALSE\r
1019                         ))) {\r
1020           ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;\r
1021           LoopVar++;\r
1022         }\r
1023       }\r
1024     } else if (UnicodeCollation->StriColl (\r
1025                                    UnicodeCollation,\r
1026                                    L"-exit",\r
1027                                    CurrentArg\r
1028                                    ) == 0) {\r
1029       ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = TRUE;\r
1030     } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {\r
1031       // Unrecognized option\r
1032       ShellPrintHiiEx(-1, -1, NULL,\r
1033         STRING_TOKEN (STR_GEN_PROBLEM),\r
1034         ShellInfoObject.HiiHandle,\r
1035         CurrentArg\r
1036         );\r
1037       return EFI_INVALID_PARAMETER;\r
1038     } else {\r
1039       //\r
1040       // First argument should be Shell.efi image name\r
1041       //\r
1042       if (LoopVar == 0) {\r
1043         continue;\r
1044       }\r
1045 \r
1046       ShellInfoObject.ShellInitSettings.FileName = NULL;\r
1047       Size = 0;\r
1048       //\r
1049       // If first argument contains a space, then add double quotes before the argument\r
1050       //\r
1051       if (StrStr (CurrentArg, L" ") != NULL) {\r
1052         StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);\r
1053         if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1054           return (EFI_OUT_OF_RESOURCES);\r
1055         }\r
1056       }\r
1057       StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileName, &Size, CurrentArg, 0);\r
1058       if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1059         return (EFI_OUT_OF_RESOURCES);\r
1060       }\r
1061       //\r
1062       // If first argument contains a space, then add double quotes after the argument\r
1063       //\r
1064       if (StrStr (CurrentArg, L" ") != NULL) {\r
1065         StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileName, &Size, L"\"", 0);\r
1066         if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
1067           return (EFI_OUT_OF_RESOURCES);\r
1068         }\r
1069       }\r
1070       //\r
1071       // We found `file-name`.\r
1072       //\r
1073       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;\r
1074       LoopVar++;\r
1075 \r
1076       // Add `file-name-options`\r
1077       for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
1078         ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
1079         //\r
1080         // Add a space between arguments\r
1081         //\r
1082         if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
1083           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions, &Size, L" ", 0);\r
1084           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
1085             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
1086             return (EFI_OUT_OF_RESOURCES);\r
1087           }\r
1088         }\r
1089         //\r
1090         // If an argumnent contains a space, then add double quotes before the argument\r
1091         //\r
1092         if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {\r
1093           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
1094                       &Size,\r
1095                       L"\"",\r
1096                       0);\r
1097           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
1098             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
1099             return (EFI_OUT_OF_RESOURCES);\r
1100           }\r
1101         }\r
1102         StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
1103                     &Size,\r
1104                     gEfiShellParametersProtocol->Argv[LoopVar],\r
1105                     0);\r
1106         if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
1107           SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
1108           return (EFI_OUT_OF_RESOURCES);\r
1109         }\r
1110         //\r
1111         // If an argumnent contains a space, then add double quotes after the argument\r
1112         //\r
1113         if (StrStr (gEfiShellParametersProtocol->Argv[LoopVar], L" ") != NULL) {\r
1114           StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
1115                       &Size,\r
1116                       L"\"",\r
1117                       0);\r
1118           if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {\r
1119             SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);\r
1120             return (EFI_OUT_OF_RESOURCES);\r
1121           }\r
1122         }\r
1123       }\r
1124     }\r
1125   }\r
1126 \r
1127   // "-nointerrupt" overrides "-delay"\r
1128   if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {\r
1129     ShellInfoObject.ShellInitSettings.Delay = 0;\r
1130   }\r
1131 \r
1132   return EFI_SUCCESS;\r
1133 }\r
1134 \r
1135 /**\r
1136   Function try to find location of the Startup.nsh file.\r
1137 \r
1138   The buffer is callee allocated and should be freed by the caller.\r
1139 \r
1140   @param    ImageDevicePath       The path to the image for shell.  first place to look for the startup script\r
1141   @param    FileDevicePath        The path to the file for shell.  second place to look for the startup script.\r
1142 \r
1143   @retval   NULL                  No Startup.nsh file was found.\r
1144   @return   !=NULL                Pointer to NULL-terminated path.\r
1145 **/\r
1146 CHAR16 *\r
1147 LocateStartupScript (\r
1148   IN EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,\r
1149   IN EFI_DEVICE_PATH_PROTOCOL *FileDevicePath\r
1150   )\r
1151 {\r
1152   CHAR16          *StartupScriptPath;\r
1153   CHAR16          *TempSpot;\r
1154   CONST CHAR16    *MapName;\r
1155   UINTN           Size;\r
1156 \r
1157   StartupScriptPath = NULL;\r
1158   Size              = 0;\r
1159 \r
1160   //\r
1161   // Try to find 'Startup.nsh' in the directory where the shell itself was launched.\r
1162   //\r
1163   MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath (&ImageDevicePath);\r
1164   if (MapName != NULL) {\r
1165     StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, MapName, 0);\r
1166     if (StartupScriptPath == NULL) {\r
1167       //\r
1168       // Do not locate the startup script in sys path when out of resource.\r
1169       //\r
1170       return NULL;\r
1171     }\r
1172     TempSpot = StrStr (StartupScriptPath, L";");\r
1173     if (TempSpot != NULL) {\r
1174       *TempSpot = CHAR_NULL;\r
1175     }\r
1176 \r
1177     InternalEfiShellSetEnv(L"homefilesystem", StartupScriptPath, TRUE);\r
1178 \r
1179     StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, ((FILEPATH_DEVICE_PATH *)FileDevicePath)->PathName, 0);\r
1180     PathRemoveLastItem (StartupScriptPath);\r
1181     StartupScriptPath = StrnCatGrow (&StartupScriptPath, &Size, mStartupScript, 0);\r
1182   }\r
1183 \r
1184   //\r
1185   // Try to find 'Startup.nsh' in the execution path defined by the envrionment variable PATH.\r
1186   //\r
1187   if ((StartupScriptPath == NULL) || EFI_ERROR (ShellIsFile (StartupScriptPath))) {\r
1188     SHELL_FREE_NON_NULL (StartupScriptPath);\r
1189     StartupScriptPath = ShellFindFilePath (mStartupScript);\r
1190   }\r
1191 \r
1192   return StartupScriptPath;\r
1193 }\r
1194 \r
1195 /**\r
1196   Handles all interaction with the default startup script.\r
1197 \r
1198   this will check that the correct command line parameters were passed, handle the delay, and then start running the script.\r
1199 \r
1200   @param ImagePath              the path to the image for shell.  first place to look for the startup script\r
1201   @param FilePath               the path to the file for shell.  second place to look for the startup script.\r
1202 \r
1203   @retval EFI_SUCCESS           the variable is initialized.\r
1204 **/\r
1205 EFI_STATUS\r
1206 DoStartupScript(\r
1207   IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,\r
1208   IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
1209   )\r
1210 {\r
1211   EFI_STATUS                    Status;\r
1212   EFI_STATUS                    CalleeStatus;\r
1213   UINTN                         Delay;\r
1214   EFI_INPUT_KEY                 Key;\r
1215   CHAR16                        *FileStringPath;\r
1216   CHAR16                        *FullFileStringPath;\r
1217   UINTN                         NewSize;\r
1218 \r
1219   Key.UnicodeChar = CHAR_NULL;\r
1220   Key.ScanCode    = 0;\r
1221 \r
1222   if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {\r
1223     //\r
1224     // launch something else instead\r
1225     //\r
1226     NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);\r
1227     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
1228       NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);\r
1229     }\r
1230     FileStringPath = AllocateZeroPool(NewSize);\r
1231     if (FileStringPath == NULL) {\r
1232       return (EFI_OUT_OF_RESOURCES);\r
1233     }\r
1234     StrCpyS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileName);\r
1235     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {\r
1236       StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
1237       StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);\r
1238     }\r
1239     Status = RunShellCommand(FileStringPath, &CalleeStatus);\r
1240     if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {\r
1241       ShellCommandRegisterExit(gEfiShellProtocol->BatchIsActive(), (UINT64)CalleeStatus);\r
1242     }\r
1243     FreePool(FileStringPath);\r
1244     return (Status);\r
1245 \r
1246   }\r
1247 \r
1248   //\r
1249   // for shell level 0 we do no scripts\r
1250   // Without the Startup bit overriding we allow for nostartup to prevent scripts\r
1251   //\r
1252   if ( (PcdGet8(PcdShellSupportLevel) < 1)\r
1253     || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)\r
1254    ){\r
1255     return (EFI_SUCCESS);\r
1256   }\r
1257 \r
1258   gST->ConOut->EnableCursor(gST->ConOut, FALSE);\r
1259   //\r
1260   // print out our warning and see if they press a key\r
1261   //\r
1262   for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay\r
1263       ; Delay != 0 && EFI_ERROR(Status)\r
1264       ; Delay--\r
1265      ){\r
1266     ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);\r
1267     gBS->Stall (1000000);\r
1268     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
1269       Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
1270     }\r
1271   }\r
1272   ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);\r
1273   gST->ConOut->EnableCursor(gST->ConOut, TRUE);\r
1274 \r
1275   //\r
1276   // ESC was pressed\r
1277   //\r
1278   if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {\r
1279     return (EFI_SUCCESS);\r
1280   }\r
1281 \r
1282   FileStringPath = LocateStartupScript (ImagePath, FilePath);\r
1283   if (FileStringPath != NULL) {\r
1284     FullFileStringPath = FullyQualifyPath(FileStringPath);\r
1285     if (FullFileStringPath == NULL) {\r
1286       Status = RunScriptFile (FileStringPath, NULL, FileStringPath, ShellInfoObject.NewShellParametersProtocol);\r
1287     } else {\r
1288       Status = RunScriptFile (FullFileStringPath, NULL, FullFileStringPath, ShellInfoObject.NewShellParametersProtocol);\r
1289       FreePool(FullFileStringPath);\r
1290     }\r
1291     FreePool (FileStringPath);\r
1292   } else {\r
1293     //\r
1294     // we return success since startup script is not mandatory.\r
1295     //\r
1296     Status = EFI_SUCCESS;\r
1297   }\r
1298 \r
1299   return (Status);\r
1300 }\r
1301 \r
1302 /**\r
1303   Function to perform the shell prompt looping.  It will do a single prompt,\r
1304   dispatch the result, and then return.  It is expected that the caller will\r
1305   call this function in a loop many times.\r
1306 \r
1307   @retval EFI_SUCCESS\r
1308   @retval RETURN_ABORTED\r
1309 **/\r
1310 EFI_STATUS\r
1311 DoShellPrompt (\r
1312   VOID\r
1313   )\r
1314 {\r
1315   UINTN         Column;\r
1316   UINTN         Row;\r
1317   CHAR16        *CmdLine;\r
1318   CONST CHAR16  *CurDir;\r
1319   UINTN         BufferSize;\r
1320   EFI_STATUS    Status;\r
1321   LIST_ENTRY    OldBufferList;\r
1322 \r
1323   CurDir  = NULL;\r
1324 \r
1325   //\r
1326   // Get screen setting to decide size of the command line buffer\r
1327   //\r
1328   gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);\r
1329   BufferSize  = Column * Row * sizeof (CHAR16);\r
1330   CmdLine     = AllocateZeroPool (BufferSize);\r
1331   if (CmdLine == NULL) {\r
1332     return EFI_OUT_OF_RESOURCES;\r
1333   }\r
1334 \r
1335   SaveBufferList(&OldBufferList);\r
1336   CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
1337 \r
1338   //\r
1339   // Prompt for input\r
1340   //\r
1341   gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);\r
1342 \r
1343   if (CurDir != NULL && StrLen(CurDir) > 1) {\r
1344     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
1345   } else {\r
1346     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
1347   }\r
1348 \r
1349   //\r
1350   // Read a line from the console\r
1351   //\r
1352   Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);\r
1353 \r
1354   //\r
1355   // Null terminate the string and parse it\r
1356   //\r
1357   if (!EFI_ERROR (Status)) {\r
1358     //\r
1359     // Reset the CTRL-C event just before running the command (yes we ignore the return values)\r
1360     //\r
1361     Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);\r
1362 \r
1363     CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;\r
1364     Status = RunCommand(CmdLine);\r
1365   }\r
1366 \r
1367   //\r
1368   // Done with this command\r
1369   //\r
1370   RestoreBufferList(&OldBufferList);\r
1371   FreePool (CmdLine);\r
1372   return Status;\r
1373 }\r
1374 \r
1375 /**\r
1376   Add a buffer to the Buffer To Free List for safely returning buffers to other\r
1377   places without risking letting them modify internal shell information.\r
1378 \r
1379   @param Buffer   Something to pass to FreePool when the shell is exiting.\r
1380 **/\r
1381 VOID*\r
1382 AddBufferToFreeList (\r
1383   VOID *Buffer\r
1384   )\r
1385 {\r
1386   BUFFER_LIST   *BufferListEntry;\r
1387 \r
1388   if (Buffer == NULL) {\r
1389     return (NULL);\r
1390   }\r
1391 \r
1392   BufferListEntry = AllocateZeroPool (sizeof (BUFFER_LIST));\r
1393   if (BufferListEntry == NULL) {\r
1394     return NULL;\r
1395   }\r
1396 \r
1397   BufferListEntry->Buffer = Buffer;\r
1398   InsertTailList (&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);\r
1399   return (Buffer);\r
1400 }\r
1401 \r
1402 \r
1403 /**\r
1404   Create a new buffer list and stores the old one to OldBufferList\r
1405 \r
1406   @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.\r
1407 **/\r
1408 VOID\r
1409 SaveBufferList (\r
1410   OUT LIST_ENTRY     *OldBufferList\r
1411   )\r
1412 {\r
1413   CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));\r
1414   InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);\r
1415 }\r
1416 \r
1417 /**\r
1418   Restore previous nodes into BufferToFreeList .\r
1419 \r
1420   @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.\r
1421 **/\r
1422 VOID\r
1423 RestoreBufferList (\r
1424   IN OUT LIST_ENTRY     *OldBufferList\r
1425   )\r
1426 {\r
1427   FreeBufferList (&ShellInfoObject.BufferToFreeList);\r
1428   CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));\r
1429 }\r
1430 \r
1431 \r
1432 /**\r
1433   Add a buffer to the Line History List\r
1434 \r
1435   @param Buffer     The line buffer to add.\r
1436 **/\r
1437 VOID\r
1438 AddLineToCommandHistory(\r
1439   IN CONST CHAR16 *Buffer\r
1440   )\r
1441 {\r
1442   BUFFER_LIST *Node;\r
1443   BUFFER_LIST *Walker;\r
1444   UINT16       MaxHistoryCmdCount;\r
1445   UINT16       Count;\r
1446 \r
1447   Count = 0;\r
1448   MaxHistoryCmdCount = PcdGet16(PcdShellMaxHistoryCommandCount);\r
1449 \r
1450   if (MaxHistoryCmdCount == 0) {\r
1451     return ;\r
1452   }\r
1453 \r
1454 \r
1455   Node = AllocateZeroPool(sizeof(BUFFER_LIST));\r
1456   if (Node == NULL) {\r
1457     return;\r
1458   }\r
1459 \r
1460   Node->Buffer = AllocateCopyPool (StrSize (Buffer), Buffer);\r
1461   if (Node->Buffer == NULL) {\r
1462     FreePool (Node);\r
1463     return;\r
1464   }\r
1465 \r
1466   for ( Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)\r
1467       ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
1468       ; Walker = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)\r
1469    ){\r
1470     Count++;\r
1471   }\r
1472   if (Count < MaxHistoryCmdCount){\r
1473     InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1474   } else {\r
1475     Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link);\r
1476     RemoveEntryList(&Walker->Link);\r
1477     if (Walker->Buffer != NULL) {\r
1478       FreePool(Walker->Buffer);\r
1479     }\r
1480     FreePool(Walker);\r
1481     InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);\r
1482   }\r
1483 }\r
1484 \r
1485 /**\r
1486   Checks if a string is an alias for another command.  If yes, then it replaces the alias name\r
1487   with the correct command name.\r
1488 \r
1489   @param[in, out] CommandString    Upon entry the potential alias.  Upon return the\r
1490                                    command name if it was an alias.  If it was not\r
1491                                    an alias it will be unchanged.  This function may\r
1492                                    change the buffer to fit the command name.\r
1493 \r
1494   @retval EFI_SUCCESS             The name was changed.\r
1495   @retval EFI_SUCCESS             The name was not an alias.\r
1496   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.\r
1497 **/\r
1498 EFI_STATUS\r
1499 ShellConvertAlias(\r
1500   IN OUT CHAR16 **CommandString\r
1501   )\r
1502 {\r
1503   CONST CHAR16  *NewString;\r
1504 \r
1505   NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);\r
1506   if (NewString == NULL) {\r
1507     return (EFI_SUCCESS);\r
1508   }\r
1509   FreePool(*CommandString);\r
1510   *CommandString = AllocateCopyPool(StrSize(NewString), NewString);\r
1511   if (*CommandString == NULL) {\r
1512     return (EFI_OUT_OF_RESOURCES);\r
1513   }\r
1514   return (EFI_SUCCESS);\r
1515 }\r
1516 \r
1517 /**\r
1518   This function will eliminate unreplaced (and therefore non-found) environment variables.\r
1519 \r
1520   @param[in,out] CmdLine   The command line to update.\r
1521 **/\r
1522 EFI_STATUS\r
1523 StripUnreplacedEnvironmentVariables(\r
1524   IN OUT CHAR16 *CmdLine\r
1525   )\r
1526 {\r
1527   CHAR16 *FirstPercent;\r
1528   CHAR16 *FirstQuote;\r
1529   CHAR16 *SecondPercent;\r
1530   CHAR16 *SecondQuote;\r
1531   CHAR16 *CurrentLocator;\r
1532 \r
1533   for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {\r
1534     FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);\r
1535     FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);\r
1536     SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;\r
1537     if (FirstPercent == NULL || SecondPercent == NULL) {\r
1538       //\r
1539       // If we ever don't have 2 % we are done.\r
1540       //\r
1541       break;\r
1542     }\r
1543 \r
1544     if (FirstQuote!= NULL && FirstQuote < FirstPercent) {\r
1545       SecondQuote = FindNextInstance(FirstQuote+1, L"\"", TRUE);\r
1546       //\r
1547       // Quote is first found\r
1548       //\r
1549 \r
1550       if (SecondQuote < FirstPercent) {\r
1551         //\r
1552         // restart after the pair of "\r
1553         //\r
1554         CurrentLocator = SecondQuote + 1;\r
1555       } else /* FirstPercent < SecondQuote */{\r
1556         //\r
1557         // Restart on the first percent\r
1558         //\r
1559         CurrentLocator = FirstPercent;\r
1560       }\r
1561       continue;\r
1562     }\r
1563 \r
1564     if (FirstQuote == NULL || SecondPercent < FirstQuote) {\r
1565       if (IsValidEnvironmentVariableName(FirstPercent, SecondPercent)) {\r
1566         //\r
1567         // We need to remove from FirstPercent to SecondPercent\r
1568         //\r
1569         CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));\r
1570         //\r
1571         // don't need to update the locator.  both % characters are gone.\r
1572         //\r
1573       } else {\r
1574         CurrentLocator = SecondPercent + 1;\r
1575       }\r
1576       continue;\r
1577     }\r
1578     CurrentLocator = FirstQuote;\r
1579   }\r
1580   return (EFI_SUCCESS);\r
1581 }\r
1582 \r
1583 /**\r
1584   Function allocates a new command line and replaces all instances of environment\r
1585   variable names that are correctly preset to their values.\r
1586 \r
1587   If the return value is not NULL the memory must be caller freed.\r
1588 \r
1589   @param[in] OriginalCommandLine    The original command line\r
1590 \r
1591   @retval NULL                      An error occurred.\r
1592   @return                           The new command line with no environment variables present.\r
1593 **/\r
1594 CHAR16*\r
1595 ShellConvertVariables (\r
1596   IN CONST CHAR16 *OriginalCommandLine\r
1597   )\r
1598 {\r
1599   CONST CHAR16        *MasterEnvList;\r
1600   UINTN               NewSize;\r
1601   CHAR16              *NewCommandLine1;\r
1602   CHAR16              *NewCommandLine2;\r
1603   CHAR16              *Temp;\r
1604   UINTN               ItemSize;\r
1605   CHAR16              *ItemTemp;\r
1606   SCRIPT_FILE         *CurrentScriptFile;\r
1607   ALIAS_LIST          *AliasListNode;\r
1608 \r
1609   ASSERT(OriginalCommandLine != NULL);\r
1610 \r
1611   ItemSize          = 0;\r
1612   NewSize           = StrSize(OriginalCommandLine);\r
1613   CurrentScriptFile = ShellCommandGetCurrentScriptFile();\r
1614   Temp              = NULL;\r
1615 \r
1616   ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?\r
1617 \r
1618   //\r
1619   // calculate the size required for the post-conversion string...\r
1620   //\r
1621   if (CurrentScriptFile != NULL) {\r
1622     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1623       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1624       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1625    ){\r
1626       for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)\r
1627         ;  Temp != NULL\r
1628         ;  Temp = StrStr(Temp+1, AliasListNode->Alias)\r
1629        ){\r
1630         //\r
1631         // we need a preceding and if there is space no ^ preceding (if no space ignore)\r
1632         //\r
1633         if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {\r
1634           NewSize += StrSize(AliasListNode->CommandString);\r
1635         }\r
1636       }\r
1637     }\r
1638   }\r
1639 \r
1640   for (MasterEnvList = EfiShellGetEnv(NULL)\r
1641     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL\r
1642     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
1643    ){\r
1644     if (StrSize(MasterEnvList) > ItemSize) {\r
1645       ItemSize = StrSize(MasterEnvList);\r
1646     }\r
1647     for (Temp = StrStr(OriginalCommandLine, MasterEnvList)\r
1648       ;  Temp != NULL\r
1649       ;  Temp = StrStr(Temp+1, MasterEnvList)\r
1650      ){\r
1651       //\r
1652       // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)\r
1653       //\r
1654       if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&\r
1655         ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {\r
1656         NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));\r
1657       }\r
1658     }\r
1659   }\r
1660 \r
1661   //\r
1662   // now do the replacements...\r
1663   //\r
1664   NewCommandLine1 = AllocateZeroPool (NewSize);\r
1665   NewCommandLine2 = AllocateZeroPool(NewSize);\r
1666   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));\r
1667   if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {\r
1668     SHELL_FREE_NON_NULL(NewCommandLine1);\r
1669     SHELL_FREE_NON_NULL(NewCommandLine2);\r
1670     SHELL_FREE_NON_NULL(ItemTemp);\r
1671     return (NULL);\r
1672   }\r
1673   CopyMem (NewCommandLine1, OriginalCommandLine, StrSize (OriginalCommandLine));\r
1674 \r
1675   for (MasterEnvList = EfiShellGetEnv(NULL)\r
1676     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL\r
1677     ;  MasterEnvList += StrLen(MasterEnvList) + 1\r
1678    ){\r
1679     StrCpyS( ItemTemp,\r
1680               ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),\r
1681               L"%"\r
1682               );\r
1683     StrCatS( ItemTemp,\r
1684               ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),\r
1685               MasterEnvList\r
1686               );\r
1687     StrCatS( ItemTemp,\r
1688               ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),\r
1689               L"%"\r
1690               );\r
1691     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);\r
1692     StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
1693   }\r
1694   if (CurrentScriptFile != NULL) {\r
1695     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)\r
1696       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1697       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)\r
1698    ){\r
1699     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
1700     StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
1701     }\r
1702   }\r
1703 \r
1704   //\r
1705   // Remove non-existent environment variables\r
1706   //\r
1707   StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
1708 \r
1709   //\r
1710   // Now cleanup any straggler intentionally ignored "%" characters\r
1711   //\r
1712   ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);\r
1713   StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);\r
1714 \r
1715   FreePool(NewCommandLine2);\r
1716   FreePool(ItemTemp);\r
1717 \r
1718   return (NewCommandLine1);\r
1719 }\r
1720 \r
1721 /**\r
1722   Internal function to run a command line with pipe usage.\r
1723 \r
1724   @param[in] CmdLine        The pointer to the command line.\r
1725   @param[in] StdIn          The pointer to the Standard input.\r
1726   @param[in] StdOut         The pointer to the Standard output.\r
1727 \r
1728   @retval EFI_SUCCESS       The split command is executed successfully.\r
1729   @retval other             Some error occurs when executing the split command.\r
1730 **/\r
1731 EFI_STATUS\r
1732 RunSplitCommand(\r
1733   IN CONST CHAR16             *CmdLine,\r
1734   IN       SHELL_FILE_HANDLE  StdIn,\r
1735   IN       SHELL_FILE_HANDLE  StdOut\r
1736   )\r
1737 {\r
1738   EFI_STATUS        Status;\r
1739   CHAR16            *NextCommandLine;\r
1740   CHAR16            *OurCommandLine;\r
1741   UINTN             Size1;\r
1742   UINTN             Size2;\r
1743   SPLIT_LIST        *Split;\r
1744   SHELL_FILE_HANDLE TempFileHandle;\r
1745   BOOLEAN           Unicode;\r
1746 \r
1747   ASSERT(StdOut == NULL);\r
1748 \r
1749   ASSERT(StrStr(CmdLine, L"|") != NULL);\r
1750 \r
1751   Status          = EFI_SUCCESS;\r
1752   NextCommandLine = NULL;\r
1753   OurCommandLine  = NULL;\r
1754   Size1           = 0;\r
1755   Size2           = 0;\r
1756 \r
1757   NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);\r
1758   OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);\r
1759 \r
1760   if (NextCommandLine == NULL || OurCommandLine == NULL) {\r
1761     SHELL_FREE_NON_NULL(OurCommandLine);\r
1762     SHELL_FREE_NON_NULL(NextCommandLine);\r
1763     return (EFI_OUT_OF_RESOURCES);\r
1764   } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {\r
1765     SHELL_FREE_NON_NULL(OurCommandLine);\r
1766     SHELL_FREE_NON_NULL(NextCommandLine);\r
1767     return (EFI_INVALID_PARAMETER);\r
1768   } else if (NextCommandLine[0] == L'a' &&\r
1769              (NextCommandLine[1] == L' ' || NextCommandLine[1] == CHAR_NULL)\r
1770             ){\r
1771     CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
1772     while (NextCommandLine[0] == L' ') {\r
1773       CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));\r
1774     }\r
1775     if (NextCommandLine[0] == CHAR_NULL) {\r
1776       SHELL_FREE_NON_NULL(OurCommandLine);\r
1777       SHELL_FREE_NON_NULL(NextCommandLine);\r
1778       return (EFI_INVALID_PARAMETER);\r
1779     }\r
1780     Unicode = FALSE;\r
1781   } else {\r
1782     Unicode = TRUE;\r
1783   }\r
1784 \r
1785 \r
1786   //\r
1787   // make a SPLIT_LIST item and add to list\r
1788   //\r
1789   Split = AllocateZeroPool(sizeof(SPLIT_LIST));\r
1790   if (Split == NULL) {\r
1791     return EFI_OUT_OF_RESOURCES;\r
1792   }\r
1793   Split->SplitStdIn   = StdIn;\r
1794   Split->SplitStdOut  = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);\r
1795   ASSERT(Split->SplitStdOut != NULL);\r
1796   InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);\r
1797 \r
1798   Status = RunCommand(OurCommandLine);\r
1799 \r
1800   //\r
1801   // move the output from the first to the in to the second.\r
1802   //\r
1803   TempFileHandle      = Split->SplitStdOut;\r
1804   if (Split->SplitStdIn == StdIn) {\r
1805     Split->SplitStdOut = NULL;\r
1806   } else {\r
1807     Split->SplitStdOut  = Split->SplitStdIn;\r
1808   }\r
1809   Split->SplitStdIn   = TempFileHandle;\r
1810   ShellInfoObject.NewEfiShellProtocol->SetFilePosition (Split->SplitStdIn, 0);\r
1811 \r
1812   if (!EFI_ERROR(Status)) {\r
1813     Status = RunCommand(NextCommandLine);\r
1814   }\r
1815 \r
1816   //\r
1817   // remove the top level from the ScriptList\r
1818   //\r
1819   ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);\r
1820   RemoveEntryList(&Split->Link);\r
1821 \r
1822   //\r
1823   // Note that the original StdIn is now the StdOut...\r
1824   //\r
1825   if (Split->SplitStdOut != NULL) {\r
1826     ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdOut);\r
1827   }\r
1828   if (Split->SplitStdIn != NULL) {\r
1829     ShellInfoObject.NewEfiShellProtocol->CloseFile (Split->SplitStdIn);\r
1830   }\r
1831 \r
1832   FreePool(Split);\r
1833   FreePool(NextCommandLine);\r
1834   FreePool(OurCommandLine);\r
1835 \r
1836   return (Status);\r
1837 }\r
1838 \r
1839 /**\r
1840   Take the original command line, substitute any variables, free\r
1841   the original string, return the modified copy.\r
1842 \r
1843   @param[in] CmdLine  pointer to the command line to update.\r
1844 \r
1845   @retval EFI_SUCCESS           the function was successful.\r
1846   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.\r
1847 **/\r
1848 EFI_STATUS\r
1849 ShellSubstituteVariables(\r
1850   IN CHAR16 **CmdLine\r
1851   )\r
1852 {\r
1853   CHAR16      *NewCmdLine;\r
1854   NewCmdLine = ShellConvertVariables(*CmdLine);\r
1855   SHELL_FREE_NON_NULL(*CmdLine);\r
1856   if (NewCmdLine == NULL) {\r
1857     return (EFI_OUT_OF_RESOURCES);\r
1858   }\r
1859   *CmdLine = NewCmdLine;\r
1860   return (EFI_SUCCESS);\r
1861 }\r
1862 \r
1863 /**\r
1864   Take the original command line, substitute any alias in the first group of space delimited characters, free\r
1865   the original string, return the modified copy.\r
1866 \r
1867   @param[in] CmdLine  pointer to the command line to update.\r
1868 \r
1869   @retval EFI_SUCCESS           the function was successful.\r
1870   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.\r
1871 **/\r
1872 EFI_STATUS\r
1873 ShellSubstituteAliases(\r
1874   IN CHAR16 **CmdLine\r
1875   )\r
1876 {\r
1877   CHAR16      *NewCmdLine;\r
1878   CHAR16      *CommandName;\r
1879   EFI_STATUS  Status;\r
1880   UINTN       PostAliasSize;\r
1881   ASSERT(CmdLine != NULL);\r
1882   ASSERT(*CmdLine!= NULL);\r
1883 \r
1884 \r
1885   CommandName = NULL;\r
1886   if (StrStr((*CmdLine), L" ") == NULL){\r
1887     StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);\r
1888   } else {\r
1889     StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));\r
1890   }\r
1891 \r
1892   //\r
1893   // This cannot happen 'inline' since the CmdLine can need extra space.\r
1894   //\r
1895   NewCmdLine = NULL;\r
1896   if (!ShellCommandIsCommandOnList(CommandName)) {\r
1897     //\r
1898     // Convert via alias\r
1899     //\r
1900     Status = ShellConvertAlias(&CommandName);\r
1901     if (EFI_ERROR(Status)){\r
1902       return (Status);\r
1903     }\r
1904     PostAliasSize = 0;\r
1905     NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);\r
1906     if (NewCmdLine == NULL) {\r
1907       SHELL_FREE_NON_NULL(CommandName);\r
1908       SHELL_FREE_NON_NULL(*CmdLine);\r
1909       return (EFI_OUT_OF_RESOURCES);\r
1910     }\r
1911     NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);\r
1912     if (NewCmdLine == NULL) {\r
1913       SHELL_FREE_NON_NULL(CommandName);\r
1914       SHELL_FREE_NON_NULL(*CmdLine);\r
1915       return (EFI_OUT_OF_RESOURCES);\r
1916     }\r
1917   } else {\r
1918     NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);\r
1919   }\r
1920 \r
1921   SHELL_FREE_NON_NULL(*CmdLine);\r
1922   SHELL_FREE_NON_NULL(CommandName);\r
1923 \r
1924   //\r
1925   // re-assign the passed in double pointer to point to our newly allocated buffer\r
1926   //\r
1927   *CmdLine = NewCmdLine;\r
1928 \r
1929   return (EFI_SUCCESS);\r
1930 }\r
1931 \r
1932 /**\r
1933   Takes the Argv[0] part of the command line and determine the meaning of it.\r
1934 \r
1935   @param[in] CmdName  pointer to the command line to update.\r
1936 \r
1937   @retval Internal_Command    The name is an internal command.\r
1938   @retval File_Sys_Change     the name is a file system change.\r
1939   @retval Script_File_Name    the name is a NSH script file.\r
1940   @retval Unknown_Invalid     the name is unknown.\r
1941   @retval Efi_Application     the name is an application (.EFI).\r
1942 **/\r
1943 SHELL_OPERATION_TYPES\r
1944 GetOperationType(\r
1945   IN CONST CHAR16 *CmdName\r
1946   )\r
1947 {\r
1948         CHAR16* FileWithPath;\r
1949   CONST CHAR16* TempLocation;\r
1950   CONST CHAR16* TempLocation2;\r
1951 \r
1952   FileWithPath = NULL;\r
1953   //\r
1954   // test for an internal command.\r
1955   //\r
1956   if (ShellCommandIsCommandOnList(CmdName)) {\r
1957     return (Internal_Command);\r
1958   }\r
1959 \r
1960   //\r
1961   // Test for file system change request.  anything ending with first : and cant have spaces.\r
1962   //\r
1963   if (CmdName[(StrLen(CmdName)-1)] == L':') {\r
1964     if ( StrStr(CmdName, L" ") != NULL\r
1965       || StrLen(StrStr(CmdName, L":")) > 1\r
1966       ) {\r
1967       return (Unknown_Invalid);\r
1968     }\r
1969     return (File_Sys_Change);\r
1970   }\r
1971 \r
1972   //\r
1973   // Test for a file\r
1974   //\r
1975   if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {\r
1976     //\r
1977     // See if that file has a script file extension\r
1978     //\r
1979     if (StrLen(FileWithPath) > 4) {\r
1980       TempLocation = FileWithPath+StrLen(FileWithPath)-4;\r
1981       TempLocation2 = mScriptExtension;\r
1982       if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {\r
1983         SHELL_FREE_NON_NULL(FileWithPath);\r
1984         return (Script_File_Name);\r
1985       }\r
1986     }\r
1987 \r
1988     //\r
1989     // Was a file, but not a script.  we treat this as an application.\r
1990     //\r
1991     SHELL_FREE_NON_NULL(FileWithPath);\r
1992     return (Efi_Application);\r
1993   }\r
1994 \r
1995   SHELL_FREE_NON_NULL(FileWithPath);\r
1996   //\r
1997   // No clue what this is... return invalid flag...\r
1998   //\r
1999   return (Unknown_Invalid);\r
2000 }\r
2001 \r
2002 /**\r
2003   Determine if the first item in a command line is valid.\r
2004 \r
2005   @param[in] CmdLine            The command line to parse.\r
2006 \r
2007   @retval EFI_SUCCESS           The item is valid.\r
2008   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.\r
2009   @retval EFI_NOT_FOUND         The operation type is unknown or invalid.\r
2010 **/\r
2011 EFI_STATUS\r
2012 IsValidSplit(\r
2013   IN CONST CHAR16 *CmdLine\r
2014   )\r
2015 {\r
2016   CHAR16        *Temp;\r
2017   CHAR16        *FirstParameter;\r
2018   CHAR16        *TempWalker;\r
2019   EFI_STATUS    Status;\r
2020 \r
2021   Temp           = NULL;\r
2022 \r
2023   Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);\r
2024   if (Temp == NULL) {\r
2025     return (EFI_OUT_OF_RESOURCES);\r
2026   }\r
2027 \r
2028   FirstParameter = StrStr(Temp, L"|");\r
2029   if (FirstParameter != NULL) {\r
2030     *FirstParameter = CHAR_NULL;\r
2031   }\r
2032 \r
2033   FirstParameter = NULL;\r
2034 \r
2035   //\r
2036   // Process the command line\r
2037   //\r
2038   Status = ProcessCommandLineToFinal(&Temp);\r
2039 \r
2040   if (!EFI_ERROR(Status)) {\r
2041     FirstParameter = AllocateZeroPool(StrSize(CmdLine));\r
2042     if (FirstParameter == NULL) {\r
2043       SHELL_FREE_NON_NULL(Temp);\r
2044       return (EFI_OUT_OF_RESOURCES);\r
2045     }\r
2046     TempWalker = (CHAR16*)Temp;\r
2047     if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CmdLine), TRUE))) {\r
2048       if (GetOperationType(FirstParameter) == Unknown_Invalid) {\r
2049         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2050         SetLastError(SHELL_NOT_FOUND);\r
2051         Status = EFI_NOT_FOUND;\r
2052       }\r
2053     }\r
2054   }\r
2055 \r
2056   SHELL_FREE_NON_NULL(Temp);\r
2057   SHELL_FREE_NON_NULL(FirstParameter);\r
2058   return Status;\r
2059 }\r
2060 \r
2061 /**\r
2062   Determine if a command line contains with a split contains only valid commands.\r
2063 \r
2064   @param[in] CmdLine      The command line to parse.\r
2065 \r
2066   @retval EFI_SUCCESS     CmdLine has only valid commands, application, or has no split.\r
2067   @retval EFI_ABORTED     CmdLine has at least one invalid command or application.\r
2068 **/\r
2069 EFI_STATUS\r
2070 VerifySplit(\r
2071   IN CONST CHAR16 *CmdLine\r
2072   )\r
2073 {\r
2074   CONST CHAR16  *TempSpot;\r
2075   EFI_STATUS    Status;\r
2076 \r
2077   //\r
2078   // If this was the only item, then get out\r
2079   //\r
2080   if (!ContainsSplit(CmdLine)) {\r
2081     return (EFI_SUCCESS);\r
2082   }\r
2083 \r
2084   //\r
2085   // Verify up to the pipe or end character\r
2086   //\r
2087   Status = IsValidSplit(CmdLine);\r
2088   if (EFI_ERROR(Status)) {\r
2089     return (Status);\r
2090   }\r
2091 \r
2092   //\r
2093   // recurse to verify the next item\r
2094   //\r
2095   TempSpot = FindFirstCharacter(CmdLine, L"|", L'^') + 1;\r
2096   if (*TempSpot == L'a' &&\r
2097       (*(TempSpot + 1) == L' ' || *(TempSpot + 1) == CHAR_NULL)\r
2098      ) {\r
2099     // If it's an ASCII pipe '|a'\r
2100     TempSpot += 1;\r
2101   }\r
2102 \r
2103   return (VerifySplit(TempSpot));\r
2104 }\r
2105 \r
2106 /**\r
2107   Process a split based operation.\r
2108 \r
2109   @param[in] CmdLine    pointer to the command line to process\r
2110 \r
2111   @retval EFI_SUCCESS   The operation was successful\r
2112   @return               an error occurred.\r
2113 **/\r
2114 EFI_STATUS\r
2115 ProcessNewSplitCommandLine(\r
2116   IN CONST CHAR16 *CmdLine\r
2117   )\r
2118 {\r
2119   SPLIT_LIST                *Split;\r
2120   EFI_STATUS                Status;\r
2121 \r
2122   Status = VerifySplit(CmdLine);\r
2123   if (EFI_ERROR(Status)) {\r
2124     return (Status);\r
2125   }\r
2126 \r
2127   Split = NULL;\r
2128 \r
2129   //\r
2130   // are we in an existing split???\r
2131   //\r
2132   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {\r
2133     Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);\r
2134   }\r
2135 \r
2136   if (Split == NULL) {\r
2137     Status = RunSplitCommand(CmdLine, NULL, NULL);\r
2138   } else {\r
2139     Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);\r
2140   }\r
2141   if (EFI_ERROR(Status)) {\r
2142     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);\r
2143   }\r
2144   return (Status);\r
2145 }\r
2146 \r
2147 /**\r
2148   Handle a request to change the current file system.\r
2149 \r
2150   @param[in] CmdLine  The passed in command line.\r
2151 \r
2152   @retval EFI_SUCCESS The operation was successful.\r
2153 **/\r
2154 EFI_STATUS\r
2155 ChangeMappedDrive(\r
2156   IN CONST CHAR16 *CmdLine\r
2157   )\r
2158 {\r
2159   EFI_STATUS Status;\r
2160   Status = EFI_SUCCESS;\r
2161 \r
2162   //\r
2163   // make sure we are the right operation\r
2164   //\r
2165   ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);\r
2166 \r
2167   //\r
2168   // Call the protocol API to do the work\r
2169   //\r
2170   Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);\r
2171 \r
2172   //\r
2173   // Report any errors\r
2174   //\r
2175   if (EFI_ERROR(Status)) {\r
2176     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);\r
2177   }\r
2178 \r
2179   return (Status);\r
2180 }\r
2181 \r
2182 /**\r
2183   Reprocess the command line to direct all -? to the help command.\r
2184 \r
2185   if found, will add "help" as argv[0], and move the rest later.\r
2186 \r
2187   @param[in,out] CmdLine        pointer to the command line to update\r
2188 **/\r
2189 EFI_STATUS\r
2190 DoHelpUpdate(\r
2191   IN OUT CHAR16 **CmdLine\r
2192   )\r
2193 {\r
2194   CHAR16 *CurrentParameter;\r
2195   CHAR16 *Walker;\r
2196   CHAR16 *NewCommandLine;\r
2197   EFI_STATUS Status;\r
2198   UINTN  NewCmdLineSize;\r
2199 \r
2200   Status = EFI_SUCCESS;\r
2201 \r
2202   CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));\r
2203   if (CurrentParameter == NULL) {\r
2204     return (EFI_OUT_OF_RESOURCES);\r
2205   }\r
2206 \r
2207   Walker = *CmdLine;\r
2208   while(Walker != NULL && *Walker != CHAR_NULL) {\r
2209     if (!EFI_ERROR(GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine), TRUE))) {\r
2210       if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {\r
2211         CurrentParameter[0] = L' ';\r
2212         CurrentParameter[1] = L' ';\r
2213         NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine);\r
2214         NewCommandLine = AllocateZeroPool(NewCmdLineSize);\r
2215         if (NewCommandLine == NULL) {\r
2216           Status = EFI_OUT_OF_RESOURCES;\r
2217           break;\r
2218         }\r
2219 \r
2220         //\r
2221         // We know the space is sufficient since we just calculated it.\r
2222         //\r
2223         StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5);\r
2224         StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine));\r
2225         SHELL_FREE_NON_NULL(*CmdLine);\r
2226         *CmdLine = NewCommandLine;\r
2227         break;\r
2228       }\r
2229     }\r
2230   }\r
2231 \r
2232   SHELL_FREE_NON_NULL(CurrentParameter);\r
2233 \r
2234   return (Status);\r
2235 }\r
2236 \r
2237 /**\r
2238   Function to update the shell variable "lasterror".\r
2239 \r
2240   @param[in] ErrorCode      the error code to put into lasterror.\r
2241 **/\r
2242 EFI_STATUS\r
2243 SetLastError(\r
2244   IN CONST SHELL_STATUS   ErrorCode\r
2245   )\r
2246 {\r
2247   CHAR16 LeString[19];\r
2248   if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
2249     UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);\r
2250   } else {\r
2251     UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);\r
2252   }\r
2253   DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
2254   InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
2255 \r
2256   return (EFI_SUCCESS);\r
2257 }\r
2258 \r
2259 /**\r
2260   Converts the command line to it's post-processed form.  this replaces variables and alias' per UEFI Shell spec.\r
2261 \r
2262   @param[in,out] CmdLine        pointer to the command line to update\r
2263 \r
2264   @retval EFI_SUCCESS           The operation was successful\r
2265   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.\r
2266   @return                       some other error occurred\r
2267 **/\r
2268 EFI_STATUS\r
2269 ProcessCommandLineToFinal(\r
2270   IN OUT CHAR16 **CmdLine\r
2271   )\r
2272 {\r
2273   EFI_STATUS                Status;\r
2274   TrimSpaces(CmdLine);\r
2275 \r
2276   Status = ShellSubstituteAliases(CmdLine);\r
2277   if (EFI_ERROR(Status)) {\r
2278     return (Status);\r
2279   }\r
2280 \r
2281   TrimSpaces(CmdLine);\r
2282 \r
2283   Status = ShellSubstituteVariables(CmdLine);\r
2284   if (EFI_ERROR(Status)) {\r
2285     return (Status);\r
2286   }\r
2287   ASSERT (*CmdLine != NULL);\r
2288 \r
2289   TrimSpaces(CmdLine);\r
2290 \r
2291   //\r
2292   // update for help parsing\r
2293   //\r
2294   if (StrStr(*CmdLine, L"?") != NULL) {\r
2295     //\r
2296     // This may do nothing if the ? does not indicate help.\r
2297     // Save all the details for in the API below.\r
2298     //\r
2299     Status = DoHelpUpdate(CmdLine);\r
2300   }\r
2301 \r
2302   TrimSpaces(CmdLine);\r
2303 \r
2304   return (EFI_SUCCESS);\r
2305 }\r
2306 \r
2307 /**\r
2308   Run an internal shell command.\r
2309 \r
2310   This API will update the shell's environment since these commands are libraries.\r
2311 \r
2312   @param[in] CmdLine          the command line to run.\r
2313   @param[in] FirstParameter   the first parameter on the command line\r
2314   @param[in] ParamProtocol    the shell parameters protocol pointer\r
2315   @param[out] CommandStatus   the status from the command line.\r
2316 \r
2317   @retval EFI_SUCCESS     The command was completed.\r
2318   @retval EFI_ABORTED     The command's operation was aborted.\r
2319 **/\r
2320 EFI_STATUS\r
2321 RunInternalCommand(\r
2322   IN CONST CHAR16                   *CmdLine,\r
2323   IN       CHAR16                   *FirstParameter,\r
2324   IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
2325   OUT EFI_STATUS                    *CommandStatus\r
2326 )\r
2327 {\r
2328   EFI_STATUS                Status;\r
2329   UINTN                     Argc;\r
2330   CHAR16                    **Argv;\r
2331   SHELL_STATUS              CommandReturnedStatus;\r
2332   BOOLEAN                   LastError;\r
2333   CHAR16                    *Walker;\r
2334   CHAR16                    *NewCmdLine;\r
2335 \r
2336   NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);\r
2337   if (NewCmdLine == NULL) {\r
2338     return EFI_OUT_OF_RESOURCES;\r
2339   }\r
2340 \r
2341   for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {\r
2342     if (*Walker == L'^' && *(Walker+1) == L'#') {\r
2343       CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));\r
2344     }\r
2345   }\r
2346 \r
2347   //\r
2348   // get the argc and argv updated for internal commands\r
2349   //\r
2350   Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);\r
2351   if (!EFI_ERROR(Status)) {\r
2352     //\r
2353     // Run the internal command.\r
2354     //\r
2355     Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);\r
2356 \r
2357     if (!EFI_ERROR(Status)) {\r
2358       if (CommandStatus != NULL) {\r
2359         if (CommandReturnedStatus != SHELL_SUCCESS) {\r
2360           *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);\r
2361         } else {\r
2362           *CommandStatus = EFI_SUCCESS;\r
2363         }\r
2364       }\r
2365 \r
2366       //\r
2367       // Update last error status.\r
2368       // some commands do not update last error.\r
2369       //\r
2370       if (LastError) {\r
2371         SetLastError(CommandReturnedStatus);\r
2372       }\r
2373 \r
2374       //\r
2375       // Pass thru the exitcode from the app.\r
2376       //\r
2377       if (ShellCommandGetExit()) {\r
2378         //\r
2379         // An Exit was requested ("exit" command), pass its value up.\r
2380         //\r
2381         Status = CommandReturnedStatus;\r
2382       } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {\r
2383         //\r
2384         // Always abort when a script only command fails for any reason\r
2385         //\r
2386         Status = EFI_ABORTED;\r
2387       } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {\r
2388         //\r
2389         // Abort when in a script and a command aborted\r
2390         //\r
2391         Status = EFI_ABORTED;\r
2392       }\r
2393     }\r
2394   }\r
2395 \r
2396   //\r
2397   // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
2398   // This is safe even if the update API failed.  In this case, it may be a no-op.\r
2399   //\r
2400   RestoreArgcArgv(ParamProtocol, &Argv, &Argc);\r
2401 \r
2402   //\r
2403   // If a script is running and the command is not a script only command, then\r
2404   // change return value to success so the script won't halt (unless aborted).\r
2405   //\r
2406   // Script only commands have to be able halt the script since the script will\r
2407   // not operate if they are failing.\r
2408   //\r
2409   if ( ShellCommandGetCurrentScriptFile() != NULL\r
2410     && !IsScriptOnlyCommand(FirstParameter)\r
2411     && Status != EFI_ABORTED\r
2412     ) {\r
2413     Status = EFI_SUCCESS;\r
2414   }\r
2415 \r
2416   FreePool (NewCmdLine);\r
2417   return (Status);\r
2418 }\r
2419 \r
2420 /**\r
2421   Function to run the command or file.\r
2422 \r
2423   @param[in] Type             the type of operation being run.\r
2424   @param[in] CmdLine          the command line to run.\r
2425   @param[in] FirstParameter   the first parameter on the command line\r
2426   @param[in] ParamProtocol    the shell parameters protocol pointer\r
2427   @param[out] CommandStatus   the status from the command line.\r
2428 \r
2429   @retval EFI_SUCCESS     The command was completed.\r
2430   @retval EFI_ABORTED     The command's operation was aborted.\r
2431 **/\r
2432 EFI_STATUS\r
2433 RunCommandOrFile(\r
2434   IN       SHELL_OPERATION_TYPES    Type,\r
2435   IN CONST CHAR16                   *CmdLine,\r
2436   IN       CHAR16                   *FirstParameter,\r
2437   IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
2438   OUT EFI_STATUS                    *CommandStatus\r
2439 )\r
2440 {\r
2441   EFI_STATUS                Status;\r
2442   EFI_STATUS                StartStatus;\r
2443   CHAR16                    *CommandWithPath;\r
2444   CHAR16                    *FullCommandWithPath;\r
2445   EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
2446   SHELL_STATUS              CalleeExitStatus;\r
2447 \r
2448   Status            = EFI_SUCCESS;\r
2449   CommandWithPath   = NULL;\r
2450   DevPath           = NULL;\r
2451   CalleeExitStatus  = SHELL_INVALID_PARAMETER;\r
2452 \r
2453   switch (Type) {\r
2454     case   Internal_Command:\r
2455       Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
2456       break;\r
2457     case   Script_File_Name:\r
2458     case   Efi_Application:\r
2459       //\r
2460       // Process a fully qualified path\r
2461       //\r
2462       if (StrStr(FirstParameter, L":") != NULL) {\r
2463         ASSERT (CommandWithPath == NULL);\r
2464         if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {\r
2465           CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);\r
2466         }\r
2467       }\r
2468 \r
2469       //\r
2470       // Process a relative path and also check in the path environment variable\r
2471       //\r
2472       if (CommandWithPath == NULL) {\r
2473         CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);\r
2474       }\r
2475 \r
2476       //\r
2477       // This should be impossible now.\r
2478       //\r
2479       ASSERT(CommandWithPath != NULL);\r
2480 \r
2481       //\r
2482       // Make sure that path is not just a directory (or not found)\r
2483       //\r
2484       if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {\r
2485         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2486         SetLastError(SHELL_NOT_FOUND);\r
2487       }\r
2488       switch (Type) {\r
2489         case   Script_File_Name:\r
2490           FullCommandWithPath = FullyQualifyPath(CommandWithPath);\r
2491           if (FullCommandWithPath == NULL) {\r
2492             Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);\r
2493           } else {\r
2494             Status = RunScriptFile (FullCommandWithPath, NULL, CmdLine, ParamProtocol);\r
2495             FreePool(FullCommandWithPath);\r
2496           }\r
2497           break;\r
2498         case   Efi_Application:\r
2499           //\r
2500           // Get the device path of the application image\r
2501           //\r
2502           DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);\r
2503           if (DevPath == NULL){\r
2504             Status = EFI_OUT_OF_RESOURCES;\r
2505             break;\r
2506           }\r
2507 \r
2508           //\r
2509           // Execute the device path\r
2510           //\r
2511           Status = InternalShellExecuteDevicePath(\r
2512             &gImageHandle,\r
2513             DevPath,\r
2514             CmdLine,\r
2515             NULL,\r
2516             &StartStatus\r
2517            );\r
2518 \r
2519           SHELL_FREE_NON_NULL(DevPath);\r
2520 \r
2521           if(EFI_ERROR (Status)) {\r
2522             CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
2523           } else {\r
2524             CalleeExitStatus = (SHELL_STATUS) StartStatus;\r
2525           }\r
2526 \r
2527           if (CommandStatus != NULL) {\r
2528             *CommandStatus = CalleeExitStatus;\r
2529           }\r
2530 \r
2531           //\r
2532           // Update last error status.\r
2533           //\r
2534           // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS\r
2535           SetLastError(CalleeExitStatus);\r
2536           break;\r
2537         default:\r
2538           //\r
2539           // Do nothing.\r
2540           //\r
2541           break;\r
2542       }\r
2543       break;\r
2544     default:\r
2545       //\r
2546       // Do nothing.\r
2547       //\r
2548       break;\r
2549   }\r
2550 \r
2551   SHELL_FREE_NON_NULL(CommandWithPath);\r
2552 \r
2553   return (Status);\r
2554 }\r
2555 \r
2556 /**\r
2557   Function to setup StdIn, StdErr, StdOut, and then run the command or file.\r
2558 \r
2559   @param[in] Type             the type of operation being run.\r
2560   @param[in] CmdLine          the command line to run.\r
2561   @param[in] FirstParameter   the first parameter on the command line.\r
2562   @param[in] ParamProtocol    the shell parameters protocol pointer\r
2563   @param[out] CommandStatus   the status from the command line.\r
2564 \r
2565   @retval EFI_SUCCESS     The command was completed.\r
2566   @retval EFI_ABORTED     The command's operation was aborted.\r
2567 **/\r
2568 EFI_STATUS\r
2569 SetupAndRunCommandOrFile(\r
2570   IN   SHELL_OPERATION_TYPES          Type,\r
2571   IN   CHAR16                         *CmdLine,\r
2572   IN   CHAR16                         *FirstParameter,\r
2573   IN   EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,\r
2574   OUT EFI_STATUS                      *CommandStatus\r
2575 )\r
2576 {\r
2577   EFI_STATUS                Status;\r
2578   SHELL_FILE_HANDLE         OriginalStdIn;\r
2579   SHELL_FILE_HANDLE         OriginalStdOut;\r
2580   SHELL_FILE_HANDLE         OriginalStdErr;\r
2581   SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
2582   CONST SCRIPT_FILE         *ConstScriptFile;\r
2583 \r
2584   //\r
2585   // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII\r
2586   //\r
2587   Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
2588 \r
2589   //\r
2590   // The StdIn, StdOut, and StdErr are set up.\r
2591   // Now run the command, script, or application\r
2592   //\r
2593   if (!EFI_ERROR(Status)) {\r
2594     TrimSpaces(&CmdLine);\r
2595     Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);\r
2596   }\r
2597 \r
2598   //\r
2599   // Now print errors\r
2600   //\r
2601   if (EFI_ERROR(Status)) {\r
2602     ConstScriptFile = ShellCommandGetCurrentScriptFile();\r
2603     if (ConstScriptFile == NULL || ConstScriptFile->CurrentCommand == NULL) {\r
2604       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));\r
2605     } else {\r
2606       ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR_SCRIPT), ShellInfoObject.HiiHandle, (VOID*)(Status), ConstScriptFile->CurrentCommand->Line);\r
2607     }\r
2608   }\r
2609 \r
2610   //\r
2611   // put back the original StdIn, StdOut, and StdErr\r
2612   //\r
2613   RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);\r
2614 \r
2615   return (Status);\r
2616 }\r
2617 \r
2618 /**\r
2619   Function will process and run a command line.\r
2620 \r
2621   This will determine if the command line represents an internal shell\r
2622   command or dispatch an external application.\r
2623 \r
2624   @param[in] CmdLine      The command line to parse.\r
2625   @param[out] CommandStatus   The status from the command line.\r
2626 \r
2627   @retval EFI_SUCCESS     The command was completed.\r
2628   @retval EFI_ABORTED     The command's operation was aborted.\r
2629 **/\r
2630 EFI_STATUS\r
2631 RunShellCommand(\r
2632   IN CONST CHAR16   *CmdLine,\r
2633   OUT EFI_STATUS    *CommandStatus\r
2634   )\r
2635 {\r
2636   EFI_STATUS                Status;\r
2637   CHAR16                    *CleanOriginal;\r
2638   CHAR16                    *FirstParameter;\r
2639   CHAR16                    *TempWalker;\r
2640   SHELL_OPERATION_TYPES     Type;\r
2641   CONST CHAR16              *CurDir;\r
2642 \r
2643   ASSERT(CmdLine != NULL);\r
2644   if (StrLen(CmdLine) == 0) {\r
2645     return (EFI_SUCCESS);\r
2646   }\r
2647 \r
2648   Status              = EFI_SUCCESS;\r
2649   CleanOriginal       = NULL;\r
2650 \r
2651   CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);\r
2652   if (CleanOriginal == NULL) {\r
2653     return (EFI_OUT_OF_RESOURCES);\r
2654   }\r
2655 \r
2656   TrimSpaces(&CleanOriginal);\r
2657 \r
2658   //\r
2659   // NULL out comments (leveraged from RunScriptFileHandle() ).\r
2660   // The # character on a line is used to denote that all characters on the same line\r
2661   // and to the right of the # are to be ignored by the shell.\r
2662   // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.\r
2663   //\r
2664   for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {\r
2665     if (*TempWalker == L'^') {\r
2666       if (*(TempWalker + 1) == L'#') {\r
2667         TempWalker++;\r
2668       }\r
2669     } else if (*TempWalker == L'#') {\r
2670       *TempWalker = CHAR_NULL;\r
2671     }\r
2672   }\r
2673 \r
2674   TrimSpaces(&CleanOriginal);\r
2675 \r
2676   //\r
2677   // Handle case that passed in command line is just 1 or more " " characters.\r
2678   //\r
2679   if (StrLen (CleanOriginal) == 0) {\r
2680     SHELL_FREE_NON_NULL(CleanOriginal);\r
2681     return (EFI_SUCCESS);\r
2682   }\r
2683 \r
2684   Status = ProcessCommandLineToFinal(&CleanOriginal);\r
2685   if (EFI_ERROR(Status)) {\r
2686     SHELL_FREE_NON_NULL(CleanOriginal);\r
2687     return (Status);\r
2688   }\r
2689 \r
2690   //\r
2691   // We don't do normal processing with a split command line (output from one command input to another)\r
2692   //\r
2693   if (ContainsSplit(CleanOriginal)) {\r
2694     Status = ProcessNewSplitCommandLine(CleanOriginal);\r
2695     SHELL_FREE_NON_NULL(CleanOriginal);\r
2696     return (Status);\r
2697   }\r
2698 \r
2699   //\r
2700   // We need the first parameter information so we can determine the operation type\r
2701   //\r
2702   FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));\r
2703   if (FirstParameter == NULL) {\r
2704     SHELL_FREE_NON_NULL(CleanOriginal);\r
2705     return (EFI_OUT_OF_RESOURCES);\r
2706   }\r
2707   TempWalker = CleanOriginal;\r
2708   if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CleanOriginal), TRUE))) {\r
2709     //\r
2710     // Depending on the first parameter we change the behavior\r
2711     //\r
2712     switch (Type = GetOperationType(FirstParameter)) {\r
2713       case   File_Sys_Change:\r
2714         Status = ChangeMappedDrive (FirstParameter);\r
2715         break;\r
2716       case   Internal_Command:\r
2717       case   Script_File_Name:\r
2718       case   Efi_Application:\r
2719         Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);\r
2720         break;\r
2721       default:\r
2722         //\r
2723         // Whatever was typed, it was invalid.\r
2724         //\r
2725         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2726         SetLastError(SHELL_NOT_FOUND);\r
2727         break;\r
2728     }\r
2729   } else {\r
2730     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);\r
2731     SetLastError(SHELL_NOT_FOUND);\r
2732   }\r
2733   //\r
2734   // Check whether the current file system still exists. If not exist, we need update "cwd" and gShellCurMapping.\r
2735   //\r
2736   CurDir = EfiShellGetCurDir (NULL);\r
2737   if (CurDir != NULL) {\r
2738     if (EFI_ERROR(ShellFileExists (CurDir))) {\r
2739       //\r
2740       // EfiShellSetCurDir() cannot set current directory to NULL.\r
2741       // EfiShellSetEnv() is not allowed to set the "cwd" variable.\r
2742       // Only InternalEfiShellSetEnv () is allowed setting the "cwd" variable.\r
2743       //\r
2744       InternalEfiShellSetEnv (L"cwd", NULL, TRUE);\r
2745       gShellCurMapping = NULL;\r
2746     }\r
2747   }\r
2748 \r
2749   SHELL_FREE_NON_NULL(CleanOriginal);\r
2750   SHELL_FREE_NON_NULL(FirstParameter);\r
2751 \r
2752   return (Status);\r
2753 }\r
2754 \r
2755 /**\r
2756   Function will process and run a command line.\r
2757 \r
2758   This will determine if the command line represents an internal shell\r
2759   command or dispatch an external application.\r
2760 \r
2761   @param[in] CmdLine      The command line to parse.\r
2762 \r
2763   @retval EFI_SUCCESS     The command was completed.\r
2764   @retval EFI_ABORTED     The command's operation was aborted.\r
2765 **/\r
2766 EFI_STATUS\r
2767 RunCommand(\r
2768   IN CONST CHAR16   *CmdLine\r
2769   )\r
2770 {\r
2771   return (RunShellCommand(CmdLine, NULL));\r
2772 }\r
2773 \r
2774 /**\r
2775   Function to process a NSH script file via SHELL_FILE_HANDLE.\r
2776 \r
2777   @param[in] Handle             The handle to the already opened file.\r
2778   @param[in] Name               The name of the script file.\r
2779 \r
2780   @retval EFI_SUCCESS           the script completed successfully\r
2781 **/\r
2782 EFI_STATUS\r
2783 RunScriptFileHandle (\r
2784   IN SHELL_FILE_HANDLE  Handle,\r
2785   IN CONST CHAR16       *Name\r
2786   )\r
2787 {\r
2788   EFI_STATUS          Status;\r
2789   SCRIPT_FILE         *NewScriptFile;\r
2790   UINTN               LoopVar;\r
2791   UINTN               PrintBuffSize;\r
2792   CHAR16              *CommandLine;\r
2793   CHAR16              *CommandLine2;\r
2794   CHAR16              *CommandLine3;\r
2795   SCRIPT_COMMAND_LIST *LastCommand;\r
2796   BOOLEAN             Ascii;\r
2797   BOOLEAN             PreScriptEchoState;\r
2798   BOOLEAN             PreCommandEchoState;\r
2799   CONST CHAR16        *CurDir;\r
2800   UINTN               LineCount;\r
2801   CHAR16              LeString[50];\r
2802   LIST_ENTRY          OldBufferList;\r
2803 \r
2804   ASSERT(!ShellCommandGetScriptExit());\r
2805 \r
2806   PreScriptEchoState = ShellCommandGetEchoState();\r
2807   PrintBuffSize = PcdGet16(PcdShellPrintBufferSize);\r
2808 \r
2809   NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));\r
2810   if (NewScriptFile == NULL) {\r
2811     return (EFI_OUT_OF_RESOURCES);\r
2812   }\r
2813 \r
2814   //\r
2815   // Set up the name\r
2816   //\r
2817   ASSERT(NewScriptFile->ScriptName == NULL);\r
2818   NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);\r
2819   if (NewScriptFile->ScriptName == NULL) {\r
2820     DeleteScriptFileStruct(NewScriptFile);\r
2821     return (EFI_OUT_OF_RESOURCES);\r
2822   }\r
2823 \r
2824   //\r
2825   // Save the parameters (used to replace %0 to %9 later on)\r
2826   //\r
2827   NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;\r
2828   if (NewScriptFile->Argc != 0) {\r
2829     NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));\r
2830     if (NewScriptFile->Argv == NULL) {\r
2831       DeleteScriptFileStruct(NewScriptFile);\r
2832       return (EFI_OUT_OF_RESOURCES);\r
2833     }\r
2834     //\r
2835     // Put the full path of the script file into Argv[0] as required by section\r
2836     // 3.6.2 of version 2.2 of the shell specification.\r
2837     //\r
2838     NewScriptFile->Argv[0] = StrnCatGrow(&NewScriptFile->Argv[0], NULL, NewScriptFile->ScriptName, 0);\r
2839     for (LoopVar = 1 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {\r
2840       ASSERT(NewScriptFile->Argv[LoopVar] == NULL);\r
2841       NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);\r
2842       if (NewScriptFile->Argv[LoopVar] == NULL) {\r
2843         DeleteScriptFileStruct(NewScriptFile);\r
2844         return (EFI_OUT_OF_RESOURCES);\r
2845       }\r
2846     }\r
2847   } else {\r
2848     NewScriptFile->Argv = NULL;\r
2849   }\r
2850 \r
2851   InitializeListHead(&NewScriptFile->CommandList);\r
2852   InitializeListHead(&NewScriptFile->SubstList);\r
2853 \r
2854   //\r
2855   // Now build the list of all script commands.\r
2856   //\r
2857   LineCount = 0;\r
2858   while(!ShellFileHandleEof(Handle)) {\r
2859     CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);\r
2860     LineCount++;\r
2861     if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {\r
2862       SHELL_FREE_NON_NULL(CommandLine);\r
2863       continue;\r
2864     }\r
2865     NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));\r
2866     if (NewScriptFile->CurrentCommand == NULL) {\r
2867       SHELL_FREE_NON_NULL(CommandLine);\r
2868       DeleteScriptFileStruct(NewScriptFile);\r
2869       return (EFI_OUT_OF_RESOURCES);\r
2870     }\r
2871 \r
2872     NewScriptFile->CurrentCommand->Cl   = CommandLine;\r
2873     NewScriptFile->CurrentCommand->Data = NULL;\r
2874     NewScriptFile->CurrentCommand->Line = LineCount;\r
2875 \r
2876     InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
2877   }\r
2878 \r
2879   //\r
2880   // Add this as the topmost script file\r
2881   //\r
2882   ShellCommandSetNewScript (NewScriptFile);\r
2883 \r
2884   //\r
2885   // Now enumerate through the commands and run each one.\r
2886   //\r
2887   CommandLine = AllocateZeroPool(PrintBuffSize);\r
2888   if (CommandLine == NULL) {\r
2889     DeleteScriptFileStruct(NewScriptFile);\r
2890     return (EFI_OUT_OF_RESOURCES);\r
2891   }\r
2892   CommandLine2 = AllocateZeroPool(PrintBuffSize);\r
2893   if (CommandLine2 == NULL) {\r
2894     FreePool(CommandLine);\r
2895     DeleteScriptFileStruct(NewScriptFile);\r
2896     return (EFI_OUT_OF_RESOURCES);\r
2897   }\r
2898 \r
2899   for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)\r
2900       ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)\r
2901       ; // conditional increment in the body of the loop\r
2902   ){\r
2903     ASSERT(CommandLine2 != NULL);\r
2904     StrnCpyS( CommandLine2,\r
2905               PrintBuffSize/sizeof(CHAR16),\r
2906               NewScriptFile->CurrentCommand->Cl,\r
2907               PrintBuffSize/sizeof(CHAR16) - 1\r
2908               );\r
2909 \r
2910     SaveBufferList(&OldBufferList);\r
2911 \r
2912     //\r
2913     // NULL out comments\r
2914     //\r
2915     for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {\r
2916       if (*CommandLine3 == L'^') {\r
2917         if ( *(CommandLine3+1) == L':') {\r
2918           CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));\r
2919         } else if (*(CommandLine3+1) == L'#') {\r
2920           CommandLine3++;\r
2921         }\r
2922       } else if (*CommandLine3 == L'#') {\r
2923         *CommandLine3 = CHAR_NULL;\r
2924       }\r
2925     }\r
2926 \r
2927     if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {\r
2928       //\r
2929       // Due to variability in starting the find and replace action we need to have both buffers the same.\r
2930       //\r
2931       StrnCpyS( CommandLine,\r
2932                 PrintBuffSize/sizeof(CHAR16),\r
2933                 CommandLine2,\r
2934                 PrintBuffSize/sizeof(CHAR16) - 1\r
2935                 );\r
2936 \r
2937       //\r
2938       // Remove the %0 to %9 from the command line (if we have some arguments)\r
2939       //\r
2940       if (NewScriptFile->Argv != NULL) {\r
2941         switch (NewScriptFile->Argc) {\r
2942           default:\r
2943             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);\r
2944             ASSERT_EFI_ERROR(Status);\r
2945           case 9:\r
2946             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);\r
2947             ASSERT_EFI_ERROR(Status);\r
2948           case 8:\r
2949             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);\r
2950             ASSERT_EFI_ERROR(Status);\r
2951           case 7:\r
2952             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);\r
2953             ASSERT_EFI_ERROR(Status);\r
2954           case 6:\r
2955             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);\r
2956             ASSERT_EFI_ERROR(Status);\r
2957           case 5:\r
2958             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);\r
2959             ASSERT_EFI_ERROR(Status);\r
2960           case 4:\r
2961             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);\r
2962             ASSERT_EFI_ERROR(Status);\r
2963           case 3:\r
2964             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);\r
2965             ASSERT_EFI_ERROR(Status);\r
2966           case 2:\r
2967             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);\r
2968             ASSERT_EFI_ERROR(Status);\r
2969           case 1:\r
2970             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);\r
2971             ASSERT_EFI_ERROR(Status);\r
2972             break;\r
2973           case 0:\r
2974             break;\r
2975         }\r
2976       }\r
2977       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);\r
2978       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);\r
2979       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);\r
2980       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);\r
2981       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);\r
2982       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);\r
2983       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);\r
2984       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);\r
2985       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);\r
2986 \r
2987       StrnCpyS( CommandLine2,\r
2988                 PrintBuffSize/sizeof(CHAR16),\r
2989                 CommandLine,\r
2990                 PrintBuffSize/sizeof(CHAR16) - 1\r
2991                 );\r
2992 \r
2993       LastCommand = NewScriptFile->CurrentCommand;\r
2994 \r
2995       for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);\r
2996 \r
2997       if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {\r
2998         //\r
2999         // This line is a goto target / label\r
3000         //\r
3001       } else {\r
3002         if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {\r
3003           if (CommandLine3[0] == L'@') {\r
3004             //\r
3005             // We need to save the current echo state\r
3006             // and disable echo for just this command.\r
3007             //\r
3008             PreCommandEchoState = ShellCommandGetEchoState();\r
3009             ShellCommandSetEchoState(FALSE);\r
3010             Status = RunCommand(CommandLine3+1);\r
3011 \r
3012             //\r
3013             // If command was "@echo -off" or "@echo -on" then don't restore echo state\r
3014             //\r
3015             if (StrCmp (L"@echo -off", CommandLine3) != 0 &&\r
3016                 StrCmp (L"@echo -on", CommandLine3) != 0) {\r
3017               //\r
3018               // Now restore the pre-'@' echo state.\r
3019               //\r
3020               ShellCommandSetEchoState(PreCommandEchoState);\r
3021             }\r
3022           } else {\r
3023             if (ShellCommandGetEchoState()) {\r
3024               CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
3025               if (CurDir != NULL && StrLen(CurDir) > 1) {\r
3026                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);\r
3027               } else {\r
3028                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);\r
3029               }\r
3030               ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);\r
3031             }\r
3032             Status = RunCommand(CommandLine3);\r
3033           }\r
3034         }\r
3035 \r
3036         if (ShellCommandGetScriptExit()) {\r
3037           //\r
3038           // ShellCommandGetExitCode() always returns a UINT64\r
3039           //\r
3040           UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());\r
3041           DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
3042           InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
3043 \r
3044           ShellCommandRegisterExit(FALSE, 0);\r
3045           Status = EFI_SUCCESS;\r
3046           RestoreBufferList(&OldBufferList);\r
3047           break;\r
3048         }\r
3049         if (ShellGetExecutionBreakFlag()) {\r
3050           RestoreBufferList(&OldBufferList);\r
3051           break;\r
3052         }\r
3053         if (EFI_ERROR(Status)) {\r
3054           RestoreBufferList(&OldBufferList);\r
3055           break;\r
3056         }\r
3057         if (ShellCommandGetExit()) {\r
3058           RestoreBufferList(&OldBufferList);\r
3059           break;\r
3060         }\r
3061       }\r
3062       //\r
3063       // If that commend did not update the CurrentCommand then we need to advance it...\r
3064       //\r
3065       if (LastCommand == NewScriptFile->CurrentCommand) {\r
3066         NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
3067         if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
3068           NewScriptFile->CurrentCommand->Reset = TRUE;\r
3069         }\r
3070       }\r
3071     } else {\r
3072       NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);\r
3073       if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {\r
3074         NewScriptFile->CurrentCommand->Reset = TRUE;\r
3075       }\r
3076     }\r
3077     RestoreBufferList(&OldBufferList);\r
3078   }\r
3079 \r
3080 \r
3081   FreePool(CommandLine);\r
3082   FreePool(CommandLine2);\r
3083   ShellCommandSetNewScript (NULL);\r
3084 \r
3085   //\r
3086   // Only if this was the last script reset the state.\r
3087   //\r
3088   if (ShellCommandGetCurrentScriptFile()==NULL) {\r
3089     ShellCommandSetEchoState(PreScriptEchoState);\r
3090   }\r
3091   return (EFI_SUCCESS);\r
3092 }\r
3093 \r
3094 /**\r
3095   Function to process a NSH script file.\r
3096 \r
3097   @param[in] ScriptPath         Pointer to the script file name (including file system path).\r
3098   @param[in] Handle             the handle of the script file already opened.\r
3099   @param[in] CmdLine            the command line to run.\r
3100   @param[in] ParamProtocol      the shell parameters protocol pointer\r
3101 \r
3102   @retval EFI_SUCCESS           the script completed successfully\r
3103 **/\r
3104 EFI_STATUS\r
3105 RunScriptFile (\r
3106   IN CONST CHAR16                   *ScriptPath,\r
3107   IN SHELL_FILE_HANDLE              Handle OPTIONAL,\r
3108   IN CONST CHAR16                   *CmdLine,\r
3109   IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol\r
3110   )\r
3111 {\r
3112   EFI_STATUS          Status;\r
3113   SHELL_FILE_HANDLE   FileHandle;\r
3114   UINTN                     Argc;\r
3115   CHAR16                    **Argv;\r
3116 \r
3117   if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {\r
3118     return (EFI_INVALID_PARAMETER);\r
3119   }\r
3120 \r
3121   //\r
3122   // get the argc and argv updated for scripts\r
3123   //\r
3124   Status = UpdateArgcArgv(ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);\r
3125   if (!EFI_ERROR(Status)) {\r
3126 \r
3127     if (Handle == NULL) {\r
3128       //\r
3129       // open the file\r
3130       //\r
3131       Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);\r
3132       if (!EFI_ERROR(Status)) {\r
3133         //\r
3134         // run it\r
3135         //\r
3136         Status = RunScriptFileHandle(FileHandle, ScriptPath);\r
3137 \r
3138         //\r
3139         // now close the file\r
3140         //\r
3141         ShellCloseFile(&FileHandle);\r
3142       }\r
3143     } else {\r
3144       Status = RunScriptFileHandle(Handle, ScriptPath);\r
3145     }\r
3146   }\r
3147 \r
3148   //\r
3149   // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.\r
3150   // This is safe even if the update API failed.  In this case, it may be a no-op.\r
3151   //\r
3152   RestoreArgcArgv(ParamProtocol, &Argv, &Argc);\r
3153 \r
3154   return (Status);\r
3155 }\r
3156 \r
3157 /**\r
3158   Return the pointer to the first occurrence of any character from a list of characters.\r
3159 \r
3160   @param[in] String           the string to parse\r
3161   @param[in] CharacterList    the list of character to look for\r
3162   @param[in] EscapeCharacter  An escape character to skip\r
3163 \r
3164   @return the location of the first character in the string\r
3165   @retval CHAR_NULL no instance of any character in CharacterList was found in String\r
3166 **/\r
3167 CONST CHAR16*\r
3168 FindFirstCharacter(\r
3169   IN CONST CHAR16 *String,\r
3170   IN CONST CHAR16 *CharacterList,\r
3171   IN CONST CHAR16 EscapeCharacter\r
3172   )\r
3173 {\r
3174   UINT32 WalkChar;\r
3175   UINT32 WalkStr;\r
3176 \r
3177   for (WalkStr = 0; WalkStr < StrLen(String); WalkStr++) {\r
3178     if (String[WalkStr] == EscapeCharacter) {\r
3179       WalkStr++;\r
3180       continue;\r
3181     }\r
3182     for (WalkChar = 0; WalkChar < StrLen(CharacterList); WalkChar++) {\r
3183       if (String[WalkStr] == CharacterList[WalkChar]) {\r
3184         return (&String[WalkStr]);\r
3185       }\r
3186     }\r
3187   }\r
3188   return (String + StrLen(String));\r
3189 }\r