static bool subscription_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, void *gendata) { struct watchman_file *f; struct watchman_client_subscription *sub = gendata; w_log(W_LOG_DBG, "running subscription %s %p\n", sub->name->buf, sub); // Walk back in time until we hit the boundary for (f = root->latest_file; f; f = f->next) { if (ctx->since.is_timestamp && w_timeval_compare(f->otime.tv, ctx->since.timestamp) < 0) { break; } if (!ctx->since.is_timestamp && f->otime.ticks <= ctx->since.clock.ticks) { break; } if (!w_query_process_file(query, ctx, f)) { return false; } } return true; }
static bool time_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx) { struct watchman_file *f; // Walk back in time until we hit the boundary for (f = root->latest_file; f; f = f->next) { if (ctx->since.is_timestamp && w_timeval_compare(f->otime.tv, ctx->since.timestamp) < 0) { break; } if (!ctx->since.is_timestamp && f->otime.ticks < ctx->since.clock.ticks) { break; } if (!w_query_process_file(query, ctx, f)) { return false; } } return true; }
static bool trigger_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, void *gendata) { struct watchman_file *f; struct watchman_trigger_command *cmd = gendata; w_log(W_LOG_DBG, "assessing trigger %s %p\n", cmd->triggername->buf, cmd); // Walk back in time until we hit the boundary for (f = root->latest_file; f; f = f->next) { if (ctx->since.is_timestamp && w_timeval_compare(f->otime.tv, ctx->since.timestamp) < 0) { break; } if (!ctx->since.is_timestamp && f->otime.ticks <= ctx->since.clock.ticks) { break; } if (!w_query_file_matches_relative_root(ctx, f)) { continue; } if (!w_query_process_file(query, ctx, f)) { return false; } } return true; }
static bool dir_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, struct watchman_dir *dir, uint32_t depth) { w_ht_iter_t i; if (w_ht_first(dir->files, &i)) do { struct watchman_file *file = w_ht_val_ptr(i.value); if (!w_query_process_file(query, ctx, file)) { return false; } } while (w_ht_next(dir->files, &i)); if (depth > 0 && w_ht_first(dir->dirs, &i)) do { struct watchman_dir *child = w_ht_val_ptr(i.value); if (!dir_generator(query, root, ctx, child, depth - 1)) { return false; } } while (w_ht_next(dir->dirs, &i)); return true; }
static bool all_files_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, int64_t *num_walked) { struct watchman_file *f; int64_t n = 0; bool result = true; for (f = root->latest_file; f; f = f->next) { ++n; if (!w_query_file_matches_relative_root(ctx, f)) { continue; } if (!w_query_process_file(query, ctx, f)) { result = false; goto done; } } done: *num_walked = n; return result; }
static bool suffix_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, int64_t *num_walked) { uint32_t i; struct watchman_file *f; int64_t n = 0; bool result = true; for (i = 0; i < query->nsuffixes; i++) { // Head of suffix index for this suffix f = w_ht_val_ptr(w_ht_get(root->suffixes, w_ht_ptr_val(query->suffixes[i]))); // Walk and process for (; f; f = f->suffix_next) { ++n; if (!w_query_file_matches_relative_root(ctx, f)) { continue; } if (!w_query_process_file(query, ctx, f)) { result = false; goto done; } } } done: *num_walked = n; return result; }
static bool time_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, int64_t *num_walked) { struct watchman_file *f; int64_t n = 0; bool result = true; // Walk back in time until we hit the boundary for (f = root->latest_file; f; f = f->next) { ++n; if (ctx->since.is_timestamp && f->otime.timestamp < ctx->since.timestamp) { break; } if (!ctx->since.is_timestamp && f->otime.ticks <= ctx->since.clock.ticks) { break; } if (!w_query_file_matches_relative_root(ctx, f)) { continue; } if (!w_query_process_file(query, ctx, f)) { result = false; goto done; } } done: *num_walked = n; return result; }
static bool all_files_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx) { struct watchman_file *f; for (f = root->latest_file; f; f = f->next) { if (!w_query_process_file(query, ctx, f)) { return false; } } return true; }
static bool subscription_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, void *gendata, int64_t *num_walked) { struct watchman_file *f; struct watchman_client_subscription *sub = gendata; int64_t n = 0; bool result = true; w_log(W_LOG_DBG, "running subscription %s %p\n", sub->name->buf, sub); // Walk back in time until we hit the boundary for (f = root->latest_file; f; f = f->next) { ++n; if (ctx->since.is_timestamp && f->otime.timestamp < ctx->since.timestamp) { break; } if (!ctx->since.is_timestamp && f->otime.ticks <= ctx->since.clock.ticks) { break; } if (!w_query_file_matches_relative_root(ctx, f)) { continue; } if (!w_query_process_file(query, ctx, f)) { result = false; goto done; } } done: *num_walked = n; return result; }
static bool dir_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx, struct watchman_dir *dir, uint32_t depth, int64_t *num_walked) { w_ht_iter_t i; int64_t n = 0; bool result = true; if (w_ht_first(dir->files, &i)) do { struct watchman_file *file = w_ht_val_ptr(i.value); ++n; if (!w_query_process_file(query, ctx, file)) { result = false; goto done; } } while (w_ht_next(dir->files, &i)); if (depth > 0 && w_ht_first(dir->dirs, &i)) do { struct watchman_dir *child = w_ht_val_ptr(i.value); int64_t child_walked = 0; result = dir_generator(query, root, ctx, child, depth - 1, &child_walked); n += child_walked; if (!result) { goto done; } } while (w_ht_next(dir->dirs, &i)); done: *num_walked = n; return result; }
static bool suffix_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx) { uint32_t i; struct watchman_file *f; for (i = 0; i < query->nsuffixes; i++) { // Head of suffix index for this suffix f = w_ht_val_ptr(w_ht_get(root->suffixes, w_ht_ptr_val(query->suffixes[i]))); // Walk and process while (f) { if (!w_query_process_file(query, ctx, f)) { return false; } f = f->suffix_next; } } return true; }
static bool path_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx) { struct watchman_file *f; uint32_t i; for (i = 0; i < query->npaths; i++) { struct watchman_dir *dir; w_string_t *dir_name, *file_name, *full_name; // Compose path with root full_name = w_string_path_cat(root->root_path, query->paths[i].name); // special case of root dir itself if (w_string_equal(root->root_path, full_name)) { // dirname on the root is outside the root, which is useless dir = w_root_resolve_dir(root, full_name, false); goto is_dir; } // Ideally, we'd just resolve it directly as a dir and be done. // It's not quite so simple though, because we may resolve a dir // that had been deleted and replaced by a file. // We prefer to resolve the parent and walk down. dir_name = w_string_dirname(full_name); if (!dir_name) { w_string_delref(full_name); continue; } dir = w_root_resolve_dir(root, dir_name, false); w_string_delref(dir_name); if (!dir) { // Doesn't exist, and never has w_string_delref(full_name); continue; } if (dir->files) { file_name = w_string_basename(query->paths[i].name); f = w_ht_val_ptr(w_ht_get(dir->files, w_ht_ptr_val(file_name))); w_string_delref(file_name); // If it's a file (but not an existent dir) if (f && (!f->exists || !S_ISDIR(f->st.st_mode))) { w_string_delref(full_name); if (!w_query_process_file(query, ctx, f)) { return false; } continue; } } // Is it a dir? dir = w_ht_val_ptr(w_ht_get(dir->dirs, w_ht_ptr_val(full_name))); w_string_delref(full_name); is_dir: // We got a dir; process recursively to specified depth if (dir && !dir_generator(query, root, ctx, dir, query->paths[i].depth)) { return false; } } return true; }
/* Match each child of node against the children of dir */ void InMemoryView::globGeneratorTree( struct w_query_ctx* ctx, const struct watchman_glob_tree* node, const struct watchman_dir* dir) const { w_string_t component; if (!node->doublestar_children.empty()) { globGeneratorDoublestar(ctx, dir, node, nullptr, 0); } for (const auto& child_node : node->children) { w_assert(!child_node->is_doublestar, "should not get here with ** glob"); // If there are child dirs, consider them for recursion. // Note that we don't restrict this to !leaf because the user may have // set their globs list to something like ["some_dir", "some_dir/file"] // and we don't want to preclude matching the latter. if (!dir->dirs.empty()) { // Attempt direct lookup if possible if (!child_node->had_specials && ctx->query->case_sensitive) { w_string_new_len_typed_stack( &component, child_node->pattern.data(), child_node->pattern.size(), W_STRING_BYTE); const auto child_dir = dir->getChildDir(&component); if (child_dir) { globGeneratorTree(ctx, child_node.get(), child_dir); } } else { // Otherwise we have to walk and match for (auto& it : dir->dirs) { const auto child_dir = it.second.get(); if (!child_dir->last_check_existed) { // Globs can only match files in dirs that exist continue; } if (wildmatch( child_node->pattern.c_str(), child_dir->name.c_str(), ctx->query->glob_flags | (ctx->query->case_sensitive ? 0 : WM_CASEFOLD), 0) == WM_MATCH) { globGeneratorTree(ctx, child_node.get(), child_dir); } } } } // If the node is a leaf we are in a position to match files. if (child_node->is_leaf && !dir->files.empty()) { // Attempt direct lookup if possible if (!child_node->had_specials && ctx->query->case_sensitive) { w_string_new_len_typed_stack( &component, child_node->pattern.data(), child_node->pattern.size(), W_STRING_BYTE); auto file = dir->getChildFile(&component); if (file) { ctx->bumpNumWalked(); if (file->exists) { // Globs can only match files that exist w_query_process_file( ctx->query, ctx, make_unique<InMemoryFileResult>(file, contentHashCache_)); } } } else { for (auto& it : dir->files) { // Otherwise we have to walk and match auto file = it.second.get(); auto file_name = file->getName(); ctx->bumpNumWalked(); if (!file->exists) { // Globs can only match files that exist continue; } if (wildmatch( child_node->pattern.c_str(), file_name.data(), ctx->query->glob_flags | (ctx->query->case_sensitive ? WM_CASEFOLD : 0), 0) == WM_MATCH) { w_query_process_file( ctx->query, ctx, make_unique<InMemoryFileResult>(file, contentHashCache_)); } } } } } }
/* effectively runs the same query multiple times. By combining the * doublestar walk for both into a single walk, we can then match each * file against the list of patterns, terminating that match as soon * as any one of them matches the file node. */ void InMemoryView::globGeneratorDoublestar( struct w_query_ctx* ctx, const struct watchman_dir* dir, const struct watchman_glob_tree* node, const char* dir_name, uint32_t dir_name_len) const { bool matched; // First step is to walk the set of files contained in this node for (auto& it : dir->files) { auto file = it.second.get(); auto file_name = file->getName(); ctx->bumpNumWalked(); if (!file->exists) { // Globs can only match files that exist continue; } auto subject = make_path_name( dir_name, dir_name_len, file_name.data(), file_name.size()); // Now that we have computed the name of this candidate file node, // attempt to match against each of the possible doublestar patterns // in turn. As soon as any one of them matches we can stop this loop // as it doesn't make a lot of sense to yield multiple results for // the same file. for (const auto& child_node : node->doublestar_children) { matched = wildmatch( child_node->pattern.c_str(), subject.c_str(), ctx->query->glob_flags | WM_PATHNAME | (ctx->query->case_sensitive ? 0 : WM_CASEFOLD), 0) == WM_MATCH; if (matched) { w_query_process_file( ctx->query, ctx, make_unique<InMemoryFileResult>(file, contentHashCache_)); // No sense running multiple matches for this same file node // if this one succeeded. break; } } } // And now walk down to any dirs; all dirs are eligible for (auto& it : dir->dirs) { const auto child = it.second.get(); if (!child->last_check_existed) { // Globs can only match files in dirs that exist continue; } auto subject = make_path_name( dir_name, dir_name_len, child->name.data(), child->name.size()); globGeneratorDoublestar(ctx, child, node, subject.data(), subject.size()); } }