Fix #218 - Impossible to ignore sub-subdirectory via griveignore

Now an example .griveignore will look like this:

**
!dir/subdir/subdir
pull/221/head
Vitaliy Filippov 2018-07-31 01:11:09 +03:00
parent 93cae255fa
commit cbac85a8e5
1 changed files with 62 additions and 35 deletions

View File

@ -28,6 +28,8 @@
#include "util/log/Log.hh" #include "util/log/Log.hh"
#include "json/JsonParser.hh" #include "json/JsonParser.hh"
#include <boost/algorithm/string.hpp>
#include <fstream> #include <fstream>
namespace gr { namespace gr {
@ -76,7 +78,7 @@ State::State( const fs::path& root, const Val& options ) :
} }
m_ign_changed = m_orig_ign != "" && m_orig_ign != m_ign; m_ign_changed = m_orig_ign != "" && m_orig_ign != m_ign;
m_ign_re = boost::regex( m_ign.empty() ? "^\\.(grive$|grive_state$|trash)" : ( m_ign+"|^\\.(grive|grive_state|trash)" ) ); m_ign_re = boost::regex( m_ign.empty() ? "^\\.(grive$|grive_state$|trash)" : ( m_ign+"|^\\.(grive$|grive_state$|trash)" ) );
} }
State::~State() State::~State()
@ -93,7 +95,7 @@ void State::FromLocal( const fs::path& p )
bool State::IsIgnore( const std::string& filename ) bool State::IsIgnore( const std::string& filename )
{ {
return regex_search( filename.c_str(), m_ign_re ); return regex_search( filename.c_str(), m_ign_re, boost::format_perl );
} }
void State::FromLocal( const fs::path& p, Resource* folder, Val& tree ) void State::FromLocal( const fs::path& p, Resource* folder, Val& tree )
@ -106,7 +108,7 @@ void State::FromLocal( const fs::path& p, Resource* folder, Val& tree )
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i ) for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
{ {
std::string fname = i->path().filename().string() ; std::string fname = i->path().filename().string() ;
std::string path = folder->IsRoot() ? fname : ( folder->RelPath() / fname ).string(); std::string path = ( folder->IsRoot() ? fname : ( folder->RelPath() / fname ).string() );
if ( IsIgnore( path ) ) if ( IsIgnore( path ) )
Log( "file %1% is ignored by grive", path, log::verbose ) ; Log( "file %1% is ignored by grive", path, log::verbose ) ;
@ -317,53 +319,78 @@ void State::Read()
} }
} }
std::vector<std::string> split( const boost::regex& re, const char* str, int len )
{
std::vector<std::string> vec;
boost::cregex_token_iterator i( str, str+len, re, -1, boost::format_perl );
boost::cregex_token_iterator j;
while ( i != j )
{
vec.push_back( *i++ );
}
return vec;
}
bool State::ParseIgnoreFile( const char* buffer, int size ) bool State::ParseIgnoreFile( const char* buffer, int size )
{ {
const boost::regex re1( "/\\\\\\*\\\\\\*$" ); const boost::regex re1( "([^\\\\]|^)[\\t\\r ]+$" );
const boost::regex re2( "^\\\\\\*\\\\\\*/" ); const boost::regex re2( "^[\\t\\r ]+" );
const boost::regex re3( "([^\\\\](\\\\\\\\)*)/\\\\\\*\\\\\\*/" );
const boost::regex re4( "([^\\\\](\\\\\\\\)*|^)\\\\\\*" );
const boost::regex re5( "([^\\\\](\\\\\\\\)*|^)\\\\\\?" ); const boost::regex re5( "([^\\\\](\\\\\\\\)*|^)\\\\\\?" );
std::string exclude_re, include_re; std::string exclude_re, include_re;
int prev = 0; std::vector<std::string> lines = split( boost::regex( "[\\n\\r]+" ), buffer, size );
for ( int i = 0; i <= size; i++ ) for ( int i = 0; i < (int)lines.size(); i++ )
{ {
if ( buffer[i] == '\n' || ( i == size && i > prev ) ) std::string str = regex_replace( regex_replace( lines[i], re1, "$1" ), re2, "" );
if ( str[0] == '#' || !str.size() )
{ {
while ( prev < i && ( buffer[prev] == ' ' || buffer[prev] == '\t' || buffer[prev] == '\r' ) ) continue;
prev++; }
if ( buffer[prev] != '#' ) bool inc = str[0] == '!';
if ( inc )
{
str = str.substr( 1 );
}
std::vector<std::string> parts = split( boost::regex( "/+" ), str.c_str(), str.size() );
for ( int j = 0; j < (int)parts.size(); j++ )
{
if ( parts[j] == "**" )
{ {
int j; parts[j] = ".*";
for ( j = i-1; j > prev; j-- ) }
if ( buffer[j-1] == '\\' || ( buffer[j] != ' ' && buffer[j] != '\t' && buffer[j] != '\r' ) ) else if ( parts[j] == "*" )
break; {
std::string str( buffer+prev, j+1-prev ); parts[j] = "[^/]*";
bool inc = str[0] == '!'; }
if ( inc ) else
str = str.substr( 1 ); {
str = regex_escape( str ); parts[j] = regex_escape( parts[j] );
str = regex_replace( str, re1, "/.*", boost::format_perl );
str = regex_replace( str, re2, ".*/", boost::format_perl );
str = regex_replace( str, re3, "$1/(.*/)*", boost::format_perl );
str = regex_replace( str, re4, "$1[^/]*", boost::format_perl );
std::string str1; std::string str1;
while (1) while (1)
{ {
str1 = regex_replace( str, re5, "$1[^/]", boost::format_perl ); str1 = regex_replace( parts[j], re5, "$1[^/]", boost::format_perl );
if ( str1.size() == str.size() ) if ( str1.size() == parts[j].size() )
break; break;
str = str1; parts[j] = str1;
} }
if ( !inc )
exclude_re = exclude_re + ( exclude_re.size() > 0 ? "|" : "" ) + str;
else
include_re = include_re + ( include_re.size() > 0 ? "|" : "" ) + str;
} }
prev = i+1; }
if ( !inc )
{
str = boost::algorithm::join( parts, "/" ) + "(/|$)";
exclude_re = exclude_re + ( exclude_re.size() > 0 ? "|" : "" ) + str;
}
else
{
str = "";
std::string cur;
for ( int j = 0; j < (int)parts.size(); j++ )
{
cur = cur.size() > 0 ? cur + "/" + parts[j] : "^" + parts[j];
str = ( str.size() > 0 ? str + "|" + cur : cur ) + ( j < (int)parts.size()-1 ? "$" : "(/|$)" );
}
include_re = include_re + ( include_re.size() > 0 ? "|" : "" ) + str;
} }
} }
if ( exclude_re.size() > 0 ) if ( exclude_re.size() > 0 )
{ {
m_ign = "^" + ( include_re.size() > 0 ? "(?!" + include_re + ")" : std::string() ) + "(" + exclude_re + ")$"; m_ign = "^" + ( include_re.size() > 0 ? "(?!" + include_re + ")" : std::string() ) + "(" + exclude_re + ")$";