// par1_cmd.c
// Copyright : 2024-11-30 Yutaka Sawada
// License : GPL

#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601	// Windows 7 or later
#endif

#include <stdio.h>

#include <windows.h>

#include "common1.h"
#include "par1.h"
#include "ini.h"
#include "version.h"


void print_help(void)
{
	printf(
"Usage\n"
"c(reate) [f,fu,r,n,p,m,c,d,in,u] <par file> [input files]\n"
"v(erify) [  vs,vd,d,i,u,b,br] <par file>\n"
"r(epair) [m,vs,vd,d,i,u,b,br] <par file>\n"
"l(ist)   [u,h] <par file>\n"
"\nOption\n"
" /f    : Use file-list instead of files\n"
" /fu   : Use file-list which is encoded with UTF-8\n"
" /r<n> : Rate of redundancy (%%)\n"
" /n<n> : Number of parity volumes\n"
" /p<n> : First parity volume number\n"
" /m<n> : Memory usage\n"
" /vs<n>: Skip verification by recent result\n"
" /vd\"*\": Set directory of recent result\n"
" /c\"*\" : Set comment\n"
" /d\"*\" : Set directory of input files\n"
" /i    : Recreate index file\n"
" /in   : Do not create index file\n"
" /u    : Console output is encoded with UTF-8\n"
" /b    : Backup existing files at repair\n"
" /br   : Send existing files into recycle bin at repair\n"
" /h    : List hash value of input files\n"
	);
}

// 動作環境の表示
static void print_environment(void)
{
	MEMORYSTATUSEX statex;

	// 「\\?\」は常に付加されてるので表示する際には無視する
	printf_cp("Base Directory\t: \"%s\"\n", base_dir);
	printf_cp("Recovery File\t: \"%s\"\n", recovery_file);

	printf("Memory usage\t: ");
	if (memory_use == 0){
		printf("Auto");
	} else {
		printf("%d/8", memory_use);
	}
	statex.dwLength = sizeof(statex);
	if (GlobalMemoryStatusEx(&statex))
		printf(" (%I64d MB available)", statex.ullAvailPhys >> 20);
	printf("\n\n");
}

