04 Nov 2020 09:48
/*
Author Edmond Broux
In a dbf file characters from chr (0) to chr (31) indicate a corrupt record.
The program replaces corrupted characters with a space and destroys the file.
This can damage the index files.
*/
PROCEDURE Main ( xFile , xNbRecord )
LOCAL I,aRet
IF Empty(xFile)
QOut( "file name ???" )
WAIT
RETURN
ENDIF
QOut( "file name = " , xFile ) // file name
*
* aRet=FIX_CORRUPTED_DBF( xFile , xNbRecord )
* xNbRecord = number of files to be processed in one pass
* aRet[1,1] = file name
* aRet[1,2] = number of records
* aRet[1,3] = number of corrupted records
* aRet[1,4] = number of characters replaced
* for i=2 to len(aRet)
* aRet[i,1] = recno()
* aRet[i,2] = number of characters replaced
* next
*
xNbRecord=IIF( Empty(xNbRecord) , 10 , VAL(xNbRecord) )
aRet=FIX_CORRUPTED_DBF( xFile , xNbRecord )
QOut( "file name = " , aRet[1,1] ) // file name
QOut( "number of records = " , aRet[1,2] ) // number of records
QOut( "number of corrupted records = " , aRet[1,3] ) // number of corrupted records
QOut( "number of characters replaced = " , aRet[1,4] ) // number of characters replaced
QOut()
FOR I=2 to len(aRet)
QOut( "recno() = " , aRet[i,1] ) // recno()
QQOut( " number of characters replaced = " , aRet[i,2] ) // number of characters replaced
NEXT
QOut()
WAIT
RETURN
*
*
*
/* File seek mode flags fileio.ch */
#define FS_SET 0 /* Seek from beginning of file */
#define FS_RELATIVE 1 /* Seek from current file pointer */
#define FS_END 2 /* Seek from end of file */
*
*
*
FUNCTION FIX_CORRUPTED_DBF ( xFile , xNbRecord )
STATIC xChrList:=Chr(00)+Chr(01)+Chr(02)+Chr(03)+Chr(04)+Chr(05)+Chr(06)+Chr(07)+Chr(08)+Chr(09)+;
Chr(10)+Chr(11)+Chr(12)+Chr(13)+Chr(14)+Chr(15)+Chr(16)+Chr(17)+Chr(18)+Chr(19)+;
Chr(20)+Chr(21)+Chr(22)+Chr(23)+Chr(24)+Chr(25)+Chr(26)+Chr(27)+Chr(28)+Chr(29)+;
Chr(30)+Chr(31)
LOCAL I,X,hFile,xPos,aRet,xLenFile,xNbChar,xDelChr,IsBug,aHead
LOCAL xRecCount,xHeader,xFCount,xRecSize,xLenBuffer,xRecno
xFile=iif( Empty(xFile) , "" , xFile )
xNbRecord=iif( Empty(xNbRecord) , 10 , xNbRecord )
aRet={ { xFile , 0 , 0 , 0 } }
IF Empty( xFile )
RETURN aRet
ELSEIF .NOT. File( xFile )
RETURN aRet
ENDIF
*
aHead=CORRUPTED_HEADER_DBF( xFile )
*
IF .NOT. aHead[1]
RETURN aRet
ENDIF
xRecCount=aHead[4] // RecCount()
xHeader=aHead[5] // Header()
xFCount=aHead[6] // FCount()
xRecSize=aHead[7] // RecSize()
xLenBuffer=xRecSize * xNbRecord
aRet={ { xFile , xRecCount , 0 , 0 } }
hFile=FOpen( xFile , 2 + 16 ) // READ_WRITE + EXCLUSIVE
FSeek( hFile , xHeader , FS_SET )
xPos=xHeader
xLenFile=hb_FSize(xFile)
xDelChr=" "
PRIVATE xBuffer:=SPACE(xLenBuffer)
PRIVATE xChar:=" "
xRecno=0
DO WHILE .T.
NbOctet=FRead( hFile , @xBuffer , xLenBuffer )
IF NbOctet=0
xRecno=xRecCount
EXIT
ENDIF
IF .NOT. CORRUPTED_BUFFER( xBuffer )
xPos=xPos+NbOctet
IF NbOctet=xLenBuffer
xRecno=xRecno+xNbRecord
ELSE
xRecno=xRecno+Int(NbOctet / xRecSize)
ENDIF
LOOP
ENDIF
FSeek( hFile , -NbOctet , FS_RELATIVE )
xNbChar=0
IsBug="N"
FOR I=1 TO NbOctet
X=FRead( hFile , @xChar , FS_RELATIVE )
xPos=xPos+X
xNbChar=iif( xNbChar=xRecSize , 1 , xNbChar+1 )
IF I=NbOctet .AND. xPos=xLenFile .AND. xChar=Chr(26) // caractere valide fin de fichier
EXIT
ENDIF
IF xNbChar=1
IsBug="N"
xDelChr=xChar
xRecno=xRecno+1
ENDIF
IF .NOT. xChar $ xChrList // valid character
IF xNbChar=xRecSize .AND. IsBug="O"
DEL_CORRUPTED_RECORD( hFile , xDelChr , xRecSize )
ENDIF
LOOP
ENDIF
IF IsBug="N"
aRet[1,3]=aRet[1,3]+1 // number of corrupted records
AAdd( aRet , { xRecno , 0 } )
IsBug="O"
ENDIF
aRet[1,4]=aRet[1,4]+1 // number of characters replaced
ATail(aRet)[2]=ATail(aRet)[2]+1 // number of characters replaced
// replace the wrong character with a space
FSeek( hFile , -1 , FS_RELATIVE )
FWrite( hFile , " " )
IF xNbChar=xRecSize
// delete corrupted record
DEL_CORRUPTED_RECORD( hFile , xDelChr , xRecSize )
ENDIF
NEXT
ENDDO
FClose( hFile )
RETURN aRet
*
*
*
FUNCTION DEL_CORRUPTED_RECORD ( hFile , xDelChr , xRecSize )
IF xDelChr<>"*"
FSeek( hFile , -(xRecSize) , FS_RELATIVE )
FWrite( hFile , "*" )
FSeek( hFile , (xRecSize-1) , FS_RELATIVE )
RETURN .T.
ENDIF
RETURN .F.
*
*
*
FUNCTION CORRUPTED_BUFFER ( xBuffer )
STATIC Chr_00 := Chr( 00 )
STATIC Chr_01 := Chr( 01 )
STATIC Chr_02 := Chr( 02 )
STATIC Chr_03 := Chr( 03 )
STATIC Chr_04 := Chr( 04 )
STATIC Chr_05 := Chr( 05 )
STATIC Chr_06 := Chr( 06 )
STATIC Chr_07 := Chr( 07 )
STATIC Chr_08 := Chr( 08 )
STATIC Chr_09 := Chr( 09 )
STATIC Chr_10 := Chr( 10 )
STATIC Chr_11 := Chr( 11 )
STATIC Chr_12 := Chr( 12 )
STATIC Chr_13 := Chr( 13 )
STATIC Chr_14 := Chr( 14 )
STATIC Chr_15 := Chr( 15 )
STATIC Chr_16 := Chr( 16 )
STATIC Chr_17 := Chr( 17 )
STATIC Chr_18 := Chr( 18 )
STATIC Chr_19 := Chr( 19 )
STATIC Chr_20 := Chr( 20 )
STATIC Chr_21 := Chr( 21 )
STATIC Chr_22 := Chr( 22 )
STATIC Chr_23 := Chr( 23 )
STATIC Chr_24 := Chr( 24 )
STATIC Chr_25 := Chr( 25 )
STATIC Chr_26 := Chr( 26 )
STATIC Chr_27 := Chr( 27 )
STATIC Chr_28 := Chr( 28 )
STATIC Chr_29 := Chr( 29 )
STATIC Chr_30 := Chr( 30 )
STATIC Chr_31 := Chr( 31 )
*
* in a dbf file characters from chr (0) to chr (31) indicate a corrupt record.
*
IF Chr_00 $ xBuffer ; RETURN .T.
ELSEIF Chr_01 $ xBuffer ; RETURN .T.
ELSEIF Chr_02 $ xBuffer ; RETURN .T.
ELSEIF Chr_03 $ xBuffer ; RETURN .T.
ELSEIF Chr_04 $ xBuffer ; RETURN .T.
ELSEIF Chr_05 $ xBuffer ; RETURN .T.
ELSEIF Chr_06 $ xBuffer ; RETURN .T.
ELSEIF Chr_07 $ xBuffer ; RETURN .T.
ELSEIF Chr_08 $ xBuffer ; RETURN .T.
ELSEIF Chr_09 $ xBuffer ; RETURN .T.
ELSEIF Chr_10 $ xBuffer ; RETURN .T.
ELSEIF Chr_11 $ xBuffer ; RETURN .T.
ELSEIF Chr_12 $ xBuffer ; RETURN .T.
ELSEIF Chr_13 $ xBuffer ; RETURN .T.
ELSEIF Chr_14 $ xBuffer ; RETURN .T.
ELSEIF Chr_15 $ xBuffer ; RETURN .T.
ELSEIF Chr_16 $ xBuffer ; RETURN .T.
ELSEIF Chr_17 $ xBuffer ; RETURN .T.
ELSEIF Chr_18 $ xBuffer ; RETURN .T.
ELSEIF Chr_19 $ xBuffer ; RETURN .T.
ELSEIF Chr_20 $ xBuffer ; RETURN .T.
ELSEIF Chr_21 $ xBuffer ; RETURN .T.
ELSEIF Chr_22 $ xBuffer ; RETURN .T.
ELSEIF Chr_23 $ xBuffer ; RETURN .T.
ELSEIF Chr_24 $ xBuffer ; RETURN .T.
ELSEIF Chr_25 $ xBuffer ; RETURN .T.
ELSEIF Chr_26 $ xBuffer ; RETURN .T.
ELSEIF Chr_27 $ xBuffer ; RETURN .T.
ELSEIF Chr_28 $ xBuffer ; RETURN .T.
ELSEIF Chr_29 $ xBuffer ; RETURN .T.
ELSEIF Chr_30 $ xBuffer ; RETURN .T.
ELSEIF Chr_31 $ xBuffer ; RETURN .T.
ENDIF
RETURN .F.
*
*
*
/*
FUNCTION CORRUPTED_BUFFER ( xBuffer )
LOCAL I
FOR I=0 TO 31
IF Chr(I) $ xBuffer
RETURN .T.
ENDIF
NEXT
RETURN .T.
*/
*
*
*
FUNCTION CORRUPTED_HEADER_DBF ( XFILE )
LOCAL I,X,hFile,aVerDbf,xVerDbf
LOCAL xYear,xMonth,xDay,xUpdate
LOCAL xRecCount,xHeader,xRecSize
LOCAL aHead
* structure du fichier dbf
*
* structure de l'entete
* 32 premiers octets du fichier :
* 1 type de dbf
* 2 annee de la derniere mise a jour
* 3 mois de la derniere mise a jour
* 4 jour de la derniere mise a jour
* 5,6,7,8 nombre de fiches saisies
* 9,10 longueur de l'entete header()
* 11,12 longueur d'une fiche recsize()
* 13,,,,32
* longueur de l'entete header() = 32 + (32*nombre de champs) + 2
* puis 2 octets chr(13)+chr(0)
* puis 32 octets par champs
*
* ensuite les fiches saisies
* le premier caractere de la fiche indique si la fiche est valide ou supprimee
* l'espace " " = valide, l'etoile "*" = fiche supprimee
* ensuite tous les champs de la fiche sans aucun separateur
*
* fin de fichier avec chr(26)
*
* File header
* |=======================|
* 1 | Dbf Version Number |
* |-----------------------|
* 2 | Date of last update |
* 3 | YYMMDD |
* 4 | |
* |-----------------------|
* 5 | Number of records |
* 6 | in data file |
* 7 | ( 32 bits ) |
* 8 | |
* |-----------------------|
* 9 | Length of header |
* 10 | structure ( 16 bits ) |
* |-----------------------|
* 11 | Length of each record |
* 12 | ( 16 bits ) |
* |-----------------------|
* hexa , bit masq , ascci
aVerDbf={ { 0x02 , "00000010" , 2 , "" , "foxbase" } ,;
{ 0x03 , "00000011" , 3 , "" , "file without dbt" } ,;
{ 0x04 , "00000100" , 4 , "" , "dbase IV without memofile" } ,;
{ 0x05 , "00000101" , 5 , "" , "dbase V without memofile" } ,;
{ 0x07 , "00000111" , 7 , "" , "visual object for dbase III without memofile" } ,;
{ 0x30 , "00110000" , 48 , "0" , "visual foxpro" } ,;
{ 0x30 , "00110000" , 48 , "0" , "visual foxpro with DBC" } ,;
{ 0x31 , "00110001" , 49 , "1" , "visual foxpro with autoincrement field" } ,;
{ 0x43 , "01000011" , 67 , "C" , ".dbv memo var size Flagship" } ,;
{ 0x7B , "01111011" ,123 , "{" , "dbase IV with memo" } ,;
{ 0x83 , "10000011" ,131 , "" , "file with dbt" } ,;
{ 0x83 , "10000011" ,131 , "" , "dbase III with memo" } ,;
{ 0x87 , "10000111" ,135 , "" , "visual object for dbase III with memofile" } ,;
{ 0x8B , "10001011" ,139 , "" , "dbase IV with memo" } ,;
{ 0x8E , "10001110" ,142 , "" , "dbase IV with SQL table" } ,;
{ 0xB3 , "10110011" ,179 , "" , ".dbv memo and dbt memo flagship" } ,;
{ 0xE5 , "11100101" ,229 , "" , "clipper six driver with SMT memo" } ,;
{ 0xF5 , "11110101" ,245 , "" , "foxpro with memo " } ,;
{ 0xFB , "11111011" ,251 , "" , "foxpro ???" } }
xYear=0
xMonth=0
xDay=0
xUpdate=""
xRecCount=""
xHeader=""
xRecSize=""
*
*
aHead={ .F. , "fichier de type inconnu" , "00000000" , 0 , 0 , 0 , 0 , 0 }
*
*
PRIVATE xChr:=" "
hFile := FOpen( XFILE , 2+16) // READ_WRITE + EXCLUSIVE
FSeek( hFile , 0 , 0 )
FOR I=1 TO 32
X=FRead( hFile , @xChr , FS_RELATIVE )
IF X=0
RETURN aHead
ENDIF
IF I=1
IF Asc(xChr)=0
RETURN aHead
ENDIF
xVerDbf=""
FOR X=1 TO LEN(aVerDbf)
IF Asc(xChr)=aVerDbf[X,3]
xVerDbf=aVerDbf[X,5]
EXIT
ENDIF
NEXT
IF Empty(xVerDbf)
RETURN aHead
ENDIF
aHead[2]=xVerDbf
ELSEIF I=2
xYear=1900+Asc(xChr)
IF xYear<1900 .AND. xYear>2155
RETURN aHead
ENDIF
ELSEIF I=3
xMonth=Asc(xChr)
IF xMonth<0 .AND. xMonth>12
RETURN aHead
ENDIF
ELSEIF I=4
xDay=Asc(xChr)
IF xDay<0 .AND. xDay>31
RETURN aHead
ENDIF
xUpdate=STRZERO(xYear,4)+STRZERO(xMonth,2)+STRZERO(xDay,2)
IF Empty( hb_StoD(xUpdate) )
RETURN aHead
ENDIF
aHead[3]=CToD(xUpdate) // LUpdate()
ELSEIF I>=5 .AND. I<=8
xRecCount=xRecCount+xChr
IF I=8
aHead[4]=Bin2U(xRecCount) // RecCount() Bin2U hbxpp.lib
ENDIF
ELSEIF I>=9 .AND. I<=10
xHeader=xHeader+xChr
IF I=10
IF Bin2L(xHeader)<66 // 32 + 32 pour 1 champ + 2 fin de liste des champs
RETURN aHead
ENDIF
aHead[5]=Bin2L(xHeader) // Header()
aHead[6]=(aHead[5]-32-2) / 32 // FCount()
ENDIF
ELSEIF I>=11 .AND. I<=12
xRecSize=xRecSize+xChr
IF I=12
IF Bin2L(xRecSize)<2 // au moins deux caracteres
RETURN aHead
ENDIF
aHead[7]=Bin2L(xRecSize) // RecSize()
ENDIF
ENDIF
NEXT
FClose( hFile )
aHead[1]=.T.
RETURN aHead
*
*
*