/* HEADFIX.C by Erik A McBeth Program will replace the bad header of a dBASE II/III/IV file with a clean one from another file. 1989.12.12 History: 1989.10.23 Version 1.2.3 of HEADFIX released on SAS 1989.10.31 Version 1.2.4 has the following changes: Allows good file to be read-only File size of bad file is calculated through ANSI stdio.h functions fgetpos()/fseek() Does fcloseall() on error condition Smaller EXE file 1989.11.10 Added prototypes and casting for portablity in v 1.2.4 1989.11.16 Changed dBASE II header length to 521 1989.11.17 v 1.2.4a released 1989.11.18 Changed return value check for fwrite and fread to check for the actual number of bytes written 1989.11.19 Got rid of pow256 function, replaced with constants changed fsize again to use ftell/fseek for unix portability, changed fcloseall() for portability 1989.12.12 Changed fsize again for speed, fseek might be slow on some machines or file sizes (speculating). Also made it so that the default extension to input files is ".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 NO_FSTAT 0 /* Set true if no fstat() in library */ #define VERSION "126" #define NAMELEN 128 #if !NO_FSTAT #include "sys\types.h" #include "sys\stat.h" #endif int hferror(char *msg,char *filename,int warning); fsize(FILE *fp,long *filelen); static FILE *bfp,*gfp; main(argc,argv) int argc; char *argv[]; { unsigned char buffer[32],*header; char badfile[NAMELEN+1],goodfile[NAMELEN+1]; unsigned int headlen,reclen; int dbase2=0; long numrec=0,filelen,estnum=0; fprintf(stderr,"HEADFIX Erik A McBeth v%s %s\n"\ "-- Repairs damaged dBASE II/III/IV file headers\n" ,VERSION,__DATE__); if (argc < 4) hferror("Usage: HEADFIX Bad File Good File # of Records | ?","",0); strcpy(badfile,argv[1]); if (!strrchr(badfile,'.') || strrchr(badfile,'.') ",badfile,0); strcpy(goodfile,argv[2]); if (!strrchr(goodfile,'.') || strrchr(goodfile,'.') ",goodfile,0); if (fsize(bfp,&filelen)) hferror("Unable to find file info on -> ",badfile,0); printf("\tReading clean header ....\n"); /* Read the first 16 chars in the good header so we can determine the length of the entire header */ if (fread(buffer,sizeof(char),16,gfp)<16) hferror("Unable to read header length from -> ",goodfile,0); dbase2 = buffer[0] == 2; /* Have a dBASE II file ? */ if (!dbase2) { headlen = buffer[8]+buffer[9]*256; /* Size of good header */ reclen = buffer[10]+buffer[11]*256; /* Record length */ } else { headlen = 521; /* The value for dBASE II headers */ reclen = buffer[6]+buffer[7]*256; } if (!(header=(unsigned char *)calloc(headlen,sizeof(char)))) hferror("Unable to allocate memory for header record","",0); if (fseek(gfp,0,SEEK_SET)) hferror("Unable to seek BOF of -> ",goodfile,0); if (fread(header,sizeof(char),headlen,gfp) ",goodfile,0); printf("\tSetting record count ....\n"); /* Estimate the number of records in the bad file */ estnum = (filelen - (long) headlen) / (long) reclen; if (argv[3][0] == '?') /* Use the computer's estimate of records */ numrec=estnum; else numrec=atol(argv[3]); /* Or ours */ /* This next block of code breaks apart our record count into 4 bytes with the low byte first, if we have a dBASE II database then we only break it into 2 bytes */ if (dbase2) { header[1]=(unsigned char)(numrec % 256L); header[2]=(unsigned char)(numrec / 256L); } else { header[4]=(unsigned char)((((numrec % 16777216L) % 65536L) % 256L)); header[5]=(unsigned char)((((numrec % 16777216L) % 65536L) / 256L)); header[6]=(unsigned char)(((numrec % 16777216L) / 65536L)); header[7]=(unsigned char)(numrec / 16777216L); } /* Warn the user so he doesn't screw up, setting the record count too low could be a disaster if he does a PACK */ if (numrec < estnum) hferror("WARNING - Number of records may be set too low","",1); if (numrec > estnum) hferror("WARNING - Number of records may be set too high","",1); printf("\tWriting clean header .... \n"); if (fwrite(header,sizeof(char),headlen,bfp) ",badfile,0); /* Print out some stats on the repaired file */ printf("\n"); printf("\tFixed file name: %s\n",badfile); printf("\t type: %s\n",dbase2 ? "dBASE II":"dBASE III/IV"); printf("\t size: %ld\n",filelen); printf("\t header size: %u\n",headlen); printf("\t record size: %u\n",reclen); printf("\t # of records: %lu\n",numrec); printf("\t estimate: %lu\n",estnum); /* We print the following warning message because dBASE II writes out files in 512 byte increments, meaning that it will round up the file size to the next multiple of 512, this would of course throw off our estimate of the number of actual records */ if (dbase2 && !(filelen % 512)) hferror("WARNING - Estimate of dBASE II records may be set too high","",1); free(header); 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; } fsize(fp,filelen) FILE *fp; long *filelen; { #if NO_FSTAT long cur_pos; if ((cur_pos=ftell(fp)) < 0) /* Get the current position */ return -1; if (fseek(fp,0,SEEK_END)) /* Go to the end of file */ return -1; if ((*filelen=ftell(fp)) < 0) /* Get the EOF position */ return -1; return fseek(fp,cur_pos,SEEK_SET); /* Go back to our original position*/ #else struct stat info; if (fstat(fileno(fp),&info)) return -1; *filelen=info.st_size; return 0; #endif } fcloseall() { fclose(bfp); fclose(gfp); return 0; }