E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gpath.cpp
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gpath.cpp
19 //
20
21 #include "gdef.h"
22 #include "gpath.h"
23 #include "gstr.h"
24 #include "gdebug.h"
25 #include "glog.h"
26 #include "gassert.h"
27 #include <cstring>
28 #include <algorithm>
29
30 /// \class G::PathImp
31 /// A private implementation class used by G::Path providing a set of
32 /// static methods. Supports both posix-style and windows-style paths
33 /// at run-time, with only the default selected at compile-time.
34 ///
35 class G::PathImp
36 {
37 public:
38
39 static bool use_posix ;
40 typedef std::string::size_type pos_t ;
41
42 static std::string windows_sep()
43 {
44 return "\\" ;
45 }
46 static pos_t windows_slashpos( const std::string & s )
47 {
48 return s.rfind('\\') ;
49 }
50 static bool windows_simple( const std::string & s )
51 {
52 return s.find('/') == std::string::npos && s.find('\\') == std::string::npos ;
53 }
54 static bool windows_absolute( const std::string & s )
55 {
56 return
57 ( s.length() >= 3U && s.at(1U) == ':' && s.at(2U) == '\\' ) ||
58 ( s.length() >= 1U && s.at(0U) == '\\' ) ;
59 }
60 static pos_t windows_rootsize( const std::string & s , size_t chars , size_t parts )
61 {
62 G_ASSERT( s.length() >= chars ) ;
63 G_ASSERT( parts == 1U || parts == 2U ) ;
64 pos_t pos = s.find( '\\' , chars ) ;
65 if( parts == 2U && pos != std::string::npos )
66 pos = s.find( '\\' , pos+1U ) ;
67 return pos == std::string::npos ? s.length() : pos ;
68 }
69 static pos_t windows_rootsize( const std::string & s )
70 {
71 if( s.empty() ) return 0U ;
72 if( s.length() >= 3U && s.at(1U) == ':' && s.at(2U) == '\\' ) return 3U ; // C:|...
73 if( s.length() >= 2U && s.at(1U) == ':' ) return 2U ; // C:...
74 if( s.find("\\\\?\\UNC\\") == 0U ) return windows_rootsize(s,8U,2U) ; // ||?|UNC|server|volume|...
75 if( s.find("\\\\?\\") == 0U && s.size() > 5U && s.at(5U) == ':' ) return windows_rootsize(s,4U,1U) ; // ||?|C:|...
76 if( s.find("\\\\?\\") == 0U ) return windows_rootsize(s,4U,2U) ; // ||?|server|volume|...
77 if( s.find("\\\\.\\") == 0U ) return windows_rootsize(s,4U,1U) ; // ||.|dev|...
78 if( s.find("\\\\") == 0U ) return windows_rootsize(s,2U,2U) ; // ||server|volume|...
79 if( s.find("\\") == 0U ) return 1U ; // |...
80 return 0U ;
81 }
82 static void windows_normalise( std::string & s )
83 {
84 Str::replaceAll( s , "/" , "\\" ) ;
85 bool special = s.find("\\\\") == 0U ;
86 while( Str::replaceAll( s , "\\\\" , "\\" ) ) {;}
87 if( special ) s = "\\" + s ;
88
89 while( s.length() > 1U )
90 {
91 pos_t pos = s.rfind('\\') ;
92 if( pos == std::string::npos ) break ;
93 if( (pos+1U) != s.length() ) break ;
94 if( pos < windows_rootsize(s) ) break ;
95 s.resize( pos ) ;
96 }
97 }
98 static std::string windows_null()
99 {
100 return "NUL" ;
101 }
102
103 static std::string posix_sep()
104 {
105 return "/" ;
106 }
107 static pos_t posix_slashpos( const std::string & s )
108 {
109 return s.rfind('/') ;
110 }
111 static bool posix_simple( const std::string & s )
112 {
113 return s.find('/') == std::string::npos ;
114 }
115 static void posix_normalise( std::string & s )
116 {
117 while( Str::replaceAll( s , "//" , "/" ) ) {;}
118 while( s.length() > 1U && s.at(s.length()-1U) == '/' ) s.resize(s.length()-1U) ;
119 }
120 static bool posix_absolute( const std::string & s )
121 {
122 return !s.empty() && s.at(0U) == '/' ;
123 }
124 static pos_t posix_rootsize( const std::string & s )
125 {
126 return s.empty() || s.at(0U) != '/' ? 0U : 1U ;
127 }
128 static std::string posix_null()
129 {
130 return "/dev/null" ;
131 }
132
133 static std::string sep()
134 {
135 return use_posix ? posix_sep() : windows_sep() ;
136 }
137 static void normalise( std::string & s )
138 {
139 use_posix ? posix_normalise(s) : windows_normalise(s) ;
140 }
141 static bool simple( const std::string & s )
142 {
143 return use_posix ? posix_simple(s) : windows_simple(s) ;
144 }
145 static bool absolute( const std::string & s )
146 {
147 return use_posix ? posix_absolute(s) : windows_absolute(s) ;
148 }
149 static std::string null()
150 {
151 return use_posix ? posix_null() : windows_null() ;
152 }
153 static pos_t rootsize( const std::string & s )
154 {
155 return use_posix ? posix_rootsize(s) : windows_rootsize(s) ;
156 }
157 static pos_t slashpos( const std::string & s )
158 {
159 return use_posix ? posix_slashpos(s) : windows_slashpos(s) ;
160 }
161 static pos_t dotpos( const std::string & s )
162 {
163 const pos_t npos = std::string::npos ;
164 const pos_t sp = slashpos( s ) ;
165 const pos_t dp = s.rfind( '.' ) ;
166 if( dp == npos )
167 return npos ;
168 else if( sp == npos )
169 return dp ;
170 else if( dp < sp )
171 return npos ;
172 else
173 return dp ;
174 }
175
176 static void splitInto( const std::string & str , StringArray & a )
177 {
178 pos_t rs = rootsize(str) ;
179 if( str.empty() )
180 {
181 }
182 else if( rs != 0U ) // ie. absolute or like "c:foo"
183 {
184 std::string root = str.substr( 0U , rs ) ;
185 Str::splitIntoTokens( Str::tail(str,rs-1U,std::string()) , a , sep() ) ;
186 a.insert( a.begin() , root ) ;
187 }
188 else
189 {
190 Str::splitIntoTokens( str , a , sep() ) ;
191 }
192 }
193
194 static bool purge( StringArray & a )
195 {
196 const std::string dot( 1U , '.' ) ;
197 a.erase( std::remove( a.begin() , a.end() , std::string() ) , a.end() ) ;
198 size_t n = a.size() ;
199 a.erase( std::remove( a.begin() , a.end() , dot ) , a.end() ) ;
200 const bool all_dots = a.empty() && n != 0U ;
201 return all_dots ;
202 }
203
204 static std::string join( StringArray::const_iterator p , StringArray::const_iterator end )
205 {
206 std::string str ;
207 int i = 0 ;
208 for( ; p != end ; ++p , i++ )
209 {
210 bool drive = !use_posix && str.length() == 2U && str.at(1U) == ':' ;
211 bool last_is_slash = !str.empty() && ( str.at(str.length()-1U) == '/' || str.at(str.length()-1U) == '\\' ) ;
212 if( i == 1 && (drive || last_is_slash) )
213 ;
214 else if( i != 0 )
215 str.append( sep() ) ;
216 str.append( *p ) ;
217 }
218 return str ;
219 }
220
221 static std::string join( const StringArray & a )
222 {
223 return join( a.begin() , a.end() ) ;
224 }
225 } ;
226
227 #ifdef G_WIN32
228 bool G::PathImp::use_posix = false ;
229 #else
230 bool G::PathImp::use_posix = true ;
231 #endif
232
233 // ==
234
235 void G::Path::setPosixStyle()
236 {
237 G::PathImp::use_posix = true ;
238 }
239
240 void G::Path::setWindowsStyle()
241 {
242 G::PathImp::use_posix = false ;
243 }
244
245 G::Path::Path()
246 {
247 }
248
249 G::Path::~Path()
250 {
251 }
252
253 G::Path::Path( const std::string & path ) :
254 m_str(path)
255 {
256 PathImp::normalise( m_str ) ;
257 }
258
259 G::Path::Path( const char * path ) :
260 m_str(path)
261 {
262 PathImp::normalise( m_str ) ;
263 }
264
265 G::Path::Path( const Path & path , const std::string & tail ) :
266 m_str(path.m_str)
267 {
268 pathAppend( tail ) ;
269 PathImp::normalise( m_str ) ;
270 }
271
272 G::Path::Path( const Path & path , const std::string & tail_1 , const std::string & tail_2 ) :
273 m_str(path.m_str)
274 {
275 pathAppend( tail_1 ) ;
276 pathAppend( tail_2 ) ;
277 PathImp::normalise( m_str ) ;
278 }
279
280 G::Path::Path( const Path & other ) :
281 m_str(other.m_str)
282 {
283 }
284
285 G::Path G::Path::nullDevice()
286 {
287 return Path(PathImp::null()) ;
288 }
289
290 std::string G::Path::str() const
291 {
292 return m_str ;
293 }
294
295 bool G::Path::simple() const
296 {
297 return dirname() == Path() ;
298 }
299
300 bool G::Path::isAbsolute() const
301 {
302 return PathImp::absolute( m_str ) ;
303 }
304
305 bool G::Path::isRelative() const
306 {
307 return !isAbsolute() ;
308 }
309
310 std::string G::Path::basename() const
311 {
312 StringArray a ;
313 PathImp::splitInto( m_str , a ) ;
314 PathImp::purge( a ) ;
315 return a.empty() ? std::string() : a.at(a.size()-1U) ;
316 }
317
318 G::Path G::Path::dirname() const
319 {
320 StringArray a ;
321 PathImp::splitInto( m_str , a ) ;
322 PathImp::purge( a ) ;
323 if( a.empty() ) return Path() ;
324 a.pop_back() ;
325 return join( a ) ;
326 }
327
328 G::Path G::Path::withoutExtension() const
329 {
330 std::string::size_type sp = PathImp::slashpos(m_str) ;
331 std::string::size_type dp = PathImp::dotpos(m_str) ;
332 if( dp != std::string::npos )
333 {
334 std::string result = m_str ;
335 result.resize( dp ) ;
336 if( (sp == std::string::npos && dp == 0U) || ((sp+1U) == dp) )
337 result.append(".") ; // special case
338 return Path( result ) ;
339 }
340 else
341 {
342 return *this ;
343 }
344 }
345
346 G::Path G::Path::withExtension( const std::string & ext ) const
347 {
348 std::string result = m_str ;
349 std::string::size_type dp = PathImp::dotpos(m_str) ;
350 if( dp != std::string::npos )
351 result.resize( dp ) ;
352 result.append( 1U , '.' ) ;
353 result.append( ext ) ;
354 return Path( result ) ;
355 }
356
357 void G::Path::pathAppend( const std::string & tail )
358 {
359 if( tail.empty() )
360 {
361 }
362 else if( PathImp::simple(tail) )
363 {
364 m_str.append( PathImp::sep() + tail ) ;
365 }
366 else
367 {
368 Path result = join( *this , tail ) ;
369 result.swap( *this ) ;
370 }
371 }
372
373 std::string G::Path::extension() const
374 {
375 std::string::size_type pos = PathImp::dotpos(m_str) ;
376 return
377 pos == std::string::npos || (pos+1U) == m_str.length() ?
378 std::string() :
379 m_str.substr( pos+1U ) ;
380 }
381
382 G::StringArray G::Path::split() const
383 {
384 StringArray a ;
385 PathImp::splitInto( m_str , a ) ;
386 if( PathImp::purge(a) ) a.push_back( "." ) ;
387 return a ;
388 }
389
390 G::Path G::Path::join( const G::StringArray & a )
391 {
392 if( a.empty() ) return Path() ;
393 return Path( PathImp::join(a) ) ;
394 }
395
396 G::Path G::Path::join( const G::Path & p1 , const G::Path & p2 )
397 {
398 if( p1 == Path() )
399 {
400 return p2 ;
401 }
402 else if( p2 == Path() )
403 {
404 return p1 ;
405 }
406 else
407 {
408 StringArray a1 = p1.split() ;
409 StringArray a2 = p2.split() ;
410 a2.insert( a2.begin() , a1.begin() , a1.end() ) ;
411 return join( a2 ) ;
412 }
413 }
414
415 G::Path G::Path::collapsed() const
416 {
417 typedef StringArray::iterator Ptr ;
418 const std::string dots = ".." ;
419
420 StringArray a = split() ;
421 Ptr start = a.begin() ;
422 Ptr end = a.end() ;
423 if( start != end && isAbsolute() )
424 ++start ;
425
426 while( start != end )
427 {
428 while( start != end && *start == dots )
429 start++ ;
430
431 Ptr p_dots = std::find( start , end , dots ) ;
432 if( p_dots == end )
433 break ;
434
435 G_ASSERT( p_dots != a.begin() ) ;
436 G_ASSERT( a.size() >= 2U ) ;
437
438 a.erase( a.erase(--p_dots) ) ;
439 end = a.end() ;
440 }
441
442 return join( a ) ;
443 }
444
445 bool G::Path::operator==( const Path & other ) const
446 {
447 return m_str == other.m_str ;
448 }
449
450 bool G::Path::operator!=( const Path & other ) const
451 {
452 return m_str != other.m_str ;
453 }
454
455 void G::Path::swap( Path & other )
456 {
457 using std::swap ;
458 swap( m_str , other.m_str ) ;
459 }
460
461 G::Path & G::Path::operator=( const Path & other )
462 {
463 Path tmp( other ) ;
464 tmp.swap( *this ) ;
465 return *this ;
466 }
467
468 static bool string_less( const std::string & a , const std::string & b )
469 {
470 return a.compare(b) < 0 ; // uses std::char_traits<char>::compare()
471 }
472
473 bool G::Path::less( const G::Path & a , const G::Path & b )
474 {
475 StringArray a_parts = a.split() ;
476 StringArray b_parts = b.split() ;
477 return std::lexicographical_compare(
478 a_parts.begin() , a_parts.end() ,
479 b_parts.begin() , b_parts.end() ,
480 string_less ) ;
481 }
482
483 G::Path G::Path::difference( const G::Path & root_in , const G::Path & path_in )
484 {
485 StringArray path_parts ;
486 StringArray root_parts ;
487 if( root_in != G::Path() ) root_parts = root_in.collapsed().split() ;
488 if( path_in != G::Path() ) path_parts = path_in.collapsed().split() ;
489 if( root_parts.size() == 1U && root_parts.at(0U) == "." ) root_parts.clear() ;
490 if( path_parts.size() == 1U && path_parts.at(0U) == "." ) path_parts.clear() ;
491
492 if( path_parts.size() < root_parts.size() )
493 return G::Path() ;
494
495 typedef std::pair<StringArray::iterator,StringArray::iterator> Pair ;
496 Pair p = std::mismatch( root_parts.begin() , root_parts.end() , path_parts.begin() ) ;
497
498 if( p.first == root_parts.end() && p.second == path_parts.end() )
499 return Path(".") ;
500 else if( p.first != root_parts.end() )
501 return Path() ;
502 else
503 return Path( PathImp::join(p.second,path_parts.end()) ) ;
504 }
505
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:290
G::Path::operator=
Path & operator=(const Path &other)
Assignment operator.
Definition: gpath.cpp:461
G::Path::swap
void swap(Path &other)
Swaps this with other.
Definition: gpath.cpp:455
G::PathImp
A private implementation class used by G::Path providing a set of static methods. ...
Definition: gpath.cpp:35
G::Path::Path
Path()
Default constructor for a zero-length path.
Definition: gpath.cpp:245
G::Str::tail
static std::string tail(const std::string &in, size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1102
G::Path::withoutExtension
Path withoutExtension() const
Returns a path without the basename extension, if any.
Definition: gpath.cpp:328
glog.h
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:310
G::Path::operator!=
bool operator!=(const Path &path) const
Comparison operator.
Definition: gpath.cpp:450
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:908
G::Path::extension
std::string extension() const
Returns the path's basename extension, ie.
Definition: gpath.cpp:373
G::Path::simple
bool simple() const
Returns true if the path has a single component (ignoring "." parts), ie.
Definition: gpath.cpp:295
G::Path::isRelative
bool isRelative() const
Returns true if the path is a relative path.
Definition: gpath.cpp:305
G::Path::difference
static Path difference(const Path &p1, const Path &p2)
Returns the relative path from p1 to p2.
Definition: gpath.cpp:483
G::Path::less
static bool less(const Path &a, const Path &b)
Compares two paths, with simple eight-bit lexicographical comparisons of each path component...
Definition: gpath.cpp:473
gstr.h
gassert.h
G::Str::replaceAll
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurances of sub-string 'from' with 'to'...
Definition: gstr.cpp:195
G::Path::isAbsolute
bool isAbsolute() const
Returns !isRelative().
Definition: gpath.cpp:300
G::Path::operator==
bool operator==(const Path &path) const
Comparison operator.
Definition: gpath.cpp:445
G::Path::dirname
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
Definition: gpath.cpp:318
G::Path::split
StringArray split() const
Spits the path into a list of component parts (ignoring "." parts unless the whole path is "...
Definition: gpath.cpp:382
G::Path::collapsed
Path collapsed() const
Returns the path with "foo/.." and "." parts removed, so far as is possible without changing the mean...
Definition: gpath.cpp:415
gpath.h
G::Path::pathAppend
void pathAppend(const std::string &tail)
Appends a filename or a relative path to this path.
Definition: gpath.cpp:357
G::Path::setWindowsStyle
static void setWindowsStyle()
Sets windows mode for testing purposes.
Definition: gpath.cpp:240
G::Path::setPosixStyle
static void setPosixStyle()
Sets posix mode for testing purposes.
Definition: gpath.cpp:235
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
G::Path::join
static Path join(const StringArray &parts)
Builds a path from a set of parts.
Definition: gpath.cpp:390
G::Path::~Path
~Path()
Destructor.
Definition: gpath.cpp:249
gdebug.h
G::Path::nullDevice
static Path nullDevice()
Returns the path of the "/dev/null" special file, or equivalent.
Definition: gpath.cpp:285
G::Path::withExtension
Path withExtension(const std::string &ext) const
Returns the path with the new basename extension.
Definition: gpath.cpp:346