Files
MultiPar/source/sfv_md5/common.c
2024-11-30 13:08:09 +09:00

1040 lines
29 KiB
C
Raw Permalink 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 : The MIT license
#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 "common.h"
// グローバル変数
wchar_t checksum_file[MAX_LEN]; // チェックサム・ファイルのパス
wchar_t base_dir[MAX_LEN]; // ソース・ファイルの基準ディレクトリ
wchar_t ini_path[MAX_LEN]; // 検査結果ファイルのパス
int base_len; // ソース・ファイルの基準ディレクトリの長さ
int file_num; // ソース・ファイルの数
// 可変長サイズの領域にテキストを保存する
wchar_t *text_buf; // チェックサム・ファイルのテキスト内容
int text_len; // テキストの文字数
int text_max; // テキストの最大文字数
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
unsigned int cp_output; // Console Output Code Page
// 指定された Code Page から UTF-16 に変換する
int cp_to_utf16(char *in, wchar_t *out, int max_size, 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_size) == 0){
err = GetLastError();
if ((dwFlags != 0) && (err == ERROR_INVALID_FLAGS)){
//printf("MultiByteToWideChar, ERROR_INVALID_FLAGS\n");
if (MultiByteToWideChar(cp, 0, in, -1, out, max_size) != 0)
return 0;
err = GetLastError();
}
wcscpy(out, L"cannot encode");
return err;
}
return 0;
}
// Windown OS の UTF-16 から指定された Code Page に変換する
int utf16_to_cp(wchar_t *in, char *out, int max_size, 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_size, NULL, NULL) == 0){
if ((dwFlags != 0) && (GetLastError() == ERROR_INVALID_FLAGS)){
//printf("WideCharToMultiByte, ERROR_INVALID_FLAGS\n");
if (WideCharToMultiByte(cp, 0, in, -1, out, max_size, NULL, NULL) != 0)
return 0;
}
strcpy(out, "cannot encode");
return 1;
}
return 0;
}
// 文字列が 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;
// 禁止 0xC00xC10xF50xFF
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;
}
// 文字列が UTF-16 かどうかを判定して変換する
int utf16_to_utf16(unsigned char *in, int len, wchar_t *out)
{
unsigned char *text;
int i;
int little_endian = 0, space_mark = 0, return_mark = 0;
if (len % 2)
return 1; // サイズは 2の倍数のはず
// BOM を探す
if ((in[0] == 0xFF) && (in[1] == 0xFE)){
text = in + 2;
little_endian = 1; // UTF-16LE
} else if ((in[0] == 0xFE) && (in[1] == 0xFF)){
text = in + 2;
} else {
text = in;
}
// 必ず存在するはずのスペースと改行文字が一致するか調べる
if (little_endian == 1){
for (i = 0; i < len; i += 2){
if ((text[i] == ' ') && (text[i + 1] == 0))
space_mark++;
if (((text[i] == '\n') || (text[i] == '\r'))&& (text[i + 1] == 0))
return_mark++;
}
} else {
for (i = 0; i < len; i += 2){
if ((text[i + 1] == 0) && (text[i] == ' '))
space_mark++;
if ((text[i + 1] == 0) && ((text[i] == '\n') || (text[i] == '\r')))
return_mark++;
}
}
if ((space_mark == 0) && (return_mark == 0))
return 1;
// 変換する
if (little_endian == 1){
// Little Endian ならそのままコピーする
memcpy(out, text, len);
} else {
// Big Endian なら上位と下位を入れ替える
for (i = 0; i < len; i += 2){
out[i / 2] = ((unsigned short)(text[i]) << 8) || ((unsigned short)(text[i + 1]));
}
}
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;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ファイル・パスがファイル・リスト上に既に存在するか調べる
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);
}
// テキストに新しい文字列を追加する
int add_text(wchar_t *new_text) // 追加するテキスト
{
wchar_t *tmp_p;
int len;
len = wcslen(new_text);
if (text_len + len >= text_max){ // 領域が足りなくなるなら拡張する
text_max += ALLOC_LEN;
tmp_p = (wchar_t *)realloc(text_buf, text_max * 2);
if (tmp_p == NULL){
return 1;
} else {
text_buf = tmp_p;
}
}
wcscpy(text_buf + text_len, new_text);
text_len += len;
return 0;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ファイル・パスからファイル名の位置を戻す
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;
}
// ディレクトリ記号の「\」を「/」に置換する
void unix_directory(wchar_t *path)
{
wchar_t *tmp_p;
tmp_p = wcschr(path, '\\');
while (tmp_p != NULL){
*tmp_p = '/';
tmp_p = wcschr(tmp_p, '\\');
}
}
// 絶対パスかどうかを判定する
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;
}
// デバイス名かどうかを判定する
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=変更無し, +1=ディレクトリ記号, +2=浄化した, +4=文字数が変わった, +8=警告, 16=エラー
int sanitize_filename(wchar_t *name)
{
int i, j, rv = 0, len = 0, off;
// 制御文字 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){
name[0] = 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] = '_'; // 「*,?,",<,>,|」 ->「_」
rv |= 2;
} else if (name[i] == ':'){ // NTFS の「alternate data stream」の区切りに「:」が使われる
for (j = i + 1; j < len; j++){
// 「:」の後にディレクトリ記号や再度同じのが出現するのは駄目
if ((name[j] == '\\') || (name[j] == '/') || (name[j] == ':')){
j = -1;
break;
}
}
if ((i == 0) || (i == len - 1) || (j < 0)){
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--;
}
// 「\」の前に「.」があれば削除する (ディレクトリ移動を防ぐ)
// ただし、SFV/MD5 ではファイルを書き換えないので、親ディレクトリへの移動は許可する。
off = 0;
while ((name[off] == '.') && (name[off + 1] == '.') && (name[off + 2] == '\\'))
off += 3;
i = len - 1;
while (i > off){ // 先に許可した所まで遡る。
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 (len == 0){ // 最終的にファイル名が無くなればエラー
name[0] = 0;
return 16;
}
return rv;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#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文字減らす
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// ユニコードの16進数文字列から数値を読み取る
unsigned int get_val32h(wchar_t *s){
int i;
unsigned int v, ret = 0;
for (i = 0; i < 8; i++){
v = s[i];
if ((v >= 0x30) && (v <= 0x39)){ // 0 to 9
ret = (ret * 16) + (v - 0x30);
} else if ((v >= 0x61) && (v <= 0x66)){ // a to f
ret = (ret * 16) + (v + 10 - 0x61);
} else if ((v >= 0x41) && (v <= 0x46)){ // A to F
ret = (ret * 16) + (v + 10 - 0x41);
} else { // 数字以外の文字に出会ったら終わる
break;
}
}
return ret;
}
// 16進数の文字が何個続いてるか
unsigned int base16_len(wchar_t *s)
{
unsigned int v, len;
len = 0;
while (len <= 32){
v = s[len];
if ((v >= 0x30) && (v <= 0x39)){ // 0 to 9
len++;
} else if ((v >= 0x61) && (v <= 0x66)){ // a to f
len++;
} else if ((v >= 0x41) && (v <= 0x46)){ // A to F
len++;
} else { // 数字以外の文字に出会ったら終わる
break;
}
}
return len;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#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){ // 初めて経過を表示する時だけファイル名を表示する
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, sizeof(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; // 進捗状況をリセットする
}
}
// 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, sizeof(buf), CP_UTF8);
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);
}
// エクスプローラーで隠しファイルを表示する設定になってるか調べる
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;
}