aboutsummaryrefslogtreecommitdiff
path: root/src/c2.c
diff options
context:
space:
mode:
authorallusive-dev <[email protected]>2023-09-19 17:46:20 +1000
committerallusive-dev <[email protected]>2023-09-19 17:46:20 +1000
commit5650d887357bf2a3fac8c5fd4f467bf8795b5fc4 (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/c2.c
parentUpdate picom.sample.conf (diff)
downloadcompfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.tar.xz
compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.zip
reset
Diffstat (limited to 'src/c2.c')
-rw-r--r--src/c2.c1674
1 files changed, 0 insertions, 1674 deletions
diff --git a/src/c2.c b/src/c2.c
deleted file mode 100644
index 3500f7b..0000000
--- a/src/c2.c
+++ /dev/null
@@ -1,1674 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-/*
- * Compton - a compositor for X11
- *
- * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
- *
- * Copyright (c) 2011-2013, Christopher Jeffrey
- * See LICENSE-mit for more information.
- *
- */
-
-#include <ctype.h>
-#include <fnmatch.h>
-#include <stdio.h>
-#include <string.h>
-
-// libpcre
-#ifdef CONFIG_REGEX_PCRE
-#include <pcre.h>
-
-// For compatibility with <libpcre-8.20
-#ifndef PCRE_STUDY_JIT_COMPILE
-#define PCRE_STUDY_JIT_COMPILE 0
-#define LPCRE_FREE_STUDY(extra) pcre_free(extra)
-#else
-#define LPCRE_FREE_STUDY(extra) pcre_free_study(extra)
-#endif
-
-#endif
-
-#include <X11/Xlib.h>
-#include <xcb/xcb.h>
-
-#include "atom.h"
-#include "common.h"
-#include "compiler.h"
-#include "config.h"
-#include "log.h"
-#include "string_utils.h"
-#include "utils.h"
-#include "win.h"
-#include "x.h"
-
-#include "c2.h"
-
-#pragma GCC diagnostic error "-Wunused-parameter"
-
-#define C2_MAX_LEVELS 10
-
-typedef struct _c2_b c2_b_t;
-typedef struct _c2_l c2_l_t;
-
-/// Pointer to a condition tree.
-typedef struct {
- bool isbranch : 1;
- union {
- c2_b_t *b;
- c2_l_t *l;
- };
-} c2_ptr_t;
-
-/// Initializer for c2_ptr_t.
-#define C2_PTR_INIT \
- { .isbranch = false, .l = NULL, }
-
-static const c2_ptr_t C2_PTR_NULL = C2_PTR_INIT;
-
-/// Operator of a branch element.
-typedef enum {
- C2_B_OUNDEFINED,
- C2_B_OAND,
- C2_B_OOR,
- C2_B_OXOR,
-} c2_b_op_t;
-
-/// Structure for branch element in a window condition
-struct _c2_b {
- bool neg : 1;
- c2_b_op_t op;
- c2_ptr_t opr1;
- c2_ptr_t opr2;
-};
-
-/// Initializer for c2_b_t.
-#define C2_B_INIT \
- { .neg = false, .op = C2_B_OUNDEFINED, .opr1 = C2_PTR_INIT, .opr2 = C2_PTR_INIT, }
-
-/// Structure for leaf element in a window condition
-struct _c2_l {
- bool neg : 1;
- enum { C2_L_OEXISTS,
- C2_L_OEQ,
- C2_L_OGT,
- C2_L_OGTEQ,
- C2_L_OLT,
- C2_L_OLTEQ,
- } op : 3;
- enum { C2_L_MEXACT,
- C2_L_MSTART,
- C2_L_MCONTAINS,
- C2_L_MWILDCARD,
- C2_L_MPCRE,
- } match : 3;
- bool match_ignorecase : 1;
- char *tgt;
- xcb_atom_t tgtatom;
- bool tgt_onframe;
- int index;
- enum { C2_L_PUNDEFINED = -1,
- C2_L_PID = 0,
- C2_L_PX,
- C2_L_PY,
- C2_L_PX2,
- C2_L_PY2,
- C2_L_PWIDTH,
- C2_L_PHEIGHT,
- C2_L_PWIDTHB,
- C2_L_PHEIGHTB,
- C2_L_PBDW,
- C2_L_PFULLSCREEN,
- C2_L_POVREDIR,
- C2_L_PARGB,
- C2_L_PFOCUSED,
- C2_L_PWMWIN,
- C2_L_PBSHAPED,
- C2_L_PROUNDED,
- C2_L_PCLIENT,
- C2_L_PWINDOWTYPE,
- C2_L_PLEADER,
- C2_L_PNAME,
- C2_L_PCLASSG,
- C2_L_PCLASSI,
- C2_L_PROLE,
- } predef;
- enum c2_l_type {
- C2_L_TUNDEFINED,
- C2_L_TSTRING,
- C2_L_TCARDINAL,
- C2_L_TWINDOW,
- C2_L_TATOM,
- C2_L_TDRAWABLE,
- } type;
- int format;
- enum { C2_L_PTUNDEFINED,
- C2_L_PTSTRING,
- C2_L_PTINT,
- } ptntype;
- char *ptnstr;
- long ptnint;
-#ifdef CONFIG_REGEX_PCRE
- pcre *regex_pcre;
- pcre_extra *regex_pcre_extra;
-#endif
-};
-
-/// Initializer for c2_l_t.
-#define C2_L_INIT \
- { \
- .neg = false, .op = C2_L_OEXISTS, .match = C2_L_MEXACT, \
- .match_ignorecase = false, .tgt = NULL, .tgtatom = 0, .tgt_onframe = false, \
- .predef = C2_L_PUNDEFINED, .index = 0, .type = C2_L_TUNDEFINED, \
- .format = 0, .ptntype = C2_L_PTUNDEFINED, .ptnstr = NULL, .ptnint = 0, \
- }
-
-static const c2_l_t leaf_def = C2_L_INIT;
-
-/// Linked list type of conditions.
-struct _c2_lptr {
- c2_ptr_t ptr;
- void *data;
- struct _c2_lptr *next;
-};
-
-/// Initializer for c2_lptr_t.
-#define C2_LPTR_INIT \
- { .ptr = C2_PTR_INIT, .data = NULL, .next = NULL, }
-
-/// Structure representing a predefined target.
-typedef struct {
- const char *name;
- enum c2_l_type type;
- int format;
-} c2_predef_t;
-
-// Predefined targets.
-static const c2_predef_t C2_PREDEFS[] = {
- [C2_L_PID] = {"id", C2_L_TCARDINAL, 0},
- [C2_L_PX] = {"x", C2_L_TCARDINAL, 0},
- [C2_L_PY] = {"y", C2_L_TCARDINAL, 0},
- [C2_L_PX2] = {"x2", C2_L_TCARDINAL, 0},
- [C2_L_PY2] = {"y2", C2_L_TCARDINAL, 0},
- [C2_L_PWIDTH] = {"width", C2_L_TCARDINAL, 0},
- [C2_L_PHEIGHT] = {"height", C2_L_TCARDINAL, 0},
- [C2_L_PWIDTHB] = {"widthb", C2_L_TCARDINAL, 0},
- [C2_L_PHEIGHTB] = {"heightb", C2_L_TCARDINAL, 0},
- [C2_L_PBDW] = {"border_width", C2_L_TCARDINAL, 0},
- [C2_L_PFULLSCREEN] = {"fullscreen", C2_L_TCARDINAL, 0},
- [C2_L_POVREDIR] = {"override_redirect", C2_L_TCARDINAL, 0},
- [C2_L_PARGB] = {"argb", C2_L_TCARDINAL, 0},
- [C2_L_PFOCUSED] = {"focused", C2_L_TCARDINAL, 0},
- [C2_L_PWMWIN] = {"wmwin", C2_L_TCARDINAL, 0},
- [C2_L_PBSHAPED] = {"bounding_shaped", C2_L_TCARDINAL, 0},
- [C2_L_PROUNDED] = {"rounded_corners", C2_L_TCARDINAL, 0},
- [C2_L_PCLIENT] = {"client", C2_L_TWINDOW, 0},
- [C2_L_PWINDOWTYPE] = {"window_type", C2_L_TSTRING, 0},
- [C2_L_PLEADER] = {"leader", C2_L_TWINDOW, 0},
- [C2_L_PNAME] = {"name", C2_L_TSTRING, 0},
- [C2_L_PCLASSG] = {"class_g", C2_L_TSTRING, 0},
- [C2_L_PCLASSI] = {"class_i", C2_L_TSTRING, 0},
- [C2_L_PROLE] = {"role", C2_L_TSTRING, 0},
-};
-
-/**
- * Get the numeric property value from a win_prop_t.
- */
-static inline long winprop_get_int(winprop_t prop, size_t index) {
- long tgt = 0;
-
- if (!prop.nitems || index >= prop.nitems) {
- return 0;
- }
-
- switch (prop.format) {
- case 8: tgt = *(prop.p8 + index); break;
- case 16: tgt = *(prop.p16 + index); break;
- case 32: tgt = *(prop.p32 + index); break;
- default: assert(0); break;
- }
-
- return tgt;
-}
-
-/**
- * Compare next word in a string with another string.
- */
-static inline int strcmp_wd(const char *needle, const char *src) {
- int ret = mstrncmp(needle, src);
- if (ret)
- return ret;
-
- char c = src[strlen(needle)];
- if (isalnum((unsigned char)c) || '_' == c)
- return 1;
- else
- return 0;
-}
-
-/**
- * Return whether a c2_ptr_t is empty.
- */
-static inline attr_unused bool c2_ptr_isempty(const c2_ptr_t p) {
- return !(p.isbranch ? (bool)p.b : (bool)p.l);
-}
-
-/**
- * Reset a c2_ptr_t.
- */
-static inline void c2_ptr_reset(c2_ptr_t *pp) {
- if (pp)
- memcpy(pp, &C2_PTR_NULL, sizeof(c2_ptr_t));
-}
-
-/**
- * Combine two condition trees.
- */
-static inline c2_ptr_t c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
- c2_ptr_t p = {.isbranch = true, .b = NULL};
- p.b = cmalloc(c2_b_t);
-
- p.b->neg = false;
- p.b->op = op;
- p.b->opr1 = p1;
- p.b->opr2 = p2;
-
- return p;
-}
-
-/**
- * Get the precedence value of a condition branch operator.
- */
-static inline int c2h_b_opp(c2_b_op_t op) {
- switch (op) {
- case C2_B_OAND: return 2;
- case C2_B_OOR: return 1;
- case C2_B_OXOR: return 1;
- default: break;
- }
-
- assert(0);
- return 0;
-}
-
-/**
- * Compare precedence of two condition branch operators.
- *
- * Associativity is left-to-right, forever.
- *
- * @return positive number if op1 > op2, 0 if op1 == op2 in precedence,
- * negative number otherwise
- */
-static inline int c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) {
- return c2h_b_opp(op1) - c2h_b_opp(op2);
-}
-
-static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int level);
-
-static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult);
-
-static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult);
-
-static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult);
-
-static int c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult);
-
-static void c2_free(c2_ptr_t p);
-
-/**
- * Wrapper of c2_free().
- */
-static inline void c2_freep(c2_ptr_t *pp) {
- if (pp) {
- c2_free(*pp);
- c2_ptr_reset(pp);
- }
-}
-
-static const char *c2h_dump_str_tgt(const c2_l_t *pleaf);
-
-static const char *c2h_dump_str_type(const c2_l_t *pleaf);
-
-static void attr_unused c2_dump(c2_ptr_t p);
-
-static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf);
-
-static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_ptr_t cond);
-
-/**
- * Parse a condition string.
- */
-c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data) {
- if (!pattern)
- return NULL;
-
- // Parse the pattern
- c2_ptr_t result = C2_PTR_INIT;
- int offset = -1;
-
- if (strlen(pattern) >= 2 && ':' == pattern[1])
- offset = c2_parse_legacy(pattern, 0, &result);
- else
- offset = c2_parse_grp(pattern, 0, &result, 0);
-
- if (offset < 0) {
- c2_freep(&result);
- return NULL;
- }
-
- // Insert to pcondlst
- {
- static const c2_lptr_t lptr_def = C2_LPTR_INIT;
- auto plptr = cmalloc(c2_lptr_t);
- memcpy(plptr, &lptr_def, sizeof(c2_lptr_t));
- plptr->ptr = result;
- plptr->data = data;
- if (pcondlst) {
- plptr->next = *pcondlst;
- *pcondlst = plptr;
- }
-
-#ifdef DEBUG_C2
- log_trace("(\"%s\"): ", pattern);
- c2_dump(plptr->ptr);
- putchar('\n');
-#endif
-
- return plptr;
- }
-}
-
-#define c2_error(format, ...) \
- do { \
- log_error("Pattern \"%s\" pos %d: " format, pattern, offset, ##__VA_ARGS__); \
- goto fail; \
- } while (0)
-
-// TODO(yshui) Not a very good macro, should probably be a function
-#define C2H_SKIP_SPACES() \
- { \
- while (isspace((unsigned char)pattern[offset])) \
- ++offset; \
- }
-
-/**
- * Parse a group in condition string.
- *
- * @return offset of next character in string
- */
-static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int level) {
- // Check for recursion levels
- if (level > C2_MAX_LEVELS)
- c2_error("Exceeded maximum recursion levels.");
-
- if (!pattern)
- return -1;
-
- // Expected end character
- const char endchar = (offset ? ')' : '\0');
-
- // We use a system that a maximum of 2 elements are kept. When we find
- // the third element, we combine the elements according to operator
- // precedence. This design limits operators to have at most two-levels
- // of precedence and fixed left-to-right associativity.
-
- // For storing branch operators. ops[0] is actually unused
- c2_b_op_t ops[3] = {};
- // For storing elements
- c2_ptr_t eles[2] = {C2_PTR_INIT, C2_PTR_INIT};
- // Index of next free element slot in eles
- int elei = 0;
- // Pointer to the position of next element
- c2_ptr_t *pele = eles;
- // Negation flag of next operator
- bool neg = false;
- // Whether we are expecting an element immediately, is true at first, or
- // after encountering a logical operator
- bool next_expected = true;
-
- // Parse the pattern character-by-character
- for (; pattern[offset]; ++offset) {
- assert(elei <= 2);
-
- // Jump over spaces
- if (isspace((unsigned char)pattern[offset]))
- continue;
-
- // Handle end of group
- if (')' == pattern[offset])
- break;
-
- // Handle "!"
- if ('!' == pattern[offset]) {
- if (!next_expected)
- c2_error("Unexpected \"!\".");
-
- neg = !neg;
- continue;
- }
-
- // Handle AND and OR
- if ('&' == pattern[offset] || '|' == pattern[offset]) {
- if (next_expected)
- c2_error("Unexpected logical operator.");
-
- next_expected = true;
- if (!mstrncmp("&&", pattern + offset)) {
- ops[elei] = C2_B_OAND;
- ++offset;
- } else if (!mstrncmp("||", pattern + offset)) {
- ops[elei] = C2_B_OOR;
- ++offset;
- } else
- c2_error("Illegal logical operator.");
-
- continue;
- }
-
- // Parsing an element
- if (!next_expected)
- c2_error("Unexpected expression.");
-
- assert(!elei || ops[elei]);
-
- // If we are out of space
- if (2 == elei) {
- --elei;
- // If the first operator has higher or equal precedence, combine
- // the first two elements
- if (c2h_b_opcmp(ops[1], ops[2]) >= 0) {
- eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]);
- c2_ptr_reset(&eles[1]);
- pele = &eles[elei];
- ops[1] = ops[2];
- }
- // Otherwise, combine the second and the incoming one
- else {
- eles[1] = c2h_comb_tree(ops[2], eles[1], C2_PTR_NULL);
- assert(eles[1].isbranch);
- pele = &eles[1].b->opr2;
- }
- // The last operator always needs to be reset
- ops[2] = C2_B_OUNDEFINED;
- }
-
- // It's a subgroup if it starts with '('
- if ('(' == pattern[offset]) {
- if ((offset = c2_parse_grp(pattern, offset + 1, pele, level + 1)) < 0)
- goto fail;
- }
- // Otherwise it's a leaf
- else {
- if ((offset = c2_parse_target(pattern, offset, pele)) < 0)
- goto fail;
-
- assert(!pele->isbranch && !c2_ptr_isempty(*pele));
-
- if ((offset = c2_parse_op(pattern, offset, pele)) < 0)
- goto fail;
-
- if ((offset = c2_parse_pattern(pattern, offset, pele)) < 0)
- goto fail;
- }
- // Decrement offset -- we will increment it in loop update
- --offset;
-
- // Apply negation
- if (neg) {
- neg = false;
- if (pele->isbranch)
- pele->b->neg = !pele->b->neg;
- else
- pele->l->neg = !pele->l->neg;
- }
-
- next_expected = false;
- ++elei;
- pele = &eles[elei];
- }
-
- // Wrong end character?
- if (pattern[offset] && !endchar)
- c2_error("Expected end of string but found '%c'.", pattern[offset]);
- if (!pattern[offset] && endchar)
- c2_error("Expected '%c' but found end of string.", endchar);
-
- // Handle end of group
- if (!elei) {
- c2_error("Empty group.");
- } else if (next_expected) {
- c2_error("Missing rule before end of group.");
- } else if (elei > 1) {
- assert(2 == elei);
- assert(ops[1]);
- eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]);
- c2_ptr_reset(&eles[1]);
- }
-
- *presult = eles[0];
-
- if (')' == pattern[offset])
- ++offset;
-
- return offset;
-
-fail:
- c2_freep(&eles[0]);
- c2_freep(&eles[1]);
-
- return -1;
-}
-
-/**
- * Parse the target part of a rule.
- */
-static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) {
- // Initialize leaf
- presult->isbranch = false;
- presult->l = cmalloc(c2_l_t);
-
- c2_l_t *const pleaf = presult->l;
- memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
-
- // Parse negation marks
- while ('!' == pattern[offset]) {
- pleaf->neg = !pleaf->neg;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Copy target name out
- int tgtlen = 0;
- for (; pattern[offset] &&
- (isalnum((unsigned char)pattern[offset]) || '_' == pattern[offset]);
- ++offset) {
- ++tgtlen;
- }
- if (!tgtlen) {
- c2_error("Empty target.");
- }
- pleaf->tgt = strndup(&pattern[offset - tgtlen], (size_t)tgtlen);
-
- // Check for predefined targets
- static const int npredefs = (int)(sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]));
- for (int i = 0; i < npredefs; ++i) {
- if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) {
- pleaf->predef = i;
- pleaf->type = C2_PREDEFS[i].type;
- pleaf->format = C2_PREDEFS[i].format;
- break;
- }
- }
-
- C2H_SKIP_SPACES();
-
- // Parse target-on-frame flag
- if ('@' == pattern[offset]) {
- pleaf->tgt_onframe = true;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse index
- if ('[' == pattern[offset]) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- c2_error("Predefined targets can't have index.");
- }
-
- offset++;
-
- C2H_SKIP_SPACES();
-
- long index = -1;
- const char *endptr = NULL;
-
- if ('*' == pattern[offset]) {
- index = -1;
- endptr = pattern + offset + 1;
- } else {
- index = strtol(pattern + offset, (char **)&endptr, 0);
- if (index < 0) {
- c2_error("Index number invalid.");
- }
- }
-
- if (!endptr || pattern + offset == endptr) {
- c2_error("No index number found after bracket.");
- }
-
- pleaf->index = to_int_checked(index);
- offset = to_int_checked(endptr - pattern);
-
- C2H_SKIP_SPACES();
-
- if (pattern[offset] != ']') {
- c2_error("Index end marker not found.");
- }
-
- ++offset;
-
- C2H_SKIP_SPACES();
- }
-
- // Parse target type and format
- if (':' == pattern[offset]) {
- ++offset;
- C2H_SKIP_SPACES();
-
- // Look for format
- bool hasformat = false;
- long format = 0;
- {
- char *endptr = NULL;
- format = strtol(pattern + offset, &endptr, 0);
- assert(endptr);
- if ((hasformat = (endptr && endptr != pattern + offset))) {
- offset = to_int_checked(endptr - pattern);
- }
- C2H_SKIP_SPACES();
- }
-
- // Look for type
- enum c2_l_type type = C2_L_TUNDEFINED;
- switch (pattern[offset]) {
- case 'w': type = C2_L_TWINDOW; break;
- case 'd': type = C2_L_TDRAWABLE; break;
- case 'c': type = C2_L_TCARDINAL; break;
- case 's': type = C2_L_TSTRING; break;
- case 'a': type = C2_L_TATOM; break;
- default: c2_error("Invalid type character.");
- }
-
- if (type) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- log_warn("Type specified for a default target "
- "will be ignored.");
- } else {
- if (pleaf->type && type != pleaf->type) {
- log_warn("Default type overridden on "
- "target.");
- }
- pleaf->type = type;
- }
- }
-
- offset++;
- C2H_SKIP_SPACES();
-
- // Default format
- if (!pleaf->format) {
- switch (pleaf->type) {
- case C2_L_TWINDOW:
- case C2_L_TDRAWABLE:
- case C2_L_TATOM: pleaf->format = 32; break;
- case C2_L_TSTRING: pleaf->format = 8; break;
- default: break;
- }
- }
-
- // Write format
- if (hasformat) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- log_warn("Format \"%ld\" specified on a default target "
- "will be ignored.",
- format);
- } else if (pleaf->type == C2_L_TSTRING) {
- log_warn("Format \"%ld\" specified on a string target "
- "will be ignored.",
- format);
- } else {
- if (pleaf->format && pleaf->format != format) {
- log_warn("Default format %d overridden on "
- "target.",
- pleaf->format);
- }
- pleaf->format = to_int_checked(format);
- }
- }
- }
-
- if (!pleaf->type) {
- c2_error("Target type cannot be determined.");
- }
-
- // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type)
- // c2_error("Target format cannot be determined.");
-
- if (pleaf->format && 8 != pleaf->format && 16 != pleaf->format && 32 != pleaf->format) {
- c2_error("Invalid format.");
- }
-
- return offset;
-
-fail:
- return -1;
-}
-
-/**
- * Parse the operator part of a leaf.
- */
-static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) {
- c2_l_t *const pleaf = presult->l;
-
- // Parse negation marks
- C2H_SKIP_SPACES();
- while ('!' == pattern[offset]) {
- pleaf->neg = !pleaf->neg;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse qualifiers
- if ('*' == pattern[offset] || '^' == pattern[offset] || '%' == pattern[offset] ||
- '~' == pattern[offset]) {
- switch (pattern[offset]) {
- case '*': pleaf->match = C2_L_MCONTAINS; break;
- case '^': pleaf->match = C2_L_MSTART; break;
- case '%': pleaf->match = C2_L_MWILDCARD; break;
- case '~': pleaf->match = C2_L_MPCRE; break;
- default: assert(0);
- }
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse flags
- while ('?' == pattern[offset]) {
- pleaf->match_ignorecase = true;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Parse operator
- while ('=' == pattern[offset] || '>' == pattern[offset] || '<' == pattern[offset]) {
- if ('=' == pattern[offset] && C2_L_OGT == pleaf->op)
- pleaf->op = C2_L_OGTEQ;
- else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op)
- pleaf->op = C2_L_OLTEQ;
- else if (pleaf->op) {
- c2_error("Duplicate operator.");
- } else {
- switch (pattern[offset]) {
- case '=': pleaf->op = C2_L_OEQ; break;
- case '>': pleaf->op = C2_L_OGT; break;
- case '<': pleaf->op = C2_L_OLT; break;
- default: assert(0);
- }
- }
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Check for problems
- if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase))
- c2_error("Exists/greater-than/less-than operators cannot have a "
- "qualifier.");
-
- return offset;
-
-fail:
- return -1;
-}
-
-/**
- * Parse the pattern part of a leaf.
- */
-static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult) {
- c2_l_t *const pleaf = presult->l;
-
- // Exists operator cannot have pattern
- if (!pleaf->op) {
- return offset;
- }
-
- C2H_SKIP_SPACES();
-
- char *endptr = NULL;
- if (!strcmp_wd("true", &pattern[offset])) {
- pleaf->ptntype = C2_L_PTINT;
- pleaf->ptnint = true;
- offset += 4; // length of "true";
- } else if (!strcmp_wd("false", &pattern[offset])) {
- pleaf->ptntype = C2_L_PTINT;
- pleaf->ptnint = false;
- offset += 5; // length of "false";
- } else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0),
- pattern + offset != endptr) {
- pleaf->ptntype = C2_L_PTINT;
- offset = to_int_checked(endptr - pattern);
- // Make sure we are stopping at the end of a word
- if (isalnum((unsigned char)pattern[offset])) {
- c2_error("Trailing characters after a numeric pattern.");
- }
- } else {
- // Parse string patterns
- bool raw = false;
- char delim = '\0';
-
- // String flags
- if (tolower((unsigned char)pattern[offset]) == 'r') {
- raw = true;
- ++offset;
- C2H_SKIP_SPACES();
- }
-
- // Check for delimiters
- if (pattern[offset] == '\"' || pattern[offset] == '\'') {
- pleaf->ptntype = C2_L_PTSTRING;
- delim = pattern[offset];
- ++offset;
- }
-
- if (pleaf->ptntype != C2_L_PTSTRING) {
- c2_error("Invalid pattern type.");
- }
-
- // Parse the string now
- // We can't determine the length of the pattern, so we use the length
- // to the end of the pattern string -- currently escape sequences
- // cannot be converted to a string longer than itself.
- auto tptnstr = ccalloc((strlen(pattern + offset) + 1), char);
- char *ptptnstr = tptnstr;
- pleaf->ptnstr = tptnstr;
- for (; pattern[offset] && delim != pattern[offset]; ++offset) {
- // Handle escape sequences if it's not a raw string
- if ('\\' == pattern[offset] && !raw) {
- switch (pattern[++offset]) {
- case '\\': *(ptptnstr++) = '\\'; break;
- case '\'': *(ptptnstr++) = '\''; break;
- case '\"': *(ptptnstr++) = '\"'; break;
- case 'a': *(ptptnstr++) = '\a'; break;
- case 'b': *(ptptnstr++) = '\b'; break;
- case 'f': *(ptptnstr++) = '\f'; break;
- case 'n': *(ptptnstr++) = '\n'; break;
- case 'r': *(ptptnstr++) = '\r'; break;
- case 't': *(ptptnstr++) = '\t'; break;
- case 'v': *(ptptnstr++) = '\v'; break;
- case 'o':
- case 'x': {
- char *tstr = strndup(pattern + offset + 1, 2);
- char *pstr = NULL;
- long val = strtol(
- tstr, &pstr, ('o' == pattern[offset] ? 8 : 16));
- free(tstr);
- if (pstr != &tstr[2] || val <= 0)
- c2_error("Invalid octal/hex escape "
- "sequence.");
- *(ptptnstr++) = to_char_checked(val);
- offset += 2;
- break;
- }
- default: c2_error("Invalid escape sequence.");
- }
- } else {
- *(ptptnstr++) = pattern[offset];
- }
- }
- if (!pattern[offset])
- c2_error("Premature end of pattern string.");
- ++offset;
- *ptptnstr = '\0';
- pleaf->ptnstr = strdup(tptnstr);
- free(tptnstr);
- }
-
- C2H_SKIP_SPACES();
-
- if (!pleaf->ptntype)
- c2_error("Invalid pattern type.");
-
- // Check if the type is correct
- if (!(((C2_L_TSTRING == pleaf->type || C2_L_TATOM == pleaf->type) &&
- C2_L_PTSTRING == pleaf->ptntype) ||
- ((C2_L_TCARDINAL == pleaf->type || C2_L_TWINDOW == pleaf->type ||
- C2_L_TDRAWABLE == pleaf->type) &&
- C2_L_PTINT == pleaf->ptntype)))
- c2_error("Pattern type incompatible with target type.");
-
- if (C2_L_PTINT == pleaf->ptntype && pleaf->match)
- c2_error("Integer/boolean pattern cannot have operator qualifiers.");
-
- if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase)
- c2_error("Integer/boolean pattern cannot have flags.");
-
- if (C2_L_PTSTRING == pleaf->ptntype &&
- (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op || C2_L_OLT == pleaf->op ||
- C2_L_OLTEQ == pleaf->op))
- c2_error("String pattern cannot have an arithmetic operator.");
-
- return offset;
-
-fail:
- return -1;
-}
-
-/**
- * Parse a condition with legacy syntax.
- */
-static int c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult) {
- if (strlen(pattern + offset) < 4 || pattern[offset + 1] != ':' ||
- !strchr(pattern + offset + 2, ':')) {
- c2_error("Legacy parser: Invalid format.");
- }
-
- // Allocate memory for new leaf
- auto pleaf = cmalloc(c2_l_t);
- presult->isbranch = false;
- presult->l = pleaf;
- memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
- pleaf->type = C2_L_TSTRING;
- pleaf->op = C2_L_OEQ;
- pleaf->ptntype = C2_L_PTSTRING;
-
- // Determine the pattern target
-#define TGTFILL(pdefid) \
- (pleaf->predef = pdefid, pleaf->type = C2_PREDEFS[pdefid].type, \
- pleaf->format = C2_PREDEFS[pdefid].format)
- switch (pattern[offset]) {
- case 'n': TGTFILL(C2_L_PNAME); break;
- case 'i': TGTFILL(C2_L_PCLASSI); break;
- case 'g': TGTFILL(C2_L_PCLASSG); break;
- case 'r': TGTFILL(C2_L_PROLE); break;
- default: c2_error("Target \"%c\" invalid.\n", pattern[offset]);
- }
-#undef TGTFILL
-
- offset += 2;
-
- // Determine the match type
- switch (pattern[offset]) {
- case 'e': pleaf->match = C2_L_MEXACT; break;
- case 'a': pleaf->match = C2_L_MCONTAINS; break;
- case 's': pleaf->match = C2_L_MSTART; break;
- case 'w': pleaf->match = C2_L_MWILDCARD; break;
- case 'p': pleaf->match = C2_L_MPCRE; break;
- default: c2_error("Type \"%c\" invalid.\n", pattern[offset]);
- }
- ++offset;
-
- // Determine the pattern flags
- while (':' != pattern[offset]) {
- switch (pattern[offset]) {
- case 'i': pleaf->match_ignorecase = true; break;
- default: c2_error("Flag \"%c\" invalid.", pattern[offset]);
- }
- ++offset;
- }
- ++offset;
-
- // Copy the pattern
- pleaf->ptnstr = strdup(pattern + offset);
-
- return offset;
-
-fail:
- return -1;
-}
-
-#undef c2_error
-
-/**
- * Do postprocessing on a condition leaf.
- */
-static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
- // Give a pattern type to a leaf with exists operator, if needed
- if (C2_L_OEXISTS == pleaf->op && !pleaf->ptntype) {
- pleaf->ptntype = (C2_L_TSTRING == pleaf->type ? C2_L_PTSTRING : C2_L_PTINT);
- }
-
- // Get target atom if it's not a predefined one
- if (pleaf->predef == C2_L_PUNDEFINED) {
- pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt);
- if (!pleaf->tgtatom) {
- log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
- return false;
- }
- }
-
- // Insert target Atom into atom track list
- if (pleaf->tgtatom) {
- bool found = false;
- for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
- if (pleaf->tgtatom == platom->atom) {
- found = true;
- break;
- }
- }
- if (!found) {
- auto pnew = cmalloc(latom_t);
- pnew->next = ps->track_atom_lst;
- pnew->atom = pleaf->tgtatom;
- ps->track_atom_lst = pnew;
- }
- }
-
- // Warn about lower case characters in target name
- if (pleaf->predef == C2_L_PUNDEFINED) {
- for (const char *pc = pleaf->tgt; *pc; ++pc) {
- if (islower((unsigned char)*pc)) {
- log_warn("Lowercase character in target name \"%s\".",
- pleaf->tgt);
- break;
- }
- }
- }
-
- // PCRE patterns
- if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) {
-#ifdef CONFIG_REGEX_PCRE
- const char *error = NULL;
- int erroffset = 0;
- int options = 0;
-
- // Ignore case flag
- if (pleaf->match_ignorecase)
- options |= PCRE_CASELESS;
-
- // Compile PCRE expression
- pleaf->regex_pcre =
- pcre_compile(pleaf->ptnstr, options, &error, &erroffset, NULL);
- if (!pleaf->regex_pcre) {
- log_error("Pattern \"%s\": PCRE regular expression parsing "
- "failed on "
- "offset %d: %s",
- pleaf->ptnstr, erroffset, error);
- return false;
- }
-#ifdef CONFIG_REGEX_PCRE_JIT
- pleaf->regex_pcre_extra =
- pcre_study(pleaf->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error);
- if (!pleaf->regex_pcre_extra) {
- printf("Pattern \"%s\": PCRE regular expression study failed: %s",
- pleaf->ptnstr, error);
- }
-#endif
-
- // Free the target string
- // free(pleaf->tgt);
- // pleaf->tgt = NULL;
-#else
- log_error("PCRE regular expression support not compiled in.");
- return false;
-#endif
- }
-
- return true;
-}
-
-static bool c2_tree_postprocess(session_t *ps, c2_ptr_t node) {
- if (!node.isbranch) {
- return c2_l_postprocess(ps, node.l);
- }
- if (!c2_tree_postprocess(ps, node.b->opr1))
- return false;
- return c2_tree_postprocess(ps, node.b->opr2);
-}
-
-bool c2_list_postprocess(session_t *ps, c2_lptr_t *list) {
- c2_lptr_t *head = list;
- while (head) {
- if (!c2_tree_postprocess(ps, head->ptr))
- return false;
- head = head->next;
- }
- return true;
-}
-/**
- * Free a condition tree.
- */
-static void c2_free(c2_ptr_t p) {
- // For a branch element
- if (p.isbranch) {
- c2_b_t *const pbranch = p.b;
-
- if (!pbranch)
- return;
-
- c2_free(pbranch->opr1);
- c2_free(pbranch->opr2);
- free(pbranch);
- }
- // For a leaf element
- else {
- c2_l_t *const pleaf = p.l;
-
- if (!pleaf)
- return;
-
- free(pleaf->tgt);
- free(pleaf->ptnstr);
-#ifdef CONFIG_REGEX_PCRE
- pcre_free(pleaf->regex_pcre);
- LPCRE_FREE_STUDY(pleaf->regex_pcre_extra);
-#endif
- free(pleaf);
- }
-}
-
-/**
- * Free a condition tree in c2_lptr_t.
- */
-c2_lptr_t *c2_free_lptr(c2_lptr_t *lp) {
- if (!lp)
- return NULL;
-
- c2_lptr_t *pnext = lp->next;
- c2_free(lp->ptr);
- free(lp);
-
- return pnext;
-}
-
-/**
- * Get a string representation of a rule target.
- */
-static const char *c2h_dump_str_tgt(const c2_l_t *pleaf) {
- if (pleaf->predef != C2_L_PUNDEFINED) {
- return C2_PREDEFS[pleaf->predef].name;
- } else {
- return pleaf->tgt;
- }
-}
-
-/**
- * Get a string representation of a target.
- */
-static const char *c2h_dump_str_type(const c2_l_t *pleaf) {
- switch (pleaf->type) {
- case C2_L_TWINDOW: return "w";
- case C2_L_TDRAWABLE: return "d";
- case C2_L_TCARDINAL: return "c";
- case C2_L_TSTRING: return "s";
- case C2_L_TATOM: return "a";
- case C2_L_TUNDEFINED: break;
- }
-
- return NULL;
-}
-
-/**
- * Dump a condition tree.
- */
-static void c2_dump(c2_ptr_t p) {
- // For a branch
- if (p.isbranch) {
- const c2_b_t *const pbranch = p.b;
-
- if (!pbranch) {
- return;
- }
-
- if (pbranch->neg) {
- putchar('!');
- }
-
- printf("(");
- c2_dump(pbranch->opr1);
-
- switch (pbranch->op) {
- case C2_B_OAND: printf(" && "); break;
- case C2_B_OOR: printf(" || "); break;
- case C2_B_OXOR: printf(" XOR "); break;
- default: assert(0); break;
- }
-
- c2_dump(pbranch->opr2);
- printf(") ");
- }
- // For a leaf
- else {
- const c2_l_t *const pleaf = p.l;
-
- if (!pleaf) {
- return;
- }
-
- if (C2_L_OEXISTS == pleaf->op && pleaf->neg) {
- putchar('!');
- }
-
- // Print target name, type, and format
- {
- printf("%s", c2h_dump_str_tgt(pleaf));
- if (pleaf->tgt_onframe) {
- putchar('@');
- }
- if (pleaf->predef == C2_L_PUNDEFINED) {
- if (pleaf->index < 0) {
- printf("[*]");
- } else {
- printf("[%d]", pleaf->index);
- }
- }
- printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf));
- }
-
- // Print operator
- putchar(' ');
-
- if (C2_L_OEXISTS != pleaf->op && pleaf->neg) {
- putchar('!');
- }
-
- switch (pleaf->match) {
- case C2_L_MEXACT: break;
- case C2_L_MCONTAINS: putchar('*'); break;
- case C2_L_MSTART: putchar('^'); break;
- case C2_L_MPCRE: putchar('~'); break;
- case C2_L_MWILDCARD: putchar('%'); break;
- }
-
- if (pleaf->match_ignorecase) {
- putchar('?');
- }
-
- switch (pleaf->op) {
- case C2_L_OEXISTS: break;
- case C2_L_OEQ: fputs("=", stdout); break;
- case C2_L_OGT: fputs(">", stdout); break;
- case C2_L_OGTEQ: fputs(">=", stdout); break;
- case C2_L_OLT: fputs("<", stdout); break;
- case C2_L_OLTEQ: fputs("<=", stdout); break;
- }
-
- if (C2_L_OEXISTS == pleaf->op) {
- return;
- }
-
- // Print pattern
- putchar(' ');
- switch (pleaf->ptntype) {
- case C2_L_PTINT: printf("%ld", pleaf->ptnint); break;
- case C2_L_PTSTRING:
- // TODO(yshui) Escape string before printing out?
- printf("\"%s\"", pleaf->ptnstr);
- break;
- default: assert(0); break;
- }
- }
-}
-
-/**
- * Get the type atom of a condition.
- */
-static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf) {
- switch (pleaf->type) {
- case C2_L_TCARDINAL: return XCB_ATOM_CARDINAL;
- case C2_L_TWINDOW: return XCB_ATOM_WINDOW;
- case C2_L_TSTRING: return XCB_ATOM_STRING;
- case C2_L_TATOM: return XCB_ATOM_ATOM;
- case C2_L_TDRAWABLE: return XCB_ATOM_DRAWABLE;
- default: assert(0); break;
- }
- unreachable;
-}
-
-/**
- * Match a window against a single leaf window condition.
- *
- * For internal use.
- */
-static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w,
- const c2_l_t *pleaf, bool *pres, bool *perr) {
- assert(pleaf);
-
- const xcb_window_t wid = (pleaf->tgt_onframe ? w->client_win : w->base.id);
-
- // Return if wid is missing
- if (pleaf->predef == C2_L_PUNDEFINED && !wid) {
- return;
- }
-
- const int idx = (pleaf->index < 0 ? 0 : pleaf->index);
-
- switch (pleaf->ptntype) {
- // Deal with integer patterns
- case C2_L_PTINT: {
- long *targets = NULL;
- long *targets_free = NULL;
- size_t ntargets = 0;
-
- // Get the value
- // A predefined target
- long predef_target = 0;
- if (pleaf->predef != C2_L_PUNDEFINED) {
- *perr = false;
- switch (pleaf->predef) {
- case C2_L_PID: predef_target = wid; break;
- case C2_L_PX: predef_target = w->g.x; break;
- case C2_L_PY: predef_target = w->g.y; break;
- case C2_L_PX2: predef_target = w->g.x + w->widthb; break;
- case C2_L_PY2: predef_target = w->g.y + w->heightb; break;
- case C2_L_PWIDTH: predef_target = w->g.width; break;
- case C2_L_PHEIGHT: predef_target = w->g.height; break;
- case C2_L_PWIDTHB: predef_target = w->widthb; break;
- case C2_L_PHEIGHTB: predef_target = w->heightb; break;
- case C2_L_PBDW: predef_target = w->g.border_width; break;
- case C2_L_PFULLSCREEN:
- predef_target = win_is_fullscreen(ps, w);
- break;
- case C2_L_POVREDIR: predef_target = w->a.override_redirect; break;
- case C2_L_PARGB: predef_target = win_has_alpha(w); break;
- case C2_L_PFOCUSED:
- predef_target = win_is_focused_raw(ps, w);
- break;
- case C2_L_PWMWIN: predef_target = w->wmwin; break;
- case C2_L_PBSHAPED: predef_target = w->bounding_shaped; break;
- case C2_L_PROUNDED: predef_target = w->rounded_corners; break;
- case C2_L_PCLIENT: predef_target = w->client_win; break;
- case C2_L_PLEADER: predef_target = w->leader; break;
- default:
- *perr = true;
- assert(0);
- break;
- }
- ntargets = 1;
- targets = &predef_target;
- }
- // A raw window property
- else {
- int word_count = 1;
- if (pleaf->index < 0) {
- // Get length of property in 32-bit multiples
- auto prop_info = x_get_prop_info(ps->c, wid, pleaf->tgtatom);
- word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
- }
- winprop_t prop = x_get_prop_with_offset(
- ps->c, wid, pleaf->tgtatom, idx, word_count,
- c2_get_atom_type(pleaf), pleaf->format);
-
- ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
- if (ntargets > 0) {
- targets = targets_free = ccalloc(ntargets, long);
- *perr = false;
- for (size_t i = 0; i < ntargets; ++i) {
- targets[i] = winprop_get_int(prop, i);
- }
- }
- free_winprop(&prop);
- }
-
- if (*perr) {
- goto fail_int;
- }
-
- // Do comparison
- bool res = false;
- for (size_t i = 0; i < ntargets; ++i) {
- long tgt = targets[i];
- switch (pleaf->op) {
- case C2_L_OEXISTS:
- res = (pleaf->predef != C2_L_PUNDEFINED ? tgt : true);
- break;
- case C2_L_OEQ: res = (tgt == pleaf->ptnint); break;
- case C2_L_OGT: res = (tgt > pleaf->ptnint); break;
- case C2_L_OGTEQ: res = (tgt >= pleaf->ptnint); break;
- case C2_L_OLT: res = (tgt < pleaf->ptnint); break;
- case C2_L_OLTEQ: res = (tgt <= pleaf->ptnint); break;
- default: *perr = true; assert(0);
- }
- if (res) {
- break;
- }
- }
- *pres = res;
-
- fail_int:
- // Free property values after usage, if necessary
- if (targets_free) {
- free(targets_free);
- }
- } break;
- // String patterns
- case C2_L_PTSTRING: {
- const char **targets = NULL;
- const char **targets_free = NULL;
- const char **targets_free_inner = NULL;
- size_t ntargets = 0;
-
- // A predefined target
- const char *predef_target = NULL;
- if (pleaf->predef != C2_L_PUNDEFINED) {
- switch (pleaf->predef) {
- case C2_L_PWINDOWTYPE:
- predef_target = WINTYPES[w->window_type];
- break;
- case C2_L_PNAME: predef_target = w->name; break;
- case C2_L_PCLASSG: predef_target = w->class_general; break;
- case C2_L_PCLASSI: predef_target = w->class_instance; break;
- case C2_L_PROLE: predef_target = w->role; break;
- default: assert(0); break;
- }
- ntargets = 1;
- targets = &predef_target;
- }
- // An atom type property, convert it to string
- else if (pleaf->type == C2_L_TATOM) {
- int word_count = 1;
- if (pleaf->index < 0) {
- // Get length of property in 32-bit multiples
- auto prop_info = x_get_prop_info(ps->c, wid, pleaf->tgtatom);
- word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
- }
- winprop_t prop = x_get_prop_with_offset(
- ps->c, wid, pleaf->tgtatom, idx, word_count,
- c2_get_atom_type(pleaf), pleaf->format);
-
- ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
- targets = targets_free = (const char **)ccalloc(2 * ntargets, char *);
- targets_free_inner = targets + ntargets;
-
- for (size_t i = 0; i < ntargets; ++i) {
- xcb_atom_t atom = (xcb_atom_t)winprop_get_int(prop, i);
- if (atom) {
- xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(
- ps->c, xcb_get_atom_name(ps->c, atom), NULL);
- if (reply) {
- targets[i] = targets_free_inner[i] = strndup(
- xcb_get_atom_name_name(reply),
- (size_t)xcb_get_atom_name_name_length(reply));
- free(reply);
- }
- }
- }
- free_winprop(&prop);
- }
- // Not an atom type, just fetch the string list
- else {
- char **strlst = NULL;
- int nstr = 0;
- if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, &nstr)) {
- if (pleaf->index < 0 && nstr > 0 && strlen(strlst[0]) > 0) {
- ntargets = to_u32_checked(nstr);
- targets = (const char **)strlst;
- } else if (nstr > idx) {
- ntargets = 1;
- targets = (const char **)strlst + idx;
- }
- }
- if (strlst) {
- targets_free = (const char **)strlst;
- }
- }
-
- if (ntargets == 0) {
- goto fail_str;
- }
- for (size_t i = 0; i < ntargets; ++i) {
- if (!targets[i]) {
- goto fail_str;
- }
- }
- *perr = false;
-
- // Actual matching
- bool res = false;
- for (size_t i = 0; i < ntargets; ++i) {
- const char *tgt = targets[i];
- switch (pleaf->op) {
- case C2_L_OEXISTS: res = true; break;
- case C2_L_OEQ:
- switch (pleaf->match) {
- case C2_L_MEXACT:
- if (pleaf->match_ignorecase) {
- res = !strcasecmp(tgt, pleaf->ptnstr);
- } else {
- res = !strcmp(tgt, pleaf->ptnstr);
- }
- break;
- case C2_L_MCONTAINS:
- if (pleaf->match_ignorecase) {
- res = strcasestr(tgt, pleaf->ptnstr);
- } else {
- res = strstr(tgt, pleaf->ptnstr);
- }
- break;
- case C2_L_MSTART:
- if (pleaf->match_ignorecase) {
- res = !strncasecmp(tgt, pleaf->ptnstr,
- strlen(pleaf->ptnstr));
- } else {
- res = !strncmp(tgt, pleaf->ptnstr,
- strlen(pleaf->ptnstr));
- }
- break;
- case C2_L_MWILDCARD: {
- int flags = 0;
- if (pleaf->match_ignorecase) {
- flags |= FNM_CASEFOLD;
- }
- res = !fnmatch(pleaf->ptnstr, tgt, flags);
- } break;
- case C2_L_MPCRE:
-#ifdef CONFIG_REGEX_PCRE
- assert(strlen(tgt) <= INT_MAX);
- res = (pcre_exec(pleaf->regex_pcre,
- pleaf->regex_pcre_extra, tgt,
- (int)strlen(tgt), 0, 0, NULL, 0) >= 0);
-#else
- assert(0);
-#endif
- break;
- }
- break;
- default: *perr = true; assert(0);
- }
- if (res) {
- break;
- }
- }
- *pres = res;
-
- fail_str:
- // Free the string after usage, if necessary
- if (targets_free_inner) {
- for (size_t i = 0; i < ntargets; ++i) {
- if (targets_free_inner[i]) {
- free((void *)targets_free_inner[i]);
- }
- }
- }
- // Free property values after usage, if necessary
- if (targets_free) {
- free(targets_free);
- }
- } break;
- default: assert(0); break;
- }
-}
-
-/**
- * Match a window against a single window condition.
- *
- * @return true if matched, false otherwise.
- */
-static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_ptr_t cond) {
- bool result = false;
- bool error = true;
-
- // Handle a branch
- if (cond.isbranch) {
- const c2_b_t *pb = cond.b;
-
- if (!pb)
- return false;
-
- error = false;
-
- switch (pb->op) {
- case C2_B_OAND:
- result = (c2_match_once(ps, w, pb->opr1) &&
- c2_match_once(ps, w, pb->opr2));
- break;
- case C2_B_OOR:
- result = (c2_match_once(ps, w, pb->opr1) ||
- c2_match_once(ps, w, pb->opr2));
- break;
- case C2_B_OXOR:
- result = (c2_match_once(ps, w, pb->opr1) !=
- c2_match_once(ps, w, pb->opr2));
- break;
- default: error = true; assert(0);
- }
-
-#ifdef DEBUG_WINMATCH
- log_trace("(%#010x): branch: result = %d, pattern = ", w->base.id, result);
- c2_dump(cond);
- putchar('\n');
-#endif
- }
- // Handle a leaf
- else {
- const c2_l_t *pleaf = cond.l;
-
- if (!pleaf)
- return false;
-
- c2_match_once_leaf(ps, w, pleaf, &result, &error);
-
- // For EXISTS operator, no errors are fatal
- if (C2_L_OEXISTS == pleaf->op && error) {
- result = false;
- error = false;
- }
-
-#ifdef DEBUG_WINMATCH
- log_trace("(%#010x): leaf: result = %d, error = %d, "
- "client = %#010x, pattern = ",
- w->base.id, result, error, w->client_win);
- c2_dump(cond);
- putchar('\n');
-#endif
- }
-
- // Postprocess the result
- if (error)
- result = false;
-
- if (cond.isbranch ? cond.b->neg : cond.l->neg)
- result = !result;
-
- return result;
-}
-
-/**
- * Match a window against a condition linked list.
- *
- * @param cache a place to cache the last matched condition
- * @param pdata a place to return the data
- * @return true if matched, false otherwise.
- */
-bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condlst,
- void **pdata) {
- assert(ps->server_grabbed);
- // Then go through the whole linked list
- for (; condlst; condlst = condlst->next) {
- if (c2_match_once(ps, w, condlst->ptr)) {
- if (pdata)
- *pdata = condlst->data;
- return true;
- }
- }
-
- return false;
-}