* Merging revisions r43641,r43642,r43643,r43644,r43645 from trunk:

------------------------------------------------------------------------
    r43641 | michael | 2019-12-05 11:43:02 +0100 (Thu, 05 Dec 2019) | 1 line
    
    * Fix bug #0036361, use buffer when reading csv
    ------------------------------------------------------------------------
    r43642 | michael | 2019-12-05 13:51:14 +0100 (Thu, 05 Dec 2019) | 1 line
    
    * Fix 16 bit support for PNM (bug ID 35080)
    ------------------------------------------------------------------------
    r43643 | michael | 2019-12-05 15:01:43 +0100 (Thu, 05 Dec 2019) | 1 line
    
    * Rework default file mechanism in bufdataset to fix bug #34435
    ------------------------------------------------------------------------
    r43644 | michael | 2019-12-05 15:02:03 +0100 (Thu, 05 Dec 2019) | 1 line
    
    * Fix name array
    ------------------------------------------------------------------------
    r43645 | michael | 2019-12-05 15:54:15 +0100 (Thu, 05 Dec 2019) | 1 line
    
    * Fix bug #32532: AV when killing daemon app
    ------------------------------------------------------------------------

git-svn-id: branches/fixes_3_2@43647 -
This commit is contained in:
michael 2019-12-05 15:18:26 +00:00
parent 9b048da8fb
commit d9c6b8f145
22 changed files with 652 additions and 60 deletions

9
.gitattributes vendored
View File

@ -2006,6 +2006,7 @@ packages/fcl-base/src/wtex.pp svneol=native#text/plain
packages/fcl-base/tests/fclbase-unittests.lpi svneol=native#text/plain
packages/fcl-base/tests/fclbase-unittests.pp svneol=native#text/plain
packages/fcl-base/tests/tcbufferedfilestream.pp svneol=native#text/plain
packages/fcl-base/tests/tccsvdocument.pp svneol=native#text/plain
packages/fcl-base/tests/tccsvreadwrite.pp svneol=native#text/plain
packages/fcl-base/tests/tchashlist.pp svneol=native#text/plain
packages/fcl-base/tests/tcinifile.pp svneol=native#text/plain
@ -2308,6 +2309,14 @@ packages/fcl-extra/examples/Makefile svneol=native#text/plain
packages/fcl-extra/examples/Makefile.fpc svneol=native#text/plain
packages/fcl-extra/examples/daemon.pp svneol=native#text/plain
packages/fcl-extra/examples/daemon.txt svneol=native#text/plain
packages/fcl-extra/examples/double/daemonmapperunit1.lfm svneol=native#text/plain
packages/fcl-extra/examples/double/daemonmapperunit1.pas svneol=native#text/plain
packages/fcl-extra/examples/double/daemonunit1.lfm svneol=native#text/plain
packages/fcl-extra/examples/double/daemonunit1.pas svneol=native#text/plain
packages/fcl-extra/examples/double/daemonunit2.lfm svneol=native#text/plain
packages/fcl-extra/examples/double/daemonunit2.pas svneol=native#text/plain
packages/fcl-extra/examples/double/double.pp svneol=native#text/plain
packages/fcl-extra/examples/double/resdaemonapp.pp svneol=native#text/plain
packages/fcl-extra/fpmake.pp svneol=native#text/pascal
packages/fcl-extra/src/daemonapp.pp svneol=native#text/plain
packages/fcl-extra/src/unix/daemonapp.inc svneol=native#text/plain

View File

@ -44,7 +44,7 @@ unit csvdocument;
interface
uses
Classes, SysUtils, Contnrs, csvreadwrite;
Classes, SysUtils, Contnrs, csvreadwrite, bufstream;
type
TCSVChar = csvreadwrite.TCSVChar;
@ -73,13 +73,15 @@ type
function GetColCount(ARow: Integer): Integer;
function GetMaxColCount: Integer;
public
constructor Create;
constructor Create; override;
destructor Destroy; override;
// Input/output
// Load document from file AFileName
procedure LoadFromFile(const AFilename: String);
// Load document from file AFileName. Use default buffer size of 16kb
procedure LoadFromFile(const AFilename: String); overload;
// Load document from file AFileName. Buffer size is in Kb.
procedure LoadFromFile(const AFilename: String; ABufferSize : Integer); overload;
// Load document from stream AStream
procedure LoadFromStream(AStream: TStream);
// Save document to file AFilename
@ -392,14 +394,27 @@ begin
end;
procedure TCSVDocument.LoadFromFile(const AFilename: String);
begin
LoadFromFile(aFileName,DefaultBufferCapacity);
end;
procedure TCSVDocument.LoadFromFile(const AFilename: String; ABufferSize : Integer);
var
FileStream: TFileStream;
B : TBufStream;
begin
B:=Nil;
FileStream := TFileStream.Create(AFilename, fmOpenRead or fmShareDenyNone);
try
LoadFromStream(FileStream);
B:=TReadBufStream.Create(FileStream,aBufferSize);
B.SourceOwner:=True;
FileStream:=Nil;
LoadFromStream(B);
finally
FileStream.Free;
B.Free;
end;
end;

