2 This is THE shell (application)
4 Copyright (c) 2009 - 2014, 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 Cleans off leading and trailing spaces and tabs.
76 @param[in] String pointer to the string to trim them off.
84 ASSERT(String
!= NULL
);
85 ASSERT(*String
!= NULL
);
87 // Remove any spaces and tabs at the beginning of the (*String).
89 while (((*String
)[0] == L
' ') || ((*String
)[0] == L
'\t')) {
90 CopyMem((*String
), (*String
)+1, StrSize((*String
)) - sizeof((*String
)[0]));
94 // Remove any spaces and tabs at the end of the (*String).
96 while ((StrLen (*String
) > 0) && (((*String
)[StrLen((*String
))-1] == L
' ') || ((*String
)[StrLen((*String
))-1] == L
'\t'))) {
97 (*String
)[StrLen((*String
))-1] = CHAR_NULL
;
100 return (EFI_SUCCESS
);
104 Find a command line contains a split operation
106 @param[in] CmdLine The command line to parse.
108 @retval A pointer to the | character in CmdLine or NULL if not present.
113 IN CONST CHAR16
*CmdLine
116 CONST CHAR16
*TempSpot
;
118 if (StrStr(CmdLine
, L
"|") != NULL
) {
119 for (TempSpot
= CmdLine
; TempSpot
!= NULL
&& *TempSpot
!= CHAR_NULL
; TempSpot
++) {
120 if (*TempSpot
== L
'^' && *(TempSpot
+1) == L
'|') {
122 } else if (*TempSpot
== L
'|') {
131 Determine if a command line contains a split operation
133 @param[in] CmdLine The command line to parse.
135 @retval TRUE CmdLine has a valid split.
136 @retval FALSE CmdLine does not have a valid split.
141 IN CONST CHAR16
*CmdLine
144 CONST CHAR16
*TempSpot
;
145 TempSpot
= FindSplit(CmdLine
);
146 return (BOOLEAN
) ((TempSpot
!= NULL
) && (*TempSpot
!= CHAR_NULL
));
150 Function to start monitoring for CTRL-S using SimpleTextInputEx. This
151 feature's enabled state was not known when the shell initially launched.
153 @retval EFI_SUCCESS The feature is enabled.
154 @retval EFI_OUT_OF_RESOURCES There is not enough mnemory available.
158 InternalEfiShellStartCtrlSMonitor(
162 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
*SimpleEx
;
163 EFI_KEY_DATA KeyData
;
166 Status
= gBS
->OpenProtocol(
167 gST
->ConsoleInHandle
,
168 &gEfiSimpleTextInputExProtocolGuid
,
172 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
173 if (EFI_ERROR(Status
)) {
178 STRING_TOKEN (STR_SHELL_NO_IN_EX
),
179 ShellInfoObject
.HiiHandle
);
180 return (EFI_SUCCESS
);
183 KeyData
.KeyState
.KeyToggleState
= 0;
184 KeyData
.Key
.ScanCode
= 0;
185 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
186 KeyData
.Key
.UnicodeChar
= L
's';
188 Status
= SimpleEx
->RegisterKeyNotify(
191 NotificationFunction
,
192 &ShellInfoObject
.CtrlSNotifyHandle1
);
194 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
195 if (!EFI_ERROR(Status
)) {
196 Status
= SimpleEx
->RegisterKeyNotify(
199 NotificationFunction
,
200 &ShellInfoObject
.CtrlSNotifyHandle2
);
202 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_LEFT_CONTROL_PRESSED
;
203 KeyData
.Key
.UnicodeChar
= 19;
205 if (!EFI_ERROR(Status
)) {
206 Status
= SimpleEx
->RegisterKeyNotify(
209 NotificationFunction
,
210 &ShellInfoObject
.CtrlSNotifyHandle3
);
212 KeyData
.KeyState
.KeyShiftState
= EFI_SHIFT_STATE_VALID
|EFI_RIGHT_CONTROL_PRESSED
;
213 if (!EFI_ERROR(Status
)) {
214 Status
= SimpleEx
->RegisterKeyNotify(
217 NotificationFunction
,
218 &ShellInfoObject
.CtrlSNotifyHandle4
);
226 The entry point for the application.
228 @param[in] ImageHandle The firmware allocated handle for the EFI image.
229 @param[in] SystemTable A pointer to the EFI System Table.
231 @retval EFI_SUCCESS The entry point is executed successfully.
232 @retval other Some error occurs when executing this entry point.
238 IN EFI_HANDLE ImageHandle
,
239 IN EFI_SYSTEM_TABLE
*SystemTable
245 EFI_HANDLE ConInHandle
;
246 EFI_SIMPLE_TEXT_INPUT_PROTOCOL
*OldConIn
;
249 SHELL_STATUS ExitStatus
;
251 if (PcdGet8(PcdShellSupportLevel
) > 3) {
252 return (EFI_UNSUPPORTED
);
258 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
259 if (EFI_ERROR(Status
)) {
264 // Populate the global structure from PCDs
266 ShellInfoObject
.ImageDevPath
= NULL
;
267 ShellInfoObject
.FileDevPath
= NULL
;
268 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
269 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
270 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
273 // verify we dont allow for spec violation
275 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
278 // Initialize the LIST ENTRY objects...
280 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
281 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
282 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
285 // Check PCDs for optional features that are not implemented yet.
287 if ( PcdGetBool(PcdShellSupportOldProtocols
)
288 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
289 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
291 return (EFI_UNSUPPORTED
);
295 // turn off the watchdog timer
297 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
300 // install our console logger. This will keep a log of the output for back-browsing
302 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
303 if (!EFI_ERROR(Status
)) {
305 // Enable the cursor to be visible
307 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
310 // If supporting EFI 1.1 we need to install HII protocol
311 // only do this if PcdShellRequireHiiPlatform == FALSE
313 // remove EFI_UNSUPPORTED check above when complete.
314 ///@todo add support for Framework HII
317 // install our (solitary) HII package
319 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
320 if (ShellInfoObject
.HiiHandle
== NULL
) {
321 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
322 ///@todo Add our package into Framework HII
324 if (ShellInfoObject
.HiiHandle
== NULL
) {
325 return (EFI_NOT_STARTED
);
330 // create and install the EfiShellParametersProtocol
332 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
333 ASSERT_EFI_ERROR(Status
);
334 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
337 // create and install the EfiShellProtocol
339 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
340 ASSERT_EFI_ERROR(Status
);
341 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
344 // Now initialize the shell library (it requires Shell Parameters protocol)
346 Status
= ShellInitialize();
347 ASSERT_EFI_ERROR(Status
);
349 Status
= CommandInit();
350 ASSERT_EFI_ERROR(Status
);
353 // Check the command line
355 Status
= ProcessCommandLine();
358 // If shell support level is >= 1 create the mappings and paths
360 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
361 Status
= ShellCommandCreateInitialMappingsAndPaths();
365 // save the device path for the loaded image and the device path for the filepath (under loaded image)
366 // These are where to look for the startup.nsh file
368 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
369 ASSERT_EFI_ERROR(Status
);
372 // Display the version
374 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
377 gST
->ConOut
->Mode
->CursorRow
,
379 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL
),
380 ShellInfoObject
.HiiHandle
,
381 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
382 gEfiShellProtocol
->MajorVersion
,
383 gEfiShellProtocol
->MinorVersion
390 STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER
),
391 ShellInfoObject
.HiiHandle
,
392 (CHAR16
*) PcdGetPtr (PcdShellSupplier
)
399 STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI
),
400 ShellInfoObject
.HiiHandle
,
401 (gST
->Hdr
.Revision
&0xffff0000)>>16,
402 (gST
->Hdr
.Revision
&0x0000ffff),
404 gST
->FirmwareRevision
409 // Display the mapping
411 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
412 Status
= RunCommand(L
"map", NULL
);
413 ASSERT_EFI_ERROR(Status
);
417 // init all the built in alias'
419 Status
= SetBuiltInAlias();
420 ASSERT_EFI_ERROR(Status
);
423 // Initialize environment variables
425 if (ShellCommandGetProfileList() != NULL
) {
426 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
427 ASSERT_EFI_ERROR(Status
);
431 TempString
= AllocateZeroPool(Size
);
433 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
434 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
435 ASSERT_EFI_ERROR(Status
);
437 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
438 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
439 ASSERT_EFI_ERROR(Status
);
441 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
442 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
443 ASSERT_EFI_ERROR(Status
);
445 FreePool(TempString
);
447 if (!EFI_ERROR(Status
)) {
448 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
450 // Set up the event for CTRL-C monitoring...
452 Status
= InernalEfiShellStartMonitor();
455 if (!EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
457 // Set up the event for CTRL-S monitoring...
459 Status
= InternalEfiShellStartCtrlSMonitor();
462 if (!EFI_ERROR(Status
) && ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
464 // close off the gST->ConIn
466 OldConIn
= gST
->ConIn
;
467 ConInHandle
= gST
->ConsoleInHandle
;
468 gST
->ConIn
= CreateSimpleTextInOnFile((SHELL_FILE_HANDLE
)&FileInterfaceNulFile
, &gST
->ConsoleInHandle
);
474 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
476 // process the startup script or launch the called app.
478 Status
= DoStartupScript(
479 ShellInfoObject
.ImageDevPath
,
480 ShellInfoObject
.FileDevPath
,
485 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
&& !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
487 // begin the UI waiting loop
491 // clean out all the memory allocated for CONST <something> * return values
492 // between each shell prompt presentation
494 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
495 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
499 // Reset page break back to default.
501 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
502 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
503 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
506 // Reset the CTRL-C event (yes we ignore the return values)
508 Status
= gBS
->CheckEvent (ShellInfoObject
.NewEfiShellProtocol
->ExecutionBreak
);
513 Status
= DoShellPrompt();
514 } while (!ShellCommandGetExit());
515 ExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
517 if (OldConIn
!= NULL
&& ConInHandle
!= NULL
) {
518 CloseSimpleTextInOnFile (gST
->ConIn
);
519 gST
->ConIn
= OldConIn
;
520 gST
->ConsoleInHandle
= ConInHandle
;
526 // uninstall protocols / free memory / etc...
528 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
529 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
530 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
532 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
533 FreePool(ShellInfoObject
.ImageDevPath
);
534 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
536 if (ShellInfoObject
.FileDevPath
!= NULL
) {
537 FreePool(ShellInfoObject
.FileDevPath
);
538 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
540 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
541 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
542 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
544 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
545 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
546 InternalEfiShellSetEnv(L
"cwd", NULL
, TRUE
);
548 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
549 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
552 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
553 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
556 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
557 ASSERT(FALSE
); ///@todo finish this de-allocation.
560 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
561 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
562 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
565 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
566 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
567 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
570 if (ShellInfoObject
.HiiHandle
!= NULL
) {
571 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
572 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
575 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
576 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
579 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
580 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
581 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
582 FreePool(ShellInfoObject
.ConsoleInfo
);
583 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
586 // If the command exited with an error, we pass this error out in the ExitData
587 // so that it can be retrieved by the EfiShellExecute function (which may
588 // start the shell with gBS->StartImage)
589 if (ExitStatus
!= SHELL_SUCCESS
) {
590 // Allocate a buffer for exit data to pass to gBS->Exit().
591 // This buffer will contain the empty string immediately followed by
592 // the shell's exit status. (The empty string is required by the UEFI spec)
593 ExitDataSize
= (sizeof (CHAR16
) + sizeof (SHELL_STATUS
));
594 ExitData
= AllocatePool (ExitDataSize
);
595 if (ExitData
== NULL
) {
596 return EFI_OUT_OF_RESOURCES
;
599 // Use CopyMem to avoid alignment faults
600 CopyMem ((ExitData
+ 1), &ExitStatus
, sizeof (ExitStatus
));
602 gBS
->Exit (ImageHandle
, EFI_ABORTED
, ExitDataSize
, ExitData
);
612 Sets all the alias' that were registered with the ShellCommandLib library.
614 @retval EFI_SUCCESS all init commands were run sucessfully.
622 CONST ALIAS_LIST
*List
;
626 // Get all the commands we want to run
628 List
= ShellCommandGetInitAliasList();
631 // for each command in the List
633 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
634 ; !IsNull (&List
->Link
, &Node
->Link
)
635 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
638 // install the alias'
640 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
641 ASSERT_EFI_ERROR(Status
);
643 return (EFI_SUCCESS
);
647 Internal function to determine if 2 command names are really the same.
649 @param[in] Command1 The pointer to the first command name.
650 @param[in] Command2 The pointer to the second command name.
652 @retval TRUE The 2 command names are the same.
653 @retval FALSE The 2 command names are not the same.
658 IN CONST CHAR16
*Command1
,
659 IN CONST CHAR16
*Command2
662 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
669 Internal function to determine if a command is a script only command.
671 @param[in] CommandName The pointer to the command name.
673 @retval TRUE The command is a script only command.
674 @retval FALSE The command is not a script only command.
679 IN CONST CHAR16
*CommandName
682 if (IsCommand(CommandName
, L
"for")
683 ||IsCommand(CommandName
, L
"endfor")
684 ||IsCommand(CommandName
, L
"if")
685 ||IsCommand(CommandName
, L
"else")
686 ||IsCommand(CommandName
, L
"endif")
687 ||IsCommand(CommandName
, L
"goto")) {
694 This function will populate the 2 device path protocol parameters based on the
695 global gImageHandle. The DevPath will point to the device path for the handle that has
696 loaded image protocol installed on it. The FilePath will point to the device path
697 for the file that was loaded.
699 @param[in, out] DevPath On a sucessful return the device path to the loaded image.
700 @param[in, out] FilePath On a sucessful return the device path to the file.
702 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
703 @retval other A error from gBS->HandleProtocol.
709 GetDevicePathsForImageAndFile (
710 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
711 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
715 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
716 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
718 ASSERT(DevPath
!= NULL
);
719 ASSERT(FilePath
!= NULL
);
721 Status
= gBS
->OpenProtocol (
723 &gEfiLoadedImageProtocolGuid
,
724 (VOID
**)&LoadedImage
,
727 EFI_OPEN_PROTOCOL_GET_PROTOCOL
729 if (!EFI_ERROR (Status
)) {
730 Status
= gBS
->OpenProtocol (
731 LoadedImage
->DeviceHandle
,
732 &gEfiDevicePathProtocolGuid
,
733 (VOID
**)&ImageDevicePath
,
736 EFI_OPEN_PROTOCOL_GET_PROTOCOL
738 if (!EFI_ERROR (Status
)) {
739 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
740 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
742 LoadedImage
->DeviceHandle
,
743 &gEfiDevicePathProtocolGuid
,
749 &gEfiLoadedImageProtocolGuid
,
756 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
757 {L
"-nostartup", TypeFlag
},
758 {L
"-startup", TypeFlag
},
759 {L
"-noconsoleout", TypeFlag
},
760 {L
"-noconsolein", TypeFlag
},
761 {L
"-nointerrupt", TypeFlag
},
762 {L
"-nomap", TypeFlag
},
763 {L
"-noversion", TypeFlag
},
764 {L
"-startup", TypeFlag
},
765 {L
"-delay", TypeValue
},
766 {L
"-_exit", TypeFlag
},
771 Process all Uefi Shell 2.0 command line options.
773 see Uefi Shell 2.0 section 3.2 for full details.
775 the command line must resemble the following:
777 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
779 ShellOpt-options Options which control the initialization behavior of the shell.
780 These options are read from the EFI global variable "ShellOpt"
781 and are processed before options or file-name.
783 options Options which control the initialization behavior of the shell.
785 file-name The name of a UEFI shell application or script to be executed
786 after initialization is complete. By default, if file-name is
787 specified, then -nostartup is implied. Scripts are not supported
790 file-name-options The command-line options that are passed to file-name when it
793 This will initialize the ShellInfoObject.ShellInitSettings global variable.
795 @retval EFI_SUCCESS The variable is initialized.
806 CONST CHAR16
*TempConst
;
809 CHAR16
*ProblemParam
;
815 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
819 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
820 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
821 ShellInfoObject
.ShellInitSettings
.FileName
= AllocateZeroPool(StrSize(TempConst
));
822 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
823 return (EFI_OUT_OF_RESOURCES
);
825 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
826 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
827 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
828 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
831 // We found the file... add the rest of the params...
833 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
834 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
835 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
839 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
840 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
841 return (EFI_OUT_OF_RESOURCES
);
843 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
845 gEfiShellParametersProtocol
->Argv
[LoopVar
],
847 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
848 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
849 return (EFI_OUT_OF_RESOURCES
);
855 ShellCommandLineFreeVarList(Package
);
857 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
858 if (EFI_ERROR(Status
)) {
859 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
860 FreePool(ProblemParam
);
861 ShellCommandLineFreeVarList(Package
);
862 return (EFI_INVALID_PARAMETER
);
866 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
867 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
868 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
869 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
870 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
871 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
872 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
873 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
874 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Exit
= ShellCommandLineGetFlag(Package
, L
"-_exit");
876 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
878 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
879 ShellInfoObject
.ShellInitSettings
.Delay
= 0;
880 } else if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
881 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
882 if (TempConst
!= NULL
&& *TempConst
== L
':') {
885 if (TempConst
!= NULL
&& !EFI_ERROR(ShellConvertStringToUint64(TempConst
, &Intermediate
, FALSE
, FALSE
))) {
886 ShellInfoObject
.ShellInitSettings
.Delay
= (UINTN
)Intermediate
;
889 ShellCommandLineFreeVarList(Package
);
895 Handles all interaction with the default startup script.
897 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
899 @param ImagePath the path to the image for shell. first place to look for the startup script
900 @param FilePath the path to the file for shell. second place to look for the startup script.
902 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
904 @retval EFI_SUCCESS the variable is initialized.
909 IN EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
910 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
911 OUT SHELL_STATUS
*ExitStatus
917 SHELL_FILE_HANDLE FileHandle
;
918 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
919 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
920 CHAR16
*FileStringPath
;
923 CONST CHAR16
*MapName
;
925 Key
.UnicodeChar
= CHAR_NULL
;
929 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
931 // launch something else instead
933 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
934 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
935 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
937 FileStringPath
= AllocateZeroPool(NewSize
);
938 if (FileStringPath
== NULL
) {
939 return (EFI_OUT_OF_RESOURCES
);
941 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
942 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
943 StrCat(FileStringPath
, L
" ");
944 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
946 Status
= RunCommand(FileStringPath
, ExitStatus
);
947 FreePool(FileStringPath
);
953 // for shell level 0 we do no scripts
954 // Without the Startup bit overriding we allow for nostartup to prevent scripts
956 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
957 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
959 return (EFI_SUCCESS
);
962 gST
->ConOut
->EnableCursor(gST
->ConOut
, FALSE
);
964 // print out our warning and see if they press a key
966 for ( Status
= EFI_UNSUPPORTED
, Delay
= ShellInfoObject
.ShellInitSettings
.Delay
967 ; Delay
!= 0 && EFI_ERROR(Status
)
970 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
);
971 gBS
->Stall (1000000);
972 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
973 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
976 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
977 gST
->ConOut
->EnableCursor(gST
->ConOut
, TRUE
);
982 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
983 return (EFI_SUCCESS
);
987 // Try the first location (must be file system)
989 MapName
= ShellInfoObject
.NewEfiShellProtocol
->GetMapFromDevicePath(&ImagePath
);
990 if (MapName
!= NULL
) {
991 FileStringPath
= NULL
;
993 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, MapName
, 0);
994 if (FileStringPath
== NULL
) {
995 Status
= EFI_OUT_OF_RESOURCES
;
997 TempSpot
= StrStr(FileStringPath
, L
";");
998 if (TempSpot
!= NULL
) {
999 *TempSpot
= CHAR_NULL
;
1001 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, ((FILEPATH_DEVICE_PATH
*)FilePath
)->PathName
, 0);
1002 PathRemoveLastItem(FileStringPath
);
1003 FileStringPath
= StrnCatGrow(&FileStringPath
, &NewSize
, mStartupScript
, 0);
1004 Status
= ShellInfoObject
.NewEfiShellProtocol
->OpenFileByName(FileStringPath
, &FileHandle
, EFI_FILE_MODE_READ
);
1005 FreePool(FileStringPath
);
1008 if (EFI_ERROR(Status
)) {
1009 NamePath
= FileDevicePath (NULL
, mStartupScript
);
1010 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
1016 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1020 // If we got a file, run it
1022 if (!EFI_ERROR(Status
) && FileHandle
!= NULL
) {
1023 Status
= RunScriptFile (
1027 ShellInfoObject
.NewShellParametersProtocol
,
1030 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
1032 FileStringPath
= ShellFindFilePath(mStartupScript
);
1033 if (FileStringPath
== NULL
) {
1035 // we return success since we dont need to have a startup script
1037 Status
= EFI_SUCCESS
;
1038 ASSERT(FileHandle
== NULL
);
1040 Status
= RunScriptFile(
1044 ShellInfoObject
.NewShellParametersProtocol
,
1047 FreePool(FileStringPath
);
1056 Function to perform the shell prompt looping. It will do a single prompt,
1057 dispatch the result, and then return. It is expected that the caller will
1058 call this function in a loop many times.
1061 @retval RETURN_ABORTED
1072 CONST CHAR16
*CurDir
;
1079 // Get screen setting to decide size of the command line buffer
1081 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
1082 BufferSize
= Column
* Row
* sizeof (CHAR16
);
1083 CmdLine
= AllocateZeroPool (BufferSize
);
1084 if (CmdLine
== NULL
) {
1085 return EFI_OUT_OF_RESOURCES
;
1088 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1093 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
1095 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1096 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1098 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1102 // Read a line from the console
1104 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
1107 // Null terminate the string and parse it
1109 if (!EFI_ERROR (Status
)) {
1110 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
1111 Status
= RunCommand(CmdLine
, NULL
);
1115 // Done with this command
1122 Add a buffer to the Buffer To Free List for safely returning buffers to other
1123 places without risking letting them modify internal shell information.
1125 @param Buffer Something to pass to FreePool when the shell is exiting.
1129 AddBufferToFreeList(
1133 BUFFER_LIST
*BufferListEntry
;
1135 if (Buffer
== NULL
) {
1139 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1140 ASSERT(BufferListEntry
!= NULL
);
1141 BufferListEntry
->Buffer
= Buffer
;
1142 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
1147 Add a buffer to the Line History List
1149 @param Buffer The line buffer to add.
1153 AddLineToCommandHistory(
1154 IN CONST CHAR16
*Buffer
1159 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
1160 ASSERT(Node
!= NULL
);
1161 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
1162 ASSERT(Node
->Buffer
!= NULL
);
1163 StrCpy(Node
->Buffer
, Buffer
);
1165 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
1169 Checks if a string is an alias for another command. If yes, then it replaces the alias name
1170 with the correct command name.
1172 @param[in, out] CommandString Upon entry the potential alias. Upon return the
1173 command name if it was an alias. If it was not
1174 an alias it will be unchanged. This function may
1175 change the buffer to fit the command name.
1177 @retval EFI_SUCCESS The name was changed.
1178 @retval EFI_SUCCESS The name was not an alias.
1179 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1184 IN OUT CHAR16
**CommandString
1187 CONST CHAR16
*NewString
;
1189 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
1190 if (NewString
== NULL
) {
1191 return (EFI_SUCCESS
);
1193 FreePool(*CommandString
);
1194 *CommandString
= AllocateZeroPool(StrSize(NewString
));
1195 if (*CommandString
== NULL
) {
1196 return (EFI_OUT_OF_RESOURCES
);
1198 StrCpy(*CommandString
, NewString
);
1199 return (EFI_SUCCESS
);
1203 Function allocates a new command line and replaces all instances of environment
1204 variable names that are correctly preset to their values.
1206 If the return value is not NULL the memory must be caller freed.
1208 @param[in] OriginalCommandLine The original command line
1210 @retval NULL An error ocurred.
1211 @return The new command line with no environment variables present.
1215 ShellConvertVariables (
1216 IN CONST CHAR16
*OriginalCommandLine
1219 CONST CHAR16
*MasterEnvList
;
1221 CHAR16
*NewCommandLine1
;
1222 CHAR16
*NewCommandLine2
;
1227 SCRIPT_FILE
*CurrentScriptFile
;
1228 ALIAS_LIST
*AliasListNode
;
1230 ASSERT(OriginalCommandLine
!= NULL
);
1233 NewSize
= StrSize(OriginalCommandLine
);
1234 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
1237 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
1240 // calculate the size required for the post-conversion string...
1242 if (CurrentScriptFile
!= NULL
) {
1243 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1244 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1245 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1247 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
1249 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
1252 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
1254 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
1255 NewSize
+= StrSize(AliasListNode
->CommandString
);
1261 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1262 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1263 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1265 if (StrSize(MasterEnvList
) > ItemSize
) {
1266 ItemSize
= StrSize(MasterEnvList
);
1268 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
1270 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
1273 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
1275 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
1276 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1277 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1283 // now do the replacements...
1285 NewCommandLine1
= AllocateZeroPool(NewSize
);
1286 NewCommandLine2
= AllocateZeroPool(NewSize
);
1287 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1288 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1289 SHELL_FREE_NON_NULL(NewCommandLine1
);
1290 SHELL_FREE_NON_NULL(NewCommandLine2
);
1291 SHELL_FREE_NON_NULL(ItemTemp
);
1294 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1295 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1296 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1297 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1299 StrCpy(ItemTemp
, L
"%");
1300 StrCat(ItemTemp
, MasterEnvList
);
1301 StrCat(ItemTemp
, L
"%");
1302 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1303 StrCpy(NewCommandLine1
, NewCommandLine2
);
1305 if (CurrentScriptFile
!= NULL
) {
1306 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1307 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1308 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1310 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1311 StrCpy(NewCommandLine1
, NewCommandLine2
);
1315 // Remove non-existant environment variables in scripts only
1317 for (Temp
= NewCommandLine1
; Temp
!= NULL
; ) {
1318 Temp
= StrStr(Temp
, L
"%");
1322 while (*(Temp
- 1) == L
'^') {
1323 Temp
= StrStr(Temp
+ 1, L
"%");
1332 Temp2
= StrStr(Temp
+ 1, L
"%");
1333 if (Temp2
== NULL
) {
1336 while (*(Temp2
- 1) == L
'^') {
1337 Temp2
= StrStr(Temp2
+ 1, L
"%");
1338 if (Temp2
== NULL
) {
1342 if (Temp2
== NULL
) {
1347 CopyMem(Temp
, Temp2
, StrSize(Temp2
));
1353 // Now cleanup any straggler intentionally ignored "%" characters
1355 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, L
"^%", L
"%", TRUE
, FALSE
);
1356 StrCpy(NewCommandLine1
, NewCommandLine2
);
1358 FreePool(NewCommandLine2
);
1361 return (NewCommandLine1
);
1365 Internal function to run a command line with pipe usage.
1367 @param[in] CmdLine The pointer to the command line.
1368 @param[in] StdIn The pointer to the Standard input.
1369 @param[in] StdOut The pointer to the Standard output.
1371 @param[out] ExitStatus The exit code of the last command in the pipeline.
1374 @retval EFI_SUCCESS The split command is executed successfully.
1375 @retval other Some error occurs when executing the split command.
1380 IN CONST CHAR16
*CmdLine
,
1381 IN SHELL_FILE_HANDLE
*StdIn
,
1382 IN SHELL_FILE_HANDLE
*StdOut
,
1383 OUT SHELL_STATUS
*ExitStatus
1387 CHAR16
*NextCommandLine
;
1388 CHAR16
*OurCommandLine
;
1392 SHELL_FILE_HANDLE
*TempFileHandle
;
1395 ASSERT(StdOut
== NULL
);
1397 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1399 Status
= EFI_SUCCESS
;
1400 NextCommandLine
= NULL
;
1401 OurCommandLine
= NULL
;
1405 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1406 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1408 if (NextCommandLine
== NULL
|| OurCommandLine
== NULL
) {
1409 SHELL_FREE_NON_NULL(OurCommandLine
);
1410 SHELL_FREE_NON_NULL(NextCommandLine
);
1411 return (EFI_OUT_OF_RESOURCES
);
1412 } else if (StrStr(OurCommandLine
, L
"|") != NULL
|| Size1
== 0 || Size2
== 0) {
1413 SHELL_FREE_NON_NULL(OurCommandLine
);
1414 SHELL_FREE_NON_NULL(NextCommandLine
);
1415 return (EFI_INVALID_PARAMETER
);
1416 } else if (NextCommandLine
[0] != CHAR_NULL
&&
1417 NextCommandLine
[0] == L
'a' &&
1418 NextCommandLine
[1] == L
' '
1420 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1428 // make a SPLIT_LIST item and add to list
1430 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1431 ASSERT(Split
!= NULL
);
1432 Split
->SplitStdIn
= StdIn
;
1433 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1434 ASSERT(Split
->SplitStdOut
!= NULL
);
1435 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1437 Status
= RunCommand(OurCommandLine
, NULL
);
1440 // move the output from the first to the in to the second.
1442 TempFileHandle
= Split
->SplitStdOut
;
1443 if (Split
->SplitStdIn
== StdIn
) {
1444 Split
->SplitStdOut
= NULL
;
1446 Split
->SplitStdOut
= Split
->SplitStdIn
;
1448 Split
->SplitStdIn
= TempFileHandle
;
1449 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1451 if (!EFI_ERROR(Status
)) {
1452 Status
= RunCommand(NextCommandLine
, ExitStatus
);
1456 // remove the top level from the ScriptList
1458 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1459 RemoveEntryList(&Split
->Link
);
1462 // Note that the original StdIn is now the StdOut...
1464 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1465 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1467 if (Split
->SplitStdIn
!= NULL
) {
1468 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1472 FreePool(NextCommandLine
);
1473 FreePool(OurCommandLine
);
1479 Take the original command line, substitute any variables, free
1480 the original string, return the modified copy.
1482 @param[in] CmdLine pointer to the command line to update.
1484 @retval EFI_SUCCESS the function was successful.
1485 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1489 ShellSubstituteVariables(
1494 NewCmdLine
= ShellConvertVariables(*CmdLine
);
1495 SHELL_FREE_NON_NULL(*CmdLine
);
1496 if (NewCmdLine
== NULL
) {
1497 return (EFI_OUT_OF_RESOURCES
);
1499 *CmdLine
= NewCmdLine
;
1500 return (EFI_SUCCESS
);
1504 Take the original command line, substitute any alias in the first group of space delimited characters, free
1505 the original string, return the modified copy.
1507 @param[in] CmdLine pointer to the command line to update.
1509 @retval EFI_SUCCESS the function was successful.
1510 @retval EFI_OUT_OF_RESOURCES a memory allocation failed.
1514 ShellSubstituteAliases(
1519 CHAR16
*CommandName
;
1521 UINTN PostAliasSize
;
1522 ASSERT(CmdLine
!= NULL
);
1523 ASSERT(*CmdLine
!= NULL
);
1527 if (StrStr((*CmdLine
), L
" ") == NULL
){
1528 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), 0);
1530 StrnCatGrow(&CommandName
, NULL
, (*CmdLine
), StrStr((*CmdLine
), L
" ") - (*CmdLine
));
1534 // This cannot happen 'inline' since the CmdLine can need extra space.
1537 if (!ShellCommandIsCommandOnList(CommandName
)) {
1539 // Convert via alias
1541 Status
= ShellConvertAlias(&CommandName
);
1542 if (EFI_ERROR(Status
)){
1546 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, CommandName
, 0);
1547 if (NewCmdLine
== NULL
) {
1548 SHELL_FREE_NON_NULL(CommandName
);
1549 SHELL_FREE_NON_NULL(*CmdLine
);
1550 return (EFI_OUT_OF_RESOURCES
);
1552 NewCmdLine
= StrnCatGrow(&NewCmdLine
, &PostAliasSize
, StrStr((*CmdLine
), L
" "), 0);
1553 if (NewCmdLine
== NULL
) {
1554 SHELL_FREE_NON_NULL(CommandName
);
1555 SHELL_FREE_NON_NULL(*CmdLine
);
1556 return (EFI_OUT_OF_RESOURCES
);
1559 NewCmdLine
= StrnCatGrow(&NewCmdLine
, NULL
, (*CmdLine
), 0);
1562 SHELL_FREE_NON_NULL(*CmdLine
);
1563 SHELL_FREE_NON_NULL(CommandName
);
1566 // re-assign the passed in double pointer to point to our newly allocated buffer
1568 *CmdLine
= NewCmdLine
;
1570 return (EFI_SUCCESS
);
1574 Takes the Argv[0] part of the command line and determine the meaning of it.
1576 @param[in] CmdName pointer to the command line to update.
1578 @retval Internal_Command The name is an internal command.
1579 @retval File_Sys_Change the name is a file system change.
1580 @retval Script_File_Name the name is a NSH script file.
1581 @retval Unknown_Invalid the name is unknown.
1582 @retval Efi_Application the name is an application (.EFI).
1584 SHELL_OPERATION_TYPES
1587 IN CONST CHAR16
*CmdName
1590 CHAR16
* FileWithPath
;
1591 CONST CHAR16
* TempLocation
;
1592 CONST CHAR16
* TempLocation2
;
1594 FileWithPath
= NULL
;
1596 // test for an internal command.
1598 if (ShellCommandIsCommandOnList(CmdName
)) {
1599 return (Internal_Command
);
1603 // Test for file system change request. anything ending with : and cant have spaces.
1605 if (CmdName
[(StrLen(CmdName
)-1)] == L
':') {
1606 if (StrStr(CmdName
, L
" ") != NULL
) {
1607 return (Unknown_Invalid
);
1609 return (File_Sys_Change
);
1615 if ((FileWithPath
= ShellFindFilePathEx(CmdName
, mExecutableExtensions
)) != NULL
) {
1617 // See if that file has a script file extension
1619 if (StrLen(FileWithPath
) > 4) {
1620 TempLocation
= FileWithPath
+StrLen(FileWithPath
)-4;
1621 TempLocation2
= mScriptExtension
;
1622 if (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0) {
1623 SHELL_FREE_NON_NULL(FileWithPath
);
1624 return (Script_File_Name
);
1629 // Was a file, but not a script. we treat this as an application.
1631 SHELL_FREE_NON_NULL(FileWithPath
);
1632 return (Efi_Application
);
1635 SHELL_FREE_NON_NULL(FileWithPath
);
1637 // No clue what this is... return invalid flag...
1639 return (Unknown_Invalid
);
1643 Determine if the first item in a command line is valid.
1645 @param[in] CmdLine The command line to parse.
1647 @retval EFI_SUCCESS The item is valid.
1648 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1649 @retval EFI_NOT_FOUND The operation type is unknown or invalid.
1654 IN CONST CHAR16
*CmdLine
1658 CHAR16
*FirstParameter
;
1664 Temp
= StrnCatGrow(&Temp
, NULL
, CmdLine
, 0);
1666 return (EFI_OUT_OF_RESOURCES
);
1669 FirstParameter
= StrStr(Temp
, L
"|");
1670 if (FirstParameter
!= NULL
) {
1671 *FirstParameter
= CHAR_NULL
;
1674 FirstParameter
= NULL
;
1677 // Process the command line
1679 Status
= ProcessCommandLineToFinal(&Temp
);
1681 if (!EFI_ERROR(Status
)) {
1682 FirstParameter
= AllocateZeroPool(StrSize(CmdLine
));
1683 if (FirstParameter
== NULL
) {
1684 SHELL_FREE_NON_NULL(Temp
);
1685 return (EFI_OUT_OF_RESOURCES
);
1687 TempWalker
= (CHAR16
*)Temp
;
1688 GetNextParameter(&TempWalker
, &FirstParameter
);
1690 if (GetOperationType(FirstParameter
) == Unknown_Invalid
) {
1691 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
1692 SetLastError(SHELL_NOT_FOUND
);
1693 Status
= EFI_NOT_FOUND
;
1697 SHELL_FREE_NON_NULL(Temp
);
1698 SHELL_FREE_NON_NULL(FirstParameter
);
1703 Determine if a command line contains with a split contains only valid commands.
1705 @param[in] CmdLine The command line to parse.
1707 @retval EFI_SUCCESS CmdLine has only valid commands, application, or has no split.
1708 @retval EFI_ABORTED CmdLine has at least one invalid command or application.
1713 IN CONST CHAR16
*CmdLine
1716 CONST CHAR16
*TempSpot
;
1720 // Verify up to the pipe or end character
1722 Status
= IsValidSplit(CmdLine
);
1723 if (EFI_ERROR(Status
)) {
1728 // If this was the only item, then get out
1730 if (!ContainsSplit(CmdLine
)) {
1731 return (EFI_SUCCESS
);
1735 // recurse to verify the next item
1737 TempSpot
= FindSplit(CmdLine
)+1;
1738 return (VerifySplit(TempSpot
));
1742 Process a split based operation.
1744 @param[in] CmdLine Pointer to the command line to process
1745 @param[out] ExitStatus The exit status of the command. Ignored if NULL.
1746 Invalid if this function returns an error.
1748 @retval EFI_SUCCESS The operation was successful
1749 @return an error occured.
1753 ProcessNewSplitCommandLine(
1754 IN CONST CHAR16
*CmdLine
,
1755 OUT SHELL_STATUS
*ExitStatus
1761 Status
= VerifySplit(CmdLine
);
1762 if (EFI_ERROR(Status
)) {
1769 // are we in an existing split???
1771 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1772 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1775 if (Split
== NULL
) {
1776 Status
= RunSplitCommand(CmdLine
, NULL
, NULL
, ExitStatus
);
1778 Status
= RunSplitCommand(
1785 if (EFI_ERROR(Status
)) {
1786 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_SPLIT
), ShellInfoObject
.HiiHandle
, CmdLine
);
1792 Handle a request to change the current file system.
1794 @param[in] CmdLine The passed in command line.
1796 @retval EFI_SUCCESS The operation was successful.
1801 IN CONST CHAR16
*CmdLine
1805 Status
= EFI_SUCCESS
;
1808 // make sure we are the right operation
1810 ASSERT(CmdLine
[(StrLen(CmdLine
)-1)] == L
':' && StrStr(CmdLine
, L
" ") == NULL
);
1813 // Call the protocol API to do the work
1815 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, CmdLine
);
1818 // Report any errors
1820 if (EFI_ERROR(Status
)) {
1821 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, CmdLine
);
1828 Reprocess the command line to direct all -? to the help command.
1830 if found, will add "help" as argv[0], and move the rest later.
1832 @param[in,out] CmdLine pointer to the command line to update
1837 IN OUT CHAR16
**CmdLine
1840 CHAR16
*CurrentParameter
;
1843 CHAR16
*NewCommandLine
;
1846 Status
= EFI_SUCCESS
;
1848 CurrentParameter
= AllocateZeroPool(StrSize(*CmdLine
));
1849 if (CurrentParameter
== NULL
) {
1850 return (EFI_OUT_OF_RESOURCES
);
1854 while(Walker
!= NULL
&& *Walker
!= CHAR_NULL
) {
1855 LastWalker
= Walker
;
1856 GetNextParameter(&Walker
, &CurrentParameter
);
1857 if (StrStr(CurrentParameter
, L
"-?") == CurrentParameter
) {
1858 LastWalker
[0] = L
' ';
1859 LastWalker
[1] = L
' ';
1860 NewCommandLine
= AllocateZeroPool(StrSize(L
"help ") + StrSize(*CmdLine
));
1861 if (NewCommandLine
== NULL
) {
1862 Status
= EFI_OUT_OF_RESOURCES
;
1865 StrCpy(NewCommandLine
, L
"help ");
1866 StrCat(NewCommandLine
, *CmdLine
);
1867 SHELL_FREE_NON_NULL(*CmdLine
);
1868 *CmdLine
= NewCommandLine
;
1873 SHELL_FREE_NON_NULL(CurrentParameter
);
1879 Function to update the shell variable "lasterror".
1881 @param[in] ErrorCode the error code to put into lasterror.
1886 IN CONST SHELL_STATUS ErrorCode
1889 CHAR16 LeString
[19];
1890 if (sizeof(EFI_STATUS
) == sizeof(UINT64
)) {
1891 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", ErrorCode
);
1893 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%x", ErrorCode
);
1895 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
1896 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
1898 return (EFI_SUCCESS
);
1902 Converts the command line to it's post-processed form. this replaces variables and alias' per UEFI Shell spec.
1904 @param[in,out] CmdLine pointer to the command line to update
1906 @retval EFI_SUCCESS The operation was successful
1907 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1908 @return some other error occured
1912 ProcessCommandLineToFinal(
1913 IN OUT CHAR16
**CmdLine
1917 TrimSpaces(CmdLine
);
1919 Status
= ShellSubstituteAliases(CmdLine
);
1920 if (EFI_ERROR(Status
)) {
1924 TrimSpaces(CmdLine
);
1926 Status
= ShellSubstituteVariables(CmdLine
);
1927 if (EFI_ERROR(Status
)) {
1931 TrimSpaces(CmdLine
);
1934 // update for help parsing
1936 if (StrStr(*CmdLine
, L
"?") != NULL
) {
1938 // This may do nothing if the ? does not indicate help.
1939 // Save all the details for in the API below.
1941 Status
= DoHelpUpdate(CmdLine
);
1944 TrimSpaces(CmdLine
);
1946 return (EFI_SUCCESS
);
1950 Run an internal shell command.
1952 This API will upadate the shell's environment since these commands are libraries.
1954 @param[in] CmdLine the command line to run.
1955 @param[in] FirstParameter the first parameter on the command line
1956 @param[in] ParamProtocol the shell parameters protocol pointer
1958 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
1960 @retval EFI_SUCCESS The command was completed.
1961 @retval EFI_ABORTED The command's operation was aborted.
1966 IN CONST CHAR16
*CmdLine
,
1967 IN CHAR16
*FirstParameter
,
1968 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
1969 OUT SHELL_STATUS
*ExitStatus OPTIONAL
1975 SHELL_STATUS CommandReturnedStatus
;
1979 // get the argc and argv updated for internal commands
1981 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
1982 if (!EFI_ERROR(Status
)) {
1984 // Run the internal command.
1986 Status
= ShellCommandRunCommandHandler(FirstParameter
, &CommandReturnedStatus
, &LastError
);
1988 if (!EFI_ERROR(Status
)) {
1990 // Update last error status.
1991 // some commands do not update last error.
1994 SetLastError(CommandReturnedStatus
);
1996 if (ExitStatus
!= NULL
) {
1997 *ExitStatus
= CommandReturnedStatus
;
2001 // Pass thru the exitcode from the app.
2003 if (ShellCommandGetExit()) {
2005 // An Exit was requested ("exit" command), pass its value up.
2007 Status
= CommandReturnedStatus
;
2008 } else if (CommandReturnedStatus
!= SHELL_SUCCESS
&& IsScriptOnlyCommand(FirstParameter
)) {
2010 // Always abort when a script only command fails for any reason
2012 Status
= EFI_ABORTED
;
2013 } else if (ShellCommandGetCurrentScriptFile() != NULL
&& CommandReturnedStatus
== SHELL_ABORTED
) {
2015 // Abort when in a script and a command aborted
2017 Status
= EFI_ABORTED
;
2023 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2024 // This is safe even if the update API failed. In this case, it may be a no-op.
2026 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);
2029 // If a script is running and the command is not a scipt only command, then
2030 // change return value to success so the script won't halt (unless aborted).
2032 // Script only commands have to be able halt the script since the script will
2033 // not operate if they are failing.
2035 if ( ShellCommandGetCurrentScriptFile() != NULL
2036 && !IsScriptOnlyCommand(FirstParameter
)
2037 && Status
!= EFI_ABORTED
2039 Status
= EFI_SUCCESS
;
2046 Function to run the command or file.
2048 @param[in] Type the type of operation being run.
2049 @param[in] CmdLine the command line to run.
2050 @param[in] FirstParameter the first parameter on the command line
2051 @param[in] ParamProtocol the shell parameters protocol pointer
2053 @param[out] ExitStatus The exit code of the command or file.
2056 @retval EFI_SUCCESS The command was completed.
2057 @retval EFI_ABORTED The command's operation was aborted.
2062 IN SHELL_OPERATION_TYPES Type
,
2063 IN CONST CHAR16
*CmdLine
,
2064 IN CHAR16
*FirstParameter
,
2065 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2066 OUT SHELL_STATUS
*ExitStatus
2070 CHAR16
*CommandWithPath
;
2071 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
2072 SHELL_STATUS CalleeExitStatus
;
2074 Status
= EFI_SUCCESS
;
2075 CommandWithPath
= NULL
;
2079 case Internal_Command
:
2080 Status
= RunInternalCommand(
2087 case Script_File_Name
:
2088 case Efi_Application
:
2090 // Process a fully qualified path
2092 if (StrStr(FirstParameter
, L
":") != NULL
) {
2093 ASSERT (CommandWithPath
== NULL
);
2094 if (ShellIsFile(FirstParameter
) == EFI_SUCCESS
) {
2095 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, FirstParameter
, 0);
2100 // Process a relative path and also check in the path environment variable
2102 if (CommandWithPath
== NULL
) {
2103 CommandWithPath
= ShellFindFilePathEx(FirstParameter
, mExecutableExtensions
);
2107 // This should be impossible now.
2109 ASSERT(CommandWithPath
!= NULL
);
2112 // Make sure that path is not just a directory (or not found)
2114 if (!EFI_ERROR(ShellIsDirectory(CommandWithPath
))) {
2115 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2116 SetLastError(SHELL_NOT_FOUND
);
2119 case Script_File_Name
:
2120 Status
= RunScriptFile (
2128 case Efi_Application
:
2130 // Get the device path of the application image
2132 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
2133 if (DevPath
== NULL
){
2134 Status
= EFI_OUT_OF_RESOURCES
;
2139 // Execute the device path
2141 Status
= InternalShellExecuteDevicePath(
2150 SHELL_FREE_NON_NULL(DevPath
);
2153 // Update last error status.
2155 // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
2156 SetLastError((SHELL_STATUS
) (Status
& (~MAX_BIT
)));
2172 SHELL_FREE_NON_NULL(CommandWithPath
);
2174 if (ExitStatus
!= NULL
) {
2175 *ExitStatus
= CalleeExitStatus
;
2182 Function to setup StdIn, StdErr, StdOut, and then run the command or file.
2184 @param[in] Type the type of operation being run.
2185 @param[in] CmdLine the command line to run.
2186 @param[in] FirstParameter the first parameter on the command line.
2187 @param[in] ParamProtocol the shell parameters protocol pointer
2189 @param[out] ExitStatus The exit code of the command or file.
2192 @retval EFI_SUCCESS The command was completed.
2193 @retval EFI_ABORTED The command's operation was aborted.
2197 SetupAndRunCommandOrFile(
2198 IN SHELL_OPERATION_TYPES Type
,
2200 IN CHAR16
*FirstParameter
,
2201 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2202 OUT SHELL_STATUS
*ExitStatus
2206 SHELL_FILE_HANDLE OriginalStdIn
;
2207 SHELL_FILE_HANDLE OriginalStdOut
;
2208 SHELL_FILE_HANDLE OriginalStdErr
;
2209 SYSTEM_TABLE_INFO OriginalSystemTableInfo
;
2212 // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
2214 Status
= UpdateStdInStdOutStdErr(ParamProtocol
, CmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2217 // The StdIn, StdOut, and StdErr are set up.
2218 // Now run the command, script, or application
2220 if (!EFI_ERROR(Status
)) {
2221 Status
= RunCommandOrFile(
2233 if (EFI_ERROR(Status
)) {
2234 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_ERROR
), ShellInfoObject
.HiiHandle
, (VOID
*)(Status
));
2238 // put back the original StdIn, StdOut, and StdErr
2240 RestoreStdInStdOutStdErr(ParamProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
, &OriginalSystemTableInfo
);
2246 Function will process and run a command line.
2248 This will determine if the command line represents an internal shell
2249 command or dispatch an external application.
2251 @param[in] CmdLine The command line to parse.
2252 @param[out] ExitStatus The exit code of the command. Ignored if NULL.
2254 @retval EFI_SUCCESS The command was completed.
2255 @retval EFI_ABORTED The command's operation was aborted.
2260 IN CONST CHAR16
*CmdLine
,
2261 OUT SHELL_STATUS
*ExitStatus
2265 CHAR16
*CleanOriginal
;
2266 CHAR16
*FirstParameter
;
2268 SHELL_OPERATION_TYPES Type
;
2270 ASSERT(CmdLine
!= NULL
);
2271 if (StrLen(CmdLine
) == 0) {
2272 return (EFI_SUCCESS
);
2275 Status
= EFI_SUCCESS
;
2276 CleanOriginal
= NULL
;
2278 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
2279 if (CleanOriginal
== NULL
) {
2280 return (EFI_OUT_OF_RESOURCES
);
2283 TrimSpaces(&CleanOriginal
);
2286 // Handle case that passed in command line is just 1 or more " " characters.
2288 if (StrLen (CleanOriginal
) == 0) {
2289 SHELL_FREE_NON_NULL(CleanOriginal
);
2290 return (EFI_SUCCESS
);
2293 Status
= ProcessCommandLineToFinal(&CleanOriginal
);
2294 if (EFI_ERROR(Status
)) {
2295 SHELL_FREE_NON_NULL(CleanOriginal
);
2300 // We dont do normal processing with a split command line (output from one command input to another)
2302 if (ContainsSplit(CleanOriginal
)) {
2303 Status
= ProcessNewSplitCommandLine(CleanOriginal
, ExitStatus
);
2304 SHELL_FREE_NON_NULL(CleanOriginal
);
2309 // We need the first parameter information so we can determine the operation type
2311 FirstParameter
= AllocateZeroPool(StrSize(CleanOriginal
));
2312 if (FirstParameter
== NULL
) {
2313 SHELL_FREE_NON_NULL(CleanOriginal
);
2314 return (EFI_OUT_OF_RESOURCES
);
2316 TempWalker
= CleanOriginal
;
2317 GetNextParameter(&TempWalker
, &FirstParameter
);
2320 // Depending on the first parameter we change the behavior
2322 switch (Type
= GetOperationType(FirstParameter
)) {
2323 case File_Sys_Change
:
2324 Status
= ChangeMappedDrive(CleanOriginal
);
2326 case Internal_Command
:
2327 case Script_File_Name
:
2328 case Efi_Application
:
2329 Status
= SetupAndRunCommandOrFile(
2333 ShellInfoObject
.NewShellParametersProtocol
,
2339 // Whatever was typed, it was invalid.
2341 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, FirstParameter
);
2342 SetLastError(SHELL_NOT_FOUND
);
2346 SHELL_FREE_NON_NULL(CleanOriginal
);
2347 SHELL_FREE_NON_NULL(FirstParameter
);
2352 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
2354 Function determins if the CommandName COULD be a valid command. It does not determine whether
2355 this is a valid command. It only checks for invalid characters.
2357 @param[in] CommandName The name to check
2359 @retval TRUE CommandName could be a command name
2360 @retval FALSE CommandName could not be a valid command name
2365 IN CONST CHAR16
*CommandName
2369 if (CommandName
== NULL
) {
2374 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
2377 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
2385 Function to process a NSH script file via SHELL_FILE_HANDLE.
2387 @param[in] Handle The handle to the already opened file.
2388 @param[in] Name The name of the script file.
2390 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2392 @retval EFI_SUCCESS the script completed sucessfully
2396 RunScriptFileHandle (
2397 IN SHELL_FILE_HANDLE Handle
,
2398 IN CONST CHAR16
*Name
,
2399 OUT SHELL_STATUS
*ExitStatus
2403 SCRIPT_FILE
*NewScriptFile
;
2405 CHAR16
*CommandLine
;
2406 CHAR16
*CommandLine2
;
2407 CHAR16
*CommandLine3
;
2408 SCRIPT_COMMAND_LIST
*LastCommand
;
2410 BOOLEAN PreScriptEchoState
;
2411 BOOLEAN PreCommandEchoState
;
2412 CONST CHAR16
*CurDir
;
2414 CHAR16 LeString
[50];
2415 SHELL_STATUS CalleeExitStatus
= SHELL_SUCCESS
;
2417 ASSERT(!ShellCommandGetScriptExit());
2419 PreScriptEchoState
= ShellCommandGetEchoState();
2421 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
2422 if (NewScriptFile
== NULL
) {
2423 return (EFI_OUT_OF_RESOURCES
);
2429 ASSERT(NewScriptFile
->ScriptName
== NULL
);
2430 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
2431 if (NewScriptFile
->ScriptName
== NULL
) {
2432 DeleteScriptFileStruct(NewScriptFile
);
2433 return (EFI_OUT_OF_RESOURCES
);
2437 // Save the parameters (used to replace %0 to %9 later on)
2439 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
2440 if (NewScriptFile
->Argc
!= 0) {
2441 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
2442 if (NewScriptFile
->Argv
== NULL
) {
2443 DeleteScriptFileStruct(NewScriptFile
);
2444 return (EFI_OUT_OF_RESOURCES
);
2446 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
2447 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
2448 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
2449 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
2450 DeleteScriptFileStruct(NewScriptFile
);
2451 return (EFI_OUT_OF_RESOURCES
);
2455 NewScriptFile
->Argv
= NULL
;
2458 InitializeListHead(&NewScriptFile
->CommandList
);
2459 InitializeListHead(&NewScriptFile
->SubstList
);
2462 // Now build the list of all script commands.
2465 while(!ShellFileHandleEof(Handle
)) {
2466 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
2468 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0 || CommandLine
[0] == '#') {
2469 SHELL_FREE_NON_NULL(CommandLine
);
2472 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
2473 if (NewScriptFile
->CurrentCommand
== NULL
) {
2474 SHELL_FREE_NON_NULL(CommandLine
);
2475 DeleteScriptFileStruct(NewScriptFile
);
2476 return (EFI_OUT_OF_RESOURCES
);
2479 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
2480 NewScriptFile
->CurrentCommand
->Data
= NULL
;
2481 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
2483 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2487 // Add this as the topmost script file
2489 ShellCommandSetNewScript (NewScriptFile
);
2492 // Now enumerate through the commands and run each one.
2494 CommandLine
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2495 if (CommandLine
== NULL
) {
2496 DeleteScriptFileStruct(NewScriptFile
);
2497 return (EFI_OUT_OF_RESOURCES
);
2499 CommandLine2
= AllocateZeroPool(PcdGet16(PcdShellPrintBufferSize
));
2500 if (CommandLine2
== NULL
) {
2501 FreePool(CommandLine
);
2502 DeleteScriptFileStruct(NewScriptFile
);
2503 return (EFI_OUT_OF_RESOURCES
);
2506 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
2507 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
2508 ; // conditional increment in the body of the loop
2510 ASSERT(CommandLine2
!= NULL
);
2511 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
2514 // NULL out comments
2516 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
2517 if (*CommandLine3
== L
'^') {
2518 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
2519 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
2521 } else if (*CommandLine3
== L
'#') {
2522 *CommandLine3
= CHAR_NULL
;
2526 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
2528 // Due to variability in starting the find and replace action we need to have both buffers the same.
2530 StrCpy(CommandLine
, CommandLine2
);
2533 // Remove the %0 to %9 from the command line (if we have some arguments)
2535 if (NewScriptFile
->Argv
!= NULL
) {
2536 switch (NewScriptFile
->Argc
) {
2538 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
2539 ASSERT_EFI_ERROR(Status
);
2541 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
2542 ASSERT_EFI_ERROR(Status
);
2544 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
2545 ASSERT_EFI_ERROR(Status
);
2547 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
2548 ASSERT_EFI_ERROR(Status
);
2550 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
2551 ASSERT_EFI_ERROR(Status
);
2553 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
2554 ASSERT_EFI_ERROR(Status
);
2556 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
2557 ASSERT_EFI_ERROR(Status
);
2559 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
2560 ASSERT_EFI_ERROR(Status
);
2562 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
2563 ASSERT_EFI_ERROR(Status
);
2565 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
2566 ASSERT_EFI_ERROR(Status
);
2572 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
2573 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
2574 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
2575 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
2576 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
2577 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
2578 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
2579 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
2580 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
2582 StrCpy(CommandLine2
, CommandLine
);
2584 LastCommand
= NewScriptFile
->CurrentCommand
;
2586 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
2588 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
2590 // This line is a goto target / label
2593 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
2594 if (CommandLine3
[0] == L
'@') {
2596 // We need to save the current echo state
2597 // and disable echo for just this command.
2599 PreCommandEchoState
= ShellCommandGetEchoState();
2600 ShellCommandSetEchoState(FALSE
);
2601 Status
= RunCommand(CommandLine3
+1, NULL
);
2604 // If command was "@echo -off" or "@echo -on" then don't restore echo state
2606 if (StrCmp (L
"@echo -off", CommandLine3
) != 0 &&
2607 StrCmp (L
"@echo -on", CommandLine3
) != 0) {
2609 // Now restore the pre-'@' echo state.
2611 ShellCommandSetEchoState(PreCommandEchoState
);
2614 if (ShellCommandGetEchoState()) {
2615 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
2616 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
2617 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
2619 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
2621 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
2623 Status
= RunCommand(CommandLine3
, NULL
);
2627 if (ShellCommandGetScriptExit()) {
2629 // ShellCommandGetExitCode() always returns a UINT64
2631 CalleeExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
2632 UnicodeSPrint(LeString
, sizeof(LeString
), L
"0x%Lx", CalleeExitStatus
);
2633 DEBUG_CODE(InternalEfiShellSetEnv(L
"debuglasterror", LeString
, TRUE
););
2634 InternalEfiShellSetEnv(L
"lasterror", LeString
, TRUE
);
2636 ShellCommandRegisterExit(FALSE
, 0);
2637 Status
= EFI_SUCCESS
;
2640 if (ShellGetExecutionBreakFlag()) {
2643 if (EFI_ERROR(Status
)) {
2644 CalleeExitStatus
= (SHELL_STATUS
) Status
;
2647 if (ShellCommandGetExit()) {
2648 CalleeExitStatus
= (SHELL_STATUS
) ShellCommandGetExitCode();
2653 // If that commend did not update the CurrentCommand then we need to advance it...
2655 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
2656 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2657 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2658 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2662 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
2663 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
2664 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
2670 FreePool(CommandLine
);
2671 FreePool(CommandLine2
);
2672 ShellCommandSetNewScript (NULL
);
2675 // Only if this was the last script reset the state.
2677 if (ShellCommandGetCurrentScriptFile()==NULL
) {
2678 ShellCommandSetEchoState(PreScriptEchoState
);
2681 if (ExitStatus
!= NULL
) {
2682 *ExitStatus
= CalleeExitStatus
;
2685 return (EFI_SUCCESS
);
2689 Function to process a NSH script file.
2691 @param[in] ScriptPath Pointer to the script file name (including file system path).
2692 @param[in] Handle the handle of the script file already opened.
2693 @param[in] CmdLine the command line to run.
2694 @param[in] ParamProtocol the shell parameters protocol pointer
2696 @param[out] ExitStatus The exit code of the script. Ignored if NULL.
2698 @retval EFI_SUCCESS the script completed sucessfully
2703 IN CONST CHAR16
*ScriptPath
,
2704 IN SHELL_FILE_HANDLE Handle OPTIONAL
,
2705 IN CONST CHAR16
*CmdLine
,
2706 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ParamProtocol
,
2707 OUT SHELL_STATUS
*ExitStatus
2711 SHELL_FILE_HANDLE FileHandle
;
2715 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
2716 return (EFI_INVALID_PARAMETER
);
2720 // get the argc and argv updated for scripts
2722 Status
= UpdateArgcArgv(ParamProtocol
, CmdLine
, &Argv
, &Argc
);
2723 if (!EFI_ERROR(Status
)) {
2725 if (Handle
== NULL
) {
2729 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
2730 if (!EFI_ERROR(Status
)) {
2734 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
, ExitStatus
);
2737 // now close the file
2739 ShellCloseFile(&FileHandle
);
2742 Status
= RunScriptFileHandle(Handle
, ScriptPath
, ExitStatus
);
2747 // This is guarenteed to be called after UpdateArgcArgv no matter what else happened.
2748 // This is safe even if the update API failed. In this case, it may be a no-op.
2750 RestoreArgcArgv(ParamProtocol
, &Argv
, &Argc
);