This allow to use these scanners for remote InputStream like SMB and NFS.
---
src/tag/Aiff.cxx | 29 ++++---------
src/tag/Aiff.hxx | 4 +-
src/tag/ApeLoader.cxx | 43 ++++++++++++++-----
src/tag/ApeLoader.hxx | 11 +++++
src/tag/ApeTag.cxx | 19 +++++++++
src/tag/ApeTag.hxx | 10 +++++
src/tag/Riff.cxx | 29 ++++---------
src/tag/Riff.hxx | 4 +-
src/tag/TagId3.cxx | 113 ++++++++++++++++++++++++++++++++------------------
src/tag/TagId3.hxx | 11 +++--
10 files changed, 174 insertions(+), 99 deletions(-)
diff --git a/src/tag/Aiff.cxx b/src/tag/Aiff.cxx
index 7235b76..58f94ba 100644
--- a/src/tag/Aiff.cxx
+++ b/src/tag/Aiff.cxx
@@ -20,6 +20,7 @@
#include "config.h" /* must be first for large file support */
#include "Aiff.hxx"
#include "util/Domain.hxx"
+#include "util/Error.hxx"
#include "system/ByteOrder.hxx"
#include "Log.hxx"
@@ -43,28 +44,15 @@ struct aiff_chunk_header {
};
size_t
-aiff_seek_id3(FILE *file)
+aiff_seek_id3(InputStream &is)
{
- /* determine the file size */
-
- struct stat st;
- if (fstat(fileno(file), &st) < 0) {
- LogErrno(aiff_domain, "Failed to stat file descriptor");
- return 0;
- }
-
/* seek to the beginning and read the AIFF header */
-
- if (fseek(file, 0, SEEK_SET) != 0) {
- LogErrno(aiff_domain, "Failed to seek");
- return 0;
- }
+ is.Rewind(IgnoreError());
aiff_header header;
- size_t size = fread(&header, sizeof(header), 1, file);
- if (size != 1 ||
+ if (!is.ReadFull(&header, sizeof(header), IgnoreError()) ||
memcmp(header.id, "FORM", 4) != 0 ||
- FromBE32(header.size) > (uint32_t)st.st_size ||
+ FromBE32(header.size) > is.GetSize() ||
(memcmp(header.format, "AIFF", 4) != 0 &&
memcmp(header.format, "AIFC", 4) != 0))
/* not a AIFF file */
@@ -74,11 +62,10 @@ aiff_seek_id3(FILE *file)
/* read the chunk header */
aiff_chunk_header chunk;
- size = fread(&chunk, sizeof(chunk), 1, file);
- if (size != 1)
+ if (!is.ReadFull(&chunk, sizeof(chunk), IgnoreError()))
return 0;
- size = FromBE32(chunk.size);
+ size_t size = FromBE32(chunk.size);
if (size > unsigned(std::numeric_limits<int>::max()))
/* too dangerous, bail out: possible integer
underflow when casting to off_t */
@@ -92,7 +79,7 @@ aiff_seek_id3(FILE *file)
/* found it! */
return size;
- if (fseek(file, size, SEEK_CUR) != 0)
+ if (!is.Seek(size + is.GetOffset(), IgnoreError()))
return 0;
}
}
diff --git a/src/tag/Aiff.hxx b/src/tag/Aiff.hxx
index f9b11b5..238e5ba 100644
--- a/src/tag/Aiff.hxx
+++ b/src/tag/Aiff.hxx
@@ -28,6 +28,8 @@
#include <stddef.h>
#include <stdio.h>
+#include "input/InputStream.hxx"
+
/**
* Seeks the AIFF file to the ID3 chunk.
*
@@ -35,6 +37,6 @@
* AIFF file or no ID3 chunk was found
*/
size_t
-aiff_seek_id3(FILE *file);
+aiff_seek_id3(InputStream &is);
#endif
diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx
index 8c89f34..574eb84 100644
--- a/src/tag/ApeLoader.cxx
+++ b/src/tag/ApeLoader.cxx
@@ -21,6 +21,9 @@
#include "ApeLoader.hxx"
#include "system/ByteOrder.hxx"
#include "fs/FileSystem.hxx"
+#include "util/Error.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
#include <stdint.h>
#include <assert.h>
@@ -36,31 +39,46 @@ struct ape_footer {
unsigned char reserved[8];
};
-static bool
-ape_scan_internal(FILE *fp, ApeTagCallback callback)
+bool
+tag_ape_scan(InputStream &is, ApeTagCallback callback)
{
+ is.Lock();
+ if (!is.KnownSize() || !is.IsSeekable())
+ {
+ is.Unlock();
+ return false;
+ }
/* 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) ||
+ if (!is.Seek(is.GetSize() - (long)sizeof(footer), IgnoreError()) ||
+ !is.ReadFull(&footer, sizeof(footer), IgnoreError()) ||
memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 ||
FromLE32(footer.version) != 2000)
+ {
+ is.Rewind(IgnoreError());
+ is.Unlock();
return false;
+ }
/* find beginning of ape tag */
size_t remaining = FromLE32(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))
+ !is.Seek(is.GetSize() - (long)remaining, IgnoreError())) {
+ is.Rewind(IgnoreError());
+ is.Unlock();
return false;
+ }
/* read tag into buffer */
remaining -= sizeof(footer);
assert(remaining > 10);
char *buffer = new char[remaining];
- if (fread(buffer, 1, remaining, fp) != remaining) {
+ if (!is.ReadFull(buffer, remaining, IgnoreError())) {
+ is.Rewind(IgnoreError());
+ is.Unlock();
delete[] buffer;
return false;
}
@@ -96,6 +114,8 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
remaining -= size;
}
+ is.Rewind(IgnoreError());
+ is.Unlock();
delete[] buffer;
return true;
}
@@ -103,11 +123,14 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
bool
tag_ape_scan(Path path_fs, ApeTagCallback callback)
{
- FILE *fp = FOpen(path_fs, PATH_LITERAL("rb"));
- if (fp == nullptr)
+ Mutex mutex;
+ Cond cond;
+ InputStream *is = InputStream::OpenReady(path_fs.c_str(), mutex,
+ cond, IgnoreError());
+ if (is == nullptr)
return false;
- bool success = ape_scan_internal(fp, callback);
- fclose(fp);
+ bool success = tag_ape_scan(*is, callback);
+ delete is;
return success;
}
diff --git a/src/tag/ApeLoader.hxx b/src/tag/ApeLoader.hxx
index 1bdfe69..34ecf4b 100644
--- a/src/tag/ApeLoader.hxx
+++ b/src/tag/ApeLoader.hxx
@@ -21,6 +21,7 @@
#define MPD_APE_LOADER_HXX
#include "check.h"
+#include "input/InputStream.hxx"
#include <functional>
@@ -35,6 +36,16 @@ typedef std::function<bool(unsigned long flags, const char
*key,
/**
* Scans the APE tag values from a file.
*
+ * @param is the input stream to read from
+ * @return false if the file could not be opened or if no APE tag is
+ * present
+ */
+bool
+tag_ape_scan(InputStream &is, ApeTagCallback callback);
+
+/**
+ * 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
diff --git a/src/tag/ApeTag.cxx b/src/tag/ApeTag.cxx
index 49ae7a0..dbbd034 100644
--- a/src/tag/ApeTag.cxx
+++ b/src/tag/ApeTag.cxx
@@ -104,6 +104,25 @@ tag_ape_import_item(unsigned long flags,
}
bool
+tag_ape_scan2(InputStream &is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ bool recognized = false;
+
+ auto callback = [handler, handler_ctx, &recognized]
+ (unsigned long flags, const char *key,
+ const char *value,
+ size_t value_length) {
+ recognized |= tag_ape_import_item(flags, key, value,
+ value_length,
+ handler, handler_ctx);
+ return true;
+ };
+
+ return tag_ape_scan(is, callback) && recognized;
+}
+
+bool
tag_ape_scan2(Path path_fs,
const struct tag_handler *handler, void *handler_ctx)
{
diff --git a/src/tag/ApeTag.hxx b/src/tag/ApeTag.hxx
index 20d1d7b..7e5fa65 100644
--- a/src/tag/ApeTag.hxx
+++ b/src/tag/ApeTag.hxx
@@ -20,6 +20,7 @@
#ifndef MPD_APE_TAG_HXX
#define MPD_APE_TAG_HXX
+#include "input/InputStream.hxx"
#include "TagTable.hxx"
class Path;
@@ -30,6 +31,15 @@ extern const struct tag_table ape_tags[];
/**
* Scan the APE tags of a file.
*
+ * @param is the locked input stream to read from
+ */
+bool
+tag_ape_scan2(InputStream &is,
+ const tag_handler *handler, void *handler_ctx);
+
+/**
+ * Scan the APE tags of a file.
+ *
* @param path_fs the path of the file in filesystem encoding
*/
bool
diff --git a/src/tag/Riff.cxx b/src/tag/Riff.cxx
index 7412258..36344b2 100644
--- a/src/tag/Riff.cxx
+++ b/src/tag/Riff.cxx
@@ -20,6 +20,7 @@
#include "config.h" /* must be first for large file support */
#include "Riff.hxx"
#include "util/Domain.hxx"
+#include "util/Error.hxx"
#include "system/ByteOrder.hxx"
#include "Log.hxx"
@@ -43,28 +44,15 @@ struct riff_chunk_header {
};
size_t
-riff_seek_id3(FILE *file)
+riff_seek_id3(InputStream &is)
{
- /* determine the file size */
-
- struct stat st;
- if (fstat(fileno(file), &st) < 0) {
- LogErrno(riff_domain, "Failed to stat file descriptor");
- return 0;
- }
-
/* seek to the beginning and read the RIFF header */
-
- if (fseek(file, 0, SEEK_SET) != 0) {
- LogErrno(riff_domain, "Failed to seek");
- return 0;
- }
+ is.Rewind(IgnoreError());
riff_header header;
- size_t size = fread(&header, sizeof(header), 1, file);
- if (size != 1 ||
+ if (!is.ReadFull(&header, sizeof(header), IgnoreError()) ||
memcmp(header.id, "RIFF", 4) != 0 ||
- FromLE32(header.size) > (uint32_t)st.st_size)
+ FromLE32(header.size) > is.GetSize())
/* not a RIFF file */
return 0;
@@ -72,11 +60,10 @@ riff_seek_id3(FILE *file)
/* read the chunk header */
riff_chunk_header chunk;
- size = fread(&chunk, sizeof(chunk), 1, file);
- if (size != 1)
+ if (!is.ReadFull(&chunk, sizeof(chunk), IgnoreError()))
return 0;
- size = FromLE32(chunk.size);
+ size_t size = FromLE32(chunk.size);
if (size > size_t(std::numeric_limits<int>::max()))
/* too dangerous, bail out: possible integer
underflow when casting to off_t */
@@ -91,7 +78,7 @@ riff_seek_id3(FILE *file)
/* found it! */
return size;
- if (fseek(file, size, SEEK_CUR) != 0)
+ if (!is.Seek(size + is.GetOffset(), IgnoreError()))
return 0;
}
}
diff --git a/src/tag/Riff.hxx b/src/tag/Riff.hxx
index b157168..5deabcb 100644
--- a/src/tag/Riff.hxx
+++ b/src/tag/Riff.hxx
@@ -28,6 +28,8 @@
#include <stddef.h>
#include <stdio.h>
+#include "input/InputStream.hxx"
+
/**
* Seeks the RIFF file to the ID3 chunk.
*
@@ -35,6 +37,6 @@
* RIFF file or no ID3 chunk was found
*/
size_t
-riff_seek_id3(FILE *file);
+riff_seek_id3(InputStream &is);
#endif
diff --git a/src/tag/TagId3.cxx b/src/tag/TagId3.cxx
index 98f85da..1177c16 100644
--- a/src/tag/TagId3.cxx
+++ b/src/tag/TagId3.cxx
@@ -32,6 +32,8 @@
#include "Aiff.hxx"
#include "fs/Path.hxx"
#include "fs/FileSystem.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
#ifdef HAVE_GLIB
#include <glib.h>
@@ -393,54 +395,68 @@ tag_id3_import(struct id3_tag *tag)
: tag_builder.CommitNew();
}
-static size_t
-fill_buffer(void *buf, size_t size, FILE *stream, long offset, int whence)
+static bool
+fill_buffer(void *buf, size_t size, InputStream &is, long offset, int whence)
{
- if (fseek(stream, offset, whence) != 0) return 0;
- return fread(buf, 1, size, stream);
+ offset_type absolute_offset;
+ switch (whence)
+ {
+ case SEEK_SET:
+ absolute_offset = offset;
+ break;
+ case SEEK_CUR:
+ absolute_offset = offset + is.GetOffset();
+ break;
+ case SEEK_END:
+ absolute_offset = is.GetSize() + offset;
+ break;
+ default:
+ assert(false);
+ gcc_unreachable();
+ }
+ if (!is.Seek(absolute_offset, IgnoreError()))
+ return 0;
+
+ return is.ReadFull(buf, size, IgnoreError());
}
static long
-get_id3v2_footer_size(FILE *stream, long offset, int whence)
+get_id3v2_footer_size(InputStream &is, long offset, int whence)
{
id3_byte_t buf[ID3_TAG_QUERYSIZE];
- size_t bufsize = fill_buffer(buf, ID3_TAG_QUERYSIZE, stream, offset,
whence);
- if (bufsize == 0) return 0;
- return id3_tag_query(buf, bufsize);
+ if (!fill_buffer(buf, ID3_TAG_QUERYSIZE, is, offset, whence))
+ return 0;
+ return id3_tag_query(buf, ID3_TAG_QUERYSIZE);
}
static struct id3_tag *
-tag_id3_read(FILE *stream, long offset, int whence)
+tag_id3_read(InputStream &is, long offset, int whence)
{
/* It's ok if we get less than we asked for */
id3_byte_t query_buffer[ID3_TAG_QUERYSIZE];
- size_t query_buffer_size = fill_buffer(query_buffer, ID3_TAG_QUERYSIZE,
- stream, offset, whence);
- if (query_buffer_size <= 0)
+ if (!fill_buffer(query_buffer, ID3_TAG_QUERYSIZE, is, offset, whence))
return nullptr;
/* Look for a tag header */
- long tag_size = id3_tag_query(query_buffer, query_buffer_size);
+ long tag_size = id3_tag_query(query_buffer, ID3_TAG_QUERYSIZE);
if (tag_size <= 0) return nullptr;
/* Found a tag. Allocate a buffer and read it in. */
id3_byte_t *tag_buffer = new id3_byte_t[tag_size];
- int tag_buffer_size = fill_buffer(tag_buffer, tag_size,
- stream, offset, whence);
- if (tag_buffer_size < tag_size) {
+ if (!fill_buffer(tag_buffer, tag_size, is, offset, whence)) {
delete[] tag_buffer;
return nullptr;
}
- id3_tag *tag = id3_tag_parse(tag_buffer, tag_buffer_size);
+ id3_tag *tag = id3_tag_parse(tag_buffer, tag_size);
delete[] tag_buffer;
return tag;
}
static struct id3_tag *
-tag_id3_find_from_beginning(FILE *stream)
+tag_id3_find_from_beginning(InputStream &is)
{
- id3_tag *tag = tag_id3_read(stream, 0, SEEK_SET);
+ id3_tag *tag = tag_id3_read(is, 0, SEEK_SET);
if (!tag) {
return nullptr;
} else if (tag_is_id3v1(tag)) {
@@ -458,7 +474,7 @@ tag_id3_find_from_beginning(FILE *stream)
break;
/* Get the tag specified by the SEEK frame */
- id3_tag *seektag = tag_id3_read(stream, seek, SEEK_CUR);
+ id3_tag *seektag = tag_id3_read(is, seek, SEEK_CUR);
if (!seektag || tag_is_id3v1(seektag))
break;
@@ -471,18 +487,18 @@ tag_id3_find_from_beginning(FILE *stream)
}
static struct id3_tag *
-tag_id3_find_from_end(FILE *stream)
+tag_id3_find_from_end(InputStream &is)
{
/* Get an id3v1 tag from the end of file for later use */
- id3_tag *v1tag = tag_id3_read(stream, -128, SEEK_END);
+ id3_tag *v1tag = tag_id3_read(is, -128, SEEK_END);
/* Get the id3v2 tag size from the footer (located before v1tag) */
- int tagsize = get_id3v2_footer_size(stream, (v1tag ? -128 : 0) - 10,
SEEK_END);
+ int tagsize = get_id3v2_footer_size(is, (v1tag ? -128 : 0) - 10,
SEEK_END);
if (tagsize >= 0)
return v1tag;
/* Get the tag which the footer belongs to */
- id3_tag *tag = tag_id3_read(stream, tagsize, SEEK_CUR);
+ id3_tag *tag = tag_id3_read(is, tagsize, SEEK_CUR);
if (!tag)
return v1tag;
@@ -493,11 +509,11 @@ tag_id3_find_from_end(FILE *stream)
}
static struct id3_tag *
-tag_id3_riff_aiff_load(FILE *file)
+tag_id3_riff_aiff_load(InputStream &is)
{
- size_t size = riff_seek_id3(file);
+ size_t size = riff_seek_id3(is);
if (size == 0)
- size = aiff_seek_id3(file);
+ size = aiff_seek_id3(is);
if (size == 0)
return nullptr;
@@ -506,8 +522,7 @@ tag_id3_riff_aiff_load(FILE *file)
return nullptr;
id3_byte_t *buffer = new id3_byte_t[size];
- size_t ret = fread(buffer, size, 1, file);
- if (ret != 1) {
+ if (!is.ReadFull(buffer, size, IgnoreError())) {
LogWarning(id3_domain, "Failed to read RIFF chunk");
delete[] buffer;
return nullptr;
@@ -519,31 +534,31 @@ tag_id3_riff_aiff_load(FILE *file)
}
struct id3_tag *
-tag_id3_load(Path path_fs, Error &error)
+tag_id3_load(InputStream &is, Error &error)
{
- FILE *file = FOpen(path_fs, PATH_LITERAL("rb"));
- if (file == nullptr) {
- error.FormatErrno("Failed to open file %s", path_fs.c_str());
+ is.Lock();
+ if (!is.KnownSize() || !is.IsSeekable())
+ {
+ is.Unlock();
return nullptr;
}
-
- struct id3_tag *tag = tag_id3_find_from_beginning(file);
+ struct id3_tag *tag = tag_id3_find_from_beginning(is);
if (tag == nullptr) {
- tag = tag_id3_riff_aiff_load(file);
+ tag = tag_id3_riff_aiff_load(is);
if (tag == nullptr)
- tag = tag_id3_find_from_end(file);
+ tag = tag_id3_find_from_end(is);
}
-
- fclose(file);
+ is.Rewind(error);
+ is.Unlock();
return tag;
}
bool
-tag_id3_scan(Path path_fs,
+tag_id3_scan(InputStream &is,
const struct tag_handler *handler, void *handler_ctx)
{
Error error;
- struct id3_tag *tag = tag_id3_load(path_fs, error);
+ struct id3_tag *tag = tag_id3_load(is, error);
if (tag == nullptr) {
if (error.IsDefined())
LogError(error);
@@ -555,3 +570,19 @@ tag_id3_scan(Path path_fs,
id3_tag_delete(tag);
return true;
}
+
+bool
+tag_id3_scan(Path path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ Mutex mutex;
+ Cond cond;
+ InputStream *is = InputStream::OpenReady(path_fs.c_str(), mutex,
+ cond, IgnoreError());
+ if (is == nullptr)
+ return false;
+
+ bool success = tag_id3_scan(*is, handler, handler_ctx);
+ delete is;
+ return success;
+}
diff --git a/src/tag/TagId3.hxx b/src/tag/TagId3.hxx
index 94dfb17..d3b987b 100644
--- a/src/tag/TagId3.hxx
+++ b/src/tag/TagId3.hxx
@@ -22,6 +22,7 @@
#include "check.h"
#include "Compiler.h"
+#include "input/InputStream.hxx"
class Path;
struct tag_handler;
@@ -32,6 +33,10 @@ class Error;
#ifdef ENABLE_ID3TAG
bool
+tag_id3_scan(InputStream &is,
+ const tag_handler *handler, void *handler_ctx);
+
+bool
tag_id3_scan(Path path_fs,
const tag_handler *handler, void *handler_ctx);
@@ -46,7 +51,7 @@ tag_id3_import(id3_tag *);
* Error will be set)
*/
struct id3_tag *
-tag_id3_load(Path path_fs, Error &error);
+tag_id3_load(InputStream &is, Error &error);
/**
* Import all tags from the provided id3_tag *tag
@@ -58,10 +63,8 @@ scan_id3_tag(id3_tag *tag,
#else
-#include "fs/Path.hxx"
-
static inline bool
-tag_id3_scan(gcc_unused Path path_fs,
+tag_id3_scan(gcc_unused InputStream &is,
gcc_unused const tag_handler *handler,
gcc_unused void *handler_ctx)
{
--
2.1.4
_______________________________________________
mpd-devel mailing list
[email protected]
http://mailman.blarg.de/listinfo/mpd-devel