/*
 *	app.c
 *
 *	dpstrcnv - 文字列変換ユーティリティー
 *	Copyright (C) 2013 Naoyuki Sawa
 *
 *	* Sat Sep 28 20:39:33 JST 2013 Naoyuki Sawa
 *	- 1st リリース。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Win32 */
#define STRICT
#include <windows.h>

/* [NOTE]
 * * コマンドライン引数のワイルドカードを展開するために、
 *   [プロジェクトの設定]->[リンク]->[オブジェクト/ライブラリモジュール]に
 *   「setargv.obj」を追加しました。
 */

#define VERSION "20130928"

/****************************************************************************
 *	
 ****************************************************************************/

__declspec(noreturn) void usage();
__declspec(noreturn) void die(const char* fmt, ...);
#define DIE() die(__FILE__, __LINE__)

/* getopt */
#define __CLIP_H__
#include "../../include/getopt.h"
#include "../../getopt.c"
#undef  __CLIP_H__

/****************************************************************************
 *	
 ****************************************************************************/

int main(int argc, char* argv[]) {
	int opt;
	int flag;	/* 変換フラグ */
	FILE* fp;	/* 入力ストリーム */
	char* srcBuf;	/* 入力バッファ */
	char* dstBuf;	/* 出力バッファ */
	int srcCap;	/* 入力バッファのキャパシティ */
	int dstCap;	/* 出力バッファのキャパシティ */
	int dstReq;	/* 出力バッファに必要なサイズ */

	/* コマンドラインを解析する。 */
	flag = 0;
	while((opt = getopt(argc, argv, "ulwnkh")) != -1) {
		switch(opt) {
		case 'u': flag |= LCMAP_UPPERCASE; break; /* 大文字 */
		case 'l': flag |= LCMAP_LOWERCASE; break; /* 小文字 */
		case 'w': flag |= LCMAP_FULLWIDTH; break; /* 全角 */
		case 'n': flag |= LCMAP_HALFWIDTH; break; /* 半角 */
		case 'k': flag |= LCMAP_KATAKANA;  break; /* カタカナ */
		case 'h': flag |= LCMAP_HIRAGANA;  break; /* ひらがな */
		default:  die("%c: オプション文字の使い方が不正です。", optopt);
		}
	}
	/* 変換フラグが一つも指定されていなければ、使い方を表示して終了する。
	 * もしこのまま強行しても、LCMapStringが失敗するので、無駄である。
	 */
	if(!flag) { usage(); }
	/* 一行を読み込むのに充分な大きさの、入力バッファを確保する。
	 * 入力行の内容を判断して、入力バッファをリサイズすることはできないので、予め充分な大きさの入力バッファを確保しておく必要がある。
	 * もし、入力バッファの大きさが不足で、fgets()が行を分割してしまうと、LCMapString APIが正しい変換を行えないおそれがある。
	 * たとえば、半角カタカナの濁点を分割してしまった場合など。("ｶﾞｷﾞｸﾞｹﾞｺﾞ\n"⇒"ｶﾞｷﾞｸﾞｹ","ﾞｺﾞ\n"⇒"ガギグケ","゛ゴ")
	 */
	srcCap = 1<<20;	/* 1MB */	//調整可
	srcBuf = malloc(srcCap);
	if(!srcBuf) { die("mallocが失敗しました。\n"); }
	/* 出力バッファを未割り当てにしておく。
	 * 各行の変換に必要なサイズを都度取得し、サイズが超過する度に拡張して行く。
	 */
	dstBuf = NULL;
	dstCap = 0;
	/* 入力ファイルが指定されていなければ、標準入力を一回処理する。 */
	fp = stdin;
	do {
		/* 入力ファイルが指定されていれば、入力ファイルを開く。 */
		if(optind < argc) {
			fp = fopen(argv[optind], "r");
			if(!fp) { die("%sが開けません。\n", argv[optind]); }
		}
		/* 一行読み込む。ファイル終端でなければ… */
		while(fgets(srcBuf, srcCap, fp)) {
			/* この行の変換に必要なサイズを取得する。
			 * - 変換に必要なサイズを取得する場合は、最後の引数に0を指定する必要がある。
			 *   現在の出力バッファサイズを指定して、足りるならば一度で済ませようとすると、エラー(0)が返され、必要なサイズを取得できない。
			 */
			dstReq = LCMapString(GetSystemDefaultLCID(), flag, srcBuf, -1, NULL, 0);
			if(!dstReq) { die("LCMapStringが失敗しました。\n"); }
			/* この行の変換に必要なサイズが、現在の出力バッファサイズを超過したら、出力バッファサイズを拡張する。
			 * - LCMapString APIが返す、変換に必要なサイズは、終端のnulを含んでいる。
			 *   アプリケーションプログラム側で、割り当てサイズを(+1)する必要は無い。
			 */
			if(dstReq > dstCap) {
				dstCap = dstReq;
				dstBuf = realloc(dstBuf, dstCap);
				if(!dstBuf) { die("reallocが失敗しました。\n"); }
			}
			/* この行を変換する。 */
			dstReq = LCMapString(GetSystemDefaultLCID(), flag, srcBuf, -1, dstBuf, dstCap);
			if(!dstReq) { die("LCMapStringが失敗しました。\n"); }
			/* 標準出力に出力する。 */
			if(fputs(dstBuf, stdout) < 0) { die("fputsが失敗しました。\n"); }
		}
		/* 入力ファイルが指定されていれば、入力ファイルを閉じる。 */
		if(optind < argc) {
			if(fclose(fp)) { die("fcloseが失敗しました。\n"); }
			optind++; /* 次の入力ファイルへ進める */
		}
	} while(optind < argc);
	return EXIT_SUCCESS;
}

