dbf_dbffile.pas 80 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802
  1. unit dbf_dbffile;
  2. interface
  3. {$I dbf_common.inc}
  4. uses
  5. Classes, SysUtils,
  6. {$ifdef WINDOWS}
  7. Windows,
  8. {$else}
  9. {$ifdef KYLIX}
  10. Libc,
  11. {$endif}
  12. Types, dbf_wtil,
  13. {$endif}
  14. Db,
  15. dbf_common,
  16. dbf_cursor,
  17. dbf_pgfile,
  18. dbf_fields,
  19. dbf_memo,
  20. dbf_idxfile;
  21. //====================================================================
  22. //=== Dbf support (first part)
  23. //====================================================================
  24. // TxBaseVersion = (xUnknown,xClipper,xBaseIII,xBaseIV,xBaseV,xFoxPro,xVisualFoxPro);
  25. // TPagedFileMode = (pfOpen,pfCreate);
  26. // TDbfGetMode = (xFirst,xPrev,xCurrent, xNext, xLast);
  27. // TDbfGetResult = (xOK, xBOF, xEOF, xError);
  28. type
  29. //====================================================================
  30. TDbfIndexMissingEvent = procedure(var DeleteLink: Boolean) of object;
  31. TUpdateNullField = (unClear, unSet);
  32. //====================================================================
  33. TDbfGlobals = class;
  34. //====================================================================
  35. TDbfFile = class(TPagedFile)
  36. protected
  37. FMdxFile: TIndexFile;
  38. FMemoFile: TMemoFile;
  39. FFieldDefs: TDbfFieldDefs;
  40. FIndexNames: TStringList;
  41. FIndexFiles: TList;
  42. FDbfVersion: TXBaseVersion;
  43. FPrevBuffer: PChar;
  44. FDefaultBuffer: PChar;
  45. FRecordBufferSize: Integer;
  46. FLockUserLen: DWORD;
  47. FFileCodePage: Cardinal;
  48. FUseCodePage: Cardinal;
  49. FFileLangId: Byte;
  50. FCountUse: Integer;
  51. FCurIndex: Integer;
  52. FForceClose: Boolean;
  53. FLockField: TDbfFieldDef;
  54. FNullField: TDbfFieldDef;
  55. FAutoIncPresent: Boolean;
  56. FCopyDateTimeAsString: Boolean;
  57. FDateTimeHandling: TDateTimeHandling;
  58. FOnLocaleError: TDbfLocaleErrorEvent;
  59. FOnIndexMissing: TDbfIndexMissingEvent;
  60. function HasBlob: Boolean;
  61. function GetMemoExt: string;
  62. function GetLanguageId: Integer;
  63. function GetLanguageStr: string;
  64. protected
  65. procedure ConstructFieldDefs;
  66. procedure InitDefaultBuffer;
  67. procedure UpdateNullField(Buffer: Pointer; AFieldDef: TDbfFieldDef; Action: TUpdateNullField);
  68. procedure WriteLockInfo(Buffer: PChar);
  69. public
  70. constructor Create;
  71. destructor Destroy; override;
  72. procedure Open;
  73. procedure Close;
  74. procedure Zap;
  75. procedure FinishCreate(AFieldDefs: TDbfFieldDefs; MemoSize: Integer);
  76. function GetIndexByName(AIndexName: string): TIndexFile;
  77. procedure SetRecordSize(NewSize: Integer); override;
  78. procedure TryExclusive; override;
  79. procedure EndExclusive; override;
  80. procedure OpenIndex(IndexName, IndexField: string; CreateIndex: Boolean; Options: TIndexOptions);
  81. function DeleteIndex(const AIndexName: string): Boolean;
  82. procedure CloseIndex(AIndexName: string);
  83. procedure RepageIndex(AIndexFile: string);
  84. procedure CompactIndex(AIndexFile: string);
  85. function Insert(Buffer: PChar): integer;
  86. procedure WriteHeader; override;
  87. procedure ApplyAutoIncToBuffer(DestBuf: PChar); // dBase7 support. Writeback last next-autoinc value
  88. procedure FastPackTable;
  89. procedure RestructureTable(DbfFieldDefs: TDbfFieldDefs; Pack: Boolean);
  90. procedure Rename(DestFileName: string; NewIndexFileNames: TStrings; DeleteFiles: boolean);
  91. function GetFieldInfo(FieldName: string): TDbfFieldDef;
  92. function GetFieldData(Column: Integer; DataType: TFieldType; Src,Dst: Pointer;
  93. NativeFormat: boolean): Boolean;
  94. function GetFieldDataFromDef(AFieldDef: TDbfFieldDef; DataType: TFieldType;
  95. Src, Dst: Pointer; NativeFormat: boolean): Boolean;
  96. procedure SetFieldData(Column: Integer; DataType: TFieldType; Src,Dst: Pointer; NativeFormat: boolean);
  97. procedure InitRecord(DestBuf: PChar);
  98. procedure PackIndex(lIndexFile: TIndexFile; AIndexName: string);
  99. procedure RegenerateIndexes;
  100. procedure LockRecord(RecNo: Integer; Buffer: PChar);
  101. procedure UnlockRecord(RecNo: Integer; Buffer: PChar);
  102. procedure RecordDeleted(RecNo: Integer; Buffer: PChar);
  103. procedure RecordRecalled(RecNo: Integer; Buffer: PChar);
  104. property MemoFile: TMemoFile read FMemoFile;
  105. property FieldDefs: TDbfFieldDefs read FFieldDefs;
  106. property IndexNames: TStringList read FIndexNames;
  107. property IndexFiles: TList read FIndexFiles;
  108. property MdxFile: TIndexFile read FMdxFile;
  109. property LanguageId: Integer read GetLanguageId;
  110. property LanguageStr: string read GetLanguageStr;
  111. property FileCodePage: Cardinal read FFileCodePage;
  112. property UseCodePage: Cardinal read FUseCodePage write FUseCodePage;
  113. property FileLangId: Byte read FFileLangId write FFileLangId;
  114. property DbfVersion: TXBaseVersion read FDbfVersion write FDbfVersion;
  115. property PrevBuffer: PChar read FPrevBuffer;
  116. property ForceClose: Boolean read FForceClose;
  117. property CopyDateTimeAsString: Boolean read FCopyDateTimeAsString write FCopyDateTimeAsString;
  118. property DateTimeHandling: TDateTimeHandling read FDateTimeHandling write FDateTimeHandling;
  119. property OnIndexMissing: TDbfIndexMissingEvent read FOnIndexMissing write FOnIndexMissing;
  120. property OnLocaleError: TDbfLocaleErrorEvent read FOnLocaleError write FOnLocaleError;
  121. end;
  122. //====================================================================
  123. TDbfCursor = class(TVirtualCursor)
  124. protected
  125. FPhysicalRecNo: Integer;
  126. public
  127. constructor Create(DbfFile: TDbfFile);
  128. function Next: Boolean; override;
  129. function Prev: Boolean; override;
  130. procedure First; override;
  131. procedure Last; override;
  132. function GetPhysicalRecNo: Integer; override;
  133. procedure SetPhysicalRecNo(RecNo: Integer); override;
  134. function GetSequentialRecordCount: Integer; override;
  135. function GetSequentialRecNo: Integer; override;
  136. procedure SetSequentialRecNo(RecNo: Integer); override;
  137. end;
  138. //====================================================================
  139. TDbfGlobals = class
  140. protected
  141. FCodePages: TList;
  142. FCurrencyAsBCD: Boolean;
  143. FDefaultOpenCodePage: Integer;
  144. FDefaultCreateLangId: Byte;
  145. FUserName: string;
  146. FUserNameLen: DWORD;
  147. function GetDefaultCreateCodePage: Integer;
  148. procedure SetDefaultCreateCodePage(NewCodePage: Integer);
  149. procedure InitUserName;
  150. public
  151. constructor Create;
  152. destructor Destroy; override;
  153. function CodePageInstalled(ACodePage: Integer): Boolean;
  154. property CurrencyAsBCD: Boolean read FCurrencyAsBCD write FCurrencyAsBCD;
  155. property DefaultOpenCodePage: Integer read FDefaultOpenCodePage write FDefaultOpenCodePage;
  156. property DefaultCreateCodePage: Integer read GetDefaultCreateCodePage write SetDefaultCreateCodePage;
  157. property DefaultCreateLangId: Byte read FDefaultCreateLangId write FDefaultCreateLangId;
  158. property UserName: string read FUserName;
  159. property UserNameLen: DWORD read FUserNameLen;
  160. end;
  161. var
  162. DbfGlobals: TDbfGlobals;
  163. implementation
  164. uses
  165. {$ifndef WINDOWS}
  166. {$ifndef FPC}
  167. RTLConsts,
  168. {$else}
  169. BaseUnix,
  170. {$endif}
  171. {$endif}
  172. {$ifdef SUPPORT_MATH_UNIT}
  173. Math,
  174. {$endif}
  175. dbf_str, dbf_lang, dbf_prssupp, dbf_prsdef;
  176. const
  177. sDBF_DEC_SEP = '.';
  178. {$I dbf_struct.inc}
  179. //====================================================================
  180. // International separator
  181. // thanks to Bruno Depero from Italy
  182. // and Andreas Wöllenstein from Denmark
  183. //====================================================================
  184. function DbfStrToFloat(const Src: PChar; const Size: Integer): Extended;
  185. var
  186. iPos: PChar;
  187. eValue: extended;
  188. endChar: Char;
  189. begin
  190. // temp null-term string
  191. endChar := (Src + Size)^;
  192. (Src + Size)^ := #0;
  193. // we only have to convert if decimal separator different
  194. if DecimalSeparator <> sDBF_DEC_SEP then
  195. begin
  196. // search dec sep
  197. iPos := StrScan(Src, sDBF_DEC_SEP);
  198. // replace
  199. if iPos <> nil then
  200. iPos^ := DecimalSeparator;
  201. end else
  202. iPos := nil;
  203. // convert to double
  204. if TextToFloat(Src, eValue {$ifndef VER1_0}, fvExtended{$endif}) then
  205. Result := eValue
  206. else
  207. Result := 0;
  208. // restore dec sep
  209. if iPos <> nil then
  210. iPos^ := sDBF_DEC_SEP;
  211. // restore Char of null-term
  212. (Src + Size)^ := endChar;
  213. end;
  214. procedure FloatToDbfStr(const Val: Extended; const Size, Precision: Integer; const Dest: PChar);
  215. var
  216. Buffer: array [0..24] of Char;
  217. resLen: Integer;
  218. iPos: PChar;
  219. begin
  220. // convert to temporary buffer
  221. resLen := FloatToText(@Buffer[0], Val, {$ifndef FPC_VERSION}fvExtended,{$endif} ffFixed, Size, Precision);
  222. // prevent overflow in destination buffer
  223. if resLen > Size then
  224. resLen := Size;
  225. // null-terminate buffer
  226. Buffer[resLen] := #0;
  227. // we only have to convert if decimal separator different
  228. if DecimalSeparator <> sDBF_DEC_SEP then
  229. begin
  230. iPos := StrScan(@Buffer[0], DecimalSeparator);
  231. if iPos <> nil then
  232. iPos^ := sDBF_DEC_SEP;
  233. end;
  234. // fill destination with spaces
  235. FillChar(Dest^, Size, ' ');
  236. // now copy right-aligned to destination
  237. Move(Buffer[0], Dest[Size-resLen], resLen);
  238. end;
  239. function GetIntFromStrLength(Src: Pointer; Size: Integer; Default: Integer): Integer;
  240. var
  241. endChar: Char;
  242. Code: Integer;
  243. begin
  244. // save Char at pos term. null
  245. endChar := (PChar(Src) + Size)^;
  246. (PChar(Src) + Size)^ := #0;
  247. // convert
  248. Val(PChar(Src), Result, Code);
  249. // check success
  250. if Code <> 0 then
  251. Result := Default;
  252. // restore prev. ending Char
  253. (PChar(Src) + Size)^ := endChar;
  254. end;
  255. //====================================================================
  256. // TDbfFile
  257. //====================================================================
  258. constructor TDbfFile.Create;
  259. begin
  260. // init variables first
  261. FFieldDefs := TDbfFieldDefs.Create(nil);
  262. FIndexNames := TStringList.Create;
  263. FIndexFiles := TList.Create;
  264. // now initialize inherited
  265. inherited;
  266. end;
  267. destructor TDbfFile.Destroy;
  268. var
  269. I: Integer;
  270. begin
  271. // close file
  272. Close;
  273. // free files
  274. for I := 0 to Pred(FIndexFiles.Count) do
  275. TPagedFile(FIndexFiles.Items[I]).Free;
  276. // free lists
  277. FreeAndNil(FIndexFiles);
  278. FreeAndNil(FIndexNames);
  279. FreeAndNil(FFieldDefs);
  280. // call ancestor
  281. inherited;
  282. end;
  283. procedure TDbfFile.Open;
  284. var
  285. lMemoFileName: string;
  286. lMdxFileName: string;
  287. MemoFileClass: TMemoFileClass;
  288. I: Integer;
  289. deleteLink: Boolean;
  290. lModified: boolean;
  291. LangStr: PChar;
  292. version: byte;
  293. begin
  294. // check if not already opened
  295. if not Active then
  296. begin
  297. // open requested file
  298. OpenFile;
  299. // check if we opened an already existing file
  300. lModified := false;
  301. if not FileCreated then
  302. begin
  303. HeaderSize := sizeof(rDbfHdr); // temporary
  304. // OH 2000-11-15 dBase7 support. I build dBase Tables with different
  305. // BDE dBase Level (1. without Memo, 2. with Memo)
  306. // Header Byte ($1d hex) (29 dec) -> Language driver ID.
  307. // $03,$83 xBaseIII Header Byte $1d=$00, Float -> N($13.$04) DateTime C($1E)
  308. // $03,$8B xBaseIV/V Header Byte $1d=$58, Float -> N($14.$04)
  309. // $04,$8C xBaseVII Header Byte $1d=$00 Float -> O($08) DateTime @($08)
  310. // $03,$F5 FoxPro Level 25 Header Byte $1d=$03, Float -> N($14.$04)
  311. // Access 97
  312. // $03,$83 dBaseIII Header Byte $1d=$00, Float -> N($13.$05) DateTime D($08)
  313. // $03,$8B dBaseIV/V Header Byte $1d=$00, Float -> N($14.$05) DateTime D($08)
  314. // $03,$F5 FoxPro Level 25 Header Byte $1d=$09, Float -> N($14.$05) DateTime D($08)
  315. version := PDbfHdr(Header)^.VerDBF;
  316. case (version and $07) of
  317. $03:
  318. if LanguageID = 0 then
  319. FDbfVersion := xBaseIII
  320. else
  321. FDbfVersion := xBaseIV;
  322. $04:
  323. FDbfVersion := xBaseVII;
  324. $02, $05:
  325. FDbfVersion := xFoxPro;
  326. else
  327. // check visual foxpro
  328. if ((version and $FE) = $30) or (version = $F5) or (version = $FB) then
  329. begin
  330. FDbfVersion := xFoxPro;
  331. end else begin
  332. // not a valid DBF file
  333. raise EDbfError.Create(STRING_INVALID_DBF_FILE);
  334. end;
  335. end;
  336. FFieldDefs.DbfVersion := FDbfVersion;
  337. RecordSize := PDbfHdr(Header)^.RecordSize;
  338. HeaderSize := PDbfHdr(Header)^.FullHdrSize;
  339. if (HeaderSize = 0) or (RecordSize = 0) then
  340. begin
  341. HeaderSize := 0;
  342. RecordSize := 0;
  343. RecordCount := 0;
  344. FForceClose := true;
  345. exit;
  346. end;
  347. // check if specified recordcount correct
  348. if PDbfHdr(Header)^.RecordCount <> RecordCount then
  349. begin
  350. // This message was annoying
  351. // and was not understood by most people
  352. // ShowMessage('Invalid Record Count,'+^M+
  353. // 'RecordCount in Hdr : '+IntToStr(PDbfHdr(Header).RecordCount)+^M+
  354. // 'expected : '+IntToStr(RecordCount));
  355. PDbfHdr(Header)^.RecordCount := RecordCount;
  356. lModified := true;
  357. end;
  358. // determine codepage
  359. if FDbfVersion >= xBaseVII then
  360. begin
  361. // cache language str
  362. LangStr := @PAfterHdrVII(PChar(Header) + SizeOf(rDbfHdr))^.LanguageDriverName;
  363. // VdBase 7 Language strings
  364. // 'DBWIN...' -> Charset 1252 (ansi)
  365. // 'DB999...' -> Code page 999, 9 any digit
  366. // 'DBHEBREW' -> Code page 1255 ??
  367. // 'FOX..999' -> Code page 999, 9 any digit
  368. // 'FOX..WIN' -> Charset 1252 (ansi)
  369. if (LangStr[0] = 'D') and (LangStr[1] = 'B') then
  370. begin
  371. if StrLComp(LangStr+2, 'WIN', 3) = 0 then
  372. FFileCodePage := 1252
  373. else
  374. if StrLComp(LangStr+2, 'HEBREW', 6) = 0 then
  375. begin
  376. FFileCodePage := 1255;
  377. end else begin
  378. FFileCodePage := GetIntFromStrLength(LangStr+2, 3, 0);
  379. if (Ord(LangStr[5]) >= Ord('0')) and (Ord(LangStr[5]) <= Ord('9')) then
  380. FFileCodePage := FFileCodePage * 10 + Ord(LangStr[5]) - Ord('0');
  381. end;
  382. end else
  383. if StrLComp(LangStr, 'FOX', 3) = 0 then
  384. begin
  385. if StrLComp(LangStr+5, 'WIN', 3) = 0 then
  386. FFileCodePage := 1252
  387. else
  388. FFileCodePage := GetIntFromStrLength(LangStr+5, 3, 0)
  389. end else begin
  390. FFileCodePage := 0;
  391. end;
  392. FFileLangId := GetLangId_From_LangName(LanguageStr);
  393. end else begin
  394. // FDbfVersion <= xBaseV
  395. FFileLangId := PDbfHdr(Header)^.Language;
  396. FFileCodePage := LangId_To_CodePage[FFileLangId];
  397. end;
  398. // determine used codepage, if no codepage, then use default codepage
  399. FUseCodePage := FFileCodePage;
  400. if FUseCodePage = 0 then
  401. FUseCodePage := DbfGlobals.DefaultOpenCodePage;
  402. // get list of fields
  403. ConstructFieldDefs;
  404. // open blob file if present
  405. lMemoFileName := ChangeFileExt(FileName, GetMemoExt);
  406. if HasBlob then
  407. begin
  408. // open blob file
  409. if not FileExists(lMemoFileName) then
  410. MemoFileClass := TNullMemoFile
  411. else if FDbfVersion = xFoxPro then
  412. MemoFileClass := TFoxProMemoFile
  413. else
  414. MemoFileClass := TDbaseMemoFile;
  415. FMemoFile := MemoFileClass.Create(Self);
  416. FMemoFile.FileName := lMemoFileName;
  417. FMemoFile.Mode := Mode;
  418. FMemoFile.AutoCreate := false;
  419. FMemoFile.MemoRecordSize := 0;
  420. FMemoFile.DbfVersion := FDbfVersion;
  421. FMemoFile.Open;
  422. // set header blob flag corresponding to field list
  423. if FDbfVersion <> xFoxPro then
  424. begin
  425. PDbfHdr(Header)^.VerDBF := PDbfHdr(Header)^.VerDBF or $80;
  426. lModified := true;
  427. end;
  428. end else
  429. if FDbfVersion <> xFoxPro then
  430. begin
  431. PDbfHdr(Header)^.VerDBF := PDbfHdr(Header)^.VerDBF and $7F;
  432. lModified := true;
  433. end;
  434. // check if mdx flagged
  435. if (FDbfVersion <> xFoxPro) and (PDbfHdr(Header)^.MDXFlag <> 0) then
  436. begin
  437. // open mdx file if present
  438. lMdxFileName := ChangeFileExt(FileName, '.mdx');
  439. if FileExists(lMdxFileName) then
  440. begin
  441. // open file
  442. FMdxFile := TIndexFile.Create(Self);
  443. FMdxFile.FileName := lMdxFileName;
  444. FMdxFile.Mode := Mode;
  445. FMdxFile.AutoCreate := false;
  446. FMdxFile.OnLocaleError := FOnLocaleError;
  447. FMdxFile.CodePage := UseCodePage;
  448. FMdxFile.Open;
  449. // is index ready for use?
  450. if not FMdxFile.ForceClose then
  451. begin
  452. FIndexFiles.Add(FMdxFile);
  453. // get index tag names known
  454. FMdxFile.GetIndexNames(FIndexNames);
  455. end else begin
  456. // asked to close! close file
  457. FreeAndNil(FMdxFile);
  458. end;
  459. end else begin
  460. // ask user
  461. deleteLink := true;
  462. if Assigned(FOnIndexMissing) then
  463. FOnIndexMissing(deleteLink);
  464. // correct flag
  465. if deleteLink then
  466. begin
  467. PDbfHdr(Header)^.MDXFlag := 0;
  468. lModified := true;
  469. end else
  470. FForceClose := true;
  471. end;
  472. end;
  473. end;
  474. // record changes
  475. if lModified then
  476. WriteHeader;
  477. // open indexes
  478. for I := 0 to FIndexFiles.Count - 1 do
  479. TIndexFile(FIndexFiles.Items[I]).Open;
  480. end;
  481. end;
  482. procedure TDbfFile.Close;
  483. var
  484. MdxIndex, I: Integer;
  485. begin
  486. if Active then
  487. begin
  488. // close index files first
  489. MdxIndex := -1;
  490. for I := 0 to FIndexFiles.Count - 1 do
  491. begin
  492. TIndexFile(FIndexFiles.Items[I]).Close;
  493. if TIndexFile(FIndexFiles.Items[I]) = FMdxFile then
  494. MdxIndex := I;
  495. end;
  496. // free memo file if any
  497. FreeAndNil(FMemoFile);
  498. // now we can close physical dbf file
  499. CloseFile;
  500. // free FMdxFile, remove it from the FIndexFiles and Names lists
  501. if MdxIndex >= 0 then
  502. FIndexFiles.Delete(MdxIndex);
  503. I := 0;
  504. while I < FIndexNames.Count do
  505. begin
  506. if FIndexNames.Objects[I] = FMdxFile then
  507. begin
  508. FIndexNames.Delete(I);
  509. end else begin
  510. Inc(I);
  511. end;
  512. end;
  513. FreeAndNil(FMdxFile);
  514. FreeMemAndNil(Pointer(FPrevBuffer));
  515. FreeMemAndNil(Pointer(FDefaultBuffer));
  516. // reset variables
  517. FFileLangId := 0;
  518. end;
  519. end;
  520. procedure TDbfFile.FinishCreate(AFieldDefs: TDbfFieldDefs; MemoSize: Integer);
  521. var
  522. lFieldDescIII: rFieldDescIII;
  523. lFieldDescVII: rFieldDescVII;
  524. lFieldDescPtr: Pointer;
  525. lFieldDef: TDbfFieldDef;
  526. lMemoFileName: string;
  527. I, lFieldOffset, lSize, lPrec: Integer;
  528. lHasBlob: Boolean;
  529. lLocaleID: LCID;
  530. begin
  531. try
  532. // first reset file
  533. RecordCount := 0;
  534. lHasBlob := false;
  535. // determine codepage & locale
  536. if FFileLangId = 0 then
  537. FFileLangId := DbfGlobals.DefaultCreateLangId;
  538. FFileCodePage := LangId_To_CodePage[FFileLangId];
  539. lLocaleID := LangId_To_Locale[FFileLangId];
  540. FUseCodePage := FFileCodePage;
  541. // prepare header size
  542. if FDbfVersion = xBaseVII then
  543. begin
  544. // version xBaseVII without memo
  545. HeaderSize := SizeOf(rDbfHdr) + SizeOf(rAfterHdrVII);
  546. RecordSize := SizeOf(rFieldDescVII);
  547. FillChar(Header^, HeaderSize, #0);
  548. PDbfHdr(Header)^.VerDBF := $04;
  549. // write language string
  550. StrPLCopy(
  551. @PAfterHdrVII(PChar(Header)+SizeOf(rDbfHdr))^.LanguageDriverName[32],
  552. ConstructLangName(FFileCodePage, lLocaleID, false),
  553. 63-32);
  554. lFieldDescPtr := @lFieldDescVII;
  555. end else begin
  556. // version xBaseIII/IV/V without memo
  557. HeaderSize := SizeOf(rDbfHdr) + SizeOf(rAfterHdrIII);
  558. RecordSize := SizeOf(rFieldDescIII);
  559. FillChar(Header^, HeaderSize, #0);
  560. if FDbfVersion = xFoxPro then
  561. begin
  562. PDbfHdr(Header)^.VerDBF := $02
  563. end else
  564. PDbfHdr(Header)^.VerDBF := $03;
  565. // standard language WE, dBase III no language support
  566. if FDbfVersion = xBaseIII then
  567. PDbfHdr(Header)^.Language := 0
  568. else
  569. PDbfHdr(Header)^.Language := FFileLangId;
  570. // init field ptr
  571. lFieldDescPtr := @lFieldDescIII;
  572. end;
  573. // begin writing fields
  574. FFieldDefs.Clear;
  575. // deleted mark 1 byte
  576. lFieldOffset := 1;
  577. for I := 1 to AFieldDefs.Count do
  578. begin
  579. lFieldDef := AFieldDefs.Items[I-1];
  580. // check if datetime conversion
  581. if FCopyDateTimeAsString then
  582. if lFieldDef.FieldType = ftDateTime then
  583. begin
  584. // convert to string
  585. lFieldDef.FieldType := ftString;
  586. lFieldDef.Size := 22;
  587. end;
  588. // update source
  589. lFieldDef.FieldName := AnsiUpperCase(lFieldDef.FieldName);
  590. lFieldDef.Offset := lFieldOffset;
  591. lHasBlob := lHasBlob or lFieldDef.IsBlob;
  592. // apply field transformation tricks
  593. lSize := lFieldDef.Size;
  594. lPrec := lFieldDef.Precision;
  595. if (lFieldDef.NativeFieldType = 'C')
  596. {$ifndef USE_LONG_CHAR_FIELDS}
  597. and (FDbfVersion = xFoxPro)
  598. {$endif}
  599. then
  600. begin
  601. lPrec := lSize shr 8;
  602. lSize := lSize and $FF;
  603. end;
  604. // update temp field props
  605. if FDbfVersion = xBaseVII then
  606. begin
  607. FillChar(lFieldDescVII, SizeOf(lFieldDescVII), #0);
  608. StrPLCopy(lFieldDescVII.FieldName, lFieldDef.FieldName, SizeOf(lFieldDescVII.FieldName)-1);
  609. lFieldDescVII.FieldType := lFieldDef.NativeFieldType;
  610. lFieldDescVII.FieldSize := lSize;
  611. lFieldDescVII.FieldPrecision := lPrec;
  612. lFieldDescVII.NextAutoInc := SwapIntLE(lFieldDef.AutoInc);
  613. //lFieldDescVII.MDXFlag := ???
  614. end else begin
  615. FillChar(lFieldDescIII, SizeOf(lFieldDescIII), #0);
  616. StrPLCopy(lFieldDescIII.FieldName, lFieldDef.FieldName, SizeOf(lFieldDescIII.FieldName)-1);
  617. lFieldDescIII.FieldType := lFieldDef.NativeFieldType;
  618. lFieldDescIII.FieldSize := lSize;
  619. lFieldDescIII.FieldPrecision := lPrec;
  620. if FDbfVersion = xFoxPro then
  621. lFieldDescIII.FieldOffset := SwapIntLE(lFieldOffset);
  622. if (PDbfHdr(Header)^.VerDBF = $02) and (lFieldDef.NativeFieldType in ['0', 'Y', 'T', 'O', '+']) then
  623. PDbfHdr(Header)^.VerDBF := $30;
  624. if (PDbfHdr(Header)^.VerDBF = $30) and (lFieldDef.NativeFieldType = '+') then
  625. PDbfHdr(Header)^.VerDBF := $31;
  626. end;
  627. // update our field list
  628. with FFieldDefs.AddFieldDef do
  629. begin
  630. Assign(lFieldDef);
  631. Offset := lFieldOffset;
  632. AutoInc := 0;
  633. end;
  634. // save field props
  635. WriteRecord(I, lFieldDescPtr);
  636. Inc(lFieldOffset, lFieldDef.Size);
  637. end;
  638. // end of header
  639. WriteChar($0D);
  640. // write memo bit
  641. if lHasBlob then
  642. begin
  643. if FDbfVersion = xBaseIII then
  644. PDbfHdr(Header)^.VerDBF := PDbfHdr(Header)^.VerDBF or $80
  645. else
  646. if FDbfVersion = xFoxPro then
  647. begin
  648. if PDbfHdr(Header)^.VerDBF = $02 then
  649. PDbfHdr(Header)^.VerDBF := $F5;
  650. end else
  651. PDbfHdr(Header)^.VerDBF := PDbfHdr(Header)^.VerDBF or $88;
  652. end;
  653. // update header
  654. PDbfHdr(Header)^.RecordSize := lFieldOffset;
  655. PDbfHdr(Header)^.FullHdrSize := HeaderSize + RecordSize * AFieldDefs.Count + 1;
  656. // add empty "back-link" info, whatever it is:
  657. { A 263-byte range that contains the backlink, which is the relative path of
  658. an associated database (.dbc) file, information. If the first byte is 0x00,
  659. the file is not associated with a database. Therefore, database files always
  660. contain 0x00. }
  661. if FDbfVersion = xFoxPro then
  662. Inc(PDbfHdr(Header)^.FullHdrSize, 263);
  663. // write dbf header to disk
  664. inherited WriteHeader;
  665. finally
  666. RecordSize := PDbfHdr(Header)^.RecordSize;
  667. HeaderSize := PDbfHdr(Header)^.FullHdrSize;
  668. // write full header to disk (dbf+fields)
  669. WriteHeader;
  670. end;
  671. if HasBlob and (FMemoFile=nil) then
  672. begin
  673. lMemoFileName := ChangeFileExt(FileName, GetMemoExt);
  674. if FDbfVersion = xFoxPro then
  675. FMemoFile := TFoxProMemoFile.Create(Self)
  676. else
  677. FMemoFile := TDbaseMemoFile.Create(Self);
  678. FMemoFile.FileName := lMemoFileName;
  679. FMemoFile.Mode := Mode;
  680. FMemoFile.AutoCreate := AutoCreate;
  681. FMemoFile.MemoRecordSize := MemoSize;
  682. FMemoFile.DbfVersion := FDbfVersion;
  683. FMemoFile.Open;
  684. end;
  685. end;
  686. function TDbfFile.HasBlob: Boolean;
  687. var
  688. I: Integer;
  689. begin
  690. Result := false;
  691. for I := 0 to FFieldDefs.Count-1 do
  692. if FFieldDefs.Items[I].IsBlob then
  693. Result := true;
  694. end;
  695. function TDbfFile.GetMemoExt: string;
  696. begin
  697. if FDbfVersion = xFoxPro then
  698. Result := '.fpt'
  699. else
  700. Result := '.dbt';
  701. end;
  702. procedure TDbfFile.Zap;
  703. begin
  704. // make recordcount zero
  705. RecordCount := 0;
  706. // update recordcount
  707. PDbfHdr(Header)^.RecordCount := RecordCount;
  708. // update disk header
  709. WriteHeader;
  710. // update indexes
  711. RegenerateIndexes;
  712. end;
  713. procedure TDbfFile.WriteHeader;
  714. var
  715. SystemTime: TSystemTime;
  716. lDataHdr: PDbfHdr;
  717. EofTerminator: Byte;
  718. begin
  719. if (HeaderSize=0) then
  720. exit;
  721. //FillHeader(0);
  722. lDataHdr := PDbfHdr(Header);
  723. GetLocalTime(SystemTime);
  724. lDataHdr^.Year := SystemTime.wYear - 1900;
  725. lDataHdr^.Month := SystemTime.wMonth;
  726. lDataHdr^.Day := SystemTime.wDay;
  727. // lDataHdr.RecordCount := RecordCount;
  728. inherited WriteHeader;
  729. EofTerminator := $1A;
  730. WriteBlock(@EofTerminator, 1, CalcPageOffset(RecordCount+1));
  731. end;
  732. procedure TDbfFile.ConstructFieldDefs;
  733. var
  734. {lColumnCount,}lHeaderSize,lFieldSize: Integer;
  735. lPropHdrOffset, lFieldOffset: Integer;
  736. lFieldDescIII: rFieldDescIII;
  737. lFieldDescVII: rFieldDescVII;
  738. lFieldPropsHdr: rFieldPropsHdr;
  739. lStdProp: rStdPropEntry;
  740. TempFieldDef: TDbfFieldDef;
  741. lSize,lPrec,I, lColumnCount: Integer;
  742. lAutoInc: Cardinal;
  743. dataPtr: PChar;
  744. lNativeFieldType: Char;
  745. lFieldName: string;
  746. lCanHoldNull: boolean;
  747. lCurrentNullPosition: integer;
  748. begin
  749. FFieldDefs.Clear;
  750. if DbfVersion >= xBaseVII then
  751. begin
  752. lHeaderSize := SizeOf(rAfterHdrVII) + SizeOf(rDbfHdr);
  753. lFieldSize := SizeOf(rFieldDescVII);
  754. end else begin
  755. lHeaderSize := SizeOf(rAfterHdrIII) + SizeOf(rDbfHdr);
  756. lFieldSize := SizeOf(rFieldDescIII);
  757. end;
  758. HeaderSize := lHeaderSize;
  759. RecordSize := lFieldSize;
  760. FLockField := nil;
  761. FNullField := nil;
  762. FAutoIncPresent := false;
  763. lColumnCount := (PDbfHdr(Header)^.FullHdrSize - lHeaderSize) div lFieldSize;
  764. lFieldOffset := 1;
  765. lAutoInc := 0;
  766. I := 1;
  767. lCurrentNullPosition := 0;
  768. lCanHoldNull := false;
  769. try
  770. // there has to be minimum of one field
  771. repeat
  772. // version field info?
  773. if FDbfVersion >= xBaseVII then
  774. begin
  775. ReadRecord(I, @lFieldDescVII);
  776. lFieldName := AnsiUpperCase(PChar(@lFieldDescVII.FieldName[0]));
  777. lSize := lFieldDescVII.FieldSize;
  778. lPrec := lFieldDescVII.FieldPrecision;
  779. lNativeFieldType := lFieldDescVII.FieldType;
  780. lAutoInc := SwapIntLE(lFieldDescVII.NextAutoInc);
  781. if lNativeFieldType = '+' then
  782. FAutoIncPresent := true;
  783. end else begin
  784. ReadRecord(I, @lFieldDescIII);
  785. lFieldName := AnsiUpperCase(PChar(@lFieldDescIII.FieldName[0]));
  786. lSize := lFieldDescIII.FieldSize;
  787. lPrec := lFieldDescIII.FieldPrecision;
  788. lNativeFieldType := lFieldDescIII.FieldType;
  789. lCanHoldNull := (FDbfVersion = xFoxPro) and
  790. ((lFieldDescIII.FoxProFlags and $2) <> 0) and
  791. (lFieldName <> '_NULLFLAGS');
  792. end;
  793. // apply field transformation tricks
  794. if (lNativeFieldType = 'C')
  795. {$ifndef USE_LONG_CHAR_FIELDS}
  796. and (FDbfVersion = xFoxPro)
  797. {$endif}
  798. then
  799. begin
  800. lSize := lSize + lPrec shl 8;
  801. lPrec := 0;
  802. end;
  803. // add field
  804. TempFieldDef := FFieldDefs.AddFieldDef;
  805. with TempFieldDef do
  806. begin
  807. FieldName := lFieldName;
  808. Offset := lFieldOffset;
  809. Size := lSize;
  810. Precision := lPrec;
  811. AutoInc := lAutoInc;
  812. NativeFieldType := lNativeFieldType;
  813. if lCanHoldNull then
  814. begin
  815. NullPosition := lCurrentNullPosition;
  816. inc(lCurrentNullPosition);
  817. end else
  818. NullPosition := -1;
  819. end;
  820. // check valid field:
  821. // 1) non-empty field name
  822. // 2) known field type
  823. // {3) no changes have to be made to precision or size}
  824. if (Length(lFieldName) = 0) or (TempFieldDef.FieldType = ftUnknown) then
  825. raise EDbfError.Create(STRING_INVALID_DBF_FILE);
  826. // determine if lock field present, if present, then store additional info
  827. if lFieldName = '_DBASELOCK' then
  828. begin
  829. FLockField := TempFieldDef;
  830. FLockUserLen := lSize - 8;
  831. if FLockUserLen > DbfGlobals.UserNameLen then
  832. FLockUserLen := DbfGlobals.UserNameLen;
  833. end else
  834. if UpperCase(lFieldName) = '_NULLFLAGS' then
  835. FNullField := TempFieldDef;
  836. // goto next field
  837. Inc(lFieldOffset, lSize);
  838. Inc(I);
  839. // continue until header termination character found
  840. // or end of header reached
  841. until (I > lColumnCount) or (ReadChar = $0D);
  842. // test if not too many fields
  843. if FFieldDefs.Count >= 4096 then
  844. raise EDbfError.CreateFmt(STRING_INVALID_FIELD_COUNT, [FFieldDefs.Count]);
  845. // do not check FieldOffset = PDbfHdr(Header).RecordSize because additional
  846. // data could be present in record
  847. // get current position
  848. lPropHdrOffset := Stream.Position;
  849. // dBase 7 -> read field properties, test if enough space, maybe no header
  850. if (FDbfVersion = xBaseVII) and (lPropHdrOffset + Sizeof(lFieldPropsHdr) <
  851. PDbfHdr(Header)^.FullHdrSize) then
  852. begin
  853. // read in field properties header
  854. ReadBlock(@lFieldPropsHdr, SizeOf(lFieldPropsHdr), lPropHdrOffset);
  855. // read in standard properties
  856. lFieldOffset := lPropHdrOffset + lFieldPropsHdr.StartStdProps;
  857. for I := 0 to lFieldPropsHdr.NumStdProps - 1 do
  858. begin
  859. // read property data
  860. ReadBlock(@lStdProp, SizeOf(lStdProp), lFieldOffset+I*SizeOf(lStdProp));
  861. // is this a constraint?
  862. if lStdProp.FieldOffset = 0 then
  863. begin
  864. // this is a constraint...not implemented
  865. end else if lStdProp.FieldOffset <= FFieldDefs.Count then begin
  866. // get fielddef for this property
  867. TempFieldDef := FFieldDefs.Items[lStdProp.FieldOffset-1];
  868. // allocate space to store data
  869. TempFieldDef.AllocBuffers;
  870. // dataPtr = nil -> no data to retrieve
  871. dataPtr := nil;
  872. // store data
  873. case lStdProp.PropType of
  874. FieldPropType_Required: TempFieldDef.Required := true;
  875. FieldPropType_Default:
  876. begin
  877. dataPtr := TempFieldDef.DefaultBuf;
  878. TempFieldDef.HasDefault := true;
  879. end;
  880. FieldPropType_Min:
  881. begin
  882. dataPtr := TempFieldDef.MinBuf;
  883. TempFieldDef.HasMin := true;
  884. end;
  885. FieldPropType_Max:
  886. begin
  887. dataPtr := TempFieldDef.MaxBuf;
  888. TempFieldDef.HasMax := true;
  889. end;
  890. end;
  891. // get data for this property
  892. if dataPtr <> nil then
  893. ReadBlock(dataPtr, lStdProp.DataSize, lPropHdrOffset + lStdProp.DataOffset);
  894. end;
  895. end;
  896. // read custom properties...not implemented
  897. // read RI properties...not implemented
  898. end;
  899. finally
  900. HeaderSize := PDbfHdr(Header)^.FullHdrSize;
  901. RecordSize := PDbfHdr(Header)^.RecordSize;
  902. end;
  903. end;
  904. function TDbfFile.GetLanguageId: Integer;
  905. begin
  906. Result := PDbfHdr(Header)^.Language;
  907. end;
  908. function TDbfFile.GetLanguageStr: String;
  909. begin
  910. if FDbfVersion >= xBaseVII then
  911. Result := PAfterHdrVII(PChar(Header) + SizeOf(rDbfHdr))^.LanguageDriverName;
  912. end;
  913. {
  914. I fill the holes with the last records.
  915. now we can do an 'in-place' pack
  916. }
  917. procedure TDbfFile.FastPackTable;
  918. var
  919. iDel,iNormal: Integer;
  920. pDel,pNormal: PChar;
  921. function FindFirstDel: Boolean;
  922. begin
  923. while iDel<=iNormal do
  924. begin
  925. ReadRecord(iDel, pDel);
  926. if (PChar(pDel)^ <> ' ') then
  927. begin
  928. Result := true;
  929. exit;
  930. end;
  931. Inc(iDel);
  932. end;
  933. Result := false;
  934. end;
  935. function FindLastNormal: Boolean;
  936. begin
  937. while iNormal>=iDel do
  938. begin
  939. ReadRecord(iNormal, pNormal);
  940. if (PChar(pNormal)^= ' ') then
  941. begin
  942. Result := true;
  943. exit;
  944. end;
  945. dec(iNormal);
  946. end;
  947. Result := false;
  948. end;
  949. begin
  950. if RecordSize < 1 then Exit;
  951. GetMem(pNormal, RecordSize);
  952. GetMem(pDel, RecordSize);
  953. try
  954. iDel := 1;
  955. iNormal := RecordCount;
  956. while FindFirstDel do
  957. begin
  958. // iDel is definitely deleted
  959. if FindLastNormal then
  960. begin
  961. // but is not anymore
  962. WriteRecord(iDel, pNormal);
  963. PChar(pNormal)^ := '*';
  964. WriteRecord(iNormal, pNormal);
  965. end else begin
  966. // Cannot found a record after iDel so iDel must be deleted
  967. dec(iDel);
  968. break;
  969. end;
  970. end;
  971. // FindFirstDel failed means than iDel is full
  972. RecordCount := iDel;
  973. RegenerateIndexes;
  974. // Pack Memofields
  975. finally
  976. FreeMem(pNormal);
  977. FreeMem(pDel);
  978. end;
  979. end;
  980. procedure TDbfFile.Rename(DestFileName: string; NewIndexFileNames: TStrings; DeleteFiles: boolean);
  981. var
  982. lIndexFileNames: TStrings;
  983. lIndexFile: TIndexFile;
  984. NewBaseName: string;
  985. I: integer;
  986. begin
  987. // get memory for index file list
  988. lIndexFileNames := TStringList.Create;
  989. try
  990. // save index filenames
  991. for I := 0 to FIndexFiles.Count - 1 do
  992. begin
  993. lIndexFile := TIndexFile(IndexFiles[I]);
  994. lIndexFileNames.Add(lIndexFile.FileName);
  995. // prepare changing the dbf file name, needs changes in index files
  996. lIndexFile.PrepareRename(NewIndexFileNames[I]);
  997. end;
  998. // close file
  999. Close;
  1000. if DeleteFiles then
  1001. begin
  1002. SysUtils.DeleteFile(DestFileName);
  1003. SysUtils.DeleteFile(ChangeFileExt(DestFileName, GetMemoExt));
  1004. end else begin
  1005. I := 0;
  1006. FindNextName(DestFileName, NewBaseName, I);
  1007. SysUtils.RenameFile(DestFileName, NewBaseName);
  1008. SysUtils.RenameFile(ChangeFileExt(DestFileName, GetMemoExt),
  1009. ChangeFileExt(NewBaseName, GetMemoExt));
  1010. end;
  1011. // delete old index files
  1012. for I := 0 to NewIndexFileNames.Count - 1 do
  1013. SysUtils.DeleteFile(NewIndexFileNames.Strings[I]);
  1014. // rename the new dbf files
  1015. SysUtils.RenameFile(FileName, DestFileName);
  1016. SysUtils.RenameFile(ChangeFileExt(FileName, GetMemoExt),
  1017. ChangeFileExt(DestFileName, GetMemoExt));
  1018. // rename new index files
  1019. for I := 0 to NewIndexFileNames.Count - 1 do
  1020. SysUtils.RenameFile(lIndexFileNames.Strings[I], NewIndexFileNames.Strings[I]);
  1021. finally
  1022. lIndexFileNames.Free;
  1023. end;
  1024. end;
  1025. type
  1026. TRestructFieldInfo = record
  1027. SourceOffset: Integer;
  1028. DestOffset: Integer;
  1029. Size: Integer;
  1030. end;
  1031. { assume nobody has more than 8192 fields, otherwise possibly range check error }
  1032. PRestructFieldInfo = ^TRestructFieldInfoArray;
  1033. TRestructFieldInfoArray = array[0..8191] of TRestructFieldInfo;
  1034. procedure TDbfFile.RestructureTable(DbfFieldDefs: TDbfFieldDefs; Pack: Boolean);
  1035. var
  1036. DestDbfFile: TDbfFile;
  1037. TempIndexDef: TDbfIndexDef;
  1038. TempIndexFile: TIndexFile;
  1039. DestFieldDefs: TDbfFieldDefs;
  1040. TempDstDef, TempSrcDef: TDbfFieldDef;
  1041. OldIndexFiles: TStrings;
  1042. IndexName, NewBaseName: string;
  1043. I, lRecNo, lFieldNo, lFieldSize, lBlobPageNo, lWRecNo, srcOffset, dstOffset: Integer;
  1044. pBuff, pDestBuff: PChar;
  1045. RestructFieldInfo: PRestructFieldInfo;
  1046. BlobStream: TMemoryStream;
  1047. begin
  1048. // nothing to do?
  1049. if (RecordSize < 1) or ((DbfFieldDefs = nil) and not Pack) then
  1050. exit;
  1051. // if no exclusive access, terrible things can happen!
  1052. CheckExclusiveAccess;
  1053. // make up some temporary filenames
  1054. lRecNo := 0;
  1055. FindNextName(FileName, NewBaseName, lRecNo);
  1056. // select final field definition list
  1057. if DbfFieldDefs = nil then
  1058. begin
  1059. DestFieldDefs := FFieldDefs;
  1060. end else begin
  1061. DestFieldDefs := DbfFieldDefs;
  1062. // copy autoinc values
  1063. for I := 0 to DbfFieldDefs.Count - 1 do
  1064. begin
  1065. lFieldNo := DbfFieldDefs.Items[I].CopyFrom;
  1066. if (lFieldNo >= 0) and (lFieldNo < FFieldDefs.Count) then
  1067. DbfFieldDefs.Items[I].AutoInc := FFieldDefs.Items[lFieldNo].AutoInc;
  1068. end;
  1069. end;
  1070. // create temporary dbf
  1071. DestDbfFile := TDbfFile.Create;
  1072. DestDbfFile.FileName := NewBaseName;
  1073. DestDbfFile.AutoCreate := true;
  1074. DestDbfFile.Mode := pfExclusiveCreate;
  1075. DestDbfFile.OnIndexMissing := FOnIndexMissing;
  1076. DestDbfFile.OnLocaleError := FOnLocaleError;
  1077. DestDbfFile.DbfVersion := FDbfVersion;
  1078. DestDbfFile.FileLangId := FileLangId;
  1079. DestDbfFile.Open;
  1080. // create dbf header
  1081. if FMemoFile <> nil then
  1082. DestDbfFile.FinishCreate(DestFieldDefs, FMemoFile.RecordSize)
  1083. else
  1084. DestDbfFile.FinishCreate(DestFieldDefs, 512);
  1085. // adjust size and offsets of fields
  1086. GetMem(RestructFieldInfo, sizeof(TRestructFieldInfo)*DestFieldDefs.Count);
  1087. for lFieldNo := 0 to DestFieldDefs.Count - 1 do
  1088. begin
  1089. TempDstDef := DestFieldDefs.Items[lFieldNo];
  1090. if TempDstDef.CopyFrom >= 0 then
  1091. begin
  1092. TempSrcDef := FFieldDefs.Items[TempDstDef.CopyFrom];
  1093. if TempDstDef.NativeFieldType in ['F', 'N'] then
  1094. begin
  1095. // get minimum field length
  1096. lFieldSize := Min(TempSrcDef.Precision, TempDstDef.Precision) +
  1097. Min(TempSrcDef.Size - TempSrcDef.Precision,
  1098. TempDstDef.Size - TempDstDef.Precision);
  1099. // if one has dec separator, but other not, we lose one digit
  1100. if (TempDstDef.Precision > 0) xor
  1101. ((TempSrcDef.NativeFieldType in ['F', 'N']) and (TempSrcDef.Precision > 0)) then
  1102. Dec(lFieldSize);
  1103. // should not happen, but check nevertheless (maybe corrupt data)
  1104. if lFieldSize < 0 then
  1105. lFieldSize := 0;
  1106. srcOffset := TempSrcDef.Size - TempSrcDef.Precision -
  1107. (TempDstDef.Size - TempDstDef.Precision);
  1108. if srcOffset < 0 then
  1109. begin
  1110. dstOffset := -srcOffset;
  1111. srcOffset := 0;
  1112. end else begin
  1113. dstOffset := 0;
  1114. end;
  1115. end else begin
  1116. lFieldSize := Min(TempSrcDef.Size, TempDstDef.Size);
  1117. srcOffset := 0;
  1118. dstOffset := 0;
  1119. end;
  1120. with RestructFieldInfo[lFieldNo] do
  1121. begin
  1122. Size := lFieldSize;
  1123. SourceOffset := TempSrcDef.Offset + srcOffset;
  1124. DestOffset := TempDstDef.Offset + dstOffset;
  1125. end;
  1126. end;
  1127. end;
  1128. // add indexes
  1129. TempIndexDef := TDbfIndexDef.Create(nil);
  1130. for I := 0 to FIndexNames.Count - 1 do
  1131. begin
  1132. // get length of extension -> determines MDX or NDX
  1133. IndexName := FIndexNames.Strings[I];
  1134. TempIndexFile := TIndexFile(FIndexNames.Objects[I]);
  1135. TempIndexFile.GetIndexInfo(IndexName, TempIndexDef);
  1136. if Length(ExtractFileExt(IndexName)) > 0 then
  1137. begin
  1138. // NDX index, get unique file name
  1139. lRecNo := 0;
  1140. FindNextName(IndexName, IndexName, lRecNo);
  1141. end;
  1142. // add this index
  1143. DestDbfFile.OpenIndex(IndexName, TempIndexDef.SortField, true, TempIndexDef.Options);
  1144. end;
  1145. TempIndexDef.Free;
  1146. // get memory for record buffers
  1147. GetMem(pBuff, RecordSize);
  1148. BlobStream := TMemoryStream.Create;
  1149. OldIndexFiles := TStringList.Create;
  1150. // if restructure, we need memory for dest buffer, otherwise use source
  1151. if DbfFieldDefs = nil then
  1152. pDestBuff := pBuff
  1153. else
  1154. GetMem(pDestBuff, DestDbfFile.RecordSize);
  1155. // let the games begin!
  1156. try
  1157. {$ifdef USE_CACHE}
  1158. BufferAhead := true;
  1159. DestDbfFile.BufferAhead := true;
  1160. {$endif}
  1161. lWRecNo := 1;
  1162. for lRecNo := 1 to RecordCount do
  1163. begin
  1164. // read record from original dbf
  1165. ReadRecord(lRecNo, pBuff);
  1166. // copy record?
  1167. if (pBuff^ <> '*') or not Pack then
  1168. begin
  1169. // if restructure, initialize dest
  1170. if DbfFieldDefs <> nil then
  1171. begin
  1172. DestDbfFile.InitRecord(pDestBuff);
  1173. // copy deleted mark (the first byte)
  1174. pDestBuff^ := pBuff^;
  1175. end;
  1176. if (DbfFieldDefs <> nil) or (FMemoFile <> nil) then
  1177. begin
  1178. // copy fields
  1179. for lFieldNo := 0 to DestFieldDefs.Count-1 do
  1180. begin
  1181. TempDstDef := DestFieldDefs.Items[lFieldNo];
  1182. // handle blob fields differently
  1183. // don't try to copy new blob fields!
  1184. // DbfFieldDefs = nil -> pack only
  1185. // TempDstDef.CopyFrom >= 0 -> copy existing (blob) field
  1186. if TempDstDef.IsBlob and ((DbfFieldDefs = nil) or (TempDstDef.CopyFrom >= 0)) then
  1187. begin
  1188. // get current blob blockno
  1189. GetFieldData(lFieldNo, ftInteger, pBuff, @lBlobPageNo, false);
  1190. // valid blockno read?
  1191. if lBlobPageNo > 0 then
  1192. begin
  1193. BlobStream.Clear;
  1194. FMemoFile.ReadMemo(lBlobPageNo, BlobStream);
  1195. BlobStream.Position := 0;
  1196. // always append
  1197. DestDbfFile.FMemoFile.WriteMemo(lBlobPageNo, 0, BlobStream);
  1198. end;
  1199. // write new blockno
  1200. DestDbfFile.SetFieldData(lFieldNo, ftInteger, @lBlobPageNo, pDestBuff, false);
  1201. end else if (DbfFieldDefs <> nil) and (TempDstDef.CopyFrom >= 0) then
  1202. begin
  1203. // copy content of field
  1204. with RestructFieldInfo[lFieldNo] do
  1205. Move(pBuff[SourceOffset], pDestBuff[DestOffset], Size);
  1206. end;
  1207. end;
  1208. end;
  1209. // write record
  1210. DestDbfFile.WriteRecord(lWRecNo, pDestBuff);
  1211. // update indexes
  1212. for I := 0 to DestDbfFile.IndexFiles.Count - 1 do
  1213. TIndexFile(DestDbfFile.IndexFiles.Items[I]).Insert(lWRecNo, pDestBuff);
  1214. // go to next record
  1215. Inc(lWRecNo);
  1216. end;
  1217. end;
  1218. {$ifdef USE_CACHE}
  1219. BufferAhead := false;
  1220. DestDbfFile.BufferAhead := false;
  1221. {$endif}
  1222. // save index filenames
  1223. for I := 0 to FIndexFiles.Count - 1 do
  1224. OldIndexFiles.Add(TIndexFile(IndexFiles[I]).FileName);
  1225. // close dbf
  1226. Close;
  1227. // if restructure -> rename the old dbf files
  1228. // if pack only -> delete the old dbf files
  1229. DestDbfFile.Rename(FileName, OldIndexFiles, DbfFieldDefs = nil);
  1230. // we have to reinit fielddefs if restructured
  1231. Open;
  1232. // crop deleted records
  1233. RecordCount := lWRecNo - 1;
  1234. // update date/time stamp, recordcount
  1235. PDbfHdr(Header)^.RecordCount := RecordCount;
  1236. WriteHeader;
  1237. finally
  1238. // close temporary file
  1239. FreeAndNil(DestDbfFile);
  1240. // free mem
  1241. FreeAndNil(OldIndexFiles);
  1242. FreeMem(pBuff);
  1243. FreeAndNil(BlobStream);
  1244. FreeMem(RestructFieldInfo);
  1245. if DbfFieldDefs <> nil then
  1246. FreeMem(pDestBuff);
  1247. end;
  1248. end;
  1249. procedure TDbfFile.RegenerateIndexes;
  1250. var
  1251. lIndexNo: Integer;
  1252. begin
  1253. // recreate every index in every file
  1254. for lIndexNo := 0 to FIndexFiles.Count-1 do
  1255. begin
  1256. PackIndex(TIndexFile(FIndexFiles.Items[lIndexNo]), EmptyStr);
  1257. end;
  1258. end;
  1259. function TDbfFile.GetFieldInfo(FieldName: string): TDbfFieldDef;
  1260. var
  1261. I: Integer;
  1262. lfi: TDbfFieldDef;
  1263. begin
  1264. FieldName := AnsiUpperCase(FieldName);
  1265. for I := 0 to FFieldDefs.Count-1 do
  1266. begin
  1267. lfi := TDbfFieldDef(FFieldDefs.Items[I]);
  1268. if lfi.fieldName = FieldName then
  1269. begin
  1270. Result := lfi;
  1271. exit;
  1272. end;
  1273. end;
  1274. Result := nil;
  1275. end;
  1276. // NOTE: Dst may be nil!
  1277. function TDbfFile.GetFieldData(Column: Integer; DataType: TFieldType;
  1278. Src, Dst: Pointer; NativeFormat: boolean): Boolean;
  1279. var
  1280. TempFieldDef: TDbfFieldDef;
  1281. begin
  1282. TempFieldDef := TDbfFieldDef(FFieldDefs.Items[Column]);
  1283. Result := GetFieldDataFromDef(TempFieldDef, DataType, Src, Dst, NativeFormat);
  1284. end;
  1285. // NOTE: Dst may be nil!
  1286. function TDbfFile.GetFieldDataFromDef(AFieldDef: TDbfFieldDef; DataType: TFieldType;
  1287. Src, Dst: Pointer; NativeFormat: boolean): Boolean;
  1288. var
  1289. FieldOffset, FieldSize: Integer;
  1290. // s: string;
  1291. ldd, ldm, ldy, lth, ltm, lts: Integer;
  1292. date: TDateTime;
  1293. timeStamp: TTimeStamp;
  1294. asciiContents: boolean;
  1295. {$ifdef SUPPORT_INT64}
  1296. function GetInt64FromStrLength(Src: Pointer; Size: Integer; Default: Int64): Int64;
  1297. var
  1298. endChar: Char;
  1299. Code: Integer;
  1300. begin
  1301. // save Char at pos term. null
  1302. endChar := (PChar(Src) + Size)^;
  1303. (PChar(Src) + Size)^ := #0;
  1304. // convert
  1305. Val(PChar(Src), Result, Code);
  1306. // check success
  1307. if Code <> 0 then Result := Default;
  1308. // restore prev. ending Char
  1309. (PChar(Src) + Size)^ := endChar;
  1310. end;
  1311. {$endif}
  1312. procedure CorrectYear(var wYear: Integer);
  1313. var wD, wM, wY, CenturyBase: Word;
  1314. {$ifndef DELPHI_5}
  1315. // Delphi 3 standard-behavior no change possible
  1316. const TwoDigitYearCenturyWindow= 0;
  1317. {$endif}
  1318. begin
  1319. if wYear >= 100 then
  1320. Exit;
  1321. DecodeDate(Date, wY, wm, wD);
  1322. // use Delphi-Date-Window
  1323. CenturyBase := wY{must be CurrentYear} - TwoDigitYearCenturyWindow;
  1324. Inc(wYear, CenturyBase div 100 * 100);
  1325. if (TwoDigitYearCenturyWindow > 0) and (wYear < CenturyBase) then
  1326. Inc(wYear, 100);
  1327. end;
  1328. procedure SaveDateToDst;
  1329. begin
  1330. if not NativeFormat then
  1331. begin
  1332. // Delphi 5 requests a TDateTime
  1333. PDateTime(Dst)^ := date;
  1334. end else begin
  1335. // Delphi 3 and 4 request a TDateTimeRec
  1336. // date is TTimeStamp.date
  1337. // datetime = msecs == BDE timestamp as we implemented it
  1338. if DataType = ftDateTime then
  1339. begin
  1340. PDateTimeRec(Dst)^.DateTime := date;
  1341. end else begin
  1342. PLongInt(Dst)^ := DateTimeToTimeStamp(date).Date;
  1343. end;
  1344. end;
  1345. end;
  1346. begin
  1347. // test if non-nil source (record buffer)
  1348. if Src = nil then
  1349. begin
  1350. Result := false;
  1351. exit;
  1352. end;
  1353. // check Dst = nil, called with dst = nil to check empty field
  1354. if (FNullField <> nil) and (Dst = nil) and (AFieldDef.NullPosition >= 0) then
  1355. begin
  1356. // go to byte with null flag of this field
  1357. Src := PChar(Src) + FNullField.Offset + (AFieldDef.NullPosition shr 3);
  1358. Result := (PByte(Src)^ and (1 shl (AFieldDef.NullPosition and $7))) <> 0;
  1359. exit;
  1360. end;
  1361. FieldOffset := AFieldDef.Offset;
  1362. FieldSize := AFieldDef.Size;
  1363. Src := PChar(Src) + FieldOffset;
  1364. asciiContents := false;
  1365. Result := true;
  1366. // field types that are binary and of which the fieldsize should not be truncated
  1367. case AFieldDef.NativeFieldType of
  1368. '+', 'I':
  1369. begin
  1370. if FDbfVersion <> xFoxPro then
  1371. begin
  1372. Result := PDWord(Src)^ <> 0;
  1373. if Result and (Dst <> nil) then
  1374. begin
  1375. PDWord(Dst)^ := SwapIntBE(PDWord(Src)^);
  1376. if Result then
  1377. PInteger(Dst)^ := Integer(PDWord(Dst)^ xor $80000000);
  1378. end;
  1379. end else begin
  1380. Result := true;
  1381. if Dst <> nil then
  1382. PInteger(Dst)^ := SwapIntLE(PInteger(Src)^);
  1383. end;
  1384. end;
  1385. 'O':
  1386. begin
  1387. {$ifdef SUPPORT_INT64}
  1388. Result := PInt64(Src)^ <> 0;
  1389. if Result and (Dst <> nil) then
  1390. begin
  1391. SwapInt64BE(Src, Dst);
  1392. if PInt64(Dst)^ > 0 then
  1393. PInt64(Dst)^ := not PInt64(Dst)^
  1394. else
  1395. PDouble(Dst)^ := PDouble(Dst)^ * -1;
  1396. end;
  1397. {$endif}
  1398. end;
  1399. '@':
  1400. begin
  1401. Result := (PInteger(Src)^ <> 0) and (PInteger(PChar(Src)+4)^ <> 0);
  1402. if Result and (Dst <> nil) then
  1403. begin
  1404. SwapInt64BE(Src, Dst);
  1405. if FDateTimeHandling = dtBDETimeStamp then
  1406. date := BDETimeStampToDateTime(PDouble(Dst)^)
  1407. else
  1408. date := PDateTime(Dst)^;
  1409. SaveDateToDst;
  1410. end;
  1411. end;
  1412. 'T':
  1413. begin
  1414. // all binary zeroes -> empty datetime
  1415. {$ifdef SUPPORT_INT64}
  1416. Result := PInt64(Src)^ <> 0;
  1417. {$else}
  1418. Result := (PInteger(Src)^ <> 0) or (PInteger(PChar(Src)+4)^ <> 0);
  1419. {$endif}
  1420. if Result and (Dst <> nil) then
  1421. begin
  1422. timeStamp.Date := SwapIntLE(PInteger(Src)^) - JulianDateDelta;
  1423. timeStamp.Time := SwapIntLE(PInteger(PChar(Src)+4)^);
  1424. date := TimeStampToDateTime(timeStamp);
  1425. SaveDateToDst;
  1426. end;
  1427. end;
  1428. 'Y':
  1429. begin
  1430. {$ifdef SUPPORT_INT64}
  1431. Result := true;
  1432. if Dst <> nil then
  1433. begin
  1434. PInt64(Dst)^ := SwapIntLE(PInt64(Src)^);
  1435. if DataType = ftCurrency then
  1436. PDouble(Dst)^ := PInt64(Dst)^ / 10000.0;
  1437. end;
  1438. {$endif}
  1439. end;
  1440. 'B': // foxpro double
  1441. begin
  1442. if FDbfVersion = xFoxPro then
  1443. begin
  1444. Result := true;
  1445. if Dst <> nil then
  1446. PInt64(Dst)^ := SwapIntLE(PInt64(Src)^);
  1447. end else
  1448. asciiContents := true;
  1449. end;
  1450. 'M':
  1451. begin
  1452. if FieldSize = 4 then
  1453. begin
  1454. Result := PInteger(Src)^ <> 0;
  1455. if Dst <> nil then
  1456. PInteger(Dst)^ := SwapIntLE(PInteger(Src)^);
  1457. end else
  1458. asciiContents := true;
  1459. end;
  1460. else
  1461. asciiContents := true;
  1462. end;
  1463. if asciiContents then
  1464. begin
  1465. // SetString(s, PChar(Src) + FieldOffset, FieldSize );
  1466. // s := {TrimStr(s)} TrimRight(s);
  1467. // truncate spaces at end by shortening fieldsize
  1468. while (FieldSize > 0) and ((PChar(Src) + FieldSize - 1)^ = ' ') do
  1469. dec(FieldSize);
  1470. // if not string field, truncate spaces at beginning too
  1471. if DataType <> ftString then
  1472. while (FieldSize > 0) and (PChar(Src)^ = ' ') do
  1473. begin
  1474. inc(PChar(Src));
  1475. dec(FieldSize);
  1476. end;
  1477. // return if field is empty
  1478. Result := FieldSize > 0;
  1479. if Result and (Dst <> nil) then // data not needed if Result= false or Dst=nil
  1480. case DataType of
  1481. ftBoolean:
  1482. begin
  1483. // in DBase- FileDescription lowercase t is allowed too
  1484. // with asking for Result= true s must be longer then 0
  1485. // else it happens an AV, maybe field is NULL
  1486. if (PChar(Src)^ = 'T') or (PChar(Src)^ = 't') then
  1487. PWord(Dst)^ := 1
  1488. else
  1489. PWord(Dst)^ := 0;
  1490. end;
  1491. ftSmallInt:
  1492. PSmallInt(Dst)^ := GetIntFromStrLength(Src, FieldSize, 0);
  1493. {$ifdef SUPPORT_INT64}
  1494. ftLargeInt:
  1495. PLargeInt(Dst)^ := GetInt64FromStrLength(Src, FieldSize, 0);
  1496. {$endif}
  1497. ftInteger:
  1498. PInteger(Dst)^ := GetIntFromStrLength(Src, FieldSize, 0);
  1499. ftFloat, ftCurrency:
  1500. PDouble(Dst)^ := DbfStrToFloat(Src, FieldSize);
  1501. ftDate, ftDateTime:
  1502. begin
  1503. // get year, month, day
  1504. ldy := GetIntFromStrLength(PChar(Src) + 0, 4, 1);
  1505. ldm := GetIntFromStrLength(PChar(Src) + 4, 2, 1);
  1506. ldd := GetIntFromStrLength(PChar(Src) + 6, 2, 1);
  1507. //if (ly<1900) or (ly>2100) then ly := 1900;
  1508. //Year from 0001 to 9999 is possible
  1509. //everyting else is an error, an empty string too
  1510. //Do DateCorrection with Delphis possibillities for one or two digits
  1511. if (ldy < 100) and (PChar(Src)[0] = #32) and (PChar(Src)[1] = #32) then
  1512. CorrectYear(ldy);
  1513. try
  1514. date := EncodeDate(ldy, ldm, ldd);
  1515. except
  1516. date := 0;
  1517. end;
  1518. // time stored too?
  1519. if (AFieldDef.FieldType = ftDateTime) and (DataType = ftDateTime) then
  1520. begin
  1521. // get hour, minute, second
  1522. lth := GetIntFromStrLength(PChar(Src) + 8, 2, 1);
  1523. ltm := GetIntFromStrLength(PChar(Src) + 10, 2, 1);
  1524. lts := GetIntFromStrLength(PChar(Src) + 12, 2, 1);
  1525. // encode
  1526. try
  1527. date := date + EncodeTime(lth, ltm, lts, 0);
  1528. except
  1529. date := 0;
  1530. end;
  1531. end;
  1532. SaveDateToDst;
  1533. end;
  1534. ftString:
  1535. StrLCopy(Dst, Src, FieldSize);
  1536. end else begin
  1537. case DataType of
  1538. ftString:
  1539. if Dst <> nil then
  1540. PChar(Dst)[0] := #0;
  1541. end;
  1542. end;
  1543. end;
  1544. end;
  1545. procedure TDbfFile.UpdateNullField(Buffer: Pointer; AFieldDef: TDbfFieldDef;
  1546. Action: TUpdateNullField);
  1547. var
  1548. NullDst: pbyte;
  1549. Mask: byte;
  1550. begin
  1551. // this field has null setting capability
  1552. NullDst := PByte(PChar(Buffer) + FNullField.Offset + (AFieldDef.NullPosition shr 3));
  1553. Mask := 1 shl (AFieldDef.NullPosition and $7);
  1554. if Action = unSet then
  1555. begin
  1556. // clear the field, set null flag
  1557. NullDst^ := NullDst^ or Mask;
  1558. end else begin
  1559. // set field data, clear null flag
  1560. NullDst^ := NullDst^ and not Mask;
  1561. end;
  1562. end;
  1563. procedure TDbfFile.SetFieldData(Column: Integer; DataType: TFieldType;
  1564. Src, Dst: Pointer; NativeFormat: boolean);
  1565. const
  1566. IsBlobFieldToPadChar: array[Boolean] of Char = (#32, '0');
  1567. SrcNilToUpdateNullField: array[boolean] of TUpdateNullField = (unClear, unSet);
  1568. var
  1569. FieldSize,FieldPrec: Integer;
  1570. TempFieldDef: TDbfFieldDef;
  1571. Len: Integer;
  1572. IntValue: dword;
  1573. year, month, day: Word;
  1574. hour, minute, sec, msec: Word;
  1575. date: TDateTime;
  1576. timeStamp: TTimeStamp;
  1577. asciiContents: boolean;
  1578. procedure LoadDateFromSrc;
  1579. begin
  1580. if not NativeFormat then
  1581. begin
  1582. // Delphi 5, new format, passes a TDateTime
  1583. date := PDateTime(Src)^;
  1584. end else begin
  1585. // Delphi 3 and 4, old "native" format, pass a TDateTimeRec with a time stamp
  1586. // date = integer
  1587. // datetime = msecs == BDETimeStampToDateTime as we implemented it
  1588. if DataType = ftDateTime then
  1589. begin
  1590. date := PDouble(Src)^;
  1591. end else begin
  1592. timeStamp.Time := 0;
  1593. timeStamp.Date := PLongInt(Src)^;
  1594. date := TimeStampToDateTime(timeStamp);
  1595. end;
  1596. end;
  1597. end;
  1598. begin
  1599. TempFieldDef := TDbfFieldDef(FFieldDefs.Items[Column]);
  1600. FieldSize := TempFieldDef.Size;
  1601. FieldPrec := TempFieldDef.Precision;
  1602. // if src = nil then write empty field
  1603. // symmetry with above
  1604. // foxpro has special _nullfield for flagging fields as `null'
  1605. if (FNullField <> nil) and (TempFieldDef.NullPosition >= 0) then
  1606. UpdateNullField(Dst, TempFieldDef, SrcNilToUpdateNullField[Src = nil]);
  1607. // copy field data to record buffer
  1608. Dst := PChar(Dst) + TempFieldDef.Offset;
  1609. asciiContents := false;
  1610. case TempFieldDef.NativeFieldType of
  1611. '+', 'I':
  1612. begin
  1613. if FDbfVersion <> xFoxPro then
  1614. begin
  1615. if Src = nil then
  1616. IntValue := 0
  1617. else
  1618. IntValue := PDWord(Src)^ xor $80000000;
  1619. PDWord(Dst)^ := SwapIntBE(IntValue);
  1620. end else begin
  1621. if Src = nil then
  1622. PDWord(Dst)^ := 0
  1623. else
  1624. PDWord(Dst)^ := SwapIntLE(PDWord(Src)^);
  1625. end;
  1626. end;
  1627. 'O':
  1628. begin
  1629. {$ifdef SUPPORT_INT64}
  1630. if Src = nil then
  1631. begin
  1632. PInt64(Dst)^ := 0;
  1633. end else begin
  1634. if PDouble(Src)^ < 0 then
  1635. PInt64(Dst)^ := not PInt64(Src)^
  1636. else
  1637. PDouble(Dst)^ := (PDouble(Src)^) * -1;
  1638. SwapInt64BE(Dst, Dst);
  1639. end;
  1640. {$endif}
  1641. end;
  1642. '@':
  1643. begin
  1644. if Src = nil then
  1645. begin
  1646. {$ifdef SUPPORT_INT64}
  1647. PInt64(Dst)^ := 0;
  1648. {$else}
  1649. PInteger(Dst)^ := 0;
  1650. PInteger(PChar(Dst)+4)^ := 0;
  1651. {$endif}
  1652. end else begin
  1653. LoadDateFromSrc;
  1654. if FDateTimeHandling = dtBDETimeStamp then
  1655. date := DateTimeToBDETimeStamp(date);
  1656. SwapInt64BE(@date, Dst);
  1657. end;
  1658. end;
  1659. 'T':
  1660. begin
  1661. // all binary zeroes -> empty datetime
  1662. if Src = nil then
  1663. begin
  1664. {$ifdef SUPPORT_INT64}
  1665. PInt64(Dst)^ := 0;
  1666. {$else}
  1667. PInteger(Dst)^ := 0;
  1668. PInteger(PChar(Dst)+4)^ := 0;
  1669. {$endif}
  1670. end else begin
  1671. LoadDateFromSrc;
  1672. timeStamp := DateTimeToTimeStamp(date);
  1673. PInteger(Dst)^ := SwapIntLE(timeStamp.Date + JulianDateDelta);
  1674. PInteger(PChar(Dst)+4)^ := SwapIntLE(timeStamp.Time);
  1675. end;
  1676. end;
  1677. 'Y':
  1678. begin
  1679. {$ifdef SUPPORT_INT64}
  1680. if Src = nil then
  1681. begin
  1682. PInt64(Dst)^ := 0;
  1683. end else begin
  1684. case DataType of
  1685. ftCurrency:
  1686. PInt64(Dst)^ := Trunc(PDouble(Src)^ * 10000);
  1687. ftBCD:
  1688. PCurrency(Dst)^ := PCurrency(Src)^;
  1689. end;
  1690. SwapInt64LE(Dst, Dst);
  1691. end;
  1692. {$endif}
  1693. end;
  1694. 'B':
  1695. begin
  1696. if DbfVersion = xFoxPro then
  1697. begin
  1698. if Src = nil then
  1699. PDouble(Dst)^ := 0
  1700. else
  1701. SwapInt64LE(Src, Dst);
  1702. end else
  1703. asciiContents := true;
  1704. end;
  1705. 'M':
  1706. begin
  1707. if FieldSize = 4 then
  1708. begin
  1709. if Src = nil then
  1710. PInteger(Dst)^ := 0
  1711. else
  1712. PInteger(Dst)^ := SwapIntLE(PInteger(Src)^);
  1713. end else
  1714. asciiContents := true;
  1715. end;
  1716. else
  1717. asciiContents := true;
  1718. end;
  1719. if asciiContents then
  1720. begin
  1721. if Src = nil then
  1722. begin
  1723. FillChar(Dst^, FieldSize, ' ');
  1724. end else begin
  1725. case DataType of
  1726. ftBoolean:
  1727. begin
  1728. if PWord(Src)^ <> 0 then
  1729. PChar(Dst)^ := 'T'
  1730. else
  1731. PChar(Dst)^ := 'F';
  1732. end;
  1733. ftSmallInt:
  1734. GetStrFromInt_Width(PSmallInt(Src)^, FieldSize, PChar(Dst), #32);
  1735. {$ifdef SUPPORT_INT64}
  1736. ftLargeInt:
  1737. GetStrFromInt64_Width(PLargeInt(Src)^, FieldSize, PChar(Dst), #32);
  1738. {$endif}
  1739. ftFloat, ftCurrency:
  1740. FloatToDbfStr(PDouble(Src)^, FieldSize, FieldPrec, PChar(Dst));
  1741. ftInteger:
  1742. GetStrFromInt_Width(PInteger(Src)^, FieldSize, PChar(Dst),
  1743. IsBlobFieldToPadChar[TempFieldDef.IsBlob]);
  1744. ftDate, ftDateTime:
  1745. begin
  1746. LoadDateFromSrc;
  1747. // decode
  1748. DecodeDate(date, year, month, day);
  1749. // format is yyyymmdd
  1750. GetStrFromInt_Width(year, 4, PChar(Dst), '0');
  1751. GetStrFromInt_Width(month, 2, PChar(Dst)+4, '0');
  1752. GetStrFromInt_Width(day, 2, PChar(Dst)+6, '0');
  1753. // do time too if datetime
  1754. if DataType = ftDateTime then
  1755. begin
  1756. DecodeTime(date, hour, minute, sec, msec);
  1757. // format is hhmmss
  1758. GetStrFromInt_Width(hour, 2, PChar(Dst)+8, '0');
  1759. GetStrFromInt_Width(minute, 2, PChar(Dst)+10, '0');
  1760. GetStrFromInt_Width(sec, 2, PChar(Dst)+12, '0');
  1761. end;
  1762. end;
  1763. ftString:
  1764. begin
  1765. // copy data
  1766. Len := StrLen(Src);
  1767. if Len > FieldSize then
  1768. Len := FieldSize;
  1769. Move(Src^, Dst^, Len);
  1770. // fill remaining space with spaces
  1771. FillChar((PChar(Dst)+Len)^, FieldSize - Len, ' ');
  1772. end;
  1773. end; // case datatype
  1774. end;
  1775. end;
  1776. end;
  1777. procedure TDbfFile.InitDefaultBuffer;
  1778. var
  1779. lRecordSize: integer;
  1780. TempFieldDef: TDbfFieldDef;
  1781. I: Integer;
  1782. begin
  1783. lRecordSize := PDbfHdr(Header)^.RecordSize;
  1784. // clear buffer (assume all string, fix specific fields later)
  1785. // note: Self.RecordSize is used for reading fielddefs too
  1786. GetMem(FDefaultBuffer, lRecordSize+1);
  1787. FillChar(FDefaultBuffer^, lRecordSize, ' ');
  1788. // set nullflags field so that all fields are null
  1789. if FNullField <> nil then
  1790. FillChar(PChar(FDefaultBuffer+FNullField.Offset)^, FNullField.Size, $FF);
  1791. // check binary and default fields
  1792. for I := 0 to FFieldDefs.Count-1 do
  1793. begin
  1794. TempFieldDef := FFieldDefs.Items[I];
  1795. // binary field? (foxpro memo fields are binary, but dbase not)
  1796. if (TempFieldDef.NativeFieldType in ['I', 'O', '@', '+', '0', 'Y'])
  1797. or ((TempFieldDef.NativeFieldType = 'M') and (TempFieldDef.Size = 4)) then
  1798. FillChar(PChar(FDefaultBuffer+TempFieldDef.Offset)^, TempFieldDef.Size, 0);
  1799. // copy default value?
  1800. if TempFieldDef.HasDefault then
  1801. begin
  1802. Move(TempFieldDef.DefaultBuf[0], FDefaultBuffer[TempFieldDef.Offset], TempFieldDef.Size);
  1803. // clear the null flag, this field has a value
  1804. if FNullField <> nil then
  1805. UpdateNullField(FDefaultBuffer, TempFieldDef, unClear);
  1806. end;
  1807. end;
  1808. end;
  1809. procedure TDbfFile.InitRecord(DestBuf: PChar);
  1810. begin
  1811. if FDefaultBuffer = nil then
  1812. InitDefaultBuffer;
  1813. Move(FDefaultBuffer^, DestBuf^, RecordSize);
  1814. end;
  1815. procedure TDbfFile.ApplyAutoIncToBuffer(DestBuf: PChar);
  1816. var
  1817. TempFieldDef: TDbfFieldDef;
  1818. I, NextVal, lAutoIncOffset: {LongWord} Cardinal; {Delphi 3 does not know LongWord?}
  1819. begin
  1820. if FAutoIncPresent then
  1821. begin
  1822. // if shared, reread header to find new autoinc values
  1823. if NeedLocks then
  1824. begin
  1825. // lock header so nobody else can use this value
  1826. LockPage(0, true);
  1827. end;
  1828. // find autoinc fields
  1829. for I := 0 to FFieldDefs.Count-1 do
  1830. begin
  1831. TempFieldDef := FFieldDefs.Items[I];
  1832. if (TempFieldDef.NativeFieldType = '+') then
  1833. begin
  1834. // read current auto inc, from header or field, depending on sharing
  1835. lAutoIncOffset := sizeof(rDbfHdr) + sizeof(rAfterHdrVII) +
  1836. FieldDescVII_AutoIncOffset + I * sizeof(rFieldDescVII);
  1837. if NeedLocks then
  1838. begin
  1839. ReadBlock(@NextVal, 4, lAutoIncOffset);
  1840. NextVal := SwapIntLE(NextVal);
  1841. end else
  1842. NextVal := TempFieldDef.AutoInc;
  1843. // store to buffer, positive = high bit on, so flip it
  1844. PCardinal(DestBuf+TempFieldDef.Offset)^ := SwapIntBE(NextVal or $80000000);
  1845. // increase
  1846. Inc(NextVal);
  1847. TempFieldDef.AutoInc := NextVal;
  1848. // write new value to header buffer
  1849. PCardinal(FHeader+lAutoIncOffset)^ := SwapIntLE(NextVal);
  1850. end;
  1851. end;
  1852. // write modified header (new autoinc values) to file
  1853. WriteHeader;
  1854. // release lock if locked
  1855. if NeedLocks then
  1856. UnlockPage(0);
  1857. end;
  1858. end;
  1859. procedure TDbfFile.TryExclusive;
  1860. var
  1861. I: Integer;
  1862. begin
  1863. inherited;
  1864. // exclusive succeeded? open index & memo exclusive too
  1865. if Mode in [pfMemoryCreate..pfExclusiveOpen] then
  1866. begin
  1867. // indexes
  1868. for I := 0 to FIndexFiles.Count - 1 do
  1869. TPagedFile(FIndexFiles[I]).TryExclusive;
  1870. // memo
  1871. if FMemoFile <> nil then
  1872. FMemoFile.TryExclusive;
  1873. end;
  1874. end;
  1875. procedure TDbfFile.EndExclusive;
  1876. var
  1877. I: Integer;
  1878. begin
  1879. // end exclusive on index & memo too
  1880. for I := 0 to FIndexFiles.Count - 1 do
  1881. TPagedFile(FIndexFiles[I]).EndExclusive;
  1882. // memo
  1883. if FMemoFile <> nil then
  1884. FMemoFile.EndExclusive;
  1885. // dbf file
  1886. inherited;
  1887. end;
  1888. procedure TDbfFile.OpenIndex(IndexName, IndexField: string; CreateIndex: Boolean; Options: TIndexOptions);
  1889. //
  1890. // assumes IndexName is not empty
  1891. //
  1892. const
  1893. // memcr, memop, excr, exopen, rwcr, rwopen, rdonly
  1894. IndexOpenMode: array[boolean, pfMemoryCreate..pfReadOnly] of TPagedFileMode =
  1895. ((pfMemoryCreate, pfMemoryCreate, pfExclusiveOpen, pfExclusiveOpen, pfReadWriteOpen, pfReadWriteOpen,
  1896. pfReadOnly),
  1897. (pfMemoryCreate, pfMemoryCreate, pfExclusiveCreate, pfExclusiveCreate, pfReadWriteCreate, pfReadWriteCreate,
  1898. pfReadOnly));
  1899. var
  1900. lIndexFile: TIndexFile;
  1901. lIndexFileName: string;
  1902. createMdxFile: Boolean;
  1903. tempExclusive: boolean;
  1904. addedIndexFile: Integer;
  1905. addedIndexName: Integer;
  1906. begin
  1907. // init
  1908. addedIndexFile := -1;
  1909. addedIndexName := -1;
  1910. createMdxFile := false;
  1911. // index already opened?
  1912. lIndexFile := GetIndexByName(IndexName);
  1913. if (lIndexFile <> nil) and (lIndexFile = FMdxFile) and CreateIndex then
  1914. begin
  1915. // index already exists in MDX file
  1916. // delete it to save space, this causes a repage
  1917. FMdxFile.DeleteIndex(IndexName);
  1918. // index no longer exists
  1919. lIndexFile := nil;
  1920. end;
  1921. if (lIndexFile = nil) and (IndexName <> EmptyStr) then
  1922. begin
  1923. // check if no extension, then create MDX index
  1924. if Length(ExtractFileExt(IndexName)) = 0 then
  1925. begin
  1926. // check if mdx index already opened
  1927. if FMdxFile <> nil then
  1928. begin
  1929. lIndexFileName := EmptyStr;
  1930. lIndexFile := FMdxFile;
  1931. end else begin
  1932. lIndexFileName := ChangeFileExt(FileName, '.mdx');
  1933. createMdxFile := true;
  1934. end;
  1935. end else begin
  1936. lIndexFileName := IndexName;
  1937. end;
  1938. // do we need to open / create file?
  1939. if lIndexFileName <> EmptyStr then
  1940. begin
  1941. // try to open / create the file
  1942. lIndexFile := TIndexFile.Create(Self);
  1943. lIndexFile.FileName := lIndexFileName;
  1944. lIndexFile.Mode := IndexOpenMode[CreateIndex, Mode];
  1945. lIndexFile.AutoCreate := CreateIndex or (Length(IndexField) > 0);
  1946. lIndexFile.CodePage := UseCodePage;
  1947. lIndexFile.OnLocaleError := FOnLocaleError;
  1948. lIndexFile.Open;
  1949. // index file ready for use?
  1950. if not lIndexFile.ForceClose then
  1951. begin
  1952. // if we had to create the index, store that info
  1953. CreateIndex := lIndexFile.FileCreated;
  1954. // check if trying to create empty index
  1955. if CreateIndex and (IndexField = EmptyStr) then
  1956. begin
  1957. FreeAndNil(lIndexFile);
  1958. CreateIndex := false;
  1959. createMdxFile := false;
  1960. end else begin
  1961. // add new index file to list
  1962. addedIndexFile := FIndexFiles.Add(lIndexFile);
  1963. end;
  1964. // created accompanying mdx file?
  1965. if createMdxFile then
  1966. FMdxFile := lIndexFile;
  1967. end else begin
  1968. // asked to close! close file
  1969. FreeAndNil(lIndexFile);
  1970. end;
  1971. end;
  1972. // check if file succesfully opened
  1973. if lIndexFile <> nil then
  1974. begin
  1975. // add index to list
  1976. addedIndexName := FIndexNames.AddObject(IndexName, lIndexFile);
  1977. end;
  1978. end;
  1979. // create it or open it?
  1980. if lIndexFile <> nil then
  1981. begin
  1982. if not CreateIndex then
  1983. if lIndexFile = FMdxFile then
  1984. CreateIndex := lIndexFile.IndexOf(IndexName) < 0;
  1985. if CreateIndex then
  1986. begin
  1987. // try get exclusive mode
  1988. tempExclusive := IsSharedAccess;
  1989. if tempExclusive then TryExclusive;
  1990. // always uppercase index expression
  1991. IndexField := AnsiUpperCase(IndexField);
  1992. try
  1993. try
  1994. // create index if asked
  1995. lIndexFile.CreateIndex(IndexField, IndexName, Options);
  1996. // add all records
  1997. PackIndex(lIndexFile, IndexName);
  1998. // if we wanted to open index readonly, but we created it, then reopen
  1999. if Mode = pfReadOnly then
  2000. begin
  2001. lIndexFile.CloseFile;
  2002. lIndexFile.Mode := pfReadOnly;
  2003. lIndexFile.OpenFile;
  2004. end;
  2005. // if mdx file just created, write changes to dbf header
  2006. // set MDX flag to true
  2007. PDbfHdr(Header)^.MDXFlag := 1;
  2008. WriteHeader;
  2009. except
  2010. on EDbfError do
  2011. begin
  2012. // :-( need to undo 'damage'....
  2013. // remove index from list(s) if just added
  2014. if addedIndexFile >= 0 then
  2015. FIndexFiles.Delete(addedIndexFile);
  2016. if addedIndexName >= 0 then
  2017. FIndexNames.Delete(addedIndexName);
  2018. // if no file created, do not destroy!
  2019. if addedIndexFile >= 0 then
  2020. begin
  2021. lIndexFile.Close;
  2022. Sysutils.DeleteFile(lIndexFileName);
  2023. if FMdxFile = lIndexFile then
  2024. FMdxFile := nil;
  2025. lIndexFile.Free;
  2026. end;
  2027. raise;
  2028. end;
  2029. end;
  2030. finally
  2031. // return to previous mode
  2032. if tempExclusive then EndExclusive;
  2033. end;
  2034. end;
  2035. end;
  2036. end;
  2037. procedure TDbfFile.PackIndex(lIndexFile: TIndexFile; AIndexName: string);
  2038. var
  2039. prevMode: TIndexUpdateMode;
  2040. prevIndex: string;
  2041. cur, last: Integer;
  2042. {$ifdef USE_CACHE}
  2043. prevCache: Integer;
  2044. {$endif}
  2045. begin
  2046. // save current mode in case we change it
  2047. prevMode := lIndexFile.UpdateMode;
  2048. prevIndex := lIndexFile.IndexName;
  2049. // check if index specified
  2050. if Length(AIndexName) > 0 then
  2051. begin
  2052. // only pack specified index, not all
  2053. lIndexFile.IndexName := AIndexName;
  2054. lIndexFile.ClearIndex;
  2055. lIndexFile.UpdateMode := umCurrent;
  2056. end else begin
  2057. lIndexFile.IndexName := EmptyStr;
  2058. lIndexFile.Clear;
  2059. lIndexFile.UpdateMode := umAll;
  2060. end;
  2061. // prepare update
  2062. cur := 1;
  2063. last := RecordCount;
  2064. {$ifdef USE_CACHE}
  2065. BufferAhead := true;
  2066. prevCache := lIndexFile.CacheSize;
  2067. lIndexFile.CacheSize := GetFreeMemory;
  2068. if lIndexFile.CacheSize < 16384 * 1024 then
  2069. lIndexFile.CacheSize := 16384 * 1024;
  2070. {$endif}
  2071. try
  2072. try
  2073. while cur <= last do
  2074. begin
  2075. ReadRecord(cur, FPrevBuffer);
  2076. lIndexFile.Insert(cur, FPrevBuffer);
  2077. inc(cur);
  2078. end;
  2079. except
  2080. on E: EDbfError do
  2081. begin
  2082. lIndexFile.DeleteIndex(lIndexFile.IndexName);
  2083. raise;
  2084. end;
  2085. end;
  2086. finally
  2087. // restore previous mode
  2088. {$ifdef USE_CACHE}
  2089. BufferAhead := false;
  2090. lIndexFile.BufferAhead := true;
  2091. {$endif}
  2092. lIndexFile.Flush;
  2093. {$ifdef USE_CACHE}
  2094. lIndexFile.BufferAhead := false;
  2095. lIndexFile.CacheSize := prevCache;
  2096. {$endif}
  2097. lIndexFile.UpdateMode := prevMode;
  2098. lIndexFile.IndexName := prevIndex;
  2099. end;
  2100. end;
  2101. procedure TDbfFile.RepageIndex(AIndexFile: string);
  2102. var
  2103. lIndexNo: Integer;
  2104. begin
  2105. // DBF MDX index?
  2106. if Length(AIndexFile) = 0 then
  2107. begin
  2108. if FMdxFile <> nil then
  2109. begin
  2110. // repage attached mdx
  2111. FMdxFile.RepageFile;
  2112. end;
  2113. end else begin
  2114. // search index file
  2115. lIndexNo := FIndexNames.IndexOf(AIndexFile);
  2116. // index found?
  2117. if lIndexNo >= 0 then
  2118. TIndexFile(FIndexNames.Objects[lIndexNo]).RepageFile;
  2119. end;
  2120. end;
  2121. procedure TDbfFile.CompactIndex(AIndexFile: string);
  2122. var
  2123. lIndexNo: Integer;
  2124. begin
  2125. // DBF MDX index?
  2126. if Length(AIndexFile) = 0 then
  2127. begin
  2128. if FMdxFile <> nil then
  2129. begin
  2130. // repage attached mdx
  2131. FMdxFile.CompactFile;
  2132. end;
  2133. end else begin
  2134. // search index file
  2135. lIndexNo := FIndexNames.IndexOf(AIndexFile);
  2136. // index found?
  2137. if lIndexNo >= 0 then
  2138. TIndexFile(FIndexNames.Objects[lIndexNo]).CompactFile;
  2139. end;
  2140. end;
  2141. procedure TDbfFile.CloseIndex(AIndexName: string);
  2142. var
  2143. lIndexNo: Integer;
  2144. lIndex: TIndexFile;
  2145. begin
  2146. // search index file
  2147. lIndexNo := FIndexNames.IndexOf(AIndexName);
  2148. // don't close mdx file
  2149. if (lIndexNo >= 0) then
  2150. begin
  2151. // get index pointer
  2152. lIndex := TIndexFile(FIndexNames.Objects[lIndexNo]);
  2153. if (lIndex <> FMdxFile) then
  2154. begin
  2155. // close file
  2156. lIndex.Free;
  2157. // remove from lists
  2158. FIndexFiles.Remove(lIndex);
  2159. FIndexNames.Delete(lIndexNo);
  2160. // was this the current index?
  2161. if (FCurIndex = lIndexNo) then
  2162. begin
  2163. FCurIndex := -1;
  2164. //FCursor := FDbfCursor;
  2165. end;
  2166. end;
  2167. end;
  2168. end;
  2169. function TDbfFile.DeleteIndex(const AIndexName: string): Boolean;
  2170. var
  2171. lIndexNo: Integer;
  2172. lIndex: TIndexFile;
  2173. lFileName: string;
  2174. begin
  2175. // search index file
  2176. lIndexNo := FIndexNames.IndexOf(AIndexName);
  2177. Result := lIndexNo >= 0;
  2178. // found index?
  2179. if Result then
  2180. begin
  2181. // can only delete indexes from MDX files
  2182. lIndex := TIndexFile(FIndexNames.Objects[lIndexNo]);
  2183. if lIndex = FMdxFile then
  2184. begin
  2185. lIndex.DeleteIndex(AIndexName);
  2186. // remove it from the list
  2187. FIndexNames.Delete(lIndexNo);
  2188. // no more MDX indexes?
  2189. lIndexNo := FIndexNames.IndexOfObject(FMdxFile);
  2190. if lIndexNo = -1 then
  2191. begin
  2192. // no MDX indexes left
  2193. lIndexNo := FIndexFiles.IndexOf(FMdxFile);
  2194. if lIndexNo >= 0 then
  2195. FIndexFiles.Delete(lIndexNo);
  2196. lFileName := FMdxFile.FileName;
  2197. FreeAndNil(FMdxFile);
  2198. // erase file
  2199. Sysutils.DeleteFile(lFileName);
  2200. // clear mdx flag
  2201. PDbfHdr(Header)^.MDXFlag := 0;
  2202. WriteHeader;
  2203. end;
  2204. end else begin
  2205. // close index first
  2206. CloseIndex(AIndexName);
  2207. // delete file from disk
  2208. SysUtils.DeleteFile(AIndexName);
  2209. end;
  2210. end;
  2211. end;
  2212. function TDbfFile.Insert(Buffer: PChar): integer;
  2213. var
  2214. newRecord: Integer;
  2215. lIndex: TIndexFile;
  2216. error: Boolean;
  2217. procedure RollBackIndexesAndRaise(HighIndex: Integer; IndexError: Boolean);
  2218. var
  2219. errorMsg: string;
  2220. I: Integer;
  2221. begin
  2222. // rollback committed indexes
  2223. error := IndexError;
  2224. for I := 0 to HighIndex do
  2225. begin
  2226. lIndex := TIndexFile(FIndexFiles.Items[I]);
  2227. lIndex.Delete(newRecord, Buffer);
  2228. if lIndex.WriteError then
  2229. begin
  2230. lIndex.ResetError;
  2231. error := true;
  2232. end;
  2233. end;
  2234. // reset any dbf file error
  2235. ResetError;
  2236. // if part of indexes committed -> always index error msg
  2237. // if error while rolling back index -> index error msg
  2238. if error then
  2239. errorMsg := STRING_WRITE_INDEX_ERROR
  2240. else
  2241. errorMsg := STRING_WRITE_ERROR;
  2242. raise EDbfWriteError.Create(errorMsg);
  2243. end;
  2244. var
  2245. I: Integer;
  2246. begin
  2247. // get new record index
  2248. Result := 0;
  2249. newRecord := RecordCount+1;
  2250. // lock record so we can write data
  2251. while not LockPage(newRecord, false) do
  2252. Inc(newRecord);
  2253. // write autoinc value
  2254. ApplyAutoIncToBuffer(Buffer);
  2255. // check indexes -> possible key violation
  2256. I := 0; error := false;
  2257. while (I < FIndexFiles.Count) and not error do
  2258. begin
  2259. lIndex := TIndexFile(FIndexFiles.Items[I]);
  2260. error := lIndex.CheckKeyViolation(Buffer);
  2261. Inc(I);
  2262. end;
  2263. // error occured while inserting? -> abort
  2264. if error then
  2265. begin
  2266. UnlockPage(newRecord);
  2267. lIndex.InsertError;
  2268. // don't have to exit -- unreachable code
  2269. end;
  2270. // no key violation, insert record into index(es)
  2271. for I := 0 to FIndexFiles.Count-1 do
  2272. begin
  2273. lIndex := TIndexFile(FIndexFiles.Items[I]);
  2274. lIndex.Insert(newRecord, Buffer);
  2275. if lIndex.WriteError then
  2276. begin
  2277. // if there's an index write error, I shouldn't
  2278. // try to write the dbf header and the new record,
  2279. // but raise an exception right away
  2280. RollBackIndexesAndRaise(I, True);
  2281. end;
  2282. end;
  2283. // indexes ok -> continue inserting
  2284. // update header record count
  2285. LockPage(0, true);
  2286. // read current header
  2287. ReadHeader;
  2288. // increase current record count
  2289. Inc(PDbfHdr(Header)^.RecordCount);
  2290. // write header to disk
  2291. WriteHeader;
  2292. // done with header
  2293. UnlockPage(0);
  2294. if WriteError then
  2295. begin
  2296. // couldn't write header, so I shouldn't
  2297. // even try to write the record.
  2298. //
  2299. // At this point I should "roll back"
  2300. // the already written index records.
  2301. // if this fails, I'm in deep trouble!
  2302. RollbackIndexesAndRaise(FIndexFiles.Count-1, False);
  2303. end;
  2304. // write locking info
  2305. if FLockField <> nil then
  2306. WriteLockInfo(Buffer);
  2307. // write buffer to disk
  2308. WriteRecord(newRecord, Buffer);
  2309. // done updating, unlock
  2310. UnlockPage(newRecord);
  2311. // error occurred while writing?
  2312. if WriteError then
  2313. begin
  2314. // -- Tobias --
  2315. // The record couldn't be written, so
  2316. // the written index records and the
  2317. // change to the header have to be
  2318. // rolled back
  2319. LockPage(0, true);
  2320. ReadHeader;
  2321. Dec(PDbfHdr(Header)^.RecordCount);
  2322. WriteHeader;
  2323. UnlockPage(0);
  2324. // roll back indexes too
  2325. RollbackIndexesAndRaise(FIndexFiles.Count-1, False);
  2326. end else
  2327. Result := newRecord;
  2328. end;
  2329. procedure TDbfFile.WriteLockInfo(Buffer: PChar);
  2330. //
  2331. // *) assumes FHasLockField = true
  2332. //
  2333. var
  2334. year, month, day, hour, minute, sec, msec: Word;
  2335. lockoffset: integer;
  2336. begin
  2337. // increase change count
  2338. lockoffset := FLockField.Offset;
  2339. Inc(PWord(Buffer+lockoffset)^);
  2340. // set time
  2341. DecodeDate(Now(), year, month, day);
  2342. DecodeTime(Now(), hour, minute, sec, msec);
  2343. Buffer[lockoffset+2] := Char(hour);
  2344. Buffer[lockoffset+3] := Char(minute);
  2345. Buffer[lockoffset+4] := Char(sec);
  2346. // set date
  2347. Buffer[lockoffset+5] := Char(year - 1900);
  2348. Buffer[lockoffset+6] := Char(month);
  2349. Buffer[lockoffset+7] := Char(day);
  2350. // set name
  2351. FillChar(Buffer[lockoffset+8], FLockField.Size-8, ' ');
  2352. Move(DbfGlobals.UserName[1], Buffer[lockoffset+8], FLockUserLen);
  2353. end;
  2354. procedure TDbfFile.LockRecord(RecNo: Integer; Buffer: PChar);
  2355. begin
  2356. if LockPage(RecNo, false) then
  2357. begin
  2358. // reread data
  2359. ReadRecord(RecNo, Buffer);
  2360. // store previous data for updating indexes
  2361. Move(Buffer^, FPrevBuffer^, RecordSize);
  2362. // lock succeeded, update lock info, if field present
  2363. if FLockField <> nil then
  2364. begin
  2365. // update buffer
  2366. WriteLockInfo(Buffer);
  2367. // write to disk
  2368. WriteRecord(RecNo, Buffer);
  2369. end;
  2370. end else
  2371. raise EDbfError.Create(STRING_RECORD_LOCKED);
  2372. end;
  2373. procedure TDbfFile.UnlockRecord(RecNo: Integer; Buffer: PChar);
  2374. var
  2375. I: Integer;
  2376. lIndex: TIndexFile;
  2377. begin
  2378. // update indexes, possible key violation
  2379. for I := 0 to FIndexFiles.Count - 1 do
  2380. begin
  2381. lIndex := TIndexFile(FIndexFiles.Items[I]);
  2382. lIndex.Update(RecNo, FPrevBuffer, Buffer);
  2383. end;
  2384. // write new record buffer, all keys ok
  2385. WriteRecord(RecNo, Buffer);
  2386. // done updating, unlock
  2387. UnlockPage(RecNo);
  2388. end;
  2389. procedure TDbfFile.RecordDeleted(RecNo: Integer; Buffer: PChar);
  2390. var
  2391. I: Integer;
  2392. lIndex: TIndexFile;
  2393. begin
  2394. // notify indexes: record deleted
  2395. for I := 0 to FIndexFiles.Count - 1 do
  2396. begin
  2397. lIndex := TIndexFile(FIndexFiles.Items[I]);
  2398. lIndex.RecordDeleted(RecNo, Buffer);
  2399. end;
  2400. end;
  2401. procedure TDbfFile.RecordRecalled(RecNo: Integer; Buffer: PChar);
  2402. var
  2403. I: Integer;
  2404. lIndex: TIndexFile;
  2405. begin
  2406. // notify indexes: record recalled
  2407. for I := 0 to FIndexFiles.Count - 1 do
  2408. begin
  2409. lIndex := TIndexFile(FIndexFiles.Items[I]);
  2410. lIndex.RecordRecalled(RecNo, Buffer);
  2411. end;
  2412. end;
  2413. procedure TDbfFile.SetRecordSize(NewSize: Integer);
  2414. begin
  2415. if NewSize <> RecordSize then
  2416. begin
  2417. if FPrevBuffer <> nil then
  2418. FreeMemAndNil(Pointer(FPrevBuffer));
  2419. if NewSize > 0 then
  2420. GetMem(FPrevBuffer, NewSize);
  2421. end;
  2422. inherited;
  2423. end;
  2424. function TDbfFile.GetIndexByName(AIndexName: string): TIndexFile;
  2425. var
  2426. I: Integer;
  2427. begin
  2428. I := FIndexNames.IndexOf(AIndexName);
  2429. if I >= 0 then
  2430. Result := TIndexFile(FIndexNames.Objects[I])
  2431. else
  2432. Result := nil;
  2433. end;
  2434. //====================================================================
  2435. // TDbfCursor
  2436. //====================================================================
  2437. constructor TDbfCursor.Create(DbfFile: TDbfFile);
  2438. begin
  2439. inherited Create(DbfFile);
  2440. end;
  2441. function TDbfCursor.Next: Boolean;
  2442. begin
  2443. if TDbfFile(PagedFile).IsRecordPresent(FPhysicalRecNo) then
  2444. begin
  2445. inc(FPhysicalRecNo);
  2446. Result := TDbfFile(PagedFile).IsRecordPresent(FPhysicalRecNo);
  2447. end else begin
  2448. FPhysicalRecNo := TDbfFile(PagedFile).CachedRecordCount + 1;
  2449. Result := false;
  2450. end;
  2451. end;
  2452. function TDbfCursor.Prev: Boolean;
  2453. begin
  2454. if FPhysicalRecNo > 0 then
  2455. dec(FPhysicalRecNo)
  2456. else
  2457. FPhysicalRecNo := 0;
  2458. Result := FPhysicalRecNo > 0;
  2459. end;
  2460. procedure TDbfCursor.First;
  2461. begin
  2462. FPhysicalRecNo := 0;
  2463. end;
  2464. procedure TDbfCursor.Last;
  2465. var
  2466. max: Integer;
  2467. begin
  2468. max := TDbfFile(PagedFile).RecordCount;
  2469. if max = 0 then
  2470. FPhysicalRecNo := 0
  2471. else
  2472. FPhysicalRecNo := max + 1;
  2473. end;
  2474. function TDbfCursor.GetPhysicalRecNo: Integer;
  2475. begin
  2476. Result := FPhysicalRecNo;
  2477. end;
  2478. procedure TDbfCursor.SetPhysicalRecNo(RecNo: Integer);
  2479. begin
  2480. FPhysicalRecNo := RecNo;
  2481. end;
  2482. function TDbfCursor.GetSequentialRecordCount: Integer;
  2483. begin
  2484. Result := TDbfFile(PagedFile).RecordCount;
  2485. end;
  2486. function TDbfCursor.GetSequentialRecNo: Integer;
  2487. begin
  2488. Result := FPhysicalRecNo;
  2489. end;
  2490. procedure TDbfCursor.SetSequentialRecNo(RecNo: Integer);
  2491. begin
  2492. FPhysicalRecNo := RecNo;
  2493. end;
  2494. // codepage enumeration procedure
  2495. var
  2496. TempCodePageList: TList;
  2497. // LPTSTR = PChar ok?
  2498. function CodePagesProc(CodePageString: PChar): Cardinal; stdcall;
  2499. begin
  2500. // add codepage to list
  2501. TempCodePageList.Add(Pointer(GetIntFromStrLength(CodePageString, StrLen(CodePageString), -1)));
  2502. // continue enumeration
  2503. Result := 1;
  2504. end;
  2505. //====================================================================
  2506. // TDbfGlobals
  2507. //====================================================================
  2508. constructor TDbfGlobals.Create;
  2509. begin
  2510. FCodePages := TList.Create;
  2511. FDefaultOpenCodePage := GetACP;
  2512. // the following sets FDefaultCreateLangId
  2513. DefaultCreateCodePage := GetACP;
  2514. FCurrencyAsBCD := true;
  2515. // determine which code pages are installed
  2516. TempCodePageList := FCodePages;
  2517. EnumSystemCodePages(@CodePagesProc, {CP_SUPPORTED} CP_INSTALLED);
  2518. TempCodePageList := nil;
  2519. InitUserName;
  2520. end;
  2521. procedure TDbfGlobals.InitUserName;
  2522. {$ifdef FPC}
  2523. {$ifndef WINDOWS}
  2524. var
  2525. TempName: UTSName;
  2526. {$endif}
  2527. {$endif}
  2528. begin
  2529. {$ifdef WINDOWS}
  2530. FUserNameLen := MAX_COMPUTERNAME_LENGTH+1;
  2531. SetLength(FUserName, FUserNameLen);
  2532. Windows.GetComputerName(PChar(FUserName),
  2533. {$ifdef DELPHI_3}Windows.DWORD({$endif}
  2534. FUserNameLen
  2535. {$ifdef DELPHI_3}){$endif}
  2536. );
  2537. SetLength(FUserName, FUserNameLen);
  2538. {$else}
  2539. {$ifdef FPC}
  2540. FpUname(TempName);
  2541. FUserName := TempName.machine;
  2542. FUserNameLen := Length(FUserName);
  2543. {$endif}
  2544. {$endif}
  2545. end;
  2546. destructor TDbfGlobals.Destroy; {override;}
  2547. begin
  2548. FCodePages.Free;
  2549. end;
  2550. function TDbfGlobals.GetDefaultCreateCodePage: Integer;
  2551. begin
  2552. Result := LangId_To_CodePage[FDefaultCreateLangId];
  2553. end;
  2554. procedure TDbfGlobals.SetDefaultCreateCodePage(NewCodePage: Integer);
  2555. begin
  2556. FDefaultCreateLangId := ConstructLangId(NewCodePage, GetUserDefaultLCID, false);
  2557. end;
  2558. function TDbfGlobals.CodePageInstalled(ACodePage: Integer): Boolean;
  2559. begin
  2560. Result := FCodePages.IndexOf(Pointer(ACodePage)) >= 0;
  2561. end;
  2562. initialization
  2563. finalization
  2564. FreeAndNil(DbfGlobals);
  2565. (*
  2566. Stuffs non implemented yet
  2567. TFoxCDXHeader = Record
  2568. PointerRootNode : Integer;
  2569. PointerFreeList : Integer;
  2570. Reserved_8_11 : Cardinal;
  2571. KeyLength : Word;
  2572. IndexOption : Byte;
  2573. IndexSignature : Byte;
  2574. Reserved_Null : TFoxReservedNull;
  2575. SortOrder : Word;
  2576. TotalExpressionLen : Word;
  2577. ForExpressionLen : Word;
  2578. Reserved_506_507 : Word;
  2579. KeyExpressionLen : Word;
  2580. KeyForExpression : TKeyForExpression;
  2581. End;
  2582. PFoxCDXHeader = ^TFoxCDXHeader;
  2583. TFoxCDXNodeCommon = Record
  2584. NodeAttributes : Word;
  2585. NumberOfKeys : Word;
  2586. PointerLeftNode : Integer;
  2587. PointerRightNode : Integer;
  2588. End;
  2589. TFoxCDXNodeNonLeaf = Record
  2590. NodeCommon : TFoxCDXNodeCommon;
  2591. TempBlock : Array [12..511] of Byte;
  2592. End;
  2593. PFoxCDXNodeNonLeaf = ^TFoxCDXNodeNonLeaf;
  2594. TFoxCDXNodeLeaf = Packed Record
  2595. NodeCommon : TFoxCDXNodeCommon;
  2596. BlockFreeSpace : Word;
  2597. RecordNumberMask : Integer;
  2598. DuplicateCountMask : Byte;
  2599. TrailByteCountMask : Byte;
  2600. RecNoBytes : Byte;
  2601. DuplicateCountBytes : Byte;
  2602. TrailByteCountBytes : Byte;
  2603. HoldingByteCount : Byte;
  2604. DataBlock : TDataBlock;
  2605. End;
  2606. PFoxCDXNodeLeaf = ^TFoxCDXNodeLeaf;
  2607. *)
  2608. end.