Excludes: Introduce dir-only regex matches

This commit is contained in:
Christian Kamm 2017-11-28 09:41:52 +01:00 committed by ckamm
parent 5d668eca40
commit 1c3d5ab158
3 changed files with 75 additions and 21 deletions

View file

@ -463,7 +463,7 @@ void ExcludedFiles::clearManualExcludes()
bool ExcludedFiles::reloadExcludeFiles()
{
_allExcludes.clear();
_regex = QRegularExpression();
_bnameRegexFileDir = QRegularExpression();
bool success = true;
foreach (const QString &file, _excludeFiles) {
@ -538,7 +538,12 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, int fi
bname = path;
}
QString p = QString::fromUtf8(bname);
auto m = _regex.match(p);
QRegularExpressionMatch m;
if (filetype == CSYNC_FTW_TYPE_DIR) {
m = _bnameRegexDir.match(p);
} else {
m = _bnameRegexFileDir.match(p);
}
if (m.hasMatch()) {
if (!m.captured(1).isEmpty()) {
match = CSYNC_FILE_EXCLUDE_LIST;
@ -552,6 +557,7 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, int fi
CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, int filetype) const
{
// bname check must also apply to all path components
return _csync_excluded_common(_allExcludes, path, filetype, true);
}
@ -573,39 +579,59 @@ void ExcludedFiles::prepare()
_nonRegexExcludes.clear();
// Start out with regexes that would match nothing
QString exclude_only = "a^";
QString exclude_and_remove = "a^";
QString patternFileDirKeep = "a^";
QString patternFileDirRemove = "a^";
QString patternDirKeep = "a^";
QString patternDirRemove = "a^";
auto regexAppend = [](QString &pattern, QString v) {
if (!pattern.isEmpty())
pattern.append("|");
pattern.append(v);
};
for (auto exclude : _allExcludes) {
QString *builderToUse = &exclude_only;
if (exclude[0] == '\n')
continue; // empty line
if (exclude[0] == '\r')
continue; // empty line
bool matchDirOnly = exclude.endsWith('/');
if (matchDirOnly)
exclude = exclude.left(exclude.size() - 1);
bool removeExcluded = (exclude[0] == ']');
if (removeExcluded)
exclude = exclude.mid(1);
/* If an exclude entry contains some fnmatch-ish characters, we use the C-style codepath without QRegularEpression */
if (strchr(exclude, '/') || strchr(exclude, '[') || strchr(exclude, '{') || strchr(exclude, '\\')) {
_nonRegexExcludes.append(exclude);
continue;
}
/* Those will attempt to use QRegularExpression */
if (exclude[0] == ']') {
exclude = exclude.mid(1);
builderToUse = &exclude_and_remove;
}
if (builderToUse->size() > 0) {
builderToUse->append("|");
}
builderToUse->append(convertToBnameRegexpSyntax(QString::fromUtf8(exclude)));
/* Use QRegularExpression, append to the right pattern */
auto &patternFileDir = removeExcluded ? patternFileDirRemove : patternFileDirKeep;
auto &patternDir = removeExcluded ? patternDirRemove : patternDirKeep;
auto regexExclude = convertToBnameRegexpSyntax(QString::fromUtf8(exclude));
// patterns always match against directories
regexAppend(patternDir, regexExclude);
// but some don't match against files
if (!matchDirOnly)
regexAppend(patternFileDir, regexExclude);
}
QString pattern = "^(" + exclude_only + ")$|^(" + exclude_and_remove + ")$";
_regex.setPattern(pattern);
_bnameRegexFileDir.setPattern(
"^(" + patternFileDirKeep + ")$|^(" + patternFileDirRemove + ")$");
_bnameRegexDir.setPattern(
"^(" + patternDirKeep + ")$|^(" + patternDirRemove + ")$");
QRegularExpression::PatternOptions patternOptions = QRegularExpression::OptimizeOnFirstUsageOption;
if (OCC::Utility::fsCasePreserving())
patternOptions |= QRegularExpression::CaseInsensitiveOption;
_regex.setPatternOptions(patternOptions);
_regex.optimize();
_bnameRegexFileDir.setPatternOptions(patternOptions);
_bnameRegexFileDir.optimize();
_bnameRegexDir.setPatternOptions(patternOptions);
_bnameRegexDir.optimize();
}

View file

@ -165,7 +165,8 @@ private:
QList<QByteArray> _nonRegexExcludes;
/// see prepare()
QRegularExpression _regex;
QRegularExpression _bnameRegexFileDir;
QRegularExpression _bnameRegexDir;
};
#endif /* _CSYNC_EXCLUDE_H */

View file

@ -113,12 +113,12 @@ static void check_csync_exclude_add(void **)
excludedFiles->prepare();
assert_true(excludedFiles->_nonRegexExcludes.contains("/tmp/check_csync1/*"));
assert_false(excludedFiles->_regex.pattern().contains("csync1"));
assert_false(excludedFiles->_bnameRegexFileDir.pattern().contains("csync1"));
excludedFiles->addManualExclude("foo");
excludedFiles->prepare();
assert_true(excludedFiles->_nonRegexExcludes.size() == 1);
assert_true(excludedFiles->_regex.pattern().contains("foo"));
assert_true(excludedFiles->_bnameRegexFileDir.pattern().contains("foo"));
}
static void check_csync_excluded(void **)
@ -356,6 +356,32 @@ static void check_csync_excluded_traversal(void **)
assert_int_equal(check_file_traversal("a * ?"), CSYNC_FILE_EXCLUDE_LIST);
}
static void check_csync_dir_only(void **)
{
excludedFiles->addManualExclude("filedir");
excludedFiles->addManualExclude("dir/");
excludedFiles->prepare();
assert_int_equal(check_file_traversal("other"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_file_traversal("filedir"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_traversal("dir"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_file_traversal("s/other"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_file_traversal("s/filedir"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_traversal("s/dir"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_dir_traversal("other"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_dir_traversal("filedir"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_dir_traversal("dir"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_dir_traversal("s/other"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_dir_traversal("s/filedir"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_dir_traversal("s/dir"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_dir_full("filedir/foo"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_full("filedir/foo"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_dir_full("dir/foo"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_full("dir/foo"), CSYNC_FILE_EXCLUDE_LIST);
}
static void check_csync_pathes(void **)
{
excludedFiles->addManualExclude("/exclude");
@ -495,6 +521,7 @@ int torture_run_tests(void)
cmocka_unit_test_setup_teardown(check_csync_exclude_add, setup, teardown),
cmocka_unit_test_setup_teardown(check_csync_excluded, setup_init, teardown),
cmocka_unit_test_setup_teardown(check_csync_excluded_traversal, setup_init, teardown),
cmocka_unit_test_setup_teardown(check_csync_dir_only, setup_init, teardown),
cmocka_unit_test_setup_teardown(check_csync_pathes, setup_init, teardown),
cmocka_unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown),
cmocka_unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown),