1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | #include "medialib.h" |
18 | |
19 | |
20 | medialib mdb; |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | void |
27 | medialib_load(const char *db_file, const char *playlist_dir) |
28 | { |
29 | playlist *p; |
30 | char **pfiles; |
31 | int npfiles; |
32 | int i; |
33 | |
34 | |
35 | mdb.db_file = strdup(db_file); |
36 | mdb.playlist_dir = strdup(playlist_dir); |
37 | if (mdb.db_file == NULL((void *)0) || mdb.playlist_dir == NULL((void *)0)) |
38 | err(1, "failed to strdup db file and playlist dir in medialib_init"); |
39 | |
40 | |
41 | mdb.library = playlist_new(); |
42 | mdb.library->filename = NULL((void *)0); |
43 | mdb.library->name = strdup("--LIBRARY--"); |
44 | |
45 | mdb.filter_results = playlist_new(); |
46 | mdb.filter_results->filename = NULL((void *)0); |
47 | mdb.filter_results->name = strdup("--FILTER--"); |
48 | |
49 | if (mdb.library->name == NULL((void *)0) || mdb.filter_results->name == NULL((void *)0)) |
50 | err(1, "failed to strdup pseudo-names in medialib_load"); |
51 | |
52 | |
53 | medialib_db_load(db_file); |
54 | |
55 | |
56 | mdb.nplaylists = 0; |
57 | mdb.playlists_capacity = 2; |
58 | mdb.playlists = calloc(2, sizeof(playlist*)); |
59 | if (mdb.playlists == NULL((void *)0)) |
60 | err(1, "medialib_load: failed to allocate initial playlists"); |
61 | |
62 | |
63 | medialib_playlist_add(mdb.library); |
64 | medialib_playlist_add(mdb.filter_results); |
65 | |
66 | |
67 | npfiles = retrieve_playlist_filenames(mdb.playlist_dir, &pfiles); |
68 | for (i = 0; i < npfiles; i++) { |
69 | p = playlist_load(pfiles[i], mdb.library->files, mdb.library->nfiles); |
70 | medialib_playlist_add(p); |
71 | free(pfiles[i]); |
72 | } |
73 | |
74 | |
75 | for (i = 0; i < mdb.nplaylists; i++) |
76 | mdb.playlists[i]->needs_saving = false0; |
77 | |
78 | free(pfiles); |
79 | } |
80 | |
81 | |
82 | void |
83 | medialib_destroy() |
84 | { |
85 | int i; |
86 | |
87 | |
88 | for (i = 0; i < mdb.library->nfiles; i++) |
89 | mi_free(mdb.library->files[i]); |
90 | |
91 | |
92 | for (i = 0; i < mdb.nplaylists; i++) |
93 | playlist_free(mdb.playlists[i]); |
94 | |
95 | |
96 | free(mdb.playlists); |
97 | free(mdb.db_file); |
98 | free(mdb.playlist_dir); |
99 | |
100 | |
101 | mdb.nplaylists = 0; |
102 | mdb.playlists_capacity = 0; |
103 | } |
104 | |
105 | |
106 | void |
107 | medialib_playlist_add(playlist *p) |
108 | { |
109 | playlist **new_playlists; |
110 | |
111 | |
112 | if (mdb.nplaylists == mdb.playlists_capacity) { |
113 | mdb.playlists_capacity += MEDIALIB_PLAYLISTS_CHUNK_SIZE100; |
114 | int size = mdb.playlists_capacity * sizeof(playlist*); |
115 | if ((new_playlists = realloc(mdb.playlists, size)) == NULL((void *)0)) |
116 | err(1, "medialib_playlist_add: realloc failed"); |
117 | |
118 | mdb.playlists = new_playlists; |
119 | } |
120 | |
121 | mdb.playlists[mdb.nplaylists++] = p; |
122 | } |
123 | |
124 | |
125 | |
126 | |
127 | |
128 | void |
129 | medialib_playlist_remove(int pindex) |
130 | { |
131 | int i; |
132 | |
133 | if (pindex < 0 || pindex >= mdb.nplaylists) |
134 | errx(1, "medialib_playlist_remove: index %d out of range", pindex); |
135 | |
136 | playlist_delete(mdb.playlists[pindex]); |
137 | |
138 | |
139 | for (i = pindex + 1; i < mdb.nplaylists; i++) |
140 | mdb.playlists[i - 1] = mdb.playlists[i]; |
141 | |
142 | mdb.nplaylists--; |
143 | } |
144 | |
145 | |
146 | |
147 | |
148 | |
149 | void |
150 | medialib_setup_files(const char *vitunes_dir, const char *db_file, |
151 | const char *playlist_dir) |
152 | { |
153 | struct stat sb; |
154 | |
155 | |
156 | if (mkdir(vitunes_dir, S_IRWXU0000700) == -1) { |
157 | if (errno(*__errno()) == EEXIST17) |
158 | warnx("vitunes directory '%s' already exists (OK)", vitunes_dir); |
159 | else |
160 | err(1, "unable to create vitunes directory '%s'", vitunes_dir); |
161 | } else |
162 | warnx("vitunes directory '%s' created", vitunes_dir); |
163 | |
164 | |
165 | if (mkdir(playlist_dir, S_IRWXU0000700) == -1) { |
166 | if (errno(*__errno()) == EEXIST17) |
167 | warnx("playlists directory '%s' already exists (OK)", playlist_dir); |
168 | else |
169 | err(1, "unable to create playlists directory '%s'", playlist_dir); |
170 | } else |
171 | warnx("playlists directory '%s' created", playlist_dir); |
172 | |
173 | |
174 | if (stat(db_file, &sb) < 0) { |
175 | if (errno(*__errno()) == ENOENT2) { |
176 | |
177 | int version[3] = {DB_VERSION_MAJOR2, DB_VERSION_MINOR1, DB_VERSION_OTHER0}; |
178 | FILE *f; |
179 | |
180 | |
181 | if ((f = fopen(db_file, "w")) == NULL((void *)0)) |
182 | err(1, "failed to create database file '%s'", db_file); |
183 | |
184 | |
185 | fwrite("vitunes", strlen("vitunes"), 1, f); |
186 | fwrite(version, sizeof(version), 1, f); |
187 | |
188 | warnx("empty database at '%s' created", db_file); |
189 | fclose(f); |
190 | } else |
191 | err(1, "database file '%s' exists, but cannot access it", db_file); |
192 | } else |
193 | warnx("database file '%s' already exists (OK)", db_file); |
194 | } |
195 | |
196 | |
197 | static int mi_cmp_fn(const void *ai, const void *bi) |
198 | { |
199 | const meta_info **a2 = (const meta_info **) ai; |
200 | const meta_info **b2 = (const meta_info **) bi; |
201 | const meta_info *a = (const meta_info *) *a2; |
202 | const meta_info *b = (const meta_info *) *b2; |
203 | |
204 | return strcmp(a->filename, b->filename); |
205 | } |
206 | |
207 | |
208 | void |
209 | medialib_db_load(const char *db_file) |
210 | { |
211 | meta_info *mi; |
212 | FILE *fin; |
213 | char header[255] = { 0 }; |
214 | int version[3]; |
215 | |
216 | if ((fin = fopen(db_file, "r")) == NULL((void *)0)) |
| |
| 2 | | Assuming pointer value is null | |
|
| |
217 | err(1, "Failed to open database file '%s'", db_file); |
218 | |
219 | |
220 | fread(header, strlen("vitunes"), 1, fin); |
221 | if (strncmp(header, "vitunes", strlen("vitunes")) != 0) |
| |
222 | errx(1, "Database file '%s' NOT a vitunes database", db_file); |
223 | |
224 | fread(version, sizeof(version), 1, fin); |
225 | if (version[0] != DB_VERSION_MAJOR2 || version[1] != DB_VERSION_MINOR1 |
| |
226 | || version[2] != DB_VERSION_OTHER0) { |
227 | printf("Loading vitunes database: old database version detected.\n"); |
228 | printf("\tExisting database at '%s' is of version %d.%d.%d\n", |
229 | db_file, version[0], version[1], version[2]); |
230 | printf("\tThis version of vitunes only works with version %d.%d.%d\n", |
231 | DB_VERSION_MAJOR2, DB_VERSION_MINOR1, DB_VERSION_OTHER0); |
232 | printf("Remove the existing database and rebuild by doing:\n"); |
233 | printf("\t$ rm %s\n", db_file); |
234 | printf("\t$ vitunes -e init\n"); |
235 | printf("\t$ vitunes -e add path [...]\n"); |
236 | fflush(stdout(&__sF[1])); |
237 | exit(1); |
238 | } |
239 | |
240 | |
241 | while (!feof(fin)(!__isthreaded ? (((fin)->_flags & 0x0020) != 0) : (feof )(fin))) { |
| 6 | | Within the expansion of the macro 'feof':
|
a | Assuming '__isthreaded' is 0 |
b | Access to field '_flags' results in a dereference of a null pointer (loaded from variable 'fin') |
|
242 | mi = mi_new(); |
243 | mi_fread(mi, fin); |
244 | if (feof(fin)(!__isthreaded ? (((fin)->_flags & 0x0020) != 0) : (feof )(fin))) |
245 | mi_free(mi); |
246 | else if (ferror(fin)(!__isthreaded ? (((fin)->_flags & 0x0040) != 0) : (ferror )(fin))) |
247 | err(1, "Error loading database file '%s'", db_file); |
248 | else |
249 | playlist_files_append(mdb.library, &mi, 1, false0); |
250 | } |
251 | |
252 | fclose(fin); |
253 | |
254 | |
255 | qsort(mdb.library->files, mdb.library->nfiles, sizeof(meta_info*), mi_cmp_fn); |
256 | } |
257 | |
258 | |
259 | void |
260 | medialib_db_save(const char *db_file) |
261 | { |
262 | FILE *fout; |
263 | int version[3] = {DB_VERSION_MAJOR2, DB_VERSION_MINOR1, DB_VERSION_OTHER0}; |
264 | int i; |
265 | |
266 | if ((fout = fopen(db_file, "w")) == NULL((void *)0)) |
267 | err(1, "medialib_db_save: failed to open database file '%s'", db_file); |
268 | |
269 | |
270 | fwrite("vitunes", strlen("vitunes"), 1, fout); |
271 | fwrite(version, sizeof(version), 1, fout); |
272 | |
273 | |
274 | for (i = 0; i < mdb.library->nfiles; i++) { |
275 | mi_fwrite(mdb.library->files[i], fout); |
276 | if (ferror(fout)(!__isthreaded ? (((fout)->_flags & 0x0040) != 0) : (ferror )(fout))) |
277 | err(1, "medialib_db_save: error saving database"); |
278 | } |
279 | |
280 | fclose(fout); |
281 | } |
282 | |
283 | |
284 | void |
285 | medialib_db_flush(FILE *fout, const char *timefmt) |
286 | { |
287 | meta_info *mi; |
288 | struct tm *ltime; |
289 | char stime[255]; |
290 | int f, i; |
291 | size_t len; |
292 | |
293 | |
294 | fprintf(fout, "filename, "); |
295 | for (i = 0; i < MI_NUM_CINFO8; i++) |
296 | fprintf(fout, "\"%s\", ", MI_CINFO_NAMES[i]); |
297 | |
298 | fprintf(fout, "length-seconds, is_url, \"last-updated\"\n"); |
299 | fflush(fout); |
300 | |
301 | |
302 | for (f = 0; f < mdb.library->nfiles; f++) { |
303 | |
304 | |
305 | mi = mdb.library->files[f]; |
306 | |
307 | |
308 | fprintf(fout, "%s, ", mi->filename); |
309 | for (i = 0; i < MI_NUM_CINFO8; i++) |
310 | fprintf(fout, "\"%s\", ", mi->cinfo[i]); |
311 | |
312 | |
313 | ltime = localtime(&(mi->last_updated)); |
314 | len = strftime(stime, sizeof(stime), timefmt, ltime); |
315 | stime[len] = '\0'; |
316 | |
317 | fprintf(fout, "%i, %s, \"%s\"\n", |
318 | mi->length, (mi->is_url ? "true" : "false"), stime); |
319 | |
320 | fflush(fout); |
321 | } |
322 | } |
323 | |
324 | |
325 | |
326 | |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | void |
333 | medialib_db_update(bool_Bool show_skipped, bool_Bool force_update) |
334 | { |
335 | meta_info *mi; |
336 | struct stat sb; |
337 | char *filename; |
338 | int i; |
339 | |
340 | |
341 | int count_removed_file_gone = 0; |
342 | int count_removed_meta_gone = 0; |
343 | int count_skipped_not_updated = 0; |
344 | int count_updated = 0; |
345 | int count_errors = 0; |
346 | int count_urls = 0; |
347 | |
348 | for (i = 0; i < mdb.library->nfiles; i++) { |
349 | |
350 | filename = mdb.library->files[i]->filename; |
351 | |
352 | |
353 | if (mdb.library->files[i]->is_url) { |
354 | printf("s %s\n", filename); |
355 | count_urls++; |
356 | continue; |
357 | } |
358 | |
359 | if (stat(filename, &sb) == -1) { |
360 | |
361 | |
362 | |
363 | if (errno(*__errno()) == ENOENT2) { |
364 | |
365 | playlist_files_remove(mdb.library, i, 1, false0); |
366 | i--; |
367 | printf("x %s\n", filename); |
368 | count_removed_file_gone++; |
369 | } else { |
370 | |
371 | printf("? %s\n", filename); |
372 | count_errors++; |
373 | } |
374 | |
375 | } else { |
376 | |
377 | |
378 | |
379 | |
380 | |
381 | |
382 | if (force_update || |
383 | (sb.st_mtimest_mtim.tv_sec > mdb.library->files[i]->last_updated)) { |
384 | |
385 | mi = mi_extract(filename); |
386 | if (mi == NULL((void *)0)) { |
387 | |
388 | playlist_files_remove(mdb.library, i, 1, false0); |
389 | i--; |
390 | printf("- %s\n", filename); |
391 | count_removed_meta_gone++; |
392 | } else { |
393 | |
394 | mi_sanitize(mi); |
395 | playlist_file_replace(mdb.library, i, mi); |
396 | printf("u %s\n", filename); |
397 | count_updated++; |
398 | } |
399 | } else { |
400 | count_skipped_not_updated++; |
401 | if (show_skipped) |
402 | printf(". %s\n", filename); |
403 | } |
404 | |
405 | } |
406 | } |
407 | |
408 | |
409 | medialib_db_save(mdb.db_file); |
410 | |
411 | |
412 | printf("--------------------------------------------------\n"); |
413 | printf("Results of updating database...\n"); |
414 | printf("(s) %9d url's skipped\n", count_urls); |
415 | printf("(u) %9d files updated\n", count_updated); |
416 | printf("(x) %9d files removed (file no longer exists)\n", |
417 | count_removed_file_gone); |
418 | printf("(-) %9d files removed (meta-info gone)\n", |
419 | count_removed_meta_gone); |
420 | printf("(.) %9d files skipped (file unchanged since last checked)\n", |
421 | count_skipped_not_updated); |
422 | printf("(?) %9d files with errors (couldn't stat, but kept)\n", |
423 | count_errors); |
424 | } |
425 | |
426 | |
427 | |
428 | |
429 | |
430 | void |
431 | medialib_db_scan_dirs(char *dirlist[]) |
432 | { |
433 | FTS *fts; |
434 | FTSENT *ftsent; |
435 | meta_info *mi; |
436 | char fullname[PATH_MAX1024]; |
437 | int i, idx; |
438 | |
439 | |
440 | int count_removed_lost_info = 0; |
441 | int count_updated = 0; |
442 | int count_skipped_no_info = 0; |
443 | int count_skipped_dir = 0; |
444 | int count_skipped_error = 0; |
445 | int count_skipped_not_updated = 0; |
446 | int count_added = 0; |
447 | |
448 | |
449 | |
450 | fts = fts_open(dirlist, FTS_LOGICAL0x0002 | FTS_NOCHDIR0x0004, NULL((void *)0)); |
451 | if (fts == NULL((void *)0)) |
452 | err(1, "medialib_db_scan_dirs: fts_open failed"); |
453 | |
454 | while ((ftsent = fts_read(fts)) != NULL((void *)0)) { |
455 | |
456 | switch (ftsent->fts_info) { |
457 | case FTS_D1: |
458 | printf("Checking Directory: %s\n", ftsent->fts_path); |
459 | break; |
460 | |
461 | case FTS_DP6: |
462 | break; |
463 | |
464 | case FTS_DNR4: |
465 | printf("Directory '%s' Unreadable\n", ftsent->fts_accpath); |
466 | count_skipped_dir++; |
467 | break; |
468 | |
469 | case FTS_NS10: |
470 | case FTS_ERR7: |
471 | printf("? %s\n", ftsent->fts_path); |
472 | count_skipped_error++; |
473 | break; |
474 | |
475 | case FTS_F8: |
476 | |
477 | |
478 | if (realpath(ftsent->fts_accpath, fullname) == NULL((void *)0)) { |
479 | err(1, "medialib_db_scan_dirs: realpath failed for '%s'", |
480 | ftsent->fts_accpath); |
481 | } |
482 | |
483 | |
484 | idx = -1; |
485 | for (i = 0; i < mdb.library->nfiles; i++) { |
486 | if (strcmp(fullname, mdb.library->files[i]->filename) == 0) |
487 | idx = i; |
488 | } |
489 | |
490 | if (idx != -1) { |
491 | |
492 | |
493 | if (ftsent->fts_statp->st_mtimest_mtim.tv_sec > |
494 | mdb.library->files[idx]->last_updated) { |
495 | |
496 | |
497 | |
498 | mi = mi_extract(ftsent->fts_accpath); |
499 | |
500 | if (mi == NULL((void *)0)) { |
501 | |
502 | playlist_files_remove(mdb.library, idx, 1, false0); |
503 | printf("- %s\n", ftsent->fts_accpath); |
504 | count_removed_lost_info++; |
505 | } else { |
506 | |
507 | mi_sanitize(mi); |
508 | playlist_file_replace(mdb.library, idx, mi); |
509 | printf("u %s\n", ftsent->fts_accpath); |
510 | count_updated++; |
511 | } |
512 | } else { |
513 | printf(". %s\n", ftsent->fts_accpath); |
514 | count_skipped_not_updated++; |
515 | } |
516 | |
517 | } else { |
518 | |
519 | |
520 | |
521 | mi = mi_extract(ftsent->fts_accpath); |
522 | |
523 | if (mi == NULL((void *)0)) { |
524 | |
525 | printf("s %s\n", ftsent->fts_accpath); |
526 | count_skipped_no_info++; |
527 | } else { |
528 | |
529 | mi_sanitize(mi); |
530 | playlist_files_append(mdb.library, &mi, 1, false0); |
531 | printf("+ %s\n", ftsent->fts_accpath); |
532 | count_added++; |
533 | } |
534 | } |
535 | } |
536 | } |
537 | |
538 | if (fts_close(fts) == -1) |
539 | err(1, "medialib_db_scan_dirs: failed to close file heirarchy"); |
540 | |
541 | |
542 | medialib_db_save(mdb.db_file); |
543 | |
544 | |
545 | printf("--------------------------------------------------\n"); |
546 | printf("Results of scanning directories...\n"); |
547 | printf("(+) %9d files added\n", count_added); |
548 | printf("(u) %9d files updated\n", count_updated); |
549 | printf("(-) %9d files removed (was in DB, but no longer has meta-info)\n", |
550 | count_removed_lost_info); |
551 | printf("(.) %9d files skipped (in DB, file unchanged since last checked)\n", |
552 | count_skipped_not_updated); |
553 | printf("(s) %9d files skipped (no info)\n", count_skipped_no_info); |
554 | printf("(?) %9d files skipped (other error)\n", count_skipped_error); |
555 | printf(" %9d directories skipped (couldn't read)\n", count_skipped_dir); |
556 | } |