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