/*	
 *	dstderr.c
 *
 *	dstderr - 標準エラー出力モニタ
 *	Copyright (C) 2002 Naoyuki Sawa
 *
 *	* Sat Nov 30 14:06:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Sat Nov 30 17:27:00 JST 2002 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sun Dec 22 22:32:00 JST 2002 Naoyuki Sawa
 *	- 転送サイズ不正エラー時に、サイズも表示するよう変更。
 *	- ucs.txstatの検査時に、USBCOM_STAT_TXWAITのマスクをとるよう修正。
 *	  これをやらないと、間違ってTXING等のフラグも転送要求と見なしてしまい、転送フェーズがずれる。
 */
#define STRICT
#include <windows.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <pieceif.h>
#pragma comment(lib,"pieceif.lib")

#define VERSION		"20021222"

/****************************************************************************
 *	変数
 ****************************************************************************/

char* log_fname = NULL;	/* ログファイル名(NULL:なし) */
FILE* log_fp;		/* ログファイル(NULL:なし) */

char devno = 0;		/* P/ECEデバイス番号 */
int retry_interval = 0;	/* 再接続インターバル */
int terminate = 0;	/* Ctrl+Cによる終了要求 */

USBCOMS ucs;		/* USBCOM状態取得用 */
char txbuf[4096];	/* 標準エラー出力からの受信バッファ */
			/* Windows用P/ECEドライバのバルクPIPEバッファサイズが4KBなので、 */
			/* 4KB以上の送受信は正しく動作しないようです。 */

/****************************************************************************
 *	usage()
 ****************************************************************************/

