Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package mpd for openSUSE:Factory checked in 
at 2021-02-20 22:12:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mpd (Old)
 and      /work/SRC/openSUSE:Factory/.mpd.new.28504 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mpd"

Sat Feb 20 22:12:43 2021 rev:29 rq:873980 version:0.22.6

Changes:
--------
--- /work/SRC/openSUSE:Factory/mpd/mpd.changes  2021-01-25 18:23:56.892457233 
+0100
+++ /work/SRC/openSUSE:Factory/.mpd.new.28504/mpd.changes       2021-02-20 
22:12:49.339072992 +0100
@@ -1,0 +2,13 @@
+Fri Feb 19 23:49:35 UTC 2021 - Dirk M??ller <[email protected]>
+
+- update to 0.22.6:
+  * fix missing tags on songs in queue
+  - error for malformed ranges instead of ignoring silently
+  - better error message for open-ended range with "move"
+  - simple: fix missing CUE sheet metadata in "addid" command
+  - id: translate TPE3 to Conductor, not Performer
+  - iso9660: another fix for unaligned reads
+  - httpd: error handling on Windows improved
+  - pulse: fix deadlock with "always_on"
+
+-------------------------------------------------------------------

Old:
----
  mpd-0.22.4.tar.xz
  mpd-0.22.4.tar.xz.sig

New:
----
  mpd-0.22.6.tar.xz
  mpd-0.22.6.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mpd.spec ++++++
--- /var/tmp/diff_new_pack.YFJ4ok/_old  2021-02-20 22:12:49.911073600 +0100
+++ /var/tmp/diff_new_pack.YFJ4ok/_new  2021-02-20 22:12:49.911073600 +0100
@@ -20,7 +20,7 @@
 %bcond_with    faad
 %bcond_without mpd_iso9660
 Name:           mpd
-Version:        0.22.4
+Version:        0.22.6
 Release:        0
 Summary:        Music Player Daemon
 License:        GPL-2.0-or-later
@@ -36,10 +36,10 @@
 BuildRequires:  cmake
 BuildRequires:  gcc
 BuildRequires:  gcc-c++
-BuildRequires:  group(audio)
 BuildRequires:  hicolor-icon-theme
 BuildRequires:  libboost_headers-devel
 BuildRequires:  libcue-devel
+BuildRequires:  group(audio)
 # MPD_ENABLE_AUTO_LIB
 BuildRequires:  libgcrypt-devel
 BuildRequires:  libmikmod-devel

++++++ mpd-0.22.4.tar.xz -> mpd-0.22.6.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/NEWS new/mpd-0.22.6/NEWS
--- old/mpd-0.22.4/NEWS 2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/NEWS 2021-02-16 13:56:14.000000000 +0100
@@ -1,3 +1,24 @@
+ver 0.22.6 (2021/02/16)
+* fix missing tags on songs in queue
+
+ver 0.22.5 (2021/02/15)
+* protocol
+  - error for malformed ranges instead of ignoring silently
+  - better error message for open-ended range with "move"
+* database
+  - simple: fix missing CUE sheet metadata in "addid" command
+* tags
+  - id: translate TPE3 to Conductor, not Performer
+* archive
+  - iso9660: another fix for unaligned reads
+* output
+  - httpd: error handling on Windows improved
+  - pulse: fix deadlock with "always_on"
+* Windows:
+  - enable https:// support (via Schannel)
+* Android
+  - work around "Permission denied" on mpd.conf
+
 ver 0.22.4 (2021/01/21)
 * protocol
   - add command "binarylimit" to allow larger chunk sizes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/android/AndroidManifest.xml 
new/mpd-0.22.6/android/AndroidManifest.xml
--- old/mpd-0.22.4/android/AndroidManifest.xml  2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/android/AndroidManifest.xml  2021-02-16 13:56:14.000000000 
+0100
@@ -2,10 +2,10 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android";
           package="org.musicpd"
           android:installLocation="auto"
-          android:versionCode="51"
-          android:versionName="0.22.1">
+          android:versionCode="54"
+          android:versionName="0.22.6">
 
-  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
+  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
 
   <uses-feature android:name="android.software.leanback"
                 android:required="false" />
