summaryrefslogtreecommitdiff
path: root/filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'filter.c')
-rw-r--r--filter.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/filter.c b/filter.c
new file mode 100644
index 0000000..312704a
--- /dev/null
+++ b/filter.c
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include "filter.h"
+
+
+struct filter_rule parse_exclude_rule(const char *rule) {
+ const size_t len = strlen(rule);
+
+ const bool open_left = rule[0] == '*';
+ if (open_left) {
+ if (len < 4 || memcmp(rule, "**/", 3) != 0) {
+ fprintf(stderr, "Error: A filter rule starting with '*' must start with '**/'.\n");
+ exit(1);
+ }
+ } else {
+ if (rule[0] != '/') {
+ fprintf(stderr, "Error: A filter rule with a non-wildcard left-hand side must start with '/'.\n");
+ exit(1);
+ }
+ }
+
+ const bool open_right = len > 0 && rule[len - 1] == '*';
+ if (open_right) {
+ if (len < 4 || memcmp(rule + len - 3, "/**", 3) != 0) {
+ fprintf(stderr, "Error: A filter rule ending with '*' must end with '/**'.\n");
+ exit(1);
+ }
+ }
+
+ const size_t str_start = open_left ? 3 : 1;
+ const size_t str_len = len - str_start - (open_right ? 3 : 0);
+
+ for (size_t i = 0; i < str_len; i++) {
+ if (rule[str_start + i] == '*') {
+ fprintf(stderr, "Error: Wildcards in the middle of a pattern unsupported.\n");
+ exit(1);
+ }
+ }
+
+ char *str = malloc(str_len + 1);
+ memcpy(str, rule + str_start, str_len);
+ str[str_len] = '\0';
+
+ return (struct filter_rule){
+ .ftype = FILTER_EXCLUDE,
+ .ptype = open_left && open_right ? PATTERN_INFIX
+ : open_left ? PATTERN_SUFFIX
+ : open_right ? PATTERN_PREFIX
+ : PATTERN_EQUAL,
+ .str = str,
+ .len = str_len,
+ };
+}
+
+bool filter_rule_allows(const struct filter_rule rule, const char *path) {
+ assert(rule.ftype == FILTER_EXCLUDE);
+
+ switch (rule.ptype) {
+ case PATTERN_EQUAL:
+ if (strcmp(path, rule.str) == 0) return false;
+ break;
+
+ case PATTERN_PREFIX:
+ if (strncmp(path, rule.str, rule.len) == 0 &&
+ (path[rule.len] == '/' || path[rule.len] == '\0'))
+ return false;
+ break;
+
+ case PATTERN_SUFFIX: {
+ const size_t pathlen = strlen(path);
+ if (pathlen >= rule.len &&
+ memcmp(path + pathlen - rule.len, rule.str, rule.len) == 0 &&
+ (pathlen == rule.len || path[pathlen - rule.len - 1] == '/'))
+ return false;
+ break;
+ }
+
+ case PATTERN_INFIX: {
+ const size_t pathlen = strlen(path);
+ if (strncmp(path, rule.str, rule.len) == 0 &&
+ (path[rule.len] == '\0' || path[rule.len] == '/')) return false;
+ for (size_t i = 0; i < pathlen; i++) {
+ if (path[i] == '/') {
+ if (strncmp(path + i+1, rule.str, rule.len) == 0 &&
+ (path[i+1 + rule.len] == '\0' || path[i+1 + rule.len] == '/')) return false;
+ }
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+void filter_rule_debugdump(FILE *stream, const struct filter_rule rule) {
+ const char *ftype_str;
+ switch (rule.ftype) {
+ case FILTER_EXCLUDE: ftype_str = "exclude"; break;
+ default: ftype_str = "?unknown_ftype"; break;
+ }
+
+ const char *ptype_str;
+ switch (rule.ptype) {
+ case PATTERN_EQUAL: ptype_str = "equal"; break;
+ case PATTERN_PREFIX: ptype_str = "prefix"; break;
+ case PATTERN_SUFFIX: ptype_str = "suffix"; break;
+ case PATTERN_INFIX: ptype_str = "infix"; break;
+ default: ptype_str = "?unknown_ptype"; break;
+ }
+
+ fprintf(stream, "%s %s <%s>\n", ftype_str, ptype_str, rule.str);
+}