diff --git a/Makefile b/Makefile index ae437f8..8a4a429 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,8 @@ COMMON = \ c-sync.cc \ c-syncedit.cc \ c-syncoverrides.cc \ + \ + p-filesystem.cc \ # END COMMON TOOL = \ diff --git a/p-filesystem.cc b/p-filesystem.cc new file mode 100644 index 0000000..690a717 --- /dev/null +++ b/p-filesystem.cc @@ -0,0 +1,182 @@ +/******************************************************************************/ +/* FILESYSTEM ABSTRACTION *****************************************************/ +/******************************************************************************/ + +#include +using namespace ebcl; + +#ifdef _WIN32 +# define M_PATHSEP_ '\\' +#else +# define M_PATHSEP_ '/' +#endif + + +/*= T_FSPath =================================================================*/ + +bool T_FSPath::IsValidRoot( + T_String const& str ) noexcept +{ + if ( !str ) { + return true; + } + +#ifdef _WIN32 + // TODO: support for network names (\\server\path) + if ( str.length( ) == 3 && ( str.endsWith( ":\\" ) + || str.endsWith( ":/" ) ) ) { + return str[ 0 ].isAlpha( ); + } else { + return ( str == "/" || str == "\\" ); + } +#else + return ( str == "/" || str == "\\" ); +#endif +} + +bool T_FSPath::IsValidComponent( + T_String const& str ) noexcept +{ + if ( !str ) { + return false; + } + + T_StringIterator it{ str }; + while ( !it.atEnd( ) ) { + const auto c{ it.character( ) }; + it.next( ); + if ( c.isControl( ) || c == '\\' || c == '/' || c == '"' + || c == '<' || c == '>' || c == '|' || c == ':' + || c == '*' || c == '?' ) { + return false; + } + } + return true; +} + +/*----------------------------------------------------------------------------*/ + +T_FSPath::T_FSPath( + T_String const& path ) noexcept +{ + if ( !path ) { + valid_ = true; + return; + } + + // Find sequences of (back)slashes in the string. For each sequence + // we will store the positions of the first and last (back)slash. + T_AutoArray< uint32_t , 64 > slashPos; + T_StringIterator it{ path }; + while ( !it.atEnd( ) ) { + const auto c{ it.character( ) }; + const auto p{ it.index( ) }; + it.next( ); + + if ( c != '/' && c != '\\' ) { + continue; + } + + const auto ps{ slashPos.size( ) }; + if ( ps == 0 || slashPos[ ps - 1 ] != p - 1 ) { + slashPos.add( p ); + slashPos.add( p ); + } else { + slashPos[ ps - 1 ] ++; + } + } + + // No (back)slashes, this is a relative path + const auto sps{ slashPos.size( ) }; + if ( sps == 0 ) { + components_.add( path ); + valid_ = IsValidComponent( path ); + return; + } + + // Collect the first item, with the first (back)slash included, + // check if it's a root + const auto firstSlashPos{ slashPos[ 0 ] }; + T_String firstItem{ path.substr( 0 , 1 + firstSlashPos ) }; + valid_ = true; + if ( IsValidRoot( firstItem ) ) { + root_ = std::move( firstItem ); + } + + // Get and check the components + uint32_t pos{ root_ ? 2u : 0u }; + while ( pos <= sps ) { + T_String item{ path.range( + pos ? ( 1 + slashPos[ pos - 1 ] ) : 0 , + pos == sps ? path.length( ) : ( slashPos[ pos ] - 1 ) ) + }; + assert( item || pos == sps ); + if ( item ) { + valid_ = valid_ && IsValidComponent( item ); + components_.add( std::move( item ) ); + } + pos += 2; + } +} + +/*----------------------------------------------------------------------------*/ + +uint32_t T_FSPath::computeHash( ) const noexcept +{ + uint32_t h{ ComputeHash( root_ ) }; + const auto cs{ components_.size( ) }; + h = ( ( h << 27 ) | ( h >> 5 ) ) ^ cs; + for ( auto i = 0u ; i < cs ; i ++ ) { + h = ( ( h << 27 ) | ( h >> 5 ) ) ^ ComputeHash( components_[ i ] ); + } + return h; +} + +void T_FSPath::appendTo( + T_StringBuilder& sb ) const noexcept +{ + const auto cs{ components_.size( ) }; + if ( !( root_ || cs ) ) { + sb << '.'; + return; + } + + for ( auto i = 0u ; i < cs ; i ++ ) { + if ( i == 0 ) { + sb << root_; + } else { + sb << M_PATHSEP_; + } + sb << components_[ i ]; + } +} + +/*----------------------------------------------------------------------------*/ + +#ifdef _WIN32 +# define M_CMPSTR_(A,B) (A).compareIgnoreCase( B ) +#else +# define M_CMPSTR_(A,B) (A).compare( B ) +#endif + +int32_t T_FSPath::compareTo( + T_FSPath const& other ) const noexcept +{ + if ( &other == this ) { + return 0; + } + + int32_t cmp{ M_CMPSTR_( root_ , other.root_ ) }; + if ( cmp == 0 ) { + const auto nca{ components_.size( ) } , + ncb{ other.components_.size( ) } , + nc{ std::min( nca , ncb ) }; + for ( auto i = 0u ; i < nc && cmp == 0 ; i ++ ) { + cmp = M_CMPSTR_( components_[ i ] , other.components_[ i ] ); + } + if ( cmp == 0 ) { + cmp = T_Comparator< uint32_t >::compare( nca , ncb ); + } + } + return cmp; +} diff --git a/p-filesystem.hh b/p-filesystem.hh index 86cad99..9b9f133 100644 --- a/p-filesystem.hh +++ b/p-filesystem.hh @@ -54,7 +54,6 @@ class T_FSPath T_String const& root( ) const noexcept; T_Components const& components( ) const noexcept; - // FIXME: provide iterators for the components bool isRelative( ) const noexcept; bool isAbsolute( ) const noexcept; @@ -68,10 +67,14 @@ class T_FSPath // Append to a string builder void appendTo( T_StringBuilder& sb ) const noexcept; - // Equality operators. On Windows the comparison will be - // case-insensitive. + // Comparisons. On Windows the comparison will be case-insensitive. + int32_t compareTo( T_FSPath const& other ) const noexcept; bool operator ==( T_FSPath const& other ) const noexcept; bool operator !=( T_FSPath const& other ) const noexcept; + bool operator <( T_FSPath const& other ) const noexcept; + bool operator <=( T_FSPath const& other ) const noexcept; + bool operator >( T_FSPath const& other ) const noexcept; + bool operator >=( T_FSPath const& other ) const noexcept; //---------------------------------------------------------------------- @@ -108,13 +111,162 @@ class T_FSPath }; M_DECLARE_SWAP( T_FSPath ); M_DECLARE_HASH( T_FSPath ); -M_LSHIFT_OP( T_StringBuilder , T_FSPath ); +M_LSHIFT_OP( T_StringBuilder , T_FSPath const& ); + + +/*= FILESYSTEM ===============================================================*/ + +class T_Filesystem +{ + public: + // Return the absolute path to the current directory + static T_FSPath currentDirectory( ) noexcept; + +}; /*= T_FSPath =================================================================*/ // FIXME: inline stuff should be moved +inline T_FSPath::T_FSPath( ) noexcept + : root_{ } , components_{ } , valid_{ true } +{ } +/*----------------------------------------------------------------------------*/ + +inline T_FSPath::T_FSPath( + T_FSPath const& other ) noexcept + : root_{ other.root_ } , components_{ other.components_ } , + valid_{ other.valid_ } +{ } + +inline T_FSPath& T_FSPath::operator =( + T_FSPath const& other ) noexcept +{ + root_ = other.root_; + components_ = other.components_; + valid_ = other.valid_; + return *this; +} + +/*----------------------------------------------------------------------------*/ + +inline T_FSPath::T_FSPath( T_FSPath&& other ) noexcept + : T_FSPath{ } +{ + swap( other ); +} + +inline T_FSPath& T_FSPath::operator =( + T_FSPath&& other ) noexcept +{ + root_ = T_String{ }; + components_.free( ); + valid_ = true; + return swap( other ); +} + +/*----------------------------------------------------------------------------*/ + +inline T_FSPath& T_FSPath::swap( + T_FSPath& other ) noexcept +{ + using std::swap; + swap( root_ , other.root_ ); + swap( components_ , other.components_ ); + swap( valid_ , other.valid_ ); + return *this; +} + +inline M_DEFINE_SWAP( T_FSPath ) +{ + lhs.swap( rhs ); +} + +/*----------------------------------------------------------------------------*/ + +inline bool T_FSPath::isValid( ) const noexcept +{ + return valid_; +} + +inline T_String const& T_FSPath::root( ) const noexcept +{ + return root_; +} + +inline T_FSPath::T_Components const& T_FSPath::components( ) const noexcept +{ + return components_; +} + +inline bool T_FSPath::isRelative( ) const noexcept +{ + return !root_; +} + +inline bool T_FSPath::isAbsolute( ) const noexcept +{ + return bool( root_ ); +} + +/*----------------------------------------------------------------------------*/ + +inline T_String T_FSPath::toString( ) const noexcept +{ + T_StringBuilder sb; + appendTo( sb ); + return T_String{ std::move( sb ) }; +} + +inline M_DEFINE_HASH( T_FSPath ) +{ + return item.computeHash( ); +} + +inline M_LSHIFT_OP( T_StringBuilder , T_FSPath const& ) +{ + value.appendTo( obj ); + return obj; +} + +/*----------------------------------------------------------------------------*/ + +inline bool T_FSPath::operator ==( + T_FSPath const& other ) const noexcept +{ + return compareTo( other ) == 0; +} + +inline bool T_FSPath::operator !=( + T_FSPath const& other ) const noexcept +{ + return compareTo( other ) != 0; +} + +inline bool T_FSPath::operator <( + T_FSPath const& other ) const noexcept +{ + return compareTo( other ) < 0; +} + +inline bool T_FSPath::operator <=( + T_FSPath const& other ) const noexcept +{ + return compareTo( other ) <= 0; +} + +inline bool T_FSPath::operator >( + T_FSPath const& other ) const noexcept +{ + return compareTo( other ) > 0; +} + +inline bool T_FSPath::operator >=( + T_FSPath const& other ) const noexcept +{ + return compareTo( other ) >= 0; +} } // namespace ebcl