E-MailRelay

gpath.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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/// \file gpath.h
19///
20
21#ifndef G_PATH_H
22#define G_PATH_H
23
24#include "gdef.h"
25#include "gstringarray.h"
26#include "gstringview.h"
27#ifdef G_WINDOWS
28#include "gconvert.h"
29#endif
30#include <string>
31#include <iostream>
32#include <initializer_list>
33
34 namespace G
35{
36 class Path ;
37}
38
39//| \class G::Path
40/// A Path object represents a file system path. The class is concerned with
41/// path syntax, not file system i/o.
42///
43/// A full path is made up of a root, a set of directories, and a filename. The
44/// posix root is just a forward slash, but on Windows the root can be complex,
45/// possibly including non-splitting separator characters. The filename may have
46/// an extension part, which is to the right of the right-most dot.
47///
48/// The path separator is used between directories and filename, but only
49/// between the root and the first directory if the root does not itself end in
50/// a separator character.
51///
52/// A windows drive-letter root may end with a separator character or not; if
53/// there is no separator character at the end of the drive-letter root then
54/// the path is relative to the drive's current working directory.
55///
56/// Path components of "." are ignored by simple(), basename(), and dirname().
57/// Path components of ".." are retained but can be eliminated if they are
58/// collapsed(). Path components of "." are eliminated by split(), except
59/// in the degenerate case.
60///
61/// This class is agnostic on the choice of UTF-8 or eight-bit characters since
62/// the delimiters are all seven-bit ascii. Wide characters are not used,
63/// following "utf8everywhere.org" rather than std::filesystem::path (but
64/// see G_ANSI as a temporary deprecated feature).
65///
66/// Most file operations should be handled in o/s-aware source (see G::File,
67/// G::Environment, G::Process etc) so that the Path character encoding is
68/// opaque. However, std::fstream objects can be initialised directly by
69/// using G::Path::iopath().
70///
71/// Both posix and windows behaviours are available at run-time; the default
72/// behaviour is the native behaviour, but this can be overridden, typically
73/// for testing purposes.
74///
75/// The posix path separator character is the forward-slash; on Windows it is a
76/// back-slash, but with all forward-slashes converted to back-slashes
77/// immediately on input.
78///
79/// \see G::File, G::Directory
80///
81 class G::Path
82{
83 public:
84 using value_type = char ;
85 using string_type = std::string ;
86 #if defined(G_WINDOWS) && !defined(G_ANSI)
87 using iopath_char_type = wchar_t ;
88 #else
89 using iopath_char_type = char ;
90 #endif
91
92 Path() noexcept(noexcept(std::string())) ;
93 ///< Default constructor for a zero-length path.
94 ///< Postcondition: empty()
95
96 Path( const std::string & path ) ;
97 ///< Implicit constructor from a string.
98
99 Path( std::string_view path ) ;
100 ///< Implicit constructor from a string view.
101
102 Path( const char * path ) ;
103 ///< Implicit constructor from a c string.
104
105 Path( const Path & path , const std::string & tail ) ;
106 ///< Constructor with an implicit pathAppend().
107
108 Path( const Path & path , const std::string & tail_1 , const std::string & tail_2 ) ;
109 ///< Constructor with two implicit pathAppend()s.
110
111 Path( const Path & path , const std::string & tail_1 , const std::string & tail_2 , const std::string & tail_3 ) ;
112 ///< Constructor with three implicit pathAppend()s.
113
114 bool empty() const noexcept ;
115 ///< Returns true if the path is empty.
116
117 std::string str() const ;
118 ///< Returns the path string.
119
120 const iopath_char_type * iopath() const ;
121 ///< Returns the path's string with a type that is suitable for
122 ///< initialising std::fstreams.
123
124 const value_type * cstr() const noexcept ;
125 ///< Returns the path's c-string. Typically used by o/s-aware
126 ///< code such as G::File.
127
128 bool simple() const ;
129 ///< Returns true if the path has a single component (ignoring "." parts),
130 ///< ie. the dirname() is empty.
131
132 std::string basename() const ;
133 ///< Returns the rightmost part of the path, ignoring "." parts.
134 ///< For a directory path this may be "..", but see also collapsed().
135
136 Path dirname() const ;
137 ///< Returns the path without the rightmost part, ignoring "." parts.
138 ///< For simple() paths the empty path is returned.
139
140 std::string extension() const ;
141 ///< Returns the path's basename extension, ie. anything
142 ///< after the rightmost dot. Returns the zero-length
143 ///< string if there is none.
144
145 Path withExtension( const std::string & ext ) const ;
146 ///< Returns the path with the new basename extension.
147 ///< Any previous extension is replaced. The extension
148 ///< should not normally have a leading dot and it
149 ///< should not be the empty string.
150
151 Path withoutExtension() const ;
152 ///< Returns a path without the basename extension, if any.
153 ///< Returns this path if there is no dot in the basename.
154 ///< As a special case, a basename() like ".foo" ends up as
155 ///< "."; prefer withExtension() where appropriate to avoid
156 ///< this.
157
158 Path withoutRoot() const ;
159 ///< Returns a path without the root part. This has no effect
160 ///< if the path isRelative().
161
162 bool isRoot() const noexcept ;
163 ///< Returns true if the path is a root, like "/", "c:",
164 ///< "c:/", "\\server\volume" etc.
165
166 bool isAbsolute() const noexcept ;
167 ///< Returns !isRelative().
168
169 bool isRelative() const noexcept ;
170 ///< Returns true if the path is a relative path or empty().
171
172 Path & pathAppend( const std::string & tail ) ;
173 ///< Appends a filename or a relative path to this path.
174
175 bool replace( const std::string_view & from , const std::string_view & to , bool ex_root = false ) ;
176 ///< Replaces the first occurrence of 'from' with 'to',
177 ///< optionally excluding the root part. Returns true
178 ///< if replaced.
179
180 StringArray split() const ;
181 ///< Spits the path into a list of component parts (ignoring "." parts
182 ///< unless the whole path is ".").
183
184 static Path join( const StringArray & parts ) ;
185 ///< Builds a path from a set of parts. Note that part boundaries
186 ///< are not necessarily preserved once they have been join()ed
187 ///< into a path.
188
189 static Path join( const Path & p1 , const Path & p2 ) ;
190 ///< Joins two paths together. The second should be a relative path.
191
192 static Path difference( const Path & p1 , const Path & p2 ) ;
193 ///< Returns the relative path from p1 to p2. Returns the empty
194 ///< path if p2 is not under p1. Returns "." if p1 and p2 are the
195 ///< same. Input paths are collapsed(). Empty input paths are
196 ///< treated as ".".
197
198 Path collapsed() const ;
199 ///< Returns the path with "foo/.." and "." parts removed, so far
200 ///< as is possible without changing the meaning of the path.
201 ///< Parts like "../foo" at the beginning of the path, or immediately
202 ///< following the root, are not removed.
203
204 static Path nullDevice() ;
205 ///< Returns the path of the "/dev/null" special file, or equivalent.
206
207 void swap( Path & other ) noexcept ;
208 ///< Swaps this with other.
209
210 bool operator==( const Path & path ) const noexcept(noexcept(std::string().compare(std::string()))) ;
211 ///< Comparison operator.
212
213 bool operator!=( const Path & path ) const noexcept(noexcept(std::string().compare(std::string()))) ;
214 ///< Comparison operator.
215
216 static void setPosixStyle() ;
217 ///< Sets posix mode for testing purposes.
218
219 static void setWindowsStyle() ;
220 ///< Sets windows mode for testing purposes.
221
222 static bool less( const Path & a , const Path & b ) ;
223 ///< Compares two paths, with simple eight-bit lexicographical
224 ///< comparisons of each path component. This is slightly different
225 ///< from a lexicographical comparison of the compete strings
226 ///< (eg. "a/b" compared to "a./b"), and it is not suitable for
227 ///< UTF-8 paths.
228
229private:
230 std::string m_str ;
231 #if defined(G_WINDOWS) && !defined(G_ANSI)
232 mutable std::wstring m_wstr ;
233 #endif
234} ;
235
236 inline
237 bool G::Path::empty() const noexcept
238{
239 return m_str.empty() ;
240}
241
242 inline
243std::string G::Path::str() const
244{
245 return m_str ;
246}
247
248 inline
249 const char * G::Path::cstr() const noexcept
250{
251 return m_str.c_str() ;
252}
253
254 inline
255 const G::Path::iopath_char_type * G::Path::iopath() const
256{
257 #if defined(G_WINDOWS) && !defined(G_ANSI)
258 m_wstr = Convert::widen( m_str ) ;
259 return m_wstr.c_str() ;
260 #else
261 return m_str.c_str() ;
262 #endif
263}
264
265 namespace G
266{
267 inline
268 std::ostream & operator<<( std::ostream & stream , const Path & path )
269 {
270 return stream << path.str() ;
271 }
272
273 inline
274 Path & operator/=( Path & p , const std::string & str )
275 {
276 p.pathAppend( str ) ;
277 return p ;
278 }
279
280 inline
281 Path operator/( const Path & p , const std::string & str )
282 {
283 return Path( p , str ) ; // NOLINT not return {...}
284 }
285
286 Path & operator+=( Path & , const std::string & ) = delete ;
287 Path & operator+( const Path & , const std::string & ) = delete ;
288
289 inline
290 void swap( Path & p1 , Path & p2 ) noexcept
291 {
292 p1.swap( p2 ) ;
293 }
294}
295
296#endif
G::Convert::widen
static std::wstring widen(std::string_view)
Widens from UTF-8 to UTF-16/UCS-4 wstring.
Definition: gconvert.cpp:38
G::Path
A Path object represents a file system path.
Definition: gpath.h:82
G::Path::isAbsolute
bool isAbsolute() const noexcept
Returns !isRelative().
Definition: gpath.cpp:329
G::Path::withoutRoot
Path withoutRoot() const
Returns a path without the root part.
Definition: gpath.cpp:387
G::Path::swap
void swap(Path &other) noexcept
Swaps this with other.
Definition: gpath.cpp:506
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:513
G::Path::replace
bool replace(const std::string_view &from, const std::string_view &to, bool ex_root=false)
Replaces the first occurrence of 'from' with 'to', optionally excluding the root part.
Definition: gpath.cpp:420
G::Path::join
static Path join(const StringArray &parts)
Builds a path from a set of parts.
Definition: gpath.cpp:434
G::Path::cstr
const value_type * cstr() const noexcept
Returns the path's c-string.
Definition: gpath.h:249
G::Path::isRoot
bool isRoot() const noexcept
Returns true if the path is a root, like "/", "c:", "c:/", "\\server\volume" etc.
Definition: gpath.cpp:324
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:339
G::Path::withoutExtension
Path withoutExtension() const
Returns a path without the basename extension, if any.
Definition: gpath.cpp:357
G::Path::dirname
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
Definition: gpath.cpp:347
G::Path::extension
std::string extension() const
Returns the path's basename extension, ie.
Definition: gpath.cpp:411
G::Path::simple
bool simple() const
Returns true if the path has a single component (ignoring "." parts), ie.
Definition: gpath.cpp:319
G::Path::pathAppend
Path & pathAppend(const std::string &tail)
Appends a filename or a relative path to this path.
Definition: gpath.cpp:404
G::Path::nullDevice
static Path nullDevice()
Returns the path of the "/dev/null" special file, or equivalent.
Definition: gpath.cpp:314
G::Path::Path
Path() noexcept(noexcept(std::string()))
Default constructor for a zero-length path.
G::Path::withExtension
Path withExtension(const std::string &ext) const
Returns the path with the new basename extension.
Definition: gpath.cpp:375
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:459
G::Path::isRelative
bool isRelative() const noexcept
Returns true if the path is a relative path or empty().
Definition: gpath.cpp:334
G::Path::setPosixStyle
static void setPosixStyle()
Sets posix mode for testing purposes.
Definition: gpath.cpp:251
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.h:243
G::Path::setWindowsStyle
static void setWindowsStyle()
Sets windows mode for testing purposes.
Definition: gpath.cpp:258
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:426
G::Path::empty
bool empty() const noexcept
Returns true if the path is empty.
Definition: gpath.h:237
G::Path::iopath
const iopath_char_type * iopath() const
Returns the path's string with a type that is suitable for initialising std::fstreams.
Definition: gpath.h:255
G::Path::difference
static Path difference(const Path &p1, const Path &p2)
Returns the relative path from p1 to p2.
Definition: gpath.cpp:525
G::string_view
A class like c++17's std::string_view.
Definition: gstringview.h:70
gconvert.h
gstringarray.h
gstringview.h
G
Low-level classes.
Definition: garg.h:36
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30
std
STL namespace.