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";
59 STATIC CONST CHAR16 mStartupScript
[] = L
"startup.nsh";
62 The entry point for the application.
64 @param[in] ImageHandle The firmware allocated handle for the EFI image.
65 @param[in] SystemTable A pointer to the EFI System Table.
67 @retval EFI_SUCCESS The entry point is executed successfully.
68 @retval other Some error occurs when executing this entry point.
74 IN EFI_HANDLE ImageHandle
,
75 IN EFI_SYSTEM_TABLE
*SystemTable
83 // gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Calling\r\n");
85 // Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
87 // gST->ConOut->OutputString(gST->ConOut, L"ReadKeyStroke Done\r\n");
88 // gBS->Stall (1000000);
90 if (PcdGet8(PcdShellSupportLevel
) > 3) {
91 return (EFI_UNSUPPORTED
);
97 Status
= gST
->ConOut
->ClearScreen(gST
->ConOut
);
98 if (EFI_ERROR(Status
)) {
103 // Populate the global structure from PCDs
105 ShellInfoObject
.ImageDevPath
= NULL
;
106 ShellInfoObject
.FileDevPath
= NULL
;
107 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
108 ShellInfoObject
.ViewingSettings
.InsertMode
= PcdGetBool(PcdShellInsertModeDefault
);
109 ShellInfoObject
.LogScreenCount
= PcdGet8 (PcdShellScreenLogCount
);
112 // verify we dont allow for spec violation
114 ASSERT(ShellInfoObject
.LogScreenCount
>= 3);
117 // Initialize the LIST ENTRY objects...
119 InitializeListHead(&ShellInfoObject
.BufferToFreeList
.Link
);
120 InitializeListHead(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
);
121 InitializeListHead(&ShellInfoObject
.SplitList
.Link
);
124 // Check PCDs for optional features that are not implemented yet.
126 if ( PcdGetBool(PcdShellSupportOldProtocols
)
127 || !FeaturePcdGet(PcdShellRequireHiiPlatform
)
128 || FeaturePcdGet(PcdShellSupportFrameworkHii
)
130 return (EFI_UNSUPPORTED
);
134 // turn off the watchdog timer
136 gBS
->SetWatchdogTimer (0, 0, 0, NULL
);
139 // install our console logger. This will keep a log of the output for back-browsing
141 Status
= ConsoleLoggerInstall(ShellInfoObject
.LogScreenCount
, &ShellInfoObject
.ConsoleInfo
);
142 if (!EFI_ERROR(Status
)) {
144 // Enable the cursor to be visible
146 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
149 // If supporting EFI 1.1 we need to install HII protocol
150 // only do this if PcdShellRequireHiiPlatform == FALSE
152 // remove EFI_UNSUPPORTED check above when complete.
153 ///@todo add support for Framework HII
156 // install our (solitary) HII package
158 ShellInfoObject
.HiiHandle
= HiiAddPackages (&gEfiCallerIdGuid
, gImageHandle
, ShellStrings
, NULL
);
159 if (ShellInfoObject
.HiiHandle
== NULL
) {
160 if (PcdGetBool(PcdShellSupportFrameworkHii
)) {
161 ///@todo Add our package into Framework HII
163 if (ShellInfoObject
.HiiHandle
== NULL
) {
164 return (EFI_NOT_STARTED
);
169 // create and install the EfiShellParametersProtocol
171 Status
= CreatePopulateInstallShellParametersProtocol(&ShellInfoObject
.NewShellParametersProtocol
, &ShellInfoObject
.RootShellInstance
);
172 ASSERT_EFI_ERROR(Status
);
173 ASSERT(ShellInfoObject
.NewShellParametersProtocol
!= NULL
);
176 // create and install the EfiShellProtocol
178 Status
= CreatePopulateInstallShellProtocol(&ShellInfoObject
.NewEfiShellProtocol
);
179 ASSERT_EFI_ERROR(Status
);
180 ASSERT(ShellInfoObject
.NewEfiShellProtocol
!= NULL
);
183 // Now initialize the shell library (it requires Shell Parameters protocol)
185 Status
= ShellInitialize();
186 ASSERT_EFI_ERROR(Status
);
188 Status
= CommandInit();
189 ASSERT_EFI_ERROR(Status
);
192 // Check the command line
194 Status
= ProcessCommandLine();
197 // If shell support level is >= 1 create the mappings and paths
199 if (PcdGet8(PcdShellSupportLevel
) >= 1) {
200 Status
= ShellCommandCreateInitialMappingsAndPaths();
204 // save the device path for the loaded image and the device path for the filepath (under loaded image)
205 // These are where to look for the startup.nsh file
207 Status
= GetDevicePathsForImageAndFile(&ShellInfoObject
.ImageDevPath
, &ShellInfoObject
.FileDevPath
);
208 ASSERT_EFI_ERROR(Status
);
211 // Display the version
213 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
) {
216 gST
->ConOut
->Mode
->CursorRow
,
218 STRING_TOKEN (STR_VER_OUTPUT_MAIN
),
219 ShellInfoObject
.HiiHandle
,
220 SupportLevel
[PcdGet8(PcdShellSupportLevel
)],
221 gEfiShellProtocol
->MajorVersion
,
222 gEfiShellProtocol
->MinorVersion
,
223 (gST
->Hdr
.Revision
&0xffff0000)>>16,
224 (gST
->Hdr
.Revision
&0x0000ffff),
226 gST
->FirmwareRevision
231 // Display the mapping
233 if (PcdGet8(PcdShellSupportLevel
) >= 2 && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
) {
234 Status
= RunCommand(L
"map");
235 ASSERT_EFI_ERROR(Status
);
239 // init all the built in alias'
241 Status
= SetBuiltInAlias();
242 ASSERT_EFI_ERROR(Status
);
245 // Initialize environment variables
247 if (ShellCommandGetProfileList() != NULL
) {
248 Status
= InternalEfiShellSetEnv(L
"profiles", ShellCommandGetProfileList(), TRUE
);
249 ASSERT_EFI_ERROR(Status
);
253 TempString
= AllocateZeroPool(Size
);
255 UnicodeSPrint(TempString
, Size
, L
"%d", PcdGet8(PcdShellSupportLevel
));
256 Status
= InternalEfiShellSetEnv(L
"uefishellsupport", TempString
, TRUE
);
257 ASSERT_EFI_ERROR(Status
);
259 UnicodeSPrint(TempString
, Size
, L
"%d.%d", ShellInfoObject
.NewEfiShellProtocol
->MajorVersion
, ShellInfoObject
.NewEfiShellProtocol
->MinorVersion
);
260 Status
= InternalEfiShellSetEnv(L
"uefishellversion", TempString
, TRUE
);
261 ASSERT_EFI_ERROR(Status
);
263 UnicodeSPrint(TempString
, Size
, L
"%d.%d", (gST
->Hdr
.Revision
& 0xFFFF0000) >> 16, gST
->Hdr
.Revision
& 0x0000FFFF);
264 Status
= InternalEfiShellSetEnv(L
"uefiversion", TempString
, TRUE
);
265 ASSERT_EFI_ERROR(Status
);
267 FreePool(TempString
);
269 if (!EFI_ERROR(Status
)) {
270 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
) {
272 // Set up the event for CTRL-C monitoring...
275 ///@todo add support for using SimpleInputEx here
276 // if SimpleInputEx is not available display a warning.
279 if (!EFI_ERROR(Status
) && PcdGet8(PcdShellSupportLevel
) >= 1) {
281 // process the startup script or launch the called app.
283 Status
= DoStartupScript(ShellInfoObject
.ImageDevPath
, ShellInfoObject
.FileDevPath
);
286 if ((PcdGet8(PcdShellSupportLevel
) >= 3 || PcdGetBool(PcdShellForceConsole
)) && !EFI_ERROR(Status
) && !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
) {
288 // begin the UI waiting loop
292 // clean out all the memory allocated for CONST <something> * return values
293 // between each shell prompt presentation
295 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
296 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
300 // Reset page break back to default.
302 ShellInfoObject
.PageBreakEnabled
= PcdGetBool(PcdShellPageBreakDefault
);
303 ShellInfoObject
.ConsoleInfo
->Enabled
= TRUE
;
304 ShellInfoObject
.ConsoleInfo
->RowCounter
= 0;
309 Status
= DoShellPrompt();
310 } while (!ShellCommandGetExit());
316 // uninstall protocols / free memory / etc...
318 if (ShellInfoObject
.UserBreakTimer
!= NULL
) {
319 gBS
->CloseEvent(ShellInfoObject
.UserBreakTimer
);
320 DEBUG_CODE(ShellInfoObject
.UserBreakTimer
= NULL
;);
323 if (ShellInfoObject
.NewEfiShellProtocol
->IsRootShell()){
324 ShellInfoObject
.NewEfiShellProtocol
->SetEnv(L
"cwd", L
"", TRUE
);
327 if (ShellInfoObject
.ImageDevPath
!= NULL
) {
328 FreePool(ShellInfoObject
.ImageDevPath
);
329 DEBUG_CODE(ShellInfoObject
.ImageDevPath
= NULL
;);
331 if (ShellInfoObject
.FileDevPath
!= NULL
) {
332 FreePool(ShellInfoObject
.FileDevPath
);
333 DEBUG_CODE(ShellInfoObject
.FileDevPath
= NULL
;);
335 if (ShellInfoObject
.NewShellParametersProtocol
!= NULL
) {
336 CleanUpShellParametersProtocol(ShellInfoObject
.NewShellParametersProtocol
);
337 DEBUG_CODE(ShellInfoObject
.NewShellParametersProtocol
= NULL
;);
339 if (ShellInfoObject
.NewEfiShellProtocol
!= NULL
){
340 CleanUpShellProtocol(ShellInfoObject
.NewEfiShellProtocol
);
341 DEBUG_CODE(ShellInfoObject
.NewEfiShellProtocol
= NULL
;);
344 if (!IsListEmpty(&ShellInfoObject
.BufferToFreeList
.Link
)){
345 FreeBufferList(&ShellInfoObject
.BufferToFreeList
);
348 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)){
349 ASSERT(FALSE
); ///@todo finish this de-allocation.
352 if (ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
353 FreePool(ShellInfoObject
.ShellInitSettings
.FileName
);
354 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileName
= NULL
;);
357 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
358 FreePool(ShellInfoObject
.ShellInitSettings
.FileOptions
);
359 DEBUG_CODE(ShellInfoObject
.ShellInitSettings
.FileOptions
= NULL
;);
362 if (ShellInfoObject
.HiiHandle
!= NULL
) {
363 HiiRemovePackages(ShellInfoObject
.HiiHandle
);
364 DEBUG_CODE(ShellInfoObject
.HiiHandle
= NULL
;);
367 if (!IsListEmpty(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
)){
368 FreeBufferList(&ShellInfoObject
.ViewingSettings
.CommandHistory
);
371 ASSERT(ShellInfoObject
.ConsoleInfo
!= NULL
);
372 if (ShellInfoObject
.ConsoleInfo
!= NULL
) {
373 ConsoleLoggerUninstall(ShellInfoObject
.ConsoleInfo
);
374 FreePool(ShellInfoObject
.ConsoleInfo
);
375 DEBUG_CODE(ShellInfoObject
.ConsoleInfo
= NULL
;);
382 Sets all the alias' that were registered with the ShellCommandLib library.
384 @retval EFI_SUCCESS all init commands were run sucessfully.
392 CONST ALIAS_LIST
*List
;
396 // Get all the commands we want to run
398 List
= ShellCommandGetInitAliasList();
401 // for each command in the List
403 for ( Node
= (ALIAS_LIST
*)GetFirstNode(&List
->Link
)
404 ; !IsNull (&List
->Link
, &Node
->Link
)
405 ; Node
= (ALIAS_LIST
*)GetNextNode(&List
->Link
, &Node
->Link
)
408 // install the alias'
410 Status
= InternalSetAlias(Node
->CommandString
, Node
->Alias
, TRUE
);
411 ASSERT_EFI_ERROR(Status
);
413 return (EFI_SUCCESS
);
417 Internal function to determine if 2 command names are really the same.
419 @param[in] Command1 The pointer to the first command name.
420 @param[in] Command2 The pointer to the second command name.
422 @retval TRUE The 2 command names are the same.
423 @retval FALSE The 2 command names are not the same.
428 IN CONST CHAR16
*Command1
,
429 IN CONST CHAR16
*Command2
432 if (StringNoCaseCompare(&Command1
, &Command2
) == 0) {
439 Internal function to determine if a command is a script only command.
441 @param[in] CommandName The pointer to the command name.
443 @retval TRUE The command is a script only command.
444 @retval FALSE The command is not a script only command.
449 IN CONST CHAR16
*CommandName
452 if (IsCommand(CommandName
, L
"for")
453 ||IsCommand(CommandName
, L
"endfor")
454 ||IsCommand(CommandName
, L
"if")
455 ||IsCommand(CommandName
, L
"else")
456 ||IsCommand(CommandName
, L
"endif")
457 ||IsCommand(CommandName
, L
"goto")) {
466 This function will populate the 2 device path protocol parameters based on the
467 global gImageHandle. The DevPath will point to the device path for the handle that has
468 loaded image protocol installed on it. The FilePath will point to the device path
469 for the file that was loaded.
471 @param[in,out] DevPath On a sucessful return the device path to the loaded image.
472 @param[in,out] FilePath On a sucessful return the device path to the file.
474 @retval EFI_SUCCESS The 2 device paths were sucessfully returned.
475 @retval other A error from gBS->HandleProtocol.
481 GetDevicePathsForImageAndFile (
482 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevPath
,
483 IN OUT EFI_DEVICE_PATH_PROTOCOL
**FilePath
487 EFI_LOADED_IMAGE_PROTOCOL
*LoadedImage
;
488 EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
;
490 ASSERT(DevPath
!= NULL
);
491 ASSERT(FilePath
!= NULL
);
493 Status
= gBS
->OpenProtocol (
495 &gEfiLoadedImageProtocolGuid
,
496 (VOID
**)&LoadedImage
,
499 EFI_OPEN_PROTOCOL_GET_PROTOCOL
501 if (!EFI_ERROR (Status
)) {
502 Status
= gBS
->OpenProtocol (
503 LoadedImage
->DeviceHandle
,
504 &gEfiDevicePathProtocolGuid
,
505 (VOID
**)&ImageDevicePath
,
508 EFI_OPEN_PROTOCOL_GET_PROTOCOL
510 if (!EFI_ERROR (Status
)) {
511 *DevPath
= DuplicateDevicePath (ImageDevicePath
);
512 *FilePath
= DuplicateDevicePath (LoadedImage
->FilePath
);
516 &gEfiLoadedImageProtocolGuid
,
523 STATIC CONST SHELL_PARAM_ITEM mShellParamList
[] = {
524 {L
"-nostartup", TypeFlag
},
525 {L
"-startup", TypeFlag
},
526 {L
"-noconsoleout", TypeFlag
},
527 {L
"-noconsolein", TypeFlag
},
528 {L
"-nointerrupt", TypeFlag
},
529 {L
"-nomap", TypeFlag
},
530 {L
"-noversion", TypeFlag
},
531 {L
"-startup", TypeFlag
},
532 {L
"-delay", TypeValue
},
536 Process all Uefi Shell 2.0 command line options.
538 see Uefi Shell 2.0 section 3.2 for full details.
540 the command line must resemble the following:
542 shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
544 ShellOpt-options Options which control the initialization behavior of the shell.
545 These options are read from the EFI global variable "ShellOpt"
546 and are processed before options or file-name.
548 options Options which control the initialization behavior of the shell.
550 file-name The name of a UEFI shell application or script to be executed
551 after initialization is complete. By default, if file-name is
552 specified, then -nostartup is implied. Scripts are not supported
555 file-name-options The command-line options that are passed to file-name when it
558 This will initialize the ShellInfoObject.ShellInitSettings global variable.
560 @retval EFI_SUCCESS The variable is initialized.
571 CONST CHAR16
*TempConst
;
574 CHAR16
*ProblemParam
;
579 Status
= ShellCommandLineParse (mShellParamList
, &Package
, NULL
, FALSE
);
583 TempConst
= ShellCommandLineGetRawValue(Package
, Count
++);
584 if (TempConst
!= NULL
&& StrLen(TempConst
)) {
585 ShellInfoObject
.ShellInitSettings
.FileName
= AllocatePool(StrSize(TempConst
));
586 if (ShellInfoObject
.ShellInitSettings
.FileName
== NULL
) {
587 return (EFI_OUT_OF_RESOURCES
);
589 StrCpy(ShellInfoObject
.ShellInitSettings
.FileName
, TempConst
);
590 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= 1;
591 for (LoopVar
= 0 ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
592 if (StrCmp(gEfiShellParametersProtocol
->Argv
[LoopVar
], ShellInfoObject
.ShellInitSettings
.FileName
)==0) {
595 // We found the file... add the rest of the params...
597 for ( ; LoopVar
< gEfiShellParametersProtocol
->Argc
; LoopVar
++) {
598 ASSERT((ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
&& Size
== 0) || (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
));
599 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
603 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
604 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
605 return (EFI_OUT_OF_RESOURCES
);
607 StrnCatGrow(&ShellInfoObject
.ShellInitSettings
.FileOptions
,
609 gEfiShellParametersProtocol
->Argv
[LoopVar
],
611 if (ShellInfoObject
.ShellInitSettings
.FileOptions
== NULL
) {
612 SHELL_FREE_NON_NULL(ShellInfoObject
.ShellInitSettings
.FileName
);
613 return (EFI_OUT_OF_RESOURCES
);
619 ShellCommandLineFreeVarList(Package
);
621 Status
= ShellCommandLineParse (mShellParamList
, &Package
, &ProblemParam
, FALSE
);
622 if (EFI_ERROR(Status
)) {
623 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
), ShellInfoObject
.HiiHandle
, ProblemParam
);
624 FreePool(ProblemParam
);
625 ShellCommandLineFreeVarList(Package
);
626 return (EFI_INVALID_PARAMETER
);
630 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
= ShellCommandLineGetFlag(Package
, L
"-startup");
631 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
= ShellCommandLineGetFlag(Package
, L
"-nostartup");
632 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleOut
= ShellCommandLineGetFlag(Package
, L
"-noconsoleout");
633 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoConsoleIn
= ShellCommandLineGetFlag(Package
, L
"-noconsolein");
634 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoInterrupt
= ShellCommandLineGetFlag(Package
, L
"-nointerrupt");
635 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoMap
= ShellCommandLineGetFlag(Package
, L
"-nomap");
636 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoVersion
= ShellCommandLineGetFlag(Package
, L
"-noversion");
637 ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
= ShellCommandLineGetFlag(Package
, L
"-delay");
639 if (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Delay
) {
640 TempConst
= ShellCommandLineGetValue(Package
, L
"-delay");
641 if (TempConst
!= NULL
) {
642 ShellInfoObject
.ShellInitSettings
.Delay
= StrDecimalToUintn (TempConst
);
644 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
647 ShellInfoObject
.ShellInitSettings
.Delay
= 5;
650 ShellCommandLineFreeVarList(Package
);
656 Handles all interaction with the default startup script.
658 this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
660 @param ImagePath the path to the image for shell. first place to look for the startup script
661 @param FilePath the path to the file for shell. second place to look for the startup script.
663 @retval EFI_SUCCESS the variable is initialized.
668 EFI_DEVICE_PATH_PROTOCOL
*ImagePath
,
669 EFI_DEVICE_PATH_PROTOCOL
*FilePath
675 SHELL_FILE_HANDLE FileHandle
;
676 EFI_DEVICE_PATH_PROTOCOL
*NewPath
;
677 EFI_DEVICE_PATH_PROTOCOL
*NamePath
;
678 CHAR16
*FileStringPath
;
681 Key
.UnicodeChar
= CHAR_NULL
;
685 if (!ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
&& ShellInfoObject
.ShellInitSettings
.FileName
!= NULL
) {
687 // launch something else instead
689 NewSize
= StrSize(ShellInfoObject
.ShellInitSettings
.FileName
);
690 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
691 NewSize
+= StrSize(ShellInfoObject
.ShellInitSettings
.FileOptions
) + sizeof(CHAR16
);
693 FileStringPath
= AllocateZeroPool(NewSize
);
694 if (FileStringPath
== NULL
) {
695 return (EFI_OUT_OF_RESOURCES
);
697 StrCpy(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileName
);
698 if (ShellInfoObject
.ShellInitSettings
.FileOptions
!= NULL
) {
699 StrCat(FileStringPath
, L
" ");
700 StrCat(FileStringPath
, ShellInfoObject
.ShellInitSettings
.FileOptions
);
702 Status
= RunCommand(FileStringPath
);
703 FreePool(FileStringPath
);
709 // for shell level 0 we do no scripts
710 // Without the Startup bit overriding we allow for nostartup to prevent scripts
712 if ( (PcdGet8(PcdShellSupportLevel
) < 1)
713 || (ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.NoStartup
&& !ShellInfoObject
.ShellInitSettings
.BitUnion
.Bits
.Startup
)
715 return (EFI_SUCCESS
);
719 // print out our warning and see if they press a key
721 for ( Status
= EFI_UNSUPPORTED
, Delay
= (ShellInfoObject
.ShellInitSettings
.Delay
* 10)
722 ; Delay
> 0 && EFI_ERROR(Status
)
725 ShellPrintHiiEx(0, gST
->ConOut
->Mode
->CursorRow
, NULL
, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION
), ShellInfoObject
.HiiHandle
, Delay
/10);
727 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
729 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CRLF
), ShellInfoObject
.HiiHandle
);
734 if (Status
== EFI_SUCCESS
&& Key
.UnicodeChar
== 0 && Key
.ScanCode
== SCAN_ESC
) {
735 return (EFI_SUCCESS
);
738 NamePath
= FileDevicePath (NULL
, mStartupScript
);
740 // Try the first location
742 NewPath
= AppendDevicePathNode (ImagePath
, NamePath
);
743 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
744 if (EFI_ERROR(Status
)) {
746 // Try the second location
749 NewPath
= AppendDevicePathNode (FilePath
, NamePath
);
750 Status
= InternalOpenFileDevicePath(NewPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
754 // If we got a file, run it
756 if (!EFI_ERROR(Status
)) {
757 Status
= RunScriptFileHandle (FileHandle
, mStartupScript
);
758 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(FileHandle
);
760 FileStringPath
= ShellFindFilePath(mStartupScript
);
761 if (FileStringPath
== NULL
) {
763 // we return success since we dont need to have a startup script
765 Status
= EFI_SUCCESS
;
766 ASSERT(FileHandle
== NULL
);
768 Status
= RunScriptFile(FileStringPath
);
769 FreePool(FileStringPath
);
780 Function to perform the shell prompt looping. It will do a single prompt,
781 dispatch the result, and then return. It is expected that the caller will
782 call this function in a loop many times.
785 @retval RETURN_ABORTED
796 CONST CHAR16
*CurDir
;
803 // Get screen setting to decide size of the command line buffer
805 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &Column
, &Row
);
806 BufferSize
= Column
* Row
* sizeof (CHAR16
);
807 CmdLine
= AllocateZeroPool (BufferSize
);
808 if (CmdLine
== NULL
) {
809 return EFI_OUT_OF_RESOURCES
;
812 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
817 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, gST
->ConOut
->Mode
->CursorRow
);
819 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
820 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
822 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
826 // Read a line from the console
828 Status
= ShellInfoObject
.NewEfiShellProtocol
->ReadFile(ShellInfoObject
.NewShellParametersProtocol
->StdIn
, &BufferSize
, CmdLine
);
831 // Null terminate the string and parse it
833 if (!EFI_ERROR (Status
)) {
834 CmdLine
[BufferSize
/ sizeof (CHAR16
)] = CHAR_NULL
;
835 Status
= RunCommand(CmdLine
);
839 // Done with this command
846 Add a buffer to the Buffer To Free List for safely returning buffers to other
847 places without risking letting them modify internal shell information.
849 @param Buffer Something to pass to FreePool when the shell is exiting.
857 BUFFER_LIST
*BufferListEntry
;
859 if (Buffer
== NULL
) {
863 BufferListEntry
= AllocateZeroPool(sizeof(BUFFER_LIST
));
864 ASSERT(BufferListEntry
!= NULL
);
865 BufferListEntry
->Buffer
= Buffer
;
866 InsertTailList(&ShellInfoObject
.BufferToFreeList
.Link
, &BufferListEntry
->Link
);
871 Add a buffer to the Line History List
873 @param Buffer The line buffer to add.
877 AddLineToCommandHistory(
878 IN CONST CHAR16
*Buffer
883 Node
= AllocateZeroPool(sizeof(BUFFER_LIST
));
884 ASSERT(Node
!= NULL
);
885 Node
->Buffer
= AllocateZeroPool(StrSize(Buffer
));
886 ASSERT(Node
->Buffer
!= NULL
);
887 StrCpy(Node
->Buffer
, Buffer
);
889 InsertTailList(&ShellInfoObject
.ViewingSettings
.CommandHistory
.Link
, &Node
->Link
);
893 Checks if a string is an alias for another command. If yes, then it replaces the alias name
894 with the correct command name.
896 @param[in,out] CommandString Upon entry the potential alias. Upon return the
897 command name if it was an alias. If it was not
898 an alias it will be unchanged. This function may
899 change the buffer to fit the command name.
901 @retval EFI_SUCCESS The name was changed.
902 @retval EFI_SUCCESS The name was not an alias.
903 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
908 IN OUT CHAR16
**CommandString
911 CONST CHAR16
*NewString
;
913 NewString
= ShellInfoObject
.NewEfiShellProtocol
->GetAlias(*CommandString
, NULL
);
914 if (NewString
== NULL
) {
915 return (EFI_SUCCESS
);
917 FreePool(*CommandString
);
918 *CommandString
= AllocatePool(StrSize(NewString
));
919 if (*CommandString
== NULL
) {
920 return (EFI_OUT_OF_RESOURCES
);
922 StrCpy(*CommandString
, NewString
);
923 return (EFI_SUCCESS
);
927 Function allocates a new command line and replaces all instances of environment
928 variable names that are correctly preset to their values.
930 If the return value is not NULL the memory must be caller freed.
932 @param[in] OriginalCommandLine The original command line
934 @retval NULL An error ocurred.
935 @return The new command line with no environment variables present.
939 ShellConvertVariables (
940 IN CONST CHAR16
*OriginalCommandLine
943 CONST CHAR16
*MasterEnvList
;
945 CHAR16
*NewCommandLine1
;
946 CHAR16
*NewCommandLine2
;
950 SCRIPT_FILE
*CurrentScriptFile
;
951 ALIAS_LIST
*AliasListNode
;
953 ASSERT(OriginalCommandLine
!= NULL
);
956 NewSize
= StrSize(OriginalCommandLine
);
957 CurrentScriptFile
= ShellCommandGetCurrentScriptFile();
960 ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
963 // calculate the size required for the post-conversion string...
965 if (CurrentScriptFile
!= NULL
) {
966 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
967 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
968 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
970 for (Temp
= StrStr(OriginalCommandLine
, AliasListNode
->Alias
)
972 ; Temp
= StrStr(Temp
+1, AliasListNode
->Alias
)
975 // we need a preceeding and if there is space no ^ preceeding (if no space ignore)
977 if ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2)) {
978 NewSize
+= StrSize(AliasListNode
->CommandString
);
984 for (MasterEnvList
= EfiShellGetEnv(NULL
)
985 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
986 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
988 if (StrSize(MasterEnvList
) > ItemSize
) {
989 ItemSize
= StrSize(MasterEnvList
);
991 for (Temp
= StrStr(OriginalCommandLine
, MasterEnvList
)
993 ; Temp
= StrStr(Temp
+1, MasterEnvList
)
996 // we need a preceeding and following % and if there is space no ^ preceeding (if no space ignore)
998 if (*(Temp
-1) == L
'%' && *(Temp
+StrLen(MasterEnvList
)) == L
'%' &&
999 ((((Temp
-OriginalCommandLine
)>2) && *(Temp
-2) != L
'^') || ((Temp
-OriginalCommandLine
)<=2))) {
1000 NewSize
+=StrSize(EfiShellGetEnv(MasterEnvList
));
1006 // Quick out if none were found...
1008 if (NewSize
== StrSize(OriginalCommandLine
)) {
1009 ASSERT(Temp
== NULL
);
1010 Temp
= StrnCatGrow(&Temp
, NULL
, OriginalCommandLine
, 0);
1015 // now do the replacements...
1017 NewCommandLine1
= AllocateZeroPool(NewSize
);
1018 NewCommandLine2
= AllocateZeroPool(NewSize
);
1019 ItemTemp
= AllocateZeroPool(ItemSize
+(2*sizeof(CHAR16
)));
1020 if (NewCommandLine1
== NULL
|| NewCommandLine2
== NULL
|| ItemTemp
== NULL
) {
1021 SHELL_FREE_NON_NULL(NewCommandLine1
);
1022 SHELL_FREE_NON_NULL(NewCommandLine2
);
1023 SHELL_FREE_NON_NULL(ItemTemp
);
1026 StrCpy(NewCommandLine1
, OriginalCommandLine
);
1027 for (MasterEnvList
= EfiShellGetEnv(NULL
)
1028 ; MasterEnvList
!= NULL
&& *MasterEnvList
!= CHAR_NULL
//&& *(MasterEnvList+1) != CHAR_NULL
1029 ; MasterEnvList
+= StrLen(MasterEnvList
) + 1
1031 StrCpy(ItemTemp
, L
"%");
1032 StrCat(ItemTemp
, MasterEnvList
);
1033 StrCat(ItemTemp
, L
"%");
1034 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, ItemTemp
, EfiShellGetEnv(MasterEnvList
), TRUE
, FALSE
);
1035 StrCpy(NewCommandLine1
, NewCommandLine2
);
1037 if (CurrentScriptFile
!= NULL
) {
1038 for (AliasListNode
= (ALIAS_LIST
*)GetFirstNode(&CurrentScriptFile
->SubstList
)
1039 ; !IsNull(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1040 ; AliasListNode
= (ALIAS_LIST
*)GetNextNode(&CurrentScriptFile
->SubstList
, &AliasListNode
->Link
)
1042 ShellCopySearchAndReplace(NewCommandLine1
, NewCommandLine2
, NewSize
, AliasListNode
->Alias
, AliasListNode
->CommandString
, TRUE
, FALSE
);
1043 StrCpy(NewCommandLine1
, NewCommandLine2
);
1047 FreePool(NewCommandLine2
);
1050 return (NewCommandLine1
);
1054 Internal function to run a command line with pipe usage.
1056 @param[in] CmdLine The pointer to the command line.
1057 @param[in] StdIn The pointer to the Standard input.
1058 @param[in] StdOut The pointer to the Standard output.
1060 @retval EFI_SUCCESS The split command is executed successfully.
1061 @retval other Some error occurs when executing the split command.
1066 IN CONST CHAR16
*CmdLine
,
1067 IN SHELL_FILE_HANDLE
*StdIn
,
1068 IN SHELL_FILE_HANDLE
*StdOut
1072 CHAR16
*NextCommandLine
;
1073 CHAR16
*OurCommandLine
;
1077 SHELL_FILE_HANDLE
*TempFileHandle
;
1080 ASSERT(StdOut
== NULL
);
1082 ASSERT(StrStr(CmdLine
, L
"|") != NULL
);
1084 Status
= EFI_SUCCESS
;
1085 NextCommandLine
= NULL
;
1086 OurCommandLine
= NULL
;
1090 NextCommandLine
= StrnCatGrow(&NextCommandLine
, &Size1
, StrStr(CmdLine
, L
"|")+1, 0);
1091 OurCommandLine
= StrnCatGrow(&OurCommandLine
, &Size2
, CmdLine
, StrStr(CmdLine
, L
"|") - CmdLine
);
1092 if (NextCommandLine
[0] != CHAR_NULL
&&
1093 NextCommandLine
[0] == L
'a' &&
1094 NextCommandLine
[1] == L
' '
1096 CopyMem(NextCommandLine
, NextCommandLine
+1, StrSize(NextCommandLine
) - sizeof(NextCommandLine
[0]));
1104 // make a SPLIT_LIST item and add to list
1106 Split
= AllocateZeroPool(sizeof(SPLIT_LIST
));
1107 ASSERT(Split
!= NULL
);
1108 Split
->SplitStdIn
= StdIn
;
1109 Split
->SplitStdOut
= ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode
), NULL
);
1110 ASSERT(Split
->SplitStdOut
!= NULL
);
1111 InsertHeadList(&ShellInfoObject
.SplitList
.Link
, &Split
->Link
);
1113 ASSERT(StrStr(OurCommandLine
, L
"|") == NULL
);
1114 Status
= RunCommand(OurCommandLine
);
1117 // move the output from the first to the in to the second.
1119 TempFileHandle
= Split
->SplitStdOut
;
1120 if (Split
->SplitStdIn
== StdIn
) {
1121 Split
->SplitStdOut
= NULL
;
1123 Split
->SplitStdOut
= Split
->SplitStdIn
;
1125 Split
->SplitStdIn
= TempFileHandle
;
1126 ShellInfoObject
.NewEfiShellProtocol
->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
), 0);
1128 if (!EFI_ERROR(Status
)) {
1129 Status
= RunCommand(NextCommandLine
);
1133 // remove the top level from the ScriptList
1135 ASSERT((SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
) == Split
);
1136 RemoveEntryList(&Split
->Link
);
1139 // Note that the original StdIn is now the StdOut...
1141 if (Split
->SplitStdOut
!= NULL
&& Split
->SplitStdOut
!= StdIn
) {
1142 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdOut
));
1144 if (Split
->SplitStdIn
!= NULL
) {
1145 ShellInfoObject
.NewEfiShellProtocol
->CloseFile(ConvertShellHandleToEfiFileProtocol(Split
->SplitStdIn
));
1149 FreePool(NextCommandLine
);
1150 FreePool(OurCommandLine
);
1156 Function will process and run a command line.
1158 This will determine if the command line represents an internal shell
1159 command or dispatch an external application.
1161 @param[in] CmdLine The command line to parse.
1163 @retval EFI_SUCCESS The command was completed.
1164 @retval EFI_ABORTED The command's operation was aborted.
1169 IN CONST CHAR16
*CmdLine
1173 CHAR16
*CommandName
;
1174 SHELL_STATUS ShellStatus
;
1178 CHAR16 LeString
[11];
1179 CHAR16
*PostAliasCmdLine
;
1180 UINTN PostAliasSize
;
1181 CHAR16
*PostVariableCmdLine
;
1182 CHAR16
*CommandWithPath
;
1183 EFI_DEVICE_PATH_PROTOCOL
*DevPath
;
1184 CONST CHAR16
*TempLocation
;
1185 CONST CHAR16
*TempLocation2
;
1186 SHELL_FILE_HANDLE OriginalStdIn
;
1187 SHELL_FILE_HANDLE OriginalStdOut
;
1188 SHELL_FILE_HANDLE OriginalStdErr
;
1189 CHAR16
*TempLocation3
;
1192 CHAR16
*CleanOriginal
;
1195 ASSERT(CmdLine
!= NULL
);
1196 if (StrLen(CmdLine
) == 0) {
1197 return (EFI_SUCCESS
);
1201 PostVariableCmdLine
= NULL
;
1202 PostAliasCmdLine
= NULL
;
1203 CommandWithPath
= NULL
;
1205 Status
= EFI_SUCCESS
;
1206 CleanOriginal
= NULL
;
1209 CleanOriginal
= StrnCatGrow(&CleanOriginal
, NULL
, CmdLine
, 0);
1210 while (CleanOriginal
[StrLen(CleanOriginal
)-1] == L
' ') {
1211 CleanOriginal
[StrLen(CleanOriginal
)-1] = CHAR_NULL
;
1213 while (CleanOriginal
[0] == L
' ') {
1214 CopyMem(CleanOriginal
, CleanOriginal
+1, StrSize(CleanOriginal
) - sizeof(CleanOriginal
[0]));
1218 if (StrStr(CleanOriginal
, L
" ") == NULL
){
1219 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, 0);
1221 StrnCatGrow(&CommandName
, NULL
, CleanOriginal
, StrStr(CleanOriginal
, L
" ") - CleanOriginal
);
1224 ASSERT(PostAliasCmdLine
== NULL
);
1225 if (!ShellCommandIsCommandOnList(CommandName
)) {
1227 // Convert via alias
1229 Status
= ShellConvertAlias(&CommandName
);
1231 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, CommandName
, 0);
1232 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, &PostAliasSize
, StrStr(CleanOriginal
, L
" "), 0);
1233 ASSERT_EFI_ERROR(Status
);
1235 PostAliasCmdLine
= StrnCatGrow(&PostAliasCmdLine
, NULL
, CleanOriginal
, 0);
1238 if (CleanOriginal
!= NULL
) {
1239 FreePool(CleanOriginal
);
1240 CleanOriginal
= NULL
;
1243 if (CommandName
!= NULL
) {
1244 FreePool(CommandName
);
1248 PostVariableCmdLine
= ShellConvertVariables(PostAliasCmdLine
);
1251 // we can now free the modified by alias command line
1253 if (PostAliasCmdLine
!= NULL
) {
1254 FreePool(PostAliasCmdLine
);
1255 PostAliasCmdLine
= NULL
;
1258 if (PostVariableCmdLine
== NULL
) {
1259 return (EFI_OUT_OF_RESOURCES
);
1262 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1263 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1265 while (PostVariableCmdLine
[0] == L
' ') {
1266 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1270 // We dont do normal processing with a split command line (output from one command input to another)
1272 TempLocation3
= NULL
;
1273 if (StrStr(PostVariableCmdLine
, L
"|") != NULL
) {
1274 for (TempLocation3
= PostVariableCmdLine
; TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
; TempLocation3
++) {
1275 if (*TempLocation3
== L
'^' && *(TempLocation3
+1) == L
'|') {
1277 } else if (*TempLocation3
== L
'|') {
1282 if (TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
) {
1284 // are we in an existing split???
1286 if (!IsListEmpty(&ShellInfoObject
.SplitList
.Link
)) {
1287 Split
= (SPLIT_LIST
*)GetFirstNode(&ShellInfoObject
.SplitList
.Link
);
1290 if (Split
== NULL
) {
1291 Status
= RunSplitCommand(PostVariableCmdLine
, NULL
, NULL
);
1293 Status
= RunSplitCommand(PostVariableCmdLine
, Split
->SplitStdIn
, Split
->SplitStdOut
);
1298 // If this is a mapped drive change handle that...
1300 if (PostVariableCmdLine
[(StrLen(PostVariableCmdLine
)-1)] == L
':' && StrStr(PostVariableCmdLine
, L
" ") == NULL
) {
1301 Status
= ShellInfoObject
.NewEfiShellProtocol
->SetCurDir(NULL
, PostVariableCmdLine
);
1302 if (EFI_ERROR(Status
)) {
1303 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_MAPPING
), ShellInfoObject
.HiiHandle
, PostVariableCmdLine
);
1305 FreePool(PostVariableCmdLine
);
1309 ///@todo update this section to divide into 3 ways - run internal command, run split (above), and run an external file...
1310 /// We waste a lot of time doing processing like StdIn,StdOut,Argv,Argc for things that are external files...
1314 Status
= UpdateStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
);
1315 if (EFI_ERROR(Status
)) {
1316 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_INVALID_REDIR
), ShellInfoObject
.HiiHandle
);
1319 // remove the < and/or > from the command line now
1321 for (TempLocation3
= PostVariableCmdLine
; TempLocation3
!= NULL
&& *TempLocation3
!= CHAR_NULL
; TempLocation3
++) {
1322 if (*TempLocation3
== L
'^') {
1323 if (*(TempLocation3
+1) == L
'<' || *(TempLocation3
+1) == L
'>') {
1324 CopyMem(TempLocation3
, TempLocation3
+1, StrSize(TempLocation3
) - sizeof(TempLocation3
[0]));
1326 } else if (*TempLocation3
== L
'>') {
1327 *TempLocation3
= CHAR_NULL
;
1328 } else if ((*TempLocation3
== L
'1' || *TempLocation3
== L
'2')&&(*(TempLocation3
+1) == L
'>')) {
1329 *TempLocation3
= CHAR_NULL
;
1333 while (PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] == L
' ') {
1334 PostVariableCmdLine
[StrLen(PostVariableCmdLine
)-1] = CHAR_NULL
;
1336 while (PostVariableCmdLine
[0] == L
' ') {
1337 CopyMem(PostVariableCmdLine
, PostVariableCmdLine
+1, StrSize(PostVariableCmdLine
) - sizeof(PostVariableCmdLine
[0]));
1341 // get the argc and argv updated for internal commands
1343 Status
= UpdateArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, PostVariableCmdLine
, &Argv
, &Argc
);
1344 ASSERT_EFI_ERROR(Status
);
1346 for (Count
= 0 ; Count
< ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count
++) {
1347 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
], L
"-?") == ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]
1348 || (ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][0] == L
'?' && ShellInfoObject
.NewShellParametersProtocol
->Argv
[0][1] == CHAR_NULL
)
1351 // We need to redo the arguments since a parameter was -?
1352 // move them all down 1 to the end, then up one then replace the first with help
1354 FreePool(ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
]);
1355 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count
] = NULL
;
1356 for (Count2
= Count
; (Count2
+ 1) < ShellInfoObject
.NewShellParametersProtocol
->Argc
; Count2
++) {
1357 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
+1];
1359 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = NULL
;
1360 for (Count2
= ShellInfoObject
.NewShellParametersProtocol
->Argc
-1 ; Count2
> 0 ; Count2
--) {
1361 ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
] = ShellInfoObject
.NewShellParametersProtocol
->Argv
[Count2
-1];
1363 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = NULL
;
1364 ShellInfoObject
.NewShellParametersProtocol
->Argv
[0] = StrnCatGrow(&ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], NULL
, L
"help", 0);
1372 if (ShellCommandIsCommandOnList(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1374 // Run the command (which was converted if it was an alias)
1376 if (!EFI_ERROR(Status
)) {
1377 Status
= ShellCommandRunCommandHandler(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], &ShellStatus
, &LastError
);
1378 ASSERT_EFI_ERROR(Status
);
1379 UnicodeSPrint(LeString
, sizeof(LeString
)*sizeof(LeString
[0]), L
"0x%08x", ShellStatus
);
1380 DEBUG_CODE(InternalEfiShellSetEnv(L
"DebugLasterror", LeString
, TRUE
););
1382 InternalEfiShellSetEnv(L
"Lasterror", LeString
, TRUE
);
1385 // Pass thru the exitcode from the app.
1387 if (ShellCommandGetExit()) {
1388 Status
= ShellStatus
;
1389 } else if (ShellStatus
!= 0 && IsScriptOnlyCommand(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0])) {
1390 Status
= EFI_ABORTED
;
1395 // run an external file (or script)
1397 if (StrStr(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], L
":") != NULL
) {
1398 ASSERT (CommandWithPath
== NULL
);
1399 if (ShellIsFile(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]) == EFI_SUCCESS
) {
1400 CommandWithPath
= StrnCatGrow(&CommandWithPath
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1403 if (CommandWithPath
== NULL
) {
1404 CommandWithPath
= ShellFindFilePathEx(ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], mExecutableExtensions
);
1406 if (CommandWithPath
== NULL
|| ShellIsDirectory(CommandWithPath
) == EFI_SUCCESS
) {
1407 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_NOT_FOUND
), ShellInfoObject
.HiiHandle
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0]);
1410 // Check if it's a NSH (script) file.
1412 TempLocation
= CommandWithPath
+StrLen(CommandWithPath
)-4;
1413 TempLocation2
= mScriptExtension
;
1414 if ((StrLen(CommandWithPath
) > 4) && (StringNoCaseCompare((VOID
*)(&TempLocation
), (VOID
*)(&TempLocation2
)) == 0)) {
1415 Status
= RunScriptFile (CommandWithPath
);
1417 DevPath
= ShellInfoObject
.NewEfiShellProtocol
->GetDevicePathFromFilePath(CommandWithPath
);
1418 ASSERT(DevPath
!= NULL
);
1419 Status
= InternalShellExecuteDevicePath(
1422 PostVariableCmdLine
,
1429 CommandName
= StrnCatGrow(&CommandName
, NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[0], 0);
1431 RestoreArgcArgv(ShellInfoObject
.NewShellParametersProtocol
, &Argv
, &Argc
);
1433 RestoreStdInStdOutStdErr(ShellInfoObject
.NewShellParametersProtocol
, &OriginalStdIn
, &OriginalStdOut
, &OriginalStdErr
);
1435 if (CommandName
!= NULL
) {
1436 if (ShellCommandGetCurrentScriptFile() != NULL
&& !IsScriptOnlyCommand(CommandName
)) {
1438 // if this is NOT a scipt only command return success so the script won't quit.
1439 // prevent killing the script - this is the only place where we know the actual command name (after alias and variable replacement...)
1441 Status
= EFI_SUCCESS
;
1446 SHELL_FREE_NON_NULL(CommandName
);
1447 SHELL_FREE_NON_NULL(CommandWithPath
);
1448 SHELL_FREE_NON_NULL(PostVariableCmdLine
);
1449 SHELL_FREE_NON_NULL(DevPath
);
1454 STATIC CONST UINT16 InvalidChars
[] = {L
'*', L
'?', L
'<', L
'>', L
'\\', L
'/', L
'\"', 0x0001, 0x0002};
1456 Function determins if the CommandName COULD be a valid command. It does not determine whether
1457 this is a valid command. It only checks for invalid characters.
1459 @param[in] CommandName The name to check
1461 @retval TRUE CommandName could be a command name
1462 @retval FALSE CommandName could not be a valid command name
1467 IN CONST CHAR16
*CommandName
1471 if (CommandName
== NULL
) {
1476 ; Count
< sizeof(InvalidChars
) / sizeof(InvalidChars
[0])
1479 if (ScanMem16(CommandName
, StrSize(CommandName
), InvalidChars
[Count
]) != NULL
) {
1487 Function to process a NSH script file via SHELL_FILE_HANDLE.
1489 @param[in] Handle The handle to the already opened file.
1490 @param[in] Name The name of the script file.
1492 @retval EFI_SUCCESS the script completed sucessfully
1496 RunScriptFileHandle (
1497 IN SHELL_FILE_HANDLE Handle
,
1498 IN CONST CHAR16
*Name
1502 SCRIPT_FILE
*NewScriptFile
;
1504 CHAR16
*CommandLine
;
1505 CHAR16
*CommandLine2
;
1506 CHAR16
*CommandLine3
;
1507 SCRIPT_COMMAND_LIST
*LastCommand
;
1509 BOOLEAN PreScriptEchoState
;
1510 CONST CHAR16
*CurDir
;
1513 ASSERT(!ShellCommandGetScriptExit());
1515 PreScriptEchoState
= ShellCommandGetEchoState();
1517 NewScriptFile
= (SCRIPT_FILE
*)AllocateZeroPool(sizeof(SCRIPT_FILE
));
1518 if (NewScriptFile
== NULL
) {
1519 return (EFI_OUT_OF_RESOURCES
);
1525 ASSERT(NewScriptFile
->ScriptName
== NULL
);
1526 NewScriptFile
->ScriptName
= StrnCatGrow(&NewScriptFile
->ScriptName
, NULL
, Name
, 0);
1527 if (NewScriptFile
->ScriptName
== NULL
) {
1528 DeleteScriptFileStruct(NewScriptFile
);
1529 return (EFI_OUT_OF_RESOURCES
);
1533 // Save the parameters (used to replace %0 to %9 later on)
1535 NewScriptFile
->Argc
= ShellInfoObject
.NewShellParametersProtocol
->Argc
;
1536 if (NewScriptFile
->Argc
!= 0) {
1537 NewScriptFile
->Argv
= (CHAR16
**)AllocateZeroPool(NewScriptFile
->Argc
* sizeof(CHAR16
*));
1538 if (NewScriptFile
->Argv
== NULL
) {
1539 DeleteScriptFileStruct(NewScriptFile
);
1540 return (EFI_OUT_OF_RESOURCES
);
1542 for (LoopVar
= 0 ; LoopVar
< 10 && LoopVar
< NewScriptFile
->Argc
; LoopVar
++) {
1543 ASSERT(NewScriptFile
->Argv
[LoopVar
] == NULL
);
1544 NewScriptFile
->Argv
[LoopVar
] = StrnCatGrow(&NewScriptFile
->Argv
[LoopVar
], NULL
, ShellInfoObject
.NewShellParametersProtocol
->Argv
[LoopVar
], 0);
1545 if (NewScriptFile
->Argv
[LoopVar
] == NULL
) {
1546 DeleteScriptFileStruct(NewScriptFile
);
1547 return (EFI_OUT_OF_RESOURCES
);
1551 NewScriptFile
->Argv
= NULL
;
1554 InitializeListHead(&NewScriptFile
->CommandList
);
1555 InitializeListHead(&NewScriptFile
->SubstList
);
1558 // Now build the list of all script commands.
1561 while(!ShellFileHandleEof(Handle
)) {
1562 CommandLine
= ShellFileHandleReturnLine(Handle
, &Ascii
);
1564 if (CommandLine
== NULL
|| StrLen(CommandLine
) == 0) {
1567 NewScriptFile
->CurrentCommand
= AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST
));
1568 if (NewScriptFile
->CurrentCommand
== NULL
) {
1569 DeleteScriptFileStruct(NewScriptFile
);
1570 return (EFI_OUT_OF_RESOURCES
);
1573 NewScriptFile
->CurrentCommand
->Cl
= CommandLine
;
1574 NewScriptFile
->CurrentCommand
->Data
= NULL
;
1575 NewScriptFile
->CurrentCommand
->Line
= LineCount
;
1577 InsertTailList(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1581 // Add this as the topmost script file
1583 ShellCommandSetNewScript (NewScriptFile
);
1586 // Now enumerate through the commands and run each one.
1588 CommandLine
= AllocatePool(PcdGet16(PcdShellPrintBufferSize
));
1589 if (CommandLine
== NULL
) {
1590 DeleteScriptFileStruct(NewScriptFile
);
1591 return (EFI_OUT_OF_RESOURCES
);
1593 CommandLine2
= AllocatePool(PcdGet16(PcdShellPrintBufferSize
));
1594 if (CommandLine2
== NULL
) {
1595 FreePool(CommandLine
);
1596 DeleteScriptFileStruct(NewScriptFile
);
1597 return (EFI_OUT_OF_RESOURCES
);
1600 for ( NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetFirstNode(&NewScriptFile
->CommandList
)
1601 ; !IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)
1602 ; // conditional increment in the body of the loop
1604 ASSERT(CommandLine2
!= NULL
);
1605 StrCpy(CommandLine2
, NewScriptFile
->CurrentCommand
->Cl
);
1608 // NULL out comments
1610 for (CommandLine3
= CommandLine2
; CommandLine3
!= NULL
&& *CommandLine3
!= CHAR_NULL
; CommandLine3
++) {
1611 if (*CommandLine3
== L
'^') {
1612 if (*(CommandLine3
+1) == L
'#' || *(CommandLine3
+1) == L
':') {
1613 CopyMem(CommandLine3
, CommandLine3
+1, StrSize(CommandLine3
) - sizeof(CommandLine3
[0]));
1615 } else if (*CommandLine3
== L
'#') {
1616 *CommandLine3
= CHAR_NULL
;
1620 if (CommandLine2
!= NULL
&& StrLen(CommandLine2
) >= 1) {
1622 // Due to variability in starting the find and replace action we need to have both buffers the same.
1624 StrCpy(CommandLine
, CommandLine2
);
1627 // Remove the %0 to %9 from the command line (if we have some arguments)
1629 if (NewScriptFile
->Argv
!= NULL
) {
1630 switch (NewScriptFile
->Argc
) {
1632 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", NewScriptFile
->Argv
[9], FALSE
, TRUE
);
1633 ASSERT_EFI_ERROR(Status
);
1635 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", NewScriptFile
->Argv
[8], FALSE
, TRUE
);
1636 ASSERT_EFI_ERROR(Status
);
1638 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", NewScriptFile
->Argv
[7], FALSE
, TRUE
);
1639 ASSERT_EFI_ERROR(Status
);
1641 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", NewScriptFile
->Argv
[6], FALSE
, TRUE
);
1642 ASSERT_EFI_ERROR(Status
);
1644 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", NewScriptFile
->Argv
[5], FALSE
, TRUE
);
1645 ASSERT_EFI_ERROR(Status
);
1647 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", NewScriptFile
->Argv
[4], FALSE
, TRUE
);
1648 ASSERT_EFI_ERROR(Status
);
1650 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", NewScriptFile
->Argv
[3], FALSE
, TRUE
);
1651 ASSERT_EFI_ERROR(Status
);
1653 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", NewScriptFile
->Argv
[2], FALSE
, TRUE
);
1654 ASSERT_EFI_ERROR(Status
);
1656 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", NewScriptFile
->Argv
[1], FALSE
, TRUE
);
1657 ASSERT_EFI_ERROR(Status
);
1659 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%0", NewScriptFile
->Argv
[0], FALSE
, TRUE
);
1660 ASSERT_EFI_ERROR(Status
);
1666 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%1", L
"\"\"", FALSE
, FALSE
);
1667 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%2", L
"\"\"", FALSE
, FALSE
);
1668 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%3", L
"\"\"", FALSE
, FALSE
);
1669 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%4", L
"\"\"", FALSE
, FALSE
);
1670 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%5", L
"\"\"", FALSE
, FALSE
);
1671 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%6", L
"\"\"", FALSE
, FALSE
);
1672 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%7", L
"\"\"", FALSE
, FALSE
);
1673 Status
= ShellCopySearchAndReplace(CommandLine
, CommandLine2
, PcdGet16 (PcdShellPrintBufferSize
), L
"%8", L
"\"\"", FALSE
, FALSE
);
1674 Status
= ShellCopySearchAndReplace(CommandLine2
, CommandLine
, PcdGet16 (PcdShellPrintBufferSize
), L
"%9", L
"\"\"", FALSE
, FALSE
);
1676 StrCpy(CommandLine2
, CommandLine
);
1678 LastCommand
= NewScriptFile
->CurrentCommand
;
1680 for (CommandLine3
= CommandLine2
; CommandLine3
[0] == L
' ' ; CommandLine3
++);
1682 if (CommandLine3
!= NULL
&& CommandLine3
[0] == L
':' ) {
1684 // This line is a goto target / label
1687 if (CommandLine3
!= NULL
&& StrLen(CommandLine3
) > 0) {
1688 if (ShellCommandGetEchoState()) {
1689 CurDir
= ShellInfoObject
.NewEfiShellProtocol
->GetEnv(L
"cwd");
1690 if (CurDir
!= NULL
&& StrLen(CurDir
) > 1) {
1691 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_CURDIR
), ShellInfoObject
.HiiHandle
, CurDir
);
1693 ShellPrintHiiEx(-1, -1, NULL
, STRING_TOKEN (STR_SHELL_SHELL
), ShellInfoObject
.HiiHandle
);
1695 ShellPrintEx(-1, -1, L
"%s\r\n", CommandLine2
);
1697 Status
= RunCommand(CommandLine3
);
1700 if (ShellCommandGetScriptExit()) {
1701 ShellCommandRegisterExit(FALSE
);
1702 Status
= EFI_SUCCESS
;
1705 if (EFI_ERROR(Status
)) {
1708 if (ShellCommandGetExit()) {
1713 // If that commend did not update the CurrentCommand then we need to advance it...
1715 if (LastCommand
== NewScriptFile
->CurrentCommand
) {
1716 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1717 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
1718 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
1722 NewScriptFile
->CurrentCommand
= (SCRIPT_COMMAND_LIST
*)GetNextNode(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
);
1723 if (!IsNull(&NewScriptFile
->CommandList
, &NewScriptFile
->CurrentCommand
->Link
)) {
1724 NewScriptFile
->CurrentCommand
->Reset
= TRUE
;
1729 ShellCommandSetEchoState(PreScriptEchoState
);
1731 FreePool(CommandLine
);
1732 FreePool(CommandLine2
);
1733 ShellCommandSetNewScript (NULL
);
1734 return (EFI_SUCCESS
);
1738 Function to process a NSH script file.
1740 @param[in] ScriptPath Pointer to the script file name (including file system path).
1742 @retval EFI_SUCCESS the script completed sucessfully
1747 IN CONST CHAR16
*ScriptPath
1751 SHELL_FILE_HANDLE FileHandle
;
1753 if (ShellIsFile(ScriptPath
) != EFI_SUCCESS
) {
1754 return (EFI_INVALID_PARAMETER
);
1757 Status
= ShellOpenFileByName(ScriptPath
, &FileHandle
, EFI_FILE_MODE_READ
, 0);
1758 if (EFI_ERROR(Status
)) {
1762 Status
= RunScriptFileHandle(FileHandle
, ScriptPath
);
1764 ShellCloseFile(&FileHandle
);