Contributor: WIM VAN DER VEGT 
{---------------------------------------------------------}
{ Unit : Dbase III Access Routines }
{ Auteur : Ir. G.W. van der Vegt }
{ Hondsbroek 57 }
{ 6121 XB Born }
{---------------------------------------------------------}
{ Datum .tijd Revisie }
{ 910701.2130 Creatie. }
{ 910702.1000 Minor Errors Corrected }
{ Replace, Append & Pack Added }
{ 910706.2400 dbrec on the Heap (recsize max 64kB-16) }
{ Uppercase Conversion in Bd3_fileno }
{ Optional Halt on (fatal) Errors }
{ 910710.1500 Memo Field Support }
{ 910715.2330 Field2num bug fixed (leading sp. removed) }
{ 910960.1130 Fieldno Out of range detection }
{ 920116.1000 Two minor bugs fixed }
{ 920124.2200 Header updated when file is closed, }
{ Db3_Seekbof & Db3_Seekeof added }
{ Db3_Findfirst & Db3_Findnext implemented }
{ for wildcard search of records }
{ Db3_soudex & Db3_field2soundex for Soundex}
{ code (sound alike) operations }
{ Db3_firstsoudex & Db3_nextsoundex for }
{ soundex search on a field }
{ 920127.1300 Dbase Slack Filespace Detection & }
{ Correction }
{ 920129.2115 Trailing spaces remover in Db3_field2str }
{ Seek after truncate in Db3_open }
{ 920130.2145 Slack filespace bug removed }
{ Db3_sort implemented (based on shakersort)}
{ Bug in Db3_date2field removed }
{ 920716.2130 Empty file pack fixed in Db3_pack }
{ 920928.2200 Obscure bug in Db3_fieldname. Fieldnames }
{ seem to be are ASCIZ in stead of fixed }
{ length strings. }
{ 930927.2000 Freemem bug in db3_findnext corrected. }
{---------------------------------------------------------}
{ To Do Full Documentation }
{ Write Memo Support }
{ Extend Db3_pack with MemoFile Packing }
{ Sort *.DBF in place }
{ Insert record in *.DBF file }
{ Date format not always yy-mm-dd }
{---------------------------------------------------------}
UNIT Db3_01;
INTERFACE
USES
 DOS;
{---------------------------------------------------------}
{----Error Handling : Returns First Error Which Occured }
{---------------------------------------------------------}
VAR
 db3_ernr : INTEGER; {----DB3 Module Error Code}
 db3_fatal : BOOLEAN; {----IF True
 THEN Halt(db3_ernr)
 on an error}
 db3_memotext : TEXT; {----Memo File}
{---------------------------------------------------------}
FUNCTION Db3_ermsg(nr : INTEGER) : STRING;
{---------------------------------------------------------}
{----Initialize/Exit : Must both be Called for every file }
{---------------------------------------------------------}
PROCEDURE Db3_open(fn : STRING); {----Opens fn.DBF file &
 Inits Internals}
PROCEDURE Db3_close; {----Closes fn.DBF file}
{---------------------------------------------------------}
{----Header Function : Get .DBF header info }
{---------------------------------------------------------}
FUNCTION Db3_memo : BOOLEAN;
FUNCTION Db3_update : STRING;
FUNCTION Db3_norecs : LONGINT;
FUNCTION Db3_nofields : INTEGER;
FUNCTION Db3_reclen : INTEGER;
{---------------------------------------------------------}
{----File I/O : Dbase III Alike (pos etc. in records) }
{---------------------------------------------------------}
PROCEDURE Db3_seek(pos : LONGINT);
FUNCTION Db3_filesize : LONGINT;
FUNCTION Db3_filepos : LONGINT;
PROCEDURE Db3_readnext;
PROCEDURE Db3_read(pos : LONGINT);
PROCEDURE Db3_seekeof;
PROCEDURE Db3_seekbof;
FUNCTION Db3_eof : BOOLEAN;
FUNCTION Db3_bof : BOOLEAN;
PROCEDURE Db3_replace(no : LONGINT); {----First Read record &
 Fill all fields}
PROCEDURE Db3_append; {----First Fill all Fields}
PROCEDURE Db3_delete(no : LONGINT);
PROCEDURE Db3_undelete(no : LONGINT);
PROCEDURE Db3_pack; {----Packs File IN-PLACE}
PROCEDURE Db3_blankrec;
{---------------------------------------------------------}
{----Field Operations : no is .DBF field number }
{---------------------------------------------------------}
FUNCTION Db3_fieldname(no : INTEGER) : STRING;
FUNCTION Db3_fieldlen(no : INTEGER) : INTEGER;
FUNCTION Db3_fielddec(no : INTEGER) : INTEGER;
FUNCTION Db3_fieldno(name : STRING) : INTEGER; {----Searches Fieldnumber for
 Uppercase fieldname}
