38 static bool use_posix ;
39 using pos_t = std::string::size_type ;
45 static pos_t windows_slashpos(
const std::string & s )
47 return s.rfind(
'\\') ;
49 static bool windows_simple(
const std::string & s )
51 return s.find(
'/') == std::string::npos && s.find(
'\\') == std::string::npos ;
53 static bool windows_absolute(
const std::string & s )
56 ( s.length() >= 3U && s.at(1U) ==
':' && s.at(2U) ==
'\\' ) ||
57 ( s.length() >= 1U && s.at(0U) ==
'\\' ) ;
59 static pos_t windows_rootsize(
const std::string & s , std::size_t chars , std::size_t parts )
61 G_ASSERT( s.length() >= chars ) ;
62 G_ASSERT( parts == 1U || parts == 2U ) ;
63 pos_t pos = s.find(
'\\' , chars ) ;
64 if( parts == 2U && pos != std::string::npos )
65 pos = s.find(
'\\' , pos+1U ) ;
66 return pos == std::string::npos ? s.length() : pos ;
68 static pos_t windows_rootsize(
const std::string & s )
72 if( s.length() >= 3U && s.at(1U) ==
':' && s.at(2U) ==
'\\' )
74 if( s.length() >= 2U && s.at(1U) ==
':' )
76 if( s.find(
"\\\\?\\UNC\\") == 0U )
77 return windows_rootsize(s,8U,2U) ;
78 if( s.find(
"\\\\?\\") == 0U && s.size() > 5U && s.at(5U) ==
':' )
79 return windows_rootsize(s,4U,1U) ;
80 if( s.find(
"\\\\?\\") == 0U )
81 return windows_rootsize(s,4U,2U) ;
82 if( s.find(
"\\\\.\\") == 0U )
83 return windows_rootsize(s,4U,1U) ;
84 if( s.find(
"\\\\") == 0U )
85 return windows_rootsize(s,2U,2U) ;
86 if( s.find(
'\\') == 0U )
90 static void windows_normalise( std::string & s )
93 bool special = s.find(
"\\\\") == 0U ;
95 if( special ) s =
"\\" + s ;
97 while( s.length() > 1U )
99 pos_t pos = s.rfind(
'\\') ;
100 if( pos == std::string::npos ) break ;
101 if( (pos+1U) != s.length() ) break ;
102 if( pos < windows_rootsize(s) ) break ;
106 static std::string windows_null()
115 static pos_t posix_slashpos(
const std::string & s )
117 return s.rfind(
'/') ;
119 static bool posix_simple(
const std::string & s )
121 return s.find(
'/') == std::string::npos ;
123 static void posix_normalise( std::string & s )
126 while( s.length() > 1U && s.at(s.length()-1U) ==
'/' ) s.resize(s.length()-1U) ;
128 static bool posix_absolute(
const std::string & s )
130 return !s.empty() && s.at(0U) ==
'/' ;
132 static pos_t posix_rootsize(
const std::string & s )
134 return s.empty() || s.at(0U) !=
'/' ? 0U : 1U ;
136 static std::string posix_null()
143 return use_posix ? posix_sep() : windows_sep() ;
145 static void normalise( std::string & s )
147 use_posix ? posix_normalise(s) : windows_normalise(s) ;
149 static bool simple(
const std::string & s )
151 return use_posix ? posix_simple(s) : windows_simple(s) ;
153 static bool absolute(
const std::string & s )
155 return use_posix ? posix_absolute(s) : windows_absolute(s) ;
157 static std::string null()
159 return use_posix ? posix_null() : windows_null() ;
161 static pos_t rootsize(
const std::string & s )
163 return use_posix ? posix_rootsize(s) : windows_rootsize(s) ;
165 static pos_t slashpos(
const std::string & s )
167 return use_posix ? posix_slashpos(s) : windows_slashpos(s) ;
169 static pos_t dotpos(
const std::string & s )
171 const pos_t npos = std::string::npos ;
172 const pos_t sp = slashpos( s ) ;
173 const pos_t dp = s.rfind(
'.' ) ;
176 else if( sp == npos )
184 static void splitInto(
const std::string & str ,
StringArray & a )
186 pos_t rs = rootsize(str) ;
192 std::string root = str.substr( 0U , rs ) ;
194 a.insert( a.begin() , root ) ;
204 const std::string dot( 1U ,
'.' ) ;
205 a.erase( std::remove( a.begin() , a.end() , std::string() ) , a.end() ) ;
206 std::size_t n = a.size() ;
207 a.erase( std::remove( a.begin() , a.end() , dot ) , a.end() ) ;
208 const bool all_dots = a.empty() && n != 0U ;
212 static std::string join( StringArray::const_iterator p , StringArray::const_iterator end )
216 for( ; p != end ; ++p , i++ )
218 bool drive = !use_posix && str.length() == 2U && str.at(1U) ==
':' ;
219 bool last_is_slash = !str.empty() &&
220 ( str.at(str.length()-1U) ==
'/' || str.at(str.length()-1U) ==
'\\' ) ;
221 if( i == 1 && (drive || last_is_slash) )
224 str.append( sep().data() , sep().size() ) ;
232 return join( a.begin() , a.end() ) ;
240bool G::PathImp::use_posix = false ;
242bool G::PathImp::use_posix = true ;
249 PathImp::use_posix = true ;
254 PathImp::use_posix = false ;
263 PathImp::normalise( m_str ) ;
269 PathImp::normalise( m_str ) ;
276 PathImp::normalise( m_str ) ;
284 PathImp::normalise( m_str ) ;
288 const std::string & tail_3 ) :
294 PathImp::normalise( m_str ) ;
301 m_str = *args.begin() ;
302 for(
const auto * p = args.begin()+1 ; p != args.end() ; ++p )
304 PathImp::normalise( m_str ) ;
310 return Path( PathImp::null() ) ;
315 return dirname().empty() ;
320 return PathImp::absolute( m_str ) ;
325 return !isAbsolute() ;
331 PathImp::splitInto( m_str , a ) ;
332 PathImp::purge( a ) ;
333 return a.empty() ? std::string() : a.at(a.size()-1U) ;
339 PathImp::splitInto( m_str , a ) ;
340 PathImp::purge( a ) ;
341 if( a.empty() )
return Path() ;
348 std::string::size_type sp = PathImp::slashpos(m_str) ;
349 std::string::size_type dp = PathImp::dotpos(m_str) ;
350 if( dp != std::string::npos )
352 std::string result = m_str ;
353 result.resize( dp ) ;
354 if( (sp == std::string::npos && dp == 0U) || ((sp+1U) == dp) )
356 return Path( result ) ;
366 std::string result = m_str ;
367 std::string::size_type dp = PathImp::dotpos(m_str) ;
368 if( dp != std::string::npos )
369 result.resize( dp ) ;
370 result.append( 1U ,
'.' ) ;
371 result.append( ext ) ;
372 return Path( result ) ;
380 else if( PathImp::simple(tail) )
382 m_str.append( sv_to_string(PathImp::sep()) + tail ) ;
386 Path result = join( *
this , tail ) ;
387 result.
swap( *
this ) ;
393 std::string::size_type pos = PathImp::dotpos(m_str) ;
395 pos == std::string::npos || (pos+1U) == m_str.length() ?
397 m_str.substr( pos+1U ) ;
403 PathImp::splitInto( m_str , a ) ;
404 if( PathImp::purge(a) ) a.push_back(
"." ) ;
410 if( a.empty() )
return Path() ;
411 return Path( PathImp::join(a) ) ;
420 else if( p2.
empty() )
428 a2.insert( a2.begin() , a1.begin() , a1.end() ) ;
435 const std::string dots =
".." ;
438 auto start = a.begin() ;
440 if( start != end && isAbsolute() )
443 while( start != end )
445 while( start != end && *start == dots )
448 auto p_dots = std::find( start , end , dots ) ;
452 G_ASSERT( p_dots != a.begin() ) ;
453 G_ASSERT( a.size() >= 2U ) ;
455 a.erase( a.erase(--p_dots) ) ;
464 return m_str == other.m_str ;
469 return m_str != other.m_str ;
475 swap( m_str , other.m_str ) ;
478static bool string_less(
const std::string & a ,
const std::string & b )
480 return a.compare(b) < 0 ;
487 return std::lexicographical_compare(
488 a_parts.begin() , a_parts.end() ,
489 b_parts.begin() , b_parts.end() ,
499 if( root_parts.size() == 1U && root_parts.at(0U) ==
"." ) root_parts.clear() ;
500 if( path_parts.size() == 1U && path_parts.at(0U) ==
"." ) path_parts.clear() ;
502 if( path_parts.size() < root_parts.size() )
505 using Pair = std::pair<StringArray::iterator,StringArray::iterator> ;
506 Pair p = std::mismatch( root_parts.begin() , root_parts.end() , path_parts.begin() ) ;
508 if( p.first == root_parts.end() && p.second == path_parts.end() )
510 else if( p.first != root_parts.end() )
513 return Path( PathImp::join(p.second,path_parts.end()) ) ;
A private implementation class used by G::Path providing a set of static methods.
A Path object represents a file system path.
void swap(Path &other) noexcept
Swaps this with other.
static bool less(const Path &a, const Path &b)
Compares two paths, with simple eight-bit lexicographical comparisons of each path component.
bool operator!=(const Path &path) const
Comparison operator.
bool isRelative() const
Returns true if the path is a relative path or empty().
static Path join(const StringArray &parts)
Builds a path from a set of parts.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Path withoutExtension() const
Returns a path without the basename extension, if any.
bool isAbsolute() const
Returns !isRelative().
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
std::string extension() const
Returns the path's basename extension, ie.
bool simple() const
Returns true if the path has a single component (ignoring "." parts), ie.
static Path nullDevice()
Returns the path of the "/dev/null" special file, or equivalent.
void pathAppend(const std::string &tail)
Appends a filename or a relative path to this path.
Path withExtension(const std::string &ext) const
Returns the path with the new basename extension.
Path collapsed() const
Returns the path with "foo/.." and "." parts removed, so far as is possible without changing the mean...
static void setPosixStyle()
Sets posix mode for testing purposes.
bool operator==(const Path &path) const
Comparison operator.
static void setWindowsStyle()
Sets windows mode for testing purposes.
StringArray split() const
Spits the path into a list of component parts (ignoring "." parts unless the whole path is "....
bool empty() const noexcept
Returns true if size() is zero.
Path()
Default constructor for a zero-length path.
static Path difference(const Path &p1, const Path &p2)
Returns the relative path from p1 to p2.
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
A class template like c++17's std::basic_string_view.
std::vector< std::string > StringArray
A std::vector of std::strings.