From 5bdbe39c6e2af7b85482e0003ad52a4bc5a74212 Mon Sep 17 00:00:00 2001 From: "lulf@carrot.studby.ntnu.no" Date: Tue, 29 Apr 2008 15:40:05 +0200 Subject: - Implement query system to select data from data structures. --- include/mnode.h | 7 ++- include/mp3fs.h | 22 ++++--- src/mp3_subr.c | 173 ++++++++++++++++++++++++++++++++++++++++---------------- 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 #include +/* 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); } -- cgit v1.2.3