View File

@ -36,7 +36,7 @@
</Mode0>
</Modes>
</RunParams>
<Units Count="7">
<Units Count="8">
<Unit0>
<Filename Value="fclbase-unittests.pp"/>
<IsPartOfProject Value="True"/>
@ -65,6 +65,10 @@
<Filename Value="tcbufferedfilestream.pp"/>
<IsPartOfProject Value="True"/>
</Unit6>
<Unit7>
<Filename Value="tccsvdocument.pp"/>
<IsPartOfProject Value="True"/>
</Unit7>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -4,7 +4,7 @@ program fclbase_unittests;
uses
Classes, consoletestrunner, tests_fptemplate, tchashlist,
testexprpars, tcmaskutils, tcinifile, tccsvreadwrite,tcbufferedfilestream;
testexprpars, tcmaskutils, tcinifile, tccsvreadwrite,tcbufferedfilestream, tccsvdocument;
var
Application: TTestRunner;

View File

@ -0,0 +1,148 @@
unit tccsvdocument;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fpcunit, testregistry, csvdocument;
Type
{ TTestCSVDocument }
TTestCSVDocument = Class(TTestCase)
private
FDoc: TCSVDocument;
procedure RemoveTestFile;
function StripQuotes(S: String): string;
procedure TestTestFile;
Public
Procedure SetUp; override;
Procedure TearDown; override;
Procedure CreateTestFile;
Property Doc : TCSVDocument Read FDoc;
Published
Procedure TestEmpty;
Procedure TestRead;
end;
implementation
Const
TestFileName = 'test.csv';
{ TTestCSVDocument }
procedure TTestCSVDocument.SetUp;
begin
FDoc:=TCSVDocument.Create;
Inherited;
end;
procedure TTestCSVDocument.TearDown;
begin
RemoveTestFile;
FreeAndNil(FDoc);
Inherited;
end;
procedure TTestCSVDocument.RemoveTestFile;
begin
If FileExists(TestFileName) then
AssertTrue('Deleting test file',DeleteFile(TestFileName));
end;
Const
ColCount = 3;
RowCount = 4;
Type
TRow = Array[0..ColCount-1] of string;
TCells = Array[0..RowCount-1] of TRow;
Const
Cells : TCells = (
('a','b','c'),
('1','"one"','1.1'),
('2','"two"','2.2'),
('3','"three"','3.3')
);
procedure TTestCSVDocument.CreateTestFile;
Var
L : TStringList;
R,C : Integer;
S : String;
begin
L:=TStringList.Create;
try
for R:=0 to RowCount-1 do
begin
S:='';
for C:=0 to ColCount-1 do
begin
if S<>'' then
S:=S+',';
S:=S+Cells[R,C];
end;
L.Add(S);
end;
L.SaveToFile(TestFileName);
finally
L.Free;
end;
end;
procedure TTestCSVDocument.TestEmpty;
begin
AssertNotNull('Have document',Doc);
end;
Function TTestCSVDocument.StripQuotes(S : String) : string;
Var
L : integer;
begin
Result:=S;
L:=Length(Result);
if (L>1) then
if (Result[1]='"') and (Result[L]='"') then
Result:=Copy(Result,2,L-2);
end;
procedure TTestCSVDocument.TestTestFile;
Var
R,C : Integer;
begin
AssertEquals('Row count',RowCount,Doc.RowCount);
For R:=0 to RowCount-1 do
For C:=0 to ColCount-1 do
begin
AssertEquals('Col['+IntToStr(R)+'] count',ColCount,Doc.ColCount[R]);
AssertEquals(Format('Cell[%d,%d]',[C,R]),StripQuotes(Cells[R,C]),Doc.Cells[C,R]);
end;
end;
procedure TTestCSVDocument.TestRead;
begin
CreateTestFile;
Doc.LoadFromFile(TestFileName);
TestTestFile;
end;
initialization
RegisterTest(TTestCSVDocument);
end.

