@@ -24,6 +24,7 @@ class ClientSideFiltering {
2424 string [] syncListAnywherePathOnly; // These are 'include' rules that do not start with a '/', thus are to be searched anywhere for inclusion
2525 Regex! char fileMask;
2626 Regex! char directoryMask;
27+ bool hasDirectoryMaskEntries = false ;
2728 bool skipDirStrictMatch = false ;
2829 bool skipDotfiles = false ;
2930
@@ -220,6 +221,14 @@ class ClientSideFiltering {
220221
221222 // Configure the regex that will be used for 'skip_dir'
222223 void setDirMask (const (char )[] dirmask) {
224+ hasDirectoryMaskEntries = false ;
225+ foreach (entry; to! string (dirmask).split(" |" )) {
226+ if (! strip(entry).empty) {
227+ hasDirectoryMaskEntries = true ;
228+ break ;
229+ }
230+ }
231+
223232 directoryMask = wild2regex(dirmask);
224233 if (debugLogging) {addLogEntry(" Selective Sync Directory Mask: " ~ to! string (directoryMask), [" debug" ]);}
225234 }
@@ -258,6 +267,10 @@ class ClientSideFiltering {
258267 // Returns true if the inputPath matches a skip_dir config entry (directoryMask)
259268 // Returns false if no match
260269
270+ if (! hasDirectoryMaskEntries) {
271+ return false ;
272+ }
273+
261274 if (debugLogging) {
262275 addLogEntry(" skip_dir evaluation for: " ~ inputPath, [" debug" ]);
263276 }
@@ -297,10 +310,18 @@ class ClientSideFiltering {
297310 // Also test trailing-slash equivalence for directory roots
298311 // (treat "Documents" and "Documents/" the same, but do not create "//")
299312 string [] expanded;
300- foreach (c; candidates) {
313+ void addExpandedCandidate (string c) {
314+ if (c.empty) return ;
315+ foreach (e; expanded) {
316+ if (e == c) return ;
317+ }
301318 expanded ~= c;
319+ }
320+
321+ foreach (c; candidates) {
322+ addExpandedCandidate(c);
302323 if (c.length > 1 && c[$ - 1 ] != ' /' ) {
303- expanded ~= (c ~ " /" );
324+ addExpandedCandidate (c ~ " /" );
304325 }
305326 }
306327 candidates = expanded;
@@ -321,11 +342,22 @@ class ClientSideFiltering {
321342 if (! skipDirStrictMatch) {
322343 if (debugLogging) addLogEntry(" No Strict Matching Enforced - testing individual path segments" , [" debug" ]);
323344
345+ string [] segmentCandidates;
346+ void addSegmentCandidate (string c) {
347+ if (c.empty) return ;
348+ foreach (e; segmentCandidates) {
349+ if (e == c) return ;
350+ }
351+ segmentCandidates ~= c;
352+ }
353+
324354 foreach (c; candidates) {
325355 // buildNormalizedPath may introduce a leading '/', so we keep it as-is
326356 // and let pathSplitter do its job. We are matching segments, not full paths here.
327- string path = buildNormalizedPath(c);
357+ addSegmentCandidate(buildNormalizedPath(c));
358+ }
328359
360+ foreach (path; segmentCandidates) {
329361 if (debugLogging) addLogEntry(" skip_dir segment test path: " ~ path, [" debug" ]);
330362
331363 foreach_reverse (seg; pathSplitter (path)) {
0 commit comments