#include #include #include #include #include #define BUF_SIZ 2048 #define BSIZE 32760 #define MAX_SUB_FILES 4000 #define demand(fact, remark) {\ if (!(fact)) {\ fprintf(stderr, "\nError -> " #fact "\n");\ fprintf(stderr, #remark "\n");\ perror("error");\ fcloseall();\ _setcursortype(_NORMALCURSOR);\ abort();\ exit(1);\ }\ } /************************** Variables *************************************/ char buffer[BUF_SIZ]; /* buffer for reading and writing files */ char outbuf[BSIZE]; /* disk output buffer */ typedef struct{ /* Structure to hold file info */ unsigned long int position; /* location of file in LZH from beginning */ char filenum; /* subfile number file will be written to */ unsigned long int size; /* file size */ char *name; /* file name - allocated dynamically */ unsigned long int size2; unsigned long int position2; } record; record *file[MAX_SUB_FILES]; /* array of pointers to file records */ unsigned long int fsize; /* used to hold various file sizes */ unsigned long int nsize[256]; /* subfile lengths */ unsigned long int i; /* iteration variable */ unsigned long int skip; /* used to hold skip file length */ unsigned long int ovrwrt; /* overwrite flag(0=dont ovrwrt) */ unsigned long int tsize; /* target file size(max for subfiles */ unsigned long int bcount; /* byte count: used for file buffer */ unsigned int position; unsigned int skipper; unsigned int fsize2; unsigned int rotaten; char flag; unsigned long int startdir=0; unsigned long int dirsize=0; char *strptr; unsigned char subfile; /* subfile number */ unsigned char max; /* max = total number of subfiles */ int count; /* count = number of files in archive */ int count2; int str_len; unsigned long int total_size; /* accumulator -total size of a subfile */ unsigned int hsize; /* header size */ int j,k; /* loop counters */ char temp[MAXPATH]; /* temp string storage */ char prefix[MAXPATH]; /* prefix for target files */ char suffix[4]; /* holds subfile number suffix 1,2,3,...*/ char ifile[MAXPATH]; /* name of source file */ char tfile[MAXPATH]; /* temp storage for manip file names*/ char ans; /* answer for: Overwrite file? */ char drive[MAXDRIVE]; char dir[MAXDIR]; char comment[85]; char iname[MAXFILE]; char oname[MAXFILE+8]; char ext[MAXEXT]; char saveext[MAXEXT]; int flags; char trunc_flag=0; FILE *fp,*sfp; void sortfs(void); void help(void); /********* MAIN PROGRAM **********/ void main(int argc, char *argv[]) /** argc = number of command line args ***/ { /**** argv = array containing actual args **/ int lastarg=argc-1; if(argc<3) help(); if(strtol(argv[lastarg],NULL,10)<0) { puts("Error: Invalid file size"); exit(1);} if(!strcmp(argv[lastarg],"1.44M") || !strcmp(argv[lastarg],"1.44m")\ || !strcmp(argv[lastarg],"1.4M")|| !strcmp(argv[lastarg],"1.4m")) tsize=1457664; else if(!strcmp(argv[lastarg],"720K") || !strcmp(argv[lastarg],"720k")) tsize=730112; else if(!strcmp(argv[lastarg],"1.2M") || !strcmp(argv[lastarg],"1.2m")) tsize=1213952; else if(!strcmp(argv[lastarg],"360K") || !strcmp(argv[lastarg],"360k")) tsize=362496; else tsize=strtoul(argv[lastarg],NULL,10); if(tsize<=0) help(); strcpy(ifile,argv[1]); flags=fnsplit(ifile,drive,dir,iname,ext); if(!(flags & EXTENSION)){ /* If no extension on source file */ strcpy(tfile,ifile); strcat(tfile, ".LZH"); // try LZH fp=fopen(tfile, "rb"); if(!fp){ strcpy(tfile,ifile); strcat(tfile, ".ARJ"); // try ARJ fp=fopen(tfile, "rb"); } if(!fp){ strcpy(tfile,ifile); // try ZIP strcat(tfile, ".ZIP"); fp=fopen(tfile, "rb"); } strcpy(ifile, tfile); fclose(fp); } fp=fopen(ifile, "rb"); // Open source file demand(fp!=NULL, Could not open source file); strupr(ifile); // conv to upper case flags=fnsplit(ifile,drive,dir,iname,ext); strcpy(saveext, ext); // save file name extension /********************************* Search LZH file ************************/ /* 1 byte - header size 1 byte - header checksum 5 bytes - file header/signature 4 bytes - compressed size 4 bytes - original size 4 bytes - date/time 2 bytes - file attributes n bytes - file path\name (ends with x00) n bytes - file data . . . */ printf("\n...searching file %s\n",ifile); skip=0; count=0; if(!strcmp(ext,".LZH")) { while(!feof(fp)){ demand(countposition=skip; hsize=fgetc(fp); demand(!ferror(fp),Read error); if(hsize==0) break; if(feof(fp)) break; fgetc(fp); if(feof(fp)) break; fgets(temp,6,fp); demand(!strncmp(temp,"-lh",3),LZH file has problems); fread(&fsize,sizeof(long),1,fp); file[count]->size=fsize+hsize+2; file[count]->filenum=0; skip+=file[count]->size; fseek(fp, (long)10, SEEK_CUR); fgets(temp,fgetc(fp)+1,fp); file[count]->name = malloc(strlen(temp)+1); demand(file[count]->name!=NULL, Out of memory!); strcpy(file[count]->name, temp); count++; fseek(fp, fsize+5, SEEK_CUR); } } /*********************** SEARCH ARJ FILE ********************************** ARJ archives contains two types of header blocks: Archive main header - This is located at the head of the archive Local file header - This is located before each archived file Structure of main header (low order byte first): Bytes Description ----- ------------------------------------------------------------------- 2 header id (main and local file) = 0x60 0xEA 2 basic header size (from 'first_hdr_size' thru 'comment' below) = first_hdr_size + strlen(filename) + 1 + strlen(comment) + 1 = 0 if end of archive maximum header size is 2600 1 first_hdr_size (size up to and including 'extra data') 1 archiver version number 1 minimum archiver version to extract 1 host OS (0 = MSDOS, 1 = PRIMOS, 2 = UNIX, 3 = AMIGA, 4 = MAC-OS) (5 = OS/2, 6 = APPLE GS, 7 = ATARI ST, 8 = NEXT) (9 = VAX VMS) 1 arj flags (0x01 = NOT USED) (0x02 = OLD_SECURED_FLAG) (0x04 = VOLUME_FLAG) indicates presence of succeeding volume (0x08 = NOT USED) (0x10 = PATHSYM_FLAG) indicates archive name translated ("\" changed to "/") (0x20 = BACKUP_FLAG) indicates backup type archive (0x40 = SECURED_FLAG) 1 security version (2 = current) 1 file type (must equal 2) 1 reserved 4 date time when original archive was created 4 date time when archive was last modified 4 archive size (currently used only for secured archives) 4 security envelope file position 2 filespec position in filename 2 length in bytes of security envelope data 2 (currently not used) ? (currently none) ? filename of archive when created (null-terminated string) ? archive comment (null-terminated string) 4 basic header CRC 2 1st extended header size (0 if none) ? 1st extended header (currently not used) 4 1st extended header's CRC (not present when 0 extended header size) Structure of local file header (low order byte first): Bytes Description ----- ------------------------------------------------------------------- 2 header id (main and local file) = 0x60 0xEA 2 basic header size (from 'first_hdr_size' thru 'comment' below) = first_hdr_size + strlen(filename) + 1 + strlen(comment) + 1 = 0 if end of archive maximum header size is 2600 1 first_hdr_size (size up to and including 'extra data') 1 archiver version number 1 minimum archiver version to extract 1 host OS (0 = MSDOS, 1 = PRIMOS, 2 = UNIX, 3 = AMIGA, 4 = MAC-OS) (5 = OS/2, 6 = APPLE GS, 7 = ATARI ST, 8 = NEXT) (9 = VAX VMS) 1 arj flags (0x01 = GARBLED_FLAG) indicates passworded file (0x02 = NOT USED) (0x04 = VOLUME_FLAG) indicates continued file to next volume (file is split) (0x08 = EXTFILE_FLAG) indicates file starting position field (for split files) (0x10 = PATHSYM_FLAG) indicates filename translated ("\" changed to "/") (0x20 = BACKUP_FLAG) indicates file marked as backup 1 method (0 = stored, 1 = compressed most ... 4 compressed fastest) 1 file type (0 = binary, 1 = 7-bit text) (3 = directory, 4 = volume label) 1 reserved 4 date time modified 4 compressed size 4 original size (this will be different for text mode compression) 4 original file's CRC 2 filespec position in filename 2 file access mode 2 host data (currently not used) ? extra data 4 bytes for extended file starting position when used (these bytes are present when EXTFILE_FLAG is set). 0 bytes otherwise. ? filename (null-terminated string) ? comment (null-terminated string) 4 basic header CRC 2 1st extended header size (0 if none) ? 1st extended header (currently not used) 4 1st extended header's CRC (not present when 0 extended header size) ... ? compressed file ****************************************************************************/ if(!strcmp(ext,".ARJ")) { /* skip main header */ file[count]=malloc(sizeof(record)); file[count]->name = malloc(100); demand(file[count]->name!=NULL, Out of memory!); demand(file[count]!=NULL, Out of memory); file[count]->position=skip; /* save file position */ file[count]->filenum=1; strcpy(file[count]->name,"HEADER"); fread(&hsize,sizeof(int),1,fp); demand(hsize==0xEA60, ARJ main header file is corrupt); fread(&hsize,sizeof(long),1,fp); fseek(fp, (long)hsize+2, SEEK_CUR); fread(&position, sizeof(int),1,fp); /* read file position(skip path) */ fseek(fp, (long)position, SEEK_CUR); skip=hsize+10+position; file[count++]->size=skip; while(!feof(fp)){ /*** get files ***/ demand(countposition=skip; /* save file position */ fread(&hsize,sizeof(int),1,fp); demand(hsize==0xEA60, ARJ local hdr file is corrupt); /* verify header info */ fread(&skipper,sizeof(int),1,fp); /* get header size */ skip+=skipper+4; file[count]->size=skipper+4; hsize=(long)fgetc(fp); /* get 1st header size */ demand(!ferror(fp), read error); if(hsize==0) break; if(feof(fp)) break; fseek(fp,(long)11, SEEK_CUR); fread(&fsize,sizeof(long),1,fp); /* read compressed file size */ file[count]->filenum=0; fseek(fp, (long)8, SEEK_CUR); fread(&position, sizeof(int),1,fp); /* read file position(skip path) */ fseek(fp, (long)hsize-26+position, SEEK_CUR); //strptr=(char *)&(file[count]->name); strptr = temp; do *strptr =fgetc(fp); while(*strptr++!=0); file[count]->name = malloc(strlen(temp)+1); demand(file[count]->name!=NULL, out of memory!); strcpy(file[count]->name, temp); while(fgetc(fp)!=0); fseek(fp, (long)4, SEEK_CUR); fread(&position,sizeof(int),1,fp); file[count]->size+=fsize+6+position; skip+=fsize+6+position; count++; fseek(fp, fsize+position, SEEK_CUR); } total_size=0; } /*********************** SEARCH ZIP FILE *********************************** General Format of a ZIP file ---------------------------- Files stored in arbitrary order. Overall zipfile format: [local file header + file data + data_descriptor] . . . [central directory] end of central directory record A. Local file header: local file header signature 4 bytes (0x04034b50) version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes filename length 2 bytes extra field length 2 bytes filename (variable size) extra field (variable size) B. Data descriptor: crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes This descriptor exists only if bit 3 of the general purpose bit flag is set (see below). It is byte aligned and immediately follows the last byte of compressed data. This descriptor is used only when it was not possible to seek in the output zip file, e.g., when the output zip file was standard output or a non seekable device. C. Central directory structure: [file header] . . . end of central dir record File header: central file header signature 4 bytes (0x02014b50) version made by 2 bytes version needed to extract 2 bytes general purpose bit flag 2 bytes compression method 2 bytes last mod file time 2 bytes last mod file date 2 bytes crc-32 4 bytes compressed size 4 bytes uncompressed size 4 bytes filename length 2 bytes extra field length 2 bytes file comment length 2 bytes disk number start 2 bytes internal file attributes 2 bytes external file attributes 4 bytes relative offset of local header 4 bytes filename (variable size) extra field (variable size) file comment (variable size) End of central dir record: end of central dir signature 4 bytes (0x06054b50) number of this disk 2 bytes number of the disk with the start of the central directory 2 bytes total number of entries in the central dir on this disk 2 bytes total number of entries in the central dir 2 bytes size of the central directory 4 bytes offset of start of central directory with respect to the starting disk number 4 bytes zipfile comment length 2 bytes zipfile comment (variable size) ****************************************************************************/ if(!strcmp(ext,".ZIP")) { /* skip main header */ while(!feof(fp)){ /*** get files ***/ fread(&fsize,sizeof(long),1,fp); if(fsize==0x04034b50) /* verify header info LOCAL*/ { demand(countposition=skip; /* save file position */ fseek(fp, (long)14, SEEK_CUR); fread(&fsize,sizeof(long),1,fp); /* get file size */ file[count]->size=fsize; fseek(fp, (long)4, SEEK_CUR); fread(&skipper,sizeof(int),1,fp); /* get file name len */ fread(&hsize,sizeof(int),1,fp); /* get extra len */ file[count]->size+=hsize; if(feof(fp)) break; file[count]->name = malloc(skipper+1); demand(file[count]->name!=NULL, out of memory!); fgets(file[count]->name,skipper+1,fp); /* get file name len */ fseek(fp,file[count]->size, SEEK_CUR); file[count]->size+=skipper+30; skip+=file[count]->size; file[count]->filenum=0; count++; } else if(fsize==0x02014b50) /* verify header info CENTRALDIR*/ { file[count2]->position2=skip; /* save file position */ fseek(fp, (long)24, SEEK_CUR); fread(&fsize2,sizeof(int),1,fp); /* get file name size */ if(feof(fp)) break; fread(&skipper,sizeof(int),1,fp); /* get extra len */ if(feof(fp)) break; fread(&hsize,sizeof(int),1,fp); /* get comment len */ if(feof(fp)) break; fseek(fp,(long)8, SEEK_CUR); fread(&startdir,sizeof(long),1,fp); /* get position */ while(file[rotaten]->position!=startdir) rotaten = (rotaten+1) % count; file[rotaten]->size2=hsize+skipper+fsize2+46; file[rotaten]->size+=file[rotaten]->size2; fseek(fp, (long)fsize2+skipper+hsize, SEEK_CUR); if(feof(fp)) break; skip+=file[rotaten]->size2; count2++; } else if(fsize==0x06054b50) /* verify header info ENDOFCENTRALDIR*/ { fsize=skip; /* save file position */ demand(count==count2, ZIP file is damaged); fseek(fp, (long)16, SEEK_CUR); fread(&fsize2,sizeof(int),1,fp); /* get file name size */ if(feof(fp)) break; fseek(fp, (long)fsize2, SEEK_CUR); break; } else demand(1==0, ZIP file is damaged); } } fclose(fp); if(count<2) { printf("%s only contains one file!\n",ifile); puts("File cannot be sized! Use a physical file splitting utility"); exit(1); } sortfs(); /*** Sort file names by size ***/ printf("...sizing to %lu bytes\n",tsize); if(!strcmp(saveext,".LZH") && tsize<=file[0]->size) { printf("Error: Impossible to size archive to %lu bytes.\n",tsize); printf("%s contains a file, %s, that is %lu bytes compressed.\n",ifile\ ,file[0]->name,file[0]->size); printf("Target file size must be at least %lu bytes to accomodate this file.\n"\ ,file[0]->size+1); printf("If you require a %lu byte file size use a physical file splitting utility\n",tsize); exit(1); } if(!strcmp(saveext,".ARJ") && tsize < file[0]->size+4+file[1]->size) { printf("Error: Impossible to size archive to %lu bytes.\n",tsize); printf("%s contains a file, %s, that is %lu bytes compressed.\n",ifile\ ,file[1]->name,file[1]->size); printf("Target file size must be at least %lu bytes to accomodate this file\n"\ ,file[0]->size+4+file[1]->size); printf("If you require a %lu byte file size use a physical file splitting utility\n",tsize); exit(1); } if(!strcmp(saveext,".ZIP") && tsize < file[0]->size+22+fsize2) { printf("Error: Impossible to size archive to %lu bytes.\n",tsize); printf("%s contains a file, %s, that is %lu bytes compressed.\n",ifile\ ,file[0]->name,file[0]->size); printf("Target file size must be at least %lu bytes to accomodate this file\n"\ ,file[0]->size+22+fsize2); printf("If you require a %lu byte file size use a physical file splitting utility\n",tsize); exit(1); } max=0; /************************* Assign subfiles ****************************/ if(!strcmp(ext,".ARJ")) for(j=0;j<255;nsize[j++]=file[0]->size+4); else if(!strcmp(ext,".ZIP")) for(j=0;j<255;nsize[j++]=22+fsize2); else if(!strcmp(ext,".LZH")) for(j=0;j<255;nsize[j++]=1); for(j=0;jfilenum) { if(nsize[subfile]+file[j]->size <= tsize ) { nsize[subfile]+=file[j]->size; file[j]->filenum = subfile; if(subfile>max) max=subfile; } else { subfile++; if(subfile>254) puts("Can't create more than 254 subfiles!"); demand(subfile<=254, Target file size too small - Use a larger target size); } } } /********************* Write Subfiles ***********************************/ if(max<2) {puts("Nothing to do!");exit(0);} if(argc==4) { flags=fnsplit(argv[2],drive,dir,oname,ext); if(!(flags & FILENAME)) strncpy(oname,iname,8); } else { trunc_flag=1; strncpy(oname,iname,7); /** assume source name ***/ } if(max<10) sprintf(suffix,"%1i",(short)max); else if(max<100) sprintf(suffix,"%02i",(short)max); else sprintf(suffix,"%03i",(short)max); while(strlen(oname)+strlen(suffix)>8) { if(!trunc_flag) { puts(" **** Warning: Output filename prefix had to be truncated ****"); trunc_flag=1; } oname[strlen(oname)-1]=NULL; } fnmerge(prefix,drive,dir,oname,""); fp=fopen(ifile,"rb"); demand(fp!=NULL,"Unable to open source file"); for(j=0;jfilenum!=j+1) continue; if(k==0 && !strcmp(saveext,".ARJ")) file[0]->filenum++; str_len=strlen(file[k]->name); clreol(); printf("%s",file[k]->name); while(str_len>0) { printf("\b"); str_len--; } fseek(fp, file[k]->position, SEEK_SET); demand(!ferror(fp), Read error); bcount=file[k]->size; if(!strcmp(saveext,".ZIP")) { bcount-=file[k]->size2; file[k]->position=total_size; } total_size+=bcount; while(bcount) { if(bcount>=BUF_SIZ) { demand(fread(buffer,1,BUF_SIZ,fp)==BUF_SIZ,Read error); demand(fwrite(buffer,1,BUF_SIZ,sfp)==BUF_SIZ,Write error); demand(!ferror(sfp),Write error); bcount-=BUF_SIZ; } else { demand(fread(buffer,1,bcount,fp)==bcount,Read error); demand(fwrite(buffer,1,bcount,sfp)==bcount,Write error); demand(!ferror(sfp),Write error) bcount=0; } } } if(!strcmp(saveext,".LZH")) { fputc(0,sfp); total_size++; demand(!ferror(sfp),Write error); } if(!strcmp(saveext,".ARJ")) { fputc(0x60,sfp); fputc(0xEA,sfp); fputc(0x00,sfp); fputc(0x00,sfp); total_size+=4; demand(!ferror(sfp),Write error); } if(!strcmp(saveext,".ZIP")) { /* Write Central ZIP dir */ count2=0; startdir=total_size; for(k=0;kfilenum!=j+1) continue; fseek(fp, file[k]->position2, SEEK_SET); demand(!ferror(fp), Read error); demand(fread(buffer,1,42,fp)==42,Read error); demand(fwrite(buffer,1,42,sfp)==42,Write error); demand(!ferror(sfp),Write error) demand(fwrite(&(file[k]->position),sizeof(long),1,sfp)==1,Write error); demand(!ferror(sfp),Write error) fseek(fp,(long)4,SEEK_CUR); demand(fread(buffer,file[k]->size2-46,1,fp)==1,Read error); demand(fwrite(buffer,file[k]->size2-46,1,sfp)==1,Write error); demand(!ferror(sfp),Write error) total_size+=file[k]->size2; count2++; } dirsize=total_size-startdir; fseek(fp, fsize, SEEK_SET); /* write end of dir */ demand(!ferror(fp), Read error); demand(fread(buffer,1,8,fp)==8,Read error); /* 8 bytes */ demand(fwrite(buffer,1,8,sfp)==8,Write error); demand(!ferror(sfp),Write error) demand(fwrite(&count2,sizeof(int),1,sfp)==1,Write error); demand(!ferror(sfp),Write error); demand(fwrite(&count2,sizeof(int),1,sfp)==1,Write error); demand(!ferror(sfp),Write error); demand(fwrite(&dirsize,sizeof(long),1,sfp)==1,Write error); demand(!ferror(sfp),Write error); demand(fwrite(&startdir,sizeof(long),1,sfp)==1,Write error); demand(!ferror(sfp),Write error); demand(fwrite(&fsize2,sizeof(int),1,sfp)==1,Write error); demand(!ferror(sfp),Write error); fseek(fp, (long)14, SEEK_CUR); demand(fread(buffer,1,fsize2,fp)==fsize2,Read error); demand(fwrite(buffer,1,fsize2,sfp)==fsize2,Write error); demand(!ferror(sfp),Write error) total_size+=22+fsize2; } fflush(sfp); fclose(sfp); demand(!ferror(sfp),Write error); clreol(); printf("%7lu bytes\n",total_size); _setcursortype(_NORMALCURSOR); total_size=0; } fclose(fp); } /****************** SORTFS routine to sort files ***************************/ void sortfs(void) { int i,j,k=1; record *temp; if(!strcmp(saveext,".ARJ")) k=2; for(i=k;ik-1 && file[j-1]->size < temp->size) { file[j]=file[j-1]; j--; } file[j]=temp; } } /********************** HELP write help screen ****************************/ void help(void) { /* Print help screen */ puts("\n\ LSIZE 2.51 Archive File Sizer 2-07-94\n\ by Lawrence S. Lewis Willingboro, NJ\n\ \n This utility program will take an archive file (ZIP/LZH/ARJ) and break it\ \nup into smaller volumes. You may specify the size of these smaller files.\ \nThis is handy for backing up a large archive to a number of floppy\ \ndiskettes. The original file is left intact. Each of the smaller files\ \nwill be readable by the original archive program. This program will work\ \nwith archives created with LHA.EXE v2.13, ARJ.EXE v2.41, and PKZIP v2.04g. \ \n\nSyntax: LSIZE SourceFile [TargetPrefix] MaxSize\n\ \n SourceFile = name of the archive to be broken up.\ \n TargetPrefix = the prefix of the files to be created.\ \n = default prefix is source name.\ \n MaxSize = maximum size(in bytes) of each output file.\ \n = also accepts size: 1.44M, 720K, 1.2M, or 360K\ \nExamples: LSIZE month.lzh day 720K or LSIZE month c:\\archive 1.44M\ \n The first command would split up the file MONTH.LZH into smaller\ \n files called: DAY1.LZH, DAY2.LZH, DAY3.LZH, DAY4.LZH... Each file\ \n will be small enough to fit on a 720K floppy disk."); exit(0); }