/*
 *	app.c
 *
 *	dpserial - 簡易シリアル出力ツール
 *	Copyright (C) 2014 Naoyuki Sawa
 *
 *	* Thu Oct 10 16:22:59 JST 2013 Naoyuki Sawa
 *	- 1st リリース。
 *	* Thu Oct 10 21:00:02 JST 2013 Naoyuki Sawa
 *	- プロジェクト固有の拡張を追加しました。
 *	  「1バイトタイプ＋4バイトデータ」のコマンド形式を、「順序付き8バイト」形式に変換して送出します。
 *	* Tue Dec 16 21:38:05 JST 2014 Naoyuki Sawa
 *	- AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。
 *	  結果はこれまでと同じです。
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <mbctype.h>
/* Win32 */
#define STRICT
#include <windows.h>

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

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

#define VERSION "20141216"

__declspec(noreturn) void usage();
void AddData(int c);
void AddCommand(const char* pType, const char* pData);	//{{プロジェクト固有の拡張}}

#define __CLIP_H__
#include "../../clipmisc.h"
#include "../../clipmisc.c"
/* getopt */
#include "../../include/getopt.h"
#include "../../getopt.c"
/* SlotCmd58_Encode() */
#include <malloc.h>	/* alloca() */	/* 当プログラムでは使用しないがclipslot.cが参照しているために必要 */
#include "../../cliptlz.h"		/* 当プログラムでは使用しないがclipslot.cが参照しているために必要 */
#include "../../cliptlz.c"		/* 当プログラムでは使用しないがclipslot.cが参照しているために必要 */
#include "../../clipslot.h"
#include "../../clipslot.c"
#undef  __CLIP_H__

/* オプション */
int opt_Verbose;	/* 1ならば、詳細表示を行う。 */
int opt_Expression;	/* 1ならば、オプション以降の引数要素を、ファイル名ではなく出力データと見なす。 */
int opt_eXtension;	/* 1ならば、「1バイトタイプ＋4バイトデータ」のコマンド形式を、「順序付き8バイト」形式に変換して送出する。 */	//{{プロジェクト固有の拡張}}
int opt_CommPort = 1;	/* 通信ポート番号。VB6のMSCommコントロールのCommPortプロパティに相当する。 */		/* オプション'c'が指定されなければ、デフォルトで1とする。 */
char* opt_Settings;	/* デバイス制御文字列。VB6のMSCommコントロールのSettingsプロパティに相当する。 */

char ModuleFileName[_MAX_FNAME];	/* モジュールファイル名 */
char PortName[_MAX_FNAME];		/* ポート名 */
char MutexName[_MAX_PATH];		/* ミューテクス名 */
DCB Dcb;

HANDLE hMutex;	/* ミューテクス */
HANDLE hComm;	/* 通信ポート */

unsigned char* DataBuffer/*[DataBufferSize]*/;	/* データバッファ */
int DataBufferSize;				/* データバッファのバイト数 */
int DataLength;					/* データのバイト数 */

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

