Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
strfilter.c
Go to the documentation of this file.
1 #include "util.h"
2 #include "string.h"
3 #include "strfilter.h"
4 
5 /* Operators */
6 static const char *OP_and = "&"; /* Logical AND */
7 static const char *OP_or = "|"; /* Logical OR */
8 static const char *OP_not = "!"; /* Logical NOT */
9 
10 #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!')
11 #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')')
12 
13 static void strfilter_node__delete(struct strfilter_node *self)
14 {
15  if (self) {
16  if (self->p && !is_operator(*self->p))
17  free((char *)self->p);
18  strfilter_node__delete(self->l);
19  strfilter_node__delete(self->r);
20  free(self);
21  }
22 }
23 
24 void strfilter__delete(struct strfilter *self)
25 {
26  if (self) {
27  strfilter_node__delete(self->root);
28  free(self);
29  }
30 }
31 
32 static const char *get_token(const char *s, const char **e)
33 {
34  const char *p;
35 
36  while (isspace(*s)) /* Skip spaces */
37  s++;
38 
39  if (*s == '\0') {
40  p = s;
41  goto end;
42  }
43 
44  p = s + 1;
45  if (!is_separator(*s)) {
46  /* End search */
47 retry:
48  while (*p && !is_separator(*p) && !isspace(*p))
49  p++;
50  /* Escape and special case: '!' is also used in glob pattern */
51  if (*(p - 1) == '\\' || (*p == '!' && *(p - 1) == '[')) {
52  p++;
53  goto retry;
54  }
55  }
56 end:
57  *e = p;
58  return s;
59 }
60 
61 static struct strfilter_node *strfilter_node__alloc(const char *op,
62  struct strfilter_node *l,
63  struct strfilter_node *r)
64 {
65  struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node));
66 
67  if (ret) {
68  ret->p = op;
69  ret->l = l;
70  ret->r = r;
71  }
72 
73  return ret;
74 }
75 
76 static struct strfilter_node *strfilter_node__new(const char *s,
77  const char **ep)
78 {
79  struct strfilter_node root, *cur, *last_op;
80  const char *e;
81 
82  if (!s)
83  return NULL;
84 
85  memset(&root, 0, sizeof(root));
86  last_op = cur = &root;
87 
88  s = get_token(s, &e);
89  while (*s != '\0' && *s != ')') {
90  switch (*s) {
91  case '&': /* Exchg last OP->r with AND */
92  if (!cur->r || !last_op->r)
93  goto error;
94  cur = strfilter_node__alloc(OP_and, last_op->r, NULL);
95  if (!cur)
96  goto nomem;
97  last_op->r = cur;
98  last_op = cur;
99  break;
100  case '|': /* Exchg the root with OR */
101  if (!cur->r || !root.r)
102  goto error;
103  cur = strfilter_node__alloc(OP_or, root.r, NULL);
104  if (!cur)
105  goto nomem;
106  root.r = cur;
107  last_op = cur;
108  break;
109  case '!': /* Add NOT as a leaf node */
110  if (cur->r)
111  goto error;
112  cur->r = strfilter_node__alloc(OP_not, NULL, NULL);
113  if (!cur->r)
114  goto nomem;
115  cur = cur->r;
116  break;
117  case '(': /* Recursively parses inside the parenthesis */
118  if (cur->r)
119  goto error;
120  cur->r = strfilter_node__new(s + 1, &s);
121  if (!s)
122  goto nomem;
123  if (!cur->r || *s != ')')
124  goto error;
125  e = s + 1;
126  break;
127  default:
128  if (cur->r)
129  goto error;
130  cur->r = strfilter_node__alloc(NULL, NULL, NULL);
131  if (!cur->r)
132  goto nomem;
133  cur->r->p = strndup(s, e - s);
134  if (!cur->r->p)
135  goto nomem;
136  }
137  s = get_token(e, &e);
138  }
139  if (!cur->r)
140  goto error;
141  *ep = s;
142  return root.r;
143 nomem:
144  s = NULL;
145 error:
146  *ep = s;
147  strfilter_node__delete(root.r);
148  return NULL;
149 }
150 
151 /*
152  * Parse filter rule and return new strfilter.
153  * Return NULL if fail, and *ep == NULL if memory allocation failed.
154  */
155 struct strfilter *strfilter__new(const char *rules, const char **err)
156 {
157  struct strfilter *ret = zalloc(sizeof(struct strfilter));
158  const char *ep = NULL;
159 
160  if (ret)
161  ret->root = strfilter_node__new(rules, &ep);
162 
163  if (!ret || !ret->root || *ep != '\0') {
164  if (err)
165  *err = ep;
166  strfilter__delete(ret);
167  ret = NULL;
168  }
169 
170  return ret;
171 }
172 
173 static bool strfilter_node__compare(struct strfilter_node *self,
174  const char *str)
175 {
176  if (!self || !self->p)
177  return false;
178 
179  switch (*self->p) {
180  case '|': /* OR */
181  return strfilter_node__compare(self->l, str) ||
182  strfilter_node__compare(self->r, str);
183  case '&': /* AND */
184  return strfilter_node__compare(self->l, str) &&
185  strfilter_node__compare(self->r, str);
186  case '!': /* NOT */
187  return !strfilter_node__compare(self->r, str);
188  default:
189  return strglobmatch(str, self->p);
190  }
191 }
192 
193 /* Return true if STR matches the filter rules */
194 bool strfilter__compare(struct strfilter *self, const char *str)
195 {
196  if (!self)
197  return false;
198  return strfilter_node__compare(self->root, str);
199 }