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

#include "vec.h"

#define MC 181
#define UC 252
#define EC 253 
#define HW 254
#define VW 255

#define hws(c) (decodetab[c]==HW)
#define vws(c) (decodetab[c]==VW)
#define ws(c) (decodetab[c]>=HW)

const static unsigned char decodetab[256]=
	{
	/* unused characters, white space characters and end character: */

	UC, UC, UC, UC, UC, UC, UC, UC, UC, HW, VW, VW, UC, VW, UC, UC,
	UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC,
	HW, EC, UC, UC,

	/* bytes used by all methods: */

	0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
	16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
	32, 33, 34, 35, 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,

	/* additional bytes for >=6.5 bit methods: */

	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,

	/* unused characters */

	UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC,
	UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC,

	/* additional bytes for >=7 bit methods: */

	 91,  92,  93,  94,  95,  96,  97,  98,  99, 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, 127,

	/* additional bytes for 7.5 bit method: */

	128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
	138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
	148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
	158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
	168, 169, 170,	171, 172, 173, 174, 175, 176, 177,
	178, 179, 180,	181,

	/* unused characters */

	UC, UC, UC, UC
	};

static unsigned char uudecodetab[256];

static struct namedata
	{
	struct namedata *next;
	int count;
	char name[1];
	} *ndbase=0;

static unsigned char *start;
static unsigned char linedelim;

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

	z=*i++; z+=182**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=182**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=182**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=182**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=182**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=182**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=182**i++; *o++=z; *p  =z>>8;
	z=*i++; z+=182**i  ; *o++=z;

	*o++|=(z>>=1)&0x80;
	*o++|=(z>>=1)&0x80;
	*o++|=(z>>=1)&0x80;
	*o++|=(z>>=1)&0x80;
	*o++|=(z>>=1)&0x80;
	*o++|=(z>>=1)&0x80;
	*o  |=(z>>=1)&0x80;
	}

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

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

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

	z=*i++; z+=91**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=91**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=91**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=91**i++; *o++=z; *p++=z>>8;
	z=*i++; z+=91**i++; *o++=z; *p  =z>>8;

	z=*i++; z+=91**i++; *o++=z;
	p[-4]|=(z>>3)&0xe0;
	p[-1]|=(z>>6)&0x60;

	z=*i++; z+=91**i++; *o++=z;
	p[-3]|=(z>>3)&0xe0;
	p[ 0]|=(z>>6)&0x60;

	z=*i++; z+=91**i; *o=z;
	p[-2]|=(z>>3)&0xe0;
	p[-1]|=(z>>4)&0x80;
	p[ 0]|=(z>>5)&0x80;
	}

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

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

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

static int gethexval(unsigned char val)
	{
	return val-(val<25?12:val<48?19:51);
	}

static void hexdecoder(const unsigned char *i, unsigned char *o)
	{
	*o=gethexval(i[0])<<4|gethexval(i[1]);
	}

static int fecdecoder(void)
	{
	int c;

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) return -7;
		c=*inptr++;
		}
	while(vws(c));

	if(c>=248) return c==248?-7:-c+248;

	return c-(c>159?65:33);
	}

static int btoadecoder(unsigned char *buf)
	{
	int c;
	unsigned long z;

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) return 0;
		c=*inptr++;
		}
	while(vws(c));

	if(c=='z')
		{
		buf[0]=buf[1]=buf[2]=buf[3]=0;
		return 1;
		}

	if(c=='x') return 0;

	z=((unsigned long)(c-'!'))*(85*85*85*85);

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) return 0;
		c=*inptr++;
		}
	while(vws(c));

	z+=((unsigned long)(c-'!'))*(85*85*85);

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) return 0;
		c=*inptr++;
		}
	while(vws(c));

	z+=((unsigned long)(c-'!'))*(85*85);

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) return 0;
		c=*inptr++;
		}
	while(vws(c));

	z+=((unsigned long)(c-'!'))*85;

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) return 0;
		c=*inptr++;
		}
	while(vws(c));

	z+=(unsigned long)(c-'!');

	*buf++=(unsigned char)(z>>24);
	*buf++=(unsigned char)(z>>16);
	*buf++=(unsigned char)(z>>8);
	*buf  =(unsigned char)z;

	return 1;
	}

