directory.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. #pragma once
  2. #include <nall/file.hpp>
  3. #include <nall/function.hpp>
  4. #include <nall/inode.hpp>
  5. #include <nall/intrinsics.hpp>
  6. #include <nall/merge-sort.hpp>
  7. #include <nall/string.hpp>
  8. #include <nall/vector.hpp>
  9. #if defined(PLATFORM_WINDOWS)
  10. #include <nall/windows/utf8.hpp>
  11. #else
  12. #include <dirent.h>
  13. #include <stdio.h>
  14. #include <sys/types.h>
  15. #endif
  16. namespace nall {
  17. struct directory : inode {
  18. directory() = delete;
  19. static auto copy(const string& source, const string& target) -> bool; //recursive
  20. static auto create(const string& pathname, uint permissions = 0755) -> bool; //recursive
  21. static auto remove(const string& pathname) -> bool; //recursive
  22. static auto exists(const string& pathname) -> bool;
  23. static auto folders(const string& pathname, const string& pattern = "*") -> vector<string> {
  24. auto folders = directory::ufolders(pathname, pattern);
  25. folders.sort();
  26. for(auto& folder : folders) folder.append("/"); //must append after sorting
  27. return folders;
  28. }
  29. static auto files(const string& pathname, const string& pattern = "*") -> vector<string> {
  30. auto files = directory::ufiles(pathname, pattern);
  31. files.sort();
  32. return files;
  33. }
  34. static auto contents(const string& pathname, const string& pattern = "*") -> vector<string> {
  35. auto folders = directory::ufolders(pathname); //pattern search of contents should only filter files
  36. folders.sort();
  37. for(auto& folder : folders) folder.append("/"); //must append after sorting
  38. auto files = directory::ufiles(pathname, pattern);
  39. files.sort();
  40. for(auto& file : files) folders.append(file);
  41. return folders;
  42. }
  43. static auto ifolders(const string& pathname, const string& pattern = "*") -> vector<string> {
  44. auto folders = ufolders(pathname, pattern);
  45. folders.isort();
  46. for(auto& folder : folders) folder.append("/"); //must append after sorting
  47. return folders;
  48. }
  49. static auto ifiles(const string& pathname, const string& pattern = "*") -> vector<string> {
  50. auto files = ufiles(pathname, pattern);
  51. files.isort();
  52. return files;
  53. }
  54. static auto icontents(const string& pathname, const string& pattern = "*") -> vector<string> {
  55. auto folders = directory::ufolders(pathname); //pattern search of contents should only filter files
  56. folders.isort();
  57. for(auto& folder : folders) folder.append("/"); //must append after sorting
  58. auto files = directory::ufiles(pathname, pattern);
  59. files.isort();
  60. for(auto& file : files) folders.append(file);
  61. return folders;
  62. }
  63. static auto rcontents(const string& pathname, const string& pattern = "*") -> vector<string> {
  64. vector<string> contents;
  65. function<void (const string&, const string&, const string&)>
  66. recurse = [&](const string& basename, const string& pathname, const string& pattern) {
  67. for(auto& folder : directory::ufolders(pathname)) {
  68. contents.append(string{pathname, folder, "/"}.trimLeft(basename, 1L));
  69. recurse(basename, {pathname, folder, "/"}, pattern);
  70. }
  71. for(auto& file : directory::ufiles(pathname, pattern)) {
  72. contents.append(string{pathname, file}.trimLeft(basename, 1L));
  73. }
  74. };
  75. for(auto& folder : directory::ufolders(pathname)) {
  76. contents.append({folder, "/"});
  77. recurse(pathname, {pathname, folder, "/"}, pattern);
  78. }
  79. for(auto& file : directory::ufiles(pathname, pattern)) {
  80. contents.append(file);
  81. }
  82. contents.sort();
  83. return contents;
  84. }
  85. static auto ircontents(const string& pathname, const string& pattern = "*") -> vector<string> {
  86. vector<string> contents;
  87. function<void (const string&, const string&, const string&)>
  88. recurse = [&](const string& basename, const string& pathname, const string& pattern) {
  89. for(auto& folder : directory::ufolders(pathname)) {
  90. contents.append(string{pathname, folder, "/"}.trimLeft(basename, 1L));
  91. recurse(basename, {pathname, folder, "/"}, pattern);
  92. }
  93. for(auto& file : directory::ufiles(pathname, pattern)) {
  94. contents.append(string{pathname, file}.trimLeft(basename, 1L));
  95. }
  96. };
  97. for(auto& folder : directory::ufolders(pathname)) {
  98. contents.append({folder, "/"});
  99. recurse(pathname, {pathname, folder, "/"}, pattern);
  100. }
  101. for(auto& file : directory::ufiles(pathname, pattern)) {
  102. contents.append(file);
  103. }
  104. contents.isort();
  105. return contents;
  106. }
  107. static auto rfolders(const string& pathname, const string& pattern = "*") -> vector<string> {
  108. vector<string> folders;
  109. for(auto& folder : rcontents(pathname, pattern)) {
  110. if(directory::exists({pathname, folder})) folders.append(folder);
  111. }
  112. return folders;
  113. }
  114. static auto irfolders(const string& pathname, const string& pattern = "*") -> vector<string> {
  115. vector<string> folders;
  116. for(auto& folder : ircontents(pathname, pattern)) {
  117. if(directory::exists({pathname, folder})) folders.append(folder);
  118. }
  119. return folders;
  120. }
  121. static auto rfiles(const string& pathname, const string& pattern = "*") -> vector<string> {
  122. vector<string> files;
  123. for(auto& file : rcontents(pathname, pattern)) {
  124. if(file::exists({pathname, file})) files.append(file);
  125. }
  126. return files;
  127. }
  128. static auto irfiles(const string& pathname, const string& pattern = "*") -> vector<string> {
  129. vector<string> files;
  130. for(auto& file : ircontents(pathname, pattern)) {
  131. if(file::exists({pathname, file})) files.append(file);
  132. }
  133. return files;
  134. }
  135. private:
  136. //internal functions; these return unsorted lists
  137. static auto ufolders(const string& pathname, const string& pattern = "*") -> vector<string>;
  138. static auto ufiles(const string& pathname, const string& pattern = "*") -> vector<string>;
  139. };
  140. inline auto directory::copy(const string& source, const string& target) -> bool {
  141. bool result = true;
  142. if(!directory::create(target)) return result = false;
  143. for(auto& name : directory::folders(source)) {
  144. if(!directory::copy({source, name}, {target, name})) result = false;
  145. }
  146. for(auto& name : directory::files(source)) {
  147. if(!file::copy({source, name}, {target, name})) result = false;
  148. }
  149. return result;
  150. }
  151. #if defined(PLATFORM_WINDOWS)
  152. inline auto directory::create(const string& pathname, uint permissions) -> bool {
  153. string path;
  154. auto list = string{pathname}.transform("\\", "/").trimRight("/").split("/");
  155. bool result = true;
  156. for(auto& part : list) {
  157. path.append(part, "/");
  158. if(directory::exists(path)) continue;
  159. result &= (_wmkdir(utf16_t(path)) == 0);
  160. }
  161. return result;
  162. }
  163. inline auto directory::remove(const string& pathname) -> bool {
  164. auto list = directory::contents(pathname);
  165. for(auto& name : list) {
  166. if(name.endsWith("/")) directory::remove({pathname, name});
  167. else file::remove({pathname, name});
  168. }
  169. return _wrmdir(utf16_t(pathname)) == 0;
  170. }
  171. inline auto directory::exists(const string& pathname) -> bool {
  172. string name = pathname;
  173. name.trim("\"", "\"");
  174. DWORD result = GetFileAttributes(utf16_t(name));
  175. if(result == INVALID_FILE_ATTRIBUTES) return false;
  176. return (result & FILE_ATTRIBUTE_DIRECTORY);
  177. }
  178. inline auto directory::ufolders(const string& pathname, const string& pattern) -> vector<string> {
  179. if(!pathname) {
  180. //special root pseudo-folder (return list of drives)
  181. wchar_t drives[PATH_MAX] = {0};
  182. GetLogicalDriveStrings(PATH_MAX, drives);
  183. wchar_t* p = drives;
  184. while(*p || *(p + 1)) {
  185. if(!*p) *p = ';';
  186. *p++;
  187. }
  188. return string{(const char*)utf8_t(drives)}.replace("\\", "/").split(";");
  189. }
  190. vector<string> list;
  191. string path = pathname;
  192. path.transform("/", "\\");
  193. if(!path.endsWith("\\")) path.append("\\");
  194. path.append("*");
  195. HANDLE handle;
  196. WIN32_FIND_DATA data;
  197. handle = FindFirstFile(utf16_t(path), &data);
  198. if(handle != INVALID_HANDLE_VALUE) {
  199. if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
  200. if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  201. string name = (const char*)utf8_t(data.cFileName);
  202. if(name.match(pattern)) list.append(name);
  203. }
  204. }
  205. while(FindNextFile(handle, &data) != false) {
  206. if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
  207. if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  208. string name = (const char*)utf8_t(data.cFileName);
  209. if(name.match(pattern)) list.append(name);
  210. }
  211. }
  212. }
  213. FindClose(handle);
  214. }
  215. return list;
  216. }
  217. inline auto directory::ufiles(const string& pathname, const string& pattern) -> vector<string> {
  218. if(!pathname) return {};
  219. vector<string> list;
  220. string path = pathname;
  221. path.transform("/", "\\");
  222. if(!path.endsWith("\\")) path.append("\\");
  223. path.append("*");
  224. HANDLE handle;
  225. WIN32_FIND_DATA data;
  226. handle = FindFirstFile(utf16_t(path), &data);
  227. if(handle != INVALID_HANDLE_VALUE) {
  228. if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  229. string name = (const char*)utf8_t(data.cFileName);
  230. if(name.match(pattern)) list.append(name);
  231. }
  232. while(FindNextFile(handle, &data) != false) {
  233. if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  234. string name = (const char*)utf8_t(data.cFileName);
  235. if(name.match(pattern)) list.append(name);
  236. }
  237. }
  238. FindClose(handle);
  239. }
  240. return list;
  241. }
  242. #else
  243. inline auto directoryIsFolder(DIR* dp, struct dirent* ep) -> bool {
  244. if(ep->d_type == DT_DIR) return true;
  245. if(ep->d_type == DT_LNK || ep->d_type == DT_UNKNOWN) {
  246. //symbolic links must be resolved to determine type
  247. struct stat sp = {0};
  248. fstatat(dirfd(dp), ep->d_name, &sp, 0);
  249. return S_ISDIR(sp.st_mode);
  250. }
  251. return false;
  252. }
  253. inline auto directory::create(const string& pathname, uint permissions) -> bool {
  254. string path;
  255. auto list = string{pathname}.trimRight("/").split("/");
  256. bool result = true;
  257. for(auto& part : list) {
  258. path.append(part, "/");
  259. if(directory::exists(path)) continue;
  260. result &= (mkdir(path, permissions) == 0);
  261. }
  262. return result;
  263. }
  264. inline auto directory::remove(const string& pathname) -> bool {
  265. auto list = directory::contents(pathname);
  266. for(auto& name : list) {
  267. if(name.endsWith("/")) directory::remove({pathname, name});
  268. else file::remove({pathname, name});
  269. }
  270. return rmdir(pathname) == 0;
  271. }
  272. inline auto directory::exists(const string& pathname) -> bool {
  273. struct stat data;
  274. if(stat(pathname, &data) != 0) return false;
  275. return S_ISDIR(data.st_mode);
  276. }
  277. inline auto directory::ufolders(const string& pathname, const string& pattern) -> vector<string> {
  278. if(!pathname) return vector<string>{"/"};
  279. vector<string> list;
  280. DIR* dp;
  281. struct dirent* ep;
  282. dp = opendir(pathname);
  283. if(dp) {
  284. while(ep = readdir(dp)) {
  285. if(!strcmp(ep->d_name, ".")) continue;
  286. if(!strcmp(ep->d_name, "..")) continue;
  287. if(!directoryIsFolder(dp, ep)) continue;
  288. string name{ep->d_name};
  289. if(name.match(pattern)) list.append(std::move(name));
  290. }
  291. closedir(dp);
  292. }
  293. return list;
  294. }
  295. inline auto directory::ufiles(const string& pathname, const string& pattern) -> vector<string> {
  296. if(!pathname) return {};
  297. vector<string> list;
  298. DIR* dp;
  299. struct dirent* ep;
  300. dp = opendir(pathname);
  301. if(dp) {
  302. while(ep = readdir(dp)) {
  303. if(!strcmp(ep->d_name, ".")) continue;
  304. if(!strcmp(ep->d_name, "..")) continue;
  305. if(directoryIsFolder(dp, ep)) continue;
  306. string name{ep->d_name};
  307. if(name.match(pattern)) list.append(std::move(name));
  308. }
  309. closedir(dp);
  310. }
  311. return list;
  312. }
  313. #endif
  314. }