// Return true if the json ref is an array of string values static bool is_array_of_strings(const json_ref& ref) { uint32_t i; if (!ref.isArray()) { return false; } for (i = 0; i < json_array_size(ref); i++) { if (!json_array_get(ref, i).isString()) { return false; } } return true; }
// Translate from the legacy array into the new style, then // delegate to the main parser. // We build a big anyof expression std::shared_ptr<w_query> w_query_parse_legacy( const std::shared_ptr<w_root_t>& root, const json_ref& args, int start, uint32_t* next_arg, const char* clockspec, json_ref* expr_p) { bool include = true; bool negated = false; uint32_t i; const char *term_name = "match"; json_ref included, excluded; auto query_obj = json_object(); if (!args.isArray()) { throw QueryParseError("Expected an array"); } for (i = start; i < json_array_size(args); i++) { const char *arg = json_string_value(json_array_get(args, i)); if (!arg) { /* not a string value! */ throw QueryParseError(watchman::to<std::string>( "rule @ position ", i, " is not a string value")); } } for (i = start; i < json_array_size(args); i++) { const char *arg = json_string_value(json_array_get(args, i)); if (!strcmp(arg, "--")) { i++; break; } if (!strcmp(arg, "-X")) { include = false; continue; } if (!strcmp(arg, "-I")) { include = true; continue; } if (!strcmp(arg, "!")) { negated = true; continue; } if (!strcmp(arg, "-P")) { term_name = "ipcre"; continue; } if (!strcmp(arg, "-p")) { term_name = "pcre"; continue; } // Which group are we going to file it into json_ref container; if (include) { if (!included) { included = json_array({typed_string_to_json("anyof", W_STRING_UNICODE)}); } container = included; } else { if (!excluded) { excluded = json_array({typed_string_to_json("anyof", W_STRING_UNICODE)}); } container = excluded; } auto term = json_array({typed_string_to_json(term_name, W_STRING_UNICODE), typed_string_to_json(arg), typed_string_to_json("wholename", W_STRING_UNICODE)}); if (negated) { term = json_array({typed_string_to_json("not", W_STRING_UNICODE), term}); } json_array_append_new(container, std::move(term)); // Reset negated flag negated = false; term_name = "match"; } if (excluded) { excluded = json_array({typed_string_to_json("not", W_STRING_UNICODE), excluded}); } json_ref query_array; if (included && excluded) { query_array = json_array( {typed_string_to_json("allof", W_STRING_UNICODE), excluded, included}); } else if (included) { query_array = included; } else { query_array = excluded; } // query_array may be NULL, which means find me all files. // Otherwise, it is the expression we want to use. if (query_array) { json_object_set_new_nocheck( query_obj, "expression", std::move(query_array)); } // For trigger if (next_arg) { *next_arg = i; } if (clockspec) { json_object_set_new_nocheck(query_obj, "since", typed_string_to_json(clockspec, W_STRING_UNICODE)); } /* compose the query with the field list */ auto query = w_query_parse(root, query_obj); if (expr_p) { *expr_p = query_obj; } if (query) { w_query_legacy_field_list(&query->fieldList); } return query; }
static std::unique_ptr<QueryExpr> parse(w_query*, const json_ref& term, CaseSensitivity caseSensitive) { const char *pattern = nullptr, *scope = "basename"; const char *which = caseSensitive == CaseSensitivity::CaseInSensitive ? "iname" : "name"; std::unordered_set<w_string> set; if (!term.isArray()) { throw QueryParseError("Expected array for '", which, "' term"); } if (json_array_size(term) > 3) { throw QueryParseError( "Invalid number of arguments for '", which, "' term"); } if (json_array_size(term) == 3) { const auto& jscope = term.at(2); if (!jscope.isString()) { throw QueryParseError("Argument 3 to '", which, "' must be a string"); } scope = json_string_value(jscope); if (strcmp(scope, "basename") && strcmp(scope, "wholename")) { throw QueryParseError( "Invalid scope '", scope, "' for ", which, " expression"); } } const auto& name = term.at(1); if (name.isArray()) { uint32_t i; for (i = 0; i < json_array_size(name); i++) { if (!json_array_get(name, i).isString()) { throw QueryParseError( "Argument 2 to '", which, "' must be either a string or an array of string"); } } set.reserve(json_array_size(name)); for (i = 0; i < json_array_size(name); i++) { w_string element; const auto& jele = name.at(i); auto ele = json_to_w_string(jele); if (caseSensitive == CaseSensitivity::CaseInSensitive) { element = ele.piece().asLowerCase(ele.type()).normalizeSeparators(); } else { element = ele.normalizeSeparators(); } set.insert(element); } } else if (name.isString()) { pattern = json_string_value(name); } else { throw QueryParseError( "Argument 2 to '", which, "' must be either a string or an array of string"); } auto data = new NameExpr(std::move(set), caseSensitive, !strcmp(scope, "wholename")); if (pattern) { data->name = json_to_w_string(name).normalizeSeparators(); } return std::unique_ptr<QueryExpr>(data); }