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 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
75 feature's enabled state was not known when the shell initially launched.
77 @retval EFI_SUCCESS The feature is enabled.
78 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
82 InternalEfiShellStartCtrlSMonitor(
86 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
90 Status
= gBS
->OpenProtocol(
92 &gEfiSimpleTextInputExProtocolGuid
,
96 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
97 if (EFI_ERROR(Status
)) {
102 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
103 ShellInfoObject
.HiiHandle
);
104 return (EFI_SUCCESS
);
107 KeyData
.KeyState
.KeyToggleState
= 0;
108 KeyData
.Key
.ScanCode
= 0;
109 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
110 KeyData
.Key
.UnicodeChar
= L
's';
112 Status
= SimpleEx
->RegisterKeyNotify(
115 NotificationFunction
,
116 &ShellInfoObject
.CtrlSNotifyHandle1
);
118 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
119 if (!EFI_ERROR(Status
)) {
120 Status
= SimpleEx
->RegisterKeyNotify(
123 NotificationFunction
,
124 &ShellInfoObject
.CtrlSNotifyHandle2
);
126 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
127 KeyData
.Key
.UnicodeChar
= 19;
129 if (!EFI_ERROR(Status
)) {
130 Status
= SimpleEx
->RegisterKeyNotify(
133 NotificationFunction
,
134 &ShellInfoObject
.CtrlSNotifyHandle2
);
136 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
137 if (!EFI_ERROR(Status
)) {
138 Status
= SimpleEx
->RegisterKeyNotify(
141 NotificationFunction
,
142 &ShellInfoObject
.CtrlSNotifyHandle2
);
150 The entry point for the application.
152 @param[in] ImageHandle The firmware allocated handle for the EFI image.
153 @param[in] SystemTable A pointer to the EFI System Table.
155 @retval EFI_SUCCESS The entry point is executed successfully.
156 @retval other Some error occurs when executing this entry point.
162 IN EFI_HANDLE ImageHandle
,
163 IN EFI_SYSTEM_TABLE
*SystemTable
169 EFI_HANDLE ConInHandle
;
170 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
172 if (PcdGet8(PcdShellSupportLevel
) > 3) {
173 return (EFI_UNSUPPORTED
);
179 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
180 if (EFI_ERROR(Status
)) {
185 // Populate the global structure from PCDs
187 ShellInfoObject
.ImageDevPath
= NULL
;
188 ShellInfoObject
.FileDevPath
= NULL
;
189 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
190 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
191 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
194 // verify we dont allow for spec violation
196 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
199 // Initialize the LIST ENTRY objects...
201 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
202 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
203 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
206 // Check PCDs for optional features that are not implemented yet.
208 if ( PcdGetBool(PcdShellSupportOldProtocols
)
209 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
210 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
212 return (EFI_UNSUPPORTED
);
216 // turn off the watchdog timer
218 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
221 // install our console logger. This will keep a log of the output for back-browsing
223 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
224 if (!EFI_ERROR(Status
)) {
226 // Enable the cursor to be visible
228 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
231 // If supporting EFI 1.1 we need to install HII protocol
232 // only do this if PcdShellRequireHiiPlatform == FALSE
234 // remove EFI_UNSUPPORTED check above when complete.
235 ///@todo add support for Framework HII
238 // install our (solitary) HII package
240 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
241 if (ShellInfoObject
.HiiHandle
== NULL
) {
242 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
243 ///@todo Add our package into Framework HII
245 if (ShellInfoObject
.HiiHandle
== NULL
) {
246 return (EFI_NOT_STARTED
);
251 // create and install the EfiShellParametersProtocol
253 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
254 ASSERT_EFI_ERROR(Status
);
255 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
258 // create and install the EfiShellProtocol
260 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
261 ASSERT_EFI_ERROR(Status
);
262 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
265 // Now initialize the shell library (it requires Shell Parameters protocol)
267 Status
= ShellInitialize();
268 ASSERT_EFI_ERROR(Status
);
270 Status
= CommandInit();
271 ASSERT_EFI_ERROR(Status
);
274 // Check the command line
276 Status
= ProcessCommandLine();
279 // If shell support level is >= 1 create the mappings and paths
281 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
282 Status
= ShellCommandCreateInitialMappingsAndPaths();
286 // save the device path for the loaded image and the device path for the filepath (under loaded image)
287 // These are where to look for the startup.nsh file
289 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
290 ASSERT_EFI_ERROR(Status
);
293 // Display the version
295 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
298 gST
->ConOut
->Mode
->CursorRow
,
300 STRING_TOKEN (STR_VER_OUTPUT_MAIN
),
301 ShellInfoObject
.HiiHandle
,
302 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
303 gEfiShellProtocol
->MajorVersion
,
304 gEfiShellProtocol
->MinorVersion
,
305 (gST
->Hdr
.Revision
&0xffff0000)>>16,
306 (gST
->Hdr
.Revision
&0x0000ffff),
308 gST
->FirmwareRevision
313 // Display the mapping
315 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
316 Status
= RunCommand(L
"map");
317 ASSERT_EFI_ERROR(Status
);
321 // init all the built in alias'
323 Status
= SetBuiltInAlias();
324 ASSERT_EFI_ERROR(Status
);
327 // Initialize environment variables
329 if (ShellCommandGetProfileList() != NULL
) {
330 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
331 ASSERT_EFI_ERROR(Status
);
335 TempString
= AllocateZeroPool(Size
);
337 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
338 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
339 ASSERT_EFI_ERROR(Status
);
341 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
342 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
343 ASSERT_EFI_ERROR(Status
);
345 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
346 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
347 ASSERT_EFI_ERROR(Status
);
349 FreePool(TempString
);
351 if (!EFI_ERROR(Status
)) {
352 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
354 // Set up the event for CTRL-C monitoring...
356 Status
= InernalEfiShellStartMonitor();
359 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
361 // Set up the event for CTRL-S monitoring...
363 Status
= InternalEfiShellStartCtrlSMonitor();
366 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
368 // close off the gST->ConIn
370 OldConIn
= gST
->ConIn
;
371 ConInHandle
= gST
->ConsoleInHandle
;
372 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
378 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
380 // process the startup script or launch the called app.
382 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
385 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
387 // begin the UI waiting loop
391 // clean out all the memory allocated for CONST <something> * return values
392 // between each shell prompt presentation
394 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
395 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
399 // Reset page break back to default.
401 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
402 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
403 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
406 // Reset the CTRL-C event (yes we ignore the return values)
408 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
413 Status
= DoShellPrompt();
414 } while (!ShellCommandGetExit());
416 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
417 CloseSimpleTextInOnFile (gST
->ConIn
);
418 gST
->ConIn
= OldConIn
;
419 gST
->ConsoleInHandle
= ConInHandle
;
425 // uninstall protocols / free memory / etc...
427 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
428 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
429 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
431 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
432 FreePool(ShellInfoObject
.ImageDevPath
);
433 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
435 if (ShellInfoObject
.FileDevPath
!= NULL
) {
436 FreePool(ShellInfoObject
.FileDevPath
);
437 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
439 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
440 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
441 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
443 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
444 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
445 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
447 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
448 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
451 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
452 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
455 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
456 ASSERT(FALSE
); ///@todo finish this de-allocation.
459 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
460 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
461 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
464 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
465 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
466 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
469 if (ShellInfoObject
.HiiHandle
!= NULL
) {
470 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
471 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
474 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
475 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
478 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
479 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
480 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
481 FreePool(ShellInfoObject
.ConsoleInfo
);
482 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
485 if (ShellCommandGetExit()) {
486 return ((EFI_STATUS
)ShellCommandGetExitCode());
492 Sets all the alias' that were registered with the ShellCommandLib library.
494 @retval EFI_SUCCESS all init commands were run sucessfully.
502 CONST ALIAS_LIST
*List
;
506 // Get all the commands we want to run
508 List
= ShellCommandGetInitAliasList();
511 // for each command in the List
513 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
514 ; !IsNull (&List
->Link
, &Node
->Link
)
515 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
518 // install the alias'
520 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
521 ASSERT_EFI_ERROR(Status
);
523 return (EFI_SUCCESS
);
527 Internal function to determine if 2 command names are really the same.
529 @param[in] Command1 The pointer to the first command name.
530 @param[in] Command2 The pointer to the second command name.
532 @retval TRUE The 2 command names are the same.
533 @retval FALSE The 2 command names are not the same.
538 IN CONST CHAR16
*Command1
,
539 IN CONST CHAR16
*Command2
542 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
549 Internal function to determine if a command is a script only command.
551 @param[in] CommandName The pointer to the command name.
553 @retval TRUE The command is a script only command.
554 @retval FALSE The command is not a script only command.
559 IN CONST CHAR16
*CommandName
562 if (IsCommand(CommandName
, L
"for")
563 ||IsCommand(CommandName
, L
"endfor")
564 ||IsCommand(CommandName
, L
"if")
565 ||IsCommand(CommandName
, L
"else")
566 ||IsCommand(CommandName
, L
"endif")
567 ||IsCommand(CommandName
, L
"goto")) {
574 This function will populate the 2 device path protocol parameters based on the
575 global gImageHandle. The DevPath will point to the device path for the handle that has
576 loaded image protocol installed on it. The FilePath will point to the device path
577 for the file that was loaded.
579 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
580 @param[in, out] FilePath On a sucessful return the device path to the file.
582 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
583 @retval other A error from gBS->HandleProtocol.
589 GetDevicePathsForImageAndFile (
590 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
591 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
595 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
596 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
598 ASSERT(DevPath
!= NULL
);
599 ASSERT(FilePath
!= NULL
);
601 Status
= gBS
->OpenProtocol (
603 &gEfiLoadedImageProtocolGuid
,
604 (VOID
**)&LoadedImage
,
607 EFI_OPEN_PROTOCOL_GET_PROTOCOL
609 if (!EFI_ERROR (Status
)) {
610 Status
= gBS
->OpenProtocol (
611 LoadedImage
->DeviceHandle
,
612 &gEfiDevicePathProtocolGuid
,
613 (VOID
**)&ImageDevicePath
,
616 EFI_OPEN_PROTOCOL_GET_PROTOCOL
618 if (!EFI_ERROR (Status
)) {
619 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
620 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
622 LoadedImage
->DeviceHandle
,
623 &gEfiDevicePathProtocolGuid
,
629 &gEfiLoadedImageProtocolGuid
,
636 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
637 {L
"-nostartup", TypeFlag
},
638 {L
"-startup", TypeFlag
},
639 {L
"-noconsoleout", TypeFlag
},
640 {L
"-noconsolein", TypeFlag
},
641 {L
"-nointerrupt", TypeFlag
},
642 {L
"-nomap", TypeFlag
},
643 {L
"-noversion", TypeFlag
},
644 {L
"-startup", TypeFlag
},
645 {L
"-delay", TypeValue
},
646 {L
"-_exit", TypeFlag
},
651 Process all Uefi Shell 2.0 command line options.
653 see Uefi Shell 2.0 section 3.2 for full details.
655 the command line must resemble the following:
657 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
659 ShellOpt-options Options which control the initialization behavior of the shell.
660 These options are read from the EFI global variable "ShellOpt"
661 and are processed before options or file-name.
663 options Options which control the initialization behavior of the shell.
665 file-name The name of a UEFI shell application or script to be executed
666 after initialization is complete. By default, if file-name is
667 specified, then -nostartup is implied. Scripts are not supported
670 file-name-options The command-line options that are passed to file-name when it
673 This will initialize the ShellInfoObject.ShellInitSettings global variable.
675 @retval EFI_SUCCESS The variable is initialized.
686 CONST CHAR16
*TempConst
;
689 CHAR16
*ProblemParam
;
695 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
699 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
700 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
701 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateZeroPool(StrSize(TempConst
));
702 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
703 return (EFI_OUT_OF_RESOURCES
);
705 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
706 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
707 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
708 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
711 // We found the file... add the rest of the params...
713 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
714 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
715 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
719 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
720 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
721 return (EFI_OUT_OF_RESOURCES
);
723 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
725 gEfiShellParametersProtocol
->Argv
[LoopVar
],
727 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
728 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
729 return (EFI_OUT_OF_RESOURCES
);
735 ShellCommandLineFreeVarList(Package
);
737 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
738 if (EFI_ERROR(Status
)) {
739 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
740 FreePool(ProblemParam
);
741 ShellCommandLineFreeVarList(Package
);
742 return (EFI_INVALID_PARAMETER
);
746 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
747 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
748 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
749 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
750 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
751 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
752 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
753 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
754 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= ShellCommandLineGetFlag(Package
, L
"-_exit");
756 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
758 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
759 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
760 } else if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
761 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
762 if (TempConst
!= NULL
&& *TempConst
== L
':') {
765 if (TempConst
!= NULL
&& !EFI_ERROR(ShellConvertStringToUint64(TempConst
, &Intermediate
, FALSE
, FALSE
))) {
766 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)Intermediate
;
769 ShellCommandLineFreeVarList(Package
);
775 Handles all interaction with the default startup script.
777 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
779 @param ImagePath the path to the image for shell. first place to look for the startup script
780 @param FilePath the path to the file for shell. second place to look for the startup script.
782 @retval EFI_SUCCESS the variable is initialized.
787 EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
788 EFI_DEVICE_PATH_PROTOCOL
*FilePath
794 SHELL_FILE_HANDLE FileHandle
;
795 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
796 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
797 CHAR16
*FileStringPath
;
800 CONST CHAR16
*MapName
;
802 Key
.UnicodeChar
= CHAR_NULL
;
806 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
808 // launch something else instead
810 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
811 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
812 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
814 FileStringPath
= AllocateZeroPool(NewSize
);
815 if (FileStringPath
== NULL
) {
816 return (EFI_OUT_OF_RESOURCES
);
818 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
819 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
820 StrCat(FileStringPath
, L
" ");
821 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
823 Status
= RunCommand(FileStringPath
);
824 FreePool(FileStringPath
);
830 // for shell level 0 we do no scripts
831 // Without the Startup bit overriding we allow for nostartup to prevent scripts
833 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
834 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
836 return (EFI_SUCCESS
);
839 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
841 // print out our warning and see if they press a key
843 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
* 10
844 ; Delay
!= 0 && EFI_ERROR(Status
)
847 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
/10);
849 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
850 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
853 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
854 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
859 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
860 return (EFI_SUCCESS
);
864 // Try the first location (must be file system)
866 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
867 if (MapName
!= NULL
) {
868 FileStringPath
= NULL
;
870 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
871 if (FileStringPath
== NULL
) {
872 Status
= EFI_OUT_OF_RESOURCES
;
874 TempSpot
= StrStr(FileStringPath
, L
";");
875 if (TempSpot
!= NULL
) {
876 *TempSpot
= CHAR_NULL
;
878 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
879 PathRemoveLastItem(FileStringPath
);
880 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
881 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
882 FreePool(FileStringPath
);
885 if (EFI_ERROR(Status
)) {
886 NamePath
= FileDevicePath (NULL
, mStartupScript
);
887 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
893 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
897 // If we got a file, run it
899 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
900 Status
= RunScriptFileHandle (FileHandle
, mStartupScript
);
901 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
903 FileStringPath
= ShellFindFilePath(mStartupScript
);
904 if (FileStringPath
== NULL
) {
906 // we return success since we dont need to have a startup script
908 Status
= EFI_SUCCESS
;
909 ASSERT(FileHandle
== NULL
);
911 Status
= RunScriptFile(FileStringPath
);
912 FreePool(FileStringPath
);
921 Function to perform the shell prompt looping. It will do a single prompt,
922 dispatch the result, and then return. It is expected that the caller will
923 call this function in a loop many times.
926 @retval RETURN_ABORTED
937 CONST CHAR16
*CurDir
;
944 // Get screen setting to decide size of the command line buffer
946 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
947 BufferSize
= Column
* Row
* sizeof (CHAR16
);
948 CmdLine
= AllocateZeroPool (BufferSize
);
949 if (CmdLine
== NULL
) {
950 return EFI_OUT_OF_RESOURCES
;
953 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
958 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
960 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
961 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
963 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
967 // Read a line from the console
969 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
972 // Null terminate the string and parse it
974 if (!EFI_ERROR (Status
)) {
975 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
976 Status
= RunCommand(CmdLine
);
980 // Done with this command
987 Add a buffer to the Buffer To Free List for safely returning buffers to other
988 places without risking letting them modify internal shell information.
990 @param Buffer Something to pass to FreePool when the shell is exiting.
998 BUFFER_LIST
*BufferListEntry
;
1000 if (Buffer
== NULL
) {
1004 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1005 ASSERT(BufferListEntry
!= NULL
);
1006 BufferListEntry
->Buffer
= Buffer
;
1007 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1012 Add a buffer to the Line History List
1014 @param Buffer The line buffer to add.
1018 AddLineToCommandHistory(
1019 IN CONST CHAR16
*Buffer
1024 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1025 ASSERT(Node
!= NULL
);
1026 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
1027 ASSERT(Node
->Buffer
!= NULL
);
1028 StrCpy(Node
->Buffer
, Buffer
);
1030 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1034 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1035 with the correct command name.
1037 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1038 command name if it was an alias. If it was not
1039 an alias it will be unchanged. This function may
1040 change the buffer to fit the command name.
1042 @retval EFI_SUCCESS The name was changed.
1043 @retval EFI_SUCCESS The name was not an alias.
1044 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1049 IN OUT CHAR16
**CommandString
1052 CONST CHAR16
*NewString
;
1054 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1055 if (NewString
== NULL
) {
1056 return (EFI_SUCCESS
);
1058 FreePool(*CommandString
);
1059 *CommandString
= AllocateZeroPool(StrSize(NewString
));
1060 if (*CommandString
== NULL
) {
1061 return (EFI_OUT_OF_RESOURCES
);
1063 StrCpy(*CommandString
, NewString
);
1064 return (EFI_SUCCESS
);
1068 Function allocates a new command line and replaces all instances of environment
1069 variable names that are correctly preset to their values.
1071 If the return value is not NULL the memory must be caller freed.
1073 @param[in] OriginalCommandLine The original command line
1075 @retval NULL An error ocurred.
1076 @return The new command line with no environment variables present.
1080 ShellConvertVariables (
1081 IN CONST CHAR16
*OriginalCommandLine
1084 CONST CHAR16
*MasterEnvList
;
1086 CHAR16
*NewCommandLine1
;
1087 CHAR16
*NewCommandLine2
;
1092 SCRIPT_FILE
*CurrentScriptFile
;
1093 ALIAS_LIST
*AliasListNode
;
1095 ASSERT(OriginalCommandLine
!= NULL
);
1098 NewSize
= StrSize(OriginalCommandLine
);
1099 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1102 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1105 // calculate the size required for the post-conversion string...
1107 if (CurrentScriptFile
!= NULL
) {
1108 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1109 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1110 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1112 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1114 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1117 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1119 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1120 NewSize
+= StrSize(AliasListNode
->CommandString
);
1126 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1127 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1128 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1130 if (StrSize(MasterEnvList
) > ItemSize
) {
1131 ItemSize
= StrSize(MasterEnvList
);
1133 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1135 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1138 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1140 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1141 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1142 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1148 // now do the replacements...
1150 NewCommandLine1
= AllocateZeroPool(NewSize
);
1151 NewCommandLine2
= AllocateZeroPool(NewSize
);
1152 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1153 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1154 SHELL_FREE_NON_NULL(NewCommandLine1
);
1155 SHELL_FREE_NON_NULL(NewCommandLine2
);
1156 SHELL_FREE_NON_NULL(ItemTemp
);
1159 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1160 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1161 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1162 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1164 StrCpy(ItemTemp
, L
"%");
1165 StrCat(ItemTemp
, MasterEnvList
);
1166 StrCat(ItemTemp
, L
"%");
1167 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1168 StrCpy(NewCommandLine1
, NewCommandLine2
);
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 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1176 StrCpy(NewCommandLine1
, NewCommandLine2
);
1180 // Remove non-existant environment variables in scripts only
1182 for (Temp
= NewCommandLine1
; Temp
!= NULL
; ) {
1183 Temp
= StrStr(Temp
, L
"%");
1187 while (*(Temp
- 1) == L
'^') {
1188 Temp
= StrStr(Temp
+ 1, L
"%");
1197 Temp2
= StrStr(Temp
+ 1, L
"%");
1198 if (Temp2
== NULL
) {
1201 while (*(Temp2
- 1) == L
'^') {
1202 Temp2
= StrStr(Temp2
+ 1, L
"%");
1203 if (Temp2
== NULL
) {
1207 if (Temp2
== NULL
) {
1212 CopyMem(Temp
, Temp2
, StrSize(Temp2
));
1218 // Now cleanup any straggler intentionally ignored "%" characters
1220 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1221 StrCpy(NewCommandLine1
, NewCommandLine2
);
1223 FreePool(NewCommandLine2
);
1226 return (NewCommandLine1
);
1230 Internal function to run a command line with pipe usage.
1232 @param[in] CmdLine The pointer to the command line.
1233 @param[in] StdIn The pointer to the Standard input.
1234 @param[in] StdOut The pointer to the Standard output.
1236 @retval EFI_SUCCESS The split command is executed successfully.
1237 @retval other Some error occurs when executing the split command.
1242 IN CONST CHAR16
*CmdLine
,
1243 IN SHELL_FILE_HANDLE
*StdIn
,
1244 IN SHELL_FILE_HANDLE
*StdOut
1248 CHAR16
*NextCommandLine
;
1249 CHAR16
*OurCommandLine
;
1253 SHELL_FILE_HANDLE
*TempFileHandle
;
1256 ASSERT(StdOut
== NULL
);
1258 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1260 Status
= EFI_SUCCESS
;
1261 NextCommandLine
= NULL
;
1262 OurCommandLine
= NULL
;
1266 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1267 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1269 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1270 SHELL_FREE_NON_NULL(OurCommandLine
);
1271 SHELL_FREE_NON_NULL(NextCommandLine
);
1272 return (EFI_OUT_OF_RESOURCES
);
1273 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1274 SHELL_FREE_NON_NULL(OurCommandLine
);
1275 SHELL_FREE_NON_NULL(NextCommandLine
);
1276 return (EFI_INVALID_PARAMETER
);
1277 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1278 NextCommandLine
[0] == L
'a' &&
1279 NextCommandLine
[1] == L
' '
1281 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1289 // make a SPLIT_LIST item and add to list
1291 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1292 ASSERT(Split
!= NULL
);
1293 Split
->SplitStdIn
= StdIn
;
1294 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1295 ASSERT(Split
->SplitStdOut
!= NULL
);
1296 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1298 Status
= RunCommand(OurCommandLine
);
1301 // move the output from the first to the in to the second.
1303 TempFileHandle
= Split
->SplitStdOut
;
1304 if (Split
->SplitStdIn
== StdIn
) {
1305 Split
->SplitStdOut
= NULL
;
1307 Split
->SplitStdOut
= Split
->SplitStdIn
;
1309 Split
->SplitStdIn
= TempFileHandle
;
1310 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1312 if (!EFI_ERROR(Status
)) {
1313 Status
= RunCommand(NextCommandLine
);
1317 // remove the top level from the ScriptList
1319 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1320 RemoveEntryList(&Split
->Link
);
1323 // Note that the original StdIn is now the StdOut...
1325 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1326 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1328 if (Split
->SplitStdIn
!= NULL
) {
1329 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1333 FreePool(NextCommandLine
);
1334 FreePool(OurCommandLine
);
1340 Function will process and run a command line.
1342 This will determine if the command line represents an internal shell
1343 command or dispatch an external application.
1345 @param[in] CmdLine The command line to parse.
1347 @retval EFI_SUCCESS The command was completed.
1348 @retval EFI_ABORTED The command's operation was aborted.
1353 IN CONST CHAR16
*CmdLine
1357 EFI_STATUS StatusCode
;
1358 CHAR16
*CommandName
;
1359 SHELL_STATUS ShellStatus
;
1363 CHAR16 LeString
[19];
1364 CHAR16
*PostAliasCmdLine
;
1365 UINTN PostAliasSize
;
1366 CHAR16
*PostVariableCmdLine
;
1367 CHAR16
*CommandWithPath
;
1368 CONST EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
1369 CONST CHAR16
*TempLocation
;
1370 CONST CHAR16
*TempLocation2
;
1371 SHELL_FILE_HANDLE OriginalStdIn
;
1372 SHELL_FILE_HANDLE OriginalStdOut
;
1373 SHELL_FILE_HANDLE OriginalStdErr
;
1374 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
1375 CHAR16
*TempLocation3
;
1378 CHAR16
*CleanOriginal
;
1381 ASSERT(CmdLine
!= NULL
);
1382 if (StrLen(CmdLine
) == 0) {
1383 return (EFI_SUCCESS
);
1387 PostVariableCmdLine
= NULL
;
1388 PostAliasCmdLine
= NULL
;
1389 CommandWithPath
= NULL
;
1391 Status
= EFI_SUCCESS
;
1392 CleanOriginal
= NULL
;
1395 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
1396 if (CleanOriginal
== NULL
) {
1397 return (EFI_OUT_OF_RESOURCES
);
1399 while (CleanOriginal
[StrLen(CleanOriginal
)-1] == L
' ') {
1400 CleanOriginal
[StrLen(CleanOriginal
)-1] = CHAR_NULL
;
1402 while (CleanOriginal
[0] == L
' ') {
1403 CopyMem(CleanOriginal
, CleanOriginal
+1, StrSize(CleanOriginal
) - sizeof(CleanOriginal
[0]));
1407 if (StrStr(CleanOriginal
, L
" ") == NULL
){
1408 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, 0);
1410 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, StrStr(CleanOriginal
, L
" ") - CleanOriginal
);
1413 ASSERT(PostAliasCmdLine
== NULL
);
1414 if (!ShellCommandIsCommandOnList(CommandName
)) {
1416 // Convert via alias
1418 Status
= ShellConvertAlias(&CommandName
);
1420 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, CommandName
, 0);
1421 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, StrStr(CleanOriginal
, L
" "), 0);
1422 ASSERT_EFI_ERROR(Status
);
1424 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, NULL
, CleanOriginal
, 0);
1427 if (CleanOriginal
!= NULL
) {
1428 FreePool(CleanOriginal
);
1429 CleanOriginal
= NULL
;
1432 if (CommandName
!= NULL
) {
1433 FreePool(CommandName
);
1437 PostVariableCmdLine
= ShellConvertVariables(PostAliasCmdLine
);
1440 // we can now free the modified by alias command line
1442 if (PostAliasCmdLine
!= NULL
) {
1443 FreePool(PostAliasCmdLine
);
1444 PostAliasCmdLine
= NULL
;
1447 if (PostVariableCmdLine
== NULL
) {
1448 return (EFI_OUT_OF_RESOURCES
);
1451 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1452 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1454 while (PostVariableCmdLine
[0] == L
' ') {
1455 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1459 // We dont do normal processing with a split command line (output from one command input to another)
1461 TempLocation3
= NULL
;
1462 if (StrStr(PostVariableCmdLine
, L
"|") != NULL
) {
1463 for (TempLocation3
= PostVariableCmdLine
; TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
; TempLocation3
++) {
1464 if (*TempLocation3
== L
'^' && *(TempLocation3
+1) == L
'|') {
1466 } else if (*TempLocation3
== L
'|') {
1471 if (TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
) {
1473 // are we in an existing split???
1475 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1476 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1479 if (Split
== NULL
) {
1480 Status
= RunSplitCommand(PostVariableCmdLine
, NULL
, NULL
);
1482 Status
= RunSplitCommand(PostVariableCmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1484 if (EFI_ERROR(Status
)) {
1485 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1490 // If this is a mapped drive change handle that...
1492 if (PostVariableCmdLine
[(StrLen(PostVariableCmdLine
)-1)] == L
':' && StrStr(PostVariableCmdLine
, L
" ") == NULL
) {
1493 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, PostVariableCmdLine
);
1494 if (EFI_ERROR(Status
)) {
1495 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1497 FreePool(PostVariableCmdLine
);
1501 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1502 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1506 Status
= UpdateStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1507 if (EFI_ERROR(Status
)) {
1508 if (Status
== EFI_NOT_FOUND
) {
1509 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR
), ShellInfoObject
.HiiHandle
);
1511 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_REDIR
), ShellInfoObject
.HiiHandle
);
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 // get the argc and argv updated for internal commands
1524 Status
= UpdateArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &Argv
, &Argc
);
1525 ASSERT_EFI_ERROR(Status
);
1527 for (Count
= 0 ; Count
< ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count
++) {
1528 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
], L
"-?") == ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]
1529 || (ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][0] == L
'?' && ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][1] == CHAR_NULL
)
1532 // We need to redo the arguments since a parameter was -?
1533 // move them all down 1 to the end, then up one then replace the first with help
1535 FreePool(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]);
1536 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
] = NULL
;
1537 for (Count2
= Count
; (Count2
+ 1) < ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count2
++) {
1538 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
+1];
1540 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = NULL
;
1541 for (Count2
= ShellInfoObject
.NewShellParametersProtocol
->Argc
-1 ; Count2
> 0 ; Count2
--) {
1542 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
-1];
1544 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = NULL
;
1545 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = StrnCatGrow(&ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], NULL
, L
"help", 0);
1553 if (ShellCommandIsCommandOnList(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1555 // Run the command (which was converted if it was an alias)
1557 if (!EFI_ERROR(Status
)) {
1558 Status
= ShellCommandRunCommandHandler(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], &ShellStatus
, &LastError
);
1559 ASSERT_EFI_ERROR(Status
);
1560 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%08Lx", ShellStatus
);
1561 DEBUG_CODE(InternalEfiShellSetEnv(L
"DebugLasterror", LeString
, TRUE
););
1563 InternalEfiShellSetEnv(L
"Lasterror", LeString
, TRUE
);
1566 // Pass thru the exitcode from the app.
1568 if (ShellCommandGetExit()) {
1569 Status
= ShellStatus
;
1570 } else if (ShellStatus
!= 0 && IsScriptOnlyCommand(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1571 Status
= EFI_ABORTED
;
1576 // run an external file (or script)
1578 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], L
":") != NULL
) {
1579 ASSERT (CommandWithPath
== NULL
);
1580 if (ShellIsFile(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]) == EFI_SUCCESS
) {
1581 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1584 if (CommandWithPath
== NULL
) {
1585 CommandWithPath
= ShellFindFilePathEx(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], mExecutableExtensions
);
1587 if (CommandWithPath
== NULL
|| ShellIsDirectory(CommandWithPath
) == EFI_SUCCESS
) {
1588 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]);
1591 // Check if it's a NSH (script) file.
1593 TempLocation
= CommandWithPath
+StrLen(CommandWithPath
)-4;
1594 TempLocation2
= mScriptExtension
;
1595 if ((StrLen(CommandWithPath
) > 4) && (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0)) {
1596 Status
= RunScriptFile (CommandWithPath
);
1598 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
1599 ASSERT(DevPath
!= NULL
);
1600 Status
= InternalShellExecuteDevicePath(
1603 PostVariableCmdLine
,
1609 // Updatet last error status.
1611 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%08Lx", StatusCode
);
1612 DEBUG_CODE(InternalEfiShellSetEnv(L
"DebugLasterror", LeString
, TRUE
););
1613 InternalEfiShellSetEnv(L
"Lasterror", LeString
, TRUE
);
1619 // Print some error info.
1621 if (EFI_ERROR(Status
)) {
1622 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
1625 CommandName
= StrnCatGrow(&CommandName
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1627 RestoreArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, &Argv
, &Argc
);
1629 RestoreStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1631 if (CommandName
!= NULL
) {
1632 if (ShellCommandGetCurrentScriptFile() != NULL
&& !IsScriptOnlyCommand(CommandName
)) {
1634 // if this is NOT a scipt only command return success so the script won't quit.
1635 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1637 Status
= EFI_SUCCESS
;
1642 SHELL_FREE_NON_NULL(CommandName
);
1643 SHELL_FREE_NON_NULL(CommandWithPath
);
1644 SHELL_FREE_NON_NULL(PostVariableCmdLine
);
1649 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
1651 Function determins if the CommandName COULD be a valid command. It does not determine whether
1652 this is a valid command. It only checks for invalid characters.
1654 @param[in] CommandName The name to check
1656 @retval TRUE CommandName could be a command name
1657 @retval FALSE CommandName could not be a valid command name
1662 IN CONST CHAR16
*CommandName
1666 if (CommandName
== NULL
) {
1671 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
1674 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
1682 Function to process a NSH script file via SHELL_FILE_HANDLE.
1684 @param[in] Handle The handle to the already opened file.
1685 @param[in] Name The name of the script file.
1687 @retval EFI_SUCCESS the script completed sucessfully
1691 RunScriptFileHandle (
1692 IN SHELL_FILE_HANDLE Handle
,
1693 IN CONST CHAR16
*Name
1697 SCRIPT_FILE
*NewScriptFile
;
1699 CHAR16
*CommandLine
;
1700 CHAR16
*CommandLine2
;
1701 CHAR16
*CommandLine3
;
1702 SCRIPT_COMMAND_LIST
*LastCommand
;
1704 BOOLEAN PreScriptEchoState
;
1705 BOOLEAN PreCommandEchoState
;
1706 CONST CHAR16
*CurDir
;
1708 CHAR16 LeString
[50];
1710 ASSERT(!ShellCommandGetScriptExit());
1712 PreScriptEchoState
= ShellCommandGetEchoState();
1714 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
1715 if (NewScriptFile
== NULL
) {
1716 return (EFI_OUT_OF_RESOURCES
);
1722 ASSERT(NewScriptFile
->ScriptName
== NULL
);
1723 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
1724 if (NewScriptFile
->ScriptName
== NULL
) {
1725 DeleteScriptFileStruct(NewScriptFile
);
1726 return (EFI_OUT_OF_RESOURCES
);
1730 // Save the parameters (used to replace %0 to %9 later on)
1732 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
1733 if (NewScriptFile
->Argc
!= 0) {
1734 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
1735 if (NewScriptFile
->Argv
== NULL
) {
1736 DeleteScriptFileStruct(NewScriptFile
);
1737 return (EFI_OUT_OF_RESOURCES
);
1739 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
1740 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
1741 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
1742 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
1743 DeleteScriptFileStruct(NewScriptFile
);
1744 return (EFI_OUT_OF_RESOURCES
);
1748 NewScriptFile
->Argv
= NULL
;
1751 InitializeListHead(&NewScriptFile
->CommandList
);
1752 InitializeListHead(&NewScriptFile
->SubstList
);
1755 // Now build the list of all script commands.
1758 while(!ShellFileHandleEof(Handle
)) {
1759 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
1761 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0) {
1764 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
1765 if (NewScriptFile
->CurrentCommand
== NULL
) {
1766 DeleteScriptFileStruct(NewScriptFile
);
1767 return (EFI_OUT_OF_RESOURCES
);
1770 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
1771 NewScriptFile
->CurrentCommand
->Data
= NULL
;
1772 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
1774 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1778 // Add this as the topmost script file
1780 ShellCommandSetNewScript (NewScriptFile
);
1783 // Now enumerate through the commands and run each one.
1785 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1786 if (CommandLine
== NULL
) {
1787 DeleteScriptFileStruct(NewScriptFile
);
1788 return (EFI_OUT_OF_RESOURCES
);
1790 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1791 if (CommandLine2
== NULL
) {
1792 FreePool(CommandLine
);
1793 DeleteScriptFileStruct(NewScriptFile
);
1794 return (EFI_OUT_OF_RESOURCES
);
1797 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
1798 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
1799 ; // conditional increment in the body of the loop
1801 ASSERT(CommandLine2
!= NULL
);
1802 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
1805 // NULL out comments
1807 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
1808 if (*CommandLine3
== L
'^') {
1809 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
1810 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
1812 } else if (*CommandLine3
== L
'#') {
1813 *CommandLine3
= CHAR_NULL
;
1817 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
1819 // Due to variability in starting the find and replace action we need to have both buffers the same.
1821 StrCpy(CommandLine
, CommandLine2
);
1824 // Remove the %0 to %9 from the command line (if we have some arguments)
1826 if (NewScriptFile
->Argv
!= NULL
) {
1827 switch (NewScriptFile
->Argc
) {
1829 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
1830 ASSERT_EFI_ERROR(Status
);
1832 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
1833 ASSERT_EFI_ERROR(Status
);
1835 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
1836 ASSERT_EFI_ERROR(Status
);
1838 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
1839 ASSERT_EFI_ERROR(Status
);
1841 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
1842 ASSERT_EFI_ERROR(Status
);
1844 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
1845 ASSERT_EFI_ERROR(Status
);
1847 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
1848 ASSERT_EFI_ERROR(Status
);
1850 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
1851 ASSERT_EFI_ERROR(Status
);
1853 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
1854 ASSERT_EFI_ERROR(Status
);
1856 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
1857 ASSERT_EFI_ERROR(Status
);
1863 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
1864 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
1865 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
1866 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
1867 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
1868 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
1869 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
1870 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
1871 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
1873 StrCpy(CommandLine2
, CommandLine
);
1875 LastCommand
= NewScriptFile
->CurrentCommand
;
1877 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
1879 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
1881 // This line is a goto target / label
1884 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
1885 if (CommandLine3
[0] == L
'@') {
1887 // We need to save the current echo state
1888 // and disable echo for just this command.
1890 PreCommandEchoState
= ShellCommandGetEchoState();
1891 ShellCommandSetEchoState(FALSE
);
1892 Status
= RunCommand(CommandLine3
+1);
1895 // Now restore the pre-'@' echo state.
1897 ShellCommandSetEchoState(PreCommandEchoState
);
1899 if (ShellCommandGetEchoState()) {
1900 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1901 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1902 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1904 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1906 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
1908 Status
= RunCommand(CommandLine3
);
1912 if (ShellCommandGetScriptExit()) {
1913 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
1914 DEBUG_CODE(InternalEfiShellSetEnv(L
"DebugLasterror", LeString
, TRUE
););
1915 InternalEfiShellSetEnv(L
"Lasterror", LeString
, TRUE
);
1917 ShellCommandRegisterExit(FALSE
, 0);
1918 Status
= EFI_SUCCESS
;
1921 if (ShellGetExecutionBreakFlag()) {
1924 if (EFI_ERROR(Status
)) {
1927 if (ShellCommandGetExit()) {
1932 // If that commend did not update the CurrentCommand then we need to advance it...
1934 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
1935 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1936 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
1937 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
1941 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1942 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
1943 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
1949 FreePool(CommandLine
);
1950 FreePool(CommandLine2
);
1951 ShellCommandSetNewScript (NULL
);
1954 // Only if this was the last script reset the state.
1956 if (ShellCommandGetCurrentScriptFile()==NULL
) {
1957 ShellCommandSetEchoState(PreScriptEchoState
);
1959 return (EFI_SUCCESS
);
1963 Function to process a NSH script file.
1965 @param[in] ScriptPath Pointer to the script file name (including file system path).
1967 @retval EFI_SUCCESS the script completed sucessfully
1972 IN CONST CHAR16
*ScriptPath
1976 SHELL_FILE_HANDLE FileHandle
;
1978 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
1979 return (EFI_INVALID_PARAMETER
);
1982 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1983 if (EFI_ERROR(Status
)) {
1987 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
1989 ShellCloseFile(&FileHandle
);