1/* 2 A C++ interface to POSIX functions. 3 4 Copyright (c) 2012 - 2016, Victor Zverovich 5 All rights reserved. 6 7 For the license information refer to format.h. 8 */ 9 10// Disable bogus MSVC warnings. 11#ifndef _CRT_SECURE_NO_WARNINGS 12# define _CRT_SECURE_NO_WARNINGS 13#endif 14 15#include "posix.h" 16 17#include <limits.h> 18#include <sys/types.h> 19#include <sys/stat.h> 20 21#ifndef _WIN32 22# include <unistd.h> 23#else 24# include <windows.h> 25# include <io.h> 26 27# define O_CREAT _O_CREAT 28# define O_TRUNC _O_TRUNC 29 30# ifndef S_IRUSR 31# define S_IRUSR _S_IREAD 32# endif 33 34# ifndef S_IWUSR 35# define S_IWUSR _S_IWRITE 36# endif 37 38# ifdef __MINGW32__ 39# define _SH_DENYNO 0x40 40# endif 41 42#endif // _WIN32 43 44#ifdef fileno 45# undef fileno 46#endif 47 48namespace { 49#ifdef _WIN32 50// Return type of read and write functions. 51typedef int RWResult; 52 53// On Windows the count argument to read and write is unsigned, so convert 54// it from size_t preventing integer overflow. 55inline unsigned convert_rwcount(std::size_t count) { 56 return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; 57} 58#else 59// Return type of read and write functions. 60typedef ssize_t RWResult; 61 62inline std::size_t convert_rwcount(std::size_t count) { return count; } 63#endif 64} 65 66fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { 67 if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 68 fmt::report_system_error(errno, "cannot close file"); 69} 70 71fmt::BufferedFile::BufferedFile( 72 fmt::CStringRef filename, fmt::CStringRef mode) { 73 FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); 74 if (!file_) 75 FMT_THROW(SystemError(errno, "cannot open file {}", filename)); 76} 77 78void fmt::BufferedFile::close() { 79 if (!file_) 80 return; 81 int result = FMT_SYSTEM(fclose(file_)); 82 file_ = FMT_NULL; 83 if (result != 0) 84 FMT_THROW(SystemError(errno, "cannot close file")); 85} 86 87// A macro used to prevent expansion of fileno on broken versions of MinGW. 88#define FMT_ARGS 89 90int fmt::BufferedFile::fileno() const { 91 int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 92 if (fd == -1) 93 FMT_THROW(SystemError(errno, "cannot get file descriptor")); 94 return fd; 95} 96 97fmt::File::File(fmt::CStringRef path, int oflag) { 98 int mode = S_IRUSR | S_IWUSR; 99#if defined(_WIN32) && !defined(__MINGW32__) 100 fd_ = -1; 101 FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 102#else 103 FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 104#endif 105 if (fd_ == -1) 106 FMT_THROW(SystemError(errno, "cannot open file {}", path)); 107} 108 109fmt::File::~File() FMT_NOEXCEPT { 110 // Don't retry close in case of EINTR! 111 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 112 if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 113 fmt::report_system_error(errno, "cannot close file"); 114} 115 116void fmt::File::close() { 117 if (fd_ == -1) 118 return; 119 // Don't retry close in case of EINTR! 120 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 121 int result = FMT_POSIX_CALL(close(fd_)); 122 fd_ = -1; 123 if (result != 0) 124 FMT_THROW(SystemError(errno, "cannot close file")); 125} 126 127fmt::LongLong fmt::File::size() const { 128#ifdef _WIN32 129 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 130 // is less than 0x0500 as is the case with some default MinGW builds. 131 // Both functions support large file sizes. 132 DWORD size_upper = 0; 133 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); 134 DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 135 if (size_lower == INVALID_FILE_SIZE) { 136 DWORD error = GetLastError(); 137 if (error != NO_ERROR) 138 FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); 139 } 140 fmt::ULongLong long_size = size_upper; 141 return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 142#else 143 typedef struct stat Stat; 144 Stat file_stat = Stat(); 145 if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 146 FMT_THROW(SystemError(errno, "cannot get file attributes")); 147 FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), 148 "return type of File::size is not large enough"); 149 return file_stat.st_size; 150#endif 151} 152 153std::size_t fmt::File::read(void *buffer, std::size_t count) { 154 RWResult result = 0; 155 FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 156 if (result < 0) 157 FMT_THROW(SystemError(errno, "cannot read from file")); 158 return internal::to_unsigned(result); 159} 160 161std::size_t fmt::File::write(const void *buffer, std::size_t count) { 162 RWResult result = 0; 163 FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 164 if (result < 0) 165 FMT_THROW(SystemError(errno, "cannot write to file")); 166 return internal::to_unsigned(result); 167} 168 169fmt::File fmt::File::dup(int fd) { 170 // Don't retry as dup doesn't return EINTR. 171 // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 172 int new_fd = FMT_POSIX_CALL(dup(fd)); 173 if (new_fd == -1) 174 FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); 175 return File(new_fd); 176} 177 178void fmt::File::dup2(int fd) { 179 int result = 0; 180 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 181 if (result == -1) { 182 FMT_THROW(SystemError(errno, 183 "cannot duplicate file descriptor {} to {}", fd_, fd)); 184 } 185} 186 187void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { 188 int result = 0; 189 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 190 if (result == -1) 191 ec = ErrorCode(errno); 192} 193 194void fmt::File::pipe(File &read_end, File &write_end) { 195 // Close the descriptors first to make sure that assignments don't throw 196 // and there are no leaks. 197 read_end.close(); 198 write_end.close(); 199 int fds[2] = {}; 200#ifdef _WIN32 201 // Make the default pipe capacity same as on Linux 2.6.11+. 202 enum { DEFAULT_CAPACITY = 65536 }; 203 int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 204#else 205 // Don't retry as the pipe function doesn't return EINTR. 206 // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 207 int result = FMT_POSIX_CALL(pipe(fds)); 208#endif 209 if (result != 0) 210 FMT_THROW(SystemError(errno, "cannot create pipe")); 211 // The following assignments don't throw because read_fd and write_fd 212 // are closed. 213 read_end = File(fds[0]); 214 write_end = File(fds[1]); 215} 216 217fmt::BufferedFile fmt::File::fdopen(const char *mode) { 218 // Don't retry as fdopen doesn't return EINTR. 219 FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); 220 if (!f) 221 FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); 222 BufferedFile file(f); 223 fd_ = -1; 224 return file; 225} 226 227long fmt::getpagesize() { 228#ifdef _WIN32 229 SYSTEM_INFO si; 230 GetSystemInfo(&si); 231 return si.dwPageSize; 232#else 233 long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 234 if (size < 0) 235 FMT_THROW(SystemError(errno, "cannot get memory page size")); 236 return size; 237#endif 238} 239