/*
 * VEC.C (VEC)
 *
 * Copyright (c) 1991, 1992, 1993 Ville Saari
 * All rights reserved
 *
 * Created: 18-Dec-91
 * Updated: 04-Dec-93
 */

#include "vec.h"

const static struct
	{
	unsigned char uuencodetab[4];
	unsigned char encodetab[182];
	} tabs=
	{
	/* characters used by uuencode method: */

	96, 33, 34, 35,

	/* characters used by all methods: */

	36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
	68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
	84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,

	/* characters used by all but uuencode method */

	96, 97, 98, 99,

	/* characters used by >=6.5 bit methods: */

	100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
	114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,

	/* characters used by >=7 bit methods: */

	161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
	171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
	181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
	191, 192, 193, 194, 195, 196, 197,

	/* characters used by 7.5 bit method: */

	198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
	208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
	218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
	228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
	238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
	248, 249, 250, 251
	};

static const unsigned char hextab[]=
	{
	'0', '1', '2', '3', '4', '5', '6', '7',
	'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
	};

#define uuencodetab tabs.uuencodetab
#define encodetab tabs.encodetab

static int lineleft;

static void sevenandhalfbitcoder(const unsigned char *i, unsigned char *o)
	{
	const unsigned char *j=i+8;
	unsigned int z;

	z=(*i++|*j++<<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=(*i++|*j++<<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=(*i++|*j++<<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=(*i++|*j++<<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=(*i++|*j++<<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=(*i++|*j++<<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=(*i++|*j  <<8)&0x7fff;
	*o++=encodetab[z%182];
	*o++=encodetab[z/182];

	z=*i++;
	z|=(*i++&0x80)<<1;
	z|=(*i++&0x80)<<2;
	z|=(*i++&0x80)<<3;
	z|=(*i++&0x80)<<4;
	z|=(*i++&0x80)<<5;
	z|=(*i++&0x80)<<6;
	z|=(*i  &0x80)<<7;
	*o++=encodetab[z%182];
	*o  =encodetab[z/182];
	}

static void sevenbitcoder(const unsigned char *i, unsigned char *o)
	{
	unsigned int z;
	unsigned char c;

	*o++=encodetab[(c=*i++)&0x7f]; z=c>>7;
	*o++=encodetab[(c=*i++)&0x7f]; z|=(c&0x80)>>6;
	*o++=encodetab[(c=*i++)&0x7f]; z|=(c&0x80)>>5;
	*o++=encodetab[(c=*i++)&0x7f]; z|=(c&0x80)>>4;
	*o++=encodetab[(c=*i++)&0x7f]; z|=(c&0x80)>>3;
	*o++=encodetab[(c=*i++)&0x7f]; z|=(c&0x80)>>2;
	*o++=encodetab[(c=*i  )&0x7f]; z|=(c&0x80)>>1;
	*o  =encodetab[z];
	}

static void sixandhalfbitcoder(const unsigned char *i, unsigned char *o)
	{
	const unsigned char *j=i+8;
	unsigned int z;

	z=*i++|((*j++&0x1f)<<8);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	z=*i++|((*j++&0x1f)<<8);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	z=*i++|((*j++&0x1f)<<8);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	z=*i++|((*j++&0x1f)<<8);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	z=*i++|((*j  &0x1f)<<8);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	j-=4;
	z=*i++|((j[0]&0xe0)<<3)|((j[3]&0x60)<<6);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	j++;
	z=*i++|((j[0]&0xe0)<<3)|((j[3]&0x60)<<6);
	*o++=encodetab[z%91];
	*o++=encodetab[z/91];

	j++;
	z=*i  |((j[0]&0xe0)<<3)|((j[1]&0x80)<<4)|((j[2]&0x80)<<5);
	*o++=encodetab[z%91];
	*o  =encodetab[z/91];
	}

static void sixbitcoder(const unsigned char *i, unsigned char *o)
	{
	*o++=encodetab[*i++&0x3f];
	*o++=encodetab[*i++&0x3f];
	*o++=encodetab[*i++&0x3f];
	*o++=encodetab[*i++&0x3f];
	*o++=encodetab[*i++&0x3f];
	*o++=encodetab[*i  &0x3f];

	i-=5;

		{
		unsigned int z;

		z=*i++>>6;
		z|=(*i++&0xc0)>>4;
		z|=(*i++&0xc0)>>2;
		*o++=encodetab[z];

		z=*i++>>6;
		z|=(*i++&0xc0)>>4;
		z|=(*i  &0xc0)>>2;
		*o=encodetab[z];
		}
	}

static void hexcoder(const unsigned char *i, unsigned char *o)
	{
	*o++=hextab[*i>>4&0xf];
	*o  =hextab[*i   &0xf];
	}

static char *stox(unsigned short s)
	{
	static char buf[5];
	unsigned char t;

	t=(char)(s>>8);
	hexcoder(&t, buf);

	t=(char)s;
	hexcoder(&t, buf+2);

	return buf;
	}

static void uucoder(const unsigned char *i, unsigned char *o)
	{
   unsigned int t, u;

   *o++=uuencodetab[          (t=*i++)>>2    ];
   *o++=uuencodetab[t<<4&0x30|(u=*i++)>>4&0xf];
   *o++=uuencodetab[u<<2&0x3c|(t=*i  )>>6&0x3];
   *o  =uuencodetab[t   &0x3f                ];
	}

static void newline(void)
	{
	if(cr) put1(13);
	put1(10);

	lineleft=width;
	}

static void breakput(const unsigned char *block, int size)
	{
	for(;;)
		{
		if((lineleft-=size)>=0)
			{
			put(block, size);
			return;
			}

		put(block, lineleft+size);

		block+=lineleft+size;
		size=-lineleft;

		newline();
		}
	}

static void addbreaks(unsigned long size)
	{
	while(lineleft<size)
		{
		outptr+=lineleft;
		size-=lineleft;
		lineleft=width;

		if(cr)
			{
			unsigned int b=size;
			char *f=outptr+b;
			char *t=f+2;

			while(b--) *--t=*--f;

			*outptr++=13;
			}
		else
			{
			unsigned int b=size;
			char *f=outptr+b;
			char *t=f+1;

			while(b--) *--t=*--f;
			}

		*outptr++=10;
		}

	outptr+=size;
	lineleft-=size;

	if(outptr>=outbufend) flushoutput();
	}

#define checkeol(n) { if(lineleft<=n && (lineleft<n || next!=10)) \
	{ put1(94); newline(); } lineleft-=n; }

static void asciiencode(void)
	{
	unsigned char current, next;
	unsigned short crc=0;
	int eof=0, iso=method=='i';

	if(inptr>=inbufend && !fillbuffer(1))
		{
		eof=1;
		next=0;
		}
	else
		next=*inptr++;

	while(!eof)
		{
		current=next;

		if(inptr>=inbufend && !fillbuffer(1))
			{
			eof=1;
			next=0;
			}
		else
			next=*inptr++;

		if(!quick) crc=(crc>>8)^crc16tab[(unsigned char)(crc^current)];

		if(current==10 && shortlines || !lineleft)
			newline();
		else if(current<32 && (!literaltabs || current!=9))
			{
			if(lineleft<=3)
				if(current==10 && (lineleft<3 || next<32 && next!=10 ||
					next==92 || next==94 || next>127 && (!iso || next<160)))
					{
					newline();
					continue;
					}
				else if(lineleft<2 || lineleft==2 && next!=10)
					{
					put1(94);
					newline();
					}

			lineleft-=2;
			put1(94);
			put1(current+64);
			}
		else if((current==92 && !iso) || current==94 &&
			(next<32 || next>(iso?60:58) && (!iso || next<160 || next==255) ||
			next==33 || lineleft<3))
			{
			checkeol(2);
			put1(94);
			put1(current==92?60:61);
			}
		else if(current<127 || iso && current>=160)
			{
			checkeol(1);
			put1(current);
			}
		else
			{
			checkeol(2);

			if(current==255)
				{	
				put1(94);
				put1(59);
				}
			else if(current>=160)
				{
				put1(92);
				put1(current+128);
				}
			else if(current==127)
				{	
				put1(94);
				put1(63);
				}
			else if(current==159)
				{	
				put1(94);
				put1(62);
				}
			else
				{
				put1(94);
				put1(current-32);
				}
			}
		}

	breakput("^!", 2);

	if(!quick)
		breakput(stox(crc), 4);
	}

static void uuencode(const char *filename)
	{
	unsigned char flagbuf[3]={'6', '0', '0'};
	unsigned char outbuf[62], *inbuf, *i, *o, *l;
	unsigned long size=0;

	if(filename && !nomode) getfilemode(filename, flagbuf, 1);

	put("begin ", 6);
	put(flagbuf, 3);
	put(" ", 1);

	if(name) filename=name;
	filename=basename(filename);
	if(!*filename) filename="noname";

	put(filename, strlen(filename));
	newline();

	do	{
		i=inbuf=get(45);
		o=outbuf;
		*o++=uuencodetab[trueread];

		l=i+trueread;

		while(i<l)
			{
			uucoder(i, o);
			i+=3;
			o+=4;
			}

		if(!quick)
			{
			unsigned int s=0;

			i=inbuf;
	
			while(i<l)
				{
				s+=*i++;
				s+=*i++;
				s+=*i++;
				}

			*o++=uuencodetab[s&0x3F];
			size+=trueread;
			}

		put(outbuf, o-outbuf);
		newline();
		}
	while(trueread);

	put("end", 3);
	newline();

	if(!quick)
		{
		char *t=ltoa(size);

		put("size ", 5);
		put(t, strlen(t));
		newline();
		}
	}

void vec(const char *filename)
	{
	unsigned char flagbuf[6], outblockbuf[16], *indata;
	int inblock, outblock;
	unsigned short crc=0;
	void (*coder)(const unsigned char *, unsigned char *)=0;

	flagbuf[0]=!quick;
	flagbuf[1]=flagbuf[2]=flagbuf[3]=flagbuf[4]=flagbuf[5]=0;

	if(filename && !nomode)
		getfilemode(filename, flagbuf, 0);

	if(name) filename=name;
	filename=basename(filename);

	if(!silent && *filename)
		{
		printerr(filename);
		printerr("\n");
		}

	if(!*filename) filename="noname";

	if(method=='u')
		{
		uuencode(filename);
		return;
		}

	if(!quick) flagbuf[0]|=1;

	sixbitcoder(flagbuf, outblockbuf);

	put("yobufi", 6);
	put(&method, 1);
	put(outblockbuf, 8);
	put(filename, strlen(filename));
	newline();

	switch(method)
		{
		case '3':
			coder=sevenandhalfbitcoder;
			inblock=15;
			outblock=16;
			break;

		case '2':
			coder=sevenbitcoder;
			inblock=7;
			outblock=8;
			break;

		case '1':
			coder=sixandhalfbitcoder;
			inblock=13;
			outblock=16;
			break;

		case '0':
			coder=sixbitcoder;
			inblock=6;
			outblock=8;
			break;

		case 'x':
			coder=hexcoder;
			inblock=1;
			outblock=2;
		}

	if(coder)
		{
		while(indata=get(inblock), trueread)
			{
			coder(indata, outptr);
			addbreaks(outblock);

			if(!quick)
				{
				unsigned int datainblock=trueread;
				unsigned char *t=indata;

			   while(datainblock--)
			      crc=(crc>>8)^crc16tab[(unsigned char)(crc^*t++)];
				}

			if(trueread<inblock) break;
			}

		if(!trueread) trueread=inblock;

		breakput("!", 1);
		breakput(hextab+inblock-trueread, 1);

		if(!quick)
			breakput(stox(crc), 4);
		}
	else
		asciiencode();

	newline();
	}
