2020-11-17 11:16:16 +00:00
/*
Copyright ( C ) 2001 - 2006 , William Joseph .
All Rights Reserved .
This file is part of GtkRadiant .
GtkRadiant is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
GtkRadiant is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with GtkRadiant ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# if !defined ( INCLUDED_OS_PATH_H )
# define INCLUDED_OS_PATH_H
# include "globaldefs.h"
/// \file
/// \brief OS file-system path comparison, decomposition and manipulation.
///
/// - Paths are c-style null-terminated-character-arrays.
/// - Path separators must be forward slashes (unix style).
/// - Directory paths must end in a separator.
/// - Paths must not contain the ascii characters \\ : * ? " < > or |.
/// - Paths may be encoded in UTF-8 or any extended-ascii character set.
# include "string/string.h"
# if GDEF_OS_WINDOWS
# define OS_CASE_INSENSITIVE
# endif
/// \brief Returns true if \p path is lexicographically sorted before \p other.
/// If both \p path and \p other refer to the same file, neither will be sorted before the other.
/// O(n)
inline bool path_less ( const char * path , const char * other ) {
# if defined( OS_CASE_INSENSITIVE )
return string_less_nocase ( path , other ) ;
# else
return string_less ( path , other ) ;
# endif
}
/// \brief Returns <0 if \p path is lexicographically less than \p other.
/// Returns >0 if \p path is lexicographically greater than \p other.
/// Returns 0 if both \p path and \p other refer to the same file.
/// O(n)
inline int path_compare ( const char * path , const char * other ) {
# if defined( OS_CASE_INSENSITIVE )
return string_compare_nocase ( path , other ) ;
# else
return string_compare ( path , other ) ;
# endif
}
/// \brief Returns true if \p path and \p other refer to the same file or directory.
/// O(n)
inline bool path_equal ( const char * path , const char * other ) {
# if defined( OS_CASE_INSENSITIVE )
return string_equal_nocase ( path , other ) ;
# else
return string_equal ( path , other ) ;
# endif
}
/// \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.
/// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path.
/// O(n)
inline bool path_equal_n ( const char * path , const char * other , std : : size_t n ) {
# if defined( OS_CASE_INSENSITIVE )
return string_equal_nocase_n ( path , other , n ) ;
# else
return string_equal_n ( path , other , n ) ;
# endif
}
/// \brief Returns true if \p path is a fully qualified file-system path.
/// O(1)
inline bool path_is_absolute ( const char * path ) {
# if GDEF_OS_WINDOWS
return path [ 0 ] = = ' / '
2021-08-04 20:23:18 +00:00
| | ( path [ 0 ] ! = ' \0 ' & & path [ 1 ] = = ' : ' ) ; // local drive
2020-11-17 11:16:16 +00:00
# elif GDEF_OS_POSIX
return path [ 0 ] = = ' / ' ;
# endif
}
/// \brief Returns true if \p path is a directory.
/// O(n)
inline bool path_is_directory ( const char * path ) {
std : : size_t length = strlen ( path ) ;
if ( length > 0 ) {
return path [ length - 1 ] = = ' / ' ;
}
return false ;
}
/// \brief Returns a pointer to the first character of the component of \p path following the first directory component.
/// O(n)
inline const char * path_remove_directory ( const char * path ) {
const char * first_separator = strchr ( path , ' / ' ) ;
if ( first_separator ! = 0 ) {
return + + first_separator ;
}
return " " ;
}
/// \brief Returns a pointer to the first character of the filename component of \p path.
/// O(n)
inline const char * path_get_filename_start ( const char * path ) {
{
const char * last_forward_slash = strrchr ( path , ' / ' ) ;
if ( last_forward_slash ! = 0 ) {
return last_forward_slash + 1 ;
}
}
// not strictly necessary,since paths should not contain '\'
{
const char * last_backward_slash = strrchr ( path , ' \\ ' ) ;
if ( last_backward_slash ! = 0 ) {
return last_backward_slash + 1 ;
}
}
return path ;
}
/// \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.
/// O(n)
inline const char * path_get_filename_base_end ( const char * path ) {
const char * last_period = strrchr ( path_get_filename_start ( path ) , ' . ' ) ;
return ( last_period ! = 0 ) ? last_period : path + string_length ( path ) ;
}
/// \brief Returns the length of the filename component (not including extension) of \p path.
/// O(n)
inline std : : size_t path_get_filename_base_length ( const char * path ) {
return path_get_filename_base_end ( path ) - path ;
}
/// \brief If \p path is a child of \p base, returns the subpath relative to \p base, else returns \p path.
/// O(n)
inline const char * path_make_relative ( const char * path , const char * base ) {
const std : : size_t length = string_length ( base ) ;
if ( path_equal_n ( path , base , length ) ) {
return path + length ;
}
return path ;
}
/// \brief Returns a pointer to the first character of the file extension of \p path, or "" if not found.
/// O(n)
inline const char * path_get_extension ( const char * path ) {
const char * last_period = strrchr ( path_get_filename_start ( path ) , ' . ' ) ;
if ( last_period ! = 0 ) {
return + + last_period ;
}
return " " ;
}
/// \brief Returns true if \p extension is of the same type as \p other.
/// O(n)
inline bool extension_equal ( const char * extension , const char * other ) {
return path_equal ( extension , other ) ;
}
template < typename Functor >
class MatchFileExtension
{
const char * m_extension ;
const Functor & m_functor ;
public :
MatchFileExtension ( const char * extension , const Functor & functor ) : m_extension ( extension ) , m_functor ( functor ) {
}
void operator ( ) ( const char * name ) const {
const char * extension = path_get_extension ( name ) ;
if ( extension_equal ( extension , m_extension ) ) {
m_functor ( name ) ;
}
}
} ;
/// \brief A functor which invokes its contained \p functor if the \p name argument matches its \p extension.
template < typename Functor >
inline MatchFileExtension < Functor > matchFileExtension ( const char * extension , const Functor & functor ) {
return MatchFileExtension < Functor > ( extension , functor ) ;
}
class PathCleaned
{
public :
const char * m_path ;
PathCleaned ( const char * path ) : m_path ( path ) {
}
} ;
/// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators.
template < typename TextOutputStreamType >
TextOutputStreamType & ostream_write ( TextOutputStreamType & ostream , const PathCleaned & path ) {
const char * i = path . m_path ;
for ( ; * i ! = ' \0 ' ; + + i )
{
if ( * i = = ' \\ ' ) {
ostream < < ' / ' ;
}
else
{
ostream < < * i ;
}
}
return ostream ;
}
class DirectoryCleaned
{
public :
const char * m_path ;
DirectoryCleaned ( const char * path ) : m_path ( path ) {
}
} ;
/// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators, and appends a separator if necessary.
template < typename TextOutputStreamType >
TextOutputStreamType & ostream_write ( TextOutputStreamType & ostream , const DirectoryCleaned & path ) {
const char * i = path . m_path ;
for ( ; * i ! = ' \0 ' ; + + i )
{
if ( * i = = ' \\ ' ) {
ostream < < ' / ' ;
}
else
{
ostream < < * i ;
}
}
char c = * ( i - 1 ) ;
if ( c ! = ' / ' & & c ! = ' \\ ' ) {
ostream < < ' / ' ;
}
return ostream ;
}
# endif