int main(int argc, char* argv[]) {
	int RetVal;
	int opt;
	char* EndPtr;
	int NumberOfBytesToWrite;
	int NumberOfBytesWritten;
	char Path[_MAX_PATH];
	/* コマンドライン引数が一つも指定されていなければ、使い方を表示し、エラー終了する。 */
	if(argc <= 1) { usage(); }
	/* コマンドラインを解析する。 */
	opterr = 0; /* オプションのエラーメッセージは、本プログラムが表示する。getopt()のエラーメッセージを抑制する。 */
	while((opt = getopt(argc, argv, "vexc:s:")) != -1) {
		switch(opt) {
		case 'v':
			opt_Verbose = 1;	/* 詳細表示を行う。 */
			break;
		case 'e':
			opt_Expression = 1;	/* オプション以降の引数要素を、ファイル名ではなく出力データと見なす。 */
			break;
		case 'x':
			opt_eXtension = 1;	/* 「1バイトタイプ＋4バイトデータ」のコマンド形式を、「順序付き8バイト」形式に変換して送出する。 */	//{{プロジェクト固有の拡張}}
			break;
		case 'c':
			opt_CommPort = strtol(optarg, &EndPtr, 10);	/* 通信ポート番号。 */
			if((*EndPtr != '\0') || (opt_CommPort < 1)) { die("オプション'%c'の引数が不正です。", optopt); } /* 数値以外の文字を含んでおらず,かつ,1以上の値であること。 */
			break;
		case 's':
			opt_Settings = optarg;	/* デバイス制御文字列。 */
			break;
		default:
			die("オプション'%c'が不正です。", optopt);
			break;
		}
	}
	/* プロジェクト固有の拡張が指定されていたら… */
	if(opt_eXtension) {
//{{プロジェクト固有の拡張
		/* コマンドライン引数から、「1バイトタイプ+4バイトデータ」の組みを取得し、「順序付き8バイト」に変換して、出力データにセットする。 */
		/* - プロジェクト固有の拡張が指定された場合は、暗黙の'-e'も指定されたものと見なす。
		 *   '-e'の指定の有無にかかわらず、タイプとデータをコマンドライン引数から取得する。
		 */
		while(optind < argc) {
			if((optind + 2) > argc) { die("プロジェクト固有の拡張における、コマンドライン引数が不正です。"); }
			AddCommand(argv[optind + 0], argv[optind + 1]);
			optind += 2;
		}
//}}プロジェクト固有の拡張
	/* プロジェクト固有の拡張が指定されていなければ… */
	} else {
		/* コマンドライン引数から、出力データを収集する。 */
		if(opt_Expression) {
			/* 全ての引数の全ての文字を、出力データに追加する。 */
			while(optind < argc) {
				char  *p = argv[optind++];
				while(*p) { AddData(*p++); }
			}
		/* ファイル,又は,標準入力から、出力データを収集する。 */
		} else {
			/* ファイル名が一つも指定されていなくても、ループを一回実行する。 */
			do {
				FILE* fp = stdin;	/* ファイル名が一つも指定されていなければ、ループを一回だけ実行し、標準入力から読み込む。 */
				/* ファイル名が一つ以上指定されていたら… */
				if(optind < argc) {
					/* ファイルを開く。 */
					fp = fopen(argv[optind], "r");
					if(!fp) { die("%sが開けません。", argv[optind]); }
				}
				for(;;) {
					/* 一文字読み込む。 */
					RetVal = fgetc(fp);
					/* ファイル終端に達したら、抜ける。 */
					if(RetVal == EOF) { break; }
					/* 出力データに追加する。 */
					AddData(RetVal);
				}
				/* ファイル名が一つ以上指定されていたら… */
				if(optind < argc) {
					/* ファイルを閉じる。 */
					RetVal = fclose(fp);
					if(RetVal) { die("fcloseが失敗しました。"); }
					/* 次のファイルへ進める。 */
					optind++;
				}
			} while(optind < argc);		/* ファイル名が一つも指定されていないか,又は,ファイル名が終端に達したら、ループを抜ける。 */
		}
	}
	/* モジュールファイル名を取得する。 */
	RetVal = GetModuleFileName(NULL, Path, sizeof Path);
	if(RetVal == 0) { die("GetModuleFileNameが失敗しました。"); }
	_splitpath(Path, NULL, NULL, ModuleFileName, NULL);
	/* ポート名を作成する。 */
	sprintf(PortName, "COM%u", opt_CommPort);
	/* モジュールファイル名とポート名を連結して、ミューテクス名を作成する。
	 * □MSDNライブラリ VisualStudio 6.0「CreateMutex」より引用:
	 * ┃名前には「\」以外の任意の文字が使用できます。大文字小文字は区別されます。最大文字数は_MAX_PATHです。
	 */
	strcpy(MutexName, ModuleFileName);
	strcat(MutexName, ":");
	strcat(MutexName, PortName);
	/* ミューテクスを作成する。 */
	hMutex = CreateMutex(NULL, FALSE, MutexName);
	if(!hMutex) { die("CreateMutexが失敗しました。"); }
	/* ミューテクスの所有権を獲得する。
	 * 同じアプリケーションが同じポートに対してミューテクスの所有権を獲得している場合は、開放されるまで待つ。
	 * 戻り値は、WAIT_ABANDONED,又は,WAIT_OBJECT_0になるはずである。
	 */
//{{詳細表示
	if(opt_Verbose) { printf("排他制御中\n"); }
//}}詳細表示
	RetVal = WaitForSingleObject(hMutex, INFINITE);
	if(RetVal == WAIT_FAILED) { die("WaitForSingleObjectが失敗しました。"); }
//{{詳細表示
	if(opt_Verbose) { printf("排他制御完了\n"); }
//}}詳細表示
	/* シリアルポートを開く。
	 * □「[SDK32]COM10以上のシリアルポートを指定する方法」(http://support.microsoft.com/kb/115831/ja)より引用:
	 * ┃COM10以上のシリアルポートを指定する場合は、"\\.\"を付ける必要があります。この構文は、COM1からCOM9でも有効です。
	 */
	strcpy(Path, "\\\\.\\");
	strcat(Path, PortName);
	hComm = CreateFile(
		Path,		/* lpFileName */
		GENERIC_WRITE,	/* dwDesiredAccess */
		0,		/* dwShareMode */
		NULL,		/* lpSecurityAttributes */
		OPEN_EXISTING,	/* dwCreationDisposition */
		0,		/* dwFlagsAndAttributes */
		NULL);		/* hTemplateFile */
	if(hComm == INVALID_HANDLE_VALUE) { die("%sが開けません。", Path); }
	/* シリアルポートの設定を取得する。
	 * - この後、BuildCommDCBを呼び出してDCBを変更するが、BuildCommDCBは全てのフィールドを変更しない。
	 *   BuildCommDCBが変更しないフィールドの既定値として、先にGetCommStateでDCBを取得する必要がある。
	 */
	RetVal = GetCommState(hComm, &Dcb);
	if(!RetVal) { die("GetCommStateが失敗しました。"); }
	/* デバイス制御文字列が指定されていたら… */
	if(opt_Settings) {
		/* DCBを変更する。 */
		RetVal = BuildCommDCB(opt_Settings, &Dcb);
		if(!RetVal) { die("BuildCommDCBが失敗しました。"); }
		/* シリアルポートの設定を変更する。 */
		RetVal = SetCommState(hComm, &Dcb);
		if(!RetVal) { die("SetCommStateが失敗しました。"); }
	}
//{{詳細表示
	if(opt_Verbose) {
		printf("通信デバイスの構成\n");
		printf("PortName              = %s\n", PortName);
		printf("DCB.DCBlength         = %u\n", Dcb.DCBlength);
		printf("DCB.BaudRate          = %u\n", Dcb.BaudRate);
		printf("DCB.fBinary           = %u\n", Dcb.fBinary);
		printf("DCB.fParity           = %u\n", Dcb.fParity);
		printf("DCB.fOutxCtsFlow      = %u\n", Dcb.fOutxCtsFlow);
		printf("DCB.fOutxDsrFlow      = %u\n", Dcb.fOutxDsrFlow);
		printf("DCB.fDtrControl       = %u\n", Dcb.fDtrControl);
		printf("DCB.fDsrSensitivity   = %u\n", Dcb.fDsrSensitivity);
		printf("DCB.fTXContinueOnXoff = %u\n", Dcb.fTXContinueOnXoff);
		printf("DCB.fOutX             = %u\n", Dcb.fOutX);
		printf("DCB.fInX              = %u\n", Dcb.fInX);
		printf("DCB.fErrorChar        = %u\n", Dcb.fErrorChar);
		printf("DCB.fNull             = %u\n", Dcb.fNull);
		printf("DCB.fRtsControl       = %u\n", Dcb.fRtsControl);
		printf("DCB.fAbortOnError     = %u\n", Dcb.fAbortOnError);
		printf("DCB.fDummy2           = %u\n", Dcb.fDummy2);
		printf("DCB.wReserved         = %u\n", Dcb.wReserved);
		printf("DCB.XonLim            = %u\n", Dcb.XonLim);
		printf("DCB.XoffLim           = %u\n", Dcb.XoffLim);
		printf("DCB.ByteSize          = %u\n", Dcb.ByteSize);
		printf("DCB.Parity            = %u\n", Dcb.Parity);
		printf("DCB.StopBits          = %u\n", Dcb.StopBits);
		printf("DCB.XonChar           = %u\n", Dcb.XonChar);
		printf("DCB.XoffChar          = %u\n", Dcb.XoffChar);
		printf("DCB.ErrorChar         = %u\n", Dcb.ErrorChar);
		printf("DCB.EofChar           = %u\n", Dcb.EofChar);
		printf("DCB.EvtChar           = %u\n", Dcb.EvtChar);
		printf("DCB.wReserved1        = %u\n", Dcb.wReserved1);
	}
//}}詳細表示
	/* データを全て出力する。 */
//{{詳細表示
	if(opt_Verbose) { printf("データ出力中"); }
//}}詳細表示
	NumberOfBytesToWrite = DataLength;
	while(NumberOfBytesToWrite) {
		RetVal = WriteFile(hComm,
			&DataBuffer[DataLength - NumberOfBytesToWrite],
			opt_Verbose ? 1 : NumberOfBytesToWrite,				/* 詳細表示を行う時は、詳細表示と実際の各バイトの出力を同期させたいため、1バイトずつ送信して完了を待つことにした。 */
			&NumberOfBytesWritten, NULL);
		if(!RetVal) { die("WriteFileが失敗しました。"); }
//{{詳細表示
		if(opt_Verbose) {
			int i;
			for(i = 0; i < NumberOfBytesWritten; i++) {
				printf("%c%02X",
					((DataLength - NumberOfBytesToWrite + i) & 15) ? ' ' : '\n',
					DataBuffer[DataLength - NumberOfBytesToWrite + i]);
			}
			RetVal = FlushFileBuffers(hComm);				/* 詳細表示を行う時は、詳細表示と実際の各バイトの出力を同期させたいため、1バイトずつ送信して完了を待つことにした。 */
			if(!RetVal) { die("FlushFileBuffersが失敗しました。"); }	/* 詳細表示を行う時は、詳細表示と実際の各バイトの出力を同期させたいため、1バイトずつ送信して完了を待つことにした。 */
		}
//}}詳細表示
		NumberOfBytesToWrite -= NumberOfBytesWritten;
	}
	/* シリアルポートを閉じる前に、確実にフラッシュすることにした。 */
	RetVal = FlushFileBuffers(hComm);				/* 不要かも知れないが、安心のために。 */
	if(!RetVal) { die("FlushFileBuffersが失敗しました。"); }	/* 不要かも知れないが、安心のために。 */
//{{詳細表示
	if(opt_Verbose) { printf("\nデータ出力完了\n"); }
//}}詳細表示
	/* シリアルポートを閉じる。 */
	RetVal = CloseHandle(hComm);
	if(!RetVal) { die("CloseHandleが失敗しました。"); }
	/* ミューテクスの所有権を開放する。 */
	RetVal = ReleaseMutex(hMutex);
	if(!RetVal) { die("ReleaseMutexが失敗しました。"); }
	/* ミューテクスを開放する。 */
	RetVal = CloseHandle(hMutex);
	if(!RetVal) { die("CloseHandleが失敗しました。"); }
	return EXIT_SUCCESS;
}

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

