dbf_dbffile.pas 80 KB

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