windows-nt/Source/XPSP1/NT/base/efiutil/sdk/shell/shellenv/exec.c
2020-09-26 16:20:57 +08:00

1244 lines
35 KiB
C

/*++
Copyright (c) 1998 Intel Corporation
Module Name:
exec.c
Abstract:
Revision History
--*/
#include "shelle.h"
typedef struct {
CHAR16 **Arg;
UINTN ArgIndex;
BOOLEAN Output;
BOOLEAN Quote;
UINTN AliasLevel;
UINTN MacroParan;
UINTN RecurseLevel;
CHAR16 Buffer[MAX_ARG_LENGTH];
} PARSE_STATE;
typedef struct _SENV_OPEN_DIR {
struct _SENV_OPEN_DIR *Next;
EFI_FILE_HANDLE Handle;
} SENV_OPEN_DIR;
/*
* Internal macros
*/
#define ArgTooLong(i) (i > MAX_ARG_LENGTH-sizeof(CHAR16))
/*
* Internal prototypes
*/
EFI_STATUS
ShellParseStr (
IN CHAR16 *Str,
IN OUT PARSE_STATE *ParseState
);
EFI_STATUS
SEnvDoExecute (
IN EFI_HANDLE *ParentImageHandle,
IN CHAR16 *CommandLine,
IN ENV_SHELL_INTERFACE *Shell,
IN BOOLEAN Output
);
VOID
INTERNAL
SEnvLoadImage (
IN EFI_HANDLE ParentImage,
IN CHAR16 *IName,
OUT EFI_HANDLE *pImageHandle,
OUT EFI_FILE_HANDLE *pScriptsHandle
);
/*
* Parser driver function
*/
EFI_STATUS
SEnvStringToArg (
IN CHAR16 *Str,
IN BOOLEAN Output,
OUT CHAR16 ***pArgv,
OUT UINT32 *pArgc
)
{
PARSE_STATE ParseState;
EFI_STATUS Status;
/*
* Initialize a new state
*/
ZeroMem (&ParseState, sizeof(ParseState));
ParseState.Output = Output;
ParseState.Arg = AllocateZeroPool (MAX_ARG_COUNT * sizeof(CHAR16 *));
if (!ParseState.Arg) {
return EFI_OUT_OF_RESOURCES;
}
/*
* Parse the string
*/
Status = ShellParseStr (Str, &ParseState);
*pArgv = ParseState.Arg;
*pArgc = (UINT32) ParseState.ArgIndex;
/*
* Done
*/
return Status;
}
EFI_STATUS
ShellParseStr (
IN CHAR16 *Str,
IN OUT PARSE_STATE *ParseState
)
{
EFI_STATUS Status;
CHAR16 *Alias;
CHAR16 *NewArg;
CHAR16 *SubstituteStr;
UINTN Index;
BOOLEAN Literal;
BOOLEAN Comment;
UINTN ArgNo;
ParseState->RecurseLevel += 1;
if (ParseState->RecurseLevel > 5) {
DEBUG ((D_PARSE, "Recursive alias or macro\n"));
if (ParseState->Output) {
Print (L"Recursive alias or macro\n");
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
NewArg = ParseState->Buffer;
while (*Str) {
/*
* Skip leading white space
*/
if (IsWhiteSpace(*Str)) {
Str += 1;
continue;
}
/*
* Pull this arg out of the string
*/
Index = 0;
Literal = FALSE;
Comment = FALSE;
while (*Str) {
/*
* If we have white space (or the ',' arg separator) and we are
* not in a quote or macro expansion, move to the next word
*/
if ((IsWhiteSpace(*Str) || *Str == ',') &&
!ParseState->Quote && !ParseState->MacroParan) {
break;
}
/*
* Check arg length
*/
if ( ArgTooLong(Index) ) {
DEBUG((D_PARSE, "Argument too long\n"));
if (ParseState->Output) {
Print (L"Argument too long\n");
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
/*
* Check char
*/
switch (*Str) {
case '#':
/* Comment, discard the rest of the characters in the line */
Comment = TRUE;
while( *Str++ );
break;
case '%':
if ( IsDigit(Str[1]) && IsWhiteSpace(Str[2]) ) {
/* Found a script argument - substitute */
ArgNo = Str[1] - '0';
Status = SEnvBatchGetArg( ArgNo, &SubstituteStr );
if ( EFI_ERROR(Status) ) {
/* if not found, just ignore, as if no arg */
DEBUG((D_PARSE, "Argument %d not found - ignored\n", ArgNo));
Status = EFI_SUCCESS;
goto Done;
}
if ( ArgTooLong(StrLen(SubstituteStr)) ) {
DEBUG((D_PARSE, "Argument too long\n"));
if (ParseState->Output) {
Print (L"Argument too long\n");
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
StrCpy( &NewArg[Index], SubstituteStr );
Index += StrLen( SubstituteStr );
Str += 1;
} else if ( IsAlpha(Str[1]) && IsWhiteSpace(Str[2]) ) {
/*
* For loop index
*/
Status = SEnvSubstituteForLoopIndex( Str, &SubstituteStr );
if ( EFI_ERROR(Status) ) {
goto Done;
}
if ( SubstituteStr ) {
/* Found a match */
if ( ArgTooLong(StrLen(SubstituteStr)) ) {
DEBUG((D_PARSE, "Argument too long\n"));
if (ParseState->Output) {
Print (L"Argument too long\n");
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
StrCpy( &NewArg[Index], SubstituteStr );
Index += StrLen( SubstituteStr );
/* only advance one char - standard processing will get the 2nd char */
Str += 1;
}
/* if no match then just continue without substitution */
} else {
/*
* Found a variable of some kind
* If there is another '%' before any whitespace, look for
* an environment variable to substitute.
* If there is no environment variable, then the arg is the
* literal string including the '%' signs; otherwise substitute
*/
SubstituteStr = Str + 1;
while ( !IsWhiteSpace(*SubstituteStr) ) {
if ( *SubstituteStr == '%' ) {
CHAR16 *VarName;
UINTN VarNameLen;
/*
* Extract the (potential) variable name
*/
VarNameLen = SubstituteStr - (Str + 1);
VarName = AllocateZeroPool( (VarNameLen + 1)*sizeof(CHAR16) );
if ( !VarName ) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
CopyMem( VarName, Str+1, (VarNameLen + 1)*sizeof(CHAR16) );
VarName[VarNameLen] = (CHAR16)0x0000;
/*
* Check for special case "lasterror" variable
* Otherwise just get the matching environment variable
*/
if ( SEnvBatchVarIsLastError( VarName ) ) {
SubstituteStr = SEnvBatchGetLastError();
} else {
SubstituteStr = SEnvGetEnv( VarName );
}
FreePool( VarName );
if ( !SubstituteStr ) {
/* Not found - this is OK, then just use the original
* string %xxx% in the arg. Note that we know that
* this loop will terminate, since we found the % b4 */
NewArg[Index++] = *Str;
Str += 1;
while ( *Str != '%' ) {
NewArg[Index++] = *Str;
Str += 1;
}
NewArg[Index++] = *Str;
Str += 1;
} else {
/* Insert the variable's value in the new arg -
* the arg may include more than just the variable */
if ( ArgTooLong( Index + StrLen(SubstituteStr) ) ) {
DEBUG((D_PARSE, "Argument too long\n"));
if (ParseState->Output) {
Print (L"Argument too long\n");
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
StrCpy( &NewArg[Index], SubstituteStr );
Index += StrLen(SubstituteStr);
Str += VarNameLen + 1;
}
break;
}
SubstituteStr += 1;
} /* end while */
}
break;
case '^':
/* Literal, don't process aliases on this arg */
if (Str[1]) {
Str += 1;
NewArg[Index++] = *Str;
Literal = TRUE;
}
break;
case '"':
/* Quoted string entry and exit */
ParseState->Quote = !ParseState->Quote;
break;
case '(':
if (ParseState->MacroParan) {
ParseState->MacroParan = ParseState->MacroParan + 1;
}
NewArg[Index++] = *Str;
break;
case ')':
if (ParseState->MacroParan) {
/* End of a macro - go evaluate it */
ParseState->MacroParan -= 1;
/* BUGBUG: code not complete */
ASSERT (FALSE);
} else {
NewArg[Index++] = *Str;
}
break;
case '$':
/* If this is a start of a macro, pick it up */
if (Str[1] == '(') {
Str += 1;
ParseState->MacroParan += 1;
}
NewArg[Index++] = *Str;
break;
default:
if (!IsValidChar(*Str)) {
DEBUG((D_PARSE, "Invalid char %x in string\n", *Str));
if (ParseState->Output) {
Print (L"Invalid char %x in string\n", *Str);
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
NewArg[Index++] = *Str;
break;
}
/*
* Next char
*/
Str += 1;
}
/*
* Make sure the macro was terminated
*/
if (ParseState->MacroParan) {
DEBUG ((D_PARSE, "Too many '$(' parans\n"));
if (ParseState->Output) {
Print (L"Too many '$(' parans\n");
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
/*
* If the new argument string is empty and we have encountered a
* comment, then skip it. Otherwise we have a new arg
*/
if ( Comment && Index == 0 ) {
break;
} else {
NewArg[Index] = 0;
Alias = NULL;
}
/*
* If it was composed with a literal, do not check to see if the arg has an alias
*/
Alias = NULL;
if (!Literal && !ParseState->AliasLevel && ParseState->ArgIndex == 0) {
Alias = SEnvGetAlias(NewArg);
}
/*
* If there's an alias, parse it
*/
if (Alias) {
ParseState->AliasLevel += 1;
Status = ShellParseStr (Alias, ParseState);
ParseState->AliasLevel -= 1;
if (EFI_ERROR(Status)) {
goto Done;
}
} else {
/*
* Otherwise, copy the word to the arg array
*/
ParseState->Arg[ParseState->ArgIndex] = StrDuplicate(NewArg);
if (!ParseState->Arg[ParseState->ArgIndex]) {
Status = EFI_OUT_OF_RESOURCES;
break;
}
ParseState->ArgIndex += 1;
if (ParseState->ArgIndex >= MAX_ARG_COUNT-1) {
DEBUG ((D_PARSE, "Too many arguments: %d\n", ParseState->ArgIndex));
if (ParseState->Output) {
Print(L"Too many arguments: %d\n", ParseState->ArgIndex);
}
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
}
/*
* If last word ended with a comma, skip it to move to the next word
*/
if (*Str == ',') {
Str += 1;
}
}
Status = EFI_SUCCESS;
Done:
ParseState->RecurseLevel -= 1;
if (EFI_ERROR(Status)) {
/* Free all the args allocated */
for (Index=0; Index < ParseState->ArgIndex; Index++) {
if (ParseState->Arg[Index]) {
FreePool (ParseState->Arg[Index]);
ParseState->Arg[Index] = NULL;
}
}
ParseState->ArgIndex = 0;
}
return Status;
}
EFI_STATUS
SEnvRedirOutput (
IN OUT ENV_SHELL_INTERFACE *Shell,
IN BOOLEAN Ascii,
IN BOOLEAN Append,
IN OUT UINTN *NewArgc,
IN OUT UINTN *Index,
OUT ENV_SHELL_REDIR_FILE *Redir
)
{
CHAR16 *FileName;
EFI_STATUS Status;
EFI_FILE_INFO *Info;
UINTN Size;
CHAR16 UnicodeMarker = UNICODE_BYTE_ORDER_MARK;
UINT64 FileMode;
/*
* Update args
*/
if (!*NewArgc) {
*NewArgc = *Index;
}
*Index += 1;
if (*Index >= Shell->ShellInt.Argc) {
return EFI_INVALID_PARAMETER;
}
if (Redir->Handle) {
return EFI_INVALID_PARAMETER;
}
/*
* Open the output file
*/
Redir->Ascii = Ascii;
Redir->WriteError = EFI_SUCCESS;
FileName = Shell->ShellInt.Argv[*Index];
Redir->FilePath = SEnvNameToPath(FileName);
if (Redir->FilePath) {
FileMode = EFI_FILE_MODE_WRITE | ((Append)? 0 : EFI_FILE_MODE_CREATE);
Redir->File = ShellOpenFilePath(Redir->FilePath, FileMode);
if (Append && !Redir->File) {
/*
* If file does not exist make a new one. And send us down the other path
*/
FileMode |= EFI_FILE_MODE_CREATE;
Redir->File = ShellOpenFilePath(Redir->FilePath, FileMode);
Append = FALSE;
}
}
if (!Redir->File) {
Print(L"Could not open output file %hs\n", FileName);
return EFI_INVALID_PARAMETER;
}
Info = LibFileInfo (Redir->File);
ASSERT (Info);
if (Append) {
Size = sizeof(UnicodeMarker);
Redir->File->Read (Redir->File, &Size, &UnicodeMarker);
if ((UnicodeMarker == UNICODE_BYTE_ORDER_MARK) && Ascii) {
Print(L"Could not Append Ascii to Unicode file %hs\n", FileName);
return EFI_INVALID_PARAMETER;
} else if ((UnicodeMarker != UNICODE_BYTE_ORDER_MARK) && !Ascii) {
Print(L"Could not Append Unicode to Asci file %hs\n", FileName);
return EFI_INVALID_PARAMETER;
}
/*
* Seek to end of the file
*/
Redir->File->SetPosition (Redir->File, (UINT64)-1);
} else {
/*
* Truncate the file
*/
Info->FileSize = 0;
Size = SIZE_OF_EFI_FILE_INFO + StrSize(Info->FileName);
if (Redir->File->SetInfo) {
Redir->File->SetInfo (Redir->File, &GenericFileInfo, Size, Info);
} else {
DEBUG ((D_ERROR, "SEnvRedirOutput: SetInfo in filesystem driver not complete\n"));
}
FreePool (Info);
if (!Ascii) {
Size = sizeof(UnicodeMarker);
Redir->File->Write(Redir->File, &Size, &UnicodeMarker);
}
}
/*
* Allocate a new handle
*/
CopyMem(&Redir->Out, &SEnvConToIo, sizeof(SIMPLE_TEXT_OUTPUT_INTERFACE));
Status = LibInstallProtocolInterfaces (
&Redir->Handle,
&TextOutProtocol, &Redir->Out,
&DevicePathProtocol, Redir->FilePath,
NULL
);
Redir->Signature = ENV_REDIR_SIGNATURE;
ASSERT (!EFI_ERROR(Status));
return EFI_SUCCESS;
}
EFI_STATUS
SEnvExecRedir (
IN OUT ENV_SHELL_INTERFACE *Shell
)
{
UINTN NewArgc;
UINTN Index;
UINTN RedirIndex;
EFI_STATUS Status;
CHAR16 *p;
CHAR16 LastChar;
BOOLEAN Ascii;
BOOLEAN Append;
EFI_SYSTEM_TABLE *SysTable;
UINTN StringLen;
BOOLEAN RedirStdOut;
Status = EFI_SUCCESS;
NewArgc = 0;
SysTable = Shell->SystemTable;
for (Index=1; Index < Shell->ShellInt.Argc && !EFI_ERROR(Status); Index += 1) {
p = Shell->ShellInt.Argv[Index];
/*
* Trailing a or A means do ASCII default is unicode */
StringLen = StrLen(p);
LastChar = p[StringLen - 1];
Ascii = ((LastChar == 'a') || (LastChar == 'A'));
RedirStdOut = FALSE;
if (StrnCmp(p, L"2>", 2) == 0) {
Status = SEnvRedirOutput (Shell, Ascii, FALSE, &NewArgc, &Index, &Shell->StdErr);
SysTable->StdErr = &Shell->StdErr.Out;
SysTable->StandardErrorHandle = Shell->StdErr.Handle;
Shell->ShellInt.StdErr = Shell->StdErr.File;
} else if (StrnCmp(p, L"1>", 2) == 0) {
Append = (p[2] == '>');
RedirStdOut = TRUE;
} else if (*p == '>') {
Append = (p[1] == '>');
RedirStdOut = TRUE;
}
if (RedirStdOut) {
Status = SEnvRedirOutput (Shell, Ascii, Append, &NewArgc, &Index, &Shell->StdOut);
SysTable->ConOut = &Shell->StdOut.Out;
SysTable->ConsoleOutHandle = Shell->StdOut.Handle;
Shell->ShellInt.StdOut = Shell->StdOut.File;
}
}
/*
* Strip redirection args from arglist, saving in RedirArgv so they can be
* echoed in batch scripts.
*/
if (NewArgc) {
Shell->ShellInt.RedirArgc = Shell->ShellInt.Argc - (UINT32) NewArgc;
Shell->ShellInt.RedirArgv = AllocateZeroPool (Shell->ShellInt.RedirArgc * sizeof(CHAR16 *));
if ( !Shell->ShellInt.RedirArgv ) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
RedirIndex = 0;
for (Index = NewArgc; Index < Shell->ShellInt.Argc; Index += 1) {
Shell->ShellInt.RedirArgv[RedirIndex++] = Shell->ShellInt.Argv[Index];
Shell->ShellInt.Argv[Index] = NULL;
}
Shell->ShellInt.Argc = (UINT32) NewArgc;
} else {
Shell->ShellInt.RedirArgc = 0;
Shell->ShellInt.RedirArgv = NULL;
}
Done:
return Status;
}
VOID
SEnvCloseRedir (
IN OUT ENV_SHELL_REDIR_FILE *Redir
)
{
if (Redir->File) {
Redir->File->Close (Redir->File);
}
if (Redir->Handle) {
BS->UninstallProtocolInterface (Redir->Handle, &TextOutProtocol, &Redir->Out);
BS->UninstallProtocolInterface (Redir->Handle, &TextInProtocol, &Redir->In);
BS->UninstallProtocolInterface (Redir->Handle, &DevicePathProtocol, Redir->FilePath);
FreePool (Redir->FilePath);
}
}
EFI_STATUS
SEnvDoExecute (
IN EFI_HANDLE *ParentImageHandle,
IN CHAR16 *CommandLine,
IN ENV_SHELL_INTERFACE *Shell,
IN BOOLEAN Output
)
{
EFI_SHELL_INTERFACE *ParentShell;
EFI_SYSTEM_TABLE *ParentSystemTable;
EFI_STATUS Status;
UINTN Index;
SHELLENV_INTERNAL_COMMAND InternalCommand;
EFI_HANDLE NewImage;
EFI_FILE_HANDLE Script;
/*
* Switch output attribute to normal
*/
Print (L"%N");
/*
* Chck that there is something to do
*/
if (Shell->ShellInt.Argc < 1) {
goto Done;
}
/*
* Handle special case of the internal "set default device command"
* Is it one argument that ends with a ":"?
*/
Index = StrLen(Shell->ShellInt.Argv[0]);
if (Shell->ShellInt.Argc == 1 && Shell->ShellInt.Argv[0][Index-1] == ':') {
Status = SEnvSetCurrentDevice (Shell->ShellInt.Argv[0]);
goto Done;
}
/*
* Assume some defaults
*/
BS->HandleProtocol (ParentImageHandle, &LoadedImageProtocol, (VOID*)&Shell->ShellInt.Info);
Shell->ShellInt.ImageHandle = ParentImageHandle;
Shell->ShellInt.StdIn = &SEnvIOFromCon;
Shell->ShellInt.StdOut = &SEnvIOFromCon;
Shell->ShellInt.StdErr = &SEnvErrIOFromCon;
/*
* Get parent's image stdout & stdin
*/
Status = BS->HandleProtocol (ParentImageHandle, &ShellInterfaceProtocol, (VOID*)&ParentShell);
if (EFI_ERROR(Status)) {
goto Done;
}
ParentSystemTable = ParentShell->Info->SystemTable;
Shell->ShellInt.StdIn = ParentShell->StdIn;
Shell->ShellInt.StdOut = ParentShell->StdOut;
Shell->ShellInt.StdErr = ParentShell->StdErr;
Shell->SystemTable = NULL;
Status = BS->AllocatePool(EfiRuntimeServicesData,
sizeof(EFI_SYSTEM_TABLE),
(VOID **)&Shell->SystemTable);
if (EFI_ERROR(Status)) {
goto Done;
}
CopyMem (Shell->SystemTable, Shell->ShellInt.Info->SystemTable, sizeof(EFI_SYSTEM_TABLE));
Status = SEnvExecRedir (Shell);
SetCrc (&Shell->SystemTable->Hdr);
if (EFI_ERROR(Status)) {
goto Done;
}
/*
* Attempt to dispatch it as an internal command
*/
InternalCommand = SEnvGetCmdDispath(Shell->ShellInt.Argv[0]);
if (InternalCommand) {
/* Push & replace the current shell info on the parent image handle. (note we are using
* the parent image's loaded image information structure) */
BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, ParentShell, &Shell->ShellInt);
ParentShell->Info->SystemTable = Shell->SystemTable;
InitializeShellApplication (ParentImageHandle, Shell->SystemTable);
SEnvBatchEchoCommand( Shell );
/* Dispatch the command */
Status = InternalCommand (ParentImageHandle, Shell->ShellInt.Info->SystemTable);
/* Restore the parent's image handle shell info */
BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, &Shell->ShellInt, ParentShell);
ParentShell->Info->SystemTable = ParentSystemTable;
InitializeShellApplication (ParentImageHandle, ParentSystemTable);
goto Done;
}
/*
* Load the app, or open the script
*/
SEnvLoadImage(ParentImageHandle, Shell->ShellInt.Argv[0], &NewImage, &Script);
if (!NewImage && !Script) {
if ( Output ) {
Print(L"'%es' not found\n", Shell->ShellInt.Argv[0]);
}
Status = EFI_INVALID_PARAMETER;
goto Done;
}
if (NewImage) {
CHAR16 *CurrentDir;
CHAR16 *OptionsBuffer;
UINT32 OptionsSize;
/*
* Put the shell info on the handle
*/
BS->HandleProtocol (NewImage, &LoadedImageProtocol, (VOID*)&Shell->ShellInt.Info);
LibInstallProtocolInterfaces (&NewImage, &ShellInterfaceProtocol, &Shell->ShellInt, NULL);
/*
* Create load options which may include command line and current
* working directory
*/
CurrentDir = SEnvGetCurDir(NULL);
OptionsSize = (UINT32)StrSize(CommandLine); /* StrSize includes NULL */
if (CurrentDir)
OptionsSize += (UINT32)StrSize(CurrentDir); /* StrSize includes NULL */
OptionsBuffer = AllocateZeroPool (OptionsSize);
if (OptionsBuffer) {
/*
* Set the buffer before we manipulate it.
*/
Shell->ShellInt.Info->LoadOptions = OptionsBuffer;
Shell->ShellInt.Info->LoadOptionsSize = OptionsSize;
/*
* Copy the comamand line and current working directory
*/
StrCpy ((CHAR16*)OptionsBuffer, CommandLine);
if (CurrentDir)
StrCpy (&OptionsBuffer[ StrLen (CommandLine) + 1 ], CurrentDir);
} else {
Shell->ShellInt.Info->LoadOptions = CommandLine;
Shell->ShellInt.Info->LoadOptionsSize = (UINT32) StrSize(CommandLine);
}
/*
* Pass a copy of the system table with new input & outputs
*/
Shell->ShellInt.Info->SystemTable = Shell->SystemTable;
/*
* If the image is an app start it, else abort it
*/
if (Shell->ShellInt.Info->ImageCodeType == EfiLoaderCode) {
InitializeShellApplication (ParentImageHandle, Shell->SystemTable);
SEnvBatchEchoCommand( Shell );
Status = BS->StartImage (NewImage, 0, NULL);
} else {
Print (L"Image is not a application\n");
BS->Exit(NewImage, EFI_INVALID_PARAMETER, 0, NULL);
Status = EFI_INVALID_PARAMETER;
}
/*
* App has exited, remove our data from the image handle
*/
if (OptionsBuffer) {
BS->FreePool (OptionsBuffer);
}
BS->UninstallProtocolInterface(NewImage, &ShellInterfaceProtocol, &Shell->ShellInt);
InitializeShellApplication (ParentImageHandle, ParentSystemTable);
} else if ( Script ) {
SEnvBatchEchoCommand( Shell );
/* Push & replace the current shell info on the parent image handle. (note we are using
* the parent image's loaded image information structure) */
BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, ParentShell, &Shell->ShellInt);
ParentShell->Info->SystemTable = Shell->SystemTable;
Status = SEnvExecuteScript( Shell, Script );
/* Restore the parent's image handle shell info */
BS->ReinstallProtocolInterface (ParentImageHandle, &ShellInterfaceProtocol, &Shell->ShellInt, ParentShell);
ParentShell->Info->SystemTable = ParentSystemTable;
InitializeShellApplication (ParentImageHandle, ParentSystemTable);
}
Done:
SEnvBatchSetLastError( Status );
if (EFI_ERROR(Status) && Output) {
Print (L"Exit status code: %r\n", Status);
}
/*
* Cleanup
*/
if (Shell) {
/*
* Free copy of the system table
*/
if (Shell->SystemTable) {
BS->FreePool(Shell->SystemTable);
}
/*
* If there's an arg list, free it
*/
if (Shell->ShellInt.Argv) {
for (Index=0; Index < Shell->ShellInt.Argc; Index += 1) {
FreePool (Shell->ShellInt.Argv[Index]);
}
FreePool (Shell->ShellInt.Argv);
}
/*
* If any redirection arguments were saved, free them
*/
if (Shell->ShellInt.RedirArgv) {
for (Index=0; Index < Shell->ShellInt.RedirArgc; Index++ ) {
FreePool( Shell->ShellInt.RedirArgv[Index] );
}
FreePool( Shell->ShellInt.RedirArgv );
}
/*
* Close any file redirection
*/
SEnvCloseRedir(&Shell->StdOut);
SEnvCloseRedir(&Shell->StdErr);
SEnvCloseRedir(&Shell->StdIn);
}
/*
* Switch output attribute to normal
*/
Print (L"%N");
return Status;
}
EFI_STATUS
SEnvExecute (
IN EFI_HANDLE *ParentImageHandle,
IN CHAR16 *CommandLine,
IN BOOLEAN Output
)
{
ENV_SHELL_INTERFACE Shell;
EFI_STATUS Status = EFI_SUCCESS;
/*
* Convert the command line to an arg list
*/
ZeroMem( &Shell, sizeof(Shell ) );
Status = SEnvStringToArg( CommandLine, Output, &Shell.ShellInt.Argv, &Shell.ShellInt.Argc );
if (EFI_ERROR(Status)) {
goto Done;
}
/*
* Execute the command
*/
Status = SEnvDoExecute( ParentImageHandle, CommandLine, &Shell, Output );
if (EFI_ERROR(Status)) {
goto Done;
}
Done:
return Status;
}
VOID
INTERNAL
SEnvLoadImage (
IN EFI_HANDLE ParentImage,
IN CHAR16 *IName,
OUT EFI_HANDLE *pImageHandle,
OUT EFI_FILE_HANDLE *pScriptHandle
)
{
CHAR16 *Path;
CHAR16 *p1, *p2;
CHAR16 *PathName;
EFI_DEVICE_PATH *DevicePath;
FILEPATH_DEVICE_PATH *FilePath;
CHAR16 *FilePathStr;
CHAR16 c;
EFI_HANDLE ImageHandle;
EFI_STATUS Status;
SENV_OPEN_DIR *OpenDir, *OpenDirHead;
EFI_FILE_HANDLE ScriptHandle;
PathName = NULL;
DevicePath = NULL;
FilePathStr = NULL;
ImageHandle = NULL;
ScriptHandle = NULL;
OpenDirHead = NULL;
*pImageHandle = NULL;
*pScriptHandle = NULL;
/*
* Get the path variable
*/
Path = SEnvGetEnv (L"path");
if (!Path) {
DEBUG ((D_PARSE, "SEnvLoadImage: no path variable\n"));
return ;
}
p1 = StrDuplicate(Path);
Path = p1;
/*
* Search each path component
* (using simple ';' as separator here - oh well)
*/
c = *Path;
for (p1=Path; *p1 && c; p1=p2+1) {
for (p2=p1; *p2 && *p2 != ';'; p2++) ;
if (p1 != p2) {
c = *p2;
*p2 = 0; /* null terminate the path */
/*
* Open the directory
*/
DevicePath = SEnvNameToPath(p1);
if (!DevicePath) {
continue;
}
OpenDir = AllocateZeroPool (sizeof(SENV_OPEN_DIR));
if (!OpenDir) {
break;
}
OpenDir->Handle = ShellOpenFilePath(DevicePath, EFI_FILE_MODE_READ);
OpenDir->Next = OpenDirHead;
OpenDirHead = OpenDir;
FreePool (DevicePath);
DevicePath = NULL;
if (!OpenDir->Handle) {
continue;
}
/*
* Attempt to open it as an execuatble
*/
PathName = (p2[-1] == ':' || p2[-1] == '\\') ? L"%s%s.efi" : L"%s\\%s.efi";
PathName = PoolPrint(PathName, p1, IName);
if (!PathName) {
break;
}
DevicePath = SEnvNameToPath(PathName);
if (!DevicePath) {
continue;
}
/*
* Print the file path
*/
FilePathStr = DevicePathToStr(DevicePath);
/* DEBUG((D_PARSE, "SEnvLoadImage: load %hs\n", FilePathStr)); */
/*
* Attempt to load the image
*/
Status = BS->LoadImage (FALSE, ParentImage, DevicePath, NULL, 0, &ImageHandle);
if (!EFI_ERROR(Status)) {
goto Done;
}
/*
* Try as a ".nsh" file
*/
FreePool(DevicePath);
FreePool(PathName);
DevicePath = NULL;
PathName = NULL;
if ( StriCmp( L".nsh", &(IName[StrLen(IName)-4]) ) == 0 ) {
/* User entered entire filename with .nsh extension */
PathName = PoolPrint (L"%s", IName);
} else {
/* User entered filename without .nsh extension */
PathName = PoolPrint (L"%s.nsh", IName);
}
if (!PathName) {
break;
}
DevicePath = SEnvFileNameToPath(PathName);
if (DevicePath) {
ASSERT (
DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
DevicePathSubType(DevicePath) == MEDIA_FILEPATH_DP
);
FilePath = (FILEPATH_DEVICE_PATH *) DevicePath;
Status = OpenDir->Handle->Open (
OpenDir->Handle,
&ScriptHandle,
FilePath->PathName,
EFI_FILE_MODE_READ,
0
);
FreePool(DevicePath);
DevicePath = NULL;
if (!EFI_ERROR(Status)) {
goto Done;
}
}
ScriptHandle = NULL; /* BUGBUG */
}
if (DevicePath) {
FreePool (DevicePath);
DevicePath = NULL;
}
if (PathName) {
FreePool (PathName);
PathName = NULL;
}
if (FilePathStr) {
FreePool (FilePathStr);
FilePathStr = NULL;
}
}
Done:
while (OpenDirHead) {
if (OpenDirHead->Handle) {
OpenDirHead->Handle->Close (OpenDirHead->Handle);
}
OpenDir = OpenDirHead->Next;
FreePool (OpenDirHead);
OpenDirHead = OpenDir;
}
FreePool (Path);
if (DevicePath) {
FreePool (DevicePath);
DevicePath = NULL;
}
if (PathName) {
FreePool (PathName);
PathName = NULL;
}
if (FilePathStr) {
FreePool (FilePathStr);
FilePathStr = NULL;
}
if (ImageHandle) {
ASSERT (!ScriptHandle);
*pImageHandle = ImageHandle;
}
if (ScriptHandle) {
ASSERT (!ImageHandle);
*pScriptHandle = ScriptHandle;
}
}
EFI_STATUS
SEnvExit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
/* BUGBUG: for now just use a "magic" return code to indicate EOF */
return -1;
}