/****************************************************************************
 *	
 ****************************************************************************/

/* 使い方を表示し、エラー終了する。 */
void usage() {
	fprintf(stderr, "dpstrcnv - 文字列変換ユーティリティー (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2013 Naoyuki Sawa All Rights Reserved.\n");
	fprintf(stderr, "Usage:\n");
	fprintf(stderr, "    dpstrcnv [options] [filename ...]\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "    -u StrConv関数のvbUpperCase,又は,LCMapString APIのLCMAP_UPPERCASEに相当します。\n");
	fprintf(stderr, "    -l StrConv関数のvbLowerCase,又は,LCMapString APIのLCMAP_LOWERCASEに相当します。\n");
	fprintf(stderr, "    -w StrConv関数のvbWide     ,又は,LCMapString APIのLCMAP_FULLWIDTHに相当します。\n");
	fprintf(stderr, "    -n StrConv関数のvbNarrow   ,又は,LCMapString APIのLCMAP_HALFWIDTHに相当します。\n");
	fprintf(stderr, "    -k StrConv関数vbKatakana   ,又は,LCMapString APIのLCMAP_KATAKANA に相当します。\n");
	fprintf(stderr, "    -h StrConv関数vbHiragana   ,又は,LCMapString APIのLCMAP_HIRAGANA に相当します。\n");
	fprintf(stderr, "Summary:\n");
	fprintf(stderr, "  - 大文字/小文字,全角/半角,カタカナ/ひらがな,及び,これらを複合した変換を行います。\n");
	fprintf(stderr, "  - LCMapString APIの特性により、以下の制約があります:\n");
	fprintf(stderr, "    ・少なくとも一個以上の変換フラグを指定してください。\n");
	fprintf(stderr, "    ・-uと-lを同時に指定してはいけません。\n");
	fprintf(stderr, "    ・-wと-nを同時に指定してはいけません。\n");
	fprintf(stderr, "    ・-kと-hを同時に指定してはいけません。\n");
	fprintf(stderr, "  - 半角カタカナを全角ひらがなに変換する場合の注意点:\n");
	fprintf(stderr, "    ・半角カタカナを全角ひらがなに変換する場合は、-whを指定してください。\n");
	fprintf(stderr, "    　-hだけの指定では、半角カタカナが変換対象になりません。(LCMapString APIの特性)\n");
	fprintf(stderr, "    ・ただし-wの効果のため、半角カタカナ以外の半角文字も全角化します。\n");
	fprintf(stderr, "    　半角カタカナのみを全角ひらがなに変換する方法は有りません。\n");
	fprintf(stderr, "Example:\n");
	fprintf(stderr, "  - dpstrcnv.exe -u sample1.txt sample2.txt sample3.txt\n");
	fprintf(stderr, "    sample1〜3.txtから読み取ったテキストを大文字に変換して、標準出力へ出力します。\n");
	fprintf(stderr, "    複数のファイルを指定する方法は、ワイルドカードによる指定も可能です。(例:*.txt)\n");
	fprintf(stderr, "  - dpstrcnv.exe -nk\n");
	fprintf(stderr, "    標準入力から読み取ったテキストを半角カタカナに変換して、標準出力へ出力します。\n");
	exit(EXIT_FAILURE);
}

/****************************************************************************
 *	
 ****************************************************************************/

/* メッセージを表示し、エラー終了する。 */
void die(const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	 fprintf(stderr, "\n### ");
	vfprintf(stderr, fmt, ap);
	 fprintf(stderr, "\n");
	va_end(ap);
#ifdef  _DEBUG
	__asm int 3; /* デバッグし易いように、デバッグ時はデバッガで止める */
#endif/*_DEBUG*/
	exit(EXIT_FAILURE);
}