@@ -19,6 +19,7 @@
   <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 
   <application android:allowBackup="true"
+               android:requestLegacyExternalStorage="true"
                android:icon="@drawable/icon"
                android:banner="@drawable/icon"
                android:label="@string/app_name">
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/android/meson.build 
new/mpd-0.22.6/android/meson.build
--- old/mpd-0.22.4/android/meson.build  2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/android/meson.build  2021-02-16 13:56:14.000000000 +0100
@@ -5,8 +5,8 @@
 android_sdk = get_option('android_sdk')
 android_abi = get_option('android_abi')
 
-android_sdk_build_tools_version = '27.0.0'
-android_sdk_platform = 'android-23'
+android_sdk_build_tools_version = '29.0.3'
+android_sdk_platform = 'android-29'
 
 android_build_tools_dir = join_paths(android_sdk, 'build-tools', 
android_sdk_build_tools_version)
 android_sdk_platform_dir = join_paths(android_sdk, 'platforms', 
android_sdk_platform)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/doc/conf.py new/mpd-0.22.6/doc/conf.py
--- old/mpd-0.22.4/doc/conf.py  2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/doc/conf.py  2021-02-16 13:56:14.000000000 +0100
@@ -38,7 +38,7 @@
 # built documents.
 #
 # The short X.Y version.
-version = '0.22.4'
+version = '0.22.6'
 # The full version, including alpha/beta/rc tags.
 release = version
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/doc/developer.rst 
new/mpd-0.22.6/doc/developer.rst
--- old/mpd-0.22.4/doc/developer.rst    2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/doc/developer.rst    2021-02-16 13:56:14.000000000 +0100
@@ -68,11 +68,11 @@
 
 - the "unstable" branch called ``master`` where new features are
   merged.  This will become the next major release eventually.
-- the "stable" branch (currently called ``v0.21.x``) where only bug
+- the "stable" branch (currently called ``v0.22.x``) where only bug
   fixes are merged.
 
-Once :program:`MPD` 0.22 is released, a new branch called ``v0.22.x``
-will be created for 0.22 bug-fix releases; after that, ``v0.21.x``
+Once :program:`MPD` 0.23 is released, a new branch called ``v0.23.x``
+will be created for 0.23 bug-fix releases; after that, ``v0.22.x``
 will eventually cease to be maintained.
 
 After bug fixes have been added to the "stable" branch, it will be
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/doc/protocol.rst 
new/mpd-0.22.6/doc/protocol.rst
--- old/mpd-0.22.4/doc/protocol.rst     2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/doc/protocol.rst     2021-02-16 13:56:14.000000000 +0100
@@ -677,6 +677,11 @@
     (directories add recursively). ``URI``
     can also be a single file.
 
+    Clients that are connected via local socket may add arbitrary
+    local files (URI is an absolute path).  Exmaple::
+
+     add "/home/foo/Music/bar.ogg"
+
 .. _command_addid:
 
 :command:`addid {URI} [POSITION]`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/meson.build new/mpd-0.22.6/meson.build
--- old/mpd-0.22.4/meson.build  2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/meson.build  2021-02-16 13:56:14.000000000 +0100
@@ -1,7 +1,7 @@
 project(
   'mpd',
   ['c', 'cpp'],
-  version: '0.22.4',
+  version: '0.22.6',
   meson_version: '>= 0.49.0',
   default_options: [
     'c_std=c11',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/python/build/libs.py 
new/mpd-0.22.6/python/build/libs.py
--- old/mpd-0.22.4/python/build/libs.py 2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/python/build/libs.py 2021-02-16 13:56:14.000000000 +0100
@@ -407,6 +407,9 @@
         '--disable-progress-meter',
         '--disable-alt-svc',
         '--without-gnutls', '--without-nss', '--without-libssh2',
+
+        # native Windows SSL/TLS support, option ignored on non-Windows builds
+        '--with-schannel',
     ],
 
     patches='src/lib/curl/patches',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mpd-0.22.4/src/archive/plugins/Iso9660ArchivePlugin.cxx 
new/mpd-0.22.6/src/archive/plugins/Iso9660ArchivePlugin.cxx
--- old/mpd-0.22.4/src/archive/plugins/Iso9660ArchivePlugin.cxx 2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/archive/plugins/Iso9660ArchivePlugin.cxx 2021-02-16 
13:56:14.000000000 +0100
@@ -221,8 +221,8 @@
                if (new_offset > size)
                        throw std::runtime_error("Invalid seek offset");
 
+               offset = new_offset;
                skip = new_offset % ISO_BLOCKSIZE;
-               offset = new_offset - skip;
                buffer.Clear();
        }
 };
