forked from facebook/watchman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsince.c
162 lines (136 loc) · 3.54 KB
/
since.c
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* Copyright 2013-present Facebook, Inc.
* Licensed under the Apache License, Version 2.0 */
#include "watchman.h"
struct since_term {
struct w_clockspec *spec;
enum {
SINCE_OCLOCK,
SINCE_CCLOCK,
SINCE_MTIME,
SINCE_CTIME
} field;
};
static bool eval_since(struct w_query_ctx *ctx,
struct watchman_file *file,
void *data)
{
struct since_term *term = data;
w_clock_t clock;
struct w_query_since since;
time_t tval = 0;
w_clockspec_eval(ctx->root, term->spec, &since);
switch (term->field) {
case SINCE_OCLOCK:
case SINCE_CCLOCK:
clock = (term->field == SINCE_OCLOCK) ? file->otime : file->ctime;
if (since.is_timestamp) {
return w_timeval_compare(since.timestamp, clock.tv) > 0;
}
if (since.clock.is_fresh_instance) {
return file->exists;
}
return clock.ticks > since.clock.ticks;
case SINCE_MTIME:
tval = file->st.st_mtime;
break;
case SINCE_CTIME:
tval = file->st.st_ctime;
break;
}
assert(since.is_timestamp);
return tval > since.timestamp.tv_sec;
}
static void dispose_since(void *data)
{
struct since_term *term = data;
w_clockspec_free(term->spec);
free(data);
}
static struct {
int value;
const char *label;
} allowed_fields[] = {
{ SINCE_OCLOCK, "oclock" },
{ SINCE_CCLOCK, "cclock" },
{ SINCE_MTIME, "mtime" },
{ SINCE_CTIME, "ctime" },
{ 0, NULL }
};
w_query_expr *w_expr_since_parser(w_query *query, json_t *term)
{
json_t *jval;
struct w_clockspec *spec;
struct since_term *sterm;
int selected_field = SINCE_OCLOCK;
const char *fieldname = "oclock";
if (!json_is_array(term)) {
query->errmsg = strdup("\"since\" term must be an array");
return NULL;
}
if (json_array_size(term) < 2 || json_array_size(term) > 3) {
query->errmsg = strdup("\"since\" term has invalid number of parameters");
return NULL;
}
jval = json_array_get(term, 1);
spec = w_clockspec_parse(jval);
if (!spec) {
query->errmsg = strdup("invalid clockspec for \"since\" term");
return NULL;
}
if (spec->tag == w_cs_named_cursor) {
query->errmsg = strdup("named cursors are not allowed in \"since\" terms");
goto fail;
}
jval = json_array_get(term, 2);
if (jval) {
int i;
bool valid = false;
fieldname = json_string_value(jval);
if (!fieldname) {
query->errmsg = strdup("field name for \"since\" term must be a string");
goto fail;
}
for (i = 0; allowed_fields[i].label; i++) {
if (!strcmp(allowed_fields[i].label, fieldname)) {
selected_field = allowed_fields[i].value;
valid = true;
break;
}
}
if (!valid) {
ignore_result(asprintf(&query->errmsg,
"invalid field name \"%s\" for \"since\" term",
fieldname));
goto fail;
}
}
switch (selected_field) {
case SINCE_CTIME:
case SINCE_MTIME:
if (spec->tag != w_cs_timestamp) {
ignore_result(asprintf(&query->errmsg,
"field \"%s\" requires a timestamp value "
"for comparison in \"since\" term",
fieldname));
goto fail;
}
break;
case SINCE_OCLOCK:
case SINCE_CCLOCK:
/* we'll work with clocks or timestamps */
break;
}
sterm = calloc(1, sizeof(*sterm));
if (!sterm) {
query->errmsg = strdup("out of memory");
goto fail;
}
sterm->spec = spec;
sterm->field = selected_field;
return w_query_expr_new(eval_since, dispose_since, sterm);
fail:
w_clockspec_free(spec);
return NULL;
}
/* vim:ts=2:sw=2:et:
*/