static void getname(const char *from, char *to, const char *s)
	{
	struct namedata *nd;

	strncpy(to, from, 256);

	for(nd=ndbase; nd; nd=nd->next)
		if(!strcmp(nd->name, from))
			{
			nd->count++;

			modifyname(to, nd->count);

			if(!silent)
				{
				printerr(from);
				printerr(" -> ");
				printerr(to);
				printerr(s);
				}

			return;
			}

	if(nd=(struct namedata *)malloc(strlen(to)+sizeof *nd))
		{
		strcpy(nd->name, to);
		nd->count=0;
		nd->next=ndbase;
		ndbase=nd;
		}

	if(!silent)
		{
		printerr(to);
		printerr(s);
		}
	}

static int getline(unsigned char *buf, int size)
	{
	unsigned char c=0, *e=buf+size;

	do	{
		if(inptr>=inbufend && !fillbuffer(1)) goto end;
		c=*inptr++;
		}
	while(vws(c));

	do	{
		*buf++=c;
		if(inptr>=inbufend && !fillbuffer(1)) break;
		c=*inptr++;
		}
	while(!vws(c) && buf<e);

end:
	linedelim=c;
	*buf=0;

	return size-(e-buf);
	}

static int myfile;
static unsigned char namebuf[256];

static void startoutput(const unsigned char *filename, const unsigned char *method)
	{
	if(lines==1)
		{
#ifdef FINNISH
		writefile(stdoutfile, "### Koodattu tiedosto poistettu: ", 33);
#else
		writefile(stdoutfile, "### Encoded file removed: ", 26);
#endif
		writefile(stdoutfile, filename, strlen(filename));
		writefile(stdoutfile, method, strlen(method));
		}

	if(outfileflag || pipe)
		{
		if(!silent)
			{
			printerr(filename);
			printerr(method);
			}

		myfile=0;
		}
	else
		{
		getname(filename, namebuf, method);

		if(failedopen(outfile=openwrite(namebuf, 1)))
			{
#ifdef FINNISH
			printerrh("Tiedosto `");
			printerr(namebuf);
			printerr("' ei aukea.\n");
#else
			printerrh("Can't open output file `");
			printerr(namebuf);
			printerr("'.\n");
#endif
			errcleanup(0);
			}

		outfileflag=myfile=1;
		}
	}

static void endoutput(const unsigned char *flags)
	{
	if(!myfile) return;

	flushoutput();
	closefile(outfile);
	outfileflag=myfile=0;

	if(flags && !nomode) setfilemode(namebuf, flags);
	}

static unsigned long illegal;

static int getvecblock(char *p, int size)
	{
	unsigned int c, r;
	unsigned char *b;

	do	{
		b=get(r=size);

		if(trueread<size)
			{
			if(trueread && *b=='!') b++;
			inptr=b;
			return 0;
			}

		while(r--)
			{
			if((c=decodetab[*b++])>MC)
				{
				if(c==EC)
					{
					inptr=b;
					return 0;
					}
				if(c==UC) illegal++;
				}
			else
				{
				*p++=c;
				size--;
				}
			}
		}
	while(size);

	return 1;
	}

static unsigned short updatecrc(unsigned short crc, const unsigned char *p, unsigned long size)
	{
	while(size--) crc=(crc>>8)^crc16tab[(unsigned char)(crc^*p++)];
	return crc;
	}

