dbf_memo.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. unit dbf_memo;
  2. interface
  3. {$I dbf_common.inc}
  4. uses
  5. Classes,
  6. dbf_pgfile,
  7. dbf_common;
  8. type
  9. //====================================================================
  10. TMemoFile = class(TPagedFile)
  11. protected
  12. FDbfFile: pointer;
  13. FDbfVersion: TXBaseVersion;
  14. FMemoRecordSize: Integer;
  15. FOpened: Boolean;
  16. FBuffer: PChar;
  17. protected
  18. function GetBlockLen: Integer; virtual; abstract;
  19. function GetMemoSize: Integer; virtual; abstract;
  20. function GetNextFreeBlock: Integer; virtual; abstract;
  21. procedure SetNextFreeBlock(BlockNo: Integer); virtual; abstract;
  22. procedure SetBlockLen(BlockLen: Integer); virtual; abstract;
  23. public
  24. constructor Create(ADbfFile: pointer);
  25. destructor Destroy; override;
  26. procedure Open;
  27. procedure Close;
  28. procedure ReadMemo(BlockNo: Integer; DestStream: TStream);
  29. procedure WriteMemo(var BlockNo: Integer; ReadSize: Integer; Src: TStream);
  30. property DbfVersion: TXBaseVersion read FDbfVersion write FDbfVersion;
  31. property MemoRecordSize: Integer read FMemoRecordSize write FMemoRecordSize;
  32. end;
  33. TFoxProMemoFile = class(TMemoFile)
  34. protected
  35. function GetBlockLen: Integer; override;
  36. function GetMemoSize: Integer; override;
  37. function GetNextFreeBlock: Integer; override;
  38. procedure SetNextFreeBlock(BlockNo: Integer); override;
  39. procedure SetBlockLen(BlockLen: Integer); override;
  40. end;
  41. TDbaseMemoFile = class(TMemoFile)
  42. protected
  43. function GetBlockLen: Integer; override;
  44. function GetMemoSize: Integer; override;
  45. function GetNextFreeBlock: Integer; override;
  46. procedure SetNextFreeBlock(BlockNo: Integer); override;
  47. procedure SetBlockLen(BlockLen: Integer); override;
  48. end;
  49. { TNullMemoFile, a kind /dev/null memofile ;-) }
  50. { - inv: FHeaderModified == false!! (otherwise will try to write FStream) }
  51. { - inv: FHeaderSize == 0 }
  52. { - inv: FNeedLocks == false }
  53. { - WriteTo must NOT be used }
  54. { - WriteChar must NOT be used }
  55. TNullMemoFile = class(TMemoFile)
  56. protected
  57. procedure SetHeaderOffset(NewValue: Integer); override;
  58. procedure SetRecordSize(NewValue: Integer); override;
  59. procedure SetHeaderSize(NewValue: Integer); override;
  60. function LockSection(const Offset, Length: Cardinal; const Wait: Boolean): Boolean; override;
  61. function UnlockSection(const Offset, Length: Cardinal): Boolean; override;
  62. function GetBlockLen: Integer; override;
  63. function GetMemoSize: Integer; override;
  64. function GetNextFreeBlock: Integer; override;
  65. procedure SetNextFreeBlock(BlockNo: Integer); override;
  66. procedure SetBlockLen(BlockLen: Integer); override;
  67. public
  68. constructor Create(ADbfFile: pointer);
  69. procedure CloseFile; override;
  70. procedure OpenFile; override;
  71. function ReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer; override;
  72. procedure WriteRecord(IntRecNum: Integer; Buffer: Pointer); override;
  73. end;
  74. PInteger = ^Integer;
  75. TMemoFileClass = class of TMemoFile;
  76. implementation
  77. uses
  78. SysUtils, dbf_dbffile;
  79. //====================================================================
  80. //=== Memo and binary fields support
  81. //====================================================================
  82. type
  83. PDbtHdr = ^rDbtHdr;
  84. rDbtHdr = record
  85. NextBlock : dword;
  86. Dummy : array [4..7] of Byte;
  87. DbfFile : array [0..7] of Byte; // 8..15
  88. bVer : Byte; // 16
  89. Dummy2 : array [17..19] of Byte;
  90. BlockLen : Word; // 20..21
  91. Dummy3 : array [22..511] of Byte;
  92. end;
  93. PFptHdr = ^rFptHdr;
  94. rFptHdr = record
  95. NextBlock : dword;
  96. Dummy : array [4..5] of Byte;
  97. BlockLen : Word; // 20..21
  98. Dummy3 : array [8..511] of Byte;
  99. end;
  100. PBlockHdr = ^rBlockHdr;
  101. rBlockHdr = record
  102. MemoType : Cardinal;
  103. MemoSize : Cardinal;
  104. end;
  105. //==========================================================
  106. //============ Dbtfile
  107. //==========================================================
  108. constructor TMemoFile.Create(ADbfFile: pointer);
  109. begin
  110. // init vars
  111. FBuffer := nil;
  112. FOpened := false;
  113. // call inherited
  114. inherited Create;
  115. FDbfFile := ADbfFile;
  116. FTempMode := TDbfFile(ADbfFile).TempMode;
  117. end;
  118. destructor TMemoFile.Destroy;
  119. begin
  120. // close file
  121. Close;
  122. // call ancestor
  123. inherited;
  124. end;
  125. procedure TMemoFile.Open;
  126. begin
  127. if not FOpened then
  128. begin
  129. // memo pages count start from begining of file!
  130. PageOffsetByHeader := false;
  131. // open physical file
  132. OpenFile;
  133. // read header
  134. HeaderSize := 512;
  135. // determine version
  136. if FDbfVersion = xBaseIII then
  137. PDbtHdr(Header)^.bVer := 3;
  138. VirtualLocks := false;
  139. if FileCreated or (HeaderSize = 0) then
  140. begin
  141. if (FMemoRecordSize = 0) or (FMemoRecordSize > HeaderSize) then
  142. SetNextFreeBlock(1)
  143. else
  144. SetNextFreeBlock(HeaderSize div FMemoRecordSize);
  145. SetBlockLen(FMemoRecordSize);
  146. WriteHeader;
  147. end;
  148. RecordSize := GetBlockLen;
  149. // checking for right blocksize not needed for foxpro?
  150. // mod 128 <> 0 <-> and 0x7F <> 0
  151. if (RecordSize = 0) and ((FDbfVersion = xFoxPro) or ((RecordSize and $7F) <> 0)) then
  152. begin
  153. SetBlockLen(512);
  154. RecordSize := 512;
  155. WriteHeader;
  156. end;
  157. // get memory for temporary buffer
  158. GetMem(FBuffer, RecordSize+2);
  159. FBuffer[RecordSize] := #0;
  160. FBuffer[RecordSize+1] := #0;
  161. // now open
  162. FOpened := true;
  163. end;
  164. end;
  165. procedure TMemoFile.Close;
  166. begin
  167. if FOpened then
  168. begin
  169. // close physical file
  170. CloseFile;
  171. // free mem
  172. if FBuffer <> nil then
  173. FreeMemAndNil(Pointer(FBuffer));
  174. // now closed
  175. FOpened := false;
  176. end;
  177. end;
  178. procedure TMemoFile.ReadMemo(BlockNo: Integer; DestStream: TStream);
  179. var
  180. bytesLeft,numBytes,dataStart: Integer;
  181. done: Boolean;
  182. lastc: char;
  183. endMemo: PChar;
  184. begin
  185. // clear dest
  186. DestStream.Position := 0;
  187. DestStream.Size := 0;
  188. // no block to read?
  189. if (BlockNo<=0) or (RecordSize=0) then
  190. exit;
  191. // read first block
  192. numBytes := ReadRecord(BlockNo, @FBuffer[0]);
  193. if numBytes = 0 then
  194. begin
  195. // EOF reached?
  196. exit;
  197. end else
  198. if numBytes < RecordSize then
  199. FillChar(FBuffer[RecordSize-numBytes], numBytes, #0);
  200. bytesLeft := GetMemoSize;
  201. // bytesLeft <> -1 -> memo size is known (FoxPro, dBase4)
  202. // bytesLeft = -1 -> memo size unknown (dBase3)
  203. if bytesLeft <> -1 then
  204. begin
  205. dataStart := 8;
  206. DestStream.Size := bytesLeft;
  207. while bytesLeft > 0 do
  208. begin
  209. // get number of bytes to be read
  210. numBytes := bytesLeft;
  211. // too much for this block?
  212. if numBytes > RecordSize - dataStart then
  213. numBytes := RecordSize - dataStart;
  214. // read block to stream
  215. DestStream.Write(FBuffer[dataStart], numBytes);
  216. // numBytes done
  217. dec(bytesLeft, numBytes);
  218. // still need to read bytes?
  219. if bytesLeft > 0 then
  220. begin
  221. // read next block
  222. inc(BlockNo);
  223. dataStart := 0;
  224. ReadRecord(BlockNo, @FBuffer[0]);
  225. end;
  226. end;
  227. end else begin
  228. // dbase III memo
  229. done := false;
  230. repeat
  231. // scan for EOF
  232. endMemo := MemScan(FBuffer, $1A, RecordSize);
  233. // EOF found?
  234. if endMemo <> nil then
  235. begin
  236. // really EOF?
  237. if (endMemo-FBuffer < RecordSize - 1) and ((endMemo[1] = #$1A) or (endMemo[1] = #0)) then
  238. begin
  239. // yes, EOF found
  240. done := true;
  241. numBytes := endMemo - FBuffer;
  242. end else begin
  243. // no, fake
  244. numBytes := RecordSize;
  245. end;
  246. end else begin
  247. numBytes := RecordSize;
  248. end;
  249. // write to stream
  250. DestStream.Write(FBuffer[0], numBytes);
  251. {
  252. for i := 0 to RecordSize-2 do
  253. begin
  254. if (FBuffer[i]=#$1A) and (FBuffer[i+1]=#$1A) then
  255. begin
  256. if i>0 then
  257. DestStream.Write(FBuffer[0], i);
  258. done := true;
  259. break;
  260. end;
  261. end;
  262. }
  263. if not done then
  264. begin
  265. {
  266. DestStream.Write(FBuffer[0], 512);
  267. }
  268. lastc := FBuffer[RecordSize-1];
  269. inc(BlockNo);
  270. if ReadRecord(BlockNo, @FBuffer[0]) > 0 then
  271. begin
  272. // check if immediate terminator at begin of block
  273. done := (lastc = #$1A) and ((FBuffer[0] = #$1A) or (FBuffer[0] = #0));
  274. // if so, written one character too much
  275. if done then
  276. DestStream.Size := DestStream.Size - 1;
  277. end else begin
  278. // error while reading, stop
  279. done := true;
  280. end;
  281. end;
  282. until done;
  283. end;
  284. end;
  285. procedure TMemoFile.WriteMemo(var BlockNo: Integer; ReadSize: Integer; Src: TStream);
  286. var
  287. bytesBefore: Integer;
  288. bytesAfter: Integer;
  289. totsize: Integer;
  290. readBytes: Integer;
  291. append: Boolean;
  292. tmpRecNo: Integer;
  293. begin
  294. // if no data to write, then don't create new block
  295. if Src.Size = 0 then
  296. begin
  297. BlockNo := 0;
  298. end else begin
  299. if FDbfVersion >= xBaseIV then // dBase4 or FoxPro type
  300. begin
  301. bytesBefore := SizeOf(rBlockHdr);
  302. bytesAfter := 0;
  303. end else begin // dBase3 type
  304. bytesBefore := 0;
  305. bytesAfter := 2;
  306. end;
  307. // if ((bytesBefore + Src.Size + bytesAfter + PDbtHdr(Header).BlockLen-1) div PDbtHdr(Header).BlockLen)
  308. // <= ((ReadSize + PDbtHdr(Header).BlockLen-1) div PDbtHdr(Header).BlockLen) then
  309. if ((bytesBefore + Src.Size + bytesAfter + RecordSize-1) div RecordSize)
  310. <= ((ReadSize + RecordSize-1) div RecordSize) then
  311. begin
  312. append := false;
  313. end else begin
  314. append := true;
  315. // modifying header -> lock memo header
  316. LockPage(0, true);
  317. BlockNo := GetNextFreeBlock;
  318. if BlockNo = 0 then
  319. begin
  320. SetNextFreeBlock(1);
  321. BlockNo := 1;
  322. end;
  323. end;
  324. tmpRecNo := BlockNo;
  325. Src.Position := 0;
  326. FillChar(FBuffer[0], RecordSize, ' ');
  327. if bytesBefore=8 then
  328. begin
  329. totsize := Src.Size + bytesBefore + bytesAfter;
  330. if FDbfVersion <> xFoxPro then
  331. begin
  332. PBlockHdr(FBuffer)^.MemoType := $0008FFFF;
  333. PBlockHdr(FBuffer)^.MemoSize := totsize;
  334. end else begin
  335. PBlockHdr(FBuffer)^.MemoType := $01000000;
  336. PBlockHdr(FBuffer)^.MemoSize := SwapInt(Src.Size);
  337. end;
  338. end;
  339. repeat
  340. // read bytes, don't overwrite header
  341. readBytes := Src.Read(FBuffer[bytesBefore], RecordSize{PDbtHdr(Header).BlockLen}-bytesBefore);
  342. // end of input data reached ? check if need to write block terminators
  343. while (readBytes < RecordSize - bytesBefore) and (bytesAfter > 0) do
  344. begin
  345. FBuffer[readBytes] := #$1A;
  346. Inc(readBytes);
  347. Dec(bytesAfter);
  348. end;
  349. // have we read anything that is to be written?
  350. if readBytes > 0 then
  351. begin
  352. // clear any unused space
  353. FillChar(FBuffer[bytesBefore+readBytes], RecordSize-readBytes-bytesBefore, ' ');
  354. // write to disk
  355. WriteRecord(tmpRecNo, @FBuffer[0]);
  356. Inc(tmpRecNo);
  357. end else break;
  358. // first block read, second block can start at beginning
  359. bytesBefore := 0;
  360. until false;
  361. if append then
  362. begin
  363. SetNextFreeBlock(tmpRecNo);
  364. WriteHeader;
  365. UnlockPage(0);
  366. end;
  367. end;
  368. end;
  369. // ------------------------------------------------------------------
  370. // dBase specific helper routines
  371. // ------------------------------------------------------------------
  372. function TDbaseMemoFile.GetBlockLen: Integer;
  373. begin
  374. // Can you tell me why the header of dbase3 memo contains 1024 and is 512 ?
  375. // answer: it is not a valid field in memo db3 header
  376. if FDbfVersion = xBaseIII then
  377. Result := 512
  378. else
  379. Result := PDbtHdr(Header)^.BlockLen;
  380. end;
  381. function TDbaseMemoFile.GetMemoSize: Integer;
  382. begin
  383. // dBase4 memofiles contain small 'header'
  384. if PInteger(@FBuffer[0])^ = $0008FFFF then
  385. Result := PBlockHdr(FBuffer)^.MemoSize-8
  386. else
  387. Result := -1;
  388. end;
  389. function TDbaseMemoFile.GetNextFreeBlock: Integer;
  390. begin
  391. Result := PDbtHdr(Header)^.NextBlock;
  392. end;
  393. procedure TDbaseMemoFile.SetNextFreeBlock(BlockNo: Integer);
  394. begin
  395. PDbtHdr(Header)^.NextBlock := BlockNo;
  396. end;
  397. procedure TDbaseMemoFile.SetBlockLen(BlockLen: Integer);
  398. begin
  399. PDbtHdr(Header)^.BlockLen := BlockLen;
  400. end;
  401. // ------------------------------------------------------------------
  402. // FoxPro specific helper routines
  403. // ------------------------------------------------------------------
  404. function TFoxProMemoFile.GetBlockLen: Integer;
  405. begin
  406. Result := SwapWord(PFptHdr(Header)^.BlockLen);
  407. end;
  408. function TFoxProMemoFile.GetMemoSize: Integer;
  409. begin
  410. Result := SwapInt(PBlockHdr(FBuffer)^.MemoSize);
  411. end;
  412. function TFoxProMemoFile.GetNextFreeBlock: Integer;
  413. begin
  414. Result := SwapInt(PFptHdr(Header)^.NextBlock);
  415. end;
  416. procedure TFoxProMemoFile.SetNextFreeBlock(BlockNo: Integer);
  417. begin
  418. PFptHdr(Header)^.NextBlock := SwapInt(dword(BlockNo));
  419. end;
  420. procedure TFoxProMemoFile.SetBlockLen(BlockLen: Integer);
  421. begin
  422. PFptHdr(Header)^.BlockLen := SwapWord(dword(BlockLen));
  423. end;
  424. // ------------------------------------------------------------------
  425. // NULL file (no file) specific helper routines
  426. // ------------------------------------------------------------------
  427. constructor TNullMemoFile.Create(ADbfFile: pointer);
  428. begin
  429. inherited;
  430. end;
  431. procedure TNullMemoFile.OpenFile;
  432. begin
  433. end;
  434. procedure TNullMemoFile.CloseFile;
  435. begin
  436. end;
  437. procedure TNullMemoFile.SetHeaderOffset(NewValue: Integer);
  438. begin
  439. inherited SetHeaderOffset(0);
  440. end;
  441. procedure TNullMemoFile.SetRecordSize(NewValue: Integer);
  442. begin
  443. inherited SetRecordSize(0);
  444. end;
  445. procedure TNullMemoFile.SetHeaderSize(NewValue: Integer);
  446. begin
  447. inherited SetHeaderSize(0);
  448. end;
  449. function TNullMemoFile.LockSection(const Offset, Length: Cardinal; const Wait: Boolean): Boolean;
  450. begin
  451. Result := true;
  452. end;
  453. function TNullMemoFile.UnlockSection(const Offset, Length: Cardinal): Boolean;
  454. begin
  455. Result := true;
  456. end;
  457. function TNullMemoFile.GetBlockLen: Integer;
  458. begin
  459. Result := 0;
  460. end;
  461. function TNullMemoFile.GetMemoSize: Integer;
  462. begin
  463. Result := 0;
  464. end;
  465. function TNullMemoFile.GetNextFreeBlock: Integer;
  466. begin
  467. Result := 0;
  468. end;
  469. procedure TNullMemoFile.SetNextFreeBlock(BlockNo: Integer);
  470. begin
  471. end;
  472. procedure TNullMemoFile.SetBlockLen(BlockLen: Integer);
  473. begin
  474. end;
  475. function TNullMemoFile.ReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer;
  476. begin
  477. Result := 0;
  478. end;
  479. procedure TNullMemoFile.WriteRecord(IntRecNum: Integer; Buffer: Pointer);
  480. begin
  481. end;
  482. end.