View File

@ -112,7 +112,6 @@ begin
end;
initialization
RegisterTest(TTestCSVReadWrite);
end.

View File

@ -353,7 +353,7 @@ type
{ TDataPacketReader }
TDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny);
TDataPacketFormat = (dfBinary,dfXML,dfXMLUTF8,dfAny,dfDefault);
TDatapacketReaderClass = class of TDatapacketReader;
TDataPacketReader = class(TObject)
@ -564,6 +564,9 @@ type
Property BufIndexes[Aindex : Integer] : TBufIndex Read GetBufIndex;
protected
// abstract & virtual methods of TDataset
class function DefaultReadFileFormat : TDataPacketFormat; virtual;
class function DefaultWriteFileFormat : TDataPacketFormat; virtual;
class function DefaultPacketClass : TDataPacketReaderClass ; virtual;
procedure SetPacketRecords(aValue : integer); virtual;
procedure SetRecNo(Value: Longint); override;
function GetRecNo: Longint; override;
@ -640,9 +643,9 @@ type
procedure SetDatasetPacket(AReader : TDataPacketReader);
procedure GetDatasetPacket(AWriter : TDataPacketReader);
procedure LoadFromStream(AStream : TStream; Format: TDataPacketFormat = dfAny);
procedure LoadFromStream(AStream : TStream; Format: TDataPacketFormat = dfDefault);
procedure SaveToStream(AStream : TStream; Format: TDataPacketFormat = dfBinary);
procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfAny);
procedure LoadFromFile(AFileName: string = ''; Format: TDataPacketFormat = dfDefault);
procedure SaveToFile(AFileName: string = ''; Format: TDataPacketFormat = dfBinary);
procedure CreateDataset;
Procedure Clear; // Will close and remove all field definitions.
@ -738,17 +741,18 @@ var
begin
Result := False;
for i := 0 to length(RegisteredDatapacketReaders)-1 do if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
begin
if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
for i := 0 to length(RegisteredDatapacketReaders)-1 do
if ((AFormat=dfAny) or (AFormat=RegisteredDatapacketReaders[i].Format)) then
begin
ADataReaderClass := RegisteredDatapacketReaders[i];
Result := True;
if (AStream <> nil) then AStream.Seek(0,soFromBeginning);
break;
if (AStream=nil) or (RegisteredDatapacketReaders[i].ReaderClass.RecognizeStream(AStream)) then
begin
ADataReaderClass := RegisteredDatapacketReaders[i];
Result := True;
if (AStream <> nil) then AStream.Seek(0,soFromBeginning);
break;
end;
AStream.Seek(0,soFromBeginning);
end;
AStream.Seek(0,soFromBeginning);
end;
end;
function DBCompareText(subValue, aValue: pointer; size: integer; options: TLocateOptions): LargeInt;
@ -1431,8 +1435,8 @@ end;
procedure TCustomBufDataset.DoBeforeClose;
begin
inherited DoBeforeClose;
if FFileName<>'' then
SaveToFile(FFileName);
if (FFileName<>'') then
SaveToFile(FFileName,dfDefault);
end;
procedure TCustomBufDataset.InternalClose;
@ -2249,6 +2253,22 @@ begin
FMaxIndexesCount:=FIndexes.Count+2; // Custom+Default order
end;
class function TCustomBufDataset.DefaultReadFileFormat: TDataPacketFormat;
begin
Result:=dfAny;
end;
class function TCustomBufDataset.DefaultWriteFileFormat: TDataPacketFormat;
begin
Result:=dfBinary;
end;
class function TCustomBufDataset.DefaultPacketClass: TDataPacketReaderClass;
begin
Result:=TFpcBinaryDatapacketReader;
end;
procedure TCustomBufDataset.SetIndexFieldNames(const AValue: String);
begin
@ -3046,11 +3066,17 @@ end;
function TCustomBufDataset.GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader;
var APacketReader: TDataPacketReader;
APacketReaderReg: TDatapacketReaderRegistration;
var
APacketReader: TDataPacketReader;
APacketReaderReg: TDatapacketReaderRegistration;
Fmt : TDataPacketFormat;
begin
if GetRegisterDatapacketReader(AStream, format, APacketReaderReg) then
fmt:=Format;
if (Fmt=dfDefault) then
fmt:=DefaultReadFileFormat;
if fmt=dfDefault then
APacketReader := DefaultPacketClass.Create(Self, AStream)
else if GetRegisterDatapacketReader(AStream, fmt, APacketReaderReg) then
APacketReader := APacketReaderReg.ReaderClass.Create(Self, AStream)
else if TFpcBinaryDatapacketReader.RecognizeStream(AStream) then
begin
@ -3423,11 +3449,17 @@ end;
procedure TCustomBufDataset.SaveToStream(AStream: TStream; Format: TDataPacketFormat);
var APacketReaderReg : TDatapacketReaderRegistration;
APacketWriter : TDataPacketReader;
Fmt : TDataPacketFormat;
begin
CheckBiDirectional;
if GetRegisterDatapacketReader(Nil,format,APacketReaderReg) then
fmt:=Format;
if Fmt=dfDefault then
fmt:=DefaultWriteFileFormat;
if fmt=dfDefault then
APacketWriter := DefaultPacketClass.Create(Self, AStream)
else if GetRegisterDatapacketReader(Nil,fmt,APacketReaderReg) then
APacketWriter := APacketReaderReg.ReaderClass.Create(Self, AStream)
else if Format = dfBinary then
else if fmt = dfBinary then
APacketWriter := TFpcBinaryDatapacketReader.Create(Self, AStream)
else
DatabaseError(SNoReaderClassRegistered,Self);
@ -3439,9 +3471,13 @@ begin
end;
procedure TCustomBufDataset.LoadFromFile(AFileName: string; Format: TDataPacketFormat);
var AFileStream : TFileStream;
var
AFileStream : TFileStream;
begin
if AFileName='' then AFileName := FFileName;
if AFileName='' then
AFileName := FFileName;
AFileStream := TFileStream.Create(AFileName,fmOpenRead);
try
LoadFromStream(AFileStream, Format);
@ -3450,11 +3486,14 @@ begin
end;
end;
procedure TCustomBufDataset.SaveToFile(AFileName: string;
Format: TDataPacketFormat);
var AFileStream : TFileStream;
procedure TCustomBufDataset.SaveToFile(AFileName: string; Format: TDataPacketFormat);
var
AFileStream : TFileStream;
begin
if AFileName='' then AFileName := FFileName;
if AFileName='' then
AFileName := FFileName;
AFileStream := TFileStream.Create(AFileName,fmCreate);
try
SaveToStream(AFileStream, Format);
@ -3526,7 +3565,7 @@ begin
if not assigned(FDatasetReader) then
begin
FFileStream := TFileStream.Create(FileName, fmOpenRead);
FDatasetReader := GetPacketReader(dfAny, FFileStream);
FDatasetReader := GetPacketReader(dfDefault, FFileStream);
end;
FieldDefs.Clear;

