fork download
  1. #define UNICODE /* for TCHAR, ::MessageBox() -> ::MessageBoxW() */
  2.  
  3. #include <windows.h>
  4. #include <iostream>
  5. #include <stack>
  6. #include <cassert>
  7. #include <cmath>
  8. #include <set>
  9. #include <sstream>
  10.  
  11. static bool flag_nondelete = false;
  12.  
  13. static_assert(sizeof(TCHAR) == sizeof(char16_t));
  14. using stringU16 = std::basic_string<char16_t>;
  15.  
  16. std::ostream &operator<<(std::ostream &os, stringU16 pUnicode) {
  17. int len = ::WideCharToMultiByte(CP_THREAD_ACP, 0, (TCHAR *)pUnicode.c_str(), -1, 0, 0, 0, 0);
  18. char *pAnsi = new char [len + 2];
  19. if (pAnsi) {
  20. int actualLen = ::WideCharToMultiByte(CP_THREAD_ACP, 0, (TCHAR *)pUnicode.c_str(), -1, pAnsi, len + 2, 0, 0);
  21. if (actualLen == len) {
  22. os << pAnsi; /* output */
  23. }
  24. delete pAnsi;
  25. }
  26. return os;
  27. }
  28.  
  29.  
  30. class deletingDest {
  31.  
  32. struct PathAndFileP {
  33. public:
  34. stringU16 path;
  35. bool isFileP;
  36. PathAndFileP(stringU16 path, bool isFileP) : path(path), isFileP(isFileP) {}
  37. friend bool operator<(PathAndFileP a, PathAndFileP b) {
  38. return a.path > b.path;
  39. }
  40. };
  41.  
  42. private:
  43. std::set<PathAndFileP> deletingDestSet;
  44.  
  45. public:
  46. deletingDest() {
  47. (this->deletingDestSet).clear();
  48. }
  49.  
  50. void accumulatingAllDestPath(stringU16 path) {
  51. if (path.find_last_of(stringU16(u"\\")) != path.length() - 1) path += u"\\"; /* */
  52. stringU16 path_findfirst = path + u"*.*";
  53.  
  54. WIN32_FIND_DATA FindFileData;
  55.  
  56. HANDLE hFindFirstNext = ::FindFirstFile((TCHAR *)path_findfirst.c_str(), &FindFileData);
  57. bool bInLoop = (hFindFirstNext != INVALID_HANDLE_VALUE);
  58.  
  59. while (bInLoop) {
  60. stringU16 path_filename;
  61. stringU16 filename = stringU16((char16_t *)FindFileData.cFileName);
  62.  
  63. if (filename == u"." || filename == u"..") goto FINDNEXTFILE;
  64. path_filename = path + filename;
  65.  
  66. if (::GetFileAttributes((TCHAR *)path_filename.c_str()) & FILE_ATTRIBUTE_DIRECTORY) { /* when now_path_filename is a directory */
  67. PathAndFileP pathAndFileP(path_filename, false);
  68. (this->deletingDestSet).insert(pathAndFileP);
  69. this->accumulatingAllDestPath(path_filename);
  70. } else { /* path_filename is file */
  71. PathAndFileP pathAndFileP(path_filename, true);
  72. (this->deletingDestSet).insert(pathAndFileP);
  73. }
  74. FINDNEXTFILE:
  75. bInLoop = ::FindNextFile(hFindFirstNext, &FindFileData);
  76. } /* while (bInLoop) { */
  77. ::FindClose(hFindFirstNext);
  78. return;
  79. }
  80.  
  81. void checkingPExitDestPath(stringU16 path) {
  82. std::set<PathAndFileP>::iterator p = deletingDestSet.find(PathAndFileP(path, false));
  83. if (p != deletingDestSet.end()) {
  84. deletingDestSet.erase(p);
  85. }
  86. }
  87.  
  88. stringU16 popFromSetRemainDestPath(bool *isFileP) {
  89. stringU16 returnValue;
  90. std::set<PathAndFileP>::iterator p = deletingDestSet.begin();
  91. if (p == deletingDestSet.end())
  92. return u"";
  93. returnValue = p->path;
  94. *isFileP = p->isFileP;
  95. deletingDestSet.erase(p); /* for c++11 */
  96.  
  97. return returnValue;
  98. }
  99. };
  100.  
  101. static deletingDest deletingDestS;
  102.  
  103.  
  104. std::string printSizeApproximately(uint64_t n) {
  105. /* 0 */ /* 1 */ /* 2 */ /* 3 */ /* 4 */
  106. static std::string Suffix[] = { std::string("bytes"), std::string("KiB"), std::string("MiB"), std::string("GiB"), std::string("TiB") };
  107. uint64_t t = n;
  108. uint64_t s = 1;
  109. uint64_t r;
  110. int i = 0;
  111. for (;;) {
  112. r = t;
  113. t >>= 10;
  114.  
  115. /* out of loop : 2 conditions */
  116. if (t == 0)
  117. break;
  118. if (i == 4)
  119. break;
  120.  
  121. s <<= 10;
  122. i++;
  123. }
  124. std::stringstream ss;
  125. if (i == 0) {
  126. ss << n << " " << Suffix[i];
  127. } else {
  128. if (r >= 1 && r < (int)(1024 / 100.0)) { /* 1.52KiB */
  129. ss << (int)(n * 100.0 / s) / 100.0 << ' ' << Suffix[i];
  130. } else if (r >= (int)(1024 / 100.0) && r < (int)(1024 / 10.0)) { /* 15.2KiB */
  131. ss << (int)(n * 10.0 / s) / 10.0 << ' ' << Suffix[i];
  132. } else { /* r >= (int)(1024 / 10.0 152KiB */
  133. ss << (int)((double)n / s) << ' ' << Suffix[i];
  134. }
  135. }
  136. std::string output_s = ss.str();
  137. return output_s;
  138. }
  139.  
  140. int const BuffSize = 32 * 1024 * 1024;
  141.  
  142. bool MyCopyFile(stringU16 src, stringU16 dest, uint64_t fileSize) {
  143. static char Buff[BuffSize];
  144. DWORD ActualReadBytes, ActualWriteBytes;
  145. HANDLE hFileSrc, hFileDest;
  146.  
  147. hFileSrc = ::CreateFile((TCHAR *)src.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
  148. if (hFileSrc == INVALID_HANDLE_VALUE) {
  149. std::cerr << "Cannot open(read) :" << src << std::endl;
  150. return false;
  151. }
  152. FILETIME ftSrcCreate, ftSrcLastAccess, ftSrcLastWrite;
  153. GetFileTime(hFileSrc, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
  154.  
  155. hFileDest = ::CreateFile((TCHAR *)dest.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
  156. if (hFileSrc == INVALID_HANDLE_VALUE) {
  157. std::cerr << "Cannot open(write) :" << dest << std::endl;
  158. ::CloseHandle(hFileSrc);
  159. return false;
  160. }
  161. SetFileTime(hFileDest, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
  162.  
  163. uint64_t nReadTotalBytes = 0;
  164. uint64_t nWriteTotalBytes = 0;
  165. /* process display */
  166. std::cout << std::endl;
  167.  
  168. for (;;) {
  169.  
  170. if (!::ReadFile(hFileSrc, Buff, BuffSize, &ActualReadBytes, 0)) {
  171. std::cerr << "Opend but cannot read: " << src << " : " << nReadTotalBytes << "/" << nWriteTotalBytes << std::endl;
  172. ::CloseHandle(hFileSrc);
  173. ::CloseHandle(hFileDest);
  174. return false;
  175. }
  176. nReadTotalBytes += ActualReadBytes;
  177.  
  178. /* out of loop */
  179. if (ActualReadBytes == 0)
  180. break;
  181.  
  182. if(!::WriteFile(hFileDest, Buff, ActualReadBytes, &ActualWriteBytes, 0)) {
  183. std::cerr << "Opend and read but cannot write: " << dest << " : " << nReadTotalBytes << "/" << nWriteTotalBytes << std::endl;
  184. ::CloseHandle(hFileSrc);
  185. ::CloseHandle(hFileDest);
  186. return false;
  187. }
  188.  
  189. nWriteTotalBytes += ActualWriteBytes;
  190. putchar('*');
  191.  
  192. }
  193. putchar('\n');
  194.  
  195. if (nReadTotalBytes != nWriteTotalBytes) {
  196. std::cout << nReadTotalBytes << "/" << nWriteTotalBytes << " : differ!!" << std::endl;
  197. ::CloseHandle(hFileSrc);
  198. ::CloseHandle(hFileDest);
  199. return false;
  200. }
  201.  
  202. std::cout << "Size: " << printSizeApproximately(nWriteTotalBytes) << std::endl;
  203. ::CloseHandle(hFileSrc);
  204. ::CloseHandle(hFileDest);
  205.  
  206. return true;
  207. }
  208.  
  209. struct PathPair {
  210. stringU16 src;
  211. stringU16 dest;
  212. PathPair(stringU16 src, stringU16 dest) : src(src), dest(dest) { }
  213. };
  214.  
  215. std::stack<PathPair> path_stack;
  216. std::stack<PathPair> path_created_directory;
  217.  
  218. static int availableFileNumS = 0;
  219. static int copiedFileNumS = 0;
  220. static uint64_t totalSizeIncremental = 0;
  221.  
  222. void copy_body(stringU16 original_dest, deletingDest *deletingDestP) {
  223. while (!path_stack.empty()) {
  224. PathPair path_pair = path_stack.top(); path_stack.pop();
  225. stringU16 now_path = path_pair.src;
  226. stringU16 now_dest_path = path_pair.dest;
  227.  
  228. if (now_path.find_last_of(stringU16(u"\\")) != now_path.length() - 1) now_path += u"\\"; /* */
  229. if (now_dest_path.find_last_of(stringU16(u"\\")) != now_dest_path.length() - 1) now_dest_path += u"\\"; /* */
  230. stringU16 now_path_findfirst = now_path + u"*.*";
  231.  
  232. WIN32_FIND_DATA FindFileData;
  233.  
  234. HANDLE hFindFirstNext = ::FindFirstFile((TCHAR *)now_path_findfirst.c_str(), &FindFileData);
  235. bool bInLoop = (hFindFirstNext != INVALID_HANDLE_VALUE);
  236.  
  237. while (bInLoop) {
  238. stringU16 now_path_filename;
  239. stringU16 now_dest_path_filename;
  240. stringU16 filename = stringU16((char16_t *)FindFileData.cFileName);
  241.  
  242. if (filename == u"." || filename == u"..") goto FINDNEXTFILE;
  243.  
  244. now_path_filename = now_path + filename;
  245. now_dest_path_filename = now_dest_path + filename;
  246.  
  247. /* prevent recursively-endless-loop */
  248. if (now_path_filename == original_dest)
  249. goto FINDNEXTFILE;
  250.  
  251. if (::GetFileAttributes((TCHAR *)now_path_filename.c_str()) & FILE_ATTRIBUTE_DIRECTORY) { /* when now_path_filename is a directory */
  252. if ((signed int)::GetFileAttributes((TCHAR *)now_dest_path_filename.c_str()) == -1) {
  253. if(::CreateDirectory((TCHAR *)now_dest_path_filename.c_str(), 0)) {
  254. std::cout << "Create directory: " << now_dest_path_filename << std::endl;
  255. path_created_directory.push(PathPair(now_path_filename, now_dest_path_filename));
  256. } else {
  257. std::cerr << "Failure to create directory: " << now_dest_path_filename << std::endl;
  258. break;
  259. }
  260. }
  261. deletingDestP->checkingPExitDestPath(now_dest_path_filename); /* remove from set::set<> */
  262. path_stack.push(PathPair(now_path_filename, now_dest_path_filename));
  263.  
  264. } else { /* when now_path_filename is a file */
  265. FILETIME ftSrc, ftDest;
  266. uint64_t timeSrc, timeDest;
  267. uint64_t srcFileSize, destFileSize;
  268. uint64_t tmp;
  269. availableFileNumS++;
  270.  
  271. bool flagExcecFileCopy = false;
  272.  
  273. HANDLE hSrc = ::CreateFile((TCHAR *)now_path_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  274. HANDLE hDest = ::CreateFile((TCHAR *)now_dest_path_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  275.  
  276. /* when src's file and dest's file size are different, do copy */
  277. tmp = 0;
  278. srcFileSize = ::GetFileSize(hSrc, (LPDWORD)&tmp);
  279. srcFileSize |= (tmp << 32);
  280.  
  281. destFileSize = 0;
  282. if (hDest != (HANDLE *)0xffffffffffffffff) {
  283. destFileSize = ::GetFileSize(hSrc, (LPDWORD)&tmp);
  284. destFileSize |= (tmp << 32);
  285. deletingDestP->checkingPExitDestPath(now_dest_path_filename); /* remove from set::set<> */
  286. }
  287.  
  288. if (srcFileSize != destFileSize)
  289. flagExcecFileCopy |= true;
  290.  
  291. /* when (now_path_filename(Src)'s time) > (now_dest_path_filename(Dest)'s time), do copy */
  292. ftSrc = {0, 0}; ftDest = {0, 0};
  293. ::GetFileTime(hSrc, 0, 0, &ftSrc);
  294. ::GetFileTime(hDest, 0, 0, &ftDest);
  295. timeSrc = ftSrc.dwHighDateTime; timeSrc <<= 32, timeSrc |= ftSrc.dwLowDateTime; /* Src's time */
  296. timeDest = ftDest.dwHighDateTime; timeDest <<= 32, timeDest |= ftDest.dwLowDateTime; /* Dest's time */
  297. ::CloseHandle(hSrc); ::CloseHandle(hDest);
  298.  
  299. assert(hDest != (HANDLE *)0xffffffffffffffff || timeDest == 0); /* important assumption */
  300.  
  301. if (timeSrc > timeDest)
  302. flagExcecFileCopy |= true;
  303.  
  304. if (flagExcecFileCopy) {
  305. std::cout << now_path_filename << " -> " << now_dest_path_filename << " ";
  306. if (MyCopyFile(now_path_filename, now_dest_path_filename, srcFileSize)) {
  307. copiedFileNumS++;
  308. std::cout << "(" << copiedFileNumS << ") : OK ";
  309. totalSizeIncremental += ((srcFileSize > destFileSize) ? srcFileSize - destFileSize : srcFileSize);
  310. std::cout << srcFileSize << ":" << destFileSize << ":" << printSizeApproximately(totalSizeIncremental) << std::endl;
  311. } /* if */
  312. } /* if */
  313. } /* if-else */
  314. FINDNEXTFILE:
  315. bInLoop = ::FindNextFile(hFindFirstNext, &FindFileData);
  316.  
  317. } /* while(bInLoop) */
  318. if (hFindFirstNext != INVALID_HANDLE_VALUE)
  319. ::FindClose(hFindFirstNext);
  320. } /* while(!path_stack.empty()) */
  321. }
  322.  
  323. void setWideChar_fromAnsi(stringU16 &unicodeString, const char *ansiString) {
  324. int len = ::MultiByteToWideChar(CP_THREAD_ACP, 0, ansiString, -1, 0, 0);
  325. char16_t *pUnicode = new char16_t [len + 2];
  326. if (!pUnicode) { unicodeString = stringU16(u""); delete pUnicode; return; }
  327. int actualLen = ::MultiByteToWideChar(CP_THREAD_ACP, 0, ansiString, -1, (TCHAR *)pUnicode, len + 2);
  328. if (actualLen != len) { unicodeString = stringU16(u""); delete pUnicode; return; }
  329. unicodeString = stringU16(pUnicode);
  330. delete pUnicode;
  331. }
  332.  
  333. int main(int argc, char *argv[]) {
  334.  
  335. if (argc != 3 && argc != 4) {
  336. label_usage:
  337. std::cerr << argv[0] << " : copy files and directory recursively." << std::endl;
  338. std::cerr << "usage: " << argv[0] << " <src-path> <dest-path> " << std::endl;
  339. std::cerr << "option: /nondelete : not deleting." << std::endl;
  340. return 0;
  341. }
  342.  
  343. int const n = 1024;
  344. char16_t buff[n];
  345. GetCurrentDirectory(n, (TCHAR *)buff);
  346. stringU16 current_directory(buff);
  347.  
  348. stringU16 src;
  349. stringU16 dest;
  350.  
  351. int nowstep = 0;
  352. for (int i = 1; i < argc; i++) {
  353. if (strstr(argv[i], "/nondelete") == argv[i]) {
  354. flag_nondelete = true;
  355. continue;
  356. }
  357. switch (nowstep) {
  358. case 0:
  359. setWideChar_fromAnsi(src, argv[i]);
  360. nowstep++;
  361. continue;
  362. case 1:
  363. setWideChar_fromAnsi(dest, argv[2]);
  364. nowstep++;
  365. continue;
  366. default:
  367. goto label_usage;
  368. }
  369. }
  370. if (nowstep != 2) {
  371. std::cout << "argc > 3" << std::endl;
  372. goto label_usage;
  373. }
  374.  
  375. if (dest == u".") {
  376. std::cerr << argv[0] << ": cannot specify current directory for the destination path" << std::endl;
  377. return 0;
  378. }
  379. if (src == u".") {
  380. src.clear();
  381. }
  382. if (src == u".." || dest == u".." ) {
  383. std::cerr << argv[0] << ": cannot use the parent directory's expression \"..\". " << std::endl;
  384. return 0;
  385. }
  386. /* translate to abusolute path */
  387. if (src.find(u":") == stringU16::npos) { /* src is relative path */
  388. if (src.empty()) {
  389. src = current_directory;
  390. } else {
  391. src = current_directory + u"\\" + src;
  392. }
  393. }
  394. if (dest.find(u":") == stringU16::npos) { /* src is relative path */
  395. dest = current_directory + u"\\" + dest;
  396. }
  397. /* cf. https://docs.microsoft.com/ja-jp/windows/desktop/FileIO/naming-a-file */
  398. src = u"\\\\?\\" + src;
  399. dest = u"\\\\?\\" + dest;
  400.  
  401. int attributes = ::GetFileAttributes((TCHAR *)dest.c_str());
  402. if (attributes == -1) {
  403. std::cerr << "Directory: " << dest << " is not exists, aborted." << std::endl;
  404. return 0;
  405. }
  406.  
  407. /* */
  408. deletingDestS.accumulatingAllDestPath(dest);
  409.  
  410. /* */
  411. path_stack.push(PathPair(src, dest));
  412. copy_body(dest, &deletingDestS);
  413.  
  414. std::cout << "available: " << availableFileNumS << ", copied: " << copiedFileNumS << "." << std::endl;
  415.  
  416. /* */
  417. bool isFileP;
  418. stringU16 forDeleteDestPath;
  419. while ((forDeleteDestPath = deletingDestS.popFromSetRemainDestPath(&isFileP)) != u"") {
  420. if (isFileP) {
  421. if (::GetFileAttributes((TCHAR *)forDeleteDestPath.c_str()) != INVALID_FILE_ATTRIBUTES) {
  422. if (!flag_nondelete) {
  423. ::DeleteFile((TCHAR *)forDeleteDestPath.c_str());
  424. std::cout << "delete(file): " << forDeleteDestPath << std::endl;
  425. }
  426. }
  427. } else {
  428. if (::GetFileAttributes((TCHAR *)forDeleteDestPath.c_str()) != INVALID_FILE_ATTRIBUTES) {
  429. if (!flag_nondelete) {
  430. ::RemoveDirectory((TCHAR *)forDeleteDestPath.c_str()); /* now dir to be empty */
  431. std::cout << "delete:(dir) " << forDeleteDestPath << std::endl;
  432. }
  433. }
  434. }
  435. }
  436.  
  437. /* set created directory(= dest)'s timestamp as src's directory is */
  438. /* */
  439.  
  440. while (!path_created_directory.empty()) {
  441. PathPair path_created = path_created_directory.top(); path_created_directory.pop();
  442. stringU16 src_directory = path_created.src;
  443. stringU16 dest_directory = path_created.dest;
  444.  
  445. HANDLE hSrc = ::CreateFile((TCHAR *)src_directory.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
  446. HANDLE hDest = ::CreateFile((TCHAR *)dest_directory.c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
  447.  
  448. FILETIME ftSrcCreate, ftSrcLastAccess, ftSrcLastWrite;
  449. ::GetFileTime(hSrc, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
  450. ::SetFileTime(hDest, &ftSrcCreate, &ftSrcLastAccess, &ftSrcLastWrite);
  451. ::CloseHandle(hSrc); ::CloseHandle(hDest);
  452. }
  453. return 0;
  454. }
  455. /* end */
  456.  
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
prog.cpp:3:10: fatal error: windows.h: No such file or directory
 #include <windows.h>
          ^~~~~~~~~~~
compilation terminated.
stdout
Standard output is empty