#define UNICODE /* for TCHAR, ::MessageBox() -> ::MessageBoxW() */
#include <windows.h>
#include <iostream>
#include <stack>
#include <cassert>
static_assert(sizeof(TCHAR) == sizeof(char16_t));
using stringU16 = std::basic_string<char16_t>;
std::ostream &operator<<(std::ostream &os, stringU16 pUnicode) {
int len = ::WideCharToMultiByte(CP_THREAD_ACP, 0, (TCHAR *)pUnicode.c_str(), -1, 0, 0, 0, 0);
char *pAnsi = new char [len + 2];
if (pAnsi) {
int actualLen = ::WideCharToMultiByte(CP_THREAD_ACP, 0, (TCHAR *)pUnicode.c_str(), -1, pAnsi, len + 2, 0, 0);
if (actualLen == len) {
os << pAnsi; /* output */
}
delete pAnsi;
}
return os;
}
int const BuffSize = 65536;
bool MyCopyFile(stringU16 src, stringU16 dest) {
static char Buff[BuffSize];
DWORD ActualReadBytes, ActualWriteBytes;
HANDLE hFileSrc, hFileDest;
hFileSrc = ::CreateFile((TCHAR *)src.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
if (hFileSrc == INVALID_HANDLE_VALUE) {
std::cerr << "Cannot open(read) :" << src << std::endl;
return false;
}
FILETIME ftSrcCreate, ftSrcLastAccess, ftSrcLastWrite;
GetFileTime(hFileSrc, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
hFileDest = ::CreateFile((TCHAR *)dest.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
if (hFileSrc == INVALID_HANDLE_VALUE) {
std::cerr << "Cannot open(write) :" << dest << std::endl;
::CloseHandle(hFileSrc);
return false;
}
SetFileTime(hFileDest, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
int nReadTotalBytes = 0;
int nWriteTotalBytes = 0;
for (;;) {
if (!::ReadFile(hFileSrc, Buff, BuffSize, &ActualReadBytes, 0)) {
std::cerr << "Opend but cannot read: " << src << " : " << nReadTotalBytes << "/" << nWriteTotalBytes << std::endl;
::CloseHandle(hFileSrc);
::CloseHandle(hFileDest);
return false;
}
nReadTotalBytes += ActualReadBytes;
/* out of loop */
if (ActualReadBytes == 0)
break;
if(!::WriteFile(hFileDest, Buff, ActualReadBytes, &ActualWriteBytes, 0)) {
std::cerr << "Opend and read but cannot write: " << dest << " : " << nReadTotalBytes << "/" << nWriteTotalBytes << std::endl;
::CloseHandle(hFileSrc);
::CloseHandle(hFileDest);
return false;
}
nWriteTotalBytes += ActualWriteBytes;
}
std::cout << nReadTotalBytes << "/" << nWriteTotalBytes << " ";
::CloseHandle(hFileSrc);
::CloseHandle(hFileDest);
return true;
}
struct PathPair {
stringU16 src;
stringU16 dest;
PathPair(stringU16 src, stringU16 dest) : src(src), dest(dest) { }
};
std::stack<PathPair> path_stack;
std::stack<PathPair> path_created_directory;
void copy_body(stringU16 original_dest) {
while (!path_stack.empty()) {
PathPair path_pair = path_stack.top(); path_stack.pop();
stringU16 now_path = path_pair.src;
stringU16 now_dest_path = path_pair.dest;
if (now_path.find_last_of(stringU16(u"\\")) != now_path.length() - 1) now_path += u"\\"; /* '/' で終わらない場合に '/' を追加 */
if (now_dest_path.find_last_of(stringU16(u"\\")) != now_dest_path.length() - 1) now_dest_path += u"\\"; /* '/' で終わらない場合に '/' を追加 */
stringU16 now_path_findfirst = now_path + u"*.*";
WIN32_FIND_DATA FindFileData;
HANDLE hFindFirstNext = ::FindFirstFile((TCHAR *)now_path_findfirst.c_str(), &FindFileData);
bool bInLoop = (hFindFirstNext != INVALID_HANDLE_VALUE);
while (bInLoop) {
stringU16 now_path_filename;
stringU16 now_dest_path_filename;
stringU16 filename = stringU16((char16_t *)FindFileData.cFileName);
if (filename == u"." || filename == u"..") goto FINDNEXTFILE;
now_path_filename = now_path + filename;
now_dest_path_filename = now_dest_path + filename;
/* prevent recursively-endless-loop */
if (now_path_filename == original_dest)
goto FINDNEXTFILE;
if (::GetFileAttributes((TCHAR *)now_path_filename.c_str()) & FILE_ATTRIBUTE_DIRECTORY) { /* when now_path_filename is a directory */
if ((signed int)::GetFileAttributes((TCHAR *)now_dest_path_filename.c_str()) == -1) {
if(::CreateDirectory((TCHAR *)now_dest_path_filename.c_str(), 0)) {
std::cout << "Create directory: " << now_dest_path_filename << std::endl;
path_created_directory.push(PathPair(now_path_filename, now_dest_path_filename));
} else {
std::cerr << "Failure to create directory: " << now_dest_path_filename << std::endl;
break;
}
}
path_stack.push(PathPair(now_path_filename, now_dest_path_filename));
} else { /* when now_path_filename is a file */
/* when (now_path_filename(Src)'s time) > (now_dest_path_filename(Dest)'s time), copy */
HANDLE hSrc = ::CreateFile((TCHAR *)now_path_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
HANDLE hDest = ::CreateFile((TCHAR *)now_dest_path_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
FILETIME ftSrc = {0, 0}, ftDest = {0, 0};
::GetFileTime(hSrc, 0, 0, &ftSrc);
::GetFileTime(hDest, 0, 0, &ftDest);
uint64_t timeSrc = ftSrc.dwHighDateTime; timeSrc <<= 32, timeSrc |= ftSrc.dwLowDateTime; /* Src's time */
uint64_t timeDest = ftDest.dwHighDateTime; timeDest <<= 32, timeDest |= ftDest.dwLowDateTime; /* Dest's time */
::CloseHandle(hSrc); ::CloseHandle(hDest);
assert(hDest != INVALID_HANDLE_VALUE || timeDest == 0); /* important assumption */
if (timeSrc > timeDest) {
std::cout << now_path_filename << " -> " << now_dest_path_filename << " ";
if (MyCopyFile(now_path_filename, now_dest_path_filename)) {
std::cout << " : OK" << std::endl;
}
}
}
FINDNEXTFILE:
bInLoop = ::FindNextFile(hFindFirstNext, &FindFileData);
} /* while(bInLoop) */
if (hFindFirstNext != INVALID_HANDLE_VALUE)
::FindClose(hFindFirstNext);
} /* while(!path_stack.empty()) */
}
void setWideChar_fromAnsi(stringU16 &unicodeString, const char *ansiString) {
int len = ::MultiByteToWideChar(CP_THREAD_ACP, 0, ansiString, -1, 0, 0);
char16_t *pUnicode = new char16_t [len + 2];
if (!pUnicode) { unicodeString = stringU16(u""); delete pUnicode; return; }
int actualLen = ::MultiByteToWideChar(CP_THREAD_ACP, 0, ansiString, -1, (TCHAR *)pUnicode, len + 2);
if (actualLen != len) { unicodeString = stringU16(u""); delete pUnicode; return; }
unicodeString = stringU16(pUnicode);
delete pUnicode;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
std::cerr << argv[0] << " : copy files and directory recursively." << std::endl;
std::cerr << "usage: " << argv[0] << " <src-path> <dest-path> " << std::endl;
return 0;
}
int const n = 256;
char16_t buff[n];
GetCurrentDirectory(n, (TCHAR *)buff);
stringU16 current_directory(buff);
stringU16 src; setWideChar_fromAnsi(src, argv[1]);
stringU16 dest; setWideChar_fromAnsi(dest, argv[2]);
if (dest == u".") {
std::cerr << argv[0] << ": cannot specify current directory for the destination path" << std::endl;
return 0;
}
if (src == u".") {
src.clear();
}
if (src == u".." || dest == u".." ) {
std::cerr << argv[0] << ": cannot use the parent directory's expression \"..\". " << std::endl;
return 0;
}
/* translate to abusolute path */
if (src.find(u":") == stringU16::npos) { /* src is relative path */
if (src.empty()) {
src = current_directory;
} else {
src = current_directory + u"\\" + src;
}
}
if (dest.find(u":") == stringU16::npos) { /* src is relative path */
dest = current_directory + u"\\" + dest;
}
/* cf. https://docs.microsoft.com/ja-jp/windows/desktop/FileIO/naming-a-file */
src = u"\\\\?\\" + src;
dest = u"\\\\?\\" + dest;
int attributes = ::GetFileAttributes((TCHAR *)dest.c_str());
if (attributes == -1) {
std::cerr << "Directory: " << dest << " is not exists, aborted." << std::endl;
return 0;
}
path_stack.push(PathPair(src, dest));
copy_body(dest);
/* set created directory(= dest)'s timestamp as src's directory is */
/* 新規に作ったディレクトリのファイルスタンプはプログラムの最後で、最後に設定するしかない */
while (!path_created_directory.empty()) {
PathPair path_created = path_created_directory.top(); path_created_directory.pop();
stringU16 src_directory = path_created.src;
stringU16 dest_directory = path_created.dest;
HANDLE hSrc = ::CreateFile((TCHAR *)src_directory.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
HANDLE hDest = ::CreateFile((TCHAR *)dest_directory.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
FILETIME ftSrcCreate, ftSrcLastAccess, ftSrcLastWrite;
::GetFileTime(hSrc, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
::SetFileTime(hDest, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
::CloseHandle(hSrc); ::CloseHandle(hDest);
}
return 0;
}
/* end */