1244 lines
35 KiB
C
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;
|
|
}
|