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 Find a command line contains a split operation
76 @param[in] CmdLine The command line to parse.
78 @retval A pointer to the | character in CmdLine or NULL if not present.
83 IN CONST CHAR16
*CmdLine
86 CONST CHAR16
*TempSpot
;
88 if (StrStr(CmdLine
, L
"|") != NULL
) {
89 for (TempSpot
= CmdLine
; TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
; TempSpot
++) {
90 if (*TempSpot
== L
'^' && *(TempSpot
+1) == L
'|') {
92 } else if (*TempSpot
== L
'|') {
101 Determine if a command line contains a split operation
103 @param[in] CmdLine The command line to parse.
105 @retval TRUE CmdLine has a valid split.
106 @retval FALSE CmdLine does not have a valid split.
111 IN CONST CHAR16
*CmdLine
114 CONST CHAR16
*TempSpot
;
115 TempSpot
= FindSplit(CmdLine
);
116 return (TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
);
120 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
121 feature's enabled state was not known when the shell initially launched.
123 @retval EFI_SUCCESS The feature is enabled.
124 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
128 InternalEfiShellStartCtrlSMonitor(
132 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
133 EFI_KEY_DATA KeyData
;
136 Status
= gBS
->OpenProtocol(
137 gST
->ConsoleInHandle
,
138 &gEfiSimpleTextInputExProtocolGuid
,
142 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
143 if (EFI_ERROR(Status
)) {
148 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
149 ShellInfoObject
.HiiHandle
);
150 return (EFI_SUCCESS
);
153 KeyData
.KeyState
.KeyToggleState
= 0;
154 KeyData
.Key
.ScanCode
= 0;
155 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
156 KeyData
.Key
.UnicodeChar
= L
's';
158 Status
= SimpleEx
->RegisterKeyNotify(
161 NotificationFunction
,
162 &ShellInfoObject
.CtrlSNotifyHandle1
);
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
.CtrlSNotifyHandle2
);
172 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
173 KeyData
.Key
.UnicodeChar
= 19;
175 if (!EFI_ERROR(Status
)) {
176 Status
= SimpleEx
->RegisterKeyNotify(
179 NotificationFunction
,
180 &ShellInfoObject
.CtrlSNotifyHandle3
);
182 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
183 if (!EFI_ERROR(Status
)) {
184 Status
= SimpleEx
->RegisterKeyNotify(
187 NotificationFunction
,
188 &ShellInfoObject
.CtrlSNotifyHandle4
);
196 The entry point for the application.
198 @param[in] ImageHandle The firmware allocated handle for the EFI image.
199 @param[in] SystemTable A pointer to the EFI System Table.
201 @retval EFI_SUCCESS The entry point is executed successfully.
202 @retval other Some error occurs when executing this entry point.
208 IN EFI_HANDLE ImageHandle
,
209 IN EFI_SYSTEM_TABLE
*SystemTable
215 EFI_HANDLE ConInHandle
;
216 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
218 if (PcdGet8(PcdShellSupportLevel
) > 3) {
219 return (EFI_UNSUPPORTED
);
225 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
226 if (EFI_ERROR(Status
)) {
231 // Populate the global structure from PCDs
233 ShellInfoObject
.ImageDevPath
= NULL
;
234 ShellInfoObject
.FileDevPath
= NULL
;
235 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
236 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
237 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
240 // verify we dont allow for spec violation
242 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
245 // Initialize the LIST ENTRY objects...
247 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
248 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
249 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
252 // Check PCDs for optional features that are not implemented yet.
254 if ( PcdGetBool(PcdShellSupportOldProtocols
)
255 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
256 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
258 return (EFI_UNSUPPORTED
);
262 // turn off the watchdog timer
264 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
267 // install our console logger. This will keep a log of the output for back-browsing
269 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
270 if (!EFI_ERROR(Status
)) {
272 // Enable the cursor to be visible
274 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
277 // If supporting EFI 1.1 we need to install HII protocol
278 // only do this if PcdShellRequireHiiPlatform == FALSE
280 // remove EFI_UNSUPPORTED check above when complete.
281 ///@todo add support for Framework HII
284 // install our (solitary) HII package
286 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
287 if (ShellInfoObject
.HiiHandle
== NULL
) {
288 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
289 ///@todo Add our package into Framework HII
291 if (ShellInfoObject
.HiiHandle
== NULL
) {
292 return (EFI_NOT_STARTED
);
297 // create and install the EfiShellParametersProtocol
299 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
300 ASSERT_EFI_ERROR(Status
);
301 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
304 // create and install the EfiShellProtocol
306 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
307 ASSERT_EFI_ERROR(Status
);
308 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
311 // Now initialize the shell library (it requires Shell Parameters protocol)
313 Status
= ShellInitialize();
314 ASSERT_EFI_ERROR(Status
);
316 Status
= CommandInit();
317 ASSERT_EFI_ERROR(Status
);
320 // Check the command line
322 Status
= ProcessCommandLine();
325 // If shell support level is >= 1 create the mappings and paths
327 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
328 Status
= ShellCommandCreateInitialMappingsAndPaths();
332 // save the device path for the loaded image and the device path for the filepath (under loaded image)
333 // These are where to look for the startup.nsh file
335 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
336 ASSERT_EFI_ERROR(Status
);
339 // Display the version
341 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
344 gST
->ConOut
->Mode
->CursorRow
,
346 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
347 ShellInfoObject
.HiiHandle
,
348 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
349 gEfiShellProtocol
->MajorVersion
,
350 gEfiShellProtocol
->MinorVersion
357 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
358 ShellInfoObject
.HiiHandle
,
359 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
366 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
367 ShellInfoObject
.HiiHandle
,
368 (gST
->Hdr
.Revision
&0xffff0000)>>16,
369 (gST
->Hdr
.Revision
&0x0000ffff),
371 gST
->FirmwareRevision
376 // Display the mapping
378 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
379 Status
= RunCommand(L
"map");
380 ASSERT_EFI_ERROR(Status
);
384 // init all the built in alias'
386 Status
= SetBuiltInAlias();
387 ASSERT_EFI_ERROR(Status
);
390 // Initialize environment variables
392 if (ShellCommandGetProfileList() != NULL
) {
393 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
394 ASSERT_EFI_ERROR(Status
);
398 TempString
= AllocateZeroPool(Size
);
400 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
401 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
402 ASSERT_EFI_ERROR(Status
);
404 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
405 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
406 ASSERT_EFI_ERROR(Status
);
408 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
409 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
410 ASSERT_EFI_ERROR(Status
);
412 FreePool(TempString
);
414 if (!EFI_ERROR(Status
)) {
415 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
417 // Set up the event for CTRL-C monitoring...
419 Status
= InernalEfiShellStartMonitor();
422 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
424 // Set up the event for CTRL-S monitoring...
426 Status
= InternalEfiShellStartCtrlSMonitor();
429 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
431 // close off the gST->ConIn
433 OldConIn
= gST
->ConIn
;
434 ConInHandle
= gST
->ConsoleInHandle
;
435 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
441 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
443 // process the startup script or launch the called app.
445 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
448 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
450 // begin the UI waiting loop
454 // clean out all the memory allocated for CONST <something> * return values
455 // between each shell prompt presentation
457 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
458 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
462 // Reset page break back to default.
464 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
465 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
466 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
469 // Reset the CTRL-C event (yes we ignore the return values)
471 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
476 Status
= DoShellPrompt();
477 } while (!ShellCommandGetExit());
479 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
480 CloseSimpleTextInOnFile (gST
->ConIn
);
481 gST
->ConIn
= OldConIn
;
482 gST
->ConsoleInHandle
= ConInHandle
;
488 // uninstall protocols / free memory / etc...
490 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
491 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
492 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
494 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
495 FreePool(ShellInfoObject
.ImageDevPath
);
496 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
498 if (ShellInfoObject
.FileDevPath
!= NULL
) {
499 FreePool(ShellInfoObject
.FileDevPath
);
500 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
502 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
503 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
504 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
506 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
507 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
508 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
510 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
511 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
514 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
515 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
518 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
519 ASSERT(FALSE
); ///@todo finish this de-allocation.
522 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
523 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
524 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
527 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
528 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
529 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
532 if (ShellInfoObject
.HiiHandle
!= NULL
) {
533 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
534 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
537 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
538 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
541 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
542 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
543 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
544 FreePool(ShellInfoObject
.ConsoleInfo
);
545 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
548 if (ShellCommandGetExit()) {
549 return ((EFI_STATUS
)ShellCommandGetExitCode());
555 Sets all the alias' that were registered with the ShellCommandLib library.
557 @retval EFI_SUCCESS all init commands were run sucessfully.
565 CONST ALIAS_LIST
*List
;
569 // Get all the commands we want to run
571 List
= ShellCommandGetInitAliasList();
574 // for each command in the List
576 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
577 ; !IsNull (&List
->Link
, &Node
->Link
)
578 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
581 // install the alias'
583 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
584 ASSERT_EFI_ERROR(Status
);
586 return (EFI_SUCCESS
);
590 Internal function to determine if 2 command names are really the same.
592 @param[in] Command1 The pointer to the first command name.
593 @param[in] Command2 The pointer to the second command name.
595 @retval TRUE The 2 command names are the same.
596 @retval FALSE The 2 command names are not the same.
601 IN CONST CHAR16
*Command1
,
602 IN CONST CHAR16
*Command2
605 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
612 Internal function to determine if a command is a script only command.
614 @param[in] CommandName The pointer to the command name.
616 @retval TRUE The command is a script only command.
617 @retval FALSE The command is not a script only command.
622 IN CONST CHAR16
*CommandName
625 if (IsCommand(CommandName
, L
"for")
626 ||IsCommand(CommandName
, L
"endfor")
627 ||IsCommand(CommandName
, L
"if")
628 ||IsCommand(CommandName
, L
"else")
629 ||IsCommand(CommandName
, L
"endif")
630 ||IsCommand(CommandName
, L
"goto")) {
637 This function will populate the 2 device path protocol parameters based on the
638 global gImageHandle. The DevPath will point to the device path for the handle that has
639 loaded image protocol installed on it. The FilePath will point to the device path
640 for the file that was loaded.
642 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
643 @param[in, out] FilePath On a sucessful return the device path to the file.
645 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
646 @retval other A error from gBS->HandleProtocol.
652 GetDevicePathsForImageAndFile (
653 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
654 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
658 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
659 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
661 ASSERT(DevPath
!= NULL
);
662 ASSERT(FilePath
!= NULL
);
664 Status
= gBS
->OpenProtocol (
666 &gEfiLoadedImageProtocolGuid
,
667 (VOID
**)&LoadedImage
,
670 EFI_OPEN_PROTOCOL_GET_PROTOCOL
672 if (!EFI_ERROR (Status
)) {
673 Status
= gBS
->OpenProtocol (
674 LoadedImage
->DeviceHandle
,
675 &gEfiDevicePathProtocolGuid
,
676 (VOID
**)&ImageDevicePath
,
679 EFI_OPEN_PROTOCOL_GET_PROTOCOL
681 if (!EFI_ERROR (Status
)) {
682 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
683 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
685 LoadedImage
->DeviceHandle
,
686 &gEfiDevicePathProtocolGuid
,
692 &gEfiLoadedImageProtocolGuid
,
699 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
700 {L
"-nostartup", TypeFlag
},
701 {L
"-startup", TypeFlag
},
702 {L
"-noconsoleout", TypeFlag
},
703 {L
"-noconsolein", TypeFlag
},
704 {L
"-nointerrupt", TypeFlag
},
705 {L
"-nomap", TypeFlag
},
706 {L
"-noversion", TypeFlag
},
707 {L
"-startup", TypeFlag
},
708 {L
"-delay", TypeValue
},
709 {L
"-_exit", TypeFlag
},
714 Process all Uefi Shell 2.0 command line options.
716 see Uefi Shell 2.0 section 3.2 for full details.
718 the command line must resemble the following:
720 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
722 ShellOpt-options Options which control the initialization behavior of the shell.
723 These options are read from the EFI global variable "ShellOpt"
724 and are processed before options or file-name.
726 options Options which control the initialization behavior of the shell.
728 file-name The name of a UEFI shell application or script to be executed
729 after initialization is complete. By default, if file-name is
730 specified, then -nostartup is implied. Scripts are not supported
733 file-name-options The command-line options that are passed to file-name when it
736 This will initialize the ShellInfoObject.ShellInitSettings global variable.
738 @retval EFI_SUCCESS The variable is initialized.
749 CONST CHAR16
*TempConst
;
752 CHAR16
*ProblemParam
;
758 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
762 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
763 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
764 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateZeroPool(StrSize(TempConst
));
765 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
766 return (EFI_OUT_OF_RESOURCES
);
768 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
769 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
770 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
771 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
774 // We found the file... add the rest of the params...
776 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
777 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
778 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
782 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
783 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
784 return (EFI_OUT_OF_RESOURCES
);
786 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
788 gEfiShellParametersProtocol
->Argv
[LoopVar
],
790 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
791 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
792 return (EFI_OUT_OF_RESOURCES
);
798 ShellCommandLineFreeVarList(Package
);
800 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
801 if (EFI_ERROR(Status
)) {
802 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
803 FreePool(ProblemParam
);
804 ShellCommandLineFreeVarList(Package
);
805 return (EFI_INVALID_PARAMETER
);
809 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
810 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
811 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
812 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
813 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
814 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
815 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
816 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
817 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= ShellCommandLineGetFlag(Package
, L
"-_exit");
819 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
821 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
822 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
823 } else if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
824 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
825 if (TempConst
!= NULL
&& *TempConst
== L
':') {
828 if (TempConst
!= NULL
&& !EFI_ERROR(ShellConvertStringToUint64(TempConst
, &Intermediate
, FALSE
, FALSE
))) {
829 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)Intermediate
;
832 ShellCommandLineFreeVarList(Package
);
838 Handles all interaction with the default startup script.
840 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
842 @param ImagePath the path to the image for shell. first place to look for the startup script
843 @param FilePath the path to the file for shell. second place to look for the startup script.
845 @retval EFI_SUCCESS the variable is initialized.
850 EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
851 EFI_DEVICE_PATH_PROTOCOL
*FilePath
857 SHELL_FILE_HANDLE FileHandle
;
858 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
859 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
860 CHAR16
*FileStringPath
;
863 CONST CHAR16
*MapName
;
865 Key
.UnicodeChar
= CHAR_NULL
;
869 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
871 // launch something else instead
873 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
874 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
875 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
877 FileStringPath
= AllocateZeroPool(NewSize
);
878 if (FileStringPath
== NULL
) {
879 return (EFI_OUT_OF_RESOURCES
);
881 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
882 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
883 StrCat(FileStringPath
, L
" ");
884 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
886 Status
= RunCommand(FileStringPath
);
887 FreePool(FileStringPath
);
893 // for shell level 0 we do no scripts
894 // Without the Startup bit overriding we allow for nostartup to prevent scripts
896 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
897 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
899 return (EFI_SUCCESS
);
902 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
904 // print out our warning and see if they press a key
906 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
907 ; Delay
!= 0 && EFI_ERROR(Status
)
910 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
911 gBS
->Stall (1000000);
912 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
913 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
916 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
917 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
922 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
923 return (EFI_SUCCESS
);
927 // Try the first location (must be file system)
929 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
930 if (MapName
!= NULL
) {
931 FileStringPath
= NULL
;
933 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
934 if (FileStringPath
== NULL
) {
935 Status
= EFI_OUT_OF_RESOURCES
;
937 TempSpot
= StrStr(FileStringPath
, L
";");
938 if (TempSpot
!= NULL
) {
939 *TempSpot
= CHAR_NULL
;
941 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
942 PathRemoveLastItem(FileStringPath
);
943 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
944 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
945 FreePool(FileStringPath
);
948 if (EFI_ERROR(Status
)) {
949 NamePath
= FileDevicePath (NULL
, mStartupScript
);
950 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
956 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
960 // If we got a file, run it
962 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
963 Status
= RunScriptFileHandle (FileHandle
, mStartupScript
);
964 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
966 FileStringPath
= ShellFindFilePath(mStartupScript
);
967 if (FileStringPath
== NULL
) {
969 // we return success since we dont need to have a startup script
971 Status
= EFI_SUCCESS
;
972 ASSERT(FileHandle
== NULL
);
974 Status
= RunScriptFile(FileStringPath
);
975 FreePool(FileStringPath
);
984 Function to perform the shell prompt looping. It will do a single prompt,
985 dispatch the result, and then return. It is expected that the caller will
986 call this function in a loop many times.
989 @retval RETURN_ABORTED
1000 CONST CHAR16
*CurDir
;
1007 // Get screen setting to decide size of the command line buffer
1009 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1010 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1011 CmdLine
= AllocateZeroPool (BufferSize
);
1012 if (CmdLine
== NULL
) {
1013 return EFI_OUT_OF_RESOURCES
;
1016 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1021 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1023 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1024 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1026 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1030 // Read a line from the console
1032 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1035 // Null terminate the string and parse it
1037 if (!EFI_ERROR (Status
)) {
1038 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1039 Status
= RunCommand(CmdLine
);
1043 // Done with this command
1050 Add a buffer to the Buffer To Free List for safely returning buffers to other
1051 places without risking letting them modify internal shell information.
1053 @param Buffer Something to pass to FreePool when the shell is exiting.
1057 AddBufferToFreeList(
1061 BUFFER_LIST
*BufferListEntry
;
1063 if (Buffer
== NULL
) {
1067 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1068 ASSERT(BufferListEntry
!= NULL
);
1069 BufferListEntry
->Buffer
= Buffer
;
1070 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1075 Add a buffer to the Line History List
1077 @param Buffer The line buffer to add.
1081 AddLineToCommandHistory(
1082 IN CONST CHAR16
*Buffer
1087 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1088 ASSERT(Node
!= NULL
);
1089 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
1090 ASSERT(Node
->Buffer
!= NULL
);
1091 StrCpy(Node
->Buffer
, Buffer
);
1093 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1097 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1098 with the correct command name.
1100 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1101 command name if it was an alias. If it was not
1102 an alias it will be unchanged. This function may
1103 change the buffer to fit the command name.
1105 @retval EFI_SUCCESS The name was changed.
1106 @retval EFI_SUCCESS The name was not an alias.
1107 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1112 IN OUT CHAR16
**CommandString
1115 CONST CHAR16
*NewString
;
1117 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1118 if (NewString
== NULL
) {
1119 return (EFI_SUCCESS
);
1121 FreePool(*CommandString
);
1122 *CommandString
= AllocateZeroPool(StrSize(NewString
));
1123 if (*CommandString
== NULL
) {
1124 return (EFI_OUT_OF_RESOURCES
);
1126 StrCpy(*CommandString
, NewString
);
1127 return (EFI_SUCCESS
);
1131 Function allocates a new command line and replaces all instances of environment
1132 variable names that are correctly preset to their values.
1134 If the return value is not NULL the memory must be caller freed.
1136 @param[in] OriginalCommandLine The original command line
1138 @retval NULL An error ocurred.
1139 @return The new command line with no environment variables present.
1143 ShellConvertVariables (
1144 IN CONST CHAR16
*OriginalCommandLine
1147 CONST CHAR16
*MasterEnvList
;
1149 CHAR16
*NewCommandLine1
;
1150 CHAR16
*NewCommandLine2
;
1155 SCRIPT_FILE
*CurrentScriptFile
;
1156 ALIAS_LIST
*AliasListNode
;
1158 ASSERT(OriginalCommandLine
!= NULL
);
1161 NewSize
= StrSize(OriginalCommandLine
);
1162 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1165 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1168 // calculate the size required for the post-conversion string...
1170 if (CurrentScriptFile
!= NULL
) {
1171 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1172 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1173 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1175 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1177 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1180 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1182 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1183 NewSize
+= StrSize(AliasListNode
->CommandString
);
1189 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1190 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1191 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1193 if (StrSize(MasterEnvList
) > ItemSize
) {
1194 ItemSize
= StrSize(MasterEnvList
);
1196 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1198 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1201 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1203 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1204 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1205 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1211 // now do the replacements...
1213 NewCommandLine1
= AllocateZeroPool(NewSize
);
1214 NewCommandLine2
= AllocateZeroPool(NewSize
);
1215 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1216 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1217 SHELL_FREE_NON_NULL(NewCommandLine1
);
1218 SHELL_FREE_NON_NULL(NewCommandLine2
);
1219 SHELL_FREE_NON_NULL(ItemTemp
);
1222 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1223 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1224 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1225 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1227 StrCpy(ItemTemp
, L
"%");
1228 StrCat(ItemTemp
, MasterEnvList
);
1229 StrCat(ItemTemp
, L
"%");
1230 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1231 StrCpy(NewCommandLine1
, NewCommandLine2
);
1233 if (CurrentScriptFile
!= NULL
) {
1234 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1235 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1236 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1238 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1239 StrCpy(NewCommandLine1
, NewCommandLine2
);
1243 // Remove non-existant environment variables in scripts only
1245 for (Temp
= NewCommandLine1
; Temp
!= NULL
; ) {
1246 Temp
= StrStr(Temp
, L
"%");
1250 while (*(Temp
- 1) == L
'^') {
1251 Temp
= StrStr(Temp
+ 1, L
"%");
1260 Temp2
= StrStr(Temp
+ 1, L
"%");
1261 if (Temp2
== NULL
) {
1264 while (*(Temp2
- 1) == L
'^') {
1265 Temp2
= StrStr(Temp2
+ 1, L
"%");
1266 if (Temp2
== NULL
) {
1270 if (Temp2
== NULL
) {
1275 CopyMem(Temp
, Temp2
, StrSize(Temp2
));
1281 // Now cleanup any straggler intentionally ignored "%" characters
1283 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1284 StrCpy(NewCommandLine1
, NewCommandLine2
);
1286 FreePool(NewCommandLine2
);
1289 return (NewCommandLine1
);
1293 Internal function to run a command line with pipe usage.
1295 @param[in] CmdLine The pointer to the command line.
1296 @param[in] StdIn The pointer to the Standard input.
1297 @param[in] StdOut The pointer to the Standard output.
1299 @retval EFI_SUCCESS The split command is executed successfully.
1300 @retval other Some error occurs when executing the split command.
1305 IN CONST CHAR16
*CmdLine
,
1306 IN SHELL_FILE_HANDLE
*StdIn
,
1307 IN SHELL_FILE_HANDLE
*StdOut
1311 CHAR16
*NextCommandLine
;
1312 CHAR16
*OurCommandLine
;
1316 SHELL_FILE_HANDLE
*TempFileHandle
;
1319 ASSERT(StdOut
== NULL
);
1321 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1323 Status
= EFI_SUCCESS
;
1324 NextCommandLine
= NULL
;
1325 OurCommandLine
= NULL
;
1329 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1330 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1332 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1333 SHELL_FREE_NON_NULL(OurCommandLine
);
1334 SHELL_FREE_NON_NULL(NextCommandLine
);
1335 return (EFI_OUT_OF_RESOURCES
);
1336 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1337 SHELL_FREE_NON_NULL(OurCommandLine
);
1338 SHELL_FREE_NON_NULL(NextCommandLine
);
1339 return (EFI_INVALID_PARAMETER
);
1340 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1341 NextCommandLine
[0] == L
'a' &&
1342 NextCommandLine
[1] == L
' '
1344 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1352 // make a SPLIT_LIST item and add to list
1354 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1355 ASSERT(Split
!= NULL
);
1356 Split
->SplitStdIn
= StdIn
;
1357 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1358 ASSERT(Split
->SplitStdOut
!= NULL
);
1359 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1361 Status
= RunCommand(OurCommandLine
);
1364 // move the output from the first to the in to the second.
1366 TempFileHandle
= Split
->SplitStdOut
;
1367 if (Split
->SplitStdIn
== StdIn
) {
1368 Split
->SplitStdOut
= NULL
;
1370 Split
->SplitStdOut
= Split
->SplitStdIn
;
1372 Split
->SplitStdIn
= TempFileHandle
;
1373 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1375 if (!EFI_ERROR(Status
)) {
1376 Status
= RunCommand(NextCommandLine
);
1380 // remove the top level from the ScriptList
1382 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1383 RemoveEntryList(&Split
->Link
);
1386 // Note that the original StdIn is now the StdOut...
1388 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1389 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1391 if (Split
->SplitStdIn
!= NULL
) {
1392 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1396 FreePool(NextCommandLine
);
1397 FreePool(OurCommandLine
);
1403 Function will process and run a command line.
1405 This will determine if the command line represents an internal shell
1406 command or dispatch an external application.
1408 @param[in] CmdLine The command line to parse.
1410 @retval EFI_SUCCESS The command was completed.
1411 @retval EFI_ABORTED The command's operation was aborted.
1416 IN CONST CHAR16
*CmdLine
1420 EFI_STATUS StatusCode
;
1421 CHAR16
*CommandName
;
1422 SHELL_STATUS ShellStatus
;
1426 CHAR16 LeString
[19];
1427 CHAR16
*PostAliasCmdLine
;
1428 UINTN PostAliasSize
;
1429 CHAR16
*PostVariableCmdLine
;
1430 CHAR16
*CommandWithPath
;
1431 CONST EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
1432 CONST CHAR16
*TempLocation
;
1433 CONST CHAR16
*TempLocation2
;
1434 SHELL_FILE_HANDLE OriginalStdIn
;
1435 SHELL_FILE_HANDLE OriginalStdOut
;
1436 SHELL_FILE_HANDLE OriginalStdErr
;
1437 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
1440 CHAR16
*CleanOriginal
;
1443 ASSERT(CmdLine
!= NULL
);
1444 if (StrLen(CmdLine
) == 0) {
1445 return (EFI_SUCCESS
);
1449 PostVariableCmdLine
= NULL
;
1450 PostAliasCmdLine
= NULL
;
1451 CommandWithPath
= NULL
;
1453 Status
= EFI_SUCCESS
;
1454 CleanOriginal
= NULL
;
1457 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
1458 if (CleanOriginal
== NULL
) {
1459 return (EFI_OUT_OF_RESOURCES
);
1463 // Remove any spaces and tabs at the beginning of the string.
1465 while ((CleanOriginal
[0] == L
' ') || (CleanOriginal
[0] == L
'\t')) {
1466 CopyMem(CleanOriginal
, CleanOriginal
+1, StrSize(CleanOriginal
) - sizeof(CleanOriginal
[0]));
1470 // Handle case that passed in command line is just 1 or more " " characters.
1472 if (StrLen (CleanOriginal
) == 0) {
1473 if (CleanOriginal
!= NULL
) {
1474 FreePool(CleanOriginal
);
1475 CleanOriginal
= NULL
;
1477 return (EFI_SUCCESS
);
1481 // Remove any spaces at the end of the string.
1483 while (CleanOriginal
[StrLen(CleanOriginal
)-1] == L
' ') {
1484 CleanOriginal
[StrLen(CleanOriginal
)-1] = CHAR_NULL
;
1488 if (StrStr(CleanOriginal
, L
" ") == NULL
){
1489 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, 0);
1491 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, StrStr(CleanOriginal
, L
" ") - CleanOriginal
);
1494 ASSERT(PostAliasCmdLine
== NULL
);
1495 if (!ShellCommandIsCommandOnList(CommandName
)) {
1497 // Convert via alias
1499 Status
= ShellConvertAlias(&CommandName
);
1501 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, CommandName
, 0);
1502 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, StrStr(CleanOriginal
, L
" "), 0);
1503 ASSERT_EFI_ERROR(Status
);
1505 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, NULL
, CleanOriginal
, 0);
1508 if (CleanOriginal
!= NULL
) {
1509 FreePool(CleanOriginal
);
1510 CleanOriginal
= NULL
;
1513 if (CommandName
!= NULL
) {
1514 FreePool(CommandName
);
1518 PostVariableCmdLine
= ShellConvertVariables(PostAliasCmdLine
);
1521 // we can now free the modified by alias command line
1523 if (PostAliasCmdLine
!= NULL
) {
1524 FreePool(PostAliasCmdLine
);
1525 PostAliasCmdLine
= NULL
;
1528 if (PostVariableCmdLine
== NULL
) {
1529 return (EFI_OUT_OF_RESOURCES
);
1532 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1533 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1535 while (PostVariableCmdLine
[0] == L
' ') {
1536 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1540 // We dont do normal processing with a split command line (output from one command input to another)
1542 if (ContainsSplit(PostVariableCmdLine
)) {
1544 // are we in an existing split???
1546 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1547 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1550 if (Split
== NULL
) {
1551 Status
= RunSplitCommand(PostVariableCmdLine
, NULL
, NULL
);
1553 Status
= RunSplitCommand(PostVariableCmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1555 if (EFI_ERROR(Status
)) {
1556 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1561 // If this is a mapped drive change handle that...
1563 if (PostVariableCmdLine
[(StrLen(PostVariableCmdLine
)-1)] == L
':' && StrStr(PostVariableCmdLine
, L
" ") == NULL
) {
1564 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, PostVariableCmdLine
);
1565 if (EFI_ERROR(Status
)) {
1566 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1568 FreePool(PostVariableCmdLine
);
1572 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1573 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1577 Status
= UpdateStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1578 if (EFI_ERROR(Status
)) {
1579 if (Status
== EFI_NOT_FOUND
) {
1580 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR
), ShellInfoObject
.HiiHandle
);
1582 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_REDIR
), ShellInfoObject
.HiiHandle
);
1585 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1586 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1588 while (PostVariableCmdLine
[0] == L
' ') {
1589 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1593 // get the argc and argv updated for internal commands
1595 Status
= UpdateArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &Argv
, &Argc
);
1596 ASSERT_EFI_ERROR(Status
);
1598 for (Count
= 0 ; Count
< ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count
++) {
1599 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
], L
"-?") == ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]
1600 || (ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][0] == L
'?' && ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][1] == CHAR_NULL
)
1603 // We need to redo the arguments since a parameter was -?
1604 // move them all down 1 to the end, then up one then replace the first with help
1606 FreePool(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]);
1607 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
] = NULL
;
1608 for (Count2
= Count
; (Count2
+ 1) < ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count2
++) {
1609 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
+1];
1611 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = NULL
;
1612 for (Count2
= ShellInfoObject
.NewShellParametersProtocol
->Argc
-1 ; Count2
> 0 ; Count2
--) {
1613 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
-1];
1615 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = NULL
;
1616 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = StrnCatGrow(&ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], NULL
, L
"help", 0);
1624 if (ShellCommandIsCommandOnList(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1626 // Run the command (which was converted if it was an alias)
1628 if (!EFI_ERROR(Status
)) {
1629 Status
= ShellCommandRunCommandHandler(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], &ShellStatus
, &LastError
);
1630 ASSERT_EFI_ERROR(Status
);
1632 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1633 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellStatus
);
1635 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ShellStatus
);
1637 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1639 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1642 // Pass thru the exitcode from the app.
1644 if (ShellCommandGetExit()) {
1645 Status
= ShellStatus
;
1646 } else if (ShellStatus
!= 0 && IsScriptOnlyCommand(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1647 Status
= EFI_ABORTED
;
1652 // run an external file (or script)
1654 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], L
":") != NULL
) {
1655 ASSERT (CommandWithPath
== NULL
);
1656 if (ShellIsFile(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]) == EFI_SUCCESS
) {
1657 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1660 if (CommandWithPath
== NULL
) {
1661 CommandWithPath
= ShellFindFilePathEx(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], mExecutableExtensions
);
1663 if (CommandWithPath
== NULL
|| ShellIsDirectory(CommandWithPath
) == EFI_SUCCESS
) {
1664 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]);
1666 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1667 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", EFI_NOT_FOUND
);
1669 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", EFI_NOT_FOUND
);
1671 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1672 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1675 // Check if it's a NSH (script) file.
1677 TempLocation
= CommandWithPath
+StrLen(CommandWithPath
)-4;
1678 TempLocation2
= mScriptExtension
;
1679 if ((StrLen(CommandWithPath
) > 4) && (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0)) {
1680 Status
= RunScriptFile (CommandWithPath
);
1682 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
1683 ASSERT(DevPath
!= NULL
);
1684 Status
= InternalShellExecuteDevicePath(
1687 PostVariableCmdLine
,
1693 // Update last error status.
1695 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1696 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", StatusCode
);
1698 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", StatusCode
);
1700 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1701 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1707 // Print some error info.
1709 if (EFI_ERROR(Status
)) {
1710 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
1713 CommandName
= StrnCatGrow(&CommandName
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1715 RestoreArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, &Argv
, &Argc
);
1717 RestoreStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1719 if (CommandName
!= NULL
) {
1720 if (ShellCommandGetCurrentScriptFile() != NULL
&& !IsScriptOnlyCommand(CommandName
)) {
1722 // if this is NOT a scipt only command return success so the script won't quit.
1723 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1725 Status
= EFI_SUCCESS
;
1730 SHELL_FREE_NON_NULL(CommandName
);
1731 SHELL_FREE_NON_NULL(CommandWithPath
);
1732 SHELL_FREE_NON_NULL(PostVariableCmdLine
);
1737 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
1739 Function determins if the CommandName COULD be a valid command. It does not determine whether
1740 this is a valid command. It only checks for invalid characters.
1742 @param[in] CommandName The name to check
1744 @retval TRUE CommandName could be a command name
1745 @retval FALSE CommandName could not be a valid command name
1750 IN CONST CHAR16
*CommandName
1754 if (CommandName
== NULL
) {
1759 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
1762 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
1770 Function to process a NSH script file via SHELL_FILE_HANDLE.
1772 @param[in] Handle The handle to the already opened file.
1773 @param[in] Name The name of the script file.
1775 @retval EFI_SUCCESS the script completed sucessfully
1779 RunScriptFileHandle (
1780 IN SHELL_FILE_HANDLE Handle
,
1781 IN CONST CHAR16
*Name
1785 SCRIPT_FILE
*NewScriptFile
;
1787 CHAR16
*CommandLine
;
1788 CHAR16
*CommandLine2
;
1789 CHAR16
*CommandLine3
;
1790 SCRIPT_COMMAND_LIST
*LastCommand
;
1792 BOOLEAN PreScriptEchoState
;
1793 BOOLEAN PreCommandEchoState
;
1794 CONST CHAR16
*CurDir
;
1796 CHAR16 LeString
[50];
1798 ASSERT(!ShellCommandGetScriptExit());
1800 PreScriptEchoState
= ShellCommandGetEchoState();
1802 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
1803 if (NewScriptFile
== NULL
) {
1804 return (EFI_OUT_OF_RESOURCES
);
1810 ASSERT(NewScriptFile
->ScriptName
== NULL
);
1811 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
1812 if (NewScriptFile
->ScriptName
== NULL
) {
1813 DeleteScriptFileStruct(NewScriptFile
);
1814 return (EFI_OUT_OF_RESOURCES
);
1818 // Save the parameters (used to replace %0 to %9 later on)
1820 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
1821 if (NewScriptFile
->Argc
!= 0) {
1822 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
1823 if (NewScriptFile
->Argv
== NULL
) {
1824 DeleteScriptFileStruct(NewScriptFile
);
1825 return (EFI_OUT_OF_RESOURCES
);
1827 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
1828 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
1829 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
1830 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
1831 DeleteScriptFileStruct(NewScriptFile
);
1832 return (EFI_OUT_OF_RESOURCES
);
1836 NewScriptFile
->Argv
= NULL
;
1839 InitializeListHead(&NewScriptFile
->CommandList
);
1840 InitializeListHead(&NewScriptFile
->SubstList
);
1843 // Now build the list of all script commands.
1846 while(!ShellFileHandleEof(Handle
)) {
1847 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
1849 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0) {
1852 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
1853 if (NewScriptFile
->CurrentCommand
== NULL
) {
1854 DeleteScriptFileStruct(NewScriptFile
);
1855 return (EFI_OUT_OF_RESOURCES
);
1858 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
1859 NewScriptFile
->CurrentCommand
->Data
= NULL
;
1860 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
1862 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1866 // Add this as the topmost script file
1868 ShellCommandSetNewScript (NewScriptFile
);
1871 // Now enumerate through the commands and run each one.
1873 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1874 if (CommandLine
== NULL
) {
1875 DeleteScriptFileStruct(NewScriptFile
);
1876 return (EFI_OUT_OF_RESOURCES
);
1878 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1879 if (CommandLine2
== NULL
) {
1880 FreePool(CommandLine
);
1881 DeleteScriptFileStruct(NewScriptFile
);
1882 return (EFI_OUT_OF_RESOURCES
);
1885 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
1886 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
1887 ; // conditional increment in the body of the loop
1889 ASSERT(CommandLine2
!= NULL
);
1890 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
1893 // NULL out comments
1895 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
1896 if (*CommandLine3
== L
'^') {
1897 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
1898 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
1900 } else if (*CommandLine3
== L
'#') {
1901 *CommandLine3
= CHAR_NULL
;
1905 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
1907 // Due to variability in starting the find and replace action we need to have both buffers the same.
1909 StrCpy(CommandLine
, CommandLine2
);
1912 // Remove the %0 to %9 from the command line (if we have some arguments)
1914 if (NewScriptFile
->Argv
!= NULL
) {
1915 switch (NewScriptFile
->Argc
) {
1917 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
1918 ASSERT_EFI_ERROR(Status
);
1920 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
1921 ASSERT_EFI_ERROR(Status
);
1923 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
1924 ASSERT_EFI_ERROR(Status
);
1926 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
1927 ASSERT_EFI_ERROR(Status
);
1929 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
1930 ASSERT_EFI_ERROR(Status
);
1932 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
1933 ASSERT_EFI_ERROR(Status
);
1935 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
1936 ASSERT_EFI_ERROR(Status
);
1938 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
1939 ASSERT_EFI_ERROR(Status
);
1941 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
1942 ASSERT_EFI_ERROR(Status
);
1944 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
1945 ASSERT_EFI_ERROR(Status
);
1951 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
1952 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
1953 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
1954 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
1955 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
1956 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
1957 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
1958 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
1959 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
1961 StrCpy(CommandLine2
, CommandLine
);
1963 LastCommand
= NewScriptFile
->CurrentCommand
;
1965 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
1967 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
1969 // This line is a goto target / label
1972 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
1973 if (CommandLine3
[0] == L
'@') {
1975 // We need to save the current echo state
1976 // and disable echo for just this command.
1978 PreCommandEchoState
= ShellCommandGetEchoState();
1979 ShellCommandSetEchoState(FALSE
);
1980 Status
= RunCommand(CommandLine3
+1);
1983 // If command was "@echo -off" or "@echo -on" then don't restore echo state
1985 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
1986 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
1988 // Now restore the pre-'@' echo state.
1990 ShellCommandSetEchoState(PreCommandEchoState
);
1993 if (ShellCommandGetEchoState()) {
1994 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1995 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1996 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1998 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2000 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2002 Status
= RunCommand(CommandLine3
);
2006 if (ShellCommandGetScriptExit()) {
2008 // ShellCommandGetExitCode() always returns a UINT64
2010 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
2011 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2012 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2014 ShellCommandRegisterExit(FALSE
, 0);
2015 Status
= EFI_SUCCESS
;
2018 if (ShellGetExecutionBreakFlag()) {
2021 if (EFI_ERROR(Status
)) {
2024 if (ShellCommandGetExit()) {
2029 // If that commend did not update the CurrentCommand then we need to advance it...
2031 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2032 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2033 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2034 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2038 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2039 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2040 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2046 FreePool(CommandLine
);
2047 FreePool(CommandLine2
);
2048 ShellCommandSetNewScript (NULL
);
2051 // Only if this was the last script reset the state.
2053 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2054 ShellCommandSetEchoState(PreScriptEchoState
);
2056 return (EFI_SUCCESS
);
2060 Function to process a NSH script file.
2062 @param[in] ScriptPath Pointer to the script file name (including file system path).
2064 @retval EFI_SUCCESS the script completed sucessfully
2069 IN CONST CHAR16
*ScriptPath
2073 SHELL_FILE_HANDLE FileHandle
;
2075 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2076 return (EFI_INVALID_PARAMETER
);
2079 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2080 if (EFI_ERROR(Status
)) {
2084 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
2086 ShellCloseFile(&FileHandle
);