void AddData(int c) {
	static int mb;	/* 前回の文字がマルチバイト文字の第1バイトならば非0 */
	static int lo;	/* データの最終バイトの第2ニブルが未格納ならば非0 */
	/* 前回の文字が、マルチバイト文字の第1バイトであったならば… */
	if(mb) {
		/* この後の処理に関係無く、前回の文字をクリアしておく。 */
		mb = 0;
		/* 今回の文字が、マルチバイト文字の第2バイトならば… */
		if(_ismbbtrail(c)) {
			/* 前回の文字と、今回の文字を破棄する。 */
			/** no job **/
		/* 今回の文字が、マルチバイト文字の第2バイトでなければ… */
		} else {
			/* 不正なテキストであると判断し、エラー終了する。 */
			die("マルチバイト文字列が不正です。");
		}
	/* 前回の文字が、マルチバイト文字の第1バイトでなかったならば… */
	} else {
		/* 今回の文字が、マルチバイト文字の第1バイトならば… */
		if(_ismbblead(c)) {
			/* マルチバイト文字の第1バイトを格納する。 */
			mb = c;
		/* 今回の文字が、マルチバイト文字の第2バイトでなければ… */
		} else {
			/* 今回の文字が、16進数の文字ならば… */
			if(isxdigit(c)) {
				/* 文字⇒数値に変換する。 */
				     if(isdigit(c)) { c -= ('0' -  0); }
				else if(islower(c)) { c -= ('a' - 10); }
				else if(isupper(c)) { c -= ('A' - 10); }
				else { DIE(); } /* バグ */
				/* データの最終バイトの第2ニブルが未格納ならば… */
				if(lo) {
					/* この後の処理に関係無く、クリアしておく。 */
					lo = 0;
					/* データの最終バイトの、第2ニブルに格納する。 */
					if(!DataBuffer ||				/* 第1ニブルの格納時に確保済みであるはず */
					   (DataBufferSize < DataLength) ||		/* (バッファ容量)≧(データ長さ)であるはず */
					   (DataBuffer[DataLength-1] & 15)) { DIE(); }	/* 最終バイトの第2ニブルは未格納であるはず */
					    DataBuffer[DataLength-1] |= c;
				/* データの最終バイトの第2ニブルが格納済みならば… */
				} else {
					/* この後の処理に関係無く、セットしておく。 */
					lo = 1;
					/* 必要に応じて、データバッファを拡張する。 */
					if(DataLength == DataBufferSize) {
						DataBufferSize += (1<<10);	/* 【変更可能】1キロバイトづつ拡張することにした。 */
						DataBuffer = realloc(DataBuffer, DataBufferSize);
						if(!DataBuffer) { die("reallocが失敗しました。"); }
					}
					/* データの最終バイトの、第1ニブルに格納する。 */
					DataBuffer[DataLength++] = (c<<4);
				}
			/* 今回の文字が、16進数の文字でなければ… */
			} else {
				/* 今回の文字を破棄する。 */
				/** no job **/
			}
		}
	}
}

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