void
usage()
{
	fprintf(stderr, "dstderr - 標準エラー出力モニタ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2002 Naoyuki Sawa <nsawa@north.hokkai.net>\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE: dstderr [options] [devno]\n");
	fprintf(stderr, "    devno     P/ECEデバイス番号\n");
	fprintf(stderr, "              省略時は、#0(一台目のP/ECE)に接続します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "    -l<fname> ログファイルを作成します。\n");
	fprintf(stderr, "    -w<sec>   再接続インターバル[秒]\n");
	fprintf(stderr, "              省略するか0を指定すると、再接続しません。\n");
	exit(1);
}

/****************************************************************************
 *	disconnect
 ****************************************************************************/

void
disconnect()
{
	ismUCClose();	/* USBCOM切断(P/ECE独自のプロトコルレイヤでの切断) */
	ismExit();	/* バルクPIPE切断(OSレベルのUSB通信レイヤでの切断) */
}

/****************************************************************************
 *	sigint
 ****************************************************************************/

void
sigint(int sig)
{
	/* * ここではCtrl+Cが押されてことをマークしておき、メインループで判断します。
	 * * シグナルハンドラは非同期に呼ばれるので、この中で切断してはいけません。
	 *   P/ECEドライバがハングアップし、P/ECEをリセットするまで治らなくなります。
	 */
	retry_interval = 0;	/* もう再接続しない←※これ忘れると終わらないよ！*/
	terminate = 1;		/* 終了要求をマーク */
}

/****************************************************************************
 *	main
 ****************************************************************************/

int
main(int argc, char* argv[])
{
	int retval, i;
	char* p;
	char path[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];

	/*-------------------- 初期化 --------------------*/

	/* Ctrl+Cをフックします。 */
	signal(SIGINT, sigint);

	/* コマンドライン解析。 */
	for(i = 1; i < argc; i++) {
		p = argv[i];
		if(p[0] == '-') {
			/* オプション指定 */
			switch(p[1]) {
			case 'l':
				if(log_fname != NULL) usage(); /* 二重指定 */
				log_fname = &p[2];
				if(strlen(log_fname) < 1) usage(); /* ファイル名指定なし */
				break;
			case 'w':
				retry_interval = (char)strtol(&p[2], &p, 10);
				if(*p != '\0') usage(); /* 数字以外の文字で終端した */
				break;
			default:
				usage();
			}
		} else {
			/* デバイス番号指定 */
			devno = (char)strtol(p, &p, 10);
			if(*p != '\0') usage(); /* 数字以外の文字で終端した */
		}
	}

	/* ログファイル名が指定されていたら、ログファイルを作成します。 */
	if(log_fname != NULL) {
		/* 拡張子が省略されていたら「.log」を付加。 */
		_splitpath(log_fname, drive, dir, fname, ext);
		if(strlen(ext) == 0) strcpy(ext, ".log");
		_makepath(path, drive, dir, fname, ext);

		/* ログファイルを作成します。 */
		log_fp = fopen(path, "wt");
		if(log_fp == NULL) {
			fprintf(stderr, "### ログファイル %s が作成できません。\n", path);
			exit(1);
		}
	}

	/*-------------------- メインループ --------------------*/
REDO:
	/* P/ECEに接続します。 */
	retval = ismInitEx(devno, PIECE_DEF_WAITN);
	if(retval != 0) {
		fprintf(stderr, "### P/ECEに接続できません。\n");
		goto NEXT;
	}

	/* USBCOMに接続します。 */
	retval = ismUCOpen(&ucs);
	if(retval != 0) {
		fprintf(stderr, "### USBCOMに接続できません。\n");
		goto NEXT;
	}
	if(!ucs.mystat || strcmp(ucs.signature, "stderr") != 0) {
		fprintf(stderr, "### 標準エラー出力に接続できません。\n");
		goto NEXT;
	}

	/* Ctrl+Cが押されるまで... */
	while(!terminate) {
		/* USBCOMの状態を取得します。 */
		retval = ismUCGetStat(&ucs); /* ismUCGetStat()はシグネチャ部分は読み込みません!!要注意 */
		if(retval != 0) {
			fprintf(stderr, "### USBCOMの状態取得に失敗しました。\n");
			goto NEXT;
		}

		/* P/ECE側は、単にpceUSBCOMStop()を呼び出しで切断する*のではなく*、
		 * 空文字シグネチャでpceUSBCOMSetup()を行い、すぐにpceUSBCOMStop()を呼んで切断してください。
		 * なぜかというと、PCとの接続が確立していると、pceUSBCOMStop()は切断処理を行わないからです。
		 * そのため、pceUSBCOMSetup()を使ってPCとの接続フラグおよびシグネチャをクリアしておき、
		 * あらためてpceUSBCOMStop()を呼び出して切断を行うのです。(BIOS 1.18のふるまいに依存)
		 */
		//if(!ucs.mystat || strcmp(ucs.signature, "stderr") != 0) {
		if(!ucs.mystat/* || strcmp(ucs.signature, "stderr") != 0*/) { /* ismUCGetStat()はシグネチャ部分を読まないので、検査しても無駄でした */
			fprintf(stderr, "### 標準エラー出力が切断されました。\n");
			goto NEXT;
		}

		/* P/ECEからの送信要求があれば... */
		if(ucs.txstat & USBCOM_STAT_TXWAIT/*TXING等のビットを無視するために、このマスク必須！*/) {
			if(ucs.txlen < 1 || sizeof txbuf < ucs.txlen) {
				fprintf(stderr, "### 送信要求サイズが不正です。(%d)\n", ucs.txlen);
				goto NEXT;
			}

			/* P/ECEからの送信データを受信します。 */
			retval = ismUCRead(txbuf, ucs.txlen);
			if(retval != 0) {
				fprintf(stderr, "### データ受信エラーです。\n");
				goto NEXT;
			}

			/* PC側の標準出力に出力します。 */
			fwrite(txbuf, 1, ucs.txlen, stdout);

			/* ログファイルを開いていたら、ログファイルにも出力します。 */
			if(log_fp != NULL) fwrite(txbuf, 1, ucs.txlen, log_fp);
		}
	}
NEXT:
	/* 確実に切断します。 */
	disconnect();

	/* 再接続インターバルが設定されていたら、少し待ってリトライします。 */
	if(retry_interval > 0) {
		fprintf(stderr, "### %d秒後に再接続します。\n", retry_interval);
		/* Sleep(retry_interval * 1000)で待つと、Ctrl+Cが割り込めません。 */
		i = GetTickCount();
		while((int)(GetTickCount() - i) < retry_interval * 1000) {
			Sleep(100); /* 100ミリ秒毎にCtrl+Cの割り込み機会を与えます */
			if(terminate) goto EXIT; /* 既に切断されているのでそのまま終了 */
		}
		goto REDO;
	}
EXIT:
	/* ログファイルを開いていたら、閉じます。 */
	if(log_fp != NULL) fclose(log_fp);

	return 0;
}

