/** makepassword.cpp
 * パスワードを生成する（Windowsアプリ版）
 *
 * @copyright	(c)studio pahoo
 * @author		パパぱふぅ
 * @動作環境	MinGW C++ + Boost C++ Libraries
 * @参考URL		https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-05-01.shtm
*/
// 初期化処理 ======================================================
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <time.h>
#include <sstream>
#include <string>
#include <winsock2.h>
#include <windows.h>
#include <shlobj.h>
#include <commctrl.h>
#include <richedit.h>
#include <random>
#include <boost/program_options.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/format.hpp>
#include "resource.h"

using namespace std;
using namespace boost;
using namespace boost::program_options;
using namespace boost::property_tree;

#define MAKER		"pahoo.org"				// 作成者
#define APPNAME		"makepassword"			// アプリケーション名（GUI版）
#define APPNAMEJP	"パスワード生成機"		// アプリケーション名
#define APPCMD		"mkpsw"					// アプリケーション名（CUI版）
#define APPVERSION	"1.3.3"					// バージョン
#define APPYEAR		"2020-2026"				// 作成年
#define REFERENCE	"https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-05-01.shtm"	// 参考サイト

// char*バッファサイズ
#define SIZE_BUFF		512

// 標準フォント
#define FONT_FACE		"MS UI Gothic"

// 現在のインターフェイス
HINSTANCE hInst;

// アプリケーション・ウィンドウ
HWND hParent;

// アプリケーション・ウィンドウ位置
unsigned hParent_X, hParent_Y;

// エラー・メッセージ格納用
string ErrorMessage;

// ヘルプ・ファイル
#define HELPFILE	".\\etc\\help.chm"

// オプション
#define OPTION_UPPER	'u'		// 英大文字
#define OPTION_LOWER	'a'		// 英小文字
#define OPTION_NUMBER	'n'		// 数字
#define OPTION_SYMBOL	's'		// 記号

// パスワードに使う文字セット
const string Seed[] = {
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ",		// 英大文字
	"abcdefghijklmnopqrstuvwxyz",		// 英小文字
	"0123456789"						// 数字
};
// パスワードに使う記号
#define DEF_SYMBOL	"()%$#+-*"
string Symbol = DEF_SYMBOL;

// オプションの初期値
#define OPTION_INIT		"an"

// 生成するパスワードの最小桁数と最大桁数
#define MIN_PASSWORD	4
#define MAX_PASSWORD	20

// パスワード長
unsigned PswLen = MIN_PASSWORD;

// 乱数関係
std::random_device rnd;			// 非決定的な乱数生成器を生成
std::mt19937 mt((int)time(0));	// メルセンヌ・ツイスタの32ビット版＋シード

// バージョン情報
string Version = (boost::format(R"(
%1%  バージョン %2%

本アプリケーションはMIT Licenseです．
商用を含む無償利用が可能です．自由に改造できます．
再配布の際は，下記の著作権表記，およびURLと本使用条件を必ず明記してください．

　Copyright by (c)studio pahoo, %3%
　https://www.pahoo.org/

なお，本アプリケーションの利用または改造することによって生じた得失ついては一切関知いたしません．また，二次利用先の組織・企業・団体の目的・内容・活動については一切関知いたしません．

次のライブラリを利用しています．
MinGW（g++）／Boost C++ライブラリ
)")
% APPNAMEJP
% APPVERSION
% APPYEAR
).str();

// CUIヘルプ情報
string Help = (boost::format(R"(
%1%  バージョン %2%

%3%.exe -オプション
        -l, --len     パスワードの長さ（初期値＝%4%）
    パスワードに含める文字種
        -u, --upper   英大文字
        -a, --alpha   英小文字
        -n, --number  数字
        -s, --symbol  記号
    その他
        -p, --paste   クリップボードへ出力
        -v, --version バージョン情報
        -h, --help    オンラインヘルプ

（例）%3% -l11 -p  11桁のパスワードを生成し，クリップボードにコピー
)")
% APPNAMEJP
% APPVERSION
% APPCMD
% MIN_PASSWORD).str();

