/*
 * $Header: /usr/people/tcl/src/uutar/RCS/encode.c,v 1.1.1.5 1993/09/11 22:42:56 tcl Exp $
 * Tom Lawrence
 * tcl@sgi.com
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include "codes.h"

static FILE *infp, *outfp;
static char *name, *charset;
static mode_t inmode;

static void
usage()
{
    printf("options:\n");
    printf("-i <inputfile>\n");
    printf("-o <outputfile>\n");
    printf("-n <name>\n");
    printf("-c <charset>\n");
    exit(1);
}

/* parse command line arguments */
static void
parse(argc, argv)
    int argc;
    char **argv;
{
    char *infile, *outfile;
    struct stat statbuf;

    infile = outfile = 0;
    name = charset = 0;

    while(--argc) {
	argv++;
	if (!strcmp(*argv, "-i")) {
	    if (argc < 2)
		usage();
	    argc--;
	    argv++;
	    infile = *argv;
	}
	else if (!strcmp(*argv, "-o")) {
	    if (argc < 2)
		usage();
	    argc--;
	    argv++;
	    outfile = *argv;
	}
	else if (!strcmp(*argv, "-n")) {
	    if (argc < 2)
		usage();
	    argc--;
	    argv++;
	    name = *argv;
	}
	else if (!strcmp(*argv, "-c")) {
	    if (argc < 2)
		usage();
	    argc--;
	    argv++;
	    charset = *argv;
	}
	else
	    usage();
    }

    /* open the input stream */
    if (infile) {
	if ((infp = fopen(infile, "r")) == 0) {
	    perror(infile);
	    exit(1);
	}
	if (stat(infile, &statbuf) < 0) {
	    perror(infile);
	    exit(1);
	}
	inmode = statbuf.st_mode & 0777;
    }
    else {
	infp = stdin;
	inmode = 0666;
    }

    /* open the output stream */
    if (outfile) {
	if ((outfp = fopen(outfile, "w")) == 0) {
	    perror(outfile);
	    exit(1);
	}
    }
    else
	outfp = stdout;

    /* get the filename to store in the encoded file */
    if (name == 0) {
	if (infile == 0)
	    name = "stdin";
	else
	    name = infile;
    }

    /* set default character set if none was specified */
    if (charset == 0)
	charset = "32-126";
}

main(argc, argv)
    int argc;
    char **argv;
{
    int c;
    unsigned short buf;
    int buf_offset, inlen, cols = 0, pattern;
    unsigned int cksum;

    /* parse command line arguments */
    parse(argc, argv);

    /* parse the supplied character set specification and initialize
     * tables based on that set
     */
    parse_charval_list(charset);
    init_codes(ENCODE);

    fprintf(outfp, "BEGIN %o %s ", inmode, name);
    print_charval_list(outfp);
    putc('\n', outfp);

    /* clear the sliding input buffer */
    buf = 0;
    buf_offset = 16;
    
    cksum = 0;

    /* read in the input file */
    while((c = getc(infp)) != EOF) {

	/* compute a checksum on the input file */
	cksum = ((cksum << 7) | (cksum >> 25)) ^ (unsigned)c;

	/* shift the byte just read in into our sliding buffer */
	buf_offset -= 8;
	buf |= ((unsigned short)c << buf_offset);

	/* see if there are any complete variable length bitfields
	 * in the input buffer. If so, output their corresponding
	 * printable output character and advance the input buffer
	 * by their length in bits
	 */
	while (1) {

	    /* grab the next 8 bits in the input bitstream */
	    pattern = (int)(buf >> 8);

	    /* determine how many of those bits we will need
	     * to extract from the sliding buffer
	     */
	    inlen = codes[pattern].len;

	    /* if there are not enough bits in the sliding
	     * buffer, stop for now. (interestingly, you don't need
	     * to have all of the needed bits in order to determine
	     * that you don't have all of the needed bits)
	     */
	    if (inlen > (16 - buf_offset))
		break;

	    /* output the printable character associated with
	     * the variable length bitfield recognized in the
	     * input bitstream
	     */
	    putc(codes[pattern].code, outfp);

	    /* limit our width */
	    if (++cols == 79) {
		cols = 0;
		putc('\n', outfp);
	    }

	    /* advance the input bitstream by the length of the bitfield
	     * just recognized
	     */
	    buf_offset += inlen;
	    buf <<= inlen;
	}
    }

    /* flush the buffer. The last byte read in may still have some
     * of its bits in the sliding buffer. If so, print out one more
     * output character. This will necessarily append some garbage
     * bits to the output but what can we do? we can't write files
     * at a finer granularity that the byte. The decoder will ignore
     * them so it's ok
     */
    if (buf_offset < 16) {
	putc(codes[pattern].code, outfp);
	cols++;
    }

    /* indicate end of encoded data by 2 consecutive newlines followed
     * by the keyword END. This is necessary since the END line itself
     * is potentially valid encoded data
     */
    if (cols)
	putc('\n', outfp);
    fprintf(outfp, "\nEND %X\n", cksum);
}
