home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <io.h>
- #include <conio.h>
- #include <fcntl.h>
- #include <dos.h>
- #include <stdlib.h>
- #include <alloc.h>
- #include <math.h>
- #include <mem.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/stat.h>
-
- /*
- * Written and Copyright (c) 1990-1991 by Morgan R. Schweers (mrs@netcom.com)
- *
- *
- * History: V1.0
- * Cleaned up the code, and the output, so it looked a little
- * less like the homemade hack that it is. Also wrote the docs.
- * First public release
- *
- * One of the reasons I commented it so heavily is so others
- * can use the code to write their own handlers of .ZIP files.
- * (No, I *DON'T* normally code like this.) For all up-and-coming
- * ZIPhandler writers, *READ AND LEARN APPNOTE.TXT!* <grin>
- *
- * If you have any questions on the code, feel free to drop
- * me a line at mrs@netcom.com or ms@gnu.ai.mit.edu. If you have
- * a feature you think would be good to add, feel free to write it
- * up and send it to me.
- *
- * The source is all here. It was compiled with Turbo C++ 1.0,
- * with the command line:
- * tcc -ml -G -C zipoff.c
- *
- */
-
- #define TRUE 1
- #define FALSE !TRUE
- #define WORD *(unsigned int*)&buffer
- #define LONG *(unsigned long*)&buffer
- #define c_size sizeof(struct central_dir)
- #define BLOCKSIZE 8192
- #define INTRO "Zipoff V1.0 Copyright 1990-91 by Morgan Schweers. (408) 727-2736\n"
-
- struct central_dir {
- unsigned long central_sig;
- unsigned vmade, vneed, bflag, method, ftime, fdate;
- unsigned long crc,csize,usize;
- unsigned fnlen,eflen, comlen, dnum_st, iatt;
- unsigned long xatt,local_offset;
- char far *fname, far *efield, far *comment;
- };
-
- struct llist {
- struct central_dir far *data;
- };
-
- /*
- * parse_args(int, char **) ->
- * Order things properly, so the data can be easily
- * retrieved. I decided to do it myself, rather than
- * incorporating someone else's getopt() function.
- *
- * It packs parameters against the beginning, preserving
- * the order. Directly following that are the options.
- * Both '-' and '/' work as qualifiers.
- */
-
- int parse_args(int args, char **arglist)
- {
- char *swap;
- int i,j,total_normal=0; /* total keeps track of # of the non-option
- parameters specified. */
-
- while(i!=3 && j<=args) {
- for(i=1; i<=args && !(arglist[i][0]=='-' || arglist[i][0]=='/'); i++);
- if(i!=3) {
- for(j=i; j<=args && (arglist[j][0]=='-' || arglist[j][0]=='/'); j++);
- if(j<=args) {
- swap = arglist[i];
- arglist[i] = arglist[j];
- arglist[j] = swap;
- }
- }
- }
-
- for(i=1; i<=args; i++)
- if(arglist[i][0]=='-' || arglist[i][0]=='/')
- arglist[i]++;
- else
- ++total_normal;
-
- return(total_normal);
- }
-
- /*
- * main() ->
- * Nope, none of this is broken down into subroutines.
- * It probably could be, and the code would be much much
- * prettier afterwards. Someday I may just do that.
- *
- * The basic idea is: Find out the number of records.
- * Load all the records in. Sort them all in order of
- * largest files (after compression) first. Scan
- * through them, copying the files that will fit in the
- * remaining space on the disk. (Or that will fit within
- * the limit given.)
- * After the limit has been reached, add the central
- * directory header on, and we're finished w/ that .ZIP.
- * Prompt the user, and go around again.
- *
- * After we're all done, beep and tell the user.
- *
- * Variables used:
- * cur_loc is the current location in the file.
- * limit is the number of bytes left available on the drive.
- * bmove is the number of bytes to move to the destination ZIP.
- * moved keeps track of the number of bytes moved for each file.
- * nzsize points to the directory area of the ZIP.
- * nzsize_2 points to the "end of central dir record"
- * real_limit is the maximum limit either specified or on the destination
- *
- * fname has the destination file name.
- * buffer stores the data read in.
- *
- * zipnum is 'xx' in NEWZIPxx.ZIP
- * rval holds return values
- * gave_limit, interactive, and bad_command_line are booleans based on
- * the options selected on the command line.
- *
- * Block is used to display the "I'm alive" pulsar
- *
- * cur_rec is the total number of records.
- * total_files is a count of the total number of files
- * that have been copied so far.
- * local_files is a count of the number of local files
- * that have been copied so far.
- *
- * entry, temp, and first are all part of the array used to store
- * pointers to the directory data.
- * swap is used in the bubble sort.
- * dt is used in the diskfree routine, to determine the amount of
- * free space left.
- */
-
- main(int argc, char **argv)
- {
- unsigned long cur_loc=0L, limit=0L, bmove=0L, count=1L, moved=0L, nzsize=0L, nzsize_2=0L, real_limit=0L;
- unsigned char fname[128], ch, input_fname[128];
- unsigned char *buffer;
- int r_handle, w_handle, rw_handle, zipnum=0, rval=0;
- int i=1, j=0, gave_limit=FALSE, interactive=TRUE, bad_command_line=FALSE;
-
- char block[5]="|/-\\";
- unsigned char *zipbase=NULL, *maxspace=NULL;
- unsigned cur_rec=0, total_files=0, local_files=0, pulse=0;
-
- struct llist far *entry, far *temp, far *first;
- struct central_dir far *swap;
- struct dfree dt;
-
- printf(INTRO); /* Say hello to the nice users */
-
- argc--;
- if(argc>=2 && argc<=5) {
- /* Check what they put in for arguments... */
- rval = parse_args(argc, argv);
- if(rval!=2) bad_command_line = TRUE;
- } else bad_command_line = TRUE;
-
- /* If they've not typed a proper command line, tell them so */
- if(bad_command_line == TRUE) {
- printf("\ausage: zipoff {zipfile} {destination path} [options]\n");
-
- printf("\n[options] =\n\t-b{basename}\t-- Sets the base filename for the output .ZIP's\n");
- printf("\t\t\t(maximum of 6 characters, defaults to NEWZIP)\n\t");
-
- printf("-mXXXX\t\t-- Sets the maximum output file size for the .ZIP's\n");
- printf("\t\t\t(defaults to the space remaining on the default drive)\n");
-
- printf("\t-n\t\t-- Non-interactive mode. Does not prompt between ZIPs\n");
- printf("\t\t\t(requires the -mXXXX option. Defaults to interactive)\n");
-
- printf("\n\nNote: The destination path *MUST* start with a drive letter, unless\n");
- printf("\tyou are specifying a maximum output size.\n");
- printf("\n\nExamples:\tzipoff cdrive.zip b:\\ -bcdrive\n");
- printf("\t\tzipoff -n c:\\realbig.zip /bbigfil .\\ -m1048576\n");
- printf(" (note that both the forward slash and the dash are acceptable delimiters,\n");
- printf(" and that options can be sprinkled among the required parameters.)\n");
- printf("\t\tzipoff test.zip b:\n");
- return(1);
- }
-
- /* Check if any of the arguments were specified */
- for(i=3; i<=argc; i++) {
- if(argv[i][0]=='b') { zipbase = argv[i]; zipbase++; }
- if(argv[i][0]=='m') { maxspace= argv[i]; maxspace++; gave_limit=TRUE; }
- if(argv[i][0]=='n') { interactive = FALSE; }
- }
-
- /* If they didn't give the '-m' option, but gave the '-n' option, yell. */
- if(!gave_limit && !interactive) {
- interactive=TRUE;
- printf("\awarning: non-interactive mode requires the -m option\n");
- return(1);
- }
-
- /* No zipbase? Default to NEWZIP */
- if(zipbase == NULL) {
- zipbase = malloc(7);
- strcpy(zipbase,"NEWZIP");
- }
-
- /* If they gave a maximum, check it. */
- if(gave_limit) {
- real_limit = atol(maxspace);
- if(real_limit == 0) {
- gave_limit = FALSE;
- printf("A maximum file space of %s is not valid.\n",maxspace);
- return(1);
- }
- }
-
- /* Check the length of 'zipbase' and warn the user if truncating */
- if(strlen(zipbase)>6) {
- printf("Truncating %s to ",zipbase);
- zipbase[6]=0;
- printf("%s.\n",zipbase);
- }
-
- buffer = farmalloc(BLOCKSIZE);
-
- if(buffer==NULL) {
- printf("Memory allocation error, core dumped. {Just kidding about the core!}\n");
- printf("Not enough memory to run.\n");
- return(1);
- }
-
- /* Add on .ZIP if they haven't an extension */
- strcpy(input_fname, argv[1]);
- if(strchr(input_fname,'.')==NULL)
- strcat(input_fname,".ZIP");
-
- r_handle = open(input_fname,O_BINARY | O_RDONLY);
- if(r_handle == -1) {
- printf("File %s not found.\n",input_fname);
- return(1);
- }
-
- /* Read in the header of the first file... */
- rval = read(r_handle, buffer, 30);
- if(rval!=30) {
- printf("Difficulty reading file %s.\n",input_fname);
- return(1);
- }
-
- /* Count the records in the file */
- while(WORD[2] == 0x0403)
- {
- cur_loc = lseek(r_handle, LONG[18] + WORD[26] + WORD[28], SEEK_CUR);
- printf("%5.5d\r",cur_rec);
- cur_rec++;
- read(r_handle, buffer,30);
- }
-
- printf("Total number of files in ZIPfile = %d.\n",cur_rec);
- if(gave_limit) printf("The maximum file size for output files is %lu.\n",(unsigned long) real_limit);
-
- /* If nothing was found, then there's clearly a problem, neh? */
- if(cur_rec==0) {
- printf("No records found. Is %s a .ZIP file?\n",input_fname);
- return(1);
- }
-
- /*
- * Yes, this *WILL* fail on more than 65528/(sizeof (struct llist))
- * entries. If anyone gets to that level, contact me.
- */
- entry = farcalloc(cur_rec, sizeof(struct llist));
-
- if(entry==NULL) {
- printf("Too many records. (WOW! That's over 16,380 files!)\n");
- printf("If it's less, please contact the author.\n");
- free(buffer);
- return(1);
- }
-
- first = entry;
- lseek(r_handle, cur_loc, SEEK_SET);
- read(r_handle, buffer, 46);
-
- entry->data = farmalloc((size_t) c_size);
-
- if(entry->data == NULL) {
- printf("Problems allocating memory.\n");
- free(buffer);
- return(1);
- }
-
- i=1; /* Initialize I again */
-
- while(WORD[2] == 0x0201 && entry->data != NULL)
- {
- printf("Loading: %5.5d, Memory left: %8.8ld\r",i++,farcoreleft());
- memcpy(entry->data,buffer,46);
-
- /* Allocate exactly enough bytes for the filename */
- entry->data->fname = farmalloc(entry->data->fnlen);
-
- /* If there's an extented field, then allocate space for the extended field */
- if(entry->data->eflen != 0) entry->data->efield= farmalloc(entry->data->eflen);
-
- /* Allocate space for the comment, if there is one. */
- if(entry->data->comlen!= 0) entry->data->comment = farmalloc(entry->data->comlen);
-
- /* Load up the filename */
- read(r_handle, entry->data->fname, entry->data->fnlen);
-
- /* Load up the extended data */
- if(entry->data->eflen != 0) read(r_handle, entry->data->efield, entry->data->eflen);
-
- /* Load up the comment */
- if(entry->data->comlen!= 0) read(r_handle, entry->data->comment,entry->data->comlen);
- entry++;
- entry->data = farmalloc((size_t) c_size);
- if(entry->data==NULL) {
- printf("Out of memory at record %d.\n",i);
- printf("This ZIP needs around %ld bytes of free space.\n",(unsigned long) cur_rec*(c_size+40));
- entry=first;
- for(j=0; j<i; j++) {
- free(entry->data);
- entry++;
- }
- free(first);
- free(buffer);
- return(1);
- }
- read(r_handle, buffer, 46); /* Read in the next header */
- }
-
- printf("\nSorting entry ");
- entry = first;
-
- /* Create a single header-sized swap area for the upcoming sort */
- swap = farmalloc(sizeof(struct llist));
-
- if(swap == NULL) {
- printf("Out of memory while attempting to allocate %d bytes of memory.\n");
- printf("Don't you HATE when that happens?\n");
- entry=first;
- for(j=0; j<cur_rec; j++) {
- free(entry->data);
- entry++;
- }
- free(first);
- free(buffer);
- return(1);
- }
-
- /* Yes, this *IS* a bubble sort. So what? It works! */
- while(entry->data->central_sig==0x02014b50)
- {
- printf("%5.5d.\b\b\b\b\b\b",count);
- count++;
- temp = entry;
- temp++;
- while(temp->data->central_sig==0x02014b50)
- {
- if(entry->data->csize < temp->data->csize)
- {
- swap = entry->data;
- entry->data = temp->data;
- temp->data = swap;
- }
- temp++;
- }
- entry++;
- }
-
- /* Do they want to see what's going on? */
- if(interactive) printf("\nPress 'Y' to display list of filenames, or any other key to continue:");
- if(interactive) {
- while(!kbhit());
- ch=getch();
- printf("\n");
- if(ch=='Y' || ch=='y')
- {
- entry = first;
- while(entry->data->central_sig==0x02014b50)
- {
- printf("Compressed size: %10.10lu, CRC: %8.8lx Name: ", entry->data->csize, entry->data->crc);
- /* *entry->data->fname is *NON*-zero terminated, so this displays it. */
- for(i=0; (i<entry->data->fnlen); i++) printf("%c",entry->data->fname[i]);
- printf("\n");
- entry++;
- }
- }
- }
- local_files=1;
- if(!gave_limit)
- printf("Enter disk in drive %c:, and press ANY KEY when ready: ",toupper(argv[2][0]));
- else
- if(interactive) printf("Hit ANY KEY to begin: ");
- if(interactive) {
- while(!kbhit);
- getch();
- }
- printf("\n");
- while(local_files!=0 && total_files != cur_rec)
- {
- /* Point to the beginning of the data */
- entry = first;
- local_files=0;
- sprintf(fname,"%s%s%2.2d.ZIP",argv[2],zipbase,zipnum++);
- w_handle=open(fname, O_CREAT | O_WRONLY | O_BINARY, S_IWRITE | S_IREAD);
-
- if(w_handle==-1) {
- printf("Unable to open %s for output.\n",fname);
- perror("error");
- return(1);
- }
-
- rw_handle=open("C_DIR.CDR", O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD);
- if(rw_handle==-1) {
- printf("Unable to open output C_DIR.CDR (central directory data file).\n");
- perror("error");
- return(2);
- }
-
- if(!gave_limit) {
- /* What's the free space on the drive? */
- getdfree(toupper(argv[2][0])-64, &dt);
-
- if(dt.df_sclus==65535) {
- printf("Error reading drive information for drive %c.\n",argv[2][0]);
- return(1);
- }
- }
-
- /* Limit is how much space we have to put the .ZIP in */
- if(!gave_limit) {
- limit = (unsigned long) dt.df_avail*dt.df_sclus*dt.df_bsec;
- limit -= 1024; /* Fudge factor (max floppy clustersize) */
- real_limit = limit;
- } else
- limit = real_limit;
-
- /*
- * This is the main loop. Herein we gauge how much space is available,
- * and determine the files that will be moved. The compressed code is
- * moved entirely as is, requiring no decompression. I use 8K chunks
- * to move it, since that's about the fastest. (Anything smaller takes
- * longer, anything larger tends to over-shoot.)
- */
- while(((unsigned int) entry->data->central_sig)==0x4b50)
- {
- /*
- * Ick. Not pretty, eh? It initializes the bmove variable to be
- * the size of the current entry's compressed size, plus twice the
- * filename length, plus twice the extended info length, plus
- * the comment length, plus 76 (header information). The two pieces
- * of data (extra field and filename) are doubled because they have
- * to be stored in the .ZIP file later. The number 76 includes the
- * header that will be written at the end.
- */
- bmove = entry->data->csize + entry->data->fnlen*2 + entry->data->eflen*2 + entry->data->comlen + 76;
-
- if((unsigned long) bmove > (unsigned long) real_limit && entry->data->central_sig==0x02014b50) {
- printf("Skipping file ");
-
- /* There *HAS* to be a way to print a non-zero-terminated string */
- for(i=0; i<entry->data->fnlen; i++) printf("%c",entry->data->fname[i]);
-
- printf("\n\tbecause it is larger than the maximum size of %ld.\n",real_limit);
-
- if(gave_limit) {
- printf("\a\t(because the limit is not based on disk size,\n");
- printf("\tthe file will be removed from the list.\n");
- entry->data->central_sig=0x4b50L;
- total_files++;
- }
- }
-
- if(bmove <= limit && entry->data->central_sig == 0x02014b50)
- {
- local_files++;
-
- /* There *HAS* to be a way to print a non-zero-terminated string */
- for(i=0; i<entry->data->fnlen; i++) printf("%c",entry->data->fname[i]);
-
- /* Seek to the beginning of this file */
- lseek(r_handle, entry->data->local_offset, SEEK_SET);
-
- /* Adjust bytes to move appropriately. */
- bmove -= (entry->data->fnlen + entry->data->eflen + entry->data->comlen + 46);
- nzsize = lseek(w_handle,0L,SEEK_CUR);
- count = BLOCKSIZE;
- pulse = 0;
- while (count==BLOCKSIZE)
- {
- printf("%c\b",block[pulse++]);
- pulse %= 4;
- count = read(r_handle, buffer, BLOCKSIZE);
-
- /* Yupyup. Another piece of weird code. Effectively, if we read
- * more code than we needed to, then adjust the count. Since we
- * seek each compressed file, it's no problem to overread a bit.
- */
- if((count+moved) > bmove) count=bmove-moved;
- moved+=count;
- rval = write(w_handle,buffer,count);
- if(rval!=count) {
- printf(" \nwrite error: probably out of disk space\n\tThis .ZIP is now corrupted (%s)\n",fname);
- if(gave_limit) printf("The output ZIPs were too large for the one drive.\n");
- printf("unrecoverable error\a\a\n");
- entry=first;
- for(j=0; j<cur_rec; j++) {
- free(entry->data);
- entry++;
- }
- free(first);
- free(buffer);
- free(swap);
- return(1);
- }
- }
- printf(" \n");
- moved = 0L;
- entry->data->local_offset = nzsize;
- write(rw_handle, entry->data, 46L);
- write(rw_handle, entry->data->fname, entry->data->fnlen);
- write(rw_handle, entry->data->efield, entry->data->eflen);
- write(rw_handle, entry->data->comment, entry->data->comlen);
- entry->data->central_sig=0x4b50;
-
- /* Re-adjust bmove, so that the 'limit' will be correct,
- * in terms of the final size including the header data
- * that has to go along w/ each file.
- */
- bmove += (entry->data->fnlen + entry->data->eflen + entry->data->comlen + 46);
- limit -= bmove;
- }
- entry++;
- }
- /* Keep ourselves a count of the files that have been copied. */
- total_files += local_files;
-
- /* What's our current location in the file. */
- nzsize = lseek(w_handle,0L,SEEK_CUR);
-
- /* Now we write out a directory at the end of the .ZIP file. */
- lseek(rw_handle, 0L, SEEK_SET);
- count = BLOCKSIZE;
- while(count==BLOCKSIZE)
- {
- count = read(rw_handle, buffer, BLOCKSIZE);
- write(w_handle, buffer, count);
- }
- /* nzsize_2 is the location at which the central directory pointer is.
- * That's how it's written in the appnote.txt. Then we build the final
- * 'central directory entry', which closes off the .ZIP file.
- */
- nzsize_2 = filelength(rw_handle);
- close(rw_handle);
- unlink("C_DIR.CDR");
- LONG[0] = 0x06054b50; /* Magic Marker */
- WORD[4] = 0;
- WORD[6] = 0;
- WORD[8] = local_files; /* Number of files in this .ZIP */
- WORD[10]= local_files;
- LONG[12]=nzsize_2;
- LONG[16]=nzsize;
- WORD[20]=0;
- write(w_handle, buffer, 22);
- close(w_handle);
- if(total_files!=cur_rec)
- {
- if(!gave_limit)
- printf("\aEnter disk in drive %c:, and press ANY KEY when ready: ",toupper(argv[2][0]));
- else
- if(interactive) printf("\aPress ANY KEY to make next file: ");
- if(interactive) {
- while(!kbhit);
- getch();
- }
- printf("\n");
- }
- }
- close(r_handle);
- printf("Filling of these .ZIP files done successfully.\n\a");
- entry=first;
- for(j=0; j<cur_rec; j++) {
- free(entry->data);
- entry++;
- }
- free(first);
- free(buffer);
- free(swap);
- return(0);
- }
-