2 This is THE shell (application)
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 // Initialize the global structure
20 SHELL_INFO ShellInfoObject
= {
57 STATIC CONST CHAR16 mScriptExtension
[] = L
".NSH";
58 STATIC CONST CHAR16 mExecutableExtensions
[] = L
".NSH;.EFI";
61 The entry point for the application.
63 @param[in] ImageHandle The firmware allocated handle for the EFI image.
64 @param[in] SystemTable A pointer to the EFI System Table.
66 @retval EFI_SUCCESS The entry point is executed successfully.
67 @retval other Some error occurs when executing this entry point.
73 IN EFI_HANDLE ImageHandle
,
74 IN EFI_SYSTEM_TABLE
*SystemTable
82 // gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Calling\r\n");
84 // Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
86 // gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Done\r\n");
87 // gBS->Stall (1000000);
89 if (PcdGet8(PcdShellSupportLevel
) > 3) {
90 return (EFI_UNSUPPORTED
);
96 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
97 ASSERT_EFI_ERROR(Status
);
100 // Populate the global structure from PCDs
102 ShellInfoObject
.ImageDevPath
= NULL
;
103 ShellInfoObject
.FileDevPath
= NULL
;
104 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
105 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
106 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
109 // verify we dont allow for spec violation
111 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
114 // Initialize the LIST ENTRY objects...
116 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
117 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
118 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
121 // Check PCDs for optional features that are not implemented yet.
123 if ( PcdGetBool(PcdShellSupportOldProtocols
)
124 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
125 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
127 return (EFI_UNSUPPORTED
);
131 // turn off the watchdog timer
133 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
136 // install our console logger. This will keep a log of the output for back-browsing
138 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
139 ASSERT_EFI_ERROR(Status
);
142 // Enable the cursor to be visible
144 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
147 // If supporting EFI 1.1 we need to install HII protocol
148 // only do this if PcdShellRequireHiiPlatform == FALSE
150 // remove EFI_UNSUPPORTED check above when complete.
151 ///@todo add support for Framework HII
154 // install our (solitary) HII package
156 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
157 if (ShellInfoObject
.HiiHandle
== NULL
) {
158 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
159 ///@todo Add our package into Framework HII
161 if (ShellInfoObject
.HiiHandle
== NULL
) {
162 return (EFI_NOT_STARTED
);
167 // create and install the EfiShellParametersProtocol
169 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
170 ASSERT_EFI_ERROR(Status
);
171 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
174 // create and install the EfiShellProtocol
176 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
177 ASSERT_EFI_ERROR(Status
);
178 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
181 // Now initialize the shell library (it requires Shell Parameters protocol)
183 Status
= ShellInitialize();
184 ASSERT_EFI_ERROR(Status
);
186 Status
= CommandInit();
187 ASSERT_EFI_ERROR(Status
);
190 // Check the command line
192 Status
= ProcessCommandLine();
195 // If shell support level is >= 1 create the mappings and paths
197 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
198 Status
= ShellCommandCreateInitialMappingsAndPaths();
202 // save the device path for the loaded image and the device path for the filepath (under loaded image)
203 // These are where to look for the startup.nsh file
205 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
206 ASSERT_EFI_ERROR(Status
);
209 // Display the version
211 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
214 gST
->ConOut
->Mode
->CursorRow
,
216 STRING_TOKEN (STR_VER_OUTPUT_MAIN
),
217 ShellInfoObject
.HiiHandle
,
218 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
219 gEfiShellProtocol
->MajorVersion
,
220 gEfiShellProtocol
->MinorVersion
,
221 (gST
->Hdr
.Revision
&0xffff0000)>>16,
222 (gST
->Hdr
.Revision
&0x0000ffff),
224 gST
->FirmwareRevision
229 // Display the mapping
231 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
232 Status
= RunCommand(L
"map");
233 ASSERT_EFI_ERROR(Status
);
237 // init all the built in alias'
239 Status
= SetBuiltInAlias();
240 ASSERT_EFI_ERROR(Status
);
243 // Initialize environment variables
245 if (ShellCommandGetProfileList() != NULL
) {
246 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
247 ASSERT_EFI_ERROR(Status
);
251 TempString
= AllocateZeroPool(Size
);
253 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
254 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
255 ASSERT_EFI_ERROR(Status
);
257 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
258 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
259 ASSERT_EFI_ERROR(Status
);
261 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
262 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
263 ASSERT_EFI_ERROR(Status
);
265 FreePool(TempString
);
267 if (!EFI_ERROR(Status
)) {
268 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
270 // Set up the event for CTRL-C monitoring...
273 ///@todo add support for using SimpleInputEx here
274 // if SimpleInputEx is not available display a warning.
277 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
279 // process the startup script or launch the called app.
281 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
284 if ((PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
286 // begin the UI waiting loop
290 // clean out all the memory allocated for CONST <something> * return values
291 // between each shell prompt presentation
293 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
294 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
298 // Reset page break back to default.
300 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
301 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
302 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
307 Status
= DoShellPrompt();
308 } while (!ShellCommandGetExit());
312 // uninstall protocols / free memory / etc...
314 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
315 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
316 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
319 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
320 ShellInfoObject
.NewEfiShellProtocol
->SetEnv(L
"cwd", L
"", TRUE
);
323 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
324 FreePool(ShellInfoObject
.ImageDevPath
);
325 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
327 if (ShellInfoObject
.FileDevPath
!= NULL
) {
328 FreePool(ShellInfoObject
.FileDevPath
);
329 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
331 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
332 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
333 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
335 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
336 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
337 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
340 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
341 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
344 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
348 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
349 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
350 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
353 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
354 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
355 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
358 if (ShellInfoObject
.HiiHandle
!= NULL
) {
359 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
360 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
363 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
364 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
367 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
368 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
369 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
370 FreePool(ShellInfoObject
.ConsoleInfo
);
371 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
378 Sets all the alias' that were registered with the ShellCommandLib library.
380 @retval EFI_SUCCESS all init commands were run sucessfully.
388 CONST ALIAS_LIST
*List
;
392 // Get all the commands we want to run
394 List
= ShellCommandGetInitAliasList();
397 // for each command in the List
399 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
400 ; !IsNull (&List
->Link
, &Node
->Link
)
401 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
404 // install the alias'
406 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
407 ASSERT_EFI_ERROR(Status
);
409 return (EFI_SUCCESS
);
413 Internal function to determine if 2 command names are really the same.
415 @param[in] Command1 The pointer to the first command name.
416 @param[in] Command2 The pointer to the second command name.
418 @retval TRUE The 2 command names are the same.
419 @retval FALSE The 2 command names are not the same.
424 IN CONST CHAR16
*Command1
,
425 IN CONST CHAR16
*Command2
428 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
435 Internal function to determine if a command is a script only command.
437 @param[in] CommandName The pointer to the command name.
439 @retval TRUE The command is a script only command.
440 @retval FALSE The command is not a script only command.
445 IN CONST CHAR16
*CommandName
448 if (IsCommand(CommandName
, L
"for")
449 ||IsCommand(CommandName
, L
"endfor")
450 ||IsCommand(CommandName
, L
"if")
451 ||IsCommand(CommandName
, L
"else")
452 ||IsCommand(CommandName
, L
"endif")
453 ||IsCommand(CommandName
, L
"goto")) {
462 This function will populate the 2 device path protocol parameters based on the
463 global gImageHandle. The DevPath will point to the device path for the handle that has
464 loaded image protocol installed on it. The FilePath will point to the device path
465 for the file that was loaded.
467 @param[in,out] DevPath On a sucessful return the device path to the loaded image.
468 @param[in,out] FilePath On a sucessful return the device path to the file.
470 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
471 @retval other A error from gBS->HandleProtocol.
477 GetDevicePathsForImageAndFile (
478 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
479 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
483 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
484 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
486 ASSERT(DevPath
!= NULL
);
487 ASSERT(FilePath
!= NULL
);
489 Status
= gBS
->OpenProtocol (
491 &gEfiLoadedImageProtocolGuid
,
492 (VOID
**)&LoadedImage
,
495 EFI_OPEN_PROTOCOL_GET_PROTOCOL
497 if (!EFI_ERROR (Status
)) {
498 Status
= gBS
->OpenProtocol (
499 LoadedImage
->DeviceHandle
,
500 &gEfiDevicePathProtocolGuid
,
501 (VOID
**)&ImageDevicePath
,
504 EFI_OPEN_PROTOCOL_GET_PROTOCOL
506 if (!EFI_ERROR (Status
)) {
507 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
508 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
512 &gEfiLoadedImageProtocolGuid
,
519 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
520 {L
"-nostartup", TypeFlag
},
521 {L
"-startup", TypeFlag
},
522 {L
"-noconsoleout", TypeFlag
},
523 {L
"-noconsolein", TypeFlag
},
524 {L
"-nointerrupt", TypeFlag
},
525 {L
"-nomap", TypeFlag
},
526 {L
"-noversion", TypeFlag
},
527 {L
"-startup", TypeFlag
},
528 {L
"-delay", TypeValue
},
532 Process all Uefi Shell 2.0 command line options.
534 see Uefi Shell 2.0 section 3.2 for full details.
536 the command line must resemble the following:
538 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
540 ShellOpt-options Options which control the initialization behavior of the shell.
541 These options are read from the EFI global variable "ShellOpt"
542 and are processed before options or file-name.
544 options Options which control the initialization behavior of the shell.
546 file-name The name of a UEFI shell application or script to be executed
547 after initialization is complete. By default, if file-name is
548 specified, then -nostartup is implied. Scripts are not supported
551 file-name-options The command-line options that are passed to file-name when it
554 This will initialize the ShellInfoObject.ShellInitSettings global variable.
556 @retval EFI_SUCCESS The variable is initialized.
567 CONST CHAR16
*TempConst
;
570 CHAR16
*ProblemParam
;
575 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
579 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
580 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
581 ShellInfoObject
.ShellInitSettings
.FileName
= AllocatePool(StrSize(TempConst
));
582 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
583 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
584 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
585 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
588 // We found the file... add the rest of the params...
590 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
591 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
592 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
596 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
598 gEfiShellParametersProtocol
->Argv
[LoopVar
],
604 ShellCommandLineFreeVarList(Package
);
606 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
607 if (EFI_ERROR(Status
)) {
608 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
609 FreePool(ProblemParam
);
610 ShellCommandLineFreeVarList(Package
);
611 return (EFI_INVALID_PARAMETER
);
615 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
616 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
617 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
618 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
619 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
620 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
621 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
622 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
624 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
625 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
626 if (TempConst
!= NULL
) {
627 ShellInfoObject
.ShellInitSettings
.Delay
= StrDecimalToUintn (TempConst
);
629 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
632 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
635 ShellCommandLineFreeVarList(Package
);
641 Handles all interaction with the default startup script.
643 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
645 @param ImagePath the path to the image for shell. first place to look for the startup script
646 @param FilePath the path to the file for shell. second place to look for the startup script.
648 @retval EFI_SUCCESS the variable is initialized.
653 EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
654 EFI_DEVICE_PATH_PROTOCOL
*FilePath
660 SHELL_FILE_HANDLE FileHandle
;
661 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
662 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
663 CHAR16
*FileStringPath
;
666 Key
.UnicodeChar
= CHAR_NULL
;
670 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
672 // launch something else instead
674 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
675 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
676 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
678 FileStringPath
= AllocateZeroPool(NewSize
);
679 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
680 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
681 StrCat (FileStringPath
, L
" ");
682 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
684 Status
= RunCommand(FileStringPath
);
685 FreePool(FileStringPath
);
691 // for shell level 0 we do no scripts
692 // Without the Startup bit overriding we allow for nostartup to prevent scripts
694 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
695 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
697 return (EFI_SUCCESS
);
701 // print out our warning and see if they press a key
703 for ( Status
= EFI_UNSUPPORTED
, Delay
= (ShellInfoObject
.ShellInitSettings
.Delay
* 10)
704 ; Delay
> 0 && EFI_ERROR(Status
)
707 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
/10);
709 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
711 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
716 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
717 return (EFI_SUCCESS
);
720 NamePath
= FileDevicePath (NULL
, L
"startup.nsh");
722 // Try the first location
724 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
725 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
726 if (EFI_ERROR(Status
)) {
728 // Try the second location
731 NewPath
= AppendDevicePathNode (FilePath
, NamePath
);
732 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
736 // If we got a file, run it
738 if (!EFI_ERROR(Status
)) {
739 Status
= RunScriptFileHandle (FileHandle
, L
"startup.nsh");
740 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
743 // we return success since we dont need to have a startup script
745 Status
= EFI_SUCCESS
;
746 ASSERT(FileHandle
== NULL
);
756 Function to perform the shell prompt looping. It will do a single prompt,
757 dispatch the result, and then return. It is expected that the caller will
758 call this function in a loop many times.
761 @retval RETURN_ABORTED
772 CONST CHAR16
*CurDir
;
779 // Get screen setting to decide size of the command line buffer
781 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
782 BufferSize
= Column
* Row
* sizeof (CHAR16
);
783 CmdLine
= AllocateZeroPool (BufferSize
);
784 if (CmdLine
== NULL
) {
785 return EFI_OUT_OF_RESOURCES
;
788 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
793 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
795 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
796 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
798 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
802 // Read a line from the console
804 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
807 // Null terminate the string and parse it
809 if (!EFI_ERROR (Status
)) {
810 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
811 Status
= RunCommand(CmdLine
);
815 // Done with this command
822 Add a buffer to the Buffer To Free List for safely returning buffers to other
823 places without risking letting them modify internal shell information.
825 @param Buffer Something to pass to FreePool when the shell is exiting.
833 BUFFER_LIST
*BufferListEntry
;
835 if (Buffer
== NULL
) {
839 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
840 ASSERT(BufferListEntry
!= NULL
);
841 BufferListEntry
->Buffer
= Buffer
;
842 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
847 Add a buffer to the Line History List
849 @param Buffer The line buffer to add.
853 AddLineToCommandHistory(
854 IN CONST CHAR16
*Buffer
859 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
860 ASSERT(Node
!= NULL
);
861 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
862 ASSERT(Node
->Buffer
!= NULL
);
863 StrCpy(Node
->Buffer
, Buffer
);
865 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
869 Checks if a string is an alias for another command. If yes, then it replaces the alias name
870 with the correct command name.
872 @param[in,out] CommandString Upon entry the potential alias. Upon return the
873 command name if it was an alias. If it was not
874 an alias it will be unchanged. This function may
875 change the buffer to fit the command name.
877 @retval EFI_SUCCESS The name was changed.
878 @retval EFI_SUCCESS The name was not an alias.
879 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
884 IN OUT CHAR16
**CommandString
887 CONST CHAR16
*NewString
;
889 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
890 if (NewString
== NULL
) {
891 return (EFI_SUCCESS
);
893 FreePool(*CommandString
);
894 *CommandString
= AllocatePool(StrSize(NewString
));
895 if (*CommandString
== NULL
) {
896 return (EFI_OUT_OF_RESOURCES
);
898 StrCpy(*CommandString
, NewString
);
899 return (EFI_SUCCESS
);
903 Function allocates a new command line and replaces all instances of environment
904 variable names that are correctly preset to their values.
906 If the return value is not NULL the memory must be caller freed.
908 @param[in] OriginalCommandLine The original command line
910 @retval NULL An error ocurred.
911 @return The new command line with no environment variables present.
915 ShellConvertVariables (
916 IN CONST CHAR16
*OriginalCommandLine
919 CONST CHAR16
*MasterEnvList
;
921 CHAR16
*NewCommandLine1
;
922 CHAR16
*NewCommandLine2
;
926 SCRIPT_FILE
*CurrentScriptFile
;
927 ALIAS_LIST
*AliasListNode
;
929 ASSERT(OriginalCommandLine
!= NULL
);
932 NewSize
= StrSize(OriginalCommandLine
);
933 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
936 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
939 // calculate the size required for the post-conversion string...
941 if (CurrentScriptFile
!= NULL
) {
942 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
943 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
944 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
946 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
948 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
951 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
953 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
954 NewSize
+= StrSize(AliasListNode
->CommandString
);
960 for (MasterEnvList
= EfiShellGetEnv(NULL
)
961 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
962 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
964 if (StrSize(MasterEnvList
) > ItemSize
) {
965 ItemSize
= StrSize(MasterEnvList
);
967 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
969 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
972 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
974 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
975 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
976 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
982 // Quick out if none were found...
984 if (NewSize
== StrSize(OriginalCommandLine
)) {
985 ASSERT(Temp
== NULL
);
986 Temp
= StrnCatGrow(&Temp
, NULL
, OriginalCommandLine
, 0);
991 // now do the replacements...
993 NewCommandLine1
= AllocateZeroPool(NewSize
);
994 NewCommandLine2
= AllocateZeroPool(NewSize
);
995 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
996 StrCpy(NewCommandLine1
, OriginalCommandLine
);
997 for (MasterEnvList
= EfiShellGetEnv(NULL
)
998 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
999 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1001 StrCpy(ItemTemp
, L
"%");
1002 StrCat(ItemTemp
, MasterEnvList
);
1003 StrCat(ItemTemp
, L
"%");
1004 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1005 StrCpy(NewCommandLine1
, NewCommandLine2
);
1007 if (CurrentScriptFile
!= NULL
) {
1008 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1009 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1010 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1012 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1013 StrCpy(NewCommandLine1
, NewCommandLine2
);
1017 FreePool(NewCommandLine2
);
1020 return (NewCommandLine1
);
1024 Internal function to run a command line with pipe usage.
1026 @param[in] CmdLine The pointer to the command line.
1027 @param[in] StdIn The pointer to the Standard input.
1028 @param[in] StdOut The pointer to the Standard output.
1030 @retval EFI_SUCCESS The split command is executed successfully.
1031 @retval other Some error occurs when executing the split command.
1036 IN CONST CHAR16
*CmdLine
,
1037 IN SHELL_FILE_HANDLE
*StdIn
,
1038 IN SHELL_FILE_HANDLE
*StdOut
1042 CHAR16
*NextCommandLine
;
1043 CHAR16
*OurCommandLine
;
1047 SHELL_FILE_HANDLE
*TempFileHandle
;
1050 ASSERT(StdOut
== NULL
);
1052 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1054 Status
= EFI_SUCCESS
;
1055 NextCommandLine
= NULL
;
1056 OurCommandLine
= NULL
;
1060 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1061 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1062 if (NextCommandLine
[0] != CHAR_NULL
&&
1063 NextCommandLine
[0] == L
'a' &&
1064 NextCommandLine
[1] == L
' '
1066 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1074 // make a SPLIT_LIST item and add to list
1076 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1077 ASSERT(Split
!= NULL
);
1078 Split
->SplitStdIn
= StdIn
;
1079 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1080 ASSERT(Split
->SplitStdOut
!= NULL
);
1081 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1083 ASSERT(StrStr(OurCommandLine
, L
"|") == NULL
);
1084 Status
= RunCommand(OurCommandLine
);
1087 // move the output from the first to the in to the second.
1089 TempFileHandle
= Split
->SplitStdOut
;
1090 if (Split
->SplitStdIn
== StdIn
) {
1091 Split
->SplitStdOut
= NULL
;
1093 Split
->SplitStdOut
= Split
->SplitStdIn
;
1095 Split
->SplitStdIn
= TempFileHandle
;
1096 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1098 if (!EFI_ERROR(Status
)) {
1099 Status
= RunCommand(NextCommandLine
);
1103 // remove the top level from the ScriptList
1105 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1106 RemoveEntryList(&Split
->Link
);
1109 // Note that the original StdIn is now the StdOut...
1111 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1112 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1114 if (Split
->SplitStdIn
!= NULL
) {
1115 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1119 FreePool(NextCommandLine
);
1120 FreePool(OurCommandLine
);
1126 Function will process and run a command line.
1128 This will determine if the command line represents an internal shell
1129 command or dispatch an external application.
1131 @param[in] CmdLine The command line to parse.
1133 @retval EFI_SUCCESS The command was completed.
1134 @retval EFI_ABORTED The command's operation was aborted.
1139 IN CONST CHAR16
*CmdLine
1143 CHAR16
*CommandName
;
1144 SHELL_STATUS ShellStatus
;
1148 CHAR16 LeString
[11];
1149 CHAR16
*PostAliasCmdLine
;
1150 UINTN PostAliasSize
;
1151 CHAR16
*PostVariableCmdLine
;
1152 CHAR16
*CommandWithPath
;
1153 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
1154 CONST CHAR16
*TempLocation
;
1155 CONST CHAR16
*TempLocation2
;
1156 SHELL_FILE_HANDLE OriginalStdIn
;
1157 SHELL_FILE_HANDLE OriginalStdOut
;
1158 SHELL_FILE_HANDLE OriginalStdErr
;
1159 CHAR16
*TempLocation3
;
1162 CHAR16
*CleanOriginal
;
1165 ASSERT(CmdLine
!= NULL
);
1166 if (StrLen(CmdLine
) == 0) {
1167 return (EFI_SUCCESS
);
1171 PostVariableCmdLine
= NULL
;
1172 PostAliasCmdLine
= NULL
;
1173 CommandWithPath
= NULL
;
1175 Status
= EFI_SUCCESS
;
1176 CleanOriginal
= NULL
;
1179 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
1180 while (CleanOriginal
[StrLen(CleanOriginal
)-1] == L
' ') {
1181 CleanOriginal
[StrLen(CleanOriginal
)-1] = CHAR_NULL
;
1183 while (CleanOriginal
[0] == L
' ') {
1184 CopyMem(CleanOriginal
, CleanOriginal
+1, StrSize(CleanOriginal
) - sizeof(CleanOriginal
[0]));
1188 if (StrStr(CleanOriginal
, L
" ") == NULL
){
1189 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, 0);
1191 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, StrStr(CleanOriginal
, L
" ") - CleanOriginal
);
1194 ASSERT(PostAliasCmdLine
== NULL
);
1195 if (!ShellCommandIsCommandOnList(CommandName
)) {
1197 // Convert via alias
1199 Status
= ShellConvertAlias(&CommandName
);
1201 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, CommandName
, 0);
1202 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, StrStr(CleanOriginal
, L
" "), 0);
1203 ASSERT_EFI_ERROR(Status
);
1205 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, NULL
, CleanOriginal
, 0);
1208 if (CleanOriginal
!= NULL
) {
1209 FreePool(CleanOriginal
);
1210 CleanOriginal
= NULL
;
1213 if (CommandName
!= NULL
) {
1214 FreePool(CommandName
);
1218 PostVariableCmdLine
= ShellConvertVariables(PostAliasCmdLine
);
1221 // we can now free the modified by alias command line
1223 if (PostAliasCmdLine
!= NULL
) {
1224 FreePool(PostAliasCmdLine
);
1225 PostAliasCmdLine
= NULL
;
1228 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1229 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1231 while (PostVariableCmdLine
[0] == L
' ') {
1232 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1236 // We dont do normal processing with a split command line (output from one command input to another)
1238 TempLocation3
= NULL
;
1239 if (StrStr(PostVariableCmdLine
, L
"|") != NULL
) {
1240 for (TempLocation3
= PostVariableCmdLine
; TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
; TempLocation3
++) {
1241 if (*TempLocation3
== L
'^' && *(TempLocation3
+1) == L
'|') {
1243 } else if (*TempLocation3
== L
'|') {
1248 if (TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
) {
1250 // are we in an existing split???
1252 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1253 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1256 if (Split
== NULL
) {
1257 Status
= RunSplitCommand(PostVariableCmdLine
, NULL
, NULL
);
1259 Status
= RunSplitCommand(PostVariableCmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1264 // If this is a mapped drive change handle that...
1266 if (PostVariableCmdLine
[(StrLen(PostVariableCmdLine
)-1)] == L
':' && StrStr(PostVariableCmdLine
, L
" ") == NULL
) {
1267 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, PostVariableCmdLine
);
1268 if (EFI_ERROR(Status
)) {
1269 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1271 FreePool(PostVariableCmdLine
);
1275 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1276 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1280 Status
= UpdateStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
);
1281 if (EFI_ERROR(Status
)) {
1282 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_REDIR
), ShellInfoObject
.HiiHandle
);
1285 // remove the < and/or > from the command line now
1287 for (TempLocation3
= PostVariableCmdLine
; TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
; TempLocation3
++) {
1288 if (*TempLocation3
== L
'^') {
1289 if (*(TempLocation3
+1) == L
'<' || *(TempLocation3
+1) == L
'>') {
1290 CopyMem(TempLocation3
, TempLocation3
+1, StrSize(TempLocation3
) - sizeof(TempLocation3
[0]));
1292 } else if (*TempLocation3
== L
'>') {
1293 *TempLocation3
= CHAR_NULL
;
1294 } else if ((*TempLocation3
== L
'1' || *TempLocation3
== L
'2')&&(*(TempLocation3
+1) == L
'>')) {
1295 *TempLocation3
= CHAR_NULL
;
1299 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1300 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1302 while (PostVariableCmdLine
[0] == L
' ') {
1303 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1307 // get the argc and argv updated for internal commands
1309 Status
= UpdateArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &Argv
, &Argc
);
1310 ASSERT_EFI_ERROR(Status
);
1312 for (Count
= 0 ; Count
< ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count
++) {
1313 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
], L
"-?") == ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]
1314 || (ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][0] == L
'?' && ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][1] == CHAR_NULL
)
1317 // We need to redo the arguments since a parameter was -?
1318 // move them all down 1 to the end, then up one then replace the first with help
1320 FreePool(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]);
1321 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
] = NULL
;
1322 for (Count2
= Count
; (Count2
+ 1) < ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count2
++) {
1323 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
+1];
1325 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = NULL
;
1326 for (Count2
= ShellInfoObject
.NewShellParametersProtocol
->Argc
-1 ; Count2
> 0 ; Count2
--) {
1327 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
-1];
1329 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = NULL
;
1330 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = StrnCatGrow(&ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], NULL
, L
"help", 0);
1338 if (ShellCommandIsCommandOnList(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1340 // Run the command (which was converted if it was an alias)
1342 if (!EFI_ERROR(Status
)) {
1343 Status
= ShellCommandRunCommandHandler(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], &ShellStatus
, &LastError
);
1344 ASSERT_EFI_ERROR(Status
);
1345 UnicodeSPrint(LeString
, sizeof(LeString
)*sizeof(LeString
[0]), L
"0x%08x", ShellStatus
);
1346 DEBUG_CODE(InternalEfiShellSetEnv(L
"DebugLasterror", LeString
, TRUE
););
1348 InternalEfiShellSetEnv(L
"Lasterror", LeString
, TRUE
);
1351 // Pass thru the exitcode from the app.
1353 if (ShellCommandGetExit()) {
1354 Status
= ShellStatus
;
1355 } else if (ShellStatus
!= 0 && IsScriptOnlyCommand(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1356 Status
= EFI_ABORTED
;
1361 // run an external file (or script)
1363 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], L
":") != NULL
) {
1364 ASSERT (CommandWithPath
== NULL
);
1365 if (ShellIsFile(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]) == EFI_SUCCESS
) {
1366 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1369 if (CommandWithPath
== NULL
) {
1370 CommandWithPath
= ShellFindFilePathEx(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], mExecutableExtensions
);
1372 if (CommandWithPath
== NULL
|| ShellIsDirectory(CommandWithPath
) == EFI_SUCCESS
) {
1373 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]);
1376 // Check if it's a NSH (script) file.
1378 TempLocation
= CommandWithPath
+StrLen(CommandWithPath
)-4;
1379 TempLocation2
= mScriptExtension
;
1380 if ((StrLen(CommandWithPath
) > 4) && (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0)) {
1381 Status
= RunScriptFile (CommandWithPath
);
1383 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
1384 ASSERT(DevPath
!= NULL
);
1385 Status
= InternalShellExecuteDevicePath(
1388 PostVariableCmdLine
,
1395 CommandName
= StrnCatGrow(&CommandName
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1397 RestoreArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, &Argv
, &Argc
);
1399 RestoreStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
);
1401 if (CommandName
!= NULL
) {
1402 if (ShellCommandGetCurrentScriptFile() != NULL
&& !IsScriptOnlyCommand(CommandName
)) {
1404 // if this is NOT a scipt only command return success so the script won't quit.
1405 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1407 Status
= EFI_SUCCESS
;
1412 SHELL_FREE_NON_NULL(CommandName
);
1413 SHELL_FREE_NON_NULL(CommandWithPath
);
1414 SHELL_FREE_NON_NULL(PostVariableCmdLine
);
1415 SHELL_FREE_NON_NULL(DevPath
);
1420 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
1422 Function determins if the CommandName COULD be a valid command. It does not determine whether
1423 this is a valid command. It only checks for invalid characters.
1425 @param[in] CommandName The name to check
1427 @retval TRUE CommandName could be a command name
1428 @retval FALSE CommandName could not be a valid command name
1433 IN CONST CHAR16
*CommandName
1437 if (CommandName
== NULL
) {
1442 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
1445 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
1453 Function to process a NSH script file via SHELL_FILE_HANDLE.
1455 @param[in] Handle The handle to the already opened file.
1456 @param[in] Name The name of the script file.
1458 @retval EFI_SUCCESS the script completed sucessfully
1462 RunScriptFileHandle (
1463 IN SHELL_FILE_HANDLE Handle
,
1464 IN CONST CHAR16
*Name
1468 SCRIPT_FILE
*NewScriptFile
;
1470 CHAR16
*CommandLine
;
1471 CHAR16
*CommandLine2
;
1472 CHAR16
*CommandLine3
;
1473 SCRIPT_COMMAND_LIST
*LastCommand
;
1475 BOOLEAN PreScriptEchoState
;
1476 CONST CHAR16
*CurDir
;
1479 ASSERT(!ShellCommandGetScriptExit());
1481 PreScriptEchoState
= ShellCommandGetEchoState();
1483 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
1484 ASSERT(NewScriptFile
!= NULL
);
1489 ASSERT(NewScriptFile
->ScriptName
== NULL
);
1490 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
1493 // Save the parameters (used to replace %0 to %9 later on)
1495 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
1496 if (NewScriptFile
->Argc
!= 0) {
1497 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
1498 ASSERT(NewScriptFile
->Argv
!= NULL
);
1499 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
1500 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
1501 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
1504 NewScriptFile
->Argv
= NULL
;
1507 InitializeListHead(&NewScriptFile
->CommandList
);
1508 InitializeListHead(&NewScriptFile
->SubstList
);
1511 // Now build the list of all script commands.
1514 while(!ShellFileHandleEof(Handle
)) {
1515 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
1517 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0) {
1520 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
1521 ASSERT(NewScriptFile
->CurrentCommand
!= NULL
);
1523 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
1524 NewScriptFile
->CurrentCommand
->Data
= NULL
;
1525 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
1527 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1531 // Add this as the topmost script file
1533 ShellCommandSetNewScript (NewScriptFile
);
1536 // Now enumerate through the commands and run each one.
1538 CommandLine
= AllocatePool(PcdGet16(PcdShellPrintBufferSize
));
1539 CommandLine2
= AllocatePool(PcdGet16(PcdShellPrintBufferSize
));
1541 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
1542 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
1543 ; // conditional increment in the body of the loop
1545 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
1548 // NULL out comments
1550 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
1551 if (*CommandLine3
== L
'^') {
1552 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
1553 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
1555 } else if (*CommandLine3
== L
'#') {
1556 *CommandLine3
= CHAR_NULL
;
1560 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
1562 // Due to variability in starting the find and replace action we need to have both buffers the same.
1564 StrCpy(CommandLine
, CommandLine2
);
1567 // Remove the %0 to %9 from the command line (if we have some arguments)
1569 if (NewScriptFile
->Argv
!= NULL
) {
1570 switch (NewScriptFile
->Argc
) {
1572 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
1573 ASSERT_EFI_ERROR(Status
);
1575 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
1576 ASSERT_EFI_ERROR(Status
);
1578 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
1579 ASSERT_EFI_ERROR(Status
);
1581 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
1582 ASSERT_EFI_ERROR(Status
);
1584 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
1585 ASSERT_EFI_ERROR(Status
);
1587 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
1588 ASSERT_EFI_ERROR(Status
);
1590 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
1591 ASSERT_EFI_ERROR(Status
);
1593 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
1594 ASSERT_EFI_ERROR(Status
);
1596 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
1597 ASSERT_EFI_ERROR(Status
);
1599 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
1600 ASSERT_EFI_ERROR(Status
);
1606 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
1607 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
1608 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
1609 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
1610 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
1611 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
1612 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
1613 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
1614 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
1616 StrCpy(CommandLine2
, CommandLine
);
1618 LastCommand
= NewScriptFile
->CurrentCommand
;
1620 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
1622 if (CommandLine3
[0] == L
':' ) {
1624 // This line is a goto target / label
1627 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
1628 if (ShellCommandGetEchoState()) {
1629 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1630 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1631 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1633 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1635 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
1637 Status
= RunCommand(CommandLine3
);
1640 if (ShellCommandGetScriptExit()) {
1641 ShellCommandRegisterExit(FALSE
);
1642 Status
= EFI_SUCCESS
;
1645 if (EFI_ERROR(Status
)) {
1648 if (ShellCommandGetExit()) {
1653 // If that commend did not update the CurrentCommand then we need to advance it...
1655 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
1656 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1657 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
1658 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
1662 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1663 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
1664 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
1669 ShellCommandSetEchoState(PreScriptEchoState
);
1671 FreePool(CommandLine
);
1672 FreePool(CommandLine2
);
1673 ShellCommandSetNewScript (NULL
);
1674 return (EFI_SUCCESS
);
1678 Function to process a NSH script file.
1680 @param[in] ScriptPath Pointer to the script file name (including file system path).
1682 @retval EFI_SUCCESS the script completed sucessfully
1687 IN CONST CHAR16
*ScriptPath
1691 SHELL_FILE_HANDLE FileHandle
;
1693 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
1694 return (EFI_INVALID_PARAMETER
);
1697 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1698 if (EFI_ERROR(Status
)) {
1702 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
1704 ShellCloseFile(&FileHandle
);