diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | NEWS | 21 | ||||
-rw-r--r-- | m4/faad.m4 | 31 | ||||
-rw-r--r-- | src/Main.cxx | 29 | ||||
-rw-r--r-- | src/Main.hxx | 11 | ||||
-rw-r--r-- | src/PlaylistFile.cxx | 4 | ||||
-rw-r--r-- | src/TagStream.cxx | 3 | ||||
-rw-r--r-- | src/decoder/DecoderThread.cxx | 3 | ||||
-rw-r--r-- | src/decoder/plugins/FaadDecoderPlugin.cxx | 12 | ||||
-rw-r--r-- | src/decoder/plugins/MadDecoderPlugin.cxx | 2 | ||||
-rw-r--r-- | src/decoder/plugins/Mp4v2DecoderPlugin.cxx | 12 | ||||
-rw-r--r-- | src/input/AsyncInputStream.hxx | 4 | ||||
-rw-r--r-- | src/input/InputStream.hxx | 4 | ||||
-rw-r--r-- | src/input/TextInputStream.cxx | 20 | ||||
-rw-r--r-- | src/input/plugins/CurlInputPlugin.cxx | 34 | ||||
-rw-r--r-- | src/lib/upnp/Discovery.cxx | 1 | ||||
-rw-r--r-- | src/osx/OSXMain.cxx | 37 | ||||
-rw-r--r-- | src/output/OutputThread.cxx | 30 | ||||
-rw-r--r-- | src/playlist/PlaylistRegistry.cxx | 9 | ||||
-rw-r--r-- | src/playlist/plugins/ExtM3uPlaylistPlugin.cxx | 1 | ||||
-rw-r--r-- | src/playlist/plugins/M3uPlaylistPlugin.cxx | 1 | ||||
-rw-r--r-- | src/util/UriUtil.cxx | 17 | ||||
-rw-r--r-- | src/util/UriUtil.hxx | 11 | ||||
-rw-r--r-- | test/test_util.cxx | 19 |
24 files changed, 201 insertions, 116 deletions
diff --git a/Makefile.am b/Makefile.am index c105b9945..dac3e1772 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,7 +130,6 @@ libmpd_a_SOURCES = \ src/IOThread.cxx src/IOThread.hxx \ src/Instance.cxx src/Instance.hxx \ src/win32/Win32Main.cxx \ - src/osx/OSXMain.cxx \ src/GlobalEvents.cxx src/GlobalEvents.hxx \ src/MixRampInfo.hxx \ src/MusicBuffer.cxx src/MusicBuffer.hxx \ @@ -4,11 +4,21 @@ ver 0.20 (not yet released) * output - pulse: set channel map to WAVE-EX -ver 0.19.2 (not yet released) +ver 0.19.2 (2014/11/02) +* input + - curl: fix redirected streams +* playlist + - don't allow empty playlist name + - m3u: don't ignore unterminated last line + - m3u: recognize the file suffix ".m3u8" * decoder + - ignore URI query string for plugin detection + - faad: remove workaround for ancient libfaad2 ABI bug - ffmpeg: recognize MIME type audio/aacp + - mad: fix negative replay gain values * output - fix memory leak after filter initialization error + - fall back to PCM if given DSD sample rate is not supported * fix assertion failure on unsupported PCM conversion * auto-disable plugins that require GLib when --disable-glib is used @@ -99,6 +109,15 @@ ver 0.19 (2014/10/10) * install systemd unit for socket activation * Android port +ver 0.18.17 (2014/11/02) +* playlist + - don't allow empty playlist name + - m3u: recognize the file suffix ".m3u8" +* decoder + - ignore URI query string for plugin detection + - faad: remove workaround for ancient libfaad2 ABI bug + - ffmpeg: recognize MIME type audio/aacp + ver 0.18.16 (2014/09/26) * fix DSD breakage due to typo in configure.ac diff --git a/m4/faad.m4 b/m4/faad.m4 index 5ca520e79..9dcb1ccab 100644 --- a/m4/faad.m4 +++ b/m4/faad.m4 @@ -62,36 +62,7 @@ int main() { CPPFLAGS=$oldcppflags fi -if test x$enable_aac = xyes; then - oldcflags=$CFLAGS - oldlibs=$LIBS - oldcppflags=$CPPFLAGS - CFLAGS="$CFLAGS $FAAD_CFLAGS -Werror" - LIBS="$LIBS $FAAD_LIBS" - CPPFLAGS=$CFLAGS - - AC_MSG_CHECKING(for broken libfaad headers) - AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -#include <faad.h> -#include <stddef.h> -#include <stdint.h> - -int main() { - unsigned char channels; - uint32_t sample_rate; - - NeAACDecInit2(NULL, NULL, 0, &sample_rate, &channels); - return 0; -} - ])], - [AC_MSG_RESULT(correct)], - [AC_MSG_RESULT(broken); - AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])]) - - CFLAGS=$oldcflags - LIBS=$oldlibs - CPPFLAGS=$oldcppflags -else +if test x$enable_aac = xno; then FAAD_LIBS="" FAAD_CFLAGS="" fi diff --git a/src/Main.cxx b/src/Main.cxx index d17590e44..2719c05e0 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -114,6 +114,10 @@ #include <ws2tcpip.h> #endif +#ifdef __APPLE__ +#include <dispatch/dispatch.h> +#endif + #include <limits.h> static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096; @@ -401,8 +405,6 @@ int main(int argc, char *argv[]) { #ifdef WIN32 return win32_main(argc, argv); -#elif __APPLE__ - return osx_main(argc, argv); #else return mpd_main(argc, argv); #endif @@ -410,6 +412,8 @@ int main(int argc, char *argv[]) #endif +static int mpd_main_after_fork(struct options); + #ifdef ANDROID static inline #endif @@ -513,6 +517,27 @@ int mpd_main(int argc, char *argv[]) daemonize_begin(options.daemon); #endif +#ifdef __APPLE__ + /* Runs the OS X native event loop in the main thread, and runs + the rest of mpd_main on a new thread. This lets CoreAudio receive + route change notifications (e.g. plugging or unplugging headphones). + All hardware output on OS X ultimately uses CoreAudio internally. + This must be run after forking; if dispatch is called before forking, + the child process will have a broken internal dispatch state. */ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + exit(mpd_main_after_fork(options)); + }); + dispatch_main(); + return EXIT_FAILURE; // unreachable, because dispatch_main never returns +#else + return mpd_main_after_fork(options); +#endif +} + +static int mpd_main_after_fork(struct options options) +{ + Error error; + GlobalEvents::Initialize(*instance->event_loop); GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted); #ifdef WIN32 diff --git a/src/Main.hxx b/src/Main.hxx index dae7a5043..7e3fecd0b 100644 --- a/src/Main.hxx +++ b/src/Main.hxx @@ -75,15 +75,4 @@ win32_app_stopping(void); #endif -#ifdef __APPLE__ - -/* Runs the OS X native event loop in the main thread, and runs - * mpd_main on a new thread. This lets CoreAudio receive route - * change notifications (e.g. plugging or unplugging headphones). - * All hardware output on OS X ultimately uses CoreAudio internally. - */ -int osx_main(int argc, char *argv[]); - -#endif - #endif diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index f0aa2d2d7..ab269378a 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -64,6 +64,10 @@ spl_global_init(void) bool spl_valid_name(const char *name_utf8) { + if (*name_utf8 == 0) + /* empty name not allowed */ + return false; + /* * Not supporting '/' was done out of laziness, and we should * really strive to support it in the future. diff --git a/src/TagStream.cxx b/src/TagStream.cxx index 639763373..6201028f6 100644 --- a/src/TagStream.cxx +++ b/src/TagStream.cxx @@ -46,7 +46,8 @@ tag_stream_scan(InputStream &is, const tag_handler &handler, void *ctx) { assert(is.IsReady()); - const char *const suffix = uri_get_suffix(is.GetURI()); + UriSuffixBuffer suffix_buffer; + const char *const suffix = uri_get_suffix(is.GetURI(), suffix_buffer); const char *const mime = is.GetMimeType(); if (suffix == nullptr && mime == nullptr) diff --git a/src/decoder/DecoderThread.cxx b/src/decoder/DecoderThread.cxx index a39cfa6e9..dd5518b98 100644 --- a/src/decoder/DecoderThread.cxx +++ b/src/decoder/DecoderThread.cxx @@ -237,7 +237,8 @@ static bool decoder_run_stream_locked(Decoder &decoder, InputStream &is, const char *uri, bool &tried_r) { - const char *const suffix = uri_get_suffix(uri); + UriSuffixBuffer suffix_buffer; + const char *const suffix = uri_get_suffix(uri, suffix_buffer); using namespace std::placeholders; const auto f = std::bind(decoder_run_stream_plugin, diff --git a/src/decoder/plugins/FaadDecoderPlugin.cxx b/src/decoder/plugins/FaadDecoderPlugin.cxx index 793ab1011..add23aaa4 100644 --- a/src/decoder/plugins/FaadDecoderPlugin.cxx +++ b/src/decoder/plugins/FaadDecoderPlugin.cxx @@ -255,20 +255,12 @@ faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer &buffer, } uint8_t channels; - uint32_t sample_rate; -#ifdef HAVE_FAAD_LONG - /* neaacdec.h declares all arguments as "unsigned long", but - internally expects uint32_t pointers. To avoid gcc - warnings, use this workaround. */ - unsigned long *sample_rate_p = (unsigned long *)(void *)&sample_rate; -#else - uint32_t *sample_rate_p = &sample_rate; -#endif + unsigned long sample_rate; long nbytes = NeAACDecInit(decoder, /* deconst hack, libfaad requires this */ const_cast<unsigned char *>(data.data), data.size, - sample_rate_p, &channels); + &sample_rate, &channels); if (nbytes < 0) { error.Set(faad_decoder_domain, "Not an AAC stream"); return false; diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index 41efb593d..de6c9b127 100644 --- a/src/decoder/plugins/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -657,7 +657,7 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) unsigned name = mad_bit_read(ptr, 3); /* gain name */ unsigned orig = mad_bit_read(ptr, 3); /* gain originator */ unsigned sign = mad_bit_read(ptr, 1); /* sign bit */ - unsigned gain = mad_bit_read(ptr, 9); /* gain*10 */ + int gain = mad_bit_read(ptr, 9); /* gain*10 */ if (gain && name == 1 && orig != 0) { lame->track_gain = ((sign ? -gain : gain) / 10.0) + adj; FormatDebug(mad_domain, "LAME track gain found: %f", diff --git a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx index bf97763c5..34bccd243 100644 --- a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx +++ b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx @@ -39,15 +39,7 @@ static MP4TrackId mp4_get_aac_track(MP4FileHandle handle, NeAACDecHandle decoder, AudioFormat &audio_format, Error &error) { - uint32_t sample_rate; -#ifdef HAVE_FAAD_LONG - /* neaacdec.h declares all arguments as "unsigned long", but - internally expects uint32_t pointers. To avoid gcc - warnings, use this workaround. */ - unsigned long *sample_rate_r = (unsigned long*)&sample_rate; -#else - uint32_t *sample_rate_r = sample_rate; -#endif + unsigned long sample_rate; const MP4TrackId tracks = MP4GetNumberOfTracks(handle); @@ -80,7 +72,7 @@ mp4_get_aac_track(MP4FileHandle handle, NeAACDecHandle decoder, uint8_t channels; int32_t nbytes = NeAACDecInit(decoder, buff, buff_size, - sample_rate_r, &channels); + &sample_rate, &channels); free(buff); diff --git a/src/input/AsyncInputStream.hxx b/src/input/AsyncInputStream.hxx index 7935f1a17..d1f0c3b9d 100644 --- a/src/input/AsyncInputStream.hxx +++ b/src/input/AsyncInputStream.hxx @@ -83,6 +83,10 @@ protected: */ void SetTag(Tag *_tag); + void ClearTag() { + SetTag(nullptr); + } + void Pause(); bool IsPaused() const { diff --git a/src/input/InputStream.hxx b/src/input/InputStream.hxx index 15c350103..81b903ba2 100644 --- a/src/input/InputStream.hxx +++ b/src/input/InputStream.hxx @@ -200,6 +200,10 @@ public: return mime.empty() ? nullptr : mime.c_str(); } + void ClearMimeType() { + mime.clear(); + } + gcc_nonnull_all void SetMimeType(const char *_mime) { assert(!ready); diff --git a/src/input/TextInputStream.cxx b/src/input/TextInputStream.cxx index b79f64bdc..5a8dcc065 100644 --- a/src/input/TextInputStream.cxx +++ b/src/input/TextInputStream.cxx @@ -38,8 +38,8 @@ TextInputStream::ReadLine() while (true) { auto dest = buffer.Write(); if (dest.size < 2) { - /* end of file (or line too long): terminate - the current line */ + /* line too long: terminate the current + line */ assert(!dest.IsEmpty()); dest[0] = 0; @@ -66,7 +66,19 @@ TextInputStream::ReadLine() if (line != nullptr) return line; - if (nbytes == 0) - return nullptr; + if (nbytes == 0) { + /* end of file: see if there's an unterminated + line */ + + dest = buffer.Write(); + assert(!dest.IsEmpty()); + dest[0] = 0; + + auto r = buffer.Read(); + buffer.Clear(); + return r.IsEmpty() + ? nullptr + : r.data; + } } } diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index 617805e2c..1e1a46108 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -109,6 +109,13 @@ struct CurlInputStream final : public AsyncInputStream { */ void FreeEasyIndirect(); + /** + * Called when a new response begins. This is used to discard + * headers from previous responses (for example authentication + * and redirects). + */ + void ResponseBoundary(); + void HeaderReceived(const char *name, std::string &&value); size_t DataReceived(const void *ptr, size_t size); @@ -598,6 +605,20 @@ CurlInputStream::~CurlInputStream() } inline void +CurlInputStream::ResponseBoundary() +{ + /* undo all effects of HeaderReceived() because the previous + response was not applicable for this stream */ + + seekable = false; + size = UNKNOWN_SIZE; + ClearMimeType(); + ClearTag(); + + // TODO: reset the IcyInputStream? +} + +inline void CurlInputStream::HeaderReceived(const char *name, std::string &&value) { if (IsSeekPending()) @@ -645,6 +666,11 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) size *= nmemb; const char *header = (const char *)ptr; + if (size > 5 && memcmp(header, "HTTP/", 5) == 0) { + c.ResponseBoundary(); + return size; + } + const char *end = header + size; char name[64]; @@ -720,10 +746,10 @@ CurlInputStream::InitEasy(Error &error) input_curl_writefunction); curl_easy_setopt(easy, CURLOPT_WRITEDATA, this); curl_easy_setopt(easy, CURLOPT_HTTP200ALIASES, http_200_aliases); - curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(easy, CURLOPT_NETRC, 1); - curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 5); - curl_easy_setopt(easy, CURLOPT_FAILONERROR, true); + curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1l); + curl_easy_setopt(easy, CURLOPT_NETRC, 1l); + curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 5l); + curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1l); curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer); curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 1l); curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1l); diff --git a/src/lib/upnp/Discovery.cxx b/src/lib/upnp/Discovery.cxx index 9ea78c624..1539e1512 100644 --- a/src/lib/upnp/Discovery.cxx +++ b/src/lib/upnp/Discovery.cxx @@ -26,6 +26,7 @@ #include <upnp/upnptools.h> +#include <stdlib.h> #include <string.h> // The service type string we are looking for. diff --git a/src/osx/OSXMain.cxx b/src/osx/OSXMain.cxx deleted file mode 100644 index 1ac9aec23..000000000 --- a/src/osx/OSXMain.cxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "Main.hxx" - -#ifdef __APPLE__ - -#include <stdlib.h> -#include <dispatch/dispatch.h> - -int osx_main(int argc, char *argv[]) -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - exit(mpd_main(argc, argv)); - }); - dispatch_main(); - return EXIT_FAILURE; // unreachable, because dispatch_main never returns -} - -#endif diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx index 54664bb65..2ec0670c1 100644 --- a/src/output/OutputThread.cxx +++ b/src/output/OutputThread.cxx @@ -22,6 +22,7 @@ #include "OutputAPI.hxx" #include "Domain.hxx" #include "pcm/PcmMix.hxx" +#include "pcm/Domain.hxx" #include "notify.hxx" #include "filter/FilterInternal.hxx" #include "filter/plugins/ConvertFilterPlugin.hxx" @@ -165,6 +166,10 @@ AudioOutput::Open() out_audio_format.ApplyMask(config_audio_format); mutex.unlock(); + + const AudioFormat retry_audio_format = out_audio_format; + + retry_without_dsd: success = ao_plugin_open(this, out_audio_format, error); mutex.lock(); @@ -189,6 +194,31 @@ AudioOutput::Open() mutex.unlock(); ao_plugin_close(this); + + if (error.IsDomain(pcm_domain) && + out_audio_format.format == SampleFormat::DSD) { + /* if the audio output supports DSD, but not + the given sample rate, it asks MPD to + resample; resampling DSD however is not + implemented; our last resort is to give up + DSD and fall back to PCM */ + + // TODO: clean up this workaround + + FormatError(output_domain, "Retrying without DSD"); + + out_audio_format = retry_audio_format; + out_audio_format.format = SampleFormat::FLOAT; + + /* clear the Error to allow reusing it */ + error.Clear(); + + /* sorry for the "goto" - this is a workaround + for the stable branch that should be as + unintrusive as possible */ + goto retry_without_dsd; + } + CloseFilter(); mutex.lock(); diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx index bc5932de3..4e9ef890e 100644 --- a/src/playlist/PlaylistRegistry.cxx +++ b/src/playlist/PlaylistRegistry.cxx @@ -139,12 +139,12 @@ static SongEnumerator * playlist_list_open_uri_suffix(const char *uri, Mutex &mutex, Cond &cond, const bool *tried) { - const char *suffix; SongEnumerator *playlist = nullptr; assert(uri != nullptr); - suffix = uri_get_suffix(uri); + UriSuffixBuffer suffix_buffer; + const char *const suffix = uri_get_suffix(uri, suffix_buffer); if (suffix == nullptr) return nullptr; @@ -257,7 +257,10 @@ playlist_list_open_stream(InputStream &is, const char *uri) return playlist; } - const char *suffix = uri != nullptr ? uri_get_suffix(uri) : nullptr; + UriSuffixBuffer suffix_buffer; + const char *suffix = uri != nullptr + ? uri_get_suffix(uri, suffix_buffer) + : nullptr; if (suffix != nullptr) { auto playlist = playlist_list_open_stream_suffix(is, suffix); if (playlist != nullptr) diff --git a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx index fdd4357ca..93316ca6c 100644 --- a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx +++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx @@ -130,6 +130,7 @@ ExtM3uPlaylist::NextSong() static const char *const extm3u_suffixes[] = { "m3u", + "m3u8", nullptr }; diff --git a/src/playlist/plugins/M3uPlaylistPlugin.cxx b/src/playlist/plugins/M3uPlaylistPlugin.cxx index a4125bc70..0428d291a 100644 --- a/src/playlist/plugins/M3uPlaylistPlugin.cxx +++ b/src/playlist/plugins/M3uPlaylistPlugin.cxx @@ -60,6 +60,7 @@ M3uPlaylist::NextSong() static const char *const m3u_suffixes[] = { "m3u", + "m3u8", nullptr }; diff --git a/src/util/UriUtil.cxx b/src/util/UriUtil.cxx index fdca47c00..62977e91b 100644 --- a/src/util/UriUtil.cxx +++ b/src/util/UriUtil.cxx @@ -54,6 +54,23 @@ uri_get_suffix(const char *uri) return suffix; } +const char * +uri_get_suffix(const char *uri, UriSuffixBuffer &buffer) +{ + const char *suffix = uri_get_suffix(uri); + if (suffix == nullptr) + return nullptr; + + const char *q = strchr(suffix, '?'); + if (q != nullptr && size_t(q - suffix) < sizeof(buffer.data)) { + memcpy(buffer.data, suffix, q - suffix); + buffer.data[q - suffix] = 0; + suffix = buffer.data; + } + + return suffix; +} + static const char * verify_uri_segment(const char *p) { diff --git a/src/util/UriUtil.hxx b/src/util/UriUtil.hxx index c2cc97a63..d478d5b92 100644 --- a/src/util/UriUtil.hxx +++ b/src/util/UriUtil.hxx @@ -42,6 +42,17 @@ gcc_pure const char * uri_get_suffix(const char *uri); +struct UriSuffixBuffer { + char data[8]; +}; + +/** + * Returns the file name suffix, ignoring the query string. + */ +gcc_pure +const char * +uri_get_suffix(const char *uri, UriSuffixBuffer &buffer); + /** * Returns true if this is a safe "local" URI: * diff --git a/test/test_util.cxx b/test/test_util.cxx index aaadec68f..3e79aeca0 100644 --- a/test/test_util.cxx +++ b/test/test_util.cxx @@ -34,6 +34,25 @@ public: uri_get_suffix(".jpg")); CPPUNIT_ASSERT_EQUAL((const char *)nullptr, uri_get_suffix("/foo/.jpg")); + + /* the first overload does not eliminate the query + string */ + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string"), + "jpg?query_string")); + + /* ... but the second one does */ + UriSuffixBuffer buffer; + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string", + buffer), + "jpg")); + + /* repeat some of the above tests with the second overload */ + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo/bar", buffer)); + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo.jpg/bar", buffer)); + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg", buffer), + "jpg")); } void TestRemoveAuth() { |