static int decodevec(unsigned char *linebuf)
	{
	unsigned char flags[6], inblockbuf[16], *p, *startptr=outptr;
	void (*decoder)(const unsigned char *, unsigned char *);
	int inblock, outblock, makecrc, crc, t, ok=1;

	*linebuf=0;
	crc=0;
	illegal=0;

	if(strlen(linebuf+1)<14)
		{
#ifdef FINNISH
		printerr("Virheellinen vec headeri!\n");
#else
		printerr("Corrupted vec header!\n");
#endif
		return 0;
		}

	for(p=linebuf+7; p<linebuf+15; p++) *p=decodetab[*p];

	sixbitdecoder(linebuf+7, flags);

	if(linebuf[15])
		startoutput(linebuf+15, "\n");
	else
		startoutput("noname", "\n");

	makecrc=((flags[0]&1) && !quick);

	switch(linebuf[6])
		{
		case '3':
			decoder=sevenandhalfbitdecoder;
			inblock=16;
			outblock=15;
			break;

		case '2':
			decoder=sevenbitdecoder;
			inblock=8;
			outblock=7;
			break;

		case '1':
			decoder=sixandhalfbitdecoder;
			inblock=16;
			outblock=13;
			break;

		case '0':
			decoder=sixbitdecoder;
			inblock=8;
			outblock=6;
			break;

		case 'x':
			decoder=hexdecoder;
			inblock=2;
			outblock=1;
			break;

		case 'a':
			inblock=0;
			outblock=0;
			break;

		case 'i':
			inblock=0;
			outblock=1;
			break;

		default:
			{
#ifdef FINNISH
			static char herja[]="   Tuntematon koodausmenetelm" a_ " ` '!\n";

			herja[32]=linebuf[6];
			printerr(herja);
#else
			static char nag[]="   Unknown coding method ` '!\n";

			nag[26]=linebuf[6];
			printerr(nag);
#endif
			endoutput(0);
			return 0;
			}
		}

	if(inblock)
		{
		while(getvecblock(inblockbuf, inblock))
			{
			if(outptr>=outbufend)
				{
				if(makecrc) crc=updatecrc(crc, startptr, outptr-startptr);
				flushoutput();
				startptr=outptr;
				}

			decoder(inblockbuf, outptr);
			outptr+=outblock;
			}
		}
	else
		{
		unsigned char c;
		
		for(;;)
			{
			if(inptr>=inbufend && !fillbuffer(1)) break;
			c=*inptr++;

			if(c==94 || c==92 && !outblock)
				{
				unsigned char c2=c;

				if(inptr>=inbufend && !fillbuffer(1)) break;
				c=*inptr++;

				if(c2==94)
					if(vws(c))
						continue;
					else if((c&0xe0)==0x40)
						c-=64;
					else if((c&0xe0)==0x60)
						c+=32;
					else if(c==33)
						break;
					else if(c==63)
						c=127;
					else if(c==62)
						c=159;
					else if(c==61)
						c=94;
					else if(c==60 && !outblock)
						c=92;
					else if(c==59 && !outblock)
						c=255;
					else
						{
						if(makecrc) crc=(crc>>8)^crc16tab[(unsigned char)(crc^94)];
						put1(94);
						}
				else
					c+=128;
				}
			else if(vws(c) && c!=10)
				continue;

			if(makecrc) crc=(crc>>8)^crc16tab[(unsigned char)(crc^c)];

			put1(c);
			}
		}

	if(!getvecblock(p=inblockbuf, (inblock?1:0)+(makecrc?4:0)))
		{
#ifdef FINNISH
		printerr("   Tiedosto loppui kesken!\n");
#else
		printerr("   Premature EOF!\n");
#endif
		}
	else
		{
		if(inblock)
			{
			if((t=gethexval(*p++))>outblock || t<0)
				{
#ifdef FINNISH
				printerr("   Virheellinen lopetin!\n");
#else
				printerr("   Incorrect terminator!\n");
#endif
				}
			else
				outptr-=t;

			crc=updatecrc(crc, startptr, outptr-startptr);
			}

		if(makecrc)
			{
			if(crc!=(gethexval(p[0])<<12|gethexval(p[1])<<8|
				gethexval(p[2])<<4|gethexval(p[3])))
				{
#ifdef FINNISH
				printerr("   CRC virhe!\n");
#else
				printerr("   CRC mismatch!\n");
#endif
				ok=0;
				}
			}
		}

	if(illegal)
		{
#ifdef FINNISH
		printerr("   Koodi sis" a_ "lsi ");
		if(illegal==1)
			printerr("laittoman merkin!\n");
		else
			{
			printerr(ltoa(illegal));
			printerr(" laitonta merkki" a_ "!\n");
			}
#else
		printerr("   Code contained ");
		if(illegal==1)
			printerr("illegal character!\n");
		else
			{
			printerr(ltoa(illegal));
			printerr(" illegal characters!\n");
			}
#endif
		}

	endoutput(flags);

	return ok;
	}