// クリップボード関連 ======================================================
/**
 * クリップボードに文字列をセット
 * @param	string str 文字列
 * @return	なし
*/
void setClipboardData(string str) {
	char buff[str.size() + 1];
	char_traits<char>::copy(buff, str.c_str(), str.size() + 1);

	OpenClipboard(NULL);
	EmptyClipboard();

	HGLOBAL hText = GlobalAlloc(GHND | GMEM_SHARE, SIZE_BUFF);
	PTSTR pText = (PTSTR)GlobalLock(hText);
	lstrcpy(pText, buff);
	GlobalUnlock(hText);
	SetClipboardData(CF_TEXT, hText);
	CloseClipboard();
}

// パスワード生成 ===========================================================
/**
 * パスワードを生成する
 * @param  unsigned	len パスワードの長さ
 * @param  string	str パスワードに使う文字の並び
 * @return string	パスワード
*/
string make_password(unsigned len, string str) {
	int n;

	// 範囲指定のメルセンヌ・ツイスタ乱数
	std::uniform_int_distribution<> mt_rand(0, str.length() - 1);

	string psw = "";
	for (unsigned i = 0; i < len; i++) {
		n = mt_rand(mt);
		psw = psw + str.substr(n, 1);	// 1文字追加
	}
	return psw;
}

#ifndef CMDAPP
// GUI処理 =================================================================
/**
 * テキストボックスから整数を取得
 * @param  HWND		hDlg  ダイアログID
 * @param  unsigned	id    テキストボックスID
 * @return int 取得値
*/
int getIntEditBox(HWND hDlg, unsigned id) {
	char buff[SIZE_BUFF];
	GetDlgItemText(hDlg, id, buff, sizeof(buff));

	return atoi(buff);
}

/**
 * テキストボックスに整数を代入
 * @param  HWND		hDlg  ダイアログID
 * @param  unsigned	id    テキストボックスID
 * @param  int		i     設定値
 * @return なし
*/
void setIntEditBox(HWND hDlg, unsigned id, int i) {
	char buff[SIZE_BUFF];
	snprintf(buff, sizeof(buff), "%d", i);
	SetDlgItemText(hDlg, id, buff);
}

/**
 * テキストボックスから文字列を取得
 * @param  HWND		hDlg  ダイアログID
 * @param  unsigned	id    テキストボックスID
 * @return string 取得文字列
*/
string getStrEditBox(HWND hDlg, unsigned id) {
	char buff[SIZE_BUFF];
	GetDlgItemText(hDlg, id, buff, sizeof(buff));
	return (string)buff;
}

/**
 * テキストボックスに文字列を代入
 * @param  HWND		hDlg  ダイアログID
 * @param  unsigned	id    テキストボックスID
 * @param  string	s     設定値
 * @return なし
*/
void setStrEditBox(HWND hDlg, unsigned id, string s) {
	char buff[SIZE_BUFF];
	char_traits<char>::copy(buff, s.c_str(), s.size() + 1);
	SetDlgItemText(hDlg, id, buff);
}

/**
 * ダイアログを親ウィンドウの中央に配置
 * @param	HWND hDlg		自分のハンドラ
 * @global	HWND hParent	親ウィンドウのハンドラ
 * @return	なし
*/
void CenterWindow(HWND hDlg) {
	int  x, y;
	RECT rc;

	GetWindowRect(hParent, &rc);
	x = rc.left + (rc.right - rc.left) / 2;
	y = rc.top + (rc.bottom - rc.top) / 2;

	SetWindowPos(hDlg, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

/**
 * パスワードを表示
 * @param  HWND   hDlg ダイアログID
 * @return string	なし
*/
void put_password(HWND hDlg) {
	// パスワードの長さ
	PswLen = getIntEditBox(hDlg, IDC_EDIT_NUM);
	if (PswLen < MIN_PASSWORD)		PswLen = MIN_PASSWORD;
	if (PswLen > MAX_PASSWORD)		PswLen = MAX_PASSWORD;
	setIntEditBox(hDlg, IDC_EDIT_NUM, PswLen);

	// パスワード対象文字
	string str = "";
	// 英大文字
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT1), BM_GETCHECK, 0, 0)) {
		str = str + Seed[0];
	}
	// 英小文字
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT2), BM_GETCHECK, 0, 0)) {
		str = str + Seed[1];
	}
	// 数字
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT3), BM_GETCHECK, 0, 0)) {
		str = str + Seed[2];
	}
	// 記号
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT4), BM_GETCHECK, 0, 0)) {
		Symbol = getStrEditBox(hDlg, IDC_EDIT_OPT4);
		str = str + Symbol;
	}

	// パスワード生成と表示
	if (str != "") {
		string psw = make_password(PswLen, str);
		setStrEditBox(hDlg,IDC_EDIT_PSW, psw);
	}
}

