/* HEADXTRK.C by Erik A McBeth Program will extract and create a new header from a corrupt dBASE II/III/IV header file 1989.12.12 History 1989.10.26 Most part completed 1989.11.10 Increased portability through prototypes and casts 1989.11.13 Changed method of reading and writing fields for future expansion of dBASE format. 1989.11.16 Clean up head length for dBASE 2, also End of header marker 1989.11.17 Released version 1.0.5 1989.11.18 Changed return value check for fwrite and fread to check for the actual number of bytes written 1989.11.19 Changed fcloseall() for portablity 1989.12.12 Added code to have the default file extension ".DBF" 1989.12.18 Added to the default extension routine to take into consideration those cases when we have ..\directory\filename */ #include "stdio.h" #include "stdlib.h" #include "string.h" #define VERSION "107" #define NAMELEN 128 int hferror(char *msg,char *filename,int warn); static FILE *bfp,*gfp; main(argc,argv) int argc; char *argv[]; { unsigned char buffer[32],sbuffer[4]; unsigned int headlen,reclen=1,numflds=0,db2flds=0; int dbase2=0,guess=(argc > 4); char badfile[NAMELEN+1],goodfile[NAMELEN+1]; fprintf(stderr,"HEADXTRK Erik A McBeth v%s %s\n"\ "-- Extracts headers from damaged "\ "dBASE II/III/IV file headers\n",VERSION,__DATE__); if (argc < 4) hferror( "Usage: HEADXTRK Bad File Output file Type of dBASE file (2,3,4) [/?] \n"\ " ? = HEADXTRK Guesses fields" ,"",0); strcpy(badfile,argv[1]); if (!strrchr(badfile,'.') || strrchr(badfile,'.') ",badfile,0); strcpy(goodfile,argv[2]); if (!strrchr(goodfile,'.') || strrchr(goodfile,'.') ",goodfile,0); dbase2 = (argv[3][0] == '2'); /* Have a dBASE II file ? */ printf("\tReading damaged header ....\n"); if (fseek(bfp,dbase2 ? 8:32,SEEK_SET)) hferror("Unable to seek field location in -> ",badfile,0); if (fseek(gfp,dbase2 ? 8:32,SEEK_SET)) hferror("Unable to seek field location in -> ",goodfile,0); printf("\tSelecting fields ....\n"); /* Read in the fields, stopping to ask if the field is valid for dBASE III/IV databases, with dBASE II we should read 32 of them */ while(1) { if (dbase2 && numflds==32) break; if (fread(buffer,sizeof(unsigned char),dbase2 ? 16:32,bfp)< (dbase2 ? 16:32)) break; if (!dbase2 && guess && buffer[10]) /* Valid fields have buffer[10]=0 */ break; db2flds += (buffer[0] && buffer[0] != 0xd ? 1:0); /* Check for actual dBASE 2 fields */ printf("\t %3d %-10s %c %3d %2d\n", numflds+1, buffer[0] && buffer[0] != 0xd ? (char *)buffer:"(blank)", buffer[11], buffer[dbase2 ? 12:16], buffer[dbase2 ? 15:17] ); if ((dbase2 || guess) && !((numflds+1) % 20)) { fprintf(stderr,"Hit RETURN for more fields"); gets((char *)sbuffer); } if (!dbase2) { if (guess) sbuffer[0]='Y'; else { printf("\tIs this a field? (Y/n) "); /* Ask if not dBASE II */ gets((char *)sbuffer); } /* Default the answer to yes */ if ((sbuffer[0]=='N' || sbuffer[0]=='n')) break; } /* !dbase2 */ if (fwrite(buffer,sizeof(unsigned char),dbase2 ? 16:32,gfp)< (dbase2 ? 16:32)) hferror("Unable to write field info to -> ",goodfile,0); numflds++; reclen += buffer[dbase2 ? 12:16]; /* Increase record length */ } /* Check for valid numbers of fields */ if ((dbase2 && numflds != 32) || (numflds > 128 && argv[3][0]=='3') ) /* Check for valid number of fields */ { sprintf((char *)buffer, "WARNING - Found %d fields, number of fields may be incorrect",numflds); hferror((char *)buffer,"",1); } if ((dbase2 && db2flds>=32) || !dbase2) sbuffer[0]=(char)0xd; /* End of header marker */ else sbuffer[0]='\0'; sbuffer[1]=(char)0x1a; /* End of file marker */ if (fwrite(sbuffer,sizeof(unsigned char),2,gfp)<2) hferror("Unable to write EOF marker to -> ",goodfile,0); /* Calc the header and record length */ headlen= (dbase2 ? 521:(numflds + 1)* 32 +1); /* Set up header intro info */ memset(buffer,0,sizeof(buffer)); if (dbase2) { buffer[0]=(char)2; /* Program ID */ buffer[3]=12; /* Month of last update */ buffer[4]=25; /* Day */ buffer[5]=89; /* Year */ buffer[6]=(char)(reclen % 256); /* Record length */ buffer[7]=(char)(reclen / 256); } else { buffer[0]=(char)3; /* Program ID */ buffer[1]=89; /* Year of last update*/ buffer[2]=12; /* Month of last update */ buffer[3]=25; /* Day */ buffer[8]=(char)(headlen % 256); /* Header length */ buffer[9]=(char)(headlen / 256); buffer[10]=(char)(reclen % 256); /* Record length */ buffer[11]=(char)(reclen / 256); } printf("\tWriting new header intro ....\n"); if (fseek(gfp,(long)0,SEEK_SET)) hferror("Unable to seek BOF in -> ",goodfile,0); if (fwrite(buffer,sizeof(unsigned char),dbase2 ? 8:32,gfp)< (dbase2 ? 8:32)) hferror("Unable to write header intro to -> ",goodfile,0); /* Print out some stats on the new header file */ printf("\n"); printf("\tFixed header name: %s\n",goodfile); printf("\t type: %s\n",dbase2 ? "dBASE II": argv[3][0]=='3' ? "dBASE III":"dBASE IV"); printf("\t Header size: %u\n",headlen); printf("\t Total fields: %u\n",dbase2 ? db2flds:numflds); printf("\t Record size: %u\n",reclen); fcloseall(); return 0; } hferror(msg,file,warning) char *msg; char *file; int warning; { int i; i=fprintf(stderr,"%s%s\n",msg,file); if (!warning) { fcloseall(); exit(2); } return i; } fcloseall() { fclose(gfp); fclose(bfp); return 0; }