Compare commits

...

41 Commits

Author SHA1 Message Date
Max Kellermann
98afae2520 release v0.20.21 2018-08-17 19:50:59 +02:00
Max Kellermann
ddc85c620f configure.ac: make the GIT_COMMIT command worktree-safe
`$srcdir/.git` doesn't exist if `$srcdir` is a worktree.
2018-08-17 19:50:53 +02:00
Max Kellermann
12bc625fe1 android/build.py: add aarch64 support 2018-08-17 19:20:25 +02:00
Max Kellermann
6b407356b9 configure.ac: set ANDROID_ABI=x86 for the Android-x86 build
This was missing in commit 8266ab5588 for .
2018-08-17 19:18:29 +02:00
Max Kellermann
a4e0b52468 configure.ac, Makefile.am: add variable ANDROID_ABI 2018-08-17 19:01:37 +02:00
Max Kellermann
98efb4f6d5 android: raise minSdkVersion to 21
The number of MPD installs on Android < 5.0 is negligible, and that
API version introduces lots of useful features for MPD.
2018-08-17 19:01:37 +02:00
Max Kellermann
36edb4886c android/build.py: add variable "android_api_level" 2018-08-17 19:01:37 +02:00
Max Kellermann
76290f786d python/build/meson.py: set "needs_exe_wrapper=true"
Prevent Meson from running Android-x86 binaries.  That will fail
because the Android standard libraries are most likely not installed.
2018-08-17 19:00:42 +02:00
Max Kellermann
c6299c26b5 python/build/libs.py: disable libnfs utils/examples 2018-08-17 18:32:07 +02:00
Max Kellermann
fb5f9baf9c android/build.py: enable libexpat for the "curl" storage plugin 2018-08-17 17:15:05 +02:00
Max Kellermann
dee591d970 python/build/libs.py: disable expat documentation 2018-08-17 17:13:39 +02:00
Joshua Wise
a5cc13b0c5 build: Add libexpat to the crosscompile build on Windows.
The Curl plugin requires libexpat in order to work these days, so we should
download and build it in order to get the plugin enabled on Windows.
2018-08-17 17:13:39 +02:00
Max Kellermann
aaf588aeaa python/libs: upgrade Boost to 1.68.0 2018-08-17 17:13:39 +02:00
Max Kellermann
533a3def9f Makefile.am: add missing $(CURL_CFLAGS) and $(EXPAT_CFLAGS)
Fixes problems with the Windows build because `-DCURL_STATICLIB` was
missing, causing error messages like:

 "undefined reference to `__imp_curl_slist_free_all'"
2018-08-17 17:06:03 +02:00
Max Kellermann
fcf487f4e0 playlist/cue: support file type declaration "FLAC" (non-standard)
According to http://wiki.hydrogenaud.io/index.php?title=Cue_sheet FLAC
files should use the "WAVE" file type, but I recently encountered CUE
files declared as "FLAC" which could not be read by MPD.
2018-08-13 08:18:18 +02:00
Max Kellermann
906972973e case-insensitive URI scheme comparison
Required according to RFC 3986:

> An implementation should accept uppercase letters as equivalent to
> lowercase in scheme names

Closes 
2018-08-02 11:01:45 +02:00
Max Kellermann
116edf5fce util/ASCII: add StringStartsWithCaseASCII() 2018-08-02 10:42:28 +02:00
Max Kellermann
8581013911 configure.ac: default to --disable-daemon on Windows
This typo was present since the option was added in commit ed001e0cfb
2018-08-02 10:27:13 +02:00
Max Kellermann
b1e073bacd python/build/libs.py: upgrade FFmpeg to 4.0.2 2018-07-29 18:29:48 +02:00
skidoo23
501e48daba configure.ac: Do not link libsidplayfp against libresid-builder 2018-07-29 18:22:03 +02:00
skidoo23
643ecd1edd configure.ac: clarify sidplay related info 2018-07-29 18:21:42 +02:00
Max Kellermann
7393e1cba1 python/build/libs: upgrade libnfs to 3.0.0 2018-07-16 10:58:19 +02:00
Max Kellermann
ceee47fda8 python/build/libs: upgrade CURL to 7.61.0 2018-07-16 10:55:08 +02:00
Max Kellermann
6f3c0d0a60 AudioFormat: include cleanup 2018-07-06 19:35:31 +02:00
Max Kellermann
466625f7ad input/curl: use new class HttpStatusError
This way, IsFileNotFound() can detect status 404.
2018-07-06 19:26:11 +02:00
Max Kellermann
b8259e604a db/update/{Walk,ExcludeList}: use InputStream to read .mpdignore
Supports .mpdignore on NFS/SMB and others (closes ).
2018-07-06 19:19:04 +02:00
Max Kellermann
86e2075c63 lib/nfs/Connection: use new class NfsClientError
Allows callers to extract the NFS error code.
2018-07-06 19:17:34 +02:00
Max Kellermann
30900b2fe2 input/Error: new library providing IsFileNotFound() 2018-07-06 19:16:01 +02:00
Max Kellermann
fd7ae7ea4c input/Domain: remove obsolete variable 2018-07-06 19:13:53 +02:00
Max Kellermann
60d5bf0240 util/StringFormat: new utility library 2018-07-06 19:07:02 +02:00
Max Kellermann
41cdc4e14b input/Offset: add macro PRIoffset 2018-07-06 19:06:05 +02:00
Max Kellermann
87dfca0477 input/curl: remove obsolete Windows sprintf() fallback
See commit be137a191e
2018-07-06 19:05:09 +02:00
Max Kellermann
e1ee8e7812 util/FormatString: remove obsolete Windows fallback
Since 7d353bbe2a, _GNU_SOURCE is always
defined, which implies __USE_MINGW_ANSI_STDIO and thus switches to
the mingw implementations of the printf() family.  That's
standards-compliant, unlike Microsoft's CRT implementations.
2018-07-06 19:04:33 +02:00
Max Kellermann
63406efcd8 db/update/ExcludeList: allow comments only at start of line 2018-07-06 18:27:17 +02:00
Max Kellermann
d5c132fca0 db/update/ExcludeList: move code to ParseLine() 2018-07-06 18:25:27 +02:00
Max Kellermann
5f082a2739 output/httpd: remove broken DLNA support code
This code was added in 21851c0673 but
looks completely broken:

- the status code is "206 OK" but "206" would be "Partial Content"

- the "Content-Length" header has a bogus value

- the "Content-RangeX" parameter has different bogus values (why
  "Content-RangeX" anyway and not "Content-Range"?)

Apart from that, there are strange undocumented non-standard headers
which are probably there to work around bugs/expectations in one
broken proprietary client product.  But these days, MPD doesn't bend
over to support broken clients.  So let's kill this code.

Closes 
2018-07-06 17:28:01 +02:00
Max Kellermann
7d6a762845 python/build/libs.py: upgrade FFmpeg to 4.0.1 2018-06-22 22:35:27 +02:00
Max Kellermann
8dcb1f805d db/proxy: support tags "ArtistSort", "AlbumArtistSort", "AlbumSort"
Closes 
2018-05-28 20:14:07 +02:00
Max Kellermann
a8b9e5b9b9 db/proxy: add "password" setting
Closes 
2018-05-28 20:01:08 +02:00
Max Kellermann
04f928e2b0 doc/user.xml: remove copy&paste fallout 2018-05-28 20:01:08 +02:00
Max Kellermann
c7a803c922 increment version number to 0.20.21 2018-05-28 19:46:54 +02:00
62 changed files with 581 additions and 277 deletions