View File

@ -95,6 +95,9 @@ Type
FCSVOptions: TCSVOptions;
procedure SetCSVOptions(AValue: TCSVOptions);
Protected
class function DefaultReadFileFormat : TDataPacketFormat; override;
class function DefaultWriteFileFormat : TDataPacketFormat; override;
class function DefaultPacketClass : TDataPacketReaderClass ; override;
function GetPacketReader(const Format: TDataPacketFormat; const AStream: TStream): TDataPacketReader; override;
procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField); override;
procedure InternalInitFieldDefs; override;
@ -305,10 +308,25 @@ begin
FCSVOptions.Assign(AValue);
end;
class function TCustomCSVDataset.DefaultReadFileFormat: TDataPacketFormat;
begin
Result:=dfDefault;
end;
class function TCustomCSVDataset.DefaultWriteFileFormat: TDataPacketFormat;
begin
Result:=dfDefault;
end;
class function TCustomCSVDataset.DefaultPacketClass: TDataPacketReaderClass;
begin
Result:=TCSVDataPacketReader;
end;
function TCustomCSVDataset.GetPacketReader(const Format: TDataPacketFormat;
const AStream: TStream): TDataPacketReader;
begin
If (Format=dfAny) then
If (Format in [dfAny,dfDefault]) then
Result:=TCSVDataPacketReader.Create(Self,AStream,FCSVOptions)
else
Result:=Inherited GetPacketReader(Format,AStream);

