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