// 格納ファイル・リストを読み込んでバッファーに書き込む
static wchar_t * read_list(
	wchar_t *list_path,		// リストのパス
	int *file_num,			// データ・ファイルの数
	int *list_len,			// ファイル・リストの文字数
	int *block_num,			// ソース・ブロックの数
	__int64 *block_size,	// ブロック・サイズ (最大ファイル・サイズ)
	int switch_f)			// 1=CP_OEMCP, 2=CP_UTF8
{
	char buf[MAX_LEN * 3];
	wchar_t *list_buf, file_name[MAX_LEN], file_path[MAX_LEN], *tmp_p;
	int len, base_len, l_max, l_off = 0;
	__int64 file_size;
	FILE *fp;
	WIN32_FILE_ATTRIBUTE_DATA AttrData;

	base_len = wcslen(base_dir);
	if (switch_f == 2){
		switch_f = CP_UTF8;
	} else {
		switch_f = CP_OEMCP;
	}
	l_max = ALLOC_LEN;
	list_buf = malloc(l_max);
	if (list_buf == NULL){
		printf("malloc, %d\n", l_max);
		return NULL;
	}

	// 読み込むファイルを開く
	fp = _wfopen(list_path, L"rb");
	if (fp == NULL){
		utf16_to_cp(list_path, buf);
		free(list_buf);
		printf("cannot open file-list, %s\n", buf);
		return NULL;
	}

	// 一行ずつ読み込む
	while (fgets(buf, MAX_LEN * 3, fp)){
		if (ferror(fp))
			break;
		buf[MAX_LEN * 3 - 1] = 0;
		// 末尾に改行があれば削除する
		for (len = 0; len < MAX_LEN * 3; len++){
			if (buf[len] == 0)
				break;
			if ((buf[len] == '\n') || (buf[len] == '\r')){
				buf[len] = 0;
				break;
			}
		}
		if (buf[0] == 0)
			continue;	// 改行だけなら次の行へ

		// 読み込んだ内容をユニコードに変換する
		if (!MultiByteToWideChar(switch_f, 0, buf, -1, file_name, MAX_LEN)){
			fclose(fp);
			free(list_buf);
			printf("MultiByteToWideChar, %s\n", buf);
			return NULL;
		}

		// ファイルが基準ディレクトリ以下に存在することを確認する
		len = copy_path_prefix(file_path, MAX_LEN - ADD_LEN, file_name, base_dir);	// 絶対パスにしてから比較する
		if (len == 0){
			fclose(fp);
			free(list_buf);
			printf_cp("filename is invalid, %s\n", file_name);
			return NULL;
		}
		if ((len <= base_len) || (_wcsnicmp(base_dir, file_path, base_len) != 0)){	// 基準ディレクトリ外なら
			fclose(fp);
			free(list_buf);
			printf("out of base-directory, %s\n", buf);
			return NULL;
		}
		wcscpy(file_name, file_path + base_len);
		if (wcschr(file_name, '\\') != NULL){	// サブ・ディレクトリは拒否する
			fclose(fp);
			free(list_buf);
			printf_cp("sub-directory is not supported, %s\n", file_name);
			return NULL;
		}

		// ファイルが存在するか確かめる
		if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){
			print_win32_err();
			fclose(fp);
			free(list_buf);
			printf("input file is not found, %s\n", buf);
			return NULL;
		}
		if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
			fclose(fp);
			free(list_buf);
			printf("folder is not supported, %s\n", buf);
			return NULL;
		}
		file_size = ((unsigned __int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow;
		if (*block_size < file_size)
			*block_size = file_size;

		// ファイル名が重複しないようにする
		if (search_file_path(list_buf, l_off, file_name))
			continue;

		// バッファー容量が足りなければ再確保する
		len = wcslen(file_name);
		if ((l_off + len) * 2 >= l_max){
			l_max += ALLOC_LEN;
			tmp_p = (wchar_t *)realloc(list_buf, l_max);
			if (tmp_p == NULL){
				free(list_buf);
				printf("realloc, %d\n", l_max);
				return NULL;
			} else {
				list_buf = tmp_p;
			}
		}

		// リストにコピーする
		wcscpy(list_buf + l_off, file_name);
		l_off += (len + 1);
		*file_num += 1;
		if (file_size > 0)
			*block_num += 1; // 空のファイルはブロックの計算に含めない
	}
	*list_len = l_off;
	fclose(fp);
/*
fp = fopen("list_buf.txt", "wb");
len = 0;
while (len < l_off){
	fwprintf(fp, L"%s\n", list_buf + len);
	len += (wcslen(list_buf + len) + 1);
}
fclose(fp);
*/
	return list_buf;
}

