/*
 *		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.)  On VMS, this generates a stream
	file without carriage-control attributes.  On Decus C
	systems, it generates a "fixed block, 512 byte record"
	file.  It is unneeded on Unix.
	.p -8
	-F nnn	Output file is "fixed length nnn-byte records."  
	This is used only by VMS for reading files created by Unix 
	compress or by "export" mode.  It is identical to binary on 
	Decus C.  The default value for nnn is 512.
	.p -8
	-X [n]	"Export" -- if specified, the value determines the 
	level of compatiblity.  If not specified, or specified with 
	an explicit value of zero, and lzcomp is running on VMS and 
	the input file is a disk or magtape file (block-oriented), 
	a VMS-private output format is used which is incompatible 
	with the Unix compress utility, but which preserves VMS file 
	attributes.  -X may take on the following values:
	.lm +4.s
	.i -4;#0##Choose VMS private format.
	.i -4;#1##Compatible with Unix compress version 3.0, except
	that an end code is expected. This is the default if -x is 
	given without a value.
	.i -4;#2##As above, but do not expect the extra end code.
	This should be used for files generated by Unix compress.
	.i -4;#3##As in (2) above, but suppress "block compression".
	Actually, this will give the same results as (2), since the
	block compression indicator will be reset from the header.
	.i -4;#4##As in (2) above, but do not read a compress header 
	block, and suppress block compression.  This is for compatiblity
	with a quite early version of Unix compress.
	.lm -4.s
	.p -8
	-V [n]	Verbose if specified.  If a value is specified,
	it will enable debugging code (if compiled in).  The values
	are bit-encoded as follows:
	.lm +4.s
	.i -4;#0##Don't print anything.
	.i -4;#1##Print a summary of the operation.
	.i -4;#2##Not used in lzdcmp.
	.i -4;#4##Print FDL information (for VMS export mode).
	.i -4;#8##Print some debug information.
	.i -4;#16#Print too much debug information.
	.i -4;#32#Not used in lzdcmp.
	.lm -4.s

VMS Command Language Interface

	In addition to the above (Unix-style) command line
	interface, lzcomp supports a VMS command line interface.
	The following options are available:
	.lm +8
	.p -8
	/EXPORT=(VMS, UNIX, HEADER)
	.p -8
	/MODE=(TEXT, BINARY, FIXED)
	.p -8
	/SHOW=(ALL, PROGRESS, STATISTICS, FDL,
		DEBUG, DEBUG_SERIOUS, DEBUG_IO)
	.lm -8

VMS private mode

	If the file was compressed in VMS private mode, all information
	needed to reconstruct the file is stored in the compressed
	file, using the VMS run-time library FDL routines.  This means
	that the expanded file will have the same name and directory
	location it had originally.  If the directory structure does
	not exist (for example, because you have moved the compressed
	file to another machine), you must specify the second argument
	to lzdcmp to specify the file name.

Author

	This version by Martin Minow.  See lzcomp for more
	details.

#endif

/*
 * Edit history:
 *
 * 12-Sep-93	dp	Assorted bug fixes and cleanups for VMS.
 *			- Need mrs=512 for fixed-512 output. 
 *			- Allow "-F nnn" to set record size.
 *			- Fix lies about -V and -X in "usage" text.
 *			- Replace "#if vms_v4" by "ifdef vms" for simplicity.
 *			- Add elapsed time to stats output.
 */

/*
 * Compatible with compress.c, v3.0 84/11/27
 */

/*)BUILD
		$(PROGRAM) = lzdcmp
		$(INCLUDE) = lz.h
		$(CPP) = 1
		$(RMS) = 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_HEADER is set (all except -X4) then the true value
 * for VMS|UNIX and for BLOCK will be set from the file header. ENDMARKER
 * is always set according to the user specification (-X1 -X2 or -X3)
 * If EXPORT_HEADER is clear (-X4) no header will be read.
 */
#ifdef vms
flag		export = EXPORT_VMS	/* Assume VMS private		*/
			| EXPORT_HEADER
			| EXPORT_BLOCK
			| EXPORT_ENDMARKER;
#else
flag		export = EXPORT_UNIX	/* Assume standard Unix		*/
			| EXPORT_HEADER
			| EXPORT_BLOCK
			| EXPORT_ENDMARKER;
