home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Software Money Savers / VirtualDub / Source / VirtualDub-1.7.7-src.7z / src / system / source / file.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-10-13  |  18.6 KB  |  766 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    System library component
  3. //    Copyright (C) 1998-2004 Avery Lee, All Rights Reserved.
  4. //
  5. //    Beginning with 1.6.0, the VirtualDub system library is licensed
  6. //    differently than the remainder of VirtualDub.  This particular file is
  7. //    thus licensed as follows (the "zlib" license):
  8. //
  9. //    This software is provided 'as-is', without any express or implied
  10. //    warranty.  In no event will the authors be held liable for any
  11. //    damages arising from the use of this software.
  12. //
  13. //    Permission is granted to anyone to use this software for any purpose,
  14. //    including commercial applications, and to alter it and redistribute it
  15. //    freely, subject to the following restrictions:
  16. //
  17. //    1.    The origin of this software must not be misrepresented; you must
  18. //        not claim that you wrote the original software. If you use this
  19. //        software in a product, an acknowledgment in the product
  20. //        documentation would be appreciated but is not required.
  21. //    2.    Altered source versions must be plainly marked as such, and must
  22. //        not be misrepresented as being the original software.
  23. //    3.    This notice may not be removed or altered from any source
  24. //        distribution.
  25.  
  26. #include "stdafx.h"
  27. #include <windows.h>
  28.  
  29. #include <vd2/system/error.h>
  30. #include <vd2/system/filesys.h>
  31. #include <vd2/system/VDString.h>
  32. #include <vd2/system/file.h>
  33.  
  34. namespace {
  35.     bool IsWindowsNT() {
  36.         static bool sbIsNT = (LONG)GetVersion()>=0;
  37.         return sbIsNT;
  38.     }
  39.  
  40.     bool IsHardDrivePath(const wchar_t *path) {
  41.         const VDStringW rootPath(VDFileGetRootPath(path));
  42.  
  43.         UINT type = GetDriveTypeW(rootPath.c_str());
  44.  
  45.         return type == DRIVE_FIXED || type == DRIVE_UNKNOWN || type == DRIVE_REMOVABLE;
  46.     }
  47. };
  48.  
  49. ///////////////////////////////////////////////////////////////////////////////
  50. //
  51. //    VDFile
  52. //
  53. ///////////////////////////////////////////////////////////////////////////////
  54.  
  55. using namespace nsVDFile;
  56.  
  57. VDFile::VDFile(const char *pszFileName, uint32 flags)
  58.     : mhFile(NULL)
  59. {
  60.     open_internal(pszFileName, NULL, flags);
  61. }
  62.  
  63. VDFile::VDFile(const wchar_t *pwszFileName, uint32 flags)
  64.     : mhFile(NULL)
  65. {
  66.     open_internal(NULL, pwszFileName, flags);
  67. }
  68.  
  69. VDFile::VDFile(HANDLE h)
  70.     : mhFile(h)
  71. {
  72.     LONG lo, hi = 0;
  73.  
  74.     lo = SetFilePointer(h, 0, &hi, FILE_CURRENT);
  75.  
  76.     mFilePosition = (uint32)lo + ((uint64)(uint32)hi << 32);
  77. }
  78.  
  79. VDFile::~VDFile() {
  80.     closeNT();
  81. }
  82.  
  83. void VDFile::open(const char *pszFilename, uint32 flags) {
  84.     open_internal(pszFilename, NULL, flags);
  85. }
  86.  
  87. void VDFile::open(const wchar_t *pwszFilename, uint32 flags) {
  88.     open_internal(NULL, pwszFilename, flags);
  89. }
  90.  
  91. void VDFile::open_internal(const char *pszFilename, const wchar_t *pwszFilename, uint32 flags) {
  92.     close();
  93.  
  94.     mpFilename = _wcsdup(VDFileSplitPath(pszFilename ? VDTextAToW(pszFilename).c_str() : pwszFilename));
  95.     if (!mpFilename)
  96.         throw MyMemoryError();
  97.  
  98.     // At least one of the read/write flags must be set.
  99.     VDASSERT(flags & (kRead | kWrite));
  100.  
  101.     DWORD dwDesiredAccess = 0;
  102.  
  103.     if (flags & kRead)  dwDesiredAccess  = GENERIC_READ;
  104.     if (flags & kWrite) dwDesiredAccess |= GENERIC_WRITE;
  105.  
  106.     // Win32 docs are screwed here -- FILE_SHARE_xxx is the inverse of a deny flag.
  107.  
  108.     DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
  109.     if (flags & kDenyRead)    dwShareMode = FILE_SHARE_WRITE;
  110.     if (flags & kDenyWrite) dwShareMode &= ~FILE_SHARE_WRITE;
  111.  
  112.     // One of the creation flags must be set.
  113.     VDASSERT(flags & kCreationMask);
  114.  
  115.     DWORD dwCreationDisposition;
  116.  
  117.     uint32 creationType = flags & kCreationMask;
  118.  
  119.     switch(creationType) {
  120.     case kOpenExisting:        dwCreationDisposition = OPEN_EXISTING; break;
  121.     case kOpenAlways:        dwCreationDisposition = OPEN_ALWAYS; break;
  122.     case kCreateAlways:        dwCreationDisposition = CREATE_ALWAYS; break;
  123.     case kCreateNew:        dwCreationDisposition = CREATE_NEW; break;
  124.     case kTruncateExisting:    dwCreationDisposition = TRUNCATE_EXISTING; break;
  125.     default:
  126.         VDNEVERHERE;
  127.         return;
  128.     }
  129.  
  130.     VDASSERT((flags & (kSequential | kRandomAccess)) != (kSequential | kRandomAccess));
  131.  
  132.     DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL;
  133.  
  134.     if (flags & kSequential)    dwAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
  135.     if (flags & kRandomAccess)    dwAttributes |= FILE_FLAG_RANDOM_ACCESS;
  136.     if (flags & kWriteThrough)    dwAttributes |= FILE_FLAG_WRITE_THROUGH;
  137.     if (flags & kUnbuffered)    dwAttributes |= FILE_FLAG_NO_BUFFERING;
  138.  
  139.     VDStringA tempFilenameA;
  140.     VDStringW tempFilenameW;
  141.  
  142.     if (IsWindowsNT()) {
  143.         if (pszFilename) {
  144.             tempFilenameW = VDTextAToW(pszFilename);
  145.             pwszFilename = tempFilenameW.c_str();
  146.             pszFilename = NULL;
  147.         }
  148.     } else {
  149.         if (pwszFilename) {
  150.             tempFilenameA = VDTextWToA(pwszFilename);
  151.             pszFilename = tempFilenameA.c_str();
  152.             pwszFilename = NULL;
  153.         }
  154.     }
  155.  
  156.     if (pszFilename)
  157.         mhFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
  158.     else {
  159.         if (!IsHardDrivePath(pwszFilename))
  160.             flags &= ~FILE_FLAG_NO_BUFFERING;
  161.  
  162.         mhFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
  163.     }
  164.  
  165.     DWORD err = GetLastError();
  166.  
  167.     // If we failed and FILE_FLAG_NO_BUFFERING was set, strip it and try again.
  168.     // VPC and Novell shares sometimes do this....
  169.     if (mhFile == INVALID_HANDLE_VALUE && err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) {
  170.         if (dwAttributes & FILE_FLAG_NO_BUFFERING) {
  171.             dwAttributes &= ~FILE_FLAG_NO_BUFFERING;
  172.             dwAttributes |= FILE_FLAG_WRITE_THROUGH;
  173.  
  174.             if (pszFilename)
  175.                 mhFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
  176.             else
  177.                 mhFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwAttributes, NULL);
  178.  
  179.             err = GetLastError();
  180.         }
  181.     }
  182.  
  183.     // INVALID_HANDLE_VALUE isn't NULL.  *sigh*
  184.  
  185.     if (mhFile == INVALID_HANDLE_VALUE) {
  186.         mhFile = NULL;
  187.  
  188.         throw MyWin32Error("Cannot open file \"%ls\":\n%%s", err, mpFilename.get());
  189.     }
  190.  
  191.     mFilePosition = 0;
  192. }
  193.  
  194. bool VDFile::closeNT() {
  195.     if (mhFile) {
  196.         HANDLE h = mhFile;
  197.         mhFile = NULL;
  198.         if (!CloseHandle(h))
  199.             return false;
  200.     }
  201.  
  202.     return true;
  203. }
  204.  
  205. void VDFile::close() {
  206.     if (!closeNT())
  207.         throw MyWin32Error("Cannot complete file \"%ls\": %%s", GetLastError(), mpFilename.get());
  208. }
  209.  
  210. bool VDFile::truncateNT() {
  211.     return 0 != SetEndOfFile(mhFile);
  212. }
  213.  
  214. void VDFile::truncate() {
  215.     if (!truncateNT())
  216.         throw MyWin32Error("Cannot truncate file \"%ls\": %%s", GetLastError(), mpFilename.get());
  217. }
  218.  
  219. bool VDFile::extendValidNT(sint64 pos) {
  220.     if (GetVersion() & 0x80000000)
  221.         return true;                // No need, Windows 95/98/ME do this automatically anyway.
  222.  
  223.     // The SetFileValidData() API is only available on XP and Server 2003.
  224.  
  225.     typedef BOOL (APIENTRY *tpSetFileValidData)(HANDLE hFile, LONGLONG ValidDataLength);        // Windows XP, Server 2003
  226.     static tpSetFileValidData pSetFileValidData = (tpSetFileValidData)GetProcAddress(GetModuleHandle("kernel32"), "SetFileValidData");
  227.  
  228.     if (!pSetFileValidData) {
  229.         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  230.         return false;
  231.     }
  232.  
  233.     return 0 != pSetFileValidData(mhFile, pos);
  234. }
  235.  
  236. void VDFile::extendValid(sint64 pos) {
  237.     if (!extendValidNT(pos))
  238.         throw MyWin32Error("Cannot extend file \"%ls\": %%s", GetLastError(), mpFilename.get());
  239. }
  240.  
  241. bool VDFile::enableExtendValid() {
  242.     if (GetVersion() & 0x80000000)
  243.         return true;                // Not Windows NT, no privileges involved
  244.  
  245.     // SetFileValidData() requires the SE_MANAGE_VOLUME_NAME privilege, so we must enable it
  246.     // on the process token. We don't attempt to strip the privilege afterward as that would
  247.     // introduce race conditions.
  248.     bool bSuccessful = false;
  249.     DWORD err = 0;
  250.  
  251.     SetLastError(0);
  252.  
  253.     HANDLE h;
  254.     if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &h)) {
  255.         LUID luid;
  256.  
  257.         if (LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &luid)) {
  258.             TOKEN_PRIVILEGES tp;
  259.             tp.PrivilegeCount = 1;
  260.             tp.Privileges[0].Luid = luid;
  261.             tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  262.  
  263.             if (AdjustTokenPrivileges(h, FALSE, &tp, 0, NULL, NULL))
  264.                 bSuccessful = true;
  265.             else
  266.                 err = GetLastError();
  267.         }
  268.  
  269.         CloseHandle(h);
  270.     }
  271.  
  272.     if (!bSuccessful && err)
  273.         SetLastError(err);
  274.  
  275.     return bSuccessful;
  276. }
  277.  
  278. long VDFile::readData(void *buffer, long length) {
  279.     DWORD dwActual;
  280.  
  281.     if (!ReadFile(mhFile, buffer, (DWORD)length, &dwActual, NULL))
  282.         throw MyWin32Error("Cannot read from file \"%ls\": %%s", GetLastError(), mpFilename.get());
  283.  
  284.     mFilePosition += dwActual;
  285.  
  286.     return dwActual;
  287. }
  288.  
  289. void VDFile::read(void *buffer, long length) {
  290.     if (length != readData(buffer, length))
  291.         throw MyWin32Error("Cannot read from file \"%ls\": Premature end of file.", GetLastError(), mpFilename.get());
  292. }
  293.  
  294. long VDFile::writeData(const void *buffer, long length) {
  295.     DWORD dwActual;
  296.     bool success = false;
  297.  
  298.     if (!WriteFile(mhFile, buffer, (DWORD)length, &dwActual, NULL) || dwActual != (DWORD)length)
  299.         goto found_error;
  300.  
  301.     mFilePosition += dwActual;
  302.  
  303.     return dwActual;
  304.  
  305. found_error:
  306.     throw MyWin32Error("Cannot write to file \"%ls\": %%s", GetLastError(), mpFilename.get());
  307. }
  308.  
  309. void VDFile::write(const void *buffer, long length) {
  310.     if (length != writeData(buffer, length))
  311.         throw MyWin32Error("Cannot write to file \"%ls\": Unable to write all data.", GetLastError(), mpFilename.get());
  312. }
  313.  
  314. bool VDFile::seekNT(sint64 newPos, eSeekMode mode) {
  315.     DWORD dwMode;
  316.  
  317.     switch(mode) {
  318.     case kSeekStart:
  319.         dwMode = FILE_BEGIN;
  320.         break;
  321.     case kSeekCur:
  322.         dwMode = FILE_CURRENT;
  323.         break;
  324.     case kSeekEnd:
  325.         dwMode = FILE_END;
  326.         break;
  327.     default:
  328.         VDNEVERHERE;
  329.         return false;
  330.     }
  331.  
  332.     union {
  333.         sint64 pos;
  334.         LONG l[2];
  335.     } u = { newPos };
  336.  
  337.     u.l[0] = SetFilePointer(mhFile, u.l[0], &u.l[1], dwMode);
  338.  
  339.     if (u.l[0] == -1 && GetLastError() != NO_ERROR)
  340.         return false;
  341.  
  342.     mFilePosition = u.pos;
  343.     return true;
  344. }
  345.  
  346. void VDFile::seek(sint64 newPos, eSeekMode mode) {
  347.     if (!seekNT(newPos, mode))
  348.         throw MyWin32Error("Cannot seek within file \"%ls\": %%s", GetLastError(), mpFilename.get());
  349. }
  350.  
  351. bool VDFile::skipNT(sint64 delta) {
  352.     if (!delta)
  353.         return true;
  354.  
  355.     char buf[1024];
  356.  
  357.     if (delta <= sizeof buf) {
  358.         return (long)delta == readData(buf, (long)delta);
  359.     } else
  360.         return seekNT(delta, kSeekCur);
  361. }
  362.  
  363. void VDFile::skip(sint64 delta) {
  364.     if (!delta)
  365.         return;
  366.  
  367.     char buf[1024];
  368.  
  369.     if (delta <= sizeof buf) {
  370.         if ((long)delta != readData(buf, (long)delta))
  371.             throw MyWin32Error("Cannot seek within file \"%ls\": %%s", GetLastError(), mpFilename.get());
  372.     } else
  373.         seek(delta, kSeekCur);
  374. }
  375.  
  376. sint64 VDFile::size() {
  377.     union {
  378.         uint64 siz;
  379.         DWORD l[2];
  380.     } u;
  381.  
  382.     u.l[0] = GetFileSize(mhFile, &u.l[1]);
  383.  
  384.     DWORD err;
  385.  
  386.     if (u.l[0] == (DWORD)-1L && (err = GetLastError()) != NO_ERROR)
  387.         throw MyWin32Error("Cannot retrieve size of file \"%ls\": %%s", GetLastError(), mpFilename.get());
  388.  
  389.     return (sint64)u.siz;
  390. }
  391.  
  392. sint64 VDFile::tell() {
  393.     return mFilePosition;
  394. }
  395.  
  396. bool VDFile::isOpen() {
  397.     return mhFile != 0;
  398. }
  399.  
  400. VDFileHandle VDFile::getRawHandle() {
  401.     return mhFile;
  402. }
  403.  
  404. void *VDFile::AllocUnbuffer(size_t nBytes) {
  405.     return VirtualAlloc(NULL, nBytes, MEM_COMMIT, PAGE_READWRITE);
  406. }
  407.  
  408. void VDFile::FreeUnbuffer(void *p) {
  409.     VirtualFree(p, 0, MEM_RELEASE);
  410. }
  411.  
  412. ///////////////////////////////////////////////////////////////////////////////
  413.  
  414. VDFileStream::~VDFileStream() {
  415. }
  416.  
  417. const wchar_t *VDFileStream::GetNameForError() {
  418.     return getFilenameForError();
  419. }
  420.  
  421. sint64 VDFileStream::Pos() {
  422.     return tell();
  423. }
  424.  
  425. void VDFileStream::Read(void *buffer, sint32 bytes) {
  426.     read(buffer, bytes);
  427. }
  428.  
  429. sint32 VDFileStream::ReadData(void *buffer, sint32 bytes) {
  430.     return readData(buffer, bytes);
  431. }
  432.  
  433. sint64 VDFileStream::Length() {
  434.     return size();
  435. }
  436.  
  437. void VDFileStream::Seek(sint64 offset) {
  438.     seek(offset);
  439. }
  440.  
  441. ///////////////////////////////////////////////////////////////////////////////
  442.  
  443. VDMemoryStream::VDMemoryStream(const void *pSrc, uint32 len) 
  444.     : mpSrc((const char *)pSrc)
  445.     , mPos(0)
  446.     , mLength(len)
  447. {
  448. }
  449.  
  450. const wchar_t *VDMemoryStream::GetNameForError() {
  451.     return L"memory stream";
  452. }
  453.  
  454. sint64 VDMemoryStream::Pos() {
  455.     return mPos;
  456. }
  457.  
  458. void VDMemoryStream::Read(void *buffer, sint32 bytes) {
  459.     if (bytes != ReadData(buffer, bytes))
  460.         throw MyError("Attempt to read beyond stream.");
  461. }
  462.  
  463. sint32 VDMemoryStream::ReadData(void *buffer, sint32 bytes) {
  464.     if (bytes <= 0)
  465.         return 0;
  466.  
  467.     if (bytes + mPos > mLength)
  468.         bytes = mLength - mPos;
  469.  
  470.     if (bytes > 0) {
  471.         memcpy(buffer, mpSrc+mPos, bytes);
  472.         mPos += bytes;
  473.     }
  474.  
  475.     return bytes;
  476. }
  477.  
  478. sint64 VDMemoryStream::Length() {
  479.     return mLength;
  480. }
  481.  
  482. void VDMemoryStream::Seek(sint64 offset) {
  483.     if (offset < 0 || offset > mLength)
  484.         throw MyError("Invalid seek position");
  485.  
  486.     mPos = (uint32)offset;
  487. }
  488.  
  489. ///////////////////////////////////////////////////////////////////////////////
  490.  
  491. VDBufferedStream::VDBufferedStream(IVDRandomAccessStream *pSrc, uint32 bufferSize)
  492.     : mpSrc(pSrc)
  493.     , mBuffer(bufferSize)
  494.     , mBasePosition(0)
  495.     , mBufferOffset(0)
  496.     , mBufferValidSize(0)
  497. {
  498. }
  499.  
  500. VDBufferedStream::~VDBufferedStream() {
  501. }
  502.  
  503. const wchar_t *VDBufferedStream::GetNameForError() {
  504.     return mpSrc->GetNameForError();
  505. }
  506.  
  507. sint64 VDBufferedStream::Pos() {
  508.     return mBasePosition + mBufferOffset;
  509. }
  510.  
  511. void VDBufferedStream::Read(void *buffer, sint32 bytes) {
  512.     if (bytes != ReadData(buffer, bytes))
  513.         throw MyError("Cannot read %d bytes at location %08llx from %ls", mpSrc->GetNameForError());
  514. }
  515.  
  516. sint32 VDBufferedStream::ReadData(void *buffer, sint32 bytes) {
  517.     if (bytes <= 0)
  518.         return 0;
  519.  
  520.     uint32 actual = 0;
  521.     for(;;) {
  522.         uint32 tc = mBufferValidSize - mBufferOffset;
  523.  
  524.         if (tc > (uint32)bytes)
  525.             tc = (uint32)bytes;
  526.  
  527.         if (tc) {
  528.             if (buffer) {
  529.                 memcpy(buffer, mBuffer.data() + mBufferOffset, tc);
  530.                 buffer = (char *)buffer + tc;
  531.             }
  532.  
  533.             mBufferOffset += tc;
  534.             bytes -= tc;
  535.             actual += tc;
  536.  
  537.             if (!bytes)
  538.                 break;
  539.         }
  540.  
  541.         // At this point, the buffer is empty.
  542.         if (mBufferValidSize) {
  543.             VDASSERT(mBufferOffset >= mBufferValidSize);
  544.  
  545.             mBasePosition += mBufferValidSize;
  546.             mBufferOffset = 0;
  547.             mBufferValidSize = 0;
  548.         }
  549.  
  550.         // If the remaining read is large, issue it directly to the underlying stream.
  551.         if (buffer && (uint32)bytes >= mBuffer.size() * 2) {
  552.             sint32 localActual = mpSrc->ReadData(buffer, bytes);
  553.             mBasePosition += localActual;
  554.             actual += localActual;
  555.             break;
  556.         }
  557.  
  558.         // Refill the buffer.
  559.         mBufferValidSize = mpSrc->ReadData(mBuffer.data(), mBuffer.size());
  560.         mBufferOffset = 0;
  561.         if (!mBufferValidSize)
  562.             break;
  563.     }
  564.  
  565.     return actual;
  566. }
  567.  
  568. sint64 VDBufferedStream::Length() {
  569.     return mpSrc->Length();
  570. }
  571.  
  572. void VDBufferedStream::Seek(sint64 offset) {
  573.     // check if an in-buffer skip is possible
  574.     sint64 relativeOffset = offset - mBasePosition;
  575.     if (relativeOffset >= 0 && relativeOffset <= (sint64)mBufferValidSize) {
  576.         mBufferOffset = (uint32)relativeOffset;
  577.         return;
  578.     }
  579.  
  580.     // flush buffer
  581.     mBufferOffset = 0;
  582.     mBufferValidSize = 0;
  583.  
  584.     // issue seek
  585.     mpSrc->Seek(offset);
  586.     mBasePosition = offset;
  587. }
  588.  
  589. void VDBufferedStream::Skip(sint64 size) {
  590.     sint64 targetPos = mBasePosition + mBufferOffset + size;
  591.     sint64 bufferEnd = mBasePosition + mBufferValidSize;
  592.  
  593.     // check if we can do a buffered skip
  594.     if (targetPos >= bufferEnd && targetPos < bufferEnd + (sint64)mBuffer.size()) {
  595.         Read(NULL, (sint32)size);
  596.         return;
  597.     }
  598.  
  599.     // issue a seek
  600.     Seek(targetPos);
  601. }
  602.  
  603. ///////////////////////////////////////////////////////////////////////////////
  604.  
  605. VDTextStream::VDTextStream(IVDStream *pSrc)
  606.     : mpSrc(pSrc)
  607.     , mBufferPos(0)
  608.     , mBufferLimit(0)
  609.     , mState(kFetchLine)
  610.     , mFileBuffer(kFileBufferSize)
  611. {
  612. }
  613.  
  614. VDTextStream::~VDTextStream() {
  615. }
  616.  
  617. const char *VDTextStream::GetNextLine() {
  618.     if (!mpSrc)
  619.         return NULL;
  620.  
  621.     mLineBuffer.clear();
  622.  
  623.     for(;;) {
  624.         if (mBufferPos >= mBufferLimit) {
  625.             mBufferPos = 0;
  626.             mBufferLimit = mpSrc->ReadData(mFileBuffer.data(), mFileBuffer.size());
  627.  
  628.             if (!mBufferLimit) {
  629.                 mpSrc = NULL;
  630.  
  631.                 if (mLineBuffer.empty())
  632.                     return NULL;
  633.  
  634.                 mLineBuffer.push_back(0);
  635.  
  636.                 return mLineBuffer.data();
  637.             }
  638.         }
  639.  
  640.         switch(mState) {
  641.  
  642.             case kEatNextIfCR:
  643.                 mState = kFetchLine;
  644.                 if (mFileBuffer[mBufferPos] == '\r')
  645.                     ++mBufferPos;
  646.                 continue;
  647.  
  648.             case kEatNextIfLF:
  649.                 mState = kFetchLine;
  650.                 if (mFileBuffer[mBufferPos] == '\n')
  651.                     ++mBufferPos;
  652.                 continue;
  653.  
  654.             case kFetchLine:
  655.                 uint32 base = mBufferPos;
  656.  
  657.                 do {
  658.                     const char c = mFileBuffer[mBufferPos++];
  659.  
  660.                     if (c == '\r') {
  661.                         mState = kEatNextIfLF;
  662.                         mLineBuffer.insert(mLineBuffer.end(), mFileBuffer.begin() + base, mFileBuffer.begin() + (mBufferPos-1));
  663.                         mLineBuffer.push_back(0);
  664.                         return mLineBuffer.data();
  665.                     }
  666.                     if (c == '\n') {
  667.                         mState = kEatNextIfCR;
  668.                         mLineBuffer.insert(mLineBuffer.end(), mFileBuffer.begin() + base, mFileBuffer.begin() + (mBufferPos-1));
  669.                         mLineBuffer.push_back(0);
  670.                         return mLineBuffer.data();
  671.                     }
  672.                 } while(mBufferPos < mBufferLimit);
  673.                 mLineBuffer.insert(mLineBuffer.end(), mFileBuffer.begin() + base, mFileBuffer.begin() + mBufferLimit);
  674.                 break;
  675.         }
  676.     }
  677. }
  678.  
  679. ///////////////////////////////////////////////////////////////////////////////
  680.  
  681. VDTextInputFile::VDTextInputFile(const wchar_t *filename, uint32 flags)
  682.     : mFileStream(filename, flags | nsVDFile::kRead)
  683.     , mTextStream(&mFileStream)
  684. {
  685. }
  686.  
  687. VDTextInputFile::~VDTextInputFile() {
  688. }
  689.  
  690. ///////////////////////////////////////////////////////////////////////////////
  691.  
  692. VDTextOutputFile::VDTextOutputFile(const wchar_t *filename, uint32 flags)
  693.     : mFile(filename, flags | nsVDFile::kWrite)
  694.     , mLevel(0)
  695. {
  696. }
  697.  
  698. VDTextOutputFile::~VDTextOutputFile() {
  699.     try { 
  700.         Close();
  701.     } catch(const MyError&) {
  702.         // ignore errors in destructor
  703.     }
  704. }
  705.  
  706. void VDTextOutputFile::Close() {
  707.     if (mFile.isOpen()) {
  708.         if (mLevel)
  709.             mFile.write(mBuf, mLevel);
  710.         mFile.close();
  711.     }
  712. }
  713.  
  714. void VDTextOutputFile::PutLine(const char *s) {
  715.     PutData(s, strlen(s));
  716.     PutData("\r\n", 2);
  717. }
  718.  
  719. void VDTextOutputFile::FormatLine(const char *format, ...) {
  720.     va_list val;
  721.  
  722.     va_start(val, format);
  723.  
  724.     int rv = -1;
  725.     if (mLevel < kBufSize-4)
  726.         rv = _vsnprintf(mBuf+mLevel, kBufSize-mLevel, format, val);
  727.  
  728.     if (rv >= 0)
  729.         mLevel += rv;
  730.     else
  731.         FormatLine2(format, val);
  732.  
  733.     PutData("\r\n", 2);
  734.     va_end(val);
  735. }
  736.  
  737. void VDTextOutputFile::FormatLine2(const char *format, va_list val) {
  738.     char buf[3072];
  739.  
  740.     int rv = _vsnprintf(buf, 3072, format, val);
  741.     if (rv > 0)
  742.         PutData(buf, rv);
  743. }
  744.  
  745. void VDTextOutputFile::PutData(const char *s, int len) {
  746.     while(len > 0) {
  747.         int left = kBufSize - mLevel;
  748.         if (!left) {
  749.             mFile.write(mBuf, kBufSize);
  750.             mLevel = 0;
  751.             left = kBufSize;
  752.         }
  753.  
  754.         int tc = len;
  755.  
  756.         if (tc > left)
  757.             tc = left;
  758.  
  759.         memcpy(mBuf + mLevel, s, tc);
  760.  
  761.         s += tc;
  762.         len -= tc;
  763.         mLevel += tc;
  764.     }
  765. }
  766.