From a33a6cd5fa66fb12435c4e5d7ad85c921ca50360 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Sun, 3 Aug 2008 22:56:31 +0200 Subject: - Add TODO - Support deeper nested hierarchies. - Add convenience functions for tokenizing and lookups. --- TODO | 11 ++++++ include/mp3fs.h | 4 +- src/mp3_subr.c | 86 +++++++++++++++++++++++++++++++++++++---- src/mp3_vnops.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 203 insertions(+), 15 deletions(-) create mode 100644 TODO 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); } -- cgit v1.2.3