DBF.pas
上传用户:hbtcygglw
上传日期:2007-01-07
资源大小:281k
文件大小:48k
源码类别:

其他

开发平台:

Delphi

  1. unit DBF;
  2. (* ===========================================================================
  3.  * dbf.dcu - tDBF : A custom data set which uses a flat binary
  4.  *             structured datafile for single client usage only.
  5.  *
  6.  * Author:  Horacio Jamilis
  7.  * Copyright (C) 1998, Terabyte Computacion
  8.  *
  9.  * ===========================================================================
  10.  * v 0.91
  11.  * - Fixed error on deleting records
  12.  * - Added filtering capabilities (work wrong when there are no records within
  13.  *   the filter expresion - Only support expresion with one field like
  14.  *   "NUMFIELD>10" or "TEXTFIELD<='TEST'" or "DATEFIELD=19980626"
  15.  *   (in yyyymmdd format)).
  16.  * ===========================================================================
  17.  * FOR C++ Builder users:
  18.  * Use the file named DBF_C instead of this one.
  19.  * Especial thanks to Michael Beauregard (Michael_Beauregard@mck.com).
  20.  * ===========================================================================
  21.  *)
  22. interface
  23. uses
  24.   SysUtils, Classes, Db, DsgnIntf;
  25. type
  26.   TFilenameProperty = class(TStringProperty)
  27.   public
  28.     procedure Edit; override;
  29.     function GetAttributes: TPropertyAttributes; override;
  30.   end;
  31.   EDBFError = class (Exception);
  32.   pDateTime = ^TDateTime;
  33.   pBoolean = ^Boolean;
  34.   pInteger = ^Integer;
  35.   PRecInfo = ^TRecInfo;
  36.   TRecInfo = record
  37.     Bookmark: Longint;
  38.     BookmarkFlag: TBookmarkFlag;
  39.   end;
  40.   TdbfHeader = record  { Dbase III + header definition        }
  41.      VersionNumber    :byte;  { version number (03h or 83h ) }
  42.      LastUpdateYear   :byte;  { last update YY MM DD         }
  43.      LastUpdateMonth  :byte;
  44.      LastUpdateDay    :byte;
  45.      NumberOfRecords  :longint; { number of record in database }
  46.      BytesInHeader    :smallint;{ number of bytes in header }
  47.      BytesInRecords   :smallint;{ number of bytes in records }
  48.      ReservedInHeader :array[1..20] of char;   { reserved bytes in header }
  49.   end;
  50.   TdbfField = record
  51.      FieldName   :array[1..11] of char; { Name of this record             }
  52.      FieldType   :char;           { type of record - C,N,D,L,etc.         }
  53.      fld_addr    :longint;        { not used }
  54.      Width       :byte;           { total field width of this record      }
  55.      Decimals    :byte;           { number of digits to right of decimal  }
  56.      MultiUser   :smallint;       { reserved for multi user }
  57.      WorkAreaID  :byte;           { Work area ID }
  58.      MUser       :smallint;       { reserved for multi_user }
  59.      SetFields   :byte;           { SET_FIELDS flag }
  60.      Reserved    :array[1..4] of byte;      { 8 bytes reserved }
  61.   end;                           { record starts                         }
  62. Type
  63.   pRecordHeader = ^tRecordHeader;
  64.   tRecordHeader = record
  65.     DeletedFlag : char;
  66.   end;
  67. type
  68.   TDBF = class(TDataSet)
  69.   protected
  70.     FStream: TStream; // the physical table
  71.     FTableName: string; // table path and file name
  72.     fDBFHeader : TdbfHeader;
  73.     // record data
  74.     fRecordHeaderSize : Integer;   // The size of the record header
  75.     FRecordCount,                  // current number of record
  76.     FRecordSize,                   // the size of the actual data
  77.     FRecordBufferSize,             // data + housekeeping (TRecInfo)
  78.     FRecordInfoOffset,             // offset of RecInfo in record buffer
  79.     FCurrentRecord,                // current record (0 to FRecordCount - 1)
  80.     BofCrack,                      // before the first record (crack)
  81.     EofCrack: Integer;             // after the last record (crack)
  82.     FIsTableOpen: Boolean;         // status
  83.     FFileWidth,                    // field widths in record
  84.     FFileDecimals,                 // field decimals in record
  85.     FFileOffset: TList;            // field offsets in record
  86.     fReadOnly : Boolean;           // Enhancements
  87.     fStartData : Integer;          // Position in file where data starts
  88.     function FFieldType(F : char):TFieldType;
  89.     function FFieldSize(FType:char;FWidth:integer):integer;
  90.   protected
  91.     // TDataSet virtual abstract method
  92.     function AllocRecordBuffer: PChar; override;
  93.     procedure FreeRecordBuffer(var Buffer: PChar); override;
  94.     procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  95.     function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  96.     function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  97.     function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  98.     function GetRecordSize: Word; override;
  99.     procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
  100.     procedure InternalClose; override;
  101.     procedure InternalDelete; override;
  102.     procedure InternalFirst; override;
  103.     procedure InternalGotoBookmark(Bookmark: Pointer); override;
  104.     procedure InternalHandleException; override;
  105.     procedure InternalInitFieldDefs; override;
  106.     procedure InternalInitRecord(Buffer: PChar); override;
  107.     procedure InternalLast; override;
  108.     procedure InternalOpen; override;
  109.     procedure InternalPost; override;
  110.     procedure InternalSetToRecord(Buffer: PChar); override;
  111.     function IsCursorOpen: Boolean; override;
  112.     procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  113.     procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  114.     procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  115.     // TDataSet virtual method (optional)
  116.     function GetRecordCount: Integer; override;
  117.     procedure SetRecNo(Value: Integer); override;
  118.     function GetRecNo: Integer; override;
  119.     Procedure WriteHeader;
  120.   private
  121.     Procedure _ReadRecord(Buffer:PChar;IntRecNum:Integer);
  122.     Procedure _WriteRecord(Buffer:PChar;IntRecNum:Integer);
  123.     Procedure _AppendRecord(Buffer:PChar);
  124.     Procedure _SwapRecords(Rec1,REc2:Integer);
  125.     Function _CompareRecords(SortFields:Array of String;Rec1,Rec2:Integer):Integer;
  126.     Function _ProcessFilter(Buffer:PChar):boolean;
  127.   public
  128.     constructor Create(AOwner:tComponent); override;
  129.     procedure CreateTable;
  130.     Procedure PackTable;
  131.     Procedure SortTable(SortFields : Array of String);
  132.     Procedure UnsortTable;
  133.   published
  134.     property TableName: string read FTableName write FTableName;
  135.     property ReadOnly : Boolean read fReadOnly write fReadonly default False;
  136.     property DBFHeader : tDBFHeader read fDBFHeader;
  137.     // redeclared data set properties
  138.     property Active;
  139.     property Filter;
  140.     property Filtered;
  141.     property BeforeOpen;
  142.     property AfterOpen;
  143.     property BeforeClose;
  144.     property AfterClose;
  145.     property BeforeInsert;
  146.     property AfterInsert;
  147.     property BeforeEdit;
  148.     property AfterEdit;
  149.     property BeforePost;
  150.     property AfterPost;
  151.     property BeforeCancel;
  152.     property AfterCancel;
  153.     property BeforeDelete;
  154.     property AfterDelete;
  155.     property BeforeScroll;
  156.     property AfterScroll;
  157.     property OnCalcFields;
  158.     property OnDeleteError;
  159.     property OnEditError;
  160.     property OnNewRecord;
  161.     property OnPostError;
  162.   end;
  163. procedure Register;
  164. implementation
  165. uses
  166.   TypInfo, Dialogs, Windows, Forms, Controls, IniFiles;
  167. Const
  168.   dfhVersionNumber = 13;
  169. TYPE
  170.   PBufArray = ^BufArray;
  171.   BufArray = Array[0..0] of Char;
  172. // ****************************************************************************
  173. // Low Level Routines for accessing an internal record
  174. // ____________________________________________________________________________
  175. // TDBF._ReadRecord
  176. Procedure TDBF._ReadRecord(Buffer:PChar;IntRecNum:Integer);
  177.   {-Read a record based on the internal record number (absolute)}
  178. BEGIN
  179.   FStream.Position := FStartData + (FRecordSize * IntRecNum);
  180.  try
  181.   FStream.ReadBuffer(Buffer^, FRecordSize);
  182.  except
  183.  end;
  184. END;
  185. // ____________________________________________________________________________
  186. // TDBF._WriteRecord
  187. Procedure TDBF._WriteRecord(Buffer:PChar;IntRecNum:Integer);
  188.   {-Write a record based on the internal record number (absolute)}
  189. BEGIN
  190.   FStream.Position := FStartData + (FRecordSize * IntRecNum);
  191.   FStream.WriteBuffer (Buffer^, FRecordSize);
  192. END;
  193. // ____________________________________________________________________________
  194. // TDBF._AppendRecord
  195. Procedure TDBF._AppendRecord(Buffer:PChar);
  196. BEGIN
  197.   FStream.Position := FStartData + (FRecordSize * (FRecordCount{+FDeletedCount}));
  198.   FStream.WriteBuffer (Buffer^, FRecordSize);
  199. END;
  200. /////////////////////////////////////////////////
  201. ////// Part I:
  202. ////// Initialization, opening, and closing
  203. /////////////////////////////////////////////////
  204. // ____________________________________________________________________________
  205. // TDBF.InternalOpen
  206. // I: open the table/file
  207. procedure TDBF.InternalOpen;
  208. var
  209.   Field : TField;
  210.   i,j : integer;
  211.   d : string;
  212. begin
  213.   // check if the file exists
  214.   if not FileExists (FTableName) then
  215.     raise eDBFError.Create ('Open: Table file not found');
  216.   // create a stream for the file
  217.   if fReadOnly then
  218.     fStream := tFileStream.Create( fTableName, fmOpenRead + fmShareDenyWrite)
  219.   else
  220.     FStream := TFileStream.Create (FTableName, fmOpenReadWrite + fmShareExclusive);
  221.   fStream.ReadBuffer(fDBFHeader,SizeOf(TDBFHeader));
  222.   // sets cracks and record position
  223.   BofCrack := -1;
  224.   EofCrack := fRecordCount{+fDeletedCount};
  225.   FCurrentRecord := BofCrack;
  226.   // set the bookmark size
  227.   BookmarkSize := sizeOf (Integer);
  228.   if not (assigned(FFileOffset)) then
  229.     FFileOffset := TList.Create;
  230.   if not (assigned(FFileWidth)) then
  231.     FFileWidth := TList.Create;
  232.   if not (assigned(FFileDecimals)) then
  233.     FFileDecimals := TList.Create;
  234.   // initialize the field definitions
  235.   // (another virtual abstract method of TDataSet)
  236.   InternalInitFieldDefs;
  237.   FRecordInfoOffset := FRecordSize;
  238.   FRecordBufferSize := FRecordSize + sizeof (TRecInfo);
  239.   // if there are no persistent field objects,
  240.   // create the fields dynamically
  241.   if DefaultFields then
  242.     CreateFields;
  243.   // connect the TField objects with the actual fields
  244.   BindFields (True);
  245.   for i := 0 to FieldCount-1 do
  246.     begin
  247.       Field := Fields[i];
  248.       if (Field.DataType = ftFloat) and (Integer(FFileDecimals[i])>0) then
  249.         begin
  250.           d := '0.';
  251.           for j := 1 to Integer(FFileDecimals[i]) do
  252.             d := d + '0';
  253.           (Field as TFloatField).DisplayFormat := d;
  254.         end;
  255.     end;
  256.   // get the number of records and check size
  257.   fRecordCount := fDBFHeader.NumberOfRecords;
  258.   // everything OK: table is now open
  259.   FIsTableOpen := True;
  260.   // ShowMessage ('InternalOpen: RecCount: ' + IntToStr (FRecordCount));
  261. end;
  262. // Returns the Type of the field
  263. function TDBF.FFieldType(F : char):TFieldType;
  264. begin
  265.   if F = 'C' then
  266.     FFieldType := ftString
  267.   else if (F = 'N') or (F = 'F') then
  268.     FFieldType := ftFloat
  269.   else if F = 'L' then
  270.     FFieldType := ftBoolean
  271.   else if F = 'D' then
  272.     FFieldType := ftDate
  273. //    FFieldType := ftString
  274.   else
  275.     FFieldType := ftUnknown;
  276. end;
  277. function TDBF.FFieldSize(FType:char;FWidth:integer):integer;
  278. begin
  279.   if FType = 'C' then
  280.     FFieldSize := FWidth
  281.   else if (FType = 'N') or (FType = 'F') then
  282.     FFieldSize := 0
  283.   else if FType = 'L' then
  284.     FFieldSize := 0
  285.   else if FType = 'D' then
  286.     FFieldSize := 0
  287. //    FFieldSize := 8
  288.   else
  289.     FFieldSize := 0;
  290. end;
  291. // ____________________________________________________________________________
  292. // TDBF.InternalInitFieldDefs
  293. // I: define the fields
  294. procedure TDBF.InternalInitFieldDefs;
  295. var
  296.   Il : Integer;
  297.   TmpFileOffset : Integer;
  298.   NumberOfFields : integer;
  299.   Fld : TDBFField;
  300.   FldName : PChar;
  301. begin
  302.   FieldDefs.Clear;
  303.   FStream.Seek(SizeOf(TDbfHeader),soFromBeginning);
  304.   NumberOfFields := ((fDbfHeader.BytesInHeader-sizeof(DbfHeader))div 32);
  305.   if not (assigned(FFileOffset)) then
  306.     FFileOffset := TList.Create;
  307.   FFileOffset.Clear;
  308.   if not (assigned(FFileWidth)) then
  309.     FFileWidth := TList.Create;
  310.   FFileWidth.Clear;
  311.   if not (assigned(FFileDecimals)) then
  312.     FFileDecimals := TList.Create;
  313.   FFileDecimals.Clear;
  314.   TmpFileOffset := 0;
  315.   if (NumberOfFields>0) then
  316.     begin
  317.       for Il:=0 to NumberOfFields-1 do
  318.         begin
  319.           FStream.Read(Fld,SizeOf(Fld));
  320.           GetMem(FldName,Length(Fld.FieldName)+1);
  321.           CharToOem(PChar(@Fld.FieldName),FldName);
  322.           TFieldDef.Create(FieldDefs, FldName,FFieldType(Fld.FieldType){DescribF.DataType},
  323.                            FFieldSize(Fld.FieldType,Fld.Width){DescribF.Size},False,Il+1);
  324.           FreeMem(FldName);
  325.           FFileOffset.Add(Pointer(TmpFileOffset));
  326.           FFileWidth.Add(Pointer(Fld.Width));
  327.           FFileDecimals.Add(Pointer(Fld.Decimals));
  328.           Inc(tmpFileOffset,Fld.Width);
  329.         end;
  330.       fRecordSize := tmpFileOffset+FrecordHeaderSize;
  331.       FStartData := FStream.Position+1;
  332.     end;
  333. end;
  334. // ____________________________________________________________________________
  335. // TDBF.InternalClose
  336. // I: close the table/file
  337. procedure TDBF.InternalClose;
  338. begin
  339.   // if required, save updated header
  340.   if (fDBFHeader.NumberOfRecords <> fRecordCount) or
  341.     (fDBFHeader.BytesInRecords = 0) then
  342.     BEGIN
  343.       fDBFHeader.BytesInRecords := fRecordSize;
  344.       fDBFHeader.NumberOfRecords := fRecordCount;
  345.       WriteHeader;
  346.     END;
  347.   // disconnet field objects
  348.   BindFields(False);
  349.   // destroy field object (if not persistent)
  350.   if DefaultFields then
  351.     DestroyFields;
  352.   // free the internal list field offsets
  353.   if Assigned(FFileOffset) then
  354.     FFileOffset.Free;
  355.   FFileOffset := nil;
  356.   if Assigned(FFileWidth) then
  357.     FFileWidth.Free;
  358.   FFileWidth := nil;
  359.   if Assigned(FFileDecimals) then
  360.     FFileDecimals.Free;
  361.   FFileDecimals := nil;
  362.   FCurrentRecord := -1;
  363.   // close the file
  364.   FIsTableOpen := False;
  365.   FStream.Free;
  366.   FStream := nil;
  367. end;
  368. // ____________________________________________________________________________
  369. // TDBF.IsCursorOpen
  370. // I: is table open
  371. function TDBF.IsCursorOpen: Boolean;
  372. begin
  373.   Result := FIsTableOpen;
  374. end;
  375. // ____________________________________________________________________________
  376. // TDBF.WriteHeader
  377. procedure TDBF.WriteHeader;
  378. begin
  379. //  Assert(FStream<>nil,'fStream=Nil');
  380.   if fStream <> nil then
  381.     begin
  382.       FSTream.Seek(0,soFromBeginning);
  383.       FStream.WriteBuffer(fDBFHeader,SizeOf(TDbfHeader));
  384.     end;
  385. end;
  386. // ____________________________________________________________________________
  387. // TDBF.Create
  388. constructor TDBF.Create(AOwner:tComponent);
  389. BEGIN
  390.   inherited create(aOwner);
  391.   fRecordHeaderSize := SizeOf(tRecordHeader);
  392. END;
  393. // ____________________________________________________________________________
  394. // TDBF.CreateTable
  395. // I: Create a new table/file
  396. procedure TDBF.CreateTable;
  397. var
  398.   Ix : Integer;
  399. //  DescribF : TBDescribField;
  400.   Offs : Integer;
  401.   Fld : TDbfField;
  402.   FldName : PChar;
  403.   i : integer;
  404. begin
  405.   CheckInactive;
  406.   //  InternalInitFieldDefs;
  407.   // create the new file
  408.   if FileExists (FTableName) and
  409.     (MessageDlg ('File ' + FTableName +
  410.       ' already exists. OK to override?',
  411.       mtConfirmation, [mbYes, mbNo], 0) = mrNo) then
  412.     Exit;
  413.   if FieldDefs.Count = 0 then
  414.   begin
  415.     for Ix := 0 to FieldCount - 1 do
  416.     begin
  417.       with Fields[Ix] do
  418.       begin
  419.         if FieldKind = fkData then
  420.           FieldDefs.Add(FieldName,DataType,Size,Required);
  421.       end;
  422.     end;
  423.   end;
  424.   FStream := TFileStream.Create (FTableName,
  425.     fmCreate or fmShareExclusive);
  426.   try
  427.     FillChar(fDBFHeader,SizeOf(TDbfHeader),0);
  428.     fDBFHeader.BytesInRecords := 0; // filled later
  429.     fDBFHeader.NumberOfRecords := 0; // empty
  430.     WriteHeader;
  431.     Offs:=0;
  432.     for Ix:=0 to FieldDefs.Count-1 do
  433.       begin
  434.         with FieldDefs.Items[Ix] do
  435.         begin
  436.           FillChar(Fld,SizeOf(TDbfField),#0);
  437.           Fld.FieldType := 'C';
  438.           Fld.Width := Size;
  439.           GetMem(FldName,SizeOf(FieldDefs.Items[Ix].Name));
  440.           OemToChar(PChar(FieldDefs.Items[Ix].Name),FldName);
  441.           for i := 1 to Length(FldName) do
  442.             Fld.FieldName[i] := FldName[i];
  443.           Fld.FieldName[Length(FldName)+1] := #0;
  444.           FreeMem(FldName);
  445.           Inc(Offs,Fld.Width);
  446.           FStream.Write(Fld,SizeOf(TDbfField));
  447.         end;
  448.       end;
  449.     fStartData := FStream.Position;
  450.     fDBFHeader.BytesInRecords := Offs;
  451.     FRecordSize := Offs+FRecordHeaderSize;
  452.     WriteHeader;
  453.   finally
  454.     // close the file
  455.     fStream.Free;
  456.     fStream := nil;
  457.   end;
  458. end;
  459. // ____________________________________________________________________________
  460. // TDBF.PackTable
  461. //Enhancement: Remove all deleted items from the table.
  462. Procedure TDBF.PackTable;
  463. var
  464.   NewStream, OldStream : tStream;
  465.   PC : PChar;
  466.   Ix : Integer;
  467. //  DescribF : TBDescribField;
  468.   NewDataFileHeader : tDBFHeader;
  469.   DataBuffer : Pointer;
  470.   NumberOfFields : integer;
  471.   Fld : TDBFField;
  472. BEGIN
  473.   OldStream := Nil;
  474.   NewStream := Nil;
  475.   CheckInactive;
  476. //  if Active then
  477. //    raise eBinaryDataSetError.Create ('Dataset must be closed before packing.');
  478.   if fTableName = '' then
  479.     raise EDBFError.Create('Table name not specified.');
  480.   if not FileExists (FTableName) then
  481.     raise EDBFError.Create('Table '+fTableName+' does not exist.');
  482.   PC := @fTablename[1];
  483.   CopyFile(PChar(PC),PChar(PC+',old'+#0),False);
  484.   // create the new file
  485.   if FieldDefs.Count = 0 then
  486.   begin
  487.     for Ix := 0 to FieldCount - 1 do
  488.     begin
  489.       with Fields[Ix] do
  490.       begin
  491.         if FieldKind = fkData then
  492.           FieldDefs.Add(FieldName,DataType,Size,Required);
  493.       end;
  494.     end;
  495.   end;
  496.   TRY
  497.     NewStream := TFileStream.Create (FTableName+',new',
  498.       fmCreate or fmShareExclusive);
  499.     OldStream := tFileStream.Create (fTableName+',old',
  500.       fmOpenRead or fmShareExclusive);
  501.     OldStream.ReadBuffer(NewDataFileHeader,SizeOf(TDbfHeader));
  502.     NewStream.WriteBuffer(NewDataFileHeader,SizeOf(TDbfHeader));
  503.     NumberOfFields := ((NewDataFileHeader.BytesInHeader-sizeof(TDbfHeader))div 32);
  504.     for IX := 0 to NumberOfFields do
  505.       BEGIN
  506.         OldStream.Read(Fld,SizeOf(TDbfField));
  507.         NewStream.Write(Fld,SizeOf(TDbfField));
  508.       END;
  509.     GetMem(DataBuffer,NewDataFileHeader.BytesInRecords);
  510.     REPEAT
  511.       IX := OldStream.Read(DataBuffer^,NewDataFileHeader.BytesInRecords);
  512.       if (IX = NewDataFileHeader.BytesInRecords) and (pRecordHeader(DataBuffer)^.DeletedFlag <> '*') then
  513.         NewStream.WRite(DataBuffer^,NewDataFileHeader.BytesInRecords);
  514.     Until IX <> NewDataFileHeader.BytesInRecords;
  515.     FreeMem(DataBuffer,NewDataFileHeader.BytesInRecords);
  516.   finally
  517.     // close the file
  518.     NewStream.Free;
  519.     OldStream.Free;
  520.   end;
  521.   CopyFile(PChar(PC+',new'+#0),PChar(PC),False);
  522.   DeleteFile(Pchar(PC+',new'+#0));
  523.   DeleteFile(Pchar(PC+',old'+#0));
  524. END;
  525. // ____________________________________________________________________________
  526. // TDBF._SwapRecords
  527. // Enhancement: Quick swap of two records.  Used primarily for sorting.
  528. Procedure TDBF._SwapRecords(Rec1,REc2:Integer);
  529. VAR
  530.   Buffer1, Buffer2 : PChar;
  531.   Bookmark1, BOokmark2 : TBookmarkFlag;
  532. BEGIN
  533.   Rec1 := Rec1 - 1;
  534.   Rec2 := Rec2 - 1;
  535.   if Rec1 < 0 then Exit;
  536.   if Rec2 < 0 then Exit;
  537.   Buffer1 := AllocRecordBuffer;
  538.   Buffer2 := AllocRecordBuffer;
  539.   _ReadRecord(Buffer1,Rec1);
  540.   _ReadRecord(Buffer2,Rec2);
  541.   Bookmark1 := GetBookmarkFlag(Buffer1);
  542.   Bookmark2 := GetBookmarkFlag(Buffer2);
  543.   SetBookmarkFlag(Buffer1,Bookmark2);
  544.   SetBookmarkFlag(Buffer2,Bookmark1);
  545.   _WriteRecord(Buffer1,Rec2);
  546.   _WriteRecord(Buffer2,Rec1);
  547.   StrDispose(Buffer1);
  548.   StrDispose(Buffer2);
  549. END;
  550. // ____________________________________________________________________________
  551. // TDBF._CompareRecords
  552. // Compare two records.  Returns -1 if REC1 < REC2, 0 if REC1 = REC2, or
  553. // 1 if REC1 > REC2.
  554. Function TDBF._CompareRecords(SortFields:Array of String;Rec1,Rec2:Integer):Integer; FAR;
  555. {-Compare the records Rec1, Rec2 and return -1 if Rec1 < Rec2, 0 if Rec1 = Rec2,
  556.   1 if Rec1 > Rec2 }
  557. VAR
  558.   IX : Integer;
  559.   Function CompareHelper(KeyId:String;Rec1,Rec2:Integer):Integer;
  560.   VAR
  561.     SKey1, SKey2 : String;
  562.     IKey1, IKey2 : Integer;
  563.     fKey1, fKey2 : Double;
  564.     dKey1, dKey2 : tDateTime;
  565.     CompareType : tFieldType;
  566.     KeyField : tField;
  567.   BEGIN
  568.     KeyField := FieldByName(KeyID);
  569.     CompareType := KeyField.DataType;
  570.     Case CompareType of
  571.       ftFloat,
  572.       ftCurrency,
  573.       ftBCD :
  574.         BEGIN
  575.           _ReadRecord(ActiveBuffer,Rec1-1);
  576.           fKey1 := KeyField.AsFloat;
  577.           _ReadRecord(ActiveBuffer,Rec2-1);
  578.           fKey2 := KeyField.AsFloat;
  579.           if fKey1 < fKey2 then
  580.             Result := -1
  581.           else
  582.             if fKey1 > fKey2 then
  583.               Result := 1
  584.             else
  585.               Result := 0;
  586.         END;
  587.       ftSmallInt,
  588.       ftInteger,
  589.       ftWord :
  590.         BEGIN
  591.           _ReadRecord(ActiveBuffer,Rec1-1);
  592.           IKey1 := KeyField.AsInteger;
  593.           _ReadRecord(ActiveBuffer,Rec2-1);
  594.           IKey2 := KeyField.AsInteger;
  595.           if IKey1 < IKey2 then
  596.             Result := -1
  597.           else
  598.             if IKey1 > IKey2 then
  599.               Result := 1
  600.             else
  601.               Result := 0;
  602.         END;
  603.       ftDate,
  604.       ftTime,
  605.       ftDateTime :
  606.         BEGIN
  607.           _ReadRecord(ActiveBuffer,Rec1-1);
  608.           dKey1 := KeyField.AsDateTime;
  609.           _ReadRecord(ActiveBuffer,Rec2-1);
  610.           dKey2 := KeyField.AsDateTime;
  611.           if dKey1 < dKey2 then
  612.             Result := -1
  613.           else
  614.             if dKey1 > dKey2 then
  615.               Result := 1
  616.             else
  617.               Result := 0;
  618.         END;
  619.       else
  620.         BEGIN
  621.           _ReadRecord(ActiveBuffer,Rec1-1);
  622.           SKey1 := KeyField.AsString;
  623.           _ReadRecord(ActiveBuffer,Rec2-1);
  624.           SKey2 := KeyField.AsString;
  625.           if SKey1 < SKey2 then
  626.             Result := -1
  627.           else
  628.             if SKey1 > SKey2 then
  629.               Result := 1
  630.             else
  631.               Result := 0;
  632.         END;
  633.     END;
  634.   END;
  635. BEGIN
  636.   IX := 0;
  637.   REPEAT // Loop through all available sortfields until not equal or no more sort fiels.
  638.     Result := CompareHelper(SortFields[IX],Rec1,Rec2);
  639.     Inc(IX);
  640.   UNTIL (Result <> 0) or (IX > High(SortFields));
  641. END;
  642. // ____________________________________________________________________________
  643. // TDBF.SortTable
  644. // Enhancement: Sort the table by the fields passed.
  645. Procedure TDBF.SortTable(SortFields : Array of String);
  646.   { This is the main sorting routine. It is passed the number of elements and the
  647.     two callback routines. The first routine is the function that will perform
  648.     the comparison between two elements. The second routine is the procedure that
  649.     will swap two elements if necessary } // Source: UNDU #8
  650.   procedure QSort(uNElem: Integer);
  651.   { uNElem - number of elements to sort }
  652.     procedure qSortHelp(pivotP: Integer; nElem: word);
  653.     label
  654.       TailRecursion,
  655.       qBreak;
  656.     var
  657.       leftP, rightP, pivotEnd, pivotTemp, leftTemp: word;
  658.       lNum: Integer;
  659.       retval: integer;
  660.     begin
  661.       TailRecursion:
  662.         if (nElem <= 2) then
  663.           begin
  664.             if (nElem = 2) then
  665.               begin
  666.                 rightP := pivotP +1;
  667.                 if (_CompareRecords(SortFields,pivotP, rightP) > 0) then
  668.                   _SwapRecords(pivotP, rightP);
  669.               end;
  670.             exit;
  671.           end;
  672.         rightP := (nElem -1) + pivotP;
  673.         leftP :=  (nElem shr 1) + pivotP;
  674.         { sort pivot, left, and right elements for "median of 3" }
  675.         if (_CompareRecords(SortFields,leftP, rightP) > 0) then _SwapRecords(leftP, rightP);
  676.         if (_CompareRecords(SortFields,leftP, pivotP) > 0) then _SwapRecords(leftP, pivotP)
  677.         else if (_CompareRecords(SortFields,pivotP, rightP) > 0) then _SwapRecords(pivotP, rightP);
  678.         if (nElem = 3) then
  679.           begin
  680.             _SwapRecords(pivotP, leftP);
  681.             exit;
  682.           end;
  683.         { now for the classic Horae algorithm }
  684.         pivotEnd := pivotP + 1;
  685.         leftP := pivotEnd;
  686.         repeat
  687.           retval := _CompareRecords(SortFields,leftP, pivotP);
  688.           while (retval <= 0) do
  689.             begin
  690.               if (retval = 0) then
  691.                 begin
  692.                   _SwapRecords(LeftP, PivotEnd);
  693.                   Inc(PivotEnd);
  694.                 end;
  695.               if (leftP < rightP) then
  696.                 Inc(leftP)
  697.               else
  698.                 goto qBreak;
  699.               retval := _CompareRecords(SortFields,leftP, pivotP);
  700.             end; {while}
  701.           while (leftP < rightP) do
  702.             begin
  703.               retval := _CompareRecords(SortFields,pivotP, rightP);
  704.               if (retval < 0) then
  705.                 Dec(rightP)
  706.               else
  707.                 begin
  708.                   _SwapRecords(leftP, rightP);
  709.                   if (retval <> 0) then
  710.                     begin
  711.                       Inc(leftP);
  712.                       Dec(rightP);
  713.                     end;
  714.                   break;
  715.                 end;
  716.             end; {while}
  717.         until (leftP >= rightP);
  718.       qBreak:
  719.         if (_CompareRecords(SortFields,leftP, pivotP) <= 0) then Inc(leftP);
  720.         leftTemp := leftP -1;
  721.         pivotTemp := pivotP;
  722.         while ((pivotTemp < pivotEnd) and (leftTemp >= pivotEnd)) do
  723.           begin
  724.             _SwapRecords(pivotTemp, leftTemp);
  725.             Inc(pivotTemp);
  726.             Dec(leftTemp);
  727.           end; {while}
  728.         lNum := (leftP - pivotEnd);
  729.         nElem := ((nElem + pivotP) -leftP);
  730.         if (nElem < lNum) then
  731.           begin
  732.             qSortHelp(leftP, nElem);
  733.             nElem := lNum;
  734.           end
  735.         else
  736.           begin
  737.             qSortHelp(pivotP, lNum);
  738.             pivotP := leftP;
  739.           end;
  740.         goto TailRecursion;
  741.       end; {qSortHelp }
  742.   begin
  743.     if (uNElem < 2) then  exit; { nothing to sort }
  744.     qSortHelp(1, uNElem);
  745.   end; { QSort }
  746. BEGIN
  747.   CheckActive;
  748.   if fReadOnly then
  749.     raise eDBFError.Create ('Dataset must be opened for read/write to perform sort.');
  750. //  if fDataFileHeader.DeletedCount > 0 then
  751. //    BEGIN
  752. //      Close;
  753. //      PackTable;
  754. //      Open;
  755. //    END;
  756.   QSort(FRecordCount {+ fDeletedCount});
  757.   First;
  758. END;
  759. // ____________________________________________________________________________
  760. // TDBF.UnsortTable
  761. // Used to help test the sort routine.  Attempts to generate a random
  762. // dispersment of the records in the dataset.
  763. Procedure TDBF.UnsortTable;
  764. Var
  765.   IX : Integer;
  766. BEGIN
  767.   First;
  768.   Randomize;
  769.   for IX := 0 to RecordCOunt do
  770.     BEGIN
  771.       _SwapRecords(IX,Random(RecordCount+1));
  772.     END;
  773.   First;
  774. END;
  775. ////////////////////////////////////////
  776. ////// Part II:
  777. ////// Bookmarks management and movement
  778. ////////////////////////////////////////
  779. // ____________________________________________________________________________
  780. // TDBF.InternalGotoBookmark
  781. // II: set the requested bookmark as current record
  782. procedure TDBF.InternalGotoBookmark (Bookmark: Pointer);
  783. var
  784.   ReqBookmark: Integer;
  785. begin
  786.   ReqBookmark := PInteger (Bookmark)^;
  787. //  ShowMessage ('InternalGotoBookmark: ' +
  788. //    IntToStr (ReqBookmark));
  789.   if (ReqBookmark >= 0) and (ReqBookmark < FRecordCount {+ fDeletedCount}) then
  790.     FCurrentRecord := ReqBookmark
  791.   else
  792.     raise eDBFError.Create ('Bookmark ' +
  793.       IntToStr (ReqBookmark) + ' not found');
  794. end;
  795. // ____________________________________________________________________________
  796. // TDBF.InternalSetToRecord
  797. // II: same as above (but passes a buffer)
  798. procedure TDBF.InternalSetToRecord (Buffer: PChar);
  799. var
  800.   ReqBookmark: Integer;
  801. begin
  802. //  ShowMessage ('InternalSetToRecord');
  803.   ReqBookmark := PRecInfo(Buffer + FRecordInfoOffset).Bookmark;
  804.   InternalGotoBookmark (@ReqBookmark);
  805. end;
  806. // ____________________________________________________________________________
  807. // TDBF.GetBookmarkFlag
  808. // II: retrieve bookmarks flags from buffer
  809. function TDBF.GetBookmarkFlag (
  810.   Buffer: PChar): TBookmarkFlag;
  811. begin
  812. //  ShowMessage ('GetBookmarkFlag');
  813.   Result := PRecInfo(Buffer + FRecordInfoOffset).BookmarkFlag;
  814. end;
  815. // ____________________________________________________________________________
  816. // TDBF.SetBookmarkFlag
  817. // II: change the bookmark flags in the buffer
  818. procedure TDBF.SetBookmarkFlag (Buffer: PChar;
  819.   Value: TBookmarkFlag);
  820. begin
  821. //  ShowMessage ('SetBookmarkFlag');
  822.   PRecInfo(Buffer + FRecordInfoOffset).BookmarkFlag := Value;
  823. end;
  824. // ____________________________________________________________________________
  825. // TDBF.GetBookmarkData
  826. // II: read the bookmark data from record buffer
  827. procedure TDBF.GetBookmarkData (
  828.   Buffer: PChar; Data: Pointer);
  829. begin
  830. //  ShowMessage ('GetBookmarkData: ' + IntToStr (PRecInfo(Buffer + FRecordInfoOffset).Bookmark));
  831.   PInteger(Data)^ :=
  832.     PRecInfo(Buffer + FRecordInfoOffset).Bookmark;
  833. end;
  834. // ____________________________________________________________________________
  835. // TDBF.SetBookmarkData
  836. // II: set the bookmark data in the buffer
  837. procedure TDBF.SetBookmarkData (
  838.   Buffer: PChar; Data: Pointer);
  839. begin
  840. //  ShowMessage ('SetBookmarkData: ' + IntToStr (PInteger(Data)^));
  841.   PRecInfo(Buffer + FRecordInfoOffset).Bookmark :=
  842.     PInteger(Data)^;
  843. end;
  844. // ____________________________________________________________________________
  845. // TDBF.InternalFirst
  846. // II: Go to a special position before the first record
  847. procedure TDBF.InternalFirst;
  848. begin
  849.   FCurrentRecord := BofCrack;
  850. end;
  851. // ____________________________________________________________________________
  852. // TDBF.InternalLast
  853. // II: Go to a special position after the last record
  854. procedure TDBF.InternalLast;
  855. begin
  856.   EofCrack := FRecordCount {+ fDeletedCount};
  857.   FCurrentRecord := EofCrack;
  858. end;
  859. // ____________________________________________________________________________
  860. // TDBF.GetRecordCount
  861. // II (optional): Record count
  862. function TDBF.GetRecordCount: Longint;
  863. begin
  864.   CheckActive;
  865.   Result := FRecordCount;
  866. end;
  867. // ____________________________________________________________________________
  868. // TDBF.GetRecNo
  869. // II (optional): Get the number of the current record
  870. function TDBF.GetRecNo: Longint;
  871. begin
  872.   UpdateCursorPos;
  873.   if FCurrentRecord < 0 then
  874.     Result := 1
  875.   else
  876.     Result := FCurrentRecord + 1;
  877. end;
  878. // ____________________________________________________________________________
  879. // TDBF.SetRecNo
  880. // II (optional): Move to the given record number
  881. procedure TDBF.SetRecNo(Value: Integer);
  882. begin
  883.   CheckBrowseMode;
  884.   if (Value > 1) and (Value <= (FRecordCount{+FDeletedCount})) then
  885.   begin
  886.     FCurrentRecord := Value - 1;
  887.     Resync([]);
  888.   end;
  889. end;
  890. //////////////////////////////////////////
  891. ////// Part III:
  892. ////// Record buffers and field management
  893. //////////////////////////////////////////
  894. // ____________________________________________________________________________
  895. // TDBF.GetRecordSize
  896. /// III: Determine the size of each record buffer in memory
  897. function TDBF.GetRecordSize: Word;
  898. begin
  899.   Result := FRecordSize; // data only
  900. end;
  901. // ____________________________________________________________________________
  902. // TDBF.AllocRecordBuffer
  903. /// III: Allocate a buffer for the record
  904. function TDBF.AllocRecordBuffer: PChar;
  905. begin
  906.   Result := StrAlloc(FRecordBufferSize+1);
  907. end;
  908. // ____________________________________________________________________________
  909. // TDBF.InternalInitRecord
  910. // III: Initialize the record (set to zero)
  911. procedure TDBF.InternalInitRecord(Buffer: PChar);
  912. (*var
  913.   Field : TField;
  914.   i : integer;
  915.   FieldOffset : integer;
  916.   S : string; *)
  917. begin
  918.   FillChar(Buffer^, FRecordBufferSize, 32);
  919. (*  for i := 0 to FieldCount-1 do
  920.     begin
  921.       Field := Fields[i];
  922.       FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;
  923.       if Field.DataType = ftString then
  924.         begin
  925.           pChar(Buffer+FieldOffset)^ := #0;
  926.         end
  927.       else if Field.DataType = ftFloat then
  928.         begin
  929.           pChar(Buffer+FieldOffset)^ := '0';
  930.           pChar(Buffer+FieldOffset+1)^ := #0;
  931.         end
  932.       else if Field.DataType = ftDate then
  933.         begin
  934.           S := '19900101';
  935.           CopyMemory(PChar(Buffer+FieldOffset),PChar(S),8);
  936.         end
  937.       else if Field.DataType = ftBoolean then
  938.         begin
  939.           pChar(Buffer+FieldOffset)^ := 'F';
  940.         end;
  941.     end; *)
  942. end;
  943. // ____________________________________________________________________________
  944. // TDBF.FreeRecordBuffer
  945. // III: Free the buffer
  946. procedure TDBF.FreeRecordBuffer (var Buffer: PChar);
  947. begin
  948.   StrDispose(Buffer);
  949. end;
  950. // ____________________________________________________________________________
  951. // TDBF.GetRecord
  952. // III: Retrieve data for current, previous, or next record
  953. // (eventually moving to it) and return the status
  954. function TDBF.GetRecord(Buffer: PChar;
  955.   GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  956. var
  957.   Acceptable : Boolean;
  958. begin
  959.   result := grOk;
  960.   if FRecordCount < 1 then
  961.     Result := grEOF
  962.   else
  963.     repeat
  964.       case GetMode of
  965.         gmCurrent :
  966.           begin
  967.             // ShowMessage ('GetRecord Current');
  968.             if (FCurrentRecord >= FRecordCount{+fDeletedCount}) or
  969.                 (FCurrentRecord < 0) then
  970.               Result := grError;
  971.           end;
  972.         gmNext :
  973.           begin
  974.             if (fCurrentRecord < (fRecordCount{+fDeletedCount})-1) then
  975.               Inc (FCurrentRecord)
  976.             else
  977.               Result := grEOF;
  978.           end;
  979.         gmPrior :
  980.           begin
  981.            if (fCurrentRecord > 0) then
  982.               Dec(fCurrentRecord)
  983.            else
  984.               Result := grBOF;
  985.           end;
  986.       end;
  987.       // fill record data area of buffer
  988.       if Result = grOK then
  989.         begin
  990.           _ReadRecord(Buffer, fCurrentRecord );
  991.           {FStream.Position := FDataFileHeader.StartData +
  992.           FRecordSize * FCurrentRecord;
  993.           FStream.ReadBuffer (Buffer^, FRecordSize);}
  994.           ClearCalcFields(Buffer);
  995.           GetCalcFields(Buffer);
  996.           with PRecInfo(Buffer + FRecordInfoOffset)^ do
  997.             begin
  998.               BookmarkFlag := bfCurrent;
  999.               Bookmark := FCurrentRecord;
  1000.             end;
  1001.         end
  1002.       else
  1003.         if (Result = grError) and DoCheck then
  1004.           raise eDBFError.Create('GetRecord: Invalid record');
  1005.       Acceptable := pRecordHeader(Buffer)^.DeletedFlag <> '*';
  1006.       if Filtered then
  1007.         Acceptable := Acceptable and (_ProcessFilter(Buffer));
  1008.       if (GetMode=gmCurrent) and Not Acceptable then
  1009.         Result := grError;
  1010.     until (Result <> grOK) or Acceptable;
  1011.   if ((Result=grEOF)or(Result=grBOF)) and Filtered and not (_ProcessFilter(Buffer)) then
  1012.     Result := grError;
  1013. end;
  1014. // ____________________________________________________________________________
  1015. // TDBF.InternalPost
  1016. // III: Write the current data to the file
  1017. procedure TDBF.InternalPost;
  1018. begin
  1019.   CheckActive;
  1020.   if State = dsEdit then
  1021.     begin
  1022.       // replace data with new data
  1023.       {FStream.Position := FDataFileHeader.StartData + (FRecordSize * FCurrentRecord);
  1024.       FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1025.       _WriteRecord (ActiveBuffer, fCurrentRecord);
  1026.     end
  1027.   else
  1028.     begin
  1029.       // always append
  1030.       InternalLast;
  1031.       {FStream.Seek (0, soFromEnd);
  1032.       FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1033.       pRecordHeader(ActiveBuffer)^.DeletedFlag := ' ';
  1034.       _AppendRecord(ActiveBuffer);
  1035.       Inc (FRecordCount);
  1036.     end;
  1037. end;
  1038. // ____________________________________________________________________________
  1039. // TDBF.InternalAddRecord
  1040. // III: Add the current data to the file
  1041. procedure TDBF.InternalAddRecord(Buffer:Pointer; Append:Boolean);
  1042. begin
  1043.   // always append
  1044.   InternalLast;
  1045.   // add record at the end of the file
  1046.   {FStream.Seek (0, soFromEnd);}
  1047.   pRecordHeader(ActiveBuffer)^.DeletedFlag := ' ';
  1048.   _AppendRecord(ActiveBuffer);
  1049.   {FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1050.   Inc (FRecordCount);
  1051. end;
  1052. // ____________________________________________________________________________
  1053. // TDBF.InternalDelete
  1054. // III: Delete the current record
  1055. procedure TDBF.InternalDelete;
  1056. begin
  1057.   CheckActive;
  1058.   // not supported in this version
  1059. {  raise eBinaryDataSetError.Create (
  1060.     'Delete: Operation not supported');}
  1061. //  pRecordHeader(ActiveBuffer)^.DeletedFlag := fDataFileHeader.LastDeleted;
  1062.   PChar(ActiveBuffer)^ := '*';
  1063.   _WriteRecord(ActiveBuffer,fCurrentRecord);
  1064.   {FStream.Position := FDataFileHeader.StartData + (FRecordSize * FCurrentRecord);
  1065.   FStream.WriteBuffer (ActiveBuffer^, FRecordSize);}
  1066. //  fDBFHeader.LastDeleted := GetRecNo;
  1067. //  Inc(fDeletedCount);
  1068. //  Dec(fRecordCount);
  1069. //  fDBFHeader.NumberOfRecords := fRecordCount;
  1070. //  WriteHeader;
  1071.   Resync([]);
  1072. end;
  1073. // ____________________________________________________________________________
  1074. // TDBF.GetFieldData
  1075. // III: Move data from record buffer to field
  1076. function TDBF.GetFieldData(Field:TField; Buffer:Pointer):Boolean;
  1077. var
  1078.   FieldOffset: Integer;
  1079.   S : string;
  1080.   Buf2 : PChar;
  1081.   i,l : integer;
  1082.   D : Double;
  1083.   n : integer;
  1084.   T : TDateTime;
  1085.   j : integer;
  1086.   OldDateFormat : string;
  1087. begin
  1088.   Result := False;
  1089.   Buf2 := ActiveBuffer;
  1090.   if (FRecordCount>0) and (Field.FieldNo > 0) and (Assigned(Buffer)) and (Assigned(Buf2))  then
  1091.     begin
  1092.       FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;
  1093.       if Field.DataType = ftString then
  1094.         begin
  1095.           l := Integer(FFileWidth[Field.FieldNo-1]);
  1096.           S := '';
  1097.           i := 0;
  1098.           While (Buf2[FieldOffset+i] <> #0) and (i<l) do
  1099.             begin
  1100.               S := S+pChar(Buf2+FieldOffset+i)^;
  1101.               inc(i);
  1102.             end;
  1103.           SetLength(S,l);
  1104.           S := Trim(S);
  1105.           CharToOemBuff(PChar(S), Buffer,l);
  1106.           Result := True;
  1107.         end
  1108.       else if Field.DataType = ftFloat then
  1109.         begin
  1110.           n := Integer(FFileWidth[Field.FieldNo-1]);
  1111.           S := '';
  1112.           for i := FieldOffset to FieldOffset+n-1 do
  1113.             S := S+pChar(Buf2+i)^;
  1114.           S := Trim(S);
  1115.           if S='' then
  1116.             Result := False
  1117.           else
  1118.             begin
  1119.               if (Pos('.',S) > 0) and (DecimalSeparator <> '.') then
  1120.                 S[Pos('.',S)] := DecimalSeparator;
  1121.               Result := True;
  1122.              try
  1123.               D := StrToFloat(S);
  1124.              except
  1125.               D := 0;
  1126.               Result := False;
  1127.              end;
  1128.               PDouble(Buffer)^ := D;
  1129.             end;
  1130.         end
  1131.       else if Field.DataType = ftDate then
  1132.         begin
  1133.           S := '';
  1134.           for j := 0 to 7 do
  1135.             S := S + pChar(Buf2+FieldOffset+j);
  1136.           SetLength(S,8);
  1137.           if (trim(S) = '') or (S='00000000') then
  1138.             Result := false
  1139.           else
  1140.             begin
  1141.               S := Copy(S,7,2)+DateSeparator+Copy(S,5,2)+DateSeparator+Copy(S,1,4);
  1142.               OldDateFormat := ShortDateFormat;
  1143.               ShortDateFormat := 'dd/mm/yyyy';
  1144.               t := StrToDate(S);
  1145.               ShortDateFormat := OldDateFormat;
  1146.               j := Trunc(pDouble(@t)^)+693594;
  1147.               pInteger(Buffer)^ := j;
  1148.               result := True;
  1149.             end;
  1150.         end
  1151.       else if Field.DataType = ftBoolean then
  1152.         begin
  1153.           Result := True;
  1154.           if PChar(Buf2+FieldOffset)^ in ['S','T','Y'] then
  1155.             pBoolean(Buffer)^ := True
  1156.           else if PChar(Buf2+FieldOffset)^ in ['N','F'] then
  1157.             pBoolean(Buffer)^ := False
  1158.           else
  1159.             Result := False;
  1160.         end
  1161.       else
  1162.         begin
  1163.           ShowMessage ('very bad error in get field data');
  1164.           Result := False;
  1165.         end;
  1166.     end;
  1167. end;
  1168. // ____________________________________________________________________________
  1169. // TDBF.SetFieldData
  1170. // III: Move data from field to record buffer
  1171. procedure TDBF.SetFieldData(Field: TField; Buffer: Pointer);
  1172. var
  1173.   FieldOffset: Integer;
  1174.   Buf2 : PChar;
  1175.   l,i,n:integer;
  1176.   S : string;
  1177.   D : TDateTime;
  1178.   j : integer;
  1179. begin
  1180.   Buf2 := ActiveBuffer;
  1181.   if (Field.FieldNo >= 0) and (Assigned(Buffer)) and (Assigned(Buf2)) then
  1182.     begin
  1183.       FieldOffset := Integer(FFileOffset[Field.FieldNo-1])+FRecordHeaderSize;
  1184.       if Field.DataType = ftString then
  1185.         begin
  1186.           l := Integer(FFileWidth[Field.FieldNo-1]);
  1187.           S := '';
  1188.           i := 0;
  1189.           While (PChar(Buffer)[i] <> #0) and (i<l) do
  1190.             begin
  1191.               S := S+PChar(Buffer)[i];
  1192.               inc(i);
  1193.             end;
  1194.           SetLength(S,l);
  1195.           OemToCharBuff(PChar(S),PChar(Buf2+FieldOffset),l);
  1196.         end
  1197.       else if Field.DataType = ftFloat then
  1198.         begin
  1199.           n := Integer(FFileWidth[Field.FieldNo-1]);
  1200.           Str(pDouble(Buffer)^:n:Integer(FFileDecimals[Field.FieldNo-1]),S);
  1201.           while Length(S)<n do
  1202.             S :=  ' '+S;
  1203.           if (Pos(DecimalSeparator,S) > 0) and (DecimalSeparator <> '.') then
  1204.             S[Pos(DecimalSeparator,S)] := '.';
  1205.           CopyMemory(Pchar(Buf2+FieldOffset),PChar(S),n);
  1206.         end
  1207.       else if Field.DataType = ftDate then
  1208.         begin
  1209.           j := pInteger(Buffer)^-693594;
  1210.           pDouble(@d)^ := j;
  1211.           S := FormatDateTime('yyyymmdd',d);
  1212.           StrLCopy(pChar(Buf2+FieldOffset),pChar(S),8);
  1213.         end
  1214.       else if Field.DataType = ftBoolean then
  1215.         begin
  1216.           if pBoolean(Buffer)^ then
  1217.             PChar(Buf2+FieldOffset)^ := 'T'
  1218.           else
  1219.             PChar(Buf2+FieldOffset)^ := 'F';
  1220.         end
  1221.       else
  1222.         ShowMessage ('very bad error in setfield data');
  1223.       DataEvent (deFieldChange, Longint(Field));
  1224.     end;
  1225. end;
  1226. // ____________________________________________________________________________
  1227. // TDBF.InternalHandleException
  1228. // default exception handling
  1229. procedure TDBF.InternalHandleException;
  1230. begin
  1231.   // standard exception handling
  1232.   Application.HandleException(Self);
  1233. end;
  1234. Function TDBF._ProcessFilter(Buffer:PChar):boolean;
  1235. var
  1236.   FilterExpresion : string;
  1237.   PosComp : integer;
  1238.   FName : string;
  1239.   FieldPos : integer;
  1240.   FieldOffset : integer;
  1241.   FieldValue : Variant;
  1242.   TestValue : Variant;
  1243.   FieldText : string;
  1244.   OldShortDateFormat : string;
  1245. begin
  1246.   FilterExpresion := Filter;
  1247.   PosComp := Pos('>',FilterExpresion);
  1248.   if PosComp=0 then
  1249.     PosComp := Pos('<',FilterExpresion);
  1250.   if PosComp=0 then
  1251.     PosComp := Pos('=',FilterExpresion);
  1252.   if PosComp=0 then
  1253.     begin
  1254.       _ProcessFilter := True;
  1255.       Exit;
  1256.     end;
  1257.   FName := Trim(Copy(FilterExpresion,1,PosComp-1));
  1258.   FieldPos := FieldDefs.IndexOf(FName);
  1259.   FieldOffset := integer(FFileOffset[FieldPos]);
  1260.   if FieldPos < 0 then
  1261.     _ProcessFilter := True
  1262.   else if FieldDefs.Items[FieldPos].DataType = ftString then
  1263.     begin // STRING
  1264.      try
  1265.       FieldValue := '';
  1266.       FieldOffset := FieldOffset+1;
  1267.       While (Buffer[FieldOffset]<>#0) and (Length(FieldValue)<integer(FFileWidth[FieldPos])) do
  1268.         begin
  1269.           FieldValue := FieldValue + Buffer[FieldOffset];
  1270.           FieldOffset := FieldOffset+1;
  1271.         end;
  1272.       FieldValue := Trim(FieldValue);
  1273.       TestValue := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-2));
  1274.       if FilterExpresion[PosComp]='=' then
  1275.         _ProcessFilter := (FieldValue=TestValue)
  1276.       else if FilterExpresion[PosComp]='>' then
  1277.         begin
  1278.           if FilterExpresion[PosComp+1]='=' then
  1279.             _ProcessFilter := (FieldValue>=Copy(TestValue,2,(Length(TestValue)-1)))
  1280.           else
  1281.             _ProcessFilter := (FieldValue>TestValue);
  1282.         end
  1283.       else if FilterExpresion[PosComp]='<' then
  1284.         begin
  1285.           if FilterExpresion[PosComp+1]='=' then
  1286.             _ProcessFilter := (FieldValue<=Copy(TestValue,2,(Length(TestValue)-1)))
  1287.           else
  1288.             _ProcessFilter := (FieldValue<TestValue);
  1289.         end
  1290.       else
  1291.         _ProcessFilter := False;
  1292.      except
  1293.        _ProcessFilter := False;
  1294.      end;
  1295.     end
  1296.   else if FieldDefs.Items[FieldPos].DataType = ftFloat then
  1297.     begin // FLOAT
  1298.      try
  1299.       FieldText := '';
  1300.       FieldOffset := FieldOffset+1;
  1301.       While (Buffer[FieldOffset]<>#0) and (Length(FieldText)<integer(FFileWidth[FieldPos])) do
  1302.         begin
  1303.           FieldText := FieldText + Buffer[FieldOffset];
  1304.           FieldOffset := FieldOffset+1;
  1305.         end;
  1306.       FieldText := Trim(FieldText);
  1307.       if Pos('.',FieldText)>0 then
  1308.         FieldText[Pos('.',FieldText)] := DecimalSeparator;
  1309.       FieldValue := StrToFloat(FieldText);
  1310.       if FilterExpresion[PosComp+1]='='then
  1311.         FieldText := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-1))
  1312.       else
  1313.         FieldText := Trim(Copy(FilterExpresion,PosComp+1,Length(FilterExpresion)-PosComp));
  1314.       if Pos('.',FieldText)>0 then
  1315.         FieldText[Pos('.',FieldText)] := DecimalSeparator;
  1316.       TestValue := StrToFloat(FieldText);
  1317.       if FilterExpresion[PosComp]='=' then
  1318.         _ProcessFilter := (FieldValue=TestValue)
  1319.       else if FilterExpresion[PosComp]='>'then
  1320.         begin
  1321.           if FilterExpresion[PosComp+1]='='then
  1322.             _ProcessFilter := (FieldValue>=TestValue)
  1323.           else
  1324.             _ProcessFilter := (FieldValue>TestValue);
  1325.         end
  1326.       else if FilterExpresion[PosComp]='<'then
  1327.         begin
  1328.           if FilterExpresion[PosComp+1]='='then
  1329.             _ProcessFilter := (FieldValue<=TestValue)
  1330.           else
  1331.             _ProcessFilter := (FieldValue<TestValue);
  1332.         end
  1333.       else
  1334.         _ProcessFilter := False;
  1335.      except
  1336.       _ProcessFilter := False;
  1337.      end;
  1338.     end
  1339.   else if FieldDefs.Items[FieldPos].DataType = ftDate then
  1340.     begin // DATE
  1341.       OldShortDateFormat := ShortDateFormat;
  1342.      try
  1343.       FieldText := '';
  1344.       FieldOffset := FieldOffset+1;
  1345.       While (Buffer[FieldOffset]<>#0) and (Length(FieldText)<integer(FFileWidth[FieldPos])) do
  1346.         begin
  1347.           FieldText := FieldText + Buffer[FieldOffset];
  1348.           FieldOffset := FieldOffset+1;
  1349.         end;
  1350.       FieldText := Trim(FieldText);
  1351.       FieldText := Copy(FieldText,1,4)+DateSeparator+Copy(FieldText,5,2)+DateSeparator+Copy(FieldText,7,2);
  1352.       ShortDateFormat := 'yyyy/mm/dd';
  1353.       FieldValue := StrToDate(FieldText);
  1354.       if FilterExpresion[PosComp+1]='=' then
  1355.         FieldText := Trim(Copy(FilterExpresion,PosComp+2,Length(FilterExpresion)-PosComp-1))
  1356.       else
  1357.         FieldText := Trim(Copy(FilterExpresion,PosComp+1,Length(FilterExpresion)-PosComp));
  1358.       FieldText := Copy(FieldText,1,4)+DateSeparator+Copy(FieldText,5,2)+DateSeparator+Copy(FieldText,7,2);
  1359.       TestValue := StrToDate(FieldText);
  1360.       if FilterExpresion[PosComp]='=' then
  1361.         begin
  1362.           _ProcessFilter := (FieldValue=TestValue);
  1363.         end
  1364.       else if FilterExpresion[PosComp]='>' then
  1365.         begin
  1366.           if FilterExpresion[PosComp+1]='='then
  1367.             _ProcessFilter := (FieldValue>=TestValue)
  1368.           else
  1369.             _ProcessFilter := (FieldValue>TestValue);
  1370.         end
  1371.       else if FilterExpresion[PosComp]='<' then
  1372.         begin
  1373.           if FilterExpresion[PosComp+1]='='then
  1374.             _ProcessFilter := (FieldValue<=TestValue)
  1375.           else
  1376.             _ProcessFilter := (FieldValue<TestValue);
  1377.         end
  1378.       else
  1379.         _ProcessFilter := False;
  1380.      except
  1381.       _ProcessFilter := False;
  1382.      end;
  1383.       ShortDateFormat := OldShortDateFormat;
  1384.     end
  1385.   else
  1386.     _ProcessFilter := False;
  1387. end;
  1388. {******************************************************************************}
  1389. {* Property Editors Code                                                       *}
  1390. {******************************************************************************}
  1391. procedure TFilenameProperty.Edit;
  1392. var
  1393.   FileOpen: TOpenDialog;
  1394. begin
  1395.   FileOpen := TOpenDialog.Create(Nil);
  1396.   FileOpen.Filename := GetValue;
  1397.   FileOpen.Filter := 'dBase Files (*.DBF)|*.DBF|All Files (*.*)|*.*';
  1398.   FileOpen.Options := FileOpen.Options + [ofPathMustExist, ofFileMustExist];
  1399.   try
  1400.     if FileOpen.Execute then SetValue(FileOpen.Filename);
  1401.   finally
  1402.     FileOpen.Free;
  1403.   end;
  1404. end;
  1405. function TFilenameProperty.GetAttributes: TPropertyAttributes;
  1406. begin
  1407.   Result := [paDialog, paRevertable];
  1408. end;
  1409. procedure Register;
  1410. begin
  1411.   RegisterComponents('Terabyte', [TDBF]);
  1412.   RegisterPropertyEditor(TypeInfo(String), TDBF, 'TableName', TFileNameProperty);
  1413. end;
  1414. end.