Skip to content

Commit

Permalink
Generalize string criteria regex matching
Browse files Browse the repository at this point in the history
  • Loading branch information
earboxer committed Jul 29, 2023
1 parent b25d38f commit 94bf6a2
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 86 deletions.
177 changes: 103 additions & 74 deletions criteria.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,34 @@ struct mako_criteria *create_criteria(struct mako_config *config) {
return criteria;
}

void free_cond(struct mako_condition *cond) {
switch(cond->operator) {
case OP_EQUALS:
case OP_NOT_EQUALS:
free(cond->value);
return;
case OP_REGEX_MATCHES:
regfree(&cond->pattern);
return;
case OP_NONE:
case OP_TRUTHY:
case OP_FALSEY:
default:
// Nothing to free.
return;
}
}

void destroy_criteria(struct mako_criteria *criteria) {
wl_list_remove(&criteria->link);

finish_style(&criteria->style);
free(criteria->app_name);
free(criteria->app_icon);
free(criteria->category);
free(criteria->desktop_entry);
free(criteria->summary);
regfree(&criteria->summary_pattern);
free(criteria->body);
regfree(&criteria->body_pattern);
free_cond(&criteria->app_name);
free_cond(&criteria->app_icon);
free_cond(&criteria->category);
free_cond(&criteria->desktop_entry);
free_cond(&criteria->summary);
free_cond(&criteria->body);
free(criteria->raw_string);
free(criteria->output);
free(criteria->mode);
Expand All @@ -59,6 +75,24 @@ static bool match_regex_criteria(regex_t *pattern, char *value) {
return true;
}

bool match_condition(struct mako_condition *cond, char *value) {
switch(cond->operator) {
case OP_EQUALS:
return strcmp(cond->value, value) == 0;
case OP_NOT_EQUALS:
return strcmp(cond->value, value) != 0;
case OP_REGEX_MATCHES:
return match_regex_criteria(&cond->pattern, value);
case OP_TRUTHY:
return strcmp("", value) != 0;
case OP_FALSEY:
return strcmp("", value) == 0;
case OP_NONE:
return true;
}
return true;
}

bool match_criteria(struct mako_criteria *criteria,
struct mako_notification *notif) {
struct mako_criteria_spec spec = criteria->spec;
Expand All @@ -74,12 +108,12 @@ bool match_criteria(struct mako_criteria *criteria,
}

if (spec.app_name &&
strcmp(criteria->app_name, notif->app_name) != 0) {
!match_condition(&criteria->app_name, notif->app_name)) {
return false;
}

if (spec.app_icon &&
strcmp(criteria->app_icon, notif->app_icon) != 0) {
!match_condition(&criteria->app_icon, notif->app_icon)) {
return false;
}

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

if (spec.category &&
strcmp(criteria->category, notif->category) != 0) {
!match_condition(&criteria->category, notif->category)) {
return false;
}

if (spec.desktop_entry &&
strcmp(criteria->desktop_entry, notif->desktop_entry) != 0) {
!match_condition(&criteria->desktop_entry, notif->desktop_entry)) {
return false;
}

if (spec.summary &&
strcmp(criteria->summary, notif->summary) != 0) {
!match_condition(&criteria->summary, notif->summary)) {
return false;
}

if (spec.summary_pattern) {
bool ret = match_regex_criteria(&criteria->summary_pattern, notif->summary);
if (!ret) {
return false;
}
}

if (spec.body &&
strcmp(criteria->body, notif->body) != 0) {
!match_condition(&criteria->body, notif->body)) {
return false;
}

if (spec.body_pattern) {
bool ret = match_regex_criteria(&criteria->body_pattern, notif->body);
if (!ret) {
return false;
}
}

if (spec.group_index &&
criteria->group_index != notif->group_index) {
return false;
Expand Down Expand Up @@ -258,14 +278,38 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) {
return true;
}

// Takes a token from the criteria string that looks like "key=value", figures
// out which field of the criteria "key" refers to, and sets it to "value".
bool assign_condition(struct mako_condition *cond, enum operator op, char *value) {
cond->operator = op;
switch (op) {
case OP_REGEX_MATCHES:
if (regcomp(&cond->pattern, value, REG_EXTENDED | REG_NOSUB)) {
fprintf(stderr, "Invalid regex '%s'\n", value);
return false;
}
return true;
case OP_EQUALS:
case OP_NOT_EQUALS:
cond->value = strdup(value);
// fall-thru
case OP_FALSEY:
case OP_TRUTHY:
case OP_NONE:
default:
return true;
}
return true;
}

// Takes a token from the criteria string that looks like
// "key=value", "key!=value", or "key~=value"; and figures
// out which field of the criteria "key" refers to, and sets it to the condition.
// Any further equal signs are assumed to be part of the value. If there is no .
// equal sign present, the field is treated as a boolean, with a leading
// exclamation point signifying negation.
//
// Note that the token will be consumed.
bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
enum operator op = OP_EQUALS;
char *key = token;
char *value = strstr(key, "=");
bool bare_key = !value;
Expand All @@ -275,6 +319,15 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
}

if (value) {
if(value[-1] == '~') {
op = OP_REGEX_MATCHES;
// shorten the key.
value[-1] = '\0';
} else if (value[-1] == '!') {
op = OP_NOT_EQUALS;
// shorten the key.
value[-1] = '\0';
}
// Skip past the equal sign to the value itself.
*value = '\0';
++value;
Expand All @@ -284,8 +337,10 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
if (*key == '!') {
// Negated boolean, skip past the exclamation point.
++key;
op = OP_FALSEY;
value = "false";
} else {
op = OP_TRUTHY;
value = "true";
}
}
Expand All @@ -297,13 +352,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {

if (!bare_key) {
if (strcmp(key, "app-name") == 0) {
criteria->app_name = strdup(value);
criteria->spec.app_name = true;
return true;
return assign_condition(&criteria->app_name, op, value);
} else if (strcmp(key, "app-icon") == 0) {
criteria->app_icon = strdup(value);
criteria->spec.app_icon = true;
return true;
return assign_condition(&criteria->app_icon, op, value);
} else if (strcmp(key, "urgency") == 0) {
if (!parse_urgency(value, &criteria->urgency)) {
fprintf(stderr, "Invalid urgency value '%s'", value);
Expand All @@ -312,13 +365,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
criteria->spec.urgency = true;
return true;
} else if (strcmp(key, "category") == 0) {
criteria->category = strdup(value);
criteria->spec.category = true;
return true;
return assign_condition(&criteria->category, op, value);
} else if (strcmp(key, "desktop-entry") == 0) {
criteria->desktop_entry = strdup(value);
criteria->spec.desktop_entry = true;
return true;
return assign_condition(&criteria->desktop_entry, op, value);
} else if (strcmp(key, "group-index") == 0) {
if (!parse_int(value, &criteria->group_index)) {
fprintf(stderr, "Invalid group-index value '%s'", value);
Expand All @@ -327,29 +378,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) {
criteria->spec.group_index = true;
return true;
} else if (strcmp(key, "summary") == 0) {
criteria->summary = strdup(value);
criteria->spec.summary = true;
return true;
} else if (strcmp(key, "summary~") == 0) {
if (regcomp(&criteria->summary_pattern, value,
REG_EXTENDED | REG_NOSUB)) {
fprintf(stderr, "Invalid summary~ regex '%s'\n", value);
return false;
}
criteria->spec.summary_pattern = true;
return true;
return assign_condition(&criteria->summary, op, value);
} else if (strcmp(key, "body") == 0) {
criteria->body = strdup(value);
criteria->spec.body = true;
return true;
} else if (strcmp(key, "body~") == 0) {
if (regcomp(&criteria->body_pattern, value,
REG_EXTENDED | REG_NOSUB)) {
fprintf(stderr, "Invalid body~ regex '%s'\n", value);
return false;
}
criteria->spec.body_pattern = true;
return true;
return assign_condition(&criteria->body, op, value);
} else if (strcmp(key, "anchor") == 0) {
return criteria->spec.anchor =
parse_anchor(value, &criteria->anchor);
Expand Down Expand Up @@ -477,15 +510,21 @@ struct mako_criteria *create_criteria_from_notification(
// We only really need to copy the ones that are in the spec, but it
// doesn't hurt anything to do the rest and it makes this code much nicer
// to look at.
criteria->app_name = strdup(notif->app_name);
criteria->app_icon = strdup(notif->app_icon);
criteria->app_name.operator = OP_EQUALS;
criteria->app_name.value = strdup(notif->app_name);
criteria->app_icon.operator = OP_EQUALS;
criteria->app_icon.value = strdup(notif->app_icon);
criteria->actionable = !wl_list_empty(&notif->actions);
criteria->expiring = (notif->requested_timeout != 0);
criteria->urgency = notif->urgency;
criteria->category = strdup(notif->category);
criteria->desktop_entry = strdup(notif->desktop_entry);
criteria->summary = strdup(notif->summary);
criteria->body = strdup(notif->body);
criteria->category.operator = OP_EQUALS;
criteria->category.value = strdup(notif->category);
criteria->desktop_entry.operator = OP_EQUALS;
criteria->desktop_entry.value = strdup(notif->desktop_entry);
criteria->summary.operator = OP_EQUALS;
criteria->summary.value = strdup(notif->summary);
criteria->body.operator = OP_EQUALS;
criteria->body.value = strdup(notif->body);
criteria->group_index = notif->group_index;
criteria->grouped = (notif->group_index >= 0);
criteria->hidden = notif->hidden;
Expand Down Expand Up @@ -540,16 +579,6 @@ bool validate_criteria(struct mako_criteria *criteria) {
return false;
}

if (criteria->spec.summary && criteria->spec.summary_pattern) {
fprintf(stderr, "Cannot set both `summary` and `summary~`\n");
return false;
}

if (criteria->spec.body && criteria->spec.body_pattern) {
fprintf(stderr, "Cannot set both `body` and `body~`\n");
return false;
}

if (criteria->style.spec.group_criteria_spec) {
struct mako_criteria_spec *spec = &criteria->style.group_criteria_spec;

Expand Down
22 changes: 14 additions & 8 deletions include/criteria.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
struct mako_config;
struct mako_notification;

enum operator { OP_NONE, OP_EQUALS, OP_REGEX_MATCHES, OP_NOT_EQUALS, OP_TRUTHY, OP_FALSEY };

struct mako_condition {
enum operator operator;
char* value;
regex_t pattern;
};

struct mako_criteria {
struct mako_criteria_spec spec;
struct wl_list link; // mako_config::criteria
Expand All @@ -21,17 +29,15 @@ struct mako_criteria {
struct mako_style style;

// Fields that can be matched:
char *app_name;
char *app_icon;
struct mako_condition app_name;
struct mako_condition app_icon;
bool actionable; // Whether mako_notification.actions is nonempty
bool expiring; // Whether mako_notification.requested_timeout is non-zero
enum mako_notification_urgency urgency;
char *category;
char *desktop_entry;
char *summary;
regex_t summary_pattern;
char *body;
regex_t body_pattern;
struct mako_condition category;
struct mako_condition desktop_entry;
struct mako_condition summary;
struct mako_condition body;

char *mode;

Expand Down
2 changes: 0 additions & 2 deletions include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ struct mako_criteria_spec {
bool category;
bool desktop_entry;
bool summary;
bool summary_pattern;
bool body;
bool body_pattern;

bool mode;

Expand Down
2 changes: 0 additions & 2 deletions types.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,7 @@ bool mako_criteria_spec_any(const struct mako_criteria_spec *spec) {
spec->category ||
spec->desktop_entry ||
spec->summary ||
spec->summary_pattern ||
spec->body ||
spec->body_pattern ||
spec->none ||
spec->group_index ||
spec->grouped ||
Expand Down

0 comments on commit 94bf6a2

Please sign in to comment.