]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/os/path.h
allow undo “make detail/structural”, <3 @SpiKe, thanks @Garux, fix #76
[xonotic/netradiant.git] / libs / os / path.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined ( INCLUDED_OS_PATH_H )
23 #define INCLUDED_OS_PATH_H
24
25 #include "globaldefs.h"
26
27 /// \file
28 /// \brief OS file-system path comparison, decomposition and manipulation.
29 ///
30 /// - Paths are c-style null-terminated-character-arrays.
31 /// - Path separators must be forward slashes (unix style).
32 /// - Directory paths must end in a separator.
33 /// - Paths must not contain the ascii characters \\ : * ? " < > or |.
34 /// - Paths may be encoded in UTF-8 or any extended-ascii character set.
35
36 #include "string/string.h"
37
38 #if GDEF_OS_WINDOWS
39 #define OS_CASE_INSENSITIVE
40 #endif
41
42 /// \brief Returns true if \p path is lexicographically sorted before \p other.
43 /// If both \p path and \p other refer to the same file, neither will be sorted before the other.
44 /// O(n)
45 inline bool path_less( const char* path, const char* other ){
46 #if defined( OS_CASE_INSENSITIVE )
47         return string_less_nocase( path, other );
48 #else
49         return string_less( path, other );
50 #endif
51 }
52
53 /// \brief Returns <0 if \p path is lexicographically less than \p other.
54 /// Returns >0 if \p path is lexicographically greater than \p other.
55 /// Returns 0 if both \p path and \p other refer to the same file.
56 /// O(n)
57 inline int path_compare( const char* path, const char* other ){
58 #if defined( OS_CASE_INSENSITIVE )
59         return string_compare_nocase( path, other );
60 #else
61         return string_compare( path, other );
62 #endif
63 }
64
65 /// \brief Returns true if \p path and \p other refer to the same file or directory.
66 /// O(n)
67 inline bool path_equal( const char* path, const char* other ){
68 #if defined( OS_CASE_INSENSITIVE )
69         return string_equal_nocase( path, other );
70 #else
71         return string_equal( path, other );
72 #endif
73 }
74
75 /// \brief Returns true if the first \p n bytes of \p path and \p other form paths that refer to the same file or directory.
76 /// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path.
77 /// O(n)
78 inline bool path_equal_n( const char* path, const char* other, std::size_t n ){
79 #if defined( OS_CASE_INSENSITIVE )
80         return string_equal_nocase_n( path, other, n );
81 #else
82         return string_equal_n( path, other, n );
83 #endif
84 }
85
86
87 /// \brief Returns true if \p path is a fully qualified file-system path.
88 /// O(1)
89 inline bool path_is_absolute( const char* path ){
90 #if GDEF_OS_WINDOWS
91         return path[0] == '/'
92                    || ( path[0] != '\0' && path[1] == ':' ); // local drive
93 #elif GDEF_OS_POSIX
94         return path[0] == '/';
95 #endif
96 }
97
98 /// \brief Returns true if \p path is a directory.
99 /// O(n)
100 inline bool path_is_directory( const char* path ){
101         std::size_t length = strlen( path );
102         if ( length > 0 ) {
103                 return path[length - 1] == '/';
104         }
105         return false;
106 }
107
108 /// \brief Returns a pointer to the first character of the component of \p path following the first directory component.
109 /// O(n)
110 inline const char* path_remove_directory( const char* path ){
111         const char* first_separator = strchr( path, '/' );
112         if ( first_separator != 0 ) {
113                 return ++first_separator;
114         }
115         return "";
116 }
117
118 /// \brief Returns a pointer to the first character of the filename component of \p path.
119 /// O(n)
120 inline const char* path_get_filename_start( const char* path ){
121         {
122                 const char* last_forward_slash = strrchr( path, '/' );
123                 if ( last_forward_slash != 0 ) {
124                         return last_forward_slash + 1;
125                 }
126         }
127
128         // not strictly necessary,since paths should not contain '\'
129         {
130                 const char* last_backward_slash = strrchr( path, '\\' );
131                 if ( last_backward_slash != 0 ) {
132                         return last_backward_slash + 1;
133                 }
134         }
135
136         return path;
137 }
138
139 /// \brief Returns a pointer to the character after the end of the filename component of \p path - either the extension separator or the terminating null character.
140 /// O(n)
141 inline const char* path_get_filename_base_end( const char* path ){
142         const char* last_period = strrchr( path_get_filename_start( path ), '.' );
143         return ( last_period != 0 ) ? last_period : path + string_length( path );
144 }
145
146 /// \brief Returns the length of the filename component (not including extension) of \p path.
147 /// O(n)
148 inline std::size_t path_get_filename_base_length( const char* path ){
149         return path_get_filename_base_end( path ) - path;
150 }
151
152 /// \brief If \p path is a child of \p base, returns the subpath relative to \p base, else returns \p path.
153 /// O(n)
154 inline const char* path_make_relative( const char* path, const char* base ){
155         const std::size_t length = string_length( base );
156         if ( path_equal_n( path, base, length ) ) {
157                 return path + length;
158         }
159         return path;
160 }
161
162 /// \brief Returns a pointer to the first character of the file extension of \p path, or "" if not found.
163 /// O(n)
164 inline const char* path_get_extension( const char* path ){
165         const char* last_period = strrchr( path_get_filename_start( path ), '.' );
166         if ( last_period != 0 ) {
167                 return ++last_period;
168         }
169         return "";
170 }
171
172 /// \brief Returns true if \p extension is of the same type as \p other.
173 /// O(n)
174 inline bool extension_equal( const char* extension, const char* other ){
175         return path_equal( extension, other );
176 }
177
178 template<typename Functor>
179 class MatchFileExtension
180 {
181 const char* m_extension;
182 const Functor& m_functor;
183 public:
184 MatchFileExtension( const char* extension, const Functor& functor ) : m_extension( extension ), m_functor( functor ){
185 }
186 void operator()( const char* name ) const {
187         const char* extension = path_get_extension( name );
188         if ( extension_equal( extension, m_extension ) ) {
189                 m_functor( name );
190         }
191 }
192 };
193
194 /// \brief A functor which invokes its contained \p functor if the \p name argument matches its \p extension.
195 template<typename Functor>
196 inline MatchFileExtension<Functor> matchFileExtension( const char* extension, const Functor& functor ){
197         return MatchFileExtension<Functor>( extension, functor );
198 }
199
200 class PathCleaned
201 {
202 public:
203 const char* m_path;
204 PathCleaned( const char* path ) : m_path( path ){
205 }
206 };
207
208 /// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators.
209 template<typename TextOutputStreamType>
210 TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const PathCleaned& path ){
211         const char* i = path.m_path;
212         for (; *i != '\0'; ++i )
213         {
214                 if ( *i == '\\' ) {
215                         ostream << '/';
216                 }
217                 else
218                 {
219                         ostream << *i;
220                 }
221         }
222         return ostream;
223 }
224
225 class DirectoryCleaned
226 {
227 public:
228 const char* m_path;
229 DirectoryCleaned( const char* path ) : m_path( path ){
230 }
231 };
232
233 /// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators, and appends a separator if necessary.
234 template<typename TextOutputStreamType>
235 TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const DirectoryCleaned& path ){
236         const char* i = path.m_path;
237         for (; *i != '\0'; ++i )
238         {
239                 if ( *i == '\\' ) {
240                         ostream << '/';
241                 }
242                 else
243                 {
244                         ostream << *i;
245                 }
246         }
247         char c = *( i - 1 );
248         if ( c != '/' && c != '\\' ) {
249                 ostream << '/';
250         }
251         return ostream;
252 }
253
254
255 #endif