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