#include <stdio.h>
#include <string.h>
#include <ctype.h>

/*
 * Decrypt an adobe PFB or PFA file, into a printer
 * downloadable format.
 *
 * Author:
 *	Per Lindqvist, pgd@compuram.bbt.se
 */

int binary = 0;
int decrypt = 1;		/* Decrypt eexec */
int rdhex = 1;			/* convert RD strings to hex */

char keyword[] = "\ncurrentfile eexec\n";
char buffer[40];
char *bufpt = buffer;

FILE *ifp, *ofp;

main(argc, argv)
	int argc;
	char **argv;
{
	char *cp;
	int len, ft;
	int c, i, cc;
	int j, k;
	int match;

	argc--; argv++;		/* Skip program name */
	while (argc >= 0 && **argv == '-') {
		switch ((*argv)[1]) {
		default:
			fprintf(stderr, "Illegal switch %s\n", *argv);
			exit(1);
		}
		argc--;
		argv++;
	}
	if (argc < 1) {
		fprintf(stderr, "Usage: decrypt infile {outfile}\n");
		exit(1);
	}
	ifp = fopen(argv[0], "r");
	if (ifp == NULL) {
		perror(argv[0]);
		exit(1);
	}
	if (argc > 1) {
		if ((ofp = fopen(argv[1], "w")) == NULL) {
			perror(argv[1]);
			exit(1);
		}
	} else
		ofp = stdout;
	/*
	 * Check if binary or ascii format
	 */
	c = getc(ifp);
	binary = (c == 128);
	ungetc(c, ifp);

	cc = 0;
	match = 0;
	while ((c = decrypt ? egetch() : getch()) != -1) {
redo:
		if (!rdhex)
			putc(c, ofp);
		else if (cc) {
			fprintf(ofp, "%02X", c);
			cc--;
			if (cc == 0) {
				fprintf(ofp, ">");
				bufpt = buffer;
			}
		} else if (bufpt == buffer) {
			if (isdigit(c)) {
				*bufpt++ = c;
				match = 0;
			} else
				putc(c, ofp);
		} else if (bufpt == buffer+sizeof(buffer)) {
			for (cp = buffer; cp != bufpt; )
				putc(*cp++, ofp);
			bufpt = buffer;
			putc(c, ofp);
		} else if (!match && (isdigit(c) || c == ' ')) {
			*bufpt++ = c;
			if (c == ' ')
				match = 'R';
		} else if (match && c == match) {
			*bufpt++ = c;
			if (c == 'R') match = 'D';
			else if (c == 'D') match = ' ';
			else {
				/* c = ' ' */
				*bufpt = '\0';
				cc = atoi(buffer);
				match = 0;
				putc(' ', ofp); putc('<', ofp);
			}
		} else {
			for (cp = buffer; cp < bufpt; )
				putc(*cp++, ofp);
			bufpt = buffer;
			goto redo;
		}
	}
	exit(0);
}


/*
 * Get next decrypted char from input file
 */
egetch()
{
	static int ineexec = 0;
	static int eecc, zcc, zc, zpb;
	static char *keypt = keyword;
	static int undokc = 0;
	register int c;
	int c2;
	static int peekc = 0;

 redo:
	if (undokc) {
		if (undokc == 1) {
			c = peekc;
			keypt = keyword;
			peekc = 0;
		} else
			c = *keypt++;
		undokc--;
	} else if (!ineexec) {
		c = getch();
		if (c == -1)
			return -1;
		if (c == *keypt) {
			keypt++;
			if (!*keypt) {
				ineexec = 1;
				eecc = zcc = zpb = 0;
				keypt = keyword;
				c = '\n';
			} else
				goto redo;
		} else if (keypt != keyword) {
			peekc = c;
			undokc = keypt - keyword + 1;
			keypt = keyword;
			goto redo;
		}
	} else {	/* eexec encrypted */
		if (zc) {
			if (zc == 1) {
				c = zpb; zpb = zc = 0;
			} else {
				zc--; c = 0;
			}
		} else {
			do c = getch(); while isspace(c);
			do c2 = getch(); while (isspace(c2));
			if (c >= '0' && c <= '9') c -= '0';
			else c = toupper(c) - 'A' + 10;
			if (c2 >= '0' && c2 <= '9') c2 -= '0';
			else c2 = toupper(c2) - 'A' + 10;
			c = (c<<4) | c2;
			if (c == 0) {
				zcc++;
				if (zcc >= 256) {
					ineexec = 0;
					return '\n';
				} else
					goto redo;
			} else if (zcc > 0) {
				zpb = c;
				zc = zcc;
				zcc = c = 0;
			}
		}
		c = xdecrypt(c);
		eecc++;
		if (eecc <= 4)
			goto redo;
	}
	return c;
}

/*
 * Get next character from input file
 */
getch()
{
	static int ft, peekc, cc;
	int c, i;

	if (peekc) {
		c = peekc;
		peekc = 0;
		return c;
	}
	if (!binary) 
		c = getc(ifp);
	else {
		if (ft == 3)
			return -1;
		if (cc == 0) {
			c = getc(ifp);
			if (c == -1) goto eof;
			if (c != 128) {
				fprintf(stderr, "Illegal format on input file\n");
				exit(1);
			}
			c = getc(ifp);
			if (c == -1) goto eof;
			if (c < 1 || c > 3) {
				fprintf(stderr, "Illegal font format on input file\n");
				exit(1);
			}
			ft = c;
			if (ft == 3)
				return '\n';
			cc = 0;
			for (i = 0; i < 4; i++) {
				c = getc(ifp);
				if (c == -1) goto eof;
				cc |= (c << (i*8));
			}
		}
		if (cc <= 0) {
			fprintf(stderr, "Illegal format on input file\n");
			exit(1);
		}
		c = getc(ifp); cc--;
		if (ft == 2) {
			peekc = c & 0xf;
			if (peekc <= 9)
				peekc += '0';
			else
				peekc += 'A'-10;
			c >>= 4;
			if (c <= 9)
				c += '0';
			else
				c += 'A'-10;
		} else if (c == '\r')
			c = '\n';
	}
	return c;

 eof:
	fprintf(stderr, "Premature end-of-file on input file\n");
	exit(1);
}


int
xdecrypt(int cipher)
{
	static unsigned short int r =  55665;
	static unsigned short int c1 = 52845;
	static unsigned short int c2 = 22719;
	int plain;

	plain = (cipher ^ (r>>8));
	r = (cipher + r) * c1 + c2;
	return plain;
}

