-h- lzdcm1.c Wed Jul 24 11:49:34 1985 USER$A:[MINOW.LZ]LZDCM1.C;11 /* * lzdcmp [-options] [infile [outfile]] */ #ifdef DOCUMENTATION title lzdcmp File Decompression index File decompression synopsis .s.nf lzdcmp [-options] [infile [outfile]] .s.f description lzdcmp decompresses files compressed by lzcomp. The documentation for lzcomp describes the process in greater detail. Options may be given in either case. .lm +8 .p -8 -B Output file is "binary", not text. (Ignored in VMS private mode.) .p -8 -X 3 To read files compressed by an old Unix version that doesn't generate header records. .p -8 -V val Verbose (print status messages and debugging information). The value selects the amount of verbosity. Author This version by Martin Minow. See lzcomp for more details. #endif /* * Compatible with compress.c, v3.0 84/11/27 */ /*)BUILD * $(PROGRAM) = lzdcmp * $(INCLUDE) = lz.h * $(FILES) = { lzdcm1.c lzdcm2.c lzdcm3.c lzio.c lzvio.c } */ #include "lz.h" /* * These global parameters are read from the compressed file. * The decompressor needs them. */ short maxbits = BITS; /* settable max # bits/code */ code_int maxmaxcode = 1 << BITS; /* * Note, if export is zero or 1, the "true" value will be set from * the file header. If it is 2, no header will be read. */ #if VMS_V4 flag export = 0; /* Assume VMS private */ #else flag export = 1; /* Assume Unix compatible */ #endif flag binary = FALSE; /* Read text if false */ flag block_compress = TRUE; /* TRUE if CLEAR enabled */ flag noheader = FALSE; /* No magic header if TRUE */ flag verbose = VERBOSE_DEFAULT; /* Non-zero for status/debug */ flag background = FALSE; /* TRUE (Unix) if detached */ flag is_compress = FALSE; /* For lzio.c (?) */ char *infilename = NULL; /* For error printouts */ char *outfilename = NULL; /* For openoutput and errors */ int firstcode; /* First code after internals */ static long start_time; /* Time we started (in msec) */ extern long cputime(); /* Returns process time in msec */ jmp_buf failure; STREAM instream; STREAM outstream; char_type inbuffer[MAXIO]; char_type outbuffer[MAXIO]; static STREAM mem_stream; #if VMS_V4 #include #ifndef FDLSTUFF #define FDLSTUFF char #endif FDLSTUFF *fdl_input; FDLSTUFF *fdl_output; static struct dsc$descriptor fdl_descriptor; #endif main(argc, argv) int argc; char *argv[]; /* * Decompress mainline */ { int result; #ifndef decus /* * background is TRUE if running detached from the command terminal. */ background = (signal(SIGINT, SIG_IGN) == SIG_IGN) ? TRUE : FALSE; if (!background) background = !isatty(fileno(stderr)); if (!background) { if (verbose > 0) signal(SIGINT, abort); else { signal(SIGINT, interrupt); signal(SIGSEGV, address_error); } } #endif if (setjmp(failure) == 0) { setup(argc, argv); openinput(); get_magic_header(); /* Sets export, etc. */ openoutput(); if (verbose > 0) start_time = cputime(); init_decompress(); result = decompress(&outstream); if (!export && result != LZ_ETX && getcode() != (code_int) LZ_ETX) { fprintf(stderr, "Decompress didn't finish correctly.\n"); goto fail; } lz_flush(&outstream); #if DEBUG if ((verbose & 04) != 0) dump_tab(stdout); #endif if (verbose > 0) { start_time = cputime() - start_time; fprintf(stderr, "%ld.%02ld seconds (process time) for decompression.\n", start_time / 1000L, (start_time % 1000L) / 10L); } exit(IO_SUCCESS); } else { fail: fprintf(stderr, "Error when decompressing \"%s\" to \"%s\"\n", (infilename == NULL) ? "" : infilename, (outfilename == NULL) ? "" : outfilename); if (errno != 0) perror("lzdcmp fatal error"); exit(IO_ERROR); } } static get_magic_header() { int head1; int head2; int head3; head2 = 0; if (export != 2) { if ((head1 = GET(&instream)) != HEAD1_MAGIC) { fprintf(stderr, "Incorrect first header byte 0x%X\n", head1); FAIL("can't get header"); } head2 = GET(&instream); head3 = GET(&instream); switch (head2) { case HEAD2_MAGIC: export = 1; break; case VMS_HEAD2_MAGIC: export = 0; break; default: fprintf(stderr, "Incorrect second header byte 0x%X\n", head2); FAIL("can't get header"); } maxbits = head3 & BIT_MASK; block_compress = ((head3 & BLOCK_MASK) != 0) ? TRUE : FALSE; #if DEBUG if (verbose > 1) { fprintf(stderr, "%s: compressed with %d bits,", infilename, maxbits); fprintf(stderr, " block compression %s.\n", (block_compress != 0) ? "enabled" : "disabled"); } #endif } if (maxbits > BITS) { fprintf(stderr, "%s: compressed with %d bits,", infilename, maxbits); fprintf(stderr, " lzdcmp can only handle %d bits\n", BITS); FAIL("too many bits"); } maxmaxcode = 1 << maxbits; if (export == 0) firstcode = GET(&instream) + 0x100; /* From compressed file */ else if (block_compress) firstcode = LZ_CLEAR + 1; /* Default */ else firstcode = 256; /* Backwards compatible */ #if VMS_V4 if (!export) { register code_int code; char text[256]; /* * Get the attribute record. */ if ((code = getcode()) != LZ_SOH) { fprintf(stderr, "Expected header, read 0x%X\n", code); FAIL("can't get header (private)"); } init_decompress(); code = mem_decompress(text, sizeof text); text[code] = EOS; if (strncmp(text, ATT_NAME, ATT_SIZE) != 0) { fprintf(stderr, "Expected \"%s\", read \"%.*s\"\n", ATT_NAME, code, text); FAIL("can't get attribute block header"); } code = atoi(text + ATT_SIZE); fdl_descriptor.dsc$a_pointer = malloc(code); fdl_descriptor.dsc$w_length = code; if ((code = mem_decompress(fdl_descriptor.dsc$a_pointer, code)) != fdl_descriptor.dsc$w_length) { fprintf(stderr, "\nError reading fdl attributes block,"); fprintf(stderr, " expected %d bytes, read %d bytes\n", fdl_descriptor.dsc$w_length, code); FAIL("can't get attribute block data"); } if (verbose > 1) { fprintf(stderr, "\nFDL information read from \"%s\"\n", infilename); fdl_dump(&fdl_descriptor, stderr); } if ((code = getcode()) != LZ_STX) { fprintf(stderr, "\nExpecting start of text, got 0x%X\n", code); FAIL("no start of text"); } } #endif } int mem_decompress(buffer, size) char_type *buffer; int size; /* * Decompress up to size bytes to buffer. Return actual size. */ { int result; mem_stream.bp = mem_stream.bstart = buffer; mem_stream.bend = buffer + size; mem_stream.bsize = size; mem_stream.func = lz_fail; if ((result = decompress(&mem_stream)) == LZ_EOR || result == LZ_ETX) return (mem_stream.bp - buffer); else { fprintf(stderr, "Decompress to memory failed.\n"); FAIL("can't decompress to memory"); } return (-1); /* Can't happen */ } static readonly char *helptext[] = { "The following options are valid:", "-B\tBinary file (important on VMS/RSX, ignored on Unix)", "-M val\tSet the maximum number of code bits (unless header present)", "-V val\tPrint status information or debugging data.", "-X val\tSet export (compatibility) mode:", "-X 0\tVMS private mode", "-X 1\tCompatibility with Unix compress", "-X 2\tDo not read a header, disable \"block-compress\" mode", "\t(If a header is present, lzdcmp will properly configure itself,", "\toverriding the -X, -B and -M flag values.", NULL, }; static setup(argc, argv) int argc; char *argv[]; /* * Get parameters and open files. Exit fatally on errors. */ { register char *ap; register int c; char **hp; auto int i; int j; #ifdef vms argc = getredirection(argc, argv); #endif for (i = j = 1; i < argc; i++) { ap = argv[i]; if (*ap++ != '-' || *ap == EOS) /* Filename? */ argv[j++] = argv[i]; /* Just copy it */ else { while ((c = *ap++) != EOS) { if (islower(c)) c = toupper(c); switch (c) { case 'B': binary = TRUE; break; case 'M': maxbits = getvalue(ap, &i, argv); if (maxbits < MIN_BITS) { fprintf(stderr, "Illegal -M value\n"); goto usage; } break; case 'V': verbose = getvalue(ap, &i, argv); break; case 'X': export = getvalue(ap, &i, argv); if (export < 0 || export > 3) { fprintf(stderr, "Illegal -X value: %d\n", export); goto usage; } block_compress = (export < 2); noheader = (export == 3); break; default: fprintf(stderr, "Unknown option '%c' in \"%s\"\n", *ap, argv[i]); usage: for (hp = helptext; *hp != NULL; hp++) fprintf(stderr, "%s\n", *hp); FAIL("unknown option"); } /* Switch on options */ } /* Everything for -xxx */ } /* If -option */ } /* For all argc's */ /* infilename = NULL; */ /* Set "stdin" signal */ /* outfilename = NULL; */ /* Set "stdout" signal */ switch (j) { /* Any file arguments? */ case 3: /* both files given */ if (!streq(argv[2], "-")) /* But - means stdout */ outfilename = argv[2]; case 2: /* Input file given */ if (!streq(argv[1], "-")) { infilename = argv[1]; } break; case 0: /* None! */ case 1: /* No file arguments */ break; default: fprintf(stderr, "Too many file arguments\n"); FAIL("too many files"); } } static int getvalue(ap, ip, argv) register char *ap; int *ip; char *argv[]; /* * Compile a "value". We are currently scanning *ap, part of argv[*ip]. * The following are possible: * -x123 return (123) and set *ap to EOS so the caller * ap^ cycles to the next argument. * * -x 123 *ap == EOS and argv[*ip + 1][0] is a digit. * return (123) and increment *i to skip over the * next argument. * * -xy or -x y return(1), don't touch *ap or *ip. * * Note that the default for "flag option without value" is 1. This * can only cause a problem for the -M option where the value is * mandatory. However, the result of 1 is illegal as it is less * than INIT_BITS. */ { register int result; register int i; i = *ip + 1; if (isdigit(*ap)) { result = atoi(ap); *ap = EOS; } else if (*ap == EOS && argv[i] != NULL && isdigit(argv[i][0])) { result = atoi(argv[i]); *ip = i; } else { result = 1; } return (result); } openinput() { #ifdef decus if (infilename == NULL) { infilename = malloc(257); fgetname(stdin, infilename); infilename = realloc(infilename, strlen(infilename) + 1); } if (freopen(infilename, "rn", stdin) == NULL) { perror(infilename); FAIL("can't open compressed input"); } #else #ifdef vms #if VMS_V4 if (!export) { if (infilename == NULL) { infilename = malloc(256 + 1); fgetname(stdin, infilename); infilename = realloc(infilename, strlen(infilename) + 1); } if ((fdl_input = fdl_open(infilename, NULL)) == NULL) { fdl_message(NULL, infilename); FAIL("can't open compressed input (vms private)"); } } else #endif { if (infilename == NULL) { infilename = malloc(256 + 1); fgetname(stdin, infilename); infilename = realloc(infilename, strlen(infilename) + 1); } else { if (freopen(infilename, "r", stdin) == NULL) { perror(infilename); FAIL("can't open compressed input (export)"); } } } #else if (infilename == NULL) infilename = ""; else { if (freopen(infilename, "r", stdin) == NULL) { perror(infilename); FAIL("can't open input"); } } #endif #endif instream.bp = instream.bend = NULL; instream.bstart = inbuffer; instream.bsize = sizeof inbuffer; instream.func = lz_fill; } openoutput() { #ifdef vms #if VMS_V4 if (!export) { fclose(stdout); stdout = NULL; if ((fdl_output = fdl_create(&fdl_descriptor, outfilename)) == NULL) { fprintf(stderr, "Can't create output file\n"); if ((fdl_status & 01) == 0) fdl_message(NULL, outfilename); FAIL("can't create output (vms private)"); } if (outfilename == NULL) { outfilename = malloc(256 + 1); fdl_getname(fdl_output, outfilename); outfilename = realloc(outfilename, strlen(outfilename) + 1); } } else #endif { /* * Not VMS Version 4, or export mode. */ if (outfilename == NULL) { outfilename = malloc(256 + 1); fgetname(stdout, outfilename); outfilename = realloc(outfilename, strlen(outfilename) + 1); if (!binary) goto do_reopen; } else { if (binary) { if (freopen(outfilename, "w", stdout) == NULL) { perror(outfilename); FAIL("can't create output (binary)"); } } else { int i; do_reopen: if ((i = creat(outfilename, 0, "rat=cr", "rfm=var")) == -1 || dup2(i, fileno(stdout)) == -1) { perror(outfilename); FAIL("can't create output (text)"); } } } } #else #ifdef decus if (outfilename == NULL) { outfilename = malloc(256 + 1); fgetname(stdout, outfilename); outfilename = realloc(outfilename, strlen(outfilename) + 1); if (binary) { if (freopen(outfilename, "wn", stdout) == NULL) { perror(outfilename); FAIL("can't create (binary)"); } } } else { if (freopen(outfilename, (binary) ? "wn" : "w", stdout) == NULL) { perror(outfilename); FAIL("can't create"); } } #else if (outfilename == NULL) outfilename = ""; else { if (freopen(outfilename, "w", stdout) == NULL) { perror(outfilename); FAIL("can't create"); } } #endif #endif outstream.bp = outstream.bstart = outbuffer; outstream.bend = outbuffer + sizeof outbuffer; outstream.bsize = sizeof outbuffer; outstream.func = lz_flush; } -h- lzdcm2.c Wed Jul 24 11:49:34 1985 USER$A:[MINOW.LZ]LZDCM2.C;10 /* * l z d c m 2 . c * * Actual decompression code */ #include "lz.h" /* * These global parameters are read from the compressed file. * The decompressor needs them. */ extern short maxbits; /* settable max # bits/code */ extern code_int maxmaxcode; /* 1 << maxbits */ static flag first_clear = TRUE; /* * Big data storage stuff */ static char_type stack[MAXSTACK]; #define STACK_TOP (&stack[MAXSTACK]) static U_short tab_prefix[1 << BITS]; /* prefix code */ static char_type tab_suffix[1 << BITS]; /* last char in string */ code_int next_code; #if DEBUG #define CHECK(what) \ if (stp <= stack) { \ fprintf(stderr, "Stack overflow -- %s\n", what); \ abort(); \ } #else #define CHECK(what) #endif int decompress(out) STREAM *out; /* * Decompress instream (global) to out. Returns "end" signal: * -1 end of file * LZ_EOR end of record * LZ_ETX end of segment */ { register char_type *stp; /* Stack pointer */ register code_int code, oldcode, incode; register int final; /* End of a sequence? */ extern code_int getcode(); stp = STACK_TOP; final = oldcode = getcode(); PUT((char) final, out); while ((code = getcode()) >= 0) { test: if (code >= LZ_CLEAR && code < firstcode) { lz_putbuf(stp, STACK_TOP - stp, out); stp = STACK_TOP; switch (code) { case LZ_ETX: case LZ_EOR: goto finish; case LZ_SOH: /* Unexpected */ case LZ_STX: /* Unexpected */ default: fprintf(stderr, "\nUnexpected control 0x%X\n", code); FAIL("Unexpected control"); case LZ_CLEAR: init_decompress(); /* Before getcode() !! */ if ((code = getcode()) < 0 || (export && code == LZ_CLEAR)) goto finish; else { /* * init_decompress has set next_code to firstcode, * however, for magical reasons, we want to toss * the next substring, so we set next_code so * that the tab_... entry is effectively ignored. * Note below that tab_prefix[next_code] is set * to the character before the LZ_CLEAR and * tab_suffix to the character after the LZ_CLEAR. * But, these values have no relationship to one * another, so, by decrementing next_code, they * will be ignored. (I think.) */ next_code--; goto test; } } } incode = code; /* * Special case for KwKwK string. */ if (code >= next_code) { CHECK("KwKwK"); *--stp = final; code = oldcode; } /* * Generate output characters in reverse order */ #ifdef interdata while (((unsigned long) code) >= ((unsigned long) NBR_CHAR)) { #else while (code >= NBR_CHAR) { #endif CHECK("generate output"); *--stp = tab_suffix[code]; code = tab_prefix[code]; } CHECK("final char"); *--stp = final = tab_suffix[code]; /* * And put them out in forward order */ lz_putbuf(stp, STACK_TOP - stp, out); stp = STACK_TOP; /* * Generate the new entry. */ if ((code = next_code) < maxmaxcode) { tab_prefix[code] = (U_short) oldcode; tab_suffix[code] = final; next_code++; } /* * Remember previous code. */ oldcode = incode; } finish: return (code); } init_decompress() /* * Called on cold start, or on LZ_SOH, LZ_STX, LZ_CLEAR. */ { register char_type *cp; register U_short *up; register int code; if (first_clear) { for (cp = &tab_suffix[0], code = 0; cp < &tab_suffix[NBR_CHAR];) *cp++ = code++; first_clear = FALSE; } else { #if ((NBR_CHAR % 8) != 0) << error, the following won't work >> #endif for (up = &tab_prefix[0]; up < &tab_prefix[NBR_CHAR];) { *up++ = 0; *up++ = 0; *up++ = 0; *up++ = 0; *up++ = 0; *up++ = 0; *up++ = 0; *up++ = 0; } } next_code = firstcode; } #if DEBUG dump_tab(dumpfd) FILE *dumpfd; /* * dump string table */ { register char_type *stp; /* Stack pointer */ register int i; register int ent; extern char *dumpchar(); stp = STACK_TOP; fprintf(dumpfd, "%d %s in string table\n", next_code, (next_code == 1) ? "entry" : "entries"); for (i = 0; i < next_code; i++) { fprintf(dumpfd, "%5d: %5d/'%s' ", i, tab_prefix[i], dumpchar(tab_suffix[i])); for (ent = i;;) { *--stp = tab_suffix[ent]; if (ent < firstcode) break; ent = tab_prefix[ent]; } dumptext(stp, STACK_TOP - stp, dumpfd); stp = STACK_TOP; } } #endif -h- lzdcm3.c Wed Jul 24 11:49:34 1985 USER$A:[MINOW.LZ]LZDCM3.C;14 /* * l z d c m 3 . c * * Read codes from the input stream. */ #include "lz.h" #if !vax_asm static readonly char_type rmask[9] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF }; #endif #if DEBUG extern int col; static int todump; #endif /* * getcode() * * Read one code from the standard input. If EOF, return -1. * Inputs: * stdin (via GET) * Outputs: * code or -1 is returned. */ extern code_int next_code; extern STREAM instream; extern code_int maxmaxcode; extern short maxbits; static short n_bits = INIT_BITS; static code_int maxcode = MAXCODE(INIT_BITS); /* * buf[] contains 8-bit data read from the input stream. getcode() * treats buf[] as a vector of bits, repacking it into variable-bit codes. */ static char_type buf[BITS]; static int offset = 0; /* Offset into buf IN BITS */ static int size = 0; /* Actual size of buf IN BITS */ code_int getcode() { /* * On the VAX (4.2 bsd), it is important to have the register * declarations in exactly the order given, or the asm will break. */ register code_int code; register int r_off, bits; register char_type *bp; bp = buf; if (next_code > maxcode) { n_bits++; if (n_bits == maxbits) maxcode = maxmaxcode; else { maxcode = MAXCODE(n_bits); } size = 0; #if DEBUG if (verbose > 2) { fprintf(stderr, "\nChange to %d bits", n_bits); col = 74; } #endif } if (offset >= size) { size = lz_getbuf(buf, n_bits, &instream); #if DEBUG if (verbose > 4 || todump > 0) { dumphex(buf, size, stderr); if (todump > 0) todump -= size; } #endif if (size <= 0) return (-1); /* end of file */ offset = 0; /* * Round size down to integral number of codes in the buffer. * (Expressed as a number of bits). */ size = (size << 3) - (n_bits - 1); } r_off = offset; bits = n_bits; #if vax_asm asm("extzv r10,r9,(r8),r11"); #else /* * Get to the first byte. */ bp += (r_off >> 3); r_off &= 7; /* * Get first part (low order bits) */ #if UCHAR code = (*bp++ >> r_off); #else /* * Don't touch the 0xFF; it prevents sign extension. */ code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xFF; #endif bits -= (8 - r_off); r_off = 8 - r_off; /* now, offset into code word */ /* * Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { #if UCHAR code |= *bp++ << r_off; #else code |= (*bp++ & 0xFF) << r_off; #endif r_off += 8; bits -= 8; } /* high order bits. */ #if UCHAR code |= (*bp & rmask[bits]) << r_off; #else code |= (*bp & rmask[bits]) << r_off; #endif /* * End of non-vax (Unix) specific code. */ #endif offset += n_bits; if (code >= LZ_CLEAR && code < firstcode) { switch (code) { case LZ_SOH: case LZ_STX: case LZ_CLEAR: size = 0; /* Force read next time */ n_bits = INIT_BITS; maxcode = MAXCODE(INIT_BITS); #if DEBUG if (verbose > 1) { fprintf(stderr, "Read %s (%d)\n", lz_names[code - LZ_CLEAR], code); todump = 32; } #endif break; } } #if DEBUG if (verbose > 3) { fprintf(stderr, "%c%5d %5d", ((col += 12) >= 72) ? (col = 0, '\n') : ' ', code, next_code); if (code >= LZ_CLEAR && code < firstcode) { fprintf(stderr, " = %s", lz_names[code - LZ_CLEAR]); col = 74; } } #endif return (code); }