dbf.pas 76 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899
  1. unit dbf;
  2. {===============================================================================
  3. || TDbf Component || http://tdbf.netfirms.com ||
  4. ===============================================================================}
  5. interface
  6. uses
  7. {$ifdef fpc}
  8. SysUtils, Classes, db;
  9. {$else}
  10. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  11. Db, DsgnIntf, ExptIntf;
  12. {$endif}
  13. // If you got a compilation error here or asking for dsgntf.pas, then just add
  14. // this file in your project:
  15. // dsgnintf.pas in 'C:\Program Files\Borland\Delphi5\Source\Toolsapi\dsgnintf.pas'
  16. const
  17. _MAJOR_VERSION = 3;
  18. _MINOR_VERSION = 007;
  19. {$ifdef VER100} // Delphi 3
  20. {$define DELPHI_3}
  21. {$endif}
  22. {$ifdef VER110} // CBuilder 3
  23. {$define DELPHI_3}
  24. {$endif}
  25. {$ifdef unix}
  26. DirSeparator = '/';
  27. {$else}
  28. DirSeparator = '\';
  29. {$endif}
  30. //====================================================================
  31. // Delphi is a bit to permissive for me, I mean protected doesn't work within
  32. // one unit. So i decided that convention:
  33. // private member begins by '_'
  34. // It's forbidden to access any '_something' except from the class where it
  35. // is defined. To check that, I just have to look for '._' anywhere in the code.
  36. //====================================================================
  37. type
  38. //====================================================================
  39. //=== Common exceptions and constants
  40. //====================================================================
  41. EBinaryDataSetError = class (Exception);
  42. EFieldToLongError = class (Exception);
  43. xBaseVersion = (xBaseIII,xBaseIV,xBaseV);
  44. //====================================================================
  45. //=== Utility classes
  46. //====================================================================
  47. TPagedFile = class(TObject)
  48. protected
  49. Stream : TStream;
  50. HeaderSize : Integer;
  51. RecordSize : Integer;
  52. _cntuse:integer;
  53. _Filename:string;
  54. public
  55. constructor Create(const FileName: string; Mode: Word);
  56. destructor Destroy; override;
  57. procedure Release;
  58. function CalcRecordCount:Integer;
  59. procedure _Seek(page:Integer);
  60. procedure ReadRecord(IntRecNum:Integer;Buffer:Pointer);
  61. procedure WriteRecord(IntRecNum:Integer;Buffer:Pointer);
  62. end;
  63. //====================================================================
  64. //=== Dbf support (first part)
  65. //====================================================================
  66. rDbfHdr = record
  67. VerDBF : byte; // 0
  68. Year : byte; // 1
  69. Month : byte; // 2
  70. Day : byte; // 3
  71. RecordCount : Integer; // 4-7
  72. FullHdrSize : word; // 8-9
  73. RecordSize : word; // 10-11
  74. Dummy1 : Word; // 12-13
  75. IncTrans : byte; // 14
  76. Encrypt : byte; // 15
  77. Dummy2 : Integer; // 16-19
  78. Dummy3 : array[20..27] of byte; // 20-27
  79. MDXFlag : char; // 28
  80. Language : char; // 29
  81. dummy4 : word; // 30-31
  82. end;
  83. //====================================================================
  84. TMyFieldInfo = class
  85. public
  86. FieldName:string;
  87. Size:Integer;
  88. Prec:Integer;
  89. Offset:Integer;
  90. end;
  91. //====================================================================
  92. TDbfFile = class(TPagedFile)
  93. protected
  94. _RecordBufferSize:integer;
  95. _DataHdr : rDbfHdr;
  96. _DbfVersion : xBaseVersion;
  97. _MyFieldInfos: TList;
  98. public
  99. constructor Create(const FileName: string; Mode: Word);
  100. destructor Destroy; override;
  101. function RecordCount:integer;
  102. procedure CreateFieldDefs(FieldDefs:TFieldDefs);
  103. procedure ClearMyFieldInfos;
  104. procedure DbfFile_CreateTable(FieldDefs:TFieldDefs);
  105. procedure DbfFile_PackTable;
  106. function GetFieldInfo(FieldName:string):TMyFieldInfo;
  107. function GetFieldData(Column:Integer;DataType:TFieldType; Src,Dst: Pointer): Boolean;
  108. procedure SetFieldData(Column:integer;DataType:TFieldType; Src,Dst: Pointer);
  109. procedure WriteHeader;
  110. end;
  111. //====================================================================
  112. //=== Index support
  113. //====================================================================
  114. TIndex = class;
  115. //====================================================================
  116. rNdxHdr = record
  117. startpage : Integer; // 0..3
  118. nbPage : Integer; // 4..7
  119. keyformat: Char; //8
  120. keytype : char; //9
  121. dummy : Word; // 10..11
  122. keylen : Word; // 12..13
  123. nbkey : Word; // 14..15
  124. skeytype : Word; // 16..17
  125. keyreclen : Word; // 18..19
  126. dummy2 : Word; // 20..21
  127. dummy3 : Byte; // 22
  128. Unique : Byte; // 23
  129. KeyDesc : array[0..255] of char; // 24...
  130. end;
  131. rMdxTag = record
  132. pageno : Integer; // 0..3
  133. tagname : array [0..11] of char; // 4..14
  134. keyformat : byte; // 15
  135. forwardTag1 : char; // 16
  136. forwardTag2 : byte; // 17
  137. backwardTag : byte; // 18
  138. dummy : byte; // 19
  139. keytype : byte; // 20
  140. end;
  141. NdxKeyType = (N,C);
  142. PNdxPage = ^rNdxPage;
  143. rNdxPage = record
  144. NbEntries : longint; // 0..3 lower page
  145. Entries : ARRAY [0..507] OF char;
  146. end;
  147. PNdxentry = ^rNdxentry;
  148. rNdxentry = record
  149. _LowerPage : longint; // 0..3 lower page
  150. RecNo : Longint; // 4..7 recno
  151. case NdxKeyType of
  152. N: ( NKey: double);
  153. C: ( CKey: array [0..503] of char);
  154. end;
  155. //====================================================================
  156. rMdxHdr = record
  157. MdxHdr : byte; // 0
  158. Year : byte; // 1
  159. Month : byte; // 2
  160. Day : byte; // 3
  161. FileName : array[0..15] of char; // 4..19 of byte
  162. BlockSize : word; // 20 21
  163. BlockAdder : word; // 22 23
  164. IndexFlag : byte; // 24
  165. NoTag : byte; // 25
  166. TagSize : byte; // 26
  167. Dummy1 : byte; // 27
  168. TagUsed : word; // 28..29
  169. Dummy2 : word; // 30..31
  170. NbPage : Integer; // 32..35
  171. FreePage : Integer; // 36..39
  172. BlockFree : Integer; // 40..43
  173. UpdYear : byte; // 44
  174. UpdMonth : byte; // 45
  175. UpdDay : byte; // 46
  176. end;
  177. //====================================================================
  178. TIndexFile = class(TPagedFile)
  179. protected
  180. _IndexVersion : xBaseVersion;
  181. _MdxHdr : rMdxHdr;
  182. public
  183. constructor Create(const FileName: string; Mode: Word);
  184. destructor Destroy; override;
  185. end;
  186. //====================================================================
  187. PIndexPosInfo = ^TIndexPage;
  188. TIndexPage = class
  189. protected
  190. _Index : TIndex;
  191. _PageNo : Integer;
  192. _EntryNo : Integer;
  193. Entry : PNdxentry;
  194. _LowerLevel : TIndexPage;
  195. _UpperLevel : TIndexPage;
  196. _PageBuff:rNdxPage;
  197. procedure LocalFirst;
  198. procedure LocalLast;
  199. function LocalPrev:boolean;
  200. function LocalNext:boolean;
  201. function LastEntryNo:integer;
  202. function LocalInsert(Recno:Integer; Buffer:PChar; LowerPage:integer):boolean;
  203. function LocalDelete:boolean;
  204. function GetPEntry(EntryNo:integer):PNdxEntry;
  205. procedure First;
  206. procedure Last;
  207. function Prev:boolean;
  208. function Next:boolean;
  209. procedure Write;
  210. procedure AddNewLevel;
  211. public
  212. constructor Create(Parent:TIndex);
  213. destructor Destroy; override;
  214. procedure SetPageNo(page:Integer);
  215. procedure SetEntryNo(entryno:Integer);
  216. procedure WritePage(Page:integer);
  217. function FindNearest(Recno:integer; Key:PChar):integer;
  218. function Insert(Recno:integer; Buffer:pchar; LowerPage:integer):boolean;
  219. procedure SetEntry(Recno:integer; key:pchar; LowerPage:integer);
  220. function Delete:boolean;
  221. function LowerLevel : TIndexPage;
  222. end;
  223. //====================================================================
  224. TIndex = class(TObject)
  225. protected
  226. _IndexFile:TIndexFile;
  227. _NdxHdr:rNdxHdr;
  228. _Root:TIndexPage;
  229. _TagPosition:Integer;
  230. _FieldPos : integer;
  231. _FieldLen : integer;
  232. _NbLevel : integer;
  233. _RootPage: integer;
  234. function Pos:TIndexPage;
  235. public
  236. IndexRecNo:integer;
  237. function Prev:boolean;
  238. function Next:boolean;
  239. procedure First;
  240. procedure Last;
  241. function Find(Recno:integer; Buffer:PChar; var pPos:TIndexPage):integer;
  242. procedure Insert(Recno:integer; Buffer:PChar);
  243. function Delete:boolean;
  244. procedure GotoKey(Recno:integer; Buffer:PChar);
  245. procedure Update(Recno: integer; PrevBuffer,NewBuffer: PChar);
  246. // procedure ResyncInd;
  247. function GetRealRecNo: Integer;
  248. constructor Create(Parent:TIndexFile; RootPage:integer;CreateIt:boolean);
  249. procedure InitFieldDef(dbfFile:TDbfFile;FieldDesc:string);
  250. destructor Destroy; override;
  251. // optionnal
  252. function GuessRecordCount: Integer;
  253. function GuessRecNo: Integer;
  254. end;
  255. //====================================================================
  256. //=== Memo and binary fields support
  257. //====================================================================
  258. rDbtHdr = record
  259. NextBlock:Longint;
  260. Dummy : array [4..7] of byte;
  261. _dbfFile : array [0..7] of Byte; //8..15
  262. bVer : Byte; //16
  263. Dummy2 : array [17..19] of byte;
  264. BlockLen: Word;
  265. end;
  266. //====================================================================
  267. TDbtFile = class(TPagedFile)
  268. protected
  269. _DbtVersion:xBaseVersion;
  270. _MemoHdr:rDbtHdr;
  271. public
  272. constructor Create(const FileName: string; Mode: Word; Ver:xBaseVersion);
  273. procedure ReadMemo(recno:Integer;Dst:TStream);
  274. procedure WriteMemo(var MemoRecno:Integer;ReadSize:Integer;Src:TStream);
  275. end;
  276. //====================================================================
  277. TMyBlobFile = class(TMemoryStream)
  278. public
  279. Mode: TBlobStreamMode;
  280. Field:TField;
  281. MemoRecno:Integer;
  282. ReadSize:Integer;
  283. constructor Create(ModeVal:TBlobStreamMode; FieldVal:TField);
  284. destructor destroy; override;
  285. end;
  286. //====================================================================
  287. //=== Dbf support 2
  288. //====================================================================
  289. rFieldHdrIII = record
  290. FieldName : array[0..10] of char;
  291. FieldType : char; // 11
  292. Dummy : array[12..15] of byte;
  293. FieldSize : byte; // 16
  294. FieldPrecision : byte; //17
  295. dummy2 : array[18..31] of byte;
  296. end;
  297. //====================================================================
  298. rFieldHdrV = record
  299. FieldName : array[0..10] of char;
  300. Dummy0 : array[11..31] of byte;
  301. FieldType : char; // 32
  302. FieldSize : byte; // 33
  303. FieldPrecision : byte; //34
  304. dummy2 : array[35..47] of byte;
  305. end;
  306. //====================================================================
  307. PBookMarkData = ^rBookMarkData;
  308. rBookmarkData = record
  309. RecNo:longint;
  310. end;
  311. //====================================================================
  312. rBeforeRecord = record
  313. BookmarkData: rBookmarkData;
  314. BookmarkFlag: TBookmarkFlag;
  315. //... record come here
  316. end;
  317. //====================================================================
  318. pDbfRecord = ^rDbfRecord;
  319. rDbfRecord = record
  320. BookmarkData: rBookmarkData;
  321. BookmarkFlag: TBookmarkFlag;
  322. DeletedFlag : char;
  323. Fields : array[0..4000] of char;
  324. end;
  325. //====================================================================
  326. PRecInfo = ^TRecInfo;
  327. TRecInfo = record
  328. Bookmark: Longint;
  329. IdxBookmark: Longint;
  330. BookmarkFlag: TBookmarkFlag;
  331. end;
  332. //====================================================================
  333. pRecordHdr = ^tRecordHdr;
  334. tRecordHdr = record
  335. DeletedFlag : char;
  336. end;
  337. // and at LEAST the most useful class : TDbf
  338. //====================================================================
  339. TDbf = class(TDataSet)
  340. private
  341. _ShowDeleted:boolean;
  342. _TableName: string; // table path and file name
  343. _RunTimePath: string; // table path and file name
  344. _DesignTimePath: string; // table path and file name
  345. _ReadOnly : Boolean;
  346. _FilterBuffer:pchar;
  347. _PrevBuffer:pchar;
  348. _IndexFiles:TStrings;
  349. protected
  350. function _FullRecordSize:integer;
  351. function _FilterRecord(Buffer: PChar): Boolean;
  352. procedure _OpenFiles(CreateIt:boolean);
  353. procedure _CloseFiles;
  354. procedure _ResyncIndexes(Buffer: PChar);
  355. function _GetIndexName: string;
  356. procedure _SetIndexName(const Value: string);
  357. function _GetIndex(filename:string):TIndex;
  358. function _GetPath:string;
  359. function _ComponentInfo:string;
  360. public
  361. { my own methods and properties}
  362. { most looks like ttable functions but they are not tdataset related
  363. I use the same syntax to facilitate the conversion between bde and tdbf }
  364. easyfilter:string;
  365. procedure CreateTable; //(FieldDefs:TFieldDefs);
  366. procedure DeleteIndex(const AName: string);
  367. property IndexName: string read _GetIndexName write _SetIndexName;
  368. {$ifdef DELPHI_3}
  369. procedure AddIndex(const IndexName, Fields: String; Options: TIndexOptions);
  370. {$else}
  371. {$ifndef FPC}
  372. procedure AddIndex(const IndexName, Fields: String; Options: TIndexOptions; const DescFields: String='');
  373. {$else}
  374. procedure AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions);
  375. procedure AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions; const DescFields: String);
  376. {$endif}
  377. {$endif}
  378. procedure CloseIndexFile(const IndexFileName: string);
  379. procedure OpenIndexFile(AnIndexName:string);
  380. procedure PackTable;
  381. public
  382. { abstract methods }
  383. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override; {virtual abstract}
  384. {virtual methods (mostly optionnal) }
  385. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override; {virtual}
  386. {$ifdef DELPHI_3}
  387. procedure Translate(Src, Dest: PChar; ToOem: Boolean); override; {virtual}
  388. {$else}
  389. {$ifdef fpc}
  390. procedure Translate(Src, Dest: PChar; ToOem: Boolean); override; {virtual}
  391. {$else}
  392. function Translate(Src, Dest: PChar; ToOem: Boolean): Integer; override; {virtual}
  393. {$endif}
  394. {$endif}
  395. procedure ClearCalcFields(Buffer : PChar); override;
  396. protected
  397. { abstract methods }
  398. function AllocRecordBuffer: PChar; override; {virtual abstract}
  399. procedure FreeRecordBuffer(var Buffer: PChar); override; {virtual abstract}
  400. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override; {virtual abstract}
  401. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override; {virtual abstract}
  402. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override; {virtual abstract}
  403. function GetRecordSize: Word; override; {virtual abstract}
  404. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override; {virtual abstract}
  405. procedure InternalClose; override; {virtual abstract}
  406. procedure InternalDelete; override; {virtual abstract}
  407. procedure InternalFirst; override; {virtual abstract}
  408. procedure InternalGotoBookmark(ABookmark: Pointer); override; {virtual abstract}
  409. procedure InternalHandleException; override; {virtual abstract}
  410. procedure InternalInitFieldDefs; override; {virtual abstract}
  411. procedure InternalInitRecord(Buffer: PChar); override; {virtual abstract}
  412. procedure InternalLast; override; {virtual abstract}
  413. procedure InternalOpen; override; {virtual abstract}
  414. procedure InternalPost; override; {virtual abstract}
  415. procedure InternalSetToRecord(Buffer: PChar); override; {virtual abstract}
  416. function IsCursorOpen: Boolean; override; {virtual abstract}
  417. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override; {virtual abstract}
  418. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override; {virtual abstract}
  419. procedure SetFieldData(Field: TField; Buffer: Pointer); override; {virtual abstract}
  420. {virtual methods (mostly optionnal) }
  421. function GetRecordCount: Integer; override; {virtual}
  422. function GetRecNo: Integer; override; {virtual}
  423. procedure SetRecNo(Value: Integer); override; {virual}
  424. public
  425. constructor Create(AOwner: TComponent); override;
  426. destructor Destroy; override;
  427. published
  428. property ComponentInfo: string read _ComponentInfo;
  429. property TableName: string read _TableName write _TableName;
  430. property RunTimePath: string read _RunTimePath write _RunTimePath;
  431. property DesignTimePath: string read _DesignTimePath write _DesignTimePath;
  432. property ReadOnly : Boolean read _ReadOnly write _Readonly default False;
  433. property ShowDeleted:boolean read _ShowDeleted write _ShowDeleted;
  434. // redeclared data set properties
  435. property Active;
  436. property Filtered;
  437. property BeforeOpen;
  438. property AfterOpen;
  439. property BeforeClose;
  440. property AfterClose;
  441. property BeforeInsert;
  442. property AfterInsert;
  443. property BeforeEdit;
  444. property AfterEdit;
  445. property BeforePost;
  446. property AfterPost;
  447. property BeforeCancel;
  448. property AfterCancel;
  449. property BeforeDelete;
  450. property AfterDelete;
  451. property BeforeScroll;
  452. property AfterScroll;
  453. property OnCalcFields;
  454. property OnDeleteError;
  455. property OnEditError;
  456. property OnFilterRecord;
  457. property OnNewRecord;
  458. property OnPostError;
  459. //my datas....
  460. protected
  461. _IsCursorOpen:boolean;
  462. _PhysicalRecno:integer;
  463. _CurIndex: TIndex;
  464. _Indexes:TList; // index
  465. _indexFile : TIndexFile;
  466. _dbtFile : TDbtFile;
  467. public
  468. _dbfFile:TDbfFile;
  469. property PhysicalRecno:integer read _PhysicalRecno;
  470. function _RecordDataSize:integer;
  471. end;
  472. {$ifndef fpc}
  473. procedure Register;
  474. {$endif}
  475. var
  476. tDbf_TrimFields : boolean;
  477. implementation
  478. var
  479. _PagedFiles : TList;
  480. //====================================================================
  481. // Some types and consts which are not useful in the interface.
  482. //====================================================================
  483. (*
  484. * tSmallint 16 bits = -32768 to 32767
  485. * 123456 = 6 digit max
  486. * ftInteger 32 bits = -2147483648 to 2147483647
  487. * 12345678901 = 11 digits max
  488. * ftLargeInt 64 bits = -9223372036854775808 to 9223372036854775807
  489. * 12345678901234567890 = 20 digits max
  490. *)
  491. const
  492. DIGITS_SMALLINT = 6;
  493. DIGITS_INTEGER = 11;
  494. DIGITS_LARGEINT = 20;
  495. sDBF_DEC_SEP= '.';
  496. type
  497. rAfterHdrIII = record // Empty
  498. end;
  499. rAfterHdrV = record
  500. Dummy : array[32..67] of byte;
  501. end;
  502. PMdxTag = ^rMdxTag;
  503. rMdxTagHdr = record
  504. RootPage : longint;// 0..3
  505. FilePages : longint;// 4..7
  506. KeyFormat : byte; // 8
  507. KeyType : char; // 9
  508. dummy : word; // 10..11
  509. IndexKeyLength : word; // 12..13
  510. MaxNbKeys : word; // 14..15
  511. SecondKeyType : word; // 16..17
  512. IndexKeyItemLen : word; // 18..19
  513. dummy2 : array [20..22] of byte;
  514. UniqueFlag : byte; // 23
  515. end;
  516. rAfterHdrV3 = record
  517. Dummy : array[12..31] of byte;
  518. end;
  519. rAfterHdrV4 = record
  520. Dummy : array[12..67] of byte;
  521. end;
  522. rFieldHdrV3 = record
  523. FieldName : array[0..10] of char;
  524. FieldType : char; // 11
  525. Dummy : array[12..15] of byte;
  526. FieldSize : byte; // 16
  527. FieldPrecision : byte; //17
  528. dummy2 : array[18..31] of byte;
  529. end;
  530. rFieldHdrV4 = record
  531. FieldName : array[0..10] of char;
  532. Dummy0 : array[11..31] of byte;
  533. FieldType : char; // 32
  534. FieldSize : byte; // 33
  535. FieldPrecision : byte; //34
  536. dummy2 : array[35..47] of byte;
  537. end;
  538. PDouble = ^double;
  539. //====================================================================
  540. // Now some common functions and procedure
  541. //====================================================================
  542. // ****************************************************************************
  543. // International separator
  544. // thanks to Bruno Depero from Italy
  545. // and Andreas Wöllenstein from Denmark
  546. function DbfStrToFloat(s: string): Extended;
  547. var iPos: integer;
  548. eValue: extended;
  549. begin
  550. iPos:= Pos(sDBF_DEC_SEP, s);
  551. if iPos> 0 then
  552. s[iPos]:= DecimalSeparator;
  553. {$ifndef fpc}
  554. if TextToFloat(pchar(s), eValue, fvExtended) then
  555. {$else}
  556. Val(s,eValue,Ipos);
  557. If Ipos=0 then
  558. {$endif}
  559. Result:= eValue
  560. else Result:= 0;
  561. end;
  562. function FloatToDbfStr(f: Extended; size, prec: integer): string;
  563. var iPos: integer;
  564. begin
  565. Result:= FloatToStrF(f, ffFixed, Size, prec);
  566. iPos:= Pos(DecimalSeparator, Result);
  567. if iPos> 0 then
  568. Result[iPos]:= sDBF_DEC_SEP;
  569. end;
  570. procedure MyMove(Source, Dest:PChar; Count: Integer);
  571. var
  572. c:char;
  573. i:integer;
  574. begin
  575. i:=0;
  576. while i<Count do begin
  577. c:=PChar(Source)[i];
  578. if c=#0 then break;
  579. PChar(Dest)[i]:=c;
  580. Inc(i);
  581. end;
  582. while i<Count do begin
  583. PChar(Dest)[i]:=' ';
  584. Inc(i);
  585. end;
  586. end;
  587. //====================================================================
  588. // TPagedFile
  589. //====================================================================
  590. function GetPagedFile(FileName: string):TPagedFile;
  591. var
  592. idx:integer;
  593. idf:TPagedFile;
  594. begin
  595. FileName:=LowerCase(FileName);
  596. for idx:=0 to _PagedFiles.Count-1 do begin
  597. idf:= TPagedFile(_PagedFiles[idx]);
  598. if idf._FileName=FileName then begin
  599. result:=idf;
  600. exit;
  601. end;
  602. end;
  603. result:=nil;
  604. end;
  605. procedure TPagedFile.Release;
  606. var
  607. i: integer;
  608. begin
  609. dec(_cntuse);
  610. if _cntuse<=0 then begin
  611. i:=_PagedFiles.IndexOf(self);
  612. if i>=0 then _PagedFiles.Delete(i);
  613. Free;
  614. end;
  615. end;
  616. function TPagedFile.CalcRecordCount:Integer;
  617. begin
  618. if RecordSize = 0 then Result:=0
  619. else Result:=(Stream.Size - HeaderSize) div RecordSize;
  620. end;
  621. constructor TPagedFile.Create(const FileName: string; Mode: Word);
  622. begin
  623. if filename='' then Stream:=TMemoryStream.Create()
  624. else begin
  625. Stream:=TFileStream.Create(FileName,Mode);
  626. end;
  627. HeaderSize:=0;
  628. RecordSize:=0;
  629. _cntuse:=0;
  630. _filename:=lowercase(filename);
  631. _PagedFiles.Add(Self);
  632. end;
  633. destructor TPagedFile.Destroy;
  634. begin
  635. Stream.Free;
  636. Stream:=nil;
  637. inherited;
  638. end;
  639. procedure TPagedFile._Seek(page:Integer);
  640. var
  641. p:Integer;
  642. begin
  643. p:=HeaderSize + (RecordSize * page );
  644. Stream.Position := p;
  645. end;
  646. Procedure TPagedFile.ReadRecord(IntRecNum:Integer; Buffer:Pointer);
  647. begin
  648. _Seek(IntRecNum);
  649. Stream.Read(Buffer^,RecordSize);
  650. end;
  651. procedure TPagedFile.WriteRecord(IntRecNum:Integer; Buffer:Pointer);
  652. begin
  653. _Seek(IntRecNum);
  654. Stream.Write(Buffer^, RecordSize);
  655. end;
  656. //====================================================================
  657. // TDbfFile
  658. //====================================================================
  659. constructor TDbfFile.Create(const FileName: string; Mode: Word);
  660. var
  661. lRecordCount:Integer;
  662. begin
  663. _MyFieldInfos:=TList.Create;
  664. // check if the file exists
  665. inherited Create(Filename, Mode);
  666. if Mode = fmCreate then begin
  667. FillChar(_DataHdr,sizeof(_DataHdr),0);
  668. HeaderSize:=0;
  669. RecordSize:=0;
  670. _DataHdr.VerDBF:=$03; // Default version xBaseIV without memo
  671. _DataHdr.Language:='X';
  672. end else begin
  673. Stream.Seek(0,soFromBeginning);
  674. Stream.ReadBuffer (_DataHdr, SizeOf(_DataHdr));
  675. case _DataHdr.VerDBF of
  676. $03,$83: _DbfVersion:=xBaseIII;
  677. $04,$8B,$8E,$7B: _DbfVersion:=xBaseIV;
  678. $05 : _DbfVersion:=xbaseV;
  679. else
  680. _DbfVersion:=xBaseIV; // My favorite...
  681. end;
  682. HeaderSize:=_DataHdr.FullHdrSize;
  683. RecordSize:=_DataHdr.RecordSize;
  684. lRecordCount:=CalcRecordCount;
  685. if _DataHdr.RecordCount <> lRecordCount then begin
  686. {$ifndef fpc}
  687. ShowMessage('Invalid Record Count,'+^M+
  688. 'RecordCount in Hdr : '+IntToStr(_DataHdr.RecordCount)+^M+
  689. 'expected : '+IntToStr(lRecordCount));
  690. {$endif}
  691. _DataHdr.RecordCount := lRecordCount;
  692. end;
  693. end;
  694. end;
  695. destructor TDbfFile.Destroy;
  696. begin
  697. inherited;
  698. ClearMyFieldInfos;
  699. _MyFieldInfos.Free;
  700. _MyFieldInfos:=nil;
  701. end;
  702. function TDbfFile.RecordCount:integer;
  703. begin
  704. if RecordSize=0 then result:=0
  705. else result:=(Stream.Size - HeaderSize) div RecordSize;
  706. if result<0 then result:=0;
  707. end;
  708. procedure TDbfFile.ClearMyFieldInfos;
  709. var
  710. i:Integer;
  711. begin
  712. for i:=0 to _MyFieldInfos.Count-1 do begin
  713. TMyFieldInfo(_MyFieldInfos.Items[i]).Free;
  714. end;
  715. _MyFieldInfos.Clear;
  716. end;
  717. procedure TDbfFile.CreateFieldDefs(FieldDefs:TFieldDefs);
  718. var
  719. lColumnCount,lHeaderSize,lFieldSize:Integer;
  720. Il : Integer;
  721. lFieldOffset : Integer;
  722. fn:string;
  723. ft:TFieldType;
  724. fs,nfs,fd:Integer;
  725. MyFieldInfo:TMyFieldInfo;
  726. lFieldHdrIII:rFieldHdrIII;
  727. lFieldHdrV:rFieldHdrV;
  728. function ToFieldType(dbasetype:char;fs,fd:Integer):TFieldType;
  729. begin
  730. case dbasetype of
  731. 'C' :
  732. begin
  733. Result:=ftString;
  734. end;
  735. 'L' :
  736. begin
  737. Result:=ftBoolean;
  738. end;
  739. 'F' :
  740. begin
  741. Result:=ftFloat;
  742. end;
  743. 'N' :
  744. begin
  745. if fd=0 then begin
  746. if fs <= DIGITS_SMALLINT then begin
  747. Result:=ftSmallInt;
  748. end else begin
  749. {$ifdef DELPHI_3}
  750. Result:=ftInteger;
  751. {$else}
  752. if fs <= DIGITS_INTEGER then Result:=ftInteger
  753. else Result:=ftLargeInt;
  754. {$endif}
  755. end;
  756. end else begin
  757. Result:=ftFloat;
  758. end;
  759. end;
  760. 'D' :
  761. begin
  762. Result:=ftDate;
  763. end;
  764. 'M' :
  765. begin
  766. Result:=ftMemo;
  767. end;
  768. else
  769. begin
  770. Result:=ftString;
  771. end;
  772. end; //case
  773. end;
  774. begin
  775. ClearMyFieldInfos;
  776. if _DbfVersion>=xBaseV then begin
  777. lHeaderSize:=SizeOf(rAfterHdrV) + SizeOf(rDbfHdr);
  778. lFieldSize:=SizeOf(rFieldHdrV);
  779. end else begin
  780. lHeaderSize:=SizeOf(rAfterHdrIII) + SizeOf(rDbfHdr);
  781. lFieldSize:=SizeOf(rFieldHdrIII);
  782. end;
  783. lColumnCount:= (_DataHdr.FullHdrSize - lHeaderSize) div lFieldSize;
  784. if (lColumnCount <= 0) or (lColumnCount > 255) then
  785. Raise eBinaryDataSetError.Create('Invalid field count : ' + IntToStr(lColumnCount) + ' (must be between 1 and 255)');
  786. lFieldOffset := 1;
  787. Stream.Position := lHeaderSize;
  788. for Il:=0 to lColumnCount-1 do begin
  789. if _DbfVersion>=xBaseV then begin
  790. Stream.ReadBuffer(lFieldHdrV,SizeOf(lFieldHdrV));
  791. fn:=PCHAR(@lFieldHdrV.FieldName[0]);
  792. fs:=lFieldHdrV.FieldSize;
  793. fd:=lFieldHdrV.FieldPrecision;
  794. nfs:=fs;
  795. ft:=ToFieldType(lFieldHdrV.FieldType,nfs,fd);
  796. end else begin
  797. Stream.ReadBuffer(lFieldHdrIII,SizeOf(lFieldHdrIII));
  798. fn:=PCHAR(@lFieldHdrIII.FieldName[0]);
  799. fs:=lFieldHdrIII.FieldSize;
  800. fd:=lFieldHdrIII.FieldPrecision;
  801. nfs:=fs;
  802. ft:=ToFieldType(lFieldHdrIII.FieldType,nfs,fd);
  803. end;
  804. // first create the bde field
  805. if ft in [ftString,ftBCD] then fieldDefs.Add(fn,ft,fs,false)
  806. else fieldDefs.Add(fn,ft,0,false);
  807. // then create the for our own fieldinfo
  808. MyFieldInfo:=TMyFieldInfo.Create;
  809. MyFieldInfo.Offset:=lFieldOffset;
  810. MyFieldInfo.Size:=fs;
  811. MyFieldInfo.Prec:=fd;
  812. MyFieldInfo.FieldName:=lowercase(fn);
  813. _MyFieldInfos.Add(MyFieldInfo);
  814. Inc(lFieldOffset,fs);
  815. end;
  816. if (lFieldOffset <> _DataHdr.RecordSize) then begin
  817. {$ifndef fpc}
  818. ShowMessage('Invalid Record Size,'+^M+
  819. 'Record Size in Hdr : '+IntToStr(_DataHdr.RecordSize)+^M+
  820. 'Expected : '+IntToStr(lFieldOffset));
  821. {$endif}
  822. _DataHdr.RecordSize := lFieldOffset;
  823. end;
  824. end;
  825. procedure TDbfFile.DbfFile_CreateTable(FieldDefs:TFieldDefs);
  826. var
  827. ix:Integer;
  828. lFieldHdrIII:rFieldHdrIII;
  829. lType:Char;
  830. lSize,lPrec:Integer;
  831. Offs:Integer;
  832. lterminator:Byte;
  833. begin
  834. // first reset file.
  835. Stream.Size:= 0;
  836. Stream.Position:=SizeOf(rDbfHdr) + SizeOf(rAfterHdrIII);
  837. Offs:=1; // deleted mark count 1.
  838. for Ix:=0 to FieldDefs.Count-1 do
  839. begin
  840. with FieldDefs.Items[Ix] do
  841. begin
  842. FillChar(lFieldHdrIII,SizeOf(lFieldHdrIII),#0);
  843. lPrec:=0;
  844. case DataType of
  845. ftString:
  846. begin
  847. ltype:='C';
  848. lSize := Size;
  849. end;
  850. ftBoolean:
  851. begin
  852. ltype:='L';
  853. lSize := 1;
  854. end;
  855. ftSmallInt:
  856. begin
  857. ltype:='N';
  858. lSize := 6;
  859. end;
  860. ftInteger:
  861. begin
  862. ltype:='N';
  863. lSize := 11;
  864. end;
  865. ftCurrency:
  866. begin
  867. ltype:='N';
  868. lSize := 20;
  869. lPrec := 2;
  870. end;
  871. {$ifndef DELPHI_3}
  872. ftLargeInt:
  873. begin
  874. ltype:='N';
  875. lSize := 20;
  876. lPrec := 0;
  877. end;
  878. {$endif}
  879. ftFloat:
  880. begin
  881. ltype:='N';
  882. lSize := 20;
  883. lPrec := 4;
  884. end;
  885. ftDate:
  886. begin
  887. ltype:='D';
  888. lSize := 8;
  889. end;
  890. ftMemo:
  891. begin
  892. ltype:='M';
  893. lSize := 10;
  894. end;
  895. else
  896. begin
  897. raise EBinaryDataSetError.Create(
  898. 'InitFieldDefs: Unsupported field type');
  899. end;
  900. end; // case
  901. lFieldHdrIII.FieldType:=ltype; //DataType;
  902. StrPCopy(lFieldHdrIII.FieldName,FieldDefs.Items[Ix].Name);
  903. lFieldHdrIII.FieldSize:=lSize;
  904. lFieldHdrIII.FieldPrecision:=lPrec;
  905. Stream.Write(lFieldHdrIII,SizeOf(lFieldHdrIII));
  906. Inc(Offs,lSize);
  907. end;
  908. end;
  909. // end of header
  910. lterminator := $0d;
  911. Stream.Write(lterminator,SizeOf(lterminator));
  912. // update header
  913. _DataHdr.RecordSize := Offs;
  914. _DataHdr.FullHdrSize := Stream.Position;
  915. RecordSize := _DataHdr.RecordSize;
  916. HeaderSize := _DataHdr.FullHdrSize;
  917. // write the updated header
  918. WriteHeader;
  919. end;
  920. procedure TDbfFile.DbfFile_PackTable;
  921. var
  922. first,last:integer;
  923. p: Pointer;
  924. begin
  925. // Non tested.
  926. if (RecordSize <> 0) then
  927. begin
  928. first:=0;
  929. last:=CalcRecordCount-1;
  930. GetMem(p, RecordSize);
  931. try
  932. while first<last do begin
  933. // first find the first hole
  934. while first<last do begin
  935. ReadRecord(first, p);
  936. if (pRecordHdr(p)^.DeletedFlag <> ' ') then break;
  937. inc(first);
  938. end;
  939. // now find last one non deleted.
  940. while first<last do begin
  941. ReadRecord(last, p);
  942. if (pRecordHdr(p)^.DeletedFlag = ' ') then break;
  943. dec(last);
  944. end;
  945. if first<last then begin
  946. // found a non deleted record to put in the hole.
  947. WriteRecord(first, p);
  948. inc(first);
  949. dec(last);
  950. end;
  951. end;
  952. last:=CalcRecordCount;
  953. Stream.Size:=(last+1) * RecordSize + HeaderSize;
  954. finally
  955. FreeMem(p);
  956. end;
  957. end;
  958. end;
  959. function TDbfFile.GetFieldInfo(FieldName:string):TMyFieldInfo;
  960. var
  961. i:Integer;
  962. lfi:TMyFieldInfo;
  963. begin
  964. FieldName:=LowerCase(FieldName);
  965. for i:=0 to _MyFieldInfos.Count-1 do begin
  966. lfi:=TMyFieldInfo(_MyFieldInfos.Items[i]);
  967. if lfi.FieldName = FieldName then begin
  968. result:=lfi;
  969. exit;
  970. end;
  971. end;
  972. result:=nil;
  973. end;
  974. function TDbfFile.GetFieldData(Column:Integer;DataType:TFieldType; Src,Dst:Pointer): Boolean;
  975. var
  976. FieldOffset: Integer;
  977. FieldSize: Integer;
  978. s:string;
  979. d:TDateTime;
  980. ld,lm,ly: word;
  981. MyFieldInfo:TMyFieldInfo;
  982. function TrimStr(const s: string): string;
  983. var
  984. iPos: integer;
  985. begin
  986. if DataType=ftString then
  987. begin
  988. if tDbf_TrimFields then Result:=Trim(s)
  989. else Result:=TrimRight(s);
  990. end
  991. else Result:= Trim(s);
  992. end;
  993. procedure CorrectYear(var wYear: word);
  994. var wD, wM, wY, CenturyBase: word;
  995. {$ifdef DELPHI_3}
  996. // Delphi 3 standard-behavior no change possible
  997. const TwoDigitYearCenturyWindow= 0;
  998. {$endif}
  999. begin
  1000. if wYear>= 100 then
  1001. Exit;
  1002. DecodeDate(Date, wY, wm, wD);
  1003. // use Delphi-Date-Window
  1004. CenturyBase := wY{must be CurrentYear} - TwoDigitYearCenturyWindow;
  1005. Inc(wYear, CenturyBase div 100 * 100);
  1006. if (TwoDigitYearCenturyWindow > 0) and (wYear < CenturyBase) then
  1007. Inc(wYear, 100);
  1008. end;
  1009. begin
  1010. MyFieldInfo:=TMyFieldInfo(_MyFieldInfos.Items[Column]);
  1011. FieldOffset := MyFieldInfo.Offset;
  1012. FieldSize := MyFieldInfo.Size;
  1013. SetString(s, PChar(Src) + FieldOffset, FieldSize );
  1014. s:=TrimStr(s);
  1015. result:=length(s)>0; // return if field is empty
  1016. if Result and (Dst<>nil) then// data not needed if Result= FALSE or Dst=nil
  1017. case DataType of
  1018. ftBoolean:
  1019. begin
  1020. // in DBase- FileDescription lowercase t is allowed too
  1021. // with asking for Result= TRUE s must be longer then 0
  1022. // else it happens an AV, maybe field is NULL
  1023. if (UpCase(s[1])='T') then Word(Dst^) := 1
  1024. else Word(Dst^) := 0;
  1025. end;
  1026. ftInteger, ftSmallInt{$ifndef DELPHI_3},ftLargeInt{$endif}:
  1027. begin
  1028. case DataType of
  1029. ftSmallInt : SmallInt(Dst^):= StrToIntDef(s, 0);
  1030. {$ifndef DELPHI_3}
  1031. ftLargeint : LargeInt(Dst^):= StrToInt64Def(s, 0);
  1032. {$endif}
  1033. else // ftInteger :
  1034. Integer(Dst^):= StrToIntDef(s, 0);
  1035. end; // case
  1036. end;
  1037. ftFloat:
  1038. begin
  1039. Extended(Dst^) := DBFStrToFloat(s);
  1040. end;
  1041. ftCurrency:
  1042. begin
  1043. Extended(Dst^) := DBFStrToFloat(s);
  1044. end;
  1045. ftDate:
  1046. begin
  1047. ld:=StrToIntDef(Copy(s,7,2),1);
  1048. lm:=StrToIntDef(Copy(s,5,2),1);
  1049. ly:=StrToIntDef(Copy(s,1,4),0);
  1050. if ld=0 then ld:=1;
  1051. if lm=0 then lm:=1;
  1052. // if (ly<1900) or (ly>2100) then ly:=1900;
  1053. // Year from 0001 to 9999 is possible
  1054. // everyting else is an error, an empty string too
  1055. // Do DateCorrection with Delphis possibillities for one or two digits
  1056. if (ly< 100) and (Length(Trim(Copy(s,1,4)))in [1, 2]) then CorrectYear(ly);
  1057. try
  1058. d:=EncodeDate(ly,lm,ld);
  1059. if Assigned(Dst) then Integer(Dst^) := DateTimeToTimeStamp(d).Date;
  1060. except
  1061. Integer(Dst^) := 0;
  1062. end;
  1063. end;
  1064. ftString: begin
  1065. StrPCopy(Dst,s);
  1066. end;
  1067. end;
  1068. end;
  1069. procedure TDbfFile.SetFieldData(Column:integer;DataType:TFieldType; Src,Dst:Pointer);
  1070. var
  1071. FieldSize,FieldPrec: Integer;
  1072. s:string;
  1073. fl:Double;
  1074. ts:TTimeStamp;
  1075. MyFieldInfo:TMyFieldInfo;
  1076. begin
  1077. MyFieldInfo:=TMyFieldInfo(_MyFieldInfos.Items[Column]);
  1078. FieldSize := MyFieldInfo.Size;
  1079. FieldPrec := MyFieldInfo.Prec;
  1080. Dst:=PChar(Dst)+MyFieldInfo.Offset;
  1081. if src<>nil then begin
  1082. case DataType of
  1083. ftBoolean:
  1084. begin
  1085. if Word(Src^) = 1 then s:='T'
  1086. else s:='F';
  1087. end;
  1088. ftInteger, ftSmallInt {$ifndef DELPHI_3},ftLargeInt{$endif}:
  1089. begin
  1090. case DataType of
  1091. ftSmallInt : s:= IntToStr(SmallInt(Src^));
  1092. {$ifndef DELPHI_3}
  1093. ftLargeInt: s:= IntToStr(LargeInt(Src^));
  1094. {$endif}
  1095. else //ftInteger
  1096. s:= IntToStr(Integer(Src^));
  1097. end;
  1098. // left filling
  1099. if Length(s)<FieldSize then s:=StringOfChar(' ',FieldSize-Length(s)) + s;
  1100. end;
  1101. ftFloat,ftCurrency:
  1102. begin
  1103. fl := Double(Src^);
  1104. s:=FloatToDbfStr(fl,FieldSize,FieldPrec);
  1105. if Length(s)<FieldSize then s:=StringOfChar(' ',FieldSize-Length(s)) + s;
  1106. end;
  1107. ftDate:
  1108. begin
  1109. ts.Time:=0;
  1110. ts.Date:=Integer(Src^);
  1111. s:= FormatDateTime('yyyymmdd', TimeStampToDateTime(ts));
  1112. end;
  1113. ftString:
  1114. begin
  1115. s:=PChar(Src); // finish with first 0
  1116. end;
  1117. end; // case
  1118. end; // if src<>nil (thanks andreas)
  1119. if Length(s)<FieldSize then begin
  1120. s:=s+StringOfChar(' ',FieldSize-Length(s));
  1121. end else if (Length(s)>FieldSize) then begin
  1122. if DataType= ftString then begin
  1123. // never raise for strings to long, its not customary
  1124. // TTable never raises
  1125. SetLength(s, FieldSize)
  1126. end else begin
  1127. raise eFieldToLongError.Create('Fielddata too long :' + IntToStr(Length(s))
  1128. + ' (must be between 1 and ' + IntToStr(FieldSize) + ').');
  1129. end;
  1130. end;
  1131. Move(PChar(s)^, Dst^, FieldSize);
  1132. end;
  1133. procedure TDbfFile.WriteHeader;
  1134. var
  1135. SystemTime: TSystemTime;
  1136. lAfterHdrIII:rAfterHdrIII;
  1137. lAfterHdrV:rAfterHdrV;
  1138. lterminator:Byte;
  1139. begin
  1140. Assert(Stream<>nil,'_dbfFile=Nil');
  1141. Stream.Position:=0;
  1142. GetLocalTime(SystemTime);
  1143. {$ifndef fpc}
  1144. _DataHdr.Year := SystemTime.wYear - 1900;
  1145. _DataHdr.Month := SystemTime.wMonth;
  1146. _DataHdr.Day := SystemTime.wDay;
  1147. {$else}
  1148. _DataHdr.Year := SystemTime.Year - 1900;
  1149. _DataHdr.Month := SystemTime.Month;
  1150. _DataHdr.Day := SystemTime.Day;
  1151. {$endif}
  1152. Stream.Seek(0,soFromBeginning);
  1153. Stream.WriteBuffer (_DataHdr, SizeOf(_DataHdr));
  1154. _DataHdr.RecordCount := CalcRecordCount;
  1155. if _DbfVersion >= xBaseV then begin
  1156. FillChar(lAfterHdrV,SizeOf(lAfterHdrV),0);
  1157. Stream.WriteBuffer (lAfterHdrV, SizeOf(lAfterHdrV));
  1158. end else begin
  1159. FillChar(lAfterHdrIII,SizeOf(lAfterHdrIII),0);
  1160. Stream.WriteBuffer (lAfterHdrIII, SizeOf(lAfterHdrIII));
  1161. end;
  1162. _Seek(_DataHdr.RecordCount); // last byte usually...
  1163. lterminator := $1A;
  1164. Stream.Write(lterminator,SizeOf(lterminator));
  1165. end;
  1166. function TDbf._ComponentInfo:string;
  1167. begin
  1168. Result:='TDbf V' + IntToStr(_MAJOR_VERSION) + '.' + IntToStr(_MINOR_VERSION);
  1169. end;
  1170. procedure TDbf._OpenFiles(CreateIt:boolean);
  1171. var
  1172. fileopenmode : integer;
  1173. lPath,lFilename,lIndexName,lMemoName : string;
  1174. isAbsolute:boolean;
  1175. design,doreadonly:boolean;
  1176. begin
  1177. design:=(csDesigning in ComponentState);
  1178. doreadonly:=design or _ReadOnly;
  1179. lPath:=_GetPath;
  1180. isAbsolute:=((length(_TableName)>=1) and (_TableName[1]='\'))
  1181. or ((length(_TableName)>=2) and (_TableName[2]=':'));
  1182. if isAbsolute then lfilename:=_TableName
  1183. else lFilename:=lPath+_TableName;
  1184. lFilename:=ChangeFileExt(lFilename,'.dbf');
  1185. lIndexName:=ChangeFileExt(lFilename,'.mdx');
  1186. lMemoName:=ChangeFileExt(lFilename,'.dbt');
  1187. // check if the file exists
  1188. _dbfFile:=TDbfFile(GetPagedFile(lFileName));
  1189. _indexFile:=TIndexFile(GetPagedFile(lIndexName));
  1190. _dbtFile:=TDbtFile(GetPagedFile(lMemoName));
  1191. if CreateIt then begin
  1192. if _dbfFile=nil then _dbfFile:=TDbfFile.Create(lFileName,fmCreate);
  1193. //if _indexfile=nil then _indexFile := TIndexFile.Create(lIndexName, fmCreate);
  1194. if _dbtfile=nil then _dbtFile := TDbtFile.Create(lMemoName, fmCreate,_dbfFile._DbfVersion);
  1195. end else if not FileExists(lFileName) then begin
  1196. raise eBinaryDataSetError.Create ('Open: Table file not found : ' + lFileName);
  1197. end else begin
  1198. if DoReadOnly then
  1199. fileopenmode := fmOpenRead + fmShareDenyNone
  1200. else
  1201. fileopenmode := fmOpenReadWrite + fmShareDenyWrite;
  1202. if _dbfFile=nil then _dbfFile := TDBFFile.Create(lFileName, fileopenmode);
  1203. if (_indexFile=nil) and FileExists (lIndexName) then begin
  1204. _indexFile := TIndexFile.Create(lIndexName, fileopenmode);
  1205. end;
  1206. if (_dbtFile=nil) and FileExists (lMemoName) then begin
  1207. _dbtFile := TDbtFile.Create(lMemoName, fileopenmode,_dbfFile._DbfVersion);
  1208. end;
  1209. end;
  1210. _PrevBuffer:=AllocRecordBuffer;
  1211. _IsCursorOpen:=true;
  1212. end;
  1213. function TDbf._GetPath:string;
  1214. var
  1215. lPath:string;
  1216. begin
  1217. if (csDesigning in ComponentState) then begin
  1218. lPath:=_DesignTimePath;
  1219. end else begin
  1220. if ((length(_RunTimePath)>=1) and (_RunTimePath[1]=DirSeparator))
  1221. or ((length(_RunTimePath)>=2) and (_RunTimePath[2]=':'))
  1222. then begin
  1223. // if the _RunTimePath is absolute...
  1224. // it is either \ or \blahblah or c:\
  1225. lPath:=_RunTimePath;
  1226. end else begin
  1227. {$ifndef fpc}
  1228. lPath:=extractfilepath(Application.Exename)+_RunTimePath;
  1229. {$else}
  1230. lPath:=extractfilepath(paramstr(0))+_RunTimePath;
  1231. {$endif}
  1232. end;
  1233. end;
  1234. lPath:=ExpandFileName(trim(lPath));
  1235. if (length(lPath)>0) and (lPath[length(lPath)]<>DirSeparator) then lPath:=lPath+DirSeparator;
  1236. result:=lPath;
  1237. end;
  1238. procedure TDbf._CloseFiles;
  1239. var
  1240. i:integer;
  1241. begin
  1242. if _dbfFile<>nil then begin
  1243. if not _ReadOnly then _dbfFile.WriteHeader;
  1244. _dbfFile.Release;
  1245. _dbfFile:=nil;
  1246. end;
  1247. if _indexFile<>nil then begin
  1248. _indexFile.Release;
  1249. _indexFile:=nil;
  1250. end;
  1251. if _dbtFile<>nil then begin
  1252. _dbtFile.Release;
  1253. _dbtFile:=nil;
  1254. end;
  1255. if _indexes<>nil then begin
  1256. for i:=0 to _Indexes.Count-1 do begin
  1257. TIndex(_Indexes[i]).Free;
  1258. end;
  1259. _Indexes.Clear;
  1260. _CurIndex:=nil;
  1261. end;
  1262. if (_PrevBuffer<>nil) then begin
  1263. FreeRecordBuffer(_PrevBuffer);
  1264. _PrevBuffer:=nil;
  1265. end;
  1266. _IsCursorOpen:=false;
  1267. end;
  1268. procedure TDbf._SetIndexName(const Value: string);
  1269. begin
  1270. _CurIndex:=_GetIndex(Value);
  1271. Resync([]);
  1272. end;
  1273. function TDbf._GetIndexName: string;
  1274. begin
  1275. if _CurIndex=nil then Result:=''
  1276. else Result:=_CurIndex._IndexFile._Filename;
  1277. end;
  1278. function TDbf._GetIndex(filename:string):TIndex;
  1279. var
  1280. i:integer;
  1281. lindex:TIndex;
  1282. begin
  1283. result:=nil;
  1284. filename:=lowercase(_GetPath + filename);
  1285. for i:=0 to _indexes.Count-1 do begin
  1286. lindex:=TIndex(_indexes.Items[i]);
  1287. if lindex._IndexFile._Filename=filename then begin
  1288. result:=lindex;
  1289. exit;
  1290. end;
  1291. end;
  1292. end;
  1293. //==========================================================
  1294. //============ TMyBlobFile
  1295. //==========================================================
  1296. constructor TMyBlobFile.Create(ModeVal:TBlobStreamMode;FieldVal:TField);
  1297. begin
  1298. Mode:=ModeVal;
  1299. Field:=FieldVal;
  1300. end;
  1301. destructor TMyBlobFile.destroy;
  1302. var
  1303. Dbf:TDbf;
  1304. begin
  1305. if (Mode=bmWrite) then begin
  1306. Size:=Position; // Strange but it leave tailing trash bytes if I do not write that.
  1307. Dbf:=TDbf(Field.DataSet);
  1308. Dbf._dbtFile.WriteMemo(MemoRecno,ReadSize,Self);
  1309. Dbf._dbfFile.SetFieldData(Field.FieldNo-1,
  1310. ftInteger,@MemoRecno,@pDbfRecord(TDbf(Field.DataSet).ActiveBuffer)^.deletedflag);
  1311. // seems not bad
  1312. {$ifndef fpc}
  1313. // FPC doesn't allow to call protected methods ?!!
  1314. Dbf.SetModified(true);
  1315. {$endif}
  1316. // but would that be better
  1317. //if not (State in [dsCalcFields, dsFilter, dsNewValue]) then begin
  1318. // DataEvent(deFieldChange, Longint(Field));
  1319. //end;
  1320. end;
  1321. inherited;
  1322. end;
  1323. //====================================================================
  1324. // TDbf = TDataset Descendant.
  1325. //====================================================================
  1326. constructor TDbf.Create(AOwner: TComponent); {override;}
  1327. begin
  1328. inherited create(aOwner);
  1329. BookmarkSize:=sizeof(rBookmarkData);
  1330. _RunTimePath:='.';
  1331. _IsCursorOpen:=false;
  1332. _Indexes:=TList.Create;
  1333. _CurIndex:=nil;
  1334. _IndexFiles:=TStringList.Create;
  1335. end;
  1336. destructor TDbf.Destroy; {override;}
  1337. var
  1338. i:integer;
  1339. begin
  1340. inherited;
  1341. _CurIndex:=nil;
  1342. for i:=0 to _Indexes.Count-1 do begin
  1343. TIndex(_Indexes[i]).Free;
  1344. end;
  1345. _Indexes.Free;
  1346. _IndexFiles.Free;
  1347. // _MemIndex.Free;
  1348. end;
  1349. function TDbf._FilterRecord(Buffer: PChar): Boolean;
  1350. var
  1351. SaveState: TDatasetState;
  1352. s:string;
  1353. begin
  1354. Result:=True;
  1355. if Length(easyfilter)<>0 then begin
  1356. SetString(s,buffer,RecordSize);
  1357. s:=LowerCase(s);
  1358. if Pos(easyfilter,s)=0 then begin
  1359. Result:=False;
  1360. Exit;
  1361. end;
  1362. end;
  1363. if not Assigned(OnFilterRecord) then Exit;
  1364. if not Filtered then Exit;
  1365. _FilterBuffer:=buffer;
  1366. SaveState:=SetTempState(dsFilter);
  1367. OnFilterRecord(self,Result);
  1368. RestoreState(SaveState);
  1369. end;
  1370. function TDbf._RecordDataSize:integer;
  1371. begin
  1372. if _dbfFile=nil then result:=0
  1373. else result:=_dbfFile.RecordSize;
  1374. end;
  1375. function TDbf._FullRecordSize:integer;
  1376. begin
  1377. result:=sizeof(rBeforeRecord) + _RecordDataSize + CalcFieldsSize;
  1378. end;
  1379. function TDbf.AllocRecordBuffer: PChar; {override virtual abstract from TDataset}
  1380. begin
  1381. result:=StrAlloc(_FullRecordSize);
  1382. InternalInitRecord(result);
  1383. end;
  1384. procedure TDbf.FreeRecordBuffer(var Buffer: PChar); {override virtual abstract from TDataset}
  1385. begin
  1386. StrDispose(Buffer);
  1387. end;
  1388. procedure TDbf.GetBookmarkData(Buffer: PChar; Data: Pointer); {override virtual abstract from TDataset}
  1389. var
  1390. prec:pDbfRecord;
  1391. begin
  1392. prec:=pDbfRecord(Buffer);
  1393. pBookMarkData(Data)^:=prec^.BookMarkData;
  1394. end;
  1395. function TDbf.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; {override virtual abstract from TDataset}
  1396. var
  1397. prec:pDbfRecord;
  1398. begin
  1399. prec:=pDbfRecord(Buffer);
  1400. result:=prec^.BookMarkFlag;
  1401. end;
  1402. function TDbf.GetFieldData(Field: TField; Buffer: Pointer): Boolean; {override virtual abstract from TDataset}
  1403. var
  1404. ptr:pointer;
  1405. begin
  1406. Result := False;
  1407. if State=dsFilter then begin
  1408. Ptr:=_FilterBuffer;
  1409. end else if State = dsCalcFields then begin
  1410. // ***** calc fields ***** set correct buffer
  1411. ptr := @(pDbfRecord(CalcBuffer)^.deletedflag);
  1412. end else begin
  1413. if IsEmpty then exit;
  1414. ptr:=@(pDbfRecord(ActiveBuffer)^.deletedflag);
  1415. end;
  1416. if Field.FieldNo>0 then begin
  1417. Result:=_dbfFile.GetFieldData(Field.FieldNo - 1,Field.DataType,ptr,Buffer);
  1418. end else begin { calculated fields.... }
  1419. Inc(PChar(Ptr), Field.Offset + GetRecordSize);
  1420. {$ifndef fpc}
  1421. Result := Boolean(PChar(Ptr)[0]);
  1422. {$else}
  1423. Result := (Pchar(ptr)[0]<>#0);
  1424. {$endif}
  1425. if Result and (Buffer <> nil) then
  1426. Move(PChar(Ptr)[1], Buffer^, Field.DataSize);
  1427. end;
  1428. end;
  1429. function TDbf.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; {override virtual abstract from TDataset}
  1430. var
  1431. Acceptable : Boolean;
  1432. prec:pDBFRecord;
  1433. begin
  1434. prec:=pDBFRecord(Buffer);
  1435. if _dbfFile.RecordCount < 1 then
  1436. Result := grEOF
  1437. else repeat
  1438. result := grOk;
  1439. case GetMode of
  1440. gmCurrent :
  1441. begin
  1442. if prec^.BookmarkData.Recno=_PhysicalRecno then begin
  1443. exit; // try to fasten a bit...
  1444. end;
  1445. end;
  1446. gmNext :
  1447. begin
  1448. if _curIndex<>nil then begin
  1449. Acceptable:=_curIndex.Next;
  1450. end else begin
  1451. inc(_PhysicalRecno);
  1452. Acceptable:=(_PhysicalRecno<_dbfFile.RecordCount);
  1453. end;
  1454. if Acceptable then begin
  1455. result:= grOk;
  1456. end else begin
  1457. InternalLast;
  1458. result:= grEOF
  1459. end;
  1460. end;
  1461. gmPrior :
  1462. begin
  1463. if _curIndex<>nil then begin
  1464. Acceptable:=_curIndex.Prev;
  1465. end else begin
  1466. dec(_PhysicalRecno);
  1467. Acceptable:=(_PhysicalRecno>=0);
  1468. end;
  1469. if Acceptable then begin
  1470. result:= grOk;
  1471. end else begin
  1472. InternalFirst;
  1473. result:= grBOF
  1474. end;
  1475. end;
  1476. end;
  1477. if result=grOk then begin
  1478. if _curIndex<>nil then _PhysicalRecno:=_CurIndex.GetRealRecNo;
  1479. if (_PhysicalRecno>=_dbfFile.RecordCount)
  1480. or (_PhysicalRecno<0) then begin
  1481. result:=grError;
  1482. end else begin
  1483. _dbfFile.ReadRecord(_PhysicalRecno,@prec^.DeletedFlag);
  1484. result:=grOk;
  1485. end;
  1486. if Result = grOK then begin
  1487. ClearCalcFields(Buffer);
  1488. GetCalcFields(Buffer);
  1489. prec^.BookmarkFlag := bfCurrent;
  1490. prec^.BookmarkData.Recno:=PhysicalRecno;
  1491. end else if (Result = grError) and DoCheck then
  1492. raise eBinaryDataSetError.Create ('GetRecord: Invalid record');
  1493. end;
  1494. Acceptable := (_ShowDeleted or (prec^.DeletedFlag = ' '))
  1495. and _FilterRecord(Buffer);
  1496. if (GetMode=gmCurrent) and Not Acceptable then Result := grError;
  1497. until (Result <> grOK) or Acceptable;
  1498. end;
  1499. function TDbf.GetRecordSize: Word; {override virtual abstract from TDataset}
  1500. begin
  1501. Result := _RecordDataSize; // data only
  1502. end;
  1503. procedure TDbf.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); {override virtual abstract from TDataset}
  1504. begin
  1505. end;
  1506. procedure TDbf.InternalClose; {override virtual abstract from TDataset}
  1507. begin
  1508. _CloseFiles;
  1509. // disconnect field objects
  1510. BindFields(False);
  1511. // destroy field object (if not persistent)
  1512. if DefaultFields then
  1513. DestroyFields;
  1514. end;
  1515. procedure TDbf.InternalDelete; {override virtual abstract from TDataset}
  1516. begin
  1517. // CheckActive;
  1518. pRecordHdr(ActiveBuffer)^.DeletedFlag := '*'; //_DataHdr.LastDeleted;
  1519. _dbfFile.WriteRecord(_PhysicalRecNo,ActiveBuffer);
  1520. Resync([]);
  1521. end;
  1522. procedure TDbf.InternalFirst; {override virtual abstract from TDataset}
  1523. begin
  1524. if _dbfFile.RecordCount=0 then InternalLast
  1525. else if _curindex=nil then _PhysicalRecno:=-1
  1526. else _curIndex.First;
  1527. end;
  1528. procedure TDbf.InternalGotoBookmark(ABookmark: Pointer); {override virtual abstract from TDataset}
  1529. var
  1530. RecInfo: TRecInfo;
  1531. begin
  1532. RecInfo := TRecInfo(ABookmark^);
  1533. if (RecInfo.Bookmark >= 0) and (RecInfo.Bookmark < _dbfFile.RecordCount) then begin
  1534. _PhysicalRecno:=RecInfo.Bookmark;
  1535. end else
  1536. raise eBinaryDataSetError.Create ('Bookmark ' +
  1537. IntToStr (RecInfo.Bookmark) + ' not found');
  1538. end;
  1539. procedure TDbf.InternalHandleException; {override virtual abstract from TDataset}
  1540. begin
  1541. {$ifndef fpc}
  1542. Application.HandleException(Self);
  1543. {$endif}
  1544. end;
  1545. procedure TDbf.InternalInitFieldDefs; {override virtual abstract from TDataset}
  1546. begin
  1547. FieldDefs.Clear;
  1548. with FieldDefs do
  1549. begin
  1550. if IsCursorOpen then begin
  1551. _dbfFile.CreateFieldDefs(FieldDefs);
  1552. end else begin
  1553. _OpenFiles(false);
  1554. _dbfFile.CreateFieldDefs(FieldDefs);
  1555. Close();
  1556. end;
  1557. end;
  1558. end;
  1559. procedure TDbf.InternalInitRecord(Buffer: PChar); {override virtual abstract from TDataset}
  1560. var
  1561. prec:pDbfRecord;
  1562. begin
  1563. prec:=pDbfRecord(Buffer);
  1564. prec^.BookmarkData.RecNo:=-1;
  1565. prec^.BookmarkFlag:=TBookmarkFlag(0);
  1566. fillchar(prec^.DeletedFlag,_RecordDataSize,' ');
  1567. end;
  1568. procedure TDbf.InternalLast; {override virtual abstract from TDataset}
  1569. begin
  1570. if _curindex=nil then _PhysicalRecno:=_dbfFile.RecordCount
  1571. else _curIndex.Last;
  1572. end;
  1573. procedure TDbf.InternalOpen; {override virtual abstract from TDataset}
  1574. begin
  1575. _OpenFiles(false);
  1576. // if there are no persistent field objects,
  1577. InternalInitFieldDefs;
  1578. // create the fields dynamically
  1579. if DefaultFields then begin
  1580. CreateFields;
  1581. end;
  1582. BindFields (True);
  1583. // connect the TField objects with the actual fields
  1584. InternalFirst;
  1585. end;
  1586. procedure TDbf.InternalPost; {override virtual abstract from TDataset}
  1587. var
  1588. prec:pDbfRecord;
  1589. lIndex:TIndex;
  1590. i:integer;
  1591. begin
  1592. CheckActive;
  1593. prec:=pDbfRecord(ActiveBuffer);
  1594. prec^.DeletedFlag:=' ';
  1595. if State = dsEdit then
  1596. begin
  1597. // replace data with new data
  1598. if _indexes.Count>0 then begin
  1599. _dbfFile.ReadRecord(_PhysicalRecno,_PrevBuffer);
  1600. for i:=0 to _indexes.Count-1 do begin
  1601. lindex:=TIndex(_indexes.Items[i]);
  1602. lindex.Update(_PhysicalRecno,_PrevBuffer,@prec^.DeletedFlag);
  1603. end;
  1604. end;
  1605. end else begin
  1606. // append
  1607. _PhysicalRecno:=_dbfFile._DataHdr.RecordCount;
  1608. inc(_dbfFile._DataHdr.RecordCount);
  1609. if _indexes.Count>0 then begin
  1610. _dbfFile.ReadRecord(_PhysicalRecno,_PrevBuffer);
  1611. for i:=0 to _indexes.Count-1 do begin
  1612. lindex:=TIndex(_indexes.Items[i]);
  1613. lindex.Insert(_PhysicalRecno,@prec^.DeletedFlag);
  1614. end;
  1615. end;
  1616. end;
  1617. _dbfFile.WriteRecord(_PhysicalRecno,@prec^.DeletedFlag);
  1618. end;
  1619. procedure TDbf.CreateTable; //(FieldDefs:TFieldDefs);
  1620. var
  1621. ix:integer;
  1622. begin
  1623. CheckInactive;
  1624. // InternalInitFieldDefs;
  1625. if FieldDefs.Count = 0 then
  1626. begin
  1627. for Ix := 0 to FieldCount - 1 do
  1628. begin
  1629. with Fields[Ix] do
  1630. begin
  1631. if FieldKind = fkData then
  1632. FieldDefs.Add(FieldName,DataType,Size,Required);
  1633. end;
  1634. end;
  1635. end;
  1636. _OpenFiles(true);
  1637. try
  1638. _dbfFile.DbfFile_CreateTable(FieldDefs);
  1639. finally
  1640. // close the file
  1641. _CloseFiles;
  1642. end;
  1643. end;
  1644. procedure TDbf.PackTable;
  1645. begin
  1646. _dbfFile.dbfFile_PackTable;
  1647. Resync([]);
  1648. end;
  1649. function TDbf.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; {override virtual}
  1650. var
  1651. Memoi:array[1..32] of char;
  1652. lBlob:TMyBlobFile;
  1653. begin
  1654. lBlob:=TMyBlobFile.Create(Mode,Field);
  1655. if _dbfFile.GetFieldData(Field.FieldNo-1, ftString,@pDbfRecord(ActiveBuffer)^.deletedflag,@Memoi[1]) then begin
  1656. lBlob.MemoRecno:=StrToIntDef(Memoi,0);
  1657. _dbtFile.ReadMemo(lBlob.MemoRecno,lBlob);
  1658. lBlob.ReadSize:=lBlob.Size;
  1659. end else lBlob.MemoRecno:=0;
  1660. Result:=lBlob;
  1661. end;
  1662. {$ifdef DELPHI_3}
  1663. procedure TDbf.Translate(Src, Dest: PChar; ToOem: Boolean); {override virtual}
  1664. begin
  1665. if (Src <> nil) and (Dest<>nil) then begin
  1666. if ToOem then CharToOem(Src,Dest)
  1667. else OemToChar(Src,Dest);
  1668. end;
  1669. end;
  1670. {$else}
  1671. {$ifndef fpc}
  1672. function TDbf.Translate(Src, Dest: PChar; ToOem: Boolean): Integer; {override virtual}
  1673. begin
  1674. if (Src <> nil) and (Dest<>nil) then begin
  1675. if ToOem then CharToOem(Src,Dest)
  1676. else OemToChar(Src,Dest);
  1677. result:= StrLen(Dest);
  1678. end else result:=0;
  1679. end;
  1680. {$else}
  1681. procedure TDbf.Translate(Src, Dest: PChar; ToOem: Boolean); {override virtual}
  1682. begin
  1683. end;
  1684. {$endif}
  1685. {$endif}
  1686. procedure TDbf.ClearCalcFields(Buffer: PChar);
  1687. begin
  1688. FillChar(Buffer[_dbfFile.RecordSize], CalcFieldsSize, 0);
  1689. end;
  1690. procedure TDbf.InternalSetToRecord(Buffer: PChar); {override virtual abstract from TDataset}
  1691. var
  1692. prec:pDbfRecord;
  1693. begin
  1694. if Buffer=nil then exit;
  1695. prec:=pDbfRecord(Buffer);
  1696. _PhysicalRecno:=prec^.BookmarkData.RecNo;
  1697. _ResyncIndexes(Buffer);
  1698. end;
  1699. procedure TDbf._ResyncIndexes(Buffer: PChar);
  1700. var
  1701. i:integer;
  1702. lindex:TIndex;
  1703. begin
  1704. if _indexes.Count>0 then begin
  1705. _dbfFile.ReadRecord(_PhysicalRecno,_PrevBuffer);
  1706. for i:=0 to _indexes.Count-1 do begin
  1707. lindex:=TIndex(_indexes.Items[i]);
  1708. lindex.GotoKey(_physicalRecno,nil);
  1709. end;
  1710. end;
  1711. end;
  1712. function TDbf.IsCursorOpen: Boolean; {override virtual abstract from TDataset}
  1713. begin
  1714. result:=_IsCursorOpen;
  1715. end;
  1716. procedure TDbf.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); {override virtual abstract from TDataset}
  1717. var
  1718. prec:pDbfRecord;
  1719. begin
  1720. prec:=pDbfRecord(Buffer);
  1721. prec^.BookMarkFlag:=Value;
  1722. end;
  1723. procedure TDbf.SetBookmarkData(Buffer: PChar; Data: Pointer); {override virtual abstract from TDataset}
  1724. var
  1725. prec:pDbfRecord;
  1726. begin
  1727. prec:=pDbfRecord(Buffer);
  1728. prec^.BookMarkData:=pBookMarkData(Data)^;
  1729. end;
  1730. procedure TDbf.SetFieldData(Field: TField; Buffer: Pointer); {override virtual abstract from TDataset}
  1731. var
  1732. prec:pDbfRecord;
  1733. dst:pointer;
  1734. begin
  1735. if (Field.FieldNo >= 0) then begin
  1736. prec:=pDbfRecord(ActiveBuffer);
  1737. dst:=@prec^.DeletedFlag;
  1738. _dbfFile.SetFieldData(Field.FieldNo - 1,Field.DataType,Buffer,Dst);
  1739. end else begin { ***** fkCalculated, fkLookup ***** }
  1740. prec:=pDbfRecord(CalcBuffer);
  1741. dst:=@prec^.DeletedFlag;
  1742. Inc(integer(dst), GetRecordSize + Field.Offset);
  1743. Boolean(dst^) := LongBool(Buffer);
  1744. if Boolean(dst^) then begin
  1745. Inc(integer(dst), 1);
  1746. Move(Buffer^, dst^, Field.DataSize);
  1747. end;
  1748. end; { end of ***** fkCalculated, fkLookup ***** }
  1749. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then begin
  1750. DataEvent(deFieldChange, Longint(Field));
  1751. end;
  1752. end;
  1753. // this function is just for the grid scrollbars
  1754. // it doesn't have to be perfectly accurate, but fast.
  1755. function TDbf.GetRecordCount: Integer; {override virtual}
  1756. begin
  1757. if _curIndex=nil then begin
  1758. result:=_dbfFile.RecordCount;
  1759. end else begin
  1760. result:=_curIndex.GuessRecordCount;
  1761. end;
  1762. end;
  1763. // this function is just for the grid scrollbars
  1764. // it doesn't have to be perfectly accurate, but fast.
  1765. function TDbf.GetRecNo: Integer; {override virtual}
  1766. begin
  1767. UpdateCursorPos;
  1768. if _curIndex=nil then begin
  1769. result:=_PhysicalRecno+1;
  1770. end else begin
  1771. result:=_curIndex.GuessRecNo;
  1772. end;
  1773. end;
  1774. procedure TDbf.SetRecNo(Value: Integer); {override virual}
  1775. begin
  1776. if _curIndex=nil then begin
  1777. _PhysicalRecno:=Value-1;
  1778. end else begin
  1779. //result:=_curIndex.GuessRecNo;
  1780. end;
  1781. Resync([rmExact]);
  1782. end;
  1783. procedure TDBf.DeleteIndex(const AName: string);
  1784. begin
  1785. // I must admit that is seems a bit expeditive.
  1786. // but I does implement this method because TTable does
  1787. DeleteFile(_GetPath + Name);
  1788. end;
  1789. procedure TDbf.CloseIndexFile(const IndexFileName: string);
  1790. var
  1791. lindex:tindex;
  1792. begin
  1793. lindex:=_GetIndex(IndexFileName);
  1794. if lindex<>nil then begin
  1795. lindex.Free;
  1796. _indexes.Delete(_indexes.IndexOf(lindex));
  1797. if _curindex = lindex then begin
  1798. _curindex:=nil;
  1799. resync([]);
  1800. end;
  1801. end;
  1802. end;
  1803. procedure TDbf.OpenIndexFile(AnIndexName:string);
  1804. var
  1805. lIndexFile:TIndexFile;
  1806. lIndex:TIndex;
  1807. begin
  1808. lindex:=_GetIndex(IndexName);
  1809. if lindex=nil then begin
  1810. IndexName:=lowercase(_GetPath + IndexName);
  1811. lIndexFile:=TIndexFile(GetPagedFile(IndexName));
  1812. if lIndexFile=nil then begin
  1813. lIndexFile:=TIndexFile.Create(IndexName,fmOpenReadWrite + fmShareDenyWrite);
  1814. end;
  1815. lIndex:=TIndex.Create(lIndexFile,0,false);
  1816. _Indexes.Add(lIndex);
  1817. lIndex.InitFieldDef(_DbfFile,lIndex._NdxHdr.KeyDesc);
  1818. end;
  1819. end;
  1820. (*
  1821. procedure TDbfFile.DbfFile_PackTable;
  1822. var
  1823. begin
  1824. end;
  1825. *)
  1826. {$ifdef fpc}
  1827. procedure TDbf.AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions);
  1828. begin
  1829. AddIndex(indexName,IndexFields,options,'');
  1830. end;
  1831. {$endif}
  1832. {$ifdef DELPHI_3}
  1833. procedure TDbf.AddIndex(const IndexName, Fields: String; Options: TIndexOptions);
  1834. var
  1835. DescFields:string;
  1836. {$else}
  1837. {$ifndef fpc}
  1838. procedure TDbf.AddIndex(const IndexName, Fields: String; Options: TIndexOptions; const DescFields: String='');
  1839. var
  1840. {$else}
  1841. procedure TDbf.AddIndex(const AnIndexName, IndexFields: String; Options: TIndexOptions; const DescFields: String);
  1842. var
  1843. {$endif}
  1844. {$endif}
  1845. lfilename:string;
  1846. lIndexFile:TIndexFile;
  1847. lIndex:TIndex;
  1848. cur,thelast:integer;
  1849. begin
  1850. lfilename:=lowercase(_GetPath+IndexName);
  1851. lIndexFile:=TIndexFile(GetPagedFile(lfilename));
  1852. if lIndexFile<>nil then exit;
  1853. lIndexFile:=TIndexFile.Create(lfilename,fmCreate);
  1854. lIndex:=TIndex.Create(lIndexFile,0,true);
  1855. {$ifndef fpc}
  1856. lIndex.InitFieldDef(_DbfFile,Fields);
  1857. {$else}
  1858. lIndex.InitFieldDef(_DbfFile,IndexFields);
  1859. {$endif}
  1860. with lIndex._NdxHdr do begin
  1861. startpage:=1;
  1862. nbPage:=1;
  1863. keyformat:=#0;
  1864. keytype:='C';
  1865. dummy:=$5800;
  1866. keylen:=lindex._FieldLen;
  1867. nbkey:=(512-8) div (lindex._FieldLen+8);
  1868. keyreclen:=lindex._FieldLen+8;
  1869. Unique:=0;
  1870. KeyDesc[0]:=' ';
  1871. {$ifndef fpc}
  1872. StrLCopy(KeyDesc,PChar(UpperCase(Fields)),255);
  1873. {$else}
  1874. StrLCopy(KeyDesc,PChar(UpperCase(IndexFields)),255);
  1875. {$endif}
  1876. end;
  1877. lindex._IndexFile._Seek(lindex._RootPage);
  1878. lindex._IndexFile.Stream.Write(lindex._NdxHdr,SizeOf(lindex._NdxHdr));
  1879. cur:=0;
  1880. thelast:=_DbfFile.CalcRecordCount;
  1881. while cur<thelast do begin
  1882. _DbfFile.ReadRecord(cur, _PrevBuffer);
  1883. lIndex.Insert(cur,_PrevBuffer);
  1884. inc(cur);
  1885. end;
  1886. _Indexes.Add(lIndex);
  1887. end;
  1888. //==========================================================
  1889. //============ dbtfile
  1890. //==========================================================
  1891. constructor TDbtFile.Create(const FileName: string; Mode: Word; Ver:xBaseVersion);
  1892. begin
  1893. inherited Create(FileName,Mode);
  1894. _DbtVersion:=Ver;
  1895. if mode = fmCreate then begin
  1896. FillChar(_MemoHdr,sizeof(_MemoHdr),0);
  1897. end else begin
  1898. Stream.Position:=0;
  1899. Stream.read(_MemoHdr,SizeOf(_MemoHdr));
  1900. end;
  1901. HeaderSize:=0;
  1902. RecordSize:=_MemoHdr.BlockLen;
  1903. if (RecordSize=0) or ((RecordSize mod 128)<>0) then begin
  1904. _MemoHdr.BlockLen := $200;
  1905. RecordSize := $200;
  1906. end;
  1907. // Can you tell me why the header of dbase3 memo contains 1024 and it 512 ?
  1908. if _DbtVersion=xBaseIII then RecordSize:=512;
  1909. end;
  1910. procedure TDbtFile.ReadMemo(recno:Integer;Dst:TStream);
  1911. var
  1912. Buff:array[0..511] of char;
  1913. i,lsize:integer;
  1914. finish:boolean;
  1915. lastc:char;
  1916. begin
  1917. if recno=0 then Exit;
  1918. Stream.Position:= RecordSize * recno;
  1919. if _DbtVersion >= xBaseIV then begin // dBase4 memofiles
  1920. Stream.read(Buff[0],8);
  1921. if (Buff[0]=#$ff) and (Buff[1]=#$ff) and
  1922. (Buff[2]=#$08) and (Buff[3]=#$00) then begin
  1923. // dbase IV memo
  1924. lsize:=(PInteger(@Buff[4])^)-8;
  1925. end else begin
  1926. lsize:=0;
  1927. end;
  1928. repeat
  1929. if lsize>SizeOf(Buff) then begin
  1930. Stream.read(Buff,SizeOf(Buff));
  1931. Dst.Write(buff,SizeOf(Buff));
  1932. Dec(lsize,SizeOf(Buff));
  1933. end else if lsize>0 then begin
  1934. Stream.read(Buff,lsize);
  1935. Dst.Write(buff,lsize);
  1936. lsize:=0;
  1937. end;
  1938. until lsize=0;
  1939. end else begin
  1940. finish:=False;
  1941. Stream.read(Buff,SizeOf(Buff));
  1942. lastc:=#0;
  1943. repeat
  1944. for i:=0 to SizeOf(Buff)-2 do begin
  1945. if ((Buff[i]=#$1A) and
  1946. ((Buff[i+1]=#$1A) or ((i=0) and (lastc=#$1A))))
  1947. or (Buff[i]=#$0)
  1948. then begin
  1949. if i>0 then Dst.Write(buff,i);
  1950. finish:=True;
  1951. break;
  1952. end;
  1953. end;
  1954. if finish then Break;
  1955. Dst.Write(buff,512);
  1956. lastc:=Buff[511];
  1957. Stream.read(Buff,SizeOf(Buff));
  1958. until finish;
  1959. end;
  1960. Dst.Seek(0,0);
  1961. end;
  1962. procedure TDbtFile.WriteMemo(var MemoRecno:Integer;ReadSize:Integer;Src:TStream);
  1963. var
  1964. ByteBefore:Integer;
  1965. ByteAfter:Integer;
  1966. Buff:array[0..511] of char;
  1967. i:Integer;
  1968. c:Byte;
  1969. Append:Boolean;
  1970. begin
  1971. if _DbtVersion >= xBaseIV then begin // dBase4 memofiles
  1972. ByteBefore:=8;
  1973. ByteAfter:=0;
  1974. end else begin // stupid files
  1975. ByteBefore:=0;
  1976. ByteAfter:=2;
  1977. end;
  1978. if Src.Size = 0 then begin
  1979. MemoRecno:=0;
  1980. end else begin
  1981. if ((ByteBefore+Src.Size+ByteAfter+_MemoHdr.BlockLen-1) div _MemoHdr.BlockLen)
  1982. <= ((ReadSize+_MemoHdr.BlockLen-1) div _MemoHdr.BlockLen)
  1983. then begin
  1984. Append:=false;
  1985. //MemoRecno:=MemoRecno;
  1986. end else begin
  1987. Append:=True;
  1988. MemoRecno:=_MemoHdr.NextBlock;
  1989. if MemoRecno=0 then begin
  1990. _MemoHdr.NextBlock:=1;
  1991. MemoRecno:=1;
  1992. end;
  1993. end;
  1994. Stream.Seek(_MemoHdr.BlockLen * MemoRecno,0);
  1995. i:=Src.Position;
  1996. Src.Seek(0,0);
  1997. if ByteBefore=8 then begin
  1998. i:=$0008ffff;
  1999. Stream.Write(i,4);
  2000. i:=Src.Size+ByteBefore+ByteAfter;
  2001. Stream.Write(i,4);
  2002. end;
  2003. repeat
  2004. i:=Src.Read(buff,512);
  2005. if i=0 then break;
  2006. Inc(_MemoHdr.NextBlock);
  2007. Stream.Write(Buff,i);
  2008. until i<512;
  2009. if ByteAfter=2 then begin
  2010. c:=$1A;
  2011. Stream.Write(c,1);
  2012. Stream.Write(c,1);
  2013. end;
  2014. if Append then begin
  2015. Stream.Seek(0,0);
  2016. Stream.Write(_MemoHdr,SizeOf(_MemoHdr))
  2017. end;
  2018. end;
  2019. end;
  2020. //==========================================================
  2021. //============ TIndexFile
  2022. //==========================================================
  2023. constructor TIndexFile.Create(const FileName: string; Mode: Word);
  2024. var
  2025. ext:string;
  2026. i:Integer;
  2027. begin
  2028. inherited Create(FileName,Mode);
  2029. HeaderSize:=0;
  2030. RecordSize:=512;
  2031. ext:=ExtractFileExt(FileName);
  2032. if (ext='.mdx') then begin
  2033. _IndexVersion:=xBaseIV;
  2034. if Mode = fmCreate then begin
  2035. FillChar(_MdxHdr,sizeof(_MdxHdr),0);
  2036. end else begin
  2037. Stream.read(_MdxHdr,SizeOf(_MdxHdr));
  2038. end;
  2039. for i:= 0 to _MdxHdr.TagUsed-1 do begin
  2040. // Stream.Position :=544 + i * _MdxHdr.TagSize;
  2041. // Stream.read(lMdxTag,SizeOf(rMdxTag));
  2042. // lIndex:=TIndex.Create(Self,lMdxTag.pageno);
  2043. // _Indexes.Add(lIndex);
  2044. // if i=0 then lIndex.ReadPage(lIndex._NdxHdr.startpage);
  2045. end;
  2046. end else begin
  2047. _IndexVersion:=xBaseIII;
  2048. (*
  2049. _IndexFile._Seek(Pos);
  2050. _IndexFile.Stream.Read(_NdxHdr,SizeOf(_NdxHdr));
  2051. _Root:=TIndexPage.Create(Self);
  2052. _Root.SetPageNo(_NdxHdr.startpage);
  2053. lPos:=_Root;
  2054. _nblevel:=1;
  2055. repeat
  2056. lPos.LocalFirst;
  2057. if lPos.Entry._LowerPage=0 then break;
  2058. inc(_nblevel);
  2059. lChild:=TIndexPage.Create(Self);
  2060. lChild._UpperLevel:=lPos;
  2061. lPos._LowerLevel:=lChild;
  2062. lChild.SetPageNo(lPos.Entry._LowerPage);
  2063. lPos:=lChild;
  2064. until false;
  2065. _Spare:=TIndexPage.Create(Self);
  2066. // _Field:=_IndexFile._Dbf.FindField(_NdxHdr.KeyDesc);
  2067. First;
  2068. *)
  2069. end;
  2070. end;
  2071. destructor TIndexFile.Destroy;
  2072. begin
  2073. inherited;
  2074. end;
  2075. //==========================================================
  2076. //============ TIndexPage
  2077. //==========================================================
  2078. constructor TIndexPage.Create(Parent:TIndex);
  2079. begin
  2080. _LowerLevel:=nil;
  2081. _UpperLevel:=nil;
  2082. _Index:=Parent;
  2083. _PageNo:=-1;
  2084. _EntryNo:=-1;
  2085. end;
  2086. destructor TIndexPage.Destroy;
  2087. begin
  2088. if _LowerLevel<>nil then _LowerLevel.Free;
  2089. end;
  2090. function TIndexPage.GetPEntry(EntryNo:integer):PNdxEntry;
  2091. begin
  2092. Result:=PNdxentry(@_PageBuff.Entries[_Index._NdxHdr.keyreclen*entryno]);
  2093. end;
  2094. function TIndexPage.LocalInsert(Recno:integer; Buffer:Pchar;LowerPage:integer):boolean;
  2095. var
  2096. src,dst:pointer;
  2097. siz:integer;
  2098. begin
  2099. if _PageBuff.NbEntries < _Index._NdxHdr.nbkey then begin
  2100. src:=Entry;
  2101. dst:=GetPEntry(_EntryNo+1);
  2102. siz:=(_PageBuff.NbEntries - _EntryNo)
  2103. * _Index._NdxHdr.keyreclen + 8;
  2104. Move(Src^, Dst^, Siz);
  2105. inc(_PageBuff.NbEntries);
  2106. SetEntry(Recno,Buffer,LowerPage);
  2107. Write;
  2108. Result:=true;
  2109. end else begin
  2110. Result:=false;
  2111. end;
  2112. end;
  2113. function TIndexPage.LocalDelete:boolean;
  2114. var
  2115. src,dst:pointer;
  2116. siz:integer;
  2117. begin
  2118. if _PageBuff.NbEntries >=0 then begin
  2119. if _EntryNo<_PageBuff.NbEntries then begin
  2120. src:=GetPEntry(_EntryNo+1);
  2121. dst:=Entry;
  2122. siz:=(_PageBuff.NbEntries - _EntryNo - 1)
  2123. * _Index._NdxHdr.keyreclen + 8;
  2124. Move(Src^, Dst^, Siz);
  2125. end;
  2126. dec(_PageBuff.NbEntries);
  2127. Write;
  2128. if ((_PageBuff.NbEntries=0) and (_lowerlevel=nil))
  2129. or (_PageBuff.NbEntries<0) then begin
  2130. if _UpperLevel<>nil then begin
  2131. _UpperLevel.LocalDelete;
  2132. end;
  2133. end else if (_EntryNo>LastEntryNo) then begin
  2134. SetEntryNo(LastEntryNo); // We just removed the last on this page.
  2135. if (_UpperLevel<>nil) then begin
  2136. _UpperLevel.SetEntry(0,Entry^.CKey,_PageNo);
  2137. end;
  2138. end;
  2139. Result:=true;
  2140. end else begin
  2141. Result:=false;
  2142. end;
  2143. end;
  2144. function TIndexPage.LastEntryNo:integer;
  2145. begin
  2146. if (_LowerLevel=nil) then begin
  2147. result := _PageBuff.NbEntries - 1;
  2148. end else begin
  2149. result := _PageBuff.NbEntries;
  2150. end;
  2151. end;
  2152. procedure TIndexPage.LocalFirst;
  2153. begin
  2154. SetEntryNo(0);
  2155. end;
  2156. procedure TIndexPage.LocalLast;
  2157. begin
  2158. SetEntryNo(LastEntryNo);
  2159. end;
  2160. function TIndexPage.LocalPrev:boolean;
  2161. begin
  2162. if _EntryNo>0 then begin
  2163. SetEntryNo(_EntryNo-1);
  2164. Result:=true;
  2165. end else begin
  2166. Result:=false;
  2167. end;
  2168. end;
  2169. function TIndexPage.LocalNext:boolean;
  2170. begin
  2171. if (_EntryNo<LastEntryNo) then begin
  2172. SetEntryNo(_EntryNo+1);
  2173. Result:=true;
  2174. end else begin
  2175. Result:=false;
  2176. end;
  2177. end;
  2178. procedure TIndexPage.First;
  2179. begin
  2180. LocalFirst;
  2181. if (_LowerLevel<>nil) then LowerLevel.First;
  2182. end;
  2183. procedure TIndexPage.Last;
  2184. begin
  2185. LocalLast;
  2186. if (_LowerLevel<>nil) then LowerLevel.Last;
  2187. end;
  2188. function TIndexPage.Prev:boolean;
  2189. begin
  2190. if (_LowerLevel<>nil) and LowerLevel.Prev then begin
  2191. result:=true;
  2192. exit;
  2193. end;
  2194. Result:=LocalPrev;
  2195. if Result and (Entry^._LowerPage>0) then LowerLevel.Last;
  2196. end;
  2197. function TIndexPage.Next:boolean;
  2198. begin
  2199. if (_LowerLevel<>nil) and LowerLevel.next then begin
  2200. result:=true;
  2201. exit;
  2202. end;
  2203. Result:=LocalNext;
  2204. if Result and (Entry^._LowerPage>0) then LowerLevel.First;
  2205. end;
  2206. function TIndexPage.FindNearest(Recno:integer; Key:pchar):integer;
  2207. var
  2208. cmpres:integer;
  2209. v1,v2:double;
  2210. p:TIndexPage;
  2211. begin
  2212. Result:=-1;
  2213. if @Key=nil then begin
  2214. Exit;
  2215. end;
  2216. SetEntryNo(0);
  2217. while _EntryNo<=_PageBuff.NbEntries do begin
  2218. if _EntryNo=_PageBuff.NbEntries then break;
  2219. if _Index._NdxHdr.keytype='C' then begin
  2220. cmpres:=StrLIComp(PChar(Key),Entry^.CKey,_Index._FieldLen);
  2221. end else begin
  2222. // Numeric field... to do
  2223. v1:=PDouble(Key)^;
  2224. v2:=Entry^.NKey;
  2225. if v1>v2 then cmpres:=1
  2226. else if v1<v2 then cmpres:=-1
  2227. else cmpres:=0;
  2228. end;
  2229. if cmpres=0 then begin
  2230. if _LowerLevel=nil then begin
  2231. if (Entry^.RecNo=Recno) then begin
  2232. result:=0;
  2233. Exit;
  2234. end else if (Entry^.Recno>Recno) then begin
  2235. result:=-1;
  2236. Exit;
  2237. end;
  2238. end else begin
  2239. p:=self;
  2240. while p._LowerLevel<>nil do begin
  2241. p:=p.LowerLevel;
  2242. p.LocalLast;
  2243. end;
  2244. if (p.Entry^.Recno>=Recno) then begin
  2245. result:=-1;
  2246. Exit;
  2247. end;
  2248. end;
  2249. end else if cmpres<0 then begin
  2250. result:=-1;
  2251. exit;
  2252. end;
  2253. SetEntryNo(_EntryNo+1);
  2254. end;
  2255. result:=1;
  2256. Exit;
  2257. end;
  2258. procedure TIndexPage.SetEntry(Recno:Integer; key:PChar; LowerPage:integer);
  2259. begin
  2260. assert((_EntryNo>=0) and (_EntryNo<=_PageBuff.NbEntries));
  2261. if (_EntryNo=self._PageBuff.NbEntries) then begin
  2262. if (_UpperLevel<>nil) then begin
  2263. _UpperLevel.SetEntry(0,key,Self._PageNo);
  2264. end;
  2265. end else begin
  2266. if _Index._NdxHdr.keytype='C' then begin
  2267. mymove(key,Entry^.CKey,_Index._NdxHdr.keylen);
  2268. end else begin
  2269. Entry^.NKey:=PDouble(key)^;
  2270. end;
  2271. end;
  2272. Entry^.RecNo:=RecNo;
  2273. Entry^._LowerPage:=LowerPage;
  2274. Write;
  2275. end;
  2276. function TIndexPage.LowerLevel : TIndexPage;
  2277. begin
  2278. if (_LowerLevel<>nil) and (_LowerLevel._PageNo<>Entry^._LowerPage) then begin
  2279. _LowerLevel.SetPageNo(Entry^._LowerPage);
  2280. end;
  2281. result:=_LowerLevel;
  2282. end;
  2283. function TIndexPage.Insert(Recno:Integer; Buffer:PChar; LowerPage:integer):boolean;
  2284. var
  2285. src,dst:PNdxEntry;
  2286. siz:integer;
  2287. split,old_entry:integer;
  2288. lSpare:TIndexPage;
  2289. begin
  2290. if not LocalInsert(recno,buffer,lowerpage) then begin
  2291. // The entry is FULL so we will split this page
  2292. // 1 - Check parent exist
  2293. if _UpperLevel=nil then begin
  2294. AddNewLevel;
  2295. end;
  2296. old_entry:=_EntryNo;
  2297. split:=_EntryNo;
  2298. if split < _Index._NdxHdr.nbkey div 2 then begin
  2299. split:=_Index._NdxHdr.nbkey div 2;
  2300. end;
  2301. lSpare:=TIndexPage.Create(_Index);
  2302. try
  2303. // 2 - Create new page with first part
  2304. inc(_Index._NdxHdr.nbPage);
  2305. lSpare._PageNo:=_Index._NdxHdr.nbPage;
  2306. _Index._IndexFile._Seek(_Index._RootPage);
  2307. _Index._IndexFile.Stream.WriteBuffer (_Index._NdxHdr, SizeOf(_Index._NdxHdr));
  2308. if _lowerlevel=nil then begin
  2309. lSpare._PageBuff.NbEntries:=split;
  2310. end else begin
  2311. lSpare._PageBuff.NbEntries:=split-1;
  2312. end;
  2313. siz:=split*_Index._NdxHdr.keyreclen+8;
  2314. src:=@_PageBuff.Entries;
  2315. dst:=@lSpare._PageBuff.Entries;
  2316. Move(src^,dst^,siz);
  2317. lSpare.Write;
  2318. // 3 - Keep only end-part in this page
  2319. siz:=(_PageBuff.NbEntries-Split);
  2320. _PageBuff.NbEntries:=siz;
  2321. siz:=siz*_Index._NdxHdr.keyreclen+8;
  2322. SetEntryNo(split);
  2323. src:=Entry;
  2324. SetEntryNo(0);
  2325. dst:=Entry;
  2326. Move(src^,dst^,siz);
  2327. // 3 - Update upper level
  2328. lSpare.SetEntryNo(split-1);
  2329. _UpperLevel.Insert(0,lSpare.Entry^.CKey,lSpare._PageNo);
  2330. // We just need to go on inserted record now
  2331. if old_entry>=split then begin
  2332. _UpperLevel.LocalNext;
  2333. SetEntryNo(old_entry - split);
  2334. LocalInsert(Recno,Buffer,LowerPage);
  2335. lSpare.Write;
  2336. end else begin
  2337. lSpare.SetEntryNo(old_entry);
  2338. lSpare.LocalInsert(Recno,Buffer,LowerPage);
  2339. Write;
  2340. end;
  2341. finally
  2342. lspare.free;
  2343. end;
  2344. end;
  2345. Result:=true;
  2346. end;
  2347. function TIndexPage.Delete:boolean;
  2348. begin
  2349. Result:=LocalDelete;
  2350. end;
  2351. procedure TIndexPage.SetPageNo(page:Integer);
  2352. begin
  2353. if (_PageNo<>page) and (page>0) then begin
  2354. _Index._IndexFile.ReadRecord(Page,@_PageBuff);
  2355. _PageNo:=page;
  2356. _EntryNo:=-1;
  2357. end;
  2358. end;
  2359. procedure TIndexPage.AddNewLevel;
  2360. var
  2361. lNewPage:TIndexPage;
  2362. begin
  2363. lNewPage:=TIndexPage.Create(_Index);
  2364. inc(_Index._NdxHdr.nbPage);
  2365. lNewPage._PageNo:= _Index._NdxHdr.nbPage;
  2366. _Index._NdxHdr.startpage:= _Index._NdxHdr.nbPage;
  2367. _Index._IndexFile._Seek(_Index._RootPage);
  2368. _Index._IndexFile.Stream.WriteBuffer (_Index._NdxHdr, SizeOf(_Index._NdxHdr));
  2369. lNewPage._PageBuff.NbEntries:=0;
  2370. lNewPage._UpperLevel:=nil;
  2371. lNewPage._LowerLevel:=_Index._Root;
  2372. lNewPage.SetEntryNo(0);
  2373. lNewPage.SetEntry(0,nil,_PageNo);
  2374. _Index._Root._UpperLevel:=lNewPage;
  2375. _Index._Root:=lNewPage;
  2376. lNewPage:=nil;
  2377. end;
  2378. procedure TIndexPage.Write;
  2379. begin
  2380. _Index._IndexFile.WriteRecord(_PageNo,@_PageBuff);
  2381. end;
  2382. procedure TIndexPage.SetEntryNo(entryno:Integer);
  2383. begin
  2384. if (_EntryNo<>entryno) then begin
  2385. _EntryNo:=entryno;
  2386. if _EntryNo>=0 then Entry:=PNdxentry(@_PageBuff.Entries[_Index._NdxHdr.keyreclen*entryno]);
  2387. end;
  2388. end;
  2389. procedure TIndexPage.WritePage(Page:integer);
  2390. begin
  2391. _Index._IndexFile.WriteRecord(Page,@_PageBuff);
  2392. end;
  2393. //==========================================================
  2394. //============ TIndex
  2395. //==========================================================
  2396. constructor TIndex.Create(Parent:TIndexFile; RootPage:integer;CreateIt:boolean);
  2397. var
  2398. lPos:TIndexPage;
  2399. lChild:TIndexPage;
  2400. begin
  2401. _RootPage:=RootPage;
  2402. _IndexFile:=Parent;
  2403. //_IndexOrder:=TList.Create;
  2404. if CreateIt then begin
  2405. FillChar(_NdxHdr,sizeof(_NdxHdr),0);
  2406. _NdxHdr.startpage:=1;
  2407. _NdxHdr.nbPage:=2;
  2408. _NdxHdr.keyformat:=#0;
  2409. _NdxHdr.keytype:='C';
  2410. _IndexFile._Seek(RootPage);
  2411. _IndexFile.Stream.Write(_NdxHdr,SizeOf(_NdxHdr));
  2412. _FieldPos := 0;
  2413. _FieldLen := 0;
  2414. end else begin
  2415. _IndexFile._Seek(RootPage);
  2416. _IndexFile.Stream.Read(_NdxHdr,SizeOf(_NdxHdr));
  2417. end;
  2418. _Root:=TIndexPage.Create(Self);
  2419. _Root.SetPageNo(_NdxHdr.startpage);
  2420. lPos:=_Root;
  2421. _nblevel:=1;
  2422. repeat
  2423. lPos.LocalFirst;
  2424. if lPos.Entry^._LowerPage=0 then break;
  2425. inc(_nblevel);
  2426. lChild:=TIndexPage.Create(Self);
  2427. lChild._UpperLevel:=lPos;
  2428. lPos._LowerLevel:=lChild;
  2429. lChild.SetPageNo(lPos.Entry^._LowerPage);
  2430. lPos:=lChild;
  2431. until false;
  2432. inc(_IndexFile._cntuse);
  2433. First;
  2434. end;
  2435. destructor TIndex.Destroy;
  2436. begin
  2437. _IndexFile.Release;
  2438. _Root.Free;
  2439. end;
  2440. function TIndex.Find(Recno:integer; Buffer:PChar; var pPos:TIndexPage):integer;
  2441. var
  2442. res:integer;
  2443. begin
  2444. pPos:=_Root;
  2445. repeat
  2446. res:=pPos.FindNearest(Recno,Buffer);
  2447. if res<>0 then begin
  2448. if pPos.Entry^._LowerPage<>0 then begin
  2449. pPos:=pPos.LowerLevel;
  2450. res:=2;
  2451. end;
  2452. end;
  2453. until res<>2;
  2454. Result:=res;
  2455. end;
  2456. procedure TIndex.Update(Recno: integer; PrevBuffer,NewBuffer: PChar);
  2457. var
  2458. lPos:TIndexPage;
  2459. begin
  2460. if _FieldLen=0 then exit;
  2461. inc(PrevBuffer,_FieldPos);
  2462. inc(NewBuffer,_FieldPos);
  2463. if StrLIComp(PrevBuffer,NewBuffer,_FieldLen)<>0 then begin
  2464. Delete;
  2465. Find(Recno+1,NewBuffer,lPos);
  2466. lPos.Insert(Recno+1,NewBuffer,0);
  2467. end;
  2468. end;
  2469. procedure TIndex.Insert(Recno:integer; Buffer:PChar);
  2470. var
  2471. lPos:TIndexPage;
  2472. begin
  2473. if _FieldLen=0 then exit;
  2474. inc(Buffer,_FieldPos);
  2475. Find(Recno+1,Buffer,lPos);
  2476. lPos.Insert(Recno+1,Buffer,0);
  2477. end;
  2478. function TIndex.Delete:boolean;
  2479. var
  2480. lPos:TIndexPage;
  2481. begin
  2482. lpos:=_root;
  2483. while lpos._LowerLevel<>nil do begin
  2484. lPos:=lPos.LowerLevel;
  2485. end;
  2486. lPos.Delete;
  2487. Result:=true;
  2488. end;
  2489. function TIndex.Pos:TIndexPage;
  2490. var
  2491. p:TIndexPage;
  2492. begin
  2493. p:=_Root;
  2494. while p.Entry^._LowerPage>0 do begin
  2495. p:=p.LowerLevel;
  2496. end;
  2497. result:=p;
  2498. end;
  2499. procedure TIndex.First;
  2500. begin
  2501. _Root.First;
  2502. dec(Pos._EntryNo);
  2503. end;
  2504. procedure TIndex.Last;
  2505. begin
  2506. _Root.Last;
  2507. inc(Pos._EntryNo);
  2508. end;
  2509. function TIndex.Prev:boolean;
  2510. begin
  2511. result:=_Root.Prev;
  2512. end;
  2513. function TIndex.Next:boolean;
  2514. begin
  2515. result:=_Root.Next;
  2516. end;
  2517. (*
  2518. procedure TIndex.SetRecNo(Value: Integer);
  2519. var
  2520. pos:integer;
  2521. p:TIndexPage;
  2522. i:integer;
  2523. ldiv:integer;
  2524. begin
  2525. p:=_Root;
  2526. ldiv:=1;
  2527. while p.Entry^._LowerPage>0 do begin
  2528. ldiv:=ldiv*(_NdxHdr.nbkey+1);
  2529. p:=p._LowerLevel;
  2530. end;
  2531. pos:=value div ldiv;
  2532. p:=_Root;
  2533. while p.Entry^._LowerPage>0 do begin
  2534. p._EntryNo:=pos;
  2535. value:=value - pos * (_NdxHdr.nbkey+1);
  2536. ldiv:=ldiv div (_NdxHdr.nbkey+1);
  2537. pos:=value div ldiv;
  2538. p:=p._LowerLevel;
  2539. end;
  2540. {
  2541. pos:=1;
  2542. First;
  2543. While pos<value do begin
  2544. if Next = false then break;
  2545. inc(pos);
  2546. end;
  2547. }
  2548. end;
  2549. *)
  2550. function TIndex.GuessRecordCount: Integer;
  2551. var
  2552. lPos:TIndexPage;
  2553. nbrecord:integer;
  2554. begin
  2555. // I just read first level and Guess an approximate record count...
  2556. nbrecord:=_Root._PageBuff.NbEntries;
  2557. lPos:=_Root.LowerLevel;
  2558. while lpos<>nil do begin
  2559. nbrecord:=nbrecord*(_NdxHdr.nbkey+1);
  2560. lPos:=lPos.LowerLevel;
  2561. end;
  2562. result:=nbrecord;
  2563. end;
  2564. function TIndex.GuessRecNo:Integer;
  2565. var
  2566. p:TIndexPage;
  2567. begin
  2568. p:=_Root;
  2569. result:=p._EntryNo;
  2570. while p.Entry^._LowerPage>0 do begin
  2571. p:=p.LowerLevel;
  2572. Result:=Result*(_NdxHdr.nbkey+1) + p._EntryNo;
  2573. end;
  2574. end;
  2575. function TIndex.GetRealRecNo:integer;
  2576. var
  2577. ippos : TIndexPage;
  2578. begin
  2579. ippos:=_Root;
  2580. while ippos._LowerLevel<>nil do begin
  2581. ippos:=pos.LowerLevel;
  2582. end;
  2583. if (ippos._EntryNo<0) or (ippos._EntryNo>=ippos._PageBuff.NbEntries) then Result:=-1
  2584. else Result:=ippos.Entry^.RecNo-1;
  2585. end;
  2586. procedure TIndex.GotoKey(recno:integer; buffer:pchar);
  2587. begin
  2588. // very temporary implementation
  2589. // could definitely be a bit faster.
  2590. _Root.First;
  2591. repeat
  2592. if self.Pos.Entry^.RecNo=(recno+1) then begin
  2593. exit;
  2594. end;
  2595. until Next=false;
  2596. end;
  2597. procedure TIndex.InitFieldDef(dbfFile:TDbfFile;FieldDesc:string);
  2598. var
  2599. FieldInfo:TMyFieldInfo;
  2600. begin
  2601. FieldInfo:=DbfFile.GetFieldInfo(FieldDesc);
  2602. if FieldInfo<>nil then begin
  2603. _FieldPos:=FieldInfo.Offset;
  2604. _FieldLen:=FieldInfo.Size;
  2605. end;
  2606. end;
  2607. //==========================================================
  2608. //============ initialization
  2609. //==========================================================
  2610. {$ifndef fpc}
  2611. type
  2612. TTableNameProperty = class(TStringProperty)
  2613. public
  2614. procedure Edit; override;
  2615. function GetAttributes: TPropertyAttributes; override;
  2616. end;
  2617. procedure TTableNameProperty.Edit; {override;}
  2618. var
  2619. FileOpen: TOpenDialog;
  2620. Dbf: TDbf;
  2621. begin
  2622. FileOpen := TOpenDialog.Create(Application);
  2623. try
  2624. with fileopen do begin
  2625. Dbf:=GetComponent(0) as TDbf;
  2626. Filename := Dbf.DesignTimePath + GetValue;
  2627. Filter := 'Dbf table|*.dbf';
  2628. if Execute then begin
  2629. SetValue(ExtractFilename(Filename));
  2630. Dbf.DesignTimePath:=ExtractFilePath(Filename);
  2631. end;
  2632. end;
  2633. finally
  2634. Fileopen.free;
  2635. end;
  2636. end;
  2637. function TTableNameProperty.GetAttributes: TPropertyAttributes; {override;}
  2638. begin
  2639. Result := [paDialog, paRevertable];
  2640. end;
  2641. type
  2642. TRunTimePathProperty = class(TStringProperty)
  2643. end;
  2644. TDesignTimePathProperty = class(TStringProperty)
  2645. end;
  2646. //==========================================================
  2647. //============ initialization
  2648. //==========================================================
  2649. procedure Register;
  2650. begin
  2651. RegisterComponents('Exemples', [TDbf]);
  2652. RegisterPropertyEditor(TypeInfo(string), TDbf, 'TableName', TTableNameProperty);
  2653. RegisterPropertyEditor(TypeInfo(string), TDbf, 'RunTimePath', TRunTimePathProperty);
  2654. RegisterPropertyEditor(TypeInfo(string), TDbf, 'DesignTimePath', TDesignTimePathProperty);
  2655. // RegisterPropertyEditor(TypeInfo(TStrings), TDbf, 'IndexFiles', TIndexFilesProperty);
  2656. // ShowMessage(ToolServices.GetProjectName);
  2657. end;
  2658. {$endif fpc}
  2659. initialization
  2660. _PagedFiles := TList.Create;
  2661. tDbf_TrimFields := true;
  2662. finalization
  2663. _PagedFiles.free;
  2664. end.