aboutsummaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Alloc.cxx86
-rw-r--r--src/util/Alloc.hxx21
-rw-r--r--src/util/ByteReverse.cxx2
-rw-r--r--src/util/ByteReverse.hxx2
-rw-r--r--src/util/Cast.hxx4
-rw-r--r--src/util/DivideString.cxx48
-rw-r--r--src/util/DivideString.hxx75
-rw-r--r--src/util/Error.cxx8
-rw-r--r--src/util/FormatString.cxx2
-rw-r--r--src/util/FormatString.hxx2
-rw-r--r--src/util/LazyRandomEngine.cxx2
-rw-r--r--src/util/LazyRandomEngine.hxx2
-rw-r--r--src/util/Manual.hxx37
-rw-r--r--src/util/OptionDef.hxx2
-rw-r--r--src/util/OptionParser.cxx2
-rw-r--r--src/util/OptionParser.hxx2
-rw-r--r--src/util/PeakBuffer.cxx2
-rw-r--r--src/util/PeakBuffer.hxx2
-rw-r--r--src/util/RefCount.hxx2
-rw-r--r--src/util/SliceBuffer.hxx2
-rw-r--r--src/util/SplitString.cxx44
-rw-r--r--src/util/SplitString.hxx58
-rw-r--r--src/util/StringUtil.cxx22
-rw-r--r--src/util/StringUtil.hxx10
-rw-r--r--src/util/UriUtil.cxx2
-rw-r--r--src/util/UriUtil.hxx2
-rw-r--r--src/util/bit_reverse.c2
-rw-r--r--src/util/bit_reverse.h2
-rw-r--r--src/util/format.c252
-rw-r--r--src/util/format.h51
30 files changed, 655 insertions, 95 deletions
diff --git a/src/util/Alloc.cxx b/src/util/Alloc.cxx
index ec3579470..d4941f88b 100644
--- a/src/util/Alloc.cxx
+++ b/src/util/Alloc.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -74,3 +74,87 @@ xstrndup(const char *s, size_t n)
return p;
}
+
+#if CLANG_OR_GCC_VERSION(4,7)
+
+template<typename... Args>
+static inline size_t
+FillLengths(size_t *lengths, const char *a, Args&&... args)
+{
+ return FillLengths(lengths, a) + FillLengths(lengths + 1, args...);
+}
+
+template<>
+inline size_t
+FillLengths(size_t *lengths, const char *a)
+{
+ return *lengths = strlen(a);
+}
+
+template<typename... Args>
+static inline void
+StringCat(char *p, const size_t *lengths, const char *a, Args&&... args)
+{
+ StringCat(p, lengths, a);
+ StringCat(p + *lengths, lengths + 1, args...);
+}
+
+template<>
+inline void
+StringCat(char *p, const size_t *lengths, const char *a)
+{
+ memcpy(p, a, *lengths);
+}
+
+#endif
+
+template<typename... Args>
+gcc_malloc gcc_nonnull_all
+static inline char *
+t_xstrcatdup(Args&&... args)
+{
+#if CLANG_OR_GCC_VERSION(4,7)
+ constexpr size_t n = sizeof...(args);
+
+ size_t lengths[n];
+ const size_t total = FillLengths(lengths, args...);
+
+ char *p = (char *)xalloc(total + 1);
+ StringCat(p, lengths, args...);
+ p[total] = 0;
+ return p;
+#else
+ /* fallback implementation for gcc 4.6, because that old
+ compiler is too buggy to compile the above template
+ functions */
+ const char *const argv[] = { args... };
+
+ size_t total = 0;
+ for (auto i : argv)
+ total += strlen(i);
+
+ char *p = (char *)xalloc(total + 1), *q = p;
+ for (auto i : argv)
+ q = stpcpy(q, i);
+
+ return p;
+#endif
+}
+
+char *
+xstrcatdup(const char *a, const char *b)
+{
+ return t_xstrcatdup(a, b);
+}
+
+char *
+xstrcatdup(const char *a, const char *b, const char *c)
+{
+ return t_xstrcatdup(a, b, c);
+}
+
+char *
+xstrcatdup(const char *a, const char *b, const char *c, const char *d)
+{
+ return t_xstrcatdup(a, b, c, d);
+}
diff --git a/src/util/Alloc.hxx b/src/util/Alloc.hxx
index 15c123b7a..9e1007e69 100644
--- a/src/util/Alloc.hxx
+++ b/src/util/Alloc.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -64,4 +64,23 @@ gcc_malloc gcc_nonnull_all
char *
xstrndup(const char *s, size_t n);
+/**
+ * Concatenate two strings, returning a new allocation. Use free() to
+ * free it.
+ *
+ * This function never fails; in out-of-memory situations, it aborts
+ * the process.
+ */
+gcc_malloc gcc_nonnull_all
+char *
+xstrcatdup(const char *a, const char *b);
+
+gcc_malloc gcc_nonnull_all
+char *
+xstrcatdup(const char *a, const char *b, const char *c);
+
+gcc_malloc gcc_nonnull_all
+char *
+xstrcatdup(const char *a, const char *b, const char *c, const char *d);
+
#endif
diff --git a/src/util/ByteReverse.cxx b/src/util/ByteReverse.cxx
index 5cc8692a7..c0c2946ca 100644
--- a/src/util/ByteReverse.cxx
+++ b/src/util/ByteReverse.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/ByteReverse.hxx b/src/util/ByteReverse.hxx
index 0c060c0cb..55d6a64db 100644
--- a/src/util/ByteReverse.hxx
+++ b/src/util/ByteReverse.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/Cast.hxx b/src/util/Cast.hxx
index 887137da4..647171970 100644
--- a/src/util/Cast.hxx
+++ b/src/util/Cast.hxx
@@ -84,7 +84,7 @@ ContainerAttributeOffset(const A C::*p)
* Cast the given pointer to a struct member to its parent structure.
*/
template<class C, class A>
-#if defined(__clang__) || GCC_CHECK_VERSION(4,7)
+#if CLANG_OR_GCC_VERSION(4,7)
constexpr
#endif
static inline C &
@@ -97,7 +97,7 @@ ContainerCast(A &a, A C::*member)
* Cast the given pointer to a struct member to its parent structure.
*/
template<class C, class A>
-#if defined(__clang__) || GCC_CHECK_VERSION(4,7)
+#if CLANG_OR_GCC_VERSION(4,7)
constexpr
#endif
static inline const C &
diff --git a/src/util/DivideString.cxx b/src/util/DivideString.cxx
new file mode 100644
index 000000000..8e3d5a1b8
--- /dev/null
+++ b/src/util/DivideString.cxx
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2003-2015 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 "DivideString.hxx"
+#include "StringUtil.hxx"
+
+#include <string.h>
+
+DivideString::DivideString(const char *s, char separator, bool strip)
+ :first(nullptr)
+{
+ const char *x = strchr(s, separator);
+ if (x == nullptr)
+ return;
+
+ size_t length = x - s;
+ second = x + 1;
+
+ if (strip)
+ second = StripLeft(second);
+
+ if (strip) {
+ const char *end = s + length;
+ s = StripLeft(s);
+ end = StripRight(s, end);
+ length = end - s;
+ }
+
+ first = new char[length + 1];
+ memcpy(first, s, length);
+ first[length] = 0;
+}
diff --git a/src/util/DivideString.hxx b/src/util/DivideString.hxx
new file mode 100644
index 000000000..d98b512a6
--- /dev/null
+++ b/src/util/DivideString.hxx
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2003-2015 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_DIVIDE_STRING_HXX
+#define MPD_DIVIDE_STRING_HXX
+
+#include "Compiler.h"
+
+#include <assert.h>
+
+/**
+ * Split a given constant string at a separator character. Duplicates
+ * the first part to be able to null-terminate it.
+ */
+class DivideString {
+ char *first;
+ const char *second;
+
+public:
+ /**
+ * @param strip strip the first part and left-strip the second
+ * part?
+ */
+ DivideString(const char *s, char separator, bool strip=false);
+
+ ~DivideString() {
+ delete[] first;
+ }
+
+ /**
+ * Was the separator found?
+ */
+ bool IsDefined() const {
+ return first != nullptr;
+ }
+
+ /**
+ * Is the first part empty?
+ */
+ bool IsEmpty() const {
+ assert(IsDefined());
+
+ return *first == 0;
+ }
+
+ const char *GetFirst() const {
+ assert(IsDefined());
+
+ return first;
+ }
+
+ const char *GetSecond() const {
+ assert(IsDefined());
+
+ return second;
+ }
+};
+
+#endif
diff --git a/src/util/Error.cxx b/src/util/Error.cxx
index 92b2cc5d0..67a1b03fd 100644
--- a/src/util/Error.cxx
+++ b/src/util/Error.cxx
@@ -32,7 +32,7 @@
#include "Domain.hxx"
#ifdef WIN32
-#include <glib.h>
+#include <windows.h>
#endif
#include <errno.h>
@@ -135,7 +135,11 @@ Error::FormatErrno(const char *fmt, ...)
void
Error::SetLastError(DWORD _code, const char *prefix)
{
- const char *msg = g_win32_error_message(_code);
+ char msg[256];
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, _code, 0, msg, sizeof(msg), nullptr);
+
Format(win32_domain, int(_code), "%s: %s", prefix, msg);
}
diff --git a/src/util/FormatString.cxx b/src/util/FormatString.cxx
index d222a505c..5ada067cb 100644
--- a/src/util/FormatString.cxx
+++ b/src/util/FormatString.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/FormatString.hxx b/src/util/FormatString.hxx
index dc1ac3c67..b0f8dd7f9 100644
--- a/src/util/FormatString.hxx
+++ b/src/util/FormatString.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/LazyRandomEngine.cxx b/src/util/LazyRandomEngine.cxx
index b0aac913c..abd83da8c 100644
--- a/src/util/LazyRandomEngine.cxx
+++ b/src/util/LazyRandomEngine.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/LazyRandomEngine.hxx b/src/util/LazyRandomEngine.hxx
index 4156b3bb1..7b9b1c655 100644
--- a/src/util/LazyRandomEngine.hxx
+++ b/src/util/LazyRandomEngine.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/Manual.hxx b/src/util/Manual.hxx
index 75cffac06..6ba932bdd 100644
--- a/src/util/Manual.hxx
+++ b/src/util/Manual.hxx
@@ -41,7 +41,7 @@
#include <assert.h>
-#if defined(__clang__) || GCC_CHECK_VERSION(4,7)
+#if CLANG_OR_GCC_VERSION(4,7)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
@@ -54,12 +54,7 @@
*/
template<class T>
class Manual {
-#if GCC_OLDER_THAN(4,8)
- /* no alignas() on gcc < 4.8: apply worst-case fallback */
- __attribute__((aligned(8)))
-#else
- alignas(T)
-#endif
+ gcc_alignas(T, 8)
char data[sizeof(T)];
#ifndef NDEBUG
@@ -89,32 +84,46 @@ public:
void Destruct() {
assert(initialized);
- T *t = (T *)data;
- t->T::~T();
+ T &t = Get();
+ t.T::~T();
#ifndef NDEBUG
initialized = false;
#endif
}
+ T &Get() {
+ assert(initialized);
+
+ void *p = static_cast<void *>(data);
+ return *static_cast<T *>(p);
+ }
+
+ const T &Get() const {
+ assert(initialized);
+
+ const void *p = static_cast<const void *>(data);
+ return *static_cast<const T *>(p);
+ }
+
operator T &() {
- return *(T *)data;
+ return Get();
}
operator const T &() const {
- return *(const T *)data;
+ return Get();
}
T *operator->() {
- return (T *)data;
+ return &Get();
}
const T *operator->() const {
- return (T *)data;
+ return &Get();
}
};
-#if defined(__clang__) || GCC_VERSION >= 40700
+#if CLANG_OR_GCC_VERSION(4,7)
#pragma GCC diagnostic pop
#endif
diff --git a/src/util/OptionDef.hxx b/src/util/OptionDef.hxx
index dd82154c4..29b7e268b 100644
--- a/src/util/OptionDef.hxx
+++ b/src/util/OptionDef.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/OptionParser.cxx b/src/util/OptionParser.cxx
index b10008527..45be084c9 100644
--- a/src/util/OptionParser.cxx
+++ b/src/util/OptionParser.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/OptionParser.hxx b/src/util/OptionParser.hxx
index b9d34adbb..c6c794a7d 100644
--- a/src/util/OptionParser.hxx
+++ b/src/util/OptionParser.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/PeakBuffer.cxx b/src/util/PeakBuffer.cxx
index e4624bbec..da3b275d8 100644
--- a/src/util/PeakBuffer.cxx
+++ b/src/util/PeakBuffer.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/PeakBuffer.hxx b/src/util/PeakBuffer.hxx
index 702a3dee0..784b3cdbd 100644
--- a/src/util/PeakBuffer.hxx
+++ b/src/util/PeakBuffer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/RefCount.hxx b/src/util/RefCount.hxx
index 02ef8818c..c6cf2e41f 100644
--- a/src/util/RefCount.hxx
+++ b/src/util/RefCount.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx
index 63ca087ae..16c1cf744 100644
--- a/src/util/SliceBuffer.hxx
+++ b/src/util/SliceBuffer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/SplitString.cxx b/src/util/SplitString.cxx
index 75e799279..9588312f8 100644
--- a/src/util/SplitString.cxx
+++ b/src/util/SplitString.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,42 @@
*/
#include "SplitString.hxx"
+#include "StringUtil.hxx"
#include <string.h>
-SplitString::SplitString(const char *s, char separator)
- :first(nullptr)
+std::forward_list<std::string>
+SplitString(const char *s, char separator, bool strip)
{
- const char *x = strchr(s, separator);
- if (x == nullptr)
- return;
+ if (strip)
+ s = StripLeft(s);
- size_t length = x - s;
- second = x + 1;
+ std::forward_list<std::string> list;
+ if (*s == 0)
+ return list;
- first = new char[length + 1];
- memcpy(first, s, length);
- first[length] = 0;
+ auto i = list.before_begin();
+
+ while (true) {
+ const char *next = strchr(s, separator);
+ if (next == nullptr)
+ break;
+
+ const char *end = next++;
+ if (strip)
+ end = StripRight(s, end);
+
+ i = list.emplace_after(i, s, end);
+
+ s = next;
+ if (strip)
+ s = StripLeft(s);
+ }
+
+ const char *end = s + strlen(s);
+ if (strip)
+ end = StripRight(s, end);
+
+ list.emplace_after(i, s, end);
+ return list;
}
diff --git a/src/util/SplitString.hxx b/src/util/SplitString.hxx
index 96ffb21ec..545470c7a 100644
--- a/src/util/SplitString.hxx
+++ b/src/util/SplitString.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,52 +20,20 @@
#ifndef MPD_SPLIT_STRING_HXX
#define MPD_SPLIT_STRING_HXX
-#include "Compiler.h"
-
-#include <assert.h>
+#include <forward_list>
+#include <string>
/**
- * Split a given constant string at a separator character. Duplicates
- * the first part to be able to null-terminate it.
+ * Split a string at a certain separator character into sub strings
+ * and returns a list of these.
+ *
+ * Two consecutive separator characters result in an empty string in
+ * the list.
+ *
+ * An empty input string, as a special case, results in an empty list
+ * (and not a list with an empty string).
*/
-class SplitString {
- char *first;
- const char *second;
-
-public:
- SplitString(const char *s, char separator);
-
- ~SplitString() {
- delete[] first;
- }
-
- /**
- * Was the separator found?
- */
- bool IsDefined() const {
- return first != nullptr;
- }
-
- /**
- * Is the first part empty?
- */
- bool IsEmpty() const {
- assert(IsDefined());
-
- return *first == 0;
- }
-
- const char *GetFirst() const {
- assert(IsDefined());
-
- return first;
- }
-
- const char *GetSecond() const {
- assert(IsDefined());
-
- return second;
- }
-};
+std::forward_list<std::string>
+SplitString(const char *s, char separator, bool strip=true);
#endif
diff --git a/src/util/StringUtil.cxx b/src/util/StringUtil.cxx
index bcade2b3b..60734ad8d 100644
--- a/src/util/StringUtil.cxx
+++ b/src/util/StringUtil.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -120,3 +120,23 @@ string_array_contains(const char *const* haystack, const char *needle)
return false;
}
+
+void
+ToUpperASCII(char *dest, const char *src, size_t size)
+{
+ assert(dest != nullptr);
+ assert(src != nullptr);
+ assert(size > 1);
+
+ char *const end = dest + size - 1;
+
+ do {
+ char ch = *src++;
+ if (ch == 0)
+ break;
+
+ *dest++ = ToUpperASCII(ch);
+ } while (dest < end);
+
+ *dest = 0;
+}
diff --git a/src/util/StringUtil.hxx b/src/util/StringUtil.hxx
index 9beda5441..9f2dab935 100644
--- a/src/util/StringUtil.hxx
+++ b/src/util/StringUtil.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -114,4 +114,12 @@ gcc_pure
bool
string_array_contains(const char *const* haystack, const char *needle);
+/**
+ * Convert the specified ASCII string (0x00..0x7f) to upper case.
+ *
+ * @param size the destination buffer size
+ */
+void
+ToUpperASCII(char *dest, const char *src, size_t size);
+
#endif
diff --git a/src/util/UriUtil.cxx b/src/util/UriUtil.cxx
index 54d0ded77..0782304e3 100644
--- a/src/util/UriUtil.cxx
+++ b/src/util/UriUtil.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/UriUtil.hxx b/src/util/UriUtil.hxx
index d478d5b92..e8edfb5a0 100644
--- a/src/util/UriUtil.hxx
+++ b/src/util/UriUtil.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/bit_reverse.c b/src/util/bit_reverse.c
index 9226c4261..271eee166 100644
--- a/src/util/bit_reverse.c
+++ b/src/util/bit_reverse.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/bit_reverse.h b/src/util/bit_reverse.h
index b39b02e92..f4d378e61 100644
--- a/src/util/bit_reverse.h
+++ b/src/util/bit_reverse.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/util/format.c b/src/util/format.c
new file mode 100644
index 000000000..d360df3dc
--- /dev/null
+++ b/src/util/format.c
@@ -0,0 +1,252 @@
+/*
+ * music player command (mpc)
+ * Copyright (C) 2003-2015 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 "format.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/**
+ * Reallocate the given string and append the source string.
+ */
+gcc_malloc
+static char *
+string_append(char *dest, const char *src, size_t len)
+{
+ size_t destlen = dest != NULL
+ ? strlen(dest)
+ : 0;
+
+ dest = realloc(dest, destlen + len + 1);
+ memcpy(dest + destlen, src, len);
+ dest[destlen + len] = '\0';
+
+ return dest;
+}
+
+/**
+ * Skip the format string until the current group is closed by either
+ * '&', '|' or ']' (supports nesting).
+ */
+gcc_pure
+static const char *
+skip_format(const char *p)
+{
+ unsigned stack = 0;
+
+ while (*p != '\0') {
+ if (*p == '[')
+ stack++;
+ else if (*p == '#' && p[1] != '\0')
+ /* skip escaped stuff */
+ ++p;
+ else if (stack > 0) {
+ if (*p == ']')
+ --stack;
+ } else if (*p == '&' || *p == '|' || *p == ']')
+ break;
+
+ ++p;
+ }
+
+ return p;
+}
+
+static char *
+format_object2(const char *format, const char **last, const void *object,
+ const char *(*getter)(const void *object, const char *name))
+{
+ char *ret = NULL;
+ const char *p;
+ bool found = false;
+
+ for (p = format; *p != '\0';) {
+ switch (p[0]) {
+ case '|':
+ ++p;
+ if (!found) {
+ /* nothing found yet: try the next
+ section */
+ free(ret);
+ ret = NULL;
+ } else
+ /* already found a value: skip the
+ next section */
+ p = skip_format(p);
+ break;
+
+ case '&':
+ ++p;
+ if (!found)
+ /* nothing found yet, so skip this
+ section */
+ p = skip_format(p);
+ else
+ /* we found something yet, but it will
+ only be used if the next section
+ also found something, so reset the
+ flag */
+ found = false;
+ break;
+
+ case '[': {
+ char *t = format_object2(p + 1, &p, object, getter);
+ if (t != NULL) {
+ ret = string_append(ret, t, strlen(t));
+ free(t);
+ found = true;
+ }
+ }
+ break;
+
+ case ']':
+ if (last != NULL)
+ *last = p + 1;
+ if (!found) {
+ free(ret);
+ ret = NULL;
+ }
+ return ret;
+
+ case '\\': {
+ /* take care of escape sequences */
+ char ltemp;
+ switch (p[1]) {
+ case 'a':
+ ltemp = '\a';
+ break;
+
+ case 'b':
+ ltemp = '\b';
+ break;
+
+ case 't':
+ ltemp = '\t';
+ break;
+
+ case 'n':
+ ltemp = '\n';
+ break;
+
+ case 'v':
+ ltemp = '\v';
+ break;
+
+ case 'f':
+ ltemp = '\f';
+ break;
+
+ case 'r':
+ ltemp = '\r';
+ break;
+
+ case '[':
+ case ']':
+ ltemp = p[1];
+ break;
+
+ default:
+ /* unknown escape: copy the
+ backslash */
+ ltemp = p[0];
+ --p;
+ break;
+ }
+
+ ret = string_append(ret, &ltemp, 1);
+ p += 2;
+ }
+ break;
+
+ case '%': {
+ /* find the extent of this format specifier
+ (stop at \0, ' ', or esc) */
+ const char *end = p + 1;
+ while (*end >= 'a' && *end <= 'z')
+ ++end;
+
+ const size_t length = end - p + 1;
+
+ if (*end != '%') {
+ ret = string_append(ret, p, length - 1);
+ p = end;
+ continue;
+ }
+
+ char name[32];
+ if (length > (int)sizeof(name)) {
+ ret = string_append(ret, p, length);
+ p = end + 1;
+ continue;
+ }
+
+ memcpy(name, p + 1, length - 2);
+ name[length - 2] = 0;
+
+ const char *value = getter(object, name);
+ size_t value_length;
+ if (value != NULL) {
+ if (*value != 0)
+ found = true;
+ value_length = strlen(value);
+ } else {
+ /* unknown variable: copy verbatim
+ from format string */
+ value = p;
+ value_length = length;
+ }
+
+ ret = string_append(ret, value, value_length);
+
+ /* advance past the specifier */
+ p = end + 1;
+ }
+ break;
+
+ case '#':
+ /* let the escape character escape itself */
+ if (p[1] != '\0') {
+ ret = string_append(ret, p + 1, 1);
+ p += 2;
+ break;
+ }
+
+ /* fall through */
+
+ default:
+ /* pass-through non-escaped portions of the format string */
+ ret = string_append(ret, p, 1);
+ ++p;
+ }
+ }
+
+ if (last != NULL)
+ *last = p;
+ return ret;
+}
+
+char *
+format_object(const char *format, const void *object,
+ const char *(*getter)(const void *object, const char *name))
+{
+ return format_object2(format, NULL, object, getter);
+}
diff --git a/src/util/format.h b/src/util/format.h
new file mode 100644
index 000000000..fa3624b51
--- /dev/null
+++ b/src/util/format.h
@@ -0,0 +1,51 @@
+/*
+ * music player command (mpc)
+ * Copyright (C) 2003-2015 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 MPC_FORMAT_H
+#define MPC_FORMAT_H
+
+#include "Compiler.h"
+
+struct mpd_song;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Pretty-print an object into a string using the given format
+ * specification.
+ *
+ * @param format the format string
+ * @param object the object
+ * @param getter a getter function that extracts a value from the object
+ * @return the resulting string to be freed by free(); NULL if
+ * no format string group produced any output
+ */
+gcc_malloc
+char *
+format_object(const char *format, const void *object,
+ const char *(*getter)(const void *object, const char *name));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif