From aafff8fd5c60476702466ba06281236222455167 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 7 Aug 2014 18:35:57 +0200 Subject: fs/output, fs/TextFile: move to fs/io/ --- src/fs/StandardDirectory.cxx | 2 +- src/fs/TextFile.cxx | 81 ------------------- src/fs/TextFile.hxx | 62 -------------- src/fs/io/BufferedOutputStream.cxx | 143 +++++++++++++++++++++++++++++++++ src/fs/io/BufferedOutputStream.hxx | 71 ++++++++++++++++ src/fs/io/FileOutputStream.cxx | 135 +++++++++++++++++++++++++++++++ src/fs/io/FileOutputStream.hxx | 68 ++++++++++++++++ src/fs/io/GzipOutputStream.cxx | 106 ++++++++++++++++++++++++ src/fs/io/GzipOutputStream.hxx | 71 ++++++++++++++++ src/fs/io/OutputStream.hxx | 38 +++++++++ src/fs/io/StdioOutputStream.hxx | 45 +++++++++++ src/fs/io/TextFile.cxx | 81 +++++++++++++++++++ src/fs/io/TextFile.hxx | 62 ++++++++++++++ src/fs/output/BufferedOutputStream.cxx | 143 --------------------------------- src/fs/output/BufferedOutputStream.hxx | 71 ---------------- src/fs/output/FileOutputStream.cxx | 135 ------------------------------- src/fs/output/FileOutputStream.hxx | 68 ---------------- src/fs/output/GzipOutputStream.cxx | 106 ------------------------ src/fs/output/GzipOutputStream.hxx | 71 ---------------- src/fs/output/OutputStream.hxx | 38 --------- src/fs/output/StdioOutputStream.hxx | 45 ----------- 21 files changed, 821 insertions(+), 821 deletions(-) delete mode 100644 src/fs/TextFile.cxx delete mode 100644 src/fs/TextFile.hxx create mode 100644 src/fs/io/BufferedOutputStream.cxx create mode 100644 src/fs/io/BufferedOutputStream.hxx create mode 100644 src/fs/io/FileOutputStream.cxx create mode 100644 src/fs/io/FileOutputStream.hxx create mode 100644 src/fs/io/GzipOutputStream.cxx create mode 100644 src/fs/io/GzipOutputStream.hxx create mode 100644 src/fs/io/OutputStream.hxx create mode 100644 src/fs/io/StdioOutputStream.hxx create mode 100644 src/fs/io/TextFile.cxx create mode 100644 src/fs/io/TextFile.hxx delete mode 100644 src/fs/output/BufferedOutputStream.cxx delete mode 100644 src/fs/output/BufferedOutputStream.hxx delete mode 100644 src/fs/output/FileOutputStream.cxx delete mode 100644 src/fs/output/FileOutputStream.hxx delete mode 100644 src/fs/output/GzipOutputStream.cxx delete mode 100644 src/fs/output/GzipOutputStream.hxx delete mode 100644 src/fs/output/OutputStream.hxx delete mode 100644 src/fs/output/StdioOutputStream.hxx (limited to 'src/fs') diff --git a/src/fs/StandardDirectory.cxx b/src/fs/StandardDirectory.cxx index ea91399d1..7a8666501 100644 --- a/src/fs/StandardDirectory.cxx +++ b/src/fs/StandardDirectory.cxx @@ -41,7 +41,7 @@ #ifdef USE_XDG #include "util/StringUtil.hxx" -#include "TextFile.hxx" +#include "io/TextFile.hxx" #include #include #endif diff --git a/src/fs/TextFile.cxx b/src/fs/TextFile.cxx deleted file mode 100644 index b1a92b9cc..000000000 --- a/src/fs/TextFile.cxx +++ /dev/null @@ -1,81 +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 "TextFile.hxx" -#include "util/Alloc.hxx" -#include "fs/Path.hxx" -#include "fs/FileSystem.hxx" - -#include -#include -#include - -TextFile::TextFile(Path path_fs) - :file(FOpen(path_fs, FOpenMode::ReadText)), - buffer((char *)xalloc(step)), capacity(step), length(0) {} - -TextFile::~TextFile() -{ - free(buffer); - - if (file != nullptr) - fclose(file); -} - -char * -TextFile::ReadLine() -{ - assert(file != nullptr); - - while (true) { - if (length >= capacity) { - if (capacity >= max_length) - /* too large already - bail out */ - return nullptr; - - capacity <<= 1; - char *new_buffer = (char *)realloc(buffer, capacity); - if (new_buffer == nullptr) - /* out of memory - bail out */ - return nullptr; - } - - char *p = fgets(buffer + length, capacity - length, file); - if (p == nullptr) { - if (length == 0 || ferror(file)) - return nullptr; - break; - } - - length += strlen(buffer + length); - if (buffer[length - 1] == '\n') - break; - } - - /* remove the newline characters */ - if (buffer[length - 1] == '\n') - --length; - if (buffer[length - 1] == '\r') - --length; - - buffer[length] = 0; - length = 0; - return buffer; -} diff --git a/src/fs/TextFile.hxx b/src/fs/TextFile.hxx deleted file mode 100644 index e3a712a88..000000000 --- a/src/fs/TextFile.hxx +++ /dev/null @@ -1,62 +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. - */ - -#ifndef MPD_TEXT_FILE_HXX -#define MPD_TEXT_FILE_HXX - -#include "Compiler.h" - -#include -#include - -class Path; - -class TextFile { - static constexpr size_t max_length = 512 * 1024; - static constexpr size_t step = 1024; - - FILE *const file; - - char *buffer; - size_t capacity, length; - -public: - TextFile(Path path_fs); - - TextFile(const TextFile &other) = delete; - - ~TextFile(); - - bool HasFailed() const { - return gcc_unlikely(file == nullptr); - } - - /** - * Reads a line from the input file, and strips trailing - * space. There is a reasonable maximum line length, only to - * prevent denial of service. - * - * @param file the source file, opened in text mode - * @param buffer an allocator for the buffer - * @return a pointer to the line, or nullptr on end-of-file or error - */ - char *ReadLine(); -}; - -#endif diff --git a/src/fs/io/BufferedOutputStream.cxx b/src/fs/io/BufferedOutputStream.cxx new file mode 100644 index 000000000..088a3e279 --- /dev/null +++ b/src/fs/io/BufferedOutputStream.cxx @@ -0,0 +1,143 @@ +/* + * 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 "BufferedOutputStream.hxx" +#include "OutputStream.hxx" + +#include +#include +#include + +bool +BufferedOutputStream::AppendToBuffer(const void *data, size_t size) +{ + auto r = buffer.Write(); + if (r.size < size) + return false; + + memcpy(r.data, data, size); + buffer.Append(size); + return true; +} + +bool +BufferedOutputStream::Write(const void *data, size_t size) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + if (AppendToBuffer(data, size)) + return true; + + if (!Flush()) + return false; + + if (AppendToBuffer(data, size)) + return true; + + return os.Write(data, size, last_error); +} + +bool +BufferedOutputStream::Write(const char *p) +{ + return Write(p, strlen(p)); +} + +bool +BufferedOutputStream::Format(const char *fmt, ...) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + auto r = buffer.Write(); + if (r.IsEmpty()) { + if (!Flush()) + return false; + + r = buffer.Write(); + } + + /* format into the buffer */ + va_list ap; + va_start(ap, fmt); + size_t size = vsnprintf(r.data, r.size, fmt, ap); + va_end(ap); + + if (gcc_unlikely(size >= r.size)) { + /* buffer was not large enough; flush it and try + again */ + + if (!Flush()) + return false; + + r = buffer.Write(); + + if (gcc_unlikely(size >= r.size)) { + /* still not enough space: grow the buffer and + try again */ + r.size = size + 1; + r.data = buffer.Write(r.size); + } + + /* format into the new buffer */ + va_start(ap, fmt); + size = vsnprintf(r.data, r.size, fmt, ap); + va_end(ap); + + /* this time, it must fit */ + assert(size < r.size); + } + + buffer.Append(size); + return true; +} + +bool +BufferedOutputStream::Flush() +{ + if (!Check()) + return false; + + auto r = buffer.Read(); + if (r.IsEmpty()) + return true; + + bool success = os.Write(r.data, r.size, last_error); + if (gcc_likely(success)) + buffer.Consume(r.size); + return success; +} + +bool +BufferedOutputStream::Flush(Error &error) +{ + if (!Check(error)) + return false; + + auto r = buffer.Read(); + if (r.IsEmpty()) + return true; + + bool success = os.Write(r.data, r.size, error); + if (gcc_likely(success)) + buffer.Consume(r.size); + return success; +} diff --git a/src/fs/io/BufferedOutputStream.hxx b/src/fs/io/BufferedOutputStream.hxx new file mode 100644 index 000000000..f2de758a2 --- /dev/null +++ b/src/fs/io/BufferedOutputStream.hxx @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef MPD_BUFFERED_OUTPUT_STREAM_HXX +#define MPD_BUFFERED_OUTPUT_STREAM_HXX + +#include "check.h" +#include "Compiler.h" +#include "util/DynamicFifoBuffer.hxx" +#include "util/Error.hxx" + +#include + +class OutputStream; +class Error; + +class BufferedOutputStream { + OutputStream &os; + + DynamicFifoBuffer buffer; + + Error last_error; + +public: + BufferedOutputStream(OutputStream &_os) + :os(_os), buffer(32768) {} + + bool Write(const void *data, size_t size); + bool Write(const char *p); + + gcc_printf(2,3) + bool Format(const char *fmt, ...); + + gcc_pure + bool Check() const { + return !last_error.IsDefined(); + } + + bool Check(Error &error) const { + if (last_error.IsDefined()) { + error.Set(last_error); + return false; + } else + return true; + } + + bool Flush(); + + bool Flush(Error &error); + +private: + bool AppendToBuffer(const void *data, size_t size); +}; + +#endif diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx new file mode 100644 index 000000000..dc4456d1f --- /dev/null +++ b/src/fs/io/FileOutputStream.cxx @@ -0,0 +1,135 @@ +/* + * 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 "FileOutputStream.hxx" +#include "fs/FileSystem.hxx" +#include "system/fd_util.h" +#include "util/Error.hxx" + +#ifdef WIN32 + +FileOutputStream::FileOutputStream(Path _path, Error &error) + :path(_path), + handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr, + TRUNCATE_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, + nullptr)) +{ + if (handle == INVALID_HANDLE_VALUE) + error.FormatLastError("Failed to create %s", path.c_str()); +} + +bool +FileOutputStream::Write(const void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + DWORD nbytes; + if (!WriteFile(handle, data, size, &nbytes, nullptr)) { + error.FormatLastError("Failed to write to %s", path.c_str()); + return false; + } + + if (size_t(nbytes) != size) { + error.FormatLastError(ERROR_DISK_FULL, + "Failed to write to %s", path.c_str()); + return false; + } + + return true; +} + +bool +FileOutputStream::Commit(gcc_unused Error &error) +{ + assert(IsDefined()); + + CloseHandle(handle); + return true; +} + +void +FileOutputStream::Cancel() +{ + assert(IsDefined()); + + CloseHandle(handle); + RemoveFile(path); +} + +#else + +#include +#include +#include + +FileOutputStream::FileOutputStream(Path _path, Error &error) + :path(_path), + fd(open_cloexec(path.c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + 0666)) +{ + if (fd < 0) + error.FormatErrno("Failed to create %s", path.c_str()); +} + +bool +FileOutputStream::Write(const void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + ssize_t nbytes = write(fd, data, size); + if (nbytes < 0) { + error.FormatErrno("Failed to write to %s", path.c_str()); + return false; + } else if ((size_t)nbytes < size) { + error.FormatErrno(ENOSPC, + "Failed to write to %s", path.c_str()); + return false; + } + + return true; +} + +bool +FileOutputStream::Commit(Error &error) +{ + assert(IsDefined()); + + bool success = close(fd) == 0; + fd = -1; + if (!success) + error.FormatErrno("Failed to commit %s", path.c_str()); + + return success; +} + +void +FileOutputStream::Cancel() +{ + assert(IsDefined()); + + close(fd); + fd = -1; + + RemoveFile(path); +} + +#endif diff --git a/src/fs/io/FileOutputStream.hxx b/src/fs/io/FileOutputStream.hxx new file mode 100644 index 000000000..68174ec83 --- /dev/null +++ b/src/fs/io/FileOutputStream.hxx @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef MPD_FILE_OUTPUT_STREAM_HXX +#define MPD_FILE_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "fs/AllocatedPath.hxx" + +#include + +#ifdef WIN32 +#include +#endif + +class Path; + +class FileOutputStream final : public OutputStream { + AllocatedPath path; + +#ifdef WIN32 + HANDLE handle; +#else + int fd; +#endif + +public: + FileOutputStream(Path _path, Error &error); + + ~FileOutputStream() { + if (IsDefined()) + Cancel(); + } + + + bool IsDefined() const { +#ifdef WIN32 + return handle != INVALID_HANDLE_VALUE; +#else + return fd >= 0; +#endif + } + + bool Commit(Error &error); + void Cancel(); + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/GzipOutputStream.cxx b/src/fs/io/GzipOutputStream.cxx new file mode 100644 index 000000000..d05645d9e --- /dev/null +++ b/src/fs/io/GzipOutputStream.cxx @@ -0,0 +1,106 @@ +/* + * 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 "GzipOutputStream.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +const Domain zlib_domain("zlib"); + +GzipOutputStream::GzipOutputStream(OutputStream &_next, Error &error) + :next(_next) +{ + z.next_in = nullptr; + z.avail_in = 0; + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + constexpr int windowBits = 15; + constexpr int gzip_encoding = 16; + + int result = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + windowBits | gzip_encoding, + 8, Z_DEFAULT_STRATEGY); + if (result != Z_OK) { + z.opaque = this; + error.Set(zlib_domain, result, zError(result)); + } +} + +GzipOutputStream::~GzipOutputStream() +{ + if (IsDefined()) + deflateEnd(&z); +} + +bool +GzipOutputStream::Flush(Error &error) +{ + /* no more input */ + z.next_in = nullptr; + z.avail_in = 0; + + while (true) { + Bytef output[4096]; + z.next_out = output; + z.avail_out = sizeof(output); + + int result = deflate(&z, Z_FINISH); + if (z.next_out > output && + !next.Write(output, z.next_out - output, error)) + return false; + + if (result == Z_STREAM_END) + return true; + else if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return false; + } + } +} + +bool +GzipOutputStream::Write(const void *_data, size_t size, Error &error) +{ + /* zlib's API requires non-const input pointer */ + void *data = const_cast(_data); + + z.next_in = reinterpret_cast(data); + z.avail_in = size; + + while (z.avail_in > 0) { + Bytef output[4096]; + z.next_out = output; + z.avail_out = sizeof(output); + + int result = deflate(&z, Z_NO_FLUSH); + if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return false; + } + + if (z.next_out > output && + !next.Write(output, z.next_out - output, error)) + return false; + } + + return true; +} diff --git a/src/fs/io/GzipOutputStream.hxx b/src/fs/io/GzipOutputStream.hxx new file mode 100644 index 000000000..4f6d8b357 --- /dev/null +++ b/src/fs/io/GzipOutputStream.hxx @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef MPD_GZIP_OUTPUT_STREAM_HXX +#define MPD_GZIP_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" + +#include +#include + +class Error; +class Domain; + +extern const Domain zlib_domain; + +/** + * A filter that compresses data written to it using zlib, forwarding + * compressed data in the "gzip" format. + * + * Don't forget to call Flush() before destructing this object. + */ +class GzipOutputStream final : public OutputStream { + OutputStream &next; + + z_stream z; + +public: + /** + * Construct the filter. Call IsDefined() to check whether + * the constructor has succeeded. If not, #error will hold + * information about the failure. + */ + GzipOutputStream(OutputStream &_next, Error &error); + ~GzipOutputStream(); + + /** + * Check whether the constructor has succeeded. + */ + bool IsDefined() const { + return z.opaque == nullptr; + } + + /** + * Finish the file and write all data remaining in zlib's + * output buffer. + */ + bool Flush(Error &error); + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/OutputStream.hxx b/src/fs/io/OutputStream.hxx new file mode 100644 index 000000000..71311c71f --- /dev/null +++ b/src/fs/io/OutputStream.hxx @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef MPD_OUTPUT_STREAM_HXX +#define MPD_OUTPUT_STREAM_HXX + +#include "check.h" +#include "Compiler.h" + +#include + +class Error; + +class OutputStream { +public: + OutputStream() = default; + OutputStream(const OutputStream &) = delete; + + virtual bool Write(const void *data, size_t size, Error &error) = 0; +}; + +#endif diff --git a/src/fs/io/StdioOutputStream.hxx b/src/fs/io/StdioOutputStream.hxx new file mode 100644 index 000000000..e00db922f --- /dev/null +++ b/src/fs/io/StdioOutputStream.hxx @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef MPD_STDIO_OUTPUT_STREAM_HXX +#define MPD_STDIO_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "fs/AllocatedPath.hxx" + +#include + +class StdioOutputStream final : public OutputStream { + FILE *const file; + +public: + StdioOutputStream(FILE *_file):file(_file) {} + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, + gcc_unused Error &error) override { + fwrite(data, 1, size, file); + + /* this class is debug-only and ignores errors */ + return true; + } +}; + +#endif diff --git a/src/fs/io/TextFile.cxx b/src/fs/io/TextFile.cxx new file mode 100644 index 000000000..b1a92b9cc --- /dev/null +++ b/src/fs/io/TextFile.cxx @@ -0,0 +1,81 @@ +/* + * 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 "TextFile.hxx" +#include "util/Alloc.hxx" +#include "fs/Path.hxx" +#include "fs/FileSystem.hxx" + +#include +#include +#include + +TextFile::TextFile(Path path_fs) + :file(FOpen(path_fs, FOpenMode::ReadText)), + buffer((char *)xalloc(step)), capacity(step), length(0) {} + +TextFile::~TextFile() +{ + free(buffer); + + if (file != nullptr) + fclose(file); +} + +char * +TextFile::ReadLine() +{ + assert(file != nullptr); + + while (true) { + if (length >= capacity) { + if (capacity >= max_length) + /* too large already - bail out */ + return nullptr; + + capacity <<= 1; + char *new_buffer = (char *)realloc(buffer, capacity); + if (new_buffer == nullptr) + /* out of memory - bail out */ + return nullptr; + } + + char *p = fgets(buffer + length, capacity - length, file); + if (p == nullptr) { + if (length == 0 || ferror(file)) + return nullptr; + break; + } + + length += strlen(buffer + length); + if (buffer[length - 1] == '\n') + break; + } + + /* remove the newline characters */ + if (buffer[length - 1] == '\n') + --length; + if (buffer[length - 1] == '\r') + --length; + + buffer[length] = 0; + length = 0; + return buffer; +} diff --git a/src/fs/io/TextFile.hxx b/src/fs/io/TextFile.hxx new file mode 100644 index 000000000..e3a712a88 --- /dev/null +++ b/src/fs/io/TextFile.hxx @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef MPD_TEXT_FILE_HXX +#define MPD_TEXT_FILE_HXX + +#include "Compiler.h" + +#include +#include + +class Path; + +class TextFile { + static constexpr size_t max_length = 512 * 1024; + static constexpr size_t step = 1024; + + FILE *const file; + + char *buffer; + size_t capacity, length; + +public: + TextFile(Path path_fs); + + TextFile(const TextFile &other) = delete; + + ~TextFile(); + + bool HasFailed() const { + return gcc_unlikely(file == nullptr); + } + + /** + * Reads a line from the input file, and strips trailing + * space. There is a reasonable maximum line length, only to + * prevent denial of service. + * + * @param file the source file, opened in text mode + * @param buffer an allocator for the buffer + * @return a pointer to the line, or nullptr on end-of-file or error + */ + char *ReadLine(); +}; + +#endif diff --git a/src/fs/output/BufferedOutputStream.cxx b/src/fs/output/BufferedOutputStream.cxx deleted file mode 100644 index 088a3e279..000000000 --- a/src/fs/output/BufferedOutputStream.cxx +++ /dev/null @@ -1,143 +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 "BufferedOutputStream.hxx" -#include "OutputStream.hxx" - -#include -#include -#include - -bool -BufferedOutputStream::AppendToBuffer(const void *data, size_t size) -{ - auto r = buffer.Write(); - if (r.size < size) - return false; - - memcpy(r.data, data, size); - buffer.Append(size); - return true; -} - -bool -BufferedOutputStream::Write(const void *data, size_t size) -{ - if (gcc_unlikely(last_error.IsDefined())) - return false; - - if (AppendToBuffer(data, size)) - return true; - - if (!Flush()) - return false; - - if (AppendToBuffer(data, size)) - return true; - - return os.Write(data, size, last_error); -} - -bool -BufferedOutputStream::Write(const char *p) -{ - return Write(p, strlen(p)); -} - -bool -BufferedOutputStream::Format(const char *fmt, ...) -{ - if (gcc_unlikely(last_error.IsDefined())) - return false; - - auto r = buffer.Write(); - if (r.IsEmpty()) { - if (!Flush()) - return false; - - r = buffer.Write(); - } - - /* format into the buffer */ - va_list ap; - va_start(ap, fmt); - size_t size = vsnprintf(r.data, r.size, fmt, ap); - va_end(ap); - - if (gcc_unlikely(size >= r.size)) { - /* buffer was not large enough; flush it and try - again */ - - if (!Flush()) - return false; - - r = buffer.Write(); - - if (gcc_unlikely(size >= r.size)) { - /* still not enough space: grow the buffer and - try again */ - r.size = size + 1; - r.data = buffer.Write(r.size); - } - - /* format into the new buffer */ - va_start(ap, fmt); - size = vsnprintf(r.data, r.size, fmt, ap); - va_end(ap); - - /* this time, it must fit */ - assert(size < r.size); - } - - buffer.Append(size); - return true; -} - -bool -BufferedOutputStream::Flush() -{ - if (!Check()) - return false; - - auto r = buffer.Read(); - if (r.IsEmpty()) - return true; - - bool success = os.Write(r.data, r.size, last_error); - if (gcc_likely(success)) - buffer.Consume(r.size); - return success; -} - -bool -BufferedOutputStream::Flush(Error &error) -{ - if (!Check(error)) - return false; - - auto r = buffer.Read(); - if (r.IsEmpty()) - return true; - - bool success = os.Write(r.data, r.size, error); - if (gcc_likely(success)) - buffer.Consume(r.size); - return success; -} diff --git a/src/fs/output/BufferedOutputStream.hxx b/src/fs/output/BufferedOutputStream.hxx deleted file mode 100644 index f2de758a2..000000000 --- a/src/fs/output/BufferedOutputStream.hxx +++ /dev/null @@ -1,71 +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. - */ - -#ifndef MPD_BUFFERED_OUTPUT_STREAM_HXX -#define MPD_BUFFERED_OUTPUT_STREAM_HXX - -#include "check.h" -#include "Compiler.h" -#include "util/DynamicFifoBuffer.hxx" -#include "util/Error.hxx" - -#include - -class OutputStream; -class Error; - -class BufferedOutputStream { - OutputStream &os; - - DynamicFifoBuffer buffer; - - Error last_error; - -public: - BufferedOutputStream(OutputStream &_os) - :os(_os), buffer(32768) {} - - bool Write(const void *data, size_t size); - bool Write(const char *p); - - gcc_printf(2,3) - bool Format(const char *fmt, ...); - - gcc_pure - bool Check() const { - return !last_error.IsDefined(); - } - - bool Check(Error &error) const { - if (last_error.IsDefined()) { - error.Set(last_error); - return false; - } else - return true; - } - - bool Flush(); - - bool Flush(Error &error); - -private: - bool AppendToBuffer(const void *data, size_t size); -}; - -#endif diff --git a/src/fs/output/FileOutputStream.cxx b/src/fs/output/FileOutputStream.cxx deleted file mode 100644 index dc4456d1f..000000000 --- a/src/fs/output/FileOutputStream.cxx +++ /dev/null @@ -1,135 +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 "FileOutputStream.hxx" -#include "fs/FileSystem.hxx" -#include "system/fd_util.h" -#include "util/Error.hxx" - -#ifdef WIN32 - -FileOutputStream::FileOutputStream(Path _path, Error &error) - :path(_path), - handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr, - TRUNCATE_EXISTING, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, - nullptr)) -{ - if (handle == INVALID_HANDLE_VALUE) - error.FormatLastError("Failed to create %s", path.c_str()); -} - -bool -FileOutputStream::Write(const void *data, size_t size, Error &error) -{ - assert(IsDefined()); - - DWORD nbytes; - if (!WriteFile(handle, data, size, &nbytes, nullptr)) { - error.FormatLastError("Failed to write to %s", path.c_str()); - return false; - } - - if (size_t(nbytes) != size) { - error.FormatLastError(ERROR_DISK_FULL, - "Failed to write to %s", path.c_str()); - return false; - } - - return true; -} - -bool -FileOutputStream::Commit(gcc_unused Error &error) -{ - assert(IsDefined()); - - CloseHandle(handle); - return true; -} - -void -FileOutputStream::Cancel() -{ - assert(IsDefined()); - - CloseHandle(handle); - RemoveFile(path); -} - -#else - -#include -#include -#include - -FileOutputStream::FileOutputStream(Path _path, Error &error) - :path(_path), - fd(open_cloexec(path.c_str(), - O_WRONLY|O_CREAT|O_TRUNC, - 0666)) -{ - if (fd < 0) - error.FormatErrno("Failed to create %s", path.c_str()); -} - -bool -FileOutputStream::Write(const void *data, size_t size, Error &error) -{ - assert(IsDefined()); - - ssize_t nbytes = write(fd, data, size); - if (nbytes < 0) { - error.FormatErrno("Failed to write to %s", path.c_str()); - return false; - } else if ((size_t)nbytes < size) { - error.FormatErrno(ENOSPC, - "Failed to write to %s", path.c_str()); - return false; - } - - return true; -} - -bool -FileOutputStream::Commit(Error &error) -{ - assert(IsDefined()); - - bool success = close(fd) == 0; - fd = -1; - if (!success) - error.FormatErrno("Failed to commit %s", path.c_str()); - - return success; -} - -void -FileOutputStream::Cancel() -{ - assert(IsDefined()); - - close(fd); - fd = -1; - - RemoveFile(path); -} - -#endif diff --git a/src/fs/output/FileOutputStream.hxx b/src/fs/output/FileOutputStream.hxx deleted file mode 100644 index 68174ec83..000000000 --- a/src/fs/output/FileOutputStream.hxx +++ /dev/null @@ -1,68 +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. - */ - -#ifndef MPD_FILE_OUTPUT_STREAM_HXX -#define MPD_FILE_OUTPUT_STREAM_HXX - -#include "check.h" -#include "OutputStream.hxx" -#include "fs/AllocatedPath.hxx" - -#include - -#ifdef WIN32 -#include -#endif - -class Path; - -class FileOutputStream final : public OutputStream { - AllocatedPath path; - -#ifdef WIN32 - HANDLE handle; -#else - int fd; -#endif - -public: - FileOutputStream(Path _path, Error &error); - - ~FileOutputStream() { - if (IsDefined()) - Cancel(); - } - - - bool IsDefined() const { -#ifdef WIN32 - return handle != INVALID_HANDLE_VALUE; -#else - return fd >= 0; -#endif - } - - bool Commit(Error &error); - void Cancel(); - - /* virtual methods from class OutputStream */ - bool Write(const void *data, size_t size, Error &error) override; -}; - -#endif diff --git a/src/fs/output/GzipOutputStream.cxx b/src/fs/output/GzipOutputStream.cxx deleted file mode 100644 index d05645d9e..000000000 --- a/src/fs/output/GzipOutputStream.cxx +++ /dev/null @@ -1,106 +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 "GzipOutputStream.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" - -const Domain zlib_domain("zlib"); - -GzipOutputStream::GzipOutputStream(OutputStream &_next, Error &error) - :next(_next) -{ - z.next_in = nullptr; - z.avail_in = 0; - z.zalloc = Z_NULL; - z.zfree = Z_NULL; - z.opaque = Z_NULL; - - constexpr int windowBits = 15; - constexpr int gzip_encoding = 16; - - int result = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - windowBits | gzip_encoding, - 8, Z_DEFAULT_STRATEGY); - if (result != Z_OK) { - z.opaque = this; - error.Set(zlib_domain, result, zError(result)); - } -} - -GzipOutputStream::~GzipOutputStream() -{ - if (IsDefined()) - deflateEnd(&z); -} - -bool -GzipOutputStream::Flush(Error &error) -{ - /* no more input */ - z.next_in = nullptr; - z.avail_in = 0; - - while (true) { - Bytef output[4096]; - z.next_out = output; - z.avail_out = sizeof(output); - - int result = deflate(&z, Z_FINISH); - if (z.next_out > output && - !next.Write(output, z.next_out - output, error)) - return false; - - if (result == Z_STREAM_END) - return true; - else if (result != Z_OK) { - error.Set(zlib_domain, result, zError(result)); - return false; - } - } -} - -bool -GzipOutputStream::Write(const void *_data, size_t size, Error &error) -{ - /* zlib's API requires non-const input pointer */ - void *data = const_cast(_data); - - z.next_in = reinterpret_cast(data); - z.avail_in = size; - - while (z.avail_in > 0) { - Bytef output[4096]; - z.next_out = output; - z.avail_out = sizeof(output); - - int result = deflate(&z, Z_NO_FLUSH); - if (result != Z_OK) { - error.Set(zlib_domain, result, zError(result)); - return false; - } - - if (z.next_out > output && - !next.Write(output, z.next_out - output, error)) - return false; - } - - return true; -} diff --git a/src/fs/output/GzipOutputStream.hxx b/src/fs/output/GzipOutputStream.hxx deleted file mode 100644 index 4f6d8b357..000000000 --- a/src/fs/output/GzipOutputStream.hxx +++ /dev/null @@ -1,71 +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. - */ - -#ifndef MPD_GZIP_OUTPUT_STREAM_HXX -#define MPD_GZIP_OUTPUT_STREAM_HXX - -#include "check.h" -#include "OutputStream.hxx" - -#include -#include - -class Error; -class Domain; - -extern const Domain zlib_domain; - -/** - * A filter that compresses data written to it using zlib, forwarding - * compressed data in the "gzip" format. - * - * Don't forget to call Flush() before destructing this object. - */ -class GzipOutputStream final : public OutputStream { - OutputStream &next; - - z_stream z; - -public: - /** - * Construct the filter. Call IsDefined() to check whether - * the constructor has succeeded. If not, #error will hold - * information about the failure. - */ - GzipOutputStream(OutputStream &_next, Error &error); - ~GzipOutputStream(); - - /** - * Check whether the constructor has succeeded. - */ - bool IsDefined() const { - return z.opaque == nullptr; - } - - /** - * Finish the file and write all data remaining in zlib's - * output buffer. - */ - bool Flush(Error &error); - - /* virtual methods from class OutputStream */ - bool Write(const void *data, size_t size, Error &error) override; -}; - -#endif diff --git a/src/fs/output/OutputStream.hxx b/src/fs/output/OutputStream.hxx deleted file mode 100644 index 71311c71f..000000000 --- a/src/fs/output/OutputStream.hxx +++ /dev/null @@ -1,38 +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. - */ - -#ifndef MPD_OUTPUT_STREAM_HXX -#define MPD_OUTPUT_STREAM_HXX - -#include "check.h" -#include "Compiler.h" - -#include - -class Error; - -class OutputStream { -public: - OutputStream() = default; - OutputStream(const OutputStream &) = delete; - - virtual bool Write(const void *data, size_t size, Error &error) = 0; -}; - -#endif diff --git a/src/fs/output/StdioOutputStream.hxx b/src/fs/output/StdioOutputStream.hxx deleted file mode 100644 index e00db922f..000000000 --- a/src/fs/output/StdioOutputStream.hxx +++ /dev/null @@ -1,45 +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. - */ - -#ifndef MPD_STDIO_OUTPUT_STREAM_HXX -#define MPD_STDIO_OUTPUT_STREAM_HXX - -#include "check.h" -#include "OutputStream.hxx" -#include "fs/AllocatedPath.hxx" - -#include - -class StdioOutputStream final : public OutputStream { - FILE *const file; - -public: - StdioOutputStream(FILE *_file):file(_file) {} - - /* virtual methods from class OutputStream */ - bool Write(const void *data, size_t size, - gcc_unused Error &error) override { - fwrite(data, 1, size, file); - - /* this class is debug-only and ignores errors */ - return true; - } -}; - -#endif -- cgit v1.2.3