mirror of
				https://github.com/fmtlib/fmt.git
				synced 2025-10-28 04:31:44 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			198 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  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 <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
 | |
| 
 | |
| #ifndef S_IRUSR
 | |
| # define S_IRUSR _S_IREAD
 | |
| #endif
 | |
| 
 | |
| #ifndef S_IWUSR
 | |
| # define S_IWUSR _S_IWRITE
 | |
| #endif
 | |
| 
 | |
| # ifdef __MINGW32__
 | |
| #  define _SH_DENYNO 0x40
 | |
| # endif
 | |
| 
 | |
| #endif  // _WIN32
 | |
| 
 | |
| 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 convert_rwcount(std::size_t count) {
 | |
|   return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
 | |
| }
 | |
| #else
 | |
| inline std::size_t convert_rwcount(std::size_t count) { return count; }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT(true) {
 | |
|   if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
 | |
|     fmt::report_system_error(errno, "cannot close file");
 | |
| }
 | |
| 
 | |
| fmt::BufferedFile::BufferedFile(fmt::StringRef filename, fmt::StringRef mode) {
 | |
|   FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
 | |
|   if (!file_)
 | |
|     throw SystemError(errno, "cannot open file {}", filename);
 | |
| }
 | |
| 
 | |
| void fmt::BufferedFile::close() {
 | |
|   if (!file_)
 | |
|     return;
 | |
|   int result = FMT_SYSTEM(fclose(file_));
 | |
|   file_ = 0;
 | |
|   if (result != 0)
 | |
|     throw SystemError(errno, "cannot close file");
 | |
| }
 | |
| 
 | |
| int fmt::BufferedFile::fileno() const {
 | |
|   int fd = FMT_POSIX_CALL(fileno(file_));
 | |
|   if (fd == -1)
 | |
|     throw SystemError(errno, "cannot get file descriptor");
 | |
|   return fd;
 | |
| }
 | |
| 
 | |
| fmt::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)
 | |
|     throw SystemError(errno, "cannot open file {}", path);
 | |
| }
 | |
| 
 | |
| fmt::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::report_system_error(errno, "cannot close file");
 | |
| }
 | |
| 
 | |
| void fmt::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)
 | |
|     throw SystemError(errno, "cannot close file");
 | |
| }
 | |
| 
 | |
| std::streamsize fmt::File::read(void *buffer, std::size_t count) {
 | |
|   std::streamsize result = 0;
 | |
|   FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
 | |
|   if (result == -1)
 | |
|     throw SystemError(errno, "cannot read from file");
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| std::streamsize fmt::File::write(const void *buffer, std::size_t count) {
 | |
|   std::streamsize result = 0;
 | |
|   FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
 | |
|   if (result == -1)
 | |
|     throw SystemError(errno, "cannot write to file");
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| fmt::File fmt::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)
 | |
|     throw SystemError(errno, "cannot duplicate file descriptor {}", fd);
 | |
|   return File(new_fd);
 | |
| }
 | |
| 
 | |
| void fmt::File::dup2(int fd) {
 | |
|   int result = 0;
 | |
|   FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
 | |
|   if (result == -1) {
 | |
|     throw SystemError(errno,
 | |
|       "cannot duplicate file descriptor {} to {}", fd_, fd);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void fmt::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 fmt::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)
 | |
|     throw SystemError(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]);
 | |
| }
 | |
| 
 | |
| fmt::BufferedFile fmt::File::fdopen(const char *mode) {
 | |
|   // Don't retry as fdopen doesn't return EINTR.
 | |
|   FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
 | |
|   if (!f)
 | |
|     throw SystemError(errno, "cannot associate stream with file descriptor");
 | |
|   BufferedFile file(f);
 | |
|   fd_ = -1;
 | |
|   return file;
 | |
| }
 |