]> git.mxchange.org Git - simgear.git/blob - simgear/misc/sg_path.cxx
Remove-on-destroy option for simgear::Dir, to help with cleaning up temporary directo...
[simgear.git] / simgear / misc / sg_path.cxx
1 // sg_path.cxx -- routines to abstract out path separator differences
2 //               between MacOS and the rest of the world
3 //
4 // Written by Curtis L. Olson, started April 1999.
5 //
6 // Copyright (C) 1999  Curtis L. Olson - http://www.flightgear.org/~curt
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24
25 #include <simgear/compiler.h>
26
27 #include <simgear_config.h>
28 #include <simgear/debug/logstream.hxx>
29 #include <stdio.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32
33 #ifdef _WIN32
34 #  include <direct.h>
35 #endif
36 #include "sg_path.hxx"
37
38 #include <boost/algorithm/string/case_conv.hpp>
39
40 using std::string;
41
42 /**
43  * define directory path separators
44  */
45
46 static const char sgDirPathSep = '/';
47 static const char sgDirPathSepBad = '\\';
48
49 #ifdef _WIN32
50 static const char sgSearchPathSep = ';';
51 #else
52 static const char sgSearchPathSep = ':';
53 #endif
54
55
56 // If Unix, replace all ":" with "/".  In windoze, allow the
57 // second character to be a ":" for things like c:\foo\bar
58
59 void
60 SGPath::fix()
61 {
62     for ( string::size_type i = 0; i < path.size(); ++i ) {
63 #if defined( WIN32 )
64     // for windoze, don't replace the ":" for the second character
65     if ( i == 1 ) {
66         continue;
67     }
68 #endif
69     if ( path[i] == sgDirPathSepBad ) {
70         path[i] = sgDirPathSep;
71     }
72     }
73 }
74
75
76 // default constructor
77 SGPath::SGPath()
78     : path(""),
79     _cached(false),
80     _cacheEnabled(true)
81 {
82 }
83
84
85 // create a path based on "path"
86 SGPath::SGPath( const std::string& p )
87     : path(p),
88     _cached(false),
89     _cacheEnabled(true)
90 {
91     fix();
92 }
93
94 // create a path based on "path" and a "subpath"
95 SGPath::SGPath( const SGPath& p, const std::string& r )
96     : path(p.path),
97     _cached(false),
98     _cacheEnabled(p._cacheEnabled)
99 {
100     append(r);
101     fix();
102 }
103
104 SGPath::SGPath(const SGPath& p) :
105   path(p.path),
106   _cached(p._cached),
107   _cacheEnabled(p._cacheEnabled),
108   _exists(p._exists),
109   _isDir(p._isDir),
110   _isFile(p._isFile),
111   _modTime(p._modTime)
112 {
113 }
114     
115 SGPath& SGPath::operator=(const SGPath& p)
116 {
117   path = p.path;
118   _cached = p._cached;
119   _cacheEnabled = p._cacheEnabled;
120   _exists = p._exists;
121   _isDir = p._isDir;
122   _isFile = p._isFile;
123   _modTime = p._modTime;
124   return *this;
125 }
126
127 // destructor
128 SGPath::~SGPath() {
129 }
130
131
132 // set path
133 void SGPath::set( const string& p ) {
134     path = p;
135     fix();
136     _cached = false;
137 }
138
139 void SGPath::set_cached(bool cached)
140 {
141     _cacheEnabled = cached;
142 }
143
144 // append another piece to the existing path
145 void SGPath::append( const string& p ) {
146     if ( path.size() == 0 ) {
147     path = p;
148     } else {
149     if ( p[0] != sgDirPathSep ) {
150         path += sgDirPathSep;
151     }
152     path += p;
153     }
154     fix();
155     _cached = false;
156 }
157
158 //add a new path component to the existing path string
159 void SGPath::add( const string& p ) {
160     append( sgSearchPathSep+p );
161 }
162
163
164 // concatenate a string to the end of the path without inserting a
165 // path separator
166 void SGPath::concat( const string& p ) {
167     if ( path.size() == 0 ) {
168     path = p;
169     } else {
170     path += p;
171     }
172     fix();
173     _cached = false;
174 }
175
176
177 // Get the file part of the path (everything after the last path sep)
178 string SGPath::file() const {
179     int index = path.rfind(sgDirPathSep);
180     if (index >= 0) {
181     return path.substr(index + 1);
182     } else {
183     return "";
184     }
185 }
186   
187
188 // get the directory part of the path.
189 string SGPath::dir() const {
190     int index = path.rfind(sgDirPathSep);
191     if (index >= 0) {
192     return path.substr(0, index);
193     } else {
194     return "";
195     }
196 }
197
198 // get the base part of the path (everything but the extension.)
199 string SGPath::base() const {
200     unsigned int index = path.rfind(sgDirPathSep);
201     if (index == string::npos) {
202         index = 0; // no separator in the name
203     } else {
204         ++index; // skip past the separator
205     }
206
207 // find the first dot after the final separator
208     unsigned int firstDot = path.find(".", index);
209     if (firstDot == string::npos) {
210         return path; // no extension, return whole path
211     }
212     
213     return path.substr(0, firstDot);
214 }
215
216 string SGPath::file_base() const
217 {
218     unsigned int index = path.rfind(sgDirPathSep);
219     if (index == string::npos) {
220         index = 0; // no separator in the name
221     } else {
222         ++index; // skip past the separator
223     }
224     
225     unsigned int firstDot = path.find(".", index);
226     if (firstDot == string::npos) {
227         return path.substr(index); // no extensions
228     }
229     
230     return path.substr(index, firstDot - index);
231 }
232
233 // get the extension (everything after the final ".")
234 // but make sure no "/" follows the "." character (otherwise it
235 // is has to be a directory name containing a ".").
236 string SGPath::extension() const {
237     int index = path.rfind(".");
238     if ((index >= 0)  && (path.find("/", index) == string::npos)) {
239         return path.substr(index + 1);
240     } else {
241         return "";
242     }
243 }
244
245 string SGPath::lower_extension() const {
246     return boost::to_lower_copy(extension());
247 }
248
249 string SGPath::complete_lower_extension() const
250 {
251     unsigned int index = path.rfind(sgDirPathSep);
252     if (index == string::npos) {
253         index = 0; // no separator in the name
254     } else {
255         ++index; // skip past the separator
256     }
257     
258     int firstDot = path.find(".", index);
259     if ((firstDot >= 0)  && (path.find(sgDirPathSep, firstDot) == string::npos)) {
260         return boost::to_lower_copy(path.substr(firstDot + 1));
261     } else {
262         return "";
263     }
264 }
265
266 void SGPath::validate() const
267 {
268   if (_cached && _cacheEnabled) {
269     return;
270   }
271   
272 #ifdef _WIN32
273   struct _stat buf ;
274
275   bool remove_trailing = false;
276   if ( path.length() > 1 && path[path.length()-1] == '/' )
277       remove_trailing=true;
278   if (_stat (path.substr(0,remove_trailing?path.length()-1:path.length()).c_str(), &buf ) < 0) {
279     _exists = false;
280   } else {
281     _exists = true;
282     _isFile = ((S_IFREG & buf.st_mode ) !=0);
283     _isDir = ((S_IFDIR & buf.st_mode ) !=0);
284     _modTime = buf.st_mtime;
285   }
286
287 #else
288   struct stat buf ;
289
290   if (stat(path.c_str(), &buf ) < 0) {
291     _exists = false;
292   } else {
293     _exists = true;
294     _isFile = ((S_ISREG(buf.st_mode )) != 0);
295     _isDir = ((S_ISDIR(buf.st_mode )) != 0);
296     _modTime = buf.st_mtime;
297   }
298   
299 #endif
300   _cached = true;
301 }
302
303 bool SGPath::exists() const
304 {
305   validate();
306   return _exists;
307 }
308
309 bool SGPath::isDir() const
310 {
311   validate();
312   return _exists && _isDir;
313 }
314
315 bool SGPath::isFile() const
316 {
317   validate();
318   return _exists && _isFile;
319 }
320
321 #ifdef _WIN32
322 #  define sgMkDir(d,m)       _mkdir(d)
323 #else
324 #  define sgMkDir(d,m)       mkdir(d,m)
325 #endif
326
327
328 int SGPath::create_dir( mode_t mode ) {
329     string_list dirlist = sgPathSplit(dir());
330     if ( dirlist.empty() )
331         return -1;
332     string path = dirlist[0];
333     string_list path_elements = sgPathBranchSplit(path);
334     bool absolute = !path.empty() && path[0] == sgDirPathSep;
335
336     unsigned int i = 1;
337     SGPath dir = absolute ? string( 1, sgDirPathSep ) : "";
338     dir.concat( path_elements[0] );
339 #ifdef _WIN32
340     if ( dir.str().find(':') != string::npos && path_elements.size() >= 2 ) {
341         dir.append( path_elements[1] );
342         i = 2;
343     }
344 #endif
345     struct stat info;
346     int r;
347     for(; ( r = stat( dir.c_str(), &info ) ) == 0 && i < path_elements.size(); i++) {
348         dir.append(path_elements[i]);
349     }
350     if ( r == 0 ) {
351         return 0; // Directory already exists
352     }
353     if ( sgMkDir( dir.c_str(), mode) ) {
354         SG_LOG( SG_IO, SG_ALERT, "Error creating directory: " + dir.str() );
355         return -2;
356     }
357     for(; i < path_elements.size(); i++) {
358         dir.append(path_elements[i]);
359         if ( sgMkDir( dir.c_str(), mode) ) {
360             SG_LOG( SG_IO, SG_ALERT, "Error creating directory: " + dir.str() );
361             return -2;
362         }
363     }
364
365     return 0;
366 }
367
368 string_list sgPathBranchSplit( const string &dirpath ) {
369     string_list path_elements;
370     string element, path = dirpath;
371     while ( path.size() ) {
372         size_t p = path.find( sgDirPathSep );
373         if ( p != string::npos ) {
374             element = path.substr( 0, p );
375             path.erase( 0, p + 1 );
376         } else {
377             element = path;
378             path = "";
379         }
380         if ( element.size() )
381             path_elements.push_back( element );
382     }
383     return path_elements;
384 }
385
386
387 string_list sgPathSplit( const string &search_path ) {
388     string tmp = search_path;
389     string_list result;
390     result.clear();
391
392     bool done = false;
393
394     while ( !done ) {
395         int index = tmp.find(sgSearchPathSep);
396         if (index >= 0) {
397             result.push_back( tmp.substr(0, index) );
398             tmp = tmp.substr( index + 1 );
399         } else {
400             if ( !tmp.empty() )
401                 result.push_back( tmp );
402             done = true;
403         }
404     }
405
406     return result;
407 }
408
409 bool SGPath::isAbsolute() const
410 {
411   if (path.empty()) {
412     return false;
413   }
414   
415 #ifdef _WIN32
416   // detect '[A-Za-z]:/'
417   if (path.size() > 2) {
418     if (isalpha(path[0]) && (path[1] == ':') && (path[2] == sgDirPathSep)) {
419       return true;
420     }
421   }
422 #endif
423   
424   return (path[0] == sgDirPathSep);
425 }
426
427 bool SGPath::isNull() const
428 {
429   return path.empty() || (path == "");
430 }
431
432 std::string SGPath::str_native() const
433 {
434 #ifdef _WIN32
435     std::string s = str();
436     std::string::size_type pos;
437     std::string nativeSeparator;
438     nativeSeparator = sgDirPathSepBad;
439
440     while( (pos=s.find( sgDirPathSep )) != std::string::npos ) {
441         s.replace( pos, 1, nativeSeparator );
442     }
443     return s;
444 #else
445     return str();
446 #endif
447 }
448
449 bool SGPath::remove()
450 {
451     int err = ::unlink(c_str());
452     if (err) {
453         SG_LOG(SG_IO, SG_WARN,  "file remove failed: (" << str() << ") " << strerror(errno));
454     }
455     return (err == 0);
456 }
457
458 time_t SGPath::modTime() const
459 {
460     validate();
461     return _modTime;
462 }
463
464 bool SGPath::operator==(const SGPath& other) const
465 {
466     return (path == other.path);
467 }
468
469 bool SGPath::operator!=(const SGPath& other) const
470 {
471     return (path != other.path);
472 }
473