mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-07 05:23:20 +00:00
1010 lines
25 KiB
Nim
Executable File
1010 lines
25 KiB
Nim
Executable File
#
|
|
#
|
|
# Nimrod's Runtime Library
|
|
# (c) Copyright 2006 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
|
|
interface
|
|
|
|
type
|
|
TProcess = opaque
|
|
|
|
proc
|
|
open(out p: TProcess, command, workingDir: string,
|
|
|
|
implementation
|
|
|
|
|
|
Uses Classes,
|
|
pipes,
|
|
SysUtils;
|
|
|
|
Type
|
|
TProcessOption = (poRunSuspended,poWaitOnExit,
|
|
poUsePipes,poStderrToOutPut,
|
|
poNoConsole,poNewConsole,
|
|
poDefaultErrorMode,poNewProcessGroup,
|
|
poDebugProcess,poDebugOnlyThisProcess);
|
|
|
|
TShowWindowOptions = (swoNone,swoHIDE,swoMaximize,swoMinimize,swoRestore,swoShow,
|
|
swoShowDefault,swoShowMaximized,swoShowMinimized,
|
|
swoshowMinNOActive,swoShowNA,swoShowNoActivate,swoShowNormal);
|
|
|
|
TStartupOption = (suoUseShowWindow,suoUseSize,suoUsePosition,
|
|
suoUseCountChars,suoUseFillAttribute);
|
|
|
|
TProcessPriority = (ppHigh,ppIdle,ppNormal,ppRealTime);
|
|
|
|
TProcessOptions = Set of TPRocessOption;
|
|
TstartUpoptions = set of TStartupOption;
|
|
|
|
|
|
Type
|
|
TProcess = Class (TComponent)
|
|
Private
|
|
FProcessOptions : TProcessOptions;
|
|
FStartupOptions : TStartupOptions;
|
|
FProcessID : Integer;
|
|
FThreadID : Integer;
|
|
FProcessHandle : Thandle;
|
|
FThreadHandle : Thandle;
|
|
FFillAttribute : Cardinal;
|
|
FApplicationName : string;
|
|
FConsoleTitle : String;
|
|
FCommandLine : String;
|
|
FCurrentDirectory : String;
|
|
FDeskTop : String;
|
|
FEnvironment : Tstrings;
|
|
FExitCode : Cardinal;
|
|
FShowWindow : TShowWindowOptions;
|
|
FInherithandles : Boolean;
|
|
FInputSTream : TOutputPipeStream;
|
|
FOutputStream : TInPutPipeStream;
|
|
FStdErrStream : TInputPipeStream;
|
|
FRunning : Boolean;
|
|
FPRocessPriority : TProcessPriority;
|
|
dwXCountchars,
|
|
dwXSize,
|
|
dwYsize,
|
|
dwx,
|
|
dwYcountChars,
|
|
dwy : Cardinal;
|
|
Procedure FreeStreams;
|
|
Function GetExitStatus : Integer;
|
|
Function GetRunning : Boolean;
|
|
Function GetWindowRect : TRect;
|
|
Procedure SetWindowRect (Value : TRect);
|
|
Procedure SetShowWindow (Value : TShowWindowOptions);
|
|
Procedure SetWindowColumns (Value : Cardinal);
|
|
Procedure SetWindowHeight (Value : Cardinal);
|
|
Procedure SetWindowLeft (Value : Cardinal);
|
|
Procedure SetWindowRows (Value : Cardinal);
|
|
Procedure SetWindowTop (Value : Cardinal);
|
|
Procedure SetWindowWidth (Value : Cardinal);
|
|
Procedure CreateStreams(InHandle,OutHandle,Errhandle : Longint);
|
|
procedure SetApplicationname(const Value: String);
|
|
procedure SetProcessOptions(const Value: TProcessOptions);
|
|
procedure SetActive(const Value: Boolean);
|
|
procedure SetEnvironment(const Value: TStrings);
|
|
function PeekExitStatus: Boolean;
|
|
procedure CloseProcessHandles;
|
|
Public
|
|
Constructor Create (AOwner : TComponent);override;
|
|
Destructor Destroy; override;
|
|
Procedure Execute; virtual;
|
|
Function Resume : Integer; virtual;
|
|
Function Suspend : Integer; virtual;
|
|
Function Terminate (AExitCode : Integer): Boolean; virtual;
|
|
Function WaitOnExit : DWord;
|
|
Property WindowRect : Trect Read GetWindowRect Write SetWindowRect;
|
|
Property Handle : THandle Read FProcessHandle;
|
|
Property ProcessHandle : THandle Read FProcessHandle;
|
|
Property ThreadHandle : THandle Read FThreadHandle;
|
|
Property ProcessID : Integer Read FProcessID;
|
|
Property ThreadID : Integer Read FThreadID;
|
|
Property Input : TOutPutPipeStream Read FInPutStream;
|
|
Property OutPut : TInputPipeStream Read FOutPutStream;
|
|
Property StdErr : TinputPipeStream Read FStdErrStream;
|
|
Property ExitStatus : Integer Read GetExitStatus;
|
|
Property InheritHandles : Boolean Read FInheritHandles Write FInheritHandles;
|
|
Published
|
|
Property Active : Boolean Read Getrunning Write SetActive;
|
|
Property ApplicationName : String Read FApplicationname Write SetApplicationname;
|
|
Property CommandLine : String Read FCommandLine Write FCommandLine;
|
|
Property ConsoleTitle : String Read FConsoleTitle Write FConsoleTitle;
|
|
Property CurrentDirectory : String Read FCurrentDirectory Write FCurrentDirectory;
|
|
Property DeskTop : String Read FDeskTop Write FDeskTop;
|
|
Property Environment : TStrings Read FEnvironment Write SetEnvironment;
|
|
Property Options : TProcessOptions Read FProcessOptions Write SetPRocessOptions;
|
|
Property Priority : TProcessPriority Read FProcessPriority Write FProcessPriority;
|
|
Property StartUpOptions : TStartUpOptions Read FStartUpOptions Write FStartupOptions;
|
|
Property Running : Boolean Read GetRunning;
|
|
Property ShowWindow : TShowWindowOptions Read FShowWindow Write SetShowWindow;
|
|
Property WindowColumns : Cardinal Read dwXCountchars Write SetWindowColumns;
|
|
Property WindowHeight : Cardinal Read dwYsize Write SetWindowHeight;
|
|
Property WindowLeft : Cardinal Read dwx Write SetWindowLeft;
|
|
Property WindowRows : Cardinal Read dwYcountChars Write SetWindowRows;
|
|
Property WindowTop : Cardinal Read dwy Write SetWindowTop ;
|
|
Property WindowWidth : Cardinal Read dwXsize Write SetWindowWidth;
|
|
Property FillAttribute : Cardinal read FFillAttribute Write FFillAttribute;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{
|
|
Win32 Process .inc.
|
|
}
|
|
|
|
uses Windows;
|
|
|
|
Const
|
|
PriorityConstants : Array [TProcessPriority] of Cardinal =
|
|
(HIGH_PRIORITY_CLASS,IDLE_PRIORITY_CLASS,
|
|
NORMAL_PRIORITY_CLASS,REALTIME_PRIORITY_CLASS);
|
|
|
|
procedure TProcess.CloseProcessHandles;
|
|
begin
|
|
if (FProcessHandle<>0) then
|
|
CloseHandle(FProcessHandle);
|
|
if (FThreadHandle<>0) then
|
|
CloseHandle(FThreadHandle);
|
|
end;
|
|
|
|
Function TProcess.PeekExitStatus : Boolean;
|
|
|
|
begin
|
|
GetExitCodeProcess(ProcessHandle,FExitCode);
|
|
Result:=(FExitCode<>Still_Active);
|
|
end;
|
|
|
|
Function GetStartupFlags (P : TProcess): Cardinal;
|
|
|
|
begin
|
|
With P do
|
|
begin
|
|
Result:=0;
|
|
if poUsePipes in FProcessOptions then
|
|
Result:=Result or Startf_UseStdHandles;
|
|
if suoUseShowWindow in FStartupOptions then
|
|
Result:=Result or startf_USESHOWWINDOW;
|
|
if suoUSESIZE in FStartupOptions then
|
|
Result:=Result or startf_usesize;
|
|
if suoUsePosition in FStartupOptions then
|
|
Result:=Result or startf_USEPOSITION;
|
|
if suoUSECOUNTCHARS in FStartupoptions then
|
|
Result:=Result or startf_usecountchars;
|
|
if suoUsefIllAttribute in FStartupOptions then
|
|
Result:=Result or startf_USEFILLATTRIBUTE;
|
|
end;
|
|
end;
|
|
|
|
Function GetCreationFlags(P : TProcess) : Cardinal;
|
|
|
|
begin
|
|
With P do
|
|
begin
|
|
Result:=0;
|
|
if poNoConsole in FProcessOptions then
|
|
Result:=Result or Detached_Process;
|
|
if poNewConsole in FProcessOptions then
|
|
Result:=Result or Create_new_console;
|
|
if poNewProcessGroup in FProcessOptions then
|
|
Result:=Result or CREATE_NEW_PROCESS_GROUP;
|
|
If poRunSuspended in FProcessOptions Then
|
|
Result:=Result or Create_Suspended;
|
|
if poDebugProcess in FProcessOptions Then
|
|
Result:=Result or DEBUG_PROCESS;
|
|
if poDebugOnlyThisProcess in FProcessOptions Then
|
|
Result:=Result or DEBUG_ONLY_THIS_PROCESS;
|
|
if poDefaultErrorMode in FProcessOptions Then
|
|
Result:=Result or CREATE_DEFAULT_ERROR_MODE;
|
|
result:=result or PriorityConstants[FProcessPriority];
|
|
end;
|
|
end;
|
|
|
|
Function StringsToPChars(List : TStrings): pointer;
|
|
|
|
var
|
|
EnvBlock: string;
|
|
I: Integer;
|
|
|
|
begin
|
|
EnvBlock := '';
|
|
For I:=0 to List.Count-1 do
|
|
EnvBlock := EnvBlock + List[i] + #0;
|
|
EnvBlock := EnvBlock + #0;
|
|
GetMem(Result, Length(EnvBlock));
|
|
CopyMemory(Result, @EnvBlock[1], Length(EnvBlock));
|
|
end;
|
|
|
|
Procedure InitProcessAttributes(P : TProcess; Var PA : TSecurityAttributes);
|
|
|
|
begin
|
|
FillChar(PA,SizeOf(PA),0);
|
|
PA.nLength := SizeOf(PA);
|
|
end;
|
|
|
|
Procedure InitThreadAttributes(P : TProcess; Var TA : TSecurityAttributes);
|
|
|
|
begin
|
|
FillChar(TA,SizeOf(TA),0);
|
|
TA.nLength := SizeOf(TA);
|
|
end;
|
|
|
|
Procedure InitStartupInfo(P : TProcess; Var SI : STARTUPINFO);
|
|
|
|
Const
|
|
SWC : Array [TShowWindowOptions] of Cardinal =
|
|
(0,SW_HIDE,SW_Maximize,SW_Minimize,SW_Restore,SW_Show,
|
|
SW_ShowDefault,SW_ShowMaximized,SW_ShowMinimized,
|
|
SW_showMinNOActive,SW_ShowNA,SW_ShowNoActivate,SW_ShowNormal);
|
|
|
|
begin
|
|
FillChar(SI,SizeOf(SI),0);
|
|
With SI do
|
|
begin
|
|
dwFlags:=GetStartupFlags(P);
|
|
if P.FShowWindow<>swoNone then
|
|
dwFlags:=dwFlags or Startf_UseShowWindow
|
|
else
|
|
dwFlags:=dwFlags and not Startf_UseShowWindow;
|
|
wShowWindow:=SWC[P.FShowWindow];
|
|
if (poUsePipes in P.Options) then
|
|
begin
|
|
dwFlags:=dwFlags or Startf_UseStdHandles;
|
|
end;
|
|
if P.FillAttribute<>0 then
|
|
begin
|
|
dwFlags:=dwFlags or Startf_UseFillAttribute;
|
|
dwFillAttribute:=P.FillAttribute;
|
|
end;
|
|
dwXCountChars:=P.WindowColumns;
|
|
dwYCountChars:=P.WindowRows;
|
|
dwYsize:=P.WindowHeight;
|
|
dwXsize:=P.WindowWidth;
|
|
dwy:=P.WindowTop;
|
|
dwX:=P.WindowLeft;
|
|
end;
|
|
end;
|
|
|
|
Procedure CreatePipes(Var HI,HO,HE : Thandle; Var SI : TStartupInfo; CE : Boolean);
|
|
|
|
Procedure DoCreatePipeHandles(Var H1,H2 : THandle);
|
|
|
|
Var
|
|
I,O : Longint;
|
|
|
|
begin
|
|
CreatePipeHandles(I,O);
|
|
H1:=Thandle(I);
|
|
H2:=THandle(O);
|
|
end;
|
|
|
|
|
|
|
|
|
|
begin
|
|
DoCreatePipeHandles(SI.hStdInput,HI);
|
|
DoCreatePipeHandles(HO,Si.hStdOutput);
|
|
if CE then
|
|
DoCreatePipeHandles(HE,SI.hStdError)
|
|
else
|
|
begin
|
|
SI.hStdError:=SI.hStdOutput;
|
|
HE:=HO;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TProcess.Execute;
|
|
|
|
|
|
Var
|
|
PName,PDir,PCommandLine : PChar;
|
|
FEnv: pointer;
|
|
FCreationFlags : Cardinal;
|
|
FProcessAttributes : TSecurityAttributes;
|
|
FThreadAttributes : TSecurityAttributes;
|
|
FProcessInformation : TProcessInformation;
|
|
FStartupInfo : STARTUPINFO;
|
|
HI,HO,HE : THandle;
|
|
|
|
begin
|
|
FInheritHandles:=True;
|
|
PName:=Nil;
|
|
PCommandLine:=Nil;
|
|
PDir:=Nil;
|
|
If FApplicationName<>'' then
|
|
PName:=Pchar(FApplicationName);
|
|
If FCommandLine<>'' then
|
|
PCommandLine:=Pchar(FCommandLine);
|
|
If FCurrentDirectory<>'' then
|
|
PDir:=Pchar(FCurrentDirectory);
|
|
if FEnvironment.Count<>0 then
|
|
FEnv:=StringsToPChars(FEnvironment)
|
|
else
|
|
FEnv:=Nil;
|
|
Try
|
|
FCreationFlags:=GetCreationFlags(Self);
|
|
InitProcessAttributes(Self,FProcessAttributes);
|
|
InitThreadAttributes(Self,FThreadAttributes);
|
|
InitStartupInfo(Self,FStartUpInfo);
|
|
If poUsePipes in FProcessOptions then
|
|
CreatePipes(HI,HO,HE,FStartupInfo,Not(poStdErrToOutPut in FProcessOptions));
|
|
Try
|
|
If Not CreateProcess (PName,PCommandLine,@FProcessAttributes,@FThreadAttributes,
|
|
FInheritHandles,FCreationFlags,FEnv,PDir,FStartupInfo,
|
|
fProcessInformation) then
|
|
Raise Exception.CreateFmt('Failed to execute %s : %d',[FCommandLine,GetLastError]);
|
|
FProcessHandle:=FProcessInformation.hProcess;
|
|
FThreadHandle:=FProcessInformation.hThread;
|
|
FProcessID:=FProcessINformation.dwProcessID;
|
|
Finally
|
|
if POUsePipes in FProcessOptions then
|
|
begin
|
|
FileClose(FStartupInfo.hStdInput);
|
|
FileClose(FStartupInfo.hStdOutput);
|
|
if Not (poStdErrToOutPut in FProcessOptions) then
|
|
FileClose(FStartupInfo.hStdError);
|
|
CreateStreams(HI,HO,HE);
|
|
end;
|
|
end;
|
|
FRunning:=True;
|
|
Finally
|
|
If FEnv<>Nil then
|
|
FreeMem(FEnv);
|
|
end;
|
|
if not (csDesigning in ComponentState) and // This would hang the IDE !
|
|
(poWaitOnExit in FProcessOptions) and
|
|
not (poRunSuspended in FProcessOptions) then
|
|
WaitOnExit;
|
|
end;
|
|
|
|
Function TProcess.WaitOnExit : Dword;
|
|
|
|
begin
|
|
Result:=WaitForSingleObject (FProcessHandle,Infinite);
|
|
If Result<>Wait_Failed then
|
|
GetExitStatus;
|
|
FRunning:=False;
|
|
end;
|
|
|
|
Function TProcess.Suspend : Longint;
|
|
|
|
begin
|
|
Result:=SuspendThread(ThreadHandle);
|
|
end;
|
|
|
|
Function TProcess.Resume : LongInt;
|
|
|
|
begin
|
|
Result:=ResumeThread(ThreadHandle);
|
|
end;
|
|
|
|
Function TProcess.Terminate(AExitCode : Integer) : Boolean;
|
|
|
|
begin
|
|
Result:=False;
|
|
If ExitStatus=Still_active then
|
|
Result:=TerminateProcess(Handle,AexitCode);
|
|
end;
|
|
|
|
Procedure TProcess.SetShowWindow (Value : TShowWindowOptions);
|
|
|
|
|
|
begin
|
|
FShowWindow:=Value;
|
|
end;
|
|
|
|
// ---------------------------- end of platform dependant code --------------
|
|
|
|
{
|
|
Unix Process .inc.
|
|
}
|
|
|
|
uses
|
|
Unix,
|
|
Baseunix;
|
|
|
|
|
|
|
|
Const
|
|
PriorityConstants : Array [TProcessPriority] of Integer =
|
|
(20,20,0,-20);
|
|
|
|
Const
|
|
GeometryOption : String = '-geometry';
|
|
TitleOption : String ='-title';
|
|
|
|
|
|
|
|
procedure TProcess.CloseProcessHandles;
|
|
|
|
begin
|
|
// Do nothing. Win32 call.
|
|
end;
|
|
|
|
Function TProcess.PeekExitStatus : Boolean;
|
|
|
|
begin
|
|
Result:=fpWaitPid(Handle,@FExitCode,WNOHANG)=Handle;
|
|
If Result then
|
|
FExitCode:=wexitstatus(FExitCode)
|
|
else
|
|
FexitCode:=0;
|
|
end;
|
|
|
|
Type
|
|
TPCharArray = Array[Word] of pchar;
|
|
PPCharArray = ^TPcharArray;
|
|
|
|
Function StringsToPCharList(List : TStrings) : PPChar;
|
|
|
|
Var
|
|
I : Integer;
|
|
S : String;
|
|
|
|
begin
|
|
I:=(List.Count)+1;
|
|
GetMem(Result,I*sizeOf(PChar));
|
|
PPCharArray(Result)^[List.Count]:=Nil;
|
|
For I:=0 to List.Count-1 do
|
|
begin
|
|
S:=List[i];
|
|
Result[i]:=StrNew(PChar(S));
|
|
end;
|
|
end;
|
|
|
|
Procedure FreePCharList(List : PPChar);
|
|
|
|
Var
|
|
I : integer;
|
|
|
|
begin
|
|
I:=0;
|
|
While List[i]<>Nil do
|
|
begin
|
|
StrDispose(List[i]);
|
|
Inc(I);
|
|
end;
|
|
FreeMem(List);
|
|
end;
|
|
|
|
|
|
Procedure CommandToList(S : String; List : TStrings);
|
|
|
|
Function GetNextWord : String;
|
|
|
|
Const
|
|
WhiteSpace = [' ',#8,#10];
|
|
Literals = ['"',''''];
|
|
|
|
Var
|
|
Wstart,wend : Integer;
|
|
InLiteral : Boolean;
|
|
LastLiteral : char;
|
|
|
|
begin
|
|
WStart:=1;
|
|
While (WStart<=Length(S)) and (S[WStart] in WhiteSpace) do
|
|
Inc(WStart);
|
|
WEnd:=WStart;
|
|
InLiteral:=False;
|
|
LastLiteral:=#0;
|
|
While (Wend<=Length(S)) and (Not (S[Wend] in WhiteSpace) or InLiteral) do
|
|
begin
|
|
if S[Wend] in Literals then
|
|
If InLiteral then
|
|
InLiteral:=Not (S[Wend]=LastLiteral)
|
|
else
|
|
begin
|
|
InLiteral:=True;
|
|
LastLiteral:=S[Wend];
|
|
end;
|
|
inc(wend);
|
|
end;
|
|
Result:=Copy(S,WStart,WEnd-WStart);
|
|
Result:=StringReplace(Result,'"','',[rfReplaceAll]);
|
|
Result:=StringReplace(Result,'''','',[rfReplaceAll]);
|
|
While (WEnd<=Length(S)) and (S[Wend] in WhiteSpace) do
|
|
inc(Wend);
|
|
Delete(S,1,WEnd-1);
|
|
|
|
end;
|
|
|
|
Var
|
|
W : String;
|
|
|
|
begin
|
|
While Length(S)>0 do
|
|
begin
|
|
W:=GetNextWord;
|
|
If (W<>'') then
|
|
List.Add(W);
|
|
end;
|
|
end;
|
|
|
|
|
|
Function MakeCommand(P : TProcess) : PPchar;
|
|
|
|
Const
|
|
SNoCommandLine = 'Cannot execute empty command-line';
|
|
|
|
Var
|
|
Cmd : String;
|
|
S : TStringList;
|
|
G : String;
|
|
|
|
begin
|
|
if (P.ApplicationName='') then
|
|
begin
|
|
If (P.CommandLine='') then
|
|
Raise Exception.Create(SNoCommandline);
|
|
Cmd:=P.CommandLine;
|
|
end
|
|
else
|
|
begin
|
|
If (P.CommandLine='') then
|
|
Cmd:=P.ApplicationName
|
|
else
|
|
Cmd:=P.CommandLine;
|
|
end;
|
|
S:=TStringList.Create;
|
|
try
|
|
CommandToList(Cmd,S);
|
|
if poNewConsole in P.Options then
|
|
begin
|
|
S.Insert(0,'-e');
|
|
If (P.ApplicationName<>'') then
|
|
begin
|
|
S.Insert(0,P.ApplicationName);
|
|
S.Insert(0,'-title');
|
|
end;
|
|
if suoUseCountChars in P.StartupOptions then
|
|
begin
|
|
S.Insert(0,Format('%dx%d',[P.dwXCountChars,P.dwYCountChars]));
|
|
S.Insert(0,'-geometry');
|
|
end;
|
|
S.Insert(0,'xterm');
|
|
end;
|
|
if (P.ApplicationName<>'') then
|
|
begin
|
|
S.Add(TitleOption);
|
|
S.Add(P.ApplicationName);
|
|
end;
|
|
G:='';
|
|
if (suoUseSize in P.StartupOptions) then
|
|
g:=format('%dx%d',[P.dwXSize,P.dwYsize]);
|
|
if (suoUsePosition in P.StartupOptions) then
|
|
g:=g+Format('+%d+%d',[P.dwX,P.dwY]);
|
|
if G<>'' then
|
|
begin
|
|
S.Add(GeometryOption);
|
|
S.Add(g);
|
|
end;
|
|
Result:=StringsToPcharList(S);
|
|
Finally
|
|
S.free;
|
|
end;
|
|
end;
|
|
|
|
Function GetLastError : Integer;
|
|
|
|
begin
|
|
Result:=-1;
|
|
end;
|
|
|
|
Type
|
|
TPipeEnd = (peRead,peWrite);
|
|
TPipePair = Array[TPipeEnd] of Integer;
|
|
|
|
Procedure CreatePipes(Var HI,HO,HE : TPipePair; CE : Boolean);
|
|
|
|
Procedure CreatePair(Var P : TPipePair);
|
|
|
|
begin
|
|
If not CreatePipeHandles(P[peRead],P[peWrite]) then
|
|
Raise Exception.Create('Failed to create pipes');
|
|
end;
|
|
|
|
Procedure ClosePair(Var P : TPipePair);
|
|
|
|
begin
|
|
if (P[peRead]<>-1) then
|
|
FileClose(P[peRead]);
|
|
if (P[peWrite]<>-1) then
|
|
FileClose(P[peWrite]);
|
|
end;
|
|
|
|
begin
|
|
HO[peRead]:=-1;HO[peWrite]:=-1;
|
|
HI[peRead]:=-1;HI[peWrite]:=-1;
|
|
HE[peRead]:=-1;HE[peWrite]:=-1;
|
|
Try
|
|
CreatePair(HO);
|
|
CreatePair(HI);
|
|
If CE then
|
|
CreatePair(HE);
|
|
except
|
|
ClosePair(HO);
|
|
ClosePair(HI);
|
|
If CE then
|
|
ClosePair(HE);
|
|
Raise;
|
|
end;
|
|
end;
|
|
|
|
Procedure TProcess.Execute;
|
|
|
|
Var
|
|
HI,HO,HE : TPipePair;
|
|
PID : Longint;
|
|
FEnv : PPChar;
|
|
Argv : PPChar;
|
|
fd : Integer;
|
|
PName : String;
|
|
|
|
begin
|
|
If (poUsePipes in FProcessOptions) then
|
|
CreatePipes(HI,HO,HE,Not (poStdErrToOutPut in FProcessOptions));
|
|
Try
|
|
if FEnvironment.Count<>0 then
|
|
FEnv:=StringsToPcharList(FEnvironment)
|
|
else
|
|
FEnv:=Nil;
|
|
Try
|
|
Argv:=MakeCommand(Self);
|
|
Try
|
|
If (Argv<>Nil) and (ArgV[0]<>Nil) then
|
|
PName:=StrPas(Argv[0])
|
|
else
|
|
begin
|
|
// This should never happen, actually.
|
|
PName:=ApplicationName;
|
|
If (PName='') then
|
|
PName:=CommandLine;
|
|
end;
|
|
if (pos('/',PName)<>1) then
|
|
PName:=FileSearch(Pname,fpgetenv('PATH'));
|
|
Pid:=fpfork;
|
|
if Pid<0 then
|
|
Raise Exception.Create('Failed to Fork process');
|
|
if (PID>0) then
|
|
begin
|
|
// Parent process. Copy process information.
|
|
FProcessHandle:=PID;
|
|
FThreadHandle:=PID;
|
|
FProcessId:=PID;
|
|
//FThreadId:=PID;
|
|
end
|
|
else
|
|
begin
|
|
{ We're in the child }
|
|
if (FCurrentDirectory<>'') then
|
|
ChDir(FCurrentDirectory);
|
|
if PoUsePipes in Options then
|
|
begin
|
|
fpdup2(HI[peRead],0);
|
|
fpdup2(HO[peWrite],1);
|
|
if (poStdErrToOutPut in Options) then
|
|
fpdup2(HO[peWrite],2)
|
|
else
|
|
fpdup2(HE[peWrite],2);
|
|
end
|
|
else if poNoConsole in Options then
|
|
begin
|
|
fd:=FileOpen('/dev/null',fmOpenReadWrite);
|
|
fpdup2(fd,0);
|
|
fpdup2(fd,1);
|
|
fpdup2(fd,2);
|
|
end;
|
|
if (poRunSuspended in Options) then
|
|
sigraise(SIGSTOP);
|
|
if FEnv<>Nil then
|
|
fpexecve(PName,Argv,Fenv)
|
|
else
|
|
fpexecv(PName,argv);
|
|
Halt(127);
|
|
end
|
|
Finally
|
|
FreePcharList(Argv);
|
|
end;
|
|
Finally
|
|
If (FEnv<>Nil) then
|
|
FreePCharList(FEnv);
|
|
end;
|
|
Finally
|
|
if POUsePipes in FProcessOptions then
|
|
begin
|
|
FileClose(HO[peWrite]);
|
|
FileClose(HI[peRead]);
|
|
if Not (poStdErrToOutPut in FProcessOptions) then
|
|
FileClose(HE[peWrite]);
|
|
CreateStreams(HI[peWrite],HO[peRead],HE[peRead]);
|
|
end;
|
|
end;
|
|
FRunning:=True;
|
|
if not (csDesigning in ComponentState) and // This would hang the IDE !
|
|
(poWaitOnExit in FProcessOptions) and
|
|
not (poRunSuspended in FProcessOptions) then
|
|
WaitOnExit;
|
|
end;
|
|
|
|
Function TProcess.WaitOnExit : Dword;
|
|
|
|
begin
|
|
Result:=fpWaitPid(Handle,@FExitCode,0);
|
|
If Result=Handle then
|
|
FExitCode:=WexitStatus(FExitCode);
|
|
FRunning:=False;
|
|
end;
|
|
|
|
Function TProcess.Suspend : Longint;
|
|
|
|
begin
|
|
If fpkill(Handle,SIGSTOP)<>0 then
|
|
Result:=-1
|
|
else
|
|
Result:=1;
|
|
end;
|
|
|
|
Function TProcess.Resume : LongInt;
|
|
|
|
begin
|
|
If fpKill(Handle,SIGCONT)<>0 then
|
|
Result:=-1
|
|
else
|
|
Result:=0;
|
|
end;
|
|
|
|
Function TProcess.Terminate(AExitCode : Integer) : Boolean;
|
|
|
|
begin
|
|
Result:=False;
|
|
Result:=fpkill(Handle,SIGTERM)=0;
|
|
If Result then
|
|
begin
|
|
If Running then
|
|
Result:=fpkill(Handle,SIGKILL)=0;
|
|
end;
|
|
GetExitStatus;
|
|
end;
|
|
|
|
Procedure TProcess.SetShowWindow (Value : TShowWindowOptions);
|
|
|
|
begin
|
|
FShowWindow:=Value;
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
Constructor TProcess.Create (AOwner : TComponent);
|
|
begin
|
|
Inherited;
|
|
FProcessPriority:=ppNormal;
|
|
FShowWindow:=swoNone;
|
|
FInheritHandles:=True;
|
|
FEnvironment:=TStringList.Create;
|
|
end;
|
|
|
|
Destructor TProcess.Destroy;
|
|
|
|
begin
|
|
FEnvironment.Free;
|
|
FreeStreams;
|
|
CloseProcessHandles;
|
|
Inherited Destroy;
|
|
end;
|
|
|
|
Procedure TProcess.FreeStreams;
|
|
|
|
procedure FreeStream(var S: THandleStream);
|
|
|
|
begin
|
|
if (S<>Nil) then
|
|
begin
|
|
FileClose(S.Handle);
|
|
FreeAndNil(S);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
If FStdErrStream<>FOutputStream then
|
|
FreeStream(FStdErrStream);
|
|
FreeStream(FOutputStream);
|
|
FreeStream(FInputStream);
|
|
end;
|
|
|
|
|
|
Function TProcess.GetExitStatus : Integer;
|
|
|
|
begin
|
|
If FRunning then
|
|
PeekExitStatus;
|
|
Result:=FExitCode;
|
|
end;
|
|
|
|
|
|
Function TProcess.GetRunning : Boolean;
|
|
|
|
begin
|
|
IF FRunning then
|
|
FRunning:=Not PeekExitStatus;
|
|
Result:=FRunning;
|
|
end;
|
|
|
|
|
|
Procedure TProcess.CreateStreams(InHandle,OutHandle,Errhandle : Longint);
|
|
|
|
begin
|
|
FreeStreams;
|
|
FInputStream:=TOutputPipeStream.Create (InHandle);
|
|
FOutputStream:=TInputPipeStream.Create (OutHandle);
|
|
if Not (poStdErrToOutPut in FProcessOptions) then
|
|
FStdErrStream:=TInputPipeStream.Create(ErrHandle);
|
|
end;
|
|
|
|
|
|
Procedure TProcess.SetWindowColumns (Value : Cardinal);
|
|
|
|
begin
|
|
if Value<>0 then
|
|
Include(FStartUpOptions,suoUseCountChars);
|
|
dwXCountChars:=Value;
|
|
end;
|
|
|
|
|
|
Procedure TProcess.SetWindowHeight (Value : Cardinal);
|
|
|
|
begin
|
|
if Value<>0 then
|
|
include(FStartUpOptions,suoUsePosition);
|
|
dwYSize:=Value;
|
|
end;
|
|
|
|
Procedure TProcess.SetWindowLeft (Value : Cardinal);
|
|
|
|
begin
|
|
if Value<>0 then
|
|
Include(FStartUpOptions,suoUseSize);
|
|
dwx:=Value;
|
|
end;
|
|
|
|
Procedure TProcess.SetWindowTop (Value : Cardinal);
|
|
|
|
begin
|
|
if Value<>0 then
|
|
Include(FStartUpOptions,suoUsePosition);
|
|
dwy:=Value;
|
|
end;
|
|
|
|
Procedure TProcess.SetWindowWidth (Value : Cardinal);
|
|
begin
|
|
If (Value<>0) then
|
|
Include(FStartUpOptions,suoUseSize);
|
|
dwXSize:=Value;
|
|
end;
|
|
|
|
Function TProcess.GetWindowRect : TRect;
|
|
begin
|
|
With Result do
|
|
begin
|
|
Left:=dwx;
|
|
Right:=dwx+dwxSize;
|
|
Top:=dwy;
|
|
Bottom:=dwy+dwysize;
|
|
end;
|
|
end;
|
|
|
|
Procedure TProcess.SetWindowRect (Value : Trect);
|
|
begin
|
|
Include(FStartupOptions,suouseSize);
|
|
Include(FStartupOptions,suoUsePosition);
|
|
With Value do
|
|
begin
|
|
dwx:=Left;
|
|
dwxSize:=Right-Left;
|
|
dwy:=Top;
|
|
dwySize:=Bottom-top;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure TProcess.SetWindowRows (Value : Cardinal);
|
|
|
|
begin
|
|
if Value<>0 then
|
|
Include(FStartUpOptions,suoUseCountChars);
|
|
dwYCountChars:=Value;
|
|
end;
|
|
|
|
procedure TProcess.SetApplicationname(const Value: String);
|
|
begin
|
|
FApplicationname := Value;
|
|
If (csdesigning in ComponentState) and
|
|
(FCommandLine='') then
|
|
FCommandLine:=Value;
|
|
end;
|
|
|
|
procedure TProcess.SetProcessOptions(const Value: TProcessOptions);
|
|
begin
|
|
FProcessOptions := Value;
|
|
If poNewConsole in FPRocessOptions then
|
|
Exclude(FProcessoptions,poNoConsole);
|
|
if poRunSuspended in FProcessOptions then
|
|
Exclude(FPRocessoptions,poWaitOnExit);
|
|
end;
|
|
|
|
procedure TProcess.SetActive(const Value: Boolean);
|
|
begin
|
|
if (Value<>GetRunning) then
|
|
If Value then
|
|
Execute
|
|
else
|
|
Terminate(0);
|
|
end;
|
|
|
|
procedure TProcess.SetEnvironment(const Value: TStrings);
|
|
begin
|
|
FEnvironment.Assign(Value);
|
|
end;
|
|
|
|
function CallProcess(const command: string): string;
|
|
const
|
|
READ_BYTES = 2048;
|
|
// executes the command and returns the program's output
|
|
var
|
|
M: TMemoryStream;
|
|
P: TProcess;
|
|
n: LongInt;
|
|
BytesRead: LongInt;
|
|
begin
|
|
// We cannot use poWaitOnExit here since we don't
|
|
// know the size of the output. On Linux the size of the
|
|
// output pipe is 2 kB. If the output data is more, we
|
|
// need to read the data. This isn't possible since we are
|
|
// waiting. So we get a deadlock here.
|
|
//
|
|
// A temp Memorystream is used to buffer the output
|
|
|
|
M := TMemoryStream.Create;
|
|
BytesRead := 0;
|
|
|
|
P := TProcess.Create(nil);
|
|
P.CommandLine := Command;
|
|
P.Options := [poUsePipes];
|
|
P.Execute;
|
|
while P.Running do begin
|
|
// make sure we have room
|
|
M.SetSize(BytesRead + READ_BYTES);
|
|
|
|
// try reading it
|
|
n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
|
|
if n > 0 then
|
|
Inc(BytesRead, n)
|
|
else
|
|
// no data, wait 100 ms
|
|
Sleep(100)
|
|
end;
|
|
// read last part
|
|
repeat
|
|
// make sure we have room
|
|
M.SetSize(BytesRead + READ_BYTES);
|
|
// try reading it
|
|
n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
|
|
if n > 0 then Inc(BytesRead, n)
|
|
until n <= 0;
|
|
M.SetSize(BytesRead);
|
|
|
|
setLength(result, bytesRead);
|
|
m.read(result[1], bytesRead);
|
|
P.Free; M.Free;
|
|
end;
|
|
|
|
end.
|