1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import stat
18 import pwd
19 import grp
20 import re
21
22 import libxyz.parser as parser
23
24 from libxyz.exceptions import XYZValueError
25 from libxyz.exceptions import LexerError
26 from libxyz.exceptions import FSRuleError
27 from libxyz.vfs.vfsobj import VFSObject
28 from libxyz.vfs.types import *
29 from libxyz.core.utils import ustring
30
31 -class FSRule(parser.BaseParser):
32 """
33 FS rule parser
34
35 Rule syntax is following:
36
37 rule ::= expr $
38 | expr op rule
39 expr ::= expr_body
40 | NOT expr_body
41 | "(" rule ")"
42 expr_body ::= ftype "{" ARG "}"
43 op ::= AND | OR
44 ftype ::= TYPE | PERM | OWNER | NAME | SIZE
45 | LINK_TYPE | LINK_PERM | LINK_OWNER | LINK_NAME
46 | LINK_EXISTS | LINK_SIZE
47
48 Examples:
49
50 type{file} and perm{+0111}
51 (owner{user} and not owner{:group}) or owner{root}
52 """
53
54
55 TOKEN_TYPE = "type"
56 TOKEN_PERM = "perm"
57 TOKEN_OWNER = "owner"
58 TOKEN_NAME = "name"
59 TOKEN_INAME = "iname"
60 TOKEN_SIZE = "size"
61 TOKEN_LINK_TYPE = "link_type"
62 TOKEN_LINK_PERM = "link_perm"
63 TOKEN_LINK_OWNER = "link_owner"
64 TOKEN_LINK_NAME = "link_name"
65 TOKEN_LINK_INAME = "link_iname"
66 TOKEN_LINK_EXISTS = "link_exists"
67 TOKEN_LINK_SIZE = "link_size"
68 TOKEN_AND = "and"
69 TOKEN_OR = "or"
70 TOKEN_NOT = "not"
71 TOKEN_OPEN_BR = "{"
72 TOKEN_CLOSE_BR = "}"
73 TOKEN_OPEN_PAR = "("
74 TOKEN_CLOSE_PAR = ")"
75 TOKEN_DEFAULT = True
76 TOKEN_ARG = False
77 EOF = None
78
79 TOKENS_EXTENDED = []
80 TRANSFORM_EXTENDED = {}
81
82 TOKENS = [TOKEN_TYPE, TOKEN_PERM, TOKEN_OWNER, TOKEN_NAME, TOKEN_INAME,
83 TOKEN_LINK_TYPE, TOKEN_LINK_PERM, TOKEN_LINK_OWNER,
84 TOKEN_LINK_NAME, TOKEN_LINK_INAME, TOKEN_LINK_EXISTS,
85 TOKEN_AND, TOKEN_OR, TOKEN_NOT, TOKEN_OPEN_BR, TOKEN_CLOSE_BR,
86 TOKEN_OPEN_PAR, TOKEN_CLOSE_PAR, TOKEN_DEFAULT,
87 TOKEN_SIZE, TOKEN_LINK_SIZE, EOF]
88
89
90 NTOKEN_START = 100
91 NTOKEN_RULE = 101
92 NTOKEN_EXPR = 102
93 NTOKEN_EXPR_BODY = 103
94 NTOKEN_OP = 104
95 NTOKEN_FTYPE = 105
96
97 FTYPE = [TOKEN_TYPE,
98 TOKEN_PERM,
99 TOKEN_OWNER,
100 TOKEN_NAME,
101 TOKEN_INAME,
102 TOKEN_SIZE,
103 TOKEN_LINK_TYPE,
104 TOKEN_LINK_PERM,
105 TOKEN_LINK_OWNER,
106 TOKEN_LINK_NAME,
107 TOKEN_LINK_INAME,
108 TOKEN_LINK_EXISTS,
109 TOKEN_LINK_SIZE,
110 ]
111
112 INFIX_OP = (TOKEN_AND, TOKEN_OR)
113
114 @classmethod
115 - def extend(cls, token, trans_func, match_func):
116 """
117 Extend FSRule parser with new expressions
118 @param token: new token expression
119 @param trans_func: Transformation function
120 @param match_func: Match function
121 """
122
123 if token in cls.TOKENS_EXTENDED or token in cls.TOKENS or \
124 token in cls.FTYPE:
125 raise FSRuleError(_(u"Error extending FSRule: "\
126 u"token %s already registered") % token)
127
128 if not callable(trans_func) or not callable(match_func):
129 raise FSRuleError(_(u"Error extending FSRule: "\
130 u"trans_func and match_func arguments "\
131 u"must be functions."))
132
133
134 cls.TOKENS_EXTENDED.append(token)
135 cls.TOKENS.append(token)
136 cls.FTYPE.append(token)
137
138
139 cls.TRANSFORM_EXTENDED[token] = trans_func
140
141
142 Expression.extend(token, match_func)
143
144
145
146 @classmethod
176
177
178
180 """
181 @param rule: String rule
182 """
183
184 super(FSRule, self).__init__()
185
186 self.raw_rule = rule
187
188 self._stack = []
189 self._done = False
190 self._cur_obj = None
191 self._expressions = parser.lr.Tree()
192 self._exp_pointer = self._expressions
193 self._exp_stack = []
194
195
196 self._action = parser.lr.ActionTable()
197
198 _s = self._shift
199 _r = self._reduce
200
201 self._action.add(0, self.TOKEN_TYPE, (_s, 2))
202 self._action.add(0, self.TOKEN_PERM, (_s, 3))
203 self._action.add(0, self.TOKEN_OWNER, (_s, 4))
204 self._action.add(0, self.TOKEN_NAME, (_s, 5))
205 self._action.add(0, self.TOKEN_INAME, (_s, 5))
206 self._action.add(0, self.TOKEN_SIZE, (_s, 27))
207 self._action.add(0, self.TOKEN_LINK_TYPE, (_s, 27))
208 self._action.add(0, self.TOKEN_LINK_PERM, (_s, 27))
209 self._action.add(0, self.TOKEN_LINK_OWNER, (_s, 27))
210 self._action.add(0, self.TOKEN_LINK_NAME, (_s, 27))
211 self._action.add(0, self.TOKEN_LINK_INAME, (_s, 27))
212 self._action.add(0, self.TOKEN_LINK_EXISTS, (_s, 27))
213 self._action.add(0, self.TOKEN_LINK_SIZE, (_s, 27))
214 self._action.add(0, self.TOKEN_NOT, (_s, 1))
215 self._action.add(0, self.TOKEN_OPEN_PAR, (_s, 6))
216
217 self._action.add(1, self.TOKEN_TYPE, (_s, 2))
218 self._action.add(1, self.TOKEN_PERM, (_s, 3))
219 self._action.add(1, self.TOKEN_OWNER, (_s, 4))
220 self._action.add(1, self.TOKEN_NAME, (_s, 5))
221 self._action.add(1, self.TOKEN_INAME, (_s, 5))
222 self._action.add(1, self.TOKEN_SIZE, (_s, 27))
223 self._action.add(1, self.TOKEN_LINK_TYPE, (_s, 27))
224 self._action.add(1, self.TOKEN_LINK_PERM, (_s, 27))
225 self._action.add(1, self.TOKEN_LINK_OWNER, (_s, 27))
226 self._action.add(1, self.TOKEN_LINK_NAME, (_s, 27))
227 self._action.add(1, self.TOKEN_LINK_INAME, (_s, 27))
228 self._action.add(1, self.TOKEN_LINK_EXISTS, (_s, 27))
229 self._action.add(1, self.TOKEN_LINK_SIZE, (_s, 27))
230
231 self._action.add(2, self.TOKEN_DEFAULT, (_r, 10))
232 self._action.add(3, self.TOKEN_DEFAULT, (_r, 11))
233 self._action.add(4, self.TOKEN_DEFAULT, (_r, 12))
234 self._action.add(5, self.TOKEN_DEFAULT, (_r, 13))
235
236 self._action.add(6, self.TOKEN_TYPE, (_s, 2))
237 self._action.add(6, self.TOKEN_PERM, (_s, 3))
238 self._action.add(6, self.TOKEN_OWNER, (_s, 4))
239 self._action.add(6, self.TOKEN_NAME, (_s, 5))
240 self._action.add(6, self.TOKEN_INAME, (_s, 5))
241 self._action.add(6, self.TOKEN_SIZE, (_s, 27))
242 self._action.add(6, self.TOKEN_LINK_TYPE, (_s, 27))
243 self._action.add(6, self.TOKEN_LINK_PERM, (_s, 27))
244 self._action.add(6, self.TOKEN_LINK_OWNER, (_s, 27))
245 self._action.add(6, self.TOKEN_LINK_NAME, (_s, 27))
246 self._action.add(6, self.TOKEN_LINK_INAME, (_s, 27))
247 self._action.add(6, self.TOKEN_LINK_EXISTS, (_s, 27))
248 self._action.add(6, self.TOKEN_LINK_SIZE, (_s, 27))
249 self._action.add(6, self.TOKEN_NOT, (_s, 1))
250 self._action.add(6, self.TOKEN_OPEN_PAR, (_s, 6))
251
252 self._action.add(7, self.EOF, (_s, 14))
253 self._action.add(8, self.TOKEN_DEFAULT, (_r, 1))
254
255 self._action.add(9, self.TOKEN_AND, (_s, 15))
256 self._action.add(9, self.TOKEN_OR, (_s, 16))
257 self._action.add(9, self.TOKEN_DEFAULT, (_r, 2))
258
259 self._action.add(10, self.TOKEN_DEFAULT, (_r, 4))
260 self._action.add(11, self.TOKEN_OPEN_BR, (_s, 18))
261 self._action.add(12, self.TOKEN_DEFAULT, (_r, 5))
262 self._action.add(13, self.TOKEN_CLOSE_PAR, (_s, 19))
263 self._action.add(14, self.TOKEN_DEFAULT, (self._accept, None))
264 self._action.add(15, self.TOKEN_DEFAULT, (_r, 8))
265 self._action.add(16, self.TOKEN_DEFAULT, (_r, 9))
266
267 self._action.add(17, self.TOKEN_TYPE, (_s, 2))
268 self._action.add(17, self.TOKEN_PERM, (_s, 3))
269 self._action.add(17, self.TOKEN_OWNER, (_s, 4))
270 self._action.add(17, self.TOKEN_NAME, (_s, 5))
271 self._action.add(17, self.TOKEN_INAME, (_s, 5))
272 self._action.add(17, self.TOKEN_SIZE, (_s, 27))
273 self._action.add(17, self.TOKEN_LINK_TYPE, (_s, 27))
274 self._action.add(17, self.TOKEN_LINK_PERM, (_s, 27))
275 self._action.add(17, self.TOKEN_LINK_OWNER, (_s, 27))
276 self._action.add(17, self.TOKEN_LINK_NAME, (_s, 27))
277 self._action.add(17, self.TOKEN_LINK_INAME, (_s, 27))
278 self._action.add(17, self.TOKEN_LINK_EXISTS, (_s, 27))
279 self._action.add(17, self.TOKEN_LINK_SIZE, (_s, 27))
280 self._action.add(17, self.TOKEN_NOT, (_s, 1))
281 self._action.add(17, self.TOKEN_OPEN_PAR, (_s, 6))
282
283 self._action.add(18, self.TOKEN_ARG, (_s, 21))
284 self._action.add(19, self.TOKEN_DEFAULT, (_r, 6))
285 self._action.add(20, self.TOKEN_DEFAULT, (_r, 3))
286 self._action.add(21, self.TOKEN_CLOSE_BR, (_s, 22))
287 self._action.add(22, self.TOKEN_DEFAULT, (_r, 7))
288 self._action.add(23, self.TOKEN_OPEN_BR, (_s, 24))
289 self._action.add(24, self.TOKEN_ARG, (_s, 25))
290 self._action.add(25, self.TOKEN_CLOSE_BR, (_s, 26))
291 self._action.add(26, self.TOKEN_DEFAULT, (_r, 14))
292 self._action.add(27, self.TOKEN_DEFAULT, (_r, 131))
293
294
295 for _ext_token in self.TOKENS_EXTENDED:
296 for _state in (0, 1, 6, 17):
297 self._action.add(_state, _ext_token, (_s, 27))
298
299 self._rules = parser.lr.Rules()
300
301 self._rules.add(1, self.NTOKEN_START, 1)
302 self._rules.add(2, self.NTOKEN_RULE, 1)
303 self._rules.add(3, self.NTOKEN_RULE, 3)
304 self._rules.add(4, self.NTOKEN_EXPR, 1)
305 self._rules.add(5, self.NTOKEN_EXPR, 2)
306 self._rules.add(6, self.NTOKEN_EXPR, 3)
307 self._rules.add(7, self.NTOKEN_EXPR_BODY, 4)
308 self._rules.add(8, self.NTOKEN_OP, 1)
309 self._rules.add(9, self.NTOKEN_OP, 1)
310 self._rules.add(10, self.NTOKEN_FTYPE, 1)
311 self._rules.add(11, self.NTOKEN_FTYPE, 1)
312 self._rules.add(12, self.NTOKEN_FTYPE, 1)
313 self._rules.add(13, self.NTOKEN_FTYPE, 1)
314 self._rules.add(14, self.NTOKEN_EXPR_BODY, 5)
315 self._rules.add(131, self.NTOKEN_FTYPE, 1)
316
317
318 self._goto = parser.lr.GotoTable()
319
320 self._goto.add(0, self.NTOKEN_START, 7)
321 self._goto.add(0, self.NTOKEN_RULE, 8)
322 self._goto.add(0, self.NTOKEN_EXPR, 9)
323 self._goto.add(0, self.NTOKEN_EXPR_BODY, 10)
324 self._goto.add(0, self.NTOKEN_FTYPE, 11)
325
326 self._goto.add(1, self.NTOKEN_EXPR_BODY, 10)
327 self._goto.add(1, self.NTOKEN_FTYPE, 23)
328
329 self._goto.add(6, self.NTOKEN_RULE, 13)
330 self._goto.add(6, self.NTOKEN_EXPR, 9)
331 self._goto.add(6, self.NTOKEN_EXPR_BODY, 10)
332 self._goto.add(6, self.NTOKEN_FTYPE, 11)
333
334 self._goto.add(9, self.NTOKEN_OP, 17)
335
336 self._goto.add(17, self.NTOKEN_RULE, 20)
337 self._goto.add(17, self.NTOKEN_EXPR, 9)
338 self._goto.add(17, self.NTOKEN_EXPR_BODY, 10)
339 self._goto.add(17, self.NTOKEN_FTYPE, 11)
340
341 self._unget = []
342 self._chain = self._parse(rule)
343
344
345
347 """
348 Match given object against rule
349
350 @param obj: VFSObject instance
351 @return: True if matches and False otherwise
352 """
353
354 if not isinstance(obj, VFSObject):
355 raise XYZValueError(_(u"Invalid argument type: %s, "\
356 u"VFSObject expected") % type(obj))
357
358 return self._match(obj, self._expressions)
359
360
361
362 - def _match(self, obj, _expressions):
363 _op = None
364 _res = None
365
366 for exp in _expressions:
367 if exp in ("AND", "OR"):
368 _op = exp
369 continue
370
371 if isinstance(exp, parser.lr.Tree):
372
373 _r = self._match(obj, exp)
374 else:
375 _r = exp.match(obj)
376
377 if _res is not None:
378 if _op == "AND":
379 _res = _res and _r
380
381
382
383 if not _res:
384 break
385 elif _op == "OR":
386 _res = _res or _r
387
388
389
390 if _res:
391 break
392 else:
393 _res = _r
394
395 _op = None
396
397 if _res is None:
398 return _r
399 else:
400 return _res
401
402
403
405 """
406 Parse rule
407 """
408
409
410 self._stack.append(0)
411
412 _tokens = (self.TOKEN_OPEN_PAR,
413 self.TOKEN_CLOSE_PAR,
414 self.TOKEN_OPEN_BR,
415 self.TOKEN_CLOSE_BR,
416 u"=", u",")
417
418 self._lexer = parser.Lexer(rule, _tokens, u"#")
419 self._lexer.escaping_on()
420
421 try:
422 while True:
423 if self._done:
424 break
425
426 if self._unget:
427 _tok = self._unget.pop()
428 else:
429 _res = self._lexer.lexer()
430
431 if _res is not None:
432 _tok = _res[1]
433 else:
434 _tok = _res
435
436 if _tok not in self.TOKENS:
437 _tok_type = self.TOKEN_ARG
438 else:
439 _tok_type = _tok
440
441 try:
442 _f, _arg = self._action.get(self._stack[-1], _tok_type)
443 except KeyError:
444 try:
445 _f, _arg = self._action.get(self._stack[-1],
446 self.TOKEN_DEFAULT)
447 except KeyError:
448 self.error(_tok)
449
450 _f(_tok, _arg)
451
452 except LexerError, e:
453 self.error(e)
454
455
456
457 - def _shift(self, token, state):
458 """
459 Shift token and state onto stack
460 """
461
462 self._stack.append(token)
463 self._stack.append(state)
464
465 if state == 6:
466 _new = parser.lr.Tree()
467 self._exp_pointer.add(_new)
468 self._exp_stack.append(self._exp_pointer)
469 self._exp_pointer = _new
470 elif state == 19:
471 if self._exp_stack:
472 self._exp_pointer = self._exp_stack.pop()
473
474
475
477 """
478 Reduce stack by rule
479 """
480 _transform = {
481 u"type": self._type,
482 u"name": self._name,
483 u"iname": self._iname,
484 u"owner": self._owner,
485 u"perm": self._perm,
486 u"size": self._size,
487 u"link_type": self._type,
488 u"link_name": self._name,
489 u"link_iname": self._iname,
490 u"link_owner": self._owner,
491 u"link_perm": self._perm,
492 u"link_size": self._size,
493 }
494
495 try:
496 _ntok, _len = self._rules.get(rule)
497 except KeyError:
498 self.error(token)
499
500 if rule in (10, 11, 12, 13, 131):
501 self._cur_obj = Expression()
502 self._cur_obj.otype = self._stack[-2]
503 elif rule in (7, 14):
504 _arg = self._stack[-4]
505 _cur = self._cur_obj
506
507 if _cur.otype in _transform:
508 _cur.arg = _transform[_cur.otype](_arg)
509 elif _cur.otype in self.TRANSFORM_EXTENDED:
510 try:
511 _cur.arg = self.TRANSFORM_EXTENDED[_cur.otype](_arg)
512 except Exception, e:
513 self.error(_(u"Error in calling extended transformation "\
514 u"function: %s") % ustring(str(e)))
515 else:
516 _cur.arg = _arg
517
518 if rule == 14:
519 self._cur_obj.negative = True
520 elif rule in (4, 5):
521 self._exp_pointer.add(self._cur_obj)
522 self._cur_obj = None
523 elif rule == 8:
524 self._exp_pointer.add("AND")
525 elif rule == 9:
526 self._exp_pointer.add("OR")
527
528 self._stack = self._stack[:(_len * -2)]
529 _top = self._stack[-1]
530 self._stack.append(_ntok)
531
532 try:
533 self._stack.append(self._goto.get(_top, _ntok))
534 except KeyError:
535 self.error(token)
536
537 self._unget.append(token)
538
539
540
542 """
543 Complete parsing
544 """
545
546 self._done = True
547
548
549
565
566
567
569 return re.compile(arg, re.U)
570
571
572
574 return re.compile(arg, re.U | re.I)
575
576
577
579 if not re.match(r"^(\w+)?(:(\w+))?$", arg):
580 self.error(_(u"Invalid owner{} argument: %s" % arg))
581
582 _tmp = arg.split(":")
583 _uid = _tmp[0]
584
585 if _uid == "":
586 _uid = None
587 elif not _uid.isdigit():
588 try:
589 _uid = pwd.getpwnam(_uid).pw_uid
590 except (KeyError, TypeError):
591 self.error(_(u"Invalid uid: %s" % _uid))
592 else:
593 _uid = int(_uid)
594
595 if len(_tmp) > 1:
596 _gid = _tmp[1]
597
598 if not _gid.isdigit():
599 try:
600 _gid = grp.getgrnam(_gid).gr_gid
601 except (KeyError, TypeError):
602 self.error(_(u"Invalid gid: %s" % _gid))
603 else:
604 _gid = int(_gid)
605 else:
606 _gid = None
607
608 return (_uid, _gid)
609
610
611
613 _any = False
614
615 if not re.match(r"^\+?\d{4}$", arg):
616 self.error(_(u"Invalid perm{} argument: %s" % arg))
617
618 if arg.startswith(u"+"):
619 _any = True
620 _perm = int(arg[1:], 8)
621 else:
622 _perm = int(arg, 8)
623
624 return (_any, _perm)
625
626
627
629 _bytes = {
630 u"B": 1,
631 u"K": 1024,
632 u"M": 1024 * 1024,
633 u"G": 1024 * 1024 * 1024,
634 u"T": 1024 * 1024 * 1024 * 1024,
635 }
636
637 _re = re.match(r"^\s*([<>]?\=?)\s*(\d+)\s*([BbKkMmGgTt]?)\s*$", arg)
638
639 if _re is None:
640 self.error(_(u"Invalid size{} argument: %s") % arg)
641 else:
642 _op = _re.group(1) or u"="
643 _size = long(_re.group(2))
644 _mod = _re.group(3) or None
645
646 if _mod is not None:
647 _size *= _bytes[_mod.upper()]
648
649 return (_op, _size)
650
651
652
653 -def link(func):
654 """
655 Wrap matching func to aplly only for links
656 """
657
658 def _trans(vfsobj, arg):
659 if isinstance(vfsobj.ftype, VFSTypeLink) and vfsobj.data is not None:
660 return func(vfsobj.data, arg)
661 else:
662 return False
663
664 return _trans
665
669 """
670 FS rule expression class
671 """
672
673 MATCH_EXTENDED = {}
674
675 @classmethod
676 - def extend(cls, token, match_func):
678
679
680
681 @classmethod
683 try:
684 del(cls.MATCH_EXTENDED[token])
685 except KeyError:
686 pass
687
688
689
691 self.otype = None
692 self.arg = None
693 self.negative = False
694
695
696
697 - def match(self, vfsobj):
698 """
699 Check if object matches the rule
700 """
701
702 def _match_type(obj, arg):
703 return isinstance(obj.ftype, arg)
704
705
706
707 def _match_name(obj, arg):
708 if arg.search(obj.name) is None:
709 return False
710 else:
711 return True
712
713
714
715 def _match_iname(obj, arg):
716 if arg.search(obj.name) is None:
717 return False
718 else:
719 return True
720
721
722
723 def _match_owner(obj, arg):
724 if arg[0] is not None and arg[1] is not None:
725 if (obj.uid, obj.gid) == arg:
726 return True
727 elif arg[0] is not None and obj.uid == arg[0]:
728 return True
729 elif arg[1] is not None and obj.gid == arg[1]:
730 return True
731
732 return False
733
734
735
736 def _match_perm(obj, arg):
737 if obj.mode is None:
738 return False
739
740 _any, _m = arg
741 _mode = stat.S_IMODE(obj.mode.raw)
742
743 if not _any and _mode == _m:
744 return True
745 elif _mode & _m:
746 return True
747
748 return False
749
750
751
752 def _match_size(obj, args):
753 if obj.size is None:
754 return False
755
756 _op, _size = args
757
758 _data = {u">": lambda x, y: x > y,
759 u">=": lambda x, y: x >= y,
760 u"<": lambda x, y: x < y,
761 u"<=": lambda x, y: x <= y,
762 u"=": lambda x, y: x == y,
763 }
764
765 if _op in _data and _data[_op](obj.size, _size):
766 return True
767
768 return False
769
770
771
772 def _match_link_exists(obj, arg):
773 if isinstance(obj.ftype, VFSTypeLink) and obj.data is not None:
774 return True
775 else:
776 return False
777
778
779
780 _match_link_type = link(_match_type)
781 _match_link_name = link(_match_name)
782 _match_link_iname = link(_match_iname)
783 _match_link_owner = link(_match_owner)
784 _match_link_perm = link(_match_perm)
785 _match_link_size = link(_match_size)
786
787
788
789 _match_f = {
790 u"type": _match_type,
791 u"name": _match_name,
792 u"iname": _match_iname,
793 u"owner": _match_owner,
794 u"perm": _match_perm,
795 u"size": _match_size,
796 u"link_type": _match_link_type,
797 u"link_name": _match_link_name,
798 u"link_iname": _match_link_iname,
799 u"link_owner": _match_link_owner,
800 u"link_perm": _match_link_perm,
801 u"link_exists": _match_link_exists,
802 u"link_size": _match_link_size,
803 }
804
805 if self.otype in _match_f:
806 _res = _match_f[self.otype](vfsobj, self.arg)
807 elif self.otype in self.MATCH_EXTENDED:
808 try:
809 _res = self.MATCH_EXTENDED[self.otype](vfsobj, self.arg)
810 except Exception, e:
811 self.error(_(u"Error in calling extended match "\
812 u"function: %s") % ustring(str(e)))
813 else:
814 raise FSRuleError(_(u"Unable to find match function for token: %s")
815 % self.otype)
816
817 if self.negative:
818 return not _res
819 else:
820 return _res
821
822
823
825 return "<FSRule expression: %s, %s, %s>" % \
826 (self.otype, str(self.arg), str(self.negative))
827
828
829
832