Skip to content

Commit 94bf6a2

Browse files
committed
Generalize string criteria regex matching
Closes emersion#306
1 parent b25d38f commit 94bf6a2

File tree

4 files changed

+117
-86
lines changed

4 files changed

+117
-86
lines changed

criteria.c

+103-74
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,34 @@ struct mako_criteria *create_criteria(struct mako_config *config) {
2727
return criteria;
2828
}
2929

30+
void free_cond(struct mako_condition *cond) {
31+
switch(cond->operator) {
32+
case OP_EQUALS:
33+
case OP_NOT_EQUALS:
34+
free(cond->value);
35+
return;
36+
case OP_REGEX_MATCHES:
37+
regfree(&cond->pattern);
38+
return;
39+
case OP_NONE:
40+
case OP_TRUTHY:
41+
case OP_FALSEY:
42+
default:
43+
// Nothing to free.
44+
return;
45+
}
46+
}
47+
3048
void destroy_criteria(struct mako_criteria *criteria) {
3149
wl_list_remove(&criteria->link);
3250

3351
finish_style(&criteria->style);
34-
free(criteria->app_name);
35-
free(criteria->app_icon);
36-
free(criteria->category);
37-
free(criteria->desktop_entry);
38-
free(criteria->summary);
39-
regfree(&criteria->summary_pattern);
40-
free(criteria->body);
41-
regfree(&criteria->body_pattern);
52+
free_cond(&criteria->app_name);
53+
free_cond(&criteria->app_icon);
54+
free_cond(&criteria->category);
55+
free_cond(&criteria->desktop_entry);
56+
free_cond(&criteria->summary);
57+
free_cond(&criteria->body);
4258
free(criteria->raw_string);
4359
free(criteria->output);
4460
free(criteria->mode);
@@ -59,6 +75,24 @@ static bool match_regex_criteria(regex_t *pattern, char *value) {
5975
return true;
6076
}
6177

78+
bool match_condition(struct mako_condition *cond, char *value) {
79+
switch(cond->operator) {
80+
case OP_EQUALS:
81+
return strcmp(cond->value, value) == 0;
82+
case OP_NOT_EQUALS:
83+
return strcmp(cond->value, value) != 0;
84+
case OP_REGEX_MATCHES:
85+
return match_regex_criteria(&cond->pattern, value);
86+
case OP_TRUTHY:
87+
return strcmp("", value) != 0;
88+
case OP_FALSEY:
89+
return strcmp("", value) == 0;
90+
case OP_NONE:
91+
return true;
92+
}
93+
return true;
94+
}
95+
6296
bool match_criteria(struct mako_criteria *criteria,
6397
struct mako_notification *notif) {
6498
struct mako_criteria_spec spec = criteria->spec;
@@ -74,12 +108,12 @@ bool match_criteria(struct mako_criteria *criteria,
74108
}
75109

76110
if (spec.app_name &&
77-
strcmp(criteria->app_name, notif->app_name) != 0) {
111+
!match_condition(&criteria->app_name, notif->app_name)) {
78112
return false;
79113
}
80114

81115
if (spec.app_icon &&
82-
strcmp(criteria->app_icon, notif->app_icon) != 0) {
116+
!match_condition(&criteria->app_icon, notif->app_icon)) {
83117
return false;
84118
}
85119

@@ -99,39 +133,25 @@ bool match_criteria(struct mako_criteria *criteria,
99133
}
100134

101135
if (spec.category &&
102-
strcmp(criteria->category, notif->category) != 0) {
136+
!match_condition(&criteria->category, notif->category)) {
103137
return false;
104138
}
105139

106140
if (spec.desktop_entry &&
107-
strcmp(criteria->desktop_entry, notif->desktop_entry) != 0) {
141+
!match_condition(&criteria->desktop_entry, notif->desktop_entry)) {
108142
return false;
109143
}
110144

111145
if (spec.summary &&
112-
strcmp(criteria->summary, notif->summary) != 0) {
146+
!match_condition(&criteria->summary, notif->summary)) {
113147
return false;
114148
}
115149

116-
if (spec.summary_pattern) {
117-
bool ret = match_regex_criteria(&criteria->summary_pattern, notif->summary);
118-
if (!ret) {
119-
return false;
120-
}
121-
}
122-
123150
if (spec.body &&
124-
strcmp(criteria->body, notif->body) != 0) {
151+
!match_condition(&criteria->body, notif->body)) {
125152
return false;
126153
}
127154

128-
if (spec.body_pattern) {
129-
bool ret = match_regex_criteria(&criteria->body_pattern, notif->body);
130-
if (!ret) {
131-
return false;
132-
}
133-
}
134-
135155
if (spec.group_index &&
136156
criteria->group_index != notif->group_index) {
137157
return false;
@@ -258,14 +278,38 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) {
258278
return true;
259279
}
260280

261-
// Takes a token from the criteria string that looks like "key=value", figures
262-
// out which field of the criteria "key" refers to, and sets it to "value".
281+
bool assign_condition(struct mako_condition *cond, enum operator op, char *value) {
282+
cond->operator = op;
283+
switch (op) {
284+
case OP_REGEX_MATCHES:
285+
if (regcomp(&cond->pattern, value, REG_EXTENDED | REG_NOSUB)) {
286+
fprintf(stderr, "Invalid regex '%s'\n", value);
287+
return false;
288+
}
289+
return true;
290+
case OP_EQUALS:
291+
case OP_NOT_EQUALS:
292+
cond->value = strdup(value);
293+
// fall-thru
294+
case OP_FALSEY:
295+
case OP_TRUTHY:
296+
case OP_NONE:
297+
default:
298+
return true;
299+
}
300+
return true;
301+
}
302+
303+
// Takes a token from the criteria string that looks like
304+
// "key=value", "key!=value", or "key~=value"; and figures
305+
// out which field of the criteria "key" refers to, and sets it to the condition.
263306
// Any further equal signs are assumed to be part of the value. If there is no .
264307
// equal sign present, the field is treated as a boolean, with a leading
265308
// exclamation point signifying negation.
266309
//
267310
// Note that the token will be consumed.
268311
bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
312+
enum operator op = OP_EQUALS;
269313
char *key = token;
270314
char *value = strstr(key, "=");
271315
bool bare_key = !value;
@@ -275,6 +319,15 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
275319
}
276320

277321
if (value) {
322+
if(value[-1] == '~') {
323+
op = OP_REGEX_MATCHES;
324+
// shorten the key.
325+
value[-1] = '\0';
326+
} else if (value[-1] == '!') {
327+
op = OP_NOT_EQUALS;
328+
// shorten the key.
329+
value[-1] = '\0';
330+
}
278331
// Skip past the equal sign to the value itself.
279332
*value = '\0';
280333
++value;
@@ -284,8 +337,10 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
284337
if (*key == '!') {
285338
// Negated boolean, skip past the exclamation point.
286339
++key;
340+
op = OP_FALSEY;
287341
value = "false";
288342
} else {
343+
op = OP_TRUTHY;
289344
value = "true";
290345
}
291346
}
@@ -297,13 +352,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
297352

298353
if (!bare_key) {
299354
if (strcmp(key, "app-name") == 0) {
300-
criteria->app_name = strdup(value);
301355
criteria->spec.app_name = true;
302-
return true;
356+
return assign_condition(&criteria->app_name, op, value);
303357
} else if (strcmp(key, "app-icon") == 0) {
304-
criteria->app_icon = strdup(value);
305358
criteria->spec.app_icon = true;
306-
return true;
359+
return assign_condition(&criteria->app_icon, op, value);
307360
} else if (strcmp(key, "urgency") == 0) {
308361
if (!parse_urgency(value, &criteria->urgency)) {
309362
fprintf(stderr, "Invalid urgency value '%s'", value);
@@ -312,13 +365,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
312365
criteria->spec.urgency = true;
313366
return true;
314367
} else if (strcmp(key, "category") == 0) {
315-
criteria->category = strdup(value);
316368
criteria->spec.category = true;
317-
return true;
369+
return assign_condition(&criteria->category, op, value);
318370
} else if (strcmp(key, "desktop-entry") == 0) {
319-
criteria->desktop_entry = strdup(value);
320371
criteria->spec.desktop_entry = true;
321-
return true;
372+
return assign_condition(&criteria->desktop_entry, op, value);
322373
} else if (strcmp(key, "group-index") == 0) {
323374
if (!parse_int(value, &criteria->group_index)) {
324375
fprintf(stderr, "Invalid group-index value '%s'", value);
@@ -327,29 +378,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
327378
criteria->spec.group_index = true;
328379
return true;
329380
} else if (strcmp(key, "summary") == 0) {
330-
criteria->summary = strdup(value);
331381
criteria->spec.summary = true;
332-
return true;
333-
} else if (strcmp(key, "summary~") == 0) {
334-
if (regcomp(&criteria->summary_pattern, value,
335-
REG_EXTENDED | REG_NOSUB)) {
336-
fprintf(stderr, "Invalid summary~ regex '%s'\n", value);
337-
return false;
338-
}
339-
criteria->spec.summary_pattern = true;
340-
return true;
382+
return assign_condition(&criteria->summary, op, value);
341383
} else if (strcmp(key, "body") == 0) {
342-
criteria->body = strdup(value);
343384
criteria->spec.body = true;
344-
return true;
345-
} else if (strcmp(key, "body~") == 0) {
346-
if (regcomp(&criteria->body_pattern, value,
347-
REG_EXTENDED | REG_NOSUB)) {
348-
fprintf(stderr, "Invalid body~ regex '%s'\n", value);
349-
return false;
350-
}
351-
criteria->spec.body_pattern = true;
352-
return true;
385+
return assign_condition(&criteria->body, op, value);
353386
} else if (strcmp(key, "anchor") == 0) {
354387
return criteria->spec.anchor =
355388
parse_anchor(value, &criteria->anchor);
@@ -477,15 +510,21 @@ struct mako_criteria *create_criteria_from_notification(
477510
// We only really need to copy the ones that are in the spec, but it
478511
// doesn't hurt anything to do the rest and it makes this code much nicer
479512
// to look at.
480-
criteria->app_name = strdup(notif->app_name);
481-
criteria->app_icon = strdup(notif->app_icon);
513+
criteria->app_name.operator = OP_EQUALS;
514+
criteria->app_name.value = strdup(notif->app_name);
515+
criteria->app_icon.operator = OP_EQUALS;
516+
criteria->app_icon.value = strdup(notif->app_icon);
482517
criteria->actionable = !wl_list_empty(&notif->actions);
483518
criteria->expiring = (notif->requested_timeout != 0);
484519
criteria->urgency = notif->urgency;
485-
criteria->category = strdup(notif->category);
486-
criteria->desktop_entry = strdup(notif->desktop_entry);
487-
criteria->summary = strdup(notif->summary);
488-
criteria->body = strdup(notif->body);
520+
criteria->category.operator = OP_EQUALS;
521+
criteria->category.value = strdup(notif->category);
522+
criteria->desktop_entry.operator = OP_EQUALS;
523+
criteria->desktop_entry.value = strdup(notif->desktop_entry);
524+
criteria->summary.operator = OP_EQUALS;
525+
criteria->summary.value = strdup(notif->summary);
526+
criteria->body.operator = OP_EQUALS;
527+
criteria->body.value = strdup(notif->body);
489528
criteria->group_index = notif->group_index;
490529
criteria->grouped = (notif->group_index >= 0);
491530
criteria->hidden = notif->hidden;
@@ -540,16 +579,6 @@ bool validate_criteria(struct mako_criteria *criteria) {
540579
return false;
541580
}
542581

543-
if (criteria->spec.summary && criteria->spec.summary_pattern) {
544-
fprintf(stderr, "Cannot set both `summary` and `summary~`\n");
545-
return false;
546-
}
547-
548-
if (criteria->spec.body && criteria->spec.body_pattern) {
549-
fprintf(stderr, "Cannot set both `body` and `body~`\n");
550-
return false;
551-
}
552-
553582
if (criteria->style.spec.group_criteria_spec) {
554583
struct mako_criteria_spec *spec = &criteria->style.group_criteria_spec;
555584

include/criteria.h

+14-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
struct mako_config;
1212
struct mako_notification;
1313

14+
enum operator { OP_NONE, OP_EQUALS, OP_REGEX_MATCHES, OP_NOT_EQUALS, OP_TRUTHY, OP_FALSEY };
15+
16+
struct mako_condition {
17+
enum operator operator;
18+
char* value;
19+
regex_t pattern;
20+
};
21+
1422
struct mako_criteria {
1523
struct mako_criteria_spec spec;
1624
struct wl_list link; // mako_config::criteria
@@ -21,17 +29,15 @@ struct mako_criteria {
2129
struct mako_style style;
2230

2331
// Fields that can be matched:
24-
char *app_name;
25-
char *app_icon;
32+
struct mako_condition app_name;
33+
struct mako_condition app_icon;
2634
bool actionable; // Whether mako_notification.actions is nonempty
2735
bool expiring; // Whether mako_notification.requested_timeout is non-zero
2836
enum mako_notification_urgency urgency;
29-
char *category;
30-
char *desktop_entry;
31-
char *summary;
32-
regex_t summary_pattern;
33-
char *body;
34-
regex_t body_pattern;
37+
struct mako_condition category;
38+
struct mako_condition desktop_entry;
39+
struct mako_condition summary;
40+
struct mako_condition body;
3541

3642
char *mode;
3743

include/types.h

-2
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,7 @@ struct mako_criteria_spec {
5151
bool category;
5252
bool desktop_entry;
5353
bool summary;
54-
bool summary_pattern;
5554
bool body;
56-
bool body_pattern;
5755

5856
bool mode;
5957

types.c

-2
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,7 @@ bool mako_criteria_spec_any(const struct mako_criteria_spec *spec) {
245245
spec->category ||
246246
spec->desktop_entry ||
247247
spec->summary ||
248-
spec->summary_pattern ||
249248
spec->body ||
250-
spec->body_pattern ||
251249
spec->none ||
252250
spec->group_index ||
253251
spec->grouped ||

0 commit comments

Comments
 (0)