View File

@ -15,6 +15,7 @@ type
private
FCSVDataset: TCSVDataset;
// Load CSVDataset from CSV stream containing lines
procedure DoOpenClose;
Procedure LoadFromLines(Const Lines: Array of string);
// Save CSVDataset to CSV stream, transform to lines
Procedure SaveToLines(Const Lines: TStrings);
@ -47,6 +48,7 @@ type
Procedure TestLoadPriorFieldDefsNoFieldNamesWrongCount;
Procedure TestLoadPriorFieldDefsFieldNamesWrongCount;
Procedure TestLoadPriorFieldDefsFieldNamesWrongNames;
Procedure TestOpenCloseCycle;
end;
implementation
@ -421,6 +423,89 @@ begin
Fail(OK);
end;
const
FILENAME = 'test.dat';
procedure TTestCSVDataset.DoOpenClose;
begin
CSVDataset.FileName := FILENAME;
With CSVDataset do
begin
CSVOptions.FirstLineAsFieldNames := True;
CSVOptions.DefaultFieldLength := 255;
CSVOptions.Delimiter := ',';
CSVOptions.QuoteChar := '"';
CSVOptions.IgnoreOuterWhitespace := False;
CSVOptions.QuoteOuterWhitespace := True;
end;
// When the program runs for the first time, the data file does not yet exist.
// We must create the FieldDefs and create the dataset.
if FileExists(CSVDataset.FileName) then
CSVDataset.Open
else
with CSVDataset do
begin
FieldDefs.Add('FirstName', ftString, 20);
FieldDefs.Add('LastName', ftstring, 20);
FieldDefs.Add('City', ftString, 20);
FieldDefs.Add('Address', ftString, 30);
FieldDefs.Add('Birthdate', ftDate);
CreateDataset;
// Open the dataset...
Open;
// ... and add some dummy data:
// Names from https://donatellanobatti.blogspot.de/
Append;
FieldByName('FirstName').AsString := 'Walter';
FieldByName('LastName').AsString := 'Mellon';
FieldByName('City').AsString := 'Oklahoma City';
FieldByName('Address').AsString := '1261, Main Street';
FieldbyName('Birthdate').AsDateTime := EncodeDate(1980, 1, 1);
Post;
Append;
FieldByName('FirstName').AsString := 'Mario';
FieldByName('LastName').AsString := 'Speedwagon';
FieldByName('City').AsString := 'Hollywood';
FieldByName('Address').AsString := '1500, Hollywood Blvd';
FieldbyName('Birthdate').AsDateTime := EncodeDate(1982, 12, 17);
Post;
Append;
FieldByName('FirstName').AsString := 'Anna';
FieldByName('LastName').AsString := 'Mull';
FieldByName('City').AsString := 'Los Angeles';
FieldByName('Address').AsString := '2202, Capitol Square';
FieldbyName('Birthdate').AsDateTime := EncodeDate(1982, 12, 17);
Post;
end;
// This will write the file;
CSVDataset.Close;
end;
procedure TTestCSVDataset.TestOpenCloseCycle;
begin
if FileExists(FileName) then
AssertTrue('Delete before',DeleteFile(FileName));
try
// This will create the file
DoOpenClose;
// Recreate to be sure
FreeAndNil(FCSVDataset);
FCSVDataset:=TCSVDataset.Create(Nil);
FCSVDataset.Name:='DS';
DoOpenClose;
except
On E : Exception do
Fail('Failed using exception %s : %s',[E.ClassName,E.Message]);
end;
if FileExists(FileName) then
AssertTrue('Delete after',DeleteFile(FileName));
end;
procedure TTestCSVDataset.SetUp;
begin
FCSVDataset:=TCSVDataset.Create(Nil);

View File

@ -100,7 +100,7 @@ uses toolsunit, SQLDBToolsUnit, sqldb, XMLDatapacketReader;
const TestXMLFileName = 'test.xml';
TestBINFileName = 'test.dat';
TestFileNames: array[TDataPacketFormat] of string = (TestBINFileName, TestXMLFileName, TestXMLFileName, '');
TestFileNames: array[TDataPacketFormat] of string = (TestBINFileName, TestXMLFileName, TestXMLFileName, '','');
{ TMyCustomBufDataset }