// ファイルを検索してファイル・リストに追加する
static wchar_t * search_files(
	wchar_t *list_buf,		// ファイル・リスト
	wchar_t *search_path,	// 検索するファイルのフル・パス
	int dir_len,			// ディレクトリ部分の長さ
	int single_file,		// -1 = *や?で検索指定、0～ = 単独指定
	int *file_num,			// データ・ファイルの数
	int *list_max,			// ファイル・リストの確保サイズ
	int *list_len,			// ファイル・リストの文字数
	int *block_num,			// ソース・ブロックの数
	__int64 *block_size,	// ブロック・サイズ (最大ファイル・サイズ)
	unsigned int filter)	// 隠しファイルを見つけるかどうか
{
	wchar_t *tmp_p;
	int len, l_max, l_off;
	__int64 file_size;
	HANDLE hFind;
	WIN32_FIND_DATA FindData;

	if (list_buf == NULL){
		l_off = 0;
		l_max = ALLOC_LEN;
		list_buf = malloc(l_max);
		if (list_buf == NULL){
			printf("malloc, %d\n", l_max);
			return NULL;
		}
	} else {
		l_max = *list_max;
		l_off = *list_len;
	}

	// 検索する
	hFind = FindFirstFile(search_path, &FindData);
	if (hFind == INVALID_HANDLE_VALUE)
		return list_buf; // 見つからなかったらそのまま
	do {
		if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			continue;	// フォルダは無視する

		if ((single_file < 0) && ((FindData.dwFileAttributes & filter) == filter))
			continue;	// 検索中は隠し属性が付いてるファイルを無視する

		// フォルダは無視する、ファイル名が重複しないようにする
		if (!search_file_path(list_buf, l_off, FindData.cFileName)){
			//printf("FindData.cFileName =\n%S\n\n", FindData.cFileName);
			len = wcslen(FindData.cFileName);
			if (dir_len + len >= MAX_LEN - ADD_LEN){
				FindClose(hFind);
				free(list_buf);
				printf("filename is too long\n");
				return NULL;
			}
			if ((l_off + len) * 2 >= l_max){ // 領域が足りなくなるなら拡張する
				l_max += ALLOC_LEN;
				tmp_p = (wchar_t *)realloc(list_buf, l_max);
				if (tmp_p == NULL){
					FindClose(hFind);
					free(list_buf);
					printf("realloc, %d\n", l_max);
					return NULL;
				} else {
					list_buf = tmp_p;
				}
			}

			file_size = ((unsigned __int64)FindData.nFileSizeHigh << 32) | (unsigned __int64)FindData.nFileSizeLow;
			if (*block_size < file_size)
				*block_size = file_size;

			// リストにコピーする
			wcscpy(list_buf + l_off, FindData.cFileName);
			l_off += len + 1;
			*file_num += 1;
			if (file_size > 0)
				*block_num += 1; // 空のファイルはブロックの計算に含めない
		}
	} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
	FindClose(hFind);

	*list_max = l_max;
	*list_len = l_off;
	return list_buf;
}

// リカバリ・ファイルとソース・ファイルが同じ名前にならないようにする
static int check_recovery_match(
	int file_num,
	wchar_t *list_buf,
	int list_len,
	int switch_p)	// インデックス・ファイルを作らない
{
	wchar_t *tmp_p;
	int i, num, len, list_off = 0, recovery_len, base_len;

	// 基準ディレクトリ外にリカバリ・ファイルが存在するなら問題なし
	base_len = wcslen(base_dir);
	if (_wcsnicmp(recovery_file, base_dir, base_len) != 0)
		return 0;

	// リカバリ・ファイル (*.PXX) の名前の基
	recovery_len = wcslen(recovery_file);
	recovery_len -= base_len + 2;	// 末尾の 2文字を除くファイル名の長さ

	// ファイルごとに比較する
	for (num = 0; num < file_num; num++){
		tmp_p = list_buf + list_off;
		while (list_buf[list_off] != 0)
			list_off++;
		list_off++;

		// ファイル名との前方一致を調べる
		if (_wcsnicmp(recovery_file + base_len, tmp_p, recovery_len) == 0){	// リカバリ・ファイルと基準が同じ
			len = wcslen(tmp_p);
			// 拡張子も同じか調べる
			if (len == recovery_len + 2){
				// インデックス・ファイルと比較する
				if (switch_p == 0){
					if (_wcsicmp(tmp_p + recovery_len, L"ar") == 0){
						printf_cp("filename is invalid, %s\n", tmp_p);
						return 1;
					}
				}
				// リカバリ・ファイル (*.PXX) と比較する
				for (i = 1; i <= 99; i++){
					if ((tmp_p[recovery_len    ] == '0' + (i / 10)) &&
						(tmp_p[recovery_len + 1] == '0' + (i % 10))){
						printf_cp("filename is invalid, %s\n", tmp_p);
						return 1;
					}
				}
			}
		}
	}
	return 0;
}

wmain(int argc, wchar_t *argv[])
{
	wchar_t file_path[MAX_LEN], *list_buf;
	wchar_t par_comment[COMMENT_LEN], *tmp_p;
	int i, j;
	int switch_b = 0, switch_p = 0, switch_f = 0, switch_r = 0, switch_h = 0;
	int file_num = 0, source_num = 0, list_len = 0, parity_num = 0, first_vol = 1;
	__int64 block_size = 0;

	printf("Parchive 1.0 client version " FILE_VERSION " by Yutaka Sawada\n\n");
	if (argc < 3){
		printf("Self-Test: ");
		i = par1_checksum(file_path);
		if (i == 0){
			printf("Success");
		} else if (i == 2){
			printf("PE checksum is different");
		} else if (i == 3){
			printf("CRC-32 is different");
		} else {
			printf("Error\0thedummytext");
		}
		printf("\n\n");
		print_help();
		return 0;
	}

	// 初期化
	recovery_file[0] = 0;
	par_comment[0] = 0;
	base_dir[0] = 0;
	ini_path[0] = 0;
	cp_output = GetConsoleOutputCP();
	memory_use = 0;

	// コマンド
	switch (argv[1][0]){
	case 'c': // create
	case 'v': // verify
	case 'r': // repair
	case 'l': // list
		break;
	default:
		print_help();
		return 0;
	}

	// オプションとリカバリ・ファイルの指定
	for (i = 2; i < argc; i++){
		tmp_p = argv[i];
		// オプション
		if (((tmp_p[0] == '/') || (tmp_p[0] == '-')) && (tmp_p[0] == argv[2][0])){
			tmp_p++;	// 先頭の識別文字をとばす
			// オプション
			if (wcscmp(tmp_p, L"b") == 0){
				switch_b = 1;
			} else if (wcscmp(tmp_p, L"br") == 0){
				switch_b = 2;
			} else if (wcscmp(tmp_p, L"f") == 0){
				switch_f = 1;
			} else if (wcscmp(tmp_p, L"fu") == 0){
				switch_f = 2;
			} else if (wcscmp(tmp_p, L"i") == 0){
				switch_p = 1;
			} else if (wcscmp(tmp_p, L"in") == 0){
				switch_p = 2;
			} else if (wcscmp(tmp_p, L"u") == 0){
				cp_output = CP_UTF8;
			} else if (wcscmp(tmp_p, L"h") == 0){
				switch_h = 1;

			// オプション (数値)
			} else if (wcsncmp(tmp_p, L"n", 1) == 0){
				j = 1;
				while ((j < 1 + 4) && (tmp_p[j] >= 48) && (tmp_p[j] <= 57)){
					parity_num = (parity_num * 10) + (tmp_p[j] - 48);
					j++;
				}
			} else if (wcsncmp(tmp_p, L"r", 1) == 0){
				j = 1;
				while ((j < 1 + 5) && (tmp_p[j] >= 48) && (tmp_p[j] <= 57)){
					switch_r = (switch_r * 10) + (tmp_p[j] - 48);
					j++;
				}
			} else if (wcsncmp(tmp_p, L"p", 1) == 0){
				j = 1;
				first_vol = 0;
				while ((j < 1 + 4) && (tmp_p[j] >= 48) && (tmp_p[j] <= 57)){
					first_vol = (first_vol * 10) + (tmp_p[j] - 48);
					j++;
				}
				if (first_vol < 1)
					first_vol = 1;
				if (first_vol > 99)
					first_vol = 99;
			} else if (wcsncmp(tmp_p, L"m", 1) == 0){
				if ((tmp_p[1] >= 48) && (tmp_p[1] <= 55))	// 0～7 の範囲
					memory_use = tmp_p[1] - 48;
			} else if (wcsncmp(tmp_p, L"vs", 2) == 0){
				recent_data = 0;
				j = 2;
				while ((j < 2 + 2) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){
					recent_data = (recent_data * 10) + (tmp_p[j] - '0');
					j++;
				}
				if ((recent_data == 8) || (recent_data > 15))
					recent_data = 0;

			// オプション (文字列)
			} else if (wcsncmp(tmp_p, L"c", 1) == 0){
				tmp_p++;
				if (wcslen(tmp_p) >= COMMENT_LEN){
					printf("comment is too long\n");
					return 1;
				}
				wcscpy(par_comment, tmp_p);
				//printf("comment = %S\n", tmp_p);
			} else if (wcsncmp(tmp_p, L"vd", 2) == 0){
				tmp_p += 2;
				j = copy_path_prefix(ini_path, MAX_LEN - INI_NAME_LEN - 1, tmp_p, NULL);
				if (j == 0){
					printf("save-directory is invalid\n");
					return 1;
				}
				if (ini_path[j - 1] != '\\'){	// 末尾が「\」でなければ付けておく
					ini_path[j    ] = '\\';
					ini_path[j + 1] = 0;
				}
				//printf("Save Directory : %S\n", ini_path);
				j = GetFileAttributes(ini_path);
				if ((j == INVALID_FILE_ATTRIBUTES) || !(j & FILE_ATTRIBUTE_DIRECTORY)){
					printf("save-directory is invalid\n");
					return 1;
				}
			} else if (wcsncmp(tmp_p, L"d", 1) == 0){
				tmp_p++;
				j = copy_path_prefix(base_dir, MAX_LEN - 2, tmp_p, NULL);	// 末尾に追加される分の余裕を見ておく
				if (j == 0){
					printf("base-directory is invalid\n");
					return 1;
				}
				if (base_dir[j - 1] != '\\'){	// 末尾が「\」でなければ付けておく
					base_dir[j    ] = '\\';
					base_dir[j + 1] = 0;
				}
				j = GetFileAttributes(base_dir);
				if ((j == INVALID_FILE_ATTRIBUTES) || !(j & FILE_ATTRIBUTE_DIRECTORY)){
					printf("base-directory is invalid\n");
					return 1;
				}

			} else {	// 未対応のオプション
				printf_cp("invalid option, %s\n", tmp_p - 1);
				return 1;
			}
			continue;
		}
		// オプション以外ならリカバリ・ファイル
		j = copy_path_prefix(recovery_file, MAX_LEN - 4, tmp_p, NULL);	// 「.par」が追加されるかも
		if (j == 0){
			printf("PAR filename is invalid\n");
			return 1;
		}
		if (argv[1][0] == 'c'){ // 作成なら
			// 指定されたファイル名が適切か調べる
			if (sanitize_filename(offset_file_name(recovery_file)) != 0){
				printf("PAR filename is invalid\n");
				return 1;
			}
			// 拡張子が「.par」以外なら標準の拡張子を追加する
			j = wcslen(recovery_file);	// 浄化でファイル名が短縮されるかもしれない
			if (_wcsicmp(recovery_file + (j - 4), L".par"))
				wcscpy(recovery_file + j, L".par");
		} else { // 検査や修復なら
			if (_wcsicmp(recovery_file + (j - 4), L".par")){
				// 拡張子が「.par」以外でも「.p??」ならよい
				if ((recovery_file[j - 4] != '.') || 
						((recovery_file[j - 3] != 'p') && (recovery_file[j - 3] != 'P')) || 
						(recovery_file[j - 2] < 48) || (recovery_file[j - 2] > 57) || 
						(recovery_file[j - 1] < 48) || (recovery_file[j - 1] > 57) ){
				    wcscpy(recovery_file + j, L".par");
				}
			}
			j = GetFileAttributes(recovery_file);
			if ((j == INVALID_FILE_ATTRIBUTES) || (j & FILE_ATTRIBUTE_DIRECTORY)){
				wchar_t search_path[MAX_LEN];
				int name_len, dir_len, path_len, find_flag = 0;
				HANDLE hFind;
				WIN32_FIND_DATA FindData;
				path_len = wcslen(recovery_file);
				if (wcspbrk(offset_file_name(recovery_file), L"*?") != NULL){
					// 「*」や「?」で検索する場合は指定された拡張子を優先する
					hFind = FindFirstFile(recovery_file, &FindData);
					if (hFind != INVALID_HANDLE_VALUE){
						get_base_dir(recovery_file, search_path);
						dir_len = wcslen(search_path);
						do {
							//printf("file name = %S\n", FindData.cFileName);
							name_len = wcslen(FindData.cFileName);
							if ((dir_len + name_len < MAX_LEN) &&	// ファイル名が長すぎない
									(_wcsicmp(FindData.cFileName + (name_len - 4), recovery_file + (path_len - 4)) == 0)){
								find_flag = 1;
								break;	// 見つけたファイル名で問題なし
							}
						} while (FindNextFile(hFind, &FindData));	// 次のファイルを検索する
						FindClose(hFind);
					}
				}
				if (find_flag == 0){	// リカバリ・ファイルの拡張子を「.p??」にして検索する
					wcscpy(search_path, recovery_file);
					search_path[path_len - 2] = '?';
					search_path[path_len - 1] = '?';
					hFind = FindFirstFile(search_path, &FindData);
					if (hFind != INVALID_HANDLE_VALUE){
						get_base_dir(recovery_file, search_path);
						dir_len = wcslen(search_path);
						do {
							//printf("file name = %S\n", FindData.cFileName);
							name_len = wcslen(FindData.cFileName);
							if ((dir_len + name_len < MAX_LEN) &&	// ファイル名が長すぎない
									 (FindData.cFileName[name_len - 4] == '.') &&
									((FindData.cFileName[name_len - 3] == 'p') || (FindData.cFileName[name_len - 3] == 'P')) &&
									 (FindData.cFileName[name_len - 2] >= '0') && (FindData.cFileName[name_len - 2] <= '9') &&
									 (FindData.cFileName[name_len - 1] >= '0') && (FindData.cFileName[name_len - 1] <= '9')){
								find_flag = 1;
								break;	// 見つけたファイル名で問題なし
							}
						} while (FindNextFile(hFind, &FindData));	// 次のファイルを検索する
						FindClose(hFind);
					}
				}
				if (find_flag == 0){
					printf("valid file is not found\n");
					return 1;
				}
				wcscpy(recovery_file + dir_len, FindData.cFileName);
			}
		}
		break;
	}
	//printf("m = %d, f = %d, p = %d, n = %d\n", switch_b, switch_f, switch_p, parity_num);
	if (recovery_file[0] == 0){	// リカバリ・ファイルが指定されてないなら
		printf("PAR file is not specified\n");
		return 1;
	}

	// input file の位置が指定されて無くて、
	// 最初のソース・ファイル指定に絶対パスが含まれてるなら、それを使う
	if (argv[1][0] == 'c'){
		if ((base_dir[0] == 0) && (switch_f == 0) && (i + 1 < argc)){
			tmp_p = argv[i + 1];
			if (is_full_path(tmp_p) != 0){	// 絶対パスなら
				if (copy_path_prefix(file_path, MAX_LEN - 2, tmp_p, NULL) == 0){
					printf("base-directory is invalid\n");
					return 1;
				}
				get_base_dir(file_path, base_dir);	// 最初のソース・ファイルの位置にする
				if (len_without_prefix(base_dir) == 0)	// サブ・ディレクトリが無ければ
					base_dir[0] = 0;
			}
		}
	}
	if (base_dir[0] == 0)	// input file の位置が指定されてないなら
		get_base_dir(recovery_file, base_dir);	// リカバリ・ファイルの位置にする

	// 環境の表示
	print_environment();

	switch (argv[1][0]){
	case 'c':
		// リカバリ・ファイル作成ならソース・ファイルのリストがいる
		i++;
		if (i >= argc){
			printf("input file is not specified\n");
			return 1;
		}
		if (switch_f){	// ファイル・リストの読み込み
			list_buf = read_list(argv[i], &file_num, &list_len, &source_num, &block_size, switch_f);
			if (list_buf == NULL)
				return 1;
		} else {	// 入力ファイルの指定
			int base_len, list_max;
			unsigned int filter = get_show_hidden();
			base_len = wcslen(base_dir);
			list_max = 0;
			list_buf = NULL;
			for (; i < argc; i++){
				// 絶対パスや、親パスへの移動を含むパスは危険なのでファイル名だけにする
				tmp_p = argv[i];
				j = copy_path_prefix(file_path, MAX_LEN - ADD_LEN, tmp_p, base_dir);	// 絶対パスにしてから比較する
				if (j == 0){
					free(list_buf);
					printf_cp("filename is invalid, %s\n", tmp_p);
					return 1;
				}
				if ((j <= base_len) || (_wcsnicmp(base_dir, file_path, base_len) != 0)){	// 基準ディレクトリ外なら
					free(list_buf);
					printf_cp("out of base-directory, %s\n", tmp_p);
					return 1;
				}
				if (wcschr(file_path + base_len, '\\') != NULL){
					free(list_buf);
					printf_cp("sub-directory is not supported, %s\n", file_path + base_len);
					return 1;
				}
				//printf("%d = %S\nsearch = %S\n", i, argv[i], file_path);
				// 「*」や「?」で検索しない場合、ファイルが見つからなければエラーにする
				j = -1;
				if (wcspbrk(file_path + base_len, L"*?") == NULL)
					j = file_num;
				// ファイルを検索する
				list_buf = search_files(list_buf, file_path, base_len, j, &file_num, &list_max, &list_len, &source_num, &block_size, filter);
				if (list_buf == NULL)
					return 1;
				if ((j != -1) && (j == file_num)){	// ファイルが見つかったか確かめる
					free(list_buf);
					printf_cp("input file is not found, %s\n", file_path + base_len);
					return 1;
				}
			}
		}
		if (list_len == 0){
			if (list_buf)
				free(list_buf);
			printf("input file is not found\n");
			return 1;
		}
		if (check_recovery_match(file_num, list_buf, list_len, switch_p & 2)){
			free(list_buf);
			return 1;
		}

		// ファイル・リストの内容を並び替える (リストの末尾は null 1個にすること)
		sort_list(list_buf, list_len);
		//printf("file_num = %d, source_num = %d, block_size = %I64d\n", file_num, source_num, block_size);

		if ((parity_num == 0) && (switch_r > 0)){
			parity_num = (source_num * switch_r) / 100;
			if (parity_num == 0)
				parity_num = 1;
		}
		printf("Input File count: %d\n", file_num);
		printf("Data File count : %d\n", source_num);
		printf("Max file size\t: %I64d\n", block_size);
		printf("Parity Volume count\t: %d\n", parity_num);
		if (first_vol > 1)
			printf("Parity Volume start\t: %d\n", first_vol);
		if (source_num != 0){
			i = 100 * parity_num / source_num;
		} else {
			i = 0;
		}
		printf("Redundancy rate : %d%%\n", i);
		if (source_num + parity_num + first_vol - 1 > 256){
			free(list_buf);
			printf("too many total volumes %d\n", source_num + parity_num + first_vol - 1);
			return 1;
		}
		if (parity_num + first_vol - 1 > 99){
			free(list_buf);
			printf("too many parity volumes %d\n", parity_num + first_vol - 1);
			return 1;
		}
		i = par1_create(switch_p & 2, source_num, block_size, parity_num, first_vol,
				file_num, list_buf, list_len, par_comment);
		break;
	case 'v':
	case 'r':
		// 検査結果ファイルの位置が指定されてないなら
		if ((recent_data != 0) && (ini_path[0] == 0)){
			// 実行ファイルのディレクトリにする
			j = GetModuleFileName(NULL, ini_path, MAX_LEN);
			if ((j == 0) || (j >= MAX_LEN)){
				printf("GetModuleFileName\n");
				return 1;
			}
			while (j > 0){
				if (ini_path[j - 1] == '\\'){
					ini_path[j] = 0;
					break;
				}
				j--;
			}
			if (j >= MAX_LEN - INI_NAME_LEN - 1){
				printf("save-directory is invalid\n");
				return 1;
			}
		}
		if (argv[1][0] == 'v'){
			i = par1_verify(switch_b, switch_p & 1, par_comment);
		} else {
			i = par1_repair(switch_b, switch_p & 1, par_comment);
		}
		break;
	case 'l':
		i = par1_list(switch_h, par_comment);
		break;
	}

	//printf("ExitCode: 0x%02X\n", i);
	return i;
}

