1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | #include "playlist.h" |
18 | |
19 | int history_size = DEFAULT_HISTORY_SIZE100; |
20 | |
21 | void |
22 | playlist_increase_capacity(playlist *p) |
23 | { |
24 | meta_info **new_files; |
25 | size_t nbytes; |
26 | |
27 | p->capacity += PLAYLIST_CHUNK_SIZE100; |
28 | nbytes = p->capacity * sizeof(meta_info*); |
29 | if ((new_files = realloc(p->files, nbytes)) == NULL((void *)0)) |
30 | err(1, "%s: failed to realloc(3) files", __FUNCTION__); |
31 | |
32 | p->files = new_files; |
33 | } |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | playlist * |
40 | playlist_new(void) |
41 | { |
42 | playlist *p; |
43 | |
44 | if ((p = malloc(sizeof(playlist))) == NULL((void *)0)) |
45 | err(1, "playlist_new: failed to allocate playlist"); |
46 | |
47 | if ((p->files = calloc(PLAYLIST_CHUNK_SIZE100, sizeof(meta_info*))) == NULL((void *)0)) |
48 | err(1, "playlist_new: failed to allocate files"); |
49 | |
50 | p->capacity = PLAYLIST_CHUNK_SIZE100; |
51 | p->filename = NULL((void *)0); |
52 | p->name = NULL((void *)0); |
53 | p->nfiles = 0; |
54 | p->history = playlist_history_new(); |
55 | p->hist_present = -1; |
56 | p->needs_saving = false0; |
57 | |
58 | return p; |
59 | } |
60 | |
61 | |
62 | void |
63 | playlist_free(playlist *p) |
64 | { |
65 | if (p->filename != NULL((void *)0)) free(p->filename); |
66 | if (p->name != NULL((void *)0)) free(p->name); |
67 | if (p->files != NULL((void *)0)) free(p->files); |
68 | playlist_history_free(p); |
69 | free(p); |
70 | } |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | playlist * |
77 | playlist_dup(const playlist *original, const char *filename, |
78 | const char *name) |
79 | { |
80 | playlist *newplist; |
81 | int i; |
82 | |
83 | |
84 | newplist = playlist_new(); |
85 | newplist->nfiles = original->nfiles; |
86 | newplist->capacity = original->nfiles; |
87 | |
88 | if (name != NULL((void *)0)) { |
89 | if ((newplist->name = strdup(name)) == NULL((void *)0)) |
90 | err(1, "playlist_dup: strdup name failed"); |
91 | } |
92 | if (filename != NULL((void *)0)) { |
93 | if ((newplist->filename = strdup(filename)) == NULL((void *)0)) |
94 | err(1, "playlist_dup: strdup filename failed"); |
95 | } |
96 | |
97 | |
98 | newplist->files = calloc(original->nfiles, sizeof(meta_info*)); |
99 | if (newplist->files == NULL((void *)0)) |
100 | err(1, "playlist_dup: failed to allocate files"); |
101 | |
102 | for (i = 0; i < original->nfiles; i++) |
103 | newplist->files[i] = original->files[i]; |
104 | |
105 | return newplist; |
106 | } |
107 | |
108 | |
109 | |
110 | |
111 | |
112 | void |
113 | playlist_files_add(playlist *p, meta_info **f, int start, int size, bool_Bool record) |
114 | { |
115 | int i; |
116 | |
117 | if (start < 0 || start > p->nfiles) |
118 | errx(1, "playlist_file_add: index %d out of range", start); |
119 | |
120 | while (p->capacity <= p->nfiles + size) |
121 | playlist_increase_capacity(p); |
122 | |
123 | |
124 | for (i = p->nfiles + size; i > start; i--) |
125 | p->files[i] = p->files[i - size]; |
126 | |
127 | |
128 | for (i = 0; i < size; i++) |
129 | p->files[start + i] = f[i]; |
130 | |
131 | p->nfiles += size; |
132 | |
133 | |
134 | if (record) { |
135 | playlist_changeset *changes = changeset_create(CHANGE_ADD0, size, |
136 | f, start); |
137 | playlist_history_push(p, changes); |
138 | p->needs_saving = true1; |
139 | } |
140 | } |
141 | |
142 | |
143 | void |
144 | playlist_files_append(playlist *p, meta_info **f, int size, bool_Bool record) |
145 | { |
146 | return playlist_files_add(p, f, p->nfiles, size, record); |
147 | } |
148 | |
149 | |
150 | void |
151 | playlist_files_remove(playlist *p, int start, int size, bool_Bool record) |
152 | { |
153 | playlist_changeset *changes; |
154 | int i; |
155 | |
156 | if (start < 0 || start >= p->nfiles) |
157 | errx(1, "playlist_remove_file: index %d out of range", start); |
158 | |
159 | if (record) { |
160 | changes = changeset_create(CHANGE_REMOVE1, size, &(p->files[start]), start); |
161 | playlist_history_push(p, changes); |
162 | p->needs_saving = true1; |
163 | } |
164 | |
165 | for (i = start; i < p->nfiles; i++) |
166 | p->files[i] = p->files[i + size]; |
167 | |
168 | p->nfiles -= size; |
169 | |
170 | } |
171 | |
172 | |
173 | void |
174 | playlist_file_replace(playlist *p, int index, meta_info *newEntry) |
175 | { |
176 | if (index < 0 || index >= p->nfiles) |
177 | errx(1, "playlist_file_replace: index %d out of range", index); |
178 | |
179 | p->files[index] = newEntry; |
180 | } |
181 | |
182 | |
183 | static int cmp_fn_mi(const void *ai, const void *bi) |
184 | { |
185 | const char *a = (const char *) ai; |
186 | const meta_info **b2 = (const meta_info **) bi; |
187 | const meta_info *b = (const meta_info *) *b2; |
188 | |
189 | return strcmp(a, b->filename); |
190 | } |
191 | |
192 | |
193 | |
194 | |
195 | |
196 | |
197 | |
198 | |
199 | |
200 | |
201 | |
202 | playlist * |
203 | playlist_load(const char *filename, meta_info **db, int ndb) |
204 | { |
205 | meta_info *mi, **mit; |
206 | FILE *fin; |
207 | char *period; |
208 | char entry[PATH_MAX1024 + 1]; |
209 | |
210 | |
211 | if ((fin = fopen(filename, "r")) == NULL((void *)0)) |
212 | err(1, "playlist_load: failed to open playlist '%s'", filename); |
213 | |
214 | |
215 | playlist *p = playlist_new(); |
216 | p->filename = strdup(filename); |
217 | p->name = strdup(basename(p->filename)); |
218 | if (p->filename == NULL((void *)0) || p->name == NULL((void *)0)) |
219 | err(1, "playlist_load: failed to allocate info for playlist '%s'", filename); |
220 | |
221 | |
222 | period = strrchr(p->name, '.'); |
223 | *period = '\0'; |
224 | |
225 | |
226 | while (fgets(entry, PATH_MAX1024, fin) != NULL((void *)0)) { |
227 | |
228 | entry[strcspn(entry, "\n")] = '\0'; |
229 | |
230 | |
231 | mit = bsearch(entry, db, ndb, sizeof(meta_info *), cmp_fn_mi); |
232 | |
233 | if (mit != NULL((void *)0)) { |
234 | mi = *mit; |
235 | playlist_files_append(p, &mi, 1, false0); |
236 | } else { |
237 | |
238 | mi = mi_new(); |
239 | mi->filename = strdup(entry); |
240 | if (mi->filename == NULL((void *)0)) |
241 | err(1, "playlist_load: failed to strdup filename"); |
242 | |
243 | |
244 | playlist_files_append(p, &mi, 1, false0); |
245 | warnx("playlist \"%s\", file \"%s\" is NOT in media database (added for now)", |
246 | p->name, entry); |
247 | } |
248 | } |
249 | |
250 | fclose(fin); |
251 | return p; |
252 | } |
253 | |
254 | |
255 | |
256 | |
257 | |
258 | void |
259 | playlist_save(const playlist *p) |
260 | { |
261 | FILE *fout; |
262 | int i; |
263 | |
264 | if ((fout = fopen(p->filename, "w")) == NULL((void *)0)) |
265 | err(1, "playlist_save: failed to open playlist \"%s\"", p->filename); |
266 | |
267 | |
268 | for (i = 0; i < p->nfiles; i++) { |
269 | if (fprintf(fout, "%s\n", p->files[i]->filename) == -1) |
270 | err(1, "playlist_save: failed to record playlist \"%s\"", p->filename); |
271 | } |
272 | |
273 | fclose(fout); |
274 | } |
275 | |
276 | |
277 | |
278 | |
279 | |
280 | void |
281 | playlist_delete(playlist *p) |
282 | { |
283 | |
284 | if (p->filename != NULL((void *)0) && unlink(p->filename) != 0) |
285 | err(1, "playlist_delete: failed to delete playlist \"%s\"", p->filename); |
286 | |
287 | |
288 | playlist_free(p); |
289 | } |
290 | |
291 | |
292 | |
293 | |
294 | |
295 | |
296 | |
297 | |
298 | |
299 | |
300 | |
301 | |
302 | |
303 | playlist * |
304 | playlist_filter(const playlist *p, bool_Bool m) |
305 | { |
306 | playlist *results; |
307 | int i; |
308 | |
309 | if (!mi_query_isset()) |
310 | return NULL((void *)0); |
311 | |
312 | results = playlist_new(); |
313 | for (i = 0; i < p->nfiles; i++) { |
314 | if (mi_match(p->files[i])) { |
315 | if (m) playlist_files_append(results, &(p->files[i]), 1, false0); |
316 | } else { |
317 | if (!m) playlist_files_append(results, &(p->files[i]), 1, false0); |
318 | } |
319 | } |
320 | |
321 | return results; |
322 | } |
323 | |
324 | |
325 | |
326 | |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | |
333 | |
334 | |
335 | |
336 | |
337 | |
338 | |
339 | |
340 | |
341 | |
342 | |
343 | int |
344 | retrieve_playlist_filenames(const char *dirname, char ***fnames) |
345 | { |
346 | char *glob_pattern; |
347 | glob_t files; |
348 | int globbed; |
349 | # if defined(__linux) || defined(__FreeBSD__) || defined(__MACH__) |
350 | size_t fcount; |
351 | # else |
352 | int fcount; |
353 | # endif |
354 | |
355 | |
356 | if (asprintf(&glob_pattern, "%s/*.playlist", dirname) == -1) |
357 | errx(1, "failed in building glob pattern"); |
358 | |
359 | |
360 | globbed = glob(glob_pattern, 0, NULL((void *)0), &files); |
361 | if (globbed != 0 && globbed != GLOB_NOMATCH(-3) && errno(*__errno()) != 0) |
362 | err(1, "failed to glob playlists directory"); |
363 | |
364 | |
365 | if ((*fnames = calloc(files.gl_pathc, sizeof(char*))) == NULL((void *)0)) |
366 | err(1, "failed to allocate playlist filenames array"); |
367 | |
368 | for (fcount = 0; fcount < files.gl_pathc; fcount++) { |
369 | if (asprintf(&((*fnames)[fcount]), "%s", files.gl_pathv[fcount]) == -1) |
370 | errx(1, "failed to allocate filename for playlist"); |
371 | } |
372 | |
373 | |
374 | globfree(&files); |
375 | free(glob_pattern); |
376 | |
377 | return fcount; |
378 | } |
379 | |
380 | playlist_changeset* |
381 | changeset_create(short type, size_t size, meta_info **files, int loc) |
382 | { |
383 | size_t i; |
384 | |
385 | playlist_changeset *c; |
386 | |
387 | if ((c = malloc(sizeof(playlist_changeset))) == NULL((void *)0)) |
388 | err(1, "%s: malloc(3) failed", __FUNCTION__); |
389 | |
390 | if ((c->files = calloc(size, sizeof(meta_info*))) == NULL((void *)0)) |
391 | err(1, "%s: calloc(3) failed", __FUNCTION__); |
392 | |
393 | c->type = type; |
394 | c->size = size; |
395 | c->location = loc; |
396 | |
397 | for (i = 0; i < size; i++) |
398 | c->files[i] = files[i]; |
399 | |
400 | return c; |
401 | } |
402 | |
403 | void |
404 | changeset_free(playlist_changeset *c) |
405 | { |
406 | free(c->files); |
407 | free(c); |
408 | } |
409 | |
410 | playlist_changeset** |
411 | playlist_history_new(void) |
412 | { |
413 | playlist_changeset **h; |
414 | int i; |
415 | |
416 | if ((h = calloc(history_size, sizeof(playlist_changeset*))) == NULL((void *)0)) |
| |
| 2 | | Assuming pointer value is null | |
|
| |
417 | err(1, "%s: calloc(3) failed", __FUNCTION__); |
418 | |
419 | for (i = 0; i < history_size; i++) |
| 4 | | Assuming 'i' is < 'history_size' | |
|
| 5 | | Loop condition is true. Entering loop body | |
|
420 | h[i] = NULL((void *)0); |
| 6 | | Array access (from variable 'h') results in a null pointer dereference |
|
421 | |
422 | return h; |
423 | } |
424 | |
425 | void |
426 | playlist_history_free_future(playlist *p) |
427 | { |
428 | int i; |
429 | |
430 | for (i = p->hist_present + 1; i < history_size; i++) { |
431 | if (p->history[i] != NULL((void *)0)) { |
432 | changeset_free(p->history[i]); |
433 | p->history[i] = NULL((void *)0); |
434 | } |
435 | } |
436 | } |
437 | |
438 | void |
439 | playlist_history_free(playlist *p) |
440 | { |
441 | p->hist_present = 0; |
442 | playlist_history_free_future(p); |
443 | free(p->history); |
444 | } |
445 | |
446 | void |
447 | playlist_history_push(playlist *p, playlist_changeset *c) |
448 | { |
449 | if (p->hist_present < history_size - 1) |
450 | playlist_history_free_future(p); |
451 | else { |
452 | int i; |
453 | for (i = 0; i < history_size - 1; i++) |
454 | p->history[i] = p->history[i + 1]; |
455 | |
456 | p->hist_present--; |
457 | } |
458 | |
459 | p->hist_present++; |
460 | p->history[p->hist_present] = c; |
461 | } |
462 | |
463 | |
464 | int |
465 | playlist_undo(playlist *p) |
466 | { |
467 | playlist_changeset *c; |
468 | |
469 | if (p->hist_present == -1) |
470 | return 1; |
471 | |
472 | c = p->history[p->hist_present]; |
473 | |
474 | switch (c->type) { |
475 | case CHANGE_ADD0: |
476 | playlist_files_remove(p, c->location, c->size, false0); |
477 | break; |
478 | case CHANGE_REMOVE1: |
479 | playlist_files_add(p, c->files, c->location, c->size, false0); |
480 | break; |
481 | default: |
482 | errx(1, "%s: invalid change type", __FUNCTION__); |
483 | } |
484 | |
485 | p->hist_present--; |
486 | return 0; |
487 | } |
488 | |
489 | |
490 | int |
491 | playlist_redo(playlist *p) |
492 | { |
493 | playlist_changeset *c; |
494 | |
495 | if (p->hist_present == history_size - 1 |
496 | || p->history[p->hist_present + 1] == NULL((void *)0)) |
497 | return 1; |
498 | |
499 | c = p->history[p->hist_present + 1]; |
500 | |
501 | switch (c->type) { |
502 | case CHANGE_ADD0: |
503 | playlist_files_add(p, c->files, c->location, c->size, false0); |
504 | break; |
505 | case CHANGE_REMOVE1: |
506 | playlist_files_remove(p, c->location, c->size, false0); |
507 | break; |
508 | default: |
509 | errx(1, "%s: invalid change type", __FUNCTION__); |
510 | } |
511 | |
512 | p->hist_present++; |
513 | return 0; |
514 | } |