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

static char *radixN =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static char pad = '=';

radix_encode(inbuf, outbuf, len, decode)
unsigned char inbuf[], outbuf[];
int *len, decode;
{
	int i,j,D;
	char *p;
	unsigned char c;

	if (decode) {
		for (i=0,j=0; inbuf[i] && inbuf[i] != pad; i++) {
		    if ((p = strchr(radixN, inbuf[i])) == NULL) return(1);
		    D = p - radixN;
		    switch (i&3) {
			case 0:
			    outbuf[j] = D<<2;
			    break;
			case 1:
			    outbuf[j++] |= D>>4;
			    outbuf[j] = (D&15)<<4;
			    break;
			case 2:
			    outbuf[j++] |= D>>2;
			    outbuf[j] = (D&3)<<6;
			    break;
			case 3:
			    outbuf[j++] |= D;
		    }
		}
		switch (i&3) {
			case 1: return(3);
			case 2: if (D&15) return(3);
				if (strcmp((char *)&inbuf[i], "==")) return(2);
				break;
			case 3: if (D&3) return(3);
				if (strcmp((char *)&inbuf[i], "="))  return(2);
		}
		*len = j;
	} else {
		for (i=0,j=0; i < *len; i++)
		    switch (i%3) {
			case 0:
			    outbuf[j++] = radixN[inbuf[i]>>2];
			    c = (inbuf[i]&3)<<4;
			    break;
			case 1:
			    outbuf[j++] = radixN[c|inbuf[i]>>4];
			    c = (inbuf[i]&15)<<2;
			    break;
			case 2:
			    outbuf[j++] = radixN[c|inbuf[i]>>6];
			    outbuf[j++] = radixN[inbuf[i]&63];
			    c = 0;
		    }
		if (i%3) outbuf[j++] = radixN[c];
		switch (i%3) {
			case 1: outbuf[j++] = pad;
			case 2: outbuf[j++] = pad;
		}
		outbuf[*len = j] = '\0';
	}
	return(0);
}

char *
radix_error(e)
{
	switch (e) {
	    case 0:  return("Success");
	    case 1:  return("Bad character in encoding");
	    case 2:  return("Encoding not properly padded");
	    case 3:  return("Decoded # of bits not a multiple of 8");
	    default: return("Unknown error");
	}
}

#ifdef STANDALONE
usage(s)
char *s;
{
	fprintf(stderr, "Usage: %s [ -d ] [ string ]\n", s);
	exit(2);
}

static int n;

putbuf(inbuf, outbuf, len, decode)
unsigned char inbuf[], outbuf[];
int len, decode;
{
	int c;

	if (c = radix_encode(inbuf, outbuf, &len, decode)) {
		fprintf(stderr, "Couldn't %scode input: %s\n",
				decode ? "de" : "en", radix_error(c));
		exit(1);
	}
	if (decode)
		write(1, outbuf, len);
	else
		for (c = 0; c < len;) {
			putchar(outbuf[c++]);
			if (++n%76 == 0) putchar('\n');
		}
}

main(argc,argv)
int argc;
char *argv[];
{
	unsigned char *inbuf, *outbuf;
	int c, len = 0, decode = 0;
	extern int optind;

	while ((c = getopt(argc, argv, "d")) != -1)
		switch(c) {
			default:
				usage(argv[0]);
			case 'd':
				decode++;
		}

	switch (argc - optind) {
		case 0:
			inbuf  = (unsigned char *) malloc(5);
			outbuf = (unsigned char *) malloc(5);
			while ((c = getchar()) != EOF)
			    if (c != '\n') {
				inbuf[len++] = c;
				if (len == (decode ? 4 : 3)) {
					inbuf[len] = '\0';
					putbuf(inbuf, outbuf, len, decode);
					len=0;
				}
			    }
			if (len) {
				inbuf[len] = '\0';
				putbuf(inbuf, outbuf, len, decode);
			}
			break;
		case 1:
			inbuf = (unsigned char *)argv[optind];
			len = strlen(inbuf);
			outbuf = (unsigned char *)
				malloc((len * (decode?3:4)) / (decode?4:3) + 1);
			putbuf(inbuf, outbuf, len, decode);
			break;
		default:
			fprintf(stderr, "Only one argument allowed\n");
			usage(argv[0]);
	}
	if (n%76) putchar('\n');
	exit(0);
}
#endif /* STANDALONE */