// オプション読込／保存 ====================================================
/**
 * AppDataのパスを取得
 * @param	char* appname アプリケーション名
 * @return	string パス
 */
string getMyPath(const char* appname) {
	static TCHAR myPath[MAX_PATH] = "";

	if (strlen(myPath) == 0) {
		if (SHGetSpecialFolderPath(NULL, myPath, CSIDL_APPDATA, 0)) {
			TCHAR *ptmp = _tcsrchr(myPath, _T('\\'));
			if (ptmp != NULL) {
				ptmp = _tcsinc(ptmp);
				*ptmp = _T('\0');
			}
			strcat(myPath, _T("Roaming"));
			CreateDirectory((LPCTSTR)myPath, NULL);
			strcat(myPath, _T("\\pahoo.org"));
			CreateDirectory((LPCTSTR)myPath, NULL);
			strcat(myPath, _T("\\"));
			strcat(myPath, _T(appname));
			CreateDirectory((LPCTSTR)myPath, NULL);
			strcat(myPath, _T("\\"));
		} else {
		}
	}
	return (string)myPath;
}

/**
 * オプションの読み込み
 * @param	なし
 * @return	string オプション
 */
string loadOption(void) {
	hParent_X = 0;
	hParent_Y = 0;
	PswLen = MIN_PASSWORD;
	string option = OPTION_INIT;
	ptree pt;

	// XMLファイル読み込み
	try {
		xml_parser::read_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt);

		// XML解釈
		try {
			// 形式チェック
			if (boost::optional<std::string>str = pt.get_optional<string>("parameter")) {
			} else {
				return OPTION_INIT;
			}
			// パラメータ読み込み
			for (auto it : pt.get_child("parameter")) {
				string type= it.second.get_optional<string>("<xmlattr>.type").value();
				if (type == "len") {
					PswLen = (unsigned)stoi(it.second.data());
				} else if (type == "option") {
					option = (string)it.second.data();
				} else if (type == "wx") {
					hParent_X = (unsigned)stoi(it.second.data());
				} else if (type == "wy") {
					hParent_Y = (unsigned)stoi(it.second.data());
				} else if (type == "symbol") {
					Symbol = (string)it.second.data();
				}
			}
		// 解釈失敗したら初期値設定
		} catch (xml_parser_error& e) {
			hParent_X = 0;
			hParent_Y = 0;
			PswLen = MIN_PASSWORD;
			Symbol = DEF_SYMBOL;
			return OPTION_INIT;
		}
	// 読み込み失敗したら初期値設定
	} catch (xml_parser_error& e) {
		hParent_X = 0;
		hParent_Y = 0;
		PswLen = MIN_PASSWORD;
		Symbol = DEF_SYMBOL;
		return OPTION_INIT;
	}

	// アプリケーション・ウィンドウの位置（デスクトップ範囲外なら原点移動）
	HWND hDesktop = GetDesktopWindow();
	WINDOWINFO windowInfo;
	windowInfo.cbSize = sizeof(WINDOWINFO);
	GetWindowInfo(hDesktop, &windowInfo);
	if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
		hParent_X = 0;
	}
	if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
		hParent_Y = 0;
	}

	return option;
}

/**
 * オプションの保存
 * @param	string option オプション
 * @param	HWND   hDlg ダイアログID
 * @return	なし
 */
