summaryrefslogtreecommitdiffstats
path: root/c2.h
blob: 000c68448412aa7e6b68cfdfcc920c113c38f554 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*
 * Compton - a compositor for X11
 *
 * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
 *
 * Copyright (c) 2011-2013, Christopher Jeffrey
 * See LICENSE for more information.
 *
 */

#include "common.h"

#include <fnmatch.h>
#include <ctype.h>

// libpcre
#ifdef CONFIG_REGEX_PCRE
#include <pcre.h>

// For compatiblity 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

#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, \
}

const static 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;
  Atom tgtatom;
  bool tgt_onframe;
  int index;
  enum {
    C2_L_PUNDEFINED,
    C2_L_PID,
    C2_L_POVREDIR,
    C2_L_PFOCUSED,
    C2_L_PWMWIN,
    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 = -1, \
  .type = C2_L_TUNDEFINED, \
  .format = 0, \
  .ptntype = C2_L_PTUNDEFINED, \
  .ptnstr = NULL, \
  .ptnint = 0, \
}

const static c2_l_t leaf_def = C2_L_INIT;

/// Linked list type of conditions.
struct _c2_lptr {
  c2_ptr_t ptr;
  struct _c2_lptr *next;
};

/// Initializer for c2_lptr_t.
#define C2_LPTR_INIT { \
  .ptr = C2_PTR_INIT, \
  .next = NULL, \
}

/// Structure representing a predefined target.
typedef struct {
  const char *name;
  enum c2_l_type type;
  int format;
} c2_predef_t;

// Predefined targets.
const static c2_predef_t C2_PREDEFS[] = {
  [C2_L_PID         ] = { "id"                , C2_L_TCARDINAL  , 0  },
  [C2_L_POVREDIR    ] = { "override_redirect" , C2_L_TCARDINAL  , 0  },
  [C2_L_PFOCUSED    ] = { "focused"           , C2_L_TCARDINAL  , 0  },
  [C2_L_PWMWIN      ] = { "wmwin"             , 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  },
};

#define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))

/**
 * 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(c) || '_' == c)
    return 1;
  else
    return 0;
}

/**
 * Return whether a c2_ptr_t is empty.
 */
static inline 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 = malloc(sizeof(c2_b_t))
 };

 p.b->opr1 = p1;
 p.b->opr2 = p2;
 p.b->op = op;

 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(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level);

static int
c2_parse_target(session_t *ps, 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(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);

static int
c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);

static bool
c2_l_postprocess(session_t *ps, c2_l_t *pleaf);

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
c2_dump_raw(c2_ptr_t p);

/**
 * Wrapper of c2_dump_raw().
 */
static inline void
c2_dump(c2_ptr_t p) {
  c2_dump_raw(p);
  printf("\n");
  fflush(stdout);
}

static Atom
c2_get_atom_type(const c2_l_t *pleaf);

static bool
c2_match_once(session_t *ps, win *w, const c2_ptr_t cond);