2013-01-05 01:02:47 +04:00
# define _LARGEFILE64_SOURCE
2013-01-07 21:38:04 +04:00
# define _FILE_OFFSET_BITS 64
2013-01-05 01:02:47 +04:00
2012-10-15 16:58:33 +04:00
# include "util/file.hh"
# include "util/exception.hh"
2014-06-02 21:28:02 +04:00
# include <algorithm>
2012-10-15 16:58:33 +04:00
# include <cstdlib>
# include <cstdio>
# include <iostream>
2014-06-02 21:28:02 +04:00
# include <limits>
# include <sstream>
2012-10-15 16:58:33 +04:00
2015-03-28 15:37:48 +03:00
# include <cassert>
# include <cerrno>
# include <climits>
2012-10-15 16:58:33 +04:00
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <stdint.h>
2014-06-02 21:28:02 +04:00
# if defined(__MINGW32__)
2013-12-19 00:15:39 +04:00
# include <windows.h>
# include <unistd.h>
2014-01-28 04:51:35 +04:00
# warning "The file functions on MinGW have not been tested for file sizes above 2^31 - 1. Please read https: //stackoverflow.com/questions/12539488/determine-64-bit-file-size-in-c-on-mingw-32-bit and fix"
2013-12-19 00:15:39 +04:00
# elif defined(_WIN32) || defined(_WIN64)
2012-10-15 16:58:33 +04:00
# include <windows.h>
# include <io.h>
# else
# include <unistd.h>
# endif
namespace util {
scoped_fd : : ~ scoped_fd ( ) {
if ( fd_ ! = - 1 & & close ( fd_ ) ) {
2024-02-22 23:09:02 +03:00
throw std : : runtime_error ( " Could not close file " + fd_ ) ;
2012-10-15 16:58:33 +04:00
}
}
2014-10-08 16:42:53 +04:00
void scoped_FILE_closer : : Close ( std : : FILE * file ) {
if ( file & & std : : fclose ( file ) ) {
2024-02-22 23:09:02 +03:00
throw std : : runtime_error ( " Could not close file " ) ;
2012-10-15 16:58:33 +04:00
}
}
2013-01-17 15:58:58 +04:00
// Note that ErrnoException records errno before NameFromFD is called.
FDException : : FDException ( int fd ) throw ( ) : fd_ ( fd ) , name_guess_ ( NameFromFD ( fd ) ) {
* this < < " in " < < name_guess_ < < ' ' ;
}
FDException : : ~ FDException ( ) throw ( ) { }
EndOfFileException : : EndOfFileException ( ) throw ( ) {
* this < < " End of file " ;
}
EndOfFileException : : ~ EndOfFileException ( ) throw ( ) { }
2015-08-27 12:55:52 +03:00
bool InputFileIsStdin ( StringPiece path ) {
return path = = " - " | | path = = " /dev/stdin " ;
}
bool OutputFileIsStdout ( StringPiece path ) {
return path = = " - " | | path = = " /dev/stdout " ;
}
2012-10-15 16:58:33 +04:00
int OpenReadOrThrow ( const char * name ) {
int ret ;
# if defined(_WIN32) || defined(_WIN64)
UTIL_THROW_IF ( - 1 = = ( ret = _open ( name , _O_BINARY | _O_RDONLY ) ) , ErrnoException , " while opening " < < name ) ;
# else
UTIL_THROW_IF ( - 1 = = ( ret = open ( name , O_RDONLY ) ) , ErrnoException , " while opening " < < name ) ;
# endif
return ret ;
}
int CreateOrThrow ( const char * name ) {
int ret ;
# if defined(_WIN32) || defined(_WIN64)
2012-10-30 23:33:22 +04:00
UTIL_THROW_IF ( - 1 = = ( ret = _open ( name , _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY , _S_IREAD | _S_IWRITE ) ) , ErrnoException , " while creating " < < name ) ;
2012-10-15 16:58:33 +04:00
# else
UTIL_THROW_IF ( - 1 = = ( ret = open ( name , O_CREAT | O_TRUNC | O_RDWR , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ) , ErrnoException , " while creating " < < name ) ;
# endif
return ret ;
}
uint64_t SizeFile ( int fd ) {
2013-12-19 00:15:39 +04:00
# if defined __MINGW32__
struct stat sb ;
2014-01-28 04:51:35 +04:00
// Does this handle 64-bit?
2013-12-19 00:15:39 +04:00
int ret = fstat ( fd , & sb ) ;
if ( ret = = - 1 | | ( ! sb . st_size & & ! S_ISREG ( sb . st_mode ) ) ) return kBadSize ;
return sb . st_size ;
# elif defined(_WIN32) || defined(_WIN64)
2012-10-15 16:58:33 +04:00
__int64 ret = _filelengthi64 ( fd ) ;
return ( ret = = - 1 ) ? kBadSize : ret ;
2013-01-10 17:32:17 +04:00
# else // Not windows.
# ifdef OS_ANDROID
struct stat64 sb ;
int ret = fstat64 ( fd , & sb ) ;
2012-10-15 16:58:33 +04:00
# else
struct stat sb ;
2013-01-10 17:32:17 +04:00
int ret = fstat ( fd , & sb ) ;
# endif
if ( ret = = - 1 | | ( ! sb . st_size & & ! S_ISREG ( sb . st_mode ) ) ) return kBadSize ;
2012-10-15 16:58:33 +04:00
return sb . st_size ;
# endif
}
2013-01-17 15:58:58 +04:00
uint64_t SizeOrThrow ( int fd ) {
uint64_t ret = SizeFile ( fd ) ;
UTIL_THROW_IF_ARG ( ret = = kBadSize , FDException , ( fd ) , " Failed to size " ) ;
return ret ;
}
2012-10-15 16:58:33 +04:00
void ResizeOrThrow ( int fd , uint64_t to ) {
2015-08-27 12:55:52 +03:00
# if defined __MINGW32__
// Does this handle 64-bit?
int ret = ftruncate
# elif defined(_WIN32) || defined(_WIN64)
2013-01-23 00:23:35 +04:00
errno_t ret = _chsize_s
2013-01-10 17:45:41 +04:00
# elif defined(OS_ANDROID)
2013-01-23 00:23:35 +04:00
int ret = ftruncate64
2012-10-15 16:58:33 +04:00
# else
2013-01-23 00:23:35 +04:00
int ret = ftruncate
2012-10-15 16:58:33 +04:00
# endif
2013-01-23 00:23:35 +04:00
( fd , to ) ;
UTIL_THROW_IF_ARG ( ret , FDException , ( fd ) , " while resizing to " < < to < < " bytes " ) ;
2012-10-15 16:58:33 +04:00
}
2013-03-31 18:58:34 +04:00
namespace {
std : : size_t GuardLarge ( std : : size_t size ) {
// The following operating systems have broken read/write/pread/pwrite that
// only supports up to 2^31.
2014-06-02 21:28:02 +04:00
// OS X man pages claim to support 64-bit, but Kareem M. Darwish had problems
// building with larger files, so APPLE is also here.
2014-01-28 04:51:35 +04:00
# if defined(_WIN32) || defined(_WIN64) || defined(__APPLE__) || defined(OS_ANDROID) || defined(__MINGW32__)
2014-06-02 21:28:02 +04:00
return size < INT_MAX ? size : INT_MAX ;
2013-03-31 18:58:34 +04:00
# else
return size ;
# endif
}
}
2015-09-29 18:58:02 +03:00
# if defined(_WIN32) || defined(_WIN64)
namespace {
const std : : size_t kMaxDWORD = static_cast < std : : size_t > ( 4294967295UL ) ;
} // namespace
# endif
2012-10-30 23:33:22 +04:00
std : : size_t PartialRead ( int fd , void * to , std : : size_t amount ) {
2012-10-25 16:01:36 +04:00
# if defined(_WIN32) || defined(_WIN64)
2015-09-29 18:58:02 +03:00
DWORD ret ;
HANDLE file_handle = reinterpret_cast < HANDLE > ( _get_osfhandle ( fd ) ) ;
DWORD larger_size = static_cast < DWORD > ( std : : min < std : : size_t > ( kMaxDWORD , amount ) ) ;
DWORD smaller_size = 28672 ; // Received reports that 31346 worked but higher values did not. This rounds down to the nearest multiple of 4096, the page size.
if ( ! ReadFile ( file_handle , to , larger_size , & ret , NULL ) )
{
DWORD last_error = GetLastError ( ) ;
if ( last_error ! = ERROR_NOT_ENOUGH_MEMORY | | ! ReadFile ( file_handle , to , smaller_size , & ret , NULL ) ) {
UTIL_THROW ( WindowsException , " Windows error in ReadFile. " ) ;
}
}
2012-10-25 16:01:36 +04:00
# else
2013-01-17 15:58:58 +04:00
errno = 0 ;
ssize_t ret ;
do {
2013-03-31 18:58:34 +04:00
ret = read ( fd , to , GuardLarge ( amount ) ) ;
2013-01-17 15:58:58 +04:00
} while ( ret = = - 1 & & errno = = EINTR ) ;
UTIL_THROW_IF_ARG ( ret < 0 , FDException , ( fd ) , " while reading " < < amount < < " bytes " ) ;
2015-09-29 18:58:02 +03:00
# endif
2012-10-30 23:33:22 +04:00
return static_cast < std : : size_t > ( ret ) ;
2012-10-25 16:01:36 +04:00
}
2012-10-15 16:58:33 +04:00
void ReadOrThrow ( int fd , void * to_void , std : : size_t amount ) {
uint8_t * to = static_cast < uint8_t * > ( to_void ) ;
while ( amount ) {
2012-10-30 23:33:22 +04:00
std : : size_t ret = PartialRead ( fd , to , amount ) ;
2013-01-17 15:58:58 +04:00
UTIL_THROW_IF ( ret = = 0 , EndOfFileException , " in " < < NameFromFD ( fd ) < < " but there should be " < < amount < < " more bytes to read. " ) ;
2012-10-15 16:58:33 +04:00
amount - = ret ;
to + = ret ;
}
}
std : : size_t ReadOrEOF ( int fd , void * to_void , std : : size_t amount ) {
uint8_t * to = static_cast < uint8_t * > ( to_void ) ;
std : : size_t remaining = amount ;
while ( remaining ) {
2012-10-30 23:33:22 +04:00
std : : size_t ret = PartialRead ( fd , to , remaining ) ;
2012-10-15 16:58:33 +04:00
if ( ! ret ) return amount - remaining ;
remaining - = ret ;
to + = ret ;
}
return amount ;
}
2014-06-02 21:28:02 +04:00
void WriteOrThrow ( int fd , const void * data_void , std : : size_t size ) {
const uint8_t * data = static_cast < const uint8_t * > ( data_void ) ;
while ( size ) {
2013-01-17 15:58:58 +04:00
# if defined(_WIN32) || defined(_WIN64)
2014-06-02 21:28:02 +04:00
int ret ;
# else
ssize_t ret ;
2013-01-23 00:23:35 +04:00
# endif
2014-06-02 21:28:02 +04:00
errno = 0 ;
do {
ret =
# if defined(_WIN32) || defined(_WIN64)
_write
# else
write
# endif
( fd , data , GuardLarge ( size ) ) ;
} while ( ret = = - 1 & & errno = = EINTR ) ;
UTIL_THROW_IF_ARG ( ret < 1 , FDException , ( fd ) , " while writing " < < size < < " bytes " ) ;
data + = ret ;
size - = ret ;
}
}
void WriteOrThrow ( FILE * to , const void * data , std : : size_t size ) {
if ( ! size ) return ;
UTIL_THROW_IF ( 1 ! = std : : fwrite ( data , size , 1 , to ) , ErrnoException , " Short write; requested size " < < size ) ;
}
void ErsatzPRead ( int fd , void * to_void , std : : size_t size , uint64_t off ) {
uint8_t * to = static_cast < uint8_t * > ( to_void ) ;
while ( size ) {
2013-01-23 00:23:35 +04:00
# if defined(_WIN32) || defined(_WIN64)
/* BROKEN: changes file pointer. Even if you save it and change it back, it won't be safe to use concurrently with write() or read() which lmplz does. */
// size_t might be 64-bit. DWORD is always 32.
DWORD reading = static_cast < DWORD > ( std : : min < std : : size_t > ( kMaxDWORD , size ) ) ;
DWORD ret ;
OVERLAPPED overlapped ;
memset ( & overlapped , 0 , sizeof ( OVERLAPPED ) ) ;
overlapped . Offset = static_cast < DWORD > ( off ) ;
overlapped . OffsetHigh = static_cast < DWORD > ( off > > 32 ) ;
2015-09-29 18:58:02 +03:00
UTIL_THROW_IF ( ! ReadFile ( ( HANDLE ) _get_osfhandle ( fd ) , to , reading , & ret , & overlapped ) , WindowsException , " ReadFile failed for offset " < < off ) ;
2013-01-23 00:23:35 +04:00
# else
2013-01-17 15:58:58 +04:00
ssize_t ret ;
errno = 0 ;
2014-06-02 21:28:02 +04:00
ret =
2013-01-17 15:58:58 +04:00
# ifdef OS_ANDROID
2014-06-02 21:28:02 +04:00
pread64
2013-01-17 15:58:58 +04:00
# else
2014-06-02 21:28:02 +04:00
pread
2013-01-17 15:58:58 +04:00
# endif
2014-06-02 21:28:02 +04:00
( fd , to , GuardLarge ( size ) , off ) ;
2013-01-17 15:58:58 +04:00
if ( ret < = 0 ) {
2014-06-02 21:28:02 +04:00
if ( ret = = - 1 & & errno = = EINTR ) continue ;
2013-01-17 15:58:58 +04:00
UTIL_THROW_IF ( ret = = 0 , EndOfFileException , " for reading " < < size < < " bytes at " < < off < < " from " < < NameFromFD ( fd ) ) ;
UTIL_THROW_ARG ( FDException , ( fd ) , " while reading " < < size < < " bytes at offset " < < off ) ;
}
2013-01-23 00:23:35 +04:00
# endif
2013-01-17 15:58:58 +04:00
size - = ret ;
off + = ret ;
to + = ret ;
}
}
2014-06-02 21:28:02 +04:00
void ErsatzPWrite ( int fd , const void * from_void , std : : size_t size , uint64_t off ) {
const uint8_t * from = static_cast < const uint8_t * > ( from_void ) ;
while ( size ) {
2012-10-25 16:01:36 +04:00
# if defined(_WIN32) || defined(_WIN64)
2014-06-02 21:28:02 +04:00
/* Changes file pointer. Even if you save it and change it back, it won't be safe to use concurrently with write() or read() */
// size_t might be 64-bit. DWORD is always 32.
DWORD writing = static_cast < DWORD > ( std : : min < std : : size_t > ( kMaxDWORD , size ) ) ;
DWORD ret ;
OVERLAPPED overlapped ;
memset ( & overlapped , 0 , sizeof ( OVERLAPPED ) ) ;
overlapped . Offset = static_cast < DWORD > ( off ) ;
overlapped . OffsetHigh = static_cast < DWORD > ( off > > 32 ) ;
UTIL_THROW_IF ( ! WriteFile ( ( HANDLE ) _get_osfhandle ( fd ) , from , writing , & ret , & overlapped ) , Exception , " WriteFile failed for offset " < < off ) ;
2012-10-25 16:01:36 +04:00
# else
2013-01-17 15:58:58 +04:00
ssize_t ret ;
2013-03-31 18:58:34 +04:00
errno = 0 ;
2014-06-02 21:28:02 +04:00
ret =
# ifdef OS_ANDROID
pwrite64
2013-03-31 18:58:34 +04:00
# else
2014-06-02 21:28:02 +04:00
pwrite
# endif
( fd , from , GuardLarge ( size ) , off ) ;
if ( ret < = 0 ) {
if ( ret = = - 1 & & errno = = EINTR ) continue ;
UTIL_THROW_IF ( ret = = 0 , EndOfFileException , " for writing " < < size < < " bytes at " < < off < < " from " < < NameFromFD ( fd ) ) ;
UTIL_THROW_ARG ( FDException , ( fd ) , " while writing " < < size < < " bytes at offset " < < off ) ;
}
2012-10-25 16:01:36 +04:00
# endif
2012-10-15 16:58:33 +04:00
size - = ret ;
2014-06-02 21:28:02 +04:00
off + = ret ;
from + = ret ;
2012-10-15 16:58:33 +04:00
}
}
void FSyncOrThrow ( int fd ) {
2013-06-24 19:05:47 +04:00
// Apparently windows doesn't have fsync?
2012-10-15 16:58:33 +04:00
# if !defined(_WIN32) && !defined(_WIN64)
2013-01-18 19:58:54 +04:00
UTIL_THROW_IF_ARG ( - 1 = = fsync ( fd ) , FDException , ( fd ) , " while syncing " ) ;
2012-10-15 16:58:33 +04:00
# endif
}
namespace {
2013-01-10 17:32:17 +04:00
// Static assert for 64-bit off_t size.
# if !defined(_WIN32) && !defined(_WIN64) && !defined(OS_ANDROID)
template < unsigned > struct CheckOffT ;
template < > struct CheckOffT < 8 > {
struct True { } ;
} ;
// If there's a compiler error on the next line, then off_t isn't 64 bit. And
// that makes me a sad panda.
typedef CheckOffT < sizeof ( off_t ) > : : True IgnoredType ;
# endif
2013-06-24 19:05:47 +04:00
// Can't we all just get along?
2012-10-15 16:58:33 +04:00
void InternalSeek ( int fd , int64_t off , int whence ) {
2013-01-23 00:23:35 +04:00
if (
2013-12-19 00:15:39 +04:00
# if defined __MINGW32__
2014-01-28 04:51:35 +04:00
// Does this handle 64-bit?
2013-12-19 00:15:39 +04:00
( off_t ) - 1 = = lseek ( fd , off , whence )
# elif defined(_WIN32) || defined(_WIN64)
2013-01-23 00:23:35 +04:00
( __int64 ) - 1 = = _lseeki64 ( fd , off , whence )
2013-01-10 17:32:17 +04:00
# elif defined(OS_ANDROID)
2013-01-23 00:23:35 +04:00
( off64_t ) - 1 = = lseek64 ( fd , off , whence )
2012-10-15 16:58:33 +04:00
# else
2013-01-23 00:23:35 +04:00
( off_t ) - 1 = = lseek ( fd , off , whence )
2012-10-15 16:58:33 +04:00
# endif
2013-01-23 00:23:35 +04:00
) UTIL_THROW_ARG ( FDException , ( fd ) , " while seeking to " < < off < < " whence " < < whence ) ;
2012-10-15 16:58:33 +04:00
}
} // namespace
void SeekOrThrow ( int fd , uint64_t off ) {
InternalSeek ( fd , off , SEEK_SET ) ;
}
void AdvanceOrThrow ( int fd , int64_t off ) {
InternalSeek ( fd , off , SEEK_CUR ) ;
}
void SeekEnd ( int fd ) {
InternalSeek ( fd , 0 , SEEK_END ) ;
}
std : : FILE * FDOpenOrThrow ( scoped_fd & file ) {
std : : FILE * ret = fdopen ( file . get ( ) , " r+b " ) ;
2013-01-17 15:58:58 +04:00
UTIL_THROW_IF_ARG ( ! ret , FDException , ( file . get ( ) ) , " Could not fdopen for write " ) ;
2012-10-30 23:33:22 +04:00
file . release ( ) ;
return ret ;
}
std : : FILE * FDOpenReadOrThrow ( scoped_fd & file ) {
std : : FILE * ret = fdopen ( file . get ( ) , " rb " ) ;
2013-01-17 15:58:58 +04:00
UTIL_THROW_IF_ARG ( ! ret , FDException , ( file . get ( ) ) , " Could not fdopen for read " ) ;
2012-10-15 16:58:33 +04:00
file . release ( ) ;
return ret ;
}
// Sigh. Windows temporary file creation is full of race conditions.
# if defined(_WIN32) || defined(_WIN64)
/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright
( C ) 1991 - 1999 , 2000 , 2001 , 2006 Free Software Foundation , Inc .
The GNU C Library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 2.1 of the License , or ( at your option ) any later version . */
/* This has been modified from the original version to rename the function and
* set the Windows temporary flag . */
static const char letters [ ] =
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
/* Generate a temporary file name based on TMPL. TMPL must match the
rules for mk [ s ] temp ( i . e . end in " XXXXXX " ) . The name constructed
does not exist at the time of the call to mkstemp . TMPL is
overwritten with the result . */
int
mkstemp_and_unlink ( char * tmpl )
{
int len ;
char * XXXXXX ;
static unsigned long long value ;
unsigned long long random_time_bits ;
unsigned int count ;
int fd = - 1 ;
int save_errno = errno ;
/* A lower bound on the number of temporary files to attempt to
generate . The maximum total number of temporary file names that
can exist for a given template is 62 * * 6. It should never be
necessary to try all these combinations . Instead if a reasonable
number of names is tried ( we define reasonable as 62 * * 3 ) fail to
give the system administrator the chance to remove the problems . */
# define ATTEMPTS_MIN (62 * 62 * 62)
/* The number of times to attempt to generate a temporary file. To
conform to POSIX , this must be no smaller than TMP_MAX . */
# if ATTEMPTS_MIN < TMP_MAX
unsigned int attempts = TMP_MAX ;
# else
unsigned int attempts = ATTEMPTS_MIN ;
# endif
len = strlen ( tmpl ) ;
if ( len < 6 | | strcmp ( & tmpl [ len - 6 ] , " XXXXXX " ) )
{
errno = EINVAL ;
return - 1 ;
}
/* This is where the Xs start. */
XXXXXX = & tmpl [ len - 6 ] ;
/* Get some more or less random data. */
{
SYSTEMTIME stNow ;
FILETIME ftNow ;
// get system time
GetSystemTime ( & stNow ) ;
stNow . wMilliseconds = 500 ;
if ( ! SystemTimeToFileTime ( & stNow , & ftNow ) )
{
errno = - 1 ;
return - 1 ;
}
random_time_bits = ( ( ( unsigned long long ) ftNow . dwHighDateTime < < 32 )
| ( unsigned long long ) ftNow . dwLowDateTime ) ;
}
value + = random_time_bits ^ ( unsigned long long ) GetCurrentThreadId ( ) ;
for ( count = 0 ; count < attempts ; value + = 7777 , + + count )
{
unsigned long long v = value ;
/* Fill in the random bits. */
XXXXXX [ 0 ] = letters [ v % 62 ] ;
v / = 62 ;
XXXXXX [ 1 ] = letters [ v % 62 ] ;
v / = 62 ;
XXXXXX [ 2 ] = letters [ v % 62 ] ;
v / = 62 ;
XXXXXX [ 3 ] = letters [ v % 62 ] ;
v / = 62 ;
XXXXXX [ 4 ] = letters [ v % 62 ] ;
v / = 62 ;
XXXXXX [ 5 ] = letters [ v % 62 ] ;
/* Modified for windows and to unlink */
// fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
int flags = _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY ;
flags | = _O_TEMPORARY ;
fd = _open ( tmpl , flags , _S_IREAD | _S_IWRITE ) ;
if ( fd > = 0 )
{
errno = save_errno ;
return fd ;
}
else if ( errno ! = EEXIST )
return - 1 ;
}
/* We got out of the loop because we ran out of combinations to try. */
errno = EEXIST ;
return - 1 ;
}
# else
int
mkstemp_and_unlink ( char * tmpl ) {
int ret = mkstemp ( tmpl ) ;
if ( ret ! = - 1 ) {
2013-01-17 15:58:58 +04:00
UTIL_THROW_IF ( unlink ( tmpl ) , ErrnoException , " while deleting delete " < < tmpl ) ;
2012-10-15 16:58:33 +04:00
}
return ret ;
}
# endif
2013-01-18 19:58:54 +04:00
// If it's a directory, add a /. This lets users say -T /tmp without creating
// /tmpAAAAAA
void NormalizeTempPrefix ( std : : string & base ) {
if ( base . empty ( ) ) return ;
if ( base [ base . size ( ) - 1 ] = = ' / ' ) return ;
struct stat sb ;
// It's fine for it to not exist.
if ( - 1 = = stat ( base . c_str ( ) , & sb ) ) return ;
2013-01-23 00:23:35 +04:00
if (
# if defined(_WIN32) || defined(_WIN64)
sb . st_mode & _S_IFDIR
# else
S_ISDIR ( sb . st_mode )
# endif
) base + = ' / ' ;
2013-01-18 19:58:54 +04:00
}
2014-06-02 21:28:02 +04:00
int MakeTemp ( const StringPiece & base ) {
std : : string name ( base . data ( ) , base . size ( ) ) ;
2013-01-17 15:58:58 +04:00
name + = " XXXXXX " ;
2012-10-15 16:58:33 +04:00
name . push_back ( 0 ) ;
int ret ;
2013-01-17 15:58:58 +04:00
UTIL_THROW_IF ( - 1 = = ( ret = mkstemp_and_unlink ( & name [ 0 ] ) ) , ErrnoException , " while making a temporary based on " < < base ) ;
2012-10-15 16:58:33 +04:00
return ret ;
}
2014-06-02 21:28:02 +04:00
std : : FILE * FMakeTemp ( const StringPiece & base ) {
2013-01-17 15:58:58 +04:00
util : : scoped_fd file ( MakeTemp ( base ) ) ;
2012-10-15 16:58:33 +04:00
return FDOpenOrThrow ( file ) ;
}
2013-01-17 15:58:58 +04:00
int DupOrThrow ( int fd ) {
int ret = dup ( fd ) ;
UTIL_THROW_IF_ARG ( ret = = - 1 , FDException , ( fd ) , " in duplicating the file descriptor " ) ;
return ret ;
}
namespace {
// Try to name things but be willing to fail too.
bool TryName ( int fd , std : : string & out ) {
# if defined(_WIN32) || defined(_WIN64)
return false ;
# else
std : : string name ( " /proc/self/fd/ " ) ;
std : : ostringstream convert ;
convert < < fd ;
name + = convert . str ( ) ;
2013-06-24 19:05:47 +04:00
2013-01-17 15:58:58 +04:00
struct stat sb ;
2013-06-24 19:05:47 +04:00
if ( - 1 = = lstat ( name . c_str ( ) , & sb ) )
2013-01-17 15:58:58 +04:00
return false ;
out . resize ( sb . st_size + 1 ) ;
2014-06-02 21:28:02 +04:00
// lstat gave us a size, but I've seen it grow, possibly due to symlinks on top of symlinks.
while ( true ) {
ssize_t ret = readlink ( name . c_str ( ) , & out [ 0 ] , out . size ( ) ) ;
if ( - 1 = = ret )
return false ;
if ( ( size_t ) ret < out . size ( ) ) {
out . resize ( ret ) ;
break ;
}
// Exponential growth.
out . resize ( out . size ( ) * 2 ) ;
2013-01-17 15:58:58 +04:00
}
// Don't use the non-file names.
2013-06-24 19:05:47 +04:00
if ( ! out . empty ( ) & & out [ 0 ] ! = ' / ' )
2013-01-17 15:58:58 +04:00
return false ;
return true ;
# endif
}
} // namespace
std : : string NameFromFD ( int fd ) {
std : : string ret ;
if ( TryName ( fd , ret ) ) return ret ;
switch ( fd ) {
case 0 : return " stdin " ;
case 1 : return " stdout " ;
case 2 : return " stderr " ;
}
ret = " fd " ;
std : : ostringstream convert ;
convert < < fd ;
ret + = convert . str ( ) ;
return ret ;
}
2012-10-15 16:58:33 +04:00
} // namespace util