void saveOption(string option, HWND  hDlg) {
	// アプリケーション・ウィンドウの位置取得
	WINDOWINFO windowInfo;
	windowInfo.cbSize = sizeof(WINDOWINFO);
	GetWindowInfo(hParent, &windowInfo);
	hParent_X = (unsigned)windowInfo.rcWindow.left;
	hParent_Y = (unsigned)windowInfo.rcWindow.top;
	if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
		hParent_X = 0;
	}
	if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
		hParent_Y = 0;
	}
	// 記号の文字種を取得
	Symbol = getStrEditBox(hDlg, IDC_EDIT_OPT4);

	// XMLファイルへ書き込む
	ptree pt;
	ptree& child1 = pt.add("parameter.param", (string)to_string(PswLen));
	child1.add("<xmlattr>.type", "len");
	ptree& child2 = pt.add("parameter.param", option);
	child2.add("<xmlattr>.type", "option");
	ptree& child3 = pt.add("parameter.param", (string)to_string(hParent_X));
	child3.add("<xmlattr>.type", "wx");
	ptree& child4 = pt.add("parameter.param", (string)to_string(hParent_Y));
	child4.add("<xmlattr>.type", "wy");
	ptree& child5 = pt.add("parameter.param", Symbol);
	child5.add("<xmlattr>.type", "symbol");

	const int indent = 4;
	write_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt, std::locale(),
		xml_writer_make_settings<std::string>(' ', indent));
}

// オプション取得・設定 ======================================================
/**
 * オプションを設定する
 * @param	HWND hDlg	ウィンドウ・ハンドラ
 * @param	string opt	オプション
 * @return	なし
 */
void setOption(HWND hDlg, string opt) {
	for (int i = 0; i < (int)opt.length(); i++) {
		char* ch = (char*)opt.substr(i, 1).c_str();
		switch (*ch) {
		case OPTION_UPPER:
			SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT1),
							BM_SETCHECK, BST_CHECKED, 0);
			break;
		case OPTION_LOWER:
			SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT2),
							BM_SETCHECK, BST_CHECKED, 0);
			break;
		case OPTION_NUMBER:
			SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT3),
							BM_SETCHECK, BST_CHECKED, 0);
			break;
		case OPTION_SYMBOL:
			SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT4),
							BM_SETCHECK, BST_CHECKED, 0);
			break;
		default:
			break;
		}
	}
}

/**
 * オプションを取得する
 * @param	HWND hDlg	ウィンドウ・ハンドラ
 * @return	string オプション
 */
string getOption(HWND hDlg) {
	string opt = "";
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT1), BM_GETCHECK, 0, 0)) {
		opt += OPTION_UPPER;
	}
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT2), BM_GETCHECK, 0, 0)) {
		opt += OPTION_LOWER;
	}
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT3), BM_GETCHECK, 0, 0)) {
		opt += OPTION_NUMBER;
	}
	if (SendMessage(GetDlgItem(hDlg, IDC_CHECK_OPT4), BM_GETCHECK, 0, 0)) {
		opt += OPTION_SYMBOL;
	}

	return opt;
}

// バージョン表示ダイアログ =================================================
/**
 * イベントハンドラ：バージョン表示ダイアログ
 * @param	HWND hDlg	ウィンドウ・ハンドラ
 * @paramm	UINT uMsg
 * @param	WPARAM wParam
 * @paramL	PARAM lParam
 * @return	INT_PTR CALLBACK
*/
INT_PTR CALLBACK processHelp(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch (uMsg) {
	// ダイアログ初期化
	case WM_INITDIALOG:
		CenterWindow(hDlg);
		setStrEditBox(hDlg, IDC_TEXT_HELP, Version);
		break;

	// ボタン押下
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		// 実行
		case IDC_BUTTON_OK:
			EndDialog(hDlg, 0);
			break;
		default:
			return 1;
		}
		break;
	// プログラム終了
	case WM_CLOSE:
		EndDialog(hDlg, 0);
		break;
	}
	return 0;
}