#endif
flag		method = METHOD_LZ;	/* Of course			*/
flag		mode = MODE_TEXT;
flag		show = 0;		/* No statistics		*/
flag		background = FALSE;	/* TRUE (Unix) if detached	*/
flag		is_compress = FALSE;	/* For lzio.c and lzdcl.c	*/
char		*infilename = NULL;	/* For error printouts		*/
char		*outfilename = NULL;	/* For openoutput and errors	*/
int		firstcode;		/* First code after internals	*/
int		fblksiz = 512;		/* Block size if -F 		*/
static time_t   start_walltime;		/* Wall time we started 	*/
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];
#ifdef vms
FDLSTUFF	*fdl_input;
FDLSTUFF	*fdl_output;
static struct dsc$descriptor fdl_descriptor;
int		xab_lrl = 0;
static STREAM	mem_stream;

typedef struct more_attributes {
    char	*name;
    int		type;
    char	*result;	/* Actually a union (int *, char *)	*/
} MORE_ATTRIBUTES;

#define	STRING	0
#define INTEGER	1

/*
 * Additional attributes (put into the compressed file by lzcomp)
 * are extracted using this extensible table.
 */

static MORE_ATTRIBUTES more_attributes[] = {
    { "xabfhc$w_lrl", INTEGER, &xab_lrl },
    NULL
};

#endif

main(argc, argv)
int		argc;
char		*argv[];
/*
 * Decompress mainline
 */
{
	int		result;
	int		status;
	extern code_int	getcode();

#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 ((show & (SHOW_DEBUG | SHOW_SERIOUS_DEBUG | SHOW_IO_DEBUG)) != 0)
		signal(SIGINT, abort);
	    else {
		signal(SIGINT, interrupt);
		signal(SIGSEGV, address_error);
	    }
	}
#endif
	if ((status = setjmp(failure)) == 0) {
	    setup(argc, argv);
#if DEBUG
	    if (show & SHOW_DEBUG)
		dumpoptions();
#endif
	    do {
	        openinput();
	    } while (get_magic_header());	/* Sets export, etc.	*/
	    openoutput();
	    if ((show & SHOW_STATISTICS) != 0) {
		start_walltime = time(NULL);
		start_time = cputime();
	    }
	    init_decompress();
	    result = decompress(&outstream);
	    if ((export & EXPORT_ENDMARKER) != 0
	     && result != LZ_ETX
	     && getcode() != (code_int) LZ_ETX) {
		fprintf(stderr, "Decompress didn't finish correctly.\n");
		goto fail;
	    }
	    lz_flush(&outstream);
#ifdef vms
	    if ((export & EXPORT_VMS) != 0)
		fdl_close(fdl_output);
	    else
		fclose(stdout);
#else
	    fclose(stdout);
#endif
#if DEBUG
	    if ((show & SHOW_SERIOUS_DEBUG) != 0)
		dump_tab(stderr);
#endif
	    if ((show & SHOW_STATISTICS) != 0) {
		start_time = cputime() - start_time;
		start_walltime = time(NULL) - start_walltime;
		fprintf(stderr,
		    "%ld.%02ld seconds run, %ld seconds elapsed, for decompression.\n",
		    start_time / 1000L, (start_time % 1000L) / 10L,
		    start_walltime);
	    }
	    exit(EXIT_SUCCESS);
	}
	else {
fail:	    fprintf(stderr, "Error when decompressing \"%s\" to \"%s\"\n",
		(infilename  == NULL) ? "<unknown>" : infilename,
		(outfilename == NULL) ? "<unknown>" : outfilename);
	    exit(status);
	}
}

