aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--src/ape.c113
-rw-r--r--src/ape.h42
-rw-r--r--src/tag_ape.c112
4 files changed, 176 insertions, 93 deletions
diff --git a/Makefile.am b/Makefile.am
index 92a2a9f5..6160ac79 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,6 +34,7 @@ mpd_headers = \
src/check.h \
src/notify.h \
src/ack.h \
+ src/ape.h \
src/audio.h \
src/audio_format.h \
src/audio_check.h \
@@ -412,6 +413,7 @@ TAG_LIBS = \
$(ID3TAG_LIBS)
TAG_SRC = \
+ src/ape.c \
src/tag_ape.c
if HAVE_ID3TAG
diff --git a/src/ape.c b/src/ape.c
new file mode 100644
index 00000000..5fca98e2
--- /dev/null
+++ b/src/ape.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ape.h"
+
+#include <glib.h>
+
+#include <stdint.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+struct ape_footer {
+ unsigned char id[8];
+ uint32_t version;
+ uint32_t length;
+ uint32_t count;
+ unsigned char flags[4];
+ unsigned char reserved[8];
+};
+
+static bool
+ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
+{
+ /* determine if file has an apeV2 tag */
+ struct ape_footer footer;
+ if (fseek(fp, -(long)sizeof(footer), SEEK_END) ||
+ fread(&footer, 1, sizeof(footer), fp) != sizeof(footer) ||
+ memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 ||
+ GUINT32_FROM_LE(footer.version) != 2000)
+ return false;
+
+ /* find beginning of ape tag */
+ size_t remaining = GUINT32_FROM_LE(footer.length);
+ if (remaining <= sizeof(footer) + 10 ||
+ /* refuse to load more than one megabyte of tag data */
+ remaining > 1024 * 1024 ||
+ fseek(fp, -(long)remaining, SEEK_END))
+ return false;
+
+ /* read tag into buffer */
+ remaining -= sizeof(footer);
+ assert(remaining > 10);
+
+ char *buffer = g_malloc(remaining);
+ if (fread(buffer, 1, remaining, fp) != remaining)
+ return false;
+
+ /* read tags */
+ unsigned n = GUINT32_FROM_LE(footer.count);
+ const char *p = buffer;
+ while (n-- && remaining > 10) {
+ size_t size = GUINT32_FROM_LE(*(const uint32_t *)p);
+ p += 4;
+ remaining -= 4;
+ unsigned long flags = GUINT32_FROM_LE(*(const uint32_t *)p);
+ p += 4;
+ remaining -= 4;
+
+ /* get the key */
+ const char *key = p;
+ while (remaining > size && *p != '\0') {
+ p++;
+ remaining--;
+ }
+ p++;
+ remaining--;
+
+ /* get the value */
+ if (remaining < size)
+ break;
+
+ if (!callback(flags, key, p, size, ctx))
+ break;
+
+ p += size;
+ remaining -= size;
+ }
+
+ g_free(buffer);
+ return true;
+}
+
+bool
+tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx)
+{
+ FILE *fp;
+
+ fp = fopen(path_fs, "rb");
+ if (fp == NULL)
+ return false;
+
+ bool success = ape_scan_internal(fp, callback, ctx);
+ fclose(fp);
+ return success;
+}
diff --git a/src/ape.h b/src/ape.h
new file mode 100644
index 00000000..754b9bb2
--- /dev/null
+++ b/src/ape.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_APE_H
+#define MPD_APE_H
+
+#include "check.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+typedef bool (*tag_ape_callback_t)(unsigned long flags, const char *key,
+ const char *value, size_t value_length,
+ void *ctx);
+
+/**
+ * Scans the APE tag values from a file.
+ *
+ * @param path_fs the path of the file in filesystem encoding
+ * @return false if the file could not be opened or if no APE tag is
+ * present
+ */
+bool
+tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx);
+
+#endif
diff --git a/src/tag_ape.c b/src/tag_ape.c
index 4841b313..82e36d7a 100644
--- a/src/tag_ape.c
+++ b/src/tag_ape.c
@@ -21,11 +21,7 @@
#include "tag_ape.h"
#include "tag.h"
#include "tag_table.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <stdio.h>
+#include "ape.h"
static const char *const ape_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
[TAG_ALBUM_ARTIST] = "album artist",
@@ -61,96 +57,26 @@ tag_ape_import_item(struct tag *tag, unsigned long flags,
return tag;
}
-struct tag *
-tag_ape_load(const char *file)
-{
- struct tag *ret = NULL;
- FILE *fp;
- int tagCount;
- char *buffer = NULL;
- char *p;
- size_t tagLen;
- size_t size;
- unsigned long flags;
- char *key;
-
- struct {
- unsigned char id[8];
- uint32_t version;
- uint32_t length;
- uint32_t tagCount;
- unsigned char flags[4];
- unsigned char reserved[8];
- } footer;
-
- fp = fopen(file, "rb");
- if (!fp)
- return NULL;
-
- /* determine if file has an apeV2 tag */
- if (fseek(fp, 0, SEEK_END))
- goto fail;
- size = (size_t)ftell(fp);
- if (fseek(fp, size - sizeof(footer), SEEK_SET))
- goto fail;
- if (fread(&footer, 1, sizeof(footer), fp) != sizeof(footer))
- goto fail;
- if (memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0)
- goto fail;
- if (GUINT32_FROM_LE(footer.version) != 2000)
- goto fail;
-
- /* find beginning of ape tag */
- tagLen = GUINT32_FROM_LE(footer.length);
- if (tagLen <= sizeof(footer) + 10)
- goto fail;
- if (tagLen > 1024 * 1024)
- /* refuse to load more than one megabyte of tag data */
- goto fail;
- if (fseek(fp, size - tagLen, SEEK_SET))
- goto fail;
-
- /* read tag into buffer */
- tagLen -= sizeof(footer);
- assert(tagLen > 10);
-
- buffer = g_malloc(tagLen);
- if (fread(buffer, 1, tagLen, fp) != tagLen)
- goto fail;
-
- /* read tags */
- tagCount = GUINT32_FROM_LE(footer.tagCount);
- p = buffer;
- while (tagCount-- && tagLen > 10) {
- size = GUINT32_FROM_LE(*(const uint32_t *)p);
- p += 4;
- tagLen -= 4;
- flags = GUINT32_FROM_LE(*(const uint32_t *)p);
- p += 4;
- tagLen -= 4;
-
- /* get the key */
- key = p;
- while (tagLen > size && *p != '\0') {
- p++;
- tagLen--;
- }
- p++;
- tagLen--;
+struct tag_ape_ctx {
+ struct tag *tag;
+};
- /* get the value */
- if (tagLen < size)
- goto fail;
+static bool
+tag_ape_callback(unsigned long flags, const char *key,
+ const char *value, size_t value_length, void *_ctx)
+{
+ struct tag_ape_ctx *ctx = _ctx;
- ret = tag_ape_import_item(ret, flags, key, p, size);
+ ctx->tag = tag_ape_import_item(ctx->tag, flags, key,
+ value, value_length);
+ return true;
+}
- p += size;
- tagLen -= size;
- }
+struct tag *
+tag_ape_load(const char *file)
+{
+ struct tag_ape_ctx ctx = { .tag = NULL };
-fail:
- if (fp)
- fclose(fp);
- g_free(buffer);
- return ret;
+ tag_ape_scan(file, tag_ape_callback, &ctx);
+ return ctx.tag;
}