123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- unit dbf_memo;
- interface
- {$I dbf_common.inc}
- uses
- Classes,
- dbf_pgfile,
- dbf_common;
- type
- //====================================================================
- TMemoFile = class(TPagedFile)
- protected
- FDbfFile: pointer;
- FDbfVersion: TXBaseVersion;
- FMemoRecordSize: Integer;
- FOpened: Boolean;
- FBuffer: PChar;
- protected
- function GetBlockLen: Integer; virtual; abstract;
- function GetMemoSize: Integer; virtual; abstract;
- function GetNextFreeBlock: Integer; virtual; abstract;
- procedure SetNextFreeBlock(BlockNo: Integer); virtual; abstract;
- procedure SetBlockLen(BlockLen: Integer); virtual; abstract;
- public
- constructor Create(ADbfFile: pointer);
- destructor Destroy; override;
- procedure Open;
- procedure Close;
- procedure ReadMemo(BlockNo: Integer; DestStream: TStream);
- procedure WriteMemo(var BlockNo: Integer; ReadSize: Integer; Src: TStream);
- property DbfVersion: TXBaseVersion read FDbfVersion write FDbfVersion;
- property MemoRecordSize: Integer read FMemoRecordSize write FMemoRecordSize;
- end;
- TFoxProMemoFile = class(TMemoFile)
- protected
- function GetBlockLen: Integer; override;
- function GetMemoSize: Integer; override;
- function GetNextFreeBlock: Integer; override;
- procedure SetNextFreeBlock(BlockNo: Integer); override;
- procedure SetBlockLen(BlockLen: Integer); override;
- end;
- TDbaseMemoFile = class(TMemoFile)
- protected
- function GetBlockLen: Integer; override;
- function GetMemoSize: Integer; override;
- function GetNextFreeBlock: Integer; override;
- procedure SetNextFreeBlock(BlockNo: Integer); override;
- procedure SetBlockLen(BlockLen: Integer); override;
- end;
- { TNullMemoFile, a kind /dev/null memofile ;-) }
- { - inv: FHeaderModified == false!! (otherwise will try to write FStream) }
- { - inv: FHeaderSize == 0 }
- { - inv: FNeedLocks == false }
- { - WriteTo must NOT be used }
- { - WriteChar must NOT be used }
- TNullMemoFile = class(TMemoFile)
- protected
- procedure SetHeaderOffset(NewValue: Integer); override;
- procedure SetRecordSize(NewValue: Integer); override;
- procedure SetHeaderSize(NewValue: Integer); override;
- function LockSection(const Offset, Length: Cardinal; const Wait: Boolean): Boolean; override;
- function UnlockSection(const Offset, Length: Cardinal): Boolean; override;
- function GetBlockLen: Integer; override;
- function GetMemoSize: Integer; override;
- function GetNextFreeBlock: Integer; override;
- procedure SetNextFreeBlock(BlockNo: Integer); override;
- procedure SetBlockLen(BlockLen: Integer); override;
- public
- constructor Create(ADbfFile: pointer);
- procedure CloseFile; override;
- procedure OpenFile; override;
- function ReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer; override;
- procedure WriteRecord(IntRecNum: Integer; Buffer: Pointer); override;
- end;
- PInteger = ^Integer;
- TMemoFileClass = class of TMemoFile;
- implementation
- uses
- SysUtils, dbf_dbffile;
- //====================================================================
- //=== Memo and binary fields support
- //====================================================================
- type
- PDbtHdr = ^rDbtHdr;
- rDbtHdr = record
- NextBlock : dword;
- Dummy : array [4..7] of Byte;
- DbfFile : array [0..7] of Byte; // 8..15
- bVer : Byte; // 16
- Dummy2 : array [17..19] of Byte;
- BlockLen : Word; // 20..21
- Dummy3 : array [22..511] of Byte;
- end;
- PFptHdr = ^rFptHdr;
- rFptHdr = record
- NextBlock : dword;
- Dummy : array [4..5] of Byte;
- BlockLen : Word; // 20..21
- Dummy3 : array [8..511] of Byte;
- end;
- PBlockHdr = ^rBlockHdr;
- rBlockHdr = record
- MemoType : Cardinal;
- MemoSize : Cardinal;
- end;
- //==========================================================
- //============ Dbtfile
- //==========================================================
- constructor TMemoFile.Create(ADbfFile: pointer);
- begin
- // init vars
- FBuffer := nil;
- FOpened := false;
- // call inherited
- inherited Create;
- FDbfFile := ADbfFile;
- FTempMode := TDbfFile(ADbfFile).TempMode;
- end;
- destructor TMemoFile.Destroy;
- begin
- // close file
- Close;
- // call ancestor
- inherited;
- end;
- procedure TMemoFile.Open;
- begin
- if not FOpened then
- begin
- // memo pages count start from begining of file!
- PageOffsetByHeader := false;
- // open physical file
- OpenFile;
- // read header
- HeaderSize := 512;
- // determine version
- if FDbfVersion = xBaseIII then
- PDbtHdr(Header)^.bVer := 3;
- VirtualLocks := false;
- if FileCreated or (HeaderSize = 0) then
- begin
- if (FMemoRecordSize = 0) or (FMemoRecordSize > HeaderSize) then
- SetNextFreeBlock(1)
- else
- SetNextFreeBlock(HeaderSize div FMemoRecordSize);
- SetBlockLen(FMemoRecordSize);
- WriteHeader;
- end;
- RecordSize := GetBlockLen;
- // checking for right blocksize not needed for foxpro?
- // mod 128 <> 0 <-> and 0x7F <> 0
- if (RecordSize = 0) and ((FDbfVersion = xFoxPro) or ((RecordSize and $7F) <> 0)) then
- begin
- SetBlockLen(512);
- RecordSize := 512;
- WriteHeader;
- end;
- // get memory for temporary buffer
- GetMem(FBuffer, RecordSize+2);
- FBuffer[RecordSize] := #0;
- FBuffer[RecordSize+1] := #0;
- // now open
- FOpened := true;
- end;
- end;
- procedure TMemoFile.Close;
- begin
- if FOpened then
- begin
- // close physical file
- CloseFile;
- // free mem
- if FBuffer <> nil then
- FreeMemAndNil(Pointer(FBuffer));
- // now closed
- FOpened := false;
- end;
- end;
- procedure TMemoFile.ReadMemo(BlockNo: Integer; DestStream: TStream);
- var
- bytesLeft,numBytes,dataStart: Integer;
- done: Boolean;
- lastc: char;
- endMemo: PChar;
- begin
- // clear dest
- DestStream.Position := 0;
- DestStream.Size := 0;
- // no block to read?
- if (BlockNo<=0) or (RecordSize=0) then
- exit;
- // read first block
- numBytes := ReadRecord(BlockNo, @FBuffer[0]);
- if numBytes = 0 then
- begin
- // EOF reached?
- exit;
- end else
- if numBytes < RecordSize then
- FillChar(FBuffer[RecordSize-numBytes], numBytes, #0);
- bytesLeft := GetMemoSize;
- // bytesLeft <> -1 -> memo size is known (FoxPro, dBase4)
- // bytesLeft = -1 -> memo size unknown (dBase3)
- if bytesLeft <> -1 then
- begin
- dataStart := 8;
- DestStream.Size := bytesLeft;
- while bytesLeft > 0 do
- begin
- // get number of bytes to be read
- numBytes := bytesLeft;
- // too much for this block?
- if numBytes > RecordSize - dataStart then
- numBytes := RecordSize - dataStart;
- // read block to stream
- DestStream.Write(FBuffer[dataStart], numBytes);
- // numBytes done
- dec(bytesLeft, numBytes);
- // still need to read bytes?
- if bytesLeft > 0 then
- begin
- // read next block
- inc(BlockNo);
- dataStart := 0;
- ReadRecord(BlockNo, @FBuffer[0]);
- end;
- end;
- end else begin
- // dbase III memo
- done := false;
- repeat
- // scan for EOF
- endMemo := MemScan(FBuffer, $1A, RecordSize);
- // EOF found?
- if endMemo <> nil then
- begin
- // really EOF?
- if (endMemo-FBuffer < RecordSize - 1) and ((endMemo[1] = #$1A) or (endMemo[1] = #0)) then
- begin
- // yes, EOF found
- done := true;
- numBytes := endMemo - FBuffer;
- end else begin
- // no, fake
- numBytes := RecordSize;
- end;
- end else begin
- numBytes := RecordSize;
- end;
- // write to stream
- DestStream.Write(FBuffer[0], numBytes);
- {
- for i := 0 to RecordSize-2 do
- begin
- if (FBuffer[i]=#$1A) and (FBuffer[i+1]=#$1A) then
- begin
- if i>0 then
- DestStream.Write(FBuffer[0], i);
- done := true;
- break;
- end;
- end;
- }
- if not done then
- begin
- {
- DestStream.Write(FBuffer[0], 512);
- }
- lastc := FBuffer[RecordSize-1];
- inc(BlockNo);
- if ReadRecord(BlockNo, @FBuffer[0]) > 0 then
- begin
- // check if immediate terminator at begin of block
- done := (lastc = #$1A) and ((FBuffer[0] = #$1A) or (FBuffer[0] = #0));
- // if so, written one character too much
- if done then
- DestStream.Size := DestStream.Size - 1;
- end else begin
- // error while reading, stop
- done := true;
- end;
- end;
- until done;
- end;
- end;
- procedure TMemoFile.WriteMemo(var BlockNo: Integer; ReadSize: Integer; Src: TStream);
- var
- bytesBefore: Integer;
- bytesAfter: Integer;
- totsize: Integer;
- readBytes: Integer;
- append: Boolean;
- tmpRecNo: Integer;
- begin
- // if no data to write, then don't create new block
- if Src.Size = 0 then
- begin
- BlockNo := 0;
- end else begin
- if FDbfVersion >= xBaseIV then // dBase4 or FoxPro type
- begin
- bytesBefore := SizeOf(rBlockHdr);
- bytesAfter := 0;
- end else begin // dBase3 type
- bytesBefore := 0;
- bytesAfter := 2;
- end;
- // if ((bytesBefore + Src.Size + bytesAfter + PDbtHdr(Header).BlockLen-1) div PDbtHdr(Header).BlockLen)
- // <= ((ReadSize + PDbtHdr(Header).BlockLen-1) div PDbtHdr(Header).BlockLen) then
- if ((bytesBefore + Src.Size + bytesAfter + RecordSize-1) div RecordSize)
- <= ((ReadSize + RecordSize-1) div RecordSize) then
- begin
- append := false;
- end else begin
- append := true;
- // modifying header -> lock memo header
- LockPage(0, true);
- BlockNo := GetNextFreeBlock;
- if BlockNo = 0 then
- begin
- SetNextFreeBlock(1);
- BlockNo := 1;
- end;
- end;
- tmpRecNo := BlockNo;
- Src.Position := 0;
- FillChar(FBuffer[0], RecordSize, ' ');
- if bytesBefore=8 then
- begin
- totsize := Src.Size + bytesBefore + bytesAfter;
- if FDbfVersion <> xFoxPro then
- begin
- PBlockHdr(FBuffer)^.MemoType := $0008FFFF;
- PBlockHdr(FBuffer)^.MemoSize := totsize;
- end else begin
- PBlockHdr(FBuffer)^.MemoType := $01000000;
- PBlockHdr(FBuffer)^.MemoSize := SwapInt(Src.Size);
- end;
- end;
- repeat
- // read bytes, don't overwrite header
- readBytes := Src.Read(FBuffer[bytesBefore], RecordSize{PDbtHdr(Header).BlockLen}-bytesBefore);
- // end of input data reached ? check if need to write block terminators
- while (readBytes < RecordSize - bytesBefore) and (bytesAfter > 0) do
- begin
- FBuffer[readBytes] := #$1A;
- Inc(readBytes);
- Dec(bytesAfter);
- end;
- // have we read anything that is to be written?
- if readBytes > 0 then
- begin
- // clear any unused space
- FillChar(FBuffer[bytesBefore+readBytes], RecordSize-readBytes-bytesBefore, ' ');
- // write to disk
- WriteRecord(tmpRecNo, @FBuffer[0]);
- Inc(tmpRecNo);
- end else break;
- // first block read, second block can start at beginning
- bytesBefore := 0;
- until false;
- if append then
- begin
- SetNextFreeBlock(tmpRecNo);
- WriteHeader;
- UnlockPage(0);
- end;
- end;
- end;
- // ------------------------------------------------------------------
- // dBase specific helper routines
- // ------------------------------------------------------------------
- function TDbaseMemoFile.GetBlockLen: Integer;
- begin
- // Can you tell me why the header of dbase3 memo contains 1024 and is 512 ?
- // answer: it is not a valid field in memo db3 header
- if FDbfVersion = xBaseIII then
- Result := 512
- else
- Result := PDbtHdr(Header)^.BlockLen;
- end;
- function TDbaseMemoFile.GetMemoSize: Integer;
- begin
- // dBase4 memofiles contain small 'header'
- if PInteger(@FBuffer[0])^ = $0008FFFF then
- Result := PBlockHdr(FBuffer)^.MemoSize-8
- else
- Result := -1;
- end;
- function TDbaseMemoFile.GetNextFreeBlock: Integer;
- begin
- Result := PDbtHdr(Header)^.NextBlock;
- end;
- procedure TDbaseMemoFile.SetNextFreeBlock(BlockNo: Integer);
- begin
- PDbtHdr(Header)^.NextBlock := BlockNo;
- end;
- procedure TDbaseMemoFile.SetBlockLen(BlockLen: Integer);
- begin
- PDbtHdr(Header)^.BlockLen := BlockLen;
- end;
- // ------------------------------------------------------------------
- // FoxPro specific helper routines
- // ------------------------------------------------------------------
- function TFoxProMemoFile.GetBlockLen: Integer;
- begin
- Result := SwapWord(PFptHdr(Header)^.BlockLen);
- end;
- function TFoxProMemoFile.GetMemoSize: Integer;
- begin
- Result := SwapInt(PBlockHdr(FBuffer)^.MemoSize);
- end;
- function TFoxProMemoFile.GetNextFreeBlock: Integer;
- begin
- Result := SwapInt(PFptHdr(Header)^.NextBlock);
- end;
- procedure TFoxProMemoFile.SetNextFreeBlock(BlockNo: Integer);
- begin
- PFptHdr(Header)^.NextBlock := SwapInt(dword(BlockNo));
- end;
- procedure TFoxProMemoFile.SetBlockLen(BlockLen: Integer);
- begin
- PFptHdr(Header)^.BlockLen := SwapWord(dword(BlockLen));
- end;
- // ------------------------------------------------------------------
- // NULL file (no file) specific helper routines
- // ------------------------------------------------------------------
- constructor TNullMemoFile.Create(ADbfFile: pointer);
- begin
- inherited;
- end;
- procedure TNullMemoFile.OpenFile;
- begin
- end;
- procedure TNullMemoFile.CloseFile;
- begin
- end;
- procedure TNullMemoFile.SetHeaderOffset(NewValue: Integer);
- begin
- inherited SetHeaderOffset(0);
- end;
- procedure TNullMemoFile.SetRecordSize(NewValue: Integer);
- begin
- inherited SetRecordSize(0);
- end;
- procedure TNullMemoFile.SetHeaderSize(NewValue: Integer);
- begin
- inherited SetHeaderSize(0);
- end;
- function TNullMemoFile.LockSection(const Offset, Length: Cardinal; const Wait: Boolean): Boolean;
- begin
- Result := true;
- end;
- function TNullMemoFile.UnlockSection(const Offset, Length: Cardinal): Boolean;
- begin
- Result := true;
- end;
- function TNullMemoFile.GetBlockLen: Integer;
- begin
- Result := 0;
- end;
- function TNullMemoFile.GetMemoSize: Integer;
- begin
- Result := 0;
- end;
- function TNullMemoFile.GetNextFreeBlock: Integer;
- begin
- Result := 0;
- end;
- procedure TNullMemoFile.SetNextFreeBlock(BlockNo: Integer);
- begin
- end;
- procedure TNullMemoFile.SetBlockLen(BlockLen: Integer);
- begin
- end;
- function TNullMemoFile.ReadRecord(IntRecNum: Integer; Buffer: Pointer): Integer;
- begin
- Result := 0;
- end;
- procedure TNullMemoFile.WriteRecord(IntRecNum: Integer; Buffer: Pointer);
- begin
- end;
- end.
|