aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2014-08-11 22:08:26 +0200
committerMax Kellermann <max@duempel.org>2014-08-11 22:38:58 +0200
commit5d2506e69713cd8fd6086fd574b79e9de5364a4c (patch)
tree0b67fa0d4c85bf72174869db802d22cf19290bbc /src
parent14908b7f285305fe4a1e0a14f8be5751027d8164 (diff)
downloadmpd-5d2506e69713cd8fd6086fd574b79e9de5364a4c.tar.gz
mpd-5d2506e69713cd8fd6086fd574b79e9de5364a4c.tar.xz
mpd-5d2506e69713cd8fd6086fd574b79e9de5364a4c.zip
SongFilter: new filter "modified-since"
Diffstat (limited to 'src')
-rw-r--r--src/SongFilter.cxx75
-rw-r--r--src/SongFilter.hxx8
2 files changed, 83 insertions, 0 deletions
diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
index 7a8e6fd12..794cb9208 100644
--- a/src/SongFilter.cxx
+++ b/src/SongFilter.cxx
@@ -48,6 +48,9 @@ locate_parse_type(const char *str)
if (strcmp(str, "base") == 0)
return LOCATE_TAG_BASE_TYPE;
+ if (strcmp(str, "modified-since") == 0)
+ return LOCATE_TAG_MODIFIED_SINCE;
+
return tag_name_parse_i(str);
}
@@ -66,6 +69,11 @@ SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
{
}
+SongFilter::Item::Item(unsigned _tag, time_t _time)
+ :tag(_tag), time(_time)
+{
+}
+
bool
SongFilter::Item::StringMatch(const char *s) const
{
@@ -128,6 +136,9 @@ SongFilter::Item::Match(const DetachedSong &song) const
if (tag == LOCATE_TAG_BASE_TYPE)
return uri_is_child_or_same(value.c_str(), song.GetURI());
+ if (tag == LOCATE_TAG_MODIFIED_SINCE)
+ return song.GetLastModified() >= time;
+
if (tag == LOCATE_TAG_FILE_TYPE)
return StringMatch(song.GetURI());
@@ -142,6 +153,9 @@ SongFilter::Item::Match(const LightSong &song) const
return uri_is_child_or_same(value.c_str(), uri.c_str());
}
+ if (tag == LOCATE_TAG_MODIFIED_SINCE)
+ return song.mtime >= time;
+
if (tag == LOCATE_TAG_FILE_TYPE) {
const auto uri = song.GetURI();
return StringMatch(uri.c_str());
@@ -160,6 +174,58 @@ SongFilter::~SongFilter()
/* this destructor exists here just so it won't get inlined */
}
+#if !defined(__GLIBC__) && !defined(WIN32)
+
+/**
+ * Determine the time zone offset in a portable way.
+ */
+gcc_const
+static time_t
+GetTimeZoneOffset()
+{
+ time_t t = 1234567890;
+ struct tm tm;
+ tm.tm_isdst = 0;
+ gmtime_r(&t, &tm);
+ return t - mktime(&tm);
+}
+
+#endif
+
+gcc_pure
+static time_t
+ParseTimeStamp(const char *s)
+{
+ assert(s != nullptr);
+
+ char *endptr;
+ unsigned long long value = strtoull(s, &endptr, 10);
+ if (*endptr == 0 && endptr > s)
+ /* it's an integral UNIX time stamp */
+ return (time_t)value;
+
+#ifdef WIN32
+ /* TODO: emulate strptime()? */
+ return 0;
+#else
+ /* try ISO 8601 */
+
+ struct tm tm;
+ const char *end = strptime(s, "%FT%TZ", &tm);
+ if (end == nullptr || *end != 0)
+ return 0;
+
+#ifdef __GLIBC__
+ /* timegm() is a GNU extension */
+ return timegm(&tm);
+#else
+ tm.tm_isdst = 0;
+ return mktime(&tm) + GetTimeZoneOffset();
+#endif /* !__GLIBC__ */
+
+#endif /* !WIN32 */
+}
+
bool
SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
{
@@ -175,6 +241,15 @@ SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
fold_case = false;
}
+ if (tag == LOCATE_TAG_MODIFIED_SINCE) {
+ time_t t = ParseTimeStamp(value);
+ if (t == 0)
+ return false;
+
+ items.push_back(Item(tag, t));
+ return true;
+ }
+
items.push_back(Item(tag, value, fold_case));
return true;
}
diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx
index ca7d7bd90..f51bd85c6 100644
--- a/src/SongFilter.hxx
+++ b/src/SongFilter.hxx
@@ -26,11 +26,13 @@
#include <string>
#include <stdint.h>
+#include <time.h>
/**
* Limit the search to files within the given directory.
*/
#define LOCATE_TAG_BASE_TYPE (TAG_NUM_OF_ITEM_TYPES + 1)
+#define LOCATE_TAG_MODIFIED_SINCE (TAG_NUM_OF_ITEM_TYPES + 2)
#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
@@ -51,9 +53,15 @@ public:
std::string value;
+ /**
+ * For #LOCATE_TAG_MODIFIED_SINCE
+ */
+ time_t time;
+
public:
gcc_nonnull(3)
Item(unsigned tag, const char *value, bool fold_case=false);
+ Item(unsigned tag, time_t time);
Item(const Item &other) = delete;
Item(Item &&) = default;