From 96ae659fdf7013bddc85b86dd5fa990b38df3b83 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 28 Nov 2022 18:49:35 +0100 Subject: [PATCH] system/FmtError: new library Replaces the Format*() functions in system/Error.hxx. --- src/LogInit.cxx | 12 +- src/archive/plugins/ZzipArchivePlugin.cxx | 4 +- src/config/File.cxx | 5 +- .../plugins/simple/SimpleDatabasePlugin.cxx | 14 +- src/event/InotifyEvent.cxx | 4 +- src/fs/DirectoryReader.cxx | 7 +- src/fs/FileInfo.hxx | 9 +- src/fs/FileSystem.cxx | 11 +- src/input/plugins/UringInputPlugin.cxx | 4 +- src/io/FileOutputStream.cxx | 40 +++--- src/io/FileReader.cxx | 13 +- src/io/Open.cxx | 18 +-- src/lib/jack/Dynamic.hxx | 2 +- src/mixer/plugins/OssMixerPlugin.cxx | 8 +- src/output/plugins/FifoOutputPlugin.cxx | 22 ++-- src/output/plugins/OssOutputPlugin.cxx | 8 +- src/output/plugins/PipeOutputPlugin.cxx | 4 +- src/output/plugins/SolarisOutputPlugin.cxx | 5 +- src/system/Error.hxx | 65 +--------- src/system/FmtError.cxx | 73 +++++++++++ src/system/FmtError.hxx | 122 ++++++++++++++++++ src/system/meson.build | 1 + src/unix/Daemon.cxx | 27 ++-- src/unix/PidFile.hxx | 11 +- src/win32/WinEvent.cxx | 2 +- 25 files changed, 305 insertions(+), 186 deletions(-) create mode 100644 src/system/FmtError.cxx create mode 100644 src/system/FmtError.hxx diff --git a/src/LogInit.cxx b/src/LogInit.cxx index 719ce3147..847e4c60c 100644 --- a/src/LogInit.cxx +++ b/src/LogInit.cxx @@ -21,6 +21,7 @@ #include "LogInit.hxx" #include "LogBackend.hxx" #include "Log.hxx" +#include "lib/fmt/PathFormatter.hxx" #include "config/Param.hxx" #include "config/Data.hxx" #include "config/Option.hxx" @@ -29,7 +30,7 @@ #include "util/Domain.hxx" #include "util/RuntimeError.hxx" #include "util/StringAPI.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include @@ -82,10 +83,8 @@ log_init_file(int line) throw FormatRuntimeError("failed to open log file \"%s\" (config line %d)", out_path_utf8.c_str(), line); #else - int e = errno; - const std::string out_path_utf8 = out_path.ToUTF8(); - throw FormatErrno(e, "failed to open log file \"%s\" (config line %d)", - out_path_utf8.c_str(), line); + throw FmtErrno("failed to open log file \"{}\" (config line {})", + out_path, line); #endif } @@ -242,10 +241,9 @@ cycle_log_files() noexcept fd = open_log_file(); if (fd < 0) { - const std::string out_path_utf8 = out_path.ToUTF8(); FmtError(log_domain, "error re-opening log file: {}", - out_path_utf8); + out_path); return -1; } diff --git a/src/archive/plugins/ZzipArchivePlugin.cxx b/src/archive/plugins/ZzipArchivePlugin.cxx index 27003b397..097afadb3 100644 --- a/src/archive/plugins/ZzipArchivePlugin.cxx +++ b/src/archive/plugins/ZzipArchivePlugin.cxx @@ -136,8 +136,8 @@ ZzipArchiveFile::OpenStream(const char *pathname, const auto error = (zzip_error_t)zzip_error(dir->dir); switch (error) { case ZZIP_ENOENT: - throw FormatFileNotFound("Failed to open '%s' in ZIP file", - pathname); + throw FmtFileNotFound("Failed to open '{}' in ZIP file", + pathname); default: throw FormatRuntimeError("Failed to open '%s' in ZIP file: %s", diff --git a/src/config/File.cxx b/src/config/File.cxx index bd321a2b8..a72472723 100644 --- a/src/config/File.cxx +++ b/src/config/File.cxx @@ -22,6 +22,7 @@ #include "Param.hxx" #include "Block.hxx" #include "Templates.hxx" +#include "lib/fmt/PathFormatter.hxx" #include "system/Error.hxx" #include "util/Tokenizer.hxx" #include "util/StringStrip.hxx" @@ -236,9 +237,8 @@ void ReadConfigFile(ConfigData &config_data, Path path) { assert(!path.IsNull()); - const std::string path_utf8 = path.ToUTF8(); - FmtDebug(config_file_domain, "loading file {}", path_utf8); + FmtDebug(config_file_domain, "loading file {}", path); FileReader file(path); @@ -247,6 +247,7 @@ ReadConfigFile(ConfigData &config_data, Path path) try { ReadConfigFile(config_data, reader, path.GetDirectoryName()); } catch (...) { + const std::string path_utf8 = path.ToUTF8(); std::throw_with_nested(FormatRuntimeError("Error in %s line %u", path_utf8.c_str(), reader.GetLineNumber())); diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx index c2ac58ab7..f9728f792 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx @@ -33,12 +33,14 @@ #include "DatabaseSave.hxx" #include "db/DatabaseLock.hxx" #include "db/DatabaseError.hxx" +#include "lib/fmt/PathFormatter.hxx" #include "fs/io/TextFile.hxx" #include "io/BufferedOutputStream.hxx" #include "io/FileOutputStream.hxx" #include "fs/FileInfo.hxx" #include "config/Block.hxx" #include "fs/FileSystem.hxx" +#include "system/FmtError.hxx" #include "util/CharUtil.hxx" #include "util/Domain.hxx" #include "util/RecursiveMap.hxx" @@ -119,12 +121,8 @@ SimpleDatabase::Check() const #ifndef _WIN32 /* Check if we can write to the directory */ - if (!CheckAccess(dirPath, X_OK | W_OK)) { - const int e = errno; - const std::string dirPath_utf8 = dirPath.ToUTF8(); - throw FormatErrno(e, "Can't create db file in \"%s\"", - dirPath_utf8.c_str()); - } + if (!CheckAccess(dirPath, X_OK | W_OK)) + throw FmtErrno("Can't create db file in \"{}\"", dirPath); #endif return; @@ -139,8 +137,8 @@ SimpleDatabase::Check() const #ifndef _WIN32 /* And check that we can write to it */ if (!CheckAccess(path, R_OK | W_OK)) - throw FormatErrno("Can't open db file \"%s\" for reading/writing", - path_utf8.c_str()); + throw FmtErrno("Can't open db file \"{}\" for reading/writing", + path); #endif } diff --git a/src/event/InotifyEvent.cxx b/src/event/InotifyEvent.cxx index 609923d48..cc6750547 100644 --- a/src/event/InotifyEvent.cxx +++ b/src/event/InotifyEvent.cxx @@ -31,7 +31,7 @@ */ #include "InotifyEvent.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include "io/UniqueFileDescriptor.hxx" #include @@ -68,7 +68,7 @@ InotifyEvent::AddWatch(const char *pathname, uint32_t mask) int wd = inotify_add_watch(event.GetFileDescriptor().Get(), pathname, mask); if (wd < 0) - throw FormatErrno("inotify_add_watch('%s') failed", pathname); + throw FmtErrno("inotify_add_watch('{}') failed", pathname); return wd; } diff --git a/src/fs/DirectoryReader.cxx b/src/fs/DirectoryReader.cxx index 79211b5c0..546e6a7ee 100644 --- a/src/fs/DirectoryReader.cxx +++ b/src/fs/DirectoryReader.cxx @@ -18,7 +18,8 @@ */ #include "DirectoryReader.hxx" -#include "system/Error.hxx" +#include "lib/fmt/PathFormatter.hxx" +#include "system/FmtError.hxx" #ifdef _WIN32 @@ -28,7 +29,7 @@ DirectoryReader::DirectoryReader(Path dir) :handle(FindFirstFile(MakeWildcardPath(dir.c_str()), &data)) { if (handle == INVALID_HANDLE_VALUE) - throw FormatLastError("Failed to open %s", dir.c_str()); + throw FmtLastError("Failed to open {}", dir); } #else @@ -37,7 +38,7 @@ DirectoryReader::DirectoryReader(Path dir) :dirp(opendir(dir.c_str())) { if (dirp == nullptr) - throw FormatErrno("Failed to open %s", dir.c_str()); + throw FmtErrno("Failed to open {}", dir); } #endif diff --git a/src/fs/FileInfo.hxx b/src/fs/FileInfo.hxx index 251318585..a50adf437 100644 --- a/src/fs/FileInfo.hxx +++ b/src/fs/FileInfo.hxx @@ -21,7 +21,8 @@ #define MPD_FS_FILE_INFO_HXX #include "Path.hxx" -#include "system/Error.hxx" +#include "lib/fmt/PathFormatter.hxx" +#include "system/FmtError.hxx" #ifdef _WIN32 #include "time/FileTime.hxx" @@ -49,11 +50,9 @@ public: FileInfo(Path path, bool follow_symlinks=true) { if (!GetFileInfo(path, *this, follow_symlinks)) { #ifdef _WIN32 - throw FormatLastError("Failed to access %s", - path.ToUTF8().c_str()); + throw FmtLastError("Failed to access {}", path); #else - throw FormatErrno("Failed to access %s", - path.ToUTF8().c_str()); + throw FmtErrno("Failed to access {}", path); #endif } } diff --git a/src/fs/FileSystem.cxx b/src/fs/FileSystem.cxx index ab479946d..dae47a648 100644 --- a/src/fs/FileSystem.cxx +++ b/src/fs/FileSystem.cxx @@ -20,7 +20,8 @@ #include "FileSystem.hxx" #include "AllocatedPath.hxx" #include "Limits.hxx" -#include "system/Error.hxx" +#include "lib/fmt/PathFormatter.hxx" +#include "system/FmtError.hxx" #ifdef _WIN32 #include // for CloseHandle() @@ -73,13 +74,13 @@ TruncateFile(Path path) TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (h == INVALID_HANDLE_VALUE) - throw FormatLastError("Failed to truncate %s", path.c_str()); + throw FmtLastError("Failed to truncate {}", path); CloseHandle(h); #else UniqueFileDescriptor fd; if (!fd.Open(path.c_str(), O_WRONLY|O_TRUNC)) - throw FormatErrno("Failed to truncate %s", path.c_str()); + throw FmtErrno("Failed to truncate {}", path); #endif } @@ -88,9 +89,9 @@ RemoveFile(Path path) { #ifdef _WIN32 if (!DeleteFile(path.c_str())) - throw FormatLastError("Failed to delete %s", path.c_str()); + throw FmtLastError("Failed to delete {}", path); #else if (unlink(path.c_str()) < 0) - throw FormatErrno("Failed to delete %s", path.c_str()); + throw FmtErrno("Failed to delete {}", path); #endif } diff --git a/src/input/plugins/UringInputPlugin.cxx b/src/input/plugins/UringInputPlugin.cxx index 0f72ac475..e671d4850 100644 --- a/src/input/plugins/UringInputPlugin.cxx +++ b/src/input/plugins/UringInputPlugin.cxx @@ -21,7 +21,7 @@ #include "../AsyncInputStream.hxx" #include "event/Call.hxx" #include "event/Loop.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include "io/Open.hxx" #include "io/UniqueFileDescriptor.hxx" #include "io/uring/ReadOperation.hxx" @@ -188,7 +188,7 @@ OpenUringInputStream(const char *path, Mutex &mutex) // TODO: use IORING_OP_STATX struct stat st; if (fstat(fd.Get(), &st) < 0) - throw FormatErrno("Failed to access %s", path); + throw FmtErrno("Failed to access {}", path); if (!S_ISREG(st.st_mode)) throw FormatRuntimeError("Not a regular file: %s", path); diff --git a/src/io/FileOutputStream.cxx b/src/io/FileOutputStream.cxx index f25adf29a..920f83b9f 100644 --- a/src/io/FileOutputStream.cxx +++ b/src/io/FileOutputStream.cxx @@ -28,7 +28,8 @@ */ #include "FileOutputStream.hxx" -#include "system/Error.hxx" +#include "lib/fmt/PathFormatter.hxx" +#include "system/FmtError.hxx" #include "util/StringFormat.hxx" #ifdef _WIN32 @@ -106,8 +107,7 @@ FileOutputStream::OpenCreate(bool visible) FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, nullptr); if (!IsDefined()) - throw FormatLastError("Failed to create %s", - path.ToUTF8().c_str()); + throw FmtLastError("Failed to create {}", path); } inline void @@ -118,14 +118,12 @@ FileOutputStream::OpenAppend(bool create) FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, nullptr); if (!IsDefined()) - throw FormatLastError("Failed to append to %s", - path.ToUTF8().c_str()); + throw FmtLastError("Failed to append to {}", path); if (!SeekEOF()) { auto code = GetLastError(); Close(); - throw FormatLastError(code, "Failed seek end-of-file of %s", - path.ToUTF8().c_str()); + throw FmtLastError(code, "Failed seek end-of-file of {}", path); } } @@ -148,12 +146,12 @@ FileOutputStream::Write(const void *data, size_t size) DWORD nbytes; if (!WriteFile(handle, data, size, &nbytes, nullptr)) - throw FormatLastError("Failed to write to %s", - GetPath().c_str()); + throw FmtLastError("Failed to write to {}", GetPath()); if (size_t(nbytes) != size) - throw FormatLastError(ERROR_DISK_FULL, "Failed to write to %s", - GetPath().c_str()); + throw FmtLastError(DWORD{ERROR_DISK_FULL}, + "Failed to write to {}", + GetPath()); } void @@ -162,7 +160,7 @@ FileOutputStream::Sync() assert(IsDefined()); if (!FlushFileBuffers(handle)) - throw FormatLastError("Failed to sync %s", GetPath().c_str()); + throw FmtLastError("Failed to sync {}", GetPath()); } void @@ -249,8 +247,7 @@ FileOutputStream::OpenCreate([[maybe_unused]] bool visible) GetPath().c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666)) - throw FormatErrno("Failed to create %s", - GetPath().c_str()); + throw FmtErrno("Failed to create {}", GetPath()); } inline void @@ -265,8 +262,7 @@ FileOutputStream::OpenAppend(bool create) directory_fd, #endif path.c_str(), flags)) - throw FormatErrno("Failed to append to %s", - path.c_str()); + throw FmtErrno("Failed to append to {}", path); } uint64_t @@ -282,10 +278,9 @@ FileOutputStream::Write(const void *data, size_t size) ssize_t nbytes = fd.Write(data, size); if (nbytes < 0) - throw FormatErrno("Failed to write to %s", GetPath().c_str()); + throw FmtErrno("Failed to write to {}", GetPath()); else if ((size_t)nbytes < size) - throw FormatErrno(ENOSPC, "Failed to write to %s", - GetPath().c_str()); + throw FmtErrno(ENOSPC, "Failed to write to {}", GetPath()); } void @@ -299,7 +294,7 @@ FileOutputStream::Sync() const bool success = fsync(fd.Get()) == 0; #endif if (!success) - throw FormatErrno("Failed to sync %s", GetPath().c_str()); + throw FmtErrno("Failed to sync {}", GetPath()); } void @@ -316,13 +311,12 @@ try { StringFormat<64>("/proc/self/fd/%d", fd.Get()), directory_fd.Get(), path.c_str(), AT_SYMLINK_FOLLOW) < 0) - throw FormatErrno("Failed to commit %s", - path.c_str()); + throw FmtErrno("Failed to commit {}", path); } #endif if (!Close()) { - throw FormatErrno("Failed to commit %s", path.c_str()); + throw FmtErrno("Failed to commit {}", path); } if (tmp_path != nullptr) diff --git a/src/io/FileReader.cxx b/src/io/FileReader.cxx index a8eb06680..42799628d 100644 --- a/src/io/FileReader.cxx +++ b/src/io/FileReader.cxx @@ -28,8 +28,9 @@ */ #include "FileReader.hxx" +#include "lib/fmt/PathFormatter.hxx" #include "fs/FileInfo.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include "io/Open.hxx" #include @@ -43,7 +44,7 @@ FileReader::FileReader(Path _path) nullptr)) { if (handle == INVALID_HANDLE_VALUE) - throw FormatLastError("Failed to open %s", path.ToUTF8().c_str()); + throw FmtLastError("Failed to open {}", path); } FileInfo @@ -61,8 +62,7 @@ FileReader::Read(void *data, std::size_t size) DWORD nbytes; if (!ReadFile(handle, data, size, &nbytes, nullptr)) - throw FormatLastError("Failed to read from %s", - path.ToUTF8().c_str()); + throw FmtLastError("Failed to read from %s", path); return nbytes; } @@ -110,8 +110,7 @@ FileReader::GetFileInfo() const FileInfo info; const bool success = fstat(fd.Get(), &info.st) == 0; if (!success) - throw FormatErrno("Failed to access %s", - path.ToUTF8().c_str()); + throw FmtErrno("Failed to access {}", path); return info; } @@ -123,7 +122,7 @@ FileReader::Read(void *data, std::size_t size) ssize_t nbytes = fd.Read(data, size); if (nbytes < 0) - throw FormatErrno("Failed to read from %s", path.ToUTF8().c_str()); + throw FmtErrno("Failed to read from {}", path); return nbytes; } diff --git a/src/io/Open.cxx b/src/io/Open.cxx index 79455fe9f..1b767b17e 100644 --- a/src/io/Open.cxx +++ b/src/io/Open.cxx @@ -29,7 +29,7 @@ #include "Open.hxx" #include "UniqueFileDescriptor.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include @@ -38,7 +38,7 @@ OpenReadOnly(const char *path, int flags) { UniqueFileDescriptor fd; if (!fd.Open(path, O_RDONLY|flags)) - throw FormatErrno("Failed to open '%s'", path); + throw FmtErrno("Failed to open '{}'", path); return fd; } @@ -48,7 +48,7 @@ OpenWriteOnly(const char *path, int flags) { UniqueFileDescriptor fd; if (!fd.Open(path, O_WRONLY|flags)) - throw FormatErrno("Failed to open '%s'", path); + throw FmtErrno("Failed to open '{}'", path); return fd; } @@ -60,7 +60,7 @@ OpenDirectory(const char *path, int flags) { UniqueFileDescriptor fd; if (!fd.Open(path, O_DIRECTORY|O_RDONLY|flags)) - throw FormatErrno("Failed to open '%s'", path); + throw FmtErrno("Failed to open '{}'", path); return fd; } @@ -74,7 +74,7 @@ OpenPath(const char *path, int flags) { UniqueFileDescriptor fd; if (!fd.Open(path, O_PATH|flags)) - throw FormatErrno("Failed to open '%s'", path); + throw FmtErrno("Failed to open '{}'", path); return fd; } @@ -84,7 +84,7 @@ OpenPath(FileDescriptor directory, const char *name, int flags) { UniqueFileDescriptor fd; if (!fd.Open(directory, name, O_PATH|flags)) - throw FormatErrno("Failed to open '%s'", name); + throw FmtErrno("Failed to open '{}'", name); return fd; } @@ -94,7 +94,7 @@ OpenReadOnly(FileDescriptor directory, const char *name, int flags) { UniqueFileDescriptor fd; if (!fd.Open(directory, name, O_RDONLY|flags)) - throw FormatErrno("Failed to open '%s'", name); + throw FmtErrno("Failed to open '{}'", name); return fd; } @@ -104,7 +104,7 @@ OpenWriteOnly(FileDescriptor directory, const char *name, int flags) { UniqueFileDescriptor fd; if (!fd.Open(directory, name, O_WRONLY|flags)) - throw FormatErrno("Failed to open '%s'", name); + throw FmtErrno("Failed to open '{}'", name); return fd; } @@ -114,7 +114,7 @@ OpenDirectory(FileDescriptor directory, const char *name, int flags) { UniqueFileDescriptor fd; if (!fd.Open(directory, name, O_DIRECTORY|O_RDONLY|flags)) - throw FormatErrno("Failed to open '%s'", name); + throw FmtErrno("Failed to open '{}'", name); return fd; } diff --git a/src/lib/jack/Dynamic.hxx b/src/lib/jack/Dynamic.hxx index ff56cc21c..b15d7045e 100644 --- a/src/lib/jack/Dynamic.hxx +++ b/src/lib/jack/Dynamic.hxx @@ -118,7 +118,7 @@ LoadJackLibrary() auto libjack = LoadLibraryA(LIBJACK); if (!libjack) - throw FormatLastError("Failed to load " LIBJACK ".dll"); + throw MakeLastError("Failed to load " LIBJACK ".dll"); GetFunction(libjack, "jack_set_error_function", _jack_set_error_function); GetFunction(libjack, "jack_set_info_function", _jack_set_info_function); diff --git a/src/mixer/plugins/OssMixerPlugin.cxx b/src/mixer/plugins/OssMixerPlugin.cxx index 685079677..c6ef53395 100644 --- a/src/mixer/plugins/OssMixerPlugin.cxx +++ b/src/mixer/plugins/OssMixerPlugin.cxx @@ -21,7 +21,7 @@ #include "mixer/Mixer.hxx" #include "config/Block.hxx" #include "io/FileDescriptor.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include "util/ASCII.hxx" #include "util/Domain.hxx" #include "util/RuntimeError.hxx" @@ -119,7 +119,7 @@ OssMixer::Open() { device_fd.OpenReadOnly(device); if (!device_fd.IsDefined()) - throw FormatErrno("failed to open %s", device); + throw FmtErrno("failed to open {}", device); try { if (control) { @@ -129,8 +129,8 @@ OssMixer::Open() throw MakeErrno("READ_DEVMASK failed"); if (((1 << volume_control) & devmask) == 0) - throw FormatErrno("mixer control \"%s\" not usable", - control); + throw FmtErrno("mixer control \"{}\" not usable", + control); } } catch (...) { Close(); diff --git a/src/output/plugins/FifoOutputPlugin.cxx b/src/output/plugins/FifoOutputPlugin.cxx index 64cb360da..a7551255c 100644 --- a/src/output/plugins/FifoOutputPlugin.cxx +++ b/src/output/plugins/FifoOutputPlugin.cxx @@ -20,9 +20,11 @@ #include "FifoOutputPlugin.hxx" #include "../OutputAPI.hxx" #include "../Timer.hxx" +#include "lib/fmt/PathFormatter.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" #include "fs/FileInfo.hxx" +#include "system/FmtError.hxx" #include "util/Domain.hxx" #include "util/RuntimeError.hxx" #include "Log.hxx" @@ -91,7 +93,7 @@ inline void FifoOutput::Delete() { FmtDebug(fifo_output_domain, - "Removing FIFO \"{}\"", path_utf8); + "Removing FIFO \"{}\"", path); try { RemoveFile(path); @@ -125,8 +127,7 @@ inline void FifoOutput::Create() { if (!MakeFifo(path, 0666)) - throw FormatErrno("Couldn't create FIFO \"%s\"", - path_utf8.c_str()); + throw FmtErrno("Couldn't create FIFO \"{}\"", path); created = true; } @@ -142,8 +143,7 @@ FifoOutput::Check() return; } - throw FormatErrno("Failed to stat FIFO \"%s\"", - path_utf8.c_str()); + throw FmtErrno("Failed to stat FIFO \"{}\"", path); } if (!S_ISFIFO(st.st_mode)) @@ -158,13 +158,12 @@ try { input = OpenFile(path, O_RDONLY|O_NONBLOCK|O_BINARY, 0).Steal(); if (input < 0) - throw FormatErrno("Could not open FIFO \"%s\" for reading", - path_utf8.c_str()); + throw FmtErrno("Could not open FIFO \"{}\" for reading", + path); output = OpenFile(path, O_WRONLY|O_NONBLOCK|O_BINARY, 0).Steal(); if (output < 0) - throw FormatErrno("Could not open FIFO \"%s\" for writing", - path_utf8.c_str()); + throw FmtErrno("Could not open FIFO \"{}\" for writing"); } catch (...) { CloseFifo(); throw; @@ -196,7 +195,7 @@ FifoOutput::Cancel() noexcept if (bytes < 0 && errno != EAGAIN) { FmtError(fifo_output_domain, "Flush of FIFO \"{}\" failed: {}", - path_utf8, strerror(errno)); + path, strerror(errno)); } } @@ -230,8 +229,7 @@ FifoOutput::Play(std::span src) continue; } - throw FormatErrno("Failed to write to FIFO %s", - path_utf8.c_str()); + throw FmtErrno("Failed to write to FIFO {}", path); } } } diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx index f98bf6f1a..f8a18d21e 100644 --- a/src/output/plugins/OssOutputPlugin.cxx +++ b/src/output/plugins/OssOutputPlugin.cxx @@ -22,7 +22,7 @@ #include "mixer/plugins/OssMixerPlugin.hxx" #include "pcm/Export.hxx" #include "io/UniqueFileDescriptor.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include "util/Domain.hxx" #include "util/ByteOrder.hxx" #include "util/Manual.hxx" @@ -643,7 +643,7 @@ try { assert(!fd.IsDefined()); if (!fd.Open(device, O_WRONLY)) - throw FormatErrno("Error opening OSS device \"%s\"", device); + throw FmtErrno("Error opening OSS device \"{}\"", device); OssIoctlExact(fd, SNDCTL_DSP_CHANNELS, effective_channels, "Failed to set channel count"); @@ -660,7 +660,7 @@ void OssOutput::Open(AudioFormat &_audio_format) try { if (!fd.Open(device, O_WRONLY)) - throw FormatErrno("Error opening OSS device \"%s\"", device); + throw FmtErrno("Error opening OSS device \"{}\"", device); SetupOrDop(_audio_format); } catch (...) { @@ -698,7 +698,7 @@ OssOutput::Play(std::span src) return pcm_export->CalcInputSize(ret); if (ret < 0 && errno != EINTR) - throw FormatErrno("Write error on %s", device); + throw FmtErrno("Write error on {}", device); } } diff --git a/src/output/plugins/PipeOutputPlugin.cxx b/src/output/plugins/PipeOutputPlugin.cxx index 8d51a15ef..a83202ff5 100644 --- a/src/output/plugins/PipeOutputPlugin.cxx +++ b/src/output/plugins/PipeOutputPlugin.cxx @@ -19,7 +19,7 @@ #include "PipeOutputPlugin.hxx" #include "../OutputAPI.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include #include @@ -61,7 +61,7 @@ PipeOutput::Open([[maybe_unused]] AudioFormat &audio_format) { fh = popen(cmd.c_str(), "w"); if (fh == nullptr) - throw FormatErrno("Error opening pipe \"%s\"", cmd.c_str()); + throw FmtErrno("Error opening pipe \"{}\"", cmd); } std::size_t diff --git a/src/output/plugins/SolarisOutputPlugin.cxx b/src/output/plugins/SolarisOutputPlugin.cxx index 5f791ae49..763596614 100644 --- a/src/output/plugins/SolarisOutputPlugin.cxx +++ b/src/output/plugins/SolarisOutputPlugin.cxx @@ -20,7 +20,7 @@ #include "SolarisOutputPlugin.hxx" #include "../OutputAPI.hxx" #include "io/FileDescriptor.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include @@ -100,8 +100,7 @@ SolarisOutput::Open(AudioFormat &audio_format) /* open the device in non-blocking mode */ if (!fd.Open(device, O_WRONLY|O_NONBLOCK)) - throw FormatErrno("Failed to open %s", - device); + throw FmtErrno("Failed to open {}", device); /* restore blocking mode */ diff --git a/src/system/Error.hxx b/src/system/Error.hxx index 01ff29b9f..4d864c368 100644 --- a/src/system/Error.hxx +++ b/src/system/Error.hxx @@ -32,8 +32,6 @@ #include // IWYU pragma: export #include -#include - template static inline std::system_error FormatSystemError(std::error_code code, const char *fmt, @@ -47,8 +45,7 @@ FormatSystemError(std::error_code code, const char *fmt, #ifdef _WIN32 #include // for GetLastError() -#include // for HWND (needed by winbase.h) -#include // for FormatMessageA() +#include /** * Returns the error_category to be used to wrap WIN32 GetLastError() @@ -85,41 +82,10 @@ MakeLastError(const char *msg) noexcept return MakeLastError(GetLastError(), msg); } -template -static inline std::system_error -FormatLastError(DWORD code, const char *fmt, Args&&... args) noexcept -{ - char buffer[512]; - const auto end = buffer + sizeof(buffer); - constexpr std::size_t max_prefix = sizeof(buffer) - 128; - size_t length = snprintf(buffer, max_prefix, - fmt, std::forward(args)...); - if (length >= max_prefix) - length = max_prefix - 1; - char *p = buffer + length; - *p++ = ':'; - *p++ = ' '; - - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, code, 0, p, end - p, nullptr); - return MakeLastError(code, buffer); -} - -template -static inline std::system_error -FormatLastError(const char *fmt, Args&&... args) noexcept -{ - return FormatLastError(GetLastError(), fmt, - std::forward(args)...); -} - #endif /* _WIN32 */ #include // IWYU pragma: export -#include - /** * Returns the error_category to be used to wrap errno values. The * C++ standard does not define this well, so this code is based on @@ -156,35 +122,6 @@ MakeErrno(const char *msg) noexcept return MakeErrno(errno, msg); } -template -static inline std::system_error -FormatErrno(int code, const char *fmt, Args&&... args) noexcept -{ - char buffer[512]; - snprintf(buffer, sizeof(buffer), - fmt, std::forward(args)...); - return MakeErrno(code, buffer); -} - -template -static inline std::system_error -FormatErrno(const char *fmt, Args&&... args) noexcept -{ - return FormatErrno(errno, fmt, std::forward(args)...); -} - -template -static inline std::system_error -FormatFileNotFound(const char *fmt, Args&&... args) noexcept -{ -#ifdef _WIN32 - return FormatLastError(ERROR_FILE_NOT_FOUND, fmt, - std::forward(args)...); -#else - return FormatErrno(ENOENT, fmt, std::forward(args)...); -#endif -} - [[gnu::pure]] inline bool IsErrno(const std::system_error &e, int code) noexcept diff --git a/src/system/FmtError.cxx b/src/system/FmtError.cxx new file mode 100644 index 000000000..4ca2ddfc4 --- /dev/null +++ b/src/system/FmtError.cxx @@ -0,0 +1,73 @@ +/* + * Copyright 2022 Max Kellermann + * + * 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 "FmtError.hxx" + +#include + +#include + +std::system_error +VFmtSystemError(std::error_code code, + fmt::string_view format_str, fmt::format_args args) noexcept +{ + std::array buffer; + auto [p, _] = fmt::vformat_to_n(buffer.begin(), buffer.size() - 1, + format_str, args); + *p = 0; + return std::system_error{code, buffer.data()}; +} + +#ifdef _WIN32 + +#include // for HWND (needed by winbase.h) +#include // for FormatMessageA() + +std::system_error +VFmtLastError(DWORD code, + fmt::string_view format_str, fmt::format_args args) noexcept +{ + std::array buffer; + const auto end = buffer.data() + buffer.size(); + + constexpr std::size_t max_prefix = sizeof(buffer) - 128; + auto [p, _] = fmt::vformat_to_n(buffer.data(), + buffer.size() - max_prefix, + format_str, args); + *p++ = ':'; + *p++ = ' '; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, code, 0, p, end - p, nullptr); + + return MakeLastError(code, buffer.data()); +} + +#endif // _WIN32 diff --git a/src/system/FmtError.hxx b/src/system/FmtError.hxx new file mode 100644 index 000000000..81bdb07b0 --- /dev/null +++ b/src/system/FmtError.hxx @@ -0,0 +1,122 @@ +/* + * Copyright 2022 Max Kellermann + * + * 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. + */ + +#pragma once + +#include "Error.hxx" // IWYU pragma: export + +#include +#if FMT_VERSION >= 80000 && FMT_VERSION < 90000 +#include +#endif + +[[nodiscard]] [[gnu::pure]] +std::system_error +VFmtSystemError(std::error_code code, + fmt::string_view format_str, fmt::format_args args) noexcept; + +template +[[nodiscard]] [[gnu::pure]] +std::system_error +FmtSystemError(std::error_code code, + const S &format_str, Args&&... args) noexcept +{ +#if FMT_VERSION >= 90000 + return VFmtSystemError(code, format_str, + fmt::make_format_args(args...)); +#else + return VFmtSystemError(code, fmt::to_string_view(format_str), + fmt::make_args_checked(format_str, + args...)); +#endif +} + +#ifdef _WIN32 + +[[nodiscard]] [[gnu::pure]] +std::system_error +VFmtLastError(DWORD code, + fmt::string_view format_str, fmt::format_args args) noexcept; + +template +[[nodiscard]] [[gnu::pure]] +std::system_error +FmtLastError(DWORD code, + const S &format_str, Args&&... args) noexcept +{ +#if FMT_VERSION >= 90000 + return VFmtLastError(code, format_str, + fmt::make_format_args(args...)); +#else + return VFmtLastError(code, fmt::to_string_view(format_str), + fmt::make_args_checked(format_str, + args...)); +#endif +} + +template +[[nodiscard]] [[gnu::pure]] +std::system_error +FmtLastError(const S &format_str, Args&&... args) noexcept +{ + return FmtLastError(GetLastError(), + format_str, std::forward(args)...); +} + +#endif // _WIN32 + +template +[[nodiscard]] [[gnu::pure]] +std::system_error +FmtErrno(int code, const S &format_str, Args&&... args) noexcept +{ + return FmtSystemError(std::error_code(code, ErrnoCategory()), + format_str, std::forward(args)...); +} + +template +[[nodiscard]] [[gnu::pure]] +std::system_error +FmtErrno(const S &format_str, Args&&... args) noexcept +{ + return FmtErrno(errno, format_str, std::forward(args)...); +} + +template +[[nodiscard]] [[gnu::pure]] +std::system_error +FmtFileNotFound(const S &format_str, Args&&... args) noexcept +{ +#ifdef _WIN32 + return FmtLastError(DWORD{ERROR_FILE_NOT_FOUND}, + format_str, std::forward(args)...); +#else + return FmtErrno(ENOENT, format_str, std::forward(args)...); +#endif +} diff --git a/src/system/meson.build b/src/system/meson.build index 9bc557217..8272bde4d 100644 --- a/src/system/meson.build +++ b/src/system/meson.build @@ -1,5 +1,6 @@ system_sources = [ 'EventPipe.cxx', + 'FmtError.cxx', ] if host_machine.system() == 'linux' diff --git a/src/unix/Daemon.cxx b/src/unix/Daemon.cxx index 3819122cf..4e18bb111 100644 --- a/src/unix/Daemon.cxx +++ b/src/unix/Daemon.cxx @@ -19,7 +19,8 @@ #include "config.h" #include "Daemon.hxx" -#include "system/Error.hxx" +#include "lib/fmt/PathFormatter.hxx" +#include "system/FmtError.hxx" #include "fs/AllocatedPath.hxx" #include "util/RuntimeError.hxx" @@ -70,14 +71,12 @@ daemonize_kill() throw std::runtime_error("no pid_file specified in the config file"); const pid_t pid = ReadPidFile(pidfile); - if (pid < 0) { - const std::string utf8 = pidfile.ToUTF8(); - throw FormatErrno("unable to read the pid from file \"%s\"", - utf8.c_str()); - } + if (pid < 0) + throw FmtErrno("unable to read the pid from file \"{}\"", + pidfile); if (kill(pid, SIGTERM) < 0) - throw FormatErrno("unable to kill process %i", int(pid)); + throw FmtErrno("unable to kill process {}", pid); std::exit(EXIT_SUCCESS); } @@ -98,7 +97,7 @@ daemonize_set_user() /* set gid */ if (user_gid != (gid_t)-1 && user_gid != getgid() && setgid(user_gid) == -1) { - throw FormatErrno("Failed to set group %d", (int)user_gid); + throw FmtErrno("Failed to set group {}", user_gid); } #ifdef HAVE_INITGROUPS @@ -110,16 +109,16 @@ daemonize_set_user() we are already this user */ user_uid != getuid() && initgroups(user_name, user_gid) == -1) { - throw FormatErrno("Failed to set supplementary groups " - "of user \"%s\"", - user_name); + throw FmtErrno("Failed to set supplementary groups " + "of user \"{}\"", + user_name); } #endif /* set uid */ if (user_uid != (uid_t)-1 && user_uid != getuid() && setuid(user_uid) == -1) { - throw FormatErrno("Failed to set user \"%s\"", user_name); + throw FmtErrno("Failed to set user \"{}\"", user_name); } } @@ -191,8 +190,8 @@ daemonize_begin(bool detach) throw MakeErrno("waitpid() failed"); if (WIFSIGNALED(status)) - throw FormatErrno("MPD died from signal %d%s", WTERMSIG(status), - WCOREDUMP(status) ? " (core dumped)" : ""); + throw FmtErrno("MPD died from signal {}{}", WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); std::exit(WEXITSTATUS(status)); } diff --git a/src/unix/PidFile.hxx b/src/unix/PidFile.hxx index c3ec5beba..b59acabf6 100644 --- a/src/unix/PidFile.hxx +++ b/src/unix/PidFile.hxx @@ -20,9 +20,10 @@ #ifndef MPD_PID_FILE_HXX #define MPD_PID_FILE_HXX +#include "lib/fmt/PathFormatter.hxx" #include "fs/FileSystem.hxx" #include "fs/AllocatedPath.hxx" -#include "system/Error.hxx" +#include "system/FmtError.hxx" #include @@ -40,11 +41,9 @@ public: return; fd = OpenFile(path, O_WRONLY|O_CREAT|O_TRUNC, 0666).Steal(); - if (fd < 0) { - const std::string utf8 = path.ToUTF8(); - throw FormatErrno("Failed to create pid file \"%s\"", - utf8.c_str()); - } + if (fd < 0) + throw FmtErrno("Failed to create pid file \"{}\"", + path); } PidFile(const PidFile &) = delete; diff --git a/src/win32/WinEvent.cxx b/src/win32/WinEvent.cxx index a6d62061f..3c848236e 100644 --- a/src/win32/WinEvent.cxx +++ b/src/win32/WinEvent.cxx @@ -24,5 +24,5 @@ WinEvent::WinEvent() :event(CreateEventW(nullptr, false, false, nullptr)) { if (!event) - throw FormatLastError("Error creating events"); + throw MakeLastError("Error creating events"); }