/*
 *			l z i o . c
 *
 * I/O buffer management.  All input/output I/O is done through these
 * routines (and the macros in lz.h).  The rules of the game are:
 *
 * input via GET and GETBUF.
 *	GET returns an 8-bit byte, or -1 on eof/error.
 *	GETBUF returns the number of things gotten, or -1 on eof/error.
 *
 * output via PUT and PUTBUF
 *	No return on error.
 * flush output by LZ_FLUSH before closing files -- or you'll lose data.
 */

#include	"lz.h"

#ifndef	OS_DEBUG_IO

LZ lz_buffer = {
    NULL, NULL, 		/* ip, iend				*/
    lz_buffer.obuf,		/* op					*/
    &lz_buffer.obuf[MAXIO],	/* oend					*/
}; 

/*
 * Low-level I/O routines.
 */

int
lz_fill()
{
	register int	i;
	extern char	*infilename;

#ifdef unix
	i = read(fileno(stdin), (char *) LZ_IO.ibuf, MAXIO);
#else
	i = fread((char *) LZ_IO.ibuf, 1, MAXIO, stdin);
	if (ferror(stdin)) {
	    perror(infilename);
	    exit(IO_ERROR);
	}
#endif
	if (i <= 0)
	    return (EOF);
	else {
	    LZ_IO.ip = LZ_IO.ibuf;
	    LZ_IO.iend = &LZ_IO.ibuf[i];
#if UCHAR
	    return (*LZ_IO.ip++);
#else
	    return (*LZ_IO.ip++ & 0xFF);
#endif
	}
}

lz_flush()
{
	register int	count;
	extern char	*outfilename;

	count = LZ_IO.op - &LZ_IO.obuf[0];
#ifdef unix
	if (write(fileno(stdout), (char *) LZ_IO.obuf, count) != count) {
	    perror(outfilename);
	    fprintf(stderr, "Can't write to \"%s\"\n", outfilename);
	    exit(IO_ERROR);
	}
#else
	if (fwrite((char *) LZ_IO.obuf, 1, count, stdout) != count
	  || ferror(stdout)) {
	    perror(outfilename);
	    exit(IO_ERROR);
	}
#endif
	LZ_IO.op = LZ_IO.obuf;
}

int
lz_getbuf(buffer, count)
char_type		*buffer;
int			count;
/*
 * Read a block of data -- be clever.  Return number gotten, or -1
 * on eof.
 */
{
	register char_type	*bp;		/* -> buffer		*/
	register char_type	*ip;		/* -> I/O buffer	*/
	register char_type	*ep;		/* End of segment	*/
	register int		remaining;	/* Size of segment	*/
	int			datum;

	if (count == 0)				/* Shouldn't happen	*/
	    return (0);
	bp = buffer;
	while (--count >= 0) {
	    if ((datum = GET()) == EOF)		/* Maybe fill LZ buff	*/
		break;
	    *bp++ = datum;
	    remaining = LZ_IO.iend - (ip = LZ_IO.ip);
	    if (remaining > count)
		remaining = count;
	    ep = &ip[remaining];
	    while (ip < ep)
		*bp++ = *ip++;
	    count -= remaining;
	    LZ_IO.ip = ip;			/* Refresh buffer	*/
	}
	return ((bp == buffer) ? -1 : bp - buffer);
}

int
lz_putbuf(bp, count)
register char_type	*bp;
int			count;
/*
 * Write a block of data -- be clever.
 */
{
	register char_type	*op;		/* -> I/O buffer	*/
	register char_type	*ep;		/* End of segment	*/
	register int		remaining;	/* Size of segment	*/

	while (--count >= 0) {
	    PUT(*bp++);				/* Forces a buffer	*/
	    remaining = LZ_IO.oend - (op = LZ_IO.op);
	    if (remaining > count)
		remaining = count;
	    ep = &op[remaining];
	    while (op < ep)
		*op++ = *bp++;
	    count -= remaining;
	    LZ_IO.op = op;			/* Refresh buffer	*/
	}
}
#endif

/*
 * getredirection() is intended to aid in porting C programs
 * to VMS (Vax-11 C) which does not support '>' and '<'
 * I/O redirection.  With suitable modification, it may
 * useful for other portability problems as well.
 */

#ifdef	vms

int
getredirection(argc, argv)
int		argc;
char		**argv;
/*
 * Process vms redirection arg's.  Exit if any error is seen.
 * If getredirection() processes an argument, it is erased
 * from the vector.  getredirection() returns a new argc value.
 *
 * Warning: do not try to simplify the code for vms.  The code
 * presupposes that getredirection() is called before any data is
 * read from stdin or written to stdout.
 *
 * Normal usage is as follows:
 *
 *	main(argc, argv)
 *	int		argc;
 *	char		*argv[];
 *	{
 *		argc = getredirection(argc, argv);
 *	}
 */
{
	register char		*ap;	/* Argument pointer	*/
	int			i;	/* argv[] index		*/
	int			j;	/* Output index		*/
	int			file;	/* File_descriptor 	*/

	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
	    switch (*(ap = argv[i])) {
	    case '<':			/* <file		*/
		if (freopen(++ap, "r", stdin) == NULL) {
		    perror(ap);		/* Can't find file	*/
		    exit(IO_ERROR);	/* Is a fatal error	*/
		}
		break;

	    case '>':			/* >file or >>file	*/
		if (*++ap == '>') {	/* >>file		*/
		    /*
		     * If the file exists, and is writable by us,
		     * call freopen to append to the file (using the
		     * file's current attributes).  Otherwise, create
		     * a new file with "vanilla" attributes as if
		     * the argument was given as ">filename".
		     * access(name, 2) is TRUE if we can write on
		     * the specified file.
		     */
		    if (access(++ap, 2) == 0) {
			if (freopen(ap, "a", stdout) != NULL)
			    break;	/* Exit case statement	*/
			perror(ap);	/* Error, can't append	*/
			exit(IO_ERROR);	/* After access test	*/
		    }			/* If file accessable	*/
		}
		/*
		 * On vms, we want to create the file using "standard"
		 * record attributes.  create(...) creates the file
		 * using the caller's default protection mask and
		 * "variable length, implied carriage return"
		 * attributes. dup2() associates the file with stdout.
		 */
		if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
		 || dup2(file, fileno(stdout)) == -1) {
		    perror(ap);		/* Can't create file	*/
		    exit(IO_ERROR);	/* is a fatal error	*/
		}			/* If '>' creation	*/
		break;			/* Exit case test	*/

	    default:
		argv[j++] = ap;		/* Not a redirector	*/
		break;			/* Exit case test	*/
	    }
	}				/* For all arguments	*/
	argv[j] = NULL;			/* Terminate argv[]	*/
	return (j);			/* Return new argc	*/
}
#endif