@@ -234,6 +234,7 @@ libmpd_a_SOURCES += \
endif
CURL_SOURCES = \
src/lib/curl/Error.hxx \
src/lib/curl/Version.cxx src/lib/curl/Version.hxx \
src/lib/curl/Global.cxx src/lib/curl/Global.hxx \
src/lib/curl/Request.cxx src/lib/curl/Request.hxx \
@@ -293,7 +294,7 @@ libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a
$(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS)
ANDROID_SDK_BUILD_TOOLS_VERSION = 27.0.0
ANDROID_SDK_PLATFORM = android-17
ANDROID_SDK_PLATFORM = android-21
ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)
ANDROID_SDK_PLATFORM_DIR = $(ANDROID_SDK)/platforms/$(ANDROID_SDK_PLATFORM)
@@ -340,7 +341,7 @@ android/build/include/org_musicpd_Bridge.h: android/build/classes.dex
BUILT_SOURCES = android/build/include/org_musicpd_Bridge.h
android/build/lib/armeabi-v7a/libmpd.so: libmpd.so
android/build/lib/$(ANDROID_ABI)/libmpd.so: libmpd.so
mkdir -p $(@D)
rm -f $@
$(STRIP) -o $@ $<
@@ -350,7 +351,7 @@ android/build/res/drawable/icon.png: mpd.svg
rsvg-convert --width=48 --height=48 $< -o $@
.DELETE_ON_ERROR: android/build/unsigned.apk
android/build/unsigned.apk: android/build/classes.dex android/build/resources.apk android/build/lib/armeabi-v7a/libmpd.so
android/build/unsigned.apk: android/build/classes.dex android/build/resources.apk android/build/lib/$(ANDROID_ABI)/libmpd.so
cp android/build/resources.apk $@
cd $(dir $@) && zip -q -r $(notdir $@) classes.dex lib
@@ -444,6 +445,7 @@ libutil_a_SOURCES = \
src/util/NumberParser.hxx \
src/util/MimeType.cxx src/util/MimeType.hxx \
src/util/StringBuffer.hxx \
src/util/StringFormat.hxx \
src/util/StringPointer.hxx \
src/util/StringView.cxx src/util/StringView.hxx \
src/util/AllocatedString.cxx src/util/AllocatedString.hxx \
@@ -712,6 +714,7 @@ NFS_SOURCES = \
src/lib/nfs/Cancellable.hxx \
src/lib/nfs/Lease.hxx \
src/lib/nfs/Connection.cxx src/lib/nfs/Connection.hxx \
src/lib/nfs/Error.cxx src/lib/nfs/Error.hxx \
src/lib/nfs/Manager.cxx src/lib/nfs/Manager.hxx \
src/lib/nfs/Glue.cxx src/lib/nfs/Glue.hxx \
src/lib/nfs/Base.cxx src/lib/nfs/Base.hxx \
@@ -734,6 +737,8 @@ libstorage_a_SOURCES = \
src/storage/FileInfo.hxx
libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(CURL_CFLAGS) \
$(EXPAT_CFLAGS) \
$(NFS_CFLAGS) \
$(SMBCLIENT_CFLAGS)
@@ -1292,7 +1297,7 @@ endif
#
libinput_a_SOURCES = \
src/input/Domain.cxx src/input/Domain.hxx \
src/input/Error.cxx src/input/Error.hxx \
src/input/Init.cxx src/input/Init.hxx \
src/input/Registry.cxx src/input/Registry.hxx \
src/input/Open.cxx \

13
NEWS

@@ -1,3 +1,16 @@
ver 0.20.21 (2018/08/17)
* database
- proxy: add "password" setting
- proxy: support tags "ArtistSort", "AlbumArtistSort", "AlbumSort"
- simple: allow .mpdignore comments only at start of line
* output
- httpd: remove broken DLNA support code
* playlist
- cue: support file type declaration "FLAC" (non-standard)
* URI schemes are case insensitive
* Android, Windows
- enable the "curl" storage plugin
ver 0.20.20 (2018/05/22)
* protocol
- fix "modified-since" filter regression

@@ -2,10 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="19"
android:versionName="0.20.20">
android:versionCode="20"
android:versionName="0.20.21">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21"/>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Main"

@@ -29,6 +29,15 @@ android_abis = {
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
},
'arm64-v8a': {
'android_api_level': '21',
'arch': 'aarch64-linux-android',
'ndk_arch': 'arm64',
'toolchain_arch': 'aarch64-linux-android',
'llvm_triple': 'aarch64-none-linux-android',
'cflags': '',
},
'x86': {
'arch': 'i686-linux-android',
'ndk_arch': 'x86',
@@ -65,7 +74,8 @@ class AndroidNdkToolchain:
self.build_path = build_path
ndk_arch = abi_info['ndk_arch']
ndk_platform = 'android-14'
android_api_level = '21'
ndk_platform = 'android-' + android_api_level
# select the NDK compiler
gcc_version = '4.9'
@@ -106,7 +116,7 @@ class AndroidNdkToolchain:
self.cppflags = '--sysroot=' + sysroot + \
' -isystem ' + os.path.join(install_prefix, 'include') + \
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
' -D__ANDROID_API__=14'
' -D__ANDROID_API__=' + android_api_level
self.ldflags = '--sysroot=' + sysroot + \
' -L' + os.path.join(install_prefix, 'lib') + \
' -L' + os.path.join(target_root, 'usr', 'lib') + \
@@ -116,6 +126,7 @@ class AndroidNdkToolchain:
self.is_arm = ndk_arch == 'arm'
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_aarch64 = ndk_arch == 'arm64'
self.is_windows = False
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
@@ -146,6 +157,7 @@ thirdparty_libs = [
libid3tag,
ffmpeg,
curl,
libexpat,
libnfs,
boost,
]

@@ -49,7 +49,7 @@ public class Main extends Activity implements Runnable {
TextView tv = new TextView(this);
tv.setText("Failed to load the native MPD libary.\n" +
"Report this problem to us, and include the following information:\n" +
"ABI=" + Build.CPU_ABI + "\n" +
"SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" +
"PRODUCT=" + Build.PRODUCT + "\n" +
"FINGERPRINT=" + Build.FINGERPRINT + "\n" +
"error=" + Loader.error);

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.20, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.20.21, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=20
VERSION_REVISION=20
VERSION_REVISION=21
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -16,7 +16,7 @@ AC_CONFIG_MACRO_DIR([m4])
AC_DEFINE(PROTOCOL_VERSION, "0.20.0", [The MPD protocol version])
GIT_COMMIT=`GIT_DIR="$srcdir/.git" git describe --dirty --always 2>/dev/null`
GIT_COMMIT=`cd "$srcdir" && git describe --dirty --always 2>/dev/null`
if test x$GIT_COMMIT != x; then
AC_DEFINE_UNQUOTED(GIT_COMMIT, ["$GIT_COMMIT"], [The current git commit])
fi
@@ -186,6 +186,7 @@ AC_ARG_WITH([android-sdk],
[Directory for Android SDK]),
[], [with_android_sdk=no])
android_abi=""
if test x$host_is_android = xyes; then
if test x$with_android_sdk = xno; then
AC_MSG_ERROR([Android build requires option --with-android-sdk=DIR])
@@ -194,9 +195,15 @@ if test x$host_is_android = xyes; then
if ! test -x $with_android_sdk/tools/android; then
AC_MSG_ERROR([Android SDK not found in $with_android_sdk])
fi
AS_CASE([$host_cpu],
[i686], [android_abi="x86"],
[aarch64], [android_abi="arm64-v8a"],
[android_abi="armeabi-v7a"])
fi
AC_SUBST(ANDROID_SDK, [$with_android_sdk])
AC_SUBST(ANDROID_ABI, [$android_abi])
dnl ---------------------------------------------------------------------------
dnl Language Checks
@@ -315,7 +322,7 @@ else
fi
default_enable_daemon=yes
if test x$host_is_android = xyes || test x$host_is_android = xyes; then
if test x$host_is_android = xyes || test x$host_is_windows = xyes; then
default_enable_daemon=no
fi
AC_ARG_ENABLE(daemon,
@@ -402,7 +409,7 @@ AC_ARG_ENABLE(recorder-output,
AC_ARG_ENABLE(sidplay,
AS_HELP_STRING([--enable-sidplay],
[enable C64 SID support via libsidplay2]),,
[enable C64 SID support via libsidplayfp or libsidplay2]),,
enable_sidplay=auto)
AC_ARG_ENABLE(shout,
@@ -1005,7 +1012,7 @@ if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
[found_sidplay=no])
MPD_AUTO_PRE(sidplay, [sidplay decoder plugin],
[libsidplay2 not found])
[libsidplay2 or libsidutils not found])
fi
if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
@@ -1017,10 +1024,11 @@ if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
fi
if test x$enable_sidplay = xyes; then
SIDPLAY_LIBS="$SIDPLAY_LIBS -lresid-builder"
AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support])
AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplayfp or libsidplay2 support])
if test x$found_sidplayfp = xyes; then
AC_DEFINE(HAVE_SIDPLAYFP, 1, [Define if libsidplayfp is used instead of libsidplay2])
else
SIDPLAY_LIBS="$SIDPLAY_LIBS -lresid-builder"
fi
fi

