]> git.mxchange.org Git - simgear.git/blob - simgear/misc/sg_dir.cxx
Merge branch 'next' of git://gitorious.org/fg/simgear into next
[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
27 #ifdef _WIN32
28 #  define WIN32_LEAN_AND_MEAN
29 #  include <windows.h>
30 #  include <direct.h>
31 #else
32 #  include <sys/types.h>
33 #  include <dirent.h>
34 #  include <sys/stat.h>
35 #  include <unistd.h>
36 #  include <errno.h>
37 #endif
38
39 #include <simgear/debug/logstream.hxx>
40 #include <boost/foreach.hpp>
41
42 #include <cstring>
43 #include <iostream>
44
45 using std::string;
46
47 namespace simgear
48 {
49
50 Dir::Dir()
51 {
52 }
53
54 Dir::Dir(const SGPath& path) :
55   _path(path)
56 {
57     _path.set_cached(false); // disable caching, so create/remove work
58 }
59
60 Dir::Dir(const Dir& rel, const SGPath& relPath) :
61   _path(rel.file(relPath.str()))
62 {
63     _path.set_cached(false); // disable caching, so create/remove work
64 }
65
66 Dir Dir::current()
67 {
68 #ifdef _WIN32
69     char* buf = _getcwd(NULL, 0);
70 #else
71     char* buf = ::getcwd(NULL, 0);
72 #endif
73     SGPath p(buf);
74     free(buf);
75     return Dir(p);
76 }
77
78 Dir Dir::tempDir(const std::string& templ)
79 {
80 #ifdef HAVE_MKDTEMP
81     char buf[1024];
82     char* tempPath = ::getenv("TMPDIR");
83     if (!tempPath) {
84         tempPath = "/tmp/";
85     }
86     // Mac OS-X / BSD manual says any number of 'X's, but GLibc manual
87     // says exactly six, so that's what I'm going with
88     ::snprintf(buf, 1024, "%s%s-XXXXXX", tempPath, templ.c_str());
89     if (!mkdtemp(buf)) {
90         SG_LOG(SG_IO, SG_WARN, "mkdtemp failed:" << strerror(errno));
91         return Dir();
92     }
93     
94     return Dir(SGPath(buf));
95 #else
96     SGPath p(tempnam(0, templ.c_str()));
97     Dir t(p);
98     if (!t.create(0700)) {
99         SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p.str());
100     }
101     
102     return t;
103 #endif
104 }
105
106 PathList Dir::children(int types, const std::string& nameFilter) const
107 {
108   PathList result;
109   if (types == 0) {
110     types = TYPE_FILE | TYPE_DIR | NO_DOT_OR_DOTDOT;
111   }
112   
113 #ifdef _WIN32
114   std::string search(_path.str());
115   if (nameFilter.empty()) {
116     search += "\\*"; // everything
117   } else {
118     search += "\\*" + nameFilter;
119   }
120   
121   WIN32_FIND_DATA fData;
122   HANDLE find = FindFirstFile(search.c_str(), &fData);
123   if (find == INVALID_HANDLE_VALUE) {
124     SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: FindFirstFile failed:" << _path.str());
125     return result;
126   }
127   
128   bool done = false;
129   for (bool done = false; !done; done = (FindNextFile(find, &fData) == 0)) {
130     if (fData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
131       if (!(types & INCLUDE_HIDDEN)) {
132         continue;
133       }
134     }
135     
136     if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
137           if (types & NO_DOT_OR_DOTDOT) {
138                 if (!strcmp(fData.cFileName,".") || !strcmp(fData.cFileName,"..")) {
139                   continue;
140                 }
141           }
142
143       if (!(types & TYPE_DIR)) {
144         continue;
145       }
146         } else if ((fData.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) ||
147                                 (fData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
148         {
149                 continue; // always ignore device files
150     } else if (!(types & TYPE_FILE)) {
151        continue;
152     }
153
154     result.push_back(file(fData.cFileName));
155   }
156
157   FindClose(find);
158 #else
159   DIR* dp = opendir(_path.c_str());
160   if (!dp) {
161     SG_LOG(SG_GENERAL, SG_WARN, "Dir::children: opendir failed:" << _path.str());
162     return result;
163   }
164   
165   int filterLen = nameFilter.size();
166   
167   while (true) {
168     struct dirent* entry = readdir(dp);
169     if (!entry) {
170       break; // done iteration
171     }
172     
173     // skip hidden files (names beginning with '.') unless requested
174     if (!(types & INCLUDE_HIDDEN) && (entry->d_name[0] == '.') &&
175          strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
176       continue;
177     }
178     
179     SGPath f = file(entry->d_name);
180     if (!f.exists()) {
181       continue; // stat() failed
182     }
183     
184     if (f.isDir()) {
185       // directory handling
186       if (!(types & TYPE_DIR)) {
187         continue;
188       }
189       
190       if (types & NO_DOT_OR_DOTDOT) {
191         if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
192           continue;
193         }
194       }
195     } else if (f.isFile()) {
196       // regular file handling
197       if (!(types & TYPE_FILE)) {
198         continue;
199       }
200     } else {
201       // block device /fifo/char file, ignore
202       continue;
203     }
204
205     if (!nameFilter.empty()) {
206       int nameLen = strlen(entry->d_name);
207       if (nameLen < filterLen) {
208         continue; // name is shorter than the filter
209       }
210     
211       char* nameSuffix = entry->d_name + (nameLen - filterLen);
212       if (strcmp(nameSuffix, nameFilter.c_str())) {
213         continue;
214       }
215     }
216     
217   // passed all criteria, add to our result vector
218     result.push_back(file(entry->d_name));
219   }
220   
221   closedir(dp);
222 #endif
223   return result;
224 }
225
226 bool Dir::exists() const
227 {
228   return _path.isDir();
229 }
230
231 SGPath Dir::file(const std::string& name) const
232 {
233   SGPath childPath = _path;
234   childPath.set_cached(true);
235   childPath.append(name);
236   return childPath;  
237 }
238
239 #ifdef _WIN32
240 #  define sgMkDir(d,m)       _mkdir(d)
241 #else
242 #  define sgMkDir(d,m)       mkdir(d,m)
243 #endif
244
245 bool Dir::create(mode_t mode)
246 {
247     if (exists()) {
248         return false; // already exists
249     }
250     
251 // recursively create parent directories
252     Dir pr(parent());
253     if (!pr.exists()) {
254         bool ok = pr.create(mode);
255         if (!ok) {
256             return false;
257         }
258     }
259     
260 // finally, create ourselves
261     int err = sgMkDir(_path.c_str(), mode);
262     if (err) {
263         SG_LOG(SG_IO, SG_WARN,  "directory creation failed: (" << _path.str() << ") " << strerror(errno) );
264     }
265     
266     return (err == 0);
267 }
268
269 bool Dir::remove(bool recursive)
270 {
271     if (!exists()) {
272         SG_LOG(SG_IO, SG_WARN, "attempt to remove non-existant dir:" << _path.str());
273         return false;
274     }
275     
276     if (recursive) {
277         bool ok;
278         PathList cs = children(NO_DOT_OR_DOTDOT | INCLUDE_HIDDEN | TYPE_FILE | TYPE_DIR);
279         BOOST_FOREACH(SGPath path, cs) {
280             if (path.isDir()) {
281                 Dir childDir(path);
282                 ok = childDir.remove(true);
283             } else {
284                 ok = path.remove();
285             }
286             
287             if (!ok) {
288                 return false;
289             }
290         } // of child iteration
291     } // of recursive deletion
292     
293 #ifdef _WIN32
294     int err = _rmdir(_path.c_str());
295 #else
296     int err = rmdir(_path.c_str());
297 #endif
298     if (err) {
299         SG_LOG(SG_IO, SG_WARN, "rmdir failed:" << _path.str() << ":" << strerror(errno));
300     }
301     return (err == 0);
302 }
303
304 Dir Dir::parent() const
305 {
306     return Dir(_path.dir());
307 }
308
309 } // of namespace simgear