mapper: new song-to-filesystem mapper library

The mapper library maps directory and song objects to file system
paths.  With this central library, the code mixture in path.c should
be cleaned up, and we will be able to add neat features like aliasing.
This commit is contained in:
Max Kellermann 2008-10-14 11:10:49 +02:00
parent a52a9fc1fc
commit 5b71d5f6f7
9 changed files with 241 additions and 53 deletions

View File

@ -79,6 +79,7 @@ mpd_headers = \
os_compat.h \
outputBuffer.h \
path.h \
mapper.h \
pcm_utils.h \
permission.h \
player_thread.h \
@ -158,6 +159,7 @@ mpd_SOURCES = \
compress.c \
outputBuffer.c \
path.c \
mapper.c \
pcm_utils.c \
permission.c \
player_thread.c \

View File

@ -22,27 +22,28 @@
#include "decoder_internal.h"
#include "player_control.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
#include "log.h"
#include "ls.h"
static void decodeStart(void)
{
struct song *song = dc.next_song;
struct decoder decoder;
int ret;
bool close_instream = true;
InputStream inStream;
struct decoder_plugin *plugin = NULL;
char path_max_fs[MPD_PATH_MAX];
char path_max_utf8[MPD_PATH_MAX];
song_get_url(dc.next_song, path_max_utf8);
if (!isRemoteUrl(path_max_utf8)) {
rmp2amp_r(path_max_fs,
utf8_to_fs_charset(path_max_fs, path_max_utf8));
} else
if (song_is_file(song))
map_song_fs(song, path_max_fs);
else {
char path_max_utf8[MPD_PATH_MAX];
song_get_url(song, path_max_utf8);
pathcpy_trunc(path_max_fs, path_max_utf8);
}
dc.current_song = dc.next_song; /* NEED LOCK */
if (openInputStream(&inStream, path_max_fs) < 0) {
@ -74,7 +75,7 @@ static void decodeStart(void)
goto stop;
ret = DECODE_ERROR_UNKTYPE;
if (isRemoteUrl(path_max_utf8)) {
if (!song_is_file(song)) {
unsigned int next = 0;
/* first we try mime types: */
@ -92,7 +93,7 @@ static void decodeStart(void)
/* if that fails, try suffix matching the URL: */
if (plugin == NULL) {
const char *s = getSuffix(path_max_utf8);
const char *s = getSuffix(path_max_fs);
next = 0;
while (ret && (plugin = decoder_plugin_from_suffix(s, next++))) {
if (plugin->stream_decode == NULL)
@ -123,7 +124,7 @@ static void decodeStart(void)
}
} else {
unsigned int next = 0;
const char *s = getSuffix(path_max_utf8);
const char *s = getSuffix(path_max_fs);
while (ret && (plugin = decoder_plugin_from_suffix(s, next++))) {
if (!plugin->stream_types & INPUT_PLUGIN_STREAM_FILE)
continue;

77
src/mapper.c Normal file
View File

@ -0,0 +1,77 @@
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Maps directory and song objects to file system paths.
*/
#include "mapper.h"
#include "directory.h"
#include "song.h"
#include "path.h"
const char *
map_directory_fs(const struct directory *directory, char *buffer)
{
const char *dirname = directory_get_path(directory);
if (isRootDirectory(dirname))
return musicDir;
return rmp2amp_r(buffer, utf8_to_fs_charset(buffer, dirname));
}
const char *
map_directory_child_fs(const struct directory *directory, const char *name,
char *buffer)
{
char buffer2[MPD_PATH_MAX];
const char *parent_fs;
parent_fs = map_directory_fs(directory, buffer2);
if (parent_fs == NULL)
return NULL;
utf8_to_fs_charset(buffer, name);
pfx_dir(buffer, name, strlen(name),
parent_fs, strlen(parent_fs));
return buffer;
}
const char *
map_song_fs(const struct song *song, char *buffer)
{
assert(song->parent != NULL);
return map_directory_child_fs(song->parent, song->url, buffer);
}
const char *
map_fs_to_utf8(const char *path_fs, char *buffer)
{
size_t music_path_length = strlen(musicDir);
if (strncmp(path_fs, musicDir, music_path_length) == 0 &&
path_fs[music_path_length] == '/')
/* remove musicDir prefix */
path_fs += music_path_length;
else if (path_fs[0] == '/')
/* not within musicDir */
return NULL;
return fs_charset_to_utf8(buffer, path_fs);
}

74
src/mapper.h Normal file
View File

@ -0,0 +1,74 @@
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Maps directory and song objects to file system paths.
*/
#ifndef MAPPER_H
#define MAPPER_H
struct directory;
struct song;
/**
* Determines the file system path of a directory object.
*
* @param directory the directory object
* @param a buffer which is MPD_PATH_MAX bytes long
* @return the path in file system encoding, or NULL if mapping failed
*/
const char *
map_directory_fs(const struct directory *directory, char *buffer);
/**
* Determines the file system path of a directory's child (may be a
* sub directory or a song).
*
* @param directory the parent directory object
* @param name the child's name in UTF-8
* @param a buffer which is MPD_PATH_MAX bytes long
* @return the path in file system encoding, or NULL if mapping failed
*/
const char *
map_directory_child_fs(const struct directory *directory, const char *name,
char *buffer);
/**
* Determines the file system path of a song. This must not be a
* remote song.
*
* @param song the song object
* @param a buffer which is MPD_PATH_MAX bytes long
* @return the path in file system encoding, or NULL if mapping failed
*/
const char *
map_song_fs(const struct song *song, char *buffer);
/**
* Maps a file system path (relative to the music directory or
* absolute) to a relative path in UTF-8 encoding.
*
* @param path_fs a path in file system encoding
* @param buffer a buffer which is MPD_PATH_MAX bytes long
* @return the relative path in UTF-8, or NULL if mapping failed
*/
const char *
map_fs_to_utf8(const char *path_fs, char *buffer);
#endif

View File

@ -28,6 +28,7 @@
#include "conf.h"
#include "database.h"
#include "log.h"
#include "mapper.h"
#include "path.h"
#include "utils.h"
#include "state_file.h"

View File

@ -19,21 +19,25 @@
#include "playlist_save.h"
#include "playlist.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
#include "ls.h"
#include "database.h"
void
playlist_print_song(FILE *file, const struct song *song)
{
char tmp1[MPD_PATH_MAX], tmp2[MPD_PATH_MAX];
song_get_url(song, tmp1);
utf8_to_fs_charset(tmp2, tmp1);
if (playlist_saveAbsolutePaths && song_is_file(song))
fprintf(file, "%s\n", rmp2amp_r(tmp2, tmp2));
else
if (playlist_saveAbsolutePaths && song_is_file(song)) {
const char *path = map_song_fs(song, tmp1);
if (path != NULL)
fprintf(file, "%s\n", path);
} else {
song_get_url(song, tmp1);
utf8_to_fs_charset(tmp2, tmp1);
fprintf(file, "%s\n", tmp2);
}
}
void
@ -42,8 +46,10 @@ playlist_print_uri(FILE *file, const char *uri)
char tmp[MPD_PATH_MAX];
const char *s;
s = utf8_to_fs_charset(tmp, uri);
if (playlist_saveAbsolutePaths && !isValidRemoteUtf8Url(s))
s = rmp2amp_r(tmp, s);
s = map_directory_child_fs(db_get_root(), uri, tmp);
else
s = utf8_to_fs_charset(tmp, uri);
fprintf(file, "%s\n", s);
}

View File

@ -21,6 +21,7 @@
#include "directory.h"
#include "utils.h"
#include "log.h"
#include "mapper.h"
#include "path.h"
#include "playlist.h"
#include "decoder_list.h"
@ -95,14 +96,12 @@ song_file_update(struct song *song)
{
struct decoder_plugin *plugin;
unsigned int next = 0;
char path_max_tmp[MPD_PATH_MAX];
char abs_path[MPD_PATH_MAX];
struct stat st;
assert(song_is_file(song));
utf8_to_fs_charset(abs_path, song_get_url(song, path_max_tmp));
rmp2amp_r(abs_path, abs_path);
map_song_fs(song, abs_path);
if (song->tag != NULL) {
tag_free(song->tag);

View File

@ -19,6 +19,7 @@
#include "storedPlaylist.h"
#include "playlist_save.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
#include "utils.h"
#include "ls.h"
@ -91,7 +92,6 @@ List *loadStoredPlaylist(const char *utf8path)
FILE *file;
char buffer[MPD_PATH_MAX];
char path_max_tmp[MPD_PATH_MAX];
const size_t musicDir_len = strlen(musicDir);
if (!is_valid_playlist_name(utf8path))
return NULL;
@ -105,19 +105,27 @@ List *loadStoredPlaylist(const char *utf8path)
while (myFgets(buffer, sizeof(buffer), file)) {
char *s = buffer;
struct song *song;
const char *path_utf8;
if (*s == PLAYLIST_COMMENT)
continue;
if (s[musicDir_len] == '/' &&
!strncmp(s, musicDir, musicDir_len))
memmove(s, s + musicDir_len + 1,
strlen(s + musicDir_len + 1) + 1);
if ((song = db_get_song(s))) {
if (isValidRemoteUtf8Url(s))
insertInListWithoutKey(list, xstrdup(s));
else {
struct song *song;
path_utf8 = map_fs_to_utf8(s, path_max_tmp);
if (path_utf8 == NULL)
continue;
song = db_get_song(path_utf8);
if (song == NULL)
continue;
song_get_url(song, path_max_tmp);
insertInListWithoutKey(list, xstrdup(path_max_tmp));
} else if (isValidRemoteUtf8Url(s))
insertInListWithoutKey(list, xstrdup(s));
}
if (list->numberOfNodes >= playlist_max_length)
break;

View File

@ -23,6 +23,7 @@
#include "song.h"
#include "log.h"
#include "ls.h"
#include "mapper.h"
#include "path.h"
#include "playlist.h"
#include "utils.h"
@ -150,11 +151,11 @@ static int
delete_song_if_removed(struct song *song, void *_data)
{
struct delete_data *data = _data;
const char *path;
struct stat st;
data->tmp = song_get_url(song, data->tmp);
assert(data->tmp);
if (!isFile(data->tmp, NULL)) {
if ((path = map_song_fs(song, data->tmp)) == NULL ||
stat(data->tmp, &st) < 0 || !S_ISREG(st.st_mode)) {
delete_song(data->dir, song);
modified = true;
}
@ -169,7 +170,12 @@ removeDeletedFromDirectory(char *path_max_tmp, struct directory *directory)
struct delete_data data;
for (i = dv->nr; --i >= 0; ) {
if (isDir(dv->base[i]->path))
const char *path_fs;
struct stat st;
path_fs = map_directory_fs(dv->base[i], path_max_tmp);
if (path_fs == NULL || (stat(path_fs, &st) == 0 &&
S_ISDIR(st.st_mode)))
continue;
LOG("removing directory: %s\n", dv->base[i]->path);
dirvec_delete(dv, dv->base[i]);
@ -181,12 +187,26 @@ removeDeletedFromDirectory(char *path_max_tmp, struct directory *directory)
songvec_for_each(&directory->songs, delete_song_if_removed, &data);
}
static const char *opendir_path(char *path_max_tmp, const char *dirname)
static int
stat_directory(const struct directory *directory, struct stat *st)
{
if (*dirname != '\0')
return rmp2amp_r(path_max_tmp,
utf8_to_fs_charset(path_max_tmp, dirname));
return musicDir;
char buffer[MPD_PATH_MAX];
const char *path_fs;
path_fs = map_directory_fs(directory, buffer);
if (path_fs == NULL)
return -1;
return stat(path_fs, st);
}
static int
stat_directory_child(const struct directory *parent, const char *name,
struct stat *st)
{
char path_fs[MPD_PATH_MAX];
map_directory_child_fs(parent, name, path_fs);
return stat(path_fs, st);
}
static int
@ -194,7 +214,7 @@ statDirectory(struct directory *dir)
{
struct stat st;
if (myStat(directory_get_path(dir), &st) < 0)
if (stat_directory(dir, &st) < 0)
return -1;
directory_set_stat(dir, &st);
@ -296,15 +316,19 @@ static bool
updateDirectory(struct directory *directory, const struct stat *st)
{
DIR *dir;
const char *dirname = directory_get_path(directory);
struct dirent *ent;
char path_max_tmp[MPD_PATH_MAX];
const char *path_fs;
assert(S_ISDIR(st->st_mode));
directory_set_stat(directory, st);
dir = opendir(opendir_path(path_max_tmp, dirname));
path_fs = map_directory_fs(directory, path_max_tmp);
if (path_fs == NULL)
return false;
dir = opendir(path_fs);
if (!dir)
return false;
@ -321,15 +345,11 @@ updateDirectory(struct directory *directory, const struct stat *st)
if (!utf8)
continue;
if (!isRootDirectory(directory->path))
utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8),
dirname, strlen(dirname));
if (myStat(path_max_tmp, &st2) == 0)
if (stat_directory_child(directory, utf8, &st2) == 0)
updateInDirectory(directory,
mpd_basename(path_max_tmp), &st2);
path_max_tmp, &st2);
else
delete_name_in(directory, mpd_basename(path_max_tmp));
delete_name_in(directory, path_max_tmp);
}
closedir(dir);
@ -348,7 +368,7 @@ directory_make_child_checked(struct directory *parent, const char *path)
if (directory != NULL)
return directory;
if (myStat(path, &st) < 0 ||
if (stat_directory_child(parent, mpd_basename(path), &st) < 0 ||
inodeFoundInParent(parent, st.st_ino, st.st_dev))
return NULL;
@ -398,7 +418,7 @@ updatePath(const char *path)
name = mpd_basename(path);
if (myStat(path, &st) == 0)
if (stat_directory_child(parent, name, &st) == 0)
updateInDirectory(parent, name, &st);
else
delete_name_in(parent, name);
@ -413,7 +433,7 @@ static void * update_task(void *_path)
struct directory *directory = db_get_root();
struct stat st;
if (myStat(directory_get_path(directory), &st) == 0)
if (stat_directory(directory, &st) == 0)
updateDirectory(directory, &st);
}