mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
183 lines
4.5 KiB
ObjectPascal
183 lines
4.5 KiB
ObjectPascal
unit un_process;
|
|
|
|
{$mode delphi}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Process, SysUtils;
|
|
|
|
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;
|
|
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 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:= TProcess.Create(nil);
|
|
FProcess.CommandLine:= CommandLine;
|
|
FProcess.Options:= [poUsePipes, poNoConsole];
|
|
end;
|
|
|
|
procedure TExProcess.Execute;
|
|
var
|
|
P: Integer;
|
|
S, OutputBuffer: String;
|
|
begin
|
|
S:= EmptyStr;
|
|
FProcess.Execute;
|
|
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;
|
|
if Assigned(FOnReadLn) then
|
|
FOnReadLn(OutputBuffer); // Empty line to notify DC about process finish
|
|
end;
|
|
|
|
procedure TExProcess.Stop;
|
|
begin
|
|
FStop:= True;
|
|
FProcess.Terminate(-1);
|
|
end;
|
|
|
|
procedure TExProcess.SetCmdLine(CommandLine: String);
|
|
begin
|
|
FProcess.CommandLine:= CommandLine;
|
|
end;
|
|
|
|
destructor TExProcess.Destroy;
|
|
begin
|
|
FreeAndNil(FProcess);
|
|
end;
|
|
|
|
end.
|