fixed: LINUX build errors
[xbmc:xbmc.git] / xbmc / FileSystem / File.cpp
1 /*
2 * XBMC Media Center
3 * Copyright (c) 2002 Frodo
4 * Portions Copyright (c) by the authors of ffmpeg and xvid
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "File.h"
22 #include "FileFactory.h"
23 #include "Application.h"
24 #include "Util.h"
25 #include "DirectoryCache.h"
26 #include "FileCache.h"
27 #include "FileItem.h"
28 #include "utils/log.h"
29
30 #ifndef _LINUX
31 #include "utils/Win32Exception.h"
32 #endif
33 #include "URL.h"
34
35 using namespace XFILE;
36 using namespace DIRECTORY;
37 using namespace std;
38
39 //////////////////////////////////////////////////////////////////////
40 // Construction/Destruction
41 //////////////////////////////////////////////////////////////////////
42 #ifndef __GNUC__
43 #pragma warning (disable:4244)
44 #endif
45
46 class CAsyncFileCallback
47   : public CThread
48 {
49 public:
50   ~CAsyncFileCallback()
51   {
52     StopThread();
53   }
54
55   CAsyncFileCallback(IFileCallback* callback, void* context)
56   {
57     m_callback = callback;
58     m_context = context;
59     m_percent = 0;
60     m_speed = 0.0f;
61     m_cancel = false;
62     Create();
63   }
64
65   virtual void Process()
66   {
67     while(!m_bStop)
68     {
69       m_event.WaitMSec(1000/30);
70       if (m_callback)
71         if(!m_callback->OnFileCallback(m_context, m_percent, m_speed))
72           if(!m_cancel)
73             m_cancel = true;
74     }
75   }
76
77   void SetStatus(int percent, float speed)
78   {
79     m_percent = percent;
80     m_speed = speed;
81     m_event.Set();
82   }
83
84   bool IsCanceled()
85   {
86     return m_cancel;
87   }
88
89 private:
90   IFileCallback* m_callback;
91   void* m_context;
92   int   m_percent;
93   float m_speed;
94   CEvent m_event;
95   bool m_cancel;
96 };
97
98
99 //*********************************************************************************************
100 CFile::CFile()
101 {
102   m_pFile = NULL;
103   m_pBuffer = NULL;
104   m_flags = 0;
105 }
106
107 //*********************************************************************************************
108 CFile::~CFile()
109 {
110   if (m_pFile)
111     SAFE_DELETE(m_pFile);
112   if (m_pBuffer)
113     SAFE_DELETE(m_pBuffer);
114 }
115
116 //*********************************************************************************************
117
118 class CAutoBuffer
119 {
120   char* p;
121 public:
122   explicit CAutoBuffer(size_t s) { p = (char*)malloc(s); }
123   ~CAutoBuffer() { free(p); }
124 char* get() { return p; }
125 };
126
127 bool CFile::Cache(const CStdString& strFileName, const CStdString& strDest, XFILE::IFileCallback* pCallback, void* pContext)
128 {
129   CFile file;
130   CAsyncFileCallback* helper = NULL;
131
132   if (file.Open(strFileName, READ_TRUNCATED))
133   {
134     if (file.GetLength() <= 0)
135     {
136       CLog::Log(LOGWARNING, "FILE::cache: the file %s has a length of 0 bytes", strFileName.c_str());
137       file.Close();
138       // no need to return false here.  Technically, we should create the new file and leave it at that
139 //      return false;
140     }
141
142     CFile newFile;
143     if (CUtil::IsHD(strDest)) // create possible missing dirs
144     {
145       vector<CStdString> tokens;
146       CStdString strDirectory;
147       CUtil::GetDirectory(strDest,strDirectory);
148       CUtil::RemoveSlashAtEnd(strDirectory);  // for the test below
149       if (!(strDirectory.size() == 2 && strDirectory[1] == ':'))
150       {
151         CURL url(strDirectory);
152         CStdString pathsep;
153 #ifndef _LINUX
154         pathsep = "\\";
155 #else
156         pathsep = "/";
157 #endif
158         CUtil::Tokenize(url.GetFileName(),tokens,pathsep.c_str());
159         CStdString strCurrPath;
160         // Handle special
161         if (!url.GetProtocol().IsEmpty()) {
162           pathsep = "/";
163           strCurrPath += url.GetProtocol() + "://";
164         } // If the directory has a / at the beginning, don't forget it
165         else if (strDirectory[0] == pathsep[0])
166           strCurrPath += pathsep;
167         for (vector<CStdString>::iterator iter=tokens.begin();iter!=tokens.end();++iter)
168         {
169           strCurrPath += *iter+pathsep;
170           CDirectory::Create(strCurrPath);
171         }
172       }
173     }
174     if (CFile::Exists(strDest))
175       CFile::Delete(strDest);
176     if (!newFile.OpenForWrite(strDest, true))  // overwrite always
177     {
178       file.Close();
179       return false;
180     }
181
182     /* larger then 1 meg, let's do rendering async */
183     // Async render cannot be done in SDL builds because of the resulting ThreadMessage deadlock
184     // we should call CAsyncFileCopy::Copy() instead.
185 #if defined(_XBOX)
186     if( file.GetLength() > 1024*1024 )
187       helper = new CAsyncFileCallback(pCallback, pContext);
188 #endif
189
190     // 128k is optimal for xbox
191     int iBufferSize = 128 * 1024;
192
193     CAutoBuffer buffer(iBufferSize);
194     int iRead, iWrite;
195
196     UINT64 llFileSize = file.GetLength();
197     UINT64 llFileSizeOrg = llFileSize;
198     UINT64 llPos = 0;
199     int ipercent = 0;
200
201     CStopWatch timer;
202     timer.StartZero();
203     float start = 0.0f;
204     while (llFileSize > 0)
205     {
206       g_application.ResetScreenSaver();
207       unsigned int iBytesToRead = iBufferSize;
208
209       /* make sure we don't try to read more than filesize*/
210       if (iBytesToRead > llFileSize) iBytesToRead = llFileSize;
211
212       iRead = file.Read(buffer.get(), iBytesToRead);
213       if (iRead == 0) break;
214       else if (iRead < 0)
215       {
216         CLog::Log(LOGERROR, "%s - Failed read from file %s", __FUNCTION__, strFileName.c_str());
217         break;
218       }
219
220       /* write data and make sure we managed to write it all */
221       iWrite = 0;
222       while(iWrite < iRead)
223       {
224         int iWrite2 = newFile.Write(buffer.get()+iWrite, iRead-iWrite);
225         if(iWrite2 <=0)
226           break;
227         iWrite+=iWrite2;
228       }
229
230       if (iWrite != iRead)
231       {
232         CLog::Log(LOGERROR, "%s - Failed write to file %s", __FUNCTION__, strDest.c_str());
233         break;
234       }
235
236       llFileSize -= iRead;
237       llPos += iRead;
238
239       // calculate the current and average speeds
240       float end = timer.GetElapsedSeconds();
241       float averageSpeed = llPos / end;
242       start = end;
243
244       float fPercent = 100.0f * (float)llPos / (float)llFileSizeOrg;
245
246       if ((int)fPercent != ipercent)
247       {
248         if( helper )
249         {
250           helper->SetStatus((int)fPercent, averageSpeed);
251           if(helper->IsCanceled())
252             break;
253         }
254         else if( pCallback )
255         {
256           if (!pCallback->OnFileCallback(pContext, ipercent, averageSpeed))
257             break;
258         }
259
260         ipercent = (int)fPercent;
261       }
262     }
263
264     /* close both files */
265     newFile.Close();
266     file.Close();
267
268     delete helper;
269
270     /* verify that we managed to completed the file */
271     if (llPos != llFileSizeOrg)
272     {
273       CFile::Delete(strDest);
274       return false;
275     }
276     return true;
277   }
278   return false;
279 }
280
281 //*********************************************************************************************
282 bool CFile::Open(const CStdString& strFileName, unsigned int flags)
283 {
284   m_flags = flags;
285   try
286   {
287     bool bPathInCache;
288     if (!g_directoryCache.FileExists(strFileName, bPathInCache) )
289     {
290       if (bPathInCache)
291         return false;
292     }
293
294     CFileItem fileItem;
295     fileItem.m_strPath = strFileName;
296     if ( (flags & READ_NO_CACHE) == 0 && fileItem.IsInternetStream() && !fileItem.IsPicture())
297       m_flags |= READ_CACHED;
298
299     CURL url(strFileName);
300     if (m_flags & READ_CACHED)
301     {
302       m_pFile = new CFileCache();
303       return m_pFile->Open(url);
304     }
305
306     m_pFile = CFileFactory::CreateLoader(url);
307     if (!m_pFile)
308       return false;
309
310     try
311     {
312       if (!m_pFile->Open(url))
313       {
314         SAFE_DELETE(m_pFile);
315         return false;
316       }
317     }
318     catch (CRedirectException *pRedirectEx)
319     {
320       // the file implementation decided this item should use a different implementation.
321       // the exception will contain the new implementation.
322
323       CLog::Log(LOGDEBUG,"File::Open - redirecting implementation for %s", strFileName.c_str());
324       SAFE_DELETE(m_pFile);
325       if (pRedirectEx && pRedirectEx->m_pNewFileImp)
326       {
327         m_pFile = pRedirectEx->m_pNewFileImp;
328         delete pRedirectEx;
329
330         if (!m_pFile->Open(url))
331         {
332           SAFE_DELETE(m_pFile);
333           return false;
334         }
335       }
336     }
337     catch (...)
338     {
339       CLog::Log(LOGDEBUG,"File::Open - unknown exception when opening %s", strFileName.c_str());
340       SAFE_DELETE(m_pFile);
341       return false;
342     }
343
344     if (m_flags & READ_BUFFERED)
345     {
346       if (m_pFile->GetChunkSize())
347       {
348         m_pBuffer = new CFileStreamBuffer(0);
349         m_pBuffer->Attach(m_pFile);
350       }
351     }
352
353     m_bitStreamStats.Start();
354     return true;
355   }
356 #ifndef _LINUX
357   catch (const win32_exception &e)
358   {
359     e.writelog(__FUNCTION__);
360   }
361 #endif
362   catch(...)
363   {
364     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
365   }
366   CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
367   return false;
368 }
369
370 void CFile::Attach(IFile *pFile, unsigned int flags) {
371   m_pFile = pFile;
372   m_flags = flags;
373   if (m_flags & READ_BUFFERED)
374   {
375     if (m_pFile->GetChunkSize())
376     {
377       m_pBuffer = new CFileStreamBuffer(0);
378       m_pBuffer->Attach(m_pFile);
379     }
380   }
381 }
382
383 IFile* CFile::Detach() {
384   // TODO - currently the buffered reading is broken if in use, it should be
385   //        moved to a IFile instead, then it will work just fine
386
387   IFile* file = m_pFile;
388   m_pFile = NULL;
389   m_flags = 0;
390   return file;
391 }
392
393
394 bool CFile::OpenForWrite(const CStdString& strFileName, bool bOverWrite)
395 {
396   try
397   {
398     CURL url(strFileName);
399
400     m_pFile = CFileFactory::CreateLoader(url);
401     if (m_pFile && m_pFile->OpenForWrite(url, bOverWrite))
402     {
403       // add this file to our directory cache (if it's stored)
404       g_directoryCache.AddFile(strFileName);
405       return true;
406     }
407     return false;
408   }
409 #ifndef _LINUX
410   catch (const win32_exception &e)
411   {
412     e.writelog(__FUNCTION__);
413   }
414 #endif
415   catch(...)
416   {
417     CLog::Log(LOGERROR, "%s - Unhandled exception opening %s", __FUNCTION__, strFileName.c_str());
418   }
419   CLog::Log(LOGERROR, "%s - Error opening %s", __FUNCTION__, strFileName.c_str());
420   return false;
421 }
422
423 bool CFile::Exists(const CStdString& strFileName)
424 {
425   try
426   {
427     if (strFileName.IsEmpty()) return false;
428
429     bool bPathInCache;
430     if (g_directoryCache.FileExists(strFileName, bPathInCache) )
431       return true;
432     if (bPathInCache)
433       return false;
434
435     CURL url(strFileName);
436
437     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
438     if (!pFile.get()) return false;
439
440     return pFile->Exists(url);
441   }
442 #ifndef _LINUX
443   catch (const win32_exception &e)
444   {
445     e.writelog(__FUNCTION__);
446   }
447 #endif
448   catch(...)
449   {
450     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
451   }
452   CLog::Log(LOGERROR, "%s - Error checking for %s", __FUNCTION__, strFileName.c_str());
453   return false;
454 }
455
456 int CFile::Stat(struct stat64 *buffer)
457 {
458   return m_pFile->Stat(buffer);
459 }
460
461 int CFile::Stat(const CStdString& strFileName, struct stat64* buffer)
462 {
463   try
464   {
465     CURL url(strFileName);
466
467     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
468     if (!pFile.get()) return false;
469
470     return pFile->Stat(url, buffer);
471   }
472 #ifndef _LINUX
473   catch (const win32_exception &e)
474   {
475     e.writelog(__FUNCTION__);
476   }
477 #endif
478   catch(...)
479   {
480     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
481   }
482   CLog::Log(LOGERROR, "%s - Error statting %s", __FUNCTION__, strFileName.c_str());
483   return -1;
484 }
485
486 unsigned int CFile::Read(void *lpBuf, int64_t uiBufSize)
487 {
488   if (!m_pFile)
489     return 0;
490
491   if(m_pBuffer)
492   {
493     if(m_flags & READ_TRUNCATED)
494     {
495       unsigned int nBytes = m_pBuffer->sgetn(
496         (char *)lpBuf, min<streamsize>((streamsize)uiBufSize,
497                                                   m_pBuffer->in_avail()));
498       if (nBytes>0)
499         m_bitStreamStats.AddSampleBytes(nBytes);
500       return nBytes;
501     }
502     else
503     {
504       unsigned int nBytes = m_pBuffer->sgetn((char*)lpBuf, uiBufSize);
505       if (nBytes>0)
506         m_bitStreamStats.AddSampleBytes(nBytes);
507       return nBytes;
508     }
509   }
510
511   try
512   {
513     if(m_flags & READ_TRUNCATED)
514     {
515       unsigned int nBytes = m_pFile->Read(lpBuf, uiBufSize);
516       if (nBytes>0)
517         m_bitStreamStats.AddSampleBytes(nBytes);
518       return nBytes;
519     }
520     else
521     {
522       unsigned int done = 0;
523       while((uiBufSize-done) > 0)
524       {
525         int curr = m_pFile->Read((char*)lpBuf+done, uiBufSize-done);
526         if(curr<=0)
527           break;
528
529         done+=curr;
530       }
531       if (done > 0)
532         m_bitStreamStats.AddSampleBytes(done);
533       return done;
534     }
535   }
536 #ifndef _LINUX
537   catch (const win32_exception &e)
538   {
539     e.writelog(__FUNCTION__);
540   }
541 #endif
542   catch(...)
543   {
544     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
545   }
546   return 0;
547 }
548
549 //*********************************************************************************************
550 void CFile::Close()
551 {
552   try
553   {
554     if (m_pBuffer)
555       SAFE_DELETE(m_pBuffer);
556
557     if (m_pFile)
558       SAFE_DELETE(m_pFile);
559   }
560 #ifndef _LINUX
561   catch (const win32_exception &e)
562   {
563     e.writelog(__FUNCTION__);
564   }
565 #endif
566   catch(...)
567   {
568     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
569   }
570   return;
571 }
572
573 void CFile::Flush()
574 {
575   try
576   {
577     if (m_pFile) m_pFile->Flush();
578   }
579 #ifndef _LINUX
580   catch (const win32_exception &e)
581   {
582     e.writelog(__FUNCTION__);
583   }
584 #endif
585   catch(...)
586   {
587     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
588   }
589   return;
590 }
591
592 //*********************************************************************************************
593 int64_t CFile::Seek(int64_t iFilePosition, int iWhence)
594 {
595   if (!m_pFile)
596     return -1;
597
598   if (m_pBuffer)
599   {
600     if(iWhence == SEEK_CUR)
601       return m_pBuffer->pubseekoff(iFilePosition,ios_base::cur);
602     else if(iWhence == SEEK_END)
603       return m_pBuffer->pubseekoff(iFilePosition,ios_base::end);
604     else if(iWhence == SEEK_SET)
605       return m_pBuffer->pubseekoff(iFilePosition,ios_base::beg);
606   }
607
608   try
609   {
610     if(iWhence == SEEK_POSSIBLE)
611     {
612       int64_t ret = m_pFile->Seek(iFilePosition, iWhence);
613       if(ret >= 0)
614         return ret;
615       else
616       {
617         if(m_pFile->GetLength() && m_pFile->Seek(0, SEEK_CUR) >= 0)
618           return 1;
619         else
620           return 0;
621       }
622     }
623     else
624       return m_pFile->Seek(iFilePosition, iWhence);
625   }
626 #ifndef _LINUX
627   catch (const win32_exception &e)
628   {
629     e.writelog(__FUNCTION__);
630   }
631 #endif
632   catch(...)
633   {
634     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
635   }
636   return -1;
637 }
638
639 //*********************************************************************************************
640 int64_t CFile::GetLength()
641 {
642   try
643   {
644     if (m_pFile) return m_pFile->GetLength();
645     return 0;
646   }
647 #ifndef _LINUX
648   catch (const win32_exception &e)
649   {
650     e.writelog(__FUNCTION__);
651   }
652 #endif
653   catch(...)
654   {
655     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
656   }
657   return 0;
658 }
659
660 //*********************************************************************************************
661 int64_t CFile::GetPosition()
662 {
663   if (!m_pFile)
664     return -1;
665
666   if (m_pBuffer)
667     return m_pBuffer->pubseekoff(0, ios_base::cur);
668
669   try
670   {
671     return m_pFile->GetPosition();
672   }
673 #ifndef _LINUX
674   catch (const win32_exception &e)
675   {
676     e.writelog(__FUNCTION__);
677   }
678 #endif
679   catch(...)
680   {
681     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
682   }
683   return -1;
684 }
685
686
687 //*********************************************************************************************
688 bool CFile::ReadString(char *szLine, int iLineLength)
689 {
690   if (!m_pFile)
691     return false;
692
693   if (m_pBuffer)
694   {
695     typedef CFileStreamBuffer::traits_type traits;
696     CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
697
698     if(aByte == traits::eof())
699       return false;
700
701     while(iLineLength>0)
702     {
703       aByte = m_pBuffer->sbumpc();
704
705       if(aByte == traits::eof())
706         break;
707
708       if(aByte == traits::to_int_type('\n'))
709       {
710         if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
711           m_pBuffer->sbumpc();
712         break;
713       }
714
715       if(aByte == traits::to_int_type('\r'))
716       {
717         if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
718           m_pBuffer->sbumpc();
719         break;
720       }
721
722       *szLine = traits::to_char_type(aByte);
723       szLine++;
724       iLineLength--;
725     }
726
727     // if we have no space for terminating character we failed
728     if(iLineLength==0)
729       return false;
730
731     *szLine = 0;
732
733     return true;
734   }
735
736   try
737   {
738     return m_pFile->ReadString(szLine, iLineLength);
739   }
740 #ifndef _LINUX
741   catch (const win32_exception &e)
742   {
743     e.writelog(__FUNCTION__);
744   }
745 #endif
746   catch(...)
747   {
748     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
749   }
750   return false;
751 }
752
753 int CFile::Write(const void* lpBuf, int64_t uiBufSize)
754 {
755   try
756   {
757     return m_pFile->Write(lpBuf, uiBufSize);
758   }
759 #ifndef _LINUX
760   catch (const win32_exception &e)
761   {
762     e.writelog(__FUNCTION__);
763   }
764 #endif
765   catch(...)
766   {
767     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
768   }
769   return -1;
770 }
771
772 bool CFile::Delete(const CStdString& strFileName)
773 {
774   try
775   {
776     CURL url(strFileName);
777
778     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
779     if (!pFile.get()) return false;
780
781     if(pFile->Delete(url))
782     {
783       g_directoryCache.ClearFile(strFileName);
784       return true;
785     }
786   }
787 #ifndef _LINUX
788   catch (const access_violation &e)
789   {
790     e.writelog(__FUNCTION__);
791   }
792   catch (const win32_exception &e)
793   {
794     e.writelog(__FUNCTION__);
795   }
796 #endif
797   catch(...)
798   {
799     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
800   }
801   CLog::Log(LOGERROR, "%s - Error deleting file %s", __FUNCTION__, strFileName.c_str());
802   return false;
803 }
804
805 bool CFile::Rename(const CStdString& strFileName, const CStdString& strNewFileName)
806 {
807   try
808   {
809     CURL url(strFileName);
810     CURL urlnew(strNewFileName);
811
812     auto_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
813     if (!pFile.get()) return false;
814
815     if(pFile->Rename(url, urlnew))
816     {
817       g_directoryCache.ClearFile(strFileName);
818       g_directoryCache.ClearFile(strNewFileName);
819       return true;
820     }
821   }
822 #ifndef _LINUX
823   catch (const win32_exception &e)
824   {
825     e.writelog(__FUNCTION__);
826   }
827 #endif
828   catch(...)
829   {
830     CLog::Log(LOGERROR, "%s - Unhandled exception ", __FUNCTION__);
831   }
832   CLog::Log(LOGERROR, "%s - Error renaming file %s", __FUNCTION__, strFileName.c_str());
833   return false;
834 }
835
836 //*********************************************************************************************
837 //*************** Stream IO for CFile objects *************************************************
838 //*********************************************************************************************
839 CFileStreamBuffer::~CFileStreamBuffer()
840 {
841   sync();
842   Detach();
843 }
844
845 CFileStreamBuffer::CFileStreamBuffer(int backsize)
846   : streambuf()
847   , m_file(NULL)
848   , m_buffer(NULL)
849   , m_backsize(backsize)
850   , m_frontsize(0)
851 {}
852
853 void CFileStreamBuffer::Attach(IFile *file)
854 {
855   m_file = file;
856
857   m_frontsize = m_file->GetChunkSize();
858   if(!m_frontsize)
859     m_frontsize = 1024;
860
861   m_buffer = new char[m_frontsize+m_backsize];
862   setg(0,0,0);
863   setp(0,0);
864 }
865
866 void CFileStreamBuffer::Detach()
867 {
868   setg(0,0,0);
869   setp(0,0);
870   delete[] m_buffer;
871   m_buffer = NULL;
872 }
873
874 CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
875 {
876   if(gptr() < egptr())
877     return traits_type::to_int_type(*gptr());
878
879   if(!m_file)
880     return traits_type::eof();
881
882   size_t backsize = 0;
883   if(m_backsize)
884   {
885     backsize = (size_t)min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
886     memmove(m_buffer, egptr()-backsize, backsize);
887   }
888
889   unsigned int size = 0;
890 #ifndef _LINUX
891   try
892   {
893 #endif
894     size = m_file->Read(m_buffer+backsize, m_frontsize);
895 #ifndef _LINUX
896   }
897   catch (const win32_exception &e)
898   {
899     e.writelog(__FUNCTION__);
900   }
901 #endif
902
903   if(size == 0)
904     return traits_type::eof();
905
906   setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
907   return traits_type::to_int_type(*gptr());
908 }
909
910 CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
911   off_type offset,
912   ios_base::seekdir way,
913   ios_base::openmode mode)
914 {
915   // calculate relative offset
916   off_type offset2;
917   if(way == ios_base::cur)
918     offset2 = offset;
919   else if(way == ios_base::beg)
920     offset2 = offset - m_file->GetPosition();
921   else if(way == ios_base::end)
922     offset2 = m_file->GetLength() + offset - 1;
923   else
924     offset2 = 0;
925
926   // a non seek shouldn't modify our buffer
927   if(offset2 == 0)
928     return m_file->GetPosition() - (egptr() - gptr());
929
930   // try to seek within buffer
931   if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
932   {
933     gbump(offset2);
934     return m_file->GetPosition() - (egptr() - gptr());
935   }
936
937   // reset our buffer pointer, will
938   // start buffering on next read
939   setg(0,0,0);
940   setp(0,0);
941
942   int64_t position = -1;
943 #ifndef _LINUX
944   try
945   {
946 #endif
947     if(way == ios_base::cur)
948       position = m_file->Seek(offset, SEEK_CUR);
949     else if(way == ios_base::end)
950       position = m_file->Seek(offset, SEEK_END);
951     else
952       position = m_file->Seek(offset, SEEK_SET);
953 #ifndef _LINUX
954   }
955   catch (const win32_exception &e)
956   {
957     e.writelog(__FUNCTION__);
958     return streampos(-1);
959   }
960 #endif
961
962   if(position<0)
963     return streampos(-1);
964
965   return position;
966 }
967
968 CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
969   pos_type pos,
970   ios_base::openmode mode)
971 {
972   return seekoff(pos, ios_base::beg, mode);
973 }
974
975 streamsize CFileStreamBuffer::showmanyc()
976 {
977   underflow();
978   return egptr() - gptr();
979 }
980
981 CFileStream::CFileStream(int backsize /*= 0*/) :
982     istream(&m_buffer),
983     m_buffer(backsize),
984     m_file(NULL)
985 {
986 }
987
988 CFileStream::~CFileStream()
989 {
990   Close();
991 }
992
993
994 bool CFileStream::Open(const CURL& filename)
995 {
996   Close();
997
998   m_file = CFileFactory::CreateLoader(filename);
999   if(m_file && m_file->Open(filename))
1000   {
1001     m_buffer.Attach(m_file);
1002     return true;
1003   }
1004
1005   setstate(failbit);
1006   return false;
1007 }
1008
1009 int64_t CFileStream::GetLength()
1010 {
1011   return m_file->GetLength();
1012 }
1013
1014 void CFileStream::Close()
1015 {
1016   if(!m_file)
1017     return;
1018
1019   m_buffer.Detach();
1020   SAFE_DELETE(m_file);
1021 }
1022
1023 bool CFileStream::Open(const CStdString& filename)
1024 {
1025   return Open(CURL(filename));
1026 }