FUNCTION Db3_fieldtype(no : INTEGER) : CHAR;
FUNCTION Db3_deleted : BOOLEAN;
{---------------------------------------------------------}
{----Field Conversions : date format 'dd-mm-19yy' }
{---------------------------------------------------------}
FUNCTION Db3_field2str(no :INTEGER) : STRING;
FUNCTION Db3_field2char(no :INTEGER) : CHAR;
FUNCTION Db3_field2logic(no : INTEGER) : BOOLEAN;
FUNCTION Db3_field2num(no : INTEGER) : REAL;
FUNCTION Db3_field2date(no :INTEGER) : STRING;
PROCEDURE Db3_field2memo(no : INTEGER);
FUNCTION Db3_field2soundex(no : INTEGER) : STRING;
PROCEDURE Db3_str2field(no :INTEGER;s : STRING);
PROCEDURE Db3_char2field(no :INTEGER;s : CHAR);
PROCEDURE Db3_logic2field(no : INTEGER;l : BOOLEAN);
PROCEDURE Db3_num2field(no : INTEGER;n : REAL);
PROCEDURE Db3_date2field(no :INTEGER;d : STRING);
{---------------------------------------------------------}
{----Database Search, spaces are used as wildcards. }
{ Db3_blankrec can be used for creating a wildcard }
{ record. Then if Findfirst is true the use Findnext }
{ until Findnext becomes false. After each succesfull }
{ call the internal readbuffer will contain the }
{ matching record. Use casesense=true for a case }
{ sensitive search. }
{---------------------------------------------------------}
FUNCTION Db3_findfirst(cs : BOOLEAN) : BOOLEAN;
FUNCTION Db3_findnext(cs : BOOLEAN) : BOOLEAN;
{---------------------------------------------------------}
{----Soundex Code Function (sound alike) }
{---------------------------------------------------------}
FUNCTION Db3_soundex(name : STRING) : STRING;
FUNCTION Db3_firstsoundex(no : INTEGER; s : STRING) : BOOLEAN;
FUNCTION Db3_nextsoundex(no : INTEGER; s : STRING) : BOOLEAN;
{---------------------------------------------------------}
{----Shaker Sort Almost Sorted *.DBF Files }
{---------------------------------------------------------}
PROCEDURE Db3_sort(no : INTEGER);
IMPLEMENTATION
{---------------------------------------------------------}
{----Error Handling }
{---------------------------------------------------------}
PROCEDURE Seternr(e : INTEGER);
BEGIN
 IF (db3_ernr=0) THEN db3_ernr:=e;
 IF db3_fatal
 THEN
 BEGIN
 Writeln;
 Writeln('Db3_01 [Error : ',db3_ernr:0,' = '+Db3_ermsg(db3_ernr)+']');
 Writeln;
 IF (db3_ernr1) THEN Db3_close;
 Halt(e);
 END;
END; {of Seternr}
{---------------------------------------------------------}
FUNCTION Db3_ermsg(nr : INTEGER) : STRING;
BEGIN
 CASE nr OF
 0 : Db3_ermsg:='No Error';
 1 : Db3_ermsg:='Error Opening File';
 2 : Db3_ermsg:='Seek Past EOF';
 3 : Db3_ermsg:='Seek Before BOF';
 4 : Db3_ermsg:='Read Past EOF';
 5 : Db3_ermsg:='Invalid Numeric Field';
 6 : Db3_ermsg:='Field Name NOT Found';
 7 : Db3_ermsg:='Invalid Header';
 8 : Db3_ermsg:='Incorrect Filesize';
 9 : Db3_ermsg:='Records to Large';
 10 : Db3_ermsg:='To many Fields';
 11 : Db3_ermsg:='Invalid Date Format';
 12 : Db3_ermsg:='Cannot Format Real';
 13 : Db3_ermsg:='Record was already deleted';
 14 : Db3_ermsg:='Record was not deleted';
 15 : Db3_ermsg:='NOT a Dbase III File';
 16 : Db3_ermsg:='Field Number NOT Found';
 17 : Db3_ermsg:='No Memofields in this file';
 18 : Db3_ermsg:='All matching records already found';
 19 : Db3_ermsg:='No *.DBF file open';
 20 : Db3_ermsg:='*.DBF already file open';
 99 : Db3_ermsg:='NOT Yet Implemented';
 ELSE Db3_ermsg:='Unkown Error';
 END;
 db3_ernr:=0;
END; {of Db3_ermsg}
{---------------------------------------------------------}
{----Types/Vars & Constants }
{---------------------------------------------------------}
TYPE
 dbheader = RECORD
 dbvers : BYTE;
 dbupdy,
 dbupdm,
 dbupdd : BYTE;
 dbnorec: LONGINT;
 dbheadl,
 dbrecl : INTEGER;
 dbres : ARRAY[1..20] OF BYTE;
 END;
 dbfield = RECORD {----Definition of Field Header}
 dbname : ARRAY[1..11] OF CHAR;
 dbtype : CHAR;
 dbadr : LONGINT;
 dblen,
 dbdec : BYTE;
 dbres : ARRAY[1..14] OF CHAR;
 END;
 fptr = RECORD {----Definition of Readbuf Index}
 fppos : WORD;
 fplen : BYTE;
 END;
