summaryrefslogtreecommitdiffstats
path: root/src/data.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/data.c')
-rw-r--r--src/data.c628
1 files changed, 354 insertions, 274 deletions
diff --git a/src/data.c b/src/data.c
index 5ef1abe..aba4ba7 100644
--- a/src/data.c
+++ b/src/data.c
@@ -3,9 +3,6 @@
#include "player.h"
#include "list.h"
#include "log.h"
-#include "ref.h"
-#include "track.h"
-#include "tag.h"
#include "tui.h"
#include <fts.h>
@@ -18,88 +15,21 @@
const char *datadir;
-struct list tracks;
-struct list tags;
-struct list tags_sel;
+struct list tracks; /* struct track (link) */
+struct list tags; /* struct track (link) */
+struct list tags_sel; /* struct tag (link_sel) */
-void
-data_load(void)
-{
- struct dirent *ent;
- struct tag *tag;
- struct stat st;
- char *path;
- DIR *dir;
-
- list_init(&tracks);
- list_init(&tags);
- list_init(&tags_sel);
-
- datadir = getenv("TMUS_DATA");
- if (!datadir) ERROR("TMUS_DATA not set!\n");
-
- dir = opendir(datadir);
- if (!dir) ERROR("Failed to access dir: %s\n", datadir);
-
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
-
- path = aprintf("%s/%s", datadir, ent->d_name);
- OOM_CHECK(path);
-
- if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
- tag = tag_alloc(datadir, ent->d_name);
- OOM_CHECK(tag);
- tracks_load(tag);
- list_push_back(&tags, LINK(tag));
- }
-
- free(path);
- }
- closedir(dir);
-
- list_sort(&tracks, track_fid_compare);
-}
-
-void
-data_save(void)
-{
- struct link *iter;
- struct tag *tag;
-
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- tracks_save(tag);
- }
-}
-
-void
-data_free(void)
-{
- struct link *track_link;
- struct link *tag_link;
- struct tag *tag;
-
- log_info("MAIN: data_free()\n");
+static int get_fid(const char *path);
- refs_free(&tracks);
- refs_free(&tags_sel);
+static struct tag *tag_alloc(const char *path, const char *fname);
+static void tag_free(struct tag *tag);
- while (!list_empty(&tags)) {
- tag_link = list_pop_front(&tags);
+static struct track *track_alloc(const char *path, const char *fname);
+static void track_free(struct track *t);
+static int track_name_compare(struct link *a, struct link *b);
- tag = UPCAST(tag_link, struct tag);
- while (!list_empty(&tag->tracks)) {
- track_link = list_pop_front(&tag->tracks);
- track_free(UPCAST(track_link, struct ref)->data);
- ref_free(UPCAST(track_link, struct ref));
- }
- tag_free(tag);
- }
-}
+static void tracks_load(struct tag *tag);
+static void tracks_save(struct tag *tag);
int
get_fid(const char *path)
@@ -108,168 +38,86 @@ get_fid(const char *path)
return stat(path, &st) ? -1 : st.st_ino;
}
-int
-track_fid_compare(struct link *a, struct link *b)
-{
- struct track *ta, *tb;
-
- ta = UPCAST(a, struct ref)->data;
- tb = UPCAST(b, struct ref)->data;
-
- return ta->fid - tb->fid;
-}
-
-void
-index_update(struct tag *tag)
+struct tag *
+tag_alloc(const char *path, const char *fname)
{
- struct dirent *ent;
- struct stat st;
- char *path;
- FILE *file;
- DIR *dir;
- int fid;
+ struct tag *tag;
- path = aprintf("%s/index", tag->fpath);
- OOM_CHECK(path);
+ tag = malloc(sizeof(struct tag));
+ ASSERT(tag != NULL);
- dir = opendir(tag->fpath);
- if (!dir) ERROR("Failed to access dir: %s\n", tag->fpath);
+ tag->fpath = aprintf("%s/%s", path, fname);
+ ASSERT(tag->fpath != NULL);
- file = fopen(path, "w+");
- if (!file) ERROR("Failed to create file: %s\n", path);
- free(path);
+ tag->name = strdup(fname);
+ ASSERT(tag->name != NULL);
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
- if (!strcmp(ent->d_name, "index"))
- continue;
+ tag->link = LINK_EMPTY;
+ tag->link_sel = LINK_EMPTY;
- /* skip files without extension */
- if (!strchr(ent->d_name + 1, '.'))
- continue;
+ list_init(&tag->tracks);
- path = aprintf("%s/%s", tag->fpath, ent->d_name);
- OOM_CHECK(path);
- fid = get_fid(path);
- free(path);
-
- fprintf(file, "%i:%s\n", fid, ent->d_name);
- }
+ return tag;
+}
- closedir(dir);
- fclose(file);
+void
+tag_free(struct tag *tag)
+{
+ free(tag->fpath);
+ free(tag->name);
+ list_clear(&tag->tracks);
+ free(tag);
}
-bool
-tracks_update(struct tag *tag)
+struct track *
+track_alloc(const char *dir, const char *fname)
{
- struct dirent *ent;
- struct stat st;
- struct link *link;
- struct ref *ref;
struct track *track;
- char *path;
- DIR *dir;
- int fid;
-
- dir = opendir(tag->fpath);
- if (!dir) return false;
- while (!list_empty(&tag->tracks)) {
- link = list_pop_front(&tag->tracks);
- ref = UPCAST(link, struct ref);
- track = ref->data;
- ref_free(ref);
- for (LIST_ITER(&tracks, link)) {
- ref = UPCAST(link, struct ref);
- if (ref->data == track) {
- link = link_pop(link);
- ref_free(ref);
- break;
- }
- }
- if (player.track == track)
- player.track = NULL;
- track_free(track);
- }
+ track = malloc(sizeof(struct track));
+ ASSERT(track != NULL);
- while ((ent = readdir(dir))) {
- if (!strcmp(ent->d_name, "."))
- continue;
- if (!strcmp(ent->d_name, ".."))
- continue;
- if (!strcmp(ent->d_name, "index"))
- continue;
+ track->fpath = aprintf("%s/%s", dir, fname);
+ ASSERT(track->fpath != NULL);
- /* skip files without extension */
- if (!strchr(ent->d_name + 1, '.'))
- continue;
+ track->name = strdup(fname);
+ ASSERT(track->name != NULL);
- path = aprintf("%s/%s", tag->fpath, ent->d_name);
- OOM_CHECK(path);
+ track->tag = NULL;
- fid = get_fid(path);
+ track->link = LINK_EMPTY;
+ track->link_pl = LINK_EMPTY;
+ track->link_tt = LINK_EMPTY;
+ track->link_pq = LINK_EMPTY;
+ track->link_hs = LINK_EMPTY;
- track = track_alloc(tag->fpath, ent->d_name, fid);
- OOM_CHECK(track);
-
- ref = ref_alloc(tag);
- OOM_CHECK(ref);
- list_push_back(&track->tags, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tag->tracks, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tracks, LINK(ref));
-
- free(path);
- }
-
- list_sort(&tracks, track_fid_compare);
-
- playlist_update();
-
- closedir(dir);
- return true;
+ return track;
}
void
-playlist_update(void)
+track_free(struct track *t)
{
- struct link *link, *link2;
- struct track *track;
- struct ref *ref;
- struct tag *tag;
+ free(t->fpath);
+ free(t->name);
+ free(t);
+}
- refs_free(&player.playlist);
- for (LIST_ITER(&tags_sel, link)) {
- tag = UPCAST(link, struct ref)->data;
- for (LIST_ITER(&tag->tracks, link2)) {
- track = UPCAST(link2, struct ref)->data;
- ref = ref_alloc(track);
- ASSERT(ref != NULL);
- list_push_back(&player.playlist, LINK(ref));
- }
- }
+int
+track_name_compare(struct link *a, struct link *b)
+{
+ struct track *ta, *tb;
+
+ ta = UPCAST(a, struct track, link);
+ tb = UPCAST(b, struct track, link);
+
+ return strcmp(ta->name, tb->name);
}
void
tracks_load(struct tag *tag)
{
char linebuf[1024];
- struct link *link;
- struct track *track;
- struct track *track2;
- struct ref *ref;
char *index_path;
- char *track_name, *sep;
- int track_fid;
FILE *file;
index_path = aprintf("%s/index", tag->fpath);
@@ -283,28 +131,10 @@ tracks_load(struct tag *tag)
}
while (fgets(linebuf, sizeof(linebuf), file)) {
- sep = strchr(linebuf, '\n');
- if (sep) *sep = '\0';
-
- sep = strchr(linebuf, ':');
- if (!sep) ERROR("Syntax error in index file: %s\n", index_path);
- *sep = '\0';
-
- track_fid = atoi(linebuf);
- track_name = sep + 1;
- track = track_alloc(tag->fpath, track_name, track_fid);
-
- ref = ref_alloc(tag);
- OOM_CHECK(ref);
- list_push_back(&track->tags, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tag->tracks, LINK(ref));
-
- ref = ref_alloc(track);
- OOM_CHECK(ref);
- list_push_back(&tracks, LINK(ref));
+ if (!*linebuf) continue;
+ if (linebuf[strlen(linebuf) - 1] == '\n')
+ linebuf[strlen(linebuf) - 1] = '\0';
+ track_add(tag, linebuf);
}
fclose(file);
@@ -333,8 +163,8 @@ tracks_save(struct tag *tag)
}
for (LIST_ITER(&tag->tracks, link)) {
- track = UPCAST(link, struct ref)->data;
- fprintf(file, "%i:%s\n", track->fid, track->name);
+ track = UPCAST(link, struct track, link_tt);
+ fprintf(file, "%s\n", track->name);
}
fclose(file);
@@ -342,36 +172,6 @@ tracks_save(struct tag *tag)
}
bool
-track_rm(struct track *track)
-{
- struct link *link;
- struct ref *ref;
- struct tag *tag;
-
- if (!rm_file(track->fpath)) {
- CMD_SET_STATUS("Failed to remove track");
- return false;
- }
-
- refs_rm(&tracks, track);
-
- for (LIST_ITER(&track->tags, link)) {
- ref = UPCAST(link, struct ref);
- tag = ref->data;
- refs_rm(&tag->tracks, track);
- }
-
- refs_rm(&player.playlist, track);
-
- if (player.track == track)
- player.track = NULL;
-
- track_free(track);
-
- return true;
-}
-
-bool
make_dir(const char *path)
{
return mkdir(path, S_IRWXU | S_IRWXG) == 0;
@@ -428,7 +228,7 @@ copy_file(const char *src, const char *dst)
{
FILE *in, *out;
char buf[4096];
- int len, nread;
+ int nread;
bool ok;
ok = false;
@@ -457,25 +257,305 @@ cleanup:
}
bool
+dup_file(const char *src, const char *dst)
+{
+ /* TODO add option for hard link dup if possible */
+ return copy_file(src, dst);
+}
+
+bool
move_file(const char *src, const char *dst)
{
return rename(src, dst) == 0;
}
+void
+index_update(struct tag *tag)
+{
+ struct dirent *ent;
+ char *index_path;
+ FILE *file;
+ DIR *dir;
+
+ dir = opendir(tag->fpath);
+ if (!dir) ERROR("Failed to access dir: %s\n", tag->fpath);
+
+ index_path = aprintf("%s/index", tag->fpath);
+ OOM_CHECK(index_path);
+
+ file = fopen(index_path, "w+");
+ if (!file) ERROR("Failed to create index file in dir %s\n", tag->name);
+ free(index_path);
+
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ /* skip files without extension */
+ if (!strchr(ent->d_name + 1, '.'))
+ continue;
+
+ fprintf(file, "%s\n", ent->d_name);
+ }
+
+ closedir(dir);
+ fclose(file);
+}
+
+bool
+tracks_update(struct tag *tag)
+{
+ struct link *link;
+ struct track *track;
+ struct dirent *ent;
+ DIR *dir;
+
+ dir = opendir(tag->fpath);
+ if (!dir) return false;
+
+ while (!list_empty(&tag->tracks)) {
+ link = list_pop_front(&tag->tracks);
+ track = UPCAST(link, struct track, link_tt);
+ track_rm(track, false);
+ }
+
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ /* skip files without extension */
+ if (!strchr(ent->d_name + 1, '.'))
+ continue;
+
+ track_add(tag, ent->d_name);
+ }
+
+ closedir(dir);
+
+ return true;
+}
+
+struct track *
+tracks_vis_track(struct link *link)
+{
+ if (tracks_vis == &player.playlist) {
+ return UPCAST(link, struct track, link_pl);
+ } else {
+ return UPCAST(link, struct track, link_tt);
+ }
+}
+
+void
+playlist_clear(void)
+{
+ while (!list_empty(&player.playlist))
+ list_pop_front(&player.playlist);
+}
+
+void
+playlist_update(bool exec)
+{
+ static bool update = false;
+ struct link *link, *link2;
+ struct track *track;
+ struct tag *tag;
+
+ if (!exec) {
+ update = true;
+ return;
+ }
+
+ if (!update) return;
+ update = false;
+
+ playlist_clear();
+
+ for (LIST_ITER(&tags_sel, link)) {
+ tag = UPCAST(link, struct tag, link_sel);
+ for (LIST_ITER(&tag->tracks, link2)) {
+ track = UPCAST(link2, struct track, link_tt);
+ link_pop(&track->link_pl);
+ list_push_back(&player.playlist, &track->link_pl);
+ }
+ }
+}
+
struct tag *
-tag_find(const char *query)
+tag_add(const char *fname)
{
- struct link *iter;
struct tag *tag;
- for (LIST_ITER(&tags, iter)) {
- tag = UPCAST(iter, struct tag);
- if (!strcmp(tag->name, query)) {
+ /* add to tags list */
+ tag = tag_alloc(datadir, fname);
+ OOM_CHECK(tag);
+ list_push_back(&tags, &tag->link);
+
+ return tag;
+}
+
+struct tag *
+tag_find(const char *name)
+{
+ struct link *link;
+ struct tag *tag;
+
+ for (LIST_ITER(&tags, link)) {
+ tag = UPCAST(link, struct tag, link);
+ if (!strcmp(tag->name, name))
return tag;
- }
}
return NULL;
}
+bool
+tag_rm(struct tag *tag, bool sync_fs)
+{
+ struct link *link;
+ struct track *track;
+
+ /* remove contained tracks */
+ while (!list_empty(&tag->tracks)) {
+ link = list_pop_front(&tag->tracks);
+ track = UPCAST(link, struct track, link_tt);
+ if (!track_rm(track, sync_fs))
+ return false;
+ }
+
+ /* delete dir and remaining non-track files */
+ if (sync_fs && !rm_dir(tag->fpath, true))
+ return false;
+
+ /* remove from selected */
+ link_pop(&tag->link_sel);
+
+ /* remove from tags list */
+ link_pop(&tag->link);
+
+ tag_free(tag);
+
+ return true;
+}
+
+struct track *
+track_add(struct tag *tag, const char *fname)
+{
+ struct track *track;
+
+ track = track_alloc(tag->fpath, fname);
+ OOM_CHECK(track);
+
+ track->tag = tag;
+
+ /* insert track into sorted tracks list */
+ list_push_back(&tracks, &track->link);
+
+ /* add to tag's tracks list */
+ list_push_back(&tag->tracks, &track->link_tt);
+
+ /* if track's tag is selected, update playlist */
+ if (link_inuse(&tag->link_sel))
+ playlist_update(false);
+
+ return track;
+}
+
+bool
+track_rm(struct track *track, bool sync_fs)
+{
+ if (sync_fs && !rm_file(track->fpath))
+ return false;
+
+ /* remove from tracks list */
+ link_pop(&track->link);
+
+ /* remove from tag's track list */
+ link_pop(&track->link_tt);
+
+ /* remove from playlist */
+ link_pop(&track->link_pl);
+
+ /* remove from player queue */
+ link_pop(&track->link_pq);
+
+ /* remove the reference as last used track */
+ if (player.track == track)
+ player.track = NULL;
+
+ track_free(track);
+
+ return true;
+}
+
+void
+data_load(void)
+{
+ struct dirent *ent;
+ struct tag *tag;
+ struct stat st;
+ char *path;
+ DIR *dir;
+
+ list_init(&tracks);
+ list_init(&tags);
+ list_init(&tags_sel);
+
+ datadir = getenv("TMUS_DATA");
+ if (!datadir) ERROR("TMUS_DATA not set!\n");
+
+ dir = opendir(datadir);
+ if (!dir) ERROR("Failed to access dir: %s\n", datadir);
+
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ path = aprintf("%s/%s", datadir, ent->d_name);
+ OOM_CHECK(path);
+
+ if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
+ tag = tag_add(ent->d_name);
+ tracks_load(tag);
+ }
+
+ free(path);
+ }
+
+ closedir(dir);
+}
+
+void
+data_save(void)
+{
+ struct link *link;
+ struct tag *tag;
+
+ for (LIST_ITER(&tags, link)) {
+ tag = UPCAST(link, struct tag, link);
+ tracks_save(tag);
+ }
+}
+
+void
+data_free(void)
+{
+ struct link *link;
+ struct tag *tag;
+
+ list_clear(&player.playlist);
+ list_clear(&player.queue);
+ list_clear(&player.history);
+
+ while (!list_empty(&tags)) {
+ link = list_pop_front(&tags);
+ tag = UPCAST(link, struct tag, link);
+ tag_rm(tag, false);
+ }
+}