@@ -2087,13 +2087,6 @@ run</programlisting>
database.
</para>
<para>
Note that unless overridden by the below settings (e.g. by
setting them to a blank value), general curl configuration
from environment variables such as http_proxy or specified
in ~/.curlrc will be in effect.
</para>
<informaltable>
<tgroup cols="2">
<thead>
@@ -2121,6 +2114,15 @@ run</programlisting>
<application>MPD</application> instance.
</entry>
</row>
<row>
<entry>
<varname>password</varname>
</entry>
<entry>
The password used to log in to the "master"
<application>MPD</application> instance.
</entry>
</row>
<row>
<entry>
<varname>keepalive</varname>

@@ -21,6 +21,8 @@ class FfmpegProject(Project):
if toolchain.is_arm:
arch = 'arm'
elif toolchain.is_aarch64:
arch = 'aarch64'
else:
arch = 'x86'

@@ -112,8 +112,8 @@ liblame = AutotoolsProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-4.0.tar.xz',
'ed945daf40b124e77a685893cc025d086f638bc703183460aff49508edb3a43f',
'http://ffmpeg.org/releases/ffmpeg-4.0.2.tar.xz',
'a95c0cc9eb990e94031d2183f2e6e444cc61c99f6f182d1575c433d62afb2f97',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',
@@ -341,8 +341,8 @@ ffmpeg = FfmpegProject(
)
curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.60.0.tar.xz',
'8736ff8ded89ddf7e926eec7b16f82597d029fc1469f3a551f1fafaac164e6a0',
'http://curl.haxx.se/download/curl-7.61.0.tar.xz',
'ef6e55192d04713673b4409ccbcb4cb6cd723137d6e10ca45b0c593a454e1720',
'lib/libcurl.a',
[
'--disable-shared', '--enable-static',
@@ -364,9 +364,19 @@ curl = AutotoolsProject(
patches='src/lib/curl/patches',
)
libexpat = AutotoolsProject(
'https://github.com/libexpat/libexpat/releases/download/R_2_2_6/expat-2.2.6.tar.bz2',
'17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2',
'lib/libexpat.a',
[
'--disable-shared', '--enable-static',
'--without-docbook',
],
)
libnfs = AutotoolsProject(
'https://github.com/sahlberg/libnfs/archive/libnfs-2.0.0.tar.gz',
'7ea6cd8fa6c461d01091e584d424d28e137d23ff4b65b95d01a3fd0ef95d120e',
'https://github.com/sahlberg/libnfs/archive/libnfs-3.0.0.tar.gz',
'445d92c5fc55e4a5b115e358e60486cf8f87ee50e0103d46a02e7fb4618566a5',
'lib/libnfs.a',
[
'--disable-shared', '--enable-static',
@@ -374,13 +384,15 @@ libnfs = AutotoolsProject(
# work around -Wtautological-compare
'--disable-werror',
'--disable-utils', '--disable-examples',
],
base='libnfs-libnfs-2.0.0',
base='libnfs-libnfs-3.0.0',
autoreconf=True,
)
boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.66.0/boost_1_66_0.tar.bz2',
'5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9',
'http://downloads.sourceforge.net/project/boost/boost/1.68.0/boost_1_68_0.tar.bz2',
'7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7',
'include/boost/version.hpp',
)

@@ -20,6 +20,9 @@ class MesonProject(Project):
cpu = 'armv7'
else:
cpu = 'armv6'
elif toolchain.is_aarch64:
cpu_family = 'aarch64'
cpu = 'arm64-v8a'
else:
cpu_family = 'x86'
if 'x86_64' in toolchain.arch:
@@ -51,6 +54,9 @@ c_link_args = %s
cpp_args = %s
cpp_link_args = %s
# Keep Meson from executing Android-x86 test binariees
needs_exe_wrapper = true
[host_machine]
system = '%s'
cpu_family = '%s'

@@ -19,9 +19,9 @@
#include "AudioFormat.hxx"
#include "util/StringBuffer.hxx"
#include "util/StringFormat.hxx"
#include <assert.h>
#include <stdio.h>
void
AudioFormat::ApplyMask(AudioFormat mask) noexcept
@@ -44,21 +44,16 @@ AudioFormat::ApplyMask(AudioFormat mask) noexcept
StringBuffer<24>
ToString(const AudioFormat af) noexcept
{
StringBuffer<24> buffer;
if (af.format == SampleFormat::DSD && af.sample_rate > 0 &&
af.sample_rate % 44100 == 0) {
/* use shortcuts such as "dsd64" which implies the
sample rate */
snprintf(buffer.data(), buffer.capacity(), "dsd%u:%u",
af.sample_rate * 8 / 44100,
af.channels);
return buffer;
return StringFormat<24>("dsd%u:%u",
af.sample_rate * 8 / 44100,
af.channels);
}
snprintf(buffer.data(), buffer.capacity(), "%u:%s:%u",
af.sample_rate, sample_format_to_string(af.format),
af.channels);
return buffer;
return StringFormat<24>("%u:%s:%u",
af.sample_rate, sample_format_to_string(af.format),
af.channels);
}

@@ -23,7 +23,6 @@
#include "pcm/SampleFormat.hxx"
#include "Compiler.h"
#include <assert.h>
#include <stdint.h>
#include <stddef.h>

@@ -23,7 +23,7 @@
#include "fs/AllocatedPath.hxx"
#include "ls.hxx"
#include "util/UriUtil.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#ifdef ENABLE_DATABASE
#include "storage/StorageInterface.hxx"
@@ -83,7 +83,7 @@ LocateUri(const char *uri, const Client *client
)
{
/* skip the obsolete "file://" prefix */
const char *path_utf8 = StringAfterPrefix(uri, "file://");
const char *path_utf8 = StringAfterPrefixCaseASCII(uri, "file://");
if (path_utf8 != nullptr) {
if (!PathTraitsUTF8::IsAbsolute(path_utf8))
throw std::runtime_error("Malformed file:// URI");

@@ -82,6 +82,7 @@ class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor {
DatabaseListener &listener;
const std::string host;
const std::string password;
const unsigned port;
const bool keepalive;
@@ -169,6 +170,13 @@ static constexpr struct {
#if LIBMPDCLIENT_CHECK_VERSION(2,10,0)
{ TAG_MUSICBRAINZ_RELEASETRACKID,
MPD_TAG_MUSICBRAINZ_RELEASETRACKID },
#endif
#if LIBMPDCLIENT_CHECK_VERSION(2,11,0)
{ TAG_ARTIST_SORT, MPD_TAG_ARTIST_SORT },
{ TAG_ALBUM_ARTIST_SORT, MPD_TAG_ALBUM_ARTIST_SORT },
#endif
#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
{ TAG_ALBUM_SORT, MPD_TAG_ALBUM_SORT },
#endif
{ TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT }
};
@@ -359,6 +367,7 @@ ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
SocketMonitor(_loop), IdleMonitor(_loop),
listener(_listener),
host(block.GetBlockValue("host", "")),
password(block.GetBlockValue("password", "")),
port(block.GetBlockValue("port", 0u)),
keepalive(block.GetBlockValue("keepalive", false))
{
@@ -402,6 +411,10 @@ ProxyDatabase::Connect()
try {
CheckError(connection);
if (!password.empty() &&
!mpd_run_password(connection, password.c_str()))
ThrowError(connection);
} catch (...) {
mpd_connection_free(connection);
connection = nullptr;

@@ -27,6 +27,7 @@
#include "util/UriUtil.hxx"
#include "util/RuntimeError.hxx"
#include "util/ScopeExit.hxx"
#include "util/StringFormat.hxx"
#include <stdio.h>
@@ -47,10 +48,6 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
unsigned &didreadp,
unsigned &totalp) const
{
// Create request
char ofbuf[100], cntbuf[100];
sprintf(ofbuf, "%u", offset);
sprintf(cntbuf, "%u", count);
// Some devices require an empty SortCriteria, else bad params
IXML_Document *request =
MakeActionHelper("Browse", m_serviceType.c_str(),
@@ -58,8 +55,10 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
"BrowseFlag", "BrowseDirectChildren",
"Filter", "*",
"SortCriteria", "",
"StartingIndex", ofbuf,
"RequestedCount", cntbuf);
"StartingIndex",
StringFormat<32>("%u", offset).c_str(),
"RequestedCount",
StringFormat<32>("%u", count).c_str());
if (request == nullptr)
throw std::runtime_error("UpnpMakeAction() failed");
@@ -112,15 +111,13 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
unsigned offset = 0, total = -1, count;
do {
char ofbuf[100];
sprintf(ofbuf, "%d", offset);
UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
"ContainerID", objectId,
"SearchCriteria", ss,
"Filter", "*",
"SortCriteria", "",
"StartingIndex", ofbuf,
"StartingIndex",
StringFormat<32>("%u", offset).c_str(),
"RequestedCount", "0")); // Setting a value here gets twonky into fits
if (!request)
throw std::runtime_error("UpnpMakeAction() failed");

@@ -26,8 +26,8 @@
#include "ExcludeList.hxx"
#include "fs/Path.hxx"
#include "fs/NarrowPath.hxx"
#include "fs/io/TextFile.hxx"
#include "system/Error.hxx"
#include "input/TextInputStream.hxx"
#include "util/StringUtil.hxx"
#include "Log.hxx"
#include <stdexcept>
@@ -35,35 +35,29 @@
#include <assert.h>
#include <string.h>
inline void
ExcludeList::ParseLine(char *line) noexcept
{
char *p = Strip(line);
if (*p != 0 && *p != '#')
patterns.emplace_front(p);
}
bool
ExcludeList::LoadFile(Path path_fs) noexcept
try {
ExcludeList::Load(InputStreamPtr is)
{
#ifdef HAVE_CLASS_GLOB
TextFile file(path_fs);
TextInputStream tis(std::move(is));
char *line;
while ((line = file.ReadLine()) != nullptr) {
char *p = strchr(line, '#');
if (p != nullptr)
*p = 0;
p = Strip(line);
if (*p != 0)
patterns.emplace_front(p);
}
while ((line = tis.ReadLine()) != nullptr)
ParseLine(line);
#else
/* not implemented */
(void)path_fs;
#endif
return true;
} catch (const std::system_error &e) {
if (!IsFileNotFound(e))
LogError(e);
return false;
} catch (const std::exception &e) {
LogError(e);
return false;
}
bool

@@ -28,6 +28,7 @@
#include "check.h"
#include "Compiler.h"
#include "fs/Glob.hxx"
#include "input/Ptr.hxx"
#ifdef HAVE_CLASS_GLOB
#include <forward_list>
@@ -62,13 +63,16 @@ public:
/**
* Loads and parses a .mpdignore file.
*/
bool LoadFile(Path path_fs) noexcept;
bool Load(InputStreamPtr is);
/**
* Checks whether one of the patterns in the .mpdignore file matches
* the specified file name.
*/
bool Check(Path name_fs) const noexcept;
private:
void ParseLine(char *line) noexcept;
};

@@ -36,6 +36,9 @@
#include "fs/Traits.hxx"
#include "fs/FileSystem.hxx"
#include "storage/FileInfo.hxx"
#include "input/InputStream.hxx"
#include "input/Error.hxx"
#include "thread/Cond.hxx"
#include "util/Alloc.hxx"
#include "util/StringCompare.hxx"
#include "util/UriUtil.hxx"
@@ -345,11 +348,16 @@ UpdateWalk::UpdateDirectory(Directory &directory,
ExcludeList child_exclude_list(exclude_list);
{
const auto exclude_path_fs =
storage.MapChildFS(directory.GetPath(), ".mpdignore");
if (!exclude_path_fs.IsNull())
child_exclude_list.LoadFile(exclude_path_fs);
try {
Mutex mutex;
Cond cond;
auto is = InputStream::OpenReady(PathTraitsUTF8::Build(storage.MapUTF8(directory.GetPath()).c_str(),
".mpdignore").c_str(),
mutex, cond);
child_exclude_list.Load(std::move(is));
} catch (...) {
if (!IsFileNotFound(std::current_exception()))
LogError(std::current_exception());
}
if (!child_exclude_list.IsEmpty())

@@ -28,7 +28,7 @@
#include "fs/Path.hxx"
#include "fs/AllocatedPath.hxx"
#include "util/ScopeExit.hxx"
#include "util/FormatString.hxx"
#include "util/StringFormat.hxx"
#include "util/UriUtil.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
@@ -38,7 +38,6 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define SUBTUNE_PREFIX "tune_"
@@ -191,20 +190,17 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
tag_handler_invoke_duration(handler, handler_ctx,
SongTime::FromMS(info.play_length));
if (track_count > 1) {
char track[16];
sprintf(track, "%u", song_num + 1);
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track);
}
if (track_count > 1)
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
StringFormat<16>("%u", song_num + 1));
if (info.song != nullptr) {
if (track_count > 1) {
/* start numbering subtunes from 1 */
char tag_title[1024];
snprintf(tag_title, sizeof(tag_title),
"%s (%u/%d)",
info.song, song_num + 1,
track_count);
const auto tag_title =
StringFormat<1024>("%s (%u/%d)",
info.song, song_num + 1,
track_count);
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, tag_title);
} else
@@ -297,9 +293,9 @@ gme_container_scan(Path path_fs)
ScanMusicEmu(emu, i,
add_tag_handler, &tag_builder);
char track_name[64];
snprintf(track_name, sizeof(track_name),
SUBTUNE_PREFIX "%03u.%s", i+1, subtune_suffix);
const auto track_name =
StringFormat<64>(SUBTUNE_PREFIX "%03u.%s", i+1,
subtune_suffix);
tail = list.emplace_after(tail, track_name,
tag_builder.Commit());
}

@@ -26,7 +26,7 @@
#include "fs/Path.hxx"
#include "fs/AllocatedPath.hxx"
#include "util/Macros.hxx"
#include "util/FormatString.hxx"
#include "util/StringFormat.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
#include "Log.hxx"
@@ -413,10 +413,9 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
title = "";
if (n_tracks > 1) {
char tag_title[1024];
snprintf(tag_title, sizeof(tag_title),
"%s (%u/%u)",
title, track, n_tracks);
const auto tag_title =
StringFormat<1024>("%s (%u/%u)",
title, track, n_tracks);
tag_handler_invoke_tag(handler, handler_ctx,
TAG_TITLE, tag_title);
} else
@@ -435,9 +434,8 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
date);
/* track */
char track_buffer[16];
sprintf(track_buffer, "%d", track);
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track_buffer);
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
StringFormat<16>("%u", track));
}
static bool

