diff options
Diffstat (limited to 'src/io/write.cpp')
-rw-r--r-- | src/io/write.cpp | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/src/io/write.cpp b/src/io/write.cpp new file mode 100644 index 0000000..5f8a975 --- /dev/null +++ b/src/io/write.cpp @@ -0,0 +1,174 @@ +#include "write.hpp" +// io/write.cpp - Output to files +// +// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> +// +// This file is part of The Mana World (Athena server) +// +// 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 3 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, see <http://www.gnu.org/licenses/>. + +#include <sys/uio.h> + +#include <fcntl.h> +#include <unistd.h> + +#include "../strings/xstring.hpp" + +#include "src/poison.hpp" + + +namespace io +{ + WriteFile::WriteFile(int f, bool linebuffered) + : fd(f), lb(linebuffered), buflen(0) + {} + WriteFile::WriteFile(ZString name, bool linebuffered) + : fd(open(name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)), lb(linebuffered), buflen(0) + {} + WriteFile::~WriteFile() + { + if (fd != -1) + { + if (!close()) + abort(); + } + } + + void WriteFile::put(char c) + { + really_put(&c, 1); + } + void WriteFile::really_put(const char *dat, size_t len) + { + if (len + buflen <= sizeof(buf)) + { + std::copy(dat, dat + len, buf + buflen); + buflen += len; + goto maybe_linebuffered; + } + + { + size_t offset = 0; + while (offset < buflen) + { + // iovec is used for both reading and writing, + // so it declares the pointer to non-const. + iovec iov[2] = + { + {buf + offset, buflen - offset}, + {const_cast<char *>(dat), len}, + }; + ssize_t rv = writev(fd, iov, 2); + if (rv <= 0) + goto write_fail; + offset += rv; + } + offset -= buflen; + dat += offset; + len -= offset; + } + + while (len > sizeof(buf)) + { + ssize_t rv = write(fd, dat, len); + if (rv <= 0) + goto write_fail; + dat += rv; + len -= rv; + } + buflen = len; + + maybe_linebuffered: + if (lb) + { + auto rbegin = std::reverse_iterator<char *>(buf + buflen); + auto rend = std::reverse_iterator<char *>(buf); + auto last_nl = std::find(rbegin, rend, '\n'); + + if (last_nl != rend) + { + size_t offset = 0; + size_t remaining = rend - last_nl; + while (remaining) + { + ssize_t rv = write(fd, buf + offset, remaining); + if (rv <= 0) + goto write_fail; + offset += rv; + remaining -= rv; + } + std::copy(buf + offset, buf + buflen, buf); + buflen -= offset; + } + } + return; + + write_fail: + ::close(fd); + fd = -1; + return; + } + + void WriteFile::put_line(XString xs) + { + really_put(xs.data(), xs.size()); + if (!xs.endswith('\n')) + put('\n'); + } + + bool WriteFile::close() + { + size_t off = 0; + while (off < buflen) + { + ssize_t rv = write(fd, buf + off, buflen - off); + if (rv <= 0) + { + ::close(fd); + fd = -1; + return false; + } + off += rv; + } + + int f = fd; + fd = -1; + return ::close(f) == 0; + } + + bool WriteFile::is_open() + { + return fd != -1; + } + + AppendFile::AppendFile(ZString name, bool linebuffered) + : WriteFile(open(name.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666), linebuffered) + {} + + int do_vprint(WriteFile& out, const char *fmt, va_list ap) + { + int len; + { + va_list ap2; + va_copy(ap2, ap); + len = vsnprintf(nullptr, 0, fmt, ap2); + va_end(ap2); + } + char buffer[len + 1]; + vsnprintf(buffer, len + 1, fmt, ap); + + out.really_put(buffer, len); + return len; + } +} // namespace io |