static void resetuudecodetab(void)
	{
	int f;

	for(f=0; f<256; f++)
		uudecodetab[f]=f-' '&0x3f;
	}

static int readuudecodetab(unsigned char *linebuf)
	{
	int f, c;

	*linebuf=0;

	for(f=0; f<64; f++)
		{
		do c=get1(); while(c>=0 && vws(c));

		uudecodetab[c]=f;
		}

	if((c=get1())<0 || !vws(c))
		{
#ifdef FINNISH
		printerr("Virheellinen uuencodetaulukko!\n");
#else
		printerr("Incorrect uuencode table!\n");
#endif
		resetuudecodetab();
		return 0;
		}

	return 1;
	}

static int decodeuu(unsigned char *linebuf)
	{
	int ercount=0, sum, n, cont=1;
	unsigned long size=0;
	long mode=0;
	unsigned char c, *p, flagbuf[6];

 	*linebuf=0;

	for(p=linebuf+6; hws(*p); p++);

	while(*p>='0' && *p<='7') mode=(mode<<3)|*p++-'0';

	while(hws(*p)) p++;

#ifdef FINNISH
	startoutput(p, " (uuenkoodattu)\n");
#else
	startoutput(p, " (uuencoded)\n");
#endif

	while(cont)
		{
		do {
			if(inptr>=inbufend && !fillbuffer(1))
				{
#ifdef FINNISH
				printerr("   Tiedosto loppui kesken!\n");
#else
				printerr("   Premature EOF!\n");
#endif
				endoutput(0);
				return 1;
				}

			c=*inptr++;
			}
		while(vws(c));

		sum=0;

		size+=n=uudecodetab[c];

		p=get((n+2)/3*4+1);
		if(outptr>outbufend-63) flushoutput();

		if(!n) cont=0;

		while(n>0)
			{
			unsigned char a=uudecodetab[*p++], b;

			sum+=*outptr++=a<<2|(b=uudecodetab[*p++])>>4;
			sum+=*outptr++=b<<4|(a=uudecodetab[*p++])>>2;
			sum+=*outptr++=a<<6|   uudecodetab[*p++];
			n-=3;
			}

		outptr+=n;

		if(!quick && !(vws(*p)) && (sum&0x3f)!=uudecodetab[*p]) ercount++;
		}

	flagbuf[2]=2;
	flagbuf[4]=(mode>>8)&0x01;
	flagbuf[5]=mode;

	endoutput(flagbuf);

	if(ercount)
		{
		printerr("  ");
#ifdef FINNISH
		if(ercount==1)
			printerr("tarkistussummavirhe!\n");
		else
			{
			printerr(ltoa(ercount));
			printerr(" tarkistussummavirhett" a_ "!\n");
			}
#else
		if(ercount==1)
			printerr("checksum error!\n");
		else
			{
			printerr(ltoa(ercount));
			printerr(" checksum errors!\n");
			}
#endif
		}

	getline(linebuf, 512);

	if(strcmp(linebuf, "end"))
		{
#ifdef FINNISH
		printerr("   `end'-rivi puuttuu!\n");
#else
		printerr("   No `end'-line!\n");
#endif
		ercount++;
		}
	else
		getline(linebuf, 512);

	if(!strncmp(linebuf, "size ", 5))
		{
		if(atol(linebuf+5)!=size)
			{
#ifdef FINNISH
			printerr("   Kokovirhe!\n");
#else
			printerr("   Size mismatch!\n");
#endif
			ercount++;
			}

		*linebuf=0;
		}

	if(resetuu) resetuudecodetab();

	return ercount==0;
	}

static int decodefec(unsigned char *linebuf)
	{
	int start=1, f;
	unsigned char outblock[7], *p;

	*linebuf=0;

#ifdef FINNISH
	startoutput(linebuf+3, " (fipenkoodattu)\n");
#else
	startoutput(linebuf+3, " (fipencoded)\n");
#endif

	for(;;)
		{
		if((f=fecdecoder())<0) break;

		if(start)
			start=0;
		else
			put(outblock, 7);

		p=outblock;

		*p++=(f<<1)|(f=fecdecoder())>>6;
		*p++=(f<<2)|(f=fecdecoder())>>5;
		*p++=(f<<3)|(f=fecdecoder())>>4;
		*p++=(f<<4)|(f=fecdecoder())>>3;
		*p++=(f<<5)|(f=fecdecoder())>>2;
		*p++=(f<<6)|(f=fecdecoder())>>1;
		*p  =(f<<7)|fecdecoder();
		}
				
	put(outblock, -f);

	endoutput(0);

	return 1;
	}