@@ -20,6 +20,7 @@
#include "config.h"
#include "FileOutputStream.hxx"
#include "system/Error.hxx"
#include "util/StringFormat.hxx"
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
:path(_path), mode(_mode)
@@ -212,10 +213,9 @@ FileOutputStream::Commit()
unlink(GetPath().c_str());
/* hard-link the temporary file to the final path */
char fd_path[64];
snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d",
fd.Get());
if (linkat(AT_FDCWD, fd_path, AT_FDCWD, path.c_str(),
if (linkat(AT_FDCWD,
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
AT_FDCWD, path.c_str(),
AT_SYMLINK_FOLLOW) < 0)
throw FormatErrno("Failed to commit %s",
path.c_str());

@@ -19,7 +19,6 @@
#include "config.h"
#include "AsyncInputStream.hxx"
#include "Domain.hxx"
#include "tag/Tag.hxx"
#include "thread/Cond.hxx"
#include "IOThread.hxx"

53
src/input/Error.cxx Normal file

@@ -0,0 +1,53 @@
/*
* Copyright 2003-2018 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 "config.h"
#include "Error.hxx"
#include "system/Error.hxx"
#ifdef ENABLE_CURL
#include "lib/curl/Error.hxx"
#endif
#ifdef ENABLE_NFS
#include "lib/nfs/Error.hxx"
#include <nfsc/libnfs-raw-nfs.h>
#endif
bool
IsFileNotFound(std::exception_ptr ep)
{
try {
std::rethrow_exception(ep);
} catch (const std::system_error &e) {
return IsFileNotFound(e);
#ifdef ENABLE_CURL
} catch (const HttpStatusError &e) {
return e.GetStatus() == 404;
#endif
#ifdef ENABLE_NFS
} catch (const NfsClientError &e) {
return e.GetCode() == NFS3ERR_NOENT;
#endif
} catch (...) {
}
return false;
}

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_INPUT_DOMAIN_HXX
#define MPD_INPUT_DOMAIN_HXX
#ifndef INPUT_ERROR_HXX
#define INPUT_ERROR_HXX
class Domain;
#include "check.h"
#include "Compiler.h"
extern const Domain input_domain;
#include <exception>
/**
* Was this exception thrown because the requested file does not
* exist? This function attempts to recognize exceptions thrown by
* various input plugins.
*/
gcc_pure
bool
IsFileNotFound(std::exception_ptr e);
#endif

@@ -20,7 +20,7 @@
#include "config.h"
#include "InputStream.hxx"
#include "thread/Cond.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#include <stdexcept>
@@ -77,8 +77,8 @@ gcc_pure
static bool
ExpensiveSeeking(const char *uri) noexcept
{
return StringStartsWith(uri, "http://") ||
StringStartsWith(uri, "https://");
return StringStartsWithCaseASCII(uri, "http://") ||
StringStartsWithCaseASCII(uri, "https://");
}
bool

@@ -29,4 +29,10 @@
*/
typedef uint64_t offset_type;
/**
* To format an offset_type with printf(). To use this, include
* <cinttypes>.
*/
#define PRIoffset PRIu64
#endif

@@ -22,7 +22,6 @@
#include "Registry.hxx"
#include "InputPlugin.hxx"
#include "LocalOpen.hxx"
#include "Domain.hxx"
#include "plugins/RewindInputPlugin.hxx"
#include "fs/Traits.hxx"
#include "fs/AllocatedPath.hxx"

@@ -33,7 +33,7 @@
#include "util/RuntimeError.hxx"
#include "util/StringCompare.hxx"
#include "util/ReusableArray.hxx"
#include "util/ASCII.hxx"
#include "Log.hxx"
#include "event/MultiSocketMonitor.hxx"
#include "event/DeferredMonitor.hxx"
@@ -147,7 +147,7 @@ private:
inline InputStream *
AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond)
{
const char *device = StringAfterPrefix(uri, "alsa://");
const char *device = StringAfterPrefixCaseASCII(uri, "alsa://");
if (device == nullptr)
return nullptr;

@@ -26,7 +26,7 @@
#include "../InputStream.hxx"
#include "../InputPlugin.hxx"
#include "util/StringUtil.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
@@ -128,7 +128,7 @@ struct cdio_uri {
static bool
parse_cdio_uri(struct cdio_uri *dest, const char *src)
{
if (!StringStartsWith(src, "cdda://"))
if (!StringStartsWithCaseASCII(src, "cdda://"))
return false;
src += 7;

@@ -19,6 +19,7 @@
#include "config.h"
#include "CurlInputPlugin.hxx"
#include "lib/curl/Error.hxx"
#include "lib/curl/Easy.hxx"
#include "lib/curl/Global.hxx"
#include "lib/curl/Request.hxx"
@@ -34,12 +35,15 @@
#include "IOThread.hxx"
#include "util/ASCII.hxx"
#include "util/StringUtil.hxx"
#include "util/StringFormat.hxx"
#include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
#include "PluginUnavailable.hxx"
#include <cinttypes>
#include <assert.h>
#include <string.h>
@@ -185,7 +189,9 @@ CurlInputStream::OnHeaders(unsigned status,
assert(!postponed_exception);
if (status < 200 || status >= 300)
throw FormatRuntimeError("got HTTP status %ld", status);
throw HttpStatusError(status,
StringFormat<40>("got HTTP status %u",
status).c_str());
const std::lock_guard<Mutex> protect(mutex);
@@ -371,13 +377,10 @@ CurlInputStream::InitEasy()
if (proxy_port > 0)
request->SetOption(CURLOPT_PROXYPORT, (long)proxy_port);
if (proxy_user != nullptr && proxy_password != nullptr) {
char proxy_auth_str[1024];
snprintf(proxy_auth_str, sizeof(proxy_auth_str),
"%s:%s",
proxy_user, proxy_password);
request->SetOption(CURLOPT_PROXYUSERPWD, proxy_auth_str);
}
if (proxy_user != nullptr && proxy_password != nullptr)
request->SetOption(CURLOPT_PROXYUSERPWD,
StringFormat<1024>("%s:%s", proxy_user,
proxy_password).c_str());
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1l : 0l);
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l);
@@ -414,16 +417,10 @@ CurlInputStream::SeekInternal(offset_type new_offset)
/* send the "Range" header */
if (offset > 0) {
char range[32];
#ifdef _WIN32
// TODO: what can we use on Windows to format 64 bit?
sprintf(range, "%lu-", (long)offset);
#else
sprintf(range, "%llu-", (unsigned long long)offset);
#endif
request->SetOption(CURLOPT_RANGE, range);
}
if (offset > 0)
request->SetOption(CURLOPT_RANGE,
StringFormat<40>("%" PRIoffset "-",
offset).c_str());
StartRequest();
}
@@ -461,8 +458,8 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond)
static InputStream *
input_curl_open(const char *url, Mutex &mutex, Cond &cond)
{
if (strncmp(url, "http://", 7) != 0 &&
strncmp(url, "https://", 8) != 0)
if (!StringStartsWithCaseASCII(url, "http://") &&
!StringStartsWithCaseASCII(url, "https://"))
return nullptr;
return CurlInputStream::Open(url, mutex, cond);

@@ -28,7 +28,7 @@
#include "../InputStream.hxx"
#include "../InputPlugin.hxx"
#include "PluginUnavailable.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
extern "C" {
#include <libavformat/avio.h>
@@ -85,12 +85,12 @@ static InputStream *
input_ffmpeg_open(const char *uri,
Mutex &mutex, Cond &cond)
{
if (!StringStartsWith(uri, "gopher://") &&
!StringStartsWith(uri, "rtp://") &&
!StringStartsWith(uri, "rtsp://") &&
!StringStartsWith(uri, "rtmp://") &&
!StringStartsWith(uri, "rtmpt://") &&
!StringStartsWith(uri, "rtmps://"))
if (!StringStartsWithCaseASCII(uri, "gopher://") &&
!StringStartsWithCaseASCII(uri, "rtp://") &&
!StringStartsWithCaseASCII(uri, "rtsp://") &&
!StringStartsWithCaseASCII(uri, "rtmp://") &&
!StringStartsWithCaseASCII(uri, "rtmpt://") &&
!StringStartsWithCaseASCII(uri, "rtmps://"))
return nullptr;
AVIOContext *h;

@@ -65,12 +65,9 @@ OpenFileInputStream(Path path,
throw FormatRuntimeError("Not a regular file: %s",
path.c_str());
#if !defined(__BIONIC__) || __ANDROID_API__ >= 21
/* posix_fadvise() requires Android API 21 */
#ifdef POSIX_FADV_SEQUENTIAL
posix_fadvise(reader.GetFD().Get(), (off_t)0, info.GetSize(),
POSIX_FADV_SEQUENTIAL);
#endif
#endif
return InputStreamPtr(new FileInputStream(path.ToUTF8().c_str(),

@@ -22,7 +22,7 @@
#include "input/ThreadInputStream.hxx"
#include "input/InputPlugin.hxx"
#include "system/Error.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#include <libmms/mmsx.h>
@@ -74,10 +74,10 @@ static InputStream *
input_mms_open(const char *url,
Mutex &mutex, Cond &cond)
{
if (!StringStartsWith(url, "mms://") &&
!StringStartsWith(url, "mmsh://") &&
!StringStartsWith(url, "mmst://") &&
!StringStartsWith(url, "mmsu://"))
if (!StringStartsWithCaseASCII(url, "mms://") &&
!StringStartsWithCaseASCII(url, "mmsh://") &&
!StringStartsWithCaseASCII(url, "mmst://") &&
!StringStartsWithCaseASCII(url, "mmsu://"))
return nullptr;
auto m = new MmsInputStream(url, mutex, cond);

@@ -23,9 +23,7 @@
#include "../InputPlugin.hxx"
#include "lib/nfs/Glue.hxx"
#include "lib/nfs/FileReader.hxx"
#include "util/StringCompare.hxx"
#include <string.h>
#include "util/ASCII.hxx"
/**
* Do not buffer more than this number of bytes. It should be a
@@ -219,7 +217,7 @@ static InputStream *
input_nfs_open(const char *uri,
Mutex &mutex, Cond &cond)
{
if (!StringStartsWith(uri, "nfs://"))
if (!StringStartsWithCaseASCII(uri, "nfs://"))
return nullptr;
NfsInputStream *is = new NfsInputStream(uri, mutex, cond);

@@ -25,7 +25,7 @@
#include "../InputPlugin.hxx"
#include "PluginUnavailable.hxx"
#include "system/Error.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#include <libsmbclient.h>
@@ -87,7 +87,7 @@ static InputStream *
input_smbclient_open(const char *uri,
Mutex &mutex, Cond &cond)
{
if (!StringStartsWith(uri, "smb://"))
if (!StringStartsWithCaseASCII(uri, "smb://"))
return nullptr;
const std::lock_guard<Mutex> protect(smbclient_mutex);

@@ -1,5 +1,5 @@
/*
* Copyright 2003-2017 The Music Player Daemon Project
* Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,26 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Domain.hxx"
#include "util/Domain.hxx"
#ifndef CURL_ERROR_HXX
#define CURL_ERROR_HXX
const Domain input_domain("input");
#include <stdexcept>
/**
* Thrown when an unsuccessful status was received from the HTTP
* server.
*/
class HttpStatusError : public std::runtime_error {
unsigned status;
public:
template<typename M>
explicit HttpStatusError(unsigned _status, M &&_msg) noexcept
:std::runtime_error(std::forward<M>(_msg)), status(_status) {}
unsigned GetStatus() const noexcept {
return status;
}
};
#endif

@@ -25,6 +25,7 @@
#include "Domain.hxx"
#include "LogV.hxx"
#include "util/Domain.hxx"
#include "util/StringFormat.hxx"
extern "C" {
#include <libavutil/log.h>
@@ -57,9 +58,10 @@ FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl)
cls = *(const AVClass *const*)ptr;
if (cls != nullptr) {
char domain[64];
snprintf(domain, sizeof(domain), "%s/%s",
ffmpeg_domain.GetName(), cls->item_name(ptr));
const auto domain =
StringFormat<64>("%s/%s",
ffmpeg_domain.GetName(),
cls->item_name(ptr));
const Domain d(domain);
LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl);
}

@@ -19,6 +19,7 @@
#include "config.h"
#include "Connection.hxx"
#include "Error.hxx"
#include "Lease.hxx"
#include "Callback.hxx"
#include "event/Loop.hxx"
@@ -139,7 +140,7 @@ NfsConnection::CancellableCallback::Callback(int err, void *data)
if (err >= 0)
cb.OnNfsCallback((unsigned)err, data);
else
cb.OnNfsError(std::make_exception_ptr(std::runtime_error((const char *)data)));
cb.OnNfsError(std::make_exception_ptr(NfsClientError(-err, (const char *)data)));
} else {
if (open) {
/* a nfs_open_async() call was cancelled - to

76
src/lib/nfs/Error.cxx Normal file

@@ -0,0 +1,76 @@
/*
* Copyright 2007-2017 Content Management AG
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - 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 "Error.hxx"
#include "util/StringFormat.hxx"
extern "C" {
#include <nfsc/libnfs.h>
}
#include <assert.h>
#include <string.h>
static StringBuffer<256>
FormatNfsClientError(struct nfs_context *nfs, const char *msg) noexcept
{
assert(msg != nullptr);
const char *msg2 = nfs_get_error(nfs);
return StringFormat<256>("%s: %s", msg, msg2);
}
NfsClientError::NfsClientError(struct nfs_context *nfs, const char *msg) noexcept
:std::runtime_error(FormatNfsClientError(nfs, msg).c_str()),
code(0) {}
static StringBuffer<256>
FormatNfsClientError(int err, struct nfs_context *nfs, void *data,
const char *msg) noexcept
{
assert(msg != nullptr);
assert(err < 0);
const char *msg2 = (const char *)data;
if (data == nullptr || *(const char *)data == 0) {
msg2 = nfs_get_error(nfs);
if (msg2 == nullptr)
msg2 = strerror(-err);
}
return StringFormat<256>("%s: %s", msg, msg2);
}
NfsClientError::NfsClientError(int err, struct nfs_context *nfs, void *data,
const char *msg) noexcept
:std::runtime_error(FormatNfsClientError(err, nfs, data, msg).c_str()),
code(-err) {}

58
src/lib/nfs/Error.hxx Normal file

@@ -0,0 +1,58 @@
/*
* Copyright 2007-2017 Content Management AG
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - 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 NFS_ERROR_HXX
#define NFS_ERROR_HXX
#include <stdexcept>
class NfsClientError : public std::runtime_error {
int code;
public:
explicit NfsClientError(const char *_msg) noexcept
:std::runtime_error(_msg), code(0) {}
NfsClientError(int _code, const char *_msg) noexcept
:std::runtime_error(_msg), code(_code) {}
NfsClientError(struct nfs_context *nfs, const char *msg) noexcept;
NfsClientError(int err, struct nfs_context *nfs, void *data,
const char *msg) noexcept;
int GetCode() const noexcept {
return code;
}
};
#endif

@@ -24,7 +24,7 @@
#include "Connection.hxx"
#include "event/Call.hxx"
#include "IOThread.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#include <utility>
@@ -93,7 +93,7 @@ NfsFileReader::Open(const char *uri)
{
assert(state == State::INITIAL);
if (!StringStartsWith(uri, "nfs://"))
if (!StringStartsWithCaseASCII(uri, "nfs://"))
throw std::runtime_error("Malformed nfs:// URI");
uri += 6;

@@ -20,7 +20,7 @@
#include "config.h"
#include "ls.hxx"
#include "client/Response.hxx"
#include "util/StringCompare.hxx"
#include "util/ASCII.hxx"
#include "util/UriUtil.hxx"
#include <assert.h>
@@ -97,7 +97,7 @@ uri_supported_scheme(const char *uri) noexcept
assert(uri_has_scheme(uri));
while (*urlPrefixes) {
if (StringStartsWith(uri, *urlPrefixes))
if (StringStartsWithCaseASCII(uri, *urlPrefixes))
return true;
urlPrefixes++;
}

@@ -37,6 +37,7 @@
#include "config/ConfigGlobal.hxx"
#include "config/Block.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringFormat.hxx"
#include "Log.hxx"
#include <stdexcept>

@@ -36,6 +36,8 @@
#include <roaraudio.h>
#undef new
#include <assert.h>
class RoarOutput {
friend struct AudioOutputWrapper<RoarOutput>;

@@ -122,15 +122,6 @@ HttpdClient::HandleLine(const char *line)
return true;
}
if (StringEqualsCaseASCII(line, "transferMode.dlna.org: Streaming", 32)) {
/* Send as dlna */
dlna_streaming_requested = true;
/* metadata is not supported by dlna streaming, so disable it */
metadata_supported = false;
metadata_requested = false;
return true;
}
/* expect more request headers */
return true;
}
@@ -148,22 +139,7 @@ HttpdClient::SendResponse()
assert(state == RESPONSE);
if (dlna_streaming_requested) {
snprintf(buffer, sizeof(buffer),
"HTTP/1.1 206 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: 10000\r\n"
"Content-RangeX: 0-1000000/1000000\r\n"
"transferMode.dlna.org: Streaming\r\n"
"Accept-Ranges: bytes\r\n"
"Connection: close\r\n"
"realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
"contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
"\r\n",
httpd.content_type);
response = buffer;
} else if (metadata_requested) {
if (metadata_requested) {
allocated =
icy_server_metadata_header(httpd.name, httpd.genre,
httpd.website,
@@ -202,7 +178,6 @@ HttpdClient::HttpdClient(HttpdOutput &_httpd, int _fd, EventLoop &_loop,
state(REQUEST),
queue_size(0),
head_method(false),
dlna_streaming_requested(false),
metadata_supported(_metadata_supported),
metadata_requested(false), metadata_sent(true),
metaint(8192), /*TODO: just a std value */

@@ -82,11 +82,6 @@ class HttpdClient final
*/
bool head_method;
/**
* If DLNA streaming was an option.
*/
bool dlna_streaming_requested;
/* ICY */
/**

@@ -35,6 +35,7 @@
#include "SoxrResampler.hxx"
#endif
#include <assert.h>
#include <string.h>
enum class SelectedResampler {

@@ -32,6 +32,8 @@
#include "PcmDop.hxx"
#endif
#include <assert.h>
void
PcmExport::Open(SampleFormat sample_format, unsigned _channels,
Params params)

@@ -202,6 +202,7 @@ CueParser::Feed2(char *p) noexcept
return;
if (strcmp(type, "WAVE") != 0 &&
strcmp(type, "FLAC") != 0 && /* non-standard */
strcmp(type, "MP3") != 0 &&
strcmp(type, "AIFF") != 0) {
state = IGNORE_FILE;

@@ -24,6 +24,7 @@
#include "config/Block.hxx"
#include "input/InputStream.hxx"
#include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/StringCompare.hxx"
#include "util/Alloc.hxx"
#include "util/Domain.hxx"
@@ -68,7 +69,7 @@ soundcloud_resolve(const char* uri)
{
char *u, *ru;
if (StringStartsWith(uri, "https://")) {
if (StringStartsWithCaseASCII(uri, "https://")) {
u = xstrdup(uri);
} else if (StringStartsWith(uri, "soundcloud.com")) {
u = xstrcatdup("https://", uri);
@@ -273,7 +274,7 @@ try {
static SongEnumerator *
soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond)
{
assert(strncmp(uri, "soundcloud://", 13) == 0);
assert(StringEqualsCaseASCII(uri, "soundcloud://", 13));
uri += 13;
char *u = nullptr;

@@ -20,9 +20,9 @@
#ifndef MPD_ACK_H
#define MPD_ACK_H
#include <stdexcept>
#include "util/StringFormat.hxx"
#include <stdio.h>
#include <stdexcept>
class Domain;
@@ -60,9 +60,9 @@ template<typename... Args>
static inline ProtocolError
FormatProtocolError(enum ack code, const char *fmt, Args&&... args) noexcept
{
char buffer[256];
snprintf(buffer, sizeof(buffer), fmt, std::forward<Args>(args)...);
return ProtocolError(code, buffer);
return ProtocolError(code,
StringFormat<256>(fmt,
std::forward<Args>(args)...));
}
#endif

@@ -33,8 +33,10 @@
#include "event/DeferredMonitor.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "util/ASCII.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringCompare.hxx"
#include "util/StringFormat.hxx"
#include "util/TimeParser.hxx"
#include "util/UriUtil.hxx"
@@ -296,9 +298,7 @@ public:
{
request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND");
char buffer[40];
sprintf(buffer, "depth: %u", depth);
request_headers.Append(buffer);
request_headers.Append(StringFormat<40>("depth: %u", depth));
request.SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
@@ -591,8 +591,8 @@ CurlStorage::OpenDirectory(const char *uri_utf8)
static Storage *
CreateCurlStorageURI(EventLoop &event_loop, const char *uri)
{
if (strncmp(uri, "http://", 7) != 0 &&
strncmp(uri, "https://", 8) != 0)
if (!StringStartsWithCaseASCII(uri, "http://") &&
!StringStartsWithCaseASCII(uri, "https://"))
return nullptr;
return new CurlStorage(event_loop, uri);

@@ -35,6 +35,7 @@
#include "event/Call.hxx"
#include "event/DeferredMonitor.hxx"
#include "event/TimeoutMonitor.hxx"
#include "util/ASCII.hxx"
#include "util/StringCompare.hxx"
extern "C" {
@@ -401,11 +402,10 @@ NfsStorage::OpenDirectory(const char *uri_utf8)
static Storage *
CreateNfsStorageURI(EventLoop &event_loop, const char *base)
{
if (strncmp(base, "nfs://", 6) != 0)
const char *p = StringAfterPrefixCaseASCII(base, "nfs://");
if (p == nullptr)
return nullptr;
const char *p = base + 6;
const char *mount = strchr(p, '/');
if (mount == nullptr)
throw std::runtime_error("Malformed nfs:// URI");

@@ -27,6 +27,7 @@
#include "fs/Traits.hxx"
#include "thread/Mutex.hxx"
#include "system/Error.hxx"
#include "util/ASCII.hxx"
#include "util/StringCompare.hxx"
#include "util/ScopeExit.hxx"
@@ -182,7 +183,7 @@ SmbclientDirectoryReader::GetInfo(gcc_unused bool follow)
static Storage *
CreateSmbclientStorageURI(gcc_unused EventLoop &event_loop, const char *base)
{
if (strncmp(base, "smb://", 6) != 0)
if (!StringStartsWithCaseASCII(base, "smb://"))
return nullptr;
SmbclientInit();

@@ -22,21 +22,6 @@
#include "EPollFD.hxx"
#include "FatalError.hxx"
#if defined(__BIONIC__) && __ANDROID_API__ < 21
#include <sys/syscall.h>
#include <fcntl.h>
#define EPOLL_CLOEXEC O_CLOEXEC
static inline int
epoll_create1(int flags)
{
return syscall(__NR_epoll_create1, flags);
}
#endif
EPollFD::EPollFD()
:fd(::epoll_create1(EPOLL_CLOEXEC))
{

@@ -21,8 +21,8 @@
#include "TagHandler.hxx"
#include "TagBuilder.hxx"
#include "util/ASCII.hxx"
#include "util/StringFormat.hxx"
#include <stdio.h>
#include <stdlib.h>
static void
@@ -42,11 +42,8 @@ add_tag_tag(TagType type, const char *value, void *ctx)
/* filter out this extra data and leading zeroes */
char *end;
unsigned n = strtoul(value, &end, 10);
if (value != end) {
char s[21];
if (snprintf(s, 21, "%u", n) > 0)
tag.AddItem(type, s);
}
if (value != end)
tag.AddItem(type, StringFormat<21>("%u", n));
} else
tag.AddItem(type, value);
}

@@ -31,7 +31,7 @@
#endif
#ifdef HAVE_THREAD_NAME
# include <stdio.h>
#include "util/StringFormat.hxx"
#endif
static inline void
@@ -59,9 +59,7 @@ static inline void
FormatThreadName(const char *fmt, gcc_unused Args&&... args)
{
#ifdef HAVE_THREAD_NAME
char buffer[16];
snprintf(buffer, sizeof(buffer), fmt, args...);
SetThreadName(buffer);
SetThreadName(StringFormat<16>(fmt, args...));
#else
(void)fmt;
#endif

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Max Kellermann <max.kellermann@gmail.com>
* Copyright (C) 2013-2018 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,6 +30,7 @@
#ifndef ASCII_HXX
#define ASCII_HXX
#include "StringView.hxx"
#include "Compiler.h"
#include <assert.h>
@@ -69,4 +70,20 @@ StringEqualsCaseASCII(const char *a, const char *b, size_t n) noexcept
return strncasecmp(a, b, n) == 0;
}
gcc_pure gcc_nonnull_all
static inline bool
StringStartsWithCaseASCII(const char *haystack, StringView needle) noexcept
{
return StringEqualsCaseASCII(haystack, needle.data, needle.size);
}
gcc_pure gcc_nonnull_all
static inline const char *
StringAfterPrefixCaseASCII(const char *haystack, StringView needle) noexcept
{
return StringStartsWithCaseASCII(haystack, needle)
? haystack + needle.size
: nullptr;
}
#endif

@@ -23,14 +23,9 @@
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <string.h>
#endif
AllocatedString<>
FormatStringV(const char *fmt, va_list args)
{
#ifndef _WIN32
va_list tmp;
va_copy(tmp, args);
const int length = vsnprintf(NULL, 0, fmt, tmp);
@@ -43,22 +38,6 @@ FormatStringV(const char *fmt, va_list args)
char *buffer = new char[length + 1];
vsnprintf(buffer, length + 1, fmt, args);
return AllocatedString<>::Donate(buffer);
#else
/* On mingw32, snprintf() expects a 64 bit integer instead of
a "long int" for "%li". This is not consistent with our
expectation, so we're using plain sprintf() here, hoping
the static buffer is large enough. Sorry for this hack,
but WIN32 development is so painful, I'm not in the mood to
do it properly now. */
char buffer[16384];
vsprintf(buffer, fmt, args);
const size_t length = strlen(buffer);
char *p = new char[length + 1];
memcpy(p, buffer, length + 1);
return AllocatedString<>::Donate(p);
#endif
}
AllocatedString<>

69
src/util/StringFormat.hxx Normal file

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2010-2015 Max Kellermann <max.kellermann@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - 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 STRING_FORMAT_HXX
#define STRING_FORMAT_HXX
#include "StringBuffer.hxx"
#include <stdio.h>
template<typename... Args>
static inline void
StringFormat(char *buffer, size_t size,
const char *fmt, Args&&... args) noexcept
{
snprintf(buffer, size, fmt, args...);
}
template<size_t CAPACITY, typename... Args>
static inline void
StringFormat(StringBuffer<CAPACITY> &buffer,
const char *fmt, Args&&... args) noexcept
{
StringFormat(buffer.data(), buffer.capacity(), fmt, args...);
}
template<size_t CAPACITY, typename... Args>
static inline StringBuffer<CAPACITY>
StringFormat(const char *fmt, Args&&... args) noexcept
{
StringBuffer<CAPACITY> result;
StringFormat(result, fmt, args...);
return result;
}
template<typename... Args>
static inline void
StringFormatUnsafe(char *buffer, const char *fmt, Args&&... args) noexcept
{
sprintf(buffer, fmt, args...);
}
#endif

@@ -18,7 +18,7 @@
*/
#include "UriUtil.hxx"
#include "StringCompare.hxx"
#include "ASCII.hxx"
#include "CharUtil.hxx"
#include <assert.h>
@@ -169,7 +169,7 @@ SkipUriScheme(const char *uri) noexcept
{
const char *const schemes[] = { "http://", "https://", "ftp://" };
for (auto scheme : schemes) {
auto result = StringAfterPrefix(uri, scheme);
auto result = StringAfterPrefixCaseASCII(uri, scheme);
if (result != nullptr)
return result;
}

@@ -65,6 +65,7 @@ class CrossGccToolchain:
self.is_arm = arch.startswith('arm')
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_aarch64 = arch == 'aarch64'
self.is_windows = 'mingw32' in arch
self.env = dict(os.environ)
@@ -86,6 +87,7 @@ thirdparty_libs = [
liblame,
ffmpeg,
curl,
libexpat,
libnfs,
boost,
]