summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <lulf@nobby.studby.ntnu.no>2008-08-03 22:56:31 +0200
committerUlf Lilleengen <lulf@nobby.studby.ntnu.no>2008-08-03 22:56:31 +0200
commita33a6cd5fa66fb12435c4e5d7ad85c921ca50360 (patch)
tree2eab38e66869192904ffa382c86df7fd8a9e19ca
parent5b2fadec0592b79001a941eef03f0a1f1a95b04d (diff)
- Add TODO
- Support deeper nested hierarchies. - Add convenience functions for tokenizing and lookups.
-rw-r--r--TODO11
-rw-r--r--include/mp3fs.h4
-rw-r--r--src/mp3_subr.c86
-rwxr-xr-xsrc/mp3_vnops.c117
4 files changed, 203 insertions, 15 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..27bc335
--- /dev/null
+++ b/TODO
@@ -0,0 +1,11 @@
+TODO for the nearest future:
+- Add abstractions for checks that might be done several times, such as pathname
+ checks and try make it more generic than the current state.
+- Add abstractions for executing queries or listing albums and tracks.
+- Support other types than strings.
+- Simplify scanning a bit a lot of code used multiple times.
+- We should add filename path to database schema in case we want to touch the
+ data :)
+- Check for errors in mp3_list.
+- Add proper debugging facilities.
+- Support looking up a file and reading from it.
diff --git a/include/mp3fs.h b/include/mp3fs.h
index f8c5c5c..816af61 100644
--- a/include/mp3fs.h
+++ b/include/mp3fs.h
@@ -24,5 +24,7 @@ struct filler_data {
fuse_fill_dir_t filler;
};
-void mp3_list(char *, int, struct filler_data *);
+void mp3_list(int, struct filler_data *, char *);
+char *mp3_gettoken(const char *, int);
+int mp3_numtoken(const char *);
#endif
diff --git a/src/mp3_subr.c b/src/mp3_subr.c
index 25c5c01..c765ca9 100644
--- a/src/mp3_subr.c
+++ b/src/mp3_subr.c
@@ -104,7 +104,7 @@ mp3_scan(char *filepath)
if (artist == NULL)
break;
/* First find out if it exists. */
- ret = sqlite3_prepare(handle, "SELECT * FROM artist WHERE "
+ ret = sqlite3_prepare_v2(handle, "SELECT * FROM artist WHERE "
"name=?", -1, &st, NULL);
if (ret != SQLITE_OK) {
warnx("Error preparing statement: %s\n",
@@ -118,7 +118,7 @@ mp3_scan(char *filepath)
if (ret != SQLITE_DONE)
break;
/* Doesn't exist, so we can insert it. */
- ret = sqlite3_prepare(handle, "INSERT INTO artist(name) "
+ ret = sqlite3_prepare_v2(handle, "INSERT INTO artist(name) "
"VALUES(?)", -1, &st, NULL);
if (ret != SQLITE_OK) {
warnx("Error preparing statement: %s\n",
@@ -141,7 +141,7 @@ mp3_scan(char *filepath)
if (genre == NULL)
break;
/* First find out if it exists. */
- ret = sqlite3_prepare(handle, "SELECT * FROM genre WHERE "
+ ret = sqlite3_prepare_v2(handle, "SELECT * FROM genre WHERE "
"name=?", -1, &st, NULL);
if (ret != SQLITE_OK) {
warnx("Error preparing statement: %s\n",
@@ -155,7 +155,7 @@ mp3_scan(char *filepath)
if (ret != SQLITE_DONE)
break;
/* Doesn't exist, so we can insert it. */
- ret = sqlite3_prepare(handle, "INSERT INTO genre(name) "
+ ret = sqlite3_prepare_v2(handle, "INSERT INTO genre(name) "
"VALUES(?)", -1, &st, NULL);
if (ret != SQLITE_OK) {
warnx("Error preparing statement: %s\n",
@@ -182,7 +182,7 @@ mp3_scan(char *filepath)
break;
/* First find out if it exists. */
- ret = sqlite3_prepare(handle, "SELECT * FROM song, artist "
+ ret = sqlite3_prepare_v2(handle, "SELECT * FROM song, artist "
" WHERE artist.name=song.artistname AND title=? AND year=?",
-1, &st, NULL);
if (ret != SQLITE_OK) {
@@ -198,7 +198,7 @@ mp3_scan(char *filepath)
if (ret != SQLITE_DONE)
break;
/* Now, finally insert it. */
- ret = sqlite3_prepare(handle, "INSERT INTO song(title, "
+ ret = sqlite3_prepare_v2(handle, "INSERT INTO song(title, "
"artistname, album, genrename, year) VALUES(?, ?, ?, ?, ?)",
-1, &st, NULL);
if (ret != SQLITE_OK) {
@@ -220,13 +220,14 @@ mp3_scan(char *filepath)
}
} while (0);
taglib_tag_free_strings();
+ taglib_file_free(file);
}
/*
* Perform query and list the result from field.
*/
void
-mp3_list(char *query, int field, struct filler_data *fd)
+mp3_list(int field, struct filler_data *fd, char *query)
{
sqlite3_stmt *st;
fuse_fill_dir_t filler;
@@ -257,3 +258,74 @@ mp3_list(char *query, int field, struct filler_data *fd)
// XXX: Check for errors too.
sqlite3_close(handle);
}
+
+/*
+ * Count number of tokens in pathname.
+ * XXX: should we strip the first and last?
+ */
+int
+mp3_numtoken(const char *str)
+{
+ const char *ptr;
+ int num;
+
+ num = 0;
+ ptr = str;
+ if (*ptr == '/')
+ ptr++;
+ while ((*ptr != '\0')) {
+ if (*ptr++ == '/')
+ num++;
+ }
+ if (*(ptr - 1) == '/')
+ num--;
+ return (num + 1);
+}
+
+/*
+ * Extract token number toknum and return it in escaped form.
+ */
+char *
+mp3_gettoken(const char *str, int toknum)
+{
+ const char *ptr, *start, *end;
+ char *ret, *dest;
+ int numescape;
+ int i;
+ size_t size;
+
+ start = str;
+ /* Find the token start. */
+ for (i = 0; i < toknum; i++) {
+ start = strchr(start, '/');
+ if (start == NULL)
+ return (NULL);
+ start++;
+ }
+ /* Find the end pointer. */
+ end = strchr(start, '/');
+ if (end == NULL)
+ end = strchr(start, '\0');
+ /*
+ * Count how many bytes extra to take into account because of escaping.
+ */
+ numescape = 0;
+ ptr = start;
+ while ((ptr = strchr(ptr, '\'')) != NULL && ptr < end) {
+ ptr++;
+ numescape++;
+ }
+ size = (end - start) + numescape;
+ ret = malloc(size + 1);
+ if (ret == NULL)
+ return (ret);
+ ptr = start;
+ dest = ret;
+ while (ptr < end) {
+ if (*ptr == '\'')
+ *dest++ = '\\';
+ *dest++ = *ptr++;
+ }
+ ret[size] = '\0';
+ return (ret);
+}
diff --git a/src/mp3_vnops.c b/src/mp3_vnops.c
index 75f9ae1..9d592ae 100755
--- a/src/mp3_vnops.c
+++ b/src/mp3_vnops.c
@@ -20,6 +20,7 @@ char *logpath = "/home/lulf/dev/mp3fs/mp3fs.log";
static int mp3_getattr (const char *path, struct stat *stbuf)
{
+ int tokens;
memset (stbuf, 0, sizeof (struct stat));
if (strcmp (path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
@@ -27,12 +28,30 @@ static int mp3_getattr (const char *path, struct stat *stbuf)
return 0;
}
if (strcmp(path, "/Artists") == 0 ||
- strcmp(path, "/Genres") == 0) {
+ strcmp(path, "/Genres") == 0 ||
+ strcmp(path, "/Tracks") == 0) {
stbuf->st_mode = S_IFDIR | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = 12;
return 0;
}
+ tokens = mp3_numtoken(path);
+ if (tokens == 2) {
+ stbuf->st_mode = S_IFDIR | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = 12;
+ return 0;
+ } else if (tokens == 3) {
+ stbuf->st_mode = S_IFDIR | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = 12;
+ return 0;
+ } else if (tokens == 4) {
+ stbuf->st_mode = S_IFREG | 0644;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = 512;
+ return 0;
+ }
return -ENOENT;
}
@@ -41,6 +60,7 @@ static int mp3_readdir (const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi)
{
struct filler_data fd;
+ char *album, *genre, *name, *query;
filler (buf, ".", NULL, 0);
filler (buf, "..", NULL, 0);
@@ -48,8 +68,9 @@ static int mp3_readdir (const char *path, void *buf, fuse_fill_dir_t filler,
fd.filler = filler;
if (!strcmp(path, "/")) {
- filler (buf, "Artists", NULL, 0);
- filler (buf, "Genres", NULL, 0);
+ filler(buf, "Artists", NULL, 0);
+ filler(buf, "Genres", NULL, 0);
+ filler(buf, "Tracks", NULL, 0);
return (0);
}
@@ -58,13 +79,95 @@ static int mp3_readdir (const char *path, void *buf, fuse_fill_dir_t filler,
* 2. Find the mp3s that matches the tags given from the path.
* 3. Return the list of those mp3s.
*/
- if (strcmp(path, "/Artists") == 0) {
- mp3_list("SELECT name FROM artist", 0, &fd);
+ if (strncmp(path, "/Artists", 8) == 0) {
+ switch (mp3_numtoken(path)) {
+ // XXX: Put these into generic functions that can be
+ // reused.
+ case 1:
+ mp3_list(0, &fd, "SELECT name FROM artist");
+ break;
+ case 2:
+ /* So, now we got to find out the artist and list its albums. */
+ name = mp3_gettoken(path, 2);
+ if (name == NULL)
+ break;
+ asprintf(&query, "SELECT DISTINCT album FROM "
+ "song, artist WHERE song.artistname = "
+ "artist.name AND artist.name LIKE '%s'",
+ name);
+ if (query == NULL)
+ break;
+ mp3_list(0, &fd, query);
+ free(query);
+ free(name);
+ break;
+ case 3:
+ /* List songs in an album. */
+ name = mp3_gettoken(path, 2);
+ if (name == NULL)
+ break;
+ album = mp3_gettoken(path, 3);
+ if (album == NULL)
+ break;
+ asprintf(&query, "SELECT title FROM song, "
+ "artist WHERE song.artistname = artist.name"
+ " AND ARTIST.name LIKE '%s' AND song.album "
+ " LIKE '%s'", name, album);
+ if (query == NULL)
+ break;
+ mp3_list(0, &fd, query);
+ free(query);
+ free(album);
+ free(name);
+ break;
+ }
return (0);
- } else if (strcmp(path, "/Genres") == 0) {
- mp3_list("SELECT name FROM genre", 0, &fd);
+ } else if (strncmp(path, "/Genres", 7) == 0) {
+ switch (mp3_numtoken(path)) {
+ // XXX: Put these into generic functions that can be
+ // reused.
+ case 1:
+ mp3_list(0, &fd, "SELECT name FROM genre");
+ break;
+ case 2:
+ genre = mp3_gettoken(path, 2);
+ if (genre == NULL)
+ break;
+ asprintf(&query, "SELECT DISTINCT album FROM "
+ "song, genre WHERE song.genrename = "
+ "genre.name AND genre.name LIKE '%s'",
+ genre);
+ if (query == NULL)
+ break;
+ mp3_list(0, &fd, query);
+ free(query);
+ free(genre);
+ break;
+ case 3:
+ genre = mp3_gettoken(path, 2);
+ if (genre == NULL)
+ break;
+ album = mp3_gettoken(path, 3);
+ if (album == NULL)
+ break;
+ asprintf(&query, "SELECT title FROM song, genre "
+ "WHERE song.genrename = genre.name AND "
+ "genre.name LIKE '%s' AND song.album LIKE "
+ "'%s'", genre, album);
+ if (query == NULL)
+ break;
+ mp3_list(0, &fd, query);
+ free(query);
+ free(album);
+ free(genre);
+ break;
+ }
+ return (0);
+ } else if (strcmp(path, "/Tracks") == 0) {
+ mp3_list(0, &fd, "SELECT title FROM song");
return (0);
}
+
return (-ENOENT);
}