/* * 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 $(CPP) = 1 $(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; }