1 #include "libfilezilla/recursive_remove.hpp" 2 #include "libfilezilla/local_filesys.hpp" 3 4 #ifndef FZ_WINDOWS 5 #include <unistd.h> 6 #endif 7 8 namespace fz { 9 remove(const native_string & path)10bool recursive_remove::remove(const native_string& path) 11 { 12 std::list<native_string> paths; 13 paths.push_back(path); 14 return remove(paths); 15 } 16 remove(std::list<native_string> dirsToVisit)17bool recursive_remove::remove(std::list<native_string> dirsToVisit) 18 { 19 bool success = true; 20 21 // Under Windows use SHFileOperation to delete files and directories. 22 // Under other systems, we have to recurse into subdirectories manually 23 // to delete all contents. 24 25 #ifdef FZ_WINDOWS 26 // SHFileOperation accepts a list of null-terminated strings. Go through all 27 // paths to get the required buffer length 28 29 size_t len = 1; // String list terminated by empty string 30 31 for (auto const& dir : dirsToVisit) { 32 len += dir.size() + 1; 33 } 34 35 // Allocate memory 36 native_string::value_type* pBuffer = new native_string::value_type[len]; 37 native_string::value_type* p = pBuffer; 38 39 for (auto& dir : dirsToVisit) { 40 if (!dir.empty() && local_filesys::is_separator(dir.back())) { 41 dir.pop_back(); 42 } 43 if (local_filesys::get_file_type(dir) == local_filesys::unknown) { 44 continue; 45 } 46 47 wcscpy(p, dir.c_str()); 48 p += dir.size() + 1; 49 } 50 if (p != pBuffer) { 51 *p = 0; 52 53 // Now we can delete the files in the buffer 54 SHFILEOPSTRUCT op{}; 55 op.wFunc = FO_DELETE; 56 op.pFrom = pBuffer; 57 58 adjust_shfileop(op); 59 60 if (SHFileOperation(&op) != 0) { 61 success = false; 62 } 63 } 64 delete [] pBuffer; 65 #else 66 if (!confirm()) { 67 return false; 68 } 69 70 for (auto& dir : dirsToVisit) { 71 if (dir.size() > 1 && dir.back() == '/') { 72 dir.pop_back(); 73 } 74 } 75 76 // Remember the directories to delete after recursing into them 77 std::list<native_string> dirsToDelete; 78 79 local_filesys fs; 80 81 // Process all dirctories that have to be visited 82 while (!dirsToVisit.empty()) { 83 auto const iter = dirsToVisit.begin(); 84 native_string const& path = *iter; 85 86 if (path.empty()) { 87 dirsToVisit.erase(iter); 88 continue; 89 } 90 91 if (fs.get_file_type(path) != local_filesys::dir) { 92 if (unlink(path.c_str()) != 0) { 93 success = false; 94 } 95 dirsToVisit.erase(iter); 96 continue; 97 } 98 99 dirsToDelete.splice(dirsToDelete.begin(), dirsToVisit, iter); 100 101 if (!fs.begin_find_files(path, false)) { 102 continue; 103 } 104 105 // Depending on underlying platform, wxDir does not handle 106 // changes to the directory contents very well. 107 // See https://trac.filezilla-project.org/ticket/3482 108 // To work around this, delete files after enumerating everything in current directory 109 std::list<native_string> filesToDelete; 110 111 native_string file; 112 while (fs.get_next_file(file)) { 113 if (file.empty()) { 114 continue; 115 } 116 117 native_string const fullName = path + fzT("/") + file; 118 119 if (local_filesys::get_file_type(fullName) == local_filesys::dir) { 120 dirsToVisit.push_back(fullName); 121 } 122 else { 123 filesToDelete.push_back(fullName); 124 } 125 } 126 fs.end_find_files(); 127 128 // Delete all files and links in current directory enumerated before 129 for (auto const& filename : filesToDelete) { 130 if (unlink(filename.c_str()) != 0) { 131 success = false; 132 } 133 } 134 } 135 136 // Delete the now empty directories 137 for (auto const& dir : dirsToDelete) { 138 if (rmdir(dir.c_str()) != 0) { 139 success = false; 140 } 141 } 142 #endif 143 144 return success; 145 } 146 147 #ifdef FZ_WINDOWS adjust_shfileop(SHFILEOPSTRUCT & op)148void recursive_remove::adjust_shfileop(SHFILEOPSTRUCT & op) 149 { 150 op.fFlags = FOF_NO_UI | FOF_ALLOWUNDO; 151 } 152 #endif 153 154 } 155