home *** CD-ROM | disk | FTP | other *** search
- {
- { If this code is used commercially, please send a few bucks to }
- { Bill Himmelstoss, PO BOX 23246, Jacksonville, FL 32241-3246, }
- { Otherwise, it's freely distributable. }
-
- unit DBF;
-
- interface
-
- uses
- Objects,
- OString;
-
- type
- TYMDDate = record
- Year,
- Month,
- Day: Byte;
- end;
-
- PDatabase = ^TDatabase;
- TDatabase = object(TObject)
- DatabaseType: Byte;
- LastUpdate: TYMDDate;
- NumRecords: Longint;
- FirstRecordPos: Word;
- RecordLength: Word;
-
- S: TDosStream;
- Pathname: TOString;
- Modified: Boolean;
- Fields: TCollection;
-
- constructor Init(APathname: TOString);
- constructor InitCreate(APathname: TOString; AFields: PCollection);
- destructor Done; virtual;
- procedure RefreshHeader;
- procedure UpdateHeader;
- function GetRecord(RecordNum: Longint): Pointer;
- procedure PutRecord(RecordNum: Longint; Rec: Pointer);
- procedure Append(Rec: Pointer);
- procedure Zap;
- procedure RefreshFields;
- end;
-
- PFieldDef = ^TFieldDef;
- TFieldDef = object(TObject)
- Name: TOString;
- DataType: Char;
- Displacement: Longint;
- Length: Byte;
- Decimal: Byte;
-
- constructor Init(
- AName: String;
- ADataType: Char;
- ALength,
- ADecimal: Byte);
- destructor Done; virtual;
- constructor Load(var S: TStream);
- procedure Store(var S: TStream);
- end;
-
- implementation
-
- uses
- WinDos;
-
- constructor TDatabase.Init(APathname: TOString); begin
- inherited Init;
- Pathname.InitText(APathname);
- S.Init(Pathname.CString, stOpen);
- if S.Status <> stOk then Fail;
- Fields.Init(5, 5);
- RefreshHeader;
- end;
-
- constructor TDatabase.InitCreate(APathname: TOString; AFields: PCollection);
- const
- Terminator: Byte = $0D;
- var
- Year, Month, Day, Dummy: Word;
-
- procedure CopyField(Item: PFieldDef); far;
- begin
- Fields.Insert(Item);
- end;
-
- procedure WriteFieldSubrecord(Item: PFieldDef); far;
- begin
- Item^.Store(S);
- Inc(RecordLength, Item^.Length);
- end;
-
- begin
- inherited Init;
-
- DatabaseType := $03;
- GetDate(Year, Month, Day, Dummy);
- LastUpdate.Year := Year - 1900;
- LastUpdate.Month := Month;
- LastUpdate.Day := Day;
- NumRecords := 0;
- RecordLength := 0;
-
- Pathname.InitText(APathname);
- S.Init(Pathname.CString, stCreate);
- if S.Status <> stOk then Fail;
- UpdateHeader;
-
- S.Seek(32); { beginning of field subrecords }
- Fields.Init(AFields^.Count, 5);
- AFields^.ForEach(@CopyField);
- Fields.ForEach(@WriteFieldSubrecord);
-
- S.Write(Terminator, SizeOf(Terminator));
- Modified := true;
- FirstRecordPos := S.GetPos;
- UpdateHeader;
- end;
-
- destructor TDatabase.Done;
- begin
- if Modified then UpdateHeader;
- Pathname.Done;
- S.Done;
- Fields.Done;
- inherited Done;
- end;
-
- procedure TDatabase.RefreshHeader;
- var
- OldPos: Longint;
- begin
- OldPos := S.GetPos;
- S.Seek(0);
- S.Read(DatabaseType, SizeOf(DatabaseType));
- S.Read(LastUpdate, SizeOf(LastUpdate));
- S.Read(NumRecords, SizeOf(NumRecords));
- S.Read(FirstRecordPos, SizeOf(FirstRecordPos));
- S.Read(RecordLength, SizeOf(RecordLength));
- S.Seek(OldPos);
- RefreshFields;
- end;
-
- procedure TDatabase.UpdateHeader;
- var
- OldPos: Longint;
- Reserved: array[12..31] of Char;
- begin
- OldPos := S.GetPos;
- S.Seek(0);
- S.Write(DatabaseType, SizeOf(DatabaseType));
- S.Write(LastUpdate, SizeOf(LastUpdate));
- S.Write(NumRecords, SizeOf(NumRecords));
- S.Write(FirstRecordPos, SizeOf(FirstRecordPos));
- S.Write(RecordLength, SizeOf(RecordLength));
- FillChar(Reserved, SizeOf(Reserved), #0);
- S.Write(Reserved, SizeOf(Reserved));
- S.Seek(OldPos);
- end;
-
- function TDatabase.GetRecord(RecordNum: Longint): Pointer; var
- Temp: Pointer;
- Pos: Longint;
- begin
- Temp := NIL;
- GetMem(Temp, RecordLength);
- if Temp <> NIL then
- begin
- Pos := FirstRecordPos + ((RecordNum - 1) * RecordLength);
- if S.GetPos <> Pos then
- S.Seek(Pos);
- S.Read(Temp^, RecordLength);
- end;
- GetRecord := Temp;
- end;
-
- procedure TDatabase.Append(Rec: Pointer); begin
- if Assigned(Rec) then
- begin
- Modified := true;
- Inc(NumRecords);
- PutRecord(NumRecords, Rec);
- end;
- end;
-
- procedure TDatabase.PutRecord(RecordNum: Longint; Rec: Pointer); var
- Pos: Longint;
- begin
- if Assigned(Rec) and (RecordNum <= NumRecords) then
- begin
- Pos := FirstRecordPos + ((RecordNum - 1) * RecordLength);
- if S.GetPos <> Pos then
- S.Seek(Pos);
- S.Write(Rec^, RecordLength);
- end;
- end;
-
- procedure TDatabase.Zap;
- var
- T: TDosStream;
- Temp, D, N, E: TOString;
- F: File;
- begin
- D.Init(fsDirectory);
- N.Init(fsFilename);
- E.Init(fsExtension);
- FileSplit(Pathname.CString, D.CString, N.CString, E.CString);
- D.RecalcLength;
- N.RecalcLength;
- E.RecalcLength;
- Temp.InitText(D);
- Temp.Append(N);
- Temp.AppendP('.TMP');
- D.Done;
- N.Done;
- E.Done;
-
- T.Init(Temp.CString, stCreate);
- S.Seek(0);
- T.CopyFrom(S, FirstRecordPos - 1);
- T.Done;
- S.Done;
- Assign(F, Pathname.CString);
- Erase(F);
- Assign(F, Temp.CString);
- Rename(F, Pathname.CString);
- S.Init(Pathname.CString, stOpen);
- NumRecords := 0;
- Modified := false;
- UpdateHeader;
- end;
-
- procedure TDatabase.RefreshFields;
- var
- Terminator: Byte;
- HoldPos: Longint;
- FieldDef: PFieldDef;
- begin
- S.Seek(32); { beginning of Field subrecords }
-
- repeat
- HoldPos := S.GetPos;
- S.Read(Terminator, SizeOf(Terminator));
- if Terminator <> $0D then
- begin
- S.Seek(HoldPos);
- FieldDef := New(PFieldDef, Load(S));
- Fields.Insert(FieldDef);
- end;
- until Terminator = $0D;
- end;
-
- constructor TFieldDef.Init(
- AName: String;
- ADataType: Char;
- ALength,
- ADecimal: Byte);
- begin
- inherited Init;
- Name.InitTextP(AName);
- DataType := ADataType;
- Length := ALength;
- Decimal := ADecimal;
- Displacement := 0;
- end;
-
- destructor TFieldDef.Done;
- begin
- Name.Done;
- inherited Done;
- end;
-
- constructor TFieldDef.Load(var S: TStream); var
- AName: array[1..11] of Char;
- Reserved: array[18..31] of Char;
- begin
- S.Read(AName, SizeOf(AName));
- Name.Init(SizeOf(AName));
- Name.SetText_(@AName[1], 11);
- S.Read(DataType, SizeOf(DataType));
- S.Read(Displacement, Sizeof(Displacement));
- S.Read(Length, SizeOf(Length));
- S.Read(Decimal, SizeOf(Decimal));
- S.Read(Reserved, SizeOf(Reserved));
- end;
-
- procedure TFieldDef.Store(var S: TStream); var
- Reserved: array[18..31] of Char;
- begin
- S.Write(Name.CString^, 11);
- S.Write(DataType, SizeOf(DataType));
- S.Write(Displacement, Sizeof(Displacement));
- S.Write(Length, SizeOf(Length));
- S.Write(Decimal, SizeOf(Decimal));
- FillChar(Reserved, SizeOf(Reserved), #0);
- S.Write(Reserved, SizeOf(Reserved));
- end;
-
- end.
-
-
-
-
-
- program DbfTest;
-
- uses
- dbf, wincrt, ostring, objects, strings;
-
- type
- PDbfTest = ^TDbfTest;
- TDbfTest = record
- Deleted: Char; { ' '=no, '*'=yes }
- AcctNo: array[1..16] of Char;
- Chunk: array[1..8] of Char;
- Baskard: array[1..5] of Char;
- Extra: array[1..8] of Char;
- Sandwich: array[1..25] of Char;
- end;
-
- var
- rec: PDbfTest;
- database: tdatabase;
- pathname: tostring;
- temp: string;
- fields: tcollection;
-
- procedure DoShow;
-
- procedure show(item: pfielddef); far;
- begin
- writeln(
- item^.name.cstring:15, ' ',
- item^.datatype, ' ',
- item^.length:10, ' ',
- item^.decimal:10, ' ');
- end;
-
- begin
- database.fields.foreach(@show);
- end;
-
-
- begin
- InitWinCrt;
-
- fields.init(5, 0);
- fields.insert(new(pfielddef, init('ACCTNO', 'C', 16, 0)));
- fields.insert(new(pfielddef, init('CHUNK', 'N', 8, 2)));
- fields.insert(new(pfielddef, init('BASKARD', 'C', 5, 0)));
- fields.insert(new(pfielddef, init('EXTRA', 'D', 8, 0)));
- fields.insert(new(pfielddef, init('SANDWICH', 'C', 25, 0)));
- pathname.inittextp('c:\dbftest.dbf');
- database.initcreate(pathname, @fields);
- pathname.done;
- DoShow;
-
- New(Rec);
- with Rec^ do
- begin
- Acctno := '1313558000001005'; { <-will self-check, but not valid }
- Chunk := ' 10.00';
- Baskard := 'ABCDE';
- Extra := '19931125';
- Sandwich := 'Turkey Leftovers ';
- end;
- database.append(rec);
- dispose(rec);
-
- rec := database.getrecord(1);
- writeln(rec^.acctno, ' ', rec^.Sandwich);
- dispose(rec);
-
- database.done;
- end.