/* File: ZT.C - BDS C source code for ZTYPE. Date: August 6, 1990 Author: Copyright 1990 by Carson Wilson, Sysop, Antelope Freeway RAS, 312/764-5162, Chicago. Derived from UNZIP20.ZIP, Copyright 1989 by Samuel H. Smith. Notes: I welcome efforts to improve this program, but please contact me if possible before releasing modified versions publicly in order to avoid duplication of effort. First, if the indentation of this file looks odd, note that I used ZDE with "fixed tab display spacing" set to 4 instead of the usual 8. Many sections of this source code are compatible with BDS C only, and the Z System interface is almost completely the product of BDS's excellent Z System version. In particular, BDS C does not allow long integers, so calls to long() and several other library routines (ltoi(), itol(), etc.) were necessary. Various other functions called, but not included in the source code to this program, are features exclusive to BDS C. I also have included Z80 source code and its .CRL linkable equivalent for CPYMEM, a library routine to quickly copy memory from one address to another _without_ preventing recopying of bytes. This is actually required by the Explode routine, and does not seem to have harmful effects elsewhere. To compile ZTYPE with BDS C, see the file MAKE.VAR included in this library. If you run Z System and have the CLED command installed in your RCP, you can load the .VAR file directly with CLEDSAVE; otherwise you will have to invoke the commands manually. Note that I have renamed my copy of CLINK.COM to CL.COM. I have had to resort to various unusual tactics in order to allow ZTYPE to function within the memory constraints of CP/M. First, memory is reclaimed by the three extract routines. Rather than declaring static arrays for the Unshrink, Expand, and Unimplode functions, my routine TPACheck tests for the required amount of memory before any extraction is done. Second, file i/o is done in "raw" mode. This not only saves lots of TPA, but allows much faster "skipping" of .ZIP file members. Several features could be added to ZTYPE, probably without requiring excessive amounts of memory. First, ZTYPE should really read the "central directory" of the target file and use the data there to access the file randomly, rather than using the somewhat slower sequential "skipping" approach it does now. I think this would also allow processing of "self-extracting" .EXE files built with PKZIP. Second, ZTYPE should really allow wildcards to be used in the member spec. Finally, unImploding of files is still rather slow. Performance could probably be further improved by building more sophisticated trees than the current odd/even lists. */ #include #include /*#define DEBUG*/ int extra; char comp_siz[4]; char header_flag; /* ------------------------------------------------ */ int open_input_file() /* return non-0 if creat failed */ { zipfd = open(zipfn, 0); if (zipfd < 0) { printf("%c Can't find %s",7,zipfn); return (1); } return 0; } /* ------------------------------------------------ */ int FillBuffer() /* fill input buffer if possible */ { int readsize, readrecs, skiprecs; char lincnt[4], linbufsize[4], ltemp[4], lskip[4]; if (lcomp(comp_siz,longone) == -1) return incnt = 0; /* EOD */ if (!header_flag) more(); printf("%c\b",propch[nextprop]); if (++nextprop == 4) nextprop = 0; if (extra) { incnt = 128 - extra; extra = 0; #ifdef DEBUG printf("e=%d\n",incnt); #endif itol(lincnt,incnt); lsub(comp_siz,comp_siz,lincnt); /* decrement by incnt */ if (lcomp(comp_siz,longone) == -1) itol(comp_siz,0); return incnt--; } itol(linbufsize,INBUFSIZ); if (lcomp(comp_siz,linbufsize) == 1) readsize = INBUFSIZ; else readsize = ltoi(comp_siz); incnt = readsize; readrecs = (readsize / 128); /* records to read */ extra = readsize % 128; /* add 1 if extra */ if (extra) readrecs++; if (skipflag && (readrecs > 1)) { itol(ltemp,128); ldiv(lskip,comp_siz,ltemp); skiprecs = ltoi(lskip) - 1; seek(zipfd,skiprecs,1); itol(lskip,skiprecs); lmul(lskip,lskip,ltemp); lsub(comp_siz,comp_siz,lskip); incnt = readsize = ltoi(comp_siz); readrecs = (readsize / 128); /* records to read */ extra = readsize % 128; /* add 1 if extra */ if (extra) readrecs++; } int tcnt; tcnt = read(zipfd, inbuf, readrecs); #ifdef DEBUG printf("rr=%d\n",tcnt); #endif itol(lincnt,readsize); lsub(comp_siz,comp_siz,lincnt); /* decrement by incnt */ #ifdef DEBUG printf("cs=%d\n",ltoi(comp_siz)); #endif inptr = inbuf; return incnt--; } /* ------------------------------------------------ */ int ReadByte(x) int *x; /* read a byte; return 8 if byte available, 0 if not */ { if (incnt-- == 0) if (FillBuffer() == 0) return 0; *x = *inptr++; return 8; } /* ------------------------------------------------ */ int FillBitBuffer(bits) int bits; { /* get the bits that are left and read the next word */ int temp, result, sbits; sbits = bits_left; result = bitbuf; bits -= bits_left; /* read next word of input */ if (incnt-- != 0) { bitbuf = *inptr++; bits_left = 8; } else if (FillBuffer() == 0) bits_left = 0; else { bitbuf = *inptr++; bits_left = 8; } /* bits_left = ReadByte(&bitbuf); */ if (incnt-- != 0) { temp = *inptr++; bits_left += 8; } else if (FillBuffer() != 0) { temp = *inptr++; bits_left += 8; } /* bits_left += ReadByte(&temp); */ if (bits_left == 0) zipeof = 1; /* EOF */ bitbuf |= (temp << 8); /* OR with temp shl 8 */ /* get the remaining bits */ result |= ((bitbuf & mask_bits[bits]) << sbits); bitbuf >>= bits; bits_left -= bits; return result; } /* ------------------------------------------------ */ void FlushOutput(bufsize) int bufsize; /* flush contents of output buffer */ { int soutcnt; printf(" \b"); /* remove 'props' */ scrcnt = 0; soutcnt = ltoi(outcnt); for(; lastout < soutcnt ; lastout++) { if (outbuf[lastout] != 26) putchar(outbuf[lastout]); if (outbuf[lastout] == 10) { scrline++; inch = 0; more(); } } if (lastout == bufsize) { ladd(outpos,outpos,outcnt); lastout = 0; itol(outcnt,0); /* reset counter */ outptr = outbuf; /* reset pointer */ } } /* ----------------------------------------------------------- */ int more() { inch = bdos(6,255); if ((moreflag) && (scrline > 21)) { printf(" [more] "); while((inch = bdos(6,255)) == 0); printf("%c %c",13,13); scrline = 0; } if (inch >= 'C') inch &= 0x1F; /* (c, C) --> ^C, etc. */ switch (inch) { case 0 : break; /* usually the case */ case 24 : /* ^X */ if (!skipflag) skip_member(); break; case 3 : /* ^C */ Abort(); break; case 19 : /* ^S */ printf(" [pause] "); while(bdos(6,255) == 0); /* in case we "paused" in the middle of a line: */ printf("\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b"); break; case 26 : /* ^Z */ moreflag = 0; break; } return 1; } /* ----------------------------------------------------------- */ void Abort() { printf(" \n [abort]\n"); /* leading " " to remove 'props' */ exit(1); } /* ------------------------------------------------ */ int skip_member() { int b; skipflag = 1; /* tell FillBuffer() to skip */ printf(" Skipping"); print_name(); while(ReadByte(&b)); longjmp(jmpbuf,1); /* abort extraction routines */ } /* ------------------------------------------------ */ int TPACheck(stackrsv,outlen) unsigned stackrsv, outlen; { unsigned *memtop; memtop = 6; /* BDOS or RSX */ if ((stackrsv + outlen + outbuf) >= *memtop) { printf("%c TPA error.\n",7); skip_member(); return 0; } else { /*printf("%d bytes extra.\n",extra);*/ outptr = outbuf; return 1; } } /* ------------------------------------------------ */ int print_name() { printf(" %s:\n\n",filename); scrline += 2; return (more()); /* may need to pause */ } /* ------------------------------------------------ */ void extr_member() { int b; if ((strlen(zipmn) > 0) && (strcmp(zipmn,filename) != 0)) skip_member(); else { header_flag = 0; /* enable more() */ if (print_name()) { /* allow skipping here */ switch (lrec.compression_method) { case 0: TPACheck(0,0); /* set outptr */ while (ReadByte(&b)) OUTB(b); break; case 1: if (TPACheck(25000,ONEK)) unShrink(); break; case 2: case 3: case 4: case 5: if (TPACheck(8800,FOURK)) unReduce(); break; case 6: if (TPACheck(3600,EIGHTK)) unImplode(); break; default: printf(" Unknown method; "); skip_member(); } flushoutput(-1); printf("\n"); scrline++; more(); } } } /* ------------------------------------------------ */ void get_string(len, s) int len; char *s; { s[len] = 0; while (len--) { if (incnt-- == 0) if (FillBuffer() == 0) return 0; *s++ = *inptr++; #ifdef DEBUG printf("%c(%0xh) ",*(s-1),*(s-1)); #endif } return (1); } /* ------------------------------------------------ */ void get_bytes(len, s) int len; char *s; { while (len--) { if (incnt-- == 0) if (FillBuffer() == 0) return 0; *s++ = *inptr++; #ifdef DEBUG printf("%0x ",*(s-1)); #endif } return (1); } /* ------------------------------------------------ */ void swap_lbytes(longp) char *longp; { char temp[4]; temp[3] = *longp++; temp[2] = *longp++; temp[1] = *longp++; temp[0] = *longp; *longp-- = temp[3]; *longp-- = temp[2]; *longp-- = temp[1]; *longp = temp[0]; } /* ------------------------------------------------ */ void process_local_file_header() { char longtemp[4]; /* initialize vars for this member */ bitbuf = bits_left = zipeof = lastout = scrcnt = 0; moreflag = 1; itol(outpos,0); itol(outcnt,0); get_bytes(sizeof(lrec),&lrec); /* note: will not handle filenames or extra fields longer than INBUFSIZ. */ itol(comp_siz,INBUFSIZ); /* initial read size for headers */ get_string(lrec.fname_length, &filename); itol(comp_siz,INBUFSIZ); /* initial read size for headers */ get_bytes(lrec.extra_field_length, outbuf); /* drop */ lassign(comp_siz,lrec.compr_size); /* store actual compressed size */ swap_lbytes(&comp_siz[0]); swap_lbytes(&lrec.uncompressed_size[0]); itol(longtemp,incnt); /* compensate for file data already read */ if (lcomp(comp_siz,longtemp) == 1) /* "comp_siz > incnt" */ lsub(comp_siz,comp_siz,longtemp); else { extra = 128 - incnt + ltoi(comp_siz); incnt = ltoi(comp_siz); itol(comp_siz,0); /* flag EOD */ } if ((index(filename,".EXE") == -1) && (index(filename,".COM") == -1) && (index(filename,".OBJ") == -1) && (index(filename,".BIN") == -1) && (index(filename,".ZIP") == -1) && (index(filename,".SYS") == -1) && (index(filename,".DAT") == -1) && (index(filename,".DTA") == -1) && (index(filename,".GIF") == -1) && (index(filename,".PIF") == -1)) extr_member(); else skip_member(); } /* ------------------------------------------------ */ void proc_headers() { struct iarray {unsigned word[2];}; struct iarray sig; incnt = 0; while (1) { setjmp(jmpbuf); /* mark place for return from skip_member() */ header_flag = 1; /* disable more() */ skipflag = 0; /* don't skip headers! */ itol(comp_siz,INBUFSIZ); /* initial read size for headers */ if (get_bytes(sizeof(sig),&sig) != 1) return; if ((sig.word[0] == LOCAL0) && (sig.word[1] == LOCAL1)) { process_local_file_header(); if ((strlen(zipmn) > 0) && (strcmp(zipmn,filename) == 0)) return; /* processing complete */ } else if ((sig.word[0] == CENTRAL0) && (sig.word[1]==CENTRAL1)) return; /* processing complete */ else if ((sig.word[0] == END0) && (sig.word[1] == END1)) { return; /* processing complete */ } else { printf("%c Invalid header\n",7); return; } } } /* ------------------------------------------------ */ void extract_zipfile() { /* * open the zipfile for reading and in BINARY mode to prevent cr/lf * translation, which would corrupt the bitstreams */ if (open_input_file()) Abort(); printf(" ^S pause, ^C abort, ^X skip, ^Z no paging\n"); extra = 0; proc_headers(); close(zipfd); } /* ------------------------------------------------- */ void AFNErr() { printf("%c Ambiguous filename.",7); Abort(); } /* ================================================ */ /* * MAIN PROGRAM * */ void main(argc, argv) int argc; char **argv; { printf("\n%s\n",VERSION); if ((argc < 2) || strcmp(argv[1],"//") == 0) { printf(" Syntax:\n"); printf(" ZTYPE [dir:]ufn[.ZIP] ufn - display member\n"); printf(" ZTYPE [dir:]ufn[.ZIP] - display all members\n"); exit(1); } strcpy(zipfn, argv[1]); if ((index(zipfn,"?") != -1) || (index(zipfn,"*") != -1)) AFNErr(); if (argc > 2) { strcpy(zipmn, argv[2]); if ((index(zipmn,"?") != -1) || (index(zipmn,"*") != -1)) AFNErr(); } else zipmn[0] = 0; /* .ZIP default if none provided by user */ if (index(zipfn,".") == -1) /* no filetype */ strcat(zipfn, ".ZIP"); else if (index(zipfn,".ZIP") == -1) { printf("%c Can't process \"%s\" files.",7,zipfn+index(zipfn,".")); Abort(); } iobreak(0); /* ignore ^C ^S ^Q */ pwdmode(0); /* don't re-check Z System passwords */ inbuf = &inputbuf[0]; outbuf = endext() + 30; /* end of external data area */ mask = 0xFFFF; for (count = 16; count > -1; count--) { mask_bits[count] = mask; mask >>= 1; } itol(longone,1); itol(LOneK,1024); itol(LZero,0); propch[0] = '|'; propch[1] = '/'; propch[2] = '-'; propch[3] = '\\'; nextprop = 0; scrline = 3; /* for headers */ /* do the job... */ extract_zipfile(); printf(" \n ZTYPE done."); /* leading " " to remove 'prop' */ exit(0); }