@@ -260,13 +260,13 @@
        if (r.empty()) {
                /* the buffer is empty - read more data from the ISO file */
 
-               assert(offset % ISO_BLOCKSIZE == 0);
+               assert((offset - skip) % ISO_BLOCKSIZE == 0);
 
                const ScopeUnlock unlock(mutex);
 
                const lsn_t read_lsn = lsn + offset / ISO_BLOCKSIZE;
 
-               if (read_size >= ISO_BLOCKSIZE) {
+               if (read_size >= ISO_BLOCKSIZE && skip == 0) {
                        /* big read - read right into the caller's buffer */
 
                        auto nbytes = iso->SeekRead(ptr, read_lsn,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/command/QueueCommands.cxx 
new/mpd-0.22.6/src/command/QueueCommands.cxx
--- old/mpd-0.22.4/src/command/QueueCommands.cxx        2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/command/QueueCommands.cxx        2021-02-16 
13:56:14.000000000 +0100
@@ -326,6 +326,11 @@
 handle_move(Client &client, Request args, [[maybe_unused]] Response &r)
 {
        RangeArg range = args.ParseRange(0);
+       if (range.IsOpenEnded()) {
+               r.Error(ACK_ERROR_ARG, "Open-ended range not supported");
+               return CommandResult::ERROR;
+       }
+
        int to = args.ParseInt(1);
        client.GetPartition().MoveRange(range.start, range.end, to);
        return CommandResult::OK;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/db/plugins/simple/ExportedSong.hxx 
new/mpd-0.22.6/src/db/plugins/simple/ExportedSong.hxx
--- old/mpd-0.22.4/src/db/plugins/simple/ExportedSong.hxx       2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/db/plugins/simple/ExportedSong.hxx       2021-02-16 
13:56:14.000000000 +0100
@@ -29,6 +29,12 @@
  * a #LightSong, e.g. a merged #Tag.
  */
 class ExportedSong : public LightSong {
+       /**
+        * A reference target for LightSong::tag, but it is only used
+        * if this instance "owns" the #Tag.  For instances referring
+        * to a foreign #Tag instance (e.g. a Song::tag), this field
+        * is not used (and empty).
+        */
        Tag tag_buffer;
 
 public:
@@ -37,6 +43,25 @@
        ExportedSong(const char *_uri, Tag &&_tag) noexcept
                :LightSong(_uri, tag_buffer),
                 tag_buffer(std::move(_tag)) {}
+
+       /* this custom move constructor is necessary so LightSong::tag
+          points to this instance's #Tag field instead of leaving a
+          dangling reference to the source object's #Tag field */
+       ExportedSong(ExportedSong &&src) noexcept
+               :LightSong(src,
+                          /* refer to tag_buffer only if the
+                             moved-from instance also owned the Tag
+                             which its LightSong::tag field refers
+                             to */
+                          OwnsTag() ? tag_buffer : src.tag),
+                tag_buffer(std::move(src.tag_buffer)) {}
+
+       ExportedSong &operator=(ExportedSong &&) = delete;
+
+private:
+       bool OwnsTag() const noexcept {
+               return &tag == &tag_buffer;
+       }
 };
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/event/BufferedSocket.cxx 
new/mpd-0.22.6/src/event/BufferedSocket.cxx
--- old/mpd-0.22.4/src/event/BufferedSocket.cxx 2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/event/BufferedSocket.cxx 2021-02-16 13:56:14.000000000 
+0100
@@ -36,7 +36,7 @@
        }
 
        const auto code = GetSocketError();
-       if (IsSocketErrorAgain(code))
+       if (IsSocketErrorReceiveWouldBlock(code))
                return 0;
 
        if (IsSocketErrorClosed(code))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/event/FullyBufferedSocket.cxx 
new/mpd-0.22.6/src/event/FullyBufferedSocket.cxx
--- old/mpd-0.22.4/src/event/FullyBufferedSocket.cxx    2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/event/FullyBufferedSocket.cxx    2021-02-16 
13:56:14.000000000 +0100
@@ -31,7 +31,7 @@
        const auto nbytes = GetSocket().Write((const char *)data, length);
        if (gcc_unlikely(nbytes < 0)) {
                const auto code = GetSocketError();
-               if (IsSocketErrorAgain(code))
+               if (IsSocketErrorSendWouldBlock(code))
                        return 0;
 
                IdleMonitor::Cancel();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/net/SocketError.cxx 
new/mpd-0.22.6/src/net/SocketError.cxx
--- old/mpd-0.22.4/src/net/SocketError.cxx      2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/net/SocketError.cxx      2021-02-16 13:56:14.000000000 
+0100
@@ -1,20 +1,30 @@
 /*
- * Copyright 2003-2021 The Music Player Daemon Project
- * http://www.musicpd.org
+ * Copyright 2015-2021 Max Kellermann <[email protected]>
  *
- * 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * 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.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
  *
- * 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.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "SocketError.hxx"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/net/SocketError.hxx 
new/mpd-0.22.6/src/net/SocketError.hxx
--- old/mpd-0.22.4/src/net/SocketError.hxx      2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/net/SocketError.hxx      2021-02-16 13:56:14.000000000 
+0100
@@ -1,24 +1,34 @@
 /*
- * Copyright 2003-2021 The Music Player Daemon Project
- * http://www.musicpd.org
+ * Copyright 2015-2021 Max Kellermann <[email protected]>
  *
- * 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * 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.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
  *
- * 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.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef MPD_SOCKET_ERROR_HXX
-#define MPD_SOCKET_ERROR_HXX
+#ifndef SOCKET_ERROR_HXX
+#define SOCKET_ERROR_HXX
 
 #include "util/Compiler.h"
 #include "system/Error.hxx"
@@ -42,14 +52,79 @@
 #endif
 }
 
-gcc_const
-static inline bool
-IsSocketErrorAgain(socket_error_t code) noexcept
+constexpr bool
+IsSocketErrorInProgress(socket_error_t code) noexcept
 {
 #ifdef _WIN32
        return code == WSAEINPROGRESS;
 #else
-       return code == EAGAIN;
+       return code == EINPROGRESS;
+#endif
+}
+
+constexpr bool
+IsSocketErrorWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+       return code == WSAEWOULDBLOCK;
+#else
+       return code == EWOULDBLOCK;
+#endif
+}
+
+constexpr bool
+IsSocketErrorConnectWouldBlock(socket_error_t code) noexcept
+{
+#if defined(_WIN32) || defined(__linux__)
+       /* on Windows, WSAEINPROGRESS is for blocking sockets and
+          WSAEWOULDBLOCK for non-blocking sockets */
+       /* on Linux, EAGAIN==EWOULDBLOCK is for local sockets and
+          EINPROGRESS is for all other sockets */
+       return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+       /* on all other operating systems, there's just EINPROGRESS */
+       return IsSocketErrorInProgress(code);
+#endif
+}
+
+constexpr bool
+IsSocketErrorSendWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+       /* on Windows, WSAEINPROGRESS is for blocking sockets and
+          WSAEWOULDBLOCK for non-blocking sockets */
+       return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+       /* on all other operating systems, there's just EAGAIN==EWOULDBLOCK */
+       return IsSocketErrorWouldBlock(code);
+#endif
+}
+
+constexpr bool
+IsSocketErrorReceiveWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+       /* on Windows, WSAEINPROGRESS is for blocking sockets and
+          WSAEWOULDBLOCK for non-blocking sockets */
+       return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+       /* on all other operating systems, there's just
+          EAGAIN==EWOULDBLOCK */
+       return IsSocketErrorWouldBlock(code);
+#endif
+}
+
+constexpr bool
+IsSocketErrorAcceptWouldBlock(socket_error_t code) noexcept
+{
+#ifdef _WIN32
+       /* on Windows, WSAEINPROGRESS is for blocking sockets and
+          WSAEWOULDBLOCK for non-blocking sockets */
+       return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code);
+#else
+       /* on all other operating systems, there's just
+          EAGAIN==EWOULDBLOCK */
+       return IsSocketErrorWouldBlock(code);
 #endif
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/output/Control.hxx 
new/mpd-0.22.6/src/output/Control.hxx
--- old/mpd-0.22.4/src/output/Control.hxx       2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/output/Control.hxx       2021-02-16 13:56:14.000000000 
+0100
@@ -182,6 +182,14 @@
        bool open = false;
 
        /**
+        * Is the device currently playing, i.e. is its buffer
+        * (likely) non-empty?  If not, then it will never be drained.
+        *
+        * This field is only valid while the output is open.
+        */
+       bool playing;
+
+       /**
         * Is the device paused?  i.e. the output thread is in the
         * ao_pause() loop.
         */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/output/Thread.cxx 
new/mpd-0.22.6/src/output/Thread.cxx
--- old/mpd-0.22.4/src/output/Thread.cxx        2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/output/Thread.cxx        2021-02-16 13:56:14.000000000 
+0100
@@ -53,7 +53,7 @@
        if (open && cf != output->filter_audio_format)
                /* if the filter's output format changes, the output
                   must be reopened as well */
-               InternalCloseOutput(true);
+               InternalCloseOutput(playing);
 
        output->filter_audio_format = cf;
 
@@ -64,6 +64,7 @@
                }
 
                open = true;
+               playing = false;
        } else if (in_audio_format != output->out_audio_format) {
                /* reconfigure the final ConvertFilter for its new
                   input AudioFormat */
@@ -285,6 +286,9 @@
                assert(nbytes % output->out_audio_format.GetFrameSize() == 0);
 
                source.ConsumeData(nbytes);
+
+               /* there's data to be drained from now on */
+               playing = true;
        }
 
        return true;