CONST
 maxfield = 60; {----Max number of Fields}
 maxsize = 65000; {----Maximum Record Size}
TYPE
 rectyp = ARRAY[0..maxsize] OF CHAR; {----Record Readbuffer Type}
VAR
 f : file; {----.DBF File}
 header : dbheader; {----Space for Header}
 nofields : INTEGER; {----Number of Fields}
 fields : ARRAY[1..maxfield] OF dbfield; {----Field Definitions}
 fieldptr : ARRAY[1..maxfield] OF fptr; {----Index into Readbuffer}
 recstart : LONGINT; {----Start of Record Area}
 dbrec : ^rectyp; {----Record Buffer}
 reclen : WORD; {----Record Length}
 memo : FILE; {----Memo File}
 memopos : LONGINT; {----Location of Memo Record}
 memobuf : ARRAY[1..512] OF CHAR; {----Memo Text File buffer}
 dbsearch : ^rectyp; {----Search Record Buffer}
{---------------------------------------------------------}
{----Initialize }
{---------------------------------------------------------}
PROCEDURE Db3_open(fn : STRING);
VAR
 i : INTEGER;
 j : WORD;
 ch : CHAR;
BEGIN
 IF (dbrecNIL)
 THEN Seternr(20)
 ELSE
 BEGIN
 Assign(f,fn+'.DBF');
 {$I-} Reset(f,1); {$I+}
 IF (Ioresult0)
 THEN Seternr(1)
 ELSE
 BEGIN
 {----Dump Header}
 Blockread(f,header,32);
 Getmem(dbrec,header.dbrecl+1);
 {---Scan for Fieldnames & Recordlength}
 reclen :=1;
 nofields:=0;
 Blockread(f,ch,1);
 WHILE (nofields#13) DO
 BEGIN
 Inc(nofields);
 WITH fields[nofields] DO
 BEGIN
 dbname[1]:=ch;
 Blockread(f,dbname[2],Sizeof(dbfield)-1);
 Inc(reclen,dblen);
 Blockread(f,ch,1);
 END;
 END;
 IF (ch#13) THEN Seternr(10);
 {----Zapped file contains only a EOF}
 recstart:=Filepos(f);
 {----Set fieldptr}
 j:=1;
 FOR i:=1 TO nofields DO
 WITH fieldptr[i],fields[i] DO
 BEGIN
 fplen:=dblen;
 fppos:=j;
 Inc(j,dblen);
 END;
 {----Header Integrity Checks}
 IF NOT(header.dbvers IN [03,ドル83ドル]) THEN Seternr(15);
 IF ((header.dbheadl DIV 32)-1nofields) OR
 (header.dbreclreclen)
 THEN Seternr(7);
 {----File Size Check}
 IF (header.dbnorec*reclen(Filesize(f)-recstart-1))
 THEN
 BEGIN
 {----Truncate DBASE Slack Filespace}
 { Writeln('Truncating'); }
 Db3_Seek(header.dbnorec+1);
 {$I-} Seek(f,Filepos(f)+1); {$I+}
 IF (IOresult=0)
 THEN Truncate(f)
 ELSE Seternr(8);
 END;
 IF (reclen>Sizeof(rectyp)) THEN Seternr(9);
 IF Db3_memo
 THEN
 BEGIN
 Assign(memo,fn+'.DBT');
 {$I-} Reset(memo,1); {$I+}
 IF (IOresult0) THEN Seternr(17);
 END;
 IF (db3_ernr0) THEN Freemem(dbrec,header.dbrecl+1);
 END;
 IF (db3_ernr0)
 THEN dbrec:=NIL
 ELSE Db3_Seekbof
 END;
END; {of Db3_open}
{---------------------------------------------------------}
PROCEDURE Db3_close;
VAR
 y,m,d,dow : WORD;
BEGIN
 IF (dbrecNIL)
 THEN
 BEGIN
 {----Update *.DBF File Header}
 Getdate(y,m,d,dow);
 WITH header DO
 BEGIN
 dbupdy :=y MOD 100;
 dbupdm :=m;
 dbupdd :=d;
 dbnorec:=Db3_filesize;
 END;
 Reset(f,1);
 Blockwrite(f,header,32);
 Close(f);
 {----Cleanup Memory}
 Freemem(dbrec,header.dbrecl+1);
 IF dbsearchNIL THEN Freemem(dbsearch,header.dbrecl+1);
 dbrec :=NIL;
 dbsearch :=NIL;
 END
 ELSE Seternr(19);
END; {of DB3_close}
{---------------------------------------------------------}
{----Header Operations }
{---------------------------------------------------------}
FUNCTION Db3_memo : BOOLEAN;
BEGIN
 Db3_memo:=header.dbvers=83ドル;
END; {of Db3_memo}
{---------------------------------------------------------}
FUNCTION Db3_update : STRING;
VAR
 s : STRING;
BEGIN
 s:='dd-mm-19yy';
 s[ 1]:=Chr(Ord('0')+header.dbupdd DIV 10);
 s[ 2]:=Chr(Ord('0')+header.dbupdd MOD 10);
 s[ 4]:=Chr(Ord('0')+header.dbupdm DIV 10);
 s[ 5]:=Chr(Ord('0')+header.dbupdm MOD 10);
 s[ 9]:=Chr(Ord('0')+header.dbupdy DIV 10);
 s[10]:=Chr(Ord('0')+header.dbupdy MOD 10);
 Db3_update:=s;
END; {of Db3_update}
{---------------------------------------------------------}
FUNCTION Db3_norecs : LONGINT;
BEGIN
 Db3_norecs:=header.dbnorec;
END; {of Db3_norecs}
{---------------------------------------------------------}
FUNCTION Db3_nofields : INTEGER;
BEGIN
 Db3_nofields:=nofields;
END; {of Db3_nofields}
{---------------------------------------------------------}
FUNCTION Db3_reclen : INTEGER;
BEGIN
 Db3_reclen:=reclen;
END; {of Db3_reclen}
{---------------------------------------------------------}
{----File I/O }
{---------------------------------------------------------}
PROCEDURE Db3_seek(pos : LONGINT);
BEGIN
 {$I-} Seek(f,recstart+(pos-1)*reclen); {$I+}
 IF (Ioresult0) OR (pos<1) OR (pos>Db3_filesize+1)
 THEN
 BEGIN
 IF (pos>0)
 THEN Seternr(2)
 ELSE Seternr(3);
 END;
END; {of Db3_seek}
{---------------------------------------------------------}
FUNCTION Db3_filesize : LONGINT;
BEGIN
 Db3_filesize:=(Filesize(f)-recstart) DIV reclen;
END; {of Db3_filesize}
{---------------------------------------------------------}
FUNCTION Db3_filepos : LONGINT;
BEGIN
 Db3_filepos:=((Filepos(f)-recstart) DIV reclen)+1;
END; {of Db3_filepos}
{---------------------------------------------------------}
PROCEDURE Db3_readnext;
BEGIN
 IF EOF(f) OR Db3_Eof
 THEN Seternr(4)
 ELSE Blockread(f,dbrec^,reclen);
END; {of Db3_readnext}
{---------------------------------------------------------}
PROCEDURE Db3_read(pos : LONGINT);
BEGIN
 Db3_seek(pos);
 Db3_readnext;
END; {of Db3_read}
{---------------------------------------------------------}
PROCEDURE Db3_seekeof;
BEGIN
 Db3_Seek(Db3_filesize+1);
END; {of Db3_seekeof}
{---------------------------------------------------------}
PROCEDURE Db3_seekbof;
BEGIN
 Seek(f,recstart);
END; {of Db3_seekeof}
{---------------------------------------------------------}
FUNCTION Db3_eof : BOOLEAN;
BEGIN
 Db3_eof:=(Filepos(f)>=Filesize(f)-1);
END; {of Db3_eof}
{---------------------------------------------------------}
FUNCTION Db3_bof : BOOLEAN;
BEGIN
 Db3_bof:=Filepos(f)=recstart;
END; {of Db3_bof}
{---------------------------------------------------------}
PROCEDURE Db3_replace(no : LONGINT);
BEGIN
 Db3_seek(no);
 IF (db3_ernr=0) THEN Blockwrite(f,dbrec^[0],reclen)
END; {of Db3_append}
{---------------------------------------------------------}
PROCEDURE Db3_append;
VAR
 ch : CHAR;
BEGIN
 Db3_seek(Db3_filesize+1);
 Blockwrite(f,dbrec^[0],reclen);
 ch:=^Z;
 Blockwrite(f,ch,1);
 Db3_seek(Db3_filesize+1);
END; {of Db3_append}
{---------------------------------------------------------}
PROCEDURE Db3_delete(no : LONGINT);
BEGIN
 Db3_read(no);
 IF dbrec^[0]='*'
 THEN Seternr(13)
 ELSE dbrec^[0]:='*';
 Db3_replace(no)
END; {of Db3_delete}
{---------------------------------------------------------}
PROCEDURE Db3_undelete(no : LONGINT);
BEGIN
 Db3_read(no);
 IF dbrec^[0]=' '
 THEN Seternr(14)
 ELSE dbrec^[0]:=' ';
 Db3_replace(no)
END; {of Db3_undelete}
{---------------------------------------------------------}
PROCEDURE Db3_pack;
VAR
 i,j : LONGINT;
 ch : CHAR;
BEGIN
 j:=0;
 FOR i:=1 TO Db3_filesize DO
 BEGIN
 Db3_read(i);
 IF NOT(Db3_deleted)
 THEN
 BEGIN
 Inc(j);
 Db3_replace(j)
 END
 END;
{----New EOF Marker}
 IF (j=0)
 THEN db3_SeekBof
 ELSE Db3_read(j);
 ch:=^Z;
 Blockwrite(f,ch,1);
 Truncate(f);
 Db3_seek(1);
END; {of Db3_pack}
{---------------------------------------------------------}
PROCEDURE Db3_blankrec;
VAR
 i : INTEGER;
BEGIN
 FOR i:=0 TO reclen-1 DO dbrec^[i]:=#32;
END; {of Db3_blankrec}
{---------------------------------------------------------}
{----Field Operations }
{---------------------------------------------------------}
FUNCTION Db3_fieldname(no : INTEGER) : STRING;
VAR
 s : STRING;
 i : WORD;
BEGIN
 s:='';
 i:=1;
 IF no IN [1..nofields]
 THEN
 BEGIN
 WITH fields[no] DO
 WHILE (i<=sizeof(dbname)) AND (dbname[i]#0) DO
 BEGIN
 s:=s+dbname[i];
 Inc(i);
 END;
 END
 ELSE Seternr(16);
 Db3_fieldname:=s;
END; {of Db3_fieldname}
{---------------------------------------------------------}
FUNCTION Db3_fieldlen(no : INTEGER) : INTEGER;
BEGIN
 Db3_fieldlen:=0;
 IF no IN [1..nofields]
 THEN Db3_fieldlen:=fields[no].dblen
 ELSE Seternr(16);
END; {of Db3_fieldlen}
{---------------------------------------------------------}
FUNCTION Db3_fielddec(no : INTEGER) : INTEGER;
BEGIN
 Db3_fielddec:=0;
 IF no IN [1..nofields]
 THEN Db3_fielddec:=fields[no].dbdec
 ELSE Seternr(16)
END; {of Db3_fielddec}
{---------------------------------------------------------}
FUNCTION Db3_fieldno(name : STRING) : INTEGER;
VAR
 i,j : INTEGER;
 s : STRING;
BEGIN
 Db3_fieldno:=0;
 s:=name;
 FOR i:=1 TO Length(s) DO s[i]:=Upcase(s[i]);
 i:=1;
 WHILE (i<=nofields) AND (sDb3_fieldname(i)) DO
 Inc(i);
 IF (i>nofields)
 THEN Seternr(6)
 ELSE Db3_fieldno:=i;
END; {of Db3_fieldno}
{---------------------------------------------------------}
FUNCTION Db3_fieldtype(no : INTEGER) : CHAR;
BEGIN
 Db3_fieldtype:=#00;
 IF no IN [1..nofields]
 THEN Db3_fieldtype:=fields[no].dbtype
 ELSE Seternr(16);
END; {of Db3_fieldtype}
{---------------------------------------------------------}
FUNCTION Db3_deleted : BOOLEAN;
BEGIN
 Db3_deleted:=dbrec^[0]#32;
END; {of Db3_deleted}
{---------------------------------------------------------}
{----Field Conversions }
{---------------------------------------------------------}
FUNCTION Db3_field2str(no :INTEGER) : STRING;
VAR
 s : STRING;
 i : WORD;
BEGIN
 s:='';
 IF (no IN [1..nofields])
 THEN
 BEGIN
 s[0]:=Chr(fieldptr[no].fplen);
 Move(dbrec^[fieldptr[no].fppos],s[1],fieldptr[no].fplen);
 END
 ELSE Seternr(16);
{----Strip Trailing Spaces}
 WHILE (Length(s)>0) AND (s[Length(s)]=#32) DO Dec(s[0]);
 Db3_field2str:=s;
END; {of Db3_field2str}
{---------------------------------------------------------}
FUNCTION Db3_field2char(no :INTEGER) : CHAR;
VAR
 s : STRING;
BEGIN
 IF (Db3_fieldlen(no)=1)
 THEN s:=Db3_field2str(no)
 ELSE s:=#00;
 IF (Length(s)=0)
 THEN Db3_field2char:=#32
 ELSE Db3_field2char:=s[1];
END; {of Db3_field2char}
{---------------------------------------------------------}
FUNCTION Db3_field2logic(no : INTEGER) : BOOLEAN;
BEGIN
 Db3_field2logic:=(Db3_field2char(no)='T');
END; {of Db3_field2logic}
{---------------------------------------------------------}
FUNCTION Db3_field2num(no : INTEGER) : REAL;
VAR
 r : REAL;
 s : STRING;
 e : INTEGER;
BEGIN
 s:=Db3_field2str(no);
 WHILE (Length(s)>0) AND (s[1]=#32) DO Delete(s,1,1);
 Val(s,r,e);
 IF (e0)
 THEN Seternr(5);
 Db3_field2num:=r;
END; {of Db3_field2num}
{---------------------------------------------------------}
FUNCTION Db3_field2date(no :INTEGER) : STRING;
VAR
 s : STRING;
BEGIN
 s:='dd-mm-yyyy';
 IF (no IN [1..nofields])
 THEN
 BEGIN
 Move(dbrec^[fieldptr[no].fppos+6],s[1],2);
 Move(dbrec^[fieldptr[no].fppos+4],s[4],2);
 Move(dbrec^[fieldptr[no].fppos+0],s[7],4);
 END
 ELSE Seternr(16);
 Db3_field2date:=s;
END; {of Db3_field2date}
{---------------------------------------------------------}
FUNCTION Db3_field2soundex(no : INTEGER) : STRING;
BEGIN
 Db3_field2soundex:=Db3_soundex(Db3_field2str(no));
END; {of Db3_field2soundex}
{---------------------------------------------------------}
PROCEDURE Db3_str2field(no :INTEGER;s : STRING);
BEGIN
 IF (no IN [1..nofields])
 THEN
 BEGIN
 Fillchar(dbrec^[fieldptr[no].fppos],fieldptr[no].fplen,#32);
 WITH fields[no] DO
 IF (Length(s)>dblen)
 THEN Move(s[1],dbrec^[fieldptr[no].fppos],dblen)
 ELSE Move(s[1],dbrec^[fieldptr[no].fppos],Length(s));
 END
 ELSE Seternr(16)
END; {of Db3_str2field}
{---------------------------------------------------------}
PROCEDURE Db3_char2field(no :INTEGER;s : CHAR);
BEGIN
 Db3_str2field(no,s);
END; {of Db3_char2field}
{---------------------------------------------------------}
PROCEDURE Db3_logic2field(no : INTEGER;l : BOOLEAN);
BEGIN
 IF l
 THEN Db3_char2field(no,'T')
 ELSE Db3_char2field(no,'F')
END; {of Db3_logic2field}
{---------------------------------------------------------}
PROCEDURE Db3_num2field(no : INTEGER;n: REAL);
VAR
 s : STRING;
BEGIN
 IF (no IN [1..nofields])
 THEN
 BEGIN
 Str(n:fields[no].dblen:fields[no].dbdec,s);
 IF (Length(s)>fields[no].dblen)
 THEN Seternr(12)
 ELSE Db3_str2field(no,s);
 END
 ELSE Seternr(16)
END; {of Db3_num2field}
{---------------------------------------------------------}
PROCEDURE Db3_date2field(no :INTEGER;d : STRING);
VAR
 s : STRING;
BEGIN
 IF (Length(d)10) OR
 (d[3]'-') OR
 (d[6]'-')
 THEN Seternr(11)
 ELSE
 BEGIN
 {----dd-mm-yyyy}
 s[1]:=d[ 7];
 s[2]:=d[ 8];
 s[3]:=d[ 9];
 s[4]:=d[10];
 s[5]:=d[ 4];
 s[6]:=d[ 5];
 s[7]:=d[ 1];
 s[8]:=d[ 2];
 Db3_str2field(no,s);
 END;
END; {of Db3_date2field}
{---------------------------------------------------------}
{----Memo text field support }
{---------------------------------------------------------}
{$F+}
FUNCTION memoignore(VAR f : textrec) : INTEGER;
BEGIN
 memoignore:=0;
END; {of memoignore}
{---------------------------------------------------------}
FUNCTION memoinput(VAR f : textrec) : INTEGER;
VAR
 chread : WORD;
BEGIN
 WITH Textrec(f) DO
 BEGIN
 Blockread(memo,memobuf[1],Sizeof(memobuf),chread);
 bufpos :=0;
 bufend :=chread;
 END;
 memoinput:=0;
END; {of memoinput}
{$F-}
{---------------------------------------------------------}
PROCEDURE Assignmemo(VAR f : TEXT);
VAR
 chread : WORD;
CONST
 fminput =$D7B1;
BEGIN
 WITH Textrec(f) DO
 BEGIN
 handle :=$ffff;
 mode :=fminput;
 bufsize :=SIZEOF(memobuf);
 bufpos :=0;
 bufptr :=@memobuf;
 Blockread(memo,memobuf[1],Sizeof(memobuf),chread);
 bufpos :=0;
 bufend :=chread;
 openfunc :=@memoignore;
 inoutfunc:=@memoinput;
 flushfunc:=@memoignore;
 closefunc:=@memoignore;
 name[0] :=#00;
 END;
END; {of Assignmemo}
{---------------------------------------------------------}
PROCEDURE Db3_field2memo(no : INTEGER);
VAR
 e : INTEGER;
 s : STRING;
BEGIN
 IF Db3_memo
 THEN
 BEGIN
 s:=Db3_field2str(no);
 WHILE (Length(s)>0) AND (s[1]=#32) DO Delete(s,1,1);
 Val(s,memopos,e);
 IF (e0)
 THEN Seternr(5)
 ELSE
 BEGIN
 Seek(memo,memopos*Sizeof(memobuf));
 Assignmemo(db3_memotext);
 END;
 END
 ELSE Seternr(17);
END; {of Db3_field2memo}
{---------------------------------------------------------}
FUNCTION Db3_findfirst(cs : BOOLEAN) : BOOLEAN;
VAR
 match,
 found : BOOLEAN;
 i : INTEGER;
BEGIN
 Getmem(dbsearch,Db3_reclen+1);
 Move(dbrec^,dbsearch^,Db3_reclen);
 Db3_Seekbof;
 found:=False;
 WHILE NOT(found OR Db3_eof OR (Db3_ernr0)) DO
 BEGIN
 Db3_readnext;
 i:=0;
 match:=true;
 WHILE (i#32)
 THEN
 CASE cs OF
 TRUE : match:=( dbsearch^[i] = dbrec^[i]);
 FALSE : match:=(Upcase(dbsearch^[i])=Upcase(dbrec^[i]));
 END;
 INC(i);
 END;
 found:=match;
 END;
 Db3_findfirst:=found;
 IF (found=False)
 THEN
 BEGIN
 Freemem(dbsearch,Db3_reclen+1);
 dbsearch:=NIL;
 END;
END; {of Db3_findfirst}
{---------------------------------------------------------}
FUNCTION Db3_findnext(cs : BOOLEAN) : BOOLEAN;
VAR
 match,
 found : BOOLEAN;
 i : INTEGER;
BEGIN
 IF (dbsearch=NIL)
 THEN Seternr(18);
 found:=False;
 WHILE NOT(found OR Db3_eof OR (Db3_ernr0)) DO
 BEGIN
 Db3_readnext;
 i:=0;
 match:=true;
 WHILE (i#32)
 THEN
 CASE cs OF
 TRUE : match:=( dbsearch^[i] = dbrec^[i]);
 FALSE : match:=(Upcase(dbsearch^[i])=Upcase(dbrec^[i]));
 END;
 INC(i);
 END;
 found:=match;
 END;
 Db3_findnext:=found;
 If (found=False) AND (dbsearchNIL)
 Then
 BEGIN
 Freemem(dbsearch,Db3_reclen+1);
 dbsearch:=NIL;
 END;
END; {of Db3_findnext}
{---------------------------------------------------------}
FUNCTION Db3_soundex(name : STRING) : STRING;
VAR
 work : STRING;
 code : CHAR;
 i,j : INTEGER;
 {---------------------------------------------------------}
 FUNCTION Encode(VAR c: CHAR): CHAR;
 BEGIN
 CASE Upcase(c) OF
 'B','F','P','V': encode:='1';
 'C','G','J','K','Q','S','X','Z': encode:='2';
 'D','T': encode:='3';
 'L': encode:='4';
 'M','N': encode:='5';
 'R': encode:='6';
 'A','E','I','O','U','Y': encode:='7';
 'H','W': encode:='8';
 ELSE encode:=' ';
 END;
 END; {of Encode}
 {---------------------------------------------------------}
BEGIN
{----If we can't calculate, this is the answer}
 work:='';
{----Skip all non alpha codes in front}
 i:=1;
 WHILE (i<=length(name)) AND (Encode(name[i])=' ') DO Inc(i); {----If any alpha characters left, start calculating the SOUNDEX code} IF (i<=length(name)) THEN BEGIN {----The first alpha letter of string is the first letter of the code} work:=Upcase(name[i]); Inc(i); {----Be sure while loop precondition is correct} j:=1; code:=#00; {----Calculate the numeric part of the code, } { with a maximum of 3 digits, stop if a non } { alpha character is encountered } WHILE (i<=length(name)) AND (j<=3) AND (code' ') DO
 BEGIN
 code:=Encode(name[i]);
 {----If new code group then add the goup number}
 IF (code IN ['1'..'6']) AND (work[j]code)
 THEN
 BEGIN
 Inc(j);
 work:=work+code;
 END;
 Inc(i);
 END;
 END;
{----Return the resulting SOUNDEX code}
 Db3_soundex:=work;
END; {of Db3_soundex}
{---------------------------------------------------------}
FUNCTION Db3_firstsoundex(no : INTEGER;s : STRING) : BOOLEAN;
VAR
 found : BOOLEAN;
 sdx : STRING;
BEGIN
 Db3_Seekbof;
 sdx:=Db3_soundex(s);
 found:=False;
 WHILE NOT(found OR Db3_eof OR (Db3_ernr0)) DO
 BEGIN
 Db3_readnext;
 found:=(Pos(sdx,Db3_field2soundex(no))=1);
 END;
 Db3_firstsoundex:=found;
END; {of Db3_firstsoundex}
{---------------------------------------------------------}
FUNCTION Db3_nextsoundex(no : INTEGER; s : STRING) : BOOLEAN;
VAR
 found : BOOLEAN;
 sdx : STRING;
BEGIN
 sdx:=Db3_soundex(s);
 found:=False;
 WHILE NOT(found OR Db3_eof OR (Db3_ernr0)) DO
 BEGIN
 Db3_readnext;
 found:=(Pos(sdx,Db3_field2soundex(no))=1);
 END;
 Db3_nextsoundex:=found;
END; {of Db3_nextsoundex}
{---------------------------------------------------------}
PROCEDURE Db3_sort(no : INTEGER);
VAR
 dbsort : ^rectyp;
 swapped : BOOLEAN;
 i,j,l,r : LONGINT;
 s1,s2 : STRING;
 typ : CHAR;
 {---------------------------------------------------------}
 PROCEDURE Swap(r1,r2 : LONGINT);
 BEGIN
 {----Side Effects}
 i:=j;
 swapped:=True;
 {----the Swapping itself}
 Db3_replace(r1);
 Move(dbsort^,dbrec^,Db3_reclen);
 Db3_replace(r2);
 END; {of Swapped}
 {---------------------------------------------------------}
 FUNCTION Compare(VAR c1,c2 : STRING) : BOOLEAN;
 VAR
 i : INTEGER;
 s : STRING;
 BEGIN
 CASE typ OF
 'M',
 'N' : BEGIN
 {----Insert spaces for correct numeric compare}
 FOR i:=1 TO Db3_fieldlen(no)-Length(c1) DO Insert(#32,c1,i);
 FOR i:=1 TO Db3_fieldlen(no)-Length(c2) DO Insert(#32,c2,i);
 END;
 'L',
 'S',
 'C' : BEGIN
 {----Convert to Uppercase for correct alpha compare}
 FOR i:=1 TO Length(c1) Do c1[i]:=Upcase(c1[i]);
 FOR i:=1 TO Length(c2) Do c2[i]:=Upcase(c2[i]);
 END;
 'D' : ;
 END;
 {----Return TRUE if c2>c1}
 Compare:=(c2>c1);
 END; {of Compare}
 {---------------------------------------------------------}
BEGIN
{----Use ShakerSort on almost sorted *.DBF file}
 Getmem(dbsort,Db3_reclen+1);
 Move(dbrec^,dbsort^,Db3_reclen);
 l:=2;
 r:=Db3_filesize;
 i:=r-1;
 swapped:=TRUE;
 typ :=Db3_fieldtype(no);
 WHILE (l<=r) AND swapped DO
 BEGIN
 swapped:=False;
 {----Bubble Up}
 FOR j:=r DOWNTO l DO
 BEGIN
 {----Fetch record j-1 & save it}
 Db3_read(j-1);
 s2:=Db3_field2str(no);
 Move(dbrec^,dbsort^,Db3_reclen);
 {----Fetch record j}
 Db3_read(j);
 s1:=Db3_field2str(no);
 {----Bubble}
 IF Compare(s1,s2)
 THEN Swap(j-1,j);
 END;
 l:=i+1;
 {----Bubble Down}
 IF swapped
 THEN
 BEGIN
 FOR j:=l TO r DO
 BEGIN
 {----Fetch record j-1 & save it}
 Db3_read(j-1);
 s2:=Db3_field2str(no);
 Move(dbrec^,dbsort^,Db3_reclen);
 {----Fetch record j}
 Db3_read(j);
 s1:=Db3_field2str(no);
 {----Bubble}
 IF Compare(s1,s2)
 THEN Swap(j-1,j);
 END;
 r:=i-1;
 END;
 END;
 Freemem(dbsort,Db3_reclen+1);
 Db3_seekbof;
END; {of Db3_sort}
{---------------------------------------------------------}
BEGIN
 db3_ernr :=0;
 db3_fatal:=False;
 dbsearch :=NIL;
 dbrec :=NIL;
END.
{ DOCUMENTATION }
Db3_01.PAS is written by
 Ir. G.W. van der Vegt
 Hondbroek 57
 6121 XB Born (L)
and uploaded as public domain software because the author likes to
share it with other Turbo Pascal Users. Please keep the source the
way it is and write extentions as separate units.
This unit provides read/write access to Dbase III (Plus) *.DBF files. The
unit is uploaded as it is, the author is not responsible for any damgage
by programs using this module. The unit is, of course, tested.
Before using any of the Db3 routine a program shall call Db3_open to
initialize the file internal buffers & info. When finishing the program
should call Db3_close to close the file & cleanup the internal buffer.
All routines are documented so there's not much to say about them. Access
to the DBF file is only allowed through this unit, so the file record
isn't exported.
Records must be read by Db3_read or Db3_readnext, and written by Db3_append
or Db3_replace. All record functions use LONGINTs as parameter for addressing
records in the file.
When a record is read, one can read the field in the record by using the
record number as parameter of the Db3_field2 procedures. This record
number lies between 1 and maxfield. If one 's to be independend of the
location of the record the Db3_fieldno can be used to convert a field
name to the field number.
When writing records fill all field with Db3_2field routines and don't
forget to use Db3_undelete to initialize the deleted marker. It's of
course also possible to read a record, modify some field and replace it.
The Db3_pack routine packs the file in-place, so no temp file is created.
This unit can't create DBase III *.DBF files as it can't write the file
header & fieldefinitions. It's also impossble to change the structure of
a DBase III *.DBF database with it. This is done to keep the unit simple.
Creating & modifing databases is much easier in Dbase III Language.
This unit uses a special naming convention to be sure there's no
confict with procedures from other units. All exported names have
a three letter prefix Db3_. The 01 in the Unit name is a unique
version number.
 

AltStyle によって変換されたページ (->オリジナル) /