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