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.
84 IN CONST CHAR16
*CmdLine
87 CONST CHAR16
*TempSpot
;
89 if (StrStr(CmdLine
, L
"|") != NULL
) {
90 for (TempSpot
= CmdLine
; TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
; TempSpot
++) {
91 if (*TempSpot
== L
'^' && *(TempSpot
+1) == L
'|') {
93 } else if (*TempSpot
== L
'|') {
102 Determine if a command line contains a split operation
104 @param[in] CmdLine The command line to parse.
106 @retval TRUE CmdLine has a valid split.
107 @retval FALSE CmdLine does not have a valid split.
112 IN CONST CHAR16
*CmdLine
115 CONST CHAR16
*TempSpot
;
116 TempSpot
= FindSplit(CmdLine
);
117 return (TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
);
121 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
122 feature's enabled state was not known when the shell initially launched.
124 @retval EFI_SUCCESS The feature is enabled.
125 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
129 InternalEfiShellStartCtrlSMonitor(
133 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
134 EFI_KEY_DATA KeyData
;
137 Status
= gBS
->OpenProtocol(
138 gST
->ConsoleInHandle
,
139 &gEfiSimpleTextInputExProtocolGuid
,
143 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
144 if (EFI_ERROR(Status
)) {
149 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
150 ShellInfoObject
.HiiHandle
);
151 return (EFI_SUCCESS
);
154 KeyData
.KeyState
.KeyToggleState
= 0;
155 KeyData
.Key
.ScanCode
= 0;
156 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
157 KeyData
.Key
.UnicodeChar
= L
's';
159 Status
= SimpleEx
->RegisterKeyNotify(
162 NotificationFunction
,
163 &ShellInfoObject
.CtrlSNotifyHandle1
);
165 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
166 if (!EFI_ERROR(Status
)) {
167 Status
= SimpleEx
->RegisterKeyNotify(
170 NotificationFunction
,
171 &ShellInfoObject
.CtrlSNotifyHandle2
);
173 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
174 KeyData
.Key
.UnicodeChar
= 19;
176 if (!EFI_ERROR(Status
)) {
177 Status
= SimpleEx
->RegisterKeyNotify(
180 NotificationFunction
,
181 &ShellInfoObject
.CtrlSNotifyHandle3
);
183 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
184 if (!EFI_ERROR(Status
)) {
185 Status
= SimpleEx
->RegisterKeyNotify(
188 NotificationFunction
,
189 &ShellInfoObject
.CtrlSNotifyHandle4
);
197 The entry point for the application.
199 @param[in] ImageHandle The firmware allocated handle for the EFI image.
200 @param[in] SystemTable A pointer to the EFI System Table.
202 @retval EFI_SUCCESS The entry point is executed successfully.
203 @retval other Some error occurs when executing this entry point.
209 IN EFI_HANDLE ImageHandle
,
210 IN EFI_SYSTEM_TABLE
*SystemTable
216 EFI_HANDLE ConInHandle
;
217 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
219 if (PcdGet8(PcdShellSupportLevel
) > 3) {
220 return (EFI_UNSUPPORTED
);
226 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
227 if (EFI_ERROR(Status
)) {
232 // Populate the global structure from PCDs
234 ShellInfoObject
.ImageDevPath
= NULL
;
235 ShellInfoObject
.FileDevPath
= NULL
;
236 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
237 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
238 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
241 // verify we dont allow for spec violation
243 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
246 // Initialize the LIST ENTRY objects...
248 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
249 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
250 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
253 // Check PCDs for optional features that are not implemented yet.
255 if ( PcdGetBool(PcdShellSupportOldProtocols
)
256 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
257 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
259 return (EFI_UNSUPPORTED
);
263 // turn off the watchdog timer
265 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
268 // install our console logger. This will keep a log of the output for back-browsing
270 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
271 if (!EFI_ERROR(Status
)) {
273 // Enable the cursor to be visible
275 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
278 // If supporting EFI 1.1 we need to install HII protocol
279 // only do this if PcdShellRequireHiiPlatform == FALSE
281 // remove EFI_UNSUPPORTED check above when complete.
282 ///@todo add support for Framework HII
285 // install our (solitary) HII package
287 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
288 if (ShellInfoObject
.HiiHandle
== NULL
) {
289 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
290 ///@todo Add our package into Framework HII
292 if (ShellInfoObject
.HiiHandle
== NULL
) {
293 return (EFI_NOT_STARTED
);
298 // create and install the EfiShellParametersProtocol
300 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
301 ASSERT_EFI_ERROR(Status
);
302 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
305 // create and install the EfiShellProtocol
307 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
308 ASSERT_EFI_ERROR(Status
);
309 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
312 // Now initialize the shell library (it requires Shell Parameters protocol)
314 Status
= ShellInitialize();
315 ASSERT_EFI_ERROR(Status
);
317 Status
= CommandInit();
318 ASSERT_EFI_ERROR(Status
);
321 // Check the command line
323 Status
= ProcessCommandLine();
326 // If shell support level is >= 1 create the mappings and paths
328 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
329 Status
= ShellCommandCreateInitialMappingsAndPaths();
333 // save the device path for the loaded image and the device path for the filepath (under loaded image)
334 // These are where to look for the startup.nsh file
336 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
337 ASSERT_EFI_ERROR(Status
);
340 // Display the version
342 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
345 gST
->ConOut
->Mode
->CursorRow
,
347 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
348 ShellInfoObject
.HiiHandle
,
349 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
350 gEfiShellProtocol
->MajorVersion
,
351 gEfiShellProtocol
->MinorVersion
358 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
359 ShellInfoObject
.HiiHandle
,
360 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
367 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
368 ShellInfoObject
.HiiHandle
,
369 (gST
->Hdr
.Revision
&0xffff0000)>>16,
370 (gST
->Hdr
.Revision
&0x0000ffff),
372 gST
->FirmwareRevision
377 // Display the mapping
379 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
380 Status
= RunCommand(L
"map");
381 ASSERT_EFI_ERROR(Status
);
385 // init all the built in alias'
387 Status
= SetBuiltInAlias();
388 ASSERT_EFI_ERROR(Status
);
391 // Initialize environment variables
393 if (ShellCommandGetProfileList() != NULL
) {
394 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
395 ASSERT_EFI_ERROR(Status
);
399 TempString
= AllocateZeroPool(Size
);
401 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
402 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
403 ASSERT_EFI_ERROR(Status
);
405 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
406 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
407 ASSERT_EFI_ERROR(Status
);
409 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
410 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
411 ASSERT_EFI_ERROR(Status
);
413 FreePool(TempString
);
415 if (!EFI_ERROR(Status
)) {
416 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
418 // Set up the event for CTRL-C monitoring...
420 Status
= InernalEfiShellStartMonitor();
423 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
425 // Set up the event for CTRL-S monitoring...
427 Status
= InternalEfiShellStartCtrlSMonitor();
430 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
432 // close off the gST->ConIn
434 OldConIn
= gST
->ConIn
;
435 ConInHandle
= gST
->ConsoleInHandle
;
436 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
442 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
444 // process the startup script or launch the called app.
446 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
449 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
451 // begin the UI waiting loop
455 // clean out all the memory allocated for CONST <something> * return values
456 // between each shell prompt presentation
458 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
459 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
463 // Reset page break back to default.
465 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
466 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
467 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
470 // Reset the CTRL-C event (yes we ignore the return values)
472 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
477 Status
= DoShellPrompt();
478 } while (!ShellCommandGetExit());
480 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
481 CloseSimpleTextInOnFile (gST
->ConIn
);
482 gST
->ConIn
= OldConIn
;
483 gST
->ConsoleInHandle
= ConInHandle
;
489 // uninstall protocols / free memory / etc...
491 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
492 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
493 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
495 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
496 FreePool(ShellInfoObject
.ImageDevPath
);
497 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
499 if (ShellInfoObject
.FileDevPath
!= NULL
) {
500 FreePool(ShellInfoObject
.FileDevPath
);
501 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
503 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
504 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
505 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
507 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
508 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
509 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
511 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
512 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
515 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
516 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
519 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
520 ASSERT(FALSE
); ///@todo finish this de-allocation.
523 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
524 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
525 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
528 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
529 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
530 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
533 if (ShellInfoObject
.HiiHandle
!= NULL
) {
534 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
535 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
538 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
539 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
542 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
543 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
544 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
545 FreePool(ShellInfoObject
.ConsoleInfo
);
546 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
549 if (ShellCommandGetExit()) {
550 return ((EFI_STATUS
)ShellCommandGetExitCode());
556 Sets all the alias' that were registered with the ShellCommandLib library.
558 @retval EFI_SUCCESS all init commands were run sucessfully.
566 CONST ALIAS_LIST
*List
;
570 // Get all the commands we want to run
572 List
= ShellCommandGetInitAliasList();
575 // for each command in the List
577 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
578 ; !IsNull (&List
->Link
, &Node
->Link
)
579 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
582 // install the alias'
584 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
585 ASSERT_EFI_ERROR(Status
);
587 return (EFI_SUCCESS
);
591 Internal function to determine if 2 command names are really the same.
593 @param[in] Command1 The pointer to the first command name.
594 @param[in] Command2 The pointer to the second command name.
596 @retval TRUE The 2 command names are the same.
597 @retval FALSE The 2 command names are not the same.
602 IN CONST CHAR16
*Command1
,
603 IN CONST CHAR16
*Command2
606 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
613 Internal function to determine if a command is a script only command.
615 @param[in] CommandName The pointer to the command name.
617 @retval TRUE The command is a script only command.
618 @retval FALSE The command is not a script only command.
623 IN CONST CHAR16
*CommandName
626 if (IsCommand(CommandName
, L
"for")
627 ||IsCommand(CommandName
, L
"endfor")
628 ||IsCommand(CommandName
, L
"if")
629 ||IsCommand(CommandName
, L
"else")
630 ||IsCommand(CommandName
, L
"endif")
631 ||IsCommand(CommandName
, L
"goto")) {
638 This function will populate the 2 device path protocol parameters based on the
639 global gImageHandle. The DevPath will point to the device path for the handle that has
640 loaded image protocol installed on it. The FilePath will point to the device path
641 for the file that was loaded.
643 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
644 @param[in, out] FilePath On a sucessful return the device path to the file.
646 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
647 @retval other A error from gBS->HandleProtocol.
653 GetDevicePathsForImageAndFile (
654 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
655 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
659 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
660 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
662 ASSERT(DevPath
!= NULL
);
663 ASSERT(FilePath
!= NULL
);
665 Status
= gBS
->OpenProtocol (
667 &gEfiLoadedImageProtocolGuid
,
668 (VOID
**)&LoadedImage
,
671 EFI_OPEN_PROTOCOL_GET_PROTOCOL
673 if (!EFI_ERROR (Status
)) {
674 Status
= gBS
->OpenProtocol (
675 LoadedImage
->DeviceHandle
,
676 &gEfiDevicePathProtocolGuid
,
677 (VOID
**)&ImageDevicePath
,
680 EFI_OPEN_PROTOCOL_GET_PROTOCOL
682 if (!EFI_ERROR (Status
)) {
683 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
684 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
686 LoadedImage
->DeviceHandle
,
687 &gEfiDevicePathProtocolGuid
,
693 &gEfiLoadedImageProtocolGuid
,
700 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
701 {L
"-nostartup", TypeFlag
},
702 {L
"-startup", TypeFlag
},
703 {L
"-noconsoleout", TypeFlag
},
704 {L
"-noconsolein", TypeFlag
},
705 {L
"-nointerrupt", TypeFlag
},
706 {L
"-nomap", TypeFlag
},
707 {L
"-noversion", TypeFlag
},
708 {L
"-startup", TypeFlag
},
709 {L
"-delay", TypeValue
},
710 {L
"-_exit", TypeFlag
},
715 Process all Uefi Shell 2.0 command line options.
717 see Uefi Shell 2.0 section 3.2 for full details.
719 the command line must resemble the following:
721 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
723 ShellOpt-options Options which control the initialization behavior of the shell.
724 These options are read from the EFI global variable "ShellOpt"
725 and are processed before options or file-name.
727 options Options which control the initialization behavior of the shell.
729 file-name The name of a UEFI shell application or script to be executed
730 after initialization is complete. By default, if file-name is
731 specified, then -nostartup is implied. Scripts are not supported
734 file-name-options The command-line options that are passed to file-name when it
737 This will initialize the ShellInfoObject.ShellInitSettings global variable.
739 @retval EFI_SUCCESS The variable is initialized.
750 CONST CHAR16
*TempConst
;
753 CHAR16
*ProblemParam
;
759 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
763 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
764 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
765 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateZeroPool(StrSize(TempConst
));
766 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
767 return (EFI_OUT_OF_RESOURCES
);
769 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
770 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
771 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
772 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
775 // We found the file... add the rest of the params...
777 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
778 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
779 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
783 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
784 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
785 return (EFI_OUT_OF_RESOURCES
);
787 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
789 gEfiShellParametersProtocol
->Argv
[LoopVar
],
791 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
792 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
793 return (EFI_OUT_OF_RESOURCES
);
799 ShellCommandLineFreeVarList(Package
);
801 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
802 if (EFI_ERROR(Status
)) {
803 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
804 FreePool(ProblemParam
);
805 ShellCommandLineFreeVarList(Package
);
806 return (EFI_INVALID_PARAMETER
);
810 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
811 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
812 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
813 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
814 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
815 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
816 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
817 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
818 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= ShellCommandLineGetFlag(Package
, L
"-_exit");
820 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
822 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
823 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
824 } else if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
825 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
826 if (TempConst
!= NULL
&& *TempConst
== L
':') {
829 if (TempConst
!= NULL
&& !EFI_ERROR(ShellConvertStringToUint64(TempConst
, &Intermediate
, FALSE
, FALSE
))) {
830 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)Intermediate
;
833 ShellCommandLineFreeVarList(Package
);
839 Handles all interaction with the default startup script.
841 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
843 @param ImagePath the path to the image for shell. first place to look for the startup script
844 @param FilePath the path to the file for shell. second place to look for the startup script.
846 @retval EFI_SUCCESS the variable is initialized.
851 EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
852 EFI_DEVICE_PATH_PROTOCOL
*FilePath
858 SHELL_FILE_HANDLE FileHandle
;
859 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
860 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
861 CHAR16
*FileStringPath
;
864 CONST CHAR16
*MapName
;
866 Key
.UnicodeChar
= CHAR_NULL
;
870 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
872 // launch something else instead
874 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
875 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
876 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
878 FileStringPath
= AllocateZeroPool(NewSize
);
879 if (FileStringPath
== NULL
) {
880 return (EFI_OUT_OF_RESOURCES
);
882 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
883 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
884 StrCat(FileStringPath
, L
" ");
885 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
887 Status
= RunCommand(FileStringPath
);
888 FreePool(FileStringPath
);
894 // for shell level 0 we do no scripts
895 // Without the Startup bit overriding we allow for nostartup to prevent scripts
897 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
898 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
900 return (EFI_SUCCESS
);
903 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
905 // print out our warning and see if they press a key
907 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
908 ; Delay
!= 0 && EFI_ERROR(Status
)
911 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
912 gBS
->Stall (1000000);
913 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
914 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
917 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
918 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
923 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
924 return (EFI_SUCCESS
);
928 // Try the first location (must be file system)
930 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
931 if (MapName
!= NULL
) {
932 FileStringPath
= NULL
;
934 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
935 if (FileStringPath
== NULL
) {
936 Status
= EFI_OUT_OF_RESOURCES
;
938 TempSpot
= StrStr(FileStringPath
, L
";");
939 if (TempSpot
!= NULL
) {
940 *TempSpot
= CHAR_NULL
;
942 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
943 PathRemoveLastItem(FileStringPath
);
944 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
945 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
946 FreePool(FileStringPath
);
949 if (EFI_ERROR(Status
)) {
950 NamePath
= FileDevicePath (NULL
, mStartupScript
);
951 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
957 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
961 // If we got a file, run it
963 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
964 Status
= RunScriptFileHandle (FileHandle
, mStartupScript
);
965 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
967 FileStringPath
= ShellFindFilePath(mStartupScript
);
968 if (FileStringPath
== NULL
) {
970 // we return success since we dont need to have a startup script
972 Status
= EFI_SUCCESS
;
973 ASSERT(FileHandle
== NULL
);
975 Status
= RunScriptFile(FileStringPath
);
976 FreePool(FileStringPath
);
985 Function to perform the shell prompt looping. It will do a single prompt,
986 dispatch the result, and then return. It is expected that the caller will
987 call this function in a loop many times.
990 @retval RETURN_ABORTED
1001 CONST CHAR16
*CurDir
;
1008 // Get screen setting to decide size of the command line buffer
1010 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1011 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1012 CmdLine
= AllocateZeroPool (BufferSize
);
1013 if (CmdLine
== NULL
) {
1014 return EFI_OUT_OF_RESOURCES
;
1017 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1022 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1024 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1025 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1027 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1031 // Read a line from the console
1033 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1036 // Null terminate the string and parse it
1038 if (!EFI_ERROR (Status
)) {
1039 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1040 Status
= RunCommand(CmdLine
);
1044 // Done with this command
1051 Add a buffer to the Buffer To Free List for safely returning buffers to other
1052 places without risking letting them modify internal shell information.
1054 @param Buffer Something to pass to FreePool when the shell is exiting.
1058 AddBufferToFreeList(
1062 BUFFER_LIST
*BufferListEntry
;
1064 if (Buffer
== NULL
) {
1068 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1069 ASSERT(BufferListEntry
!= NULL
);
1070 BufferListEntry
->Buffer
= Buffer
;
1071 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1076 Add a buffer to the Line History List
1078 @param Buffer The line buffer to add.
1082 AddLineToCommandHistory(
1083 IN CONST CHAR16
*Buffer
1088 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1089 ASSERT(Node
!= NULL
);
1090 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
1091 ASSERT(Node
->Buffer
!= NULL
);
1092 StrCpy(Node
->Buffer
, Buffer
);
1094 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1098 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1099 with the correct command name.
1101 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1102 command name if it was an alias. If it was not
1103 an alias it will be unchanged. This function may
1104 change the buffer to fit the command name.
1106 @retval EFI_SUCCESS The name was changed.
1107 @retval EFI_SUCCESS The name was not an alias.
1108 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1113 IN OUT CHAR16
**CommandString
1116 CONST CHAR16
*NewString
;
1118 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1119 if (NewString
== NULL
) {
1120 return (EFI_SUCCESS
);
1122 FreePool(*CommandString
);
1123 *CommandString
= AllocateZeroPool(StrSize(NewString
));
1124 if (*CommandString
== NULL
) {
1125 return (EFI_OUT_OF_RESOURCES
);
1127 StrCpy(*CommandString
, NewString
);
1128 return (EFI_SUCCESS
);
1132 Function allocates a new command line and replaces all instances of environment
1133 variable names that are correctly preset to their values.
1135 If the return value is not NULL the memory must be caller freed.
1137 @param[in] OriginalCommandLine The original command line
1139 @retval NULL An error ocurred.
1140 @return The new command line with no environment variables present.
1144 ShellConvertVariables (
1145 IN CONST CHAR16
*OriginalCommandLine
1148 CONST CHAR16
*MasterEnvList
;
1150 CHAR16
*NewCommandLine1
;
1151 CHAR16
*NewCommandLine2
;
1156 SCRIPT_FILE
*CurrentScriptFile
;
1157 ALIAS_LIST
*AliasListNode
;
1159 ASSERT(OriginalCommandLine
!= NULL
);
1162 NewSize
= StrSize(OriginalCommandLine
);
1163 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1166 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1169 // calculate the size required for the post-conversion string...
1171 if (CurrentScriptFile
!= NULL
) {
1172 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1173 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1174 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1176 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1178 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1181 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1183 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1184 NewSize
+= StrSize(AliasListNode
->CommandString
);
1190 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1191 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1192 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1194 if (StrSize(MasterEnvList
) > ItemSize
) {
1195 ItemSize
= StrSize(MasterEnvList
);
1197 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1199 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1202 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1204 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1205 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1206 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1212 // now do the replacements...
1214 NewCommandLine1
= AllocateZeroPool(NewSize
);
1215 NewCommandLine2
= AllocateZeroPool(NewSize
);
1216 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1217 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1218 SHELL_FREE_NON_NULL(NewCommandLine1
);
1219 SHELL_FREE_NON_NULL(NewCommandLine2
);
1220 SHELL_FREE_NON_NULL(ItemTemp
);
1223 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1224 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1225 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1226 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1228 StrCpy(ItemTemp
, L
"%");
1229 StrCat(ItemTemp
, MasterEnvList
);
1230 StrCat(ItemTemp
, L
"%");
1231 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1232 StrCpy(NewCommandLine1
, NewCommandLine2
);
1234 if (CurrentScriptFile
!= NULL
) {
1235 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1236 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1237 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1239 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1240 StrCpy(NewCommandLine1
, NewCommandLine2
);
1244 // Remove non-existant environment variables in scripts only
1246 for (Temp
= NewCommandLine1
; Temp
!= NULL
; ) {
1247 Temp
= StrStr(Temp
, L
"%");
1251 while (*(Temp
- 1) == L
'^') {
1252 Temp
= StrStr(Temp
+ 1, L
"%");
1261 Temp2
= StrStr(Temp
+ 1, L
"%");
1262 if (Temp2
== NULL
) {
1265 while (*(Temp2
- 1) == L
'^') {
1266 Temp2
= StrStr(Temp2
+ 1, L
"%");
1267 if (Temp2
== NULL
) {
1271 if (Temp2
== NULL
) {
1276 CopyMem(Temp
, Temp2
, StrSize(Temp2
));
1282 // Now cleanup any straggler intentionally ignored "%" characters
1284 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1285 StrCpy(NewCommandLine1
, NewCommandLine2
);
1287 FreePool(NewCommandLine2
);
1290 return (NewCommandLine1
);
1294 Internal function to run a command line with pipe usage.
1296 @param[in] CmdLine The pointer to the command line.
1297 @param[in] StdIn The pointer to the Standard input.
1298 @param[in] StdOut The pointer to the Standard output.
1300 @retval EFI_SUCCESS The split command is executed successfully.
1301 @retval other Some error occurs when executing the split command.
1306 IN CONST CHAR16
*CmdLine
,
1307 IN SHELL_FILE_HANDLE
*StdIn
,
1308 IN SHELL_FILE_HANDLE
*StdOut
1312 CHAR16
*NextCommandLine
;
1313 CHAR16
*OurCommandLine
;
1317 SHELL_FILE_HANDLE
*TempFileHandle
;
1320 ASSERT(StdOut
== NULL
);
1322 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1324 Status
= EFI_SUCCESS
;
1325 NextCommandLine
= NULL
;
1326 OurCommandLine
= NULL
;
1330 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1331 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1333 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1334 SHELL_FREE_NON_NULL(OurCommandLine
);
1335 SHELL_FREE_NON_NULL(NextCommandLine
);
1336 return (EFI_OUT_OF_RESOURCES
);
1337 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1338 SHELL_FREE_NON_NULL(OurCommandLine
);
1339 SHELL_FREE_NON_NULL(NextCommandLine
);
1340 return (EFI_INVALID_PARAMETER
);
1341 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1342 NextCommandLine
[0] == L
'a' &&
1343 NextCommandLine
[1] == L
' '
1345 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1353 // make a SPLIT_LIST item and add to list
1355 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1356 ASSERT(Split
!= NULL
);
1357 Split
->SplitStdIn
= StdIn
;
1358 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1359 ASSERT(Split
->SplitStdOut
!= NULL
);
1360 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1362 Status
= RunCommand(OurCommandLine
);
1365 // move the output from the first to the in to the second.
1367 TempFileHandle
= Split
->SplitStdOut
;
1368 if (Split
->SplitStdIn
== StdIn
) {
1369 Split
->SplitStdOut
= NULL
;
1371 Split
->SplitStdOut
= Split
->SplitStdIn
;
1373 Split
->SplitStdIn
= TempFileHandle
;
1374 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1376 if (!EFI_ERROR(Status
)) {
1377 Status
= RunCommand(NextCommandLine
);
1381 // remove the top level from the ScriptList
1383 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1384 RemoveEntryList(&Split
->Link
);
1387 // Note that the original StdIn is now the StdOut...
1389 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1390 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1392 if (Split
->SplitStdIn
!= NULL
) {
1393 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1397 FreePool(NextCommandLine
);
1398 FreePool(OurCommandLine
);
1404 Function will process and run a command line.
1406 This will determine if the command line represents an internal shell
1407 command or dispatch an external application.
1409 @param[in] CmdLine The command line to parse.
1411 @retval EFI_SUCCESS The command was completed.
1412 @retval EFI_ABORTED The command's operation was aborted.
1417 IN CONST CHAR16
*CmdLine
1421 EFI_STATUS StatusCode
;
1422 CHAR16
*CommandName
;
1423 SHELL_STATUS ShellStatus
;
1427 CHAR16 LeString
[19];
1428 CHAR16
*PostAliasCmdLine
;
1429 UINTN PostAliasSize
;
1430 CHAR16
*PostVariableCmdLine
;
1431 CHAR16
*CommandWithPath
;
1432 CONST EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
1433 CONST CHAR16
*TempLocation
;
1434 CONST CHAR16
*TempLocation2
;
1435 SHELL_FILE_HANDLE OriginalStdIn
;
1436 SHELL_FILE_HANDLE OriginalStdOut
;
1437 SHELL_FILE_HANDLE OriginalStdErr
;
1438 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
1441 CHAR16
*CleanOriginal
;
1444 ASSERT(CmdLine
!= NULL
);
1445 if (StrLen(CmdLine
) == 0) {
1446 return (EFI_SUCCESS
);
1450 PostVariableCmdLine
= NULL
;
1451 PostAliasCmdLine
= NULL
;
1452 CommandWithPath
= NULL
;
1454 Status
= EFI_SUCCESS
;
1455 CleanOriginal
= NULL
;
1458 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
1459 if (CleanOriginal
== NULL
) {
1460 return (EFI_OUT_OF_RESOURCES
);
1464 // Remove any spaces and tabs at the beginning of the string.
1466 while ((CleanOriginal
[0] == L
' ') || (CleanOriginal
[0] == L
'\t')) {
1467 CopyMem(CleanOriginal
, CleanOriginal
+1, StrSize(CleanOriginal
) - sizeof(CleanOriginal
[0]));
1471 // Handle case that passed in command line is just 1 or more " " characters.
1473 if (StrLen (CleanOriginal
) == 0) {
1474 if (CleanOriginal
!= NULL
) {
1475 FreePool(CleanOriginal
);
1476 CleanOriginal
= NULL
;
1478 return (EFI_SUCCESS
);
1482 // Remove any spaces at the end of the string.
1484 while (CleanOriginal
[StrLen(CleanOriginal
)-1] == L
' ') {
1485 CleanOriginal
[StrLen(CleanOriginal
)-1] = CHAR_NULL
;
1489 if (StrStr(CleanOriginal
, L
" ") == NULL
){
1490 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, 0);
1492 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, StrStr(CleanOriginal
, L
" ") - CleanOriginal
);
1495 ASSERT(PostAliasCmdLine
== NULL
);
1496 if (!ShellCommandIsCommandOnList(CommandName
)) {
1498 // Convert via alias
1500 Status
= ShellConvertAlias(&CommandName
);
1502 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, CommandName
, 0);
1503 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, StrStr(CleanOriginal
, L
" "), 0);
1504 ASSERT_EFI_ERROR(Status
);
1506 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, NULL
, CleanOriginal
, 0);
1509 if (CleanOriginal
!= NULL
) {
1510 FreePool(CleanOriginal
);
1511 CleanOriginal
= NULL
;
1514 if (CommandName
!= NULL
) {
1515 FreePool(CommandName
);
1519 PostVariableCmdLine
= ShellConvertVariables(PostAliasCmdLine
);
1522 // we can now free the modified by alias command line
1524 if (PostAliasCmdLine
!= NULL
) {
1525 FreePool(PostAliasCmdLine
);
1526 PostAliasCmdLine
= NULL
;
1529 if (PostVariableCmdLine
== NULL
) {
1530 return (EFI_OUT_OF_RESOURCES
);
1533 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1534 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1536 while (PostVariableCmdLine
[0] == L
' ') {
1537 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1541 // We dont do normal processing with a split command line (output from one command input to another)
1543 if (ContainsSplit(PostVariableCmdLine
)) {
1545 // are we in an existing split???
1547 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1548 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1551 if (Split
== NULL
) {
1552 Status
= RunSplitCommand(PostVariableCmdLine
, NULL
, NULL
);
1554 Status
= RunSplitCommand(PostVariableCmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1556 if (EFI_ERROR(Status
)) {
1557 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1562 // If this is a mapped drive change handle that...
1564 if (PostVariableCmdLine
[(StrLen(PostVariableCmdLine
)-1)] == L
':' && StrStr(PostVariableCmdLine
, L
" ") == NULL
) {
1565 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, PostVariableCmdLine
);
1566 if (EFI_ERROR(Status
)) {
1567 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1569 FreePool(PostVariableCmdLine
);
1573 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1574 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1578 Status
= UpdateStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1579 if (EFI_ERROR(Status
)) {
1580 if (Status
== EFI_NOT_FOUND
) {
1581 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_REDUNDA_REDIR
), ShellInfoObject
.HiiHandle
);
1583 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_REDIR
), ShellInfoObject
.HiiHandle
);
1586 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1587 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1589 while (PostVariableCmdLine
[0] == L
' ') {
1590 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1594 // get the argc and argv updated for internal commands
1596 Status
= UpdateArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &Argv
, &Argc
);
1597 ASSERT_EFI_ERROR(Status
);
1599 for (Count
= 0 ; Count
< ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count
++) {
1600 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
], L
"-?") == ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]
1601 || (ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][0] == L
'?' && ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][1] == CHAR_NULL
)
1604 // We need to redo the arguments since a parameter was -?
1605 // move them all down 1 to the end, then up one then replace the first with help
1607 FreePool(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]);
1608 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
] = NULL
;
1609 for (Count2
= Count
; (Count2
+ 1) < ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count2
++) {
1610 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
+1];
1612 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = NULL
;
1613 for (Count2
= ShellInfoObject
.NewShellParametersProtocol
->Argc
-1 ; Count2
> 0 ; Count2
--) {
1614 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
-1];
1616 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = NULL
;
1617 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = StrnCatGrow(&ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], NULL
, L
"help", 0);
1625 if (ShellCommandIsCommandOnList(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1627 // Run the command (which was converted if it was an alias)
1629 if (!EFI_ERROR(Status
)) {
1630 Status
= ShellCommandRunCommandHandler(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], &ShellStatus
, &LastError
);
1631 ASSERT_EFI_ERROR(Status
);
1633 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1634 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellStatus
);
1636 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ShellStatus
);
1638 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1640 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1643 // Pass thru the exitcode from the app.
1645 if (ShellCommandGetExit()) {
1646 Status
= ShellStatus
;
1647 } else if (ShellStatus
!= 0 && IsScriptOnlyCommand(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1648 Status
= EFI_ABORTED
;
1653 // run an external file (or script)
1655 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], L
":") != NULL
) {
1656 ASSERT (CommandWithPath
== NULL
);
1657 if (ShellIsFile(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]) == EFI_SUCCESS
) {
1658 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1661 if (CommandWithPath
== NULL
) {
1662 CommandWithPath
= ShellFindFilePathEx(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], mExecutableExtensions
);
1664 if (CommandWithPath
== NULL
|| ShellIsDirectory(CommandWithPath
) == EFI_SUCCESS
) {
1665 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]);
1667 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1668 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", EFI_NOT_FOUND
);
1670 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", EFI_NOT_FOUND
);
1672 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1673 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1676 // Check if it's a NSH (script) file.
1678 TempLocation
= CommandWithPath
+StrLen(CommandWithPath
)-4;
1679 TempLocation2
= mScriptExtension
;
1680 if ((StrLen(CommandWithPath
) > 4) && (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0)) {
1681 Status
= RunScriptFile (CommandWithPath
);
1683 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
1684 ASSERT(DevPath
!= NULL
);
1685 Status
= InternalShellExecuteDevicePath(
1688 PostVariableCmdLine
,
1694 // Update last error status.
1696 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1697 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", StatusCode
);
1699 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", StatusCode
);
1701 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1702 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1708 // Print some error info.
1710 if (EFI_ERROR(Status
)) {
1711 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
1714 CommandName
= StrnCatGrow(&CommandName
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1716 RestoreArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, &Argv
, &Argc
);
1718 RestoreStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
1720 if (CommandName
!= NULL
) {
1721 if (ShellCommandGetCurrentScriptFile() != NULL
&& !IsScriptOnlyCommand(CommandName
)) {
1723 // if this is NOT a scipt only command return success so the script won't quit.
1724 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1726 Status
= EFI_SUCCESS
;
1731 SHELL_FREE_NON_NULL(CommandName
);
1732 SHELL_FREE_NON_NULL(CommandWithPath
);
1733 SHELL_FREE_NON_NULL(PostVariableCmdLine
);
1738 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
1740 Function determins if the CommandName COULD be a valid command. It does not determine whether
1741 this is a valid command. It only checks for invalid characters.
1743 @param[in] CommandName The name to check
1745 @retval TRUE CommandName could be a command name
1746 @retval FALSE CommandName could not be a valid command name
1751 IN CONST CHAR16
*CommandName
1755 if (CommandName
== NULL
) {
1760 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
1763 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
1771 Function to process a NSH script file via SHELL_FILE_HANDLE.
1773 @param[in] Handle The handle to the already opened file.
1774 @param[in] Name The name of the script file.
1776 @retval EFI_SUCCESS the script completed sucessfully
1780 RunScriptFileHandle (
1781 IN SHELL_FILE_HANDLE Handle
,
1782 IN CONST CHAR16
*Name
1786 SCRIPT_FILE
*NewScriptFile
;
1788 CHAR16
*CommandLine
;
1789 CHAR16
*CommandLine2
;
1790 CHAR16
*CommandLine3
;
1791 SCRIPT_COMMAND_LIST
*LastCommand
;
1793 BOOLEAN PreScriptEchoState
;
1794 BOOLEAN PreCommandEchoState
;
1795 CONST CHAR16
*CurDir
;
1797 CHAR16 LeString
[50];
1799 ASSERT(!ShellCommandGetScriptExit());
1801 PreScriptEchoState
= ShellCommandGetEchoState();
1803 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
1804 if (NewScriptFile
== NULL
) {
1805 return (EFI_OUT_OF_RESOURCES
);
1811 ASSERT(NewScriptFile
->ScriptName
== NULL
);
1812 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
1813 if (NewScriptFile
->ScriptName
== NULL
) {
1814 DeleteScriptFileStruct(NewScriptFile
);
1815 return (EFI_OUT_OF_RESOURCES
);
1819 // Save the parameters (used to replace %0 to %9 later on)
1821 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
1822 if (NewScriptFile
->Argc
!= 0) {
1823 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
1824 if (NewScriptFile
->Argv
== NULL
) {
1825 DeleteScriptFileStruct(NewScriptFile
);
1826 return (EFI_OUT_OF_RESOURCES
);
1828 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
1829 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
1830 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
1831 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
1832 DeleteScriptFileStruct(NewScriptFile
);
1833 return (EFI_OUT_OF_RESOURCES
);
1837 NewScriptFile
->Argv
= NULL
;
1840 InitializeListHead(&NewScriptFile
->CommandList
);
1841 InitializeListHead(&NewScriptFile
->SubstList
);
1844 // Now build the list of all script commands.
1847 while(!ShellFileHandleEof(Handle
)) {
1848 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
1850 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0) {
1853 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
1854 if (NewScriptFile
->CurrentCommand
== NULL
) {
1855 DeleteScriptFileStruct(NewScriptFile
);
1856 return (EFI_OUT_OF_RESOURCES
);
1859 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
1860 NewScriptFile
->CurrentCommand
->Data
= NULL
;
1861 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
1863 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1867 // Add this as the topmost script file
1869 ShellCommandSetNewScript (NewScriptFile
);
1872 // Now enumerate through the commands and run each one.
1874 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1875 if (CommandLine
== NULL
) {
1876 DeleteScriptFileStruct(NewScriptFile
);
1877 return (EFI_OUT_OF_RESOURCES
);
1879 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
1880 if (CommandLine2
== NULL
) {
1881 FreePool(CommandLine
);
1882 DeleteScriptFileStruct(NewScriptFile
);
1883 return (EFI_OUT_OF_RESOURCES
);
1886 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
1887 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
1888 ; // conditional increment in the body of the loop
1890 ASSERT(CommandLine2
!= NULL
);
1891 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
1894 // NULL out comments
1896 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
1897 if (*CommandLine3
== L
'^') {
1898 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
1899 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
1901 } else if (*CommandLine3
== L
'#') {
1902 *CommandLine3
= CHAR_NULL
;
1906 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
1908 // Due to variability in starting the find and replace action we need to have both buffers the same.
1910 StrCpy(CommandLine
, CommandLine2
);
1913 // Remove the %0 to %9 from the command line (if we have some arguments)
1915 if (NewScriptFile
->Argv
!= NULL
) {
1916 switch (NewScriptFile
->Argc
) {
1918 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
1919 ASSERT_EFI_ERROR(Status
);
1921 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
1922 ASSERT_EFI_ERROR(Status
);
1924 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
1925 ASSERT_EFI_ERROR(Status
);
1927 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
1928 ASSERT_EFI_ERROR(Status
);
1930 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
1931 ASSERT_EFI_ERROR(Status
);
1933 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
1934 ASSERT_EFI_ERROR(Status
);
1936 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
1937 ASSERT_EFI_ERROR(Status
);
1939 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
1940 ASSERT_EFI_ERROR(Status
);
1942 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
1943 ASSERT_EFI_ERROR(Status
);
1945 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
1946 ASSERT_EFI_ERROR(Status
);
1952 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
1953 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
1954 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
1955 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
1956 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
1957 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
1958 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
1959 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
1960 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
1962 StrCpy(CommandLine2
, CommandLine
);
1964 LastCommand
= NewScriptFile
->CurrentCommand
;
1966 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
1968 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
1970 // This line is a goto target / label
1973 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
1974 if (CommandLine3
[0] == L
'@') {
1976 // We need to save the current echo state
1977 // and disable echo for just this command.
1979 PreCommandEchoState
= ShellCommandGetEchoState();
1980 ShellCommandSetEchoState(FALSE
);
1981 Status
= RunCommand(CommandLine3
+1);
1984 // If command was "@echo -off" or "@echo -on" then don't restore echo state
1986 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
1987 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
1989 // Now restore the pre-'@' echo state.
1991 ShellCommandSetEchoState(PreCommandEchoState
);
1994 if (ShellCommandGetEchoState()) {
1995 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1996 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1997 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1999 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2001 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2003 Status
= RunCommand(CommandLine3
);
2007 if (ShellCommandGetScriptExit()) {
2009 // ShellCommandGetExitCode() always returns a UINT64
2011 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ShellCommandGetExitCode());
2012 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2013 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2015 ShellCommandRegisterExit(FALSE
, 0);
2016 Status
= EFI_SUCCESS
;
2019 if (ShellGetExecutionBreakFlag()) {
2022 if (EFI_ERROR(Status
)) {
2025 if (ShellCommandGetExit()) {
2030 // If that commend did not update the CurrentCommand then we need to advance it...
2032 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2033 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2034 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2035 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2039 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2040 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2041 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2047 FreePool(CommandLine
);
2048 FreePool(CommandLine2
);
2049 ShellCommandSetNewScript (NULL
);
2052 // Only if this was the last script reset the state.
2054 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2055 ShellCommandSetEchoState(PreScriptEchoState
);
2057 return (EFI_SUCCESS
);
2061 Function to process a NSH script file.
2063 @param[in] ScriptPath Pointer to the script file name (including file system path).
2065 @retval EFI_SUCCESS the script completed sucessfully
2070 IN CONST CHAR16
*ScriptPath
2074 SHELL_FILE_HANDLE FileHandle
;
2076 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2077 return (EFI_INVALID_PARAMETER
);
2080 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2081 if (EFI_ERROR(Status
)) {
2085 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
2087 ShellCloseFile(&FileHandle
);