/*++ 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; }