/*
 *	dbase64enc - Base64 encode/decode tool
 *	Copyright (C) 2008 Naoyuki Sawa
 *
 *	* Mon Nov 17 13:25:55 JST 2008 Naoyuki Sawa
 *	- 1st リリース。
 *	* Tue Nov 18 02:02:06 JST 2008 Naoyuki Sawa
 *	- デコード時に、入力ストリーム中の改行や空白文字を読み飛ばすよう変更しました。
 *	  本来、Base64エンコードされたテキストの中に改行や空白文字が有ってはいけないのですが、
 *	  秀丸エディタで非常に長い行が扱えなかったり、読み易さのために、改行や空白文字を含められると便利です。
 *	  そこで、入力ストリーム中の改行や空白文字は、読み飛ばすことにしました。
 *	- 上記変更に併せて、エンコード時にも一行の桁数を指定して改行を出力するよう、"-c<cols>"オプションを追加しました。
 *	  "-c<cols>"オプションを指定すると、一行の桁数ごとと、最終行の末尾に改行を出力します。
 *	  "-c<cols>"オプションを指定しなければ、これまでどおり全てを一行で出力し、末尾にも改行を出力しません。
 *	- 「dbase64enc.exe」と「durlenc.exe」をパイプで連結して使う方法について、「durlenc」のコメントを参照してください。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#define DIE() abort()
#define __CLIP_H__
#include "../../clipcode.h"
#include "../../clipcode.c"

#define VERSION "20081118"

/////////////////////////////////////////////////////////////////////////////

void
usage()
{
	printf("dbase64enc - Base64 encode/decode tool (%s) Naoyuki Sawa\n", VERSION);
	printf("Usage: [-e] [-c<cols>] input-file-name(binary data) > output-text-stream(encoded text)\n");
	printf("        -d             output-file-name(binary data) <  input-text-stream(encoded text)\n");
	printf("Options:\n");
	printf("  -e       : encode input file (default)\n");
	printf("  -d       : decode input text\n");
	printf("Encode options:\n");
	printf("  -c<cols> : output text columns\n");
	exit(1);
}

int mode; /* 'd' or 'e' */
int cols;
char filename[_MAX_PATH];

#define BUFFER_SIZE	(16 * 1024 * 1024)	/* 調整可 */
int srclen;
unsigned char srcbuf[BUFFER_SIZE];
int dstlen;
unsigned char dstbuf[BUFFER_SIZE];

int
main(int argc, char* argv[])
{
	int c;
	int i;
	FILE* fp;

	/* コマンドライン解析。 */
	for(i = 1; i < argc; i++) {
		switch(argv[i][0]) {
		case '-':
		case '/':
			switch(tolower(argv[i][1])) {
			case 'd':
			case 'e':
				if(!mode) {
					mode = tolower(argv[i][1]);
					break;
				}
				usage();
				break;
			case 'c':
				if(!cols) {
					cols = atoi(&argv[i][2]);
					if(cols <= 0) {
						usage();
					}
					break;
				}
				usage();
				break;
			default:
				usage();
				break;
			}
			break;
		default:
			if(!filename[0]) {
				strcpy(filename, argv[i]);
				break;
			}
			usage();
			break;
		}
	}
	/* "-d"も"-e"も指定されていなければ、"-e"とします。 */
	if(!mode) {
		mode = 'e';
	}
	/* デコード時は、"-c<cols>"オプション指定不可です。 */
	if((mode == 'd') && cols) {
		usage();
	}
	/* ファイル名は必須です。 */
	if(!filename[0]) {
		usage();
	}

	switch(mode) {
	case 'e':
		/* 入力ファイル(バイナリ)を読み込みます。 */
		fp = fopen(filename, "rb");
		if(!fp) {
			fprintf(stderr, "Error: Failed to open file %s.\n", filename);
			exit(1);
		}
		srclen = fread(srcbuf, 1, BUFFER_SIZE, fp);
		fclose(fp);
		/* エンコードします。 */
		dstlen = base64_encode(dstbuf, BUFFER_SIZE, srcbuf, srclen);
		/* 出力ストリーム(テキスト)へ書き出します。 */
		for(i = 0; i < dstlen; i++) {
			c = dstbuf[i];
			fputc(c, stdout);
			if(cols) { /* "-c<cols>"オプションが指定されていたら、適時に改行を出力します */
				if(((i % cols) == (cols - 1)) || (i == (dstlen - 1))) {
					fputc('\n', stdout);
				}
			}
		}
		break;
	case 'd':
		/* 入力ストリーム(テキスト)から全部読み込みます。 */
		for(;;) {
			c = fgetc(stdin);
			if(c == EOF) {
				break;
			}
			if(isspace(c)) { /* 入力ストリーム中の、改行や空白文字は読み飛ばします */
				continue;
			}
			if(srclen >= (BUFFER_SIZE - 1/*nul終端の分*/)) { /* デコードの場合のnul終端を考えて、BUFFER_SIZEよりも1バイト少ないサイズまでとします */
				fprintf(stderr, "Error: Input text is too large.\n");
				exit(1);
			}
			srcbuf[srclen++] = c;
		}
		/* デコードします。 */
		dstlen = base64_decode(dstbuf, BUFFER_SIZE, srcbuf); /* srcbuf[]の初期値は0なので既にnul終端されています */
		/* 出力ファイル(バイナリ)へ書き出します。 */
		fp = fopen(filename, "wb");
		if(!fp) {
			fprintf(stderr, "Error: Failed to create file %s.\n", filename);
			exit(1);
		}
		fwrite(dstbuf, 1, dstlen, fp);
		fclose(fp);
		break;
	default:
		DIE();
	}

	return 0;
}
