123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895 |
- unit dbf;
- {===============================================================================
- || TDbf Component || http://tdbf.netfirms.com ||
- ===============================================================================}
- interface
- uses
- {$ifdef fpc}
- SysUtils, Classes, db;
- {$else}
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- Db, DsgnIntf, ExptIntf;
- {$endif}
- // If you got a compilation error here or asking for dsgntf.pas, then just add
- // this file in your project:
- // dsgnintf.pas in 'C:\Program Files\Borland\Delphi5\Source\Toolsapi\dsgnintf.pas'
- const
- _MAJOR_VERSION = 3;
- _MINOR_VERSION = 007;
- {$ifdef VER100} // Delphi 3
- {$define DELPHI_3}
- {$endif}
- {$ifdef VER110} // CBuilder 3
- {$define DELPHI_3}
- {$endif}
- {$ifdef unix}
- DirSeparator = '/';
- {$else}
- DirSeparator = '\';
- {$endif}
- //====================================================================
- // Delphi is a bit to permissive for me, I mean protected doesn't work within
- // one unit. So i decided that convention:
- // private member begins by '_'
- // It's forbidden to access any '_something' except from the class where it
- // is defined. To check that, I just have to look for '._' anywhere in the code.
- //====================================================================
- type
- //====================================================================
- //=== Common exceptions and constants
- //====================================================================
- EBinaryDataSetError = class (Exception);
- EFieldToLongError = class (Exception);
- xBaseVersion = (xBaseIII,xBaseIV,xBaseV);
- //====================================================================
- //=== Utility classes
- //====================================================================
- TPagedFile = class(TObject)
- protected
- Stream : TStream;
- HeaderSize : Integer;
- RecordSize : Integer;
- _cntuse:integer;
- _Filename:string;
- public
- constructor Create(const FileName: string; Mode: Word);
- destructor Destroy; override;
- procedure Release;
- function CalcRecordCount:Integer;
- procedure _Seek(page:Integer);
- procedure ReadRecord(IntRecNum:Integer;Buffer:Pointer);
- procedure WriteRecord(IntRecNum:Integer;Buffer:Pointer);
- end;
- //====================================================================
- //=== Dbf support (first part)
- //====================================================================
- rDbfHdr = record
- VerDBF : byte; // 0
- Year : byte; // 1
- Month : byte; // 2
- Day : byte; // 3
- RecordCount : Integer; // 4-7
- FullHdrSize : word; // 8-9
- RecordSize : word; // 10-11
- Dummy1 : Word; // 12-13
- IncTrans : byte; // 14
- Encrypt : byte; // 15
- Dummy2 : Integer; // 16-19
- Dummy3 : array[20..27] of byte; // 20-27
- MDXFlag : char; // 28
- Language : char; // 29
- dummy4 : word; // 30-31
- end;
- //====================================================================
- TMyFieldInfo = class
- public
- FieldName:string;
- Size:Integer;
- Prec:Integer;
- Offset:Integer;
- end;
- //====================================================================
- TDbfFile = class(TPagedFile)
- protected
- _RecordBufferSize:integer;
- _DataHdr : rDbfHdr;
- _DbfVersion : xBaseVersion;
- _MyFieldInfos: TList;
- public
- constructor Create(const FileName: string; Mode: Word);
- destructor Destroy; override;
- function RecordCount:integer;
- procedure CreateFieldDefs(FieldDefs:TFieldDefs);
- procedure ClearMyFieldInfos;
- procedure DbfFile_CreateTable(FieldDefs:TFieldDefs);
- procedure DbfFile_PackTable;
- function GetFieldInfo(FieldName:string):TMyFieldInfo;
- function GetFieldData(Column:Integer;DataType:TFieldType; Src,Dst: Pointer): Boolean;
- procedure SetFieldData(Column:integer;DataType:TFieldType; Src,Dst: Pointer);
- procedure WriteHeader;
- end;
- //====================================================================
- //=== Index support
- //====================================================================
- TIndex = class;
- //====================================================================
- rNdxHdr = record
- startpage : Integer; // 0..3
- nbPage : Integer; // 4..7
- keyformat: Char; //8
- keytype : char; //9
- dummy : Word; // 10..11
- keylen : Word; // 12..13
- nbkey : Word; // 14..15
- skeytype : Word; // 16..17
- keyreclen : Word; // 18..19
- dummy2 : Word; // 20..21
- dummy3 : Byte; // 22
- Unique : Byte; // 23
- KeyDesc : array[0..255] of char; // 24...
- end;
- rMdxTag = record
- pageno : Integer; // 0..3
- tagname : array [0..11] of char; // 4..14
- keyformat : byte; // 15
- forwardTag1 : char; // 16
- forwardTag2 : byte; // 17
- backwardTag : byte; // 18
- dummy : byte; // 19
- keytype : byte; // 20
- end;
- NdxKeyType = (N,C);
- PNdxPage = ^rNdxPage;
- rNdxPage = record
- NbEntries : longint; // 0..3 lower page
- Entries : ARRAY [0..507] OF char;
- end;
- PNdxentry = ^rNdxentry;
- rNdxentry = record
- _LowerPage : longint; // 0..3 lower page
- RecNo : Longint; // 4..7 recno
- case NdxKeyType of
- N: ( NKey: double);
- C: ( CKey: array [0..503] of char);
- end;
- //====================================================================
- rMdxHdr = record
- MdxHdr : byte; // 0
- Year : byte; // 1
- Month : byte; // 2
- Day : byte; // 3
- FileName : array[0..15] of char; // 4..19 of byte
- BlockSize : word; // 20 21
- BlockAdder : word; // 22 23
- IndexFlag : byte; // 24
- NoTag : byte; // 25
- TagSize : byte; // 26
- Dummy1 : byte; // 27
- TagUsed : word; // 28..29
- Dummy2 : word; // 30..31
- NbPage : Integer; // 32..35
- FreePage : Integer; // 36..39
- BlockFree : Integer; // 40..43
- UpdYear : byte; // 44
- UpdMonth : byte; // 45
- UpdDay : byte; // 46
- end;
- //====================================================================
- TIndexFile = class(TPagedFile)
- protected
- _IndexVersion : xBaseVersion;
- _MdxHdr : rMdxHdr;
- public
- constructor Create(const FileName: string; Mode: Word);
- destructor Destroy; override;
- end;
- //====================================================================
- PIndexPosInfo = ^TIndexPage;
- TIndexPage = class
- protected
- _Index : TIndex;
- _PageNo : Integer;
- _EntryNo : Integer;
- Entry : PNdxentry;
- _LowerLevel : TIndexPage;
- _UpperLevel : TIndexPage;
- _PageBuff:rNdxPage;
- procedure LocalFirst;
- procedure LocalLast;
- function LocalPrev:boolean;
- function LocalNext:boolean;
- function LastEntryNo:integer;
- function LocalInsert(Recno:Integer; Buffer:PChar; LowerPage:integer):boolean;
- function LocalDelete:boolean;
- function GetPEntry(EntryNo:integer):PNdxEntry;
- procedure First;
- procedure Last;
- function Prev:boolean;
- function Next:boolean;
- procedure Write;
- procedure AddNewLevel;
- public
- constructor Create(Parent:TIndex);
- destructor Destroy; override;
- procedure SetPageNo(page:Integer);
- procedure SetEntryNo(entryno:Integer);
- procedure WritePage(Page:integer);
- function FindNearest(Recno:integer; Key:PChar):integer;
- function Insert(Recno:integer; Buffer:pchar; LowerPage:integer):boolean;
- procedure SetEntry(Recno:integer; key:pchar; LowerPage:integer);
- function Delete:boolean;
- function LowerLevel : TIndexPage;
- end;
- //====================================================================
- TIndex = class(TObject)
- protected
- _IndexFile:TIndexFile;
- _NdxHdr:rNdxHdr;
- _Root:TIndexPage;
- _TagPosition:Integer;
- _FieldPos : integer;
- _FieldLen : integer;
- _NbLevel : integer;
- _RootPage: integer;
- function Pos:TIndexPage;
- public
- IndexRecNo:integer;
- function Prev:boolean;
- function Next:boolean;
- procedure First;
- procedure Last;
- function Find(Recno:integer; Buffer:PChar; var pPos:TIndexPage):integer;
- procedure Insert(Recno:integer; Buffer:PChar);
- function Delete:boolean;
- procedure GotoKey(Recno:integer; Buffer:PChar);
- procedure Update(Recno: integer; PrevBuffer,NewBuffer: PChar);
- // procedure ResyncInd;
- function GetRealRecNo: Integer;
- constructor Create(Parent:TIndexFile; RootPage:integer;CreateIt:boolean);
- procedure InitFieldDef(dbfFile:TDbfFile;FieldDesc:string);
- destructor Destroy; override;
- // optionnal
- function GuessRecordCount: Integer;
- function GuessRecNo: Integer;
- end;
- //====================================================================
- //=== Memo and binary fields support
- //====================================================================
- rDbtHdr = record
- NextBlock:Longint;
- Dummy : array [4..7] of byte;
- _dbfFile : array [0..7] of Byte; //8..15
- bVer : Byte; //16
- Dummy2 : array [17..19] of byte;
- BlockLen: Word;
- end;
- //====================================================================
- TDbtFile = class(TPagedFile)
- protected
- _DbtVersion:xBaseVersion;
- _MemoHdr:rDbtHdr;
- public
- constructor Create(const FileName: string; Mode: Word; Ver:xBaseVersion);
- procedure ReadMemo(recno:Integer;Dst:TStream);
- procedure WriteMemo(var MemoRecno:Integer;ReadSize:Integer;Src:TStream);
- end;
- //====================================================================
- TMyBlobFile = class(TMemoryStream)
- public
- Mode: TBlobStreamMode;
- Field:TField;
- MemoRecno:Integer;
- ReadSize:Integer;
- constructor Create(ModeVal:TBlobStreamMode; FieldVal:TField);
- destructor destroy; override;
- end;
- //====================================================================
- //=== Dbf support 2
- //====================================================================
- rFieldHdrIII = record
- FieldName : array[0..10] of char;
- FieldType : char; // 11
- Dummy : array[12..15] of byte;
- FieldSize : byte; // 16
- FieldPrecision : byte; //17
- dummy2 : array[18..31] of byte;
- end;
- //====================================================================
- rFieldHdrV = record
- FieldName : array[0..10] of char;
- Dummy0 : array[11..31] of byte;
- FieldType : char; // 32
- FieldSize : byte; // 33
- FieldPrecision : byte; //34
- dummy2 : array[35..47] of byte;
- end;
- //====================================================================
- PBookMarkData = ^rBookMarkData;
- rBookmarkData = record
- RecNo:longint;
- end;
- //====================================================================
- rBeforeRecord = record
- BookmarkData: rBookmarkData;
- BookmarkFlag: TBookmarkFlag;
- //... record come here
- end;
- //====================================================================
- pDbfRecord = ^rDbfRecord;
- rDbfRecord = record
- BookmarkData: rBookmarkData;
- BookmarkFlag: TBookmarkFlag;
- DeletedFlag : char;
- Fields : array[0..4000] of char;
- end;
- //====================================================================
- PRecInfo = ^TRecInfo;
- TRecInfo = record
- Bookmark: Longint;
- IdxBookmark: Longint;
- BookmarkFlag: TBookmarkFlag;
- end;
- //====================================================================
- pRecordHdr = ^tRecordHdr;
- tRecordHdr = record
- DeletedFlag : char;
- end;
- // and at LEAST the most useful class : TDbf
- //====================================================================
- TDbf = class(TDataSet)
- private
- _ShowDeleted:boolean;
- _TableName: string; // table path and file name
- _RunTimePath: string; // table path and file name
- _DesignTimePath: string; // table path and file name
- _ReadOnly : Boolean;
- _FilterBuffer:pchar;
- _PrevBuffer:pchar;
- _IndexFiles:TStrings;
- protected
- function _FullRecordSize:integer;
- function _FilterRecord(Buffer: PChar): Boolean;
- procedure _OpenFiles(CreateIt:boolean);
- procedure _CloseFiles;
- procedure _ResyncIndexes(Buffer: PChar);
- function _GetIndexName: string;
- procedure _SetIndexName(const Value: string);
- function _GetIndex(filename:string):TIndex;
- function _GetPath:string;
- function _ComponentInfo:string;
- public
- { my own methods and properties}
- { most looks like ttable functions but they are not tdataset related
- I use the same syntax to facilitate the conversion between bde and tdbf }
- easyfilter:string;
- procedure CreateTable; //(FieldDefs:TFieldDefs);
- procedure DeleteIndex(const AName: string);
- property IndexName: string read _GetIndexName write _SetIndexName;
- {$ifdef DELPHI_3}
- procedure AddIndex(const IndexName, Fields: String; Options: TIndexOptions);
- {$else}
- {$ifndef FPC}
- procedure AddIndex(const IndexName, Fields: String; Options: TIndexOptions; const DescFields: String='');
- {$else}
- procedure AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions);
- procedure AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions; const DescFields: String);
- {$endif}
- {$endif}
- procedure CloseIndexFile(const IndexFileName: string);
- procedure OpenIndexFile(AnIndexName:string);
- procedure PackTable;
- public
- { abstract methods }
- function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override; {virtual abstract}
- {virtual methods (mostly optionnal) }
- function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override; {virtual}
- {$ifdef DELPHI_3}
- procedure Translate(Src, Dest: PChar; ToOem: Boolean); override; {virtual}
- {$else}
- function Translate(Src, Dest: PChar; ToOem: Boolean): Integer; override; {virtual}
- {$endif}
- procedure ClearCalcFields(Buffer : PChar); override;
- protected
- { abstract methods }
- function AllocRecordBuffer: PChar; override; {virtual abstract}
- procedure FreeRecordBuffer(var Buffer: PChar); override; {virtual abstract}
- procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override; {virtual abstract}
- function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override; {virtual abstract}
- function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override; {virtual abstract}
- function GetRecordSize: Word; override; {virtual abstract}
- procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override; {virtual abstract}
- procedure InternalClose; override; {virtual abstract}
- procedure InternalDelete; override; {virtual abstract}
- procedure InternalFirst; override; {virtual abstract}
- procedure InternalGotoBookmark(ABookmark: Pointer); override; {virtual abstract}
- procedure InternalHandleException; override; {virtual abstract}
- procedure InternalInitFieldDefs; override; {virtual abstract}
- procedure InternalInitRecord(Buffer: PChar); override; {virtual abstract}
- procedure InternalLast; override; {virtual abstract}
- procedure InternalOpen; override; {virtual abstract}
- procedure InternalPost; override; {virtual abstract}
- procedure InternalSetToRecord(Buffer: PChar); override; {virtual abstract}
- function IsCursorOpen: Boolean; override; {virtual abstract}
- procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override; {virtual abstract}
- procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override; {virtual abstract}
- procedure SetFieldData(Field: TField; Buffer: Pointer); override; {virtual abstract}
- {virtual methods (mostly optionnal) }
- function GetRecordCount: Integer; override; {virtual}
- function GetRecNo: Integer; override; {virtual}
- procedure SetRecNo(Value: Integer); override; {virual}
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- published
- property ComponentInfo: string read _ComponentInfo;
- property TableName: string read _TableName write _TableName;
- property RunTimePath: string read _RunTimePath write _RunTimePath;
- property DesignTimePath: string read _DesignTimePath write _DesignTimePath;
- property ReadOnly : Boolean read _ReadOnly write _Readonly default False;
- property ShowDeleted:boolean read _ShowDeleted write _ShowDeleted;
- // redeclared data set properties
- property Active;
- property Filtered;
- property BeforeOpen;
- property AfterOpen;
- property BeforeClose;
- property AfterClose;
- property BeforeInsert;
- property AfterInsert;
- property BeforeEdit;
- property AfterEdit;
- property BeforePost;
- property AfterPost;
- property BeforeCancel;
- property AfterCancel;
- property BeforeDelete;
- property AfterDelete;
- property BeforeScroll;
- property AfterScroll;
- property OnCalcFields;
- property OnDeleteError;
- property OnEditError;
- property OnFilterRecord;
- property OnNewRecord;
- property OnPostError;
- //my datas....
- protected
- _IsCursorOpen:boolean;
- _PhysicalRecno:integer;
- _CurIndex: TIndex;
- _Indexes:TList; // index
- _indexFile : TIndexFile;
- _dbtFile : TDbtFile;
- public
- _dbfFile:TDbfFile;
- property PhysicalRecno:integer read _PhysicalRecno;
- function _RecordDataSize:integer;
- end;
- {$ifndef fpc}
- procedure Register;
- {$endif}
- var
- tDbf_TrimFields : boolean;
- implementation
- var
- _PagedFiles : TList;
- //====================================================================
- // Some types and consts which are not useful in the interface.
- //====================================================================
- (*
- * tSmallint 16 bits = -32768 to 32767
- * 123456 = 6 digit max
- * ftInteger 32 bits = -2147483648 to 2147483647
- * 12345678901 = 11 digits max
- * ftLargeInt 64 bits = -9223372036854775808 to 9223372036854775807
- * 12345678901234567890 = 20 digits max
- *)
- const
- DIGITS_SMALLINT = 6;
- DIGITS_INTEGER = 11;
- DIGITS_LARGEINT = 20;
- sDBF_DEC_SEP= '.';
- type
- rAfterHdrIII = record // Empty
- end;
- rAfterHdrV = record
- Dummy : array[32..67] of byte;
- end;
- PMdxTag = ^rMdxTag;
- rMdxTagHdr = record
- RootPage : longint;// 0..3
- FilePages : longint;// 4..7
- KeyFormat : byte; // 8
- KeyType : char; // 9
- dummy : word; // 10..11
- IndexKeyLength : word; // 12..13
- MaxNbKeys : word; // 14..15
- SecondKeyType : word; // 16..17
- IndexKeyItemLen : word; // 18..19
- dummy2 : array [20..22] of byte;
- UniqueFlag : byte; // 23
- end;
- rAfterHdrV3 = record
- Dummy : array[12..31] of byte;
- end;
- rAfterHdrV4 = record
- Dummy : array[12..67] of byte;
- end;
- rFieldHdrV3 = record
- FieldName : array[0..10] of char;
- FieldType : char; // 11
- Dummy : array[12..15] of byte;
- FieldSize : byte; // 16
- FieldPrecision : byte; //17
- dummy2 : array[18..31] of byte;
- end;
- rFieldHdrV4 = record
- FieldName : array[0..10] of char;
- Dummy0 : array[11..31] of byte;
- FieldType : char; // 32
- FieldSize : byte; // 33
- FieldPrecision : byte; //34
- dummy2 : array[35..47] of byte;
- end;
- PDouble = ^double;
- //====================================================================
- // Now some common functions and procedure
- //====================================================================
- // ****************************************************************************
- // International separator
- // thanks to Bruno Depero from Italy
- // and Andreas Wöllenstein from Denmark
- function DbfStrToFloat(s: string): Extended;
- var iPos: integer;
- eValue: extended;
- begin
- iPos:= Pos(sDBF_DEC_SEP, s);
- if iPos> 0 then
- s[iPos]:= DecimalSeparator;
- {$ifndef fpc}
- if TextToFloat(pchar(s), eValue, fvExtended) then
- {$else}
- Val(s,eValue,Ipos);
- If Ipos=0 then
- {$endif}
- Result:= eValue
- else Result:= 0;
- end;
- function FloatToDbfStr(f: Extended; size, prec: integer): string;
- var iPos: integer;
- begin
- Result:= FloatToStrF(f, ffFixed, Size, prec);
- iPos:= Pos(DecimalSeparator, Result);
- if iPos> 0 then
- Result[iPos]:= sDBF_DEC_SEP;
- end;
- procedure MyMove(Source, Dest:PChar; Count: Integer);
- var
- c:char;
- i:integer;
- begin
- i:=0;
- while i<Count do begin
- c:=PChar(Source)[i];
- if c=#0 then break;
- PChar(Dest)[i]:=c;
- Inc(i);
- end;
- while i<Count do begin
- PChar(Dest)[i]:=' ';
- Inc(i);
- end;
- end;
- //====================================================================
- // TPagedFile
- //====================================================================
- function GetPagedFile(FileName: string):TPagedFile;
- var
- idx:integer;
- idf:TPagedFile;
- begin
- FileName:=LowerCase(FileName);
- for idx:=0 to _PagedFiles.Count-1 do begin
- idf:= TPagedFile(_PagedFiles[idx]);
- if idf._FileName=FileName then begin
- result:=idf;
- exit;
- end;
- end;
- result:=nil;
- end;
- procedure TPagedFile.Release;
- var
- i: integer;
- begin
- dec(_cntuse);
- if _cntuse<=0 then begin
- i:=_PagedFiles.IndexOf(self);
- if i>=0 then _PagedFiles.Delete(i);
- Free;
- end;
- end;
- function TPagedFile.CalcRecordCount:Integer;
- begin
- if RecordSize = 0 then Result:=0
- else Result:=(Stream.Size - HeaderSize) div RecordSize;
- end;
- constructor TPagedFile.Create(const FileName: string; Mode: Word);
- begin
- if filename='' then Stream:=TMemoryStream.Create()
- else begin
- Stream:=TFileStream.Create(FileName,Mode);
- end;
- HeaderSize:=0;
- RecordSize:=0;
- _cntuse:=0;
- _filename:=lowercase(filename);
- _PagedFiles.Add(Self);
- end;
- destructor TPagedFile.Destroy;
- begin
- Stream.Free;
- Stream:=nil;
- inherited;
- end;
- procedure TPagedFile._Seek(page:Integer);
- var
- p:Integer;
- begin
- p:=HeaderSize + (RecordSize * page );
- Stream.Position := p;
- end;
- Procedure TPagedFile.ReadRecord(IntRecNum:Integer; Buffer:Pointer);
- begin
- _Seek(IntRecNum);
- Stream.Read(Buffer^,RecordSize);
- end;
- procedure TPagedFile.WriteRecord(IntRecNum:Integer; Buffer:Pointer);
- begin
- _Seek(IntRecNum);
- Stream.Write(Buffer^, RecordSize);
- end;
- //====================================================================
- // TDbfFile
- //====================================================================
- constructor TDbfFile.Create(const FileName: string; Mode: Word);
- var
- lRecordCount:Integer;
- begin
- _MyFieldInfos:=TList.Create;
- // check if the file exists
- inherited Create(Filename, Mode);
- if Mode = fmCreate then begin
- FillChar(_DataHdr,sizeof(_DataHdr),0);
- HeaderSize:=0;
- RecordSize:=0;
- _DataHdr.VerDBF:=$03; // Default version xBaseIV without memo
- _DataHdr.Language:='X';
- end else begin
- Stream.Seek(0,soFromBeginning);
- Stream.ReadBuffer (_DataHdr, SizeOf(_DataHdr));
- case _DataHdr.VerDBF of
- $03,$83: _DbfVersion:=xBaseIII;
- $04,$8B,$8E,$7B: _DbfVersion:=xBaseIV;
- $05 : _DbfVersion:=xbaseV;
- else
- _DbfVersion:=xBaseIV; // My favorite...
- end;
- HeaderSize:=_DataHdr.FullHdrSize;
- RecordSize:=_DataHdr.RecordSize;
- lRecordCount:=CalcRecordCount;
- if _DataHdr.RecordCount <> lRecordCount then begin
- {$ifndef fpc}
- ShowMessage('Invalid Record Count,'+^M+
- 'RecordCount in Hdr : '+IntToStr(_DataHdr.RecordCount)+^M+
- 'expected : '+IntToStr(lRecordCount));
- {$endif}
- _DataHdr.RecordCount := lRecordCount;
- end;
- end;
- end;
- destructor TDbfFile.Destroy;
- begin
- inherited;
- ClearMyFieldInfos;
- _MyFieldInfos.Free;
- _MyFieldInfos:=nil;
- end;
- function TDbfFile.RecordCount:integer;
- begin
- if RecordSize=0 then result:=0
- else result:=(Stream.Size - HeaderSize) div RecordSize;
- if result<0 then result:=0;
- end;
- procedure TDbfFile.ClearMyFieldInfos;
- var
- i:Integer;
- begin
- for i:=0 to _MyFieldInfos.Count-1 do begin
- TMyFieldInfo(_MyFieldInfos.Items[i]).Free;
- end;
- _MyFieldInfos.Clear;
- end;
- procedure TDbfFile.CreateFieldDefs(FieldDefs:TFieldDefs);
- var
- lColumnCount,lHeaderSize,lFieldSize:Integer;
- Il : Integer;
- lFieldOffset : Integer;
- fn:string;
- ft:TFieldType;
- fs,nfs,fd:Integer;
- MyFieldInfo:TMyFieldInfo;
- lFieldHdrIII:rFieldHdrIII;
- lFieldHdrV:rFieldHdrV;
- function ToFieldType(dbasetype:char;fs,fd:Integer):TFieldType;
- begin
- case dbasetype of
- 'C' :
- begin
- Result:=ftString;
- end;
- 'L' :
- begin
- Result:=ftBoolean;
- end;
- 'F' :
- begin
- Result:=ftFloat;
- end;
- 'N' :
- begin
- if fd=0 then begin
- if fs <= DIGITS_SMALLINT then begin
- Result:=ftSmallInt;
- end else begin
- {$ifdef DELPHI_3}
- Result:=ftInteger;
- {$else}
- if fs <= DIGITS_INTEGER then Result:=ftInteger
- else Result:=ftLargeInt;
- {$endif}
- end;
- end else begin
- Result:=ftFloat;
- end;
- end;
- 'D' :
- begin
- Result:=ftDate;
- end;
- 'M' :
- begin
- Result:=ftMemo;
- end;
- else
- begin
- Result:=ftString;
- end;
- end; //case
- end;
- begin
- ClearMyFieldInfos;
- if _DbfVersion>=xBaseV then begin
- lHeaderSize:=SizeOf(rAfterHdrV) + SizeOf(rDbfHdr);
- lFieldSize:=SizeOf(rFieldHdrV);
- end else begin
- lHeaderSize:=SizeOf(rAfterHdrIII) + SizeOf(rDbfHdr);
- lFieldSize:=SizeOf(rFieldHdrIII);
- end;
- lColumnCount:= (_DataHdr.FullHdrSize - lHeaderSize) div lFieldSize;
- if (lColumnCount <= 0) or (lColumnCount > 255) then
- Raise eBinaryDataSetError.Create('Invalid field count : ' + IntToStr(lColumnCount) + ' (must be between 1 and 255)');
- lFieldOffset := 1;
- Stream.Position := lHeaderSize;
- for Il:=0 to lColumnCount-1 do begin
- if _DbfVersion>=xBaseV then begin
- Stream.ReadBuffer(lFieldHdrV,SizeOf(lFieldHdrV));
- fn:=PCHAR(@lFieldHdrV.FieldName[0]);
- fs:=lFieldHdrV.FieldSize;
- fd:=lFieldHdrV.FieldPrecision;
- nfs:=fs;
- ft:=ToFieldType(lFieldHdrV.FieldType,nfs,fd);
- end else begin
- Stream.ReadBuffer(lFieldHdrIII,SizeOf(lFieldHdrIII));
- fn:=PCHAR(@lFieldHdrIII.FieldName[0]);
- fs:=lFieldHdrIII.FieldSize;
- fd:=lFieldHdrIII.FieldPrecision;
- nfs:=fs;
- ft:=ToFieldType(lFieldHdrIII.FieldType,nfs,fd);
- end;
- // first create the bde field
- if ft in [ftString,ftBCD] then fieldDefs.Add(fn,ft,fs,false)
- else fieldDefs.Add(fn,ft,0,false);
- // then create the for our own fieldinfo
- MyFieldInfo:=TMyFieldInfo.Create;
- MyFieldInfo.Offset:=lFieldOffset;
- MyFieldInfo.Size:=fs;
- MyFieldInfo.Prec:=fd;
- MyFieldInfo.FieldName:=lowercase(fn);
- _MyFieldInfos.Add(MyFieldInfo);
- Inc(lFieldOffset,fs);
- end;
- if (lFieldOffset <> _DataHdr.RecordSize) then begin
- {$ifndef fpc}
- ShowMessage('Invalid Record Size,'+^M+
- 'Record Size in Hdr : '+IntToStr(_DataHdr.RecordSize)+^M+
- 'Expected : '+IntToStr(lFieldOffset));
- {$endif}
- _DataHdr.RecordSize := lFieldOffset;
- end;
- end;
- procedure TDbfFile.DbfFile_CreateTable(FieldDefs:TFieldDefs);
- var
- ix:Integer;
- lFieldHdrIII:rFieldHdrIII;
- lType:Char;
- lSize,lPrec:Integer;
- Offs:Integer;
- lterminator:Byte;
- begin
- // first reset file.
- Stream.Size:= 0;
- Stream.Position:=SizeOf(rDbfHdr) + SizeOf(rAfterHdrIII);
- Offs:=1; // deleted mark count 1.
- for Ix:=0 to FieldDefs.Count-1 do
- begin
- with FieldDefs.Items[Ix] do
- begin
- FillChar(lFieldHdrIII,SizeOf(lFieldHdrIII),#0);
- lPrec:=0;
- case DataType of
- ftString:
- begin
- ltype:='C';
- lSize := Size;
- end;
- ftBoolean:
- begin
- ltype:='L';
- lSize := 1;
- end;
- ftSmallInt:
- begin
- ltype:='N';
- lSize := 6;
- end;
- ftInteger:
- begin
- ltype:='N';
- lSize := 11;
- end;
- ftCurrency:
- begin
- ltype:='N';
- lSize := 20;
- lPrec := 2;
- end;
- {$ifndef DELPHI_3}
- ftLargeInt:
- begin
- ltype:='N';
- lSize := 20;
- lPrec := 0;
- end;
- {$endif}
- ftFloat:
- begin
- ltype:='N';
- lSize := 20;
- lPrec := 4;
- end;
- ftDate:
- begin
- ltype:='D';
- lSize := 8;
- end;
- ftMemo:
- begin
- ltype:='M';
- lSize := 10;
- end;
- else
- begin
- raise EBinaryDataSetError.Create(
- 'InitFieldDefs: Unsupported field type');
- end;
- end; // case
- lFieldHdrIII.FieldType:=ltype; //DataType;
- StrPCopy(lFieldHdrIII.FieldName,FieldDefs.Items[Ix].Name);
- lFieldHdrIII.FieldSize:=lSize;
- lFieldHdrIII.FieldPrecision:=lPrec;
- Stream.Write(lFieldHdrIII,SizeOf(lFieldHdrIII));
- Inc(Offs,lSize);
- end;
- end;
- // end of header
- lterminator := $0d;
- Stream.Write(lterminator,SizeOf(lterminator));
- // update header
- _DataHdr.RecordSize := Offs;
- _DataHdr.FullHdrSize := Stream.Position;
- RecordSize := _DataHdr.RecordSize;
- HeaderSize := _DataHdr.FullHdrSize;
- // write the updated header
- WriteHeader;
- end;
- procedure TDbfFile.DbfFile_PackTable;
- var
- first,last:integer;
- p: Pointer;
- begin
- // Non tested.
- if (RecordSize <> 0) then
- begin
- first:=0;
- last:=CalcRecordCount-1;
- GetMem(p, RecordSize);
- try
- while first<last do begin
- // first find the first hole
- while first<last do begin
- ReadRecord(first, p);
- if (pRecordHdr(p)^.DeletedFlag <> ' ') then break;
- inc(first);
- end;
- // now find last one non deleted.
- while first<last do begin
- ReadRecord(last, p);
- if (pRecordHdr(p)^.DeletedFlag = ' ') then break;
- dec(last);
- end;
- if first<last then begin
- // found a non deleted record to put in the hole.
- WriteRecord(first, p);
- inc(first);
- dec(last);
- end;
- end;
- last:=CalcRecordCount;
- Stream.Size:=(last+1) * RecordSize + HeaderSize;
- finally
- FreeMem(p);
- end;
- end;
- end;
- function TDbfFile.GetFieldInfo(FieldName:string):TMyFieldInfo;
- var
- i:Integer;
- lfi:TMyFieldInfo;
- begin
- FieldName:=LowerCase(FieldName);
- for i:=0 to _MyFieldInfos.Count-1 do begin
- lfi:=TMyFieldInfo(_MyFieldInfos.Items[i]);
- if lfi.FieldName = FieldName then begin
- result:=lfi;
- exit;
- end;
- end;
- result:=nil;
- end;
- function TDbfFile.GetFieldData(Column:Integer;DataType:TFieldType; Src,Dst:Pointer): Boolean;
- var
- FieldOffset: Integer;
- FieldSize: Integer;
- s:string;
- d:TDateTime;
- ld,lm,ly: word;
- MyFieldInfo:TMyFieldInfo;
- function TrimStr(const s: string): string;
- var
- iPos: integer;
- begin
- if DataType=ftString then
- begin
- if tDbf_TrimFields then Result:=Trim(s)
- else Result:=TrimRight(s);
- end
- else Result:= Trim(s);
- end;
- procedure CorrectYear(var wYear: word);
- var wD, wM, wY, CenturyBase: word;
- {$ifdef DELPHI_3}
- // Delphi 3 standard-behavior no change possible
- const TwoDigitYearCenturyWindow= 0;
- {$endif}
- begin
- if wYear>= 100 then
- Exit;
- DecodeDate(Date, wY, wm, wD);
- // use Delphi-Date-Window
- CenturyBase := wY{must be CurrentYear} - TwoDigitYearCenturyWindow;
- Inc(wYear, CenturyBase div 100 * 100);
- if (TwoDigitYearCenturyWindow > 0) and (wYear < CenturyBase) then
- Inc(wYear, 100);
- end;
- begin
- MyFieldInfo:=TMyFieldInfo(_MyFieldInfos.Items[Column]);
- FieldOffset := MyFieldInfo.Offset;
- FieldSize := MyFieldInfo.Size;
- SetString(s, PChar(Src) + FieldOffset, FieldSize );
- s:=TrimStr(s);
- result:=length(s)>0; // return if field is empty
- if Result and (Dst<>nil) then// data not needed if Result= FALSE or Dst=nil
- case DataType of
- ftBoolean:
- begin
- // in DBase- FileDescription lowercase t is allowed too
- // with asking for Result= TRUE s must be longer then 0
- // else it happens an AV, maybe field is NULL
- if (UpCase(s[1])='T') then Word(Dst^) := 1
- else Word(Dst^) := 0;
- end;
- ftInteger, ftSmallInt{$ifndef DELPHI_3},ftLargeInt{$endif}:
- begin
- case DataType of
- ftSmallInt : SmallInt(Dst^):= StrToIntDef(s, 0);
- {$ifndef DELPHI_3}
- ftLargeint : LargeInt(Dst^):= StrToInt64Def(s, 0);
- {$endif}
- else // ftInteger :
- Integer(Dst^):= StrToIntDef(s, 0);
- end; // case
- end;
- ftFloat:
- begin
- Extended(Dst^) := DBFStrToFloat(s);
- end;
- ftCurrency:
- begin
- Extended(Dst^) := DBFStrToFloat(s);
- end;
- ftDate:
- begin
- ld:=StrToIntDef(Copy(s,7,2),1);
- lm:=StrToIntDef(Copy(s,5,2),1);
- ly:=StrToIntDef(Copy(s,1,4),0);
- if ld=0 then ld:=1;
- if lm=0 then lm:=1;
- // if (ly<1900) or (ly>2100) then ly:=1900;
- // Year from 0001 to 9999 is possible
- // everyting else is an error, an empty string too
- // Do DateCorrection with Delphis possibillities for one or two digits
- if (ly< 100) and (Length(Trim(Copy(s,1,4)))in [1, 2]) then CorrectYear(ly);
- try
- d:=EncodeDate(ly,lm,ld);
- if Assigned(Dst) then Integer(Dst^) := DateTimeToTimeStamp(d).Date;
- except
- Integer(Dst^) := 0;
- end;
- end;
- ftString: begin
- StrPCopy(Dst,s);
- end;
- end;
- end;
- procedure TDbfFile.SetFieldData(Column:integer;DataType:TFieldType; Src,Dst:Pointer);
- var
- FieldSize,FieldPrec: Integer;
- s:string;
- fl:Double;
- ts:TTimeStamp;
- MyFieldInfo:TMyFieldInfo;
- begin
- MyFieldInfo:=TMyFieldInfo(_MyFieldInfos.Items[Column]);
- FieldSize := MyFieldInfo.Size;
- FieldPrec := MyFieldInfo.Prec;
- Dst:=PChar(Dst)+MyFieldInfo.Offset;
- if src<>nil then begin
- case DataType of
- ftBoolean:
- begin
- if Word(Src^) = 1 then s:='T'
- else s:='F';
- end;
- ftInteger, ftSmallInt {$ifndef DELPHI_3},ftLargeInt{$endif}:
- begin
- case DataType of
- ftSmallInt : s:= IntToStr(SmallInt(Src^));
- {$ifndef DELPHI_3}
- ftLargeInt: s:= IntToStr(LargeInt(Src^));
- {$endif}
- else //ftInteger
- s:= IntToStr(Integer(Src^));
- end;
- // left filling
- if Length(s)<FieldSize then s:=StringOfChar(' ',FieldSize-Length(s)) + s;
- end;
- ftFloat,ftCurrency:
- begin
- fl := Double(Src^);
- s:=FloatToDbfStr(fl,FieldSize,FieldPrec);
- if Length(s)<FieldSize then s:=StringOfChar(' ',FieldSize-Length(s)) + s;
- end;
- ftDate:
- begin
- ts.Time:=0;
- ts.Date:=Integer(Src^);
- s:= FormatDateTime('yyyymmdd', TimeStampToDateTime(ts));
- end;
- ftString:
- begin
- s:=PChar(Src); // finish with first 0
- end;
- end; // case
- end; // if src<>nil (thanks andreas)
- if Length(s)<FieldSize then begin
- s:=s+StringOfChar(' ',FieldSize-Length(s));
- end else if (Length(s)>FieldSize) then begin
- if DataType= ftString then begin
- // never raise for strings to long, its not customary
- // TTable never raises
- SetLength(s, FieldSize)
- end else begin
- raise eFieldToLongError.Create('Fielddata too long :' + IntToStr(Length(s))
- + ' (must be between 1 and ' + IntToStr(FieldSize) + ').');
- end;
- end;
- Move(PChar(s)^, Dst^, FieldSize);
- end;
- procedure TDbfFile.WriteHeader;
- var
- SystemTime: TSystemTime;
- lAfterHdrIII:rAfterHdrIII;
- lAfterHdrV:rAfterHdrV;
- lterminator:Byte;
- begin
- Assert(Stream<>nil,'_dbfFile=Nil');
- Stream.Position:=0;
- GetLocalTime(SystemTime);
- {$ifndef fpc}
- _DataHdr.Year := SystemTime.wYear - 1900;
- _DataHdr.Month := SystemTime.wMonth;
- _DataHdr.Day := SystemTime.wDay;
- {$else}
- _DataHdr.Year := SystemTime.Year - 1900;
- _DataHdr.Month := SystemTime.Month;
- _DataHdr.Day := SystemTime.Day;
- {$endif}
- Stream.Seek(0,soFromBeginning);
- Stream.WriteBuffer (_DataHdr, SizeOf(_DataHdr));
- _DataHdr.RecordCount := CalcRecordCount;
- if _DbfVersion >= xBaseV then begin
- FillChar(lAfterHdrV,SizeOf(lAfterHdrV),0);
- Stream.WriteBuffer (lAfterHdrV, SizeOf(lAfterHdrV));
- end else begin
- FillChar(lAfterHdrIII,SizeOf(lAfterHdrIII),0);
- Stream.WriteBuffer (lAfterHdrIII, SizeOf(lAfterHdrIII));
- end;
- _Seek(_DataHdr.RecordCount); // last byte usually...
- lterminator := $1A;
- Stream.Write(lterminator,SizeOf(lterminator));
- end;
- function TDbf._ComponentInfo:string;
- begin
- Result:='TDbf V' + IntToStr(_MAJOR_VERSION) + '.' + IntToStr(_MINOR_VERSION);
- end;
- procedure TDbf._OpenFiles(CreateIt:boolean);
- var
- fileopenmode : integer;
- lPath,lFilename,lIndexName,lMemoName : string;
- isAbsolute:boolean;
- design,doreadonly:boolean;
- begin
- design:=(csDesigning in ComponentState);
- doreadonly:=design or _ReadOnly;
- lPath:=_GetPath;
- isAbsolute:=((length(_TableName)>=1) and (_TableName[1]='\'))
- or ((length(_TableName)>=2) and (_TableName[2]=':'));
- if isAbsolute then lfilename:=_TableName
- else lFilename:=lPath+_TableName;
- lFilename:=ChangeFileExt(lFilename,'.dbf');
- lIndexName:=ChangeFileExt(lFilename,'.mdx');
- lMemoName:=ChangeFileExt(lFilename,'.dbt');
- // check if the file exists
- _dbfFile:=TDbfFile(GetPagedFile(lFileName));
- _indexFile:=TIndexFile(GetPagedFile(lIndexName));
- _dbtFile:=TDbtFile(GetPagedFile(lMemoName));
- if CreateIt then begin
- if _dbfFile=nil then _dbfFile:=TDbfFile.Create(lFileName,fmCreate);
- //if _indexfile=nil then _indexFile := TIndexFile.Create(lIndexName, fmCreate);
- if _dbtfile=nil then _dbtFile := TDbtFile.Create(lMemoName, fmCreate,_dbfFile._DbfVersion);
- end else if not FileExists(lFileName) then begin
- raise eBinaryDataSetError.Create ('Open: Table file not found : ' + lFileName);
- end else begin
- if DoReadOnly then
- fileopenmode := fmOpenRead + fmShareDenyNone
- else
- fileopenmode := fmOpenReadWrite + fmShareDenyWrite;
- if _dbfFile=nil then _dbfFile := TDBFFile.Create(lFileName, fileopenmode);
- if (_indexFile=nil) and FileExists (lIndexName) then begin
- _indexFile := TIndexFile.Create(lIndexName, fileopenmode);
- end;
- if (_dbtFile=nil) and FileExists (lMemoName) then begin
- _dbtFile := TDbtFile.Create(lMemoName, fileopenmode,_dbfFile._DbfVersion);
- end;
- end;
- _PrevBuffer:=AllocRecordBuffer;
- _IsCursorOpen:=true;
- end;
- function TDbf._GetPath:string;
- var
- lPath:string;
- begin
- if (csDesigning in ComponentState) then begin
- lPath:=_DesignTimePath;
- end else begin
- if ((length(_RunTimePath)>=1) and (_RunTimePath[1]=DirSeparator))
- or ((length(_RunTimePath)>=2) and (_RunTimePath[2]=':'))
- then begin
- // if the _RunTimePath is absolute...
- // it is either \ or \blahblah or c:\
- lPath:=_RunTimePath;
- end else begin
- {$ifndef fpc}
- lPath:=extractfilepath(Application.Exename)+_RunTimePath;
- {$else}
- lPath:=extractfilepath(paramstr(0))+_RunTimePath;
- {$endif}
- end;
- end;
- lPath:=ExpandFileName(trim(lPath));
- if (length(lPath)>0) and (lPath[length(lPath)]<>DirSeparator) then lPath:=lPath+DirSeparator;
- result:=lPath;
- end;
- procedure TDbf._CloseFiles;
- var
- i:integer;
- begin
- if _dbfFile<>nil then begin
- if not _ReadOnly then _dbfFile.WriteHeader;
- _dbfFile.Release;
- _dbfFile:=nil;
- end;
- if _indexFile<>nil then begin
- _indexFile.Release;
- _indexFile:=nil;
- end;
- if _dbtFile<>nil then begin
- _dbtFile.Release;
- _dbtFile:=nil;
- end;
- if _indexes<>nil then begin
- for i:=0 to _Indexes.Count-1 do begin
- TIndex(_Indexes[i]).Free;
- end;
- _Indexes.Clear;
- _CurIndex:=nil;
- end;
- if (_PrevBuffer<>nil) then begin
- FreeRecordBuffer(_PrevBuffer);
- _PrevBuffer:=nil;
- end;
- _IsCursorOpen:=false;
- end;
- procedure TDbf._SetIndexName(const Value: string);
- begin
- _CurIndex:=_GetIndex(Value);
- Resync([]);
- end;
- function TDbf._GetIndexName: string;
- begin
- if _CurIndex=nil then Result:=''
- else Result:=_CurIndex._IndexFile._Filename;
- end;
- function TDbf._GetIndex(filename:string):TIndex;
- var
- i:integer;
- lindex:TIndex;
- begin
- result:=nil;
- filename:=lowercase(_GetPath + filename);
- for i:=0 to _indexes.Count-1 do begin
- lindex:=TIndex(_indexes.Items[i]);
- if lindex._IndexFile._Filename=filename then begin
- result:=lindex;
- exit;
- end;
- end;
- end;
- //==========================================================
- //============ TMyBlobFile
- //==========================================================
- constructor TMyBlobFile.Create(ModeVal:TBlobStreamMode;FieldVal:TField);
- begin
- Mode:=ModeVal;
- Field:=FieldVal;
- end;
- destructor TMyBlobFile.destroy;
- var
- Dbf:TDbf;
- begin
- if (Mode=bmWrite) then begin
- Size:=Position; // Strange but it leave tailing trash bytes if I do not write that.
- Dbf:=TDbf(Field.DataSet);
- Dbf._dbtFile.WriteMemo(MemoRecno,ReadSize,Self);
- Dbf._dbfFile.SetFieldData(Field.FieldNo-1,
- ftInteger,@MemoRecno,@pDbfRecord(TDbf(Field.DataSet).ActiveBuffer)^.deletedflag);
- // seems not bad
- {$ifndef fpc}
- // FPC doesn't allow to call protected methods ?!!
- Dbf.SetModified(true);
- {$endif}
- // but would that be better
- //if not (State in [dsCalcFields, dsFilter, dsNewValue]) then begin
- // DataEvent(deFieldChange, Longint(Field));
- //end;
- end;
- inherited;
- end;
- //====================================================================
- // TDbf = TDataset Descendant.
- //====================================================================
- constructor TDbf.Create(AOwner: TComponent); {override;}
- begin
- inherited create(aOwner);
- BookmarkSize:=sizeof(rBookmarkData);
- _RunTimePath:='.';
- _IsCursorOpen:=false;
- _Indexes:=TList.Create;
- _CurIndex:=nil;
- _IndexFiles:=TStringList.Create;
- end;
- destructor TDbf.Destroy; {override;}
- var
- i:integer;
- begin
- inherited;
- _CurIndex:=nil;
- for i:=0 to _Indexes.Count-1 do begin
- TIndex(_Indexes[i]).Free;
- end;
- _Indexes.Free;
- _IndexFiles.Free;
- // _MemIndex.Free;
- end;
- function TDbf._FilterRecord(Buffer: PChar): Boolean;
- var
- SaveState: TDatasetState;
- s:string;
- begin
- Result:=True;
- if Length(easyfilter)<>0 then begin
- SetString(s,buffer,RecordSize);
- s:=LowerCase(s);
- if Pos(easyfilter,s)=0 then begin
- Result:=False;
- Exit;
- end;
- end;
- if not Assigned(OnFilterRecord) then Exit;
- if not Filtered then Exit;
- _FilterBuffer:=buffer;
- SaveState:=SetTempState(dsFilter);
- OnFilterRecord(self,Result);
- RestoreState(SaveState);
- end;
- function TDbf._RecordDataSize:integer;
- begin
- if _dbfFile=nil then result:=0
- else result:=_dbfFile.RecordSize;
- end;
- function TDbf._FullRecordSize:integer;
- begin
- result:=sizeof(rBeforeRecord) + _RecordDataSize + CalcFieldsSize;
- end;
- function TDbf.AllocRecordBuffer: PChar; {override virtual abstract from TDataset}
- begin
- result:=StrAlloc(_FullRecordSize);
- InternalInitRecord(result);
- end;
- procedure TDbf.FreeRecordBuffer(var Buffer: PChar); {override virtual abstract from TDataset}
- begin
- StrDispose(Buffer);
- end;
- procedure TDbf.GetBookmarkData(Buffer: PChar; Data: Pointer); {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- begin
- prec:=pDbfRecord(Buffer);
- pBookMarkData(Data)^:=prec^.BookMarkData;
- end;
- function TDbf.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- begin
- prec:=pDbfRecord(Buffer);
- result:=prec^.BookMarkFlag;
- end;
- function TDbf.GetFieldData(Field: TField; Buffer: Pointer): Boolean; {override virtual abstract from TDataset}
- var
- ptr:pointer;
- begin
- Result := False;
- if State=dsFilter then begin
- Ptr:=_FilterBuffer;
- end else if State = dsCalcFields then begin
- // ***** calc fields ***** set correct buffer
- ptr := @(pDbfRecord(CalcBuffer)^.deletedflag);
- end else begin
- if IsEmpty then exit;
- ptr:=@(pDbfRecord(ActiveBuffer)^.deletedflag);
- end;
- if Field.FieldNo>0 then begin
- Result:=_dbfFile.GetFieldData(Field.FieldNo - 1,Field.DataType,ptr,Buffer);
- end else begin { calculated fields.... }
- Inc(PChar(Ptr), Field.Offset + GetRecordSize);
- {$ifndef fpc}
- Result := Boolean(PChar(Ptr)[0]);
- {$else}
- Result := (Pchar(ptr)[0]<>#0);
- {$endif}
- if Result and (Buffer <> nil) then
- Move(PChar(Ptr)[1], Buffer^, Field.DataSize);
- end;
- end;
- function TDbf.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; {override virtual abstract from TDataset}
- var
- Acceptable : Boolean;
- prec:pDBFRecord;
- begin
- prec:=pDBFRecord(Buffer);
- if _dbfFile.RecordCount < 1 then
- Result := grEOF
- else repeat
- result := grOk;
- case GetMode of
- gmCurrent :
- begin
- if prec^.BookmarkData.Recno=_PhysicalRecno then begin
- exit; // try to fasten a bit...
- end;
- end;
- gmNext :
- begin
- if _curIndex<>nil then begin
- Acceptable:=_curIndex.Next;
- end else begin
- inc(_PhysicalRecno);
- Acceptable:=(_PhysicalRecno<_dbfFile.RecordCount);
- end;
- if Acceptable then begin
- result:= grOk;
- end else begin
- InternalLast;
- result:= grEOF
- end;
- end;
- gmPrior :
- begin
- if _curIndex<>nil then begin
- Acceptable:=_curIndex.Prev;
- end else begin
- dec(_PhysicalRecno);
- Acceptable:=(_PhysicalRecno>=0);
- end;
- if Acceptable then begin
- result:= grOk;
- end else begin
- InternalFirst;
- result:= grBOF
- end;
- end;
- end;
- if result=grOk then begin
- if _curIndex<>nil then _PhysicalRecno:=_CurIndex.GetRealRecNo;
- if (_PhysicalRecno>=_dbfFile.RecordCount)
- or (_PhysicalRecno<0) then begin
- result:=grError;
- end else begin
- _dbfFile.ReadRecord(_PhysicalRecno,@prec^.DeletedFlag);
- result:=grOk;
- end;
- if Result = grOK then begin
- ClearCalcFields(Buffer);
- GetCalcFields(Buffer);
- prec^.BookmarkFlag := bfCurrent;
- prec^.BookmarkData.Recno:=PhysicalRecno;
- end else if (Result = grError) and DoCheck then
- raise eBinaryDataSetError.Create ('GetRecord: Invalid record');
- end;
- Acceptable := (_ShowDeleted or (prec^.DeletedFlag = ' '))
- and _FilterRecord(Buffer);
- if (GetMode=gmCurrent) and Not Acceptable then Result := grError;
- until (Result <> grOK) or Acceptable;
- end;
- function TDbf.GetRecordSize: Word; {override virtual abstract from TDataset}
- begin
- Result := _RecordDataSize; // data only
- end;
- procedure TDbf.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); {override virtual abstract from TDataset}
- begin
- end;
- procedure TDbf.InternalClose; {override virtual abstract from TDataset}
- begin
- _CloseFiles;
- // disconnect field objects
- BindFields(False);
- // destroy field object (if not persistent)
- if DefaultFields then
- DestroyFields;
- end;
- procedure TDbf.InternalDelete; {override virtual abstract from TDataset}
- begin
- // CheckActive;
- pRecordHdr(ActiveBuffer)^.DeletedFlag := '*'; //_DataHdr.LastDeleted;
- _dbfFile.WriteRecord(_PhysicalRecNo,ActiveBuffer);
- Resync([]);
- end;
- procedure TDbf.InternalFirst; {override virtual abstract from TDataset}
- begin
- if _dbfFile.RecordCount=0 then InternalLast
- else if _curindex=nil then _PhysicalRecno:=-1
- else _curIndex.First;
- end;
- procedure TDbf.InternalGotoBookmark(ABookmark: Pointer); {override virtual abstract from TDataset}
- var
- RecInfo: TRecInfo;
- begin
- RecInfo := TRecInfo(ABookmark^);
- if (RecInfo.Bookmark >= 0) and (RecInfo.Bookmark < _dbfFile.RecordCount) then begin
- _PhysicalRecno:=RecInfo.Bookmark;
- end else
- raise eBinaryDataSetError.Create ('Bookmark ' +
- IntToStr (RecInfo.Bookmark) + ' not found');
- end;
- procedure TDbf.InternalHandleException; {override virtual abstract from TDataset}
- begin
- {$ifndef fpc}
- Application.HandleException(Self);
- {$endif}
- end;
- procedure TDbf.InternalInitFieldDefs; {override virtual abstract from TDataset}
- begin
- FieldDefs.Clear;
- with FieldDefs do
- begin
- if IsCursorOpen then begin
- _dbfFile.CreateFieldDefs(FieldDefs);
- end else begin
- _OpenFiles(false);
- _dbfFile.CreateFieldDefs(FieldDefs);
- Close();
- end;
- end;
- end;
- procedure TDbf.InternalInitRecord(Buffer: PChar); {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- begin
- prec:=pDbfRecord(Buffer);
- prec^.BookmarkData.RecNo:=-1;
- prec^.BookmarkFlag:=TBookmarkFlag(0);
- fillchar(prec^.DeletedFlag,_RecordDataSize,' ');
- end;
- procedure TDbf.InternalLast; {override virtual abstract from TDataset}
- begin
- if _curindex=nil then _PhysicalRecno:=_dbfFile.RecordCount
- else _curIndex.Last;
- end;
- procedure TDbf.InternalOpen; {override virtual abstract from TDataset}
- begin
- _OpenFiles(false);
- // if there are no persistent field objects,
- InternalInitFieldDefs;
- // create the fields dynamically
- if DefaultFields then begin
- CreateFields;
- end;
- BindFields (True);
- // connect the TField objects with the actual fields
- InternalFirst;
- end;
- procedure TDbf.InternalPost; {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- lIndex:TIndex;
- i:integer;
- begin
- CheckActive;
- prec:=pDbfRecord(ActiveBuffer);
- prec^.DeletedFlag:=' ';
- if State = dsEdit then
- begin
- // replace data with new data
- if _indexes.Count>0 then begin
- _dbfFile.ReadRecord(_PhysicalRecno,_PrevBuffer);
- for i:=0 to _indexes.Count-1 do begin
- lindex:=TIndex(_indexes.Items[i]);
- lindex.Update(_PhysicalRecno,_PrevBuffer,@prec^.DeletedFlag);
- end;
- end;
- end else begin
- // append
- _PhysicalRecno:=_dbfFile._DataHdr.RecordCount;
- inc(_dbfFile._DataHdr.RecordCount);
- if _indexes.Count>0 then begin
- _dbfFile.ReadRecord(_PhysicalRecno,_PrevBuffer);
- for i:=0 to _indexes.Count-1 do begin
- lindex:=TIndex(_indexes.Items[i]);
- lindex.Insert(_PhysicalRecno,@prec^.DeletedFlag);
- end;
- end;
- end;
- _dbfFile.WriteRecord(_PhysicalRecno,@prec^.DeletedFlag);
- end;
- procedure TDbf.CreateTable; //(FieldDefs:TFieldDefs);
- var
- ix:integer;
- begin
- CheckInactive;
- // InternalInitFieldDefs;
- if FieldDefs.Count = 0 then
- begin
- for Ix := 0 to FieldCount - 1 do
- begin
- with Fields[Ix] do
- begin
- if FieldKind = fkData then
- FieldDefs.Add(FieldName,DataType,Size,Required);
- end;
- end;
- end;
- _OpenFiles(true);
- try
- _dbfFile.DbfFile_CreateTable(FieldDefs);
- finally
- // close the file
- _CloseFiles;
- end;
- end;
- procedure TDbf.PackTable;
- begin
- _dbfFile.dbfFile_PackTable;
- Resync([]);
- end;
- function TDbf.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; {override virtual}
- var
- Memoi:array[1..32] of char;
- lBlob:TMyBlobFile;
- begin
- lBlob:=TMyBlobFile.Create(Mode,Field);
- if _dbfFile.GetFieldData(Field.FieldNo-1, ftString,@pDbfRecord(ActiveBuffer)^.deletedflag,@Memoi[1]) then begin
- lBlob.MemoRecno:=StrToIntDef(Memoi,0);
- _dbtFile.ReadMemo(lBlob.MemoRecno,lBlob);
- lBlob.ReadSize:=lBlob.Size;
- end else lBlob.MemoRecno:=0;
- Result:=lBlob;
- end;
- {$ifdef DELPHI_3}
- procedure TDbf.Translate(Src, Dest: PChar; ToOem: Boolean); {override virtual}
- begin
- if (Src <> nil) and (Dest<>nil) then begin
- if ToOem then CharToOem(Src,Dest)
- else OemToChar(Src,Dest);
- end;
- end;
- {$else}
- {$ifndef fpc}
- function TDbf.Translate(Src, Dest: PChar; ToOem: Boolean): Integer; {override virtual}
- begin
- if (Src <> nil) and (Dest<>nil) then begin
- if ToOem then CharToOem(Src,Dest)
- else OemToChar(Src,Dest);
- result:= StrLen(Dest);
- end else result:=0;
- end;
- {$else}
- function TDbf.Translate(Src, Dest: PChar; ToOem: Boolean): Integer; {override virtual}
- begin
- end;
- {$endif}
- {$endif}
- procedure TDbf.ClearCalcFields(Buffer: PChar);
- begin
- FillChar(Buffer[_dbfFile.RecordSize], CalcFieldsSize, 0);
- end;
- procedure TDbf.InternalSetToRecord(Buffer: PChar); {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- begin
- if Buffer=nil then exit;
- prec:=pDbfRecord(Buffer);
- _PhysicalRecno:=prec^.BookmarkData.RecNo;
- _ResyncIndexes(Buffer);
- end;
- procedure TDbf._ResyncIndexes(Buffer: PChar);
- var
- i:integer;
- lindex:TIndex;
- begin
- if _indexes.Count>0 then begin
- _dbfFile.ReadRecord(_PhysicalRecno,_PrevBuffer);
- for i:=0 to _indexes.Count-1 do begin
- lindex:=TIndex(_indexes.Items[i]);
- lindex.GotoKey(_physicalRecno,nil);
- end;
- end;
- end;
- function TDbf.IsCursorOpen: Boolean; {override virtual abstract from TDataset}
- begin
- result:=_IsCursorOpen;
- end;
- procedure TDbf.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- begin
- prec:=pDbfRecord(Buffer);
- prec^.BookMarkFlag:=Value;
- end;
- procedure TDbf.SetBookmarkData(Buffer: PChar; Data: Pointer); {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- begin
- prec:=pDbfRecord(Buffer);
- prec^.BookMarkData:=pBookMarkData(Data)^;
- end;
- procedure TDbf.SetFieldData(Field: TField; Buffer: Pointer); {override virtual abstract from TDataset}
- var
- prec:pDbfRecord;
- dst:pointer;
- begin
- if (Field.FieldNo >= 0) then begin
- prec:=pDbfRecord(ActiveBuffer);
- dst:=@prec^.DeletedFlag;
- _dbfFile.SetFieldData(Field.FieldNo - 1,Field.DataType,Buffer,Dst);
- end else begin { ***** fkCalculated, fkLookup ***** }
- prec:=pDbfRecord(CalcBuffer);
- dst:=@prec^.DeletedFlag;
- Inc(integer(dst), GetRecordSize + Field.Offset);
- Boolean(dst^) := LongBool(Buffer);
- if Boolean(dst^) then begin
- Inc(integer(dst), 1);
- Move(Buffer^, dst^, Field.DataSize);
- end;
- end; { end of ***** fkCalculated, fkLookup ***** }
- if not (State in [dsCalcFields, dsFilter, dsNewValue]) then begin
- DataEvent(deFieldChange, Longint(Field));
- end;
- end;
- // this function is just for the grid scrollbars
- // it doesn't have to be perfectly accurate, but fast.
- function TDbf.GetRecordCount: Integer; {override virtual}
- begin
- if _curIndex=nil then begin
- result:=_dbfFile.RecordCount;
- end else begin
- result:=_curIndex.GuessRecordCount;
- end;
- end;
- // this function is just for the grid scrollbars
- // it doesn't have to be perfectly accurate, but fast.
- function TDbf.GetRecNo: Integer; {override virtual}
- begin
- UpdateCursorPos;
- if _curIndex=nil then begin
- result:=_PhysicalRecno+1;
- end else begin
- result:=_curIndex.GuessRecNo;
- end;
- end;
- procedure TDbf.SetRecNo(Value: Integer); {override virual}
- begin
- if _curIndex=nil then begin
- _PhysicalRecno:=Value-1;
- end else begin
- //result:=_curIndex.GuessRecNo;
- end;
- Resync([rmExact]);
- end;
- procedure TDBf.DeleteIndex(const AName: string);
- begin
- // I must admit that is seems a bit expeditive.
- // but I does implement this method because TTable does
- DeleteFile(_GetPath + Name);
- end;
- procedure TDbf.CloseIndexFile(const IndexFileName: string);
- var
- lindex:tindex;
- begin
- lindex:=_GetIndex(IndexFileName);
- if lindex<>nil then begin
- lindex.Free;
- _indexes.Delete(_indexes.IndexOf(lindex));
- if _curindex = lindex then begin
- _curindex:=nil;
- resync([]);
- end;
- end;
- end;
- procedure TDbf.OpenIndexFile(AnIndexName:string);
- var
- lIndexFile:TIndexFile;
- lIndex:TIndex;
- begin
- lindex:=_GetIndex(IndexName);
- if lindex=nil then begin
- IndexName:=lowercase(_GetPath + IndexName);
- lIndexFile:=TIndexFile(GetPagedFile(IndexName));
- if lIndexFile=nil then begin
- lIndexFile:=TIndexFile.Create(IndexName,fmOpenReadWrite + fmShareDenyWrite);
- end;
- lIndex:=TIndex.Create(lIndexFile,0,false);
- _Indexes.Add(lIndex);
- lIndex.InitFieldDef(_DbfFile,lIndex._NdxHdr.KeyDesc);
- end;
- end;
- (*
- procedure TDbfFile.DbfFile_PackTable;
- var
- begin
- end;
- *)
- {$ifdef fpc}
- procedure TDbf.AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions);
- begin
- AddIndex(indexName,IndexFields,options,'');
- end;
- {$endif}
- {$ifdef DELPHI_3}
- procedure TDbf.AddIndex(const IndexName, Fields: String; Options: TIndexOptions);
- var
- DescFields:string;
- {$else}
- {$ifndef fpc}
- procedure TDbf.AddIndex(const IndexName, Fields: String; Options: TIndexOptions; const DescFields: String='');
- var
- {$else}
- procedure TDbf.AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions; const DescFields: String);
- var
- {$endif}
- {$endif}
- lfilename:string;
- lIndexFile:TIndexFile;
- lIndex:TIndex;
- cur,thelast:integer;
- begin
- lfilename:=lowercase(_GetPath+IndexName);
- lIndexFile:=TIndexFile(GetPagedFile(lfilename));
- if lIndexFile<>nil then exit;
- lIndexFile:=TIndexFile.Create(lfilename,fmCreate);
- lIndex:=TIndex.Create(lIndexFile,0,true);
- {$ifndef fpc}
- lIndex.InitFieldDef(_DbfFile,Fields);
- {$else}
- lIndex.InitFieldDef(_DbfFile,IndexFields);
- {$endif}
- with lIndex._NdxHdr do begin
- startpage:=1;
- nbPage:=1;
- keyformat:=#0;
- keytype:='C';
- dummy:=$5800;
- keylen:=lindex._FieldLen;
- nbkey:=(512-8) div (lindex._FieldLen+8);
- keyreclen:=lindex._FieldLen+8;
- Unique:=0;
- KeyDesc[0]:=' ';
- {$ifndef fpc}
- StrLCopy(KeyDesc,PChar(UpperCase(Fields)),255);
- {$else}
- StrLCopy(KeyDesc,PChar(UpperCase(IndexFields)),255);
- {$endif}
- end;
- lindex._IndexFile._Seek(lindex._RootPage);
- lindex._IndexFile.Stream.Write(lindex._NdxHdr,SizeOf(lindex._NdxHdr));
- cur:=0;
- thelast:=_DbfFile.CalcRecordCount;
- while cur<thelast do begin
- _DbfFile.ReadRecord(cur, _PrevBuffer);
- lIndex.Insert(cur,_PrevBuffer);
- inc(cur);
- end;
- _Indexes.Add(lIndex);
- end;
- //==========================================================
- //============ dbtfile
- //==========================================================
- constructor TDbtFile.Create(const FileName: string; Mode: Word; Ver:xBaseVersion);
- begin
- inherited Create(FileName,Mode);
- _DbtVersion:=Ver;
- if mode = fmCreate then begin
- FillChar(_MemoHdr,sizeof(_MemoHdr),0);
- end else begin
- Stream.Position:=0;
- Stream.read(_MemoHdr,SizeOf(_MemoHdr));
- end;
- HeaderSize:=0;
- RecordSize:=_MemoHdr.BlockLen;
- if (RecordSize=0) or ((RecordSize mod 128)<>0) then begin
- _MemoHdr.BlockLen := $200;
- RecordSize := $200;
- end;
- // Can you tell me why the header of dbase3 memo contains 1024 and it 512 ?
- if _DbtVersion=xBaseIII then RecordSize:=512;
- end;
- procedure TDbtFile.ReadMemo(recno:Integer;Dst:TStream);
- var
- Buff:array[0..511] of char;
- i,lsize:integer;
- finish:boolean;
- lastc:char;
- begin
- if recno=0 then Exit;
- Stream.Position:= RecordSize * recno;
- if _DbtVersion >= xBaseIV then begin // dBase4 memofiles
- Stream.read(Buff[0],8);
- if (Buff[0]=#$ff) and (Buff[1]=#$ff) and
- (Buff[2]=#$08) and (Buff[3]=#$00) then begin
- // dbase IV memo
- lsize:=(PInteger(@Buff[4])^)-8;
- end else begin
- lsize:=0;
- end;
- repeat
- if lsize>SizeOf(Buff) then begin
- Stream.read(Buff,SizeOf(Buff));
- Dst.Write(buff,SizeOf(Buff));
- Dec(lsize,SizeOf(Buff));
- end else if lsize>0 then begin
- Stream.read(Buff,lsize);
- Dst.Write(buff,lsize);
- lsize:=0;
- end;
- until lsize=0;
- end else begin
- finish:=False;
- Stream.read(Buff,SizeOf(Buff));
- lastc:=#0;
- repeat
- for i:=0 to SizeOf(Buff)-2 do begin
- if ((Buff[i]=#$1A) and
- ((Buff[i+1]=#$1A) or ((i=0) and (lastc=#$1A))))
- or (Buff[i]=#$0)
- then begin
- if i>0 then Dst.Write(buff,i);
- finish:=True;
- break;
- end;
- end;
- if finish then Break;
- Dst.Write(buff,512);
- lastc:=Buff[511];
- Stream.read(Buff,SizeOf(Buff));
- until finish;
- end;
- Dst.Seek(0,0);
- end;
- procedure TDbtFile.WriteMemo(var MemoRecno:Integer;ReadSize:Integer;Src:TStream);
- var
- ByteBefore:Integer;
- ByteAfter:Integer;
- Buff:array[0..511] of char;
- i:Integer;
- c:Byte;
- Append:Boolean;
- begin
- if _DbtVersion >= xBaseIV then begin // dBase4 memofiles
- ByteBefore:=8;
- ByteAfter:=0;
- end else begin // stupid files
- ByteBefore:=0;
- ByteAfter:=2;
- end;
- if Src.Size = 0 then begin
- MemoRecno:=0;
- end else begin
- if ((ByteBefore+Src.Size+ByteAfter+_MemoHdr.BlockLen-1) div _MemoHdr.BlockLen)
- <= ((ReadSize+_MemoHdr.BlockLen-1) div _MemoHdr.BlockLen)
- then begin
- Append:=false;
- //MemoRecno:=MemoRecno;
- end else begin
- Append:=True;
- MemoRecno:=_MemoHdr.NextBlock;
- if MemoRecno=0 then begin
- _MemoHdr.NextBlock:=1;
- MemoRecno:=1;
- end;
- end;
- Stream.Seek(_MemoHdr.BlockLen * MemoRecno,0);
- i:=Src.Position;
- Src.Seek(0,0);
- if ByteBefore=8 then begin
- i:=$0008ffff;
- Stream.Write(i,4);
- i:=Src.Size+ByteBefore+ByteAfter;
- Stream.Write(i,4);
- end;
- repeat
- i:=Src.Read(buff,512);
- if i=0 then break;
- Inc(_MemoHdr.NextBlock);
- Stream.Write(Buff,i);
- until i<512;
- if ByteAfter=2 then begin
- c:=$1A;
- Stream.Write(c,1);
- Stream.Write(c,1);
- end;
- if Append then begin
- Stream.Seek(0,0);
- Stream.Write(_MemoHdr,SizeOf(_MemoHdr))
- end;
- end;
- end;
- //==========================================================
- //============ TIndexFile
- //==========================================================
- constructor TIndexFile.Create(const FileName: string; Mode: Word);
- var
- ext:string;
- i:Integer;
- begin
- inherited Create(FileName,Mode);
- HeaderSize:=0;
- RecordSize:=512;
- ext:=ExtractFileExt(FileName);
- if (ext='.mdx') then begin
- _IndexVersion:=xBaseIV;
- if Mode = fmCreate then begin
- FillChar(_MdxHdr,sizeof(_MdxHdr),0);
- end else begin
- Stream.read(_MdxHdr,SizeOf(_MdxHdr));
- end;
- for i:= 0 to _MdxHdr.TagUsed-1 do begin
- // Stream.Position :=544 + i * _MdxHdr.TagSize;
- // Stream.read(lMdxTag,SizeOf(rMdxTag));
- // lIndex:=TIndex.Create(Self,lMdxTag.pageno);
- // _Indexes.Add(lIndex);
- // if i=0 then lIndex.ReadPage(lIndex._NdxHdr.startpage);
- end;
- end else begin
- _IndexVersion:=xBaseIII;
- (*
- _IndexFile._Seek(Pos);
- _IndexFile.Stream.Read(_NdxHdr,SizeOf(_NdxHdr));
- _Root:=TIndexPage.Create(Self);
- _Root.SetPageNo(_NdxHdr.startpage);
- lPos:=_Root;
- _nblevel:=1;
- repeat
- lPos.LocalFirst;
- if lPos.Entry._LowerPage=0 then break;
- inc(_nblevel);
- lChild:=TIndexPage.Create(Self);
- lChild._UpperLevel:=lPos;
- lPos._LowerLevel:=lChild;
- lChild.SetPageNo(lPos.Entry._LowerPage);
- lPos:=lChild;
- until false;
- _Spare:=TIndexPage.Create(Self);
- // _Field:=_IndexFile._Dbf.FindField(_NdxHdr.KeyDesc);
- First;
- *)
- end;
- end;
- destructor TIndexFile.Destroy;
- begin
- inherited;
- end;
- //==========================================================
- //============ TIndexPage
- //==========================================================
- constructor TIndexPage.Create(Parent:TIndex);
- begin
- _LowerLevel:=nil;
- _UpperLevel:=nil;
- _Index:=Parent;
- _PageNo:=-1;
- _EntryNo:=-1;
- end;
- destructor TIndexPage.Destroy;
- begin
- if _LowerLevel<>nil then _LowerLevel.Free;
- end;
- function TIndexPage.GetPEntry(EntryNo:integer):PNdxEntry;
- begin
- Result:=PNdxentry(@_PageBuff.Entries[_Index._NdxHdr.keyreclen*entryno]);
- end;
- function TIndexPage.LocalInsert(Recno:integer; Buffer:Pchar;LowerPage:integer):boolean;
- var
- src,dst:pointer;
- siz:integer;
- begin
- if _PageBuff.NbEntries < _Index._NdxHdr.nbkey then begin
- src:=Entry;
- dst:=GetPEntry(_EntryNo+1);
- siz:=(_PageBuff.NbEntries - _EntryNo)
- * _Index._NdxHdr.keyreclen + 8;
- Move(Src^, Dst^, Siz);
- inc(_PageBuff.NbEntries);
- SetEntry(Recno,Buffer,LowerPage);
- Write;
- Result:=true;
- end else begin
- Result:=false;
- end;
- end;
- function TIndexPage.LocalDelete:boolean;
- var
- src,dst:pointer;
- siz:integer;
- begin
- if _PageBuff.NbEntries >=0 then begin
- if _EntryNo<_PageBuff.NbEntries then begin
- src:=GetPEntry(_EntryNo+1);
- dst:=Entry;
- siz:=(_PageBuff.NbEntries - _EntryNo - 1)
- * _Index._NdxHdr.keyreclen + 8;
- Move(Src^, Dst^, Siz);
- end;
- dec(_PageBuff.NbEntries);
- Write;
- if ((_PageBuff.NbEntries=0) and (_lowerlevel=nil))
- or (_PageBuff.NbEntries<0) then begin
- if _UpperLevel<>nil then begin
- _UpperLevel.LocalDelete;
- end;
- end else if (_EntryNo>LastEntryNo) then begin
- SetEntryNo(LastEntryNo); // We just removed the last on this page.
- if (_UpperLevel<>nil) then begin
- _UpperLevel.SetEntry(0,Entry^.CKey,_PageNo);
- end;
- end;
- Result:=true;
- end else begin
- Result:=false;
- end;
- end;
- function TIndexPage.LastEntryNo:integer;
- begin
- if (_LowerLevel=nil) then begin
- result := _PageBuff.NbEntries - 1;
- end else begin
- result := _PageBuff.NbEntries;
- end;
- end;
- procedure TIndexPage.LocalFirst;
- begin
- SetEntryNo(0);
- end;
- procedure TIndexPage.LocalLast;
- begin
- SetEntryNo(LastEntryNo);
- end;
- function TIndexPage.LocalPrev:boolean;
- begin
- if _EntryNo>0 then begin
- SetEntryNo(_EntryNo-1);
- Result:=true;
- end else begin
- Result:=false;
- end;
- end;
- function TIndexPage.LocalNext:boolean;
- begin
- if (_EntryNo<LastEntryNo) then begin
- SetEntryNo(_EntryNo+1);
- Result:=true;
- end else begin
- Result:=false;
- end;
- end;
- procedure TIndexPage.First;
- begin
- LocalFirst;
- if (_LowerLevel<>nil) then LowerLevel.First;
- end;
- procedure TIndexPage.Last;
- begin
- LocalLast;
- if (_LowerLevel<>nil) then LowerLevel.Last;
- end;
- function TIndexPage.Prev:boolean;
- begin
- if (_LowerLevel<>nil) and LowerLevel.Prev then begin
- result:=true;
- exit;
- end;
- Result:=LocalPrev;
- if Result and (Entry^._LowerPage>0) then LowerLevel.Last;
- end;
- function TIndexPage.Next:boolean;
- begin
- if (_LowerLevel<>nil) and LowerLevel.next then begin
- result:=true;
- exit;
- end;
- Result:=LocalNext;
- if Result and (Entry^._LowerPage>0) then LowerLevel.First;
- end;
- function TIndexPage.FindNearest(Recno:integer; Key:pchar):integer;
- var
- cmpres:integer;
- v1,v2:double;
- p:TIndexPage;
- begin
- Result:=-1;
- if @Key=nil then begin
- Exit;
- end;
- SetEntryNo(0);
- while _EntryNo<=_PageBuff.NbEntries do begin
- if _EntryNo=_PageBuff.NbEntries then break;
- if _Index._NdxHdr.keytype='C' then begin
- cmpres:=StrLIComp(PChar(Key),Entry^.CKey,_Index._FieldLen);
- end else begin
- // Numeric field... to do
- v1:=PDouble(Key)^;
- v2:=Entry^.NKey;
- if v1>v2 then cmpres:=1
- else if v1<v2 then cmpres:=-1
- else cmpres:=0;
- end;
- if cmpres=0 then begin
- if _LowerLevel=nil then begin
- if (Entry^.RecNo=Recno) then begin
- result:=0;
- Exit;
- end else if (Entry^.Recno>Recno) then begin
- result:=-1;
- Exit;
- end;
- end else begin
- p:=self;
- while p._LowerLevel<>nil do begin
- p:=p.LowerLevel;
- p.LocalLast;
- end;
- if (p.Entry^.Recno>=Recno) then begin
- result:=-1;
- Exit;
- end;
- end;
- end else if cmpres<0 then begin
- result:=-1;
- exit;
- end;
- SetEntryNo(_EntryNo+1);
- end;
- result:=1;
- Exit;
- end;
- procedure TIndexPage.SetEntry(Recno:Integer; key:PChar; LowerPage:integer);
- begin
- assert((_EntryNo>=0) and (_EntryNo<=_PageBuff.NbEntries));
- if (_EntryNo=self._PageBuff.NbEntries) then begin
- if (_UpperLevel<>nil) then begin
- _UpperLevel.SetEntry(0,key,Self._PageNo);
- end;
- end else begin
- if _Index._NdxHdr.keytype='C' then begin
- mymove(key,Entry^.CKey,_Index._NdxHdr.keylen);
- end else begin
- Entry^.NKey:=PDouble(key)^;
- end;
- end;
- Entry^.RecNo:=RecNo;
- Entry^._LowerPage:=LowerPage;
- Write;
- end;
- function TIndexPage.LowerLevel : TIndexPage;
- begin
- if (_LowerLevel<>nil) and (_LowerLevel._PageNo<>Entry^._LowerPage) then begin
- _LowerLevel.SetPageNo(Entry^._LowerPage);
- end;
- result:=_LowerLevel;
- end;
- function TIndexPage.Insert(Recno:Integer; Buffer:PChar; LowerPage:integer):boolean;
- var
- src,dst:PNdxEntry;
- siz:integer;
- split,old_entry:integer;
- lSpare:TIndexPage;
- begin
- if not LocalInsert(recno,buffer,lowerpage) then begin
- // The entry is FULL so we will split this page
- // 1 - Check parent exist
- if _UpperLevel=nil then begin
- AddNewLevel;
- end;
- old_entry:=_EntryNo;
- split:=_EntryNo;
- if split < _Index._NdxHdr.nbkey div 2 then begin
- split:=_Index._NdxHdr.nbkey div 2;
- end;
- lSpare:=TIndexPage.Create(_Index);
- try
- // 2 - Create new page with first part
- inc(_Index._NdxHdr.nbPage);
- lSpare._PageNo:=_Index._NdxHdr.nbPage;
- _Index._IndexFile._Seek(_Index._RootPage);
- _Index._IndexFile.Stream.WriteBuffer (_Index._NdxHdr, SizeOf(_Index._NdxHdr));
- if _lowerlevel=nil then begin
- lSpare._PageBuff.NbEntries:=split;
- end else begin
- lSpare._PageBuff.NbEntries:=split-1;
- end;
- siz:=split*_Index._NdxHdr.keyreclen+8;
- src:=@_PageBuff.Entries;
- dst:=@lSpare._PageBuff.Entries;
- Move(src^,dst^,siz);
- lSpare.Write;
- // 3 - Keep only end-part in this page
- siz:=(_PageBuff.NbEntries-Split);
- _PageBuff.NbEntries:=siz;
- siz:=siz*_Index._NdxHdr.keyreclen+8;
- SetEntryNo(split);
- src:=Entry;
- SetEntryNo(0);
- dst:=Entry;
- Move(src^,dst^,siz);
- // 3 - Update upper level
- lSpare.SetEntryNo(split-1);
- _UpperLevel.Insert(0,lSpare.Entry^.CKey,lSpare._PageNo);
- // We just need to go on inserted record now
- if old_entry>=split then begin
- _UpperLevel.LocalNext;
- SetEntryNo(old_entry - split);
- LocalInsert(Recno,Buffer,LowerPage);
- lSpare.Write;
- end else begin
- lSpare.SetEntryNo(old_entry);
- lSpare.LocalInsert(Recno,Buffer,LowerPage);
- Write;
- end;
- finally
- lspare.free;
- end;
- end;
- Result:=true;
- end;
- function TIndexPage.Delete:boolean;
- begin
- Result:=LocalDelete;
- end;
- procedure TIndexPage.SetPageNo(page:Integer);
- begin
- if (_PageNo<>page) and (page>0) then begin
- _Index._IndexFile.ReadRecord(Page,@_PageBuff);
- _PageNo:=page;
- _EntryNo:=-1;
- end;
- end;
- procedure TIndexPage.AddNewLevel;
- var
- lNewPage:TIndexPage;
- begin
- lNewPage:=TIndexPage.Create(_Index);
- inc(_Index._NdxHdr.nbPage);
- lNewPage._PageNo:= _Index._NdxHdr.nbPage;
- _Index._NdxHdr.startpage:= _Index._NdxHdr.nbPage;
- _Index._IndexFile._Seek(_Index._RootPage);
- _Index._IndexFile.Stream.WriteBuffer (_Index._NdxHdr, SizeOf(_Index._NdxHdr));
- lNewPage._PageBuff.NbEntries:=0;
- lNewPage._UpperLevel:=nil;
- lNewPage._LowerLevel:=_Index._Root;
- lNewPage.SetEntryNo(0);
- lNewPage.SetEntry(0,nil,_PageNo);
- _Index._Root._UpperLevel:=lNewPage;
- _Index._Root:=lNewPage;
- lNewPage:=nil;
- end;
- procedure TIndexPage.Write;
- begin
- _Index._IndexFile.WriteRecord(_PageNo,@_PageBuff);
- end;
- procedure TIndexPage.SetEntryNo(entryno:Integer);
- begin
- if (_EntryNo<>entryno) then begin
- _EntryNo:=entryno;
- if _EntryNo>=0 then Entry:=PNdxentry(@_PageBuff.Entries[_Index._NdxHdr.keyreclen*entryno]);
- end;
- end;
- procedure TIndexPage.WritePage(Page:integer);
- begin
- _Index._IndexFile.WriteRecord(Page,@_PageBuff);
- end;
- //==========================================================
- //============ TIndex
- //==========================================================
- constructor TIndex.Create(Parent:TIndexFile; RootPage:integer;CreateIt:boolean);
- var
- lPos:TIndexPage;
- lChild:TIndexPage;
- begin
- _RootPage:=RootPage;
- _IndexFile:=Parent;
- //_IndexOrder:=TList.Create;
- if CreateIt then begin
- FillChar(_NdxHdr,sizeof(_NdxHdr),0);
- _NdxHdr.startpage:=1;
- _NdxHdr.nbPage:=2;
- _NdxHdr.keyformat:=#0;
- _NdxHdr.keytype:='C';
- _IndexFile._Seek(RootPage);
- _IndexFile.Stream.Write(_NdxHdr,SizeOf(_NdxHdr));
- _FieldPos := 0;
- _FieldLen := 0;
- end else begin
- _IndexFile._Seek(RootPage);
- _IndexFile.Stream.Read(_NdxHdr,SizeOf(_NdxHdr));
- end;
- _Root:=TIndexPage.Create(Self);
- _Root.SetPageNo(_NdxHdr.startpage);
- lPos:=_Root;
- _nblevel:=1;
- repeat
- lPos.LocalFirst;
- if lPos.Entry^._LowerPage=0 then break;
- inc(_nblevel);
- lChild:=TIndexPage.Create(Self);
- lChild._UpperLevel:=lPos;
- lPos._LowerLevel:=lChild;
- lChild.SetPageNo(lPos.Entry^._LowerPage);
- lPos:=lChild;
- until false;
- inc(_IndexFile._cntuse);
- First;
- end;
- destructor TIndex.Destroy;
- begin
- _IndexFile.Release;
- _Root.Free;
- end;
- function TIndex.Find(Recno:integer; Buffer:PChar; var pPos:TIndexPage):integer;
- var
- res:integer;
- begin
- pPos:=_Root;
- repeat
- res:=pPos.FindNearest(Recno,Buffer);
- if res<>0 then begin
- if pPos.Entry^._LowerPage<>0 then begin
- pPos:=pPos.LowerLevel;
- res:=2;
- end;
- end;
- until res<>2;
- Result:=res;
- end;
- procedure TIndex.Update(Recno: integer; PrevBuffer,NewBuffer: PChar);
- var
- lPos:TIndexPage;
- begin
- if _FieldLen=0 then exit;
- inc(PrevBuffer,_FieldPos);
- inc(NewBuffer,_FieldPos);
- if StrLIComp(PrevBuffer,NewBuffer,_FieldLen)<>0 then begin
- Delete;
- Find(Recno+1,NewBuffer,lPos);
- lPos.Insert(Recno+1,NewBuffer,0);
- end;
- end;
- procedure TIndex.Insert(Recno:integer; Buffer:PChar);
- var
- lPos:TIndexPage;
- begin
- if _FieldLen=0 then exit;
- inc(Buffer,_FieldPos);
- Find(Recno+1,Buffer,lPos);
- lPos.Insert(Recno+1,Buffer,0);
- end;
- function TIndex.Delete:boolean;
- var
- lPos:TIndexPage;
- begin
- lpos:=_root;
- while lpos._LowerLevel<>nil do begin
- lPos:=lPos.LowerLevel;
- end;
- lPos.Delete;
- Result:=true;
- end;
- function TIndex.Pos:TIndexPage;
- var
- p:TIndexPage;
- begin
- p:=_Root;
- while p.Entry^._LowerPage>0 do begin
- p:=p.LowerLevel;
- end;
- result:=p;
- end;
- procedure TIndex.First;
- begin
- _Root.First;
- dec(Pos._EntryNo);
- end;
- procedure TIndex.Last;
- begin
- _Root.Last;
- inc(Pos._EntryNo);
- end;
- function TIndex.Prev:boolean;
- begin
- result:=_Root.Prev;
- end;
- function TIndex.Next:boolean;
- begin
- result:=_Root.Next;
- end;
- (*
- procedure TIndex.SetRecNo(Value: Integer);
- var
- pos:integer;
- p:TIndexPage;
- i:integer;
- ldiv:integer;
- begin
- p:=_Root;
- ldiv:=1;
- while p.Entry^._LowerPage>0 do begin
- ldiv:=ldiv*(_NdxHdr.nbkey+1);
- p:=p._LowerLevel;
- end;
- pos:=value div ldiv;
- p:=_Root;
- while p.Entry^._LowerPage>0 do begin
- p._EntryNo:=pos;
- value:=value - pos * (_NdxHdr.nbkey+1);
- ldiv:=ldiv div (_NdxHdr.nbkey+1);
- pos:=value div ldiv;
- p:=p._LowerLevel;
- end;
- {
- pos:=1;
- First;
- While pos<value do begin
- if Next = false then break;
- inc(pos);
- end;
- }
- end;
- *)
- function TIndex.GuessRecordCount: Integer;
- var
- lPos:TIndexPage;
- nbrecord:integer;
- begin
- // I just read first level and Guess an approximate record count...
- nbrecord:=_Root._PageBuff.NbEntries;
- lPos:=_Root.LowerLevel;
- while lpos<>nil do begin
- nbrecord:=nbrecord*(_NdxHdr.nbkey+1);
- lPos:=lPos.LowerLevel;
- end;
- result:=nbrecord;
- end;
- function TIndex.GuessRecNo:Integer;
- var
- p:TIndexPage;
- begin
- p:=_Root;
- result:=p._EntryNo;
- while p.Entry^._LowerPage>0 do begin
- p:=p.LowerLevel;
- Result:=Result*(_NdxHdr.nbkey+1) + p._EntryNo;
- end;
- end;
- function TIndex.GetRealRecNo:integer;
- var
- ippos : TIndexPage;
- begin
- ippos:=_Root;
- while ippos._LowerLevel<>nil do begin
- ippos:=pos.LowerLevel;
- end;
- if (ippos._EntryNo<0) or (ippos._EntryNo>=ippos._PageBuff.NbEntries) then Result:=-1
- else Result:=ippos.Entry^.RecNo-1;
- end;
- procedure TIndex.GotoKey(recno:integer; buffer:pchar);
- begin
- // very temporary implementation
- // could definitely be a bit faster.
- _Root.First;
- repeat
- if self.Pos.Entry^.RecNo=(recno+1) then begin
- exit;
- end;
- until Next=false;
- end;
- procedure TIndex.InitFieldDef(dbfFile:TDbfFile;FieldDesc:string);
- var
- FieldInfo:TMyFieldInfo;
- begin
- FieldInfo:=DbfFile.GetFieldInfo(FieldDesc);
- if FieldInfo<>nil then begin
- _FieldPos:=FieldInfo.Offset;
- _FieldLen:=FieldInfo.Size;
- end;
- end;
- //==========================================================
- //============ initialization
- //==========================================================
- {$ifndef fpc}
- type
- TTableNameProperty = class(TStringProperty)
- public
- procedure Edit; override;
- function GetAttributes: TPropertyAttributes; override;
- end;
- procedure TTableNameProperty.Edit; {override;}
- var
- FileOpen: TOpenDialog;
- Dbf: TDbf;
- begin
- FileOpen := TOpenDialog.Create(Application);
- try
- with fileopen do begin
- Dbf:=GetComponent(0) as TDbf;
- Filename := Dbf.DesignTimePath + GetValue;
- Filter := 'Dbf table|*.dbf';
- if Execute then begin
- SetValue(ExtractFilename(Filename));
- Dbf.DesignTimePath:=ExtractFilePath(Filename);
- end;
- end;
- finally
- Fileopen.free;
- end;
- end;
- function TTableNameProperty.GetAttributes: TPropertyAttributes; {override;}
- begin
- Result := [paDialog, paRevertable];
- end;
- type
- TRunTimePathProperty = class(TStringProperty)
- end;
- TDesignTimePathProperty = class(TStringProperty)
- end;
- //==========================================================
- //============ initialization
- //==========================================================
- procedure Register;
- begin
- RegisterComponents('Exemples', [TDbf]);
- RegisterPropertyEditor(TypeInfo(string), TDbf, 'TableName', TTableNameProperty);
- RegisterPropertyEditor(TypeInfo(string), TDbf, 'RunTimePath', TRunTimePathProperty);
- RegisterPropertyEditor(TypeInfo(string), TDbf, 'DesignTimePath', TDesignTimePathProperty);
- // RegisterPropertyEditor(TypeInfo(TStrings), TDbf, 'IndexFiles', TIndexFilesProperty);
- // ShowMessage(ToolServices.GetProjectName);
- end;
- {$endif fpc}
- initialization
- _PagedFiles := TList.Create;
- tDbf_TrimFields := true;
- finalization
- _PagedFiles.free;
- end.
|