root/trunk/Files.cpp

Revision 5, 13.0 kB (checked in by qbert, 6 years ago)

Initial ( and last :( ) commit

Line 
1 #include "StdAfx.h"
2 #include "Files.h"
3
4 #include <algorithm>
5
6 /**
7  * @brief Get the dos file time of a file.
8  * @param FileName fully qualified path.
9  */
10 int FileAge(LPCTSTR FileName)
11 {
12     HANDLE Handle;
13     WIN32_FIND_DATA FindData;
14     FILETIME LocalFileTime;
15
16     WORD wHigh;
17     WORD wLow;
18
19     Handle = FindFirstFile(FileName, &FindData);
20     if (Handle != INVALID_HANDLE_VALUE)
21     {
22         FindClose(Handle);
23         if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
24         {
25             FileTimeToLocalFileTime(&FindData.ftLastWriteTime, &LocalFileTime);
26             if (FileTimeToDosDateTime(&LocalFileTime, &wHigh, &wLow) != 0)
27             {
28                 return (int)MAKELONG(wLow, wHigh);
29             }
30         }
31     }
32   return -1;
33 }
34
35 /**
36  * @brief Recursively create a directory.
37  * @param pszDirectory full directory path to create.
38  * @param lpSA pointer to a SECURITY_ATTRIBUTES structure to be passed to CreateDirectory
39  *
40  * Credit to Eugen@Fastfertig.com who posted almost exactly this code
41  * to CodeGuru. Saved me writing it!
42  * link: http://www.codeguru.com/mfc/comments/39715.shtml
43  */
44 bool CreateDirectoryRecursive(LPCTSTR pszDirectory, LPSECURITY_ATTRIBUTES lpSA)
45 {
46     char szDir[MAX_PATH];
47     char *p, *pNext;
48     strcpy(szDir, pszDirectory);
49
50     pNext = strchr(szDir, '\\');
51     if (pNext)
52     {
53         pNext++;
54         while ( ( p = strchr(pNext, '\\') ) != 0 )
55         {
56             *p = NULL;
57             if (GetFileAttributes(szDir) == -1)
58             {
59                 if (CreateDirectory(szDir, lpSA) == FALSE)
60                 {
61                     return FALSE;
62                 }
63             }
64             *p = '\\';
65             pNext = p+1;
66         }
67     }
68     if (GetFileAttributes (szDir) == -1)
69     {
70         if (CreateDirectory(szDir, lpSA) == FALSE)
71         {
72             return false;
73         }
74     }
75
76     return true;
77 }
78
79 ///////////////////////////////////////////////////////////////////////////
80 // CElephantFile - an MFC CElephantFile replacement. No win api stuff, just pure Cpp.
81 ///////////////////////////////////////////////////////////////////////////
82
83 CElephantFile::CElephantFile()
84 {
85     m_file = NULL;
86 }
87
88 CElephantFile::~CElephantFile()
89 {
90     Close();
91 }
92
93 bool CElephantFile::Open(LPCTSTR filename, UINT flags)
94 {
95     tstring mode;
96
97     if(flags != 0)
98     {
99         if(modeWrite & flags)
100             mode = _T("w");
101         else if(modeReadWrite & flags)
102             mode = _T("r+");
103         else mode = _T("r");
104
105         if(modeText & flags)
106             mode += _T("t");
107         else
108             mode += _T("b");
109     }
110     else
111         mode = _T("rb");
112        
113
114     m_file = _tfopen(filename, mode.c_str());
115    
116     return (m_file != NULL);
117 }
118
119 int CElephantFile::Read(void* lpBuf, UINT nCount)
120 {
121     return (int)fread(lpBuf, 1, nCount, m_file);
122 }
123
124 int CElephantFile::Write(void* lpBuf, UINT nCount)
125 {
126     return (int)fwrite(lpBuf, nCount, 1, m_file);
127 }
128
129 void CElephantFile::Close()
130 {
131     if(m_file != NULL)
132     {
133         fclose(m_file);
134         m_file = NULL;
135     }
136 }
137
138 long CElephantFile::GetPosition() const
139 {
140     if(m_file)
141     {
142         return ftell(m_file);
143     }
144     else
145         return -1;
146 }
147
148 void CElephantFile::Seek(UINT offset, CElephantFile::EFrom from)
149 {
150     if(m_file)
151         fseek(m_file, offset, (int)from);
152 }
153
154 long CElephantFile::GetLength()
155 {
156     if(m_file)
157     {
158         long curPos = GetPosition();
159         fseek(m_file, 0, SEEK_END);
160         long ret = GetPosition();
161         fseek(m_file, curPos, SEEK_SET);
162         return ret;
163     }
164     else
165         return -1;
166 }
167
168 ///////////////////////////////////////////////////////////////////////////
169 // CTextFile - text file operations...
170 ///////////////////////////////////////////////////////////////////////////
171
172 // basically taken from MFC's CStdioString...
173 #ifndef PN_NO_CSTRING
174 bool CTextFile::ReadLine(CString& line)
175 {
176     line = _T("");
177     const int nMaxSize = 128;
178     LPTSTR lpsz = line.GetBuffer(nMaxSize);
179     LPTSTR lpszResult;
180     int nLen = 0;
181    
182     for (;;)
183     {
184         lpszResult = _fgetts(lpsz, nMaxSize+1, m_file);
185         line.ReleaseBuffer();
186
187         // handle error/eof case
188         if (lpszResult == NULL && !feof(m_file))
189         {
190             clearerr(m_file);
191             // could throw exception here...
192         }
193
194         // if string is read completely or EOF
195         if (lpszResult == NULL ||
196             (nLen = lstrlen(lpsz)) < nMaxSize ||
197             lpsz[nLen-1] == '\n')
198             break;
199
200         nLen = line.GetLength();
201         lpsz = line.GetBuffer(nMaxSize + nLen) + nLen;
202     }
203
204     // remove '\n' from end of string if present
205     lpsz = line.GetBuffer(0);
206     nLen = line.GetLength();
207     if (nLen != 0 && lpsz[nLen-1] == '\n')
208         line.GetBufferSetLength(nLen-1);
209
210     return lpszResult != NULL;
211 }
212 #endif
213
214 bool CTextFile::WriteLine(LPCTSTR line)
215 {
216     int ret = _fputts(line, m_file);
217     return (ret != _TEOF);
218 }
219
220 ///////////////////////////////////////////////////////////////////////////
221 // CElephantFileName - allows lots of useful operations on a filename.
222 ///////////////////////////////////////////////////////////////////////////
223
224 CElephantFileName& CElephantFileName::operator = (LPCTSTR filename)
225 {
226     m_FileName = filename;
227     return *this;
228 }
229
230 CElephantFileName& CElephantFileName::operator = (const CElephantFileName& filename)
231 {
232     m_FileName = filename.m_FileName;
233     return *this;
234 }
235
236 CElephantFileName& CElephantFileName::operator = (const tstring& filename)
237 {
238     m_FileName = filename;
239     return *this;
240 }
241
242 int CElephantFileName::GetLastSlashPos()
243 {
244     int pos = m_FileName.rfind(_T('\\'));
245     if(pos == m_FileName.npos)
246     {
247         // couldn't find '\\', try again for '/'
248         pos = m_FileName.rfind(_T('/'));
249     }
250     return ++pos;
251 }
252
253 int CElephantFileName::GetLastDotPos(tstring* str)
254 {
255     if(!str)
256     {
257         int pos = m_FileName.rfind(_T('.'));
258         if(pos != m_FileName.npos)
259             return ++pos;
260         else
261             return m_FileName.npos;
262     }
263     else
264     {
265         int pos = str->rfind(_T('.'));
266         if(pos != m_FileName.npos)
267             return ++pos;
268         else
269             return m_FileName.npos;
270     }
271 }
272
273 tstring CElephantFileName::GetPath()
274 {
275     int pos = GetLastSlashPos();
276     if (pos != m_FileName.npos)
277     {
278         return m_FileName.substr(0, pos);
279     }
280     else
281     {
282         return _T("");
283     }
284 }
285
286 void CElephantFileName::GetPath(tstring& buf)
287 {
288     buf = GetPath();
289 }
290
291 /**
292  * We assume that a filename that starts with a \ or has a drive letter
293  * at the start is non-relative. Any path with no slash at all is
294  * definitely relative.
295  */
296 bool CElephantFileName::IsRelativePath()
297 {
298     // If there's no slash in it, it's definitely relative...
299     int spos = GetLastSlashPos();
300     if( spos == m_FileName.npos )
301         return true;
302
303     // If the length is less than 3 then there isn't a drive letter.
304     if( m_FileName.length() < 3 )
305         return true;
306
307     // Is there a drive letter as the first two characters (then it's not relative)?
308     if( ::isalpha(m_FileName[0]) && (m_FileName[1] == _T(':')) )
309         return false;
310
311     // If it begins with a slash then it's not relative - mostly linux style paths.
312     if( m_FileName[0] == _T('\\') || m_FileName[0] == _T('/') )
313         return false;
314
315     return true;
316 }
317
318 /**
319  * Get the relative path from path to us.
320  */
321 tstring CElephantFileName::GetRelativePath(LPCTSTR path)
322 {
323     if(IsSubElementOf(path))
324     {
325         TCHAR slash;
326         int chopLen = _tcslen(path);
327        
328         if(_tcschr(path, _T('/')) != NULL)
329             slash = _T('/');
330         else
331             slash = _T('\\');
332
333         // We're walking down the tree...
334         if(path[_tcslen(path)-1] == slash)
335         {
336             chopLen++;
337         }
338
339         return m_FileName.substr(chopLen-1);
340     }
341     else if(PathIsParentElementOf(path))
342     {
343         //Cop out.
344         return m_FileName;
345     }
346     else
347     {
348         ///@todo Add a relative filename thing to walk down if we're a sub-element of path.
349        
350         //Cop out.
351         return m_FileName;
352     }
353 }
354
355 bool CElephantFileName::CanGetRelativePath(LPCTSTR path)
356 {
357     return (IsSubElementOf(path) || PathIsParentElementOf(path));
358 }
359
360 /**
361  * This function first checks if there are duplicate slashes, and then
362  * combines the current path with the rootPath provided.
363  */
364 void CElephantFileName::Root(LPCTSTR rootPath)
365 {
366     tstring root(rootPath);
367     bool bForwards = false;
368
369     // We always have the trailing slash in root, so make sure that
370     // m_FileName doesn't have a starting one.
371     if( m_FileName[0] == _T('\\') || m_FileName[0] == _T('/') )
372     {
373         m_FileName.erase(m_FileName.begin());
374     }
375
376     // Are we using forward slashes?
377     if( root.find(_T('/')) != root.npos )
378         bForwards = true;
379
380     // Make sure there's a trailing slash.
381     if( root[root.length()-1] != _T('\\') && root[root.length()-1] != _T('/') )
382         root += (bForwards ? _T('/') : _T('\\'));
383
384     TCHAR slash = bForwards ? _T('/') : _T('\\');
385
386     int len = m_FileName.length();
387
388     if(len >= 2)
389     {
390         while(m_FileName[0] == _T('.'))
391         {
392             if(len >= 3 && m_FileName[1] == _T('.') && m_FileName[2] == slash) // Do we have a ..\?
393             {
394                 //1. Remove one folder from the end of root.
395                 tstring::size_type slashPos = root.rfind(slash, root.length() - 2);
396                 if( slashPos != root.npos )
397                 {
398                     root.erase(slashPos + 1);
399                 }
400                
401                 //2. Remove the ..\ from m_FileName;
402                 m_FileName.erase(0, 3);
403             }
404             else if(len >= 2 && m_FileName[1] == slash) // Do we have a .\?
405             {
406                 //1. Remove the dotslash
407                 m_FileName.erase(0, 2);
408             }
409
410             len = m_FileName.length();
411         }
412     }
413
414     root += m_FileName;
415
416     m_FileName = root;
417 }
418
419 /**
420  * Find out if we're below path in the filesystem.
421  */
422 bool CElephantFileName::IsSubElementOf(LPCTSTR path)
423 {
424     // This function simply checks if the path string passed in is
425     // the start of our filename string.
426     return (_tcsnicmp(m_FileName.c_str(), path, _tcslen(path)) == 0);
427 }
428
429 /**
430  * Find out if the path passed in is below us in the file system.
431  */
432 bool CElephantFileName::PathIsParentElementOf(LPCTSTR path)
433 {
434     // This function simply checks if the path string passed in has our
435     // filename as its beginning.
436     tstring myPath = GetPath();
437     return (_tcsnicmp(path, myPath.c_str(), myPath.length()) == 0);
438 }
439
440 /**
441  * This function removes stupidities from paths, whether they be
442  * file:// style URLs or /\ issues.
443  */
444 tstring& CElephantFileName::Sanitise()
445 {
446     UINT fpos = m_FileName.find(_T("file://"));
447     if( fpos != m_FileName.npos )
448     {
449         // We have a file:// url. Fix it up.
450         m_FileName.erase(fpos, 7);
451        
452         fpos = m_FileName.find(_T("%3A"));
453         if( fpos != m_FileName.npos )
454         {
455             m_FileName.replace(fpos, 3, _T(":"));
456         }
457     }
458
459     LPCTSTR in = m_FileName.c_str();
460     LPTSTR res = new TCHAR[_tcslen(in)+1];
461
462     LPCTSTR fnd = _tcschr(in, _T(':'));
463     bool bColon = fnd != NULL;
464
465     if( bColon )
466     {
467         // There's a colon, so there's a drive letter.
468         // Make sure drive letter is at the start of the string.
469         while( (!_istalpha( *in )) && (in != fnd) )
470         {
471             in++;
472         }
473     }
474
475     bool bSlash = false;
476     LPTSTR build = res;
477
478     // Step through the input string and catch things like double-slashes,
479     // and forward instead of backward slashes.
480     while(*in != NULL)
481     {
482         char ch = *in;
483         switch(ch)
484         {
485             // Pretend forward slashes are back-slashes and fall-through
486             // to back-slash handling.
487             case _T('/'):
488                 ch = _T('\\');
489            
490             // If there is a colon (i.e. a drive letter) and a double
491             // slash, then it's a duplicate to skip. If there is no colon
492             // and a double slash closer than 4 characters to the start
493             // of the filename then we assume it's ok.
494             case _T('\\'):
495                 if( (bSlash && bColon) ||
496                     bSlash && ((in - m_FileName.c_str()) > 4) )
497                 {
498                     bSlash = false;
499                     break;
500                 }
501                 bSlash = true;
502            
503             default:
504                 if(ch != _T('\\'))
505                     bSlash = false;
506                 *build++ = ch;
507         }
508        
509         in++;
510     }
511
512     *build++ = NULL;
513
514     m_FileName = res;
515
516     delete [] res;
517
518     return m_FileName;
519 }
520
521 void CElephantFileName::GetFileName(tstring& buf)
522 {
523     buf = GetFileName();
524 }
525
526 tstring CElephantFileName::GetFileName()
527 {
528     int pos = GetLastSlashPos();
529
530     if (pos != m_FileName.npos)
531         return m_FileName.substr(pos);
532     else
533         return m_FileName;
534 }
535
536 tstring CElephantFileName::GetFileName_NoExt()
537 {
538     tstring work;
539    
540     int pos = GetLastSlashPos();
541    
542     if (pos != m_FileName.npos)
543     {
544         work = m_FileName.substr(pos);
545     }
546     else
547     {
548         work = m_FileName;
549     }
550    
551     pos = GetLastDotPos(&work);
552     if(pos != work.npos)
553     {
554         work = work.substr(0, pos-1);
555     }
556
557     return work;
558 }
559
560 ///@todo this crashes if the file has no extension!!!
561 tstring CElephantFileName::GetExtension()
562 {
563     int pos = GetLastDotPos(&m_FileName);
564     if(pos != m_FileName.npos)
565     {
566         return m_FileName.substr(pos-1);
567     }
568     else
569         return tstring(_T(""));
570 }
571
572 void CElephantFileName::GetFileName_NoExt(tstring& buf)
573 {
574     buf = GetFileName_NoExt();
575 }
576
577 void CElephantFileName::ChangeExtensionTo(LPCTSTR newext)
578 {
579     tstring str;
580     tstring buf;
581     GetPath(buf);
582     str = buf;
583     GetFileName_NoExt(buf);
584     str += buf;
585     str += newext;
586     m_FileName = str;
587 }
588
589 void CElephantFileName::ChangePathTo(LPCTSTR newpath)
590 {
591     tstring str;
592     GetFileName(str);
593     str = newpath + str;
594     m_FileName = str;
595 }
596
597 const TCHAR* CElephantFileName::c_str()
598 {
599     return m_FileName.c_str();
600 }
601
602 int CElephantFileName::GetLength()
603 {
604     return m_FileName.length();
605 }
606
607 int CElephantFileName::GetFileAge()
608 {
609     return FileAge(m_FileName.c_str());
610 }
611
612 const tstring& CElephantFileName::ToLower()
613 {
614     std::transform (m_FileName.begin(), m_FileName.end(),    // source
615                m_FileName.begin(),             // destination
616                tolower);
617
618     return m_FileName;
619 }
620
621 tstring CElephantFileName::GetCurrentDirectory()
622 {
623     TCHAR buf[MAX_PATH+1];
624     ::GetCurrentDirectory(MAX_PATH, buf);
625     return tstring(buf);
626 }
Note: See TracBrowser for help on using the browser.