summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlulf@carrot.studby.ntnu.no <lulf@carrot.studby.ntnu.no>2008-04-29 15:40:05 +0200
committerlulf@carrot.studby.ntnu.no <lulf@carrot.studby.ntnu.no>2008-04-29 15:40:05 +0200
commit5bdbe39c6e2af7b85482e0003ad52a4bc5a74212 (patch)
tree8344747258b40e7567265ecbc845a198fbd65362
parent3a5ef70e5d22148a221d89b24df09d2c60f8d0f5 (diff)
- Implement query system to select data from data structures.
-rw-r--r--include/mnode.h7
-rw-r--r--include/mp3fs.h22
-rw-r--r--src/mp3_subr.c173
3 files changed, 146 insertions, 56 deletions
diff --git a/include/mnode.h b/include/mnode.h
index d84c0ad..891ad3d 100644
--- a/include/mnode.h
+++ b/include/mnode.h
@@ -6,6 +6,11 @@ struct mnode {
char *path;
TagLib_File *tag;
/* Entry in search structure. */
- LIST_ENTRY(mnode) next;
+ LIST_ENTRY(mnode) coll_next;
+ LIST_ENTRY(mnode) sel_next;
+};
+
+struct collection {
+ LIST_HEAD(, mnode) head;
};
#endif
diff --git a/include/mp3fs.h b/include/mp3fs.h
index 4313cd1..5d17726 100644
--- a/include/mp3fs.h
+++ b/include/mp3fs.h
@@ -1,10 +1,11 @@
#ifndef _MP3FS_H_
#define _MP3FS_H_
struct fuse_args;
-int mp3_run(int, char **);
-void mp3_initscan(void);
-
+struct collection;
+struct mnode;
+int mp3_run(int, char **);
+struct collection *mp3_initscan(void);
/*
* Data passed to traverse function pointers.'
*/
@@ -12,10 +13,17 @@ struct filler_data {
void *buf;
fuse_fill_dir_t filler;
};
-/* Traverse files used in mp3_subr.c */
-typedef void traverse_fn_t(char *, void *);
-void traverse_hierarchy(char *, traverse_fn_t, void *);
-traverse_fn_t mp3_artist;
+/*
+ * Functions traversing the underlying filesystem and do operations on the
+ * files, for instance scanning the collection.
+ */
+typedef void traverse_fn_t(char *, struct collection *);
+void traverse_hierarchy(char *, traverse_fn_t, struct collection *);
traverse_fn_t mp3_scan;
+
+/*
+ * Functions performing queries on the collection.
+ */
+struct collection *mp3_select(char *, char *, char *);
#endif
diff --git a/src/mp3_subr.c b/src/mp3_subr.c
index e8d7ed5..ae684df 100644
--- a/src/mp3_subr.c
+++ b/src/mp3_subr.c
@@ -13,31 +13,46 @@
#include <tag_c.h>
#include <mp3fs.h>
+/* Local prototypes. */
+static int runfilter(struct collection *, struct collection *, struct mnode *,
+ filter_fn_t);
+
+struct queryvector {
+ char *artist;
+ char *title;
+ char *genre;
+};
+
+
+static void free_query_vector(struct queryvector *);
+static void fill_query_vector(struct queryvector *, struct mnode *);
+static int query_matches(struct queryvector *, struct queryvector *);
/* Simple list containing our nodes. */
-LIST_HEAD(, mnode) music;
+struct collection collection;
-traverse_fn_t mp3_scan;
+void
+mp3_initscan(char *musicpath)
+{
+ LIST_INIT(&collection.head);
+ traverse_hierarchy(musicpath, mp3_scan, &collection);
+}
/*
* Traverse a hierarchy given a directory path. Perform fileoperations on the
* files in the directory giving data as arguments, as well as recursing on
* sub-directories.
*/
-struct musiclist *
-traverse_hierarchy(char *dirpath, traverse_fn_t fileop, void *data)
+void
+traverse_hierarchy(char *dirpath, traverse_fn_t fileop, struct collection *coll)
{
DIR *dirp;
struct dirent *dp;
char filepath[MAXPATHLEN];
struct stat st;
- struct filler_data *fd;
- fd = data;
dirp = opendir(dirpath);
- if (dirp == NULL) {
- fd->filler(fd->buf, "lolerr", NULL, 0);
+ if (dirp == NULL)
return;
- }
/* Loop through all files. */
while ((dp = readdir(dirp)) != NULL) {
@@ -51,75 +66,137 @@ traverse_hierarchy(char *dirpath, traverse_fn_t fileop, void *data)
err(1, "error doing stat on %s", filepath);
/* Recurse if it's a directory. */
if (st.st_mode & S_IFDIR) {
- traverse_hierarchy(filepath, fileop, data);
+ traverse_hierarchy(filepath, fileop, coll);
continue;
}
/* If it's a regular file, perform the operation. */
if (st.st_mode & S_IFREG) {
- fileop(filepath, data);
+ fileop(filepath, coll);
}
}
closedir(dirp);
}
-void
-mp3_initscan(void)
-{
- LIST_INIT(&music);
-}
/* Scan the music initially. */
void
-mp3_scan(char *filepath, void *data)
+mp3_scan(char *filepath, struct collection *coll)
{
struct mnode *mp_new;
mp_new = malloc(sizeof(struct mnode));
if (mp_new == NULL)
err(1, "malloc");
- mp_new->tag = ID3Tag_New();
- if (mp_new->tag == NULL)
- err(1, "ID3Tag_New");
+ mp_new->tag = taglib_file_tag(filepath);
mp_new->path = strdup(filepath);
if (mp_new->path == NULL)
err(1, "strdup");
- ID3Tag_Link(mp_new->tag, mp_new->path);
/* Insert node into music list. */
- LIST_INSERT_HEAD(&music, mp, next);
+ LIST_INSERT_HEAD(&coll->head, mp_new, coll_next);
}
-/*
- * Retrieve artist name given a file.
+/* XXX: The query system must meet the following requirements:
+ * 1. Possible to do selections based on fields.
+ * 2. Possible to do selections based on data of a field (for instance, give the
+ * list of songs by an artist).
+ */
+/*
+ * Perform a query selecting artists given a filter.
*/
-struct mnode *
-mp3_artist(char *filepath, void *data)
+struct collection *
+mp3_select(char *artist, char *title, char *genre)
{
- struct filler_data *fd;
- fuse_fill_dir_t filler;
- void *buf;
- char name[MAXPATHLEN];
- TagLib_File *tagfile;
- TagLib_Tag *tag;
-
- /* Retrieve the filler function and pointers. */
- fd = (struct filler_data *)data;
- filler = fd->filler;
- buf = fd->buf;
-
- /* Get the tag. */
- tagfile = taglib_file_new(filepath);
- tag = taglib_file_tag(tagfile);
-
- char *artist = taglib_tag_artist(tag);
- if (artist == NULL)
- return;
+ struct mnode *mp;
+ struct collection *selection;
+ struct queryvectore qv, qv2;
+
+ qv.artist = artist;
+ qv.title = title;
+ qv.genre = genre;
+
+ /* Initialize selection structure. */
+ selection = malloc(sizeof(struct collection));
+ if (selection == NULL)
+ return (NULL);
+
+ LIST_INIT(&selection->head);
+ /* Filter our collection. */
+ LIST_FOREACH(mp, &collection.head, coll_next) {
+ /* First make sure it matches our criteria. */
+ fill_query_vector(&qv2, mp);
+ if (query_matches(&qv2, &qv) && !isduplicate(selection, &qv2))
+ LIST_INSERT_HEAD(&selection->head, mp, sel_next);
+ free_query_vector(&qv2);
+ }
+ return (selection);
+}
- strcpy(name, artist);
+/*
+ * Filter out unique fields.
+ */
+static int
+isduplicate(struct selection *selection, struct queryvector *qv)
+{
+ struct mnode *mp2;
+ struct queryvector qv_entry;
+
+ LIST_FOREACH(mp2, &selection->head, sel_next) {
+ /* Compare to determine if it's a duplicate. */
+ fill_query_vector(&qv_entry, mp2);
+ if (query_matches(&qv_entry, qv)) {
+ free_query_vector(&qv_entry);
+ return (1);
+ }
+ free_query_vector(&qv_entry);
+ }
+ return (0);
+}
- filler(buf, name, NULL, 0);
+/* Fill in a query vector given a node with tags. */
+static void
+fill_query_vector(struct queryvector *qv, struct mnode *mp)
+{
+ TagLib_Tag *tag;
+ tag = taglib_file_tag(mp->tag);
+ qv->artist = strdup(taglib_tag_artist(tag));
+ qv->title = strdup(taglib_tag_title(tag));
+ qv->genre = strdup(taglib_tag_genre(tag));
taglib_tag_free_strings();
- taglib_file_free(tagfile);
+}
+
+/* Free a query vector. */
+static void
+free_query_vector(struct queryvector *qv)
+{
+
+ if (qv->artist != NULL)
+ free(qv->artist);
+ if (qv->title != NULL)
+ free(qv->title);
+ if (qv->genre != NULL)
+ free(qv->genre);
+}
+
+/* Determine if two query vectors matches. */
+static int
+query_matches(struct queryvector *qv1, struct queryvector *qv2)
+{
+
+ /* Check if it matches the different fields. */
+ if (qv1->artist != NULL && qv2->artist != NULL) {
+ if (strcmp(qv1->artist, qv2->artist))
+ return (0);
+ }
+ if (qv1->title != NULL && qv2->title != NULL) {
+ if (strcmp(qv1->title, qv2->title))
+ return (0);
+ }
+ if (qv1->genre != NULL && qv2->genre != NULL) {
+ if (strcmp(qv1->genre, qv2->genre))
+ return (0);
+ }
+ return (1);
}