@@ -371,6 +375,9 @@
        }
 
        skip_delay = true;
+
+       /* ignore drain commands until we got something new to play */
+       playing = false;
 }
 
 static void
@@ -390,6 +397,10 @@
 inline void
 AudioOutputControl::InternalDrain() noexcept
 {
+       /* after this method finishes, there's nothing left to be
+          drained */
+       playing = false;
+
        try {
                /* flush the filter and play its remaining output */
 
@@ -518,6 +529,7 @@
                        source.Cancel();
 
                        if (open) {
+                               playing = false;
                                const ScopeUnlock unlock(mutex);
                                output->Cancel();
                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/output/plugins/PulseOutputPlugin.cxx 
new/mpd-0.22.6/src/output/plugins/PulseOutputPlugin.cxx
--- old/mpd-0.22.4/src/output/plugins/PulseOutputPlugin.cxx     2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/output/plugins/PulseOutputPlugin.cxx     2021-02-16 
13:56:14.000000000 +0100
@@ -56,8 +56,6 @@
 
        size_t writable;
 
-       bool pause;
-
        /**
         * Was Interrupt() called?  This will unblock Play().  It will
         * be reset by Cancel() and Pause(), as documented by the
@@ -113,6 +111,7 @@
 
        [[nodiscard]] std::chrono::steady_clock::duration Delay() const 
noexcept override;
        size_t Play(const void *chunk, size_t size) override;
+       void Drain() override;
        void Cancel() noexcept override;
        bool Pause() override;
 
@@ -688,7 +687,6 @@
                                     "pa_stream_connect_playback() has failed");
        }
 
-       pause = false;
        interrupted = false;
 }
 
@@ -699,17 +697,6 @@
 
        Pulse::LockGuard lock(mainloop);
 
-       if (pa_stream_get_state(stream) == PA_STREAM_READY) {
-               pa_operation *o =
-                       pa_stream_drain(stream,
-                                       pulse_output_stream_success_cb, this);
-               if (o == nullptr) {
-                       LogPulseError(context,
-                                     "pa_stream_drain() has failed");
-               } else
-                       pulse_wait_for_operation(mainloop, o);
-       }
-
        DeleteStream();
 
        if (context != nullptr &&
@@ -780,7 +767,7 @@
        Pulse::LockGuard lock(mainloop);
 
        auto result = std::chrono::steady_clock::duration::zero();
-       if (pause && pa_stream_is_corked(stream) &&
+       if (pa_stream_is_corked(stream) &&
            pa_stream_get_state(stream) == PA_STREAM_READY)
                /* idle while paused */
                result = std::chrono::seconds(1);
@@ -796,8 +783,6 @@
 
        Pulse::LockGuard lock(mainloop);
 
-       pause = false;
-
        /* check if the stream is (already) connected */
 
        WaitStream();
@@ -841,6 +826,25 @@
 }
 
 void
+PulseOutput::Drain()
+{
+       Pulse::LockGuard lock(mainloop);
+
+       if (pa_stream_get_state(stream) != PA_STREAM_READY ||
+           pa_stream_is_suspended(stream) ||
+           pa_stream_is_corked(stream))
+               return;
+
+       pa_operation *o =
+               pa_stream_drain(stream,
+                               pulse_output_stream_success_cb, this);
+       if (o == nullptr)
+               throw MakePulseError(context, "pa_stream_drain() failed");
+
+       pulse_wait_for_operation(mainloop, o);
+}
+
+void
 PulseOutput::Cancel() noexcept
 {
        assert(mainloop != nullptr);
@@ -876,7 +880,6 @@
 
        Pulse::LockGuard lock(mainloop);
 
-       pause = true;
        interrupted = false;
 
        /* check if the stream is (already/still) connected */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/output/plugins/WasapiOutputPlugin.cxx 
new/mpd-0.22.6/src/output/plugins/WasapiOutputPlugin.cxx
--- old/mpd-0.22.4/src/output/plugins/WasapiOutputPlugin.cxx    2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/output/plugins/WasapiOutputPlugin.cxx    2021-02-16 
13:56:14.000000000 +0100
@@ -24,6 +24,7 @@
 #include "mixer/MixerList.hxx"
 #include "thread/Cond.hxx"
 #include "thread/Mutex.hxx"
+#include "thread/Name.hxx"
 #include "thread/Thread.hxx"
 #include "util/AllocatedString.hxx"
 #include "util/Domain.hxx"
@@ -231,6 +232,7 @@
 }
 
 void WasapiOutputThread::Work() noexcept {
+       SetThreadName("Wasapi Output Worker");
        FormatDebug(wasapi_output_domain, "Working thread started");
        try {
                com.emplace();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/output/plugins/httpd/HttpdClient.cxx 
new/mpd-0.22.6/src/output/plugins/httpd/HttpdClient.cxx
--- old/mpd-0.22.4/src/output/plugins/httpd/HttpdClient.cxx     2021-01-21 
17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/output/plugins/httpd/HttpdClient.cxx     2021-02-16 
13:56:14.000000000 +0100
@@ -278,7 +278,7 @@
                                                      
metadata_current_position);
                        if (nbytes < 0) {
                                auto e = GetSocketError();
-                               if (IsSocketErrorAgain(e))
+                               if (IsSocketErrorSendWouldBlock(e))
                                        return true;
 
                                if (!IsSocketErrorClosed(e)) {
@@ -305,7 +305,7 @@
                        ssize_t nbytes = GetSocket().Write(&empty_data, 1);
                        if (nbytes < 0) {
                                auto e = GetSocketError();
-                               if (IsSocketErrorAgain(e))
+                               if (IsSocketErrorSendWouldBlock(e))
                                        return true;
 
                                if (!IsSocketErrorClosed(e)) {
@@ -328,7 +328,7 @@
                                      bytes_to_write);
                if (nbytes < 0) {
                        auto e = GetSocketError();
-                       if (IsSocketErrorAgain(e))
+                       if (IsSocketErrorSendWouldBlock(e))
                                return true;
 
                        if (!IsSocketErrorClosed(e)) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/protocol/ArgParser.cxx 
new/mpd-0.22.6/src/protocol/ArgParser.cxx
--- old/mpd-0.22.4/src/protocol/ArgParser.cxx   2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/protocol/ArgParser.cxx   2021-02-16 13:56:14.000000000 
+0100
@@ -94,7 +94,7 @@
                                                  s);
 
                if (test == test2)
-                       value = std::numeric_limits<int>::max();
+                       return RangeArg::OpenEnded(range.start);
 
                if (value < 0)
                        throw FormatProtocolError(ACK_ERROR_ARG,
@@ -107,9 +107,13 @@
 
                range.end = (unsigned)value;
        } else {
-               range.end = (unsigned)value + 1;
+               return RangeArg::Single(range.start);
        }
 
+       if (!range.IsWellFormed())
+               throw FormatProtocolError(ACK_ERROR_ARG,
+                                         "Malformed range: %s", s);
+
        return range;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/protocol/RangeArg.hxx 
new/mpd-0.22.6/src/protocol/RangeArg.hxx
--- old/mpd-0.22.4/src/protocol/RangeArg.hxx    2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/protocol/RangeArg.hxx    2021-02-16 13:56:14.000000000 
+0100
@@ -25,8 +25,22 @@
 struct RangeArg {
        unsigned start, end;
 
-       static constexpr RangeArg All() {
-               return { 0, std::numeric_limits<unsigned>::max() };
+       /**
+        * Construct an open-ended range starting at the given index.
+        */
+       static constexpr RangeArg OpenEnded(unsigned start) noexcept {
+               return { start, std::numeric_limits<unsigned>::max() };
+       }
+
+       static constexpr RangeArg All() noexcept {
+               return OpenEnded(0);
+       }
+
+       /**
+        * Construct an instance describing exactly one index.
+        */
+       static constexpr RangeArg Single(unsigned i) noexcept {
+               return { i, i + 1 };
        }
 
        constexpr bool operator==(RangeArg other) const noexcept {
@@ -37,13 +51,45 @@
                return !(*this == other);
        }
 
+       constexpr bool IsOpenEnded() const noexcept {
+               return end == All().end;
+       }
+
        constexpr bool IsAll() const noexcept {
                return *this == All();
        }
 
+       constexpr bool IsWellFormed() const noexcept {
+               return start <= end;
+       }
+
+       /**
+        * Is this range empty?  A malformed range also counts as
+        * "empty" for this method.
+        */
+       constexpr bool IsEmpty() const noexcept {
+               return start >= end;
+       }
+
+       /**
+        * Check if the range contains at least this number of items.
+        * Unlike Count(), this allows the object to be malformed.
+        */
+       constexpr bool HasAtLeast(unsigned n) const noexcept {
+               return start + n <= end;
+       }
+
        constexpr bool Contains(unsigned i) const noexcept {
                return i >= start && i < end;
        }
+
+       /**
+        * Count the number of items covered by this range.  This requires the
+        * object to be well-formed.
+        */
+       constexpr unsigned Count() const noexcept {
+               return end - start;
+       }
 };
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/song/LightSong.hxx 
new/mpd-0.22.6/src/song/LightSong.hxx
--- old/mpd-0.22.4/src/song/LightSong.hxx       2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/song/LightSong.hxx       2021-02-16 13:56:14.000000000 
+0100
@@ -88,6 +88,19 @@
        LightSong(const char *_uri, const Tag &_tag) noexcept
                :uri(_uri), tag(_tag) {}
 
+       /**
+        * A copy constructor which copies all fields, but only sets
+        * the tag to a caller-provided reference.  This is used by
+        * the #ExportedSong move constructor.
+        */
+       LightSong(const LightSong &src, const Tag &_tag) noexcept
+               :directory(src.directory), uri(src.uri),
+                real_uri(src.real_uri),
+                tag(_tag),
+                mtime(src.mtime),
+                start_time(src.start_time), end_time(src.end_time),
+                audio_format(src.audio_format) {}
+
        gcc_pure
        std::string GetURI() const noexcept {
                if (directory == nullptr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/tag/Id3Scan.cxx 
new/mpd-0.22.6/src/tag/Id3Scan.cxx
--- old/mpd-0.22.4/src/tag/Id3Scan.cxx  2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/tag/Id3Scan.cxx  2021-02-16 13:56:14.000000000 +0100
@@ -352,7 +352,7 @@
                            handler);
        tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER,
                            handler);
-       tag_id3_import_text(tag, "TPE3", TAG_PERFORMER,
+       tag_id3_import_text(tag, "TPE3", TAG_CONDUCTOR,
                            handler);
        tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler);
        tag_id3_import_text(tag, "TIT1", TAG_GROUPING, handler);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/win32/ComPtr.hxx 
new/mpd-0.22.6/src/win32/ComPtr.hxx
--- old/mpd-0.22.4/src/win32/ComPtr.hxx 2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/src/win32/ComPtr.hxx 2021-02-16 13:56:14.000000000 +0100
@@ -112,6 +112,6 @@
 void swap(ComPtr<T> &lhs, ComPtr<T> &rhs) noexcept {
        lhs.swap(rhs);
 }
-}
+} // namespace std
 
 #endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/src/win32/HResult.hxx 
new/mpd-0.22.6/src/win32/HResult.hxx
--- old/mpd-0.22.4/src/win32/HResult.hxx        2021-01-21 17:21:20.000000000 
+0100
+++ new/mpd-0.22.6/src/win32/HResult.hxx        2021-02-16 13:56:14.000000000 
+0100
@@ -59,6 +59,7 @@
                C(E_INVALIDARG);
                C(E_OUTOFMEMORY);
                C(E_POINTER);
+               C(NO_ERROR);
 #undef C
        }
        return std::string_view();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpd-0.22.4/test/run_input.cxx 
new/mpd-0.22.6/test/run_input.cxx
--- old/mpd-0.22.4/test/run_input.cxx   2021-01-21 17:21:20.000000000 +0100
+++ new/mpd-0.22.6/test/run_input.cxx   2021-02-16 13:56:14.000000000 +0100
@@ -56,6 +56,8 @@
 
        FromNarrowPath config_path;
 
+       std::size_t seek = 0;
+
        std::size_t chunk_size = MAX_CHUNK_SIZE;
 
        bool verbose = false;
@@ -67,6 +69,7 @@
        OPTION_CONFIG,
        OPTION_VERBOSE,
        OPTION_SCAN,
+       OPTION_SEEK,
        OPTION_CHUNK_SIZE,
 };
 
@@ -74,6 +77,7 @@
        {"config", 0, true, "Load a MPD configuration file"},
        {"verbose", 'v', false, "Verbose logging"},
        {"scan", 0, false, "Scan tags instead of reading raw data"},
+       {"seek", 0, true, "Start reading at this position"},
        {"chunk-size", 0, true, "Read this number of bytes at a time"},
 };
 
@@ -108,6 +112,10 @@
                        c.scan = true;
                        break;
 
+               case OPTION_SEEK:
+                       c.seek = ParseSize(o.value);
+                       break;
+
                case OPTION_CHUNK_SIZE:
                        c.chunk_size = ParseSize(o.value);
                        if (c.chunk_size <= 0 || c.chunk_size > MAX_CHUNK_SIZE)
@@ -118,7 +126,7 @@
 
        auto args = option_parser.GetRemaining();
        if (args.size != 1)
-               throw std::runtime_error("Usage: run_input [--verbose] 
[--config=FILE] URI");
+               throw std::runtime_error("Usage: run_input [--verbose] 
[--config=FILE] [--scan] [--chunk-size=BYTES] URI");
 
        c.uri = args.front();
        return c;
@@ -153,10 +161,14 @@
 }
 
 static int
-dump_input_stream(InputStream &is, FileDescriptor out, size_t chunk_size)
+dump_input_stream(InputStream &is, FileDescriptor out,
+                 offset_type seek, size_t chunk_size)
 {
        std::unique_lock<Mutex> lock(is.mutex);
 
+       if (seek > 0)
+               is.Seek(lock, seek);
+
        /* print meta data */
 
        if (is.HasMimeType())
@@ -256,7 +268,7 @@
        Mutex mutex;
        auto is = InputStream::OpenReady(c.uri, mutex);
        return dump_input_stream(*is, FileDescriptor(STDOUT_FILENO),
-                                c.chunk_size);
+                                c.seek, c.chunk_size);
 } catch (...) {
        PrintException(std::current_exception());
        return EXIT_FAILURE;

Reply via email to