static int decodebtoa(unsigned char *linebuf)
	{
	unsigned char outblock[2][4];
	int start=1, dataout=0, i=0, datasize, ok=1;

	*linebuf=0;

#ifdef FINNISH
	startoutput("noname", " (btoa:ttu)\n");
#else
	startoutput("noname", " (btoa'd)\n");
#endif

	while(btoadecoder(outblock[i]))
		{
		i=!i;

		if(start)
			start=0;
		else
			{
			put(outblock[i], 4);
			dataout+=4;
			}
		}

	getline(linebuf, 512);

	if(strncmp(linebuf, "btoa End N ", 11))
		{
#ifdef FINNISH
		printerr("   `btoa End'-rivi puuttuu!\n");
#else
		printerr("   No `btoa End'-line!\n");
#endif
		}
	else
		{
		*linebuf=0;

		if((datasize=atol(linebuf+11))<dataout || datasize>dataout+4)
			{
#ifdef FINNISH
			printerr("   Kokovirhe!\n");
#else
			printerr("   Size mismatch!\n");
#endif
			ok=0;
			}
		else
			put(outblock[!i], datasize-dataout);
		}

	endoutput(0);

	return ok;
	}

static int fillbufferandprint(int yes)
	{
	int rc;

	if(lines && yes) writefile(stdoutfile, start, inptr-start);
	rc=fillbuffer(1);
	start=inptr;

	return rc;
	}

int vdc(void)
	{
	int ok=1, lineswait=0;
	unsigned char c, linebuf[512], *end;

	resetuudecodetab();

	start=inptr;

	for(;;)
		{
		/* check the first character of the line */

		do	{
			if(inptr>=inbufend && !fillbufferandprint(!lineswait)) return ok;
			c=*inptr++;
			}
		while(vws(c));

		if(lineswait)
			{
			lineswait=0;
			start=inptr-1;
			}

		if(c!='F' && c!='b' && c!='y' && c!='x' && c!='t')
			{
			/* find vertical white space character */

			nextline:

			do	{
				if(inptr>=inbufend && !fillbufferandprint(1)) return ok;
				c=*inptr++;
				}
			while(!vws(c));

			continue;
			}

		inptr--;

		/* ensure 512 characters or rest of file in the buffer */

		if(inptr>inbuf+bufsize-512)
			{
			long s;

			if(lines) writefile(stdoutfile, start, inptr-start);

			memcpy(inbuf, inptr, s=inbufend-inptr);
			inbufend=inbuf+s;
			fillbuffer(0);
			inptr=start=inbuf;
			}

		end=inptr++;

		/* get the line */

			{
			char *p=linebuf;
			int maxl=inbufend-inptr;

			if(maxl>511) maxl=511;

			do *p++=c, c=*inptr++; while(--maxl && !vws(c));

			*p=0;
			}

		/* and test the magics */

		for(;;)
			{
			int (*func)(unsigned char *);

			if(!strncmp(linebuf, "yobufi", 6))
				func=decodevec;
			else if(!strncmp(linebuf, "begin ", 6))
				func=decodeuu;
			else if(!strncmp(linebuf, "table", 6))
				func=readuudecodetab;
			else if(!strncmp(linebuf, "FEC", 3))
				func=decodefec;
			else if(!strncmp(linebuf, "xbtoa Begin", 11))
				func=decodebtoa;
			else
				{
				if(end) break;

				if(!*linebuf)
					lineswait=1;
				else if(lines)
					{
					writefile(stdoutfile, linebuf, strlen(linebuf));
					writefile(stdoutfile, &linedelim, 1);
					}

				start=inptr;
				break;
				}

			if(lines && end>start)
				writefile(stdoutfile, start, end-start);

			end=0;

			ok&=func(linebuf);
			}
		}
	}
