dbf_dbffile.pas 77 KB

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