aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/audio.c2
-rw-r--r--src/audio.h2
-rw-r--r--src/audioOutput.c3
-rw-r--r--src/audioOutput.h5
-rw-r--r--src/audioOutputs/audioOutput_shout.c20
-rw-r--r--src/command.c2
-rw-r--r--src/dbUtils.c6
-rw-r--r--src/directory.c8
-rw-r--r--src/inputPlugin.h2
-rw-r--r--src/inputPlugins/_flac_common.c12
-rw-r--r--src/inputPlugins/_flac_common.h6
-rw-r--r--src/inputPlugins/aac_plugin.c8
-rw-r--r--src/inputPlugins/audiofile_plugin.c6
-rw-r--r--src/inputPlugins/flac_plugin.c20
-rw-r--r--src/inputPlugins/mod_plugin.c8
-rw-r--r--src/inputPlugins/mp3_plugin.c51
-rw-r--r--src/inputPlugins/mp4_plugin.c30
-rw-r--r--src/inputPlugins/mpc_plugin.c10
-rw-r--r--src/inputPlugins/oggflac_plugin.c6
-rw-r--r--src/inputPlugins/oggvorbis_plugin.c24
-rw-r--r--src/inputPlugins/wavpack_plugin.c13
-rw-r--r--src/locate.c8
-rw-r--r--src/main.c2
-rw-r--r--src/metadata_pipe.c34
-rw-r--r--src/metadata_pipe.h14
-rw-r--r--src/outputBuffer.c6
-rw-r--r--src/path.c2
-rw-r--r--src/playlist.c10
-rw-r--r--src/playlist.h2
-rw-r--r--src/song.c43
-rw-r--r--src/song.h4
-rw-r--r--src/tag.c515
-rw-r--r--src/tag.h64
-rw-r--r--src/tagTracker.c152
-rw-r--r--src/tagTracker.h4
-rw-r--r--src/tag_id3.c367
-rw-r--r--src/tag_id3.h33
-rw-r--r--src/tag_pool.c139
-rw-r--r--src/tag_pool.h33
-rw-r--r--src/tree.c699
-rw-r--r--src/tree.h63
-rw-r--r--src/utf8.c18
-rw-r--r--src/utf8.h4
44 files changed, 998 insertions, 1468 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 70306c125..d8640d304 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -81,8 +81,9 @@ mpd_headers = \
state_file.h \
stats.h \
tag.h \
+ tag_pool.h \
+ tag_id3.h \
tagTracker.h \
- tree.h \
utf8.h \
utils.h \
volume.h \
@@ -138,8 +139,9 @@ mpd_SOURCES = \
state_file.c \
stats.c \
tag.c \
+ tag_pool.c \
+ tag_id3.c \
tagTracker.c \
- tree.c \
utils.c \
volume.c \
utf8.c \
diff --git a/src/audio.c b/src/audio.c
index 409761177..9a65df858 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -419,7 +419,7 @@ void closeAudioDevice(void)
audioOpened = 0;
}
-void sendMetadataToAudioDevice(const MpdTag * tag)
+void sendMetadataToAudioDevice(const struct mpd_tag *tag)
{
unsigned int i;
diff --git a/src/audio.h b/src/audio.h
index 0032cff22..92cb9cf11 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -55,7 +55,7 @@ int isAudioDeviceOpen(void);
int isCurrentAudioFormat(const AudioFormat * audioFormat);
-void sendMetadataToAudioDevice(const MpdTag * tag);
+void sendMetadataToAudioDevice(const struct mpd_tag *tag);
/* these functions are called in the main parent process while the child
process is busy playing to the audio */
diff --git a/src/audioOutput.c b/src/audioOutput.c
index f165979d0..d5b37f0fe 100644
--- a/src/audioOutput.c
+++ b/src/audioOutput.c
@@ -244,7 +244,8 @@ void finishAudioOutput(AudioOutput * audioOutput)
free(audioOutput->convBuffer);
}
-void sendMetadataToAudioOutput(AudioOutput * audioOutput, const MpdTag * tag)
+void sendMetadataToAudioOutput(AudioOutput * audioOutput,
+ const struct mpd_tag *tag)
{
if (!audioOutput->sendMetdataFunc)
return;
diff --git a/src/audioOutput.h b/src/audioOutput.h
index f82eedfba..4ce67a5de 100644
--- a/src/audioOutput.h
+++ b/src/audioOutput.h
@@ -50,7 +50,7 @@ typedef void (*AudioOutputDropBufferedAudioFunc) (AudioOutput * audioOutput);
typedef void (*AudioOutputCloseDeviceFunc) (AudioOutput * audioOutput);
typedef void (*AudioOutputSendMetadataFunc) (AudioOutput * audioOutput,
- const MpdTag * tag);
+ const struct mpd_tag *tag);
struct _AudioOutput {
int open;
@@ -104,7 +104,8 @@ void dropBufferedAudioOutput(AudioOutput * audioOutput);
void closeAudioOutput(AudioOutput * audioOutput);
void finishAudioOutput(AudioOutput * audioOutput);
int keepAudioOutputAlive(AudioOutput * audioOutput, int ms);
-void sendMetadataToAudioOutput(AudioOutput * audioOutput, const MpdTag * tag);
+void sendMetadataToAudioOutput(AudioOutput * audioOutput,
+ const struct mpd_tag *tag);
void printAllOutputPluginTypes(FILE * fp);
diff --git a/src/audioOutputs/audioOutput_shout.c b/src/audioOutputs/audioOutput_shout.c
index c1f784986..b65a8253f 100644
--- a/src/audioOutputs/audioOutput_shout.c
+++ b/src/audioOutputs/audioOutput_shout.c
@@ -56,7 +56,7 @@ typedef struct _ShoutData {
int opened;
- MpdTag *tag;
+ struct mpd_tag *tag;
int tagToSend;
int timeout;
@@ -93,7 +93,7 @@ static void freeShoutData(ShoutData * sd)
if (sd->shoutConn)
shout_free(sd->shoutConn);
if (sd->tag)
- freeMpdTag(sd->tag);
+ tag_free(sd->tag);
if (sd->timer)
timer_free(sd->timer);
@@ -402,15 +402,15 @@ static void copyTagToVorbisComment(ShoutData * sd)
int i;
for (i = 0; i < sd->tag->numOfItems; i++) {
- switch (sd->tag->items[i].type) {
+ switch (sd->tag->items[i]->type) {
case TAG_ITEM_ARTIST:
- addTag(sd, "ARTIST", sd->tag->items[i].value);
+ addTag(sd, "ARTIST", sd->tag->items[i]->value);
break;
case TAG_ITEM_ALBUM:
- addTag(sd, "ALBUM", sd->tag->items[i].value);
+ addTag(sd, "ALBUM", sd->tag->items[i]->value);
break;
case TAG_ITEM_TITLE:
- addTag(sd, "TITLE", sd->tag->items[i].value);
+ addTag(sd, "TITLE", sd->tag->items[i]->value);
break;
default:
break;
@@ -593,7 +593,7 @@ static void myShout_sendMetadata(ShoutData * sd)
}
}
- /*if(sd->tag) freeMpdTag(sd->tag);
+ /*if(sd->tag) tag_free(sd->tag);
sd->tag = NULL; */
sd->tagToSend = 0;
}
@@ -663,19 +663,19 @@ static int myShout_play(AudioOutput * audioOutput,
return 0;
}
-static void myShout_setTag(AudioOutput * audioOutput, MpdTag * tag)
+static void myShout_setTag(AudioOutput * audioOutput, const struct mpd_tag *tag)
{
ShoutData *sd = (ShoutData *) audioOutput->data;
if (sd->tag)
- freeMpdTag(sd->tag);
+ tag_free(sd->tag);
sd->tag = NULL;
sd->tagToSend = 0;
if (!tag)
return;
- sd->tag = mpdTagDup(tag);
+ sd->tag = tag_dup(tag);
sd->tagToSend = 1;
}
diff --git a/src/command.c b/src/command.c
index 805addde1..bc646804e 100644
--- a/src/command.c
+++ b/src/command.c
@@ -245,7 +245,7 @@ static int handleUrlHandlers(int fd, mpd_unused int *permission,
static int handleTagTypes(int fd, mpd_unused int *permission,
mpd_unused int argc, mpd_unused char *argv[])
{
- printTagTypes(fd);
+ tag_print_types(fd);
return 0;
}
diff --git a/src/dbUtils.c b/src/dbUtils.c
index 519c1802d..d39c9908c 100644
--- a/src/dbUtils.c
+++ b/src/dbUtils.c
@@ -257,7 +257,7 @@ static void freeListCommandItem(ListCommandItem * item)
static void visitTag(int fd, Song * song, enum tag_type tagType)
{
int i;
- MpdTag *tag = song->tag;
+ struct mpd_tag *tag = song->tag;
if (tagType == LOCATE_TAG_FILE_TYPE) {
printSongUrl(fd, song);
@@ -268,8 +268,8 @@ static void visitTag(int fd, Song * song, enum tag_type tagType)
return;
for (i = 0; i < tag->numOfItems; i++) {
- if (tag->items[i].type == tagType) {
- visitInTagTracker(tagType, tag->items[i].value);
+ if (tag->items[i]->type == tagType) {
+ visitInTagTracker(tagType, tag->items[i]->value);
}
}
}
diff --git a/src/directory.c b/src/directory.c
index 94a55d664..33a862ddd 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -901,7 +901,7 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
{
char buffer[MPD_PATH_MAX * 2];
int bufferSize = MPD_PATH_MAX * 2;
- char *key;
+ char key[MPD_PATH_MAX * 2];
Directory *subDirectory;
int strcmpRet;
char *name;
@@ -911,7 +911,7 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
while (myFgets(buffer, bufferSize, fp)
&& 0 != strncmp(DIRECTORY_END, buffer, strlen(DIRECTORY_END))) {
if (0 == strncmp(DIRECTORY_DIR, buffer, strlen(DIRECTORY_DIR))) {
- key = xstrdup(&(buffer[strlen(DIRECTORY_DIR)]));
+ strcpy(key, &(buffer[strlen(DIRECTORY_DIR)]));
if (!myFgets(buffer, bufferSize, fp))
FATAL("Error reading db, fgets\n");
/* for compatibility with db's prior to 0.11 */
@@ -925,7 +925,7 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
strlen(DIRECTORY_BEGIN))) {
FATAL("Error reading db at line: %s\n", buffer);
}
- name = xstrdup(&(buffer[strlen(DIRECTORY_BEGIN)]));
+ name = &(buffer[strlen(DIRECTORY_BEGIN)]);
while (nextDirNode && (strcmpRet =
strcmp(key,
@@ -951,8 +951,6 @@ static void readDirectoryInfo(FILE * fp, Directory * directory)
(void *)subDirectory);
}
- free(name);
- free(key);
readDirectoryInfo(fp, subDirectory);
} else if (0 == strncmp(SONG_BEGIN, buffer, strlen(SONG_BEGIN))) {
readSongInfoIntoList(fp, directory->songs, directory);
diff --git a/src/inputPlugin.h b/src/inputPlugin.h
index 161a0db59..0fd39ea9f 100644
--- a/src/inputPlugin.h
+++ b/src/inputPlugin.h
@@ -54,7 +54,7 @@ typedef int (*InputPlugin_fileDecodeFunc) (char *path);
/* file should be the full path! Returns NULL if a tag cannot be found
* or read */
-typedef MpdTag *(*InputPlugin_tagDupFunc) (char *file);
+typedef struct mpd_tag *(*InputPlugin_tagDupFunc) (char *file);
typedef struct _InputPlugin {
const char *name;
diff --git a/src/inputPlugins/_flac_common.c b/src/inputPlugins/_flac_common.c
index 6890c7c50..9950f75db 100644
--- a/src/inputPlugins/_flac_common.c
+++ b/src/inputPlugins/_flac_common.c
@@ -101,7 +101,7 @@ static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static unsigned int commentMatchesAddToTag(const
FLAC__StreamMetadata_VorbisComment_Entry
* entry, unsigned int itemType,
- MpdTag ** tag)
+ struct mpd_tag ** tag)
{
const char *str;
size_t slen;
@@ -123,10 +123,10 @@ static unsigned int commentMatchesAddToTag(const
if ((vlen > 0) && (0 == strncasecmp(str, (char *)entry->entry, slen))
&& (*(entry->entry + slen) == '=')) {
if (!*tag)
- *tag = newMpdTag();
+ *tag = tag_new();
- addItemToMpdTagWithLen(*tag, itemType,
- (char *)(entry->entry + slen + 1), vlen);
+ tag_add_item_n(*tag, itemType,
+ (char *)(entry->entry + slen + 1), vlen);
return 1;
}
@@ -134,8 +134,8 @@ static unsigned int commentMatchesAddToTag(const
return 0;
}
-MpdTag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
- MpdTag * tag)
+struct mpd_tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
+ struct mpd_tag * tag)
{
unsigned int i, j;
FLAC__StreamMetadata_VorbisComment_Entry *comments;
diff --git a/src/inputPlugins/_flac_common.h b/src/inputPlugins/_flac_common.h
index 6fe5bd744..a1b0cac8f 100644
--- a/src/inputPlugins/_flac_common.h
+++ b/src/inputPlugins/_flac_common.h
@@ -146,7 +146,7 @@ typedef struct {
FLAC__uint64 position;
InputStream *inStream;
ReplayGainInfo *replayGainInfo;
- MpdTag *tag;
+ struct mpd_tag *tag;
} FlacData;
/* initializes a given FlacData struct */
@@ -157,8 +157,8 @@ void flac_error_common_cb(const char *plugin,
FLAC__StreamDecoderErrorStatus status,
FlacData * data);
-MpdTag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
- MpdTag * tag);
+struct mpd_tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
+ struct mpd_tag *tag);
/* keep this inlined, this is just macro but prettier :) */
static inline enum dc_action flacSendChunk(FlacData * data)
diff --git a/src/inputPlugins/aac_plugin.c b/src/inputPlugins/aac_plugin.c
index 512e73e53..dc97c1e08 100644
--- a/src/inputPlugins/aac_plugin.c
+++ b/src/inputPlugins/aac_plugin.c
@@ -553,14 +553,14 @@ out:
return 0;
}
-static MpdTag *aacTagDup(char *file)
+static struct mpd_tag *aacTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int file_time = getAacTotalTime(file);
if (file_time >= 0) {
- if ((ret = id3Dup(file)) == NULL)
- ret = newMpdTag();
+ if ((ret = tag_id3_load(file)) == NULL)
+ ret = tag_new();
ret->time = file_time;
} else {
DEBUG("aacTagDup: Failed to get total song time from: %s\n",
diff --git a/src/inputPlugins/audiofile_plugin.c b/src/inputPlugins/audiofile_plugin.c
index fcebf562b..6fcc98239 100644
--- a/src/inputPlugins/audiofile_plugin.c
+++ b/src/inputPlugins/audiofile_plugin.c
@@ -116,14 +116,14 @@ static int audiofile_decode(char *path)
return 0;
}
-static MpdTag *audiofileTagDup(char *file)
+static struct mpd_tag *audiofileTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int total_time = getAudiofileTotalTime(file);
if (total_time >= 0) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = total_time;
} else {
DEBUG
diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c
index de0648c90..89e988a03 100644
--- a/src/inputPlugins/flac_plugin.c
+++ b/src/inputPlugins/flac_plugin.c
@@ -292,9 +292,9 @@ static FLAC__StreamDecoderWriteStatus flacWrite(const flac_decoder *dec,
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
-static MpdTag *flacMetadataDup(char *file, int *vorbisCommentFound)
+static struct mpd_tag *flacMetadataDup(char *file, int *vorbisCommentFound)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
FLAC__Metadata_SimpleIterator *it;
FLAC__StreamMetadata *block = NULL;
@@ -338,7 +338,7 @@ static MpdTag *flacMetadataDup(char *file, int *vorbisCommentFound)
*vorbisCommentFound = 1;
} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
@@ -350,9 +350,9 @@ static MpdTag *flacMetadataDup(char *file, int *vorbisCommentFound)
return ret;
}
-static MpdTag *flacTagDup(char *file)
+static struct mpd_tag *flacTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int foundVorbisComment = 0;
ret = flacMetadataDup(file, &foundVorbisComment);
@@ -362,10 +362,10 @@ static MpdTag *flacTagDup(char *file)
return NULL;
}
if (!foundVorbisComment) {
- MpdTag *temp = id3Dup(file);
+ struct mpd_tag *temp = tag_id3_load(file);
if (temp) {
temp->time = ret->time;
- freeMpdTag(ret);
+ tag_free(ret);
ret = temp;
}
}
@@ -466,9 +466,9 @@ static int flac_decode(InputStream * inStream)
/* some of this stuff is duplicated from oggflac_plugin.c */
extern InputPlugin oggflacPlugin;
-static MpdTag *oggflac_tag_dup(char *file)
+static struct mpd_tag *oggflac_tag_dup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
FLAC__Metadata_Iterator *it;
FLAC__StreamMetadata *block;
FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
@@ -484,7 +484,7 @@ static MpdTag *oggflac_tag_dup(char *file)
ret = copyVorbisCommentBlockToMpdTag(block, ret);
} else if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
diff --git a/src/inputPlugins/mod_plugin.c b/src/inputPlugins/mod_plugin.c
index df938f0ed..586d87ae9 100644
--- a/src/inputPlugins/mod_plugin.c
+++ b/src/inputPlugins/mod_plugin.c
@@ -205,9 +205,9 @@ static int mod_decode(char *path)
return 0;
}
-static MpdTag *modTagDup(char *file)
+static struct mpd_tag *modTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
MODULE *moduleHandle;
char *title;
@@ -224,12 +224,12 @@ static MpdTag *modTagDup(char *file)
}
Player_Free(moduleHandle);
- ret = newMpdTag();
+ ret = tag_new();
ret->time = 0;
title = xstrdup(Player_LoadTitle(file));
if (title)
- addItemToMpdTag(ret, TAG_ITEM_TITLE, title);
+ tag_add_item(ret, TAG_ITEM_TITLE, title);
MikMod_Exit();
diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c
index f1304f401..ff3de80a3 100644
--- a/src/inputPlugins/mp3_plugin.c
+++ b/src/inputPlugins/mp3_plugin.c
@@ -301,13 +301,13 @@ static ReplayGainInfo *parseId3ReplayGainInfo(struct id3_tag *tag)
#ifdef HAVE_ID3TAG
static void mp3_parseId3Tag(mp3DecodeData * data, size_t tagsize,
- MpdTag ** mpdTag, ReplayGainInfo ** replayGainInfo)
+ struct mpd_tag ** mpdTag, ReplayGainInfo ** replayGainInfo)
{
struct id3_tag *id3Tag = NULL;
id3_length_t count;
id3_byte_t const *id3_data;
id3_byte_t *allocated = NULL;
- MpdTag *tmpMpdTag;
+ struct mpd_tag *tmpMpdTag;
ReplayGainInfo *tmpReplayGainInfo;
count = data->stream.bufend - data->stream.this_frame;
@@ -348,10 +348,10 @@ static void mp3_parseId3Tag(mp3DecodeData * data, size_t tagsize,
goto fail;
if (mpdTag) {
- tmpMpdTag = parseId3Tag(id3Tag);
+ tmpMpdTag = tag_id3_import(id3Tag);
if (tmpMpdTag) {
if (*mpdTag)
- freeMpdTag(*mpdTag);
+ tag_free(*mpdTag);
*mpdTag = tmpMpdTag;
}
}
@@ -373,7 +373,7 @@ fail:
#endif
static enum mp3_action
-decodeNextFrameHeader(mp3DecodeData * data, MpdTag ** tag,
+decodeNextFrameHeader(mp3DecodeData * data, struct mpd_tag ** tag,
ReplayGainInfo ** replayGainInfo)
{
enum mad_layer layer;
@@ -688,7 +688,7 @@ static int parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
}
static int decodeFirstFrame(mp3DecodeData * data,
- MpdTag ** tag, ReplayGainInfo ** replayGainInfo)
+ struct mpd_tag ** tag, ReplayGainInfo ** replayGainInfo)
{
struct xing xing;
struct lame lame;
@@ -811,7 +811,7 @@ static int getMp3TotalTime(char *file)
}
static int openMp3FromInputStream(InputStream * inStream, mp3DecodeData * data,
- MpdTag ** tag,
+ struct mpd_tag ** tag,
ReplayGainInfo ** replayGainInfo)
{
initMp3DecodeData(data, inStream);
@@ -819,7 +819,7 @@ static int openMp3FromInputStream(InputStream * inStream, mp3DecodeData * data,
if (decodeFirstFrame(data, tag, replayGainInfo) < 0) {
mp3DecodeDataFinalize(data);
if (tag && *tag)
- freeMpdTag(*tag);
+ tag_free(*tag);
return -1;
}
@@ -923,13 +923,12 @@ mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
}
if (data->inStream->metaTitle) {
- MpdTag *tag = newMpdTag();
+ struct mpd_tag *tag = tag_new();
if (data->inStream->metaName) {
- addItemToMpdTag(tag,
- TAG_ITEM_NAME,
- data->inStream->metaName);
+ tag_add_item(tag, TAG_ITEM_NAME,
+ data->inStream->metaName);
}
- addItemToMpdTag(tag, TAG_ITEM_TITLE,
+ tag_add_item(tag, TAG_ITEM_TITLE,
data->inStream->metaTitle);
free(data->inStream->metaTitle);
data->inStream->metaTitle = NULL;
@@ -1033,7 +1032,7 @@ static void initAudioFormatFromMp3DecodeData(mp3DecodeData * data,
static int mp3_decode(InputStream * inStream)
{
mp3DecodeData data;
- MpdTag *tag = NULL;
+ struct mpd_tag *tag = NULL;
ReplayGainInfo *replayGainInfo = NULL;
if (openMp3FromInputStream(inStream, &data, &tag, &replayGainInfo) < 0) {
@@ -1051,23 +1050,23 @@ static int mp3_decode(InputStream * inStream)
if (inStream->metaTitle) {
if (tag)
- freeMpdTag(tag);
- tag = newMpdTag();
- addItemToMpdTag(tag, TAG_ITEM_TITLE, inStream->metaTitle);
+ tag_free(tag);
+ tag = tag_new();
+ tag_add_item(tag, TAG_ITEM_TITLE, inStream->metaTitle);
free(inStream->metaTitle);
inStream->metaTitle = NULL;
if (inStream->metaName) {
- addItemToMpdTag(tag, TAG_ITEM_NAME, inStream->metaName);
+ tag_add_item(tag, TAG_ITEM_NAME, inStream->metaName);
}
} else if (tag) {
if (inStream->metaName) {
- clearItemsFromMpdTag(tag, TAG_ITEM_NAME);
- addItemToMpdTag(tag, TAG_ITEM_NAME, inStream->metaName);
+ tag_clear_items_by_type(tag, TAG_ITEM_NAME);
+ tag_add_item(tag, TAG_ITEM_NAME, inStream->metaName);
}
} else if (inStream->metaName) {
- tag = newMpdTag();
+ tag = tag_new();
if (inStream->metaName) {
- addItemToMpdTag(tag, TAG_ITEM_NAME, inStream->metaName);
+ tag_add_item(tag, TAG_ITEM_NAME, inStream->metaName);
}
}
if (tag)
@@ -1081,18 +1080,18 @@ static int mp3_decode(InputStream * inStream)
return 0;
}
-static MpdTag *mp3_tagDup(char *file)
+static struct mpd_tag *mp3_tagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int total_time;
- ret = id3Dup(file);
+ ret = tag_id3_load(file);
total_time = getMp3TotalTime(file);
if (total_time >= 0) {
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = total_time;
} else {
DEBUG("mp3_tagDup: Failed to get total song time from: %s\n",
diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c
index 8e3d02354..cc2b89efc 100644
--- a/src/inputPlugins/mp4_plugin.c
+++ b/src/inputPlugins/mp4_plugin.c
@@ -284,9 +284,9 @@ static int mp4_decode(InputStream * inStream)
return 0;
}
-static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
+static struct mpd_tag *mp4DataDup(char *file, int *mp4MetadataFound)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
InputStream inStream;
mp4ff_t *mp4fh;
mp4ff_callback_t *callback;
@@ -322,14 +322,14 @@ static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
return NULL;
}
- ret = newMpdTag();
+ ret = tag_new();
file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
scale = mp4ff_time_scale(mp4fh, track);
if (scale < 0) {
mp4ff_close(mp4fh);
closeInputStream(&inStream);
free(callback);
- freeMpdTag(ret);
+ tag_free(ret);
return NULL;
}
ret->time = ((float)file_time) / scale + 0.5;
@@ -341,25 +341,25 @@ static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
mp4ff_meta_get_by_index(mp4fh, i, &item, &value);
if (0 == strcasecmp("artist", item)) {
- addItemToMpdTag(ret, TAG_ITEM_ARTIST, value);
+ tag_add_item(ret, TAG_ITEM_ARTIST, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("title", item)) {
- addItemToMpdTag(ret, TAG_ITEM_TITLE, value);
+ tag_add_item(ret, TAG_ITEM_TITLE, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("album", item)) {
- addItemToMpdTag(ret, TAG_ITEM_ALBUM, value);
+ tag_add_item(ret, TAG_ITEM_ALBUM, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("track", item)) {
- addItemToMpdTag(ret, TAG_ITEM_TRACK, value);
+ tag_add_item(ret, TAG_ITEM_TRACK, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("disc", item)) { /* Is that the correct id? */
- addItemToMpdTag(ret, TAG_ITEM_DISC, value);
+ tag_add_item(ret, TAG_ITEM_DISC, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("genre", item)) {
- addItemToMpdTag(ret, TAG_ITEM_GENRE, value);
+ tag_add_item(ret, TAG_ITEM_GENRE, value);
*mp4MetadataFound = 1;
} else if (0 == strcasecmp("date", item)) {
- addItemToMpdTag(ret, TAG_ITEM_DATE, value);
+ tag_add_item(ret, TAG_ITEM_DATE, value);
*mp4MetadataFound = 1;
}
@@ -373,19 +373,19 @@ static MpdTag *mp4DataDup(char *file, int *mp4MetadataFound)
return ret;
}
-static MpdTag *mp4TagDup(char *file)
+static struct mpd_tag *mp4TagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
int mp4MetadataFound = 0;
ret = mp4DataDup(file, &mp4MetadataFound);
if (!ret)
return NULL;
if (!mp4MetadataFound) {
- MpdTag *temp = id3Dup(file);
+ struct mpd_tag *temp = tag_id3_load(file);
if (temp) {
temp->time = ret->time;
- freeMpdTag(ret);
+ tag_free(ret);
ret = temp;
}
}
diff --git a/src/inputPlugins/mpc_plugin.c b/src/inputPlugins/mpc_plugin.c
index ea27d1dbf..e5502a2f0 100644
--- a/src/inputPlugins/mpc_plugin.c
+++ b/src/inputPlugins/mpc_plugin.c
@@ -273,9 +273,9 @@ static float mpcGetTime(char *file)
return total_time;
}
-static MpdTag *mpcTagDup(char *file)
+static struct mpd_tag *mpcTagDup(char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
float total_time = mpcGetTime(file);
if (total_time < 0) {
@@ -284,11 +284,11 @@ static MpdTag *mpcTagDup(char *file)
return NULL;
}
- ret = apeDup(file);
+ ret = tag_ape_load(file);
if (!ret)
- ret = id3Dup(file);
+ ret = tag_id3_load(file);
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = total_time;
return ret;
diff --git a/src/inputPlugins/oggflac_plugin.c b/src/inputPlugins/oggflac_plugin.c
index 4b3bb0a6b..841030481 100644
--- a/src/inputPlugins/oggflac_plugin.c
+++ b/src/inputPlugins/oggflac_plugin.c
@@ -28,6 +28,8 @@
#include "../utils.h"
#include "../log.h"
+#include <OggFLAC/seekable_stream_decoder.h>
+
static void oggflac_cleanup(FlacData * data,
OggFLAC__SeekableStreamDecoder * decoder)
{
@@ -216,7 +218,7 @@ static void of_metadata_dup_cb(mpd_unused const OggFLAC__SeekableStreamDecoder *
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
if (!data->tag)
- data->tag = newMpdTag();
+ data->tag = tag_new();
data->tag->time = ((float)block->data.stream_info.
total_samples) /
block->data.stream_info.sample_rate + 0.5;
@@ -300,7 +302,7 @@ fail:
}
/* public functions: */
-static MpdTag *oggflac_TagDup(char *file)
+static struct mpd_tag *oggflac_TagDup(char *file)
{
InputStream inStream;
OggFLAC__SeekableStreamDecoder *decoder;
diff --git a/src/inputPlugins/oggvorbis_plugin.c b/src/inputPlugins/oggvorbis_plugin.c
index d14ae2bac..bb8af4465 100644
--- a/src/inputPlugins/oggvorbis_plugin.c
+++ b/src/inputPlugins/oggvorbis_plugin.c
@@ -145,7 +145,7 @@ static const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
static unsigned int ogg_parseCommentAddToTag(char *comment,
unsigned int itemType,
- MpdTag ** tag)
+ struct mpd_tag ** tag)
{
const char *needle;
unsigned int len;
@@ -163,9 +163,9 @@ static unsigned int ogg_parseCommentAddToTag(char *comment,
if (strncasecmp(comment, needle, len) == 0 && *(comment + len) == '=') {
if (!*tag)
- *tag = newMpdTag();
+ *tag = tag_new();
- addItemToMpdTag(*tag, itemType, comment + len + 1);
+ tag_add_item(*tag, itemType, comment + len + 1);
return 1;
}
@@ -173,9 +173,9 @@ static unsigned int ogg_parseCommentAddToTag(char *comment,
return 0;
}
-static MpdTag *oggCommentsParse(char **comments)
+static struct mpd_tag *oggCommentsParse(char **comments)
{
- MpdTag *tag = NULL;
+ struct mpd_tag *tag = NULL;
while (*comments) {
int j;
@@ -193,18 +193,18 @@ static void putOggCommentsIntoOutputBuffer(char *streamName,
char **comments,
float cur_time)
{
- MpdTag *tag;
+ struct mpd_tag *tag;
tag = oggCommentsParse(comments);
if (!tag && streamName) {
- tag = newMpdTag();
+ tag = tag_new();
}
if (!tag)
return;
if (streamName) {
- clearItemsFromMpdTag(tag, TAG_ITEM_NAME);
- addItemToMpdTag(tag, TAG_ITEM_NAME, streamName);
+ tag_clear_items_by_type(tag, TAG_ITEM_NAME);
+ tag_add_item(tag, TAG_ITEM_NAME, streamName);
}
metadata_pipe_send(tag, cur_time);
@@ -332,9 +332,9 @@ static int oggvorbis_decode(InputStream * inStream)
return 0;
}
-static MpdTag *oggvorbis_TagDup(char *file)
+static struct mpd_tag *oggvorbis_TagDup(char *file)
{
- MpdTag *ret;
+ struct mpd_tag *ret;
FILE *fp;
OggVorbis_File vf;
@@ -352,7 +352,7 @@ static MpdTag *oggvorbis_TagDup(char *file)
ret = oggCommentsParse(ov_comment(&vf, -1)->user_comments);
if (!ret)
- ret = newMpdTag();
+ ret = tag_new();
ret->time = (int)(ov_time_total(&vf, -1) + 0.5);
ov_clear(&vf);
diff --git a/src/inputPlugins/wavpack_plugin.c b/src/inputPlugins/wavpack_plugin.c
index c7e024a41..77339dabd 100644
--- a/src/inputPlugins/wavpack_plugin.c
+++ b/src/inputPlugins/wavpack_plugin.c
@@ -270,10 +270,10 @@ static ReplayGainInfo *wavpack_replaygain(WavpackContext *wpc)
/*
* Reads metainfo from the specified file.
*/
-static MpdTag *wavpack_tagdup(char *fname)
+static struct mpd_tag *wavpack_tagdup(char *fname)
{
WavpackContext *wpc;
- MpdTag *tag;
+ struct mpd_tag *tag;
char error[ERRORLEN];
char *s;
int ssize;
@@ -285,12 +285,7 @@ static MpdTag *wavpack_tagdup(char *fname)
return NULL;
}
- tag = newMpdTag();
- if (tag == NULL) {
- ERROR("failed to newMpdTag()\n");
- return NULL;
- }
-
+ tag = tag_new();
tag->time =
(float)WavpackGetNumSamples(wpc) / WavpackGetSampleRate(wpc);
@@ -314,7 +309,7 @@ static MpdTag *wavpack_tagdup(char *fname)
}
WavpackGetTagItem(wpc, tagtypes[i].name, s, j);
- addItemToMpdTag(tag, tagtypes[i].type, s);
+ tag_add_item(tag, tagtypes[i].type, s);
}
}
diff --git a/src/locate.c b/src/locate.c
index f68afdedb..76e229f4c 100644
--- a/src/locate.c
+++ b/src/locate.c
@@ -142,11 +142,11 @@ static int strstrSearchTag(Song * song, enum tag_type type, char *str)
for (i = 0; i < song->tag->numOfItems && !ret; i++) {
if (type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i].type != type) {
+ song->tag->items[i]->type != type) {
continue;
}
- duplicate = strDupToUpper(song->tag->items[i].value);
+ duplicate = strDupToUpper(song->tag->items[i]->value);
if (strstr(duplicate, str))
ret = 1;
free(duplicate);
@@ -186,11 +186,11 @@ static int tagItemFoundAndMatches(Song * song, enum tag_type type, char *str)
for (i = 0; i < song->tag->numOfItems; i++) {
if (type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i].type != type) {
+ song->tag->items[i]->type != type) {
continue;
}
- if (0 == strcmp(str, song->tag->items[i].value))
+ if (0 == strcmp(str, song->tag->items[i]->value))
return 1;
}
diff --git a/src/main.c b/src/main.c
index 9d75fadc7..236b3bbe1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -393,7 +393,7 @@ int main(int argc, char *argv[])
killFromPidFile();
initStats();
- initTagConfig();
+ tag_lib_init();
initLog(options.verbose);
if (options.createDB <= 0)
diff --git a/src/metadata_pipe.c b/src/metadata_pipe.c
index d5bef630e..d968dee27 100644
--- a/src/metadata_pipe.c
+++ b/src/metadata_pipe.c
@@ -30,7 +30,7 @@ static struct ringbuf *mp;
struct tag_container {
float metadata_time;
mpd_uint8 seq; /* ob.seq_decoder at time of metadata_pipe_send() */
- MpdTag *tag; /* our payload */
+ struct mpd_tag *tag; /* our payload */
};
/*
@@ -39,13 +39,13 @@ struct tag_container {
* done from one thread, so it will never block or clobber.
*/
static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
-static MpdTag *current_tag; /* requires read_lock for both r/w access */
+static struct mpd_tag *current_tag; /* requires read_lock for both r/w access */
static void metadata_pipe_finish(void)
{
ringbuf_free(mp);
if (current_tag)
- freeMpdTag(current_tag);
+ tag_free(current_tag);
}
void init_metadata_pipe(void)
@@ -54,7 +54,7 @@ void init_metadata_pipe(void)
atexit(metadata_pipe_finish);
}
-void metadata_pipe_send(MpdTag *tag, float metadata_time)
+void metadata_pipe_send(struct mpd_tag *tag, float metadata_time)
{
struct tag_container tc;
size_t written;
@@ -64,7 +64,7 @@ void metadata_pipe_send(MpdTag *tag, float metadata_time)
if (mpd_unlikely(ringbuf_write_space(mp)
< sizeof(struct tag_container))) {
DEBUG("metadata_pipe: insufficient buffer space, dropping\n");
- freeMpdTag(tag);
+ tag_free(tag);
return;
}
@@ -75,14 +75,14 @@ void metadata_pipe_send(MpdTag *tag, float metadata_time)
assert(written == sizeof(struct tag_container));
}
-MpdTag * metadata_pipe_recv(void)
+struct mpd_tag * metadata_pipe_recv(void)
{
struct tag_container tc;
size_t r;
static const size_t mpd_uint8_max = 255; /* XXX CLEANUP */
mpd_uint8 expect_seq = ob_get_player_sequence();
unsigned long current_time = ob_get_elapsed_time();
- MpdTag *tag = NULL;
+ struct mpd_tag *tag = NULL;
if (pthread_mutex_trylock(&read_lock) == EBUSY)
return NULL;
@@ -95,20 +95,20 @@ retry:
if (expect_seq == tc.seq) {
if (current_time < tc.metadata_time)
goto out; /* not ready for tag yet */
- if (mpdTagsAreEqual(tc.tag, current_tag)) {
- freeMpdTag(tc.tag);
+ if (tag_equal(tc.tag, current_tag)) {
+ tag_free(tc.tag);
ringbuf_read_advance(mp, sizeof(struct tag_container));
goto out; /* nothing changed, don't bother */
}
- tag = mpdTagDup(tc.tag);
+ tag = tag_dup(tc.tag);
if (current_tag)
- freeMpdTag(current_tag);
+ tag_free(current_tag);
current_tag = tc.tag;
ringbuf_read_advance(mp, sizeof(struct tag_container));
} else if (expect_seq > tc.seq ||
(!expect_seq && tc.seq == mpd_uint8_max)) {
DEBUG("metadata_pipe: reader is ahead of writer\n");
- freeMpdTag(tc.tag);
+ tag_free(tc.tag);
ringbuf_read_advance(mp, sizeof(struct tag_container));
goto retry; /* read and skip packets */
} else {
@@ -120,14 +120,14 @@ out:
return tag;
}
-MpdTag *metadata_pipe_current(void)
+struct mpd_tag *metadata_pipe_current(void)
{
- MpdTag *tag;
+ struct mpd_tag *tag;
assert(! pthread_equal(pthread_self(), dc.thread));
if (pthread_mutex_trylock(&read_lock) == EBUSY)
return NULL;
- tag = current_tag ? mpdTagDup(current_tag) : NULL;
+ tag = current_tag ? tag_dup(current_tag) : NULL;
pthread_mutex_unlock(&read_lock);
return tag;
@@ -142,11 +142,11 @@ void metadata_pipe_clear(void)
while ((r = ringbuf_read(mp, &tc, sizeof(struct tag_container)))) {
assert(r == sizeof(struct tag_container));
- freeMpdTag(tc.tag);
+ tag_free(tc.tag);
}
if (current_tag) {
- freeMpdTag(current_tag);
+ tag_free(current_tag);
current_tag = NULL;
}
diff --git a/src/metadata_pipe.h b/src/metadata_pipe.h
index e54c67584..cb891aa80 100644
--- a/src/metadata_pipe.h
+++ b/src/metadata_pipe.h
@@ -29,21 +29,21 @@ void init_metadata_pipe(void);
* DO NOT FREE the tag placed into the pipe; that is that job of the
* caller of metadata_pipe_recv() or metadata_pipe_clear().
*/
-void metadata_pipe_send(MpdTag * tag, float metadata_time);
+void metadata_pipe_send(struct mpd_tag * tag, float metadata_time);
/*
- * Reads and consumes one MpdTag pointer off the pipe. The caller
- * of this MUST free the MpdTag pointer after it is done using it.
+ * Reads and consumes one struct mpd_tag pointer off the pipe. The caller
+ * of this MUST free the struct mpd_tag pointer after it is done using it.
*/
-MpdTag * metadata_pipe_recv(void);
+struct mpd_tag * metadata_pipe_recv(void);
/*
- * Returns the last read MpdTag from metadata_pipe_recv(), caller
+ * Returns the last read struct mpd_tag from metadata_pipe_recv(), caller
* must free this pointer when it is done using it.
*/
-MpdTag * metadata_pipe_current(void);
+struct mpd_tag * metadata_pipe_current(void);
-/* Clears all MpdTag pointers on the pipe, freeing all associated elements */
+/* Clears all struct mpd_tag pointers on the pipe, freeing all associated elements */
void metadata_pipe_clear(void);
#endif /* METADATA_PIPE_H */
diff --git a/src/outputBuffer.c b/src/outputBuffer.c
index a9a64712a..ac0f420d8 100644
--- a/src/outputBuffer.c
+++ b/src/outputBuffer.c
@@ -410,13 +410,13 @@ static void new_song_chunk(struct ob_chunk *a)
static void send_next_tag(void)
{
- static MpdTag *last_tag;
- MpdTag *tag;
+ static struct mpd_tag *last_tag;
+ struct mpd_tag *tag;
if ((tag = metadata_pipe_recv())) { /* streaming tag */
DEBUG("Caught new metadata! %p\n", tag);
sendMetadataToAudioDevice(tag);
- freeMpdTag(tag);
+ tag_free(tag);
wakeup_main_task(); /* call sync_metadata() in playlist.c */
} else if ((tag = playlist_current_tag())) { /* static file tag */
/* shouldn't need mpdTagsAreEqual here for static tags */
diff --git a/src/path.c b/src/path.c
index 6aaff84cf..ceb00c5de 100644
--- a/src/path.c
+++ b/src/path.c
@@ -47,7 +47,7 @@ static char *path_conv_charset(char *dest, const char *to,
char *fs_charset_to_utf8(char *dst, const char *str)
{
char *ret = path_conv_charset(dst, "UTF-8", fsCharset, str);
- return (ret && !validUtf8String(ret)) ? NULL : ret;
+ return (ret && !validUtf8String(ret, strlen(ret))) ? NULL : ret;
}
char *utf8_to_fs_charset(char *dst, const char *str)
diff --git a/src/playlist.c b/src/playlist.c
index ab5908ff1..9546dee70 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -969,7 +969,7 @@ int playPlaylistById(int fd, int id, int stopOnError)
}
/* This is used when we stream data out to shout while playing static files */
-MpdTag *playlist_current_tag(void)
+struct mpd_tag *playlist_current_tag(void)
{
Song *song = song_at(playlist.current);
@@ -981,18 +981,18 @@ MpdTag *playlist_current_tag(void)
static void sync_metadata(void)
{
Song *song;
- MpdTag *tag;
+ struct mpd_tag *tag;
if (!(tag = metadata_pipe_current()))
return;
song = song_at(playlist.current);
if (!song || song->type != SONG_TYPE_URL ||
- mpdTagsAreEqual(song->tag, tag)) {
- freeMpdTag(tag);
+ tag_equal(song->tag, tag)) {
+ tag_free(tag);
return;
}
if (song->tag)
- freeMpdTag(song->tag);
+ tag_free(song->tag);
song->tag = tag;
playlist.songMod[playlist.order[playlist.current]] = playlist.version;
incrPlaylistVersion();
diff --git a/src/playlist.h b/src/playlist.h
index c51cb4d73..629bfa057 100644
--- a/src/playlist.h
+++ b/src/playlist.h
@@ -128,6 +128,6 @@ void findSongsInPlaylist(int fd, int numItems, LocateTagItem * items);
int valid_playlist_name(int err_fd, const char *utf8path);
-MpdTag *playlist_current_tag(void);
+struct mpd_tag *playlist_current_tag(void);
#endif
diff --git a/src/song.c b/src/song.c
index cc1547d10..9b8b7d4db 100644
--- a/src/song.c
+++ b/src/song.c
@@ -32,7 +32,7 @@
#include "os_compat.h"
-Song *newNullSong(void)
+static Song *newNullSong(void)
{
Song *song = xmalloc(sizeof(Song));
@@ -92,7 +92,7 @@ void freeJustSong(Song * song)
{
free(song->url);
if (song->tag)
- freeMpdTag(song->tag);
+ tag_free(song->tag);
free(song);
}
@@ -147,7 +147,7 @@ int printSongInfo(int fd, Song * song)
printSongUrl(fd, song);
if (song->tag)
- printMpdTag(fd, song->tag);
+ tag_print(fd, song->tag);
return 0;
}
@@ -200,7 +200,8 @@ static void insertSongIntoList(SongList * list, ListNode ** nextSongNode,
} else if (cmpRet == 0) {
Song *tempSong = (Song *) ((*nextSongNode)->data);
if (tempSong->mtime != song->mtime) {
- freeMpdTag(tempSong->tag);
+ tag_free(tempSong->tag);
+ tag_end_add(song->tag);
tempSong->tag = song->tag;
tempSong->mtime = song->mtime;
song->tag = NULL;
@@ -239,9 +240,12 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
while (myFgets(buffer, bufferSize, fp) && 0 != strcmp(SONG_END, buffer)) {
if (0 == strncmp(SONG_KEY, buffer, strlen(SONG_KEY))) {
- if (song)
+ if (song) {
insertSongIntoList(list, &nextSongNode,
song->url, song);
+ if (song->tag != NULL)
+ tag_end_add(song->tag);
+ }
song = newNullSong();
song->url = xstrdup(buffer + strlen(SONG_KEY));
@@ -256,15 +260,21 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
song->url = xstrdup(&(buffer[strlen(SONG_FILE)]));
*/
} else if (matchesAnMpdTagItemKey(buffer, &itemType)) {
- if (!song->tag)
- song->tag = newMpdTag();
- addItemToMpdTag(song->tag, itemType,
- &(buffer
- [strlen(mpdTagItemKeys[itemType]) +
- 2]));
+ if (!song->tag) {
+ song->tag = tag_new();
+ tag_begin_add(song->tag);
+ }
+
+ tag_add_item(song->tag, itemType,
+ &(buffer
+ [strlen(mpdTagItemKeys[itemType]) +
+ 2]));
} else if (0 == strncmp(SONG_TIME, buffer, strlen(SONG_TIME))) {
- if (!song->tag)
- song->tag = newMpdTag();
+ if (!song->tag) {
+ song->tag = tag_new();
+ tag_begin_add(song->tag);
+ }
+
song->tag->time = atoi(&(buffer[strlen(SONG_TIME)]));
} else if (0 == strncmp(SONG_MTIME, buffer, strlen(SONG_MTIME))) {
song->mtime = atoi(&(buffer[strlen(SONG_MTIME)]));
@@ -273,8 +283,11 @@ void readSongInfoIntoList(FILE * fp, SongList * list, Directory * parentDir)
FATAL("songinfo: unknown line in db: %s\n", buffer);
}
- if (song)
+ if (song) {
insertSongIntoList(list, &nextSongNode, song->url, song);
+ if (song->tag != NULL)
+ tag_end_add(song->tag);
+ }
while (nextSongNode) {
nodeTemp = nextSongNode->nextNode;
@@ -295,7 +308,7 @@ int updateSongInfo(Song * song)
rmp2amp_r(abs_path, abs_path);
if (song->tag)
- freeMpdTag(song->tag);
+ tag_free(song->tag);
song->tag = NULL;
diff --git a/src/song.h b/src/song.h
index 8e2de410a..b7dc6c90b 100644
--- a/src/song.h
+++ b/src/song.h
@@ -36,15 +36,13 @@
typedef struct _Song {
char *url;
mpd_sint8 type;
- MpdTag *tag;
+ struct mpd_tag *tag;
struct _Directory *parentDir;
time_t mtime;
} Song;
typedef List SongList;
-Song *newNullSong(void);
-
Song *newSong(const char *url, int songType, struct _Directory *parentDir);
void freeSong(Song *);
diff --git a/src/tag.c b/src/tag.c
index c1ccd0a42..0e0ff1ab7 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -17,27 +17,27 @@
*/
#include "tag.h"
+#include "tag_pool.h"
#include "myfprintf.h"
#include "utils.h"
#include "utf8.h"
#include "log.h"
#include "conf.h"
-#include "charConv.h"
#include "tagTracker.h"
#include "song.h"
-#ifdef HAVE_ID3TAG
-# define isId3v1(tag) (id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1)
-# ifndef ID3_FRAME_COMPOSER
-# define ID3_FRAME_COMPOSER "TCOM"
-# endif
-# ifndef ID3_FRAME_PERFORMER
-# define ID3_FRAME_PERFORMER "TOPE"
-# endif
-# ifndef ID3_FRAME_DISC
-# define ID3_FRAME_DISC "TPOS"
-# endif
+/**
+ * Maximum number of items managed in the bulk list; if it is
+ * exceeded, we switch back to "normal" reallocation.
+ */
+#define BULK_MAX 64
+
+static struct {
+#ifndef NDEBUG
+ int busy;
#endif
+ struct tag_item *items[BULK_MAX];
+} bulk;
const char *mpdTagItemKeys[TAG_NUM_OF_ITEM_TYPES] = {
"Artist",
@@ -55,7 +55,7 @@ const char *mpdTagItemKeys[TAG_NUM_OF_ITEM_TYPES] = {
static mpd_sint8 ignoreTagItems[TAG_NUM_OF_ITEM_TYPES];
-void initTagConfig(void)
+void tag_lib_init(void)
{
int quit = 0;
char *temp;
@@ -104,7 +104,7 @@ void initTagConfig(void)
free(temp);
}
-void printTagTypes(int fd)
+void tag_print_types(int fd)
{
int i;
@@ -114,7 +114,7 @@ void printTagTypes(int fd)
}
}
-void printMpdTag(int fd, MpdTag * tag)
+void tag_print(int fd, const struct mpd_tag *tag)
{
int i;
@@ -122,340 +122,14 @@ void printMpdTag(int fd, MpdTag * tag)
fdprintf(fd, SONG_TIME "%i\n", tag->time);
for (i = 0; i < tag->numOfItems; i++) {
- fdprintf(fd, "%s: %s\n", mpdTagItemKeys[tag->items[i].type],
- tag->items[i].value);
- }
-}
-
-#ifdef HAVE_ID3TAG
-/* This will try to convert a string to utf-8,
- */
-static id3_utf8_t * processID3FieldString (int is_id3v1, const id3_ucs4_t *ucs4, int type)
-{
- id3_utf8_t *utf8;
- id3_latin1_t *isostr;
- char *encoding;
-
- if (type == TAG_ITEM_GENRE)
- ucs4 = id3_genre_name(ucs4);
- /* use encoding field here? */
- if (is_id3v1 &&
- (encoding = getConfigParamValue(CONF_ID3V1_ENCODING))) {
- isostr = id3_ucs4_latin1duplicate(ucs4);
- if (mpd_unlikely(!isostr)) {
- return NULL;
- }
- setCharSetConversion("UTF-8", encoding);
- utf8 = xmalloc(strlen((char *)isostr) + 1);
- utf8 = (id3_utf8_t *)char_conv_str((char *)utf8, (char *)isostr);
- if (!utf8) {
- DEBUG("Unable to convert %s string to UTF-8: "
- "'%s'\n", encoding, isostr);
- free(isostr);
- return NULL;
- }
- free(isostr);
- } else {
- utf8 = id3_ucs4_utf8duplicate(ucs4);
- if (mpd_unlikely(!utf8)) {
- return NULL;
- }
- }
- return utf8;
-}
-
-static MpdTag *getID3Info(
- struct id3_tag *tag, const char *id, int type, MpdTag * mpdTag)
-{
- struct id3_frame const *frame;
- id3_ucs4_t const *ucs4;
- id3_utf8_t *utf8;
- union id3_field const *field;
- unsigned int nstrings, i;
-
- frame = id3_tag_findframe(tag, id, 0);
- /* Check frame */
- if (!frame)
- {
- return mpdTag;
- }
- /* Check fields in frame */
- if(frame->nfields == 0)
- {
- DEBUG(__FILE__": Frame has no fields\n");
- return mpdTag;
- }
-
- /* Starting with T is a stringlist */
- if (id[0] == 'T')
- {
- /* This one contains 2 fields:
- * 1st: Text encoding
- * 2: Stringlist
- * Shamefully this isn't the RL case.
- * But I am going to enforce it anyway.
- */
- if(frame->nfields != 2)
- {
- DEBUG(__FILE__": Invalid number '%i' of fields for TXX frame\n",frame->nfields);
- return mpdTag;
- }
- field = &frame->fields[0];
- /**
- * First field is encoding field.
- * This is ignored by mpd.
- */
- if(field->type != ID3_FIELD_TYPE_TEXTENCODING)
- {
- DEBUG(__FILE__": Expected encoding, found: %i\n",field->type);
- }
- /* Process remaining fields, should be only one */
- field = &frame->fields[1];
- /* Encoding field */
- if(field->type == ID3_FIELD_TYPE_STRINGLIST) {
- /* Get the number of strings available */
- nstrings = id3_field_getnstrings(field);
- for (i = 0; i < nstrings; i++) {
- ucs4 = id3_field_getstrings(field,i);
- if(!ucs4)
- continue;
- utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
- if(!utf8)
- continue;
-
- if (mpdTag == NULL)
- mpdTag = newMpdTag();
- addItemToMpdTag(mpdTag, type, (char *)utf8);
- free(utf8);
- }
- }
- else {
- ERROR(__FILE__": Field type not processed: %i\n",(int)id3_field_gettextencoding(field));
- }
- }
- /* A comment frame */
- else if(!strcmp(ID3_FRAME_COMMENT, id))
- {
- /* A comment frame is different... */
- /* 1st: encoding
- * 2nd: Language
- * 3rd: String
- * 4th: FullString.
- * The 'value' we want is in the 4th field
- */
- if(frame->nfields == 4)
- {
- /* for now I only read the 4th field, with the fullstring */
- field = &frame->fields[3];
- if(field->type == ID3_FIELD_TYPE_STRINGFULL)
- {
- ucs4 = id3_field_getfullstring(field);
- if(ucs4)
- {
- utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
- if(utf8)
- {
- if (mpdTag == NULL)
- mpdTag = newMpdTag();
- addItemToMpdTag(mpdTag, type, (char *)utf8);
- free(utf8);
- }
- }
- }
- else
- {
- DEBUG(__FILE__": 4th field in comment frame differs from expected, got '%i': ignoring\n",field->type);
- }
- }
- else
- {
- DEBUG(__FILE__": Invalid 'comments' tag, got '%i' fields instead of 4\n", frame->nfields);
- }
- }
- /* Unsupported */
- else {
- DEBUG(__FILE__": Unsupported tag type requrested\n");
- return mpdTag;
- }
-
- return mpdTag;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-MpdTag *parseId3Tag(struct id3_tag * tag)
-{
- MpdTag *ret = NULL;
-
- ret = getID3Info(tag, ID3_FRAME_ARTIST, TAG_ITEM_ARTIST, ret);
- ret = getID3Info(tag, ID3_FRAME_TITLE, TAG_ITEM_TITLE, ret);
- ret = getID3Info(tag, ID3_FRAME_ALBUM, TAG_ITEM_ALBUM, ret);
- ret = getID3Info(tag, ID3_FRAME_TRACK, TAG_ITEM_TRACK, ret);
- ret = getID3Info(tag, ID3_FRAME_YEAR, TAG_ITEM_DATE, ret);
- ret = getID3Info(tag, ID3_FRAME_GENRE, TAG_ITEM_GENRE, ret);
- ret = getID3Info(tag, ID3_FRAME_COMPOSER, TAG_ITEM_COMPOSER, ret);
- ret = getID3Info(tag, ID3_FRAME_PERFORMER, TAG_ITEM_PERFORMER, ret);
- ret = getID3Info(tag, ID3_FRAME_COMMENT, TAG_ITEM_COMMENT, ret);
- ret = getID3Info(tag, ID3_FRAME_DISC, TAG_ITEM_DISC, ret);
-
- return ret;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static int fillBuffer(void *buf, size_t size, FILE * stream,
- long offset, int whence)
-{
- if (fseek(stream, offset, whence) != 0) return 0;
- return fread(buf, 1, size, stream);
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static int getId3v2FooterSize(FILE * stream, long offset, int whence)
-{
- id3_byte_t buf[ID3_TAG_QUERYSIZE];
- int bufsize;
-
- bufsize = fillBuffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
- if (bufsize <= 0) return 0;
- return id3_tag_query(buf, bufsize);
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static struct id3_tag *getId3Tag(FILE * stream, long offset, int whence)
-{
- struct id3_tag *tag;
- id3_byte_t queryBuf[ID3_TAG_QUERYSIZE];
- id3_byte_t *tagBuf;
- int tagSize;
- int queryBufSize;
- int tagBufSize;
-
- /* It's ok if we get less than we asked for */
- queryBufSize = fillBuffer(queryBuf, ID3_TAG_QUERYSIZE,
- stream, offset, whence);
- if (queryBufSize <= 0) return NULL;
-
- /* Look for a tag header */
- tagSize = id3_tag_query(queryBuf, queryBufSize);
- if (tagSize <= 0) return NULL;
-
- /* Found a tag. Allocate a buffer and read it in. */
- tagBuf = xmalloc(tagSize);
- if (!tagBuf) return NULL;
-
- tagBufSize = fillBuffer(tagBuf, tagSize, stream, offset, whence);
- if (tagBufSize < tagSize) {
- free(tagBuf);
- return NULL;
- }
-
- tag = id3_tag_parse(tagBuf, tagBufSize);
-
- free(tagBuf);
-
- return tag;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static struct id3_tag *findId3TagFromBeginning(FILE * stream)
-{
- struct id3_tag *tag;
- struct id3_tag *seektag;
- struct id3_frame *frame;
- int seek;
-
- tag = getId3Tag(stream, 0, SEEK_SET);
- if (!tag) {
- return NULL;
- } else if (isId3v1(tag)) {
- /* id3v1 tags don't belong here */
- id3_tag_delete(tag);
- return NULL;
- }
-
- /* We have an id3v2 tag, so let's look for SEEK frames */
- while ((frame = id3_tag_findframe(tag, "SEEK", 0))) {
- /* Found a SEEK frame, get it's value */
- seek = id3_field_getint(id3_frame_field(frame, 0));
- if (seek < 0)
- break;
-
- /* Get the tag specified by the SEEK frame */
- seektag = getId3Tag(stream, seek, SEEK_CUR);
- if (!seektag || isId3v1(seektag))
- break;
-
- /* Replace the old tag with the new one */
- id3_tag_delete(tag);
- tag = seektag;
- }
-
- return tag;
-}
-#endif
-
-#ifdef HAVE_ID3TAG
-static struct id3_tag *findId3TagFromEnd(FILE * stream)
-{
- struct id3_tag *tag;
- struct id3_tag *v1tag;
- int tagsize;
-
- /* Get an id3v1 tag from the end of file for later use */
- v1tag = getId3Tag(stream, -128, SEEK_END);
-
- /* Get the id3v2 tag size from the footer (located before v1tag) */
- tagsize = getId3v2FooterSize(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
- if (tagsize >= 0)
- return v1tag;
-
- /* Get the tag which the footer belongs to */
- tag = getId3Tag(stream, tagsize, SEEK_CUR);
- if (!tag)
- return v1tag;
-
- /* We have an id3v2 tag, so ditch v1tag */
- id3_tag_delete(v1tag);
-
- return tag;
-}
-#endif
-
-MpdTag *id3Dup(char *file)
-{
- MpdTag *ret = NULL;
-#ifdef HAVE_ID3TAG
- struct id3_tag *tag;
- FILE *stream;
-
- stream = fopen(file, "r");
- if (!stream) {
- DEBUG("id3Dup: Failed to open file: '%s', %s\n", file,
- strerror(errno));
- return NULL;
+ fdprintf(fd, "%s: %s\n", mpdTagItemKeys[tag->items[i]->type],
+ tag->items[i]->value);
}
-
- tag = findId3TagFromBeginning(stream);
- if (!tag)
- tag = findId3TagFromEnd(stream);
-
- fclose(stream);
-
- if (!tag)
- return NULL;
- ret = parseId3Tag(tag);
- id3_tag_delete(tag);
-#endif
- return ret;
}
-MpdTag *apeDup(char *file)
+struct mpd_tag *tag_ape_load(const char *file)
{
- MpdTag *ret = NULL;
+ struct mpd_tag *ret = NULL;
FILE *fp;
int tagCount;
char *buffer = NULL;
@@ -556,9 +230,9 @@ MpdTag *apeDup(char *file)
for (i = 0; i < 7; i++) {
if (strcasecmp(key, apeItems[i]) == 0) {
if (!ret)
- ret = newMpdTag();
- addItemToMpdTagWithLen(ret, tagItems[i],
- p, size);
+ ret = tag_new();
+ tag_add_item_n(ret, tagItems[i],
+ p, size);
}
}
}
@@ -574,21 +248,21 @@ fail:
return ret;
}
-MpdTag *newMpdTag(void)
+struct mpd_tag *tag_new(void)
{
- MpdTag *ret = xmalloc(sizeof(MpdTag));
+ struct mpd_tag *ret = xmalloc(sizeof(*ret));
ret->items = NULL;
ret->time = -1;
ret->numOfItems = 0;
return ret;
}
-static void deleteItem(MpdTag * tag, int idx)
+static void deleteItem(struct mpd_tag *tag, int idx)
{
assert(idx < tag->numOfItems);
tag->numOfItems--;
- removeTagItemString(tag->items[idx].type, tag->items[idx].value);
+ tag_pool_put_item(tag->items[idx]);
/* free(tag->items[idx].value); */
if (tag->numOfItems - idx > 0) {
@@ -598,19 +272,19 @@ static void deleteItem(MpdTag * tag, int idx)
if (tag->numOfItems > 0) {
tag->items = xrealloc(tag->items,
- tag->numOfItems * sizeof(MpdTagItem));
+ tag->numOfItems * sizeof(*tag->items));
} else {
free(tag->items);
tag->items = NULL;
}
}
-void clearItemsFromMpdTag(MpdTag * tag, enum tag_type type)
+void tag_clear_items_by_type(struct mpd_tag *tag, enum tag_type type)
{
int i;
for (i = 0; i < tag->numOfItems; i++) {
- if (tag->items[i].type == type) {
+ if (tag->items[i]->type == type) {
deleteItem(tag, i);
/* decrement since when just deleted this node */
i--;
@@ -618,17 +292,24 @@ void clearItemsFromMpdTag(MpdTag * tag, enum tag_type type)
}
}
-static void clearMpdTag(MpdTag * tag)
+static void clearMpdTag(struct mpd_tag *tag)
{
int i;
for (i = 0; i < tag->numOfItems; i++) {
- removeTagItemString(tag->items[i].type, tag->items[i].value);
/* free(tag->items[i].value); */
+ tag_pool_put_item(tag->items[i]);
}
- if (tag->items)
+ if (tag->items == bulk.items) {
+#ifndef NDEBUG
+ assert(bulk.busy);
+ bulk.busy = 0;
+#endif
+ } else if (tag->items) {
free(tag->items);
+ }
+
tag->items = NULL;
tag->numOfItems = 0;
@@ -636,31 +317,33 @@ static void clearMpdTag(MpdTag * tag)
tag->time = -1;
}
-void freeMpdTag(MpdTag * tag)
+void tag_free(struct mpd_tag *tag)
{
clearMpdTag(tag);
free(tag);
}
-MpdTag *mpdTagDup(MpdTag * tag)
+struct mpd_tag *tag_dup(const struct mpd_tag *tag)
{
- MpdTag *ret;
+ struct mpd_tag *ret;
int i;
if (!tag)
return NULL;
- ret = newMpdTag();
+ ret = tag_new();
ret->time = tag->time;
+ ret->numOfItems = tag->numOfItems;
+ ret->items = xmalloc(ret->numOfItems * sizeof(ret->items[0]));
for (i = 0; i < tag->numOfItems; i++) {
- addItemToMpdTag(ret, tag->items[i].type, tag->items[i].value);
+ ret->items[i] = tag_pool_dup_item(tag->items[i]);
}
return ret;
}
-int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2)
+int tag_equal(const struct mpd_tag *tag1, const struct mpd_tag *tag2)
{
int i;
@@ -676,9 +359,9 @@ int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2)
return 0;
for (i = 0; i < tag1->numOfItems; i++) {
- if (tag1->items[i].type != tag2->items[i].type)
+ if (tag1->items[i]->type != tag2->items[i]->type)
return 0;
- if (strcmp(tag1->items[i].value, tag2->items[i].value)) {
+ if (strcmp(tag1->items[i]->value, tag2->items[i]->value)) {
return 0;
}
}
@@ -686,39 +369,95 @@ int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2)
return 1;
}
-#define fixUtf8(str) { \
- if(str && !validUtf8String(str)) { \
- char * temp; \
- DEBUG("not valid utf8 in tag: %s\n",str); \
- temp = latin1StrToUtf8Dup(str); \
- free(str); \
- str = temp; \
- } \
+static inline const char *fix_utf8(const char *str, size_t *length_r) {
+ const char *temp;
+
+ assert(str != NULL);
+
+ if (validUtf8String(str, *length_r))
+ return str;
+
+ DEBUG("not valid utf8 in tag: %s\n",str);
+ temp = latin1StrToUtf8Dup(str);
+ *length_r = strlen(temp);
+ return temp;
}
-static void appendToTagItems(MpdTag * tag, enum tag_type type,
- const char *value, size_t len)
+void tag_begin_add(struct mpd_tag *tag)
{
- unsigned int i = tag->numOfItems;
- char *duplicated = xmalloc(len + 1);
+ assert(!bulk.busy);
+ assert(tag != NULL);
+ assert(tag->items == NULL);
+ assert(tag->numOfItems == 0);
+
+#ifndef NDEBUG
+ bulk.busy = 1;
+#endif
+ tag->items = bulk.items;
+}
- memcpy(duplicated, value, len);
- duplicated[len] = '\0';
+void tag_end_add(struct mpd_tag *tag)
+{
+ if (tag->items == bulk.items) {
+ assert(tag->numOfItems <= BULK_MAX);
+
+ if (tag->numOfItems > 0) {
+ /* copy the tag items from the bulk list over
+ to a new list (which fits exactly) */
+ tag->items = xmalloc(tag->numOfItems *
+ sizeof(tag->items[0]));
+ memcpy(tag->items, bulk.items,
+ tag->numOfItems * sizeof(tag->items[0]));
+ } else
+ tag->items = NULL;
+ }
+
+#ifndef NDEBUG
+ bulk.busy = 0;
+#endif
+}
- fixUtf8(duplicated);
- stripReturnChar(duplicated);
+static void appendToTagItems(struct mpd_tag *tag, enum tag_type type,
+ const char *value, size_t len)
+{
+ unsigned int i = tag->numOfItems;
+ const char *p;
+
+ p = fix_utf8(value, &len);
+ if (memchr(p, '\n', len) != NULL) {
+ char *duplicated = xmalloc(len + 1);
+ memcpy(duplicated, p, len);
+ duplicated[len] = '\0';
+ if (p != value)
+ free(deconst_ptr(p));
+
+ stripReturnChar(duplicated);
+ p = duplicated;
+ }
tag->numOfItems++;
- tag->items = xrealloc(tag->items, tag->numOfItems * sizeof(MpdTagItem));
- tag->items[i].type = type;
- tag->items[i].value = getTagItemString(type, duplicated);
+ if (tag->items != bulk.items)
+ /* bulk mode disabled */
+ tag->items = xrealloc(tag->items,
+ tag->numOfItems * sizeof(*tag->items));
+ else if (tag->numOfItems >= BULK_MAX) {
+ /* bulk list already full - switch back to non-bulk */
+ assert(bulk.busy);
+
+ tag->items = xmalloc(tag->numOfItems * sizeof(tag->items[0]));
+ memcpy(tag->items, bulk.items,
+ (tag->numOfItems - 1) * sizeof(tag->items[0]));
+ }
+
+ tag->items[i] = tag_pool_get_item(type, p, len);
- free(duplicated);
+ if (p != value)
+ free(deconst_ptr(p));
}
-void addItemToMpdTagWithLen(MpdTag * tag, enum tag_type itemType,
- const char *value, size_t len)
+void tag_add_item_n(struct mpd_tag *tag, enum tag_type itemType,
+ const char *value, size_t len)
{
if (ignoreTagItems[itemType])
{
diff --git a/src/tag.h b/src/tag.h
index a9bf19934..a05f7654a 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -19,14 +19,9 @@
#ifndef TAG_H
#define TAG_H
-#include "../config.h"
-
#include "mpd_types.h"
#include "os_compat.h"
-
-#ifdef HAVE_ID3TAG
-#include <id3tag.h>
-#endif
+#include "gcc.h"
enum tag_type {
TAG_ITEM_ARTIST,
@@ -45,45 +40,56 @@ enum tag_type {
extern const char *mpdTagItemKeys[];
-typedef struct _MpdTagItem {
+struct tag_item {
enum tag_type type;
- char *value;
-} MpdTagItem;
+ char value[1];
+} mpd_packed;
-typedef struct _MpdTag {
+struct mpd_tag {
int time;
- MpdTagItem *items;
+ struct tag_item **items;
mpd_uint8 numOfItems;
-} MpdTag;
+};
-#ifdef HAVE_ID3TAG
-MpdTag *parseId3Tag(struct id3_tag *);
-#endif
+struct mpd_tag *tag_ape_load(const char *file);
-MpdTag *apeDup(char *file);
+struct mpd_tag *tag_new(void);
-MpdTag *id3Dup(char *file);
+void tag_lib_init(void);
-MpdTag *newMpdTag(void);
+void tag_clear_items_by_type(struct mpd_tag *tag, enum tag_type itemType);
-void initTagConfig(void);
+void tag_free(struct mpd_tag *tag);
-void clearItemsFromMpdTag(MpdTag * tag, enum tag_type itemType);
+/**
+ * Gives an optional hint to the tag library that we will now add
+ * several tag items; this is used by the library to optimize memory
+ * allocation. Only one tag may be in this state, and this tag must
+ * not have any items yet. You must call tag_end_add() when you are
+ * done.
+ */
+void tag_begin_add(struct mpd_tag *tag);
-void freeMpdTag(MpdTag * tag);
+/**
+ * Finishes the operation started with tag_begin_add().
+ */
+void tag_end_add(struct mpd_tag *tag);
-void addItemToMpdTagWithLen(MpdTag * tag, enum tag_type itemType,
- const char *value, size_t len);
+void tag_add_item_n(struct mpd_tag *tag, enum tag_type itemType,
+ const char *value, size_t len);
-#define addItemToMpdTag(tag, itemType, value) \
- addItemToMpdTagWithLen(tag, itemType, value, strlen(value))
+static inline void tag_add_item(struct mpd_tag *tag, enum tag_type itemType,
+ const char *value)
+{
+ tag_add_item_n(tag, itemType, value, strlen(value));
+}
-void printTagTypes(int fd);
+void tag_print_types(int fd);
-void printMpdTag(int fd, MpdTag * tag);
+void tag_print(int fd, const struct mpd_tag *tag);
-MpdTag *mpdTagDup(MpdTag * tag);
+struct mpd_tag *tag_dup(const struct mpd_tag *tag);
-int mpdTagsAreEqual(MpdTag * tag1, MpdTag * tag2);
+int tag_equal(const struct mpd_tag *tag1, const struct mpd_tag *tag2);
#endif
diff --git a/src/tagTracker.c b/src/tagTracker.c
index 892cc2955..bea58ae4c 100644
--- a/src/tagTracker.c
+++ b/src/tagTracker.c
@@ -19,137 +19,101 @@
#include "tagTracker.h"
#include "tag.h"
-#include "tree.h"
#include "utils.h"
#include "myfprintf.h"
#include "os_compat.h"
+#include "directory.h"
-static Tree *tagTrees[TAG_NUM_OF_ITEM_TYPES];
+struct visited {
+ struct visited *next;
-typedef struct tagTrackerItem {
- int count;
- mpd_sint8 visited;
-} TagTrackerItem;
+ /**
+ * this is the original pointer passed to visitInTagTracker(),
+ * i.e. the caller must not invalidate it until he calls
+ * resetVisitedFlagsInTagTracker().
+ */
+ const char *value;
+} mpd_packed;
-static pthread_mutex_t tag_item_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct visited *visited_heads[TAG_NUM_OF_ITEM_TYPES];
+static unsigned num_visited[TAG_NUM_OF_ITEM_TYPES];
-char *getTagItemString(int type, char *string)
+static int visit_tag_items(int fd mpd_unused, Song *song, void *data)
{
- TreeIterator iter;
- char *ret;
+ enum tag_type type = (enum tag_type)(size_t)data;
+ unsigned i;
- pthread_mutex_lock(&tag_item_lock);
-
- if (tagTrees[type] == NULL)
- {
- tagTrees[type] = MakeTree((TreeCompareKeyFunction)strcmp,
- (TreeFreeFunction)free,
- (TreeFreeFunction)free);
- }
+ if (song->tag == NULL)
+ return 0;
- if (FindInTree(tagTrees[type], string, &iter))
- {
- ((TagTrackerItem *)GetTreeKeyData(&iter)->data)->count++;
- ret = (char *)GetTreeKeyData(&iter)->key;
- }
- else
- {
- TagTrackerItem *item = xmalloc(sizeof(TagTrackerItem));
- char *key = xstrdup(string);
- item->count = 1;
- item->visited = 0;
- InsertInTree(tagTrees[type], key, item);
- ret = key;
+ for (i = 0; i < (unsigned)song->tag->numOfItems; ++i) {
+ const struct tag_item *item = song->tag->items[i];
+ if (item->type == type)
+ visitInTagTracker(type, item->value);
}
- pthread_mutex_unlock(&tag_item_lock);
- return ret;
+ return 0;
}
-void removeTagItemString(int type, char *string)
+int getNumberOfTagItems(int type)
{
- TreeIterator iter;
+ int ret;
- assert(string);
+ resetVisitedFlagsInTagTracker(type);
- assert(tagTrees[type]);
- if (tagTrees[type] == NULL)
- return;
-
- pthread_mutex_lock(&tag_item_lock);
- if (FindInTree(tagTrees[type], string, &iter))
- {
- TagTrackerItem * item =
- (TagTrackerItem *)GetTreeKeyData(&iter)->data;
- item->count--;
- if (item->count <= 0)
- RemoveFromTreeByIterator(tagTrees[type], &iter);
- }
+ traverseAllIn(-1, NULL, visit_tag_items, NULL, (void*)(size_t)type);
- if (GetTreeSize(tagTrees[type]) == 0)
- {
- FreeTree(tagTrees[type]);
- tagTrees[type] = NULL;
- }
- pthread_mutex_unlock(&tag_item_lock);
+ ret = (int)num_visited[type];
+ resetVisitedFlagsInTagTracker(type);
+ return ret;
}
-int getNumberOfTagItems(int type)
+void resetVisitedFlagsInTagTracker(int type)
{
- if (tagTrees[type] == NULL)
- return 0;
+ while (visited_heads[type] != NULL) {
+ struct visited *v = visited_heads[type];
+ visited_heads[type] = v->next;
+ free(v);
+ }
- return GetTreeSize(tagTrees[type]);
+ num_visited[type] = 0;
}
-void resetVisitedFlagsInTagTracker(int type)
+static struct visited *
+find_visit(int type, const char *p)
{
- TreeIterator iter;
+ struct visited *v;
- if (!tagTrees[type])
- return;
+ for (v = visited_heads[type]; v != NULL; v = v->next)
+ if (strcmp(v->value, p) == 0)
+ return v;
- for (SetTreeIteratorToBegin(tagTrees[type], &iter);
- !IsTreeIteratorAtEnd(&iter);
- IncrementTreeIterator(&iter))
- {
- ((TagTrackerItem *)GetTreeKeyData(&iter)->data)->visited = 0;
- }
+ return NULL;
}
void visitInTagTracker(int type, const char *str)
{
- TreeIterator iter;
+ struct visited *v = find_visit(type, str);
+ size_t length;
- if (!tagTrees[type])
+ if (v != NULL)
return;
- if (!FindInTree(tagTrees[type], str, &iter))
- return;
-
- ((TagTrackerItem *)GetTreeKeyData(&iter)->data)->visited = 1;
+ length = strlen(str);
+ v = xmalloc(sizeof(*v));
+ v->value = str;
+ v->next = visited_heads[type];
+ visited_heads[type] = v;
+ ++num_visited[type];
}
void printVisitedInTagTracker(int fd, int type)
{
- TreeIterator iter;
- TagTrackerItem * item;
+ struct visited *v;
- if (!tagTrees[type])
- return;
-
- for (SetTreeIteratorToBegin(tagTrees[type], &iter);
- !IsTreeIteratorAtEnd(&iter);
- IncrementTreeIterator(&iter))
- {
- item = ((TagTrackerItem *)GetTreeKeyData(&iter)->data);
-
- if (item->visited)
- {
- fdprintf(fd,
- "%s: %s\n",
- mpdTagItemKeys[type],
- (char *)GetTreeKeyData(&iter)->key);
- }
- }
+ for (v = visited_heads[type]; v != NULL; v = v->next)
+ fdprintf(fd,
+ "%s: %s\n",
+ mpdTagItemKeys[type],
+ v->value);
}
diff --git a/src/tagTracker.h b/src/tagTracker.h
index 84506426e..2edb5aad0 100644
--- a/src/tagTracker.h
+++ b/src/tagTracker.h
@@ -19,10 +19,6 @@
#ifndef TAG_TRACKER_H
#define TAG_TRACKER_H
-char *getTagItemString(int type, char *string);
-
-void removeTagItemString(int type, char *string);
-
int getNumberOfTagItems(int type);
void printMemorySavedByTagTracker(void);
diff --git a/src/tag_id3.c b/src/tag_id3.c
new file mode 100644
index 000000000..83b78150a
--- /dev/null
+++ b/src/tag_id3.c
@@ -0,0 +1,367 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * 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
+ */
+
+#include "tag_id3.h"
+#include "tag.h"
+#include "utils.h"
+#include "log.h"
+#include "conf.h"
+#include "charConv.h"
+
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
+#ifdef HAVE_ID3TAG
+# define isId3v1(tag) (id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1)
+# ifndef ID3_FRAME_COMPOSER
+# define ID3_FRAME_COMPOSER "TCOM"
+# endif
+# ifndef ID3_FRAME_PERFORMER
+# define ID3_FRAME_PERFORMER "TOPE"
+# endif
+# ifndef ID3_FRAME_DISC
+# define ID3_FRAME_DISC "TPOS"
+# endif
+#endif
+
+#ifdef HAVE_ID3TAG
+/* This will try to convert a string to utf-8,
+ */
+static id3_utf8_t * processID3FieldString (int is_id3v1, const id3_ucs4_t *ucs4, int type)
+{
+ id3_utf8_t *utf8;
+ id3_latin1_t *isostr;
+ char *encoding;
+
+ if (type == TAG_ITEM_GENRE)
+ ucs4 = id3_genre_name(ucs4);
+ /* use encoding field here? */
+ if (is_id3v1 &&
+ (encoding = getConfigParamValue(CONF_ID3V1_ENCODING))) {
+ isostr = id3_ucs4_latin1duplicate(ucs4);
+ if (mpd_unlikely(!isostr)) {
+ return NULL;
+ }
+ setCharSetConversion("UTF-8", encoding);
+ utf8 = xmalloc(strlen((char *)isostr) + 1);
+ utf8 = (id3_utf8_t *)char_conv_str((char *)utf8, (char *)isostr);
+ if (!utf8) {
+ DEBUG("Unable to convert %s string to UTF-8: "
+ "'%s'\n", encoding, isostr);
+ free(isostr);
+ return NULL;
+ }
+ free(isostr);
+ } else {
+ utf8 = id3_ucs4_utf8duplicate(ucs4);
+ if (mpd_unlikely(!utf8)) {
+ return NULL;
+ }
+ }
+ return utf8;
+}
+
+static struct mpd_tag *getID3Info(
+ struct id3_tag *tag, const char *id, int type, struct mpd_tag *mpdTag)
+{
+ struct id3_frame const *frame;
+ id3_ucs4_t const *ucs4;
+ id3_utf8_t *utf8;
+ union id3_field const *field;
+ unsigned int nstrings, i;
+
+ frame = id3_tag_findframe(tag, id, 0);
+ /* Check frame */
+ if (!frame)
+ {
+ return mpdTag;
+ }
+ /* Check fields in frame */
+ if(frame->nfields == 0)
+ {
+ DEBUG(__FILE__": Frame has no fields\n");
+ return mpdTag;
+ }
+
+ /* Starting with T is a stringlist */
+ if (id[0] == 'T')
+ {
+ /* This one contains 2 fields:
+ * 1st: Text encoding
+ * 2: Stringlist
+ * Shamefully this isn't the RL case.
+ * But I am going to enforce it anyway.
+ */
+ if(frame->nfields != 2)
+ {
+ DEBUG(__FILE__": Invalid number '%i' of fields for TXX frame\n",frame->nfields);
+ return mpdTag;
+ }
+ field = &frame->fields[0];
+ /**
+ * First field is encoding field.
+ * This is ignored by mpd.
+ */
+ if(field->type != ID3_FIELD_TYPE_TEXTENCODING)
+ {
+ DEBUG(__FILE__": Expected encoding, found: %i\n",field->type);
+ }
+ /* Process remaining fields, should be only one */
+ field = &frame->fields[1];
+ /* Encoding field */
+ if(field->type == ID3_FIELD_TYPE_STRINGLIST) {
+ /* Get the number of strings available */
+ nstrings = id3_field_getnstrings(field);
+ for (i = 0; i < nstrings; i++) {
+ ucs4 = id3_field_getstrings(field,i);
+ if(!ucs4)
+ continue;
+ utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
+ if(!utf8)
+ continue;
+
+ if (mpdTag == NULL)
+ mpdTag = tag_new();
+ tag_add_item(mpdTag, type, (char *)utf8);
+ free(utf8);
+ }
+ }
+ else {
+ ERROR(__FILE__": Field type not processed: %i\n",(int)id3_field_gettextencoding(field));
+ }
+ }
+ /* A comment frame */
+ else if(!strcmp(ID3_FRAME_COMMENT, id))
+ {
+ /* A comment frame is different... */
+ /* 1st: encoding
+ * 2nd: Language
+ * 3rd: String
+ * 4th: FullString.
+ * The 'value' we want is in the 4th field
+ */
+ if(frame->nfields == 4)
+ {
+ /* for now I only read the 4th field, with the fullstring */
+ field = &frame->fields[3];
+ if(field->type == ID3_FIELD_TYPE_STRINGFULL)
+ {
+ ucs4 = id3_field_getfullstring(field);
+ if(ucs4)
+ {
+ utf8 = processID3FieldString(isId3v1(tag),ucs4, type);
+ if(utf8)
+ {
+ if (mpdTag == NULL)
+ mpdTag = tag_new();
+ tag_add_item(mpdTag, type, (char *)utf8);
+ free(utf8);
+ }
+ }
+ }
+ else
+ {
+ DEBUG(__FILE__": 4th field in comment frame differs from expected, got '%i': ignoring\n",field->type);
+ }
+ }
+ else
+ {
+ DEBUG(__FILE__": Invalid 'comments' tag, got '%i' fields instead of 4\n", frame->nfields);
+ }
+ }
+ /* Unsupported */
+ else {
+ DEBUG(__FILE__": Unsupported tag type requrested\n");
+ return mpdTag;
+ }
+
+ return mpdTag;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+struct mpd_tag *tag_id3_import(struct id3_tag * tag)
+{
+ struct mpd_tag *ret = NULL;
+
+ ret = getID3Info(tag, ID3_FRAME_ARTIST, TAG_ITEM_ARTIST, ret);
+ ret = getID3Info(tag, ID3_FRAME_TITLE, TAG_ITEM_TITLE, ret);
+ ret = getID3Info(tag, ID3_FRAME_ALBUM, TAG_ITEM_ALBUM, ret);
+ ret = getID3Info(tag, ID3_FRAME_TRACK, TAG_ITEM_TRACK, ret);
+ ret = getID3Info(tag, ID3_FRAME_YEAR, TAG_ITEM_DATE, ret);
+ ret = getID3Info(tag, ID3_FRAME_GENRE, TAG_ITEM_GENRE, ret);
+ ret = getID3Info(tag, ID3_FRAME_COMPOSER, TAG_ITEM_COMPOSER, ret);
+ ret = getID3Info(tag, ID3_FRAME_PERFORMER, TAG_ITEM_PERFORMER, ret);
+ ret = getID3Info(tag, ID3_FRAME_COMMENT, TAG_ITEM_COMMENT, ret);
+ ret = getID3Info(tag, ID3_FRAME_DISC, TAG_ITEM_DISC, ret);
+
+ return ret;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static int fillBuffer(void *buf, size_t size, FILE * stream,
+ long offset, int whence)
+{
+ if (fseek(stream, offset, whence) != 0) return 0;
+ return fread(buf, 1, size, stream);
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static int getId3v2FooterSize(FILE * stream, long offset, int whence)
+{
+ id3_byte_t buf[ID3_TAG_QUERYSIZE];
+ int bufsize;
+
+ bufsize = fillBuffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence);
+ if (bufsize <= 0) return 0;
+ return id3_tag_query(buf, bufsize);
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct id3_tag *getId3Tag(FILE * stream, long offset, int whence)
+{
+ struct id3_tag *tag;
+ id3_byte_t queryBuf[ID3_TAG_QUERYSIZE];
+ id3_byte_t *tagBuf;
+ int tagSize;
+ int queryBufSize;
+ int tagBufSize;
+
+ /* It's ok if we get less than we asked for */
+ queryBufSize = fillBuffer(queryBuf, ID3_TAG_QUERYSIZE,
+ stream, offset, whence);
+ if (queryBufSize <= 0) return NULL;
+
+ /* Look for a tag header */
+ tagSize = id3_tag_query(queryBuf, queryBufSize);
+ if (tagSize <= 0) return NULL;
+
+ /* Found a tag. Allocate a buffer and read it in. */
+ tagBuf = xmalloc(tagSize);
+ if (!tagBuf) return NULL;
+
+ tagBufSize = fillBuffer(tagBuf, tagSize, stream, offset, whence);
+ if (tagBufSize < tagSize) {
+ free(tagBuf);
+ return NULL;
+ }
+
+ tag = id3_tag_parse(tagBuf, tagBufSize);
+
+ free(tagBuf);
+
+ return tag;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct id3_tag *findId3TagFromBeginning(FILE * stream)
+{
+ struct id3_tag *tag;
+ struct id3_tag *seektag;
+ struct id3_frame *frame;
+ int seek;
+
+ tag = getId3Tag(stream, 0, SEEK_SET);
+ if (!tag) {
+ return NULL;
+ } else if (isId3v1(tag)) {
+ /* id3v1 tags don't belong here */
+ id3_tag_delete(tag);
+ return NULL;
+ }
+
+ /* We have an id3v2 tag, so let's look for SEEK frames */
+ while ((frame = id3_tag_findframe(tag, "SEEK", 0))) {
+ /* Found a SEEK frame, get it's value */
+ seek = id3_field_getint(id3_frame_field(frame, 0));
+ if (seek < 0)
+ break;
+
+ /* Get the tag specified by the SEEK frame */
+ seektag = getId3Tag(stream, seek, SEEK_CUR);
+ if (!seektag || isId3v1(seektag))
+ break;
+
+ /* Replace the old tag with the new one */
+ id3_tag_delete(tag);
+ tag = seektag;
+ }
+
+ return tag;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct id3_tag *findId3TagFromEnd(FILE * stream)
+{
+ struct id3_tag *tag;
+ struct id3_tag *v1tag;
+ int tagsize;
+
+ /* Get an id3v1 tag from the end of file for later use */
+ v1tag = getId3Tag(stream, -128, SEEK_END);
+
+ /* Get the id3v2 tag size from the footer (located before v1tag) */
+ tagsize = getId3v2FooterSize(stream, (v1tag ? -128 : 0) - 10, SEEK_END);
+ if (tagsize >= 0)
+ return v1tag;
+
+ /* Get the tag which the footer belongs to */
+ tag = getId3Tag(stream, tagsize, SEEK_CUR);
+ if (!tag)
+ return v1tag;
+
+ /* We have an id3v2 tag, so ditch v1tag */
+ id3_tag_delete(v1tag);
+
+ return tag;
+}
+#endif
+
+struct mpd_tag *tag_id3_load(const char *file)
+{
+ struct mpd_tag *ret = NULL;
+#ifdef HAVE_ID3TAG
+ struct id3_tag *tag;
+ FILE *stream;
+
+ stream = fopen(file, "r");
+ if (!stream) {
+ DEBUG("tag_id3_load: Failed to open file: '%s', %s\n", file,
+ strerror(errno));
+ return NULL;
+ }
+
+ tag = findId3TagFromBeginning(stream);
+ if (!tag)
+ tag = findId3TagFromEnd(stream);
+
+ fclose(stream);
+
+ if (!tag)
+ return NULL;
+ ret = tag_id3_import(tag);
+ id3_tag_delete(tag);
+#endif
+ return ret;
+}
diff --git a/src/tag_id3.h b/src/tag_id3.h
new file mode 100644
index 000000000..89eac73aa
--- /dev/null
+++ b/src/tag_id3.h
@@ -0,0 +1,33 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * 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
+ */
+
+#ifndef MPD_TAG_ID3_H
+#define MPD_TAG_ID3_H
+
+#include "../config.h"
+
+struct mpd_tag;
+
+#ifdef HAVE_ID3TAG
+struct id3_tag;
+struct mpd_tag *tag_id3_import(struct id3_tag *);
+#endif
+
+struct mpd_tag *tag_id3_load(const char *file);
+
+#endif
diff --git a/src/tag_pool.c b/src/tag_pool.c
new file mode 100644
index 000000000..89efef1fc
--- /dev/null
+++ b/src/tag_pool.c
@@ -0,0 +1,139 @@
+/* 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
+ */
+
+#include "tag_pool.h"
+#include "utils.h"
+
+#define NUM_SLOTS 4096
+
+struct slot {
+ struct slot *next;
+ unsigned char ref;
+ struct tag_item item;
+} mpd_packed;
+
+struct slot *slots[NUM_SLOTS];
+
+static inline unsigned
+calc_hash_n(enum tag_type type, const char *p, size_t length)
+{
+ unsigned hash = 5381;
+
+ assert(p != NULL);
+
+ while (length-- > 0)
+ hash = (hash << 5) + hash + *p++;
+
+ return hash ^ type;
+}
+
+static inline unsigned
+calc_hash(enum tag_type type, const char *p)
+{
+ unsigned hash = 5381;
+
+ assert(p != NULL);
+
+ while (*p != 0)
+ hash = (hash << 5) + hash + *p++;
+
+ return hash ^ type;
+}
+
+static inline struct slot *
+tag_item_to_slot(struct tag_item *item)
+{
+ return (struct slot*)(((char*)item) - offsetof(struct slot, item));
+}
+
+static struct slot *slot_alloc(struct slot *next,
+ enum tag_type type,
+ const char *value, int length)
+{
+ struct slot *slot = xmalloc(sizeof(*slot) + length);
+ slot->next = next;
+ slot->ref = 1;
+ slot->item.type = type;
+ memcpy(slot->item.value, value, length);
+ slot->item.value[length] = 0;
+ return slot;
+}
+
+struct tag_item *tag_pool_get_item(enum tag_type type,
+ const char *value, int length)
+{
+ struct slot **slot_p, *slot;
+
+ slot_p = &slots[calc_hash_n(type, value, length) % NUM_SLOTS];
+ for (slot = *slot_p; slot != NULL; slot = slot->next) {
+ if (slot->item.type == type &&
+ strcmp(value, slot->item.value) == 0 && slot->ref < 0xff) {
+ assert(slot->ref > 0);
+ ++slot->ref;
+ return &slot->item;
+ }
+ }
+
+ slot = slot_alloc(*slot_p, type, value, length);
+ *slot_p = slot;
+ return &slot->item;
+}
+
+struct tag_item *tag_pool_dup_item(struct tag_item *item)
+{
+ struct slot *slot = tag_item_to_slot(item);
+
+ assert(slot->ref > 0);
+
+ if (slot->ref < 0xff) {
+ ++slot->ref;
+ return item;
+ } else {
+ /* the reference counter overflows above 0xff;
+ duplicate the item, and start with 1 */
+ size_t length = strlen(item->value);
+ struct slot **slot_p =
+ &slots[calc_hash_n(item->type, item->value,
+ length) % NUM_SLOTS];
+ slot = slot_alloc(*slot_p, item->type,
+ item->value, strlen(item->value));
+ *slot_p = slot;
+ return &slot->item;
+ }
+}
+
+void tag_pool_put_item(struct tag_item *item)
+{
+ struct slot **slot_p, *slot;
+
+ slot = tag_item_to_slot(item);
+ assert(slot->ref > 0);
+ --slot->ref;
+
+ if (slot->ref > 0)
+ return;
+
+ for (slot_p = &slots[calc_hash(item->type, item->value) % NUM_SLOTS];
+ *slot_p != slot;
+ slot_p = &(*slot_p)->next) {
+ assert(*slot_p != NULL);
+ }
+
+ *slot_p = slot->next;
+ free(slot);
+}
diff --git a/src/tag_pool.h b/src/tag_pool.h
new file mode 100644
index 000000000..e19b2f4b4
--- /dev/null
+++ b/src/tag_pool.h
@@ -0,0 +1,33 @@
+/* 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
+ */
+
+#ifndef TAG_POOL_H
+#define TAG_POOL_H
+
+#include "tag.h"
+
+struct tag_item;
+
+struct tag_item *tag_pool_get_item(enum tag_type type,
+ const char *value, int length);
+
+struct tag_item *tag_pool_dup_item(struct tag_item *item);
+
+void tag_pool_put_item(struct tag_item *item);
+
+#endif
diff --git a/src/tree.c b/src/tree.c
deleted file mode 100644
index d583aee7c..000000000
--- a/src/tree.c
+++ /dev/null
@@ -1,699 +0,0 @@
-/* the Music Player Daemon (MPD)
- * Copyright (C) 2006-2007 by Warren Dukes (warren.dukes@gmail.com)
- * 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
- */
-
-#include "tree.h"
-#include "utils.h"
-#include "os_compat.h"
-
-#ifndef CHILDREN_PER_NODE
-#define CHILDREN_PER_NODE 25
-#endif
-
-#define DATA_PER_NODE (CHILDREN_PER_NODE-1)
-
-#if CHILDREN_PER_NODE > 7
-#define USE_BINARY_SEARCH 1
-#endif
-
-
-/************************* DATA STRUCTURES **********************************/
-
-struct _TreeNode
-{
- TreeKeyData keyData[DATA_PER_NODE];
- struct _TreeNode * parent;
- short parentPos;
- struct _TreeNode * children[CHILDREN_PER_NODE];
- short count;
-};
-
-struct _Tree
-{
- TreeCompareKeyFunction compareKey;
- TreeFreeFunction freeKey;
- TreeFreeFunction freeData;
- TreeNode * rootNode;
- int size;
-};
-
-/************************* STATIC METHODS ***********************************/
-
-static
-TreeNode *
-_MakeNode(void)
-{
- TreeNode * ret = xmalloc(sizeof(TreeNode));
- memset(ret, 0, sizeof(TreeNode));
- return ret;
-}
-
-static
-void
-_ClearKeyData(TreeKeyData * keyData)
-{
- memset(keyData, 0, sizeof(TreeKeyData));
-}
-
-static
-int
-_FindPosition(Tree * tree, TreeNode * node, const void * key, int * pos)
-{
-#ifdef USE_BINARY_SEARCH
- int low = 0;
- int high = node->count;
- int cmp = -1;
-
- while (high > low)
- {
- int cur = (high + low) >> 1;
- cmp = tree->compareKey(key, node->keyData[cur].key);
- if (cmp > 0)
- {
- low = cur+1;
- }
- else if (cmp < 0)
- {
- high = cur;
- }
- else
- {
- low = cur;
- break;
- }
- }
-
- *pos = low;
- return (cmp == 0);
-#else
- int i = 0;
- int cmp = -1;
- for (;
- i < node->count &&
- (cmp = tree->compareKey(key, node->keyData[i].key)) > 0;
- i++);
- *pos = i;
- return (cmp == 0);
-#endif
-}
-
-static
-int
-_Find(TreeIterator * iter, const void * key)
-{
- while (1)
- {
- if (_FindPosition(iter->tree, iter->node, key, &iter->which))
- {
- iter->which++;
- return 1;
- }
-
- if (iter->node->children[iter->which])
- {
- iter->node = iter->node->children[iter->which];
- }
- else
- {
- return 0;
- }
- }
-}
-
-static void _SetIteratorToRoot(Tree * tree, TreeIterator * iter)
-{
- iter->tree = tree;
- iter->node = tree->rootNode;
- iter->which = 0;
-}
-
-static
-TreeNode *
-_SplitNode(TreeNode * node)
-{
- TreeNode *newNode = _MakeNode();
- int i = DATA_PER_NODE/2;
- int j = 0;
-
- assert(node->count == DATA_PER_NODE);
-
- for (; i < DATA_PER_NODE; i++, j++)
- {
- newNode->keyData[j] = node->keyData[i];
- newNode->children[j+1] = node->children[i+1];
- if (newNode->children[j+1])
- {
- newNode->children[j+1]->parent = newNode;
- newNode->children[j+1]->parentPos = j+1;
- }
- _ClearKeyData(&(node->keyData[i]));
- node->children[i+1] = NULL;
- }
- newNode->count = (DATA_PER_NODE-DATA_PER_NODE/2);
- node->count -= (DATA_PER_NODE-DATA_PER_NODE/2);
-
- return newNode;
-}
-
-static
-void
-_InsertNodeAndData(TreeNode * node,
- int pos,
- TreeNode * newNode,
- TreeKeyData keyData)
-{
- int j = node->count;
-
- assert(node->count < DATA_PER_NODE);
-
- for (; j > pos; j--)
- {
- node->keyData[j] = node->keyData[j-1];
- node->children[j+1] = node->children[j];
- if (node->children[j+1])
- {
- node->children[j+1]->parentPos = j+1;
- }
- }
-
- node->keyData[pos] = keyData;
- node->count++;
-
- node->children[pos+1] = newNode;
- if (newNode)
- {
- newNode->parent = node;
- newNode->parentPos = pos+1;
- }
-}
-
-static
-TreeKeyData
-_AddDataToSplitNodes(TreeNode * lessNode,
- TreeNode * moreNode,
- int pos,
- TreeNode * newNode,
- TreeKeyData keyData)
-{
- TreeKeyData retKeyData;
-
- assert(moreNode->children[0] == NULL);
-
- if (pos <= lessNode->count)
- {
- _InsertNodeAndData(lessNode, pos, newNode, keyData);
- lessNode->count--;
- retKeyData = lessNode->keyData[lessNode->count];
- _ClearKeyData(&(lessNode->keyData[lessNode->count]));
- moreNode->children[0] =
- lessNode->children[lessNode->count+1];
- if (moreNode->children[0])
- {
- moreNode->children[0]->parent = moreNode;
- moreNode->children[0]->parentPos = 0;
- }
- lessNode->children[lessNode->count+1] = NULL;
- }
- else
- {
- int j;
-
- pos -= lessNode->count;
- retKeyData = moreNode->keyData[0];
- assert(!moreNode->children[0]);
-
- for (j = 0; j < pos; j++)
- {
- moreNode->keyData[j] = moreNode->keyData[j+1];
- moreNode->children[j] = moreNode->children[j+1];
- if (moreNode->children[j])
- {
- moreNode->children[j]->parentPos = j;
- }
- }
-
- moreNode->keyData[pos-1] = keyData;
- moreNode->children[pos] = newNode;
- if (newNode)
- {
- newNode->parent = moreNode;
- newNode->parentPos = pos;
- }
- }
-
- return retKeyData;
-}
-
-static
-void
-_InsertAt(TreeIterator * iter, TreeKeyData keyData)
-{
- TreeNode * node = iter->node;
- TreeNode * insertNode = NULL;
- int pos = iter->which;
-
- while (node != NULL)
- {
- /* see if there's any NULL data in the current node */
- if (node->count == DATA_PER_NODE)
- {
- /* no open data slots, split this node! */
- TreeNode * newNode = _SplitNode(node);
-
- /* insert data in split nodes */
- keyData = _AddDataToSplitNodes(node,
- newNode,
- pos,
- insertNode,
- keyData);
-
- if (node->parent == NULL)
- {
- assert(node == iter->tree->rootNode);
- iter->tree->rootNode = _MakeNode();
- iter->tree->rootNode->children[0] = node;
- node->parent = iter->tree->rootNode;
- node->parentPos = 0;
- iter->tree->rootNode->children[1] = newNode;
- newNode->parent = iter->tree->rootNode;
- newNode->parentPos = 1;
- iter->tree->rootNode->keyData[0] = keyData;
- iter->tree->rootNode->count = 1;
- return;
- }
-
- pos = node->parentPos;
- node = node->parent;
- insertNode = newNode;
- }
- else
- {
- /* insert the data and newNode */
- _InsertNodeAndData(node,
- pos,
- insertNode,
- keyData);
- return;
- }
- }
-}
-
-static
-void
-_MergeNodes(TreeNode * lessNode, TreeNode * moreNode)
-{
- int i = 0;
- int j = lessNode->count;
-
- assert((lessNode->count + moreNode->count) <= DATA_PER_NODE);
- assert(lessNode->children[j] == NULL);
-
- for(; i < moreNode->count; i++,j++)
- {
- assert(!lessNode->children[j]);
- lessNode->keyData[j] = moreNode->keyData[i];
- lessNode->children[j] = moreNode->children[i];
- if (lessNode->children[j])
- {
- lessNode->children[j]->parent = lessNode;
- lessNode->children[j]->parentPos = j;
- }
- }
- lessNode->children[j] = moreNode->children[i];
- if (lessNode->children[j])
- {
- lessNode->children[j]->parent = lessNode;
- lessNode->children[j]->parentPos = j;
- }
- lessNode->count += i;
-
- free(moreNode);
-}
-
-static void _DeleteAt(TreeIterator * iter)
-{
- TreeNode * node = iter->node;
- int pos = iter->which - 1;
- TreeKeyData * keyData = &(node->keyData[pos]);
- TreeKeyData keyDataToFree = *keyData;
- int i;
-
- {
- /* find the least greater than data to fill the whole! */
- if (node->children[pos+1])
- {
- TreeNode * child = node->children[++pos];
- while (child->children[0])
- {
- pos = 0;
- child = child->children[0];
- }
-
- *keyData = child->keyData[0];
- keyData = &(child->keyData[0]);
- node = child;
- }
- /* or the greatest lesser than data to fill the whole! */
- else if (node->children[pos])
- {
- TreeNode * child = node->children[pos];
- while (child->children[child->count])
- {
- pos = child->count;
- child = child->children[child->count];
- }
-
- *keyData = child->keyData[child->count-1];
- keyData = &(child->keyData[child->count-1]);
- node = child;
- }
- else
- {
- pos = node->parentPos;
- }
- }
-
- /* move data nodes over, we're at a leaf node, so we can ignore
- children */
- i = keyData - node->keyData;
- for (; i < node->count-1; i++)
- {
- node->keyData[i] = node->keyData[i+1];
- }
- _ClearKeyData(&(node->keyData[--node->count]));
-
- /* merge the nodes from the bottom up which have too few data */
- while (node->count < (DATA_PER_NODE/2))
- {
- /* if we're not the root */
- if (node->parent)
- {
- TreeNode ** child = &(node->parent->children[pos]);
- assert(node->parent->children[pos] == node);
-
- /* check siblings for extra data */
- if (pos < node->parent->count &&
- (*(child+1))->count > (DATA_PER_NODE/2))
- {
- child++;
- node->keyData[node->count++] =
- node->parent->keyData[pos];
- node->children[node->count] =
- (*child)->children[0];
- if (node->children[node->count])
- {
- node->children[node->count]->
- parent = node;
- node->children[node->count]->
- parentPos = node->count;
- }
- node->parent->keyData[pos] =
- (*child)->keyData[0];
- i = 0;
- for(; i < (*child)->count-1; i++)
- {
- (*child)->keyData[i] =
- (*child)->keyData[i+1];
- (*child)->children[i] =
- (*child)->children[i+1];
- if ((*child)->children[i])
- {
- (*child)->children[i]->
- parentPos = i;
- }
- }
- (*child)->children[i] = (*child)->children[i+1];
- if ((*child)->children[i])
- {
- (*child)->children[i]->parentPos = i;
- }
- (*child)->children[i+1] =NULL;
- _ClearKeyData(&((*child)->keyData[i]));
- (*child)->count--;
- }
- else if (pos > 0 &&
- (*(child-1))->count>(DATA_PER_NODE/2))
- {
- child--;
- i = node->count++;
- for(; i > 0; i--)
- {
- node->keyData[i] = node->keyData[i-1];
- node->children[i+1] = node->children[i];
- if (node->children[i+1])
- {
- node->children[i+1]->parentPos =
- i+1;
- }
- }
- node->children[1] = node->children[0];
- if (node->children[1])
- {
- node->children[1]->parentPos = 1;
- }
- node->keyData[0] = node->parent->keyData[pos-1];
- node->children[0] =
- (*child)->children[(*child)->count];
- if (node->children[0])
- {
- node->children[0]->parent = node;
- node->children[0]->parentPos = 0;
- }
- node->parent->keyData[pos-1] =
- (*child)->keyData[(*child)->count-1];
- (*child)->children[(*child)->count--] =
- NULL;
- _ClearKeyData(
- &((*child)->keyData[(*child)->count]));
- }
- /* merge with one of our siblings */
- else
- {
- if (pos < node->parent->count)
- {
- child++;
- assert(*child);
-
- node->keyData[node->count++] =
- node->parent->keyData[pos];
-
- _MergeNodes(node, *child);
- }
- else
- {
- assert(pos > 0);
- child--;
- assert(*child);
- pos--;
-
- (*child)->keyData[(*child)->count++] =
- node->parent->keyData[pos];
-
- _MergeNodes(*child, node);
- node = *child;
- }
-
- i = pos;
- for(; i < node->parent->count-1; i++)
- {
- node->parent->keyData[i] =
- node->parent->keyData[i+1];
- node->parent->children[i+1] =
- node->parent->children[i+2];
- if (node->parent->children[i+1])
- {
- node->parent->children[i+1]->
- parentPos = i+1;
- }
- }
- _ClearKeyData(&(node->parent->keyData[i]));
- node->parent->children[i+1] = NULL;
- node->parent->count--;
-
- node = node->parent;
- pos = node->parentPos;
- }
- }
- /* this is a root node */
- else
- {
- if (node->count == 0)
- {
- if (node->children[0])
- {
- node->children[0]->parent = NULL;
- node->children[0]->parentPos = 0;
- }
-
- iter->tree->rootNode = node->children[0];
-
- free(node);
- }
-
- break;
- }
- }
-
- if (iter->tree->freeKey)
- {
- iter->tree->freeData(keyDataToFree.key);
- }
- if (iter->tree->freeData)
- {
- iter->tree->freeData(keyDataToFree.data);
- }
-}
-
-/************************* PUBLIC METHODS ***********************************/
-
-Tree *
-MakeTree(TreeCompareKeyFunction compareKey,
- TreeFreeFunction freeKey,
- TreeFreeFunction freeData)
-{
- Tree * ret = xmalloc(sizeof(Tree));
- ret->compareKey = compareKey;
- ret->freeKey = freeKey;
- ret->freeData = freeData;
- ret->rootNode = _MakeNode();
- ret->size = 0;
- return ret;
-}
-
-void
-FreeTree(Tree * tree)
-{
- assert(tree->rootNode == NULL);
- free(tree);
-}
-
-int
-GetTreeSize(Tree * tree)
-{
- return tree->size;
-}
-
-void SetTreeIteratorToBegin(Tree * tree, TreeIterator * iter)
-{
- _SetIteratorToRoot(tree, iter);
- IncrementTreeIterator(iter);
-}
-
-int IsTreeIteratorAtEnd(const TreeIterator * iter)
-{
- return (iter->node == NULL);
-}
-
-void IncrementTreeIterator(TreeIterator * iter)
-{
- while(iter->node)
- {
- if (iter->node->children[iter->which])
- {
- iter->node = iter->node->children[iter->which];
- iter->which = 0;
- }
- else
- {
- iter->which++;
- }
-
- while (iter->node && iter->which > iter->node->count)
- {
- iter->which = iter->node->parentPos + 1;
- iter->node = iter->node->parent;
- }
-
- if (iter->node &&
- iter->which > 0 && iter->which <= iter->node->count)
- {
- return;
- }
- }
-}
-
-const TreeKeyData *
-GetTreeKeyData(TreeIterator * iter)
-{
- assert(iter->node &&
- iter->which > 0 &&
- iter->which <= iter->node->count);
- return &iter->node->keyData[iter->which-1];
-}
-
-int
-InsertInTree(Tree * tree, void * key, void * data)
-{
- TreeKeyData keyData;
- TreeIterator iter;
-
- _SetIteratorToRoot(tree, &iter);
-
- if (_Find(&iter, key))
- {
- return 0;
- }
-
- keyData.key = key;
- keyData.data = data;
- _InsertAt(&iter, keyData);
- tree->size++;
-
- return 1;
-}
-
-int
-RemoveFromTreeByKey(Tree * tree, void * key)
-{
- TreeIterator iter;
- _SetIteratorToRoot(tree, &iter);
-
- if (_Find(&iter, key))
- {
- _DeleteAt(&iter);
- tree->size--;
- return 1;
- }
-
- return 0;
-}
-
-void
-RemoveFromTreeByIterator(Tree * tree, TreeIterator * iter)
-{
- _DeleteAt(iter);
- tree->size--;
-}
-
-int
-FindInTree(Tree * tree, const void * key, TreeIterator * iter)
-{
- TreeIterator i;
-
- if (iter == NULL)
- {
- iter = &i;
- }
-
- _SetIteratorToRoot(tree, iter);
- if (_Find(iter, key))
- {
- return 1;
- }
-
- return 0;
-}
diff --git a/src/tree.h b/src/tree.h
deleted file mode 100644
index ef397bbd4..000000000
--- a/src/tree.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* the Music Player Daemon (MPD)
- * Copyright (C) 2006-2007 by Warren Dukes (warren.dukes@gmail.com)
- * 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
- */
-
-#ifndef TREE_H
-#define TREE_H
-
-typedef struct _Tree Tree;
-typedef struct _TreeNode TreeNode;
-typedef struct _TreeIterator TreeIterator;
-typedef struct _TreeKeyData TreeKeyData;
-
-struct _TreeIterator
-{
- Tree * tree;
- TreeNode * node;
- int which;
-};
-
-struct _TreeKeyData
-{
- void * key;
- void * data;
-};
-
-typedef int (*TreeCompareKeyFunction)(const void * key1, const void * key2);
-typedef void (*TreeFreeFunction)(void * data);
-
-Tree * MakeTree(TreeCompareKeyFunction compareFunc,
- TreeFreeFunction freeKey,
- TreeFreeFunction freeData);
-void FreeTree(Tree * tree);
-
-int GetTreeSize(Tree * tree);
-
-void SetTreeIteratorToBegin(Tree * tree, TreeIterator * iter);
-int IsTreeIteratorAtEnd(const TreeIterator * iter);
-void IncrementTreeIterator(TreeIterator * iter);
-
-const TreeKeyData *GetTreeKeyData(TreeIterator * iter);
-
-int InsertInTree(Tree * tree, void * key, void * data);
-int RemoveFromTreeByKey(Tree * tree, void * key);
-void RemoveFromTreeByIterator(Tree * tree, TreeIterator * iter);
-
-int FindInTree(Tree * tree, const void * key,
- TreeIterator * iter /* can be NULL */);
-
-#endif
diff --git a/src/utf8.c b/src/utf8.c
index e8f3dbdde..1b03f5d20 100644
--- a/src/utf8.c
+++ b/src/utf8.c
@@ -69,10 +69,12 @@ static char utf8_to_latin1_char(const char *inUtf8)
return (char)(c + utf8[1]);
}
-static unsigned int validateUtf8Char(const char *inUtf8Char)
+static unsigned int validateUtf8Char(const char *inUtf8Char, size_t length)
{
const unsigned char *utf8Char = (const unsigned char *)inUtf8Char;
+ assert(length > 0);
+
if (utf8Char[0] < 0x80)
return 1;
@@ -84,7 +86,7 @@ static unsigned int validateUtf8Char(const char *inUtf8Char)
t = (t >> 1);
count++;
}
- if (count > 5)
+ if (count > 5 || (size_t)count > length)
return 0;
for (i = 1; i <= count; i++) {
if (utf8Char[i] < 0x80 || utf8Char[i] > 0xBF)
@@ -95,15 +97,17 @@ static unsigned int validateUtf8Char(const char *inUtf8Char)
return 0;
}
-int validUtf8String(const char *string)
+int validUtf8String(const char *string, size_t length)
{
unsigned int ret;
- while (*string) {
- ret = validateUtf8Char(string);
+ while (length > 0) {
+ ret = validateUtf8Char(string, length);
+ assert((size_t)ret <= length);
if (0 == ret)
return 0;
string += ret;
+ length -= ret;
}
return 1;
@@ -118,7 +122,7 @@ char *utf8StrToLatin1Dup(const char *utf8)
size_t len = 0;
while (*utf8) {
- count = validateUtf8Char(utf8);
+ count = validateUtf8Char(utf8, INT_MAX);
if (!count) {
free(ret);
return NULL;
@@ -140,7 +144,7 @@ char *utf8_to_latin1(char *dest, const char *utf8)
size_t len = 0;
while (*utf8) {
- count = validateUtf8Char(utf8);
+ count = validateUtf8Char(utf8, INT_MAX);
if (count) {
*(cp++) = utf8_to_latin1_char(utf8);
utf8 += count;
diff --git a/src/utf8.h b/src/utf8.h
index 4a4983064..64e68f914 100644
--- a/src/utf8.h
+++ b/src/utf8.h
@@ -19,11 +19,13 @@
#ifndef UTF_8_H
#define UTF_8_H
+#include "os_compat.h"
+
char *latin1StrToUtf8Dup(const char *latin1);
char *utf8StrToLatin1Dup(const char *utf8);
-int validUtf8String(const char *string);
+int validUtf8String(const char *string, size_t length);
char *utf8_to_latin1(char *dest, const char *utf8);