379 lines
8.2 KiB
C++
379 lines
8.2 KiB
C++
/******************************************************************************/
|
|
/* FILESYSTEM ABSTRACTION *****************************************************/
|
|
/******************************************************************************/
|
|
|
|
#include <p-filesystem.hh>
|
|
using namespace ebcl;
|
|
|
|
|
|
#ifdef _WIN32
|
|
# define M_PATHSEP_ '\\'
|
|
#else
|
|
# define M_PATHSEP_ '/'
|
|
# include <unistd.h>
|
|
#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;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
T_FSPath T_FSPath::parent( ) const noexcept
|
|
{
|
|
T_FSPath p{ *this };
|
|
if ( p.components_.size( ) ) {
|
|
p.components_.removeLast( );
|
|
}
|
|
return p;
|
|
}
|
|
|
|
T_FSPath T_FSPath::child(
|
|
T_String const& name ) const noexcept
|
|
{
|
|
T_FSPath c{ *this };
|
|
c.components_.add( name );
|
|
c.valid_ = c.valid_ && IsValidComponent( name );
|
|
return c;
|
|
}
|
|
|
|
T_FSPath T_FSPath::operator +(
|
|
T_FSPath const& other ) const noexcept
|
|
{
|
|
T_FSPath p{ *this };
|
|
if ( other.root_ ) {
|
|
p.valid_ = false;
|
|
} else {
|
|
p.valid_ = p.valid_ && other.valid_;
|
|
}
|
|
p.components_.addAll( other.components_ );
|
|
return p;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
bool T_FSPath::inDirectoryOf(
|
|
T_FSPath const& other ) const noexcept
|
|
{
|
|
if ( &other == this ) {
|
|
return false;
|
|
}
|
|
if ( M_CMPSTR_( root_ , other.root_ ) != 0 ) {
|
|
return false;
|
|
}
|
|
|
|
const auto nc{ components_.size( ) };
|
|
if ( nc != other.components_.size( ) || nc == 0 ) {
|
|
return false;
|
|
}
|
|
|
|
for ( auto i = 0u ; i < nc - 1 ; i ++ ) {
|
|
if ( M_CMPSTR_( components_[ i ] , other.components_[ i ] ) != 0 ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return M_CMPSTR_( components_[ nc - 1 ] , other.components_[ nc - 1 ] ) != 0;
|
|
}
|
|
|
|
bool T_FSPath::isParentOf(
|
|
T_FSPath const& other ) const noexcept
|
|
{
|
|
if ( &other == this ) {
|
|
return false;
|
|
}
|
|
if ( M_CMPSTR_( root_ , other.root_ ) != 0 ) {
|
|
return false;
|
|
}
|
|
|
|
const auto nc{ components_.size( ) };
|
|
if ( nc + 1 != other.components_.size( ) ) {
|
|
return false;
|
|
}
|
|
|
|
for ( auto i = 0u ; i < nc ; i ++ ) {
|
|
if ( M_CMPSTR_( components_[ i ] , other.components_[ i ] ) != 0 ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool T_FSPath::isAbove(
|
|
T_FSPath const& other ) const noexcept
|
|
{
|
|
if ( &other == this ) {
|
|
return false;
|
|
}
|
|
if ( M_CMPSTR_( root_ , other.root_ ) != 0 ) {
|
|
return false;
|
|
}
|
|
|
|
const auto nc{ components_.size( ) };
|
|
if ( nc >= other.components_.size( ) ) {
|
|
return false;
|
|
}
|
|
|
|
for ( auto i = 0u ; i < nc ; i ++ ) {
|
|
if ( M_CMPSTR_( components_[ i ] , other.components_[ i ] ) != 0 ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
T_FSPath T_FSPath::makeRelative(
|
|
T_FSPath const& relTo ) const noexcept
|
|
{
|
|
assert( isValid( ) && relTo.isValid( ) );
|
|
assert( isCanonical( ) && relTo.isCanonical( ) );
|
|
if ( relTo.root_ != root_ ) {
|
|
return *this;
|
|
}
|
|
|
|
const auto nca{ components_.size( ) } ,
|
|
ncb{ relTo.components_.size( ) } ,
|
|
nc{ std::min( nca , ncb ) };
|
|
uint32_t nCommon{ 0u };
|
|
for ( auto i = 0u ; i < nc ; i ++ ) {
|
|
if ( M_CMPSTR_( components_[ i ] , relTo.components_[ i ] ) != 0 ) {
|
|
break;
|
|
}
|
|
nCommon ++;
|
|
}
|
|
|
|
T_FSPath np;
|
|
const T_String parent{ T_String::Pooled( ".." ) };
|
|
for ( auto i = ncb ; i > nCommon ; i -- ) {
|
|
np.components_.add( parent );
|
|
}
|
|
for ( auto i = nCommon ; i < nca ; i ++ ) {
|
|
np.components_.add( components_[ i ] );
|
|
}
|
|
return np;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
bool T_FSPath::isCanonical( ) const noexcept
|
|
{
|
|
if ( !( valid_ && root_ ) ) {
|
|
return false;
|
|
}
|
|
|
|
auto const nc{ components_.size( ) };
|
|
for ( auto i = 0u ; i < nc ; i ++ ) {
|
|
if ( components_[ i ] == "." || components_[ i ] == ".." ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
T_FSPath T_FSPath::canonical( ) const noexcept
|
|
{
|
|
auto const nc{ components_.size( ) };
|
|
if ( !( valid_ && nc && root_ ) ) {
|
|
return *this;
|
|
}
|
|
|
|
T_FSPath np;
|
|
np.root_ = root_;
|
|
for ( auto i = 0u ; i < nc ; i ++ ) {
|
|
auto const& cmp{ components_[ i ] };
|
|
if ( cmp == ".." ) {
|
|
if ( np.components_.size( ) ) {
|
|
np.components_.removeLast( );
|
|
}
|
|
} else if ( cmp != "." ) {
|
|
np.components_.add( cmp );
|
|
}
|
|
}
|
|
return np;
|
|
}
|
|
|
|
|
|
/*= Filesystem ===============================================================*/
|
|
|
|
T_FSPath Filesystem::Cwd( ) noexcept
|
|
{
|
|
// TODO windows version
|
|
|
|
T_Buffer< char > buffer{ 256 };
|
|
while ( getcwd( &buffer[ 0 ] , buffer.bytes( ) ) == nullptr ) {
|
|
assert( errno == ERANGE );
|
|
buffer.resize( buffer.size( ) + 256 );
|
|
}
|
|
|
|
T_FSPath path{ &buffer[ 0 ] };
|
|
assert( path.isValid( ) );
|
|
return path;
|
|
}
|