static int
get_magic_header()
/*
 * Read the compressed file header.  Note: if we switch from
 * Unix to VMS (or vice-versa), we must re-open the file in
 * the new mode.
 */
{
	int		head1;
	int		head2;
	int		head3;
#ifdef vms
	int		old_export = export & (EXPORT_VMS | EXPORT_UNIX);
#endif

	head2 = 0;
	if ((export & EXPORT_HEADER) != 0) {
	    if ((head1 = GET(&instream)) != HEAD1_MAGIC) {
		fprintf(stderr, "Incorrect first header byte 0x%x\n",
		    head1);
		FAIL("can't get header", EXIT_FAILURE);
	    }
	    head2 = GET(&instream);
	    head3 = GET(&instream);
	    switch (head2) {
	    case HEAD2_MAGIC:
		export &= ~(EXPORT_VMS | EXPORT_BLOCK);
		export |= EXPORT_UNIX;
		mode &= ~MODE_DELTA;
		break;

	    case VMS_HEAD2_MAGIC:
		export &= ~(EXPORT_UNIX | EXPORT_BLOCK);
		export |= EXPORT_VMS; /* should we force ENDMARKER true? */
		mode &= ~MODE_DELTA;
		break;

	    default:
		fprintf(stderr, "Incorrect second header byte 0x%x\n",
		    head2);
		FAIL("can't get header", EXIT_FAILURE);
	    }
#ifdef vms
	    /*
	     * If export status changes, we must re-open the input file.
	     */
	    if ((export & (EXPORT_VMS | EXPORT_UNIX)) != old_export)
		return (TRUE);
#endif
	    maxbits = head3 & BIT_MASK;
	    if ((head3 & BLOCK_MASK) != 0)
		export |= EXPORT_BLOCK;
	    if ((head3 & DIFF_MASK) != 0)
		mode |= MODE_DELTA;
#if DEBUG
	    if ((show & SHOW_DEBUG) != 0) {
		fprintf(stderr, "%s: compressed with %d bits,",
		   infilename, maxbits);
		fprintf(stderr, " block compression %s.\n",
		    (export & EXPORT_BLOCK) ? "enabled" : "disabled");
		fprintf(stderr, " differential compression %s.\n",
		    (mode & MODE_DELTA) ? "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", EXIT_FAILURE);
	}
	maxmaxcode = 1 << maxbits;
	if ((export & EXPORT_VMS) != 0)
	    firstcode = GET(&instream) + 0x100;	/* From compressed file	*/
	else if ((export & EXPORT_BLOCK) != 0)
	    firstcode = LZ_CLEAR + 1;		/* Default		*/
	else
	    firstcode = 256;			/* Backwards compatible	*/
#ifdef vms
	if ((export & EXPORT_VMS) != 0) {
	    register code_int	code;
	    char		text[256];
	    extern code_int	getcode();
	    auto int		termin;
	    /*
	     * Get the attribute record.
	     */
	    if ((code = getcode()) != LZ_SOH) {
		fprintf(stderr, "Expected header, read 0x%x\n", code);
		FAIL("can't get header (private)", EXIT_FAILURE);
	    }
	    init_decompress();
	    code = mem_decompress(text, sizeof text, &termin);
	    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", EXIT_FAILURE);
	    }
	    code = atoi(text + ATT_SIZE);
	    fdl_descriptor.dsc$b_class = DSC$K_CLASS_S;
	    fdl_descriptor.dsc$b_dtype = DSC$K_DTYPE_T;
	    /*
	     * Note: malloc should probably be lib$getvm
	     */
	    fdl_descriptor.dsc$a_pointer = malloc(code);
	    fdl_descriptor.dsc$w_length = code;
	    code = mem_decompress(fdl_descriptor.dsc$a_pointer, code, &termin);
	    if (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", EXIT_FAILURE);
	    }
	    if ((show & SHOW_FDL) != 0) {
		fprintf(stderr, "\nFDL information read from \"%s\"\n",
		    infilename);
		fdl_dump(&fdl_descriptor, stderr);
	    }
	    while (termin == LZ_EOR) {
		MORE_ATTRIBUTES		*p;
		int			len;

		code = mem_decompress(text, sizeof text - 1, &termin);
		text[code] = EOS;
		if ((show & SHOW_FDL) != 0)
		    fprintf(stderr, "Additional header: \"%s\"\n", text);
		for (p = more_attributes;; p++) {
		    if (p->name == NULL) {
		        fprintf(stderr,
			    "\nIgnoring unknown descriptor \"%s\"\n", text);
			break;
		    }
		    len = strlen(p->name);
		    if (strncmp(p->name, text, len) == 0) {
			switch (p->type) {
			case INTEGER:
			    *((int *)p->result) = atoi(&text[len]);
			    break;

			case STRING:
			    strcpy(p->result, &text[len]);
			    break;
			}
			break;
		    }
		}
	    }
	    if ((code = getcode()) != LZ_STX) {
		fprintf(stderr, "\nExpecting start of text, got 0x%x\n", code);
		FAIL("no start of text", EXIT_FAILURE);
	    }
	}
#else
	if ((export & EXPORT_VMS) != 0)
	    FAIL("VMS private mode not supported", EXIT_FAILURE);
#endif
	return (FALSE);
}

#ifdef vms
int
mem_decompress(buffer, size, termin)
char_type	*buffer;
int		size;
int		*termin;
/*
 * Decompress up to size bytes to buffer.  Return actual size.
 */
{
	mem_stream.bp = mem_stream.bstart = buffer;
	mem_stream.bend = buffer + size;
	mem_stream.bsize = size;
	mem_stream.func = lz_fail;
	if ((*termin = decompress(&mem_stream)) == LZ_EOR
	 || *termin == LZ_ETX)
	    return (mem_stream.bp - buffer);
	else {
	    fprintf(stderr, "Decompress to memory failed.\n");
	    FAIL("can't decompress to memory", EXIT_FAILURE);
	}
	return (-1);				/* Can't happen		*/
}
#endif

