1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899 |
- 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}
- {$ifdef fpc}
- procedure Translate(Src, Dest: PChar; ToOem: Boolean); override; {virtual}
- {$else}
- function Translate(Src, Dest: PChar; ToOem: Boolean): Integer; override; {virtual}
- {$endif}
- {$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}
- procedure TDbf.Translate(Src, Dest: PChar; ToOem: Boolean); {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.
|