Move POSIX wrappers to separate files.
This commit is contained in:
parent
b4dc0301f6
commit
b692ab58b2
194
posix.cc
Normal file
194
posix.cc
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
A C++ interface to POSIX functions.
|
||||||
|
|
||||||
|
Copyright (c) 2014, Victor Zverovich
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. 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 COPYRIGHT OWNER 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 "posix.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <unistd.h>
|
||||||
|
#else
|
||||||
|
# include <io.h>
|
||||||
|
|
||||||
|
# define O_CREAT _O_CREAT
|
||||||
|
# define O_TRUNC _O_TRUNC
|
||||||
|
# define S_IRUSR _S_IREAD
|
||||||
|
# define S_IWUSR _S_IWRITE
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// Retries the expression while it evaluates to -1 and error equals to EINTR.
|
||||||
|
#ifndef _WIN32
|
||||||
|
# define FMT_RETRY(result, expression) \
|
||||||
|
do { \
|
||||||
|
result = (expression); \
|
||||||
|
} while (result == -1 && errno == EINTR)
|
||||||
|
#else
|
||||||
|
# define FMT_RETRY(result, expression) result = (expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// On Windows the count argument to read and write is unsigned, so convert
|
||||||
|
// it from size_t preventing integer overflow.
|
||||||
|
inline unsigned ConvertRWCount(std::size_t count) {
|
||||||
|
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
inline std::size_t ConvertRWCount(std::size_t count) { return count; }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedFile::~BufferedFile() FMT_NOEXCEPT(true) {
|
||||||
|
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||||
|
fmt::ReportSystemError(errno, "cannot close file");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferedFile::close() {
|
||||||
|
if (!file_)
|
||||||
|
return;
|
||||||
|
int result = FMT_SYSTEM(fclose(file_));
|
||||||
|
file_ = 0;
|
||||||
|
if (result != 0)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot close file");
|
||||||
|
}
|
||||||
|
|
||||||
|
int BufferedFile::fileno() const {
|
||||||
|
int fd = FMT_POSIX_CALL(fileno(file_));
|
||||||
|
if (fd == -1)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot get file descriptor");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
File::File(const char *path, int oflag) {
|
||||||
|
int mode = S_IRUSR | S_IWUSR;
|
||||||
|
#ifdef _WIN32
|
||||||
|
fd_ = -1;
|
||||||
|
FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode));
|
||||||
|
#else
|
||||||
|
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode)));
|
||||||
|
#endif
|
||||||
|
if (fd_ == -1)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot open file {}") << path;
|
||||||
|
}
|
||||||
|
|
||||||
|
File::~File() FMT_NOEXCEPT(true) {
|
||||||
|
// Don't retry close in case of EINTR!
|
||||||
|
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||||
|
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||||
|
fmt::ReportSystemError(errno, "cannot close file");
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::close() {
|
||||||
|
if (fd_ == -1)
|
||||||
|
return;
|
||||||
|
// Don't retry close in case of EINTR!
|
||||||
|
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||||
|
int result = FMT_POSIX_CALL(close(fd_));
|
||||||
|
fd_ = -1;
|
||||||
|
if (result != 0)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot close file");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize File::read(void *buffer, std::size_t count) {
|
||||||
|
std::streamsize result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, ConvertRWCount(count))));
|
||||||
|
if (result == -1)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot read from file");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize File::write(const void *buffer, std::size_t count) {
|
||||||
|
std::streamsize result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, ConvertRWCount(count))));
|
||||||
|
if (result == -1)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot write to file");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
File File::dup(int fd) {
|
||||||
|
// Don't retry as dup doesn't return EINTR.
|
||||||
|
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||||
|
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||||
|
if (new_fd == -1)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd;
|
||||||
|
return File(new_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::dup2(int fd) {
|
||||||
|
int result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
|
if (result == -1) {
|
||||||
|
fmt::ThrowSystemError(errno,
|
||||||
|
"cannot duplicate file descriptor {} to {}") << fd_ << fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
|
||||||
|
int result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
|
if (result == -1)
|
||||||
|
ec = ErrorCode(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::pipe(File &read_end, File &write_end) {
|
||||||
|
// Close the descriptors first to make sure that assignments don't throw
|
||||||
|
// and there are no leaks.
|
||||||
|
read_end.close();
|
||||||
|
write_end.close();
|
||||||
|
int fds[2] = {};
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||||
|
enum { DEFAULT_CAPACITY = 65536 };
|
||||||
|
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||||
|
#else
|
||||||
|
// Don't retry as the pipe function doesn't return EINTR.
|
||||||
|
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||||
|
int result = FMT_POSIX_CALL(pipe(fds));
|
||||||
|
#endif
|
||||||
|
if (result != 0)
|
||||||
|
fmt::ThrowSystemError(errno, "cannot create pipe");
|
||||||
|
// The following assignments don't throw because read_fd and write_fd
|
||||||
|
// are closed.
|
||||||
|
read_end = File(fds[0]);
|
||||||
|
write_end = File(fds[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedFile File::fdopen(const char *mode) {
|
||||||
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
|
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
|
if (!f) {
|
||||||
|
fmt::ThrowSystemError(errno,
|
||||||
|
"cannot associate stream with file descriptor");
|
||||||
|
}
|
||||||
|
BufferedFile file(f);
|
||||||
|
fd_ = -1;
|
||||||
|
return file;
|
||||||
|
}
|
284
posix.h
Normal file
284
posix.h
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
/*
|
||||||
|
A C++ interface to POSIX functions.
|
||||||
|
|
||||||
|
Copyright (c) 2014, Victor Zverovich
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. 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 COPYRIGHT OWNER 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX_H
|
||||||
|
#define FMT_POSIX_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <ios>
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX
|
||||||
|
# ifdef _WIN32
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX(call) _##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX(call) call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||||
|
#ifdef FMT_SYSTEM
|
||||||
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||||
|
#else
|
||||||
|
# define FMT_SYSTEM(call) call
|
||||||
|
# ifdef _WIN32
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX_CALL(call) ::_##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX_CALL(call) ::call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// An error code.
|
||||||
|
class ErrorCode {
|
||||||
|
private:
|
||||||
|
int value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ErrorCode(int value = 0) FMT_NOEXCEPT(true) : value_(value) {}
|
||||||
|
|
||||||
|
int get() const FMT_NOEXCEPT(true) { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffered file.
|
||||||
|
class BufferedFile {
|
||||||
|
private:
|
||||||
|
FILE *file_;
|
||||||
|
|
||||||
|
friend class File;
|
||||||
|
|
||||||
|
explicit BufferedFile(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructs a BufferedFile object which doesn't represent any file.
|
||||||
|
BufferedFile() FMT_NOEXCEPT(true) : file_(0) {}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
~BufferedFile() FMT_NOEXCEPT(true);
|
||||||
|
|
||||||
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
|
// references are not supported.
|
||||||
|
|
||||||
|
private:
|
||||||
|
// A proxy object to emulate a move constructor.
|
||||||
|
// It is private to make it impossible call operator Proxy directly.
|
||||||
|
struct Proxy {
|
||||||
|
FILE *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// A "move constructor" for moving from a temporary.
|
||||||
|
BufferedFile(Proxy p) FMT_NOEXCEPT(true) : file_(p.file) {}
|
||||||
|
|
||||||
|
// A "move constructor" for for moving from an lvalue.
|
||||||
|
BufferedFile(BufferedFile &f) FMT_NOEXCEPT(true) : file_(f.file_) {
|
||||||
|
f.file_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "move assignment operator" for moving from a temporary.
|
||||||
|
BufferedFile &operator=(Proxy p) {
|
||||||
|
close();
|
||||||
|
file_ = p.file;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "move assignment operator" for moving from an lvalue.
|
||||||
|
BufferedFile &operator=(BufferedFile &other) {
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a proxy object for moving from a temporary:
|
||||||
|
// BufferedFile file = BufferedFile(...);
|
||||||
|
operator Proxy() FMT_NOEXCEPT(true) {
|
||||||
|
Proxy p = {file_};
|
||||||
|
file_ = 0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
private:
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(BufferedFile);
|
||||||
|
|
||||||
|
public:
|
||||||
|
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT(true) : file_(other.file_) {
|
||||||
|
other.file_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedFile& operator=(BufferedFile &&other) {
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// Returns the pointer to a FILE object representing this file.
|
||||||
|
FILE *get() const { return file_; }
|
||||||
|
|
||||||
|
int fileno() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A file. Closed file is represented by a File object with descriptor -1.
|
||||||
|
// Methods that are not declared with FMT_NOEXCEPT(true) may throw
|
||||||
|
// fmt::SystemError in case of failure. Note that some errors such as
|
||||||
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
|
// than an exception. You can get standard behavior by overriding the
|
||||||
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
|
class File {
|
||||||
|
private:
|
||||||
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
|
// Constructs a File object with a given descriptor.
|
||||||
|
explicit File(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Possible values for the oflag argument to the constructor.
|
||||||
|
enum {
|
||||||
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs a File object which doesn't represent any file.
|
||||||
|
File() FMT_NOEXCEPT(true) : fd_(-1) {}
|
||||||
|
|
||||||
|
// Opens a file and constructs a File object representing this file.
|
||||||
|
File(const char *path, int oflag);
|
||||||
|
|
||||||
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
|
// references are not supported.
|
||||||
|
|
||||||
|
private:
|
||||||
|
// A proxy object to emulate a move constructor.
|
||||||
|
// It is private to make it impossible call operator Proxy directly.
|
||||||
|
struct Proxy {
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// A "move constructor" for moving from a temporary.
|
||||||
|
File(Proxy p) FMT_NOEXCEPT(true) : fd_(p.fd) {}
|
||||||
|
|
||||||
|
// A "move constructor" for for moving from an lvalue.
|
||||||
|
File(File &other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "move assignment operator" for moving from a temporary.
|
||||||
|
File &operator=(Proxy p) {
|
||||||
|
close();
|
||||||
|
fd_ = p.fd;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "move assignment operator" for moving from an lvalue.
|
||||||
|
File &operator=(File &other) {
|
||||||
|
close();
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a proxy object for moving from a temporary:
|
||||||
|
// File file = File(...);
|
||||||
|
operator Proxy() FMT_NOEXCEPT(true) {
|
||||||
|
Proxy p = {fd_};
|
||||||
|
fd_ = -1;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
private:
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(File);
|
||||||
|
|
||||||
|
public:
|
||||||
|
File(File &&other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& operator=(File &&other) {
|
||||||
|
close();
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
~File() FMT_NOEXCEPT(true);
|
||||||
|
|
||||||
|
// Returns the file descriptor.
|
||||||
|
int descriptor() const FMT_NOEXCEPT(true) { return fd_; }
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
|
std::streamsize read(void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
|
std::streamsize write(const void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
|
// the duplicate as a file object.
|
||||||
|
static File dup(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
void dup2(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true);
|
||||||
|
|
||||||
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
|
// and writing respectively.
|
||||||
|
static void pipe(File &read_end, File &write_end);
|
||||||
|
|
||||||
|
// Creates a BufferedFile object associated with this file and detaches
|
||||||
|
// this File object from the file.
|
||||||
|
BufferedFile fdopen(const char *mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
|
namespace std {
|
||||||
|
// For compatibility with C++98.
|
||||||
|
inline BufferedFile &move(BufferedFile &f) { return f; }
|
||||||
|
inline File &move(File &f) { return f; }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FMT_POSIX_H
|
@ -29,171 +29,7 @@
|
|||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
#include <errno.h>
|
#include "posix.cc"
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
# include <unistd.h>
|
|
||||||
#else
|
|
||||||
# include <io.h>
|
|
||||||
|
|
||||||
# define O_CREAT _O_CREAT
|
|
||||||
# define O_TRUNC _O_TRUNC
|
|
||||||
# define S_IRUSR _S_IREAD
|
|
||||||
# define S_IWUSR _S_IWRITE
|
|
||||||
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
// Retries the expression while it evaluates to -1 and error equals to EINTR.
|
|
||||||
#ifndef _WIN32
|
|
||||||
# define FMT_RETRY(result, expression) \
|
|
||||||
do { \
|
|
||||||
result = (expression); \
|
|
||||||
} while (result == -1 && errno == EINTR)
|
|
||||||
#else
|
|
||||||
# define FMT_RETRY(result, expression) result = (expression)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
#ifdef _WIN32
|
|
||||||
// On Windows the count argument to read and write is unsigned, so convert
|
|
||||||
// it from size_t preventing integer overflow.
|
|
||||||
inline unsigned ConvertRWCount(std::size_t count) {
|
|
||||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
inline std::size_t ConvertRWCount(std::size_t count) { return count; }
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferedFile::~BufferedFile() FMT_NOEXCEPT(true) {
|
|
||||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
|
||||||
fmt::ReportSystemError(errno, "cannot close file");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferedFile::close() {
|
|
||||||
if (!file_)
|
|
||||||
return;
|
|
||||||
int result = FMT_SYSTEM(fclose(file_));
|
|
||||||
file_ = 0;
|
|
||||||
if (result != 0)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot close file");
|
|
||||||
}
|
|
||||||
|
|
||||||
int BufferedFile::fileno() const {
|
|
||||||
int fd = FMT_POSIX_CALL(fileno(file_));
|
|
||||||
if (fd == -1)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot get file descriptor");
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
File::File(const char *path, int oflag) {
|
|
||||||
int mode = S_IRUSR | S_IWUSR;
|
|
||||||
#ifdef _WIN32
|
|
||||||
fd_ = -1;
|
|
||||||
FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode));
|
|
||||||
#else
|
|
||||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode)));
|
|
||||||
#endif
|
|
||||||
if (fd_ == -1)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot open file {}") << path;
|
|
||||||
}
|
|
||||||
|
|
||||||
File::~File() FMT_NOEXCEPT(true) {
|
|
||||||
// Don't retry close in case of EINTR!
|
|
||||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
|
||||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
|
||||||
fmt::ReportSystemError(errno, "cannot close file");
|
|
||||||
}
|
|
||||||
|
|
||||||
void File::close() {
|
|
||||||
if (fd_ == -1)
|
|
||||||
return;
|
|
||||||
// Don't retry close in case of EINTR!
|
|
||||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
|
||||||
int result = FMT_POSIX_CALL(close(fd_));
|
|
||||||
fd_ = -1;
|
|
||||||
if (result != 0)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot close file");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize File::read(void *buffer, std::size_t count) {
|
|
||||||
std::streamsize result = 0;
|
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, ConvertRWCount(count))));
|
|
||||||
if (result == -1)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot read from file");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize File::write(const void *buffer, std::size_t count) {
|
|
||||||
std::streamsize result = 0;
|
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, ConvertRWCount(count))));
|
|
||||||
if (result == -1)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot write to file");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
File File::dup(int fd) {
|
|
||||||
// Don't retry as dup doesn't return EINTR.
|
|
||||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
|
||||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
|
||||||
if (new_fd == -1)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd;
|
|
||||||
return File(new_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void File::dup2(int fd) {
|
|
||||||
int result = 0;
|
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
|
||||||
if (result == -1) {
|
|
||||||
fmt::ThrowSystemError(errno,
|
|
||||||
"cannot duplicate file descriptor {} to {}") << fd_ << fd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
|
|
||||||
int result = 0;
|
|
||||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
|
||||||
if (result == -1)
|
|
||||||
ec = ErrorCode(errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
void File::pipe(File &read_end, File &write_end) {
|
|
||||||
// Close the descriptors first to make sure that assignments don't throw
|
|
||||||
// and there are no leaks.
|
|
||||||
read_end.close();
|
|
||||||
write_end.close();
|
|
||||||
int fds[2] = {};
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
|
||||||
enum { DEFAULT_CAPACITY = 65536 };
|
|
||||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
|
||||||
#else
|
|
||||||
// Don't retry as the pipe function doesn't return EINTR.
|
|
||||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
|
||||||
int result = FMT_POSIX_CALL(pipe(fds));
|
|
||||||
#endif
|
|
||||||
if (result != 0)
|
|
||||||
fmt::ThrowSystemError(errno, "cannot create pipe");
|
|
||||||
// The following assignments don't throw because read_fd and write_fd
|
|
||||||
// are closed.
|
|
||||||
read_end = File(fds[0]);
|
|
||||||
write_end = File(fds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferedFile File::fdopen(const char *mode) {
|
|
||||||
// Don't retry as fdopen doesn't return EINTR.
|
|
||||||
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
|
||||||
if (!f) {
|
|
||||||
fmt::ThrowSystemError(errno,
|
|
||||||
"cannot associate stream with file descriptor");
|
|
||||||
}
|
|
||||||
BufferedFile file(f);
|
|
||||||
fd_ = -1;
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutputRedirect::Flush() {
|
void OutputRedirect::Flush() {
|
||||||
#if EOF != -1
|
#if EOF != -1
|
||||||
|
@ -28,16 +28,8 @@
|
|||||||
#ifndef FMT_GTEST_EXTRA_H
|
#ifndef FMT_GTEST_EXTRA_H
|
||||||
#define FMT_GTEST_EXTRA_H
|
#define FMT_GTEST_EXTRA_H
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <ios>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
|
||||||
# include <fcntl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
@ -46,6 +38,14 @@
|
|||||||
# include "posix-test.h"
|
# include "posix-test.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_USE_FILE_DESCRIPTORS
|
||||||
|
# define FMT_USE_FILE_DESCRIPTORS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
# include "posix.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
||||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
|
||||||
@ -92,259 +92,8 @@ std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message);
|
|||||||
EXPECT_THROW_MSG(statement, fmt::SystemError, \
|
EXPECT_THROW_MSG(statement, fmt::SystemError, \
|
||||||
FormatSystemErrorMessage(error_code, message))
|
FormatSystemErrorMessage(error_code, message))
|
||||||
|
|
||||||
#ifndef FMT_USE_FILE_DESCRIPTORS
|
|
||||||
# define FMT_USE_FILE_DESCRIPTORS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
#ifndef FMT_POSIX
|
|
||||||
# ifdef _WIN32
|
|
||||||
// Fix warnings about deprecated symbols.
|
|
||||||
# define FMT_POSIX(call) _##call
|
|
||||||
# else
|
|
||||||
# define FMT_POSIX(call) call
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
|
||||||
#ifdef FMT_SYSTEM
|
|
||||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
|
||||||
#else
|
|
||||||
# define FMT_SYSTEM(call) call
|
|
||||||
# ifdef _WIN32
|
|
||||||
// Fix warnings about deprecated symbols.
|
|
||||||
# define FMT_POSIX_CALL(call) ::_##call
|
|
||||||
# else
|
|
||||||
# define FMT_POSIX_CALL(call) ::call
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// An error code.
|
|
||||||
class ErrorCode {
|
|
||||||
private:
|
|
||||||
int value_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ErrorCode(int value = 0) FMT_NOEXCEPT(true) : value_(value) {}
|
|
||||||
|
|
||||||
int get() const FMT_NOEXCEPT(true) { return value_; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// A buffered file.
|
|
||||||
class BufferedFile {
|
|
||||||
private:
|
|
||||||
FILE *file_;
|
|
||||||
|
|
||||||
friend class File;
|
|
||||||
|
|
||||||
explicit BufferedFile(FILE *f) : file_(f) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructs a BufferedFile object which doesn't represent any file.
|
|
||||||
BufferedFile() FMT_NOEXCEPT(true) : file_(0) {}
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
|
||||||
~BufferedFile() FMT_NOEXCEPT(true);
|
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
|
||||||
// A proxy object to emulate a move constructor.
|
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
|
||||||
struct Proxy {
|
|
||||||
FILE *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
// A "move constructor" for moving from a temporary.
|
|
||||||
BufferedFile(Proxy p) FMT_NOEXCEPT(true) : file_(p.file) {}
|
|
||||||
|
|
||||||
// A "move constructor" for for moving from an lvalue.
|
|
||||||
BufferedFile(BufferedFile &f) FMT_NOEXCEPT(true) : file_(f.file_) {
|
|
||||||
f.file_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
|
||||||
BufferedFile &operator=(Proxy p) {
|
|
||||||
close();
|
|
||||||
file_ = p.file;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
|
||||||
BufferedFile &operator=(BufferedFile &other) {
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = 0;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
|
||||||
// BufferedFile file = BufferedFile(...);
|
|
||||||
operator Proxy() FMT_NOEXCEPT(true) {
|
|
||||||
Proxy p = {file_};
|
|
||||||
file_ = 0;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
private:
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(BufferedFile);
|
|
||||||
|
|
||||||
public:
|
|
||||||
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT(true) : file_(other.file_) {
|
|
||||||
other.file_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferedFile& operator=(BufferedFile &&other) {
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = 0;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Closes the file.
|
|
||||||
void close();
|
|
||||||
|
|
||||||
// Returns the pointer to a FILE object representing this file.
|
|
||||||
FILE *get() const { return file_; }
|
|
||||||
|
|
||||||
int fileno() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A file. Closed file is represented by a File object with descriptor -1.
|
|
||||||
// Methods that are not declared with FMT_NOEXCEPT(true) may throw
|
|
||||||
// fmt::SystemError in case of failure. Note that some errors such as
|
|
||||||
// closing the file multiple times will cause a crash on Windows rather
|
|
||||||
// than an exception. You can get standard behavior by overriding the
|
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
|
||||||
class File {
|
|
||||||
private:
|
|
||||||
int fd_; // File descriptor.
|
|
||||||
|
|
||||||
// Constructs a File object with a given descriptor.
|
|
||||||
explicit File(int fd) : fd_(fd) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Possible values for the oflag argument to the constructor.
|
|
||||||
enum {
|
|
||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
|
||||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructs a File object which doesn't represent any file.
|
|
||||||
File() FMT_NOEXCEPT(true) : fd_(-1) {}
|
|
||||||
|
|
||||||
// Opens a file and constructs a File object representing this file.
|
|
||||||
File(const char *path, int oflag);
|
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
|
||||||
// A proxy object to emulate a move constructor.
|
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
|
||||||
struct Proxy {
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
// A "move constructor" for moving from a temporary.
|
|
||||||
File(Proxy p) FMT_NOEXCEPT(true) : fd_(p.fd) {}
|
|
||||||
|
|
||||||
// A "move constructor" for for moving from an lvalue.
|
|
||||||
File(File &other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
|
||||||
File &operator=(Proxy p) {
|
|
||||||
close();
|
|
||||||
fd_ = p.fd;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
|
||||||
File &operator=(File &other) {
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
|
||||||
// File file = File(...);
|
|
||||||
operator Proxy() FMT_NOEXCEPT(true) {
|
|
||||||
Proxy p = {fd_};
|
|
||||||
fd_ = -1;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
private:
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(File);
|
|
||||||
|
|
||||||
public:
|
|
||||||
File(File &&other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
File& operator=(File &&other) {
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
|
||||||
~File() FMT_NOEXCEPT(true);
|
|
||||||
|
|
||||||
// Returns the file descriptor.
|
|
||||||
int descriptor() const FMT_NOEXCEPT(true) { return fd_; }
|
|
||||||
|
|
||||||
// Closes the file.
|
|
||||||
void close();
|
|
||||||
|
|
||||||
// Attempts to read count bytes from the file into the specified buffer.
|
|
||||||
std::streamsize read(void *buffer, std::size_t count);
|
|
||||||
|
|
||||||
// Attempts to write count bytes from the specified buffer to the file.
|
|
||||||
std::streamsize write(const void *buffer, std::size_t count);
|
|
||||||
|
|
||||||
// Duplicates a file descriptor with the dup function and returns
|
|
||||||
// the duplicate as a file object.
|
|
||||||
static File dup(int fd);
|
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
|
||||||
// necessary.
|
|
||||||
void dup2(int fd);
|
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
|
||||||
// necessary.
|
|
||||||
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true);
|
|
||||||
|
|
||||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
|
||||||
// and writing respectively.
|
|
||||||
static void pipe(File &read_end, File &write_end);
|
|
||||||
|
|
||||||
// Creates a BufferedFile object associated with this file and detaches
|
|
||||||
// this File object from the file.
|
|
||||||
BufferedFile fdopen(const char *mode);
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
namespace std {
|
|
||||||
// For compatibility with C++98.
|
|
||||||
inline BufferedFile &move(BufferedFile &f) { return f; }
|
|
||||||
inline File &move(File &f) { return f; }
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Captures file output by redirecting it to a pipe.
|
// Captures file output by redirecting it to a pipe.
|
||||||
// The output it can handle is limited by the pipe capacity.
|
// The output it can handle is limited by the pipe capacity.
|
||||||
class OutputRedirect {
|
class OutputRedirect {
|
||||||
|
Loading…
Reference in New Issue
Block a user