static readonly char *helptext[] = {
	"The following options are valid:",
	"-B\tBinary file (important on VMS/RSX, ignored on Unix)",
	"-F len\tWrite fixed-length records (VMS export only, default len = 512)",
	"-M val\tSet the maximum number of code bits (unless header present)",
	"-V val\tPrint status information or debugging data; bits are:",
	   "  -V 0\tDon't print anything",
	   "  -V 1\tPrint a summary of the operation",
#ifdef vms
	   "  -V 2\tNot used in lzdcmp",
	   "  -V 4\tPrint FDL information (for VMS export mode)",
#endif
#if DEBUG
	   "  -V 8\tPrint some debug information.",
	   "  -V 16\tPrint too much debug information",
	   "  -V 32\tNot used in lzdcmp",
#endif
	"-X val\tSet export (compatibility) mode:",
#ifdef vms
	"  -X 0\tExplicitly choose VMS Private mode",
#endif
	"  -X 1\tCompatible with Unix compress V3.0",
	      "\texcept that two endcodes are expected",
	"  -X 2\tCompatible with Unix compress V3.0",
	"  -X 3\tCompatible with Unix compress V3.0",
	"  -X 4\tDo not read a header, disable \"block-compress\" mode",
	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;
	int		temp;

#ifdef	vms
	argc = getredirection(argc, argv);
	/*
	 * Prescan to see whether we must do a DCL parse.
	 */
	for (j = FALSE, i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		j = TRUE;
		break;
	    }
	}
	if (j == FALSE) {
	    if ((i = lzdcl(argc, argv)) != SS$_NORMAL)
		exit(i);
	    return;
	}
#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':
			mode &= ~MODE_TEXT;
			mode |= MODE_BINARY;
			break;

		    case 'F':
			fblksiz = getvalue(ap, &i, argv, 512);
			if (fblksiz <= 0) {
			    fprintf(stderr, "Illegal -F block size\n");
			    goto usage;
			}
			mode &= ~MODE_TEXT;
			mode |= MODE_FIXED;
			break;

		    case 'M':
			maxbits = getvalue(ap, &i, argv, 0);
			if (maxbits < MIN_BITS) {
			    fprintf(stderr, "Illegal -M value\n");
			    goto usage;
			}
			break;

		    case 'V':
			show = getvalue(ap, &i, argv, 1);
			break;

		    case 'X':
			switch ((temp = getvalue(ap, &i, argv, 1))) {
			case 0:	export = EXPORT_VMS
					| EXPORT_BLOCK
					| EXPORT_HEADER
					| EXPORT_ENDMARKER;
				break;
			case 1:	export = EXPORT_UNIX
					| EXPORT_BLOCK
					| EXPORT_HEADER
					| EXPORT_ENDMARKER;
				break;

			case 2: export = EXPORT_UNIX
					| EXPORT_BLOCK
					| EXPORT_HEADER;
				break;
			case 3:	export = EXPORT_UNIX
					| EXPORT_HEADER;
				break;
			case 4:	export = EXPORT_UNIX;
				break;
			default:
			    fprintf(stderr, "Illegal -X value: %d\n", temp);
			    goto usage;
			}
			break;

		    default:
			fprintf(stderr, "Unknown option \"%c\" in \"%s\"\n",
				c, argv[i]);
usage:			for (hp = helptext; *hp != NULL; hp++)
			    fprintf(stderr, "%s\n", *hp);
			FAIL("unknown option", EXIT_FAILURE);
		    }				/* 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", EXIT_FAILURE);
	}

	if ((mode & MODE_FIXED) != 0 && (mode & MODE_BINARY) != 0) {
	    fprintf(stderr, "Incompatible modes: -B and -F\n");
	    FAIL("option error", EXIT_FAILURE);
	}

}

static int
getvalue(ap, ip, argv, def)
register char		*ap;
int			*ip;
char			*argv[];
int			def;
/*
 * 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(def), don't touch *ap or *ip.
 *
 * The default for "flag option without value" is specified by
 * the caller.
 */
{
	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 = def;
	}
	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", ERROR_EXIT);
	}

