diff --git a/Makefile.am b/Makefile.am index 9efe87adb..84261fa64 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ mpd_headers = \ src/encoder_plugin.h \ src/encoder_list.h \ src/encoder_api.h \ + src/exclude.h \ src/fifo_buffer.h \ src/update.h \ src/update_internal.h \ @@ -211,6 +212,7 @@ src_mpd_SOURCES = \ src/directory_print.c \ src/database.c \ src/dirvec.c \ + src/exclude.c \ src/fifo_buffer.c \ src/filter_plugin.c \ src/filter_registry.c \ diff --git a/NEWS b/NEWS index b240c7e3e..47a8b9778 100644 --- a/NEWS +++ b/NEWS @@ -33,12 +33,14 @@ ver 0.16 (20??/??/??) - per-device software/hardware mixer setting * commands: - added new "status" line with more precise "elapsed time" +* update: + - automatically update the database with Linux inotify + - support .mpdignore files in the music directory * log unused/unknown block parameters * removed the deprecated "error_file" option * save state when stopped * renamed option "--stdout" to "--stderr" * removed options --create-db and --no-create-db -* automatically update the database with Linux inotify * state_file: save only if something has changed * obey $(sysconfdir) for default mpd.conf location * build with large file support by default diff --git a/doc/user.xml b/doc/user.xml index a8540fe1c..ca63eccd9 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -497,6 +497,12 @@ cd mpd-version Depending on the size of your music collection and the speed of the storage, this can take a while. + + + To exclude a file from the update, create a file called + .mpdignore in its parent directory. Each + line of that file may contain a list of shell wildcards. +
diff --git a/src/exclude.c b/src/exclude.c new file mode 100644 index 000000000..4a1fb21f6 --- /dev/null +++ b/src/exclude.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * The .mpdignore backend code. + * + */ + +#include "exclude.h" +#include "path.h" + +#include +#include +#include +#include +#include + +GSList * +exclude_list_load(const char *path_fs) +{ + FILE *file; + char line[1024]; + GSList *list = NULL; + + assert(path_fs != NULL); + + file = fopen(path_fs, "r"); + if (file == NULL) { + if (errno != ENOENT) { + char *path_utf8 = fs_charset_to_utf8(path_fs); + g_debug("Failed to open %s: %s", + path_utf8, g_strerror(errno)); + g_free(path_utf8); + } + + return NULL; + } + + while (fgets(line, sizeof(line), file) != NULL) { + char *p = strchr(line, '#'); + if (p != NULL) + *p = 0; + + p = g_strstrip(line); + if (*p != 0) + list = g_slist_prepend(list, g_strdup(p)); + } + + fclose(file); + + return list; +} + +void +exclude_list_free(GSList *list) +{ + while (list != NULL) { + g_free(list->data); + list = g_slist_remove(list, list->data); + } +} + +bool +exclude_list_check(GSList *list, const char *name_fs) +{ + assert(name_fs != NULL); + + /* XXX include full path name in check */ + + for (; list != NULL; list = list->next) { + const char *pattern = list->data; + + if (fnmatch(pattern, name_fs, FNM_PATHNAME|FNM_PERIOD) == 0) + return true; + } + + return false; +} diff --git a/src/exclude.h b/src/exclude.h new file mode 100644 index 000000000..637feb846 --- /dev/null +++ b/src/exclude.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * The .mpdignore backend code. + * + */ + +#ifndef MPD_EXCLUDE_H +#define MPD_EXCLUDE_H + +#include + +#include + +/** + * Loads and parses a .mpdignore file. + */ +GSList * +exclude_list_load(const char *path_fs); + +/** + * Frees a list returned by exclude_list_load(). + */ +void +exclude_list_free(GSList *list); + +/** + * Checks whether one of the patterns in the .mpdignore file matches + * the specified file name. + */ +bool +exclude_list_check(GSList *list, const char *name_fs); + +#endif diff --git a/src/update_walk.c b/src/update_walk.c index b79e95ae0..660640a67 100644 --- a/src/update_walk.c +++ b/src/update_walk.c @@ -19,6 +19,7 @@ #include "update_internal.h" #include "database.h" +#include "exclude.h" #include "directory.h" #include "song.h" #include "uri.h" @@ -154,6 +155,48 @@ delete_name_in(struct directory *parent, const char *name) } } +/* passed to songvec_for_each */ +static int +delete_song_if_excluded(struct song *song, void *_data) +{ + GSList *exclude_list = _data; + char *name_fs; + + assert(song->parent != NULL); + + name_fs = utf8_to_fs_charset(song->uri); + if (exclude_list_check(exclude_list, name_fs)) { + delete_song(song->parent, song); + modified = true; + } + + g_free(name_fs); + return 0; +} + +static void +remove_excluded_from_directory(struct directory *directory, + GSList *exclude_list) +{ + int i; + struct dirvec *dv = &directory->children; + + for (i = dv->nr; --i >= 0; ) { + struct directory *child = dv->base[i]; + char *name_fs = utf8_to_fs_charset(directory_get_name(child)); + + if (exclude_list_check(exclude_list, name_fs)) { + delete_directory(child); + modified = true; + } + + g_free(name_fs); + } + + songvec_for_each(&directory->songs, + delete_song_if_excluded, exclude_list); +} + /* passed to songvec_for_each */ static int delete_song_if_removed(struct song *song, void *_data) @@ -613,7 +656,8 @@ updateDirectory(struct directory *directory, const struct stat *st) { DIR *dir; struct dirent *ent; - char *path_fs; + char *path_fs, *exclude_path_fs; + GSList *exclude_list; assert(S_ISDIR(st->st_mode)); @@ -631,15 +675,24 @@ updateDirectory(struct directory *directory, const struct stat *st) return false; } + exclude_path_fs = g_strconcat(path_fs, G_DIR_SEPARATOR_S, + ".mpdignore", NULL); + exclude_list = exclude_list_load(exclude_path_fs); + g_free(exclude_path_fs); + g_free(path_fs); + if (exclude_list != NULL) + remove_excluded_from_directory(directory, exclude_list); + removeDeletedFromDirectory(directory); while ((ent = readdir(dir))) { char *utf8; struct stat st2; - if (skip_path(ent->d_name)) + if (skip_path(ent->d_name) || + exclude_list_check(exclude_list, ent->d_name)) continue; utf8 = fs_charset_to_utf8(ent->d_name); @@ -656,6 +709,8 @@ updateDirectory(struct directory *directory, const struct stat *st) g_free(utf8); } + exclude_list_free(exclude_list); + closedir(dir); directory->mtime = st->st_mtime;