/* decode85 -- convert from ascii85 format */

#include <stdio.h>

static unsigned long pow85[] = {
	85*85*85*85, 85*85*85, 85*85, 85, 1
};

void wput(unsigned long tuple, int bytes) {
	switch (bytes) {
	case 4:
		putchar(tuple >> 24);
		putchar(tuple >> 16);
		putchar(tuple >>  8);
		putchar(tuple);
		break;
	case 3:
		putchar(tuple >> 24);
		putchar(tuple >> 16);
		putchar(tuple >>  8);
		break;
	case 2:
		putchar(tuple >> 24);
		putchar(tuple >> 16);
		break;
	case 1:
		putchar(tuple >> 24);
		break;
	}
}

void decode85(FILE *fp, const char *file) {
	unsigned long tuple = 0;
	int c, count = 0;
	for (;;)
		switch (c = getc(fp)) {
		default:
			if (c < '!' || c > 'u') {
				fprintf(stderr, "%s: bad character in ascii85 region: %#o\n", file, c);
				exit(1);
			}
			tuple += (c - '!') * pow85[count++];
			if (count == 5) {
				wput(tuple, 4);
				count = 0;
				tuple = 0;
			}
			break;
		case 'z':
			if (count != 0) {
				fprintf(stderr, "%s: z inside ascii85 5-tuple\n", file);
				exit(1);
			}
			putchar(0);
			putchar(0);
			putchar(0);
			putchar(0);
			break;
		case '~':
			if (getc(fp) == '>') {
				if (count > 0) {
					count--;
					tuple += pow85[count];
					wput(tuple, count);
				}
				c = getc(fp);
				return;
			}
			fprintf(stderr, "%s: ~ without > in ascii85 section\n", file);
			exit(1);
		case '\n': case '\r': case '\t': case ' ':
		case '\0': case '\f': case '\b': case 0177:
			break;
		case EOF:
			fprintf(stderr, "%s: EOF inside ascii85 section\n", file);
			exit(1);
		}
}

void decode(FILE *fp, const char *file, int preserve) {
	int c;
	while ((c = getc(fp)) != EOF)
		if (c == '<')
			if ((c = getc(fp)) == '~')
				decode85(fp, file);
			else {
				if (preserve)
					putchar('<');
				if (c == EOF)
					break;
				if (preserve)
					putchar(c);
			}
		else
			if (preserve)
				putchar(c);
}

void usage(void) {
	fprintf(stderr, "usage: decode85 [-p] file ...\n");
	exit(1);
}

extern int getopt(int, char *[], const char *);
extern int optind;
extern char *optarg;

int main(int argc, char *argv[]) {
	int i, preserve;
	preserve = 0;
	while ((i = getopt(argc, argv, "p?")) != EOF)
		switch (i) {
		case 'p': preserve = 1; break;
		case '?': usage();
		}
	

	if (optind == argc)
		decode(stdin, "decode85", preserve);
	else
		for (i = optind; i < argc; i++) {
			FILE *fp = fopen(argv[i], "r");
			if (fp == NULL) {
				perror(argv[i]);
				return 1;
			}
			decode(fp, argv[i], preserve);
			fclose(fp);
		}
	return 0;
}