//{{プロジェクト固有の拡張
// [指定形式]
// 1バイトタイプ:     T07 T06 T05 T04 T03 T02 T01 T00
// 4バイトデータ:     D31 D30 D29 D28 D27 D26 D25 D24
//                    D23 D22 D21 D20 D19 D18 D17 D16
//                    D15 D14 D13 D12 D11 D10 D09 D08
//                    D07 D06 D05 D04 D03 D02 D01 D00
// [送出形式]
// 8バイト送出データ:  0   0   0  T07 T06 T05 T04 T03
//                     0   0   1  T02 T01 T00 D31 D30
//                     0   1   0  D29 D28 D27 D26 D25
//                     0   1   1  D24 D23 D22 D21 D20
//                     1   0   0  D19 D18 D17 D16 D15
//                     1   0   1  D14 D13 D12 D11 D10
//                     1   1   0  D09 D08 D07 D06 D05
//                     1   1   1  D04 D03 D02 D01 D00
void AddCommand(const char* pType, const char* pData) {
//{{2014/12/16変更:AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。結果はこれまでと同じです。
//	unsigned char Type, Order;
//↓2014/12/16変更:AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。結果はこれまでと同じです。
	unsigned char Type;
//}}2014/12/16変更:AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。結果はこれまでと同じです。
	unsigned long Data;
	char* EndPtr;
	/* 1バイトタイプと4バイトデータを取得する。 */
	Type = (unsigned char)strtol(pType, &EndPtr, 16);	/* (unsigned char)のキャストは警告抑制の意味のみ */
	if((strlen(pType) < 1) || (strlen(pType) > 2) || (*EndPtr != '\0')) { die("1バイトタイプが不正です。1バイトタイプは、1-2桁の16進数で指定してください。"); }
	Data = strtoul(pData, &EndPtr, 16);	/* 【重要】strtol()ではなくstrtoul()を使う必要がある。strtol()ではLONG_MAX超過の値をLONG_MAXに切り詰めてしまう。 */
	if((strlen(pData) < 1) || (strlen(pData) > 8) || (*EndPtr != '\0')) { die("4バイトデータが不正です。4バイトデータは、1-8桁の16進数で指定してください。"); }
	/* 必要に応じて、データバッファを拡張する。 */
	if((DataLength + 8) > DataBufferSize) {
		DataBufferSize += (1<<10);	/* 【変更可能】1キロバイトづつ拡張することにした。 */
		DataBuffer = realloc(DataBuffer, DataBufferSize);
		if(!DataBuffer) { die("reallocが失敗しました。"); }
	}
//{{2014/12/16変更:AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。結果はこれまでと同じです。
//	/* 「順序付き8バイト」形式に変換して格納する。 */
//	for(Order = 0; Order < 8; Order++) {				//┏━━格納━━┓
//									//Ｏｒｄ┏━Ｔｙｐｅ━┓┏━━━━━━━━━━━━━Ｄａｔａ━━━━━━━━━━━━━┓
//									//２１０７６５４３２１０31302928272625242322212019181716151413121110９８７６５４３２１０
//		DataBuffer[DataLength++] = (Order << 5) | (Type >> 3);	//●●●■■■■■□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□
//									//　　　　　　　／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／
//									//　　　　　　／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／
//		Type <<= 5;						//　　　　　／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／
//		Type |= (Data >> (32-5));				//　　　　／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／／
//		Data <<= 5;						//・・・□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□０００００
//	}
//↓2014/12/16変更:AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。結果はこれまでと同じです。
	/* 「順序付き8バイト」形式に変換して格納する。 */
	SlotCmd58_Encode(Type, Data, &DataBuffer[DataLength]);
	DataLength += 8;
//}}2014/12/16変更:AddCommand()を、自前処理でなく、SlotCmd58_Encode()を呼び出すように変更しました。結果はこれまでと同じです。
}
//}}プロジェクト固有の拡張

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

