doublecmd/src/un_process.pas
2019-03-02 09:05:27 +00:00

187 lines
4.8 KiB
ObjectPascal

unit un_process;
{$mode delphi}{$H+}
interface
uses
Process, SysUtils, DCProcessUtf8;
type
TOnReadLn = procedure (str: String) of object;
TOnOperationProgress = procedure of object;
{ TExProcess }
TExProcess = class
protected
FProcess: TProcess;
FOutputLine: String;
FStop: Boolean;
FQueryString: String;
FOnReadLn,
FOnQueryString: TOnReadLn;
FOnProcessExit: TOnOperationProgress;
FOnOperationProgress: TOnOperationProgress;
function _GetExitStatus(): Integer;
public
constructor Create(CommandLine: String = '');
procedure Execute;
procedure Stop;
procedure SetCmdLine(CommandLine: String);
destructor Destroy; override;
property Process: TProcess read FProcess;
property ExitStatus: Integer read _GetExitStatus;
property QueryString: String read FQueryString write FQueryString;
property OnReadLn: TOnReadLn read FOnReadLn write FOnReadLn;
property OnQueryString: TOnReadLn read FOnQueryString write FOnQueryString;
property OnProcessExit: TOnOperationProgress read FOnProcessExit write FOnProcessExit;
property OnOperationProgress: TOnOperationProgress read FOnOperationProgress write FOnOperationProgress;
end;
implementation
const
BufferSize = 3000;
function GetNextLine(const Value: String; var S: String; var N: Integer): Boolean;
var
PS: PChar;
IP, L, P, K: Integer;
begin
P:= N;
S:= '';
Result:= False;
L:= Length(Value);
if ((L - P) < 0) then Exit;
if ((L - P) = 0) and (not (Value[P] in [#10, #13])) then Exit;
PS:= PChar(Value) + P - 1;
IP:= P;
while ((L - P) >= 0) and (not (PS^ in [#10, #13])) do
begin
P:= P + 1;
Inc(PS);
end;
K:= P;
// Point to character after #13
if (P <= L) and (Value[P] = #13) then
begin
Inc(P);
Result:= True;
end;
// Point to character after #10
if (P <= L) and (Value[P] = #10) then
begin
Inc(P);
Result:= True;
end;
if Result then
begin
N:= P;
SetLength(S, K - IP);
System.Move(Value[IP], Pointer(S)^, K - IP);
end;
end;
{ TExProcess }
function TExProcess._GetExitStatus(): Integer;
begin
Result:= FProcess.ExitStatus;
end;
constructor TExProcess.Create(CommandLine: String = '');
begin
FOutputLine:= EmptyStr;
FProcess:= TProcessUtf8.Create(nil);
FProcess.CommandLine:= CommandLine;
FProcess.Options:= [poUsePipes, poNoConsole, poNewProcessGroup];
end;
procedure TExProcess.Execute;
var
P: Integer;
S, OutputBuffer: String;
begin
S:= EmptyStr;
FProcess.Execute;
try
repeat
if Assigned(FOnOperationProgress) then
FOnOperationProgress();
if FStop then Exit;
// If no output yet
if FProcess.Output.NumBytesAvailable = 0 then
begin
if not FProcess.Running then
Break
else
begin
Sleep(1);
if Assigned(FOnQueryString) and (FProcess.Stderr.NumBytesAvailable > 0) then
begin
SetLength(OutputBuffer, BufferSize);
// Waits for the process output
SetLength(OutputBuffer, FProcess.Stderr.Read(OutputBuffer[1], Length(OutputBuffer)));
if (Pos(FQueryString, OutputBuffer) > 0) then FOnQueryString(OutputBuffer);
OutputBuffer:= EmptyStr;
end;
Continue;
end
end;
SetLength(OutputBuffer, BufferSize);
// Waits for the process output
SetLength(OutputBuffer, FProcess.Output.Read(OutputBuffer[1], Length(OutputBuffer)));
// Cut the incoming stream to lines:
FOutputLine:= FOutputLine + OutputBuffer; // Add to the accumulator
P:= 1;
while GetNextLine(FOutputLine, S, P) do
begin
if FStop then Exit;
// Return the line without the CR/LF characters
if Assigned(FOnReadLn) then FOnReadLn(S);
// Update progress
if Assigned(FOnOperationProgress) then FOnOperationProgress();
end;
// Remove the processed lines from accumulator
Delete(FOutputLine, 1, P - 1);
// Check query string
if Length(FOutputLine) > 0 then
begin
if Assigned(FOnQueryString) and (Pos(FQueryString, FOutputLine) <> 0) then
begin
FOnQueryString(FOutputLine);
FOutputLine:= EmptyStr;
end;
end;
// No more data, break
if Length(OutputBuffer) = 0 then Break;
until False;
if FStop then Exit;
if (Length(FOutputLine) <> 0) and Assigned(FOnReadLn) then
FOnReadLn(FOutputLine);
OutputBuffer:= EmptyStr;
finally
if Assigned(FOnProcessExit) then
FOnProcessExit();
end;
end;
procedure TExProcess.Stop;
begin
FStop:= FProcess.Terminate(-1);
end;
procedure TExProcess.SetCmdLine(CommandLine: String);
begin
FProcess.CommandLine:= CommandLine;
end;
destructor TExProcess.Destroy;
begin
FreeAndNil(FProcess);
end;
end.