#else
#ifdef vms
	stat_t st;
        int stdinflg = FALSE;
	if (infilename == NULL) {
	    stdinflg = TRUE;
	    infilename = malloc(256 + 1);
	    fgetname(stdin, infilename);
	    infilename = realloc(infilename, strlen(infilename) + 1);
	}
	if ((export & EXPORT_VMS) != 0) {
	    if ((fdl_input = fdl_open(infilename, NULL)) == NULL)
		FAIL("can't open compressed input (vms private)", fdl_status);
	}
	else if (!stdinflg) {
	    if (freopen(infilename, "rb", stdin) == NULL) {
		perror(infilename);
		FAIL("can't open compressed input (export)", ERROR_EXIT);
	    }
	}
	if (fstat(fileno(stdin), &st) < 0) {
	    perror(infilename);
	    FAIL("can't stat input file", ERROR_EXIT);
	}
	instream.filsz = st.st_size;
	instream.eofhack = FALSE;
	if (st.st_fab_rfm == FAB$C_FIX && 
	     st.st_fab_rat == 0 &&
	      st.st_fab_mrs == 512 &&
	       (st.st_size & 511) != 0) {
	    instream.eofhack = TRUE;
	}

#else
	if (infilename == NULL)
	    infilename = "<stdin>";
	else {
	    if (freopen(infilename, "rb", stdin) == NULL) {
		perror(infilename);
		FAIL("can't open compressed input", ERROR_EXIT);
	    }
	}
#endif
#endif

 	instream.bp = instream.bend = NULL;
	instream.bstart = inbuffer;
	instream.bsize = sizeof inbuffer;
	instream.func = lz_fill;
}

openoutput()
{
#ifdef vms
	if ((export & EXPORT_VMS) != 0) {
	    extern FDLSTUFF *fdl_create();

	    fclose(stdout);
	    stdout = NULL;
	    fdl_output = fdl_create(&fdl_descriptor, outfilename, xab_lrl);
	    if (fdl_output == NULL) {
		fprintf(stderr, "Can't create output file\n");
		FAIL("can't create output (vms private)", fdl_status);
	    }
	    if (outfilename == NULL) {
		outfilename = malloc(256 + 1);
		fdl_getname(fdl_output, outfilename);
		outfilename = realloc(outfilename, strlen(outfilename) + 1);
	    }
	}

	else {
	    int stdoutflg = FALSE;
	    if (outfilename == NULL) {
		stdoutflg = TRUE;
		outfilename = malloc(256 + 1);
		fgetname(stdout, outfilename);
		outfilename = realloc(outfilename, strlen(outfilename) + 1);
	    }
	    if ((mode & MODE_BINARY) != 0) {
		if (freopen(outfilename, "wb", stdout,
			    "alq=256", "fop=tef") == NULL) {
		    perror(outfilename);
		    FAIL("can't create output (binary)", ERROR_EXIT);
		}
	    }
	    else if ((mode & MODE_FIXED) != 0) {
		char bls[20], mrs[20];
		sprintf(bls, "bls=%d", fblksiz);
		sprintf(mrs, "mrs=%d", fblksiz);
		if (freopen(outfilename, "wb", stdout,
			    "alq=256", "fop=tef",
			    "rfm=fix", bls, mrs) == NULL) {
		    perror(outfilename);
		    FAIL("can't create output (fixed)", ERROR_EXIT);
		}
	    }
	    else if (!stdoutflg) {
	    	if (freopen(outfilename, "w", stdout,
			    "alq=256", "fop=tef",
			    "rat=cr", "rfm=var") == NULL) {
		    perror(outfilename);
		    FAIL("can't create output (text)", ERROR_EXIT);
		}
	    }
	}

#else
#ifdef decus
	if (outfilename == NULL) {
	    outfilename = malloc(256 + 1);
	    fgetname(stdout, outfilename);
	    outfilename = realloc(outfilename, strlen(outfilename) + 1);
	}
	if (freopen(outfilename,
		((mode & (MODE_BINARY | MODE_FIXED)) != 0) ? "wn" : "w",
		stdout) == NULL) {
	    perror(outfilename);
	    FAIL("can't create", ERROR_EXIT);
	}

#else
	if (outfilename == NULL)
	    outfilename = "<stdout>";
	else {
	    if (freopen(outfilename, "w", stdout) == NULL) {
		perror(outfilename);
		FAIL("can't create", ERROR_EXIT);
	    }
	}
#endif
#endif

	outstream.bp = outstream.bstart = outbuffer;
	outstream.bend = outbuffer + sizeof outbuffer;
	outstream.bsize = sizeof outbuffer;
	outstream.func = lz_flush;
}
