File: dirname.cpp

package info (click to toggle)
watchman 4.9.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,992 kB
  • sloc: cpp: 27,459; python: 6,538; java: 3,404; php: 3,257; ansic: 2,803; javascript: 1,116; makefile: 671; ruby: 364; sh: 124; xml: 102; lisp: 4
file content (133 lines) | stat: -rw-r--r-- 4,122 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* Copyright 2015-present Facebook, Inc.
 * Licensed under the Apache License, Version 2.0 */

#include "watchman.h"

#include "make_unique.h"

using watchman::CaseSensitivity;

static inline bool is_dir_sep(int c) {
  return c == '/' || c == '\\';
}

class DirNameExpr : public QueryExpr {
  w_string dirname;
  struct w_query_int_compare depth;
  using StartsWith = bool (*)(w_string_t* str, w_string_t* prefix);
  StartsWith startswith;

 public:
  explicit DirNameExpr(
      w_string dirname,
      struct w_query_int_compare depth,
      StartsWith startswith)
      : dirname(dirname), depth(depth), startswith(startswith) {}

  bool evaluate(w_query_ctx* ctx, const FileResult*) override {
    auto& str = w_query_ctx_get_wholename(ctx);
    size_t i;

    if (str.size() <= dirname.size()) {
      // Either it doesn't prefix match, or file name is == dirname.
      // That means that the best case is that the wholename matches.
      // we only want to match if dirname(wholename) matches, so it
      // is not possible for us to match unless the length of wholename
      // is greater than the dirname operand
      return false;
    }

    // Want to make sure that wholename is a child of dirname, so
    // check for a dir separator.  Special case for dirname == '' (the root),
    // which won't have a slash in position 0.
    if (dirname.size() > 0 && !is_dir_sep(str.data()[dirname.size()])) {
      // may have a common prefix with, but is not a child of dirname
      return false;
    }

    if (!startswith(str, dirname)) {
      return false;
    }

    // Now compute the depth of file from dirname.  We do this by
    // counting dir separators, not including the one we saw above.
    json_int_t actual_depth = 0;
    for (i = dirname.size() + 1; i < str.size(); i++) {
      if (is_dir_sep(str.data()[i])) {
        actual_depth++;
      }
    }

    return eval_int_compare(actual_depth, &depth);
  }

  // ["dirname", "foo"] -> ["dirname", "foo", ["depth", "ge", 0]]
  static std::unique_ptr<QueryExpr>
  parse(w_query*, const json_ref& term, CaseSensitivity case_sensitive) {
    const char *which = case_sensitive == CaseSensitivity::CaseInSensitive
                            ? "idirname"
                            : "dirname";
    struct w_query_int_compare depth_comp;

    if (!json_is_array(term)) {
      throw QueryParseError("Expected array for '", which, "' term");
    }

    if (json_array_size(term) < 2) {
      throw QueryParseError(
          "Invalid number of arguments for '", which, "' term");
    }

    if (json_array_size(term) > 3) {
      throw QueryParseError(
          "Invalid number of arguments for '", which, "' term");
    }

    const auto& name = term.at(1);
    if (!json_is_string(name)) {
      throw QueryParseError("Argument 2 to '", which, "' must be a string");
    }

    if (json_array_size(term) == 3) {
      const auto& depth = term.at(2);
      if (!json_is_array(depth)) {
        throw QueryParseError(
            "Invalid number of arguments for '", which, "' term");
      }

      parse_int_compare(depth, &depth_comp);

      if (strcmp("depth", json_string_value(json_array_get(depth, 0)))) {
        throw QueryParseError(
            "Third parameter to '",
            which,
            "' should be a relational depth term");
      }
    } else {
      depth_comp.operand = 0;
      depth_comp.op = W_QUERY_ICMP_GE;
    }

    return watchman::make_unique<DirNameExpr>(
        json_to_w_string(name), depth_comp,
        case_sensitive == CaseSensitivity::CaseInSensitive
            ? w_string_startswith_caseless
            : w_string_startswith);
  }
  static std::unique_ptr<QueryExpr> parseDirName(
      w_query* query,
      const json_ref& term) {
    return parse(query, term, query->case_sensitive);
  }
  static std::unique_ptr<QueryExpr> parseIDirName(
      w_query* query,
      const json_ref& term) {
    return parse(query, term, CaseSensitivity::CaseInSensitive);
  }
};

W_TERM_PARSER("dirname", DirNameExpr::parseDirName)
W_TERM_PARSER("idirname", DirNameExpr::parseIDirName)

/* vim:ts=2:sw=2:et:
 */