View File

@ -0,0 +1,29 @@
object DaemonMapper1: TDaemonMapper1
DaemonDefs = <
item
DaemonClassName = 'TDaemon1'
Name = 'TDaemon1'
Options = [doAllowStop, doAllowPause]
WinBindings.Dependencies = <>
WinBindings.StartType = stBoot
WinBindings.WaitHint = 0
WinBindings.IDTag = 0
WinBindings.ServiceType = stWin32
WinBindings.ErrorSeverity = esIgnore
LogStatusReport = False
end
item
DaemonClassName = 'TDaemon2'
Name = 'TDaemon2'
Options = [doAllowStop, doAllowPause]
WinBindings.Dependencies = <>
WinBindings.StartType = stBoot
WinBindings.WaitHint = 0
WinBindings.IDTag = 0
WinBindings.ServiceType = stWin32
WinBindings.ErrorSeverity = esIgnore
LogStatusReport = False
end>
Left = 284
Top = 140
end

View File

@ -0,0 +1,34 @@
unit DaemonMapperUnit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DaemonApp;
type
TDaemonMapper1 = class(TDaemonMapper)
private
public
end;
var
DaemonMapper1: TDaemonMapper1;
implementation
procedure RegisterMapper;
begin
RegisterDaemonMapper(TDaemonMapper1)
end;
{$R *.lfm}
initialization
RegisterMapper;
end.

View File

@ -0,0 +1,8 @@
object Daemon1: TDaemon1
OldCreateOrder = False
OnExecute = DataModuleExecute
Height = 150
HorizontalOffset = 284
VerticalOffset = 140
Width = 150
end

View File

@ -0,0 +1,52 @@
unit DaemonUnit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DaemonApp;
type
{ TDaemon1 }
TDaemon1 = class(TDaemon)
procedure DataModuleExecute(Sender: TCustomDaemon);
private
public
end;
var
Daemon1: TDaemon1;
implementation
procedure RegisterDaemon;
begin
RegisterDaemonClass(TDaemon1)
end;
{$R *.lfm}
{ TDaemon1 }
procedure TDaemon1.DataModuleExecute(Sender: TCustomDaemon);
Var
I : Integer;
begin
I := 0;
Application.EventLog.Log('TDaemon1 execution start');
While Self.Status = csRunning Do Begin
Sleep(10);
end;
Application.EventLog.Log('TDaemon1 execution stop');
end;
initialization
RegisterDaemon;
end.

View File

@ -0,0 +1,8 @@
object Daemon2: TDaemon2
OldCreateOrder = False
OnExecute = DataModuleExecute
Height = 150
HorizontalOffset = 284
VerticalOffset = 140
Width = 150
end

View File

@ -0,0 +1,52 @@
unit daemonunit2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DaemonApp;
type
{ TDaemon2 }
TDaemon2 = class(TDaemon)
procedure DataModuleExecute(Sender: TCustomDaemon);
private
public
end;
var
Daemon2: TDaemon2;
implementation
procedure RegisterDaemon;
begin
RegisterDaemonClass(TDaemon2)
end;
{$R *.lfm}
{ TDaemon2 }
procedure TDaemon2.DataModuleExecute(Sender: TCustomDaemon);
Var
I : Integer;
begin
I := 0;
Application.EventLog.Log('TDaemon2 execution start');
While Self.Status = csRunning Do Begin
Sleep(10);
end;
Application.EventLog.Log('TDaemon2 execution stop');
end;
initialization
RegisterDaemon;
end.

View File

@ -0,0 +1,23 @@
Program double;
Uses
{$IFDEF UNIX}
CThreads,
{$ENDIF}
ResDaemonApp, DaemonApp, DaemonMapperUnit1, DaemonUnit1, daemonunit2, SysUtils, eventlog
{ add your units here };
Var
AExecutableFilenamePath : String;
begin
AExecutableFilenamePath := ParamStr(0);
AExecutableFilenamePath := ExpandFileName(AExecutableFilenamePath);
AExecutableFilenamePath := ExtractFilePath(AExecutableFilenamePath);
Application.Title:='Daemon application';
Application.Initialize;
Application.EventLog.FileName := SysUtils.ConcatPaths([AExecutableFilenamePath, 'event-log.txt']);
Application.EventLog.LogType := ltFile;
Application.EventLog.AppendContent := False;
Application.EventLog.Active := True;
Application.Run;
end.

