]> git.mxchange.org Git - simgear.git/blob - simgear/misc/sg_dir.cxx
Ganael Laplanche: fix include dependencies for FreeBSD support
[simgear.git] / simgear / misc / sg_dir.cxx
1 // Written by James Turner, started July 2010.
2 //
3 // Copyright (C) 2010  Curtis L. Olson - http://www.flightgear.org/~curt
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 // $Id$
20
21 #ifdef HAVE_CONFIG_H
22 #  include <simgear_config.h>
23 #endif
24
25 #include <simgear/misc/sg_dir.hxx>
26 #include <math.h>
27 #include <stdlib.h>
28
29 #ifdef _WIN32
30 #  define WIN32_LEAN_AND_MEAN
31 #  include <windows.h>
32 #  include <direct.h>
33 #else
34 #  include <sys/types.h>
35 #  include <dirent.h>
36 #  include <sys/stat.h>
37 #  include <unistd.h>
38 #  include <errno.h>
39 #endif
40
41 #include <simgear/debug/logstream.hxx>
42 #include <boost/foreach.hpp>
43
44 #include <cstring>
45 #include <cstdlib>
46 #include <iostream>
47
48 using std::string;
49
50 namespace simgear
51 {
52
53 Dir::Dir() :
54     _removeOnDestroy(false)
55 {
56 }
57
58 Dir::Dir(const SGPath& path) :
59   _path(path),
60   _removeOnDestroy(false)
61 {
62     _path.set_cached(false); // disable caching, so create/remove work
63 }
64
65 Dir::Dir(const Dir& rel, const SGPath& relPath) :
66   _path(rel.file(relPath.str())),
67   _removeOnDestroy(false)
68 {
69     _path.set_cached(false); // disable caching, so create/remove work
70 }
71
72 Dir::~Dir()
73 {
74     if (_removeOnDestroy) {
75         remove(true);
76     }
77 }
78
79 void Dir::setRemoveOnDestroy()
80 {
81     _removeOnDestroy = true;
82 }
83
84 Dir Dir::current()
85 {
86 #ifdef _WIN32
87     char* buf = _getcwd(NULL, 0);
88 #else
89     char* buf = ::getcwd(NULL, 0);
90 #endif
91     SGPath p(buf);
92     free(buf);
93     return Dir(p);
94 }
95
96 Dir Dir::tempDir(const std::string& templ)
97 {
98 #ifdef HAVE_MKDTEMP
99     char buf[1024];
100     const char* tempPath = ::getenv("TMPDIR");
101     if (!tempPath) {
102         tempPath = "/tmp";
103     }
104     SGPath p(tempPath);
105     p.append(templ);
106     // Mac OS-X / BSD manual says any number of 'X's, but GLibc manual
107     // says exactly six, so that's what I'm going with
108     p.concat("-XXXXXX");
109     ::snprintf(buf, 1024, "%s", p.c_str());
110     if (!mkdtemp(buf)) {
111         SG_LOG(SG_IO, SG_WARN, "mkdtemp failed:" << strerror(errno));
112         return Dir();
113     }
114     
115     return Dir(SGPath(buf));
116 #else
117     SGPath p(tempnam(0, templ.c_str()));
118     Dir t(p);
119     if (!t.create(0700)) {
120         SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p.str());
121     }
122     
123     return t;
124 #endif
125 }
126
127 PathList Dir::children(int types, const std::string& nameFilter) const
128 {
129   PathList result;
130   if (types == 0) {
131     types = TYPE_FILE | TYPE_DIR | NO_DOT_OR_DOTDOT;
132   }
133   
134 #ifdef _WIN32
135   std::string search(_path.str());
136   if (nameFilter.empty()) {
137     search += "\\*"; // everything
138   } else {
139     search += "\\*" + nameFilter;
140   }
141   
142   WIN32_FIND_DATA fData;
143   HANDLE find = FindFirstFile(search.c_str(), &fData);
144   if (find == INVALID_HANDLE_VALUE) {
145     SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" << _path.str());
146     return result;
147   }
148   
149   bool done = false;
150   for (bool done = false; !done; done = (FindNextFile(find, &fData) == 0)) {
151     if (fData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
152       if (!(types & INCLUDE_HIDDEN)) {
153         continue;
154       }
155     }
156     
157     if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
158           if (types & NO_DOT_OR_DOTDOT) {
159                 if (!strcmp(fData.cFileName,".") || !strcmp(fData.cFileName,"..")) {
160                   continue;
161                 }
162           }
163
164       if (!(types & TYPE_DIR)) {
165         continue;
166       }
167         } else if ((fData.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) ||
168                                 (fData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
169         {
170                 continue; // always ignore device files
171     } else if (!(types & TYPE_FILE)) {
172        continue;
173     }
174
175     result.push_back(file(fData.cFileName));
176   }
177
178   FindClose(find);
179 #else
180   DIR* dp = opendir(_path.c_str());
181   if (!dp) {
182     SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: opendir failed:" << _path.str());
183     return result;
184   }
185   
186   int filterLen = nameFilter.size();
187   
188   while (true) {
189     struct dirent* entry = readdir(dp);
190     if (!entry) {
191       break; // done iteration
192     }
193     
194     // skip hidden files (names beginning with '.') unless requested
195     if (!(types & INCLUDE_HIDDEN) && (entry->d_name[0] == '.') &&
196          strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
197       continue;
198     }
199     
200     SGPath f = file(entry->d_name);
201     if (!f.exists()) {
202       continue; // stat() failed
203     }
204     
205     if (f.isDir()) {
206       // directory handling
207       if (!(types & TYPE_DIR)) {
208         continue;
209       }
210       
211       if (types & NO_DOT_OR_DOTDOT) {
212         if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
213           continue;
214         }
215       }
216     } else if (f.isFile()) {
217       // regular file handling
218       if (!(types & TYPE_FILE)) {
219         continue;
220       }
221     } else {
222       // block device /fifo/char file, ignore
223       continue;
224     }
225
226     if (!nameFilter.empty()) {
227       int nameLen = strlen(entry->d_name);
228       if (nameLen < filterLen) {
229         continue; // name is shorter than the filter
230       }
231     
232       char* nameSuffix = entry->d_name + (nameLen - filterLen);
233       if (strcmp(nameSuffix, nameFilter.c_str())) {
234         continue;
235       }
236     }
237     
238   // passed all criteria, add to our result vector
239     result.push_back(file(entry->d_name));
240   }
241   
242   closedir(dp);
243 #endif
244   return result;
245 }
246
247 bool Dir::exists() const
248 {
249   return _path.isDir();
250 }
251
252 SGPath Dir::file(const std::string& name) const
253 {
254   SGPath childPath = _path;
255   childPath.set_cached(true);
256   childPath.append(name);
257   return childPath;  
258 }
259
260 #ifdef _WIN32
261 #  define sgMkDir(d,m)       _mkdir(d)
262 #else
263 #  define sgMkDir(d,m)       mkdir(d,m)
264 #endif
265
266 bool Dir::create(mode_t mode)
267 {
268     if (exists()) {
269         return false; // already exists
270     }
271     
272 // recursively create parent directories
273     Dir pr(parent());
274     if (!pr.path().isNull() && !pr.exists()) {
275         bool ok = pr.create(mode);
276         if (!ok) {
277             return false;
278         }
279     }
280     
281 // finally, create ourselves
282     int err = sgMkDir(_path.c_str(), mode);
283     if (err) {
284         SG_LOG(SG_IO, SG_WARN,  "directory creation failed: (" << _path.str() << ") " << strerror(errno) );
285     }
286     
287     return (err == 0);
288 }
289
290 bool Dir::remove(bool recursive)
291 {
292     if (!exists()) {
293         SG_LOG(SG_IO, SG_WARN, "attempt to remove non-existant dir:" << _path.str());
294         return false;
295     }
296     
297     if (recursive) {
298         bool ok;
299         PathList cs = children(NO_DOT_OR_DOTDOT | INCLUDE_HIDDEN | TYPE_FILE | TYPE_DIR);
300         BOOST_FOREACH(SGPath path, cs) {
301             if (path.isDir()) {
302                 Dir childDir(path);
303                 ok = childDir.remove(true);
304             } else {
305                 ok = path.remove();
306             }
307             
308             if (!ok) {
309                 return false;
310             }
311         } // of child iteration
312     } // of recursive deletion
313     
314 #ifdef _WIN32
315     int err = _rmdir(_path.c_str());
316 #else
317     int err = rmdir(_path.c_str());
318 #endif
319     if (err) {
320         SG_LOG(SG_IO, SG_WARN, "rmdir failed:" << _path.str() << ":" << strerror(errno));
321     }
322     return (err == 0);
323 }
324
325 Dir Dir::parent() const
326 {
327     return Dir(_path.dir());
328 }
329
330 } // of namespace simgear