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