📄 dbf_dbffile.pas
字号:
unit dbf_dbffile;
interface
{$I dbf_common.inc}
uses
Classes, SysUtils,
{$ifdef WINDOWS}
Windows,
{$else}
{$ifdef KYLIX}
Libc,
{$endif}
Types, dbf_wtil,
{$endif}
Db,
dbf_common,
dbf_cursor,
dbf_pgfile,
dbf_fields,
dbf_memo,
dbf_idxfile;
//====================================================================
//=== Dbf support (first part)
//====================================================================
// TxBaseVersion = (xUnknown,xClipper,xBaseIII,xBaseIV,xBaseV,xFoxPro,xVisualFoxPro);
// TPagedFileMode = (pfOpen,pfCreate);
// TDbfGetMode = (xFirst,xPrev,xCurrent, xNext, xLast);
// TDbfGetResult = (xOK, xBOF, xEOF, xError);
type
//====================================================================
TDbfIndexMissingEvent = procedure(var DeleteLink: Boolean) of object;
TUpdateNullField = (unClear, unSet);
//====================================================================
TDbfGlobals = class;
//====================================================================
TDbfFile = class(TPagedFile)
protected
FMdxFile: TIndexFile;
FMemoFile: TMemoFile;
FFieldDefs: TDbfFieldDefs;
FIndexNames: TStringList;
FIndexFiles: TList;
FDbfVersion: TXBaseVersion;
FPrevBuffer: PChar;
FDefaultBuffer: PChar;
FRecordBufferSize: Integer;
FLockUserLen: DWORD;
FFileCodePage: Cardinal;
FUseCodePage: Cardinal;
FFileLangId: Byte;
FCountUse: Integer;
FCurIndex: Integer;
FForceClose: Boolean;
FLockField: TDbfFieldDef;
FNullField: TDbfFieldDef;
FAutoIncPresent: Boolean;
FCopyDateTimeAsString: Boolean;
FDateTimeHandling: TDateTimeHandling;
FOnLocaleError: TDbfLocaleErrorEvent;
FOnIndexMissing: TDbfIndexMissingEvent;
function HasBlob: Boolean;
function GetMemoExt: string;
function GetLanguageId: Integer;
function GetLanguageStr: string;
protected
procedure ConstructFieldDefs;
procedure InitDefaultBuffer;
procedure UpdateNullField(Buffer: Pointer; AFieldDef: TDbfFieldDef; Action: TUpdateNullField);
procedure WriteLockInfo(Buffer: PChar);
public
constructor Create;
destructor Destroy; override;
procedure Open;
procedure Close;
procedure Zap;
procedure FinishCreate(AFieldDefs: TDbfFieldDefs; MemoSize: Integer);
function GetIndexByName(AIndexName: string): TIndexFile;
procedure SetRecordSize(NewSize: Integer); override;
procedure TryExclusive; override;
procedure EndExclusive; override;
procedure OpenIndex(IndexName, IndexField: string; CreateIndex: Boolean; Options: TIndexOptions);
function DeleteIndex(const AIndexName: string): Boolean;
procedure CloseIndex(AIndexName: string);
procedure RepageIndex(AIndexFile: string);
procedure CompactIndex(AIndexFile: string);
function Insert(Buffer: PChar): integer;
procedure WriteHeader; override;
procedure ApplyAutoIncToBuffer(DestBuf: PChar); // dBase7 support. Writeback last next-autoinc value
procedure FastPackTable;
procedure RestructureTable(DbfFieldDefs: TDbfFieldDefs; Pack: Boolean);
procedure Rename(DestFileName: string; NewIndexFileNames: TStrings; DeleteFiles: boolean);
function GetFieldInfo(FieldName: string): TDbfFieldDef;
function GetFieldData(Column: Integer; DataType: TFieldType; Src,Dst: Pointer;
NativeFormat: boolean): Boolean;
function GetFieldDataFromDef(AFieldDef: TDbfFieldDef; DataType: TFieldType;
Src, Dst: Pointer; NativeFormat: boolean): Boolean;
procedure SetFieldData(Column: Integer; DataType: TFieldType; Src,Dst: Pointer; NativeFormat: boolean);
procedure InitRecord(DestBuf: PChar);
procedure PackIndex(lIndexFile: TIndexFile; AIndexName: string);
procedure RegenerateIndexes;
procedure LockRecord(RecNo: Integer; Buffer: PChar);
procedure UnlockRecord(RecNo: Integer; Buffer: PChar);
procedure RecordDeleted(RecNo: Integer; Buffer: PChar);
procedure RecordRecalled(RecNo: Integer; Buffer: PChar);
property MemoFile: TMemoFile read FMemoFile;
property FieldDefs: TDbfFieldDefs read FFieldDefs;
property IndexNames: TStringList read FIndexNames;
property IndexFiles: TList read FIndexFiles;
property MdxFile: TIndexFile read FMdxFile;
property LanguageId: Integer read GetLanguageId;
property LanguageStr: string read GetLanguageStr;
property FileCodePage: Cardinal read FFileCodePage;
property UseCodePage: Cardinal read FUseCodePage write FUseCodePage;
property FileLangId: Byte read FFileLangId write FFileLangId;
property DbfVersion: TXBaseVersion read FDbfVersion write FDbfVersion;
property PrevBuffer: PChar read FPrevBuffer;
property ForceClose: Boolean read FForceClose;
property CopyDateTimeAsString: Boolean read FCopyDateTimeAsString write FCopyDateTimeAsString;
property DateTimeHandling: TDateTimeHandling read FDateTimeHandling write FDateTimeHandling;
property OnIndexMissing: TDbfIndexMissingEvent read FOnIndexMissing write FOnIndexMissing;
property OnLocaleError: TDbfLocaleErrorEvent read FOnLocaleError write FOnLocaleError;
end;
//====================================================================
TDbfCursor = class(TVirtualCursor)
protected
FPhysicalRecNo: Integer;
public
constructor Create(DbfFile: TDbfFile);
function Next: Boolean; override;
function Prev: Boolean; override;
procedure First; override;
procedure Last; override;
function GetPhysicalRecNo: Integer; override;
procedure SetPhysicalRecNo(RecNo: Integer); override;
function GetSequentialRecordCount: Integer; override;
function GetSequentialRecNo: Integer; override;
procedure SetSequentialRecNo(RecNo: Integer); override;
end;
//====================================================================
TDbfGlobals = class
protected
FCodePages: TList;
FCurrencyAsBCD: Boolean;
FDefaultOpenCodePage: Integer;
FDefaultCreateLangId: Byte;
FUserName: string;
FUserNameLen: DWORD;
function GetDefaultCreateCodePage: Integer;
procedure SetDefaultCreateCodePage(NewCodePage: Integer);
procedure InitUserName;
public
constructor Create;
destructor Destroy; override;
function CodePageInstalled(ACodePage: Integer): Boolean;
property CurrencyAsBCD: Boolean read FCurrencyAsBCD write FCurrencyAsBCD;
property DefaultOpenCodePage: Integer read FDefaultOpenCodePage write FDefaultOpenCodePage;
property DefaultCreateCodePage: Integer read GetDefaultCreateCodePage write SetDefaultCreateCodePage;
property DefaultCreateLangId: Byte read FDefaultCreateLangId write FDefaultCreateLangId;
property UserName: string read FUserName;
property UserNameLen: DWORD read FUserNameLen;
end;
var
DbfGlobals: TDbfGlobals;
implementation
uses
{$ifndef WINDOWS}
{$ifndef FPC}
RTLConsts,
{$else}
BaseUnix,
{$endif}
{$endif}
{$ifdef SUPPORT_MATH_UNIT}
Math,
{$endif}
dbf_str, dbf_lang, dbf_prssupp, dbf_prsdef;
const
sDBF_DEC_SEP = '.';
{$I dbf_struct.inc}
//====================================================================
// International separator
// thanks to Bruno Depero from Italy
// and Andreas W鰈lenstein from Denmark
//====================================================================
function DbfStrToFloat(const Src: PChar; const Size: Integer): Extended;
var
iPos: PChar;
eValue: extended;
endChar: Char;
begin
// temp null-term string
endChar := (Src + Size)^;
(Src + Size)^ := #0;
// we only have to convert if decimal separator different
if DecimalSeparator <> sDBF_DEC_SEP then
begin
// search dec sep
iPos := StrScan(Src, sDBF_DEC_SEP);
// replace
if iPos <> nil then
iPos^ := DecimalSeparator;
end else
iPos := nil;
// convert to double
if TextToFloat(Src, eValue {$ifndef VER1_0}, fvExtended{$endif}) then
Result := eValue
else
Result := 0;
// restore dec sep
if iPos <> nil then
iPos^ := sDBF_DEC_SEP;
// restore Char of null-term
(Src + Size)^ := endChar;
end;
procedure FloatToDbfStr(const Val: Extended; const Size, Precision: Integer; const Dest: PChar);
var
Buffer: array [0..24] of Char;
resLen: Integer;
iPos: PChar;
begin
// convert to temporary buffer
resLen := FloatToText(@Buffer[0], Val, {$ifndef FPC_VERSION}fvExtended,{$endif} ffFixed, Size, Precision);
// prevent overflow in destination buffer
if resLen > Size then
resLen := Size;
// null-terminate buffer
Buffer[resLen] := #0;
// we only have to convert if decimal separator different
if DecimalSeparator <> sDBF_DEC_SEP then
begin
iPos := StrScan(@Buffer[0], DecimalSeparator);
if iPos <> nil then
iPos^ := sDBF_DEC_SEP;
end;
// fill destination with spaces
FillChar(Dest^, Size, ' ');
// now copy right-aligned to destination
Move(Buffer[0], Dest[Size-resLen], resLen);
end;
function GetIntFromStrLength(Src: Pointer; Size: Integer; Default: Integer): Integer;
var
endChar: Char;
Code: Integer;
begin
// save Char at pos term. null
endChar := (PChar(Src) + Size)^;
(PChar(Src) + Size)^ := #0;
// convert
Val(PChar(Src), Result, Code);
// check success
if Code <> 0 then
Result := Default;
// restore prev. ending Char
(PChar(Src) + Size)^ := endChar;
end;
//====================================================================
// TDbfFile
//====================================================================
constructor TDbfFile.Create;
begin
// init variables first
FFieldDefs := TDbfFieldDefs.Create(nil);
FIndexNames := TStringList.Create;
FIndexFiles := TList.Create;
// now initialize inherited
inherited;
end;
destructor TDbfFile.Destroy;
var
I: Integer;
begin
// close file
Close;
// free files
for I := 0 to Pred(FIndexFiles.Count) do
TPagedFile(FIndexFiles.Items[I]).Free;
// free lists
FreeAndNil(FIndexFiles);
FreeAndNil(FIndexNames);
FreeAndNil(FFieldDefs);
// call ancestor
inherited;
end;
procedure TDbfFile.Open;
var
lMemoFileName: string;
lMdxFileName: string;
MemoFileClass: TMemoFileClass;
I: Integer;
deleteLink: Boolean;
lModified: boolean;
LangStr: PChar;
version: byte;
begin
// check if not already opened
if not Active then
begin
// open requested file
OpenFile;
// check if we opened an already existing file
lModified := false;
if not FileCreated then
begin
HeaderSize := sizeof(rDbfHdr); // temporary
// OH 2000-11-15 dBase7 support. I build dBase Tables with different
// BDE dBase Level (1. without Memo, 2. with Memo)
// Header Byte ($1d hex) (29 dec) -> Language driver ID.
// $03,$83 xBaseIII Header Byte $1d=$00, Float -> N($13.$04) DateTime C($1E)
// $03,$8B xBaseIV/V Header Byte $1d=$58, Float -> N($14.$04)
// $04,$8C xBaseVII Header Byte $1d=$00 Float -> O($08) DateTime @($08)
// $03,$F5 FoxPro Level 25 Header Byte $1d=$03, Float -> N($14.$04)
// Access 97
// $03,$83 dBaseIII Header Byte $1d=$00, Float -> N($13.$05) DateTime D($08)
// $03,$8B dBaseIV/V Header Byte $1d=$00, Float -> N($14.$05) DateTime D($08)
// $03,$F5 FoxPro Level 25 Header Byte $1d=$09, Float -> N($14.$05) DateTime D($08)
version := PDbfHdr(Header)^.VerDBF;
case (version and $07) of
$03:
if LanguageID = 0 then
FDbfVersion := xBaseIII
else
FDbfVersion := xBaseIV;
$04:
FDbfVersion := xBaseVII;
$02, $05:
FDbfVersion := xFoxPro;
else
// check visual foxpro
if ((version and $FE) = $30) or (version = $F5) or (version = $FB) then
begin
FDbfVersion := xFoxPro;
end else begin
// not a valid DBF file
raise EDbfError.Create(STRING_INVALID_DBF_FILE);
end;
end;
FFieldDefs.DbfVersion := FDbfVersion;
RecordSize := PDbfHdr(Header)^.RecordSize;
HeaderSize := PDbfHdr(Header)^.FullHdrSize;
if (HeaderSize = 0) or (RecordSize = 0) then
begin
HeaderSize := 0;
RecordSize := 0;
RecordCount := 0;
FForceClose := true;
exit;
end;
// check if specified recordcount correct
if PDbfHdr(Header)^.RecordCount <> RecordCount then
begin
// This message was annoying
// and was not understood by most people
// ShowMessage('Invalid Record Count,'+^M+
// 'RecordCount in Hdr : '+IntToStr(PDbfHdr(Header).RecordCount)+^M+
// 'expected : '+IntToStr(RecordCount));
PDbfHdr(Header)^.RecordCount := RecordCount;
lModified := true;
end;
// determine codepage
if FDbfVersion >= xBaseVII then
begin
// cache language str
LangStr := @PAfterHdrVII(PChar(Header) + SizeOf(rDbfHdr))^.LanguageDriverName;
// VdBase 7 Language strings
// 'DBWIN...' -> Charset 1252 (ansi)
// 'DB999...' -> Code page 999, 9 any digit
// 'DBHEBREW' -> Code page 1255 ??
// 'FOX..999' -> Code page 999, 9 any digit
// 'FOX..WIN' -> Charset 1252 (ansi)
if (LangStr[0] = 'D') and (LangStr[1] = 'B') then
begin
if StrLComp(LangStr+2, 'WIN', 3) = 0 then
FFileCodePage := 1252
else
if StrLComp(LangStr+2, 'HEBREW', 6) = 0 then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -