mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 21:15:55 +03:00
Excludes: Introduce dir-only regex matches
This commit is contained in:
parent
5d668eca40
commit
1c3d5ab158
3 changed files with 75 additions and 21 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -165,7 +165,8 @@ private:
|
|||
QList<QByteArray> _nonRegexExcludes;
|
||||
|
||||
/// see prepare()
|
||||
QRegularExpression _regex;
|
||||
QRegularExpression _bnameRegexFileDir;
|
||||
QRegularExpression _bnameRegexDir;
|
||||
};
|
||||
|
||||
#endif /* _CSYNC_EXCLUDE_H */
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue