// common2.c // Copyright : 2023-03-14 Yutaka Sawada // License : GPL #ifndef _UNICODE #define _UNICODE #endif #ifndef UNICODE #define UNICODE #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 // Windows Vista or later #endif #include #include #include #include #include #include #include "common2.h" // グローバル変数 wchar_t recovery_file[MAX_LEN]; // リカバリ・ファイルのパス wchar_t base_dir[MAX_LEN]; // ソース・ファイルの基準ディレクトリ wchar_t ini_path[MAX_LEN]; // 検査結果ファイルのパス int base_len; // ソース・ファイルの基準ディレクトリの長さ int recovery_limit; // 作成時はリカバリ・ファイルのサイズ制限 int first_num; // 作成時は最初のパリティ・ブロック番号、検査時は初めて見つけた数 int file_num; // ソース・ファイルの数 int entity_num; // 実体のあるファイルの数 (recovery set に含まれるファイル数) int recovery_num; // リカバリ・ファイルの数 int source_num; // ソース・ブロックの数 int parity_num; // パリティ・ブロックの数 unsigned int block_size; // ブロック・サイズ unsigned int split_size; // 分割サイズ __int64 total_file_size; // 合計ファイル・サイズ int switch_v; // 検査レベル 0=詳細検査, +1=簡易検査, +2=追加検査, 4=順列検査 // +8=子フォルダを無視, +16=検出後に保存 int switch_b; // バックアップを作るか // 作成時はソース・ブロックの割合と基準サイズ // 修復時 +1=別名にして残す, +2=ゴミ箱に入れる, +4=修復できなくても再構築 // +16=完全ならPAR2削除 // 可変長サイズの領域にファイル名を記録する wchar_t *list_buf; // ソース・ファイルのリスト int list_len; // ファイル・リストの文字数 int list_max; // ファイル・リストの最大文字数 wchar_t *recv_buf; // リカバリ・ファイルのリスト int recv_len; // ファイル・リストの文字数 wchar_t *recv2_buf; // 有効なパケットを含むリカバリ・ファイルのリスト int recv2_len; // ファイル・リストの文字数 wchar_t *list2_buf; // 指定された検査対象のファイル名のリスト int list2_len; int list2_max; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ unsigned int cp_output; // Console Output Code Page // 指定された Code Page から UTF-16 に変換する int cp_to_utf16(char *in, wchar_t *out, unsigned int cp) { int err = 0; unsigned int dwFlags = MB_ERR_INVALID_CHARS; // 不適切な文字列を警告する if (cp == CP_UTF8) dwFlags = 0; if (MultiByteToWideChar(cp, dwFlags, in, -1, out, MAX_LEN) == 0){ err = GetLastError(); if ((dwFlags != 0) && (err == ERROR_INVALID_FLAGS)){ //printf("MultiByteToWideChar, ERROR_INVALID_FLAGS\n"); if (MultiByteToWideChar(cp, 0, in, -1, out, MAX_LEN) != 0) return 0; err = GetLastError(); } out[0] = 0; return err; } return 0; } // Windown OS の UTF-16 から指定された Code Page に変換する int utf16_to_cp(wchar_t *in, char *out, unsigned int cp) { unsigned int dwFlags = WC_NO_BEST_FIT_CHARS; // 似た文字への自動変換を行わない if (cp == CP_UTF8) dwFlags = 0; if (WideCharToMultiByte(cp, dwFlags, in, -1, out, MAX_LEN * 3, NULL, NULL) == 0){ if ((dwFlags != 0) && (GetLastError() == ERROR_INVALID_FLAGS)){ //printf("WideCharToMultiByte, ERROR_INVALID_FLAGS\n"); if (WideCharToMultiByte(cp, 0, in, -1, out, MAX_LEN * 3, NULL, NULL) != 0) return 0; } strcpy(out, "cannot encode"); return 1; } return 0; } // Windows OS の UTF-8 から UTF-16 に変換する void utf8_to_utf16(char *in, wchar_t *out) { // UTF-16 <-> UTF-8 間では文字数以外のエラーは発生しない if (MultiByteToWideChar(CP_UTF8, 0, in, -1, out, MAX_LEN) == 0) wcscpy(out, L"too long"); } // Windown OS の UTF-16 から UTF-8 に変換する void utf16_to_utf8(wchar_t *in, char *out) { // UTF-16 <-> UTF-8 間では文字数以外のエラーは発生しない WideCharToMultiByte(CP_UTF8, 0, in, -1, out, MAX_LEN * 3, NULL, NULL); } // 文字列が UTF-8 かどうかを判定する (0 = maybe UTF-8) int check_utf8(unsigned char *text) { unsigned char c1; int tail_len = 0; // UTF8-tail が何バイト続くか while (*text != 0){ c1 = *text; // 禁止 0xC0~0xC1,0xF5~0xFF if ((c1 == 0xC0) || (c1 == 0xC1) || (c1 >= 0xF5)) return 1; // 禁止文字 if (tail_len == 0){ // 第1バイトなら // 1バイト文字 if (c1 <= 0x7F){ tail_len = 0; // 2バイト文字の第1バイト } else if ((c1 >= 0xC2) && (c1 <= 0xDF)){ tail_len = 1; // 3バイト文字の第1バイト } else if ((c1 >= 0xE0) && (c1 <= 0xEF)){ tail_len = 2; // 4バイト文字の第1バイト } else if (c1 >= 0xF0){ tail_len = 3; } else { return 2; // 第1バイトとして不適当な文字 } } else { // 第2バイト以後なら if ((c1 >= 0x80) && (c1 <= 0xBF)){ tail_len--; } else { return 2; // 第2バイト以後として不適当な文字 } } text++; // 次の文字へ } return 0; } // ファイル・パスから、先頭にある "\\?\" を省いて、指定された Code Page に変換する int path_to_cp(wchar_t *path, char *out, unsigned int cp) { unsigned int dwFlags = WC_NO_BEST_FIT_CHARS; // 似た文字への自動変換を行わない if (cp == CP_UTF8) dwFlags = 0; if (wcsncmp(path, L"\\\\?\\", 4) == 0){ // "\\?\" を省く path += 4; if (wcsncmp(path, L"UNC\\", 4) == 0){ // "\\?\UNC" を省いて "\" を追加する path += 3; *out = '\\'; out += 1; } } if (WideCharToMultiByte(cp, dwFlags, path, -1, out, MAX_LEN * 3, NULL, NULL) == 0){ if ((dwFlags != 0) && (GetLastError() == ERROR_INVALID_FLAGS)){ //printf("WideCharToMultiByte, ERROR_INVALID_FLAGS\n"); if (WideCharToMultiByte(cp, 0, path, -1, out, MAX_LEN * 3, NULL, NULL) != 0) return 0; } strcpy(out, "cannot encode"); return 1; } return 0; } // UTF-16 のファイル・パスを画面出力用の Code Page を使って表示する void printf_cp(unsigned char *format, wchar_t *path) { unsigned char buf[MAX_LEN * 3]; path_to_cp(path, buf, cp_output); printf(format, buf); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // ファイルの offset バイト目から size バイトのデータを buf に読み込む int file_read_data( HANDLE hFileRead, __int64 offset, unsigned char *buf, unsigned int size) { unsigned int rv; // ファイルの位置を offsetバイト目にする if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)){ print_win32_err(); return 1; } // size バイトを読み込む if (!ReadFile(hFileRead, buf, size, &rv, NULL)){ print_win32_err(); return 1; } if (size != rv) return 1; // 指定サイズを読み込めなかったらエラーになる return 0; } // ファイルの offset バイト目に size バイトのデータを buf から書き込む int file_write_data( HANDLE hFileWrite, __int64 offset, unsigned char *buf, unsigned int size) { unsigned int rv; // ファイルの位置を offsetバイト目にする if (!SetFilePointerEx(hFileWrite, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)){ print_win32_err(); return 1; } // size バイトを書き込む if (!WriteFile(hFileWrite, buf, size, &rv, NULL)){ print_win32_err(); return 1; } return 0; } // ファイルの offset バイト目に size バイトの指定値を書き込む int file_fill_data( HANDLE hFileWrite, __int64 offset, unsigned char value, unsigned int size) { unsigned char buf[IO_SIZE]; unsigned int rv, len; // ファイルの位置を offsetバイト目にする if (!SetFilePointerEx(hFileWrite, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)){ print_win32_err(); return 1; } // 指定された値で埋める memset(buf, value, IO_SIZE); while (size){ len = IO_SIZE; if (size < IO_SIZE) len = size; size -= len; if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ print_win32_err(); return 1; } } return 0; } // ファイルの指定バイト目から size バイトのデータを別のファイルに書き込む int file_copy_data( HANDLE hFileRead, __int64 offset_read, HANDLE hFileWrite, __int64 offset_write, unsigned int size) { unsigned char buf[IO_SIZE]; unsigned int rv, len; // ファイルの位置を offset_read バイト目にする if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&offset_read), NULL, FILE_BEGIN)){ print_win32_err(); return 1; } // ファイルの位置を offset_write バイト目にする if (!SetFilePointerEx(hFileWrite, *((PLARGE_INTEGER)&offset_write), NULL, FILE_BEGIN)){ print_win32_err(); return 1; } // コピーする while (size){ len = IO_SIZE; if (size < IO_SIZE) len = size; size -= len; if (!ReadFile(hFileRead, buf, len, &rv, NULL)){ print_win32_err(); return 1; // 指定サイズを読み込めなくてもエラーにしない } if (rv < len){ memset(buf + rv, 0, len - rv); // 足りなかった分は 0 で埋める size = 0; } if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ print_win32_err(); return 1; } } return 0; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // 修復中のテンポラリ・ファイルの名前を作る // 末尾に _par.tmp を追加する (ADD_LEN 以下の文字数) void get_temp_name( wchar_t *file_path, // ファイル・パス wchar_t *temp_path) // テンポラリ・ファイルのパス { wcscpy(temp_path, file_path); wcscat(temp_path, L"_par.tmp"); // 末尾に追加する } // 作業用のゼロで埋められたテンポラリ・ファイルを作成する int create_temp_file( wchar_t *file_path, // ファイルのパス __int64 file_size) // ファイルのサイズ { HANDLE hFile; hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE){ if (GetLastError() == ERROR_PATH_NOT_FOUND){ // Path not found (3) make_dir(file_path); // 途中のフォルダが存在しないのなら作成する hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } if (hFile == INVALID_HANDLE_VALUE){ print_win32_err(); return 1; } } // 本来のサイズにする if (!SetFilePointerEx(hFile, *((PLARGE_INTEGER)&file_size), NULL, FILE_BEGIN)){ print_win32_err(); CloseHandle(hFile); return 1; } if (!SetEndOfFile(hFile)){ print_win32_err(); CloseHandle(hFile); return 1; } CloseHandle(hFile); return 0; } #define FILE_WAIT_SEC 200 // ファイルアクセスの待ち時間 ms #define FILE_WAIT_NUM 5 // 作業用のソース・ファイルを開く HANDLE handle_temp_file( wchar_t *file_name, // ソース・ファイル名 wchar_t *file_path) // 作業用、基準ディレクトリが入ってる { int rv, retry_time = FILE_WAIT_NUM; HANDLE hFile; get_temp_name(file_name, file_path + base_len); // 作業ファイル内でデータをコピーできるよう、SHARE_READ にする hFile = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); while (hFile == INVALID_HANDLE_VALUE){ rv = GetLastError(); if ((rv == 32) || // ERROR_SHARING_VIOLATION (rv == 33)){ // ERROR_LOCK_VIOLATION Sleep(FILE_WAIT_SEC); // 少し待ってから再挑戦する hFile = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); } else { break; } retry_time--; if (retry_time == 0) break; } if (hFile == INVALID_HANDLE_VALUE){ print_win32_err(); printf_cp("cannot open file, %s\n", file_path); } return hFile; } // 上書き用のソース・ファイルを開く HANDLE handle_write_file( wchar_t *file_name, // ソース・ファイル名 wchar_t *file_path, // 作業用、基準ディレクトリが入ってる __int64 file_size) // 本来のファイルサイズ { __int64 now_size; HANDLE hFile; wcscpy(file_path + base_len, file_name); // ソースファイル内でデータをコピーできるよう、SHARE_READ にする // 既存ファイルの内容と属性を維持するため OPEN_ALWAYS にする hFile = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL); if (hFile == INVALID_HANDLE_VALUE){ print_win32_err(); } else { // 現在のファイルサイズを取得する if (GetFileSizeEx(hFile, (PLARGE_INTEGER)&now_size) == 0){ print_win32_err(); CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; // 大きすぎる場合は本来のサイズにする(小さい場合は自動的に埋められる) } else if (now_size > file_size){ if (SetFilePointerEx(hFile, *((PLARGE_INTEGER)&file_size), NULL, FILE_BEGIN) == 0){ print_win32_err(); CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } else if (SetEndOfFile(hFile) == 0){ print_win32_err(); CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } } } if (hFile == INVALID_HANDLE_VALUE) printf_cp("cannot open file, %s\n", file_path); return hFile; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // ファイル・パスがファイル・リスト上に既に存在するか調べる int search_file_path( wchar_t *list, // ファイル・リスト int total_len, // ファイル・リストの文字数 wchar_t *search_file) // 検索するファイルのパス { int off = 0; while (off < total_len){ if (_wcsicmp(list + off, search_file) == 0) return 1; // 次のファイル名の位置へ //off += (wcslen(list + off) + 1); while (list[off] != 0) off++; off++; } return 0; } // ファイル・リストの内容を並び替える void sort_list( wchar_t *list, // ファイル・リスト int total_len) // ファイル・リストの文字数 { wchar_t *work_buf; int off1 = 0, off2, off3, work_off = 0; // 作業バッファーを確保する work_buf = (wchar_t *)calloc(total_len, 2); if (work_buf == NULL) return; // 並べ替え失敗 while (off1 < total_len){ if (list[off1] == 0){ break; } else if (list[off1] == '*'){ // 既にコピー済なら //off1 += (wcslen(list + off1) + 1); while (list[off1] != 0) off1++; off1++; continue; } off3 = off1; //off2 = off3 + wcslen(list + off3) + 1; // 次の項目 off2 = off3; while (list[off2] != 0) off2++; off2++; while (off2 < total_len){ //printf("%S (%d), %S (%d)\n", list + off3, off3, list + off2, off2); if (list[off2] != '*'){ // まだなら項目を比較する //if (wcscmp(list + off3, list + off2) > 0) // QuickPar はこちらの順序 //if (CompareString(LOCALE_USER_DEFAULT, 0, list + off3, -1, list + off2, -1) > 2) // Windows 7 以降では数値を認識できる SORT_DIGITSASNUMBERS = 0x00000008 if (CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0x00000008, list + off3, -1, list + off2, -1, NULL, NULL, 0) > 2) off3 = off2; } //off2 += (wcslen(list + off2) + 1); // 次の項目 while (list[off2] != 0) off2++; off2++; } // 順番にコピーしていく //printf("get %S (%d)\n", list + off3, off3); wcscpy(work_buf + work_off, list + off3); work_off += ((int)wcslen(work_buf + work_off) + 1); list[off3] = '*'; // コピーした印 if (off3 == off1){ //off1 += (wcslen(list + off1) + 1); while (list[off1] != 0) off1++; off1++; } } // 作業バッファーから戻す memcpy(list, work_buf, total_len * 2); free(work_buf); } // ソース・ファイルのリストに新しいファイル名を追加する int add_file_path(wchar_t *filename) // 追加するファイル名 { wchar_t *tmp_p; int len; len = (int)wcslen(filename); if (list_len + len >= list_max){ // 領域が足りなくなるなら拡張する list_max += ALLOC_LEN; tmp_p = (wchar_t *)realloc(list_buf, list_max * 2); if (tmp_p == NULL){ return 1; } else { list_buf = tmp_p; } } wcscpy(list_buf + list_len, filename); list_len += len + 1; return 0; } // ファイル・リストから指定されたファイル・パスを取り除く // 減らした後のファイル・リストの文字数を返す int remove_file_path( wchar_t *list, // ファイル・リスト int total_len, // ファイル・リストの文字数 int file_off) // 取り除くファイル・パスの位置 { int off, len; if (file_off > total_len) return total_len; len = (int)wcslen(list + file_off) + 1; // 末尾のNULLを含める if (len < total_len){ for (off = file_off; off < total_len - len; off++) list[off] = list[off + len]; for (off = total_len - len; off < total_len; off++) list[off] = 0; return total_len - len; } else { for (off = 0; off < total_len; off++) list[off] = 0; return 0; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // ファイル・パスからファイル名の位置を戻す wchar_t * offset_file_name(wchar_t *file_path) { int i; for (i = (int)wcslen(file_path) - 2; i >= 0; i--){ if ((file_path[i] == '\\') || (file_path[i] == '/')) break; } i++; return file_path + i; } // ファイル・パスからファイル名だけ取り出す void get_file_name( wchar_t *file_path, // ファイル・パス wchar_t *file_name) // ファイル名 { int i, len, find = -1; len = (int)wcslen(file_path); for (i = len - 2; i >= 0; i--){ if ((file_path[i] == '\\') || (file_path[i] == '/')){ find = i; break; } } find++; for (i = find; i < len + 1; i++){ file_name[i - find] = file_path[i]; } } // ファイル・パスからディレクトリだけ取り出す、末尾は「\」か「/」 void get_base_dir( wchar_t *file_path, // ファイル・パス wchar_t *base_path) // ディレクトリ { int i, len, find = 0; len = (int)wcslen(file_path); if (len <= 1){ base_path[0] = 0; return; } // 最初から末尾が「\」か「/」なら if ((file_path[len - 1] == '\\') || (file_path[len - 1] == '/')) len--; // それを取り除いてひとつ親のディレクトリを返す for (i = len - 1; i >= 0; i--){ if (find == 0){ if ((file_path[i] == '\\') || (file_path[i] == '/')){ find = 1; base_path[i] = file_path[i]; base_path[i + 1] = 0; } } else { base_path[i] = file_path[i]; } } if (find == 0) base_path[0] = 0; } // ディレクトリ記号の「\」を「/」に置換する void unix_directory(wchar_t *path) { int i = 0; while (path[i] != 0){ if (path[i] == '\\') path[i] = '/'; i++; } } // 絶対パスかどうかを判定する int is_full_path(wchar_t *path) { if ((path[0] == 0) || (path[1] == 0)) return 0; // 2文字以下だと短すぎ // 通常のドライブ指定「c:\」の形式 if ((path[1] == ':') && ((path[2] == '\\') || (path[2] == '/'))) return 1; //「\\?\」が付くのは絶対パスだけ // ネットワークの UNC パスなら「\\\」 if (((path[0] == '\\') || (path[0] == '/')) && ((path[1] == '\\') || (path[1] == '/'))) return 1; return 0; } // ファイルのディレクトリ位置が同じかどうかを調べる // フォルダには対応してないことに注意! int compare_directory(wchar_t *path1, wchar_t *path2) { int len1, len2; // パスの長さ len1 = 0; while (path1[len1] != 0) len1++; // フォルダの位置を調べたい時は末尾の「\」を無視する // if (accept_folder != 0) // len1--; // サブ・ディレクトリの長さにする while ((len1 > 0) && (path1[len1] != '\\')) len1--; // パスの長さ len2 = 0; while (path2[len2] != 0) len2++; // フォルダの位置を調べたい時は末尾の「\」を無視する // if (accept_folder != 0) // len2--; // サブ・ディレクトリの長さにする while ((len2 > 0) && (path2[len2] != '\\')) len2--; // サブ・ディレクトリの長さを比較する if (len1 > len2) return 1; // path1 が path2 より大きい if (len1 < len2) return -1; // path1 が path2 より小さい // ディレクトリの長さが同じなら return _wcsnicmp(path1, path2, len1); } // ワイルドカードを含む文字列をコピーする // 出力文字数を返す (null文字を含む)、不正なパスなら 0 を返す int copy_wild(wchar_t *dst, wchar_t *src) { wchar_t s; int len = 0; while ((src[0] == '\\') || (src[0] == '/')) // 先頭の「\」は無視する src++; do { s = src[0]; if (s == '/'){ // ディレクトリ記号を統一する s = '\\'; if ((src[1] == '\\') || (src[1] == '/')) return 0; } else if (s == '\\'){ // 「\\」はパスに含まれない if ((src[1] == '\\') || (src[1] == '/')) return 0; } else if ((s >= 'A') && (s <= 'Z')){ // 大文字を小文字に変換する s += 32; } else if ((s == '*') && (src[1] == '*')){ // 三個以上の「*」を一個にする while (src[2] == '*') src++; } else if ((s == '.') && (src[1] == '.')){ // 「..」はパスに含まれない return 0; } dst[len++] = s; src++; } while (s != 0); return len; // 末尾の null 文字を個数に含むことに注意 } // ワイルドカード('*', '?')を使ってユニコードのパスを比較する // 「*」と「?」はディレクトリ記号に一致しない // 「**」はディレクトリ記号に一致する int PathMatchWild( wchar_t *text, // 比較する文字列 wchar_t *wild) // ワイルドカード (小文字にすること) { wchar_t next_char, *wild_prev, *wild_dir, *text_dir; int stop_dir; next_char = 0; wild_prev = NULL; wild_dir = NULL; text_dir = text; stop_dir = 0; while (*text != 0){ //printf("%c, %c \n", *text, *wild); if (*wild == '*'){ wild++; if (*wild == '*'){ // 「**」 while (*wild == '*') // 二個より連続した「*」を無視する wild++; stop_dir = 0; wild_dir = wild; } else { // 「*」 stop_dir = 1; } if (*wild == 0){ // 「*」の次が無ければ一致とみなす if (stop_dir){ // ディレクトリ記号が存在すれば不一致 while (*text != 0){ if (*text == '\\'){ stop_dir = 2; break; } text++; } if (stop_dir == 2){ if (wild_dir != NULL){ stop_dir = 0; wild_prev = wild_dir; text = text_dir + 1; wild = wild_dir; next_char = *wild; //printf("\n agian last: %c, %c \n", *text, next_char); continue; } //printf("\n last * != include \\ \n"); return 0; } } return 1; } else { next_char = *wild; // 「*」の次の文字 wild_prev = wild; // 最後の「*」の位置 //printf("next = %c, stop_dir = %d\n", next_char, stop_dir); } } if ((*text == '\\') && (stop_dir)) // ディレクトリ記号が存在するなら再比較をしない wild_prev = NULL; if (next_char != 0){ // 「*」の後なら if ((next_char == towlower(*text)) || ((*text != '\\') && (next_char == '?'))){ wild++; next_char = 0; text_dir = text; } if ((*text == '\\') && (stop_dir)) // ディレクトリ記号が存在するならワイルドカードを無効にする next_char = 0; text++; } else if ((*wild == towlower(*text)) || ((*text != '\\') && (*wild == '?'))){ // 同じ文字なら wild++; text++; } else { // 違う文字なら if (wild_prev != NULL){ // 直前のワイルドカードから比較しなおす wild = wild_prev; next_char = *wild; //printf("\n agian: %c, %c \n", *text, next_char); } else if ((stop_dir != 0) && (wild_dir != NULL)){ stop_dir = 0; wild_prev = wild_dir; text = text_dir + 1; wild = wild_dir; next_char = *wild; //printf("\n agian dir: %c, %c \n", *text, next_char); } else{ //printf("\n diff: %c, %c \n", *text, *wild); return 0; // それ以上一致しない } } } while (*wild == '*') // 残りの「*」を無視する wild++; if (*wild == 0) // ワイルドカードも終端なら一致とみなす return 1; return 0; } /* int PathMatchWild1( wchar_t *text, // 比較する文字列 wchar_t *wild) // ワイルドカード (小文字にすること) { wchar_t next_char, *wild_prev; next_char = 0; wild_prev = NULL; while (*text != 0){ //printf("%c, %c \n", *text, *wild); if (*wild == '*'){ wild++; while (*wild == '*') // 連続した「*」を無視する wild++; if (*wild == 0){ // 「*」の次が無ければ一致とみなす return 1; } else { next_char = *wild; // 「*」の次の文字 wild_prev = wild; // 最後の「*」の位置 } } if (next_char != 0){ // 「*」の後なら if ((next_char == towlower(*text)) || (next_char == '?')){ wild++; next_char = 0; } text++; } else if ((*wild == towlower(*text)) || (*wild == '?')){ // 同じ文字なら wild++; text++; } else { // 違う文字なら if (wild_prev != NULL){ // 直前のワイルドカードから比較しなおす wild = wild_prev; next_char = *wild; //printf("\n agian: %c, %c \n", *text, next_char); } else{ return 0; // それ以上一致しない } } } while (*wild == '*') // 残りの「*」を無視する wild++; if (*wild == 0) // ワイルドカードも終端なら一致とみなす return 1; return 0; } */ // ファイルのパスを除外リストと比較する int exclude_path(wchar_t *path) { int off = 0, deny = 0; deny = list2_max; while (off < list2_len){ if (list2_buf[off++] == '+'){ // allow if ((deny != 0) && (PathMatchWild(path, list2_buf + off) != 0)){ // printf_cp("allow: \"%s\"", path); // printf_cp(" : \"%s\"\n", list2_buf + off); deny = 0; } } else { // deny if (PathMatchWild(path, list2_buf + off) != 0){ // printf_cp("deny : \"%s\"", path); // printf_cp(" : \"%s\"\n", list2_buf + off); return 1; } } off += (int)wcslen(list2_buf + off) + 1; } // if ((list2_max != 0) && (deny != 0)) // printf_cp("not : \"%s\"\n", path); return deny; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define PREFIX_LEN 4 // 「\\?\」の長さ // 相対パスを絶対パスに変換し、パスの先頭に "\\?\" を追加する // 戻り値 : 0=エラー, 5~=新しいパスの長さ int copy_path_prefix( wchar_t *new_path, // 新しいパス int max_len, // 新しいパスの最大長さ (末尾の null文字も含む) wchar_t *src_path, // 元のパス (相対パスでもよい) wchar_t *dir_path) // 相対パスの場合に基準となるディレクトリ (NULL ならカレント・ディレクトリ) { wchar_t tmp_path[MAX_LEN], *src_name, *new_name; int len; // 不正なファイル名を拒否する (検索記号は許可する) if (src_path[0] == 0) return 0; if (wcspbrk(src_path, L"\"<>|")) // WinOS ではファイル名に「"<>|」の文字は使えない return 0; // 不適切な名前のファイルは、変換時に浄化されることがある src_name = offset_file_name(src_path); //wprintf(L"src_name = \"%s\"\n", src_name); // 相対パスに対して基準となるディレクトリが指定されてるなら if ((dir_path != NULL) && (is_full_path(src_path) == 0)){ // そのまま基準ディレクトリを連結して絶対パスにする len = (int)wcslen(dir_path); if (len + (int)wcslen(src_path) >= max_len) return 0; wcscpy(tmp_path, dir_path); // dir_path の末尾は「\」にしておくこと wcscpy(tmp_path + len, src_path); if (GetFullPathName(tmp_path, max_len, new_path, &new_name) == 0) return 0; } else { // カレント・ディレクトリを使う // 相対パスから絶対パスに変換する (ディレクトリ記号も「\」に統一される) if (GetFullPathName(src_path, max_len, new_path, &new_name) == 0) return 0; } // 元のファイル名と比較して、浄化を取り消す if (new_name == NULL) new_name = offset_file_name(new_path); //wprintf(L"new_name = \"%s\"\n", new_name); if (_wcsicmp(src_name, new_name) != 0){ len = (int)wcslen(new_name); if ((_wcsnicmp(src_name, new_name, len) == 0) && ((src_name[len] == ' ') || (src_name[len] == '.'))){ len = (int)wcslen(src_name); if ((src_name[len - 1] == ' ') || (src_name[len - 1] == '.')){ // 末尾の「 」や「.」が取り除かれてるなら元に戻す wcscpy(new_name, src_name); } } } // 先頭に "\\?\" が存在しないなら、追加する if ((wcsncmp(new_path, L"\\\\?\\", PREFIX_LEN) != 0)){ len = (int)wcslen(new_path); if ((new_path[0] == '\\') && (new_path[1] == '\\')){ // UNC パスなら if (len + PREFIX_LEN + 2 >= max_len) return 0; // バッファー・サイズが足りなくて追加できない memmove(new_path + (PREFIX_LEN + 3), new_path + 1, len * 2); new_path[PREFIX_LEN ] = 'U'; new_path[PREFIX_LEN + 1] = 'N'; new_path[PREFIX_LEN + 2] = 'C'; } else { // 通常のドライブ記号で始まるパスなら if (len + PREFIX_LEN >= max_len) return 0; // バッファー・サイズが足りなくて追加できない memmove(new_path + PREFIX_LEN, new_path, (len + 1) * 2); } memcpy(new_path, L"\\\\?\\", PREFIX_LEN * 2); } // 8.3形式の短いファイル名を長いファイル名に変換する if (GetFileAttributes(new_path) == INVALID_FILE_ATTRIBUTES){ // 指定されたファイルが存在しない場合 (作成するPARファイルや「*?」で検索するソース・ファイル) for (len = PREFIX_LEN; new_path[len] != 0; len++){ if ((new_path[len] == '*') || (new_path[len] == '?')) break; // ワイルドカードを含むパス } // ワイルドカードより前のディレクトリ記号を探す for (; len >= 0; len--){ if (new_path[len] == '\\') break; } len++; wcscpy(tmp_path, new_path + len); // 存在しないファイル名を記録しておく new_path[len] = 0; // ファイル名を消す len = GetLongPathName(new_path, new_path, max_len); if (len == 0){ // フォルダーやファイルが存在しなくてもエラー終了しない //printf("GetLongPathName (not exist) : err = %d\n", GetLastError()); //print_win32_err(); wcscat(new_path, tmp_path); len = (int)wcslen(new_path); } else { // 退避させておいたファイル名を追加する len += (int)wcslen(tmp_path); if (len < max_len) wcscat(new_path, tmp_path); } } else { len = GetLongPathName(new_path, new_path, max_len); if (len == 0){ // 変換エラー //printf("GetLongPathName (exist) : err = %d\n", GetLastError()); return 0; } } if (len >= max_len) return 0; return len; } // ファイル・パスから、先頭にある "\\?\" を省いた長さを戻す int len_without_prefix(wchar_t *file_path) { // 最初から "\\?\" が無ければ if (wcsncmp(file_path, L"\\\\?\\", PREFIX_LEN) != 0) return (int)wcslen(file_path); // そのままの長さ // "\\?\UNC\\" ならネットワーク・パス if (wcsncmp(file_path + PREFIX_LEN, L"UNC\\", 4) == 0) return (int)wcslen(file_path + PREFIX_LEN + 2); // "\\?\UNC\" -> "\\" なので6文字減らす // "\\?\\" なら return (int)wcslen(file_path + PREFIX_LEN); // "\\?\" を省くので4文字減らす } // ファイル・パスから、先頭にある "\\?\" を省いてコピーする int copy_without_prefix( wchar_t *dst_path, // コピー先 (コピー元と同じアドレスでもよい) wchar_t *src_path) // コピー元のパス { int i, len; len = (int)wcslen(src_path); // 元の文字数 // 最初から "\\?\" が無ければ if (wcsncmp(src_path, L"\\\\?\\", PREFIX_LEN) != 0){ for (i = 0; i <= len; i++) dst_path[i] = src_path[i]; // そのままコピーする return len; } // "\\?\UNC\\" ならネットワーク・パス if (wcsncmp(src_path + PREFIX_LEN, L"UNC\\", 4) == 0){ len -= PREFIX_LEN + 2; // "\\?\UNC\" -> "\\" なので6文字減らす for (i = 1; i <= len; i++) dst_path[i] = src_path[PREFIX_LEN + 2 + i]; dst_path[0] = '\\'; // 先頭を "\\" に変えておく return len; } // "\\?\\" なら len -= PREFIX_LEN; // "\\?\UNC\" -> "\\" なので6文字減らす for (i = 0; i <= len; i++) dst_path[i] = src_path[PREFIX_LEN + i]; return len; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // 順番にサブ・ディレクトリを作成する int make_dir( wchar_t *file_path) // サブ・ディレクトリを含むファイル・パス { wchar_t dir_path[MAX_LEN]; unsigned int rv; get_base_dir(file_path, dir_path); // 親ディレクトリを取得する if (dir_path[0] == 0) return 0; // ディレクトリがないなら出る rv = (int)wcslen(dir_path); if ((dir_path[rv - 1] == '\\') || (dir_path[rv - 1] == '/')) // 末尾の「/」を取り除く dir_path[rv - 1] = 0; rv = GetFileAttributes(dir_path); if (rv == INVALID_FILE_ATTRIBUTES){ // フォルダがまだ存在しない場合 // 先に親フォルダを作成する if (rv = make_dir(dir_path)) return rv; if (!CreateDirectory(dir_path, NULL)) return 1; } else if ((rv & FILE_ATTRIBUTE_DIRECTORY) == 0){ // すでに同じ名前のファイルが存在する場合はエラーが発生する return 2; } return 0; } // ファイルを置き換える (ファイル名の修正、テンポラリー・ファイルからの書き戻しなど) int replace_file( wchar_t *dest_path, // 置き換える先のパス (移動先、修正されたファイル名) wchar_t *sorc_path) // 置き換える元のパス (移動元、現在のファイル名) { unsigned int rv, num, retry_time = FILE_WAIT_NUM; // 移動元のファイルが存在するかどうか num = GetFileAttributes(sorc_path); if (num == INVALID_FILE_ATTRIBUTES){ return 1; // 移動させるファイルが存在しない } else { if (num & FILE_ATTRIBUTE_DIRECTORY){ // 対象がフォルダなら末尾の「\」を取り除く rv = (unsigned int)wcslen(sorc_path); if (sorc_path[rv - 1] == '\\') sorc_path[rv - 1] = 0; rv = (unsigned int)wcslen(dest_path); if (dest_path[rv - 1] == '\\') dest_path[rv - 1] = 0; } if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、その属性を解除する if (SetFileAttributes(sorc_path, num ^ FILE_ATTRIBUTE_READONLY) == 0) return 2; // 移動元の読み取り専用属性を解除できなかった } } //printf_cp("dest_path = %s\n", dest_path); //printf_cp("sorc_path = %s\n", sorc_path); // 移動先のファイルが存在するかどうか rv = GetFileAttributes(dest_path); if (rv == INVALID_FILE_ATTRIBUTES){ // ファイルが存在しないなら、そのまま移動する if (MoveFile(sorc_path, dest_path) == 0){ if (GetLastError() == ERROR_PATH_NOT_FOUND){ // Path not found (3) make_dir(dest_path); // 途中のフォルダが存在しないのなら作成する if (MoveFile(sorc_path, dest_path) == 0) return 3; // それでも移動失敗 } else { return 4; // 移動失敗 } } if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す rv = GetFileAttributes(dest_path); SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない } } else if ((num & FILE_ATTRIBUTE_DIRECTORY) != (rv & FILE_ATTRIBUTE_DIRECTORY)){ // ファイルの移動先にフォルダ、またはフォルダの移動先にファイルが存在するなら move_away_file(dest_path); // それをどかす if (MoveFile(sorc_path, dest_path) == 0) return 5; // 移動失敗 if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す rv = GetFileAttributes(dest_path); SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない } } else { // 既にファイルが存在するなら wchar_t back_path[MAX_LEN]; back_path[0] = 0; if (switch_b & 3){ // 既存のファイルを削除せずに別名で残す for (num = 1; num < 100; num++){ swprintf(back_path, _countof(back_path), L"%s.%d", dest_path, num); // バックアップ・ファイルのパス if (GetFileAttributes(back_path) == INVALID_FILE_ATTRIBUTES) break; // 存在しないなら OK } if (num >= 100) back_path[0] = 0; // バックアップがいっぱいならそれ以上作らない } if (back_path[0] != 0){ // バックアップを作って置き換える //printf_cp("back_path = %s\n", back_path); num = ReplaceFile(dest_path, sorc_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); while (num == 0){ num = GetLastError(); //printf("error = %d\n", num); // The process cannot access the file because it is being used by another process. if ((num == 32) || // ERROR_SHARING_VIOLATION (num == 33) || // ERROR_LOCK_VIOLATION (num == 1175) || // ERROR_UNABLE_TO_REMOVE_REPLACED (num == 1176)){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT Sleep(FILE_WAIT_SEC); num = ReplaceFile(dest_path, sorc_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); } else if (num == 1177){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 Sleep(FILE_WAIT_SEC); num = MoveFile(sorc_path, dest_path); // 既存ファイルはバックアップ済みなら、移動するだけ } else { return 6; } retry_time--; if (retry_time == 0) break; } if (num == 0) return 7; if (switch_b & 2) // バックアップをゴミ箱に入れる delete_file_recycle(back_path); } else { // 既存のファイルを削除して置き換える if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、一旦その属性を解除する if (SetFileAttributes(dest_path, rv ^ FILE_ATTRIBUTE_READONLY) == 0) return 7; // 移動先の読み取り専用属性を解除できなかった = 削除もできない } num = ReplaceFile(dest_path, sorc_path, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); while (num == 0){ num = GetLastError(); //printf("error = %d\n", num); if ((num == 32) || // ERROR_SHARING_VIOLATION (num == 33) || // ERROR_LOCK_VIOLATION (num == 1175)){ // ERROR_UNABLE_TO_REMOVE_REPLACED Sleep(FILE_WAIT_SEC); num = ReplaceFile(dest_path, sorc_path, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); } else if (num == 1176){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT Sleep(FILE_WAIT_SEC); num = MoveFile(sorc_path, dest_path); // 既存ファイルは削除済みなら、移動するだけ } else { return 8; } retry_time--; if (retry_time == 0) break; } if (num == 0) return 9; if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す rv = GetFileAttributes(dest_path); SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない } } } return 0; } // ファイルまたはフォルダをどかす void move_away_file( wchar_t *file_path) // ファイル・パス { wchar_t new_path[MAX_LEN]; unsigned int rv, num; rv = GetFileAttributes(file_path); if (rv != INVALID_FILE_ATTRIBUTES){ // 既にファイルが存在するなら if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、一旦その属性を解除する if (SetFileAttributes(file_path, rv ^ FILE_ATTRIBUTE_READONLY) == 0) return; // 既存ファイルの読み取り専用属性を解除できなかった } if (switch_b & 3){ // 削除せずに別名で残す for (num = 1; num < 100; num++){ swprintf(new_path, _countof(new_path), L"%s.%d", file_path, num); if (MoveFile(file_path, new_path) != 0){ break; // 成功したらループから抜ける } else if (GetLastError() != ERROR_ALREADY_EXISTS){ break; // Destination file already exists (183) で移動できない時だけ再試行する } } if (num < 100){ // 移動できた if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す rv = GetFileAttributes(new_path); SetFileAttributes(new_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない } if (switch_b & 2) // ゴミ箱に入れる delete_file_recycle(new_path); return; } } // 削除する if (rv & FILE_ATTRIBUTE_DIRECTORY){ RemoveDirectory(file_path); // フォルダを削除する } else { DeleteFile(file_path); // 削除する } } } // ファイルを指定サイズに縮小する int shorten_file( wchar_t *file_path, // ファイル・パス __int64 new_size) { wchar_t back_path[MAX_LEN]; unsigned int rv, len, num; HANDLE hFileWrite; LARGE_INTEGER qwi; // Quad Word Integer back_path[0] = 0; if (switch_b & 3){ // 既存のファイルを削除せずに別名で残す for (num = 1; num < 100; num++){ swprintf(back_path, _countof(back_path), L"%s.%d", file_path, num); // バックアップ・ファイルのパス if (GetFileAttributes(back_path) == INVALID_FILE_ATTRIBUTES) break; // 存在しないなら OK } if (num >= 100) back_path[0] = 0; // バックアップがいっぱいならそれ以上作らない } if (back_path[0] != 0){ // バックアップを作って置き換える wchar_t temp_path[MAX_LEN]; // テンポラリー・ファイルを作ってファイル内容をコピーする get_temp_name(file_path, temp_path); hFileWrite = CreateFile(temp_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (hFileWrite == INVALID_HANDLE_VALUE){ // 作れなかったら back_path[0] = 0; } else if (new_size <= 0){ CloseHandle(hFileWrite); } else { // ファイル・サイズを指定サイズにする qwi.QuadPart = new_size; if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_BEGIN)){ back_path[0] = 0; } else if (!SetEndOfFile(hFileWrite)){ back_path[0] = 0; } else { // ポインターを先頭に戻す qwi.QuadPart = 0; if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_BEGIN)){ back_path[0] = 0; } else { __int64 left_size = new_size; HANDLE hFileRead; hFileRead = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFileRead == INVALID_HANDLE_VALUE){ back_path[0] = 0; } else { // 指定サイズまでコピーする unsigned char buf[IO_SIZE]; while (left_size > 0){ len = IO_SIZE; if (left_size < IO_SIZE) len = (unsigned int)left_size; left_size -= len; if (!ReadFile(hFileRead, buf, len, &rv, NULL) || (len != rv)){ back_path[0] = 0; break; } if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ back_path[0] = 0; break; } } CloseHandle(hFileRead); } } } CloseHandle(hFileWrite); if (back_path[0] == 0) // 失敗したらテンポラリー・ファイルは削除する DeleteFile(temp_path); } if (back_path[0] != 0){ // バックアップを作って置き換える if (ReplaceFile(file_path, temp_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0) == 0){ DeleteFile(temp_path); return 1; } if (switch_b & 2) // バックアップをゴミ箱に入れる delete_file_recycle(back_path); return 0; } } num = GetFileAttributes(file_path); if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、一旦その属性を解除する if (SetFileAttributes(file_path, num ^ FILE_ATTRIBUTE_READONLY) == 0) return 1; // 読み取り専用属性を解除できなかった = 縮小もできない } // 直接開いて縮小する hFileWrite = CreateFile(file_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFileWrite == INVALID_HANDLE_VALUE) return 1; // ファイル・サイズを指定サイズにする qwi.QuadPart = new_size; if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_BEGIN)){ CloseHandle(hFileWrite); return 1; } if (!SetEndOfFile(hFileWrite)){ CloseHandle(hFileWrite); return 1; } CloseHandle(hFileWrite); if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す num = GetFileAttributes(file_path); SetFileAttributes(file_path, num | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない } return 0; } // デバイス名かどうかを判定する static int check_device_name(wchar_t *name, int len) { if (len >= 3){ if ((name[3] == 0) || (name[3] == '.') || (name[3] == '\\')){ if (_wcsnicmp(name, L"CON", 3) == 0) return 1; if (_wcsnicmp(name, L"PRN", 3) == 0) return 1; if (_wcsnicmp(name, L"AUX", 3) == 0) return 1; if (_wcsnicmp(name, L"NUL", 3) == 0) return 1; } if (len >= 4){ if ((name[4] == 0) || (name[4] == '.') || (name[4] == '\\')){ if (_wcsnicmp(name, L"COM", 3) == 0){ if ((name[3] >= 0x31) && (name[3] <= 0x39)) return 1; } if (_wcsnicmp(name, L"LPT", 3) == 0){ if ((name[3] >= 0x31) && (name[3] <= 0x39)) return 1; } } } } return 0; } // ファイル名が有効かどうか調べる(ディレクトリ記号は警告しない) // 戻り値 0=変更無し, +2=浄化される, +4=文字数が変わる, +8=警告 int check_filename( wchar_t *name) // 検査するファイル名 { int i, len = 0; // 制御文字 1~31 (改行やタブなど) を削除する while (len < MAX_LEN){ if (name[len] == 0){ break; } else if (name[len] < 32){ return 4; } else { len++; } } if (len == 0) return 4; // WinOS ではファイル名に「:*?"<>|」の文字は使えない for (i = 0; i < len; i++){ if ((name[i] == ':') || (name[i] == '*') || (name[i] == '?') || (name[i] == '"') || (name[i] == '<') || (name[i] == '>') || (name[i] == '|')){ return 2; // 「\,:,*,?,",<,>,|」 } } // 先頭の「 」、末尾の「.」「 」を警告する if ((name[0] == ' ') || (name[len - 1] == '.') || (name[len - 1] == ' ')) return 8; // デバイス名は警告する if (check_device_name(name, len) != 0) return 8; return 0; } // ファイル名が有効か確かめて、問題があれば浄化する // 戻り値 0=変更無し, +1=ディレクトリ記号, +2=浄化した, +4=文字数が変わった, +8=警告, 16=エラー int sanitize_filename( wchar_t *name, // 検査するファイル名 file_ctx_r *files, // 各ソース・ファイルの情報 int num) // 比較から除外するファイル番号 { int i, j, rv = 0, len = 0; // 制御文字 1~31 (改行やタブなど) を削除する while (len < MAX_LEN){ if (name[len] == 0){ break; } else if (name[len] < 32){ i = len; do { name[i] = name[i + 1]; i++; } while (name[i] != 0); rv |= 4; } else { len++; } } if (len == 0) name[0] = 0; // WinOS ではファイル名に「:*?"<>|」の文字は使えないので置換する for (i = 0; i < len; i++){ if ((name[i] == ':') || (name[i] == '*') || (name[i] == '?') || (name[i] == '"') || (name[i] == '<') || (name[i] == '>') || (name[i] == '|')){ name[i] = '_'; // 「: * ? " < > |」 ->「_」 rv |= 2; } else if (name[i] == '/'){ // ディレクトリ記号を変換する、元から「\」ならそのまま name[i] = '\\'; // 「/」 -> 「\」 rv |= 1; } } // 先頭の「 」、末尾の「.」「 」を警告する if ((name[0] == ' ') || (name[len - 1] == '.') || (name[len - 1] == ' ')) rv |= 8; // 「\」の前後に「 」があれば警告する i = len - 1; while (i > 0){ if ((name[i] == '\\') && ((name[i - 1] == ' ') || (name[i + 1] == ' '))) rv |= 8; i--; } // 「\」の前に「.」があれば削除する (ディレクトリ移動を防ぐ) i = len - 1; while (i > 0){ if ((name[i] == '\\') && (name[i - 1] == '.')){ for (j = i - 1; j < len; j++) name[j] = name[j + 1]; len--; rv |= 4; } i--; } // 連続した「\」を一文字にする i = 0; while (i < len){ if ((name[i] == '\\') && (name[i + 1] == '\\')){ for (j = i + 1; j < len; j++) name[j] = name[j + 1]; len--; rv |= 4; } else { i++; } } // 先頭の「\」を削除する if (name[0] == '\\'){ for (i = 0; i < len; i++) name[i] = name[i + 1]; len--; rv |= 4; } // デバイス名を警告する (サブ・ディレクトリも含める) i = len; while (i > 0){ i--; if ((i == 0) || ((i > 0) && (name[i - 1] == '\\'))){ if (check_device_name(name + i, len) != 0) rv |= 8; } } // 既に同名のファイルが存在するなら、末尾に「(値)」を追加する if (files != NULL){ int k; if (len == 0){ // ファイル名が空になった場合は必ず追加する i = 0; } else { // 大文字と小文字を区別するシステム上で作られた PAR ファイルは Windows OS では問題になる for (i = 0; i < file_num; i++){ if (i == num){ if (rv & 6){ continue; // 元のファイル名とは比較しない } else { i = file_num; break; // それまでのファイル名とだけ比較する } } if (files[i].name <= 0) continue; // ファイル名が取得できなかったファイルとは比較しない if (_wcsicmp(name, list_buf + files[i].name) == 0) break; // ファイル名が同じ } } if (i < file_num){ if (len >= MAX_LEN - 3) return 16; k = 0; for (j = len - 1; j > 0; j--){ if (name[j] == '.'){ k = j; break; } if (name[j] == '\\'){ // サブ・ディレクトリは除外する if (j == len - 1) k = len - 1; // フォルダなら break; } } if (k > 0){ // 拡張子があるなら、拡張子の前に追加する for (j = len; j >= k; j--) name[j + 3] = name[j]; name[k ] = '('; name[k + 2] = ')'; k = k + 1; } else { // 拡張子が無ければ末尾に追加する name[len ] = '('; name[len + 2] = ')'; name[len + 3] = 0; k = len + 1; } len += 3; rv |= 4; for (j = 1; j < 10; j++){ name[k] = (unsigned short)(0x30 + j); for (i = 0; i < file_num; i++){ if (i == num){ if (rv & 6){ continue; // 元のファイル名とは比較しない } else { i = file_num; break; // それまでのファイル名とだけ比較する } } if (files[i].name <= 0) continue; // ファイル名が取得できなかったファイルとは比較しない if (_wcsicmp(name, list_buf + files[i].name) == 0) break; // ファイル名が同じ } if (i == file_num) break; } if (j >= 10) return 16; // 同名になるファイルが多過ぎる } } return rv; } // リカバリ・ファイルのパスから拡張子とボリューム番号を取り除く void get_base_filename( wchar_t *file_path, // リカバリ・ファイルのパス wchar_t *base_path, // 基準ファイル名のパス wchar_t *file_ext) // 拡張子 (存在するなら) { wchar_t *tmp_p, *file_name; int len, match; file_ext[0] = 0; wcscpy(base_path, file_path); file_name = offset_file_name(base_path); tmp_p = wcsrchr(file_name, '.'); if (tmp_p != NULL){ // 拡張子または「.vol*」が存在すれば取り除く // 拡張子が無い場合も考慮する if (_wcsnicmp(tmp_p, L".vol", 4) == 0){ match = 0; len = 4; while (tmp_p[len] != 0){ if ((tmp_p[len] >= 48) && (tmp_p[len] <= 57)){ match |= 1; // 数字は何個あってもよい } else if ((tmp_p[len] == '+') || (tmp_p[len] == '-') || (tmp_p[len] == '_')){ match += 2; // 記号は一個だけしかだめ } else { // それ以外なら判定終了 break; } len++; } if (match == 3) tmp_p[0] = 0; // 取り除く } if (tmp_p[0] != 0){ // 拡張子があるなら if (wcslen(tmp_p) < EXT_LEN){ wcscpy(file_ext, tmp_p); // 拡張子を記録しておく tmp_p[0] = 0; // 拡張子を取り除く } } // 更に「.vol*」があればそれも取り除く tmp_p = wcsrchr(file_name, '.'); if (tmp_p != NULL){ if (_wcsnicmp(tmp_p, L".vol", 4) == 0){ match = 0; len = 4; while (tmp_p[len] != 0){ if ((tmp_p[len] >= 48) && (tmp_p[len] <= 57)){ match |= 1; // 数字は何個あってもよい } else if ((tmp_p[len] == '+') || (tmp_p[len] == '-') || (tmp_p[len] == '_')){ match += 2; // 記号は一個だけしかだめ } else { // それ以外なら判定終了 break; } len++; } if (match == 3) tmp_p[0] = 0; // 取り除く } } } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* // バイトごとに16進数表示にする、十分な領域を確保しておくこと void byte_encode(wchar_t *out, unsigned char *in, int len) { int i, c; for (i = 0; i < len; i++){ c = in[i] >> 4; if (c < 10){ // 0 to 9 *out = c + 48; } else { // A to Z *out = c - 10 + 65; } out++; c = in[i] & 15; if (c < 10){ // 0 to 9 *out = c + 48; } else { // A to Z *out = c - 10 + 65; } out++; } *out = 0; } void byte_decode(unsigned char *out, wchar_t *in, int len) { unsigned int c1, c2; while (len >= 2){ c1 = *in; in++; if ((c1 >= 48) && (c1 <= 57)){ // 0 to 9 c1 -= 48; } else if ((c1 >= 65) && (c1 <= 90)){ // A to Z c1 -= 65 - 10; } else { break; } c2 = *in; in++; if ((c2 >= 48) && (c2 <= 57)){ // 0 to 9 c2 -= 48; } else if ((c2 >= 65) && (c2 <= 90)){ // A to Z c2 -= 65 - 10; } else { break; } *out = (unsigned char)((c1 << 4) | c2); out++; len -= 2; } } */ // ハッシュ値を表示する void print_hash(unsigned char hash[16]) { printf("%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]); } // 64-bit 整数の平方根を求める // by n-trino, http://d.hatena.ne.jp/n-trino/20080225 unsigned int sqrt64(__int64 num) { unsigned int i, result = 0; __int64 rest = 0, sum = 0; if (num <= 1) return 1; // 1より小さい値が入力されることは無い for (i = 0; i < 32; i++){ rest = ( rest << 2 ) | ( ( num >> 62 ) & 3 ); num <<= 2; result <<= 1; sum <<= 1; if ( ( sum | 1 ) <= rest ){ sum |= 1; rest -= sum; sum++; result |= 1; } } return result; } int sqrt32(int num) { int i, result = 0, rest = 0, sum = 0; if (num <= 1) return 1; // 1より小さい値が入力されることは無い for (i = 0; i < 16; i++){ rest = ( rest << 2 ) | ( ( num >> 30 ) & 3 ); num <<= 2; result <<= 1; sum <<= 1; if ( ( sum | 1 ) <= rest ){ sum |= 1; rest -= sum; sum++; result |= 1; } } return result; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int cpu_num = 1; // CPU/Core 個数が制限されてる場合は、上位に本来の数を置く // /arch:SSE2, +1=SSSE3, +2=SSE4.1, +4=SSE4.2, +8=CLMUL, +16=AVX2, +128=JIT(SSE2), +256=Old unsigned int cpu_flag = 0; unsigned int cpu_cache = 0; // 上位 16-bit = L2 cache * 2, 下位 16-bit = L3 cache unsigned int memory_use = 0; // メモリー使用量 0=auto, 1~7 -> 1/8 ~ 7/8 static int count_bit(DWORD_PTR value) { int bitCount = 0; // Shift を使わない方法、計算量は平均 (n/2) while (value != 0){ bitCount++; value &= (value - 1); } return bitCount; } void check_cpu(void) { int core_count = 0, use_count; unsigned int CPUInfo[4]; unsigned int returnLength, byteOffset; DWORD_PTR ProcessAffinityMask, SystemAffinityMask; // 32-bit なら 4バイト、64-bit なら 8バイト整数 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL, ptr; // CPU の拡張機能を調べる __cpuid(CPUInfo, 1); cpu_flag |= (CPUInfo[3] & (1 << 26)) >> 19; // SSE2 対応か cpu_flag |= (CPUInfo[2] & (1 << 9)) >> 9; // SSSE3 対応か cpu_flag |= (CPUInfo[2] & (1 << 19)) >> 18; // SSE4.1 対応か cpu_flag |= (CPUInfo[2] & (1 << 20)) >> 18; // SSE4.2 対応か cpu_flag |= (CPUInfo[2] & (1 << 1)) << 2; // CLMUL 対応か if ((CPUInfo[2] & (1 << 28)) != 0){ // AVX 対応なら if (IsWindows7OrGreater()){ // Windows 7 以降なら AVX2 の判定をする __cpuid(CPUInfo, 0); if (CPUInfo[0] >= 7){ // AVX2 用の基本命令領域があるなら __cpuidex(CPUInfo, 7, 0); cpu_flag |= (CPUInfo[1] & (1 << 5)) >> 1; // AVX2 対応か } } } // 使用可能なコア個数とキャッシュ性能を調べる if (GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask) != 0){ cpu_num = count_bit(ProcessAffinityMask); //printf("ProcessAffinityMask = 0x%08Ix, available logical processor cores = %d\n", ProcessAffinityMask, cpu_num); } if (cpu_num <= 0){ // プロセスが使用可能なコア個数の取得に失敗したら総数を使う SYSTEM_INFO si; ProcessAffinityMask = -1; GetSystemInfo(&si); cpu_num = si.dwNumberOfProcessors; //printf("total logical processor cores = %d\n", cpu_num); } // CPU 情報を取得する returnLength = 0; if (GetLogicalProcessorInformation(buffer, &returnLength) == FALSE){ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){ buffer = malloc(returnLength); if (buffer != NULL){ if (GetLogicalProcessorInformation(buffer, &returnLength) == FALSE){ free(buffer); buffer = NULL; } } } } // 項目ごとに解析する if (buffer != NULL){ int cache2_size = 0, cache2_way, cache3_size = 0, cache3_way; /* 実験してもらってからじゃないと、本採用しにくい・・・ DWORD numaNodeCount = 0, processorPackageCount = 0; DWORD_PTR numaNodeMask = 0, processorPackageMask = 0; byteOffset = 0; ptr = buffer; while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength){ if (ptr->ProcessorMask & ProcessAffinityMask){ switch (ptr->Relationship){ case RelationNumaNode: // Non-NUMA systems report a single record of this type. numaNodeCount++; if (count_bit(ptr->ProcessorMask) > count_bit(numaNodeMask)) numaNodeMask = ptr->ProcessorMask; break; case RelationProcessorPackage: // Logical processors share a physical package. processorPackageCount++; if (count_bit(ptr->ProcessorMask) > count_bit(processorPackageMask)) processorPackageMask = ptr->ProcessorMask; break; } } byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ptr++; } //printf("Number of NUMA nodes: %d, Mask = 0x%08Ix\n", numaNodeCount, numaNodeMask); //printf("Number of physical processor packages: %d, Mask = 0x%08Ix\n", processorPackageCount, processorPackageMask); if (numaNodeCount > 1){ // NUMAノードが複数あるなら // 使用する Core を一つの NUMAノードのだけに制限する if (SetProcessAffinityMask(GetCurrentProcess(), numaNodeMask) != 0){ cpu_num = count_bit(numaNodeMask); ProcessAffinityMask = numaNodeMask; //printf("Number of logical processor cores in a NUMA node: %d\n", cpu_num); } } */ byteOffset = 0; ptr = buffer; while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength){ //printf("Relationship = %d, ProcessorMask = 0x%08Ix\n", ptr->Relationship, ptr->ProcessorMask); if (ptr->ProcessorMask & ProcessAffinityMask){ switch (ptr->Relationship){ case RelationProcessorCore: //printf("ProcessorCore.Flags = %d, ProcessorMask = 0x%08Ix\n", ptr->ProcessorCore.Flags, ptr->ProcessorMask); if (ptr->ProcessorCore.Flags){ // Logical processors share functional units, as in Hyperthreading or SMT. core_count += 1; } else { // Logical processors do not share functional units. core_count += count_bit(ptr->ProcessorMask); } break; case RelationCache: // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache. if ((ptr->Cache.Level >= 2) && (ptr->Cache.Associativity > 0) && ((ptr->Cache.Type & 1) == 0)){ //printf("Cache: Level = %d, Size = %d KB, Associativity = %d, Type = %d, Mask = 0x%08Ix\n", ptr->Cache.Level, ptr->Cache.Size >> 10, ptr->Cache.Associativity, ptr->Cache.Type, ptr->ProcessorMask); if ((ptr->Cache.Level == 2) && (cache2_size == 0)){ if (ptr->Cache.Associativity == 0xFF){ cache2_way = 4; } else { cache2_way = ptr->Cache.Associativity; } cache2_size = ptr->Cache.Size; } else if ((ptr->Cache.Level == 3) && (cache3_size == 0)){ if (ptr->Cache.Associativity == 0xFF){ cache3_way = 4; } else { cache3_way = ptr->Cache.Associativity; } cache3_size = ptr->Cache.Size; } } break; } } byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ptr++; } free(buffer); //printf("Number of available physical processor cores: %d\n", core_count); if (cache3_size > 0){ //printf("L3 cache: %d KB (%d way)\n", cache3_size >> 10 , cache3_way); cache3_size /= cache3_way; // set-associative のサイズにする if (cache3_size < 131072) cache3_size = 128 << 10; // 128 KB 以上にする } if (cache2_size > 0){ //printf("L2 cache: %d KB (%d way)\n", cache2_size >> 10, cache2_way); cache2_size /= cache2_way; // set-associative のサイズにする if (cache2_size < 32768) cache2_size = 32 << 10; // 32 KB 以上にする //printf("Limit size of Cache Blocking: %d KB\n", cache2_size >> 10); cpu_cache = cache2_size | (cache3_size >> 17); } } if (cpu_cache == 0) // キャッシュ・サイズが不明なら、128 KB にする cpu_cache = 128 << 10; if (core_count == 0){ // 物理コア数が不明なら、論理コア数と同じにする core_count = cpu_num; use_count = cpu_num; } else if (core_count < cpu_num){ // 物理コア数が共有されてるなら if (core_count >= 6){ // 6 コア以上ならそれ以上増やさない use_count = core_count; } else { // 2~5 コアなら 1個だけ増やす use_count = core_count + 1; } } else { use_count = core_count; } if (use_count > MAX_CPU) // 利用するコア数が実装上の制限を越えないようにする use_count = MAX_CPU; //printf("Core count: logical, physical, use = %d, %d, %d\n", cpu_num, core_count, use_count); // 上位に論理コア数と物理コア数、下位に利用するコア数を配置する cpu_num = (cpu_num << 24) | (core_count << 16) | use_count; // cpu_flag の上位 17-bit にキャッシュの制限サイズを置く cpu_flag |= cpu_cache & 0xFFFF8000; // 32 KB 未満は無視する } // OS が 32-bit か 64-bit かを調べる int check_OS64(void) { BOOL bWow64Process = FALSE; IsWow64Process(GetCurrentProcess(), &bWow64Process); return bWow64Process; } // 空きメモリー量と制限値から使用できるメモリー量を計算する size_t get_mem_size(size_t trial_alloc) { size_t mem_size; MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); if (GlobalMemoryStatusEx(&statex)){ unsigned __int64 mem_size64, cache_size; /* printf("MemoryLoad = %d\n", statex.dwMemoryLoad); printf("TotalPhys = %10I64d\n", statex.ullTotalPhys); printf("AvailPhys = %10I64d\n", statex.ullAvailPhys); printf("TotalPageFile = %10I64d\n", statex.ullTotalPageFile); printf("AvailPageFile = %10I64d\n", statex.ullAvailPageFile); printf("TotalVirtual = %10I64d\n", statex.ullTotalVirtual); printf("AvailVirtual = %10I64d\n", statex.ullAvailVirtual); */ cache_size = statex.ullAvailPhys >> 3; // available size / 8 if (memory_use & 7){ // manual setting (1/8 ~ 7/8) mem_size64 = cache_size * (memory_use & 7); // using size = available size / 8 * memory_use } else { // auto setting #ifndef _WIN64 // 32-bit OS なら、OSがメモリーの半分を使うと想定する // 1 GB = 1024 MB, 1024 / 16 = 64 MB, 512 / 8 = 32 MB // 2 GB = 2048 MB, 2048 / 16 = 128 MB, 1024 / 8 = 128 MB // disk cache size is range from 100MB to 200MB. cache_size += statex.ullTotalPhys >> 4; // cache size = available size / 8 + total size / 16 #else // 64-bit OS なら、OSがメモリーを 2GB 使うと想定する // 4 GB = 4096 MB, 4096 / 32 = 128 MB, 2048 / 8 = 256 MB // 8 GB = 8192 MB, 8192 / 32 = 256 MB, 6144 / 8 = 768 MB // 16 GB = 16384 MB, 16384 / 32 = 512 MB, 14336 / 8 = 1792 MB // 32 GB = 32768 MB, 32768 / 32 = 1024 MB, 30720 / 8 = 3840 MB cache_size += statex.ullTotalPhys >> 5; // cache size = available size / 8 + total size / 32 #endif if (cache_size > statex.ullAvailPhys >> 2) cache_size = statex.ullAvailPhys >> 2; // cache size should be less than 1/4 of available size. mem_size64 = statex.ullAvailPhys - cache_size; // using size = available size - cache size } if (mem_size64 + 0x00200000 > statex.ullAvailVirtual) mem_size64 = statex.ullAvailVirtual - 0x00200000; // reserve 2MB for other task #ifndef _WIN64 // 32-bit 版なら if (mem_size64 > MAX_MEM_SIZE) // 確保する最大サイズを 2GB までにする mem_size64 = MAX_MEM_SIZE; if (check_OS64() == 0){ // 32-bit OS 上なら更に制限する if (mem_size64 > MAX_MEM_SIZE32) mem_size64 = MAX_MEM_SIZE32; } #endif mem_size = (size_t)mem_size64 & ~(size_t)0xFFFF; // multiple of 64KB } else { print_win32_err(); mem_size = 0x20000000; // メモリー量を認識できない場合は 512MB と仮定する } // try to allocate for test if (trial_alloc){ unsigned char *tmp_p; if (mem_size > trial_alloc) // no need to test over than required size mem_size = trial_alloc; //printf("\nget_mem_size: try %d MB ...", mem_size >> 20); tmp_p = malloc(mem_size); while ((tmp_p == NULL) && (mem_size > 0x08000000)){ // if over than 128MB mem_size -= (mem_size >> 4); // reduce 1/16 mem_size &= ~(size_t)0xFFFF; tmp_p = malloc(mem_size); } if (tmp_p != NULL){ free(tmp_p); //printf(" %d MB ok\n", mem_size >> 20); } else { //printf(" %d MB failed\n", mem_size >> 20); mem_size = (mem_size / 2) & ~(size_t)0xFFFF; // half size } } return mem_size; } // 外部関数の定義 com.cpp //extern int check_msft(void); // 記録装置の特性を調べるために、次の記事を参考にした。 //「SSD なら動作を変えるアプリケーションを作る」by NyaRuRu //「SSDかどうかをC#から判別する 」by EMO // Returns 0 if |physical_drive_path| has no seek penalty. // Returns 1 otherwise. // Returns 2~ if fails to retrieve the status. int check_seek_penalty(wchar_t *dir_path){ // ディレクトリを指定する wchar_t *drive_letter, logical_drive[8]; unsigned int return_value, return_size; HANDLE hDevice; STORAGE_PROPERTY_QUERY query_property; DEVICE_SEEK_PENALTY_DESCRIPTOR query_seek_penalty_desc; DEVICE_TRIM_DESCRIPTOR query_trim_desc; STORAGE_ADAPTER_DESCRIPTOR query_adapter_desc; // if (IsWindows8OrGreater()){ // Windows 8 以降なら // printf("\n calling check_msft()\n"); // return_value = check_msft(); // printf("\ncheck_msft = %d\n\n", return_value); // } // ドライブ文字を取得する drive_letter = dir_path; if (wcsncmp(drive_letter, L"\\\\?\\", PREFIX_LEN) == 0) drive_letter += PREFIX_LEN; if (drive_letter[1] != ':') return 2; // 「C:\~」のような形式にだけ対応する if ((drive_letter[0] >= 0x41) && (drive_letter[0] <= 0x5A)){ // 大文字の A~Z なら logical_drive[4] = drive_letter[0]; } else if ((drive_letter[0] >= 0x61) && (drive_letter[0] <= 0x7A)){ // 小文字の a~z なら logical_drive[4] = drive_letter[0]; } else { return 3; // ドライブ文字でないならだめ } /* // GetDriveType 用に "\\\\.\\C:\" のフォーマットにする logical_drive[0] = '\\'; logical_drive[1] = '\\'; logical_drive[2] = '.'; logical_drive[3] = '\\'; logical_drive[5] = ':'; logical_drive[6] = '\\'; logical_drive[7] = 0; return_value = GetDriveType(logical_drive); //printf("GetDriveType = %d\n", return_value); if (return_value == 6) // DRIVE_RAMDISK return 0; // RAM disk は SSD と同じ扱いにする if (return_value != 3) // DRIVE_FIXED 以外は駄目 return 1; */ // "\\\\.\\C:" のフォーマットにする logical_drive[0] = '\\'; logical_drive[1] = '\\'; logical_drive[2] = '.'; logical_drive[3] = '\\'; logical_drive[5] = ':'; logical_drive[6] = 0; // We do not need write permission. hDevice = CreateFile(logical_drive, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hDevice == INVALID_HANDLE_VALUE) return 4; // アダプター情報を取得する (BusType は USBなら 7、SATAなら 11、NVMeなら 17 らしい) return_size = 0; query_property.PropertyId = 1; // StorageAdapterProperty query_property.QueryType = 0; // PropertyStandardQuery return_value = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query_property, sizeof(query_property), &query_adapter_desc, sizeof(query_adapter_desc), &return_size, NULL); if ((return_value != 0) && (query_adapter_desc.Size == sizeof(query_adapter_desc))){ /* printf("\n STORAGE_ADAPTER_DESCRIPTOR\n Version = %d, Size = %d, BusType = %d\n", query_adapter_desc.Version, query_adapter_desc.Size, query_adapter_desc.BusType);*/ if (query_adapter_desc.BusType == 17) // BusType 17 = NVMe memory_use |= 32; } // SSD なら seek penalty よりも TRIM コマンドを調べたほうが確実らしい // https://stackoverflow.com/questions/23363115/detecting-ssd-in-windows return_size = 0; query_property.PropertyId = 8; // StorageDeviceTrimProperty query_property.QueryType = 0; // PropertyStandardQuery return_value = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query_property, sizeof(query_property), &query_trim_desc, sizeof(query_trim_desc), &return_size, NULL); if ((return_value != 0) && (query_trim_desc.Size == sizeof(query_trim_desc))){ /* printf("\n DEVICE_TRIM_DESCRIPTOR\n Version = %d, Size = %d, TrimEnabled = %d\n", query_trim_desc.Version, query_trim_desc.Size, query_trim_desc.TrimEnabled);*/ if (query_trim_desc.TrimEnabled != 0){ // TRIM command available = maybe SSD drive memory_use |= 16; CloseHandle(hDevice); return 0; } } // seek penalty なら RAM disk も判定できるかも? return_size = 0; query_property.PropertyId = 7; // StorageDeviceSeekPenaltyProperty query_property.QueryType = 0; // PropertyStandardQuery return_value = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query_property, sizeof(query_property), &query_seek_penalty_desc, sizeof(query_seek_penalty_desc), &return_size, NULL); if ((return_value != 0) && (query_seek_penalty_desc.Size == sizeof(query_seek_penalty_desc))){ /* printf("\n DEVICE_SEEK_PENALTY_DESCRIPTOR\n Version = %d, Size = %d, IncursSeekPenalty = %d\n", query_seek_penalty_desc.Version, query_seek_penalty_desc.Size, query_seek_penalty_desc.IncursSeekPenalty);*/ if (query_seek_penalty_desc.IncursSeekPenalty == 0){ // no seek penalty = maybe SSD drive memory_use |= 16; CloseHandle(hDevice); return 0; } } CloseHandle(hDevice); return 1; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define MAX_NAME_LEN 69 // 経過表示のタイトルの最大文字数 (末尾の null 文字を含む) int prog_last; // 前回と同じ進捗状況は出力しないので記録しておく int count_last; // ファイル・パスを短縮されたファイル名だけにしてコピーする // ASCII 文字は 1文字, それ以外は 2文字として数えることに注意! static void copy_filename(wchar_t *out, wchar_t *in) { int i, len, name_off, ext_off, ext_len; // ファイル名部分の文字数を数える name_off = 0; ext_off = 0; len = 0; for (i = 0; in[i] != 0; i++){ if ((in[i] == '\\') && (in[i + 1] != 0)){ len = 0; name_off = i + 1; } else { if (in[i] < 0x80){ len += 1; if ((in[i] == '.') && (in[i + 1] != 0)) ext_off = i; } else { len += 2; ext_off = 0; // 拡張子は ASCII文字だけにする } } } if (len < MAX_NAME_LEN){ wcscpy(out, in + name_off); return; } // 拡張子の長さを調べる ext_len = 0; if (ext_off > name_off){ while (in[ext_off + ext_len] != 0) ext_len++; if (ext_len >= EXT_LEN) ext_len = 0; } // ファイル名が長すぎる場合は、末尾を省いて拡張子を追加する len = 0; for (i = 0; in[name_off + i] != 0; i++){ if (in[name_off + i] < 0x80){ if (len >= MAX_NAME_LEN - 2 - ext_len) break; len += 1; } else { if (len >= MAX_NAME_LEN - 3 - ext_len) break; len += 2; } out[i] = in[name_off + i]; } out[i++] = '~'; while (ext_len > 0){ out[i++] = in[ext_off++]; ext_len--; } out[i] = 0; } // 経過のパーセントを表示する // 普段は 0 を返す、キャンセル時は 0以外 int print_progress(int prog_now) // 表示する % 値 { if (prog_now < 0) // 範囲外なら return 0; if (_kbhit()){ // キー入力があるか int ch = _getch(); if ((ch == 'c') || (ch == 'C')){ // Cancel printf("\nCancel\n"); return 2; } // 一時停止と再開の処理 if ((ch == 'p') || (ch == 'P')){ // Pause printf(" Pause\r"); // パーセントを上書きする do { ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける printf("\nCancel\n"); return 2; } } while ((ch != 'r') && (ch != 'R')); // Resume prog_last = -1; // Pause の文字を上書きする } } // 前回と同じ進捗状況は出力しない if (prog_now == prog_last) return 0; printf("%3d.%d%%\r", prog_now / 10, prog_now % 10); prog_last = prog_now; fflush(stdout); return 0; } // 経過のパーセントとテキストを表示する void print_progress_text(int prog_now, char *text) { if (prog_now < 0) // 範囲外なら return; printf("%3d.%d%% : %s\r", prog_now / 10, prog_now % 10, text); prog_last = prog_now; fflush(stdout); } // 経過のパーセントや個数やファイル名を表示する // 個数はマイナスなら表示しない int print_progress_file(int prog_now, int count_now, wchar_t *file_name) { if ((count_now >= 0) && (count_now != count_last)){ printf("%d \r", count_now); // 個数を表示する count_last = count_now; if (prog_now == prog_last) prog_last = prog_now + 1; // 個数の文字を上書きする } if (prog_now < 0) // 範囲外なら return 0; if (_kbhit()){ // キー入力があるか int ch = _getch(); if ((ch == 'c') || (ch == 'C')){ // Cancel if (prog_last >= 0) printf("\n"); printf("Cancel\n"); return 2; } // 一時停止と再開の処理 if ((ch == 'p') || (ch == 'P')){ // Pause printf(" Pause\r"); // パーセントを上書きする do { ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける printf("\nCancel\n"); return 2; } } while ((ch != 'r') && (ch != 'R')); // Resume if (prog_now == prog_last) prog_last = prog_now + 1; // Pause の文字を上書きする } } if (prog_last < 0){ // 初めて経過を表示する時だけファイル名を表示する char text[MAX_NAME_LEN * 3]; wchar_t short_name[MAX_NAME_LEN]; copy_filename(short_name, file_name); utf16_to_cp(short_name, text, cp_output); printf("%3d.%d%% : \"%s\"\r", prog_now / 10, prog_now % 10, text); } else if (prog_now == prog_last){ // 前回と同じ進捗状況は出力しない return 0; } else { printf("%3d.%d%%\r", prog_now / 10, prog_now % 10); } prog_last = prog_now; fflush(stdout); return 0; } void print_progress_done(void) // 終了と改行を表示する { if (prog_last >= 0){ // そもそも経過表示がなかった場合は表示しない if (prog_last != 1000){ printf("100.0%%\n"); } else { printf("\n"); } fflush(stdout); prog_last = -1; // 進捗状況をリセットする } } // キャンセルと一時停止を行う int cancel_progress(void) { if (_kbhit()){ // キー入力があるか int ch = _getch(); if ((ch == 'c') || (ch == 'C')){ // Cancel printf("Cancel\n"); return 2; } // 一時停止と再開の処理 if ((ch == 'p') || (ch == 'P')){ // Pause printf(" Pause\r"); do { ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける printf("Cancel\n"); return 2; } } while ((ch != 'r') && (ch != 'R')); // Resume } } return 0; } // エラー発生時にキャンセルできるようにする int error_progress(int error_now, int error_last) { if (error_now != error_last) printf("Error:%d\n", error_now); if (_kbhit()){ // キー入力があるか int ch = _getch(); if ((ch == 'c') || (ch == 'C')){ // Cancel printf("Cancel\n"); return 2; } // 一時停止と再開の処理 if ((ch == 'p') || (ch == 'P')){ // Pause printf(" Pause\r"); do { ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける printf("Cancel\n"); return 2; } } while ((ch != 'r') && (ch != 'R')); // Resume // 再会した時には現在の進行状況を表示する printf("%3d.%d%%\r", prog_last / 10, prog_last % 10); } } Sleep(UPDATE_TIME); // 一定時間待つ /* int ch; printf("Press R key to retry, or C key to cancel.\nError\r"); do { ch = _getch(); // キー入力があるまで待つ、CPU 占有率 0% if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける printf("Cancel\n"); return 2; } } while ((ch != 'r') && (ch != 'R')); // Resume */ return 0; } // Win32 API のエラー・メッセージを表示する void print_win32_err(void) { unsigned int en; LPVOID lpMsgBuf = NULL; en = GetLastError(); if (cp_output == CP_UTF8){ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, en, 0, (LPWSTR) &lpMsgBuf, 0, NULL) > 0){ char buf[MAX_LEN * 3]; // エンコードを UTF-16 から UTF-8 に変換する utf16_to_utf8(lpMsgBuf, buf); printf("0x%X, %s\n", en, buf); } } else { if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, en, 0, (LPSTR) &lpMsgBuf, 0, NULL) > 0){ printf("0x%X, %s\n", en, (char *)lpMsgBuf); } } if (lpMsgBuf != NULL) LocalFree(lpMsgBuf); } // from com.cpp extern HRESULT DeleteItem(__in PCWSTR pszSrcItem); // ファイルをゴミ箱に移す int delete_file_recycle(wchar_t *file_path) { wchar_t temp_path[MAX_LEN]; int rv; // "\\?\" が付いてると SHCreateItemFromParsingName が動作しないので取り除く // パスの文字数が長くても動くみたい copy_without_prefix(temp_path, file_path); rv = DeleteItem(temp_path); if (rv != S_OK){ //printf("DeleteItem: error = 0x%08x\n", rv); if (DeleteFile(file_path) == 0) return rv; } return 0; /* old code for Windows XP wchar_t temp_path[MAX_PATH + PREFIX_LEN]; int rv; SHFILEOPSTRUCT FileOp; // "\\?\" が付いてると ShellAPI が動作しないので取り除く rv = len_without_prefix(file_path); if (rv >= MAX_PATH - 1){ // パスの文字数が MAX_PATH - 1 以下でないといけない // 短いパス名に変換してみる rv = GetShortPathName(file_path, temp_path, MAX_PATH + PREFIX_LEN); if ((rv == 0) || (rv >= MAX_PATH + PREFIX_LEN)) return 1; // パスが長すぎで短縮できない? // 更にプリフィックスを取り除く rv = copy_without_prefix(temp_path, temp_path); //printf("\n short path = \"%S\"\n", temp_path); } else { rv = copy_without_prefix(temp_path, file_path); } temp_path[rv + 1] = 0; // 末尾にもう一個 null 文字を追加する memset(&FileOp, 0, sizeof(FileOp)); FileOp.wFunc = FO_DELETE; FileOp.pFrom = temp_path; FileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; rv = SHFileOperation(&FileOp); if (FileOp.fAnyOperationsAborted == TRUE) rv = 1; // 削除がキャンセルされた // if (rv != 0) // printf("\n SHFileOperation: error = %d\n", rv); return rv; */ } // エクスプローラーで隠しファイルを表示する設定になってるか調べる unsigned int get_show_hidden(void) { unsigned int rv; SHELLSTATE ssf; // Explorer の設定を調べる SHGetSetSettings(&ssf, SSF_SHOWALLOBJECTS | SSF_SHOWSUPERHIDDEN, FALSE); // 隠しファイルを表示するかどうか if (ssf.fShowAllObjects){ // 表示する設定なら // 保護されたオペレーティングシステムファイルを表示するかどうか if (ssf.fShowSuperHidden){ // 表示する設定なら rv = 0; } else { // 隠し属性とシステム属性の両方で判定する rv = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; } } else { // 隠しファイルを表示しない場合は、隠し属性だけで判定する rv = FILE_ATTRIBUTE_HIDDEN; } return rv; }