|
| 1 | +// Copyright (C) 2020 Matthew "strager" Glazar |
| 2 | +// See end of file for extended copyright information. |
| 3 | + |
| 4 | +#include <quick-lint-js/port/have.h> |
| 5 | + |
| 6 | +#if QLJS_HAVE_UNISTD_H |
| 7 | + |
| 8 | +#include <array> |
| 9 | +#include <cerrno> |
| 10 | +#include <cstddef> |
| 11 | +#include <cstdio> |
| 12 | +#include <cstring> |
| 13 | +#include <limits> |
| 14 | +#include <optional> |
| 15 | +#include <quick-lint-js/assert.h> |
| 16 | +#include <quick-lint-js/container/string-view.h> |
| 17 | +#include <quick-lint-js/io/file-handle.h> |
| 18 | +#include <quick-lint-js/io/pipe.h> |
| 19 | +#include <quick-lint-js/util/narrow-cast.h> |
| 20 | +#include <string> |
| 21 | +#include <string_view> |
| 22 | +#include <unistd.h> |
| 23 | +#include <utility> |
| 24 | + |
| 25 | +#if QLJS_HAVE_FCNTL_H |
| 26 | +#include <fcntl.h> |
| 27 | +#endif |
| 28 | + |
| 29 | +#if QLJS_HAVE_SYS_STAT_H |
| 30 | +#include <sys/stat.h> |
| 31 | +#endif |
| 32 | + |
| 33 | +namespace quick_lint_js { |
| 34 | +bool posix_file_io_error::is_file_not_found_error() const noexcept { |
| 35 | + return this->error == ENOENT; |
| 36 | +} |
| 37 | + |
| 38 | +std::string posix_file_io_error::to_string() const { |
| 39 | + return std::strerror(this->error); |
| 40 | +} |
| 41 | + |
| 42 | +bool operator==(posix_file_io_error lhs, posix_file_io_error rhs) noexcept { |
| 43 | + return lhs.error == rhs.error; |
| 44 | +} |
| 45 | + |
| 46 | +bool operator!=(posix_file_io_error lhs, posix_file_io_error rhs) noexcept { |
| 47 | + return !(lhs == rhs); |
| 48 | +} |
| 49 | + |
| 50 | +posix_fd_file_ref::posix_fd_file_ref() noexcept |
| 51 | + : posix_fd_file_ref(this->invalid_fd) {} |
| 52 | + |
| 53 | +posix_fd_file_ref::posix_fd_file_ref(int fd) noexcept : fd_(fd) { |
| 54 | + QLJS_ASSERT(this->fd_ >= 0 || this->fd_ == this->invalid_fd); |
| 55 | +} |
| 56 | + |
| 57 | +bool posix_fd_file_ref::valid() const noexcept { |
| 58 | + return this->fd_ != this->invalid_fd; |
| 59 | +} |
| 60 | + |
| 61 | +int posix_fd_file_ref::get() const noexcept { return this->fd_; } |
| 62 | + |
| 63 | +file_read_result posix_fd_file_ref::read(void *buffer, |
| 64 | + int buffer_size) noexcept { |
| 65 | + QLJS_ASSERT(this->valid()); |
| 66 | + ::ssize_t read_size = |
| 67 | + ::read(this->fd_, buffer, narrow_cast<std::size_t>(buffer_size)); |
| 68 | + if (read_size == -1) { |
| 69 | + return failed_result(posix_file_io_error{errno}); |
| 70 | + } |
| 71 | + return read_size == 0 ? file_read_result::end_of_file() |
| 72 | + : file_read_result(narrow_cast<int>(read_size)); |
| 73 | +} |
| 74 | + |
| 75 | +result<std::size_t, posix_file_io_error> posix_fd_file_ref::write( |
| 76 | + const void *buffer, std::size_t buffer_size) noexcept { |
| 77 | + QLJS_ASSERT(this->valid()); |
| 78 | + ::ssize_t written_size = ::write(this->fd_, buffer, buffer_size); |
| 79 | + if (written_size == -1) { |
| 80 | + return failed_result(posix_file_io_error{errno}); |
| 81 | + } |
| 82 | + return narrow_cast<std::size_t>(written_size); |
| 83 | +} |
| 84 | + |
| 85 | +result<void, posix_file_io_error> posix_fd_file_ref::write_full( |
| 86 | + const void *buffer, std::size_t buffer_size) noexcept { |
| 87 | + QLJS_ASSERT(this->valid()); |
| 88 | + auto write_result = this->write(buffer, buffer_size); |
| 89 | + if (!write_result.ok()) { |
| 90 | + return write_result.propagate(); |
| 91 | + } |
| 92 | + if (*write_result != buffer_size) { |
| 93 | + // TODO(strager): Should we retry with the remaining buffer? |
| 94 | + return failed_result(posix_file_io_error{EIO}); |
| 95 | + } |
| 96 | + return {}; |
| 97 | +} |
| 98 | + |
| 99 | +bool posix_fd_file_ref::is_pipe_non_blocking() { |
| 100 | + QLJS_ASSERT(this->valid()); |
| 101 | +#if QLJS_HAVE_FCNTL_H |
| 102 | + int rc = ::fcntl(this->get(), F_GETFL); |
| 103 | + if (rc == -1) { |
| 104 | + QLJS_UNIMPLEMENTED(); |
| 105 | + } |
| 106 | + return (rc & O_NONBLOCK) != 0; |
| 107 | +#else |
| 108 | +#error "Unsupported platform" |
| 109 | +#endif |
| 110 | +} |
| 111 | + |
| 112 | +#if !defined(__EMSCRIPTEN__) |
| 113 | +std::size_t posix_fd_file_ref::get_pipe_buffer_size() { |
| 114 | + QLJS_ASSERT(this->valid()); |
| 115 | +#if QLJS_HAVE_F_GETPIPE_SZ |
| 116 | + int size = ::fcntl(this->fd_, F_GETPIPE_SZ); |
| 117 | + if (size == -1) { |
| 118 | + QLJS_UNIMPLEMENTED(); |
| 119 | + } |
| 120 | + return narrow_cast<std::size_t>(size); |
| 121 | +#elif defined(__APPLE__) |
| 122 | + // See BIG_PIPE_SIZE in <xnu>/bsd/sys/pipe.h. |
| 123 | + return 65536; |
| 124 | +#elif QLJS_HAVE_PIPE |
| 125 | +#warning "Size returned by get_pipe_buffer_size might be inaccurate" |
| 126 | + pipe_fds pipe = make_pipe(); |
| 127 | + pipe.writer.set_pipe_non_blocking(); |
| 128 | + std::size_t pipe_buffer_size = 0; |
| 129 | + for (;;) { |
| 130 | + unsigned char c = 0; |
| 131 | + result<std::size_t, posix_file_io_error> written = |
| 132 | + pipe.writer.write(&c, sizeof(c)); |
| 133 | + if (!written.ok()) { |
| 134 | + QLJS_ASSERT(written.error().error == EAGAIN); |
| 135 | + break; |
| 136 | + } |
| 137 | + pipe_buffer_size += *written; |
| 138 | + } |
| 139 | + return pipe_buffer_size; |
| 140 | +#else |
| 141 | +#error "Unknown platform" |
| 142 | +#endif |
| 143 | +} |
| 144 | +#endif |
| 145 | + |
| 146 | +void posix_fd_file_ref::set_pipe_non_blocking() { |
| 147 | + QLJS_ASSERT(this->valid()); |
| 148 | +#if QLJS_HAVE_FCNTL_H |
| 149 | + int rc = ::fcntl(this->get(), F_SETFL, O_NONBLOCK); |
| 150 | + if (rc != 0) { |
| 151 | + QLJS_UNIMPLEMENTED(); |
| 152 | + } |
| 153 | +#else |
| 154 | +#error "Unsupported platform" |
| 155 | +#endif |
| 156 | +} |
| 157 | + |
| 158 | +std::string posix_fd_file_ref::get_last_error_message() { |
| 159 | + return std::strerror(errno); |
| 160 | +} |
| 161 | + |
| 162 | +posix_fd_file_ref posix_fd_file_ref::get_stdout() noexcept { |
| 163 | + return posix_fd_file_ref(STDOUT_FILENO); |
| 164 | +} |
| 165 | + |
| 166 | +posix_fd_file_ref posix_fd_file_ref::get_stderr() noexcept { |
| 167 | + return posix_fd_file_ref(STDERR_FILENO); |
| 168 | +} |
| 169 | + |
| 170 | +posix_fd_file::posix_fd_file() noexcept = default; |
| 171 | + |
| 172 | +posix_fd_file::posix_fd_file(int fd) noexcept : posix_fd_file_ref(fd) {} |
| 173 | + |
| 174 | +posix_fd_file::posix_fd_file(posix_fd_file &&other) noexcept |
| 175 | + : posix_fd_file_ref(std::exchange(other.fd_, this->invalid_fd)) {} |
| 176 | + |
| 177 | +posix_fd_file &posix_fd_file::operator=(posix_fd_file &&other) noexcept { |
| 178 | + if (this != &other) { |
| 179 | + std::swap(this->fd_, other.fd_); |
| 180 | + if (other.valid()) { |
| 181 | + other.close(); |
| 182 | + } |
| 183 | + } |
| 184 | + return *this; |
| 185 | +} |
| 186 | + |
| 187 | +posix_fd_file::~posix_fd_file() { |
| 188 | + if (this->valid()) { |
| 189 | + this->close(); |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +void posix_fd_file::close() { |
| 194 | + QLJS_ASSERT(this->valid()); |
| 195 | + int rc = ::close(this->fd_); |
| 196 | + if (rc != 0) { |
| 197 | + std::fprintf(stderr, "error: failed to close file: %s\n", |
| 198 | + std::strerror(errno)); |
| 199 | + } |
| 200 | + this->fd_ = invalid_fd; |
| 201 | +} |
| 202 | + |
| 203 | +posix_fd_file_ref posix_fd_file::ref() const noexcept { return *this; } |
| 204 | +} |
| 205 | + |
| 206 | +#endif |
| 207 | + |
| 208 | +// quick-lint-js finds bugs in JavaScript programs. |
| 209 | +// Copyright (C) 2020 Matthew "strager" Glazar |
| 210 | +// |
| 211 | +// This file is part of quick-lint-js. |
| 212 | +// |
| 213 | +// quick-lint-js is free software: you can redistribute it and/or modify |
| 214 | +// it under the terms of the GNU General Public License as published by |
| 215 | +// the Free Software Foundation, either version 3 of the License, or |
| 216 | +// (at your option) any later version. |
| 217 | +// |
| 218 | +// quick-lint-js is distributed in the hope that it will be useful, |
| 219 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 220 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 221 | +// GNU General Public License for more details. |
| 222 | +// |
| 223 | +// You should have received a copy of the GNU General Public License |
| 224 | +// along with quick-lint-js. If not, see <https://www.gnu.org/licenses/>. |
0 commit comments