Skip to content

Commit 2f19eed

Browse files
committed
Generalize string criteria regex matching
Closes emersion#306
1 parent 91a1fc4 commit 2f19eed

File tree

4 files changed

+116
-86
lines changed

4 files changed

+116
-86
lines changed

criteria.c

+102-74
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,33 @@ struct mako_criteria *create_criteria(struct mako_config *config) {
2626
return criteria;
2727
}
2828

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

3249
finish_style(&criteria->style);
33-
free(criteria->app_name);
34-
free(criteria->app_icon);
35-
free(criteria->category);
36-
free(criteria->desktop_entry);
37-
free(criteria->summary);
38-
regfree(&criteria->summary_pattern);
39-
free(criteria->body);
40-
regfree(&criteria->body_pattern);
50+
free_cond(&criteria->app_name);
51+
free_cond(&criteria->app_icon);
52+
free_cond(&criteria->category);
53+
free_cond(&criteria->desktop_entry);
54+
free_cond(&criteria->summary);
55+
free_cond(&criteria->body);
4156
free(criteria->raw_string);
4257
free(criteria->output);
4358
free(criteria->mode);
@@ -58,6 +73,24 @@ static bool match_regex_criteria(regex_t *pattern, char *value) {
5873
return true;
5974
}
6075

76+
bool match_condition(struct mako_condition *cond, char *value) {
77+
switch(cond->operator) {
78+
case OP_EQUALS:
79+
return strcmp(cond->value, value) == 0;
80+
case OP_NOT_EQUALS:
81+
return strcmp(cond->value, value) != 0;
82+
case OP_REGEX_MATCHES:
83+
return match_regex_criteria(&cond->pattern, value);
84+
case OP_TRUTHY:
85+
return strcmp("", value) != 0;
86+
case OP_FALSEY:
87+
return strcmp("", value) == 0;
88+
case OP_NONE:
89+
return true;
90+
}
91+
abort();
92+
}
93+
6194
bool match_criteria(struct mako_criteria *criteria,
6295
struct mako_notification *notif) {
6396
struct mako_criteria_spec spec = criteria->spec;
@@ -73,12 +106,12 @@ bool match_criteria(struct mako_criteria *criteria,
73106
}
74107

75108
if (spec.app_name &&
76-
strcmp(criteria->app_name, notif->app_name) != 0) {
109+
!match_condition(&criteria->app_name, notif->app_name)) {
77110
return false;
78111
}
79112

80113
if (spec.app_icon &&
81-
strcmp(criteria->app_icon, notif->app_icon) != 0) {
114+
!match_condition(&criteria->app_icon, notif->app_icon)) {
82115
return false;
83116
}
84117

@@ -98,39 +131,25 @@ bool match_criteria(struct mako_criteria *criteria,
98131
}
99132

100133
if (spec.category &&
101-
strcmp(criteria->category, notif->category) != 0) {
134+
!match_condition(&criteria->category, notif->category)) {
102135
return false;
103136
}
104137

105138
if (spec.desktop_entry &&
106-
strcmp(criteria->desktop_entry, notif->desktop_entry) != 0) {
139+
!match_condition(&criteria->desktop_entry, notif->desktop_entry)) {
107140
return false;
108141
}
109142

110143
if (spec.summary &&
111-
strcmp(criteria->summary, notif->summary) != 0) {
144+
!match_condition(&criteria->summary, notif->summary)) {
112145
return false;
113146
}
114147

115-
if (spec.summary_pattern) {
116-
bool ret = match_regex_criteria(&criteria->summary_pattern, notif->summary);
117-
if (!ret) {
118-
return false;
119-
}
120-
}
121-
122148
if (spec.body &&
123-
strcmp(criteria->body, notif->body) != 0) {
149+
!match_condition(&criteria->body, notif->body)) {
124150
return false;
125151
}
126152

127-
if (spec.body_pattern) {
128-
bool ret = match_regex_criteria(&criteria->body_pattern, notif->body);
129-
if (!ret) {
130-
return false;
131-
}
132-
}
133-
134153
if (spec.group_index &&
135154
criteria->group_index != notif->group_index) {
136155
return false;
@@ -257,14 +276,38 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) {
257276
return true;
258277
}
259278

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

276319
if (value) {
320+
if(value[-1] == '~') {
321+
op = OP_REGEX_MATCHES;
322+
// shorten the key.
323+
value[-1] = '\0';
324+
} else if (value[-1] == '!') {
325+
op = OP_NOT_EQUALS;
326+
// shorten the key.
327+
value[-1] = '\0';
328+
}
277329
// Skip past the equal sign to the value itself.
278330
*value = '\0';
279331
++value;
@@ -283,8 +335,10 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
283335
if (*key == '!') {
284336
// Negated boolean, skip past the exclamation point.
285337
++key;
338+
op = OP_FALSEY;
286339
value = "false";
287340
} else {
341+
op = OP_TRUTHY;
288342
value = "true";
289343
}
290344
}
@@ -296,13 +350,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
296350

297351
if (!bare_key) {
298352
if (strcmp(key, "app-name") == 0) {
299-
criteria->app_name = strdup(value);
300353
criteria->spec.app_name = true;
301-
return true;
354+
return assign_condition(&criteria->app_name, op, value);
302355
} else if (strcmp(key, "app-icon") == 0) {
303-
criteria->app_icon = strdup(value);
304356
criteria->spec.app_icon = true;
305-
return true;
357+
return assign_condition(&criteria->app_icon, op, value);
306358
} else if (strcmp(key, "urgency") == 0) {
307359
if (!parse_urgency(value, &criteria->urgency)) {
308360
fprintf(stderr, "Invalid urgency value '%s'", value);
@@ -311,13 +363,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
311363
criteria->spec.urgency = true;
312364
return true;
313365
} else if (strcmp(key, "category") == 0) {
314-
criteria->category = strdup(value);
315366
criteria->spec.category = true;
316-
return true;
367+
return assign_condition(&criteria->category, op, value);
317368
} else if (strcmp(key, "desktop-entry") == 0) {
318-
criteria->desktop_entry = strdup(value);
319369
criteria->spec.desktop_entry = true;
320-
return true;
370+
return assign_condition(&criteria->desktop_entry, op, value);
321371
} else if (strcmp(key, "group-index") == 0) {
322372
if (!parse_int(value, &criteria->group_index)) {
323373
fprintf(stderr, "Invalid group-index value '%s'", value);
@@ -326,29 +376,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
326376
criteria->spec.group_index = true;
327377
return true;
328378
} else if (strcmp(key, "summary") == 0) {
329-
criteria->summary = strdup(value);
330379
criteria->spec.summary = true;
331-
return true;
332-
} else if (strcmp(key, "summary~") == 0) {
333-
if (regcomp(&criteria->summary_pattern, value,
334-
REG_EXTENDED | REG_NOSUB)) {
335-
fprintf(stderr, "Invalid summary~ regex '%s'\n", value);
336-
return false;
337-
}
338-
criteria->spec.summary_pattern = true;
339-
return true;
380+
return assign_condition(&criteria->summary, op, value);
340381
} else if (strcmp(key, "body") == 0) {
341-
criteria->body = strdup(value);
342382
criteria->spec.body = true;
343-
return true;
344-
} else if (strcmp(key, "body~") == 0) {
345-
if (regcomp(&criteria->body_pattern, value,
346-
REG_EXTENDED | REG_NOSUB)) {
347-
fprintf(stderr, "Invalid body~ regex '%s'\n", value);
348-
return false;
349-
}
350-
criteria->spec.body_pattern = true;
351-
return true;
383+
return assign_condition(&criteria->body, op, value);
352384
} else if (strcmp(key, "anchor") == 0) {
353385
return criteria->spec.anchor =
354386
parse_anchor(value, &criteria->anchor);
@@ -476,15 +508,21 @@ struct mako_criteria *create_criteria_from_notification(
476508
// We only really need to copy the ones that are in the spec, but it
477509
// doesn't hurt anything to do the rest and it makes this code much nicer
478510
// to look at.
479-
criteria->app_name = strdup(notif->app_name);
480-
criteria->app_icon = strdup(notif->app_icon);
511+
criteria->app_name.operator = OP_EQUALS;
512+
criteria->app_name.value = strdup(notif->app_name);
513+
criteria->app_icon.operator = OP_EQUALS;
514+
criteria->app_icon.value = strdup(notif->app_icon);
481515
criteria->actionable = !wl_list_empty(&notif->actions);
482516
criteria->expiring = (notif->requested_timeout != 0);
483517
criteria->urgency = notif->urgency;
484-
criteria->category = strdup(notif->category);
485-
criteria->desktop_entry = strdup(notif->desktop_entry);
486-
criteria->summary = strdup(notif->summary);
487-
criteria->body = strdup(notif->body);
518+
criteria->category.operator = OP_EQUALS;
519+
criteria->category.value = strdup(notif->category);
520+
criteria->desktop_entry.operator = OP_EQUALS;
521+
criteria->desktop_entry.value = strdup(notif->desktop_entry);
522+
criteria->summary.operator = OP_EQUALS;
523+
criteria->summary.value = strdup(notif->summary);
524+
criteria->body.operator = OP_EQUALS;
525+
criteria->body.value = strdup(notif->body);
488526
criteria->group_index = notif->group_index;
489527
criteria->grouped = (notif->group_index >= 0);
490528
criteria->hidden = notif->hidden;
@@ -539,16 +577,6 @@ bool validate_criteria(struct mako_criteria *criteria) {
539577
return false;
540578
}
541579

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

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
@@ -244,9 +244,7 @@ bool mako_criteria_spec_any(const struct mako_criteria_spec *spec) {
244244
spec->category ||
245245
spec->desktop_entry ||
246246
spec->summary ||
247-
spec->summary_pattern ||
248247
spec->body ||
249-
spec->body_pattern ||
250248
spec->none ||
251249
spec->group_index ||
252250
spec->grouped ||

0 commit comments

Comments
 (0)