/* * Copyright (c) 1999 * Silicon Graphics Computer Systems, Inc. * * Copyright (c) 1999 * Boris Fomitchev * * This material is provided "as is", with absolutely no warranty expressed * or implied. Any use is at your own risk. * * Permission to use or copy this software for any purpose is hereby granted * without fee, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. * */ #include <fstream> #if !defined (_STLP_WCE) # ifdef __BORLANDC__ # include <cfcntl.h> // For _O_RDONLY, etc # else # include <io.h> // For _get_osfhandle # include <fcntl.h> // For _O_RDONLY, etc # endif # include <sys/stat.h> // For _fstat #endif #define _TEXTBUF_SIZE 0x1000 const _STLP_fd INVALID_STLP_FD = INVALID_HANDLE_VALUE; #if !defined (INVALID_SET_FILE_POINTER) # define INVALID_SET_FILE_POINTER 0xffffffff #endif #ifndef O_ACCMODE # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) #endif _STLP_BEGIN_NAMESPACE #if !defined(__MSL__) && !defined(_STLP_WCE) static ios_base::openmode flag_to_openmode(int mode) { ios_base::openmode ret = ios_base::__default_mode; switch (mode & O_ACCMODE) { case O_RDONLY: ret = ios_base::in; break; case O_WRONLY: ret = ios_base::out; break; case O_RDWR: ret = ios_base::in | ios_base::out; break; } if (mode & O_APPEND) ret |= ios_base::app; if (mode & O_BINARY) ret |= ios_base::binary; return ret; } #endif _STLP_MOVE_TO_PRIV_NAMESPACE // Helper functions for _Filebuf_base. static bool __is_regular_file(_STLP_fd fd) { BY_HANDLE_FILE_INFORMATION info; // Return true if the file handle isn't a directory. return GetFileInformationByHandle(fd, &info) && ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0); } // Number of characters in the file. static streamoff __file_size(_STLP_fd fd) { streamoff ret = 0; LARGE_INTEGER li; li.LowPart = GetFileSize(fd, (unsigned long*) &li.HighPart); if (li.LowPart != INVALID_FILE_SIZE || GetLastError() == NO_ERROR) ret = li.QuadPart; return ret; } _STLP_MOVE_TO_STD_NAMESPACE // Visual C++ and Intel use this, but not Metrowerks // Also MinGW, msvcrt.dll (but not crtdll.dll) dependent version #if (defined (_STLP_MSVC_LIB) && !defined (_STLP_WCE)) || \ (defined (__MINGW32__) && defined (__MSVCRT__)) // fcntl(fileno, F_GETFL) for Microsoft library // 'semi-documented' defines: # define IOINFO_L2E 5 # define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) # define _pioinfo(i) ( __pioinfo[(i) >> IOINFO_L2E] + \ ((i) & (IOINFO_ARRAY_ELTS - 1)) ) # define FAPPEND 0x20 // O_APPEND flag # define FTEXT 0x80 // O_TEXT flag // end of 'semi-documented' defines // 'semi-documented' internal structure extern "C" { struct ioinfo { long osfhnd; // the real os HANDLE char osfile; // file handle flags char pipech; // pipe buffer # if defined (_MT) // multi-threaded locking int lockinitflag; CRITICAL_SECTION lock; # endif }; # if defined (__MINGW32__) __MINGW_IMPORT ioinfo * __pioinfo[]; # else extern _CRTIMP ioinfo * __pioinfo[]; # endif } // extern "C" // end of 'semi-documented' declarations static ios_base::openmode _get_osfflags(int fd, HANDLE oshandle) { char dosflags = 0; if (fd >= 0) dosflags = _pioinfo(fd)->osfile; //else //the file will be considered as open in binary mode with no append attribute // end of 'semi-documented' stuff int mode = 0; if (dosflags & FAPPEND) mode |= O_APPEND; if (dosflags & FTEXT) mode |= O_TEXT; else mode |= O_BINARY; // For Read/Write access we have to guess DWORD dummy, dummy2; BOOL writeOk = WriteFile(oshandle, &dummy2, 0, &dummy, 0); BOOL readOk = ReadFile(oshandle, &dummy2, 0, &dummy, NULL); if (writeOk && readOk) mode |= O_RDWR; else if (readOk) mode |= O_RDONLY; else mode |= O_WRONLY; return flag_to_openmode(mode); } #elif defined (__DMC__) # define FHND_APPEND 0x04 # define FHND_DEVICE 0x08 # define FHND_TEXT 0x10 extern "C" unsigned char __fhnd_info[_NFILE]; static ios_base::openmode _get_osfflags(int fd, HANDLE oshandle) { int mode = 0; if (__fhnd_info[fd] & FHND_APPEND) mode |= O_APPEND; if (__fhnd_info[fd] & FHND_TEXT == 0) mode |= O_BINARY; for (FILE *fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) { if ((fileno(fp) == fd) && (fp->_flag & (_IOREAD | _IOWRT | _IORW))) { const int osflags = fp->_flag; if ((osflags & _IOREAD) && !(osflags & _IOWRT) && !(osflags & _IORW)) mode |= O_RDONLY; else if ((osflags & _IOWRT) && !(osflags & _IOREAD) && !(osflags & _IORW)) mode |= O_WRONLY; else mode |= O_RDWR; break; } } return flag_to_openmode(mode); } #endif size_t _Filebuf_base::_M_page_size = 4096; _Filebuf_base::_Filebuf_base() : _M_file_id(INVALID_STLP_FD), _M_openmode(0), _M_is_open(false), _M_should_close(false), _M_view_id(0) {} void _Filebuf_base::_S_initialize() { SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); _M_page_size = SystemInfo.dwPageSize; // might be .dwAllocationGranularity } // Return the size of the file. This is a wrapper for stat. // Returns zero if the size cannot be determined or is ill-defined. streamoff _Filebuf_base::_M_file_size() { return _STLP_PRIV __file_size(_M_file_id); } bool _Filebuf_base::_M_open(const char* name, ios_base::openmode openmode, long permission) { _STLP_fd file_no; if (_M_is_open) return false; DWORD dwDesiredAccess, dwCreationDisposition; bool doTruncate = false; switch (openmode & (~ios_base::ate & ~ios_base::binary)) { case ios_base::out: case ios_base::out | ios_base::trunc: dwDesiredAccess = GENERIC_WRITE; dwCreationDisposition = OPEN_ALWAYS; // boris : even though it is very non-intuitive, standard // requires them both to behave same. doTruncate = true; break; case ios_base::out | ios_base::app: dwDesiredAccess = GENERIC_WRITE; dwCreationDisposition = OPEN_ALWAYS; break; case ios_base::in: dwDesiredAccess = GENERIC_READ; dwCreationDisposition = OPEN_EXISTING; permission = 0; // Irrelevant unless we're writing. break; case ios_base::in | ios_base::out: dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; dwCreationDisposition = OPEN_EXISTING; break; case ios_base::in | ios_base::out | ios_base::trunc: dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; dwCreationDisposition = OPEN_ALWAYS; doTruncate = true; break; default: // The above are the only combinations of return false; // flags allowed by the C++ standard. } DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; #if defined(_STLP_USE_WIDE_INTERFACE) file_no = CreateFile (_STLP_PRIV __ASCIIToWide(name).c_str(), #else file_no = CreateFileA(name, #endif dwDesiredAccess, dwShareMode, 0, dwCreationDisposition, permission, 0); if (file_no == INVALID_STLP_FD) return false; if ( #if !defined (_STLP_WCE) GetFileType(file_no) == FILE_TYPE_DISK && #endif ((doTruncate && SetEndOfFile(file_no) == 0) || (((openmode & ios_base::ate) != 0) && (SetFilePointer(file_no, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)))) { CloseHandle(file_no); return false; } _M_is_open = true; _M_file_id = file_no; _M_should_close = _M_is_open; _M_openmode = openmode; if (_M_is_open) _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id); return (_M_is_open != 0); } bool _Filebuf_base::_M_open(const char* name, ios_base::openmode openmode) { // This doesn't really grant everyone in the world read/write // access. On Unix, file-creation system calls always clear // bits that are set in the umask from the permissions flag. return this->_M_open(name, openmode, FILE_ATTRIBUTE_NORMAL); } bool _Filebuf_base::_M_open(_STLP_fd __id, ios_base::openmode init_mode) { #if (defined (_STLP_MSVC_LIB) && !defined (_STLP_WCE)) || \ (defined (__MINGW32__) && defined (__MSVCRT__)) || defined (__DMC__) if (_M_is_open || __id == INVALID_STLP_FD) return false; if (init_mode != ios_base::__default_mode) _M_openmode = init_mode; else _M_openmode = _get_osfflags(-1, __id); _M_is_open = true; _M_file_id = __id; _M_should_close = false; _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id); return true; #else (void)__id; (void)init_mode; // dwa 4/27/00 - suppress unused parameter warning // not available for the API return false; #endif } // Associated the filebuf with a file descriptor pointing to an already- // open file. Mode is set to be consistent with the way that the file // was opened. bool _Filebuf_base::_M_open(int file_no, ios_base::openmode init_mode) { if (_M_is_open || file_no < 0) return false; #if (defined (_STLP_MSVC_LIB) && !defined (_STLP_WCE)) || \ (defined (__MINGW32__) && defined (__MSVCRT__)) || defined (__DMC__) HANDLE oshandle = (HANDLE)_get_osfhandle(file_no); if (oshandle == INVALID_STLP_FD) return false; if (init_mode != ios_base::__default_mode) _M_openmode = init_mode; else _M_openmode = _get_osfflags(file_no, oshandle); _M_file_id = oshandle; _M_is_open = true; _M_should_close = false; _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id); return true; #else _STLP_MARK_PARAMETER_AS_UNUSED(&init_mode) // not available for the API return false; #endif } bool _Filebuf_base::_M_close() { if (!_M_is_open) return false; bool ok; if (!_M_should_close) ok = true; else { if (_M_file_id != INVALID_STLP_FD) { ok = (CloseHandle(_M_file_id) != 0); } else { ok = false; } } _M_is_open = _M_should_close = false; _M_openmode = 0; return ok; } #define _STLP_LF 10 #define _STLP_CR 13 #define _STLP_CTRLZ 26 // Read up to n characters into a buffer. Return value is number of // characters read. ptrdiff_t _Filebuf_base::_M_read(char* buf, ptrdiff_t n) { ptrdiff_t readen = 0; //Here cast to size_t is safe as n cannot be negative. size_t chunkSize = (min)(size_t(0xffffffff), __STATIC_CAST(size_t, n)); // The following, while validating that we are still able to extract chunkSize // charaters to the buffer, avoids extraction of too small chunk of datas // which would be counter performant. while (__STATIC_CAST(size_t, (n - readen)) >= chunkSize) { DWORD numberOfBytesRead; ReadFile(_M_file_id, buf + readen, __STATIC_CAST(DWORD, chunkSize), &numberOfBytesRead, 0); if (numberOfBytesRead == 0) break; if (!(_M_openmode & ios_base::binary)) { // translate CR-LFs to LFs in the buffer char *to = buf + readen; char *from = to; char *last = from + numberOfBytesRead - 1; for (; from <= last && *from != _STLP_CTRLZ; ++from) { if (*from != _STLP_CR) *to++ = *from; else { // found CR if (from < last) { // not at buffer end if (*(from + 1) != _STLP_LF) *to++ = _STLP_CR; } else { // last char is CR, peek for LF char peek = ' '; DWORD NumberOfBytesPeeked; ReadFile(_M_file_id, (LPVOID)&peek, 1, &NumberOfBytesPeeked, 0); if (NumberOfBytesPeeked != 0) { if (peek != _STLP_LF) { //not a <CR><LF> combination *to++ = _STLP_CR; if ((to < buf + n) && (peek != _STLP_CR)) //We have enough place to store peek and it is no a special //_STLP_CR character, we can store it. *to++ = peek; else SetFilePointer(_M_file_id, (LONG)-1, 0, FILE_CURRENT); } else { // A <CR><LF> combination, we keep the <LF>: *to++ = _STLP_LF; } } else { /* This case is tedious, we could * - put peek back in the file but this would then generate an infinite loop * - report an error as we don't know if in a future call to ReadFile we won't then * get a <LF>. Doing so would make all files with a <CR> last an invalid file * for STLport, a hard solution for STLport clients. * - store the <CR> in the returned buffer, the chosen solution, even if in this * case we could miss a <CR><LF> combination. */ *to++ = _STLP_CR; } } } // found CR } // for readen = to - buf; // seek back to TEXT end of file if hit CTRL-Z if (from <= last) { // terminated due to CTRLZ SetFilePointer(_M_file_id, -(LONG)((last + 1) - from), 0, FILE_CURRENT); break; } } else readen += numberOfBytesRead; } return readen; } // Write n characters from a buffer. Return value: true if we managed // to write the entire buffer, false if we didn't. bool _Filebuf_base::_M_write(char* buf, ptrdiff_t n) { for (;;) { ptrdiff_t written; //In the following implementation we are going to cast most of the ptrdiff_t //values in size_t to work with coherent unsigned values. Doing so make code //more simple especially in the min function call. // In append mode, every write does an implicit seek to the end // of the file. if (_M_openmode & ios_base::app) _M_seek(0, ios_base::end); if (_M_openmode & ios_base::binary) { // binary mode size_t bytes_to_write = (size_t)n; DWORD NumberOfBytesWritten; written = 0; for (; bytes_to_write != 0;) { WriteFile(_M_file_id, buf + written, __STATIC_CAST(DWORD, (min)(size_t(0xffffffff), bytes_to_write)), &NumberOfBytesWritten, 0); if (NumberOfBytesWritten == 0) return false; bytes_to_write -= NumberOfBytesWritten; written += NumberOfBytesWritten; } } else { char textbuf[_TEXTBUF_SIZE + 1]; // extra 1 in case LF at end char * nextblock = buf, * ptrtextbuf = textbuf; char * endtextbuf = textbuf + _TEXTBUF_SIZE; char * endblock = buf + n; ptrdiff_t nextblocksize = (min) (n, (ptrdiff_t)_TEXTBUF_SIZE); char * nextlf; while ( (nextblocksize > 0) && (nextlf = (char *)memchr(nextblock, _STLP_LF, nextblocksize)) != 0) { ptrdiff_t linelength = nextlf - nextblock; memcpy(ptrtextbuf, nextblock, linelength); ptrtextbuf += linelength; nextblock += (linelength + 1); * ptrtextbuf ++ = _STLP_CR; * ptrtextbuf ++ = _STLP_LF; nextblocksize = (min) (ptrdiff_t(endblock - nextblock), (max) (ptrdiff_t(0), ptrdiff_t(endtextbuf - ptrtextbuf))); } // write out what's left, > condition is here since for LF at the end , // endtextbuf may get < ptrtextbuf ... if (nextblocksize > 0) { memcpy(ptrtextbuf, nextblock, nextblocksize); ptrtextbuf += nextblocksize; nextblock += nextblocksize; } // now write out the translated buffer char * writetextbuf = textbuf; for (size_t NumberOfBytesToWrite = (size_t)(ptrtextbuf - textbuf); NumberOfBytesToWrite;) { DWORD NumberOfBytesWritten; WriteFile((HANDLE)_M_file_id, writetextbuf, __STATIC_CAST(DWORD, (min)(size_t(0xffffffff), NumberOfBytesToWrite)), &NumberOfBytesWritten, 0); if (!NumberOfBytesWritten) // write shortfall return false; writetextbuf += NumberOfBytesWritten; NumberOfBytesToWrite -= NumberOfBytesWritten; } // count non-translated characters written = (nextblock - buf); } if (n == written) return true; else if (written > 0 && written < n) { n -= written; buf += written; } else return false; } } // Wrapper for lseek or the like. streamoff _Filebuf_base::_M_seek(streamoff offset, ios_base::seekdir dir) { streamoff result = -1; int whence; switch(dir) { case ios_base::beg: if (offset < 0 /* || offset > _M_file_size() */ ) return streamoff(-1); whence = FILE_BEGIN; break; case ios_base::cur: whence = FILE_CURRENT; break; case ios_base::end: if (/* offset > 0 || */ -offset > _M_file_size() ) return streamoff(-1); whence = FILE_END; break; default: return streamoff(-1); } LARGE_INTEGER li; li.QuadPart = offset; li.LowPart = SetFilePointer(_M_file_id, li.LowPart, &li.HighPart, whence); if (li.LowPart != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR) result = li.QuadPart; return result; } // Attempts to memory-map len bytes of the current file, starting // at position offset. Precondition: offset is a multiple of the // page size. Postcondition: return value is a null pointer if the // memory mapping failed. Otherwise the return value is a pointer to // the memory-mapped file and the file position is set to offset. void* _Filebuf_base::_M_mmap(streamoff offset, streamoff len) { void* base; _M_view_id = CreateFileMapping(_M_file_id, (PSECURITY_ATTRIBUTES)0 , PAGE_READONLY, 0 /* len >> 32 */ , 0 /* len & 0xFFFFFFFF */ , // low-order DWORD of size 0); if (_M_view_id) { #if 0 /* printf("view %x created from file %x, error = %d, size = %d, map_offset = %d map_len = %d\n", _M_view_id, _M_file_id, GetLastError(), (int)cur_filesize, ULL(offset) & 0xffffffff, len); */ #endif LARGE_INTEGER li; li.QuadPart = offset; base = MapViewOfFile(_M_view_id, FILE_MAP_READ, li.HighPart, li.LowPart, #if !defined (__DMC__) __STATIC_CAST(SIZE_T, len)); #else __STATIC_CAST(DWORD, len)); #endif // check if mapping succeded and is usable if (base == 0 || _M_seek(offset + len, ios_base::beg) < 0) { this->_M_unmap(base, len); base = 0; } } else base = 0; return base; } void _Filebuf_base::_M_unmap(void* base, streamoff len) { // precondition : there is a valid mapping at the moment if (base != NULL) UnmapViewOfFile(base); // destroy view handle as well if (_M_view_id != NULL) CloseHandle(_M_view_id); _M_view_id = NULL; (void)len; //unused variable } _STLP_END_NAMESPACE