Bug Summary

File:build-analysis/../src/clients/nycli/playlist_positions.c
Warning:line 131, column 26
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'i')

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 program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program 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 * General Public License for more details.
15 */
16
17#include "playlist_positions.h"
18
19#include <stdlib.h>
20#include <string.h>
21
22/** The goal is to parse the following grammar:
23 *
24 * positions := sequence | curr-context | curr-sequence
25 * sequence := interval | sequence "," interval
26 * curr-context := integer "_" integer | "_" integer | integer "_" | "_"
27 * curr-sequence := "+" sequence | "-" sequence
28 * interval := integer | integer "-" integer | "*" "-" integer | integer "-" "*"
29 * integer := digit | integer digit
30 * digit := [0-9]
31 */
32
33/* Note: duplicates are excluded during the construction */
34struct playlist_positions_St {
35 gint cached_currpos; /* Needed for relative positions */
36 GList *atoms; /* Int atoms, in decreasing order */
37 GList *intervals; /* Intervals, in decreasing order */
38};
39
40struct interval_St {
41 gint start;
42 gint end;
43};
44
45typedef struct interval_St interval_t;
46
47typedef enum {
48 INTERVAL_IS_GREATER,
49 ATOM_IS_GREATER
50} interval_vs_atom_t;
51
52#define NEXT_POSITION(list, forward)forward ? ((list) ? (((GList *)(list))->prev) : ((void*)0)
) : ((list) ? (((GList *)(list))->next) : ((void*)0))
forward ? g_list_previous (list)((list) ? (((GList *)(list))->prev) : ((void*)0)) : g_list_next (list)((list) ? (((GList *)(list))->next) : ((void*)0))
53
54static int playlist_positions_count (playlist_positions_t *positions);
55static gboolean playlist_positions_parse_token (const gchar *expr, playlist_positions_t *p);
56static gboolean playlist_positions_parse_sequence (const gchar *expr, playlist_positions_t *p);
57static gboolean playlist_positions_parse_currcontext (const gchar *expr, playlist_positions_t *p);
58static gboolean playlist_positions_parse_relsequence (const gchar *expr, playlist_positions_t *p);
59static playlist_positions_t *playlist_positions_new (gint currpos);
60static void playlist_positions_add_atom (playlist_positions_t *positions, gint x);
61static gboolean playlist_positions_add_interval (playlist_positions_t *positions, gint start, gint end);
62static gboolean playlist_positions_intervals_contains_atom (playlist_positions_t *positions, gint x);
63
64static interval_t *interval_new (gint start, gint end);
65static void interval_free (interval_t *ival);
66static interval_vs_atom_t interval_atom_list_cmp (GList *intervals, GList *atoms, gboolean forward);
67static gboolean interval_parse (const gchar *expr, gint *start, gint *end);
68static void interval_foreach (interval_t *ival, playlist_positions_func f, gboolean forward, void *userdata);
69
70
71/* FIXME: not positions vs syntax error! */
72/* FIXME: allow forward and backward foreach */
73/* FIXME: implement for: add, move, jump, info, search/list? */
74
75gboolean
76playlist_positions_parse (const gchar *expr, playlist_positions_t **p,
77 gint currpos)
78{
79 gchar **tokens;
80 playlist_positions_t *positions;
81 gint i = 0;
82 gboolean success = TRUE(!(0));
83
84 /* Empty/NULL string, or invalid chars, don't bother */
85 if (!expr || !*expr || strspn (expr, "0123456789,_+- ") != strlen (expr)) {
86 return FALSE(0);
87 }
88
89 tokens = g_strsplit (expr, " ", 0);
90 positions = playlist_positions_new (currpos);
91
92 while (tokens[i]) {
93 if (!playlist_positions_parse_token (tokens[i], positions)) {
94 success = FALSE(0);
95 break;
96 }
97 i++;
98 }
99
100 g_strfreev (tokens);
101
102 if (success) {
103 *p = positions;
104 } else {
105 playlist_positions_free (positions);
106 }
107
108 return success;
109}
110
111void
112playlist_positions_foreach (playlist_positions_t *positions,
113 playlist_positions_func f, gboolean forward,
114 void *userdata)
115{
116 GList *i, *a;
117 interval_t *ival;
118 int atom;
119
120 if (forward) {
1
Assuming 'forward' is not equal to 0
2
Taking true branch
121 i = g_list_last (positions->intervals);
3
Value assigned to 'i'
122 a = g_list_last (positions->atoms);
123 } else {
124 i = positions->intervals;
125 a = positions->atoms;
126 }
127
128 while (i || a) {
4
Assuming 'i' is null
5
Loop condition is true. Entering loop body
129 switch (interval_atom_list_cmp (i, a, forward)) {
6
Control jumps to 'case INTERVAL_IS_GREATER:' at line 130
130 case INTERVAL_IS_GREATER:
131 ival = (interval_t *) i->data;
7
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'i')
132 interval_foreach (ival, f, forward, userdata);
133 i = NEXT_POSITION (i, forward)forward ? ((i) ? (((GList *)(i))->prev) : ((void*)0)) : ((
i) ? (((GList *)(i))->next) : ((void*)0))
;
134 break;
135 case ATOM_IS_GREATER:
136 atom = GPOINTER_TO_INT (a->data)((gint) (glong) (a->data));
137 f (atom, userdata);
138 a = NEXT_POSITION (a, forward)forward ? ((a) ? (((GList *)(a))->prev) : ((void*)0)) : ((
a) ? (((GList *)(a))->next) : ((void*)0))
;
139 break;
140 }
141 }
142}
143
144gboolean
145playlist_positions_get_single (playlist_positions_t *positions, gint *pos)
146{
147 gboolean retval;
148
149 /* If a single (atom) position, get it */
150 if (playlist_positions_count (positions) == 1 && positions->atoms) {
151 *pos = GPOINTER_TO_INT (positions->atoms->data)((gint) (glong) (positions->atoms->data));
152 retval = TRUE(!(0));
153 } else {
154 retval = FALSE(0);
155 }
156
157 return retval;
158}
159
160
161/* Return the number of positions in the object. */
162static int
163playlist_positions_count (playlist_positions_t *positions)
164{
165 GList *n;
166 int count = g_list_length (positions->atoms);
167
168 for (n = positions->intervals; n; n = g_list_next (n)((n) ? (((GList *)(n))->next) : ((void*)0))) {
169 interval_t *ival = (interval_t *) n->data;
170 count += ival->end - ival->start;
171 }
172
173 return count;
174}
175
176static playlist_positions_t *
177playlist_positions_new (gint currpos)
178{
179 playlist_positions_t *positions;
180
181 positions = g_new0 (playlist_positions_t, 1)((playlist_positions_t *) g_malloc0_n ((1), sizeof (playlist_positions_t
)))
;
182 positions->cached_currpos = currpos;
183
184 return positions;
185}
186
187void
188playlist_positions_free (playlist_positions_t *positions)
189{
190 GList *n;
191
192 g_list_free (positions->atoms);
193
194 for (n = positions->intervals; n; n = g_list_next (n)((n) ? (((GList *)(n))->next) : ((void*)0))) {
195 interval_free ((interval_t *) n->data);
196 }
197 g_list_free (positions->intervals);
198
199 g_free (positions);
200}
201
202static gboolean
203playlist_positions_parse_token (const gchar *expr, playlist_positions_t *p)
204{
205 /* Empty string, don't bother */
206 if (!*expr) {
207 return TRUE(!(0));
208 }
209
210 if (!playlist_positions_parse_sequence (expr, p) &&
211 !playlist_positions_parse_currcontext (expr, p) &&
212 !playlist_positions_parse_relsequence (expr, p)) {
213 return FALSE(0);
214 }
215
216 return TRUE(!(0));
217}
218
219static gboolean
220playlist_positions_parse_sequence (const gchar *expr, playlist_positions_t *p)
221{
222 gchar **intervals;
223 gint i = 0;
224 gboolean success = TRUE(!(0));
225 gint start, end;
226
227 /* Quit on '+' or '-' prefix, which would be a relsequence */
228 if (*expr == '+' || *expr == '-') {
229 return FALSE(0);
230 }
231
232 intervals = g_strsplit (expr, ",", 0);
233
234 while (intervals[i]) {
235 if (interval_parse (intervals[i], &start, &end)) {
236 if (start >= 0) {
237 /* note: user lists start at 1 */
238 if (end >= 0) {
239 success = playlist_positions_add_interval (p, start - 1, end - 1);
240 } else {
241 playlist_positions_add_atom (p, start - 1);
242 }
243 }
244 } else {
245 success = FALSE(0);
246 break;
247 }
248 i++;
249 }
250
251 g_strfreev (intervals);
252
253 return success;
254}
255
256static gboolean
257playlist_positions_parse_relsequence (const gchar *expr, playlist_positions_t *p)
258{
259 gchar **intervals;
260 gint i = 0;
261 gboolean success = TRUE(!(0));
262 gint start, end, currpos;
263
264 /* No '+' or '-' prefix, not a relsequence */
265 if (*expr != '+' && *expr != '-') {
266 return FALSE(0);
267 }
268
269 intervals = g_strsplit (expr + 1, ",", 0);
270 currpos = p->cached_currpos;
271
272 if (currpos < 0) {
273 /* FIXME: error, cannot use relative currpos if not set */
274 return FALSE(0);
275 }
276
277 while (intervals[i] && success) {
278 if (interval_parse (intervals[i], &start, &end)) {
279 if (start >= 0) {
280 start = (*expr == '+') ? start : -start;
281 if (end >= 0) {
282 end = (*expr == '+') ? end : -end;
283 success = playlist_positions_add_interval (p, currpos + start,
284 currpos + end);
285 } else {
286 playlist_positions_add_atom (p, currpos + start);
287 }
288 }
289 } else {
290 success = FALSE(0);
291 break;
292 }
293 i++;
294 }
295
296 g_strfreev (intervals);
297
298 return success;
299}
300
301static gboolean
302playlist_positions_parse_currcontext (const gchar *expr, playlist_positions_t *p)
303{
304 gint before, after;
305 gchar *underscore;
306 gchar *endptr;
307 gint currpos;
308
309 /* parse [N]_[M] */
310
311 /* No '_' char, not a currcontext */
312 underscore = strchr (expr, '_');
313 if (!underscore) {
314 return FALSE(0);
315 }
316
317 if (expr == underscore) {
318 /* No N value */
319 before = 0;
320 } else {
321 before = strtol (expr, &endptr, 10);
322 if (endptr != underscore) {
323 /* Incorrect parsing, error! */
324 return FALSE(0);
325 }
326 }
327
328 if (*(underscore + 1) == '\0') {
329 /* No M value */
330 after = 0;
331 } else {
332 after = strtol (underscore + 1, &endptr, 10);
333 if (*endptr != '\0') {
334 /* Incorrect parsing, error! */
335 return FALSE(0);
336 }
337 }
338
339 /* Parsed a valid interval! - note: user lists start at 1 */
340 currpos = p->cached_currpos;
341
342 if (currpos < 0) {
343 /* FIXME: error, cannot use relative currpos if not set */
344 return FALSE(0);
345 }
346
347 return playlist_positions_add_interval (p, currpos - before,
348 currpos + after);
349}
350
351static void
352playlist_positions_add_atom (playlist_positions_t *positions, gint x)
353{
354 GList *n;
355
356 /* Add atom if not already in an interval */
357 if (!playlist_positions_intervals_contains_atom (positions, x)) {
358 for (n = positions->atoms; n; n = g_list_next (n)((n) ? (((GList *)(n))->next) : ((void*)0))) {
359 gint curr = GPOINTER_TO_INT (n->data)((gint) (glong) (n->data));
360 if (curr == x) {
361 /* Already present, exit */
362 return;
363 } else if (curr < x) {
364 break;
365 }
366 }
367
368 /* Found the sorted position, insert the atom */
369 positions->atoms = g_list_insert_before (positions->atoms, n,
370 GINT_TO_POINTER (x)((gpointer) (glong) (x)));
371 }
372}
373
374static gboolean
375playlist_positions_add_interval (playlist_positions_t *positions,
376 gint start, gint end)
377{
378 GList *n;
379 interval_t *ival, *prev_ival;
380
381 /* Invalid interval */
382 if (start > end || start < 0) {
383 return FALSE(0);
384 }
385
386 if (start == end) {
387 playlist_positions_add_atom (positions, start);
388 return TRUE(!(0));
389 }
390
391 /* Merge or add new interval in list */
392 prev_ival = NULL((void*)0);
393 for (n = positions->intervals; n; n = g_list_next (n)((n) ? (((GList *)(n))->next) : ((void*)0))) {
394 ival = (interval_t *) n->data;
395 if (start > ival->end) {
396 break;
397 } else if (end < ival->start) {
398 continue;
399 } else {
400 /* Previous interval already merged, free it and extend current */
401 if (prev_ival) {
402 ival->end = prev_ival->end;
403 interval_free (prev_ival);
404 positions->intervals = g_list_remove (positions->intervals,
405 prev_ival);
406 }
407
408 prev_ival = ival;
409 }
410 }
411
412 if (prev_ival) {
413 /* extend end of merged interval */
414 if (end > prev_ival->end) {
415 prev_ival->end = end;
416 }
417 /* extend start of merged interval */
418 if (start < prev_ival->start) {
419 prev_ival->start = start;
420 }
421 } else {
422 /* Not merged with an existing interval, create a new one */
423 positions->intervals = g_list_insert_before (positions->intervals, n,
424 interval_new (start, end));
425 }
426
427 /* Remove atoms included in the new interval */
428 for (n = positions->atoms; n; n = g_list_next (n)((n) ? (((GList *)(n))->next) : ((void*)0))) {
429 gint curr = GPOINTER_TO_INT (n->data)((gint) (glong) (n->data));
430 if (curr <= end) {
431 if (curr >= start) {
432 /* atom in the interval, remove */
433 positions->atoms = g_list_delete_link (positions->atoms, n);
434 } else {
435 /* atom smaller than interval, done */
436 break;
437 }
438 }
439 }
440
441 return TRUE(!(0));
442}
443
444static gboolean
445playlist_positions_intervals_contains_atom (playlist_positions_t *positions, gint x)
446{
447 GList *n;
448
449 for (n = positions->intervals; n; n = g_list_next (n)((n) ? (((GList *)(n))->next) : ((void*)0))) {
450 interval_t *ival = (interval_t *) n->data;
451 if (x >= ival->start) {
452 if (x > ival->end) {
453 /* x larger than biggest interval */
454 return FALSE(0);
455 } else {
456 /* x inside current interval */
457 return TRUE(!(0));
458 }
459 }
460
461 /* Not in this interval, tried the next (smaller) */
462 }
463
464 return FALSE(0);
465}
466
467static interval_t *
468interval_new (gint start, gint end)
469{
470 interval_t *ival;
471
472 ival = g_new0 (interval_t, 1)((interval_t *) g_malloc0_n ((1), sizeof (interval_t)));
473 ival->start = start;
474 ival->end = end;
475
476 return ival;
477}
478
479static void
480interval_free (interval_t *ival)
481{
482 g_free (ival);
483}
484
485static void
486interval_foreach (interval_t *ival, playlist_positions_func f,
487 gboolean forward, void *userdata)
488{
489 int i;
490 int start, end, inc;
491
492 if (forward) {
493 start = ival->start;
494 end = ival->end + 1;
495 inc = 1;
496 } else {
497 start = ival->end;
498 end = ival->start - 1;
499 inc = -1;
500 }
501
502 for (i = start; i != end; i+=inc) {
503 f (i, userdata);
504 }
505}
506
507static gboolean
508interval_parse (const gchar *expr, gint *start, gint *end)
509{
510 gchar *endptr;
511
512 *start = -1;
513 *end = -1;
514
515 /* Empty string, don't bother */
516 if (!*expr) {
517 return TRUE(!(0));
518 }
519
520 *start = strtol (expr, &endptr, 10);
521
522 if (*endptr == '-') {
523 /* A real interval, get end */
524 *end = strtol (endptr + 1, &endptr, 10);
525 if (*endptr != '\0') {
526 /* Incorrect parsing of end, error! */
527 return FALSE(0);
528 }
529 } else if (*endptr != '\0') {
530 /* Incorrect parsing of start, error! */
531 return FALSE(0);
532 }
533
534 return TRUE(!(0));
535}
536
537static interval_vs_atom_t
538interval_atom_list_cmp (GList *intervals, GList *atoms, gboolean forward)
539{
540 interval_t *ival;
541 int atom;
542
543 /* Empty lists lose */
544 if (!intervals) {
545 return ATOM_IS_GREATER;
546 }
547 if (!atoms) {
548 return INTERVAL_IS_GREATER;
549 }
550
551 /* Need to compare first items */
552 ival = (interval_t *) intervals->data;
553 atom = GPOINTER_TO_INT (atoms->data)((gint) (glong) (atoms->data));
554
555 /* If forward invert comparison result */
556 if (forward) {
557 return (atom < ival->end ? ATOM_IS_GREATER : INTERVAL_IS_GREATER);
558 }
559
560 return (atom > ival->end ? ATOM_IS_GREATER : INTERVAL_IS_GREATER);
561}