]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/tools/Utils/win32/FileName.cpp
Initial revision
[karo-tx-redboot.git] / tools / src / tools / Utils / win32 / FileName.cpp
1 //####COPYRIGHTBEGIN####
2 //                                                                          
3 // ----------------------------------------------------------------------------
4 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
5 //
6 // This program is part of the eCos host tools.
7 //
8 // This program is free software; you can redistribute it and/or modify it 
9 // under the terms of the GNU General Public License as published by the Free 
10 // Software Foundation; either version 2 of the License, or (at your option) 
11 // any later version.
12 // 
13 // This program is distributed in the hope that it will be useful, but WITHOUT 
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
15 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
16 // more details.
17 // 
18 // You should have received a copy of the GNU General Public License along with
19 // this program; if not, write to the Free Software Foundation, Inc., 
20 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 //
22 // ----------------------------------------------------------------------------
23 //                                                                          
24 //####COPYRIGHTEND####
25 #include "stdafx.h"
26 #include "FileName.h"
27 #include <shlwapi.h>
28
29 const TCHAR CFileName::cSep=_TCHAR('\\');
30
31 CFileName::CFileName(LPCTSTR psz1,LPCTSTR psz2):
32 CString(psz1)
33 {
34   Normalize();
35   operator+=(psz2);
36 }
37
38 CFileName::CFileName(LPCTSTR psz1,LPCTSTR psz2,LPCTSTR psz3):
39 CString(psz1)
40 {
41   Normalize();
42   operator+=(psz2);
43   operator+=(psz3);
44 }
45
46 CFileName::CFileName(LPCTSTR psz1,LPCTSTR psz2,LPCTSTR psz3,LPCTSTR psz4):
47 CString(psz1)
48 {
49   Normalize();
50   operator+=(psz2);
51   operator+=(psz3);
52   operator+=(psz4);
53 }
54
55 CFileName::CFileName(LPCTSTR psz1,LPCTSTR psz2,LPCTSTR psz3,LPCTSTR psz4,LPCTSTR psz5):
56 CString(psz1)
57 {
58   operator+=(psz2);
59   operator+=(psz3);
60   operator+=(psz4);
61   operator+=(psz5);
62 }
63
64 CFileName AFXAPI operator+(const CFileName& string1, const CFileName& string2)
65 {
66   CFileName s;
67   s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
68     string2.GetData()->nDataLength, string2.m_pchData);
69   return s;
70 }
71
72 CFileName AFXAPI operator+(const CFileName& string, LPCTSTR lpsz)
73 {
74   ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
75   CFileName s;
76   s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
77     CFileName::SafeStrlen(lpsz), lpsz);
78   return s;
79 }
80
81 CFileName AFXAPI operator+(LPCTSTR lpsz, const CFileName& string)
82 {
83   ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
84   CFileName s;
85   s.ConcatCopy(CFileName::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
86     string.m_pchData);
87   return s;
88 }
89
90 CFileName AFXAPI operator+(const CFileName& string1, TCHAR ch)
91 {
92   CFileName s;
93   s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
94   return s;
95 }
96
97 CFileName AFXAPI operator+(TCHAR ch, const CFileName& string)
98 {
99   CFileName s;
100   s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
101   return s;
102 }
103
104 const CFileName& CFileName::operator+=(LPCTSTR lpsz)
105 {
106   ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
107   ConcatInPlace(SafeStrlen(lpsz), lpsz);
108   return *this;
109 }
110
111 const CFileName& CFileName::operator+=(TCHAR ch)
112 {
113   ConcatInPlace(1, &ch);
114   return *this;
115 }
116
117 const CFileName& CFileName::operator+=(const CFileName& string)
118 {
119   ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
120   return *this;
121 }
122
123 void CFileName::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
124 {
125   if(nSrcLen>0){
126     if(GetLength()>0){
127       // Appending a single separator to a non-null string is a no-op
128       if(1==nSrcLen && cSep==*lpszSrcData){
129         return;
130       }
131       // Count the intervening separators
132       int n=(cSep==m_pchData[GetLength()-1])+(cSep==*lpszSrcData);
133       switch(n){
134       case 0:
135         CString::ConcatInPlace(1, &cSep);
136         break;
137       case 1:
138         break;
139       case 2:
140         lpszSrcData++;
141         nSrcLen--;
142         break;
143       }
144     }
145     CString::ConcatInPlace(nSrcLen, lpszSrcData);
146   }
147 }
148
149 void CFileName::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
150                            int nSrc2Len, LPCTSTR lpszSrc2Data)
151 {
152   int n=1;
153   int nNewLen = nSrc1Len + nSrc2Len;
154   if(nSrc1Len>0){
155     if(1==nSrc2Len && cSep==*lpszSrc2Data){
156       // Appending a single separator to a non-null string is a no-op
157       lpszSrc2Data++;
158       nSrc2Len--;
159       nNewLen--;
160     } else {
161       // Count the intervening separators
162       n=(cSep==lpszSrc1Data[nSrc1Len-1])+(cSep==*lpszSrc2Data);
163       
164       switch(n){
165       case 0:
166         nNewLen++;
167         break;
168       case 1:
169         break;
170       case 2:
171         lpszSrc2Data++;
172         nSrc2Len--;
173         nNewLen--;
174         break;
175       }
176     }
177   }
178   if (nNewLen != 0)
179   {
180     AllocBuffer(nNewLen);
181     memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
182     LPTSTR p=m_pchData+nSrc1Len;
183     if(0==n){
184       *p++=cSep;
185     }
186     memcpy(p, lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
187   }
188 }
189
190 void CFileName::Normalize()
191 {
192   // Remove any trailing slash
193   int &n=GetData()->nDataLength;
194   if(n>1 && (cSep==m_pchData[n-1])){
195     n--;
196     m_pchData[n] = _TCHAR('\0');
197   }
198 }
199
200
201 const CFileName CFileName::FullName() const
202 {
203   PTCHAR pFile;
204   DWORD dwSize=::GetFullPathName (*this, 0, NULL, &pFile);
205   if(dwSize>0){
206     CString strCopy;
207     ::GetFullPathName (*this, 1+dwSize, strCopy.GetBuffer(1+dwSize), &pFile);
208     strCopy.ReleaseBuffer();
209     return strCopy;
210   } else {
211     return *this;
212   }
213 }
214
215 const CFileName CFileName::ShortName() const
216 {
217   DWORD dwSize=::GetShortPathName (*this, NULL, 0);
218   if(dwSize>0){
219     CString strCopy;
220     ::GetShortPathName (*this, strCopy.GetBuffer(1+dwSize), 1+dwSize);
221     strCopy.ReleaseBuffer();
222     return strCopy;
223   } else {
224     return *this;
225   }
226 }
227
228 const CFileName CFileName::NoSpaceName() const// sans spaces
229 {
230   CStringArray ar1,ar2;
231   const CString str2(ShortName());
232   
233   LPCTSTR pc,pcStart;
234   
235   pcStart=*this;
236   for(pc=*this;*pc;pc++){
237     if(_TCHAR('\\')==*pc){
238       ar1.Add(CString(pcStart,pc-pcStart));
239       pcStart=pc;
240     }
241   }
242   ar1.Add(CString(pcStart,pc-pcStart));
243   
244   pcStart=str2;
245   for(pc=str2;*pc;pc++){
246     if(_TCHAR('\\')==*pc){
247       ar2.Add(CString(pcStart,pc-pcStart));
248       pcStart=pc;
249     }
250   }
251   ar2.Add(CString(pcStart,pc-pcStart));
252   
253   ASSERT(ar1.GetSize()==ar2.GetSize());
254   
255   CString rc;
256   for(int i=0;i<ar1.GetSize();i++){
257     rc+=(-1==ar1[i].Find(_TCHAR(' ')))?ar1[i]:ar2[i];
258   }
259   return rc;
260 }
261
262 //
263 const CFileName CFileName::Tail() const
264 {
265   TCHAR *ch=_tcsrchr(m_pchData,cSep);
266   return ch?ch+1:m_pchData;
267 }
268
269 const CFileName CFileName::Head() const
270 {
271   TCHAR *ch=_tcsrchr(m_pchData,cSep);
272   return ch?CFileName(m_pchData,ch-m_pchData):m_pchData;
273 }
274
275 // GetFileAttributes is not available in Win95 so we test the water first
276 FILETIME CFileName::LastModificationTime() const
277 {
278   static HINSTANCE hInst=LoadLibrary(_T("kernel32.dll"));
279   ASSERT(hInst);
280   
281 #ifdef _UNICODE
282 #define GETFILEATTRIBUTESNAME "GetFileAttributesExW"
283 #else
284 #define GETFILEATTRIBUTESNAME "GetFileAttributesExA"
285 #endif // !UNICODE
286   
287   typedef BOOL (WINAPI *GetFileAttributesP)(LPCTSTR,GET_FILEEX_INFO_LEVELS,LPVOID);
288   
289   static GetFileAttributesP p=(GetFileAttributesP)GetProcAddress(hInst,GETFILEATTRIBUTESNAME);
290   if(p){
291     WIN32_FILE_ATTRIBUTE_DATA data;
292     
293     if((*p)(*this, GetFileExInfoStandard, (LPVOID)&data)){
294       return data.ftLastWriteTime;
295     }
296   } else {
297     HANDLE h=CreateFile(*this,0,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
298     FILETIME ft;
299     if(INVALID_HANDLE_VALUE!=h){
300       BOOL b=::GetFileTime(h,NULL,NULL,&ft);
301       ::CloseHandle(h);
302       if(b){
303         return ft;
304       }
305     }
306   }
307   static FILETIME ftNull={0,0};
308   return ftNull;
309 }
310
311 bool CFileName::SetFileAttributes(DWORD dwFileAttributes) const
312 {
313   return ::SetFileAttributes (*this, dwFileAttributes)!=0;
314 }
315
316 bool CFileName::SameFile(const CFileName &o) const
317 {
318   return 0==ShortName().CompareNoCase(o.ShortName());
319 }
320
321 CFileName CFileName::ExpandEnvironmentStrings(LPCTSTR psz)
322 {
323   // First call simply determines the size
324   CFileName f;
325   DWORD dwSize=::ExpandEnvironmentStrings(psz, NULL, 0);
326   if(dwSize>0){
327     ::ExpandEnvironmentStrings(psz, f.GetBuffer(dwSize), dwSize);
328     f.ReleaseBuffer();
329   } else {
330     f=psz;
331   }
332   return f;
333 }
334
335 const CFileName& CFileName::ExpandEnvironmentStrings()
336 {
337   *this=CFileName::ExpandEnvironmentStrings(*this);
338   return *this;
339 }
340
341 // Helper for Relative()  psz is in full format.
342 CFileName CFileName::Drive(LPCTSTR psz)
343 {
344   if(_istalpha(psz[0])){
345     return psz[0];
346   } else if(cSep==psz[0]&&cSep==psz[1]){
347     TCHAR *c=_tcschr(psz+2,cSep);
348     if(c){
349       c=_tcschr(c+1,cSep);
350       if(c){
351         return CString(psz,c-psz);
352       }
353     }
354   }
355   return _T("");
356 }
357
358 CFileName CFileName::Relative(LPCTSTR compare,LPCTSTR current)
359 {
360   CString rc;
361   bool b=(TRUE==PathRelativePathTo(rc.GetBuffer(1+MAX_PATH),current,FILE_ATTRIBUTE_DIRECTORY,compare,0));
362   rc.ReleaseBuffer();
363   //TRACE(_T("compare=%s current=%s result=%s (b=%d)\n"),compare,current,b?rc:compare,b);
364   return b?rc:compare;
365
366   
367 const CFileName& CFileName::MakeRelative(LPCTSTR pszRelativeTo)
368 {
369   *this=CFileName::Relative(*this,pszRelativeTo);
370   return *this;
371 }
372
373
374 CFileName CFileName::GetCurrentDirectory()
375 {
376   CFileName f;
377   ::GetCurrentDirectory(1+_MAX_PATH,f.GetBuffer(1+_MAX_PATH));
378   f.ReleaseBuffer();
379   f.Normalize();
380   return f;
381 }
382
383
384 const CFileName& CFileName::Append(LPCTSTR lpsz)
385 {
386   ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
387   CString::ConcatInPlace(SafeStrlen(lpsz), lpsz);
388   return *this;
389   
390 }
391
392 const CFileName& CFileName::Append(TCHAR ch)
393 {
394   ConcatInPlace(1, &ch);
395   return *this;
396 }
397
398 bool CFileName::IsAbsolute() const
399 {
400   int nLength=GetLength();
401   LPCTSTR psz=*this;
402   return 
403     (nLength>0 && (cSep==psz[0]))|| // starts with '\'
404     (nLength>1 && (
405     (_istalpha(psz[0]) && _TCHAR(':')==psz[1]) ||  // starts with [e.g.] "c:\"
406     (cSep==psz[0] && cSep==psz[1])));              // UNC
407 }
408
409 // Return an array of filename pieces.  Plugs '\0's into 'this', which
410 // is therefore subsequently only usable as referenced by the returned array.
411 LPCTSTR *CFileName::Chop()
412 {
413   TCHAR *c;
414   // Count the separators
415   int nSeps=0;
416   for(c=_tcschr(m_pchData,cSep);c;c=_tcschr(c+1,cSep)){
417     nSeps++;
418   }
419   LPCTSTR *ar=new LPCTSTR[2+nSeps]; // +1 for first, +1 for terminating 0
420   ar[0]=m_pchData;
421   int i=1;
422   for(c=_tcschr(m_pchData,cSep);c;c=_tcschr(c+1,cSep)){
423     ar[i++]=c+1;
424     *c=_TCHAR('\0');
425   }
426   ar[i]=0;
427   return ar;
428 }
429
430 CFileName CFileName::GetTempPath()
431 {
432   CFileName f;
433   ::GetTempPath(1+_MAX_PATH,f.GetBuffer(1+_MAX_PATH));
434   f.ReleaseBuffer();
435   f.Normalize();
436   return f;
437   
438 }
439
440 const CFileName CFileName::CygPath () const 
441 {
442   TCHAR buf[2+MAX_PATH];
443   LPCTSTR rc=buf+1;
444   if(!GetShortPathName(m_pchData,1+buf,MAX_PATH)){
445     _tcscpy(1+buf,m_pchData);
446   }
447   if(_istalpha(*rc)&&_TCHAR(':')==rc[1]){
448     // Convert c:\ to //c/ [this is the bit that requires the first char of buf]
449     buf[0]=_TCHAR('/');
450     buf[2]=buf[1];
451     buf[1]=_TCHAR('/');
452     rc=buf;
453   }
454   for(TCHAR *c=buf+1;*c;c++){
455     if(_TCHAR('\\')==*c){
456       *c=_TCHAR('/');
457     }
458   }
459   
460   return rc;
461 }
462
463 bool CFileName::CreateDirectory(bool bParentsToo,bool bFailIfAlreadyExists) const
464 {
465   LPCTSTR pszDir=m_pchData;
466   if(bParentsToo){
467     // Create intermediate directories
468     for(LPCTSTR c=_tcschr(pszDir,_TCHAR('\\'));c;c=_tcschr(c+1,_TCHAR('\\'))){
469       if(c==pszDir+2 && _istalpha(pszDir[0]) && _TCHAR(':')==pszDir[1]){
470         continue; // don't attempt to create "C:"
471       }
472       const CFileName strDir(pszDir,c-pszDir);
473       if(!(strDir.IsDir()? (!bFailIfAlreadyExists) : ::CreateDirectory(strDir,NULL))){
474         return false;
475       }
476     }
477   }
478   return IsDir()? (!bFailIfAlreadyExists) : (TRUE==::CreateDirectory(pszDir,NULL));
479 }
480
481
482 const CString CFileName::Extension() const
483 {
484   LPCTSTR ch=_tcsrchr(m_pchData,_TCHAR('.'));
485   if(ch && !_tcschr(ch,cSep)){
486     return CString(ch+1);
487   } else {
488     return _T("");
489   }
490 }
491
492 const CString CFileName::Root() const
493 {
494   LPCTSTR ch=_tcsrchr(m_pchData,_TCHAR('.'));
495   if(ch && !_tcschr(ch,cSep)){
496     return CString(m_pchData,ch-m_pchData);
497   } else {
498     return m_pchData;
499   }
500 }
501
502 CFileName CFileName::SetCurrentDirectory(LPCTSTR pszDir)
503 {
504   const CFileName strPwd=GetCurrentDirectory();
505   return ::SetCurrentDirectory(pszDir)?strPwd:_T("");
506 }
507
508 bool CFileName::RecursivelyDelete()
509 {
510   CFileNameArray ar;
511   for(int i=FindFiles(*this,ar)-1;i>=0;--i){
512     ::DeleteFile(ar[i]);
513   }
514   for(i=FindFiles(*this,ar,_T("*.*"),true,0)-1;i>=0;--i){
515     ::RemoveDirectory(ar[i]);
516   }
517   return TRUE==::RemoveDirectory(*this);
518 }
519
520 int CFileName::FindFiles (LPCTSTR pszDir,CFileNameArray &ar,LPCTSTR pszPattern/*=_T("*.*")*/,bool bRecurse/*=true*/,DWORD dwExclude/*=FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN*/)
521 {
522   ar.RemoveAll();
523   CFileFind finder;
524   BOOL bMore=finder.FindFile(CFileName(pszDir)+pszPattern);
525   while (bMore)    {
526     bMore = finder.FindNextFile();
527     if(!finder.IsDots() && !finder.MatchesMask(dwExclude)){
528       CFileName strFile(finder.GetFilePath());
529       ar.Add(strFile);
530     }
531   }
532   if(bRecurse){
533     CFileFind finder;
534     BOOL bMore=finder.FindFile(CFileName(pszDir)+_T("*.*"));
535     while (bMore)    {
536       bMore = finder.FindNextFile();
537       if(!finder.IsDots() && finder.IsDirectory()){
538         CFileNameArray ar2;
539         FindFiles(finder.GetFilePath(),ar2,pszPattern,bRecurse,dwExclude);
540         ar.Append(ar2);
541       }
542     }
543   }
544   return ar.GetSize();
545 }
546
547 void CFileName::ReplaceExtension(LPCTSTR pszNewExt)
548 {
549   ASSERT(pszNewExt);
550   if(_TCHAR('.')==*pszNewExt){
551     // Be tolerant of whether '.' is included in what we are passed:
552     pszNewExt++;
553   }
554   LPTSTR pch=GetBuffer(2+GetLength()+_tcslen(pszNewExt));
555   LPTSTR pcExt=_tcsrchr(pch,_TCHAR('.'));
556   if(NULL==pcExt || _tcschr(pcExt,cSep)){
557     // No existing extension
558     pcExt=pch+GetLength();
559     *pcExt++=_TCHAR('.');
560   }
561   _tcscpy(pcExt+1,pszNewExt);
562   ReleaseBuffer();
563 }