/* 使い方を表示し、エラー終了する。 */
__declspec(noreturn) void usage() {
	fprintf(stderr, "dpserial - 簡易シリアル出力ツール (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2014 Naoyuki Sawa All Rights Reserved.\n");
	fprintf(stderr, "Usage:\n");
	fprintf(stderr, "    dpserial [-v] [-c CommPort] [-s Settings]             …出力データを標準入力から読み込む\n");
	fprintf(stderr, "    dpserial [-v] [-c CommPort] [-s Settings] FileName... …出力データをファイルから読み込む\n");
	fprintf(stderr, "    dpserial [-v] [-c CommPort] [-s Settings] -e  Data... …出力データをインラインで指定する\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "    -v            詳細表示を行う。\n");
	fprintf(stderr, "    -c CommPort   通信ポート番号を指定する。指定しない場合の既定値は1である。\n");
	fprintf(stderr, "                  VB6のMSCommコントロールのCommPortプロパティに相当する。\n");
	fprintf(stderr, "    -s Settings   デバイス制御文字列を指定する。指定しない場合はシステムの既定値を使用する。\n");
	fprintf(stderr, "                  VB6のMSCommコントロールのSettingsプロパティに相当する。\n");
	fprintf(stderr, "                  デバイス制御文字列の書式は Win32 API BuildCommDCB リファレンスを参照せよ。\n");
	fprintf(stderr, "    -e            出力データをインラインで指定する。\n");
	fprintf(stderr, "Summary:\n");
	fprintf(stderr, "  - 本ツールの別のインスタンスが同じポートを使用している場合、別のインスタンスの終了を待つ。\n");
	fprintf(stderr, "    本ツール以外のアプリケーションが同じポートを使用している場合は、本ツールはエラーとなる。\n");
	fprintf(stderr, "  - 出力データは、標準入力,ファイル,又は,コマンドライン引数で指定可能である。\n");
	fprintf(stderr, "    どの方法で指定する場合も、出力データの書式は同じである。\n");
	fprintf(stderr, "  - 出力データの書式は、16進数文字*以外*を全て無視して、2桁16進数と解釈する。\n");
	fprintf(stderr, "    下記の例は全て「0x12 0x9A 0xEF」と解釈される。\n");
	fprintf(stderr, "    ・ 12 9a ef\n");
	fprintf(stderr, "    ・ 12 9A EF\n");
	fprintf(stderr, "    ・ 1 2 9 A E F\n");
	fprintf(stderr, "    ・ 12,9A,EF\n");
	fprintf(stderr, "    ・ 1,2,9,A,E,F\n");
	fprintf(stderr, "    ・ 12\\t9A\\r\\nEF\n");
	fprintf(stderr, "    ・ 12 9A GH EF\n");
	fprintf(stderr, "    ・ 12日9A本EF語\n");
	fprintf(stderr, "    尚、最終バイトが1桁であった場合は、下位ニブルを0と解釈する。\n");
	fprintf(stderr, "  - 書式が理解し辛い場合は、'-v'オプションを指定して、実際の出力データを見てください。\n");
	fprintf(stderr, "Example:\n");
	fprintf(stderr, "    ① dpserial -c 1 -s \"9600,N,8,1\"\n");
	fprintf(stderr, "       出力データを標準入力から読み込み、COM1へ、指定された通信設定で出力する。\n");
	fprintf(stderr, "    ② dpserial -c 1 -s \"9600,N,8,1\" Data1.txt Data2.txt\n");
	fprintf(stderr, "       出力データをファイルから読み込み、COM1へ、指定された通信設定で出力する。\n");
	fprintf(stderr, "       Data1.txtとData2.txtには、適切な出力データテキストが格納されていること。\n");
	fprintf(stderr, "       尚、ワイルドカードを使って複数のファイルを指定することも可能である。\n");
	fprintf(stderr, "    ③ dpserial -c 1 -s \"9600,N,8,1\" -e 01 23 45 67 89 AB CD EF\n");
	fprintf(stderr, "       出力データをインラインで読み込み、COM1へ、指定された通信設定で出力する。\n");
	fprintf(stderr, "    ④ dpserial -c 1 -s \"9600,N,8,1\" -e 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
	fprintf(stderr, "       ③と同じ内容を出力する。\n");
	fprintf(stderr, "       本ツールは、16進数文字*以外*を全て無視して、2桁16進数と解釈することに注意せよ。\n");
	fprintf(stderr, "eXtension:\n");
	fprintf(stderr, "    プロジェクト固有の拡張として、\n");
	fprintf(stderr, "    「1バイトタイプ+4バイトデータ」形式のコマンドを、「順序付き8バイト」に変換して出力する。\n");
	fprintf(stderr, "    dpserial [-v] [-c CommPort] [-s Settings] -x Type Data ...\n");
	fprintf(stderr, "    ① dpserial -c 1 -s \"9600,N,8,1\" -x 01 0123CDEF 00 00000000\n");
	fprintf(stderr, "       {Type=0x00,Data=0x0123CDEF},{Type=0x00,Data=0x00000000}を、変換して出力する。\n");
	fprintf(stderr, "    ② dpserial -c 1 -s \"9600,N,8,1\" -x 1 123CDEF 0 0\n");
	fprintf(stderr, "       ①と同じ内容を出力する。\n");
	fprintf(stderr, "       Typeは1-2桁の16進数で指定し、上位桁の0埋めを省略しても良い。\n");
	fprintf(stderr, "       Dataは1-8桁の16進数で指定し、上位桁の0埋めを省略しても良い。\n");
	exit(EXIT_FAILURE);
}

