1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
|
{
Copyright 1998-2018 PasDoc developers.
This file is part of "PasDoc".
"PasDoc" is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"PasDoc" is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with "PasDoc"; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
----------------------------------------------------------------------------
}
{ @abstract(Talking with another process through pipes.)
@author(Michalis Kamburelis)
@author(Arno Garrels <first name.name@nospamgmx.de>)}
unit PasDoc_ProcessLineTalk;
{$I pasdoc_defines.inc}
{$ifdef FPC}
{$ifdef UNIX}
{$define HAS_PROCESS}
{$endif}
{$ifdef MSWINDOWS}
{$define HAS_PROCESS}
{$endif}
{$endif}
interface
uses SysUtils, Classes {$ifdef HAS_PROCESS} , Process {$endif};
type
{ TTextReader reads given Stream line by line.
Lines may be terminated in Stream with #13, #10, #13+#10 or #10+#13.
This way I can treat any TStream quite like standard Pascal text files:
I have simple Readln method.
After calling Readln or Eof you should STOP directly using underlying
Stream (but you CAN use Stream right after creating
TTextReader.Create(Stream) and before any Readln or Eof
operations on this TTextReader).
Original version of this class comes from Michalis Kamburelis
code library, see [http://www.camelot.homedns.org/~michalis/],
unit base/KambiClassUtils.pas. }
TTextReader = class
private
Stream: TStream;
ReadBuf: string;
FOwnsStream: boolean;
{ This is either #0 or #10 (tells to ignore next #13 char) or #13
(tells to ignore next #10 char) }
LastNewLineChar: char;
public
{ This is a comfortable constructor, equivalent to
TTextReader.Create(TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite), true) }
constructor CreateFromFileStream(const FileName: string);
{ If AOwnsStream then in Destroy we will free Stream object. }
constructor Create(AStream: TStream; AOwnsStream: boolean);
destructor Destroy; override;
{ Reads next line from Stream. Returned string does not contain
any end-of-line characters. }
function Readln: string;
function Eof: boolean;
end;
{ This is a subclass of TProcess that allows to easy "talk"
with executed process by pipes (read process stdout/stderr,
write to process stdin) on a line-by-line basis.
If symbol HAS_PROCESS is not defined, this defines a junky
implementation of TProcessLineTalk class that can't do anything
and raises exception when you try to execute a process. }
{$ifdef HAS_PROCESS}
TProcessLineTalk = class(TProcess)
private
OutputLineReader: TTextReader;
public
{ Adds poUsePipes to Options, since it's not reasonable to use
this class when you don't want to communicate with process using
pipes. }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Execute; override;
procedure WriteLine(const S: string);
function ReadLine: string;
end;
{$else HAS_PROCESS}
TProcessLineTalk = class(TComponent)
private
FCommandLine: string;
FParameters: TStrings;
FExecutable: string;
public
procedure Execute;
procedure WriteLine(const S: string);
function ReadLine: string;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property CommandLine: string read FCommandLine write FCommandLine;
property Executable: string read FExecutable write FExecutable;
property Parameters: TStrings read FParameters;
end;
{$endif else HAS_PROCESS}
implementation
uses PasDoc_Types, PasDoc_Utils, PasDoc_StreamUtils;
{ TTextReader ---------------------------------------------------------------- }
constructor TTextReader.CreateFromFileStream(const FileName: string);
begin
Create(TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite), true);
end;
constructor TTextReader.Create(AStream: TStream; AOwnsStream: boolean);
begin
inherited Create;
Stream := AStream;
FOwnsStream := AOwnsStream;
LastNewLineChar := #0;
end;
destructor TTextReader.Destroy;
begin
if FOwnsStream then Stream.Free;
inherited;
end;
function TTextReader.Readln: string;
const BUF_INC = 100;
var ReadCnt, i: integer;
begin
i := 1;
{ Note that ReadBuf may contain data that we
already read from stream at some time but did not returned it to
user of this class
(because we realized we have read too much). }
repeat
if i > Length(ReadBuf) then
begin
SetLength(ReadBuf, Length(ReadBuf) + BUF_INC);
ReadCnt := Stream.Read(ReadBuf[Length(ReadBuf) - BUF_INC + 1], BUF_INC);
SetLength(ReadBuf, Length(ReadBuf) - BUF_INC + ReadCnt);
if ReadCnt = 0 then
begin
Result := ReadBuf;
ReadBuf := '';
Exit;
end;
end;
if ((ReadBuf[i] = #10) and (LastNewLineChar = #13)) or
((ReadBuf[i] = #13) and (LastNewLineChar = #10)) then
begin
{ We got 2nd newline character ? Ignore it. }
Assert(i = 1);
Delete(ReadBuf, 1, 1);
LastNewLineChar := #0;
end else
if IsCharInSet(ReadBuf[i], [#10, #13]) then
begin
Result := Copy(ReadBuf, 1, i-1);
LastNewLineChar := ReadBuf[i];
Delete(ReadBuf, 1, i);
Exit;
end else
begin
LastNewLineChar := #0;
Inc(i);
end;
until false;
end;
function TTextReader.Eof: boolean;
var ReadCnt: Integer;
begin
if ReadBuf = '' then
begin
SetLength(ReadBuf, 1);
ReadCnt := Stream.Read(ReadBuf[1], 1);
SetLength(ReadBuf, ReadCnt);
end;
Result := ReadBuf = '';
end;
{ TProcessLineTalk ----------------------------------------------------------- }
{$ifdef HAS_PROCESS}
constructor TProcessLineTalk.Create(AOwner: TComponent);
begin
inherited;
Options := Options + [poUsePipes, poStdErrToOutput];
end;
destructor TProcessLineTalk.Destroy;
begin
FreeAndNil(OutputLineReader);
Active := False;
inherited;
end;
procedure TProcessLineTalk.Execute;
begin
inherited;
FreeAndNil(OutputLineReader);
OutputLineReader := TTextReader.Create(Output, false);
end;
procedure TProcessLineTalk.WriteLine(const S: string);
begin
StreamWriteLine(Input, S);
end;
function TProcessLineTalk.ReadLine: string;
begin
Result := OutputLineReader.Readln;
end;
{$else HAS_PROCESS}
constructor TProcessLineTalk.Create(AOwner: TComponent);
begin
inherited;
FParameters := TStringList.Create;
end;
destructor TProcessLineTalk.Destroy;
begin
FreeAndNil(FParameters);
inherited;
end;
procedure TProcessLineTalk.Execute;
begin
raise Exception.Create('TProcessLineTalk.Execute: not implemented');
end;
procedure TProcessLineTalk.WriteLine(const S: string);
begin
raise Exception.Create('TProcessLineTalk.WriteLine: not implemented');
end;
function TProcessLineTalk.ReadLine: string;
begin
raise Exception.Create('TProcessLineTalk.ReadLine: not implemented');
Result := ''; // silence warnings
end;
{$endif else HAS_PROCESS}
end.
|