Files
MultiPar/source/par1j/common1.c
2024-11-30 12:58:22 +09:00

1071 lines
34 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// common.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 <conio.h>
#include <stdio.h>
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include "common1.h"
// グローバル変数
wchar_t recovery_file[MAX_LEN]; // リカバリ・ファイルのパス
wchar_t base_dir[MAX_LEN]; // ソース・ファイルの基準ディレクトリ
wchar_t ini_path[MAX_LEN]; // 検査結果ファイルのパス
unsigned int cp_output;
/*
// Console Input Code Page から Windown OS の precomposed UTF-16 に変換する
int cp_to_utf16(char *in, wchar_t *out)
{
if (!MultiByteToWideChar(cp_input, 0, in, -1, out, MAX_LEN)){
wcscpy(out, L"cannot encode");
return 1;
}
return 0;
}
*/
// Windown OS の precomposed UTF-16 から Console Output Code Page に変換する
int utf16_to_cp(wchar_t *in, char *out)
{
if (cp_output == CP_UTF8){
if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out, MAX_LEN * 3, NULL, NULL) == 0){
strcpy(out, "cannot encode");
return 1;
}
return 0;
}
// 似た文字への自動変換を行わない
if (!WideCharToMultiByte(cp_output, WC_NO_BEST_FIT_CHARS, in, -1, out, MAX_LEN * 2, NULL, NULL)){
strcpy(out, "cannot encode");
return 1;
}
return 0;
}
// ファイル・パスから、先頭にある "\\?\" を省いて、Console Output Code Page に変換する
int path_to_cp(wchar_t *path, char *out)
{
unsigned int dwFlags = WC_NO_BEST_FIT_CHARS; // 似た文字への自動変換を行わない
if (cp_output == 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_output, dwFlags, path, -1, out, MAX_LEN * 3, NULL, NULL) == 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);
printf(format, buf);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ファイル・パスがファイル・リスト上に既に存在するか調べる
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) // 大文字と小文字を区別する
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 += (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);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ファイル・パスからファイル名の位置を戻す
wchar_t * offset_file_name(wchar_t *file_path)
{
int i;
for (i = 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 = 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 = 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;
}
// 絶対パスかどうかを判定する
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 パスなら「\\<server>\<share>」
if (((path[0] == '\\') || (path[0] == '/')) && ((path[1] == '\\') || (path[1] == '/')))
return 1;
return 0;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#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);
// 相対パスに対して基準となるディレクトリが指定されてるなら
if ((dir_path != NULL) && (is_full_path(src_path) == 0)){
// そのまま基準ディレクトリを連結して絶対パスにする
len = 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);
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 = 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ファイルや「*?」で検索するソース・ファイル)
int name_len;
// 区切り文字を探す
name_len = wcslen(new_path);
for (len = name_len - 1; len >= 0; len--){
if (new_path[len] == '\\')
break;
}
len++;
wcscpy(tmp_path, new_path + len); // 存在しないファイル名を記録しておく
name_len -= 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();
return 0;
} else { // 退避させておいたファイル名を追加する
len += name_len;
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 wcslen(file_path); // そのままの長さ
// "\\?\UNC\<server>\<share>" ならネットワーク・パス
if (wcsncmp(file_path + PREFIX_LEN, L"UNC\\", 4) == 0)
return wcslen(file_path + PREFIX_LEN + 2); // "\\?\UNC\" -> "\\" なので6文字減らす
// "\\?\<drive:>\<path>" なら
return wcslen(file_path + PREFIX_LEN); // "\\?\" を省くので4文字減らす
}
// ファイル・パスから、先頭にある "\\?\" を省いてコピーする
int copy_without_prefix(
wchar_t *dst_path, // ()
wchar_t *src_path) // コピー元のパス
{
int i, len;
len = 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\<server>\<share>" ならネットワーク・パス
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;
}
// "\\?\<drive:>\<path>" なら
len -= PREFIX_LEN; // "\\?\UNC\" -> "\\" なので6文字減らす
for (i = 0; i <= len; i++)
dst_path[i] = src_path[PREFIX_LEN + i];
return len;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ファイルを置き換える (ファイル名の修正、テンポラリー・ファイルからの書き戻しなど)
int replace_file(
wchar_t *dest_path, // ()
wchar_t *sorc_path, // 置き換える元のパス (移動元、現在のファイル名)
int switch_b) // 既存のファイルをバックアップするかどうか
{
unsigned int rv, num;
// 移動元のファイルが存在するかどうか
num = GetFileAttributes(sorc_path);
if (num == INVALID_FILE_ATTRIBUTES){
return 1; // 移動させるファイルが存在しない
} else if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、その属性を解除する
if (SetFileAttributes(sorc_path, num ^ FILE_ATTRIBUTE_READONLY) == 0)
return 2; // 移動元の読み取り専用属性を解除できなかった
}
// 移動先のファイルが存在するかどうか
rv = GetFileAttributes(dest_path);
if (rv == INVALID_FILE_ATTRIBUTES){ // ファイルが存在しないなら、そのまま移動する
if (MoveFile(sorc_path, dest_path) == 0)
return 4; // 移動失敗
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){ // 既存のファイルを削除せずに別名で残す
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){ // バックアップを作って置き換える
num = ReplaceFile(dest_path, sorc_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0);
if (num == 0){
num = GetLastError();
if ((num == 32) || // The process cannot access the file because it is being used by another process.
(num == 1175) || // ERROR_UNABLE_TO_REMOVE_REPLACED
(num == 1176)){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT
Sleep(100);
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(100);
num = MoveFile(sorc_path, dest_path); // 既存ファイルはバックアップ済みなら、移動するだけ
} else {
num = 0;
}
if (num == 0)
return 6;
}
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);
if (num == 0){
num = GetLastError();
if ((num == 32) || // The process cannot access the file because it is being used by another process.
(num == 1175)){ // ERROR_UNABLE_TO_REMOVE_REPLACED
Sleep(100);
num = ReplaceFile(dest_path, sorc_path, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0);
} else if (num == 1176){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT
Sleep(100);
num = MoveFile(sorc_path, dest_path); // 既存ファイルは削除済みなら、移動するだけ
} else {
num = 0;
}
if (num == 0)
return 8;
}
if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す
rv = GetFileAttributes(dest_path);
SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない
}
}
}
return 0;
}
// ファイルを指定サイズに縮小する
int shorten_file(
wchar_t *file_path, // ファイル・パス
__int64 new_size,
int switch_b) // 既存のファイルをバックアップするかどうか
{
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){ // 既存のファイルを削除せずに別名で残す
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] == '.')){
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] == '.')){
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=警告, 16=エラー
int sanitize_filename(wchar_t *name)
{
int i, rv = 0, len = 0;
// 制御文字 131 (改行やタブなど) を削除する
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)
return 16;
// WinOS ではファイル名に「\/:*?"<>|」の文字は使えないので置換する
for (i = 0; i < len; i++){
if ((name[i] == '\\') || (name[i] == '/') || (name[i] == ':')
|| (name[i] == '*') || (name[i] == '?') || (name[i] == '"')
|| (name[i] == '<') || (name[i] == '>') || (name[i] == '|')){
name[i] = '_'; // 「\,/,:,*,?,",<,>,|」 ->「_」
rv |= 2;
}
}
// 先頭の「 」、末尾の「.」「 」を警告する
if ((name[0] == ' ') || (name[len - 1] == '.') || (name[len - 1] == ' '))
rv |= 8;
// デバイス名を警告する
if (check_device_name(name, len) != 0)
rv |= 8;
if (len == 0) // 最終的にファイル名が無くなればエラー
return 16;
return rv;
}
// 修復中のテンポラリ・ファイルの名前を作る
// 末尾に _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"); // 末尾に追加する
}
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]);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
unsigned int memory_use; // メモリー使用量 0=auto, 17 -> 1/8 7/8
// 空きメモリー量と制限値から使用できるメモリー量を計算する
unsigned int get_mem_size(unsigned __int64 data_size)
{
unsigned int mem_size, *tmp_p;
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 (data_size + cache_size < statex.ullAvailPhys){ // all file data can be cached in memory
mem_size64 = statex.ullAvailPhys - data_size; // using size = available size - data size
if (memory_use){ // limited upto ?/8
cache_size *= memory_use;
if (mem_size64 > cache_size)
mem_size64 = cache_size;
}
} else {
if (memory_use){ // manual setting
mem_size64 = cache_size * memory_use; // using size = available size / 8 * memory_use
} else { // auto setting (mostly for 32-bit OS)
// disk cache size is range from 100MB to 200MB.
cache_size += statex.ullTotalPhys >> 4; // cache size = available size / 8 + total size / 16
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; // keep 2MB for other task
if (mem_size64 >= 0x7F000000){ // max is 2032MB
mem_size = 0x7F000000;
} else {
mem_size = (unsigned int)mem_size64 & 0xFFFF0000; // multiple of 64KB
}
} else {
print_win32_err();
mem_size = 0x20000000; // メモリー量を認識できない場合は 512MB と仮定する
}
// try to allocate for test
//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 -= 0x01000000; // reduce 16MB
tmp_p = malloc(mem_size);
}
if (tmp_p != NULL)
free(tmp_p);
//printf("%d MB ok\n", mem_size >> 20);
return mem_size;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define MAX_NAME_LEN 69 // 経過表示のタイトルの最大文字数 (末尾の null 文字を含む)
int prog_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, wchar_t *file_name)
{
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) && (file_name != NULL)){ // 初めて経過を表示する時だけファイル名を表示する
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);
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;
}
// 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_cp(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);
}
// ファイルをゴミ箱に移す
int delete_file_recycle(wchar_t *file_path)
{
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);
} 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; // 削除がキャンセルされた
return rv;
}
// エクスプローラーで隠しファイルを表示する設定になってるか調べる
unsigned int get_show_hidden(void)
{
unsigned int rv;
SHELLSTATE ssf;
rv = FILE_ATTRIBUTE_HIDDEN;
// Explorer の設定を調べる
SHGetSetSettings(&ssf, SSF_SHOWALLOBJECTS | SSF_SHOWSUPERHIDDEN, FALSE);
// 隠しファイルを表示するかどうか
if (ssf.fShowAllObjects){ // 表示する設定なら
rv = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
// 保護されたオペレーティングシステムファイルを表示するかどうか
if (ssf.fShowSuperHidden) // 表示する設定なら
rv = INVALID_FILE_ATTRIBUTES;
}
return rv;
}