1004 lines
24 KiB
C
1004 lines
24 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1999 Intel Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
batch.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Functions implementing batch scripting in the shell.
|
||
|
|
||
|
Revision History
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "shelle.h"
|
||
|
|
||
|
/*
|
||
|
* Constants
|
||
|
*/
|
||
|
|
||
|
#define ASCII_LF ((CHAR8)0x0a)
|
||
|
#define ASCII_CR ((CHAR8)0x0d)
|
||
|
#define UNICODE_LF ((CHAR16)0x000a)
|
||
|
#define UNICODE_CR ((CHAR16)0x000d)
|
||
|
|
||
|
/* Can hold 64-bit hex error numbers + null char */
|
||
|
#define LASTERROR_BUFSIZ (17)
|
||
|
|
||
|
/*
|
||
|
* Statics
|
||
|
* (needed to maintain state across multiple calls or for callbacks)
|
||
|
*/
|
||
|
STATIC UINTN NestLevel;
|
||
|
STATIC UINTN LastError;
|
||
|
STATIC CHAR16 LastErrorBuf[LASTERROR_BUFSIZ];
|
||
|
STATIC BOOLEAN Condition;
|
||
|
STATIC BOOLEAN GotoIsActive;
|
||
|
STATIC UINT64 GotoFilePos;
|
||
|
STATIC BOOLEAN BatchIsActive;
|
||
|
STATIC BOOLEAN EchoIsOn;
|
||
|
STATIC BOOLEAN BatchAbort;
|
||
|
STATIC SIMPLE_INPUT_INTERFACE *OrigConIn;
|
||
|
STATIC SIMPLE_TEXT_OUTPUT_INTERFACE *OrigConOut;
|
||
|
STATIC EFI_FILE_HANDLE CurrentBatchFile;
|
||
|
|
||
|
/*
|
||
|
* Definitions for the argument list stack
|
||
|
*
|
||
|
* In order to support nested scripts (script calling script calling script...)
|
||
|
* there is an argument list stack "BatchInfoStack". BatchInfoStack is a
|
||
|
* list of argument lists. Each argument list contains Argv[0] - Argv[n]
|
||
|
* for the corresponding script file. The head of BatchInfoStack corresponds
|
||
|
* to the currently active script file.
|
||
|
*
|
||
|
* This allows positional argument substitution to be done when each line
|
||
|
* is read and scanned, and calls to other script files can overwrite the
|
||
|
* shell interface's argument list.
|
||
|
*/
|
||
|
|
||
|
#define EFI_BATCH_INFO_SIGNATURE EFI_SIGNATURE_32('b','i','r','g')
|
||
|
typedef struct {
|
||
|
UINTN Signature;
|
||
|
LIST_ENTRY Link;
|
||
|
CHAR16 *ArgValue;
|
||
|
} EFI_SHELL_BATCH_INFO;
|
||
|
|
||
|
#define EFI_BATCH_INFOLIST_SIGNATURE EFI_SIGNATURE_32('b','l','s','t')
|
||
|
typedef struct {
|
||
|
UINTN Signature;
|
||
|
LIST_ENTRY Link;
|
||
|
LIST_ENTRY ArgListHead; /* Head of this argument list */
|
||
|
UINT64 FilePosition; /* Current file position */
|
||
|
} EFI_SHELL_BATCH_INFOLIST;
|
||
|
|
||
|
STATIC LIST_ENTRY BatchInfoStack;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Prototypes
|
||
|
*/
|
||
|
|
||
|
STATIC EFI_STATUS
|
||
|
BatchIsAscii(
|
||
|
IN EFI_FILE_HANDLE File,
|
||
|
OUT BOOLEAN *IsAscii
|
||
|
);
|
||
|
|
||
|
STATIC EFI_STATUS
|
||
|
BatchGetLine(
|
||
|
IN EFI_FILE_HANDLE File,
|
||
|
IN BOOLEAN Ascii,
|
||
|
IN OUT UINT64 *FilePosition,
|
||
|
IN OUT UINTN *BufSize,
|
||
|
OUT CHAR16 *CommandLine
|
||
|
);
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvInitBatch(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvInitBatch
|
||
|
|
||
|
Description:
|
||
|
Initializes global variables used for batch file processing.
|
||
|
--*/
|
||
|
{
|
||
|
NestLevel = 0;
|
||
|
LastError = EFI_SUCCESS;
|
||
|
ZeroMem( LastErrorBuf, LASTERROR_BUFSIZ );
|
||
|
Condition = TRUE;
|
||
|
GotoIsActive = FALSE;
|
||
|
GotoFilePos = (UINT64)0x00;
|
||
|
BatchIsActive = FALSE;
|
||
|
EchoIsOn = TRUE;
|
||
|
BatchAbort = FALSE;
|
||
|
OrigConIn = ST->ConIn;
|
||
|
OrigConOut = ST->ConOut;
|
||
|
InitializeListHead( &BatchInfoStack );
|
||
|
SEnvInitForLoopInfo();
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
SEnvBatchIsActive(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchIsActive
|
||
|
|
||
|
Description:
|
||
|
Returns whether any batch files are currently being processed.
|
||
|
--*/
|
||
|
{
|
||
|
/*
|
||
|
* BUGBUG should be able to return IsListEmpty( &BatchInfoStack );
|
||
|
* instead of using this variable
|
||
|
*/
|
||
|
return BatchIsActive;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvSetBatchAbort(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvSetBatchAbort
|
||
|
|
||
|
Description:
|
||
|
Sets a flag to notify the main batch processing loop to exit.
|
||
|
--*/
|
||
|
{
|
||
|
BatchAbort = TRUE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvBatchGetConsole(
|
||
|
OUT SIMPLE_INPUT_INTERFACE **ConIn,
|
||
|
OUT SIMPLE_TEXT_OUTPUT_INTERFACE **ConOut
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchGetConsole
|
||
|
|
||
|
Description:
|
||
|
Returns the Console I/O interface pointers.
|
||
|
--*/
|
||
|
{
|
||
|
*ConIn = OrigConIn;
|
||
|
*ConOut = OrigConOut;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
SEnvBatchEchoCommand(
|
||
|
IN ENV_SHELL_INTERFACE *Shell
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchEchoCommand
|
||
|
|
||
|
Description:
|
||
|
Echoes the given command to stdout.
|
||
|
--*/
|
||
|
{
|
||
|
UINTN i;
|
||
|
CHAR16 *BatchFileName;
|
||
|
EFI_STATUS Status;
|
||
|
|
||
|
/*
|
||
|
* Echo the parsed-and-expanded command to the console
|
||
|
*/
|
||
|
|
||
|
if ( SEnvBatchIsActive() && EchoIsOn ) {
|
||
|
|
||
|
BatchFileName = NULL;
|
||
|
Status = SEnvBatchGetArg( 0, &BatchFileName );
|
||
|
if ( EFI_ERROR(Status) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
Print( L"%E" );
|
||
|
for ( i=0; i<NestLevel; i++ ) {
|
||
|
Print( L"+" );
|
||
|
}
|
||
|
Print( L"%s> ", BatchFileName );
|
||
|
for ( i=0; i<Shell->ShellInt.Argc; i++ ) {
|
||
|
Print( L"%s ", Shell->ShellInt.Argv[i] );
|
||
|
}
|
||
|
for ( i=0; i<Shell->ShellInt.RedirArgc; i++ ) {
|
||
|
Print( L"%s ", Shell->ShellInt.RedirArgv[i] );
|
||
|
}
|
||
|
Print( L"\n" );
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
|
||
|
/*
|
||
|
* Switch output attribute to normal
|
||
|
*/
|
||
|
|
||
|
Print (L"%N");
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvBatchSetEcho(
|
||
|
IN BOOLEAN Val
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchSetEcho
|
||
|
|
||
|
Description:
|
||
|
Sets the echo flag to the specified value.
|
||
|
--*/
|
||
|
{
|
||
|
EchoIsOn = Val;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
SEnvBatchGetEcho(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchGetEcho
|
||
|
|
||
|
Description:
|
||
|
Returns the echo flag.
|
||
|
--*/
|
||
|
{
|
||
|
return EchoIsOn;
|
||
|
}
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
SEnvBatchSetFilePos(
|
||
|
IN UINT64 NewPos
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchSetFilePos
|
||
|
|
||
|
Description:
|
||
|
Sets the current script file position to the specified value.
|
||
|
--*/
|
||
|
{
|
||
|
EFI_STATUS Status = EFI_SUCCESS;
|
||
|
EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
|
||
|
|
||
|
Status = CurrentBatchFile->SetPosition( CurrentBatchFile, NewPos );
|
||
|
if ( EFI_ERROR(Status) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
if ( !IsListEmpty( &BatchInfoStack ) ) {
|
||
|
BatchInfo = CR( BatchInfoStack.Flink,
|
||
|
EFI_SHELL_BATCH_INFOLIST,
|
||
|
Link,
|
||
|
EFI_BATCH_INFOLIST_SIGNATURE );
|
||
|
}
|
||
|
if ( BatchInfo ) {
|
||
|
BatchInfo->FilePosition = NewPos;
|
||
|
} else {
|
||
|
Status = EFI_NOT_FOUND;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
SEnvBatchGetFilePos(
|
||
|
UINT64 *FilePos
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchGetFilePos
|
||
|
|
||
|
Description:
|
||
|
Returns the current script file position.
|
||
|
--*/
|
||
|
{
|
||
|
EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
|
||
|
EFI_STATUS Status = EFI_SUCCESS;
|
||
|
|
||
|
if ( !FilePos ) {
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
goto Done;
|
||
|
}
|
||
|
if ( !IsListEmpty( &BatchInfoStack ) ) {
|
||
|
BatchInfo = CR( BatchInfoStack.Flink,
|
||
|
EFI_SHELL_BATCH_INFOLIST,
|
||
|
Link,
|
||
|
EFI_BATCH_INFOLIST_SIGNATURE );
|
||
|
}
|
||
|
if ( BatchInfo ) {
|
||
|
*FilePos = BatchInfo->FilePosition;
|
||
|
} else {
|
||
|
Status = EFI_NOT_FOUND;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvBatchSetCondition(
|
||
|
IN BOOLEAN Val
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchSetCondition
|
||
|
|
||
|
Description:
|
||
|
Sets the condition flag to the specified value.
|
||
|
--*/
|
||
|
{
|
||
|
Condition = Val;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvBatchSetGotoActive(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchSetGotoActive
|
||
|
|
||
|
Description:
|
||
|
Sets the goto-is-active to TRUE and saves the current position
|
||
|
of the active script file.
|
||
|
--*/
|
||
|
{
|
||
|
GotoIsActive = TRUE;
|
||
|
SEnvBatchGetFilePos( &GotoFilePos );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
SEnvBatchVarIsLastError(
|
||
|
IN CHAR16 *Name
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchVarIsLastError
|
||
|
|
||
|
Description:
|
||
|
Checks to see if variable's name is "lasterror".
|
||
|
--*/
|
||
|
{
|
||
|
return (StriCmp( L"lasterror", Name ) == 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
SEnvBatchSetLastError(
|
||
|
IN UINTN NewLastError
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchSetLastError
|
||
|
|
||
|
Description:
|
||
|
Sets the lasterror variable's value to the given value.
|
||
|
--*/
|
||
|
{
|
||
|
LastError = NewLastError;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
CHAR16*
|
||
|
SEnvBatchGetLastError( VOID
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
SEnvBatchGetLastError
|
||
|
|
||
|
Description:
|
||
|
Returns a pointer to a string representation of the error value
|
||
|
returned by the last shell command.
|
||
|
--*/
|
||
|
{
|
||
|
ValueToHex( LastErrorBuf, (UINT64)LastError );
|
||
|
return LastErrorBuf;
|
||
|
}
|
||
|
|
||
|
|
||
|
STATIC EFI_STATUS
|
||
|
BatchIsAscii(
|
||
|
IN EFI_FILE_HANDLE File,
|
||
|
OUT BOOLEAN *IsAscii
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
BatchIsAscii
|
||
|
|
||
|
Description:
|
||
|
Checks to see if the specified batch file is ASCII.
|
||
|
--*/
|
||
|
{
|
||
|
EFI_STATUS Status=EFI_SUCCESS;
|
||
|
CHAR8 Buffer8[2]; /* UNICODE byte-order-mark is two bytes */
|
||
|
UINTN BufSize;
|
||
|
|
||
|
/*
|
||
|
* Read the first two bytes to check for byte order mark
|
||
|
*/
|
||
|
|
||
|
BufSize = sizeof(Buffer8);
|
||
|
Status = File->Read( File, &BufSize, Buffer8 );
|
||
|
if ( EFI_ERROR(Status) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
Status = File->SetPosition( File, (UINT64)0 );
|
||
|
if ( EFI_ERROR(Status) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we find a UNICODE byte order mark assume it is UNICODE,
|
||
|
* otherwise assume it is ASCII. UNICODE byte order mark on
|
||
|
* IA little endian is first byte 0xff and second byte 0xfe
|
||
|
*/
|
||
|
|
||
|
if ( (Buffer8[0] | (Buffer8[1] << 8)) == UNICODE_BYTE_ORDER_MARK ) {
|
||
|
*IsAscii = FALSE;
|
||
|
} else {
|
||
|
*IsAscii = TRUE;
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
STATIC EFI_STATUS
|
||
|
BatchGetLine(
|
||
|
IN EFI_FILE_HANDLE File,
|
||
|
IN BOOLEAN Ascii,
|
||
|
IN OUT UINT64 *FilePosition,
|
||
|
IN OUT UINTN *BufSize,
|
||
|
OUT CHAR16 *CommandLine
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
BatchGetLine
|
||
|
|
||
|
Description:
|
||
|
Reads the next line from the batch file, converting it from
|
||
|
ASCII to UNICODE if necessary. If end of file is encountered
|
||
|
then it returns 0 in the BufSize parameter.
|
||
|
--*/
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
CHAR8 Buffer8[MAX_CMDLINE];
|
||
|
CHAR16 Buffer16[MAX_CMDLINE];
|
||
|
UINTN i = 0;
|
||
|
UINTN CmdLenInChars = 0;
|
||
|
UINTN CmdLenInBytes = 0;
|
||
|
UINTN CharSize = 0;
|
||
|
|
||
|
/*
|
||
|
* Check params
|
||
|
*/
|
||
|
|
||
|
if ( !CommandLine || !BufSize || !FilePosition ) {
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Initialize OUT param
|
||
|
*/
|
||
|
ZeroMem( CommandLine, MAX_CMDLINE );
|
||
|
|
||
|
/*
|
||
|
* If beginning of UNICODE file, move past the Byte-Order-Mark (2 bytes)
|
||
|
*/
|
||
|
|
||
|
if ( !Ascii && *FilePosition == (UINT64)0 ) {
|
||
|
*FilePosition = (UINT64)2;
|
||
|
Status = File->SetPosition( File, *FilePosition );
|
||
|
if ( EFI_ERROR(Status) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* (1) Read a buffer-full from the file
|
||
|
* (2) Locate the end of the 1st line in the buffer
|
||
|
* ASCII version and UNICODE version
|
||
|
*/
|
||
|
|
||
|
if ( Ascii ) {
|
||
|
|
||
|
CharSize = sizeof(CHAR8);
|
||
|
Status = File->Read( File, BufSize, Buffer8 );
|
||
|
if ( EFI_ERROR(Status) || *BufSize == 0 ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
for ( i=0; i<*BufSize; i++ ) {
|
||
|
if ( Buffer8[i] == ASCII_LF ) {
|
||
|
CmdLenInChars = i;
|
||
|
CmdLenInBytes = CmdLenInChars;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else { /* UNICODE */
|
||
|
|
||
|
CharSize = sizeof(CHAR16);
|
||
|
Status = File->Read( File, BufSize, Buffer16 );
|
||
|
if ( EFI_ERROR(Status) || *BufSize == 0 ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
for ( i=0; i < *BufSize/CharSize; i++ ) {
|
||
|
if ( Buffer16[i] == UNICODE_LF ) {
|
||
|
CmdLenInChars = i;
|
||
|
CmdLenInBytes = CmdLenInChars * CharSize;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Reset the file position to just after the command line
|
||
|
*/
|
||
|
*FilePosition += (UINT64)(CmdLenInBytes + CharSize);
|
||
|
Status = File->SetPosition( File, *FilePosition );
|
||
|
|
||
|
/*
|
||
|
* Copy, converting chars to UNICODE if necessary
|
||
|
*/
|
||
|
if ( Ascii ) {
|
||
|
for ( i=0; i<CmdLenInChars; i++ ) {
|
||
|
CommandLine[i] = (CHAR16)Buffer8[i];
|
||
|
}
|
||
|
} else {
|
||
|
CopyMem( CommandLine, Buffer16, CmdLenInBytes );
|
||
|
}
|
||
|
CmdLenInChars = i;
|
||
|
|
||
|
Done:
|
||
|
*BufSize = CmdLenInChars * CharSize;
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
SEnvBatchGetArg(
|
||
|
IN UINTN Argno,
|
||
|
OUT CHAR16 **Argval
|
||
|
)
|
||
|
/*++
|
||
|
Function Name:
|
||
|
BatchGetArg
|
||
|
|
||
|
Description:
|
||
|
Extract the specified element from the arglist at the top of the arglist
|
||
|
stack. Return a pointer to the value field of the "Argno"th element of
|
||
|
the list.
|
||
|
--*/
|
||
|
{
|
||
|
EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
|
||
|
LIST_ENTRY *Link = NULL;
|
||
|
EFI_SHELL_BATCH_INFO *ArgEntry = NULL;
|
||
|
UINTN i = 0;
|
||
|
|
||
|
if ( !IsListEmpty( &BatchInfoStack ) ) {
|
||
|
BatchInfo = CR( BatchInfoStack.Flink,
|
||
|
EFI_SHELL_BATCH_INFOLIST,
|
||
|
Link,
|
||
|
EFI_BATCH_INFOLIST_SIGNATURE );
|
||
|
}
|
||
|
if ( !IsListEmpty( &BatchInfo->ArgListHead ) ) {
|
||
|
for ( Link=BatchInfo->ArgListHead.Flink;
|
||
|
Link!=&BatchInfo->ArgListHead;
|
||
|
Link=Link->Flink) {
|
||
|
ArgEntry = CR( Link,
|
||
|
EFI_SHELL_BATCH_INFO,
|
||
|
Link,
|
||
|
EFI_BATCH_INFO_SIGNATURE);
|
||
|
if ( i++ == Argno ) {
|
||
|
*Argval = ArgEntry->ArgValue;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*Argval = NULL;
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
|
||
|
EFI_STATUS
|
||
|
SEnvExecuteScript(
|
||
|
IN ENV_SHELL_INTERFACE *Shell,
|
||
|
IN EFI_FILE_HANDLE File
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Function Name:
|
||
|
SEnvExecuteScript
|
||
|
|
||
|
Description:
|
||
|
Execute the commands in the script file specified by the
|
||
|
file parameter.
|
||
|
|
||
|
Arguments:
|
||
|
Shell: shell interface of the caller
|
||
|
File: file handle to open script file
|
||
|
|
||
|
Returns:
|
||
|
EFI_STATUS
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
EFI_FILE_INFO *FileInfo;
|
||
|
UINTN FileNameLen = 0;
|
||
|
BOOLEAN EndOfFile = FALSE;
|
||
|
EFI_STATUS Status = EFI_SUCCESS;
|
||
|
UINTN BufSize = 0;
|
||
|
UINTN FileInfoSize = 0;
|
||
|
CHAR16 CommandLine[MAX_CMDLINE];
|
||
|
EFI_SHELL_BATCH_INFOLIST *BatchInfo = NULL;
|
||
|
EFI_SHELL_BATCH_INFO *ArgEntry = NULL;
|
||
|
UINTN i = 0;
|
||
|
BOOLEAN Output = TRUE;
|
||
|
ENV_SHELL_INTERFACE NewShell;
|
||
|
UINTN GotoTargetStatus;
|
||
|
UINTN SkippedIfCount;
|
||
|
|
||
|
/*
|
||
|
* Initialize
|
||
|
*/
|
||
|
BatchIsActive = TRUE;
|
||
|
Status = EFI_SUCCESS;
|
||
|
NestLevel++;
|
||
|
SEnvInitTargetLabel();
|
||
|
|
||
|
/*
|
||
|
* Check params
|
||
|
*/
|
||
|
|
||
|
if ( !File ) {
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Figure out if the file is ASCII or UNICODE.
|
||
|
*/
|
||
|
Status = BatchIsAscii( File, &Shell->StdIn.Ascii );
|
||
|
if ( EFI_ERROR( Status ) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the filename from the file handle.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Allocate buffer for file info (including file name)
|
||
|
* BUGBUG 1024 arbitrary space for filename, as elsewhere in shell
|
||
|
*/
|
||
|
FileInfoSize = SIZE_OF_EFI_FILE_INFO + 1024;
|
||
|
FileInfo = AllocatePool(FileInfoSize);
|
||
|
if (!FileInfo) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/* Get file info */
|
||
|
Status = File->GetInfo( File,
|
||
|
&GenericFileInfo,
|
||
|
&FileInfoSize,
|
||
|
FileInfo );
|
||
|
if ( EFI_ERROR(Status) ) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Save the handle
|
||
|
*/
|
||
|
CurrentBatchFile = File;
|
||
|
|
||
|
/*
|
||
|
* Initialize argument list for this script
|
||
|
* This list is needed since nested batch files would overwrite the
|
||
|
* argument list in Shell->ShellInt.Argv[]. Here we maintain the args in
|
||
|
* a local list on the stack.
|
||
|
*/
|
||
|
|
||
|
BatchInfo = AllocateZeroPool( sizeof( EFI_SHELL_BATCH_INFOLIST ) );
|
||
|
if ( !BatchInfo ) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto Done;
|
||
|
}
|
||
|
BatchInfo->Signature = EFI_BATCH_INFOLIST_SIGNATURE;
|
||
|
|
||
|
BatchInfo->FilePosition = (UINT64)0x00;
|
||
|
|
||
|
InitializeListHead( &BatchInfo->ArgListHead );
|
||
|
for ( i=0; i<Shell->ShellInt.Argc; i++ ) {
|
||
|
|
||
|
/* Allocate the new element of the argument list */
|
||
|
ArgEntry = AllocateZeroPool( sizeof( EFI_SHELL_BATCH_INFO ) );
|
||
|
if ( !ArgEntry ) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/* Allocate space for the argument string in the arglist element */
|
||
|
ArgEntry->ArgValue = AllocateZeroPool(StrSize(Shell->ShellInt.Argv[i]));
|
||
|
if ( !ArgEntry->ArgValue ) {
|
||
|
Status = EFI_OUT_OF_RESOURCES;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/* Copy in the argument string */
|
||
|
StrCpy( ArgEntry->ArgValue, Shell->ShellInt.Argv[i] );
|
||
|
ArgEntry->Signature = EFI_BATCH_INFO_SIGNATURE;
|
||
|
|
||
|
/* Add the arglist element to the end of the list */
|
||
|
InsertTailList( &BatchInfo->ArgListHead, &ArgEntry->Link );
|
||
|
}
|
||
|
|
||
|
/* Push the arglist onto the arglist stack */
|
||
|
InsertHeadList( &BatchInfoStack, &BatchInfo->Link );
|
||
|
|
||
|
/*
|
||
|
* Iterate through the file, reading a line at a time and executing each
|
||
|
* line as a shell command. Nested shell scripts will come through
|
||
|
* this code path recursively.
|
||
|
*/
|
||
|
EndOfFile = FALSE;
|
||
|
SkippedIfCount = 0;
|
||
|
while (1) {
|
||
|
|
||
|
/*
|
||
|
* Read a command line from the file
|
||
|
*/
|
||
|
BufSize = MAX_CMDLINE;
|
||
|
Status = BatchGetLine( File,
|
||
|
Shell->StdIn.Ascii,
|
||
|
&BatchInfo->FilePosition,
|
||
|
&BufSize,
|
||
|
CommandLine );
|
||
|
if ( EFI_ERROR( Status ) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* No error and no chars means EOF
|
||
|
* If we are in the middle of a GOTO then rewind to search for the
|
||
|
* label from the beginning of the file, otherwise we are done
|
||
|
* with this script.
|
||
|
*/
|
||
|
|
||
|
if ( BufSize == 0 ) {
|
||
|
if ( GotoIsActive ) {
|
||
|
BatchInfo->FilePosition = (UINT64)(0x00);
|
||
|
Status = File->SetPosition( File, BatchInfo->FilePosition );
|
||
|
if ( EFI_ERROR( Status ) ) {
|
||
|
goto Done;
|
||
|
} else {
|
||
|
continue;
|
||
|
}
|
||
|
} else {
|
||
|
goto Done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Convert the command line to an arg list
|
||
|
*/
|
||
|
|
||
|
ZeroMem( &NewShell, sizeof(NewShell ) );
|
||
|
Status = SEnvStringToArg(
|
||
|
CommandLine,
|
||
|
TRUE,
|
||
|
&NewShell.ShellInt.Argv,
|
||
|
&NewShell.ShellInt.Argc
|
||
|
);
|
||
|
if (EFI_ERROR(Status)) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Skip comments and blank lines
|
||
|
*/
|
||
|
|
||
|
if ( NewShell.ShellInt.Argc == 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If a GOTO command is active, skip everything until we find
|
||
|
* the target label or until we determine it doesn't exist.
|
||
|
*/
|
||
|
|
||
|
if ( GotoIsActive ) {
|
||
|
/*
|
||
|
* Check if we have the right label or if we've searched
|
||
|
* the whole file
|
||
|
*/
|
||
|
Status = SEnvCheckForGotoTarget( NewShell.ShellInt.Argv[0],
|
||
|
GotoFilePos,
|
||
|
BatchInfo->FilePosition,
|
||
|
&GotoTargetStatus );
|
||
|
if ( EFI_ERROR( Status ) ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
switch ( GotoTargetStatus ) {
|
||
|
case GOTO_TARGET_FOUND:
|
||
|
GotoIsActive = FALSE;
|
||
|
SEnvFreeTargetLabel();
|
||
|
continue;
|
||
|
case GOTO_TARGET_NOT_FOUND:
|
||
|
continue;
|
||
|
case GOTO_TARGET_DOESNT_EXIST:
|
||
|
GotoIsActive = FALSE;
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
LastError = Status;
|
||
|
SEnvPrintLabelNotFound();
|
||
|
SEnvFreeTargetLabel();
|
||
|
continue;
|
||
|
default:
|
||
|
Status = EFI_INVALID_PARAMETER;
|
||
|
SEnvFreeTargetLabel();
|
||
|
Print( L"Internal error: invalid GotoTargetStatus\n" );
|
||
|
break;
|
||
|
}
|
||
|
} else if ( NewShell.ShellInt.Argv[0][0] == L':' ) {
|
||
|
/*
|
||
|
* Skip labels when no GOTO is active
|
||
|
*/
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Skip everything between an 'if' whose condition was false and its
|
||
|
* matching 'endif'. Note that 'endif' doesn't do anything if
|
||
|
* Condition is TRUE, so we only track endif matching when it is false.
|
||
|
*/
|
||
|
|
||
|
if ( !Condition ) {
|
||
|
if ( StriCmp( NewShell.ShellInt.Argv[0], L"if") == 0 ) {
|
||
|
/*
|
||
|
* Keep track of how many endifs we have to skip before we are
|
||
|
* done with the FALSE Condition
|
||
|
*/
|
||
|
SkippedIfCount += 1;
|
||
|
continue;
|
||
|
} else if ( StriCmp( NewShell.ShellInt.Argv[0], L"endif") == 0 ) {
|
||
|
if ( SkippedIfCount > 0 ) {
|
||
|
SkippedIfCount -= 1;
|
||
|
continue;
|
||
|
}
|
||
|
/*
|
||
|
* When SkippedIfCount goes to zero (as here), we have the
|
||
|
* endif that matches the if with the FALSE condition that
|
||
|
* we are dealing with, so we want to fall through and have
|
||
|
* the endif command reset the Condition flag.
|
||
|
*/
|
||
|
} else {
|
||
|
/*
|
||
|
* Condition FALSE, not an if or an endif, so skip
|
||
|
*/
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Execute the command
|
||
|
*/
|
||
|
LastError = SEnvDoExecute(
|
||
|
Shell->ShellInt.ImageHandle,
|
||
|
CommandLine,
|
||
|
&NewShell,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
/*
|
||
|
* Save the current file handle
|
||
|
*/
|
||
|
CurrentBatchFile = File;
|
||
|
|
||
|
if ( BatchAbort ) {
|
||
|
goto Done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
/*
|
||
|
* Clean up
|
||
|
*/
|
||
|
|
||
|
/* Decrement the count of open script files */
|
||
|
NestLevel--;
|
||
|
|
||
|
/* Free any potential remaining GOTO target label */
|
||
|
SEnvFreeTargetLabel();
|
||
|
|
||
|
/* Reset the IF condition to TRUE, even if no ENDIF was found */
|
||
|
SEnvBatchSetCondition( TRUE );
|
||
|
|
||
|
/* Close the script file */
|
||
|
if ( File ) {
|
||
|
File->Close( File );
|
||
|
}
|
||
|
|
||
|
/* Free the file info structure used to get the file name from the handle */
|
||
|
if ( FileInfo ) {
|
||
|
FreePool( FileInfo );
|
||
|
FileInfo = NULL;
|
||
|
}
|
||
|
|
||
|
/* Pop the argument list for this script off the stack */
|
||
|
if ( !IsListEmpty( &BatchInfoStack ) ) {
|
||
|
BatchInfo = CR( BatchInfoStack.Flink,
|
||
|
EFI_SHELL_BATCH_INFOLIST,
|
||
|
Link,
|
||
|
EFI_BATCH_INFOLIST_SIGNATURE );
|
||
|
RemoveEntryList( &BatchInfo->Link );
|
||
|
}
|
||
|
|
||
|
/* Free the argument list for this script file */
|
||
|
while ( !IsListEmpty( &BatchInfo->ArgListHead ) ) {
|
||
|
ArgEntry = CR( BatchInfo->ArgListHead.Flink,
|
||
|
EFI_SHELL_BATCH_INFO,
|
||
|
Link,
|
||
|
EFI_BATCH_INFO_SIGNATURE );
|
||
|
if ( ArgEntry ) {
|
||
|
RemoveEntryList( &ArgEntry->Link );
|
||
|
if ( ArgEntry->ArgValue ) {
|
||
|
FreePool( ArgEntry->ArgValue );
|
||
|
ArgEntry->ArgValue = NULL;
|
||
|
}
|
||
|
FreePool( ArgEntry );
|
||
|
ArgEntry = NULL;
|
||
|
}
|
||
|
}
|
||
|
FreePool( BatchInfo );
|
||
|
BatchInfo = NULL;
|
||
|
|
||
|
/*
|
||
|
* If we are returning to the interactive shell, then reset
|
||
|
* the batch-is-active flag
|
||
|
*/
|
||
|
if ( IsListEmpty( &BatchInfoStack ) ) {
|
||
|
BatchIsActive = FALSE;
|
||
|
BatchAbort = FALSE;
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|