]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/os/path.h
Merge branch 'master' into divVerent/farplanedist-sky-fix
[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 /// \file
26 /// \brief OS file-system path comparison, decomposition and manipulation.
27 ///
28 /// - Paths are c-style null-terminated-character-arrays.
29 /// - Path separators must be forward slashes (unix style).
30 /// - Directory paths must end in a separator.
31 /// - Paths must not contain the ascii characters \\ : * ? " < > or |.
32 /// - Paths may be encoded in UTF-8 or any extended-ascii character set.
33
34 #include "string/string.h"
35
36 #if defined( WIN32 )
37 #define OS_CASE_INSENSITIVE
38 #endif
39
40 /// \brief Returns true if \p path is lexicographically sorted before \p other.
41 /// If both \p path and \p other refer to the same file, neither will be sorted before the other.
42 /// O(n)
43 inline bool path_less( const char* path, const char* other ){
44 #if defined( OS_CASE_INSENSITIVE )
45         return string_less_nocase( path, other );
46 #else
47         return string_less( path, other );
48 #endif
49 }
50
51 /// \brief Returns <0 if \p path is lexicographically less than \p other.
52 /// Returns >0 if \p path is lexicographically greater than \p other.
53 /// Returns 0 if both \p path and \p other refer to the same file.
54 /// O(n)
55 inline int path_compare( const char* path, const char* other ){
56 #if defined( OS_CASE_INSENSITIVE )
57         return string_compare_nocase( path, other );
58 #else
59         return string_compare( path, other );
60 #endif
61 }
62
63 /// \brief Returns true if \p path and \p other refer to the same file or directory.
64 /// O(n)
65 inline bool path_equal( const char* path, const char* other ){
66 #if defined( OS_CASE_INSENSITIVE )
67         return string_equal_nocase( path, other );
68 #else
69         return string_equal( path, other );
70 #endif
71 }
72
73 /// \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.
74 /// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path.
75 /// O(n)
76 inline bool path_equal_n( const char* path, const char* other, std::size_t n ){
77 #if defined( OS_CASE_INSENSITIVE )
78         return string_equal_nocase_n( path, other, n );
79 #else
80         return string_equal_n( path, other, n );
81 #endif
82 }
83
84
85 /// \brief Returns true if \p path is a fully qualified file-system path.
86 /// O(1)
87 inline bool path_is_absolute( const char* path ){
88 #if defined( WIN32 )
89         return path[0] == '/'
90                    || ( path[0] != '\0' && path[1] == ':' ); // local drive
91 #elif defined( POSIX )
92         return path[0] == '/';
93 #endif
94 }
95
96 /// \brief Returns true if \p path is a directory.
97 /// O(n)
98 inline bool path_is_directory( const char* path ){
99         std::size_t length = strlen( path );
100         if ( length > 0 ) {
101                 return path[length - 1] == '/';
102         }
103         return false;
104 }
105
106 /// \brief Returns a pointer to the first character of the component of \p path following the first directory component.
107 /// O(n)
108 inline const char* path_remove_directory( const char* path ){
109         const char* first_separator = strchr( path, '/' );
110         if ( first_separator != 0 ) {
111                 return ++first_separator;
112         }
113         return "";
114 }
115
116 /// \brief Returns a pointer to the first character of the filename component of \p path.
117 /// O(n)
118 inline const char* path_get_filename_start( const char* path ){
119         {
120                 const char* last_forward_slash = strrchr( path, '/' );
121                 if ( last_forward_slash != 0 ) {
122                         return last_forward_slash + 1;
123                 }
124         }
125
126         // not strictly necessary,since paths should not contain '\'
127         {
128                 const char* last_backward_slash = strrchr( path, '\\' );
129                 if ( last_backward_slash != 0 ) {
130                         return last_backward_slash + 1;
131                 }
132         }
133
134         return path;
135 }
136
137 /// \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.
138 /// O(n)
139 inline const char* path_get_filename_base_end( const char* path ){
140         const char* last_period = strrchr( path_get_filename_start( path ), '.' );
141         return ( last_period != 0 ) ? last_period : path + string_length( path );
142 }
143
144 /// \brief Returns the length of the filename component (not including extension) of \p path.
145 /// O(n)
146 inline std::size_t path_get_filename_base_length( const char* path ){
147         return path_get_filename_base_end( path ) - path;
148 }
149
150 /// \brief If \p path is a child of \p base, returns the subpath relative to \p base, else returns \p path.
151 /// O(n)
152 inline const char* path_make_relative( const char* path, const char* base ){
153         const std::size_t length = string_length( base );
154         if ( path_equal_n( path, base, length ) ) {
155                 return path + length;
156         }
157         return path;
158 }
159
160 /// \brief Returns a pointer to the first character of the file extension of \p path, or "" if not found.
161 /// O(n)
162 inline const char* path_get_extension( const char* path ){
163         const char* last_period = strrchr( path_get_filename_start( path ), '.' );
164         if ( last_period != 0 ) {
165                 return ++last_period;
166         }
167         return "";
168 }
169
170 /// \brief Returns true if \p extension is of the same type as \p other.
171 /// O(n)
172 inline bool extension_equal( const char* extension, const char* other ){
173         return path_equal( extension, other );
174 }
175
176 template<typename Functor>
177 class MatchFileExtension
178 {
179 const char* m_extension;
180 const Functor& m_functor;
181 public:
182 MatchFileExtension( const char* extension, const Functor& functor ) : m_extension( extension ), m_functor( functor ){
183 }
184 void operator()( const char* name ) const {
185         const char* extension = path_get_extension( name );
186         if ( extension_equal( extension, m_extension ) ) {
187                 m_functor( name );
188         }
189 }
190 };
191
192 /// \brief A functor which invokes its contained \p functor if the \p name argument matches its \p extension.
193 template<typename Functor>
194 inline MatchFileExtension<Functor> matchFileExtension( const char* extension, const Functor& functor ){
195         return MatchFileExtension<Functor>( extension, functor );
196 }
197
198 class PathCleaned
199 {
200 public:
201 const char* m_path;
202 PathCleaned( const char* path ) : m_path( path ){
203 }
204 };
205
206 /// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators.
207 template<typename TextOutputStreamType>
208 TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const PathCleaned& path ){
209         const char* i = path.m_path;
210         for (; *i != '\0'; ++i )
211         {
212                 if ( *i == '\\' ) {
213                         ostream << '/';
214                 }
215                 else
216                 {
217                         ostream << *i;
218                 }
219         }
220         return ostream;
221 }
222
223 class DirectoryCleaned
224 {
225 public:
226 const char* m_path;
227 DirectoryCleaned( const char* path ) : m_path( path ){
228 }
229 };
230
231 /// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators, and appends a separator if necessary.
232 template<typename TextOutputStreamType>
233 TextOutputStreamType& ostream_write( TextOutputStreamType& ostream, const DirectoryCleaned& path ){
234         const char* i = path.m_path;
235         for (; *i != '\0'; ++i )
236         {
237                 if ( *i == '\\' ) {
238                         ostream << '/';
239                 }
240                 else
241                 {
242                         ostream << *i;
243                 }
244         }
245         char c = *( i - 1 );
246         if ( c != '/' && c != '\\' ) {
247                 ostream << '/';
248         }
249         return ostream;
250 }
251
252
253 #endif