1 /*
2  * Compton - a compositor for X11
3  *
4  * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
5  *
6  * Copyright (c) 2011-2013, Christopher Jeffrey
7  * See LICENSE for more information.
8  *
9  */
10 
11 #include "common.h"
12 
13 #include <fnmatch.h>
14 #include <ctype.h>
15 
16 // libpcre
17 #ifdef CONFIG_REGEX_PCRE
18 #include <pcre.h>
19 
20 // For compatiblity with <libpcre-8.20
21 #ifndef PCRE_STUDY_JIT_COMPILE
22 #define PCRE_STUDY_JIT_COMPILE    0
23 #define LPCRE_FREE_STUDY(extra)   pcre_free(extra)
24 #else
25 #define LPCRE_FREE_STUDY(extra)   pcre_free_study(extra)
26 #endif
27 
28 #endif
29 
30 #define C2_MAX_LEVELS 10
31 
32 typedef struct _c2_b c2_b_t;
33 typedef struct _c2_l c2_l_t;
34 
35 /// Pointer to a condition tree.
36 typedef struct {
37   bool isbranch : 1;
38   c2_b_t *b;
39   c2_l_t *l;
40 } c2_ptr_t;
41 
42 /// Initializer for c2_ptr_t.
43 #define C2_PTR_INIT { \
44   .isbranch = false, \
45   .l = NULL, \
46 }
47 
48 const static c2_ptr_t C2_PTR_NULL = C2_PTR_INIT;
49 
50 /// Operator of a branch element.
51 typedef enum {
52   C2_B_OUNDEFINED,
53   C2_B_OAND,
54   C2_B_OOR,
55   C2_B_OXOR,
56 } c2_b_op_t;
57 
58 /// Structure for branch element in a window condition
59 struct _c2_b {
60   bool neg      : 1;
61   c2_b_op_t op;
62   c2_ptr_t opr1;
63   c2_ptr_t opr2;
64 };
65 
66 /// Initializer for c2_b_t.
67 #define C2_B_INIT { \
68   .neg = false, \
69   .op = C2_B_OUNDEFINED, \
70   .opr1 = C2_PTR_INIT, \
71   .opr2 = C2_PTR_INIT, \
72 }
73 
74 /// Structure for leaf element in a window condition
75 struct _c2_l {
76   bool neg    : 1;
77   enum {
78     C2_L_OEXISTS,
79     C2_L_OEQ,
80     C2_L_OGT,
81     C2_L_OGTEQ,
82     C2_L_OLT,
83     C2_L_OLTEQ,
84   } op        : 3;
85   enum {
86     C2_L_MEXACT,
87     C2_L_MSTART,
88     C2_L_MCONTAINS,
89     C2_L_MWILDCARD,
90     C2_L_MPCRE,
91   } match     : 3;
92   bool match_ignorecase : 1;
93   char *tgt;
94   Atom tgtatom;
95   bool tgt_onframe;
96   int index;
97   enum {
98     C2_L_PUNDEFINED,
99     C2_L_PID,
100     C2_L_PX,
101     C2_L_PY,
102     C2_L_PX2,
103     C2_L_PY2,
104     C2_L_PWIDTH,
105     C2_L_PHEIGHT,
106     C2_L_PWIDTHB,
107     C2_L_PHEIGHTB,
108     C2_L_PBDW,
109     C2_L_PFULLSCREEN,
110     C2_L_POVREDIR,
111     C2_L_PARGB,
112     C2_L_PFOCUSED,
113     C2_L_PWMWIN,
114     C2_L_PBSHAPED,
115     C2_L_PROUNDED,
116     C2_L_PCLIENT,
117     C2_L_PWINDOWTYPE,
118     C2_L_PLEADER,
119     C2_L_PNAME,
120     C2_L_PCLASSG,
121     C2_L_PCLASSI,
122     C2_L_PROLE,
123   } predef;
124   enum c2_l_type {
125     C2_L_TUNDEFINED,
126     C2_L_TSTRING,
127     C2_L_TCARDINAL,
128     C2_L_TWINDOW,
129     C2_L_TATOM,
130     C2_L_TDRAWABLE,
131   } type;
132   int format;
133   enum {
134     C2_L_PTUNDEFINED,
135     C2_L_PTSTRING,
136     C2_L_PTINT,
137   } ptntype;
138   char *ptnstr;
139   long ptnint;
140 #ifdef CONFIG_REGEX_PCRE
141   pcre *regex_pcre;
142   pcre_extra *regex_pcre_extra;
143 #endif
144 };
145 
146 /// Initializer for c2_l_t.
147 #define C2_L_INIT { \
148   .neg = false, \
149   .op = C2_L_OEXISTS, \
150   .match = C2_L_MEXACT, \
151   .match_ignorecase = false, \
152   .tgt = NULL, \
153   .tgtatom = 0, \
154   .tgt_onframe = false, \
155   .predef = C2_L_PUNDEFINED, \
156   .index = -1, \
157   .type = C2_L_TUNDEFINED, \
158   .format = 0, \
159   .ptntype = C2_L_PTUNDEFINED, \
160   .ptnstr = NULL, \
161   .ptnint = 0, \
162 }
163 
164 const static c2_l_t leaf_def = C2_L_INIT;
165 
166 /// Linked list type of conditions.
167 struct _c2_lptr {
168   c2_ptr_t ptr;
169   void *data;
170   struct _c2_lptr *next;
171 };
172 
173 /// Initializer for c2_lptr_t.
174 #define C2_LPTR_INIT { \
175   .ptr = C2_PTR_INIT, \
176   .data = NULL, \
177   .next = NULL, \
178 }
179 
180 /// Structure representing a predefined target.
181 typedef struct {
182   const char *name;
183   enum c2_l_type type;
184   int format;
185 } c2_predef_t;
186 
187 // Predefined targets.
188 const static c2_predef_t C2_PREDEFS[] = {
189   [C2_L_PID         ] = { "id"                , C2_L_TCARDINAL  , 0  },
190   [C2_L_PX          ] = { "x"                 , C2_L_TCARDINAL  , 0  },
191   [C2_L_PY          ] = { "y"                 , C2_L_TCARDINAL  , 0  },
192   [C2_L_PX2         ] = { "x2"                , C2_L_TCARDINAL  , 0  },
193   [C2_L_PY2         ] = { "y2"                , C2_L_TCARDINAL  , 0  },
194   [C2_L_PWIDTH      ] = { "width"             , C2_L_TCARDINAL  , 0  },
195   [C2_L_PHEIGHT     ] = { "height"            , C2_L_TCARDINAL  , 0  },
196   [C2_L_PWIDTHB     ] = { "widthb"            , C2_L_TCARDINAL  , 0  },
197   [C2_L_PHEIGHTB    ] = { "heightb"           , C2_L_TCARDINAL  , 0  },
198   [C2_L_PBDW        ] = { "border_width"      , C2_L_TCARDINAL  , 0  },
199   [C2_L_PFULLSCREEN ] = { "fullscreen"        , C2_L_TCARDINAL  , 0  },
200   [C2_L_POVREDIR    ] = { "override_redirect" , C2_L_TCARDINAL  , 0  },
201   [C2_L_PARGB       ] = { "argb"              , C2_L_TCARDINAL  , 0  },
202   [C2_L_PFOCUSED    ] = { "focused"           , C2_L_TCARDINAL  , 0  },
203   [C2_L_PWMWIN      ] = { "wmwin"             , C2_L_TCARDINAL  , 0  },
204   [C2_L_PBSHAPED    ] = { "bounding_shaped"   , C2_L_TCARDINAL  , 0  },
205   [C2_L_PROUNDED    ] = { "rounded_corners"   , C2_L_TCARDINAL  , 0  },
206   [C2_L_PCLIENT     ] = { "client"            , C2_L_TWINDOW    , 0  },
207   [C2_L_PWINDOWTYPE ] = { "window_type"       , C2_L_TSTRING    , 0  },
208   [C2_L_PLEADER     ] = { "leader"            , C2_L_TWINDOW    , 0  },
209   [C2_L_PNAME       ] = { "name"              , C2_L_TSTRING    , 0  },
210   [C2_L_PCLASSG     ] = { "class_g"           , C2_L_TSTRING    , 0  },
211   [C2_L_PCLASSI     ] = { "class_i"           , C2_L_TSTRING    , 0  },
212   [C2_L_PROLE       ] = { "role"              , C2_L_TSTRING    , 0  },
213 };
214 
215 #define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
216 
217 /**
218  * Compare next word in a string with another string.
219  */
220 static inline int
strcmp_wd(const char * needle,const char * src)221 strcmp_wd(const char *needle, const char *src) {
222   int ret = mstrncmp(needle, src);
223   if (ret)
224     return ret;
225 
226   char c = src[strlen(needle)];
227   if (isalnum(c) || '_' == c)
228     return 1;
229   else
230     return 0;
231 }
232 
233 /**
234  * Return whether a c2_ptr_t is empty.
235  */
236 static inline bool
c2_ptr_isempty(const c2_ptr_t p)237 c2_ptr_isempty(const c2_ptr_t p) {
238   return !(p.isbranch ? (bool) p.b: (bool) p.l);
239 }
240 
241 /**
242  * Reset a c2_ptr_t.
243  */
244 static inline void
c2_ptr_reset(c2_ptr_t * pp)245 c2_ptr_reset(c2_ptr_t *pp) {
246   if (pp)
247     memcpy(pp, &C2_PTR_NULL, sizeof(c2_ptr_t));
248 }
249 
250 /**
251  * Combine two condition trees.
252  */
253 static inline c2_ptr_t
c2h_comb_tree(c2_b_op_t op,c2_ptr_t p1,c2_ptr_t p2)254 c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
255  c2_ptr_t p = {
256    .isbranch = true,
257    .b = malloc(sizeof(c2_b_t))
258  };
259 
260  p.b->opr1 = p1;
261  p.b->opr2 = p2;
262  p.b->op = op;
263 
264  return p;
265 }
266 
267 /**
268  * Get the precedence value of a condition branch operator.
269  */
270 static inline int
c2h_b_opp(c2_b_op_t op)271 c2h_b_opp(c2_b_op_t op) {
272   switch (op) {
273     case C2_B_OAND:   return 2;
274     case C2_B_OOR:    return 1;
275     case C2_B_OXOR:   return 1;
276     default:          break;
277   }
278 
279   assert(0);
280   return 0;
281 }
282 
283 /**
284  * Compare precedence of two condition branch operators.
285  *
286  * Associativity is left-to-right, forever.
287  *
288  * @return positive number if op1 > op2, 0 if op1 == op2 in precedence,
289  *         negative number otherwise
290  */
291 static inline int
c2h_b_opcmp(c2_b_op_t op1,c2_b_op_t op2)292 c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) {
293   return c2h_b_opp(op1) - c2h_b_opp(op2);
294 }
295 
296 static int
297 c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level);
298 
299 static int
300 c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
301 
302 static int
303 c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult);
304 
305 static int
306 c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
307 
308 static int
309 c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
310 
311 static bool
312 c2_l_postprocess(session_t *ps, c2_l_t *pleaf);
313 
314 static void
315 c2_free(c2_ptr_t p);
316 
317 /**
318  * Wrapper of c2_free().
319  */
320 static inline void
c2_freep(c2_ptr_t * pp)321 c2_freep(c2_ptr_t *pp) {
322   if (pp) {
323     c2_free(*pp);
324     c2_ptr_reset(pp);
325   }
326 }
327 
328 static const char *
329 c2h_dump_str_tgt(const c2_l_t *pleaf);
330 
331 static const char *
332 c2h_dump_str_type(const c2_l_t *pleaf);
333 
334 static void
335 c2_dump_raw(c2_ptr_t p);
336 
337 /**
338  * Wrapper of c2_dump_raw().
339  */
340 static inline void
c2_dump(c2_ptr_t p)341 c2_dump(c2_ptr_t p) {
342   c2_dump_raw(p);
343   printf("\n");
344   fflush(stdout);
345 }
346 
347 static Atom
348 c2_get_atom_type(const c2_l_t *pleaf);
349 
350 static bool
351 c2_match_once(session_t *ps, win *w, const c2_ptr_t cond);
352 
353