diff options
Diffstat (limited to 'src/util')
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, <emp, 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 |