diff options
Diffstat (limited to 'src')
36 files changed, 332 insertions, 168 deletions
diff --git a/src/Main.cxx b/src/Main.cxx index 26d4e7ae4..b1960b5f2 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -50,7 +50,6 @@ #include "AudioConfig.hxx" #include "pcm/PcmConvert.hxx" #include "unix/SignalHandlers.hxx" -#include "unix/Daemon.hxx" #include "system/FatalError.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -65,6 +64,10 @@ #include "config/ConfigError.hxx" #include "Stats.hxx" +#ifdef ENABLE_DAEMON +#include "unix/Daemon.hxx" +#endif + #ifdef ENABLE_DATABASE #include "db/update/Service.hxx" #include "db/Configured.hxx" @@ -133,7 +136,7 @@ Instance *instance; static StateFile *state_file; -#ifndef ANDROID +#ifdef ENABLE_DAEMON static bool glue_daemonize_init(const struct options *options, Error &error) @@ -422,9 +425,11 @@ int mpd_main(int argc, char *argv[]) struct options options; Error error; -#ifndef ANDROID +#ifdef ENABLE_DAEMON daemonize_close_stdin(); +#endif +#ifndef ANDROID #ifdef HAVE_LOCALE_H /* initialize locale */ setlocale(LC_CTYPE,""); @@ -470,7 +475,9 @@ int mpd_main(int argc, char *argv[]) LogError(error); return EXIT_FAILURE; } +#endif +#ifdef ENABLE_DAEMON if (!glue_daemonize_init(&options, error)) { LogError(error); return EXIT_FAILURE; @@ -512,7 +519,7 @@ int mpd_main(int argc, char *argv[]) return EXIT_FAILURE; } -#ifndef ANDROID +#ifdef ENABLE_DAEMON daemonize_set_user(); daemonize_begin(options.daemon); #endif @@ -585,9 +592,11 @@ static int mpd_main_after_fork(struct options options) playlist_list_global_init(); -#ifndef ANDROID +#ifdef ENABLE_DAEMON daemonize_commit(); +#endif +#ifndef ANDROID setup_log_output(options.log_stderr); SignalHandlersInit(*instance->event_loop); @@ -724,9 +733,11 @@ static int mpd_main_after_fork(struct options options) delete instance->event_loop; delete instance; instance = nullptr; -#ifndef ANDROID + +#ifdef ENABLE_DAEMON daemonize_finish(); #endif + #ifdef WIN32 WSACleanup(); #endif diff --git a/src/archive/ArchiveList.cxx b/src/archive/ArchiveList.cxx index 79c3a16fe..4a39ea8a5 100644 --- a/src/archive/ArchiveList.cxx +++ b/src/archive/ArchiveList.cxx @@ -32,7 +32,7 @@ const ArchivePlugin *const archive_plugins[] = { #ifdef HAVE_BZ2 &bz2_archive_plugin, #endif -#ifdef HAVE_ZZIP +#ifdef ENABLE_ZZIP &zzip_archive_plugin, #endif #ifdef HAVE_ISO9660 diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 6a4b18198..be3a343a5 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -210,6 +210,16 @@ command_available(gcc_unused const Partition &partition, return neighbor_commands_available(partition.instance); #endif + if (strcmp(cmd->cmd, "save") == 0 || + strcmp(cmd->cmd, "rm") == 0 || + strcmp(cmd->cmd, "rename") == 0 || + strcmp(cmd->cmd, "playlistdelete") == 0 || + strcmp(cmd->cmd, "playlistmove") == 0 || + strcmp(cmd->cmd, "playlistclear") == 0 || + strcmp(cmd->cmd, "playlistadd") == 0 || + strcmp(cmd->cmd, "listplaylists") == 0) + return playlist_commands_available(); + return true; } diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index a3ea8d0ae..988542f44 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -32,6 +32,7 @@ #include "util/Error.hxx" #include "SongFilter.hxx" #include "protocol/Result.hxx" +#include "protocol/ArgParser.hxx" #include "BulkEdit.hxx" #include <string.h> @@ -70,6 +71,16 @@ handle_match(Client &client, unsigned argc, char *argv[], bool fold_case) { ConstBuffer<const char *> args(argv + 1, argc - 1); + unsigned window_start = 0, window_end = std::numeric_limits<int>::max(); + if (args.size >= 2 && strcmp(args[args.size - 2], "window") == 0) { + if (!check_range(client, &window_start, &window_end, + args.back())) + return CommandResult::ERROR; + + args.pop_back(); + args.pop_back(); + } + SongFilter filter; if (!filter.Parse(args, fold_case)) { command_error(client, ACK_ERROR_ARG, "incorrect arguments"); @@ -79,7 +90,8 @@ handle_match(Client &client, unsigned argc, char *argv[], bool fold_case) const DatabaseSelection selection("", true, &filter); Error error; - return db_selection_print(client, selection, true, false, error) + return db_selection_print(client, selection, true, false, + window_start, window_end, error) ? CommandResult::OK : print_error(client, error); } diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index c2b18064c..593eab865 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -35,9 +35,17 @@ #include "protocol/ArgParser.hxx" #include "protocol/Result.hxx" #include "ls.hxx" +#include "Mapper.hxx" +#include "fs/AllocatedPath.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +bool +playlist_commands_available() +{ + return !map_spl_path().IsNull(); +} + static void print_spl_list(Client &client, const PlaylistVector &list) { diff --git a/src/command/PlaylistCommands.hxx b/src/command/PlaylistCommands.hxx index fba4e1318..6dc589c8b 100644 --- a/src/command/PlaylistCommands.hxx +++ b/src/command/PlaylistCommands.hxx @@ -21,9 +21,14 @@ #define MPD_PLAYLIST_COMMANDS_HXX #include "CommandResult.hxx" +#include "Compiler.h" class Client; +gcc_const +bool +playlist_commands_available(); + CommandResult handle_save(Client &client, unsigned argc, char *argv[]); diff --git a/src/db/DatabasePrint.cxx b/src/db/DatabasePrint.cxx index 498aedf97..945ac6ab9 100644 --- a/src/db/DatabasePrint.cxx +++ b/src/db/DatabasePrint.cxx @@ -147,27 +147,49 @@ PrintPlaylistFull(Client &client, bool base, bool db_selection_print(Client &client, const DatabaseSelection &selection, - bool full, bool base, Error &error) + bool full, bool base, + unsigned window_start, unsigned window_end, + Error &error) { const Database *db = client.GetDatabase(error); if (db == nullptr) return false; + unsigned i = 0; + using namespace std::placeholders; const auto d = selection.filter == nullptr ? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief, std::ref(client), base, _1) : VisitDirectory(); - const auto s = std::bind(full ? PrintSongFull : PrintSongBrief, - std::ref(client), base, _1); + VisitSong s = std::bind(full ? PrintSongFull : PrintSongBrief, + std::ref(client), base, _1); const auto p = selection.filter == nullptr ? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief, std::ref(client), base, _1, _2) : VisitPlaylist(); + if (window_start > 0 || window_end < std::numeric_limits<int>::max()) + s = [s, window_start, window_end, &i](const LightSong &song, + Error &error2){ + const bool in_window = i >= window_start && i < window_end; + ++i; + return !in_window || s(song, error2); + }; + return db->Visit(selection, d, s, p, error); } +bool +db_selection_print(Client &client, const DatabaseSelection &selection, + bool full, bool base, + Error &error) +{ + return db_selection_print(client, selection, full, base, + 0, std::numeric_limits<int>::max(), + error); +} + static bool PrintSongURIVisitor(Client &client, const LightSong &song) { diff --git a/src/db/DatabasePrint.hxx b/src/db/DatabasePrint.hxx index 2ab5e703d..7e4dd8572 100644 --- a/src/db/DatabasePrint.hxx +++ b/src/db/DatabasePrint.hxx @@ -38,6 +38,12 @@ db_selection_print(Client &client, const DatabaseSelection &selection, bool full, bool base, Error &error); bool +db_selection_print(Client &client, const DatabaseSelection &selection, + bool full, bool base, + unsigned window_start, unsigned window_end, + Error &error); + +bool PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, const SongFilter *filter, Error &error); diff --git a/src/db/Registry.cxx b/src/db/Registry.cxx index 5681a9b82..a7d6dc05e 100644 --- a/src/db/Registry.cxx +++ b/src/db/Registry.cxx @@ -28,10 +28,10 @@ const DatabasePlugin *const database_plugins[] = { &simple_db_plugin, -#ifdef HAVE_LIBMPDCLIENT +#ifdef ENABLE_LIBMPDCLIENT &proxy_db_plugin, #endif -#ifdef HAVE_LIBUPNP +#ifdef ENABLE_UPNP &upnp_db_plugin, #endif nullptr diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx index 7b1886f1c..bc9c42d2d 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx @@ -41,7 +41,7 @@ #include "util/Domain.hxx" #include "Log.hxx" -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB #include "fs/io/GzipOutputStream.hxx" #endif @@ -52,21 +52,21 @@ static constexpr Domain simple_db_domain("simple_db"); inline SimpleDatabase::SimpleDatabase() :Database(simple_db_plugin), path(AllocatedPath::Null()), -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB compress(true), #endif cache_path(AllocatedPath::Null()), prefixed_light_song(nullptr) {} inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path, -#ifndef HAVE_ZLIB +#ifndef ENABLE_ZLIB gcc_unused #endif bool _compress) :Database(simple_db_plugin), path(std::move(_path)), path_utf8(path.ToUTF8()), -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB compress(_compress), #endif cache_path(AllocatedPath::Null()), @@ -104,7 +104,7 @@ SimpleDatabase::Configure(const config_param ¶m, Error &error) if (path.IsNull() && error.IsDefined()) return false; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB compress = param.GetBlockValue("compress", compress); #endif @@ -389,7 +389,7 @@ SimpleDatabase::Save(Error &error) OutputStream *os = &fos; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB GzipOutputStream *gzip = nullptr; if (compress) { gzip = new GzipOutputStream(*os, error); @@ -407,13 +407,13 @@ SimpleDatabase::Save(Error &error) db_save_internal(bos, *root); if (!bos.Flush(error)) { -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB delete gzip; #endif return false; } -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB if (gzip != nullptr) { bool success = gzip->Flush(error); delete gzip; @@ -484,7 +484,7 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri, std::string name(storage_uri); std::replace_if(name.begin(), name.end(), IsUnsafeChar, '_'); -#ifndef HAVE_ZLIB +#ifndef ENABLE_ZLIB constexpr bool compress = false; #endif auto db = new SimpleDatabase(AllocatedPath::Build(cache_path, diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.hxx b/src/db/plugins/simple/SimpleDatabasePlugin.hxx index 7ba71e272..eb225b2c8 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.hxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.hxx @@ -39,7 +39,7 @@ class SimpleDatabase : public Database { AllocatedPath path; std::string path_utf8; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB bool compress; #endif diff --git a/src/db/plugins/upnp/Directory.cxx b/src/db/plugins/upnp/Directory.cxx index e94a1a997..55f2693ae 100644 --- a/src/db/plugins/upnp/Directory.cxx +++ b/src/db/plugins/upnp/Directory.cxx @@ -89,18 +89,18 @@ ParseDuration(const char *duration) * this. Twonky returns directory names (titles) like 'Artist/Album'. */ gcc_pure -static std::string -titleToPathElt(std::string &&s) +static std::string && +TitleToPathSegment(std::string &&s) { std::replace(s.begin(), s.end(), '/', '_'); - return s; + return std::move(s); } /** * An XML parser which builds directory contents from DIDL lite input. */ class UPnPDirParser final : public CommonExpatParser { - UPnPDirContent &m_dir; + UPnPDirContent &directory; enum { NONE, @@ -120,22 +120,22 @@ class UPnPDirParser final : public CommonExpatParser { */ std::string value; - UPnPDirObject m_tobj; + UPnPDirObject object; TagBuilder tag; public: - UPnPDirParser(UPnPDirContent& dir) - :m_dir(dir), + UPnPDirParser(UPnPDirContent &_directory) + :directory(_directory), state(NONE), tag_type(TAG_NUM_OF_ITEM_TYPES) { - m_tobj.clear(); + object.Clear(); } protected: virtual void StartElement(const XML_Char *name, const XML_Char **attrs) { - if (m_tobj.type != UPnPDirObject::Type::UNKNOWN && + if (object.type != UPnPDirObject::Type::UNKNOWN && tag_type == TAG_NUM_OF_ITEM_TYPES) { tag_type = tag_table_lookup(upnp_tags, name); if (tag_type != TAG_NUM_OF_ITEM_TYPES) @@ -147,31 +147,31 @@ protected: switch (name[0]) { case 'c': if (!strcmp(name, "container")) { - m_tobj.clear(); - m_tobj.type = UPnPDirObject::Type::CONTAINER; + object.Clear(); + object.type = UPnPDirObject::Type::CONTAINER; const char *id = GetAttribute(attrs, "id"); if (id != nullptr) - m_tobj.m_id = id; + object.id = id; const char *pid = GetAttribute(attrs, "parentID"); if (pid != nullptr) - m_tobj.m_pid = pid; + object.parent_id = pid; } break; case 'i': if (!strcmp(name, "item")) { - m_tobj.clear(); - m_tobj.type = UPnPDirObject::Type::ITEM; + object.Clear(); + object.type = UPnPDirObject::Type::ITEM; const char *id = GetAttribute(attrs, "id"); if (id != nullptr) - m_tobj.m_id = id; + object.id = id; const char *pid = GetAttribute(attrs, "parentID"); if (pid != nullptr) - m_tobj.m_pid = pid; + object.parent_id = pid; } break; @@ -197,25 +197,15 @@ protected: } } - bool checkobjok() { - if (m_tobj.m_id.empty() || m_tobj.m_pid.empty() || - m_tobj.name.empty() || - (m_tobj.type == UPnPDirObject::Type::ITEM && - m_tobj.item_class == UPnPDirObject::ItemClass::UNKNOWN)) - return false; - - return true; - } - virtual void EndElement(const XML_Char *name) { if (tag_type != TAG_NUM_OF_ITEM_TYPES) { - assert(m_tobj.type != UPnPDirObject::Type::UNKNOWN); + assert(object.type != UPnPDirObject::Type::UNKNOWN); tag.AddItem(tag_type, value.c_str()); if (tag_type == TAG_TITLE) - m_tobj.name = titleToPathElt(std::move(value)); + object.name = TitleToPathSegment(std::move(value)); value.clear(); tag_type = TAG_NUM_OF_ITEM_TYPES; @@ -223,9 +213,9 @@ protected: } if ((!strcmp(name, "container") || !strcmp(name, "item")) && - checkobjok()) { - tag.Commit(m_tobj.tag); - m_dir.objects.emplace_back(std::move(m_tobj)); + object.Check()) { + tag.Commit(object.tag); + directory.objects.emplace_back(std::move(object)); } state = NONE; @@ -234,7 +224,7 @@ protected: virtual void CharacterData(const XML_Char *s, int len) { if (tag_type != TAG_NUM_OF_ITEM_TYPES) { - assert(m_tobj.type != UPnPDirObject::Type::UNKNOWN); + assert(object.type != UPnPDirObject::Type::UNKNOWN); value.append(s, len); return; @@ -245,11 +235,11 @@ protected: break; case RES: - m_tobj.url.assign(s, len); + object.url.assign(s, len); break; case CLASS: - m_tobj.item_class = ParseItemClass(s, len); + object.item_class = ParseItemClass(s, len); break; } } diff --git a/src/db/plugins/upnp/Object.hxx b/src/db/plugins/upnp/Object.hxx index 16a66c774..6d71c158b 100644 --- a/src/db/plugins/upnp/Object.hxx +++ b/src/db/plugins/upnp/Object.hxx @@ -21,6 +21,7 @@ #define MPD_UPNP_OBJECT_HXX #include "tag/Tag.hxx" +#include "Compiler.h" #include <string> @@ -50,8 +51,16 @@ public: PLAYLIST, }; - std::string m_id; // ObjectId - std::string m_pid; // Parent ObjectId + /** + * ObjectId + */ + std::string id; + + /** + * Parent's ObjectId + */ + std::string parent_id; + std::string url; /** @@ -71,15 +80,21 @@ public: UPnPDirObject &operator=(UPnPDirObject &&) = default; - void clear() - { - m_id.clear(); - m_pid.clear(); + void Clear() { + id.clear(); + parent_id.clear(); url.clear(); type = Type::UNKNOWN; item_class = ItemClass::UNKNOWN; tag.Clear(); } + + gcc_pure + bool Check() const { + return !id.empty() && !parent_id.empty() && !name.empty() && + (type != UPnPDirObject::Type::ITEM || + item_class != UPnPDirObject::ItemClass::UNKNOWN); + } }; #endif /* _UPNPDIRCONTENT_H_X_INCLUDED_ */ diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx index 21ddb8790..f038a668c 100644 --- a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx +++ b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx @@ -412,7 +412,7 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server, // So we return synthetic and ugly paths based on the object id, // which we later have to detect. const std::string path = songPath(server.getFriendlyName(), - dirent.m_id); + dirent.id); if (!visitSong(std::move(dirent), path.c_str(), selection, visit_song, error)) @@ -447,13 +447,13 @@ UpnpDatabase::BuildPath(const ContentDirectoryService &server, std::string &path, Error &error) const { - const char *pid = idirent.m_id.c_str(); + const char *pid = idirent.id.c_str(); path.clear(); UPnPDirObject dirent; while (strcmp(pid, rootid) != 0) { if (!ReadNode(server, pid, dirent, error)) return false; - pid = dirent.m_pid.c_str(); + pid = dirent.parent_id.c_str(); if (path.empty()) path = dirent.name; @@ -509,7 +509,7 @@ UpnpDatabase::Namei(const ContentDirectoryService &server, return false; } - objid = std::move(child->m_id); + objid = std::move(child->id); } } @@ -621,7 +621,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, } std::string path = songPath(server.getFriendlyName(), - dirent.m_id); + dirent.id); if (!visitSong(std::move(dirent), path.c_str(), selection, visit_song, error)) @@ -640,7 +640,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, recursion (1-deep) here, which will handle the "add dir" case. */ if (selection.recursive && selection.filter) - return SearchSongs(server, tdirent.m_id.c_str(), selection, + return SearchSongs(server, tdirent.id.c_str(), selection, visit_song, error); const char *const base_uri = selection.uri.empty() @@ -658,7 +658,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, and loop here, but it's not useful as mpd will only return data to the client when we're done anyway. */ UPnPDirContent dirbuf; - if (!server.readDir(handle, tdirent.m_id.c_str(), dirbuf, + if (!server.readDir(handle, tdirent.id.c_str(), dirbuf, error)) return false; diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx index 0a31d9eac..7f665fb88 100644 --- a/src/decoder/DecoderList.cxx +++ b/src/decoder/DecoderList.cxx @@ -49,10 +49,10 @@ #include <string.h> const struct DecoderPlugin *const decoder_plugins[] = { -#ifdef HAVE_MAD +#ifdef ENABLE_MAD &mad_decoder_plugin, #endif -#ifdef HAVE_MPG123 +#ifdef ENABLE_MPG123 &mpg123_decoder_plugin, #endif #ifdef HAVE_MP4V2 @@ -61,19 +61,17 @@ const struct DecoderPlugin *const decoder_plugins[] = { #ifdef ENABLE_VORBIS_DECODER &vorbis_decoder_plugin, #endif -#if defined(HAVE_FLAC) +#ifdef ENABLE_FLAC &oggflac_decoder_plugin, -#endif -#ifdef HAVE_FLAC &flac_decoder_plugin, #endif -#ifdef HAVE_OPUS +#ifdef ENABLE_OPUS &opus_decoder_plugin, #endif #ifdef ENABLE_SNDFILE &sndfile_decoder_plugin, #endif -#ifdef HAVE_AUDIOFILE +#ifdef ENABLE_AUDIOFILE &audiofile_decoder_plugin, #endif #ifdef ENABLE_DSD @@ -86,10 +84,10 @@ const struct DecoderPlugin *const decoder_plugins[] = { #ifdef HAVE_MPCDEC &mpcdec_decoder_plugin, #endif -#ifdef HAVE_WAVPACK +#ifdef ENABLE_WAVPACK &wavpack_decoder_plugin, #endif -#ifdef HAVE_MODPLUG +#ifdef ENABLE_MODPLUG &modplug_decoder_plugin, #endif #ifdef ENABLE_MIKMOD_DECODER @@ -104,10 +102,10 @@ const struct DecoderPlugin *const decoder_plugins[] = { #ifdef ENABLE_FLUIDSYNTH &fluidsynth_decoder_plugin, #endif -#ifdef HAVE_ADPLUG +#ifdef ENABLE_ADPLUG &adplug_decoder_plugin, #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG &ffmpeg_decoder_plugin, #endif #ifdef HAVE_GME diff --git a/src/decoder/plugins/DsdLib.cxx b/src/decoder/plugins/DsdLib.cxx index 8892ed387..13f6bc846 100644 --- a/src/decoder/plugins/DsdLib.cxx +++ b/src/decoder/plugins/DsdLib.cxx @@ -32,7 +32,7 @@ #include <string.h> -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG #include <id3tag.h> #endif @@ -101,7 +101,7 @@ dsdlib_valid_freq(uint32_t samplefreq) } } -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG void dsdlib_tag_id3(InputStream &is, const struct tag_handler *handler, diff --git a/src/decoder/plugins/DsdiffDecoderPlugin.cxx b/src/decoder/plugins/DsdiffDecoderPlugin.cxx index b6c79e11e..33f433330 100644 --- a/src/decoder/plugins/DsdiffDecoderPlugin.cxx +++ b/src/decoder/plugins/DsdiffDecoderPlugin.cxx @@ -244,7 +244,7 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, /** offset for title tag */ offset_type title_offset = 0; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG offset_type id3_offset = 0; #endif @@ -269,7 +269,7 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, chunk_size = chunk_header->GetSize(); title_offset = is.GetOffset(); } -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG /* 'ID3 ' chunk, offspec. Used by sacdextract */ if (chunk_header->id.Equals("ID3 ")) { chunk_size = chunk_header->GetSize(); @@ -283,7 +283,7 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, /* done processing chunk headers, process tags if any */ -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG if (id3_offset != 0) { /* a ID3 tag has preference over the other tags, do not process other tags if we have one */ diff --git a/src/decoder/plugins/DsfDecoderPlugin.cxx b/src/decoder/plugins/DsfDecoderPlugin.cxx index 690616d15..b8ae837f7 100644 --- a/src/decoder/plugins/DsfDecoderPlugin.cxx +++ b/src/decoder/plugins/DsfDecoderPlugin.cxx @@ -47,7 +47,7 @@ struct DsfMetaData { unsigned sample_rate, channels; bool bitreverse; offset_type n_blocks; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG offset_type id3_offset; #endif }; @@ -111,7 +111,7 @@ dsf_read_metadata(Decoder *decoder, InputStream &is, if (sizeof(dsf_header) != chunk_size) return false; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG const offset_type metadata_offset = dsf_header.pmeta.Read(); #endif @@ -174,7 +174,7 @@ dsf_read_metadata(Decoder *decoder, InputStream &is, metadata->n_blocks = data_size / block_size; metadata->channels = channels; metadata->sample_rate = samplefreq; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG metadata->id3_offset = metadata_offset; #endif /* check bits per sample format, determine if bitreverse is needed */ @@ -352,7 +352,7 @@ dsf_scan_stream(InputStream &is, audio_format.sample_rate); tag_handler_invoke_duration(handler, handler_ctx, songtime); -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG /* Add available tags from the ID3 tag */ dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset); #endif diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index de6c9b127..62f31974f 100644 --- a/src/decoder/plugins/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -36,7 +36,7 @@ #include <mad.h> -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG #include <id3tag.h> #endif @@ -251,7 +251,7 @@ MadDecoder::FillBuffer() return true; } -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG static bool parse_id3_replay_gain_info(ReplayGainInfo &rgi, struct id3_tag *tag) @@ -285,7 +285,7 @@ parse_id3_replay_gain_info(ReplayGainInfo &rgi, } #endif -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG gcc_pure static MixRampInfo parse_id3_mixramp(struct id3_tag *tag) @@ -317,7 +317,7 @@ parse_id3_mixramp(struct id3_tag *tag) inline void MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) { -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG id3_byte_t *allocated = nullptr; const id3_length_t count = stream.bufend - stream.this_frame; @@ -369,7 +369,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) id3_tag_delete(id3_tag); delete[] allocated; -#else /* !HAVE_ID3TAG */ +#else /* !ENABLE_ID3TAG */ (void)mpd_tag; /* This code is enabled when libid3tag is disabled. Instead @@ -386,7 +386,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) #endif } -#ifndef HAVE_ID3TAG +#ifndef ENABLE_ID3TAG /** * This function emulates libid3tag when it is disabled. Instead of * doing a real analyzation of the frame, it just checks whether the @@ -402,7 +402,7 @@ id3_tag_query(const void *p0, size_t length) ? (p[8] << 7) + p[9] + 10 : 0; } -#endif /* !HAVE_ID3TAG */ +#endif /* !ENABLE_ID3TAG */ static enum mp3_action RecoverFrameError(struct mad_stream &stream) diff --git a/src/decoder/plugins/WavpackDecoderPlugin.cxx b/src/decoder/plugins/WavpackDecoderPlugin.cxx index 67859bbd2..0b41d052e 100644 --- a/src/decoder/plugins/WavpackDecoderPlugin.cxx +++ b/src/decoder/plugins/WavpackDecoderPlugin.cxx @@ -28,10 +28,10 @@ #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/Macros.hxx" +#include "util/Alloc.hxx" #include "Log.hxx" #include <wavpack/wavpack.h> -#include <glib.h> #include <assert.h> #include <stdio.h> @@ -484,10 +484,10 @@ wavpack_open_wvc(Decoder &decoder, const char *uri) if (uri == nullptr) return nullptr; - char *wvc_url = g_strconcat(uri, "c", nullptr); + char *wvc_url = xstrcatdup(uri, "c"); InputStream *is_wvc = decoder_open_uri(decoder, uri, IgnoreError()); - g_free(wvc_url); + free(wvc_url); if (is_wvc == nullptr) return nullptr; diff --git a/src/encoder/EncoderList.cxx b/src/encoder/EncoderList.cxx index 4bca5a4fe..f3e3ffa64 100644 --- a/src/encoder/EncoderList.cxx +++ b/src/encoder/EncoderList.cxx @@ -33,16 +33,16 @@ const EncoderPlugin *const encoder_plugins[] = { &null_encoder_plugin, -#ifdef ENABLE_VORBIS_ENCODER +#ifdef ENABLE_VORBISENC &vorbis_encoder_plugin, #endif -#ifdef HAVE_OPUS +#ifdef ENABLE_OPUS &opus_encoder_plugin, #endif #ifdef ENABLE_LAME_ENCODER &lame_encoder_plugin, #endif -#ifdef ENABLE_TWOLAME_ENCODER +#ifdef ENABLE_TWOLAME &twolame_encoder_plugin, #endif #ifdef ENABLE_WAVE_ENCODER @@ -51,7 +51,7 @@ const EncoderPlugin *const encoder_plugins[] = { #ifdef ENABLE_FLAC_ENCODER &flac_encoder_plugin, #endif -#ifdef ENABLE_SHINE_ENCODER +#ifdef ENABLE_SHINE &shine_encoder_plugin, #endif nullptr diff --git a/src/fs/io/TextFile.cxx b/src/fs/io/TextFile.cxx index 28d6dabcb..1710e0e89 100644 --- a/src/fs/io/TextFile.cxx +++ b/src/fs/io/TextFile.cxx @@ -28,14 +28,14 @@ TextFile::TextFile(Path path_fs, Error &error) :file_reader(new FileReader(path_fs, error)), -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB gunzip_reader(file_reader->IsDefined() ? new AutoGunzipReader(*file_reader) : nullptr), #endif buffered_reader(file_reader->IsDefined() ? new BufferedReader(* -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB gunzip_reader #else file_reader @@ -48,7 +48,7 @@ TextFile::TextFile(Path path_fs, Error &error) TextFile::~TextFile() { delete buffered_reader; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB delete gunzip_reader; #endif delete file_reader; diff --git a/src/fs/io/TextFile.hxx b/src/fs/io/TextFile.hxx index 5577363e7..425797ce7 100644 --- a/src/fs/io/TextFile.hxx +++ b/src/fs/io/TextFile.hxx @@ -34,7 +34,7 @@ class BufferedReader; class TextFile { FileReader *const file_reader; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB AutoGunzipReader *const gunzip_reader; #endif diff --git a/src/input/Registry.cxx b/src/input/Registry.cxx index 2b981df1c..6be3233e4 100644 --- a/src/input/Registry.cxx +++ b/src/input/Registry.cxx @@ -22,7 +22,7 @@ #include "util/Macros.hxx" #include "plugins/FileInputPlugin.hxx" -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA #include "plugins/AlsaInputPlugin.hxx" #endif @@ -34,7 +34,7 @@ #include "plugins/CurlInputPlugin.hxx" #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG #include "plugins/FfmpegInputPlugin.hxx" #endif @@ -60,7 +60,7 @@ const InputPlugin *const input_plugins[] = { &input_plugin_file, -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA &input_plugin_alsa, #endif #ifdef ENABLE_ARCHIVE @@ -69,7 +69,7 @@ const InputPlugin *const input_plugins[] = { #ifdef ENABLE_CURL &input_plugin_curl, #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG &input_plugin_ffmpeg, #endif #ifdef ENABLE_SMBCLIENT diff --git a/src/ls.cxx b/src/ls.cxx index 96c9f60e5..4e8fc88ab 100644 --- a/src/ls.cxx +++ b/src/ls.cxx @@ -41,7 +41,7 @@ static const char *remoteUrlPrefixes[] = { "mmst://", "mmsu://", #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG "gopher://", "rtp://", "rtsp://", @@ -61,7 +61,7 @@ static const char *remoteUrlPrefixes[] = { #ifdef ENABLE_DESPOTIFY "spt://", #endif -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA "alsa://", #endif NULL diff --git a/src/neighbor/Registry.cxx b/src/neighbor/Registry.cxx index f6d1f97b3..6f0651423 100644 --- a/src/neighbor/Registry.cxx +++ b/src/neighbor/Registry.cxx @@ -29,7 +29,7 @@ const NeighborPlugin *const neighbor_plugins[] = { #ifdef ENABLE_SMBCLIENT &smbclient_neighbor_plugin, #endif -#ifdef HAVE_LIBUPNP +#ifdef ENABLE_UPNP &upnp_neighbor_plugin, #endif nullptr diff --git a/src/output/Registry.cxx b/src/output/Registry.cxx index 566f6b6a8..2ce844179 100644 --- a/src/output/Registry.cxx +++ b/src/output/Registry.cxx @@ -54,13 +54,13 @@ const AudioOutputPlugin *const audio_output_plugins[] = { #ifdef ENABLE_PIPE_OUTPUT &pipe_output_plugin, #endif -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA &alsa_output_plugin, #endif -#ifdef HAVE_ROAR +#ifdef ENABLE_ROAR &roar_output_plugin, #endif -#ifdef HAVE_AO +#ifdef ENABLE_AO &ao_output_plugin, #endif #ifdef HAVE_OSS @@ -75,10 +75,10 @@ const AudioOutputPlugin *const audio_output_plugins[] = { #ifdef ENABLE_SOLARIS_OUTPUT &solaris_output_plugin, #endif -#ifdef HAVE_PULSE +#ifdef ENABLE_PULSE &pulse_output_plugin, #endif -#ifdef HAVE_JACK +#ifdef ENABLE_JACK &jack_output_plugin, #endif #ifdef ENABLE_HTTPD_OUTPUT diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index 120bad090..5dc733383 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -523,7 +523,11 @@ pulse_output_setup_stream(PulseOutput *po, const pa_sample_spec *ss, assert(po != nullptr); assert(po->context != nullptr); - po->stream = pa_stream_new(po->context, po->name, ss, nullptr); + /* WAVE-EX is been adopted as the speaker map for most media files */ + pa_channel_map chan_map; + pa_channel_map_init_auto(&chan_map, ss->channels, + PA_CHANNEL_MAP_WAVEEX); + po->stream = pa_stream_new(po->context, po->name, ss, &chan_map); if (po->stream == nullptr) { SetError(error, po->context, "pa_stream_new() has failed"); return false; diff --git a/src/pcm/ConfiguredResampler.cxx b/src/pcm/ConfiguredResampler.cxx index f6aec3f95..a65ec8702 100644 --- a/src/pcm/ConfiguredResampler.cxx +++ b/src/pcm/ConfiguredResampler.cxx @@ -25,11 +25,11 @@ #include "config/ConfigError.hxx" #include "util/Error.hxx" -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE #include "LibsamplerateResampler.hxx" #endif -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR #include "SoxrResampler.hxx" #endif @@ -38,11 +38,11 @@ enum class SelectedResampler { FALLBACK, -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE LIBSAMPLERATE, #endif -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR SOXR, #endif }; @@ -58,14 +58,14 @@ pcm_resampler_global_init(Error &error) if (strcmp(converter, "internal") == 0) return true; -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR if (memcmp(converter, "soxr", 4) == 0) { selected_resampler = SelectedResampler::SOXR; return pcm_resample_soxr_global_init(converter, error); } #endif -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE selected_resampler = SelectedResampler::LIBSAMPLERATE; return pcm_resample_lsr_global_init(converter, error); #endif @@ -86,12 +86,12 @@ pcm_resampler_create() case SelectedResampler::FALLBACK: return new FallbackPcmResampler(); -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE case SelectedResampler::LIBSAMPLERATE: return new LibsampleratePcmResampler(); #endif -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR case SelectedResampler::SOXR: return new SoxrPcmResampler(); #endif diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx index 4e9ef890e..aed8e1567 100644 --- a/src/playlist/PlaylistRegistry.cxx +++ b/src/playlist/PlaylistRegistry.cxx @@ -49,7 +49,7 @@ const struct playlist_plugin *const playlist_plugins[] = { // TODO: enable without GLib &pls_playlist_plugin, #endif -#ifdef HAVE_EXPAT +#ifdef ENABLE_EXPAT &xspf_playlist_plugin, &asx_playlist_plugin, &rss_playlist_plugin, diff --git a/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx index ec4d240a5..3fe8d57b1 100644 --- a/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx +++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx @@ -25,16 +25,17 @@ #include "input/InputStream.hxx" #include "tag/TagBuilder.hxx" #include "util/StringUtil.hxx" +#include "util/Alloc.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" -#include <glib.h> #include <yajl/yajl_parse.h> #include <string> #include <string.h> +#include <stdlib.h> static struct { std::string apikey; @@ -60,7 +61,7 @@ soundcloud_init(const config_param ¶m) /** * Construct a full soundcloud resolver URL from the given fragment. * @param uri uri of a soundcloud page (or just the path) - * @return Constructed URL. Must be freed with g_free. + * @return Constructed URL. Must be freed with free(). */ static char * soundcloud_resolve(const char* uri) @@ -68,18 +69,18 @@ soundcloud_resolve(const char* uri) char *u, *ru; if (StringStartsWith(uri, "https://")) { - u = g_strdup(uri); + u = xstrdup(uri); } else if (StringStartsWith(uri, "soundcloud.com")) { - u = g_strconcat("https://", uri, nullptr); + u = xstrcatdup("https://", uri); } else { /* assume it's just a path on soundcloud.com */ - u = g_strconcat("https://soundcloud.com/", uri, nullptr); + u = xstrcatdup("https://soundcloud.com/", uri); } - ru = g_strconcat("https://api.soundcloud.com/resolve.json?url=", - u, "&client_id=", - soundcloud_config.apikey.c_str(), nullptr); - g_free(u); + ru = xstrcatdup("https://api.soundcloud.com/resolve.json?url=", + u, "&client_id=", + soundcloud_config.apikey.c_str()); + free(u); return ru; } @@ -145,12 +146,12 @@ handle_string(void *ctx, const unsigned char* stringval, switch (data->key) { case Title: - g_free(data->title); - data->title = g_strndup(s, stringlen); + free(data->title); + data->title = xstrndup(s, stringlen); break; case Stream_URL: - g_free(data->stream_url); - data->stream_url = g_strndup(s, stringlen); + free(data->stream_url); + data->stream_url = xstrndup(s, stringlen); data->got_url = 1; break; default: @@ -211,8 +212,8 @@ handle_end_map(void *ctx) /* got_url == 1, track finished, make it into a song */ data->got_url = 0; - char *u = g_strconcat(data->stream_url, "?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + char *u = xstrcatdup(data->stream_url, "?client_id=", + soundcloud_config.apikey.c_str()); TagBuilder tag; tag.SetDuration(SignedSongTime::FromMS(data->duration)); @@ -220,7 +221,7 @@ handle_end_map(void *ctx) tag.AddItem(TAG_NAME, data->title); data->songs.emplace_front(u, tag.Commit()); - g_free(u); + free(u); return 1; } @@ -325,24 +326,24 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) char *u = nullptr; if (memcmp(uri, "track/", 6) == 0) { const char *rest = uri + 6; - u = g_strconcat("https://api.soundcloud.com/tracks/", - rest, ".json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/tracks/", + rest, ".json?client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "playlist/", 9) == 0) { const char *rest = uri + 9; - u = g_strconcat("https://api.soundcloud.com/playlists/", - rest, ".json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/playlists/", + rest, ".json?client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "user/", 5) == 0) { const char *rest = uri + 5; - u = g_strconcat("https://api.soundcloud.com/users/", - rest, "/tracks.json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/users/", + rest, "/tracks.json?client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "search/", 7) == 0) { const char *rest = uri + 7; - u = g_strconcat("https://api.soundcloud.com/tracks.json?q=", - rest, "&client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/tracks.json?q=", + rest, "&client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "url/", 4) == 0) { const char *rest = uri + 4; /* Translate to soundcloud resolver call. libcurl will automatically @@ -368,10 +369,10 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) int ret = soundcloud_parse_json(u, hand, mutex, cond); - g_free(u); + free(u); yajl_free(hand); - g_free(data.title); - g_free(data.stream_url); + free(data.title); + free(data.stream_url); if (ret == -1) return nullptr; diff --git a/src/sticker/SongSticker.cxx b/src/sticker/SongSticker.cxx index b6f46f167..b0b74b1a6 100644 --- a/src/sticker/SongSticker.cxx +++ b/src/sticker/SongSticker.cxx @@ -23,11 +23,11 @@ #include "db/LightSong.hxx" #include "db/Interface.hxx" #include "util/Error.hxx" - -#include <glib.h> +#include "util/Alloc.hxx" #include <assert.h> #include <string.h> +#include <stdlib.h> std::string sticker_song_get_value(const LightSong &song, const char *name) @@ -109,7 +109,7 @@ sticker_song_find(const Database &db, const char *base_uri, const char *name, if (*data.base_uri != 0) /* append slash to base_uri */ data.base_uri = allocated = - g_strconcat(data.base_uri, "/", nullptr); + xstrcatdup(data.base_uri, "/"); else /* searching in root directory - no trailing slash */ allocated = nullptr; @@ -118,7 +118,7 @@ sticker_song_find(const Database &db, const char *base_uri, const char *name, bool success = sticker_find("song", data.base_uri, name, sticker_song_find_cb, &data); - g_free(allocated); + free(allocated); return success; } diff --git a/src/system/fd_util.h b/src/system/fd_util.h index f4a940e91..6d6fe1e69 100644 --- a/src/system/fd_util.h +++ b/src/system/fd_util.h @@ -103,7 +103,7 @@ socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]); #endif -#ifdef HAVE_LIBMPDCLIENT +#ifdef ENABLE_LIBMPDCLIENT /* Avoid symbol conflict with statically linked libmpdclient */ #define socket_cloexec_nonblock socket_cloexec_nonblock_noconflict #endif diff --git a/src/tag/TagId3.hxx b/src/tag/TagId3.hxx index 1928d539d..ece7a599b 100644 --- a/src/tag/TagId3.hxx +++ b/src/tag/TagId3.hxx @@ -29,7 +29,7 @@ struct Tag; struct id3_tag; class Error; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG bool tag_id3_scan(Path path_fs, diff --git a/src/util/Alloc.cxx b/src/util/Alloc.cxx index ec3579470..403ad535d 100644 --- a/src/util/Alloc.cxx +++ b/src/util/Alloc.cxx @@ -74,3 +74,66 @@ xstrndup(const char *s, size_t n) return p; } + +template<typename... Args> +static inline size_t +FillLengths(size_t *lengths, const char *a, Args&&... args) +{ + return FillLengths(lengths, a) + FillLengths(lengths + 1, args...); +} + +template<> +inline size_t +FillLengths(gcc_unused size_t *lengths, const char *a) +{ + return *lengths = strlen(a); +} + +template<typename... Args> +static inline void +StringCat(char *p, const size_t *lengths, const char *a, Args&&... args) +{ + StringCat(p, lengths, a); + StringCat(p + *lengths, lengths + 1, args...); +} + +template<> +inline void +StringCat(char *p, const size_t *lengths, const char *a) +{ + memcpy(p, a, *lengths); +} + +template<typename... Args> +gcc_malloc gcc_nonnull_all +static inline char * +t_xstrcatdup(Args&&... args) +{ + constexpr size_t n = sizeof...(args); + + size_t lengths[n]; + const size_t total = FillLengths(lengths, args...); + + char *p = (char *)xalloc(total + 1); + StringCat(p, lengths, args...); + p[total] = 0; + return p; +} + +char * +xstrcatdup(const char *a, const char *b) +{ + return t_xstrcatdup(a, b); +} + +char * +xstrcatdup(const char *a, const char *b, const char *c) +{ + return t_xstrcatdup(a, b, c); +} + +char * +xstrcatdup(const char *a, const char *b, const char *c, const char *d) +{ + return t_xstrcatdup(a, b, c, d); +} diff --git a/src/util/Alloc.hxx b/src/util/Alloc.hxx index 15c123b7a..654b7d0fe 100644 --- a/src/util/Alloc.hxx +++ b/src/util/Alloc.hxx @@ -64,4 +64,23 @@ gcc_malloc gcc_nonnull_all char * xstrndup(const char *s, size_t n); +/** + * Concatenate two strings, returning a new allocation. Use free() to + * free it. + * + * This function never fails; in out-of-memory situations, it aborts + * the process. + */ +gcc_malloc gcc_nonnull_all +char * +xstrcatdup(const char *a, const char *b); + +gcc_malloc gcc_nonnull_all +char * +xstrcatdup(const char *a, const char *b, const char *c); + +gcc_malloc gcc_nonnull_all +char * +xstrcatdup(const char *a, const char *b, const char *c, const char *d); + #endif |