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