2 This is THE shell (application)
4 Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2013, Hewlett-Packard Development Company, L.P.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 // Initialize the global structure
21 SHELL_INFO ShellInfoObject
= {
56 {{NULL
, NULL
}, NULL
, NULL
},
57 {{NULL
, NULL
}, NULL
, NULL
},
69 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
70 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
71 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
74 Determine if a command line contains a valid split operation
76 @param[in] CmdLine The command line to parse.
78 @retval TRUE CmdLine has a valid split.
79 @retval FALSE CmdLine does not have a valid split.
84 IN CONST CHAR16
*CmdLine
87 CONST CHAR16
*TempSpot
;
89 if (StrStr(CmdLine
, L
"|") != NULL
) {
90 for (TempSpot
= CmdLine
; TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
; TempSpot
++) {
91 if (*TempSpot
== L
'^' && *(TempSpot
+1) == L
'|') {
93 } else if (*TempSpot
== L
'|') {
98 return (TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
);
102 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
103 feature's enabled state was not known when the shell initially launched.
105 @retval EFI_SUCCESS The feature is enabled.
106 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
110 InternalEfiShellStartCtrlSMonitor(
114 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
115 EFI_KEY_DATA KeyData
;
118 Status
= gBS
->OpenProtocol(
119 gST
->ConsoleInHandle
,
120 &gEfiSimpleTextInputExProtocolGuid
,
124 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
125 if (EFI_ERROR(Status
)) {
130 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
131 ShellInfoObject
.HiiHandle
);
132 return (EFI_SUCCESS
);
135 KeyData
.KeyState
.KeyToggleState
= 0;
136 KeyData
.Key
.ScanCode
= 0;
137 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
138 KeyData
.Key
.UnicodeChar
= L
's';
140 Status
= SimpleEx
->RegisterKeyNotify(
143 NotificationFunction
,
144 &ShellInfoObject
.CtrlSNotifyHandle1
);
146 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
147 if (!EFI_ERROR(Status
)) {
148 Status
= SimpleEx
->RegisterKeyNotify(
151 NotificationFunction
,
152 &ShellInfoObject
.CtrlSNotifyHandle2
);
154 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
155 KeyData
.Key
.UnicodeChar
= 19;
157 if (!EFI_ERROR(Status
)) {
158 Status
= SimpleEx
->RegisterKeyNotify(
161 NotificationFunction
,
162 &ShellInfoObject
.CtrlSNotifyHandle3
);
164 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
165 if (!EFI_ERROR(Status
)) {
166 Status
= SimpleEx
->RegisterKeyNotify(
169 NotificationFunction
,
170 &ShellInfoObject
.CtrlSNotifyHandle4
);
178 The entry point for the application.
180 @param[in] ImageHandle The firmware allocated handle for the EFI image.
181 @param[in] SystemTable A pointer to the EFI System Table.
183 @retval EFI_SUCCESS The entry point is executed successfully.
184 @retval other Some error occurs when executing this entry point.
190 IN EFI_HANDLE ImageHandle
,
191 IN EFI_SYSTEM_TABLE
*SystemTable
197 EFI_HANDLE ConInHandle
;
198 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
200 if (PcdGet8(PcdShellSupportLevel
) > 3) {
201 return (EFI_UNSUPPORTED
);
207 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
208 if (EFI_ERROR(Status
)) {
213 // Populate the global structure from PCDs
215 ShellInfoObject
.ImageDevPath
= NULL
;
216 ShellInfoObject
.FileDevPath
= NULL
;
217 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
218 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
219 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
222 // verify we dont allow for spec violation
224 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
227 // Initialize the LIST ENTRY objects...
229 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
230 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
231 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
234 // Check PCDs for optional features that are not implemented yet.
236 if ( PcdGetBool(PcdShellSupportOldProtocols
)
237 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
238 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
240 return (EFI_UNSUPPORTED
);
244 // turn off the watchdog timer
246 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
249 // install our console logger. This will keep a log of the output for back-browsing
251 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
252 if (!EFI_ERROR(Status
)) {
254 // Enable the cursor to be visible
256 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
259 // If supporting EFI 1.1 we need to install HII protocol
260 // only do this if PcdShellRequireHiiPlatform == FALSE
262 // remove EFI_UNSUPPORTED check above when complete.
263 ///@todo add support for Framework HII
266 // install our (solitary) HII package
268 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
269 if (ShellInfoObject
.HiiHandle
== NULL
) {
270 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
271 ///@todo Add our package into Framework HII
273 if (ShellInfoObject
.HiiHandle
== NULL
) {
274 return (EFI_NOT_STARTED
);
279 // create and install the EfiShellParametersProtocol
281 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
282 ASSERT_EFI_ERROR(Status
);
283 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
286 // create and install the EfiShellProtocol
288 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
289 ASSERT_EFI_ERROR(Status
);
290 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
293 // Now initialize the shell library (it requires Shell Parameters protocol)
295 Status
= ShellInitialize();
296 ASSERT_EFI_ERROR(Status
);
298 Status
= CommandInit();
299 ASSERT_EFI_ERROR(Status
);
302 // Check the command line
304 Status
= ProcessCommandLine();
307 // If shell support level is >= 1 create the mappings and paths
309 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
310 Status
= ShellCommandCreateInitialMappingsAndPaths();
314 // save the device path for the loaded image and the device path for the filepath (under loaded image)
315 // These are where to look for the startup.nsh file
317 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
318 ASSERT_EFI_ERROR(Status
);
321 // Display the version
323 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
326 gST
->ConOut
->Mode
->CursorRow
,
328 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
329 ShellInfoObject
.HiiHandle
,
330 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
331 gEfiShellProtocol
->MajorVersion
,
332 gEfiShellProtocol
->MinorVersion
339 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
340 ShellInfoObject
.HiiHandle
,
341 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
348 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
349 ShellInfoObject
.HiiHandle
,
350 (gST
->Hdr
.Revision
&0xffff0000)>>16,
351 (gST
->Hdr
.Revision
&0x0000ffff),
353 gST
->FirmwareRevision
358 // Display the mapping
360 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
361 Status
= RunCommand(L
"map");
362 ASSERT_EFI_ERROR(Status
);
366 // init all the built in alias'
368 Status
= SetBuiltInAlias();
369 ASSERT_EFI_ERROR(Status
);
372 // Initialize environment variables
374 if (ShellCommandGetProfileList() != NULL
) {
375 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
376 ASSERT_EFI_ERROR(Status
);
380 TempString
= AllocateZeroPool(Size
);
382 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
383 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
384 ASSERT_EFI_ERROR(Status
);
386 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
387 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
388 ASSERT_EFI_ERROR(Status
);
390 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
391 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
392 ASSERT_EFI_ERROR(Status
);
394 FreePool(TempString
);
396 if (!EFI_ERROR(Status
)) {
397 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
399 // Set up the event for CTRL-C monitoring...
401 Status
= InernalEfiShellStartMonitor();
404 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
406 // Set up the event for CTRL-S monitoring...
408 Status
= InternalEfiShellStartCtrlSMonitor();
411 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
413 // close off the gST->ConIn
415 OldConIn
= gST
->ConIn
;
416 ConInHandle
= gST
->ConsoleInHandle
;
417 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
423 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
425 // process the startup script or launch the called app.
427 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
430 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
432 // begin the UI waiting loop
436 // clean out all the memory allocated for CONST <something> * return values
437 // between each shell prompt presentation
439 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
440 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
444 // Reset page break back to default.
446 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
447 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
448 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
451 // Reset the CTRL-C event (yes we ignore the return values)
453 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
458 Status
= DoShellPrompt();
459 } while (!ShellCommandGetExit());
461 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
462 CloseSimpleTextInOnFile (gST
->ConIn
);
463 gST
->ConIn
= OldConIn
;
464 gST
->ConsoleInHandle
= ConInHandle
;
470 // uninstall protocols / free memory / etc...
472 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
473 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
474 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
476 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
477 FreePool(ShellInfoObject
.ImageDevPath
);
478 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
480 if (ShellInfoObject
.FileDevPath
!= NULL
) {
481 FreePool(ShellInfoObject
.FileDevPath
);
482 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
484 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
485 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
486 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
488 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
489 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
490 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
492 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
493 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
496 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
497 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
500 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
501 ASSERT(FALSE
); ///@todo finish this de-allocation.
504 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
505 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
506 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
509 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
510 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
511 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
514 if (ShellInfoObject
.HiiHandle
!= NULL
) {
515 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
516 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
519 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
520 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
523 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
524 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
525 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
526 FreePool(ShellInfoObject
.ConsoleInfo
);
527 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
530 if (ShellCommandGetExit()) {
531 return ((EFI_STATUS
)ShellCommandGetExitCode());
537 Sets all the alias' that were registered with the ShellCommandLib library.
539 @retval EFI_SUCCESS all init commands were run sucessfully.
547 CONST ALIAS_LIST
*List
;
551 // Get all the commands we want to run
553 List
= ShellCommandGetInitAliasList();
556 // for each command in the List
558 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
559 ; !IsNull (&List
->Link
, &Node
->Link
)
560 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
563 // install the alias'
565 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
566 ASSERT_EFI_ERROR(Status
);
568 return (EFI_SUCCESS
);
572 Internal function to determine if 2 command names are really the same.
574 @param[in] Command1 The pointer to the first command name.
575 @param[in] Command2 The pointer to the second command name.
577 @retval TRUE The 2 command names are the same.
578 @retval FALSE The 2 command names are not the same.
583 IN CONST CHAR16
*Command1
,
584 IN CONST CHAR16
*Command2
587 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
594 Internal function to determine if a command is a script only command.
596 @param[in] CommandName The pointer to the command name.
598 @retval TRUE The command is a script only command.
599 @retval FALSE The command is not a script only command.
604 IN CONST CHAR16
*CommandName
607 if (IsCommand(CommandName
, L
"for")
608 ||IsCommand(CommandName
, L
"endfor")
609 ||IsCommand(CommandName
, L
"if")
610 ||IsCommand(CommandName
, L
"else")
611 ||IsCommand(CommandName
, L
"endif")
612 ||IsCommand(CommandName
, L
"goto")) {
619 This function will populate the 2 device path protocol parameters based on the
620 global gImageHandle. The DevPath will point to the device path for the handle that has
621 loaded image protocol installed on it. The FilePath will point to the device path
622 for the file that was loaded.
624 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
625 @param[in, out] FilePath On a sucessful return the device path to the file.
627 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
628 @retval other A error from gBS->HandleProtocol.
634 GetDevicePathsForImageAndFile (
635 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
636 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
640 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
641 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
643 ASSERT(DevPath
!= NULL
);
644 ASSERT(FilePath
!= NULL
);
646 Status
= gBS
->OpenProtocol (
648 &gEfiLoadedImageProtocolGuid
,
649 (VOID
**)&LoadedImage
,
652 EFI_OPEN_PROTOCOL_GET_PROTOCOL
654 if (!EFI_ERROR (Status
)) {
655 Status
= gBS
->OpenProtocol (
656 LoadedImage
->DeviceHandle
,
657 &gEfiDevicePathProtocolGuid
,
658 (VOID
**)&ImageDevicePath
,
661 EFI_OPEN_PROTOCOL_GET_PROTOCOL
663 if (!EFI_ERROR (Status
)) {
664 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
665 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
667 LoadedImage
->DeviceHandle
,
668 &gEfiDevicePathProtocolGuid
,
674 &gEfiLoadedImageProtocolGuid
,
681 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
682 {L
"-nostartup", TypeFlag
},
683 {L
"-startup", TypeFlag
},
684 {L
"-noconsoleout", TypeFlag
},
685 {L
"-noconsolein", TypeFlag
},
686 {L
"-nointerrupt", TypeFlag
},
687 {L
"-nomap", TypeFlag
},
688 {L
"-noversion", TypeFlag
},
689 {L
"-startup", TypeFlag
},
690 {L
"-delay", TypeValue
},
691 {L
"-_exit", TypeFlag
},
696 Process all Uefi Shell 2.0 command line options.
698 see Uefi Shell 2.0 section 3.2 for full details.
700 the command line must resemble the following:
702 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
704 ShellOpt-options Options which control the initialization behavior of the shell.
705 These options are read from the EFI global variable "ShellOpt"
706 and are processed before options or file-name.
708 options Options which control the initialization behavior of the shell.
710 file-name The name of a UEFI shell application or script to be executed
711 after initialization is complete. By default, if file-name is
712 specified, then -nostartup is implied. Scripts are not supported
715 file-name-options The command-line options that are passed to file-name when it
718 This will initialize the ShellInfoObject.ShellInitSettings global variable.
720 @retval EFI_SUCCESS The variable is initialized.
731 CONST CHAR16
*TempConst
;
734 CHAR16
*ProblemParam
;
740 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
744 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
745 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
746 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateZeroPool(StrSize(TempConst
));
747 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
748 return (EFI_OUT_OF_RESOURCES
);
750 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
751 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
752 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
753 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
756 // We found the file... add the rest of the params...
758 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
759 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
760 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
764 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
765 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
766 return (EFI_OUT_OF_RESOURCES
);
768 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
770 gEfiShellParametersProtocol
->Argv
[LoopVar
],
772 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
773 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
774 return (EFI_OUT_OF_RESOURCES
);
780 ShellCommandLineFreeVarList(Package
);
782 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
783 if (EFI_ERROR(Status
)) {
784 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
785 FreePool(ProblemParam
);
786 ShellCommandLineFreeVarList(Package
);
787 return (EFI_INVALID_PARAMETER
);
791 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
792 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
793 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
794 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
795 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
796 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
797 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
798 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
799 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= ShellCommandLineGetFlag(Package
, L
"-_exit");
801 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
803 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
804 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
805 } else if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
806 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
807 if (TempConst
!= NULL
&& *TempConst
== L
':') {
810 if (TempConst
!= NULL
&& !EFI_ERROR(ShellConvertStringToUint64(TempConst
, &Intermediate
, FALSE
, FALSE
))) {
811 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)Intermediate
;
814 ShellCommandLineFreeVarList(Package
);
820 Handles all interaction with the default startup script.
822 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
824 @param ImagePath the path to the image for shell. first place to look for the startup script
825 @param FilePath the path to the file for shell. second place to look for the startup script.
827 @retval EFI_SUCCESS the variable is initialized.
832 EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
833 EFI_DEVICE_PATH_PROTOCOL
*FilePath
839 SHELL_FILE_HANDLE FileHandle
;
840 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
841 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
842 CHAR16
*FileStringPath
;
845 CONST CHAR16
*MapName
;
847 Key
.UnicodeChar
= CHAR_NULL
;
851 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
853 // launch something else instead
855 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
856 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
857 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
859 FileStringPath
= AllocateZeroPool(NewSize
);
860 if (FileStringPath
== NULL
) {
861 return (EFI_OUT_OF_RESOURCES
);
863 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
864 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
865 StrCat(FileStringPath
, L
" ");
866 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
868 Status
= RunCommand(FileStringPath
);
869 FreePool(FileStringPath
);
875 // for shell level 0 we do no scripts
876 // Without the Startup bit overriding we allow for nostartup to prevent scripts
878 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
879 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
881 return (EFI_SUCCESS
);
884 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
886 // print out our warning and see if they press a key
888 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
889 ; Delay
!= 0 && EFI_ERROR(Status
)
892 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
893 gBS
->Stall (1000000);
894 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
895 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
898 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
899 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
904 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
905 return (EFI_SUCCESS
);
909 // Try the first location (must be file system)
911 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
912 if (MapName
!= NULL
) {
913 FileStringPath
= NULL
;
915 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
916 if (FileStringPath
== NULL
) {
917 Status
= EFI_OUT_OF_RESOURCES
;
919 TempSpot
= StrStr(FileStringPath
, L
";");
920 if (TempSpot
!= NULL
) {
921 *TempSpot
= CHAR_NULL
;
923 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
924 PathRemoveLastItem(FileStringPath
);
925 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
926 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
927 FreePool(FileStringPath
);
930 if (EFI_ERROR(Status
)) {
931 NamePath
= FileDevicePath (NULL
, mStartupScript
);
932 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
938 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
942 // If we got a file, run it
944 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
945 Status
= RunScriptFileHandle (FileHandle
, mStartupScript
);
946 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
948 FileStringPath
= ShellFindFilePath(mStartupScript
);
949 if (FileStringPath
== NULL
) {
951 // we return success since we dont need to have a startup script
953 Status
= EFI_SUCCESS
;
954 ASSERT(FileHandle
== NULL
);
956 Status
= RunScriptFile(FileStringPath
);
957 FreePool(FileStringPath
);
966 Function to perform the shell prompt looping. It will do a single prompt,
967 dispatch the result, and then return. It is expected that the caller will
968 call this function in a loop many times.
971 @retval RETURN_ABORTED
982 CONST CHAR16
*CurDir
;
989 // Get screen setting to decide size of the command line buffer
991 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
992 BufferSize
= Column
* Row
* sizeof (CHAR16
);
993 CmdLine
= AllocateZeroPool (BufferSize
);
994 if (CmdLine
== NULL
) {
995 return EFI_OUT_OF_RESOURCES
;
998 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1003 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1005 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1006 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1008 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1012 // Read a line from the console
1014 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1017 // Null terminate the string and parse it
1019 if (!EFI_ERROR (Status
)) {
1020 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1021 Status
= RunCommand(CmdLine
);
1025 // Done with this command
1032 Add a buffer to the Buffer To Free List for safely returning buffers to other
1033 places without risking letting them modify internal shell information.
1035 @param Buffer Something to pass to FreePool when the shell is exiting.
1039 AddBufferToFreeList(
1043 BUFFER_LIST
*BufferListEntry
;
1045 if (Buffer
== NULL
) {
1049 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1050 ASSERT(BufferListEntry
!= NULL
);
1051 BufferListEntry
->Buffer
= Buffer
;
1052 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1057 Add a buffer to the Line History List
1059 @param Buffer The line buffer to add.
1063 AddLineToCommandHistory(
1064 IN CONST CHAR16
*Buffer
1069 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1070 ASSERT(Node
!= NULL
);
1071 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
1072 ASSERT(Node
->Buffer
!= NULL
);
1073 StrCpy(Node
->Buffer
, Buffer
);
1075 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1079 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1080 with the correct command name.
1082 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1083 command name if it was an alias. If it was not
1084 an alias it will be unchanged. This function may
1085 change the buffer to fit the command name.
1087 @retval EFI_SUCCESS The name was changed.
1088 @retval EFI_SUCCESS The name was not an alias.
1089 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1094 IN OUT CHAR16
**CommandString
1097 CONST CHAR16
*NewString
;
1099 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1100 if (NewString
== NULL
) {
1101 return (EFI_SUCCESS
);
1103 FreePool(*CommandString
);
1104 *CommandString
= AllocateZeroPool(StrSize(NewString
));
1105 if (*CommandString
== NULL
) {
1106 return (EFI_OUT_OF_RESOURCES
);
1108 StrCpy(*CommandString
, NewString
);
1109 return (EFI_SUCCESS
);
1113 Function allocates a new command line and replaces all instances of environment
1114 variable names that are correctly preset to their values.
1116 If the return value is not NULL the memory must be caller freed.
1118 @param[in] OriginalCommandLine The original command line
1120 @retval NULL An error ocurred.
1121 @return The new command line with no environment variables present.
1125 ShellConvertVariables (
1126 IN CONST CHAR16
*OriginalCommandLine
1129 CONST CHAR16
*MasterEnvList
;
1131 CHAR16
*NewCommandLine1
;
1132 CHAR16
*NewCommandLine2
;
1137 SCRIPT_FILE
*CurrentScriptFile
;
1138 ALIAS_LIST
*AliasListNode
;
1140 ASSERT(OriginalCommandLine
!= NULL
);
1143 NewSize
= StrSize(OriginalCommandLine
);
1144 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1147 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1150 // calculate the size required for the post-conversion string...
1152 if (CurrentScriptFile
!= NULL
) {
1153 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1154 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1155 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1157 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1159 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1162 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1164 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1165 NewSize
+= StrSize(AliasListNode
->CommandString
);
1171 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1172 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1173 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1175 if (StrSize(MasterEnvList
) > ItemSize
) {
1176 ItemSize
= StrSize(MasterEnvList
);
1178 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1180 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1183 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1185 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1186 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1187 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1193 // now do the replacements...
1195 NewCommandLine1
= AllocateZeroPool(NewSize
);
1196 NewCommandLine2
= AllocateZeroPool(NewSize
);
1197 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1198 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1199 SHELL_FREE_NON_NULL(NewCommandLine1
);
1200 SHELL_FREE_NON_NULL(NewCommandLine2
);
1201 SHELL_FREE_NON_NULL(ItemTemp
);
1204 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1205 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1206 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1207 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1209 StrCpy(ItemTemp
, L
"%");
1210 StrCat(ItemTemp
, MasterEnvList
);
1211 StrCat(ItemTemp
, L
"%");
1212 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1213 StrCpy(NewCommandLine1
, NewCommandLine2
);
1215 if (CurrentScriptFile
!= NULL
) {
1216 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1217 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1218 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1220 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1221 StrCpy(NewCommandLine1
, NewCommandLine2
);
1225 // Remove non-existant environment variables in scripts only
1227 for (Temp
= NewCommandLine1
; Temp
!= NULL
; ) {
1228 Temp
= StrStr(Temp
, L
"%");
1232 while (*(Temp
- 1) == L
'^') {
1233 Temp
= StrStr(Temp
+ 1, L
"%");
1242 Temp2
= StrStr(Temp
+ 1, L
"%");
1243 if (Temp2
== NULL
) {
1246 while (*(Temp2
- 1) == L
'^') {
1247 Temp2
= StrStr(Temp2
+ 1, L
"%");
1248 if (Temp2
== NULL
) {
1252 if (Temp2
== NULL
) {
1257 CopyMem(Temp
, Temp2
, StrSize(Temp2
));
1263 // Now cleanup any straggler intentionally ignored "%" characters
1265 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1266 StrCpy(NewCommandLine1
, NewCommandLine2
);
1268 FreePool(NewCommandLine2
);
1271 return (NewCommandLine1
);
1275 Internal function to run a command line with pipe usage.
1277 @param[in] CmdLine The pointer to the command line.
1278 @param[in] StdIn The pointer to the Standard input.
1279 @param[in] StdOut The pointer to the Standard output.
1281 @retval EFI_SUCCESS The split command is executed successfully.
1282 @retval other Some error occurs when executing the split command.
1287 IN CONST CHAR16
*CmdLine
,
1288 IN SHELL_FILE_HANDLE
*StdIn
,
1289 IN SHELL_FILE_HANDLE
*StdOut
1293 CHAR16
*NextCommandLine
;
1294 CHAR16
*OurCommandLine
;
1298 SHELL_FILE_HANDLE
*TempFileHandle
;
1301 ASSERT(StdOut
== NULL
);
1303 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1305 Status
= EFI_SUCCESS
;
1306 NextCommandLine
= NULL
;
1307 OurCommandLine
= NULL
;
1311 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1312 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1314 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1315 SHELL_FREE_NON_NULL(OurCommandLine
);
1316 SHELL_FREE_NON_NULL(NextCommandLine
);
1317 return (EFI_OUT_OF_RESOURCES
);
1318 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1319 SHELL_FREE_NON_NULL(OurCommandLine
);
1320 SHELL_FREE_NON_NULL(NextCommandLine
);
1321 return (EFI_INVALID_PARAMETER
);
1322 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1323 NextCommandLine
[0] == L
'a' &&
1324 NextCommandLine
[1] == L
' '
1326 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1334 // make a SPLIT_LIST item and add to list
1336 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1337 ASSERT(Split
!= NULL
);
1338 Split
->SplitStdIn
= StdIn
;
1339 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1340 ASSERT(Split
->SplitStdOut
!= NULL
);
1341 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1343 Status
= RunCommand(OurCommandLine
);
1346 // move the output from the first to the in to the second.
1348 TempFileHandle
= Split
->SplitStdOut
;
1349 if (Split
->SplitStdIn
== StdIn
) {
1350 Split
->SplitStdOut
= NULL
;
1352 Split
->SplitStdOut
= Split
->SplitStdIn
;
1354 Split
->SplitStdIn
= TempFileHandle
;
1355 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1357 if (!EFI_ERROR(Status
)) {
1358 Status
= RunCommand(NextCommandLine
);
1362 // remove the top level from the ScriptList
1364 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1365 RemoveEntryList(&Split
->Link
);
1368 // Note that the original StdIn is now the StdOut...
1370 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1371 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1373 if (Split
->SplitStdIn
!= NULL
) {
1374 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1378 FreePool(NextCommandLine
);
1379 FreePool(OurCommandLine
);
1385 Function will process and run a command line.
1387 This will determine if the command line represents an internal shell
1388 command or dispatch an external application.
1390 @param[in] CmdLine The command line to parse.
1392 @retval EFI_SUCCESS The command was completed.
1393 @retval EFI_ABORTED The command's operation was aborted.
1398 IN CONST CHAR16
*CmdLine
1402 EFI_STATUS StatusCode
;
1403 CHAR16
*CommandName
;
1404 SHELL_STATUS ShellStatus
;
1408 CHAR16 LeString
[19];
1409 CHAR16
*PostAliasCmdLine
;
1410 UINTN PostAliasSize
;
1411 CHAR16
*PostVariableCmdLine
;
1412 CHAR16
*CommandWithPath
;
1413 CONST EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
1414 CONST CHAR16
*TempLocation
;
1415 CONST CHAR16
*TempLocation2
;
1416 SHELL_FILE_HANDLE OriginalStdIn
;
1417 SHELL_FILE_HANDLE OriginalStdOut
;
1418 SHELL_FILE_HANDLE OriginalStdErr
;
1419 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
1422 CHAR16
*CleanOriginal
;
1425 ASSERT(CmdLine
!= NULL
);
1426 if (StrLen(CmdLine
) == 0) {
1427 return (EFI_SUCCESS
);
1431 PostVariableCmdLine
= NULL
;
1432 PostAliasCmdLine
= NULL
;
1433 CommandWithPath
= NULL
;
1435 Status
= EFI_SUCCESS
;
1436 CleanOriginal
= NULL
;
1439 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
1440 if (CleanOriginal
== NULL
) {
1441 return (EFI_OUT_OF_RESOURCES
);
1445 // Remove any spaces and tabs at the beginning of the string.
1447 while ((CleanOriginal
[0] == L
' ') || (CleanOriginal
[0] == L
'\t')) {
1448 CopyMem(CleanOriginal
, CleanOriginal
+1, StrSize(CleanOriginal
) - sizeof(CleanOriginal
[0]));
1452 // Handle case that passed in command line is just 1 or more " " characters.
1454 if (StrLen (CleanOriginal
) == 0) {
1455 if (CleanOriginal
!= NULL
) {
1456 FreePool(CleanOriginal
);
1457 CleanOriginal
= NULL
;
1459 return (EFI_SUCCESS
);
1463 // Remove any spaces at the end of the string.
1465 while (CleanOriginal
[StrLen(CleanOriginal
)-1] == L
' ') {
1466 CleanOriginal
[StrLen(CleanOriginal
)-1] = CHAR_NULL
;
1470 if (StrStr(CleanOriginal
, L
" ") == NULL
){
1471 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, 0);
1473 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, StrStr(CleanOriginal
, L
" ") - CleanOriginal
);
1476 ASSERT(PostAliasCmdLine
== NULL
);
1477 if (!ShellCommandIsCommandOnList(CommandName
)) {
1479 // Convert via alias
1481 Status
= ShellConvertAlias(&CommandName
);
1483 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, CommandName
, 0);
1484 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, StrStr(CleanOriginal
, L
" "), 0);
1485 ASSERT_EFI_ERROR(Status
);
1487 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, NULL
, CleanOriginal
, 0);
1490 if (CleanOriginal
!= NULL
) {
1491 FreePool(CleanOriginal
);
1492 CleanOriginal
= NULL
;
1495 if (CommandName
!= NULL
) {
1496 FreePool(CommandName
);
1500 PostVariableCmdLine
= ShellConvertVariables(PostAliasCmdLine
);
1503 // we can now free the modified by alias command line
1505 if (PostAliasCmdLine
!= NULL
) {
1506 FreePool(PostAliasCmdLine
);
1507 PostAliasCmdLine
= NULL
;
1510 if (PostVariableCmdLine
== NULL
) {
1511 return (EFI_OUT_OF_RESOURCES
);
1514 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1515 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1517 while (PostVariableCmdLine
[0] == L
' ') {
1518 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1522 // We dont do normal processing with a split command line (output from one command input to another)
1524 if (ContainsSplit(PostVariableCmdLine
)) {
1526 // are we in an existing split???
1528 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1529 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1532 if (Split
== NULL
) {
1533 Status
= RunSplitCommand(PostVariableCmdLine
, NULL
, NULL
);
1535 Status
= RunSplitCommand(PostVariableCmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1537 if (EFI_ERROR(Status
)) {
1538 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1543 // If this is a mapped drive change handle that...
1545 if (PostVariableCmdLine
[(StrLen(PostVariableCmdLine
)-1)] == L
':' && StrStr(PostVariableCmdLine
, L
" ") == NULL
) {
1546 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, PostVariableCmdLine
);
1547 if (EFI_ERROR(Status
)) {
1548 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1550 FreePool(PostVariableCmdLine
);
1554 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1555 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1559 Status
= UpdateStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1560 if (EFI_ERROR(Status
)) {
1561 if (Status
== EFI_NOT_FOUND
) {
1562 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR
), ShellInfoObject
.HiiHandle
);
1564 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_REDIR
), ShellInfoObject
.HiiHandle
);
1567 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1568 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1570 while (PostVariableCmdLine
[0] == L
' ') {
1571 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1575 // get the argc and argv updated for internal commands
1577 Status
= UpdateArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &Argv
, &Argc
);
1578 ASSERT_EFI_ERROR(Status
);
1580 for (Count
= 0 ; Count
< ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count
++) {
1581 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
], L
"-?") == ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]
1582 || (ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][0] == L
'?' && ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][1] == CHAR_NULL
)
1585 // We need to redo the arguments since a parameter was -?
1586 // move them all down 1 to the end, then up one then replace the first with help
1588 FreePool(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]);
1589 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
] = NULL
;
1590 for (Count2
= Count
; (Count2
+ 1) < ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count2
++) {
1591 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
+1];
1593 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = NULL
;
1594 for (Count2
= ShellInfoObject
.NewShellParametersProtocol
->Argc
-1 ; Count2
> 0 ; Count2
--) {
1595 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
-1];
1597 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = NULL
;
1598 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = StrnCatGrow(&ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], NULL
, L
"help", 0);
1606 if (ShellCommandIsCommandOnList(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1608 // Run the command (which was converted if it was an alias)
1610 if (!EFI_ERROR(Status
)) {
1611 Status
= ShellCommandRunCommandHandler(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], &ShellStatus
, &LastError
);
1612 ASSERT_EFI_ERROR(Status
);
1614 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1615 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellStatus
);
1617 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ShellStatus
);
1619 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1621 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1624 // Pass thru the exitcode from the app.
1626 if (ShellCommandGetExit()) {
1627 Status
= ShellStatus
;
1628 } else if (ShellStatus
!= 0 && IsScriptOnlyCommand(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1629 Status
= EFI_ABORTED
;
1634 // run an external file (or script)
1636 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], L
":") != NULL
) {
1637 ASSERT (CommandWithPath
== NULL
);
1638 if (ShellIsFile(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]) == EFI_SUCCESS
) {
1639 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1642 if (CommandWithPath
== NULL
) {
1643 CommandWithPath
= ShellFindFilePathEx(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], mExecutableExtensions
);
1645 if (CommandWithPath
== NULL
|| ShellIsDirectory(CommandWithPath
) == EFI_SUCCESS
) {
1646 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]);
1648 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1649 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", EFI_NOT_FOUND
);
1651 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", EFI_NOT_FOUND
);
1653 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1654 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1657 // Check if it's a NSH (script) file.
1659 TempLocation
= CommandWithPath
+StrLen(CommandWithPath
)-4;
1660 TempLocation2
= mScriptExtension
;
1661 if ((StrLen(CommandWithPath
) > 4) && (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0)) {
1662 Status
= RunScriptFile (CommandWithPath
);
1664 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
1665 ASSERT(DevPath
!= NULL
);
1666 Status
= InternalShellExecuteDevicePath(
1669 PostVariableCmdLine
,
1675 // Update last error status.
1677 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1678 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", StatusCode
);
1680 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", StatusCode
);
1682 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1683 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1689 // Print some error info.
1691 if (EFI_ERROR(Status
)) {
1692 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
1695 CommandName
= StrnCatGrow(&CommandName
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1697 RestoreArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, &Argv
, &Argc
);
1699 RestoreStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1701 if (CommandName
!= NULL
) {
1702 if (ShellCommandGetCurrentScriptFile() != NULL
&& !IsScriptOnlyCommand(CommandName
)) {
1704 // if this is NOT a scipt only command return success so the script won't quit.
1705 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1707 Status
= EFI_SUCCESS
;
1712 SHELL_FREE_NON_NULL(CommandName
);
1713 SHELL_FREE_NON_NULL(CommandWithPath
);
1714 SHELL_FREE_NON_NULL(PostVariableCmdLine
);
1719 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
1721 Function determins if the CommandName COULD be a valid command. It does not determine whether
1722 this is a valid command. It only checks for invalid characters.
1724 @param[in] CommandName The name to check
1726 @retval TRUE CommandName could be a command name
1727 @retval FALSE CommandName could not be a valid command name
1732 IN CONST CHAR16
*CommandName
1736 if (CommandName
== NULL
) {
1741 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
1744 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
1752 Function to process a NSH script file via SHELL_FILE_HANDLE.
1754 @param[in] Handle The handle to the already opened file.
1755 @param[in] Name The name of the script file.
1757 @retval EFI_SUCCESS the script completed sucessfully
1761 RunScriptFileHandle (
1762 IN SHELL_FILE_HANDLE Handle
,
1763 IN CONST CHAR16
*Name
1767 SCRIPT_FILE
*NewScriptFile
;
1769 CHAR16
*CommandLine
;
1770 CHAR16
*CommandLine2
;
1771 CHAR16
*CommandLine3
;
1772 SCRIPT_COMMAND_LIST
*LastCommand
;
1774 BOOLEAN PreScriptEchoState
;
1775 BOOLEAN PreCommandEchoState
;
1776 CONST CHAR16
*CurDir
;
1778 CHAR16 LeString
[50];
1780 ASSERT(!ShellCommandGetScriptExit());
1782 PreScriptEchoState
= ShellCommandGetEchoState();
1784 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
1785 if (NewScriptFile
== NULL
) {
1786 return (EFI_OUT_OF_RESOURCES
);
1792 ASSERT(NewScriptFile
->ScriptName
== NULL
);
1793 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
1794 if (NewScriptFile
->ScriptName
== NULL
) {
1795 DeleteScriptFileStruct(NewScriptFile
);
1796 return (EFI_OUT_OF_RESOURCES
);
1800 // Save the parameters (used to replace %0 to %9 later on)
1802 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
1803 if (NewScriptFile
->Argc
!= 0) {
1804 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
1805 if (NewScriptFile
->Argv
== NULL
) {
1806 DeleteScriptFileStruct(NewScriptFile
);
1807 return (EFI_OUT_OF_RESOURCES
);
1809 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
1810 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
1811 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
1812 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
1813 DeleteScriptFileStruct(NewScriptFile
);
1814 return (EFI_OUT_OF_RESOURCES
);
1818 NewScriptFile
->Argv
= NULL
;
1821 InitializeListHead(&NewScriptFile
->CommandList
);
1822 InitializeListHead(&NewScriptFile
->SubstList
);
1825 // Now build the list of all script commands.
1828 while(!ShellFileHandleEof(Handle
)) {
1829 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
1831 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0) {
1834 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
1835 if (NewScriptFile
->CurrentCommand
== NULL
) {
1836 DeleteScriptFileStruct(NewScriptFile
);
1837 return (EFI_OUT_OF_RESOURCES
);
1840 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
1841 NewScriptFile
->CurrentCommand
->Data
= NULL
;
1842 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
1844 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1848 // Add this as the topmost script file
1850 ShellCommandSetNewScript (NewScriptFile
);
1853 // Now enumerate through the commands and run each one.
1855 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1856 if (CommandLine
== NULL
) {
1857 DeleteScriptFileStruct(NewScriptFile
);
1858 return (EFI_OUT_OF_RESOURCES
);
1860 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1861 if (CommandLine2
== NULL
) {
1862 FreePool(CommandLine
);
1863 DeleteScriptFileStruct(NewScriptFile
);
1864 return (EFI_OUT_OF_RESOURCES
);
1867 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
1868 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
1869 ; // conditional increment in the body of the loop
1871 ASSERT(CommandLine2
!= NULL
);
1872 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
1875 // NULL out comments
1877 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
1878 if (*CommandLine3
== L
'^') {
1879 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
1880 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
1882 } else if (*CommandLine3
== L
'#') {
1883 *CommandLine3
= CHAR_NULL
;
1887 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
1889 // Due to variability in starting the find and replace action we need to have both buffers the same.
1891 StrCpy(CommandLine
, CommandLine2
);
1894 // Remove the %0 to %9 from the command line (if we have some arguments)
1896 if (NewScriptFile
->Argv
!= NULL
) {
1897 switch (NewScriptFile
->Argc
) {
1899 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
1900 ASSERT_EFI_ERROR(Status
);
1902 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
1903 ASSERT_EFI_ERROR(Status
);
1905 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
1906 ASSERT_EFI_ERROR(Status
);
1908 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
1909 ASSERT_EFI_ERROR(Status
);
1911 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
1912 ASSERT_EFI_ERROR(Status
);
1914 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
1915 ASSERT_EFI_ERROR(Status
);
1917 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
1918 ASSERT_EFI_ERROR(Status
);
1920 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
1921 ASSERT_EFI_ERROR(Status
);
1923 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
1924 ASSERT_EFI_ERROR(Status
);
1926 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
1927 ASSERT_EFI_ERROR(Status
);
1933 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
1934 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
1935 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
1936 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
1937 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
1938 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
1939 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
1940 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
1941 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
1943 StrCpy(CommandLine2
, CommandLine
);
1945 LastCommand
= NewScriptFile
->CurrentCommand
;
1947 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
1949 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
1951 // This line is a goto target / label
1954 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
1955 if (CommandLine3
[0] == L
'@') {
1957 // We need to save the current echo state
1958 // and disable echo for just this command.
1960 PreCommandEchoState
= ShellCommandGetEchoState();
1961 ShellCommandSetEchoState(FALSE
);
1962 Status
= RunCommand(CommandLine3
+1);
1965 // If command was "@echo -off" or "@echo -on" then don't restore echo state
1967 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
1968 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
1970 // Now restore the pre-'@' echo state.
1972 ShellCommandSetEchoState(PreCommandEchoState
);
1975 if (ShellCommandGetEchoState()) {
1976 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1977 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1978 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1980 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1982 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
1984 Status
= RunCommand(CommandLine3
);
1988 if (ShellCommandGetScriptExit()) {
1990 // ShellCommandGetExitCode() always returns a UINT64
1992 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
1993 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1994 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1996 ShellCommandRegisterExit(FALSE
, 0);
1997 Status
= EFI_SUCCESS
;
2000 if (ShellGetExecutionBreakFlag()) {
2003 if (EFI_ERROR(Status
)) {
2006 if (ShellCommandGetExit()) {
2011 // If that commend did not update the CurrentCommand then we need to advance it...
2013 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2014 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2015 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2016 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2020 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2021 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2022 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2028 FreePool(CommandLine
);
2029 FreePool(CommandLine2
);
2030 ShellCommandSetNewScript (NULL
);
2033 // Only if this was the last script reset the state.
2035 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2036 ShellCommandSetEchoState(PreScriptEchoState
);
2038 return (EFI_SUCCESS
);
2042 Function to process a NSH script file.
2044 @param[in] ScriptPath Pointer to the script file name (including file system path).
2046 @retval EFI_SUCCESS the script completed sucessfully
2051 IN CONST CHAR16
*ScriptPath
2055 SHELL_FILE_HANDLE FileHandle
;
2057 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2058 return (EFI_INVALID_PARAMETER
);
2061 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2062 if (EFI_ERROR(Status
)) {
2066 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
2068 ShellCloseFile(&FileHandle
);