Hello community, here is the log from the commit of package mpd for openSUSE:Factory checked in at 2019-12-28 13:40:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mpd (Old) and /work/SRC/openSUSE:Factory/.mpd.new.6675 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "mpd" Sat Dec 28 13:40:46 2019 rev:15 rq:759768 version:0.21.18 Changes: -------- --- /work/SRC/openSUSE:Factory/mpd/mpd.changes 2019-12-21 12:29:49.247318709 +0100 +++ /work/SRC/openSUSE:Factory/.mpd.new.6675/mpd.changes 2019-12-28 13:40:56.998946016 +0100 @@ -1,0 +2,14 @@ +Sat Dec 28 10:42:29 UTC 2019 - Илья Индиго <ilya@ilya.pp.ua> + +- Update to 0.21.18 + * https://raw.githubusercontent.com/MusicPlayerDaemon/MPD/v0.21.18/NEWS + * protocol + * work around Mac OS X bug in the ISO 8601 parser + * output + * alsa: fix hang bug with ALSA "null" outputs + * storage + * curl: fix crash bug + * drop support for CURL versions older than 7.32.0 + * reduce unnecessary CPU wakeups + +------------------------------------------------------------------- Old: ---- mpd-0.21.17.tar.xz New: ---- mpd-0.21.18.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mpd.spec ++++++ --- /var/tmp/diff_new_pack.94Uwgg/_old 2019-12-28 13:40:57.818946430 +0100 +++ /var/tmp/diff_new_pack.94Uwgg/_new 2019-12-28 13:40:57.846946444 +0100 @@ -20,7 +20,7 @@ %bcond_with faad %bcond_without mpd_iso9660 Name: mpd -Version: 0.21.17 +Version: 0.21.18 Release: 0 Summary: Music Player Daemon License: GPL-2.0-or-later ++++++ mpd-0.21.17.tar.xz -> mpd-0.21.18.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/.travis.yml new/mpd-0.21.18/.travis.yml --- old/mpd-0.21.17/.travis.yml 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/.travis.yml 2019-12-24 16:13:16.000000000 +0100 @@ -2,6 +2,28 @@ matrix: include: + # Ubuntu Bionic (18.04) with GCC 7 + - os: linux + dist: bionic + addons: + apt: + sources: + - sourceline: 'ppa:deadsnakes/ppa' # for Python 3.7 (required by Meson) + packages: + - libgtest-dev + - libboost-dev + - python3.6 + - python3-urllib3 + - ninja-build + before_install: + - wget https://bootstrap.pypa.io/get-pip.py + - /usr/bin/python3.6 get-pip.py --user + install: + - /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson + env: + - MATRIX_EVAL="export PATH=\$HOME/.local/bin:\$PATH" + + # Ubuntu Trusty (16.04) with GCC 6 - os: linux dist: trusty addons: @@ -25,8 +47,9 @@ - /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson env: # use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068 - - MATRIX_EVAL="export CC=gcc-6 CXX=g++-6 LDFLAGS=-fuse-ld=gold PATH=$HOME/.local/bin:$PATH" + - MATRIX_EVAL="export CC='ccache gcc-6' CXX='ccache g++-6' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH" + # Ubuntu Trusty (16.04) with GCC 8 - os: linux dist: trusty addons: @@ -50,25 +73,37 @@ - /usr/bin/python3.6 $HOME/.local/bin/pip install --user meson env: # use gold as workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17068 - - MATRIX_EVAL="export CC=gcc-8 CXX=g++-8 LDFLAGS=-fuse-ld=gold PATH=$HOME/.local/bin:$PATH" + - MATRIX_EVAL="export CC='ccache gcc-8' CXX='ccache g++-8' LDFLAGS=-fuse-ld=gold PATH=\$HOME/.local/bin:\$PATH" - os: osx - osx_image: xcode9.3beta + osx_image: xcode9.4 + addons: + homebrew: + packages: + - ccache + - meson env: - - MATRIX_EVAL="" + - MATRIX_EVAL="export PATH=/usr/local/opt/ccache/libexec:$PATH HOMEBREW_NO_ANALYTICS=1" cache: - - apt - - ccache + apt: true + ccache: true + directories: + - $HOME/Library/Caches/Homebrew + +before_cache: + - test "$TRAVIS_OS_NAME" != "osx" || brew cleanup before_install: - eval "${MATRIX_EVAL}" - # C++14 - - test "$TRAVIS_OS_NAME" != "osx" || brew update install: # C++14 - - test "$TRAVIS_OS_NAME" != "osx" || brew install ccache meson + + # Work around "Target /usr/local/lib/libgtest.a is a symlink + # belonging to nss. You can unlink it" during gtest install + - test "$TRAVIS_OS_NAME" != "osx" || brew unlink nss + - test "$TRAVIS_OS_NAME" != "osx" || brew install --HEAD https://gist.githubusercontent.com/Kronuz/96ac10fbd8472eb1e7566d740c4034f8/r... before_script: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/NEWS new/mpd-0.21.18/NEWS --- old/mpd-0.21.17/NEWS 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/NEWS 2019-12-24 16:13:16.000000000 +0100 @@ -1,3 +1,13 @@ +ver 0.21.18 (2019/12/24) +* protocol + - work around Mac OS X bug in the ISO 8601 parser +* output + - alsa: fix hang bug with ALSA "null" outputs +* storage + - curl: fix crash bug +* drop support for CURL versions older than 7.32.0 +* reduce unnecessary CPU wakeups + ver 0.21.17 (2019/12/16) * protocol - relax the ISO 8601 parser: allow omitting field separators, the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/android/AndroidManifest.xml new/mpd-0.21.18/android/AndroidManifest.xml --- old/mpd-0.21.17/android/AndroidManifest.xml 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/android/AndroidManifest.xml 2019-12-24 16:13:16.000000000 +0100 @@ -2,8 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.musicpd" android:installLocation="auto" - android:versionCode="40" - android:versionName="0.21.17"> + android:versionCode="41" + android:versionName="0.21.18"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/doc/conf.py new/mpd-0.21.18/doc/conf.py --- old/mpd-0.21.17/doc/conf.py 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/doc/conf.py 2019-12-24 16:13:16.000000000 +0100 @@ -38,7 +38,7 @@ # built documents. # # The short X.Y version. -version = '0.21.17' +version = '0.21.18' # The full version, including alpha/beta/rc tags. release = version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/meson.build new/mpd-0.21.18/meson.build --- old/mpd-0.21.17/meson.build 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/meson.build 2019-12-24 16:13:16.000000000 +0100 @@ -1,7 +1,7 @@ project( 'mpd', ['c', 'cpp'], - version: '0.21.17', + version: '0.21.18', meson_version: '>= 0.49.0', default_options: [ 'c_std=c99', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/event/Loop.cxx new/mpd-0.21.18/src/event/Loop.cxx --- old/mpd-0.21.17/src/event/Loop.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/event/Loop.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -137,7 +137,8 @@ ExportTimeoutMS(std::chrono::steady_clock::duration timeout) { return timeout >= timeout.zero() - ? int(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()) + /* round up (+1) to avoid unnecessary wakeups */ + ? int(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()) + 1 : -1; } @@ -220,7 +221,6 @@ } while (!quit); #ifndef NDEBUG - assert(busy); assert(thread.IsInside()); #endif } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/event/MultiSocketMonitor.cxx new/mpd-0.21.18/src/event/MultiSocketMonitor.cxx --- old/mpd-0.21.17/src/event/MultiSocketMonitor.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/event/MultiSocketMonitor.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -22,6 +22,10 @@ #include <algorithm> +#ifdef USE_EPOLL +#include <errno.h> +#endif + #ifndef _WIN32 #include <poll.h> #endif @@ -37,17 +41,42 @@ assert(GetEventLoop().IsInside()); fds.clear(); +#ifdef USE_EPOLL + always_ready_fds.clear(); +#endif IdleMonitor::Cancel(); timeout_event.Cancel(); ready = refresh = false; } +bool +MultiSocketMonitor::AddSocket(SocketDescriptor fd, unsigned events) noexcept +{ + fds.emplace_front(*this, fd); + bool success = fds.front().Schedule(events); + if (!success) { + fds.pop_front(); + +#ifdef USE_EPOLL + if (errno == EPERM) + /* not supported by epoll (e.g. "/dev/null"): + add it to the "always ready" list */ + always_ready_fds.push_front({fd, events}); +#endif + } + + return success; +} + void MultiSocketMonitor::ClearSocketList() noexcept { assert(GetEventLoop().IsInside()); fds.clear(); +#ifdef USE_EPOLL + always_ready_fds.clear(); +#endif } #ifndef _WIN32 @@ -55,6 +84,10 @@ void MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n) noexcept { +#ifdef USE_EPOLL + always_ready_fds.clear(); +#endif + pollfd *const end = pfds + n; UpdateSocketList([pfds, end](SocketDescriptor fd) -> unsigned { @@ -64,9 +97,7 @@ if (i == end) return 0; - auto events = i->events; - i->events = 0; - return events; + return std::exchange(i->events, 0); }); for (auto i = pfds; i != end; ++i) @@ -79,7 +110,20 @@ void MultiSocketMonitor::Prepare() noexcept { - const auto timeout = PrepareSockets(); + auto timeout = PrepareSockets(); + +#ifdef USE_EPOLL + if (!always_ready_fds.empty()) { + /* if there was at least one file descriptor not + supported by epoll, install a very short timeout + because we assume it's always ready */ + constexpr std::chrono::steady_clock::duration ready_timeout = + std::chrono::milliseconds(1); + if (timeout < timeout.zero() || timeout > ready_timeout) + timeout = ready_timeout; + } +#endif + if (timeout >= timeout.zero()) timeout_event.Schedule(timeout); else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/event/MultiSocketMonitor.hxx new/mpd-0.21.18/src/event/MultiSocketMonitor.hxx --- old/mpd-0.21.17/src/event/MultiSocketMonitor.hxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/event/MultiSocketMonitor.hxx 2019-12-24 16:13:16.000000000 +0100 @@ -50,12 +50,10 @@ unsigned revents; public: - SingleFD(MultiSocketMonitor &_multi, SocketDescriptor _fd, - unsigned events) noexcept + SingleFD(MultiSocketMonitor &_multi, + SocketDescriptor _fd) noexcept :SocketMonitor(_fd, _multi.GetEventLoop()), - multi(_multi), revents(0) { - Schedule(events); - } + multi(_multi), revents(0) {} SocketDescriptor GetSocket() const noexcept { return SocketMonitor::GetSocket(); @@ -86,8 +84,6 @@ } }; - friend class SingleFD; - TimerEvent timeout_event; /** @@ -106,6 +102,21 @@ std::forward_list<SingleFD> fds; +#ifdef USE_EPOLL + struct AlwaysReady { + const SocketDescriptor fd; + const unsigned revents; + }; + + /** + * A list of file descriptors which are always ready. This is + * a kludge needed because the ALSA output plugin gives us a + * file descriptor to /dev/null, which is incompatible with + * epoll (epoll_ctl() returns -EPERM). + */ + std::forward_list<AlwaysReady> always_ready_fds; +#endif + public: static constexpr unsigned READ = SocketMonitor::READ; static constexpr unsigned WRITE = SocketMonitor::WRITE; @@ -147,9 +158,7 @@ * * May only be called from PrepareSockets(). */ - void AddSocket(SocketDescriptor fd, unsigned events) noexcept { - fds.emplace_front(*this, fd, events); - } + bool AddSocket(SocketDescriptor fd, unsigned events) noexcept; /** * Remove all sockets. @@ -204,6 +213,11 @@ i.ClearReturnedEvents(); } } + +#ifdef USE_EPOLL + for (const auto &i : always_ready_fds) + f(i.fd, i.revents); +#endif } protected: @@ -232,7 +246,6 @@ void OnTimeout() noexcept { SetReady(); - IdleMonitor::Schedule(); } virtual void OnIdle() noexcept final; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/event/SocketMonitor.cxx new/mpd-0.21.18/src/event/SocketMonitor.cxx --- old/mpd-0.21.17/src/event/SocketMonitor.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/event/SocketMonitor.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -68,20 +68,24 @@ Steal().Close(); } -void +bool SocketMonitor::Schedule(unsigned flags) noexcept { assert(IsDefined()); if (flags == GetScheduledFlags()) - return; + return true; + bool success; if (scheduled_flags == 0) - loop.AddFD(fd.Get(), flags, *this); + success = loop.AddFD(fd.Get(), flags, *this); else if (flags == 0) - loop.RemoveFD(fd.Get(), *this); + success = loop.RemoveFD(fd.Get(), *this); else - loop.ModifyFD(fd.Get(), flags, *this); + success = loop.ModifyFD(fd.Get(), flags, *this); - scheduled_flags = flags; + if (success) + scheduled_flags = flags; + + return success; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/event/SocketMonitor.hxx new/mpd-0.21.18/src/event/SocketMonitor.hxx --- old/mpd-0.21.17/src/event/SocketMonitor.hxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/event/SocketMonitor.hxx 2019-12-24 16:13:16.000000000 +0100 @@ -98,18 +98,22 @@ return scheduled_flags; } - void Schedule(unsigned flags) noexcept; + /** + * @return true on success, false on error (with errno set if + * USE_EPOLL is defined) + */ + bool Schedule(unsigned flags) noexcept; void Cancel() noexcept { Schedule(0); } - void ScheduleRead() noexcept { - Schedule(GetScheduledFlags() | READ | HANGUP | ERROR); + bool ScheduleRead() noexcept { + return Schedule(GetScheduledFlags() | READ | HANGUP | ERROR); } - void ScheduleWrite() noexcept { - Schedule(GetScheduledFlags() | WRITE); + bool ScheduleWrite() noexcept { + return Schedule(GetScheduledFlags() | WRITE); } void CancelRead() noexcept { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/input/plugins/CurlInputPlugin.cxx new/mpd-0.21.18/src/input/plugins/CurlInputPlugin.cxx --- old/mpd-0.21.17/src/input/plugins/CurlInputPlugin.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/input/plugins/CurlInputPlugin.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -180,7 +180,6 @@ { BlockingCall(GetEventLoop(), [this](){ FreeEasy(); - (*curl_init)->InvalidateSockets(); }); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/lib/curl/Global.cxx new/mpd-0.21.18/src/lib/curl/Global.cxx --- old/mpd-0.21.17/src/lib/curl/Global.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/lib/curl/Global.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -181,8 +181,6 @@ assert(easy != nullptr); curl_multi_remove_handle(multi.Get(), easy); - - InvalidateSockets(); } static CurlRequest * @@ -227,12 +225,12 @@ return; } - if (timeout_ms < 10) - /* CURL 7.21.1 likes to report "timeout=0", which + if (timeout_ms < 1) + /* CURL's threaded resolver sets a timeout of 0ms, which means we're running in a busy loop. Quite a bad idea to waste so much CPU. Let's use a lower limit - of 10ms. */ - timeout_ms = 10; + of 1ms. */ + timeout_ms = 1; timeout_event.Schedule(std::chrono::milliseconds(timeout_ms)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/lib/curl/Global.hxx new/mpd-0.21.18/src/lib/curl/Global.hxx --- old/mpd-0.21.17/src/lib/curl/Global.hxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/lib/curl/Global.hxx 2019-12-24 16:13:16.000000000 +0100 @@ -74,16 +74,6 @@ SocketAction(CURL_SOCKET_TIMEOUT, 0); } - /** - * This is a kludge to allow pausing/resuming a stream with - * libcurl < 7.32.0. Read the curl_easy_pause manpage for - * more information. - */ - void ResumeSockets() { - int running_handles; - curl_multi_socket_all(multi.Get(), &running_handles); - } - private: void UpdateTimeout(long timeout_ms) noexcept; static int TimerFunction(CURLM *global, long timeout_ms, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/lib/curl/Request.cxx new/mpd-0.21.18/src/lib/curl/Request.cxx --- old/mpd-0.21.17/src/lib/curl/Request.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/lib/curl/Request.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -30,7 +30,6 @@ #include "config.h" #include "Request.hxx" #include "Global.hxx" -#include "Version.hxx" #include "Handler.hxx" #include "event/Call.hxx" #include "util/RuntimeError.hxx" @@ -124,12 +123,6 @@ curl_easy_pause(easy.Get(), CURLPAUSE_CONT); - if (IsCurlOlderThan(0x072000)) - /* libcurl older than 7.32.0 does not update - its sockets after curl_easy_pause(); force - libcurl to do it now */ - global.ResumeSockets(); - global.InvalidateSockets(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/lib/curl/meson.build new/mpd-0.21.18/src/lib/curl/meson.build --- old/mpd-0.21.17/src/lib/curl/meson.build 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/lib/curl/meson.build 2019-12-24 16:13:16.000000000 +0100 @@ -1,4 +1,4 @@ -curl_dep = dependency('libcurl', version: '>= 7.18', required: get_option('curl')) +curl_dep = dependency('libcurl', version: '>= 7.32', required: get_option('curl')) conf.set('ENABLE_CURL', curl_dep.found()) if not curl_dep.found() subdir_done() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/player/Thread.cxx new/mpd-0.21.18/src/player/Thread.cxx --- old/mpd-0.21.17/src/player/Thread.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/player/Thread.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -46,6 +46,7 @@ #include "CrossFade.hxx" #include "tag/Tag.hxx" #include "Idle.hxx" +#include "util/Compiler.h" #include "util/Domain.hxx" #include "thread/Name.hxx" #include "Log.hxx" @@ -1171,6 +1172,7 @@ } /* fall through */ + gcc_fallthrough; case PlayerCommand::PAUSE: next_song.reset(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/storage/plugins/CurlStorage.cxx new/mpd-0.21.18/src/storage/plugins/CurlStorage.cxx --- old/mpd-0.21.17/src/storage/plugins/CurlStorage.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/storage/plugins/CurlStorage.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -109,7 +109,9 @@ BIND_THIS_METHOD(OnDeferredStart)), request(curl, uri, *this) { // TODO: use CurlInputStream's configuration + } + void DeferStart() noexcept { /* start the transfer inside the IOThread */ defer_start.Schedule(); } @@ -283,6 +285,7 @@ } using BlockingHttpRequest::GetEasy; + using BlockingHttpRequest::DeferStart; using BlockingHttpRequest::Wait; protected: @@ -430,6 +433,7 @@ } const StorageFileInfo &Perform() { + DeferStart(); Wait(); return info; } @@ -481,6 +485,7 @@ base_path(UriPathOrSlash(uri)) {} std::unique_ptr<StorageDirectoryReader> Perform() { + DeferStart(); Wait(); return ToReader(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/time/ISO8601.cxx new/mpd-0.21.18/src/time/ISO8601.cxx --- old/mpd-0.21.17/src/time/ISO8601.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/time/ISO8601.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -58,6 +58,8 @@ return FormatISO8601(GmTime(tp)); } +#ifndef _WIN32 + static std::pair<unsigned, unsigned> ParseTimeZoneOffsetRaw(const char *&s) { @@ -108,6 +110,67 @@ return d; } +static const char * +ParseTimeOfDay(const char *s, struct tm &tm, + std::chrono::system_clock::duration &precision) noexcept +{ + /* this function always checks "end==s" to work around a + strptime() bug on OS X: if nothing could be parsed, + strptime() returns the input string (indicating success) + instead of nullptr (indicating error) */ + + const char *end = strptime(s, "%H", &tm); + if (end == nullptr || end == s) + return end; + + s = end; + precision = std::chrono::hours(1); + + if (*s == ':') { + /* with field separators: now a minute must follow */ + + ++s; + + end = strptime(s, "%M", &tm); + if (end == nullptr || end == s) + return nullptr; + + s = end; + precision = std::chrono::minutes(1); + + /* the "seconds" field is optional */ + if (*s != ':') + return s; + + ++s; + + end = strptime(s, "%S", &tm); + if (end == nullptr || end == s) + return nullptr; + + precision = std::chrono::seconds(1); + return end; + } + + /* without field separators */ + + end = strptime(s, "%M", &tm); + if (end == nullptr || end == s) + return s; + + s = end; + precision = std::chrono::minutes(1); + + end = strptime(s, "%S", &tm); + if (end == nullptr || end == s) + return s; + + precision = std::chrono::seconds(1); + return end; +} + +#endif + std::pair<std::chrono::system_clock::time_point, std::chrono::system_clock::duration> ParseISO8601(const char *s) @@ -138,22 +201,9 @@ if (*s == 'T') { ++s; - if ((end = strptime(s, "%T", &tm)) != nullptr) - precision = std::chrono::seconds(1); - else if ((end = strptime(s, "%H%M%S", &tm)) != nullptr) - /* no field separators */ - precision = std::chrono::seconds(1); - else if ((end = strptime(s, "%H%M", &tm)) != nullptr) - /* no field separators */ - precision = std::chrono::minutes(1); - else if ((end = strptime(s, "%H:%M", &tm)) != nullptr) - precision = std::chrono::minutes(1); - else if ((end = strptime(s, "%H", &tm)) != nullptr) - precision = std::chrono::hours(1); - else + s = ParseTimeOfDay(s, tm, precision); + if (s == nullptr) throw std::runtime_error("Failed to parse time of day"); - - s = end; } auto tp = TimeGm(tm); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/util/Compiler.h new/mpd-0.21.18/src/util/Compiler.h --- old/mpd-0.21.17/src/util/Compiler.h 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/util/Compiler.h 2019-12-24 16:13:16.000000000 +0100 @@ -143,6 +143,14 @@ #define gcc_flatten #endif +#if GCC_CHECK_VERSION(7,0) +#define gcc_fallthrough __attribute__((fallthrough)) +#elif CLANG_CHECK_VERSION(10,0) +#define gcc_fallthrough [[fallthrough]] +#else +#define gcc_fallthrough +#endif + #ifndef __cplusplus /* plain C99 has "restrict" */ #define gcc_restrict restrict diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/src/util/format.c new/mpd-0.21.18/src/util/format.c --- old/mpd-0.21.17/src/util/format.c 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/src/util/format.c 2019-12-24 16:13:16.000000000 +0100 @@ -19,6 +19,7 @@ */ #include "format.h" +#include "util/Compiler.h" #include <stdbool.h> #include <stdio.h> @@ -238,6 +239,7 @@ } /* fall through */ + gcc_fallthrough; default: /* pass-through non-escaped portions of the format string */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/test/RunCurl.cxx new/mpd-0.21.18/test/RunCurl.cxx --- old/mpd-0.21.17/test/RunCurl.cxx 1970-01-01 01:00:00.000000000 +0100 +++ new/mpd-0.21.18/test/RunCurl.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -0,0 +1,93 @@ +/* + * Copyright 2003-2019 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 "ShutdownHandler.hxx" +#include "lib/curl/Global.hxx" +#include "lib/curl/Request.hxx" +#include "lib/curl/Handler.hxx" +#include "event/Loop.hxx" +#include "util/PrintException.hxx" + +#include <stdio.h> + +class MyHandler final : public CurlResponseHandler { + EventLoop &event_loop; + + std::exception_ptr error; + +public: + explicit MyHandler(EventLoop &_event_loop) noexcept + :event_loop(_event_loop) {} + + void Finish() { + if (error) + std::rethrow_exception(error); + } + + /* virtual methods from CurlResponseHandler */ + void OnHeaders(unsigned status, + std::multimap<std::string, std::string> &&headers) override { + fprintf(stderr, "status: %u\n", status); + for (const auto &i : headers) + fprintf(stderr, "%s: %s\n", + i.first.c_str(), i.second.c_str()); + } + + void OnData(ConstBuffer<void> data) override { + if (fwrite(data.data, data.size, 1, stdout) != 1) + throw std::runtime_error("Failed to write"); + } + + void OnEnd() override { + event_loop.Break(); + } + + void OnError(std::exception_ptr e) noexcept override { + error = std::move(e); + event_loop.Break(); + } +}; + +int +main(int argc, char **argv) noexcept +try { + if (argc != 2) { + fprintf(stderr, "Usage: RunCurl URI\n"); + return EXIT_FAILURE; + } + + const char *const uri = argv[1]; + + EventLoop event_loop; + const ShutdownHandler shutdown_handler(event_loop); + CurlGlobal curl_global(event_loop); + + MyHandler handler(event_loop); + CurlRequest request(curl_global, uri, handler); + request.Start(); + + event_loop.Run(); + + handler.Finish(); + + return EXIT_SUCCESS; +} catch (...) { + PrintException(std::current_exception()); + return EXIT_FAILURE; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/test/meson.build new/mpd-0.21.18/test/meson.build --- old/mpd-0.21.17/test/meson.build 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/test/meson.build 2019-12-24 16:13:16.000000000 +0100 @@ -334,6 +334,18 @@ ) if curl_dep.found() + executable( + 'RunCurl', + 'RunCurl.cxx', + 'ShutdownHandler.cxx', + '../src/Log.cxx', + '../src/LogBackend.cxx', + include_directories: inc, + dependencies: [ + curl_dep, + ], + ) + test('test_icy_parser', executable( 'test_icy_parser', 'test_icy_parser.cxx', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.21.17/test/run_storage.cxx new/mpd-0.21.18/test/run_storage.cxx --- old/mpd-0.21.17/test/run_storage.cxx 2019-12-16 23:32:44.000000000 +0100 +++ new/mpd-0.21.18/test/run_storage.cxx 2019-12-24 16:13:16.000000000 +0100 @@ -90,6 +90,29 @@ return EXIT_SUCCESS; } +static int +Stat(Storage &storage, const char *path) +{ + const auto info = storage.GetInfo(path, false); + switch (info.type) { + case StorageFileInfo::Type::OTHER: + printf("other\n"); + break; + + case StorageFileInfo::Type::REGULAR: + printf("regular\n"); + break; + + case StorageFileInfo::Type::DIRECTORY: + printf("directory\n"); + break; + } + + printf("size: %llu\n", (unsigned long long)info.size); + + return EXIT_SUCCESS; +} + int main(int argc, char **argv) try { @@ -117,6 +140,18 @@ storage_uri); return Ls(*storage, path); + } else if (strcmp(command, "stat") == 0) { + if (argc != 4) { + fprintf(stderr, "Usage: run_storage stat URI PATH\n"); + return EXIT_FAILURE; + } + + const char *const path = argv[3]; + + auto storage = MakeStorage(io_thread.GetEventLoop(), + storage_uri); + + return Stat(*storage, path); } else { fprintf(stderr, "Unknown command\n"); return EXIT_FAILURE;
participants (1)
-
root