1 /* 2 * Copyright (c) 1999 3 * Silicon Graphics Computer Systems, Inc. 4 * 5 * Copyright (c) 1999 6 * Boris Fomitchev 7 * 8 * This material is provided "as is", with absolutely no warranty expressed 9 * or implied. Any use is at your own risk. 10 * 11 * Permission to use or copy this software for any purpose is hereby granted 12 * without fee, provided the above notices are retained on all copies. 13 * Permission to modify the code and to distribute modified code is granted, 14 * provided the above notices are retained, and a notice that the code was 15 * modified is included with the above copyright notice. 16 * 17 */ 18 19 #include <fstream> 20 21 #if !defined (_STLP_WCE) 22 # ifdef __BORLANDC__ 23 # include <cfcntl.h> // For _O_RDONLY, etc 24 # else 25 # include <io.h> // For _get_osfhandle 26 # include <fcntl.h> // For _O_RDONLY, etc 27 # endif 28 # include <sys/stat.h> // For _fstat 29 #endif 30 31 #define _TEXTBUF_SIZE 0x1000 32 33 const _STLP_fd INVALID_STLP_FD = INVALID_HANDLE_VALUE; 34 35 #if !defined (INVALID_SET_FILE_POINTER) 36 # define INVALID_SET_FILE_POINTER 0xffffffff 37 #endif 38 39 #ifndef O_ACCMODE 40 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 41 #endif 42 43 _STLP_BEGIN_NAMESPACE 44 45 #if !defined(__MSL__) && !defined(_STLP_WCE) 46 static ios_base::openmode flag_to_openmode(int mode) { 47 ios_base::openmode ret = ios_base::__default_mode; 48 49 switch (mode & O_ACCMODE) { 50 case O_RDONLY: 51 ret = ios_base::in; break; 52 case O_WRONLY: 53 ret = ios_base::out; break; 54 case O_RDWR: 55 ret = ios_base::in | ios_base::out; break; 56 } 57 58 if (mode & O_APPEND) 59 ret |= ios_base::app; 60 61 if (mode & O_BINARY) 62 ret |= ios_base::binary; 63 64 return ret; 65 } 66 #endif 67 68 _STLP_MOVE_TO_PRIV_NAMESPACE 69 70 // Helper functions for _Filebuf_base. 71 72 static bool __is_regular_file(_STLP_fd fd) { 73 BY_HANDLE_FILE_INFORMATION info; 74 75 // Return true if the file handle isn't a directory. 76 return GetFileInformationByHandle(fd, &info) && 77 ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0); 78 } 79 80 // Number of characters in the file. 81 static streamoff __file_size(_STLP_fd fd) { 82 streamoff ret = 0; 83 84 LARGE_INTEGER li; 85 li.LowPart = GetFileSize(fd, (unsigned long*) &li.HighPart); 86 if (li.LowPart != INVALID_FILE_SIZE || GetLastError() == NO_ERROR) 87 ret = li.QuadPart; 88 89 return ret; 90 } 91 92 _STLP_MOVE_TO_STD_NAMESPACE 93 94 // Visual C++ and Intel use this, but not Metrowerks 95 // Also MinGW, msvcrt.dll (but not crtdll.dll) dependent version 96 #if (defined (_STLP_MSVC_LIB) && !defined (_STLP_WCE)) || \ 97 (defined (__MINGW32__) && defined (__MSVCRT__)) 98 99 // fcntl(fileno, F_GETFL) for Microsoft library 100 // 'semi-documented' defines: 101 # define IOINFO_L2E 5 102 # define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) 103 # define _pioinfo(i) ( __pioinfo[(i) >> IOINFO_L2E] + \ 104 ((i) & (IOINFO_ARRAY_ELTS - 1)) ) 105 # define FAPPEND 0x20 // O_APPEND flag 106 # define FTEXT 0x80 // O_TEXT flag 107 // end of 'semi-documented' defines 108 109 // 'semi-documented' internal structure 110 extern "C" { 111 struct ioinfo { 112 long osfhnd; // the real os HANDLE 113 char osfile; // file handle flags 114 char pipech; // pipe buffer 115 # if defined (_MT) 116 // multi-threaded locking 117 int lockinitflag; 118 CRITICAL_SECTION lock; 119 # endif 120 }; 121 # if defined (__MINGW32__) 122 __MINGW_IMPORT ioinfo * __pioinfo[]; 123 # else 124 extern _CRTIMP ioinfo * __pioinfo[]; 125 # endif 126 } // extern "C" 127 // end of 'semi-documented' declarations 128 129 static ios_base::openmode _get_osfflags(int fd, HANDLE oshandle) { 130 char dosflags = 0; 131 if (fd >= 0) 132 dosflags = _pioinfo(fd)->osfile; 133 //else 134 //the file will be considered as open in binary mode with no append attribute 135 // end of 'semi-documented' stuff 136 137 int mode = 0; 138 if (dosflags & FAPPEND) 139 mode |= O_APPEND; 140 141 if (dosflags & FTEXT) 142 mode |= O_TEXT; 143 else 144 mode |= O_BINARY; 145 146 // For Read/Write access we have to guess 147 DWORD dummy, dummy2; 148 BOOL writeOk = WriteFile(oshandle, &dummy2, 0, &dummy, 0); 149 BOOL readOk = ReadFile(oshandle, &dummy2, 0, &dummy, NULL); 150 if (writeOk && readOk) 151 mode |= O_RDWR; 152 else if (readOk) 153 mode |= O_RDONLY; 154 else 155 mode |= O_WRONLY; 156 157 return flag_to_openmode(mode); 158 } 159 160 #elif defined (__DMC__) 161 162 # define FHND_APPEND 0x04 163 # define FHND_DEVICE 0x08 164 # define FHND_TEXT 0x10 165 166 extern "C" unsigned char __fhnd_info[_NFILE]; 167 168 static ios_base::openmode _get_osfflags(int fd, HANDLE oshandle) { 169 int mode = 0; 170 171 if (__fhnd_info[fd] & FHND_APPEND) 172 mode |= O_APPEND; 173 174 if (__fhnd_info[fd] & FHND_TEXT == 0) 175 mode |= O_BINARY; 176 177 for (FILE *fp = &_iob[0]; fp < &_iob[_NFILE]; fp++) { 178 if ((fileno(fp) == fd) && (fp->_flag & (_IOREAD | _IOWRT | _IORW))) { 179 const int osflags = fp->_flag; 180 181 if ((osflags & _IOREAD) && !(osflags & _IOWRT) && !(osflags & _IORW)) 182 mode |= O_RDONLY; 183 else if ((osflags & _IOWRT) && !(osflags & _IOREAD) && !(osflags & _IORW)) 184 mode |= O_WRONLY; 185 else 186 mode |= O_RDWR; 187 break; 188 } 189 } 190 191 return flag_to_openmode(mode); 192 } 193 #endif 194 195 size_t _Filebuf_base::_M_page_size = 4096; 196 197 _Filebuf_base::_Filebuf_base() 198 : _M_file_id(INVALID_STLP_FD), 199 _M_openmode(0), 200 _M_is_open(false), 201 _M_should_close(false), 202 _M_view_id(0) 203 {} 204 205 void _Filebuf_base::_S_initialize() { 206 SYSTEM_INFO SystemInfo; 207 GetSystemInfo(&SystemInfo); 208 _M_page_size = SystemInfo.dwPageSize; 209 // might be .dwAllocationGranularity 210 } 211 212 // Return the size of the file. This is a wrapper for stat. 213 // Returns zero if the size cannot be determined or is ill-defined. 214 streamoff _Filebuf_base::_M_file_size() { 215 return _STLP_PRIV __file_size(_M_file_id); 216 } 217 218 bool _Filebuf_base::_M_open(const char* name, ios_base::openmode openmode, 219 long permission) { 220 _STLP_fd file_no; 221 222 if (_M_is_open) 223 return false; 224 225 DWORD dwDesiredAccess, dwCreationDisposition; 226 bool doTruncate = false; 227 228 switch (openmode & (~ios_base::ate & ~ios_base::binary)) { 229 case ios_base::out: 230 case ios_base::out | ios_base::trunc: 231 dwDesiredAccess = GENERIC_WRITE; 232 dwCreationDisposition = OPEN_ALWAYS; 233 // boris : even though it is very non-intuitive, standard 234 // requires them both to behave same. 235 doTruncate = true; 236 break; 237 case ios_base::out | ios_base::app: 238 dwDesiredAccess = GENERIC_WRITE; 239 dwCreationDisposition = OPEN_ALWAYS; 240 break; 241 case ios_base::in: 242 dwDesiredAccess = GENERIC_READ; 243 dwCreationDisposition = OPEN_EXISTING; 244 permission = 0; // Irrelevant unless we're writing. 245 break; 246 case ios_base::in | ios_base::out: 247 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; 248 dwCreationDisposition = OPEN_EXISTING; 249 break; 250 case ios_base::in | ios_base::out | ios_base::trunc: 251 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; 252 dwCreationDisposition = OPEN_ALWAYS; 253 doTruncate = true; 254 break; 255 default: // The above are the only combinations of 256 return false; // flags allowed by the C++ standard. 257 } 258 259 DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; 260 261 #if defined(_STLP_USE_WIDE_INTERFACE) 262 file_no = CreateFile (_STLP_PRIV __ASCIIToWide(name).c_str(), 263 #else 264 file_no = CreateFileA(name, 265 #endif 266 dwDesiredAccess, dwShareMode, 0, 267 dwCreationDisposition, permission, 0); 268 269 if (file_no == INVALID_STLP_FD) 270 return false; 271 272 if ( 273 #if !defined (_STLP_WCE) 274 GetFileType(file_no) == FILE_TYPE_DISK && 275 #endif 276 ((doTruncate && SetEndOfFile(file_no) == 0) || 277 (((openmode & ios_base::ate) != 0) && 278 (SetFilePointer(file_no, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)))) { 279 CloseHandle(file_no); 280 return false; 281 } 282 283 _M_is_open = true; 284 _M_file_id = file_no; 285 _M_should_close = _M_is_open; 286 _M_openmode = openmode; 287 288 if (_M_is_open) 289 _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id); 290 291 return (_M_is_open != 0); 292 } 293 294 bool _Filebuf_base::_M_open(const char* name, ios_base::openmode openmode) { 295 // This doesn't really grant everyone in the world read/write 296 // access. On Unix, file-creation system calls always clear 297 // bits that are set in the umask from the permissions flag. 298 return this->_M_open(name, openmode, FILE_ATTRIBUTE_NORMAL); 299 } 300 301 bool _Filebuf_base::_M_open(_STLP_fd __id, ios_base::openmode init_mode) { 302 #if (defined (_STLP_MSVC_LIB) && !defined (_STLP_WCE)) || \ 303 (defined (__MINGW32__) && defined (__MSVCRT__)) || defined (__DMC__) 304 305 if (_M_is_open || __id == INVALID_STLP_FD) 306 return false; 307 308 if (init_mode != ios_base::__default_mode) 309 _M_openmode = init_mode; 310 else 311 _M_openmode = _get_osfflags(-1, __id); 312 313 _M_is_open = true; 314 _M_file_id = __id; 315 _M_should_close = false; 316 _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id); 317 318 return true; 319 #else 320 (void)__id; 321 (void)init_mode; // dwa 4/27/00 - suppress unused parameter warning 322 323 // not available for the API 324 return false; 325 326 #endif 327 } 328 329 // Associated the filebuf with a file descriptor pointing to an already- 330 // open file. Mode is set to be consistent with the way that the file 331 // was opened. 332 bool _Filebuf_base::_M_open(int file_no, ios_base::openmode init_mode) { 333 if (_M_is_open || file_no < 0) 334 return false; 335 336 #if (defined (_STLP_MSVC_LIB) && !defined (_STLP_WCE)) || \ 337 (defined (__MINGW32__) && defined (__MSVCRT__)) || defined (__DMC__) 338 339 HANDLE oshandle = (HANDLE)_get_osfhandle(file_no); 340 if (oshandle == INVALID_STLP_FD) 341 return false; 342 343 if (init_mode != ios_base::__default_mode) 344 _M_openmode = init_mode; 345 else 346 _M_openmode = _get_osfflags(file_no, oshandle); 347 348 _M_file_id = oshandle; 349 _M_is_open = true; 350 _M_should_close = false; 351 _M_regular_file = _STLP_PRIV __is_regular_file(_M_file_id); 352 return true; 353 #else 354 _STLP_MARK_PARAMETER_AS_UNUSED(&init_mode) 355 // not available for the API 356 return false; 357 #endif 358 } 359 360 bool _Filebuf_base::_M_close() { 361 if (!_M_is_open) 362 return false; 363 364 bool ok; 365 366 if (!_M_should_close) 367 ok = true; 368 else { 369 if (_M_file_id != INVALID_STLP_FD) { 370 ok = (CloseHandle(_M_file_id) != 0); 371 } 372 else { 373 ok = false; 374 } 375 } 376 377 _M_is_open = _M_should_close = false; 378 _M_openmode = 0; 379 return ok; 380 } 381 382 383 #define _STLP_LF 10 384 #define _STLP_CR 13 385 #define _STLP_CTRLZ 26 386 387 // Read up to n characters into a buffer. Return value is number of 388 // characters read. 389 ptrdiff_t _Filebuf_base::_M_read(char* buf, ptrdiff_t n) { 390 ptrdiff_t readen = 0; 391 //Here cast to size_t is safe as n cannot be negative. 392 size_t chunkSize = (min)(size_t(0xffffffff), __STATIC_CAST(size_t, n)); 393 // The following, while validating that we are still able to extract chunkSize 394 // charaters to the buffer, avoids extraction of too small chunk of datas 395 // which would be counter performant. 396 while (__STATIC_CAST(size_t, (n - readen)) >= chunkSize) { 397 DWORD numberOfBytesRead; 398 ReadFile(_M_file_id, buf + readen, __STATIC_CAST(DWORD, chunkSize), &numberOfBytesRead, 0); 399 400 if (numberOfBytesRead == 0) 401 break; 402 403 if (!(_M_openmode & ios_base::binary)) { 404 // translate CR-LFs to LFs in the buffer 405 char *to = buf + readen; 406 char *from = to; 407 char *last = from + numberOfBytesRead - 1; 408 for (; from <= last && *from != _STLP_CTRLZ; ++from) { 409 if (*from != _STLP_CR) 410 *to++ = *from; 411 else { // found CR 412 if (from < last) { // not at buffer end 413 if (*(from + 1) != _STLP_LF) 414 *to++ = _STLP_CR; 415 } 416 else { // last char is CR, peek for LF 417 char peek = ' '; 418 DWORD NumberOfBytesPeeked; 419 ReadFile(_M_file_id, (LPVOID)&peek, 1, &NumberOfBytesPeeked, 0); 420 if (NumberOfBytesPeeked != 0) { 421 if (peek != _STLP_LF) { //not a <CR><LF> combination 422 *to++ = _STLP_CR; 423 if ((to < buf + n) && (peek != _STLP_CR)) 424 //We have enough place to store peek and it is no a special 425 //_STLP_CR character, we can store it. 426 *to++ = peek; 427 else 428 SetFilePointer(_M_file_id, (LONG)-1, 0, FILE_CURRENT); 429 } 430 else { 431 // A <CR><LF> combination, we keep the <LF>: 432 *to++ = _STLP_LF; 433 } 434 } 435 else { 436 /* This case is tedious, we could 437 * - put peek back in the file but this would then generate an infinite loop 438 * - report an error as we don't know if in a future call to ReadFile we won't then 439 * get a <LF>. Doing so would make all files with a <CR> last an invalid file 440 * for STLport, a hard solution for STLport clients. 441 * - store the <CR> in the returned buffer, the chosen solution, even if in this 442 * case we could miss a <CR><LF> combination. 443 */ 444 *to++ = _STLP_CR; 445 } 446 } 447 } // found CR 448 } // for 449 readen = to - buf; 450 // seek back to TEXT end of file if hit CTRL-Z 451 if (from <= last) { // terminated due to CTRLZ 452 SetFilePointer(_M_file_id, -(LONG)((last + 1) - from), 0, FILE_CURRENT); 453 break; 454 } 455 } 456 else 457 readen += numberOfBytesRead; 458 } 459 return readen; 460 } 461 462 // Write n characters from a buffer. Return value: true if we managed 463 // to write the entire buffer, false if we didn't. 464 bool _Filebuf_base::_M_write(char* buf, ptrdiff_t n) { 465 for (;;) { 466 ptrdiff_t written; 467 468 //In the following implementation we are going to cast most of the ptrdiff_t 469 //values in size_t to work with coherent unsigned values. Doing so make code 470 //more simple especially in the min function call. 471 472 // In append mode, every write does an implicit seek to the end 473 // of the file. 474 if (_M_openmode & ios_base::app) 475 _M_seek(0, ios_base::end); 476 477 if (_M_openmode & ios_base::binary) { 478 // binary mode 479 size_t bytes_to_write = (size_t)n; 480 DWORD NumberOfBytesWritten; 481 written = 0; 482 for (; bytes_to_write != 0;) { 483 WriteFile(_M_file_id, buf + written, 484 __STATIC_CAST(DWORD, (min)(size_t(0xffffffff), bytes_to_write)), 485 &NumberOfBytesWritten, 0); 486 if (NumberOfBytesWritten == 0) 487 return false; 488 bytes_to_write -= NumberOfBytesWritten; 489 written += NumberOfBytesWritten; 490 } 491 } 492 else { 493 char textbuf[_TEXTBUF_SIZE + 1]; // extra 1 in case LF at end 494 char * nextblock = buf, * ptrtextbuf = textbuf; 495 char * endtextbuf = textbuf + _TEXTBUF_SIZE; 496 char * endblock = buf + n; 497 ptrdiff_t nextblocksize = (min) (n, (ptrdiff_t)_TEXTBUF_SIZE); 498 char * nextlf; 499 500 while ( (nextblocksize > 0) && 501 (nextlf = (char *)memchr(nextblock, _STLP_LF, nextblocksize)) != 0) { 502 ptrdiff_t linelength = nextlf - nextblock; 503 memcpy(ptrtextbuf, nextblock, linelength); 504 ptrtextbuf += linelength; 505 nextblock += (linelength + 1); 506 * ptrtextbuf ++ = _STLP_CR; 507 * ptrtextbuf ++ = _STLP_LF; 508 nextblocksize = (min) (ptrdiff_t(endblock - nextblock), 509 (max) (ptrdiff_t(0), ptrdiff_t(endtextbuf - ptrtextbuf))); 510 } 511 // write out what's left, > condition is here since for LF at the end , 512 // endtextbuf may get < ptrtextbuf ... 513 if (nextblocksize > 0) { 514 memcpy(ptrtextbuf, nextblock, nextblocksize); 515 ptrtextbuf += nextblocksize; 516 nextblock += nextblocksize; 517 } 518 // now write out the translated buffer 519 char * writetextbuf = textbuf; 520 for (size_t NumberOfBytesToWrite = (size_t)(ptrtextbuf - textbuf); 521 NumberOfBytesToWrite;) { 522 DWORD NumberOfBytesWritten; 523 WriteFile((HANDLE)_M_file_id, writetextbuf, 524 __STATIC_CAST(DWORD, (min)(size_t(0xffffffff), NumberOfBytesToWrite)), 525 &NumberOfBytesWritten, 0); 526 if (!NumberOfBytesWritten) // write shortfall 527 return false; 528 writetextbuf += NumberOfBytesWritten; 529 NumberOfBytesToWrite -= NumberOfBytesWritten; 530 } 531 // count non-translated characters 532 written = (nextblock - buf); 533 } 534 535 if (n == written) 536 return true; 537 else if (written > 0 && written < n) { 538 n -= written; 539 buf += written; 540 } 541 else 542 return false; 543 } 544 } 545 546 // Wrapper for lseek or the like. 547 streamoff _Filebuf_base::_M_seek(streamoff offset, ios_base::seekdir dir) { 548 streamoff result = -1; 549 int whence; 550 551 switch(dir) { 552 case ios_base::beg: 553 if (offset < 0 /* || offset > _M_file_size() */ ) 554 return streamoff(-1); 555 whence = FILE_BEGIN; 556 break; 557 case ios_base::cur: 558 whence = FILE_CURRENT; 559 break; 560 case ios_base::end: 561 if (/* offset > 0 || */ -offset > _M_file_size() ) 562 return streamoff(-1); 563 whence = FILE_END; 564 break; 565 default: 566 return streamoff(-1); 567 } 568 569 LARGE_INTEGER li; 570 li.QuadPart = offset; 571 li.LowPart = SetFilePointer(_M_file_id, li.LowPart, &li.HighPart, whence); 572 if (li.LowPart != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR) 573 result = li.QuadPart; 574 575 return result; 576 } 577 578 579 // Attempts to memory-map len bytes of the current file, starting 580 // at position offset. Precondition: offset is a multiple of the 581 // page size. Postcondition: return value is a null pointer if the 582 // memory mapping failed. Otherwise the return value is a pointer to 583 // the memory-mapped file and the file position is set to offset. 584 void* _Filebuf_base::_M_mmap(streamoff offset, streamoff len) { 585 void* base; 586 _M_view_id = CreateFileMapping(_M_file_id, (PSECURITY_ATTRIBUTES)0 , 587 PAGE_READONLY, 0 /* len >> 32 */ , 588 0 /* len & 0xFFFFFFFF */ , // low-order DWORD of size 589 0); 590 591 if (_M_view_id) { 592 #if 0 593 /* 594 printf("view %x created from file %x, error = %d, size = %d, map_offset = %d map_len = %d\n", 595 _M_view_id, _M_file_id, GetLastError(), 596 (int)cur_filesize, ULL(offset) & 0xffffffff, len); 597 */ 598 #endif 599 LARGE_INTEGER li; 600 li.QuadPart = offset; 601 base = MapViewOfFile(_M_view_id, FILE_MAP_READ, li.HighPart, li.LowPart, 602 #if !defined (__DMC__) 603 __STATIC_CAST(SIZE_T, len)); 604 #else 605 __STATIC_CAST(DWORD, len)); 606 #endif 607 // check if mapping succeded and is usable 608 if (base == 0 || _M_seek(offset + len, ios_base::beg) < 0) { 609 this->_M_unmap(base, len); 610 base = 0; 611 } 612 } else 613 base = 0; 614 615 return base; 616 } 617 618 void _Filebuf_base::_M_unmap(void* base, streamoff len) { 619 // precondition : there is a valid mapping at the moment 620 if (base != NULL) 621 UnmapViewOfFile(base); 622 // destroy view handle as well 623 if (_M_view_id != NULL) 624 CloseHandle(_M_view_id); 625 _M_view_id = NULL; 626 (void)len; //unused variable 627 } 628 629 _STLP_END_NAMESPACE 630