Bug Summary

File:build-analysis/../src/clients/lib/xmmsclient/collparser.c
Warning:line 390, column 2
Value stored to 'curr' is never read

Annotated Source Code

1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2017 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <ctype.h>
21
22#include <xmmsclient/xmmsclient.h>
23#include <xmmsclientpriv/xmmsclient.h>
24#include <xmmsc/xmmsc_idnumbers.h>
25
26
27#define XMMS_COLLECTION_PARSER_DEFAULT_NAMESPACE"Collections" "Collections"
28
29typedef struct {
30 char shortstr;
31
32 /* this has to be large enough to hold all of the longstr's
33 * below.
34 */
35 char longstr[8];
36} xmmsv_coll_prop_t;
37
38static const xmmsv_coll_prop_t
39xmmsv_coll_prop_short[] = { { 'a', "artist" },
40 { 'l', "album" },
41 { 't', "title" },
42 { 'n', "tracknr" },
43 { 'y', "year" },
44 { 'g', "genre" },
45 { 'u', "url" } };
46
47
48#define TOKEN_MATCH_CHAR(symbol, type)if (*tmp == (symbol)) { *newpos = tmp + 1; return coll_token_new
(type, ((void*)0)); }
if (*tmp == (symbol)) { *newpos = tmp + 1; return coll_token_new (type, NULL((void*)0)); }
49#define TOKEN_MATCH_STRING(expr, type)if (strncmp (expr, tmp, strlen (expr)) == 0) { *newpos = tmp +
strlen (expr); return coll_token_new (type, ((void*)0)); }
if (strncmp (expr, tmp, strlen (expr)) == 0) { *newpos = tmp + strlen (expr); return coll_token_new (type, NULL((void*)0)); }
50
51#define TOKEN_ASSERT(token, tktype)do { if (!token || (token->type != tktype)) { *ret = ((void
*)0); return tokens; } } while (0)
do { \
52 if (!token || (token->type != tktype)) { \
53 *ret = NULL((void*)0); \
54 return tokens; \
55 } \
56} while (0)
57
58#define PARSER_TRY(func)do { pos = func (tokens, &coll); if (coll) { *ret = coll;
return pos; } } while (0)
do { \
59 pos = func (tokens, &coll); \
60 if (coll) { \
61 *ret = coll; \
62 return pos; \
63 } \
64} while (0)
65
66
67static int coll_parse_prepare (xmmsv_coll_token_t *tokens);
68
69static xmmsv_coll_token_t *coll_parse_expr (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
70static xmmsv_coll_token_t *coll_parse_parenexpr (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
71static xmmsv_coll_token_t *coll_parse_sequence (xmmsv_coll_token_t *token, const char *field, xmmsv_t **ret);
72static xmmsv_coll_token_t *coll_parse_idseq (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
73static xmmsv_coll_token_t *coll_parse_posseq (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
74static xmmsv_coll_token_t *coll_parse_operation (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
75static xmmsv_coll_token_t *coll_parse_unaryop (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
76static xmmsv_coll_token_t *coll_parse_notop (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
77static xmmsv_coll_token_t *coll_parse_andop (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
78static xmmsv_coll_token_t *coll_parse_orop (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
79static xmmsv_coll_token_t *coll_parse_andop_append (xmmsv_coll_token_t *tokens, xmmsv_t *operator, xmmsv_t **ret);
80static xmmsv_coll_token_t *coll_parse_orop_append (xmmsv_coll_token_t *tokens, xmmsv_t *operator, xmmsv_t **ret);
81static xmmsv_coll_token_t *coll_parse_reference (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
82static xmmsv_coll_token_t *coll_parse_filter (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
83static xmmsv_coll_token_t *coll_parse_unaryfilter (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
84static xmmsv_coll_token_t *coll_parse_binaryfilter (xmmsv_coll_token_t *tokens, xmmsv_t **ret);
85static xmmsv_coll_token_t *coll_parse_autofilter (xmmsv_coll_token_t *token, xmmsv_t **ret);
86
87static xmmsv_coll_token_t *coll_token_new (xmmsv_coll_token_type_t type, char *string);
88static void coll_token_free (xmmsv_coll_token_t *token);
89static xmmsv_coll_token_t *coll_next_token (xmmsv_coll_token_t *token);
90static void coll_append_universe (xmmsv_t *coll);
91static char *coll_parse_prop (xmmsv_coll_token_t *token);
92static char *coll_parse_strval (xmmsv_coll_token_t *token);
93
94static char *string_substr (char *start, char *end);
95static char *string_intadd (char *number, int delta);
96
97
98
99/**
100 * @defgroup CollectionParser CollectionParser
101 * @ingroup Collections
102 * @brief Generate a collection structure from a string pattern.
103 *
104 * The grammar of the default parser is the following:
105 * <pre>
106 * S := OPERATION
107 * EXPR := POSSEQ | IDSEQ | FILTER | TOKEN_GROUP_OPEN OPERATION TOKEN_GROUP_CLOSE | UNARYOP
108 * PROP := TOKEN_PROP_LONG | TOKEN_PROP_SHORT
109 * INTVAL := INTEGER | SEQUENCE
110 * STRVAL := STRING | PATTERN
111 * POSSEQ := INTVAL
112 * IDSEQ := TOKEN_SYMBOL_ID INTVAL
113 * OPERATION := ANDOP
114 * UNAOP := TOKEN_OPSET_NOT EXPR | TOKEN_REFERENCE STRING
115 * ANDOP := OROP ANDOP | OROP TOKEN_OPSET_AND ANDOP | OROP
116 * OROP := EXPR TOKEN_OPSET_OR OROP | EXPR
117 * FILTER := UNAFILTER | BINFILTER | STRVAL
118 * UNAFILTER := TOKEN_OPFIL_HAS PROP
119 * BINFILTER := PROP TOKEN_OPFIL_EQUALS STRING | PROP TOKEN_OPFIL_MATCH STRVAL |
120 * PROP TOKEN_OPFIL_SMALLER INTEGER | PROP TOKEN_OPFIL_GREATER INTEGER |
121 * TOKEN_OPFIL_EQUALS STRING | TOKEN_OPFIL_MATCH STRVAL
122 * </pre>
123 *
124 * @{
125 */
126
127/**
128 * Try to parse the given pattern to produce a collection structure.
129 *
130 * @param pattern The string to generate a collection from.
131 * @param coll The pointer to which the collection will be saved.
132 * @return TRUE if the parsing succeeded, false otherwise.
133 */
134int
135xmmsv_coll_parse (const char *pattern, xmmsv_t** coll)
136{
137 return xmmsv_coll_parse_custom (pattern,
138 xmmsv_coll_default_parse_tokens,
139 xmmsv_coll_default_parse_build,
140 coll);
141}
142
143/**
144 * Try to parse the given pattern to produce a collection structure,
145 * using custom token-parsing and collection-building functions. This
146 * can be used to extend the default syntax of the parser.
147 *
148 * New token ids can be used, starting from
149 * XMMS_COLLECTION_TOKEN_CUSTOM upwards.
150 *
151 * @param pattern The string to generate a collection from.
152 * @param parse_f The parsing function used to generate a list of tokens
153 * from the pattern string.
154 * @param build_f The building function that produces the collection
155 * structure from the list of tokens.
156 * @param coll The pointer to which the collection will be saved.
157 * @return TRUE if the parsing succeeded, false otherwise.
158 */
159int
160xmmsv_coll_parse_custom (const char *pattern,
161 xmmsv_coll_parse_tokens_f parse_f,
162 xmmsv_coll_parse_build_f build_f,
163 xmmsv_t** coll)
164{
165 xmmsv_coll_token_t *tokens;
166 xmmsv_coll_token_t *k, *last;
167 const char *next, *endstr;
168
169 endstr = pattern + strlen (pattern);
170 tokens = NULL((void*)0);
171 last = NULL((void*)0);
172
173 /* Tokenize the string */
174 while (pattern < endstr) {
175 k = parse_f (pattern, &next);
176 if (k == NULL((void*)0) || k->type == XMMS_COLLECTION_TOKEN_INVALID) {
177 /* FIXME: Check for invalid token */
178 break;
179 }
180
181 if (!last)
182 tokens = k;
183 else
184 last->next = k;
185
186 last = k;
187 pattern = next;
188 }
189
190 *coll = build_f (tokens);
191
192 /* free tokens */
193 for (k = tokens; k; k = last) {
194 last = k->next;
195 coll_token_free (k);
196 }
197
198 return (*coll != NULL((void*)0));
199}
200
201
202/* FIXME:
203 - syntax should be case-insensitive here and there
204 - support in-string quoted parts, e.g. hello"test here"world
205 - optimize sequences (group, merge, create idlist, etc)
206*/
207/**
208 * The default token parser.
209 *
210 * @param str The string to parse for a token.
211 * @param newpos The position in the string after the found token.
212 * @return The token found in the string.
213 */
214xmmsv_coll_token_t*
215xmmsv_coll_default_parse_tokens (const char *str, const char **newpos)
216{
217 int i;
218 int escape = 0;
219 xmmsv_coll_token_type_t type;
220 const char *tmp;
221 char *strval;
222 char quote;
223
224 while (*str == ' ') str++;
225 if (*str == '\0') {
226 return NULL((void*)0);
227 }
228 tmp = str;
229
230 TOKEN_MATCH_CHAR ('(', XMMS_COLLECTION_TOKEN_GROUP_OPEN)if (*tmp == ('(')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_GROUP_OPEN, ((void*)0)); }
;
231 TOKEN_MATCH_CHAR (')', XMMS_COLLECTION_TOKEN_GROUP_CLOSE)if (*tmp == (')')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_GROUP_CLOSE, ((void*)0)); }
;
232 TOKEN_MATCH_CHAR ('#', XMMS_COLLECTION_TOKEN_SYMBOL_ID)if (*tmp == ('#')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_SYMBOL_ID, ((void*)0)); }
;
233 TOKEN_MATCH_CHAR ('+', XMMS_COLLECTION_TOKEN_OPFIL_HAS)if (*tmp == ('+')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_OPFIL_HAS, ((void*)0)); }
;
234 TOKEN_MATCH_CHAR (':', XMMS_COLLECTION_TOKEN_OPFIL_EQUALS)if (*tmp == (':')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_OPFIL_EQUALS, ((void*)0)); }
;
235 TOKEN_MATCH_CHAR ('~', XMMS_COLLECTION_TOKEN_OPFIL_MATCH)if (*tmp == ('~')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_OPFIL_MATCH, ((void*)0)); }
;
236 TOKEN_MATCH_STRING ("<=", XMMS_COLLECTION_TOKEN_OPFIL_SMALLEREQ)if (strncmp ("<=", tmp, strlen ("<=")) == 0) { *newpos =
tmp + strlen ("<="); return coll_token_new (XMMS_COLLECTION_TOKEN_OPFIL_SMALLEREQ
, ((void*)0)); }
;
237 TOKEN_MATCH_STRING (">=", XMMS_COLLECTION_TOKEN_OPFIL_GREATEREQ)if (strncmp (">=", tmp, strlen (">=")) == 0) { *newpos =
tmp + strlen (">="); return coll_token_new (XMMS_COLLECTION_TOKEN_OPFIL_GREATEREQ
, ((void*)0)); }
;
238 TOKEN_MATCH_CHAR ('<', XMMS_COLLECTION_TOKEN_OPFIL_SMALLER)if (*tmp == ('<')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_OPFIL_SMALLER, ((void*)0)); }
;
239 TOKEN_MATCH_CHAR ('>', XMMS_COLLECTION_TOKEN_OPFIL_GREATER)if (*tmp == ('>')) { *newpos = tmp + 1; return coll_token_new
(XMMS_COLLECTION_TOKEN_OPFIL_GREATER, ((void*)0)); }
;
240
241 TOKEN_MATCH_STRING ("OR", XMMS_COLLECTION_TOKEN_OPSET_UNION)if (strncmp ("OR", tmp, strlen ("OR")) == 0) { *newpos = tmp +
strlen ("OR"); return coll_token_new (XMMS_COLLECTION_TOKEN_OPSET_UNION
, ((void*)0)); }
;
242 TOKEN_MATCH_STRING ("AND", XMMS_COLLECTION_TOKEN_OPSET_INTERSECTION)if (strncmp ("AND", tmp, strlen ("AND")) == 0) { *newpos = tmp
+ strlen ("AND"); return coll_token_new (XMMS_COLLECTION_TOKEN_OPSET_INTERSECTION
, ((void*)0)); }
;
243 TOKEN_MATCH_STRING ("NOT", XMMS_COLLECTION_TOKEN_OPSET_COMPLEMENT)if (strncmp ("NOT", tmp, strlen ("NOT")) == 0) { *newpos = tmp
+ strlen ("NOT"); return coll_token_new (XMMS_COLLECTION_TOKEN_OPSET_COMPLEMENT
, ((void*)0)); }
;
244
245 TOKEN_MATCH_STRING ("in:", XMMS_COLLECTION_TOKEN_REFERENCE)if (strncmp ("in:", tmp, strlen ("in:")) == 0) { *newpos = tmp
+ strlen ("in:"); return coll_token_new (XMMS_COLLECTION_TOKEN_REFERENCE
, ((void*)0)); }
;
246
247 /* Starting with double-quote => STRING or PATTERN */
248 if (*tmp == '"' || *tmp == '\'') {
249 i = 0;
250 quote = *tmp;
251 type = XMMS_COLLECTION_TOKEN_STRING;
252
253 tmp++;
254 strval = x_new0 (char, strlen (tmp) + 1)calloc (1, sizeof (char) * (strlen (tmp) + 1));
255
256 while (*tmp != '\0' && (escape || *tmp != quote)) {
257 if (!escape && (*tmp == '\\')) {
258 escape = 1;
259 } else {
260 if (escape) {
261 escape = 0;
262 } else if ((*tmp == '*') || (*tmp == '?')) {
263 type = XMMS_COLLECTION_TOKEN_PATTERN;
264 }
265 strval[i++] = *tmp;
266 }
267
268 tmp++;
269 }
270
271 if (*tmp == quote) tmp++;
272
273 *newpos = tmp;
274
275 goto out;
276 }
277
278
279 i = 0;
280 type = XMMS_COLLECTION_TOKEN_INTEGER;
281 strval = x_new0 (char, strlen (tmp) + 1)calloc (1, sizeof (char) * (strlen (tmp) + 1));
282 while (*tmp != '\0' && (escape || *tmp != ' ')) {
283
284 /* Control input chars, escape mechanism, etc */
285 if (!escape) {
286 if (*tmp == '\\') {
287 escape = 1;
288 tmp++;
289 continue;
290 } else if (*tmp == ':' || *tmp == '~' ||
291 *tmp == '<' || *tmp == '>') {
292 /* that was a property name, ends with a colon */
293 if (tmp - str == 1)
294 type = XMMS_COLLECTION_TOKEN_PROP_SHORT;
295 else
296 type = XMMS_COLLECTION_TOKEN_PROP_LONG;
297 break;
298 } else if (*tmp == '(' || *tmp == ')') {
299 /* boundary char, stop parsing the string */
300 break;
301 }
302 }
303
304 /* Determine the type of data */
305 switch (type) {
306 /* matches [0-9] => INTEGER */
307 case XMMS_COLLECTION_TOKEN_INTEGER:
308 if (*tmp == ',' || *tmp == '-') {
309 type = XMMS_COLLECTION_TOKEN_SEQUENCE;
310 break;
311 }
312
313 /* matches [0-9,-] => SEQUENCE */
314 case XMMS_COLLECTION_TOKEN_SEQUENCE:
315 if (!isdigit (*tmp)((*__ctype_b_loc ())[(int) ((*tmp))] & (unsigned short int
) _ISdigit)
&& (*tmp != ',') && (*tmp != '-')) {
316 type = XMMS_COLLECTION_TOKEN_STRING;
317 }
318
319 /* contains [*?] => PATTERN */
320 case XMMS_COLLECTION_TOKEN_STRING:
321 if (!escape && (*tmp == '*' || *tmp == '?')) {
322 type = XMMS_COLLECTION_TOKEN_PATTERN;
323 }
324 break;
325
326 /* else => STRING */
327 case XMMS_COLLECTION_TOKEN_PATTERN:
328 break;
329
330 default:
331 type = XMMS_COLLECTION_TOKEN_INVALID; /* shouldn't happen */
332 break;
333 }
334
335 if (escape) {
336 escape = 0;
337 }
338
339 strval[i++] = *tmp;
340 tmp++;
341 }
342
343 *newpos = tmp;
344
345out:
346
347 /* Did we encounter a trailing backslash? */
348 if (escape) {
349 free (strval);
350
351 return NULL((void*)0);
352 }
353
354 return coll_token_new (type, strval);
355}
356
357
358/**
359 * Default collection structure builder.
360 *
361 * @param tokens The chained list of tokens.
362 * @return The corresponding collection structure.
363 */
364xmmsv_t *
365xmmsv_coll_default_parse_build (xmmsv_coll_token_t *tokens)
366{
367 xmmsv_coll_token_t *tk;
368 xmmsv_t *coll;
369
370 coll_parse_prepare (tokens);
371 tk = coll_parse_operation (tokens, &coll);
372
373 /* Failed to parse the whole string! */
374 if (tk && coll) {
375 xmmsv_unref (coll);
376 coll = NULL((void*)0);
377 }
378
379 return coll;
380}
381
382
383/* Pre-process the token list to apply contextual updates to tokens. */
384static int
385coll_parse_prepare (xmmsv_coll_token_t *tokens)
386{
387 xmmsv_coll_token_t *prev, *curr;
388
389 prev = NULL((void*)0);
390 curr = tokens;
Value stored to 'curr' is never read
391
392 for (prev = NULL((void*)0), curr = tokens; curr; prev = curr, curr = curr->next) {
393 if (prev == NULL((void*)0)) {
394 continue;
395 }
396
397 /* Process depending on type of current token */
398 switch (curr->type) {
399 case XMMS_COLLECTION_TOKEN_OPFIL_GREATEREQ:
400 case XMMS_COLLECTION_TOKEN_OPFIL_SMALLEREQ:
401 case XMMS_COLLECTION_TOKEN_OPFIL_GREATER:
402 case XMMS_COLLECTION_TOKEN_OPFIL_SMALLER:
403 /* '<' and '>' preceded by a prop name, seen as string if spaced */
404 if (prev->type == XMMS_COLLECTION_TOKEN_STRING) {
405 if (strlen (prev->string) == 1)
406 prev->type = XMMS_COLLECTION_TOKEN_PROP_SHORT;
407 else
408 prev->type = XMMS_COLLECTION_TOKEN_PROP_LONG;
409 }
410 break;
411
412 default:
413 break;
414 }
415
416 /* Process depending on type of previous token */
417 switch (prev->type) {
418 case XMMS_COLLECTION_TOKEN_OPFIL_HAS:
419 /* _HAS is followed by a property name */
420 if (curr->type == XMMS_COLLECTION_TOKEN_STRING) {
421 if (strlen (curr->string) == 1)
422 curr->type = XMMS_COLLECTION_TOKEN_PROP_SHORT;
423 else
424 curr->type = XMMS_COLLECTION_TOKEN_PROP_LONG;
425 }
426 break;
427
428 /* Warning: must be placed _before_ the EQUALS->MATCH converter! */
429 case XMMS_COLLECTION_TOKEN_OPFIL_MATCH:
430 /* Integers are interpreted as strings in this case */
431 if (curr->type == XMMS_COLLECTION_TOKEN_INTEGER) {
432 curr->type = XMMS_COLLECTION_TOKEN_STRING;
433 }
434
435 /* Fuzzy match the operand to MATCH, i.e. surround with '*' */
436 if (curr->type == XMMS_COLLECTION_TOKEN_STRING ||
437 curr->type == XMMS_COLLECTION_TOKEN_PATTERN) {
438 int i, o;
439 char *newstr = x_new0 (char, strlen (curr->string) + 3)calloc (1, sizeof (char) * (strlen (curr->string) + 3));
440 i = 0;
441 o = 0;
442
443 if (curr->string[i] != '*') {
444 newstr[o++] = '*';
445 }
446 while (curr->string[i] != '\0') {
447 newstr[o++] = curr->string[i++];
448 }
449 if (i > 0 && curr->string[i - 1] != '*') {
450 newstr[o++] = '*';
451 }
452 newstr[o] = '\0';
453
454 free (curr->string);
455 curr->string = newstr;
456 }
457 break;
458
459 case XMMS_COLLECTION_TOKEN_OPFIL_EQUALS:
460 /* If MATCHing a pattern, use CONTAINS instead */
461 if (curr->type == XMMS_COLLECTION_TOKEN_PATTERN) {
462 prev->type = XMMS_COLLECTION_TOKEN_OPFIL_MATCH;
463
464 /* Integers are interpreted as strings in this case */
465 } else if (curr->type == XMMS_COLLECTION_TOKEN_INTEGER) {
466 curr->type = XMMS_COLLECTION_TOKEN_STRING;
467 }
468 break;
469
470 default:
471 break;
472 }
473 }
474
475 return 1;
476}
477
478static xmmsv_coll_token_t *
479coll_parse_expr (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
480{
481 xmmsv_t *coll;
482 xmmsv_coll_token_t *pos;
483
484 if (tokens == NULL((void*)0)) {
485 *ret = NULL((void*)0);
486 return tokens;
487 }
488
489 PARSER_TRY (coll_parse_posseq)do { pos = coll_parse_posseq (tokens, &coll); if (coll) {
*ret = coll; return pos; } } while (0)
;
490 PARSER_TRY (coll_parse_idseq)do { pos = coll_parse_idseq (tokens, &coll); if (coll) { *
ret = coll; return pos; } } while (0)
;
491 PARSER_TRY (coll_parse_filter)do { pos = coll_parse_filter (tokens, &coll); if (coll) {
*ret = coll; return pos; } } while (0)
;
492 PARSER_TRY (coll_parse_parenexpr)do { pos = coll_parse_parenexpr (tokens, &coll); if (coll
) { *ret = coll; return pos; } } while (0)
;
493 PARSER_TRY (coll_parse_unaryop)do { pos = coll_parse_unaryop (tokens, &coll); if (coll) {
*ret = coll; return pos; } } while (0)
;
494
495 *ret = NULL((void*)0);
496 return tokens;
497}
498
499static xmmsv_coll_token_t *
500coll_parse_parenexpr (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
501{
502 xmmsv_coll_token_t *tk;
503 xmmsv_t *expr;
504
505 tk = tokens;
506 TOKEN_ASSERT (tk, XMMS_COLLECTION_TOKEN_GROUP_OPEN)do { if (!tk || (tk->type != XMMS_COLLECTION_TOKEN_GROUP_OPEN
)) { *ret = ((void*)0); return tokens; } } while (0)
;
507
508 tk = coll_parse_operation (coll_next_token (tk), &expr);
509
510 /* paren mismatch :-/ */
511 if (!tk || tk->type != XMMS_COLLECTION_TOKEN_GROUP_CLOSE) {
512 if (expr) {
513 xmmsv_unref (expr);
514 }
515 *ret = NULL((void*)0);
516 return tokens;
517 }
518
519 *ret = expr;
520 return coll_next_token (tk);
521}
522
523
524static xmmsv_coll_token_t *
525coll_parse_sequence (xmmsv_coll_token_t *tokens, const char *field,
526 xmmsv_t **ret)
527{
528 char *start, *end, *seq, *num;
529 xmmsv_t *coll, *parent;
530 int id = strcmp (field, "id") == 0;
531
532 if (!tokens || (tokens->type != XMMS_COLLECTION_TOKEN_INTEGER &&
533 tokens->type != XMMS_COLLECTION_TOKEN_SEQUENCE)) {
534 *ret = NULL((void*)0);
535 return tokens;
536 }
537
538 start = tokens->string;
539 end = strchr (start, ',');
540
541 /* Take the union if several element in the sequence */
542 if (end != NULL((void*)0)) {
543 parent = xmmsv_new_coll (XMMS_COLLECTION_TYPE_UNION);
544 } else {
545 parent = NULL((void*)0);
546 end = start + strlen (start);
547 }
548
549 while (1) {
550 seq = strchr (start, '-');
551
552 /* Contains a '-', parse the sequence */
553 if (seq != NULL((void*)0) && seq < end) {
554 int len_from, len_to;
555 xmmsv_t *coll_from, *coll_to;
556 char *buf;
557
558 len_from = seq - start;
559 len_to = end - seq - 1;
560
561 if (len_from > 0) {
562 buf = string_substr (start, seq);
563 num = string_intadd (buf, -1);
564 coll_from = xmmsv_new_coll (XMMS_COLLECTION_TYPE_GREATER);
565 if (id) {
566 xmmsv_coll_attribute_set_string (coll_from, "type", "id");
567 } else {
568 xmmsv_coll_attribute_set_string (coll_from, "field", field);
569 }
570 xmmsv_coll_attribute_set_string (coll_from, "value", num);
571 coll_append_universe (coll_from);
572 free (buf);
573 free (num);
574 } else {
575 coll_from = xmmsv_new_coll (XMMS_COLLECTION_TYPE_UNIVERSE);
576 }
577
578 if (len_to > 0) {
579 buf = string_substr (seq + 1, end);
580 num = string_intadd (buf, 1);
581 coll_to = xmmsv_new_coll (XMMS_COLLECTION_TYPE_SMALLER);
582 if (id) {
583 xmmsv_coll_attribute_set_string (coll_to, "type", "id");
584 } else {
585 xmmsv_coll_attribute_set_string (coll_to, "field", field);
586 }
587 xmmsv_coll_attribute_set_string (coll_to, "value", num);
588 xmmsv_coll_add_operand (coll_to, coll_from);
589 xmmsv_unref (coll_from);
590 free (buf);
591 free (num);
592 } else {
593 coll_to = coll_from;
594 }
595
596 coll = coll_to;
597
598 /* Just an integer, match it */
599 } else {
600 num = string_substr (start, end);
601 coll = xmmsv_new_coll (XMMS_COLLECTION_TYPE_EQUALS);
602 if (id) {
603 xmmsv_coll_attribute_set_string (coll, "type", "id");
604 } else {
605 xmmsv_coll_attribute_set_string (coll, "field", field);
606 }
607 xmmsv_coll_attribute_set_string (coll, "value", num);
608 coll_append_universe (coll);
609 free (num);
610 }
611
612 if (parent) {
613 xmmsv_coll_add_operand (parent, coll);
614 }
615
616 /* Whole string parsed, exit */
617 if (*end == '\0') {
618 break;
619 }
620
621 start = end + 1;
622 end = strchr (start, ',');
623 if (end == NULL((void*)0)) {
624 end = start + strlen (start);
625 }
626 }
627
628 if (parent) {
629 coll = parent;
630 }
631
632 *ret = coll;
633 return coll_next_token (tokens);
634}
635
636static xmmsv_coll_token_t *
637coll_parse_idseq (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
638{
639 xmmsv_coll_token_t *tk;
640
641 tk = tokens;
642 TOKEN_ASSERT (tk, XMMS_COLLECTION_TOKEN_SYMBOL_ID)do { if (!tk || (tk->type != XMMS_COLLECTION_TOKEN_SYMBOL_ID
)) { *ret = ((void*)0); return tokens; } } while (0)
;
643
644 tk = coll_next_token (tk);
645 tk = coll_parse_sequence (tk, "id", ret);
646
647 return (ret == NULL((void*)0) ? tokens : tk);
648}
649
650static xmmsv_coll_token_t *
651coll_parse_posseq (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
652{
653 /* FIXME: link with position in (active) playlist? */
654 return coll_parse_sequence (tokens, "position", ret);
655}
656
657static xmmsv_coll_token_t *
658coll_parse_operation (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
659{
660 return coll_parse_andop (tokens, ret);
661}
662
663static xmmsv_coll_token_t *
664coll_parse_unaryop (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
665{
666 xmmsv_t *coll;
667 xmmsv_coll_token_t *pos;
668
669 PARSER_TRY (coll_parse_notop)do { pos = coll_parse_notop (tokens, &coll); if (coll) { *
ret = coll; return pos; } } while (0)
;
670 PARSER_TRY (coll_parse_reference)do { pos = coll_parse_reference (tokens, &coll); if (coll
) { *ret = coll; return pos; } } while (0)
;
671
672 *ret = NULL((void*)0);
673 return tokens;
674}
675
676static xmmsv_coll_token_t *
677coll_parse_notop (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
678{
679 xmmsv_t *coll;
680 xmmsv_t *operand;
681 xmmsv_coll_token_t *tk = tokens;
682
683 TOKEN_ASSERT (tk, XMMS_COLLECTION_TOKEN_OPSET_COMPLEMENT)do { if (!tk || (tk->type != XMMS_COLLECTION_TOKEN_OPSET_COMPLEMENT
)) { *ret = ((void*)0); return tokens; } } while (0)
;
684
685 tk = coll_parse_expr (coll_next_token (tk), &operand);
686 if (!operand) {
687 *ret = NULL((void*)0);
688 return tokens;
689 }
690
691 coll = xmmsv_new_coll (XMMS_COLLECTION_TYPE_COMPLEMENT);
692 xmmsv_coll_add_operand (coll, operand);
693 xmmsv_unref (operand);
694
695 *ret = coll;
696 return tk;
697}
698
699static xmmsv_coll_token_t *
700coll_parse_andop (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
701{
702 return coll_parse_andop_append (tokens, NULL((void*)0), ret);
703}
704
705static xmmsv_coll_token_t *
706coll_parse_orop (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
707{
708 return coll_parse_orop_append (tokens, NULL((void*)0), ret);
709}
710
711static xmmsv_coll_token_t *
712coll_parse_andop_append (xmmsv_coll_token_t *tokens, xmmsv_t *operator,
713 xmmsv_t **ret)
714{
715 xmmsv_t *first, *tmp;
716 xmmsv_coll_token_t *tk;
717
718 tk = coll_parse_orop (tokens, &first);
719 if (!first) {
720 *ret = NULL((void*)0);
721 return tokens;
722 }
723
724 /* skip the AND operator if present */
725 if (tk && tk->type == XMMS_COLLECTION_TOKEN_OPSET_INTERSECTION) {
726 tk = coll_next_token (tk);
727 }
728
729 if (!operator) {
730 operator = xmmsv_new_coll (XMMS_COLLECTION_TYPE_INTERSECTION);
731 xmmsv_coll_add_operand (operator, first);
732 tk = coll_parse_andop_append (tk, operator, &tmp);
733
734 /* actually, only one operand, bypass the 'AND' altogether */
735 if (tmp == NULL((void*)0)) {
736 xmmsv_coll_remove_operand (operator, first);
737 xmmsv_unref (operator);
738 *ret = first;
739 }
740 else {
741 xmmsv_unref (first);
742 *ret = operator;
743 }
744 }
745 else {
746 xmmsv_coll_add_operand (operator, first);
747 xmmsv_unref (first);
748
749 tk = coll_parse_andop_append (tk, operator, &tmp);
750 *ret = operator;
751 }
752
753 /* tk = coll_parse_andop_append (tk, operator, ret); */
754 return tk;
755}
756
757static xmmsv_coll_token_t *
758coll_parse_orop_append (xmmsv_coll_token_t *tokens, xmmsv_t *operator,
759 xmmsv_t **ret)
760{
761 xmmsv_t *first;
762 xmmsv_coll_token_t *tk;
763
764 tk = coll_parse_expr (tokens, &first);
765 if (!first) {
766 *ret = NULL((void*)0);
767 return tokens;
768 }
769
770 if (tk && tk->type == XMMS_COLLECTION_TOKEN_OPSET_UNION) {
771 if (!operator) {
772 operator = xmmsv_new_coll (XMMS_COLLECTION_TYPE_UNION);
773 }
774 }
775
776 if (operator) {
777 xmmsv_coll_add_operand (operator, first);
778 xmmsv_unref (first);
779
780 if (tk && tk->type == XMMS_COLLECTION_TOKEN_OPSET_UNION) {
781 tk = coll_parse_orop_append (coll_next_token (tk), operator, ret);
782 }
783
784 *ret = operator;
785 }
786 else {
787 *ret = first;
788 }
789
790 return tk;
791}
792
793static xmmsv_coll_token_t *
794coll_parse_reference (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
795{
796 xmmsv_t *coll;
797 char *namespace, *reference, *slash;
798 xmmsv_coll_token_t *tk = tokens;
799
800 TOKEN_ASSERT (tk, XMMS_COLLECTION_TOKEN_REFERENCE)do { if (!tk || (tk->type != XMMS_COLLECTION_TOKEN_REFERENCE
)) { *ret = ((void*)0); return tokens; } } while (0)
;
801
802 tk = coll_next_token (tk);
803
804 TOKEN_ASSERT (tk, XMMS_COLLECTION_TOKEN_STRING)do { if (!tk || (tk->type != XMMS_COLLECTION_TOKEN_STRING)
) { *ret = ((void*)0); return tokens; } } while (0)
;
805
806 slash = strchr (tk->string, '/');
807 if (slash != NULL((void*)0) && slash > tk->string) {
808 namespace = string_substr (tk->string, slash);
809 }
810 else {
811 namespace = strdup (XMMS_COLLECTION_PARSER_DEFAULT_NAMESPACE"Collections");
812 }
813
814 if (slash == NULL((void*)0)) {
815 reference = tk->string;
816 }
817 else {
818 reference = slash + 1;
819 }
820
821 coll = xmmsv_new_coll (XMMS_COLLECTION_TYPE_REFERENCE);
822 xmmsv_coll_attribute_set_string (coll, "namespace", namespace);
823 xmmsv_coll_attribute_set_string (coll, "reference", reference);
824
825 free (namespace);
826
827 *ret = coll;
828 return coll_next_token (tk);
829}
830
831
832static xmmsv_coll_token_t *
833coll_parse_filter (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
834{
835 xmmsv_t *coll;
836 xmmsv_coll_token_t *pos;
837
838 PARSER_TRY (coll_parse_unaryfilter)do { pos = coll_parse_unaryfilter (tokens, &coll); if (coll
) { *ret = coll; return pos; } } while (0)
;
839 PARSER_TRY (coll_parse_binaryfilter)do { pos = coll_parse_binaryfilter (tokens, &coll); if (coll
) { *ret = coll; return pos; } } while (0)
;
840
841 /* Recognize a seperate '*' as the universe collection */
842 /* FIXME: This should not be in this function!
843 See bug report 2196 for explanation why it yet is here. */
844 if (tokens->type == XMMS_COLLECTION_TOKEN_PATTERN &&
845 strcmp(tokens->string, "*") == 0) {
846 *ret = xmmsv_new_coll (XMMS_COLLECTION_TYPE_UNIVERSE);
847 return coll_next_token (tokens);
848 }
849
850 PARSER_TRY (coll_parse_autofilter)do { pos = coll_parse_autofilter (tokens, &coll); if (coll
) { *ret = coll; return pos; } } while (0)
;
851
852 *ret = NULL((void*)0);
853 return tokens;
854}
855
856static xmmsv_coll_token_t *
857coll_parse_unaryfilter (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
858{
859 xmmsv_t *coll;
860 char *prop;
861 xmmsv_coll_token_t *tk = tokens;
862
863 TOKEN_ASSERT (tk, XMMS_COLLECTION_TOKEN_OPFIL_HAS)do { if (!tk || (tk->type != XMMS_COLLECTION_TOKEN_OPFIL_HAS
)) { *ret = ((void*)0); return tokens; } } while (0)
;
864
865 tk = coll_next_token (tk);
866 prop = coll_parse_prop (tk);
867 if (!prop) {
868 *ret = NULL((void*)0);
869 return tokens;
870 }
871
872 coll = xmmsv_new_coll (XMMS_COLLECTION_TYPE_HAS);
873 xmmsv_coll_attribute_set_string (coll, "field", prop);
874 coll_append_universe (coll);
875
876 free (prop);
877
878 *ret = coll;
879 return coll_next_token (tk);
880}
881
882static xmmsv_coll_token_t *
883coll_parse_binaryfilter (xmmsv_coll_token_t *tokens, xmmsv_t **ret)
884{
885 char *prop, *strval;
886 xmmsv_coll_type_t operation;
887 xmmsv_t *coll = NULL((void*)0);
888 xmmsv_coll_token_t *operand;
889 xmmsv_coll_token_t *tk = tokens;
890
891 if (!tk) {
892 *ret = NULL((void*)0);
893 return tokens;
894 }
895
896 prop = coll_parse_prop (tk);
897 if (!prop) {
898 return NULL((void*)0);
899 }
900
901 tk = coll_next_token (tk);
902 operand = coll_next_token (tk);
903 if (tk && operand) {
904 strval = NULL((void*)0);
905
906 switch (tk->type) {
907 case XMMS_COLLECTION_TOKEN_OPFIL_EQUALS:
908 operation = XMMS_COLLECTION_TYPE_EQUALS;
909 if (operand->type == XMMS_COLLECTION_TOKEN_STRING) {
910 strval = operand->string;
911 }
912 break;
913
914 case XMMS_COLLECTION_TOKEN_OPFIL_MATCH:
915 operation = XMMS_COLLECTION_TYPE_MATCH;
916 strval = coll_parse_strval (operand);
917 break;
918
919 case XMMS_COLLECTION_TOKEN_OPFIL_SMALLER:
920 operation = XMMS_COLLECTION_TYPE_SMALLER;
921 if (operand->type == XMMS_COLLECTION_TOKEN_INTEGER) {
922 strval = operand->string;
923 }
924 break;
925
926 case XMMS_COLLECTION_TOKEN_OPFIL_GREATER:
927 operation = XMMS_COLLECTION_TYPE_GREATER;
928 if (operand->type == XMMS_COLLECTION_TOKEN_INTEGER) {
929 strval = operand->string;
930 }
931 break;
932
933 case XMMS_COLLECTION_TOKEN_OPFIL_SMALLEREQ:
934 operation = XMMS_COLLECTION_TYPE_SMALLEREQ;
935 if (operand->type == XMMS_COLLECTION_TOKEN_INTEGER) {
936 strval = operand->string;
937 }
938 break;
939
940 case XMMS_COLLECTION_TOKEN_OPFIL_GREATEREQ:
941 operation = XMMS_COLLECTION_TYPE_GREATEREQ;
942 if (operand->type == XMMS_COLLECTION_TOKEN_INTEGER) {
943 strval = operand->string;
944 }
945 break;
946
947 default:
948 break;
949 }
950
951 if (strval) {
952 coll = xmmsv_new_coll (operation);
953 xmmsv_coll_attribute_set_string (coll, "field", prop);
954 xmmsv_coll_attribute_set_string (coll, "value", strval);
955 coll_append_universe (coll);
956 }
957 }
958
959 free (prop);
960
961 *ret = coll;
962 return coll_next_token (operand);
963}
964
965static xmmsv_coll_token_t *
966coll_parse_autofilter (xmmsv_coll_token_t *token, xmmsv_t **ret)
967{
968 char *strval;
969 xmmsv_coll_type_t operation;
970 xmmsv_t *coll, *operand;
971 int i;
972 /* Properties to match by default. */
973 const char *coll_autofilter[] = { "artist", "album", "title", NULL((void*)0) };
974
975 if (token->type == XMMS_COLLECTION_TOKEN_OPFIL_EQUALS) {
976 operation = XMMS_COLLECTION_TYPE_EQUALS;
977 token = coll_next_token (token);
978 } else if (token->type == XMMS_COLLECTION_TOKEN_OPFIL_MATCH) {
979 operation = XMMS_COLLECTION_TYPE_MATCH;
980 token = coll_next_token (token);
981 } else {
982 /* No operator at all, guess from argument type */
983 if (token->type == XMMS_COLLECTION_TOKEN_PATTERN)
984 operation = XMMS_COLLECTION_TYPE_MATCH;
985 else
986 operation = XMMS_COLLECTION_TYPE_TOKEN;
987 }
988
989 strval = coll_parse_strval (token);
990 if (!strval) {
991 *ret = NULL((void*)0);
992 return token;
993 }
994
995 coll = xmmsv_new_coll (XMMS_COLLECTION_TYPE_UNION);
996
997 for (i = 0; coll_autofilter[i] != NULL((void*)0); i++) {
998 operand = xmmsv_new_coll (operation);
999 xmmsv_coll_attribute_set_string (operand, "field", coll_autofilter[i]);
1000 xmmsv_coll_attribute_set_string (operand, "value", strval);
1001 xmmsv_coll_add_operand (coll, operand);
1002 coll_append_universe (operand);
1003 xmmsv_unref (operand);
1004 }
1005
1006 *ret = coll;
1007 return coll_next_token (token);
1008}
1009
1010
1011static xmmsv_coll_token_t *
1012coll_token_new (xmmsv_coll_token_type_t type, char *string)
1013{
1014 xmmsv_coll_token_t* token;
1015
1016 token = x_new0 (xmmsv_coll_token_t, 1)calloc (1, sizeof (xmmsv_coll_token_t) * (1));
1017 token->type = type;
1018 token->string = string;
1019
1020 return token;
1021}
1022
1023static void
1024coll_token_free (xmmsv_coll_token_t *token)
1025{
1026 if (token->string != NULL((void*)0)) {
1027 free (token->string);
1028 }
1029
1030 free (token);
1031}
1032
1033static xmmsv_coll_token_t *
1034coll_next_token (xmmsv_coll_token_t *token)
1035{
1036 return (token ? token->next : NULL((void*)0));
1037}
1038
1039static void
1040coll_append_universe (xmmsv_t *coll)
1041{
1042 xmmsv_t *univ;
1043
1044 univ = xmmsv_new_coll (XMMS_COLLECTION_TYPE_UNIVERSE);
1045 xmmsv_coll_add_operand (coll, univ);
1046 xmmsv_unref (univ);
1047}
1048
1049static char *
1050coll_parse_prop (xmmsv_coll_token_t *token)
1051{
1052 int i;
1053
1054 if (!token || !token->string) {
1055 return NULL((void*)0);
1056 }
1057
1058 switch (token->type) {
1059 case XMMS_COLLECTION_TOKEN_PROP_SHORT:
1060 /* try to find short prop, else fallback to long prop */
1061 for (i = 0; i < X_N_ELEMENTS (xmmsv_coll_prop_short)(sizeof (xmmsv_coll_prop_short) / sizeof ((xmmsv_coll_prop_short
)[0]))
; i++) {
1062 if (*token->string == xmmsv_coll_prop_short[i].shortstr) {
1063 return strdup (xmmsv_coll_prop_short[i].longstr);
1064 }
1065 }
1066
1067 case XMMS_COLLECTION_TOKEN_PROP_LONG:
1068 return strdup (token->string);
1069
1070 default:
1071 break;
1072 }
1073
1074 return NULL((void*)0);
1075}
1076
1077static char *
1078coll_parse_strval (xmmsv_coll_token_t *token)
1079{
1080 if (!token || (token->type != XMMS_COLLECTION_TOKEN_STRING &&
1081 token->type != XMMS_COLLECTION_TOKEN_PATTERN)) {
1082 return NULL((void*)0);
1083 }
1084
1085 return token->string;
1086}
1087
1088/* Create a new string from a substring of an existing string, between
1089 * start and end.
1090 */
1091static char *
1092string_substr (char *start, char *end)
1093{
1094 int len;
1095 char *buf;
1096
1097 len = end - start;
1098 buf = x_new0 (char, len + 1)calloc (1, sizeof (char) * (len + 1));
1099 strncpy (buf, start, len);
1100 buf[len] = '\0';
1101
1102 return buf;
1103}
1104
1105/* Given a string containing a number, add the given delta to that
1106 * number and produce a new string with the result. The returned
1107 * string must be freed afterwards.
1108 */
1109static char *
1110string_intadd (char *number, int delta)
1111{
1112 int n, len;
1113 char *endptr;
1114 char *buf;
1115
1116 n = strtol (number, &endptr, 10);
1117
1118 /* Invalid integer string */
1119 if (*endptr != '\0') {
1120 return NULL((void*)0);
1121 }
1122
1123 n += delta;
1124 len = strlen (number) + 1;
1125 buf = x_new0 (char, len + 1)calloc (1, sizeof (char) * (len + 1));
1126 snprintf (buf, len, "%d", n);
1127
1128 return buf;
1129}
1130
1131/** @} */