1 /* Copyright 2016-present Facebook, Inc.
2  * Licensed under the Apache License, Version 2.0 */
3 
4 #include "watchman.h"
5 
6 // The path and everything below it is ignored.
7 #define FULL_IGNORE 0x1
8 // The grand-children of the path are ignored, but not the path
9 // or its direct children.
10 #define VCS_IGNORE 0x2
11 
12 void watchman_ignore::add(const w_string& path, bool is_vcs_ignore) {
13   (is_vcs_ignore ? ignore_vcs : ignore_dirs).insert(path);
14 
15   tree.insert(path, is_vcs_ignore ? VCS_IGNORE : FULL_IGNORE);
16 
17   if (!is_vcs_ignore) {
18     dirs_vec.push_back(path);
19   }
20 }
21 
22 bool watchman_ignore::isIgnored(const char* path, uint32_t pathlen) const {
23   const char *skip_prefix;
24   uint32_t len;
25   auto leaf = tree.longestMatch((const unsigned char*)path, (int)pathlen);
26 
27   if (!leaf) {
28     // No entry -> not ignored.
29     return false;
30   }
31 
32   if (pathlen < leaf->key.size()) {
33     // We wanted "buil" but matched "build"
34     return false;
35   }
36 
37   if (pathlen == leaf->key.size()) {
38     // Exact match.  This is an ignore if we are in FULL_IGNORE,
39     // but not in VCS_IGNORE mode.
40     return leaf->value == FULL_IGNORE ? true : false;
41   }
42 
43   // Our input string was longer than the leaf key string.
44   // We need to ensure that we observe a directory separator at the
45   // character after the common prefix, otherwise we may be falsely
46   // matching a sibling entry.
47   skip_prefix = path + leaf->key.size();
48   len = pathlen - leaf->key.size();
49 
50   if (!is_slash(*skip_prefix)) {
51     // we wanted "foo/bar" but we matched something like "food"
52     // this is not an ignore situation.
53     return false;
54   }
55 
56   if (leaf->value == FULL_IGNORE) {
57     // Definitely ignoring this portion of the tree
58     return true;
59   }
60 
61   // we need to apply vcs_ignore style logic to determine if we are ignoring
62   // this path.  This devolves to: "is there a '/' character after the end of
63   // the leaf key prefix?"
64 
65   if (pathlen <= leaf->key.size()) {
running_on_valgrind(void)66     // There can't be a slash after this portion of the tree, therefore
67     // this is not ignored.
68     return false;
69   }
70 
71   // Skip over the '/'
72   skip_prefix++;
73   len--;
74 
75 #ifndef _WIN32
76   // If we find a '/' from this point, we are ignoring this path.
77   return memchr(skip_prefix, '/', len) != nullptr;
78 #else
79   // On windows, both '/' and '\' are possible.
w_hash_bytes(const void * key,size_t length,uint32_t initval)80   while (len > 0) {
81     if (is_slash(*skip_prefix)) {
82       return true;
83     }
84     skip_prefix++;
85     len--;
86   }
87   return false;
88 #endif
89 }
90 
91 bool watchman_ignore::isIgnoreVCS(const w_string& path) const {
92   return ignore_vcs.find(path) != ignore_vcs.end();
93 }
94 
95 bool watchman_ignore::isIgnoreDir(const w_string& path) const {
96   return ignore_dirs.find(path) != ignore_dirs.end();
97 }
98 
99 /* vim:ts=2:sw=2:et:
100  */
101