]> git.localhorst.tv Git - blank.git/blob - src/io/filesystem.cpp
b0a9126fc5192be77dde7813abaebad3e6e32e82
[blank.git] / src / io / filesystem.cpp
1 #include "filesystem.hpp"
2
3 #include <cerrno>
4 #include <cstdio>
5 #include <cstring>
6 #ifdef _WIN32
7 #  include <conio.h>
8 #  include <direct.h>
9 #  include <windows.h>
10 #else
11 #  include <dirent.h>
12 #  include <sys/types.h>
13 #endif
14 #include <sys/stat.h>
15
16
17 namespace blank {
18
19 namespace {
20 #ifdef _WIN32
21         using Stat = struct _stat;
22         int do_stat(const char *path, Stat &info) {
23                 return _stat(path, &info);
24         }
25         bool is_dir(const Stat &info) {
26                 return (info.st_mode & _S_IFDIR) != 0;
27         }
28         bool is_file(const Stat &info) {
29                 return (info.st_mode & _S_IFEG) != 0;
30         }
31 #else
32         using Stat = struct stat;
33         int do_stat(const char *path, Stat &info) {
34                 return stat(path, &info);
35         }
36         bool is_dir(const Stat &info) {
37                 return S_ISDIR(info.st_mode);
38         }
39         bool is_file(const Stat &info) {
40                 return S_ISREG(info.st_mode);
41         }
42 #endif
43         std::time_t get_mtime(const Stat &info) {
44 #ifdef __APPLE__
45                 return info.st_mtimespec.tv_sec;
46 #else
47         return info.st_mtime;
48 #endif
49         }
50 }
51
52 bool is_dir(const char *path) {
53         Stat info;
54         if (do_stat(path, info) != 0) {
55                 return false;
56         }
57         return is_dir(info);
58 }
59
60 bool is_file(const char *path) {
61         Stat info;
62         if (do_stat(path, info) != 0) {
63                 return false;
64         }
65         return is_file(info);
66 }
67
68 std::time_t file_mtime(const char *path) {
69         Stat info;
70         if (do_stat(path, info) != 0) {
71                 return 0;
72         }
73         return get_mtime(info);
74 }
75
76
77 bool make_dir(const char *path) {
78 #ifdef _WIN32
79         int ret = _mkdir(path);
80 #else
81         int ret = mkdir(path, 0777);
82 #endif
83         return ret == 0;
84 }
85
86
87 bool make_dirs(const std::string &path) {
88         if (make_dir(path)) {
89                 return true;
90         }
91
92         switch (errno) {
93
94                 case ENOENT:
95                         // missing component
96                         {
97 #ifdef _WIN32
98                                 auto pos = path.find_last_of("\\/");
99 #else
100                                 auto pos = path.find_last_of('/');
101 #endif
102                                 if (pos == std::string::npos) {
103                                         return false;
104                                 }
105                                 if (pos == path.length() - 1) {
106                                         // trailing separator, would make final make_dir fail
107 #ifdef _WIN32
108                                          pos = path.find_last_of("\\/", pos - 1);
109 #else
110                                          pos = path.find_last_of('/', pos - 1);
111 #endif
112                                         if (pos == std::string::npos) {
113                                                 return false;
114                                         }
115                                 }
116                                 if (!make_dirs(path.substr(0, pos))) {
117                                         return false;
118                                 }
119                         }
120                         // try again
121                         return make_dir(path);
122
123                 case EEXIST:
124                         // something's there, check if it's a dir and we're good
125                         return is_dir(path);
126
127                 default:
128                         // whatever else went wrong, it can't be good
129                         return false;
130
131         }
132 }
133
134
135 bool remove_file(const std::string &path) {
136         return remove(path.c_str()) == 0;
137 }
138
139
140 bool remove_dir(const std::string &path) {
141 #ifdef _WIN32
142
143         // shamelessly stolen from http://www.codeguru.com/forum/showthread.php?t=239271
144         const std::string pattern = path + "\\*.*";
145         WIN32_FIND_DATA info;
146         HANDLE file = FindFirstFile(pattern.c_str(), &info);
147         if (file == INVALID_HANDLE_VALUE) {
148                 // already non-existing
149                 return true;
150         }
151
152         do {
153                 if (
154                         strncmp(info.cFileName, ".", 2) == 0 ||
155                         strncmp(info.cFileName, "..", 3) == 0
156                 ) {
157                         continue;
158                 }
159                 const std::string sub_path = path + '\\' + info.cFileName;
160                 if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
161                         if (!remove_dir(sub_path)) {
162                                 return false;
163                         }
164                 } else {
165                         if (!SetFileAttributes(sub_path.c_str(), FILE_ATTRIBUTE_NORMAL)) {
166                                 return false;
167                         }
168                         if (!remove_file(sub_path)) {
169                                 return false;
170                         }
171                 }
172         } while (FindNextFile(file, &info));
173         FindClose(file);
174
175         DWORD error = GetLastError();
176         if (error != ERROR_NO_MORE_FILES) {
177                 return false;
178         }
179         // is this (NORMAL vs DIRECTORY) really correct?
180         if (!SetFileAttributes(path.c_str(), FILE_ATTRIBUTE_NORMAL)) {
181                 return false;
182         }
183         return RemoveDirectory(path.c_str());
184
185 #else
186
187         DIR *dir = opendir(path.c_str());
188         for (dirent *entry = readdir(dir); entry != nullptr; entry = readdir(dir)) {
189                 if (
190                         strncmp(entry->d_name, ".", 2) == 0 ||
191                         strncmp(entry->d_name, "..", 3) == 0
192                 ) {
193                         continue;
194                 }
195                 const std::string sub_path = path + '/' + entry->d_name;
196                 if (is_dir(sub_path)) {
197                         if (!remove_dir(sub_path)) {
198                                 return false;
199                         }
200                 } else {
201                         if (!remove_file(sub_path)) {
202                                 return false;
203                         }
204                 }
205         }
206         return remove(path.c_str()) == 0;
207
208 #endif
209 }
210
211 }