View File

@ -0,0 +1,32 @@
{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
{$mode objfpc}
{$h+}
unit resdaemonapp;
interface
uses daemonapp;
Type
TResDaemonApplication = Class(TCustomDaemonApplication)
Procedure CreateDaemonInstance(Var ADaemon : TCustomDaemon; DaemonDef : TDaemonDef); override;
end;
implementation
uses classes;
Procedure TResDaemonApplication.CreateDaemonInstance(Var ADaemon : TCustomDaemon; DaemonDef : TDaemonDef);
begin
ADaemon:=DaemonDef.DaemonClass.Create(Self);
end;
Initialization
RegisterDaemonApplicationClass(TResDaemonApplication)
end.

View File

@ -56,6 +56,7 @@ Type
Function Install : Boolean; virtual;
Function UnInstall: boolean; virtual;
Function HandleCustomCode(ACode : DWord) : Boolean; Virtual;
procedure DoThreadTerminate(Sender: TObject);virtual;
Public
Procedure CheckControlMessages(Wait : Boolean);
Procedure LogMessage(const Msg : String);
@ -694,7 +695,12 @@ begin
Result:=False
end;
Procedure TCustomDaemon.CheckControlMessages(Wait : Boolean);
procedure TCustomDaemon.DoThreadTerminate(Sender: TObject);
begin
Self.FThread := NIL;
end;
procedure TCustomDaemon.CheckControlMessages(Wait: Boolean);
begin
If Assigned(FThread) then

View File

@ -167,9 +167,8 @@ begin
Try
T:=TDaemonThread.Create(FDaemon);
T.FreeOnTerminate:=True;
T.OnTerminate := @FDaemon.DoThreadTerminate;
T.Resume;
T.WaitFor;
FDaemon.FThread:=Nil;
except
On E : Exception do
FDaemon.Logmessage(Format(SErrDaemonStartFailed,[FDaemon.Definition.Name,E.Message]));

View File

@ -113,6 +113,7 @@ Var
C : Char;
begin
C:=#0;
Stream.ReadBuffer(C,1);
If (C<>'P') then
Raise Exception.Create('Not a valid PNM image.');
@ -157,7 +158,7 @@ begin
Case FBitmapType of
5,6 : FScanLineSize:=(FBitPP div 8) * FWidth;
else
FScanLineSize:=FBitPP*((FWidth+7)shr 3);
FScanLineSize:=FBitPP*((FWidth+7) shr 3);
end;
GetMem(FScanLine,FScanLineSize);
try
@ -165,6 +166,7 @@ begin
begin
ReadScanLine(Row,Stream);
WriteScanLine(Row,Img);
// Writeln(Stream.Position,' ',Stream.Size);
end;
finally
FreeMem(FScanLine);
@ -212,7 +214,8 @@ begin
Inc(P)
end;
end;
4,5,6 : Stream.ReadBuffer(FScanLine^,FScanLineSize);
4,5,6 :
Stream.ReadBuffer(FScanLine^,FScanLineSize);
end;
end;
@ -222,7 +225,7 @@ procedure TFPReaderPNM.WriteScanLine(Row : Integer; Img : TFPCustomImage);
Var
C : TFPColor;
L : Cardinal;
Scale: Cardinal;
Scale: Int64;
function ScaleByte(B: Byte):Word;
begin
@ -235,7 +238,7 @@ Var
function ScaleWord(W: Word):Word;
begin
if FMaxVal = 65535 then
Result := W
Result := BEtoN(W)
else { Mimic the above with multiplications }
Result := Int64(W*(FMaxVal+1) + W) * 65535 div Scale;
end;

View File

@ -30,15 +30,21 @@ type
{ TFPWriterPNM }
TFPWriterPNM = class(TFPCustomImageWriter)
protected
procedure InternalWrite(Stream:TStream;Img:TFPCustomImage);override;
public
ColorDepth: TPNMColorDepth;
BinaryFormat: boolean;
function GuessColorDepthOfImage(Img: TFPCustomImage): TPNMColorDepth;
function GetColorDepthOfExtension(AExtension: string): TPNMColorDepth;
function GetFileExtension(AColorDepth: TPNMColorDepth): string;
constructor Create; override;
private
FFullWidth: Boolean;
FColorDepth: TPNMColorDepth;
FBinaryFormat: boolean;
procedure SetFullWidth(AValue: Boolean);
protected
procedure InternalWrite(Stream:TStream;Img:TFPCustomImage);override;
public
Property FullWidth: Boolean Read FFullWidth Write SetFullWidth;
function GuessColorDepthOfImage(Img: TFPCustomImage): TPNMColorDepth;
function GetColorDepthOfExtension(AExtension: string): TPNMColorDepth;
function GetFileExtension(AColorDepth: TPNMColorDepth): string;
constructor Create; override;
Property BinaryFormat : Boolean Read FBinaryFormat Write FBinaryFormat;
Property ColorDepth: TPNMColorDepth Read FColorDepth Write FColorDepth;
end;
{ TFPWriterPBM }
@ -113,6 +119,14 @@ begin
BinaryFormat := True;
end;
procedure TFPWriterPNM.SetFullWidth(AValue: Boolean);
begin
if FFullWidth=AValue then Exit;
FFullWidth:=AValue;
if FFullWidth then
BinaryFormat:=True;
end;
procedure TFPWriterPNM.InternalWrite(Stream:TStream;Img:TFPCustomImage);
var useBitMapType: integer;
@ -130,8 +144,9 @@ var useBitMapType: integer;
Str(Img.Height,StrHeight);
end;
PNMInfo:=Concat(MagicWords[useBitMapType],#10,StrWidth,#32,StrHeight,#10);
if useBitMapType in [2,3,5,6]
then
if (useBitMapType in [5,6]) and FullWidth then
PNMInfo:=Concat(PNMInfo,'65535'#10)
else if (useBitMapType in [2,3,5,6]) then
PNMInfo:=Concat(PNMInfo,'255'#10);
stream.seek(0,soFromBeginning);
stream.Write(PNMInfo[1],Length(PNMInfo));
@ -141,6 +156,7 @@ var useBitMapType: integer;
Row,Coulumn,nBpLine,i:Integer;
aColor:TFPColor;
aLine:PByte;
dLine : PWord;
strCol:String[3];
LinuxEndOfLine: char;
UseColorDepth: TPNMColorDepth;
@ -160,17 +176,20 @@ var useBitMapType: integer;
pcdRGB: useBitMapType := 3;
end;
if BinaryFormat then inc(useBitMapType,3);
if FullWidth and Not BinaryFormat then
Raise FPImageException.Create('Fullwidth can only be used with binary format');
SaveHeader(Stream);
case useBitMapType of
1:nBpLine:=Img.Width*2;{p p p}
2:nBpLine:=Img.Width*4;{lll lll lll}
3:nBpLine:=Img.Width*3*4;{rrr ggg bbb rrr ggg bbb}
4:nBpLine:=(Img.Width+7) SHR 3;
5:nBpLine:=Img.Width;
6:nBpLine:=Img.Width*3;
5:nBpLine:=Img.Width*(1+Ord(FullWidth));
6:nBpLine:=Img.Width*3*(1+Ord(FullWidth));
end;
GetMem(aLine,nBpLine);//3 extra byte for BMP 4Bytes alignement.
dLine:=PWord(aLine);
for Row:=0 to img.Height-1 do
begin
FillChar(aLine^,nBpLine,0);
@ -214,8 +233,18 @@ var useBitMapType: integer;
4:if(Red<=$2F00)or(Green<=$2F00)or(Blue<=$2F00)
then
aLine[Coulumn shr 3]:=aLine[Coulumn shr 3] or ($80 shr (Coulumn and $07));
5:aLine[Coulumn]:=Hi(Word(Round(Red*0.299+Green*0.587+Blue*0.114)));
6:begin
5: if FullWidth then
dLine[Coulumn]:=Word(Round(Red*0.299+Green*0.587+Blue*0.114))
else
aLine[Coulumn]:=Hi(Word(Round(Red*0.299+Green*0.587+Blue*0.114)));
6:if FullWidth then
begin
dLine[3*Coulumn]:=NToBE(Red);
dLine[3*Coulumn+1]:=NToBE(Green);
dLine[3*Coulumn+2]:=NToBE(Blue);
end
else
begin
aLine[3*Coulumn]:=Hi(Red);
aLine[3*Coulumn+1]:=Hi(Green);
aLine[3*Coulumn+2]:=Hi(Blue);