/**
 * ヘルプ・ダイアログを作成
 * @param  HWND hDlg		親ウィンドウ・ハンドラ
 * @param  DLGPROC handler	イベント・ハンドラ
 * @return なし
*/
void createHelp(HWND hDlg, DLGPROC handler) {
	DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG_VER), hDlg, (DLGPROC)handler);
}

// メインウィンドウ ==========================================================
/**
 * イベントハンドラ：メインウィンドウ
 * @param	HWND hDlg			親ウィンドウ・ハンドラ
 * @paramm	UINT uMsg			メッセージ識別子
 * @param	WPARAM wParam		メッセージの最初のパラメータ
 * @paramL	PARAM lParam		メッセージの2番目のパラメータ
 * @return	INT_PTR CALLBACK	TRUE：メッセージ処理完了／FALSE：未完了
*/
INT_PTR CALLBACK processMain(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	LPNMUPDOWN lpnmud;
	HICON hIcon;
	static string option = OPTION_INIT;
	string psw = "";

    switch(uMsg){
	// ダイアログ初期化
	case WM_INITDIALOG:
		hParent = hDlg;
		hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
		SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
		// オプション読み込み
		option = loadOption();
		setIntEditBox(hDlg, IDC_EDIT_NUM, PswLen);
		setOption(hDlg, option);
		// アプリケーション・ウィンドウ移動
		SetWindowPos(hParent, NULL, hParent_X, hParent_Y, 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER));
		// 記号文字種をセット
		setStrEditBox(hDlg,IDC_EDIT_OPT4, Symbol);
		// パスワード生成
		put_password(hDlg);
		break;

	// ボタン押下
	case WM_COMMAND:
		 switch (LOWORD(wParam)) {
			// パスワード生成
			case IDM_EXEC:
			case IDC_BUTTON_EXEC:
				put_password(hDlg);
				break;
			// ヘルプ
			case IDM_HELP:
				ShellExecute(hParent, _T("open"), _T(HELPFILE), NULL, NULL, SW_RESTORE);
				break;
			// バージョン表示
			case IDM_VERSION:
				createHelp(hParent, processHelp);
				break;
			// 解説サイト
			case IDM_PAHOO:
				ShellExecute(NULL, _T("open"), _T(REFERENCE), NULL, NULL, SW_RESTORE);
				break;
			// コピー
			case IDM_COPY:
			case IDC_BUTTON_COPY:
				psw = getStrEditBox(hDlg, IDC_EDIT_PSW);
				setClipboardData(psw);
				break;
			// 切り取り
			case IDM_DELETE:
				setStrEditBox(hParent, IDC_EDIT_PSW, "");
				break;
			// プログラム終了
			case IDM_QUIT:
				// オプション保存
				option = getOption(hDlg);
				saveOption(option, hDlg);
				EndDialog(hParent, 0);
				return 0;
			default:
				break;
		}
		break;

	// Spin Control
	case WM_NOTIFY:
		if (wParam == (WPARAM)IDC_SPIN_NUM) {
			lpnmud = (LPNMUPDOWN)lParam;
			if (lpnmud->hdr.code == UDN_DELTAPOS) {
				PswLen = getIntEditBox(hDlg, IDC_EDIT_NUM);
				if ((lpnmud->iDelta) < 0) {
					PswLen++;
				} else if ((lpnmud->iDelta) > 0) {
					PswLen--;
				}
				if (PswLen < MIN_PASSWORD) {
					PswLen = MIN_PASSWORD;
				}
				if (PswLen > MAX_PASSWORD) {
					PswLen = MAX_PASSWORD;
				}
				setIntEditBox(hDlg, IDC_EDIT_NUM, PswLen);
			}
		}
		break;

	// プログラム終了
	case WM_CLOSE:
		// オプション保存
		option = getOption(hDlg);
		saveOption(option, hDlg);
		EndDialog(hDlg, 0);
		break;
	}
    return 0;
}

// GUIメインプログラム ======================================================
/**
 * Windowsメインプログラム
 * @param	HINSTANCE hInstance			インスタンスハンドル
 * @paramm	HINSTANCE hPrevInstance		未使用(常にNULL)：Win16時代の名残
 * @param	LPSTR lpCmdLine				コマンドライン引数
 * @paramL	int nShowCmd				ウィンドウの表示方法
 * @return	int リターンコード
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
	LoadLibrary("RICHED20.DLL");
	hInst = hInstance;
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)processMain);

	return 0;
}

#else
// CUIメインプログラム ======================================================
/**
 * CUI用メインプログラム
 * @param	int argc
 * @paramm	char* argv[]
 * @return	int リターンコード
 */
int main(int argc, char* argv[]) {
	string str = "";
	string psw = "";

	// コマンドライン・オプションの定義
	options_description options("コマンドライン・オプション");
	options.add_options()
		("len,l", value<unsigned>(), "桁数")
		("upper,u",   "英大文字")
		("alpha,a",   "英小文字")
		("number,n",  "数字")
		("symbol,s",  "記号")
		("paste,p",   "クリップボードへ出力")
		("help,h",    "ヘルプ")
		("version,v", "バージョン情報")
		;
	// コマンドライン・オプションの取得
	variables_map vm;
	try {
		store(parse_command_line(argc, argv, options), vm);
	} catch(const boost::program_options::error_with_option_name& e) {
		ErrorMessage =  e.what();
		cerr << ErrorMessage << endl;
		return 1;
	}
	notify(vm);

	// ヘルプ情報
	if (vm.count("help")) {
		cout << Help;
	// バージョン情報
	} else if (vm.count("version")) {
		cout << Version;

	// パスワード生成
	} else {
		// オプション解釈→パスワード生成
		if (vm.count("upper")) {
			str = str + Seed[0];
		}
		if (vm.count("alpha")) {
			str = str + Seed[1];
		}
		if (vm.count("number")) {
			str = str + Seed[2];
		}
		if (vm.count("symbol")) {
			str = str + Symbol;
		}
		// オプション指定が無いとき
		if (str.length() == 0) {
			str = Seed[1] + Seed[2];
		}
		// 生成数
		if (vm.count("len")) {
			PswLen = (unsigned)vm["len"].as<unsigned>();
		} else {
			PswLen = MIN_PASSWORD;
		}
		// パスワード生成
		if ((PswLen >= MIN_PASSWORD) && (PswLen <= MAX_PASSWORD)) {
			psw = make_password(PswLen, str);
		} else {
			ErrorMessage = "桁数は " + to_string(MIN_PASSWORD) + "以上，" + to_string(MAX_PASSWORD) + "以下の整数を指定してください";
			cerr << ErrorMessage << endl;
			return 1;
		}

		// クリップボードへ出力
		if (vm.count("paste")) {
			setClipboardData(psw);
		// 標準出力へ出力
		} else {
			cout << psw;
		}
	}

	return 0;
}

#endif

/*
** バージョンアップ履歴 =====================================================
 *
 * @version 1.3.3  2026/02/07 使用ライブラリ更新, optional名前衝突回避
 * @version 1.3.2  2025/10/11 使用ライブラリ更新
 * @version 1.3.1  2025/06/21 使用ライブラリ更新
 * @version 1.3.0  2025/02/24 記号の文字種を指定できるようにした，使用ライブラリ更新
 * @version 1.2.7  2024/11/03 使用ライブラリ更新
 * @version 1.2.6  2024/07/07 使用ライブラリ更新
 * @version 1.2.5  2024/02/17 使用ライブラリ更新
 * @version 1.2.4  2023/10/09 使用ライブラリ更新
 * @version 1.2.3  2023/06/03 使用ライブラリ更新
 * @version 1.2.2  2023/01/28 使用ライブラリ更新
 * @version 1.2.1  2022/10/31 使用ライブラリ更新
 * @version 1.2    2022/07/17 CUI版を追加，使用ライブラリをVerUp
 * @version 1.1    2020/09/05 インストーラー，ヘルプファイル，メニュー追加
 * @version 1.0    2020/07/12 初版
*/
