2 * Copyright 2005 Timo Hirvonen
8 #include "track_info.h"
26 #define NR_COMBINATIONS TOK_EQ
35 #define NR_SPECIALS TOK_NE
36 #define COMB_BASE TOK_NE
38 /* same as the first 3 + '=' */
47 #define NR_TOKS (TOK_STR + 1)
50 struct list_head node
;
52 /* for TOK_KEY, TOK_INT_OR_KEY and TOK_STR */
56 /* same order as TOK_* */
57 static const char specials
[NR_SPECIALS
] = "!<>=&|()";
59 static const int tok_to_op
[NR_TOKS
] = {
60 -1, OP_LT
, OP_GT
, OP_EQ
, -1, -1, -1, -1, OP_NE
, OP_LE
, OP_GE
, -1, -1, -1
63 static const char * const op_names
[NR_OPS
] = { "<", "<=", "=", ">=", ">", "!=" };
64 static const char * const expr_names
[NR_EXPRS
] = {
65 "&", "|", "!", "a string", "an integer", "a boolean"
68 static char error_buf
[64] = { 0, };
71 static void set_error(const char *format
, ...)
76 vsnprintf(error_buf
, sizeof(error_buf
), format
, ap
);
80 static struct token
*get_str(const char *str
, int *idxp
)
86 /* can't remove all backslashes here => don't remove any */
87 while (str
[e
] != '"') {
101 tok
= xmalloc(sizeof(struct token
) + e
- s
+ 1);
102 memcpy(tok
->str
, str
+ s
, e
- s
);
108 set_error("end of expression at middle of string");
112 static struct token
*get_int_or_key(const char *str
, int *idxp
)
124 for (i
= 0; i
< NR_SPECIALS
; i
++) {
125 if (c
== specials
[i
])
128 if (c
< '0' || c
> '9') {
130 if (!isalpha(c
) && c
!= '_' && c
!= '-') {
131 set_error("unexpected '%c'", c
);
138 tok
= xmalloc(sizeof(struct token
) + e
- s
+ 1);
139 memcpy(tok
->str
, str
+ s
, e
- s
);
143 tok
->type
= TOK_INT_OR_KEY
;
148 static struct token
*get_token(const char *str
, int *idxp
)
154 for (i
= 0; i
< NR_SPECIALS
; i
++) {
157 if (c
!= specials
[i
])
161 tok
= xnew(struct token
, 1);
163 if (i
< NR_COMBINATIONS
&& str
[idx
] == '=') {
164 tok
->type
= COMB_BASE
+ i
;
171 return get_str(str
, idxp
);
172 return get_int_or_key(str
, idxp
);
175 static void free_tokens(struct list_head
*head
)
177 struct list_head
*item
= head
->next
;
179 while (item
!= head
) {
180 struct list_head
*next
= item
->next
;
181 struct token
*tok
= container_of(item
, struct token
, node
);
188 static int tokenize(struct list_head
*head
, const char *str
)
196 tok
= get_token(str
, &idx
);
201 list_add_tail(&tok
->node
, head
);
206 static struct expr
*expr_new(int type
)
208 struct expr
*new = xnew(struct expr
, 1);
218 static int parse(struct expr
**rootp
, struct list_head
*head
, struct list_head
**itemp
, int level
);
220 static int parse_one(struct expr
**exprp
, struct list_head
*head
, struct list_head
**itemp
)
222 struct list_head
*item
= *itemp
;
224 enum token_type type
;
229 set_error("expression expected");
233 tok
= container_of(item
, struct token
, node
);
235 if (type
== TOK_NOT
) {
236 struct expr
*new, *tmp
;
239 rc
= parse_one(&tmp
, head
, itemp
);
242 new = expr_new(EXPR_NOT
);
246 } else if (type
== TOK_LPAREN
) {
249 return parse(exprp
, head
, itemp
, 1);
250 /* ')' already eaten */
251 } else if (type
== TOK_KEY
|| type
== TOK_INT_OR_KEY
) {
252 const char *key
= tok
->str
;
258 tok
= container_of(item
, struct token
, node
);
259 op
= tok_to_op
[tok
->type
];
261 if (item
== head
|| op
== -1) {
263 new = expr_new(EXPR_BOOL
);
264 new->key
= xstrdup(key
);
271 set_error("right side of expression expected");
274 tok
= container_of(item
, struct token
, node
);
277 if (type
== TOK_STR
) {
278 if (op
!= OP_EQ
&& op
!= OP_NE
) {
279 set_error("invalid string operator '%s'", op_names
[op
]);
282 new = expr_new(EXPR_STR
);
283 new->key
= xstrdup(key
);
284 glob_compile(&new->estr
.glob_head
, tok
->str
);
288 } else if (type
== TOK_INT_OR_KEY
) {
291 if (str_to_int(tok
->str
, &val
)) {
293 new = expr_new(EXPR_INT
);
294 new->key
= xstrdup(key
);
300 if (op
== OP_EQ
|| op
== OP_NE
) {
301 set_error("integer or string expected");
303 set_error("integer expected");
307 set_error("key expected");
311 static void add(struct expr
**rootp
, struct expr
*expr
)
313 struct expr
*tmp
, *root
= *rootp
;
323 if (tmp
->type
<= EXPR_OR
) {
324 /* tmp is binary, tree is incomplete */
330 /* tmp is unary, tree is complete
331 * expr must be a binary operator */
332 BUG_ON(expr
->type
> EXPR_OR
);
339 static int parse(struct expr
**rootp
, struct list_head
*head
, struct list_head
**itemp
, int level
)
341 struct list_head
*item
= *itemp
;
348 rc
= parse_one(&expr
, head
, &item
);
354 set_error("')' expected");
360 tok
= container_of(item
, struct token
, node
);
361 if (tok
->type
== TOK_RPAREN
) {
363 set_error("unexpected ')'");
370 if (tok
->type
== TOK_AND
) {
372 } else if (tok
->type
== TOK_OR
) {
375 set_error("'&' or '|' expected");
378 expr
= expr_new(type
);
384 struct expr
*expr_parse(const char *str
)
387 struct expr
*root
= NULL
;
388 struct list_head
*item
;
391 for (i
= 0; str
[i
]; i
++) {
392 unsigned char c
= str
[i
];
394 set_error("filter contains control characters");
398 if (!u_is_valid(str
)) {
399 set_error("invalid UTF-8");
403 if (tokenize(&head
, str
))
407 if (parse(&root
, &head
, &item
, 0))
413 static const struct {
417 { "album", EXPR_STR
},
418 { "artist", EXPR_STR
},
419 { "date", EXPR_INT
},
420 { "discnumber", EXPR_INT
},
421 { "duration", EXPR_INT
},
422 { "filename", EXPR_STR
},
423 { "genre", EXPR_STR
},
424 { "stream", EXPR_BOOL
},
425 { "tag", EXPR_BOOL
},
426 { "title", EXPR_STR
},
427 { "tracknumber",EXPR_INT
},
431 int expr_check_leaves(struct expr
**exprp
, const char *(*get_filter
)(const char *name
))
433 struct expr
*expr
= *exprp
;
439 if (expr_check_leaves(&expr
->left
, get_filter
))
442 return expr_check_leaves(&expr
->right
, get_filter
);
446 for (i
= 0; builtin
[i
].key
; i
++) {
447 int cmp
= strcmp(expr
->key
, builtin
[i
].key
);
454 if (builtin
[i
].type
!= expr
->type
) {
456 set_error("%s is %s", builtin
[i
].key
, expr_names
[builtin
[i
].type
]);
461 if (expr
->type
!= EXPR_BOOL
) {
463 set_error("unkown key %s", expr
->key
);
467 /* user defined filter */
468 filter
= get_filter(expr
->key
);
469 if (filter
== NULL
) {
470 set_error("unkown filter or boolean %s", expr
->key
);
473 e
= expr_parse(filter
);
477 rc
= expr_check_leaves(&e
, get_filter
);
484 e
->parent
= expr
->parent
;
487 /* this sets parents left pointer */
492 int expr_eval(struct expr
*expr
, struct track_info
*ti
)
494 enum expr_type type
= expr
->type
;
498 int left
= expr_eval(expr
->left
, ti
);
500 if (type
== EXPR_AND
)
501 return left
&& expr_eval(expr
->right
, ti
);
503 return left
|| expr_eval(expr
->right
, ti
);
509 if (type
== EXPR_STR
) {
513 if (strcmp(key
, "filename") == 0) {
516 val
= comments_get_val(ti
->comments
, key
);
518 /* NULL="something" is false */
519 if (expr
->estr
.op
== SOP_EQ
)
521 /* NULL!="something" is true */
525 res
= glob_match(&expr
->estr
.glob_head
, val
);
526 if (expr
->estr
.op
== SOP_EQ
)
529 } else if (type
== EXPR_INT
) {
532 if (strcmp(key
, "duration") == 0) {
534 /* duration of a stream is infinite (well, almost) */
535 if (is_url(ti
->filename
))
538 val
= comments_get_int(ti
->comments
, key
);
540 if (expr
->eint
.val
== -1) {
542 * doesn't make sense to do 123 < "not set"
543 * but it makes sense to do date=-1 (date is not set)
545 if (expr
->eint
.op
== IOP_EQ
)
547 if (expr
->eint
.op
== IOP_NE
)
551 /* tag not set, can't compare */
554 res
= val
- expr
->eint
.val
;
555 switch (expr
->eint
.op
) {
570 if (strcmp(key
, "stream") == 0)
571 return is_url(ti
->filename
);
572 return track_info_has_tag(ti
);
575 void expr_free(struct expr
*expr
)
578 expr_free(expr
->left
);
580 expr_free(expr
->right
);
583 if (expr
->type
== EXPR_STR
)
584 glob_free(&expr
->estr
.glob_head
);
588 const char *expr_error(void)