| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961 | 
							- // ==================================================================
 
- // 
 
- //  Path.cpp   
 
- //  
 
- //  Created:       20.06.2004
 
- //
 
- //  Copyright (C) Peter Hauptmann
 
- //              
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// for copyright & disclaimer see accompanying Path.h
 
- /// 
 
- /// \page pgChangeLog Change Log
 
- /// 
 
- /// June 20, 2004:  Initial release
 
- /// 
 
- /// June 22, 2004   
 
- ///     - \c fixed: nsPath::CPath::MakeSystemFolder implements unmake correctly 
 
- ///     - \c added: nsPath::CPath::MakeSystemFolder and nsPath::CPath::SearchOnPath 
 
- ///       set the windows error code to zero if the function succeeds (thanks Hans Dietrich)
 
- ///     - \c fixed: nsPath::CPath compiles correctly with warning level -W4
 
- /// 
 
- /// Mar 3, 2005
 
- ///     - fixed eppAutoQuote bug in GetStr (thanks Stlan)
 
- ///     - Added: 
 
- ///         - \ref nsPath::FromRegistry "FromRegistry"
 
- ///         - \ref nsPath::CPath::ToRegistry "ToRegistry"
 
- ///         - \ref nsPath::CPath::GetRootType, "GetRootType"
 
- ///         - \ref nsPath::CPath::GetRoot "GetRoot" has a new implementation
 
- ///         - \ref nsPath::CPath::MakeAbsolute "MakeAbsolute"
 
- ///         - \ref nsPath::CPath::MakeRelative "MakeRelative"
 
- ///         - \ref nsPath::CPath::MakeFullPath "MakeFullPath"
 
- ///         - \ref nsPath::CPath::EnvUnexpandRoot "EnvUnexpandRoot"
 
- ///         - \ref nsPath::CPath::EnvUnexpandDefaultRoots "EnvUnexpandDefaultRoots"
 
- ///     - \b Breaking \b Changes (sorry)
 
- ///         - GetRoot -> ShellGetRoot (to distinct from extended GetRoot implementation)
 
- ///         - GetFileName --> GetName (consistency)
 
- ///         - GetFileTitle --> GetTitle (consistency)
 
- ///         - made the creation functions independent functions in the nsPath namespace
 
- ///           (they are well tugged away in the namespace so conflicts are easy to avoid) 
 
- /// 
 
- /// Mar 17, 2005
 
- ///     - fixed bug in GetFileName (now: GetName): If the path ends in a backslash,
 
- ///       GetFileName did return the entire path instead of an empty string. (thanks woodland)
 
- /// 
 
- /// Aug 21, 2005
 
- ///     - fixed bug in GetStr(): re-quoting wasn't applied (doh!)
 
- ///     - fixed incompatibility with CStlString
 
- ///     - Added IsDot, IsDotDot, IsDotty (better names?)
 
- ///     - Added IsValid, ReplaceInvalid
 
- ///     - Added SplitRoot
 
- ///     - 
 
- ///        
 
- /// 
 
- ///
 
- // ------ Main Page --------------------
 
- /// @mainpage
 
- /// 
 
- /// \ref pgDisclaimer, \ref pgChangeLog (recent changes August 2005, \b breaking \b changes Mar 2005)
 
- ///
 
- ///	\par Introduction
 
- ///
 
- /// \ref nsPath::CPath "CPath" is a helper class to make manipulating file system path strings easier. 
 
- /// It is complementedby a few non-class functions (see nsPath namespace documentation)
 
- /// 
 
- /// CPath is based on the Shell Lightweight Utility API, but modifies / and extends this functionality
 
- /// (and removes some quirks). It requires CString (see below why, and why this is not too bad).
 
- /// 
 
- /// \par Main Features:
 
- /// 
 
- /// CPath acts as a string class with special "understanding" and operations for path strings.
 
- /// 
 
- /// \code CPath path("c:\\temp"); \endcode
 
- /// constructs a path string, and does some default cleanup (trimming white space, removing quotes, etc.)
 
- /// The cleanup can be customized (see \ref nsPath::CPath::CPath "CPath Constructor", 
 
- ///  \ref nsPath::EPathCleanup "EPathCleanup"). You can pass an ANSI or UNICODE string.
 
- /// 
 
- /// \code path = path & "file.txt"; \endcode
 
- /// Appends "foo" to the path, making sure that the two segments are separated by exactly one backslash
 
- /// no matter if the parts end/begin with a backslash.
 
- /// 
 
- /// The following functions give you access to the individual elements of the path:
 
- /// 
 
- ///  - \ref nsPath::CPath::GetRoot "GetRoot"
 
- ///  - \ref nsPath::CPath::GetPath "GetPath"
 
- ///  - \ref nsPath::CPath::GetName "GetName"
 
- ///  - \ref nsPath::CPath::GetTitle "GetTitle"
 
- ///  - \ref nsPath::CPath::GetExtension "GetExtension"
 
- /// 
 
- /// \code CString s = path.GetStr() \endcode
 
- /// returns a CString that is cleaned up again (re-quoted if necessary, etc). GetBStr() returns an _bstr_t
 
- /// with the same features (that automatically casts to either ANSI or UNICODE).
 
- /// To retrieve the unmodified CPath string, you can rely on the \c operator \c LPCTSTR 
 
- /// 
 
- /// There's much more - see the full nsPath documentation for details!
 
- /// 
 
- ///	@sa MSDN library: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/shlwapi/path/pathappend.asp
 
- /// 
 
- /// \par Why not CPathT ?
 
- ///  -# the class is intended for a VC6 project that won't move to VC7 to soon
 
- ///  -# CPathT contains the same quirks that made me almost give up on the Shell Helper functions. 
 
- ///  -# I wanted the class to have additional features (such as the & operator, and automatic cleanup)
 
- /// 
 
- /// \par Why CString ?
 
- ///  -# The CString implementation provides a known performance (due to the guaranteed reference counted "copy 
 
- ///     on write" implementation). I consider this preferrable over the weaker guarantees made b STL, especially
 
- ///     when designing a  "convenient" class interface.
 
- ///  -# CString's ref counting mechanism is automatically reused by CPath, constructing a CPath from a CString
 
- ///     does not involve a copy operation.
 
- ///  -# CString is widely availble independent of MFC (WTL, custom implementations, "extract" macros are
 
- ///     available, and VC7 makes CString part of the ATL)
 
- /// 
 
- /// \note if you want to port to STL, it's probably easier to use a vector<TCHAR> instead of std:string 
 
- ///   to hold the data internally
 
- /// 
 
- /// \par Why _bstr_t ?
 
- /// To make implementation easier, the class internally works with "application native" strings (that is, 
 
- /// TCHAR strings - which are either ANSI or UNICODE depending on a compile setting). GetBStr provides
 
- /// conversion to ANSI or UNICODE, whichever is required.\n
 
- /// An independent implementation would return a temporary object with cast operators to LPCSTR and LPWSTR 
 
- /// - BUT _bstr_t does exactly that (admittedly, with some overhead). 
 
- /// 
 
- #include "stdafx.h"
 
- #include "Path.h"
 
- #include <shlwapi.h>    // Link to Shell Helper API
 
- #pragma comment(lib, "shlwapi.lib")
 
- #ifdef _DEBUG
 
- #undef THIS_FILE
 
- static char THIS_FILE[]=__FILE__;
 
- #define new DEBUG_NEW
 
- #endif
 
- namespace nsPath
 
- {
 
- /// contains helper classes for nsPath namespace
 
- /// 
 
- namespace nsDetail
 
- {
 
- // ==================================================================
 
- //  CStringLock
 
- // ------------------------------------------------------------------
 
- // 
 
- // Helper class for CString::GetBuffer and CString::ReleaseBuffer
 
- // \todo: consider moving to common utility 
 
- // \todo: additional debug verification on release
 
- //
 
- class CStringLock
 
- {
 
- public:
 
-     CString * m_string;
 
-     LPTSTR    m_buffer;
 
-     static LPTSTR NullBuffer;
 
- public:
 
-     CStringLock(CString & s) : m_string(&s)
 
-     {
 
-         m_buffer = m_string->GetBuffer(0);
 
-         // fixes an incompatibility with CStdString, see "NullBuffer" in .cpp 
 
-         if (!s.GetLength())
 
-             m_buffer = NullBuffer; 
 
-     }
 
-     CStringLock(CString & s, int minChars) : m_string(&s)
 
-     {
 
-         m_buffer = m_string->GetBuffer(minChars);
 
-         // fixes an incompatibility with CStdString, see "NullBuffer" in .cpp 
 
-         if (!s.GetLength() && !minChars)
 
-             m_buffer = NullBuffer; 
 
-     }
 
-     operator LPTSTR() { return m_buffer; }
 
-     void Release(int newLen = -1)
 
-     {
 
-         if (m_string)
 
-         {
 
-             m_string->ReleaseBuffer(newLen);
 
-             m_string = NULL;
 
-             m_buffer = NULL;
 
-         }
 
-     }
 
-     ~CStringLock()  { Release(); }
 
- };
 
- /// CStdString incompatibility:
 
- /// http://www.codeproject.com/string/stdstring.asp
 
- /// If the contained string is empty, CStdString.GetBuffer returns a pointer to a constant
 
- /// empty string, which may cause an access violation when I write the terminating zero
 
- /// (which is in my understanding implicitely allowed the way I read the MSDN docs)
 
- /// Solution: we return a pointer to another buffer
 
- TCHAR NullBufferData[1] = { 0 };
 
- LPTSTR CStringLock::NullBuffer = NullBufferData;
 
- // Helper class for Close-On-Return HKEY
 
- // \todo migrate template class 
 
- class CAutoHKEY
 
- {
 
- private:
 
-     CAutoHKEY const & operator =(CAutoHKEY const & ); // not implemented
 
-     CAutoHKEY(CAutoHKEY const &);   // not implemented
 
- protected:
 
-     HKEY    key;
 
- public:
 
-     CAutoHKEY() : key(0) {}
 
-     CAutoHKEY(HKEY key_) : key(key_) {}
 
-    ~CAutoHKEY()          { Close(); }
 
-     void Close()
 
-     {
 
-         if (key) 
 
-         {
 
-             RegCloseKey(key); 
 
-             key = 0;
 
-         }
 
-     }
 
-    HKEY * OutArg()
 
-    {
 
-        Close();
 
-        return &key;
 
-    }
 
-    operator HKEY() const { return key; }
 
- }; // CAutoHKEY
 
- /// Reads an environment variable into a CString
 
- CString GetEnvVar(LPCTSTR envVar)
 
- {
 
-     SetLastError(0);
 
-     // get length of buffer
 
-     DWORD result = GetEnvironmentVariable(envVar, _T(""), 0);
 
-     if (!result)
 
-         return CString();
 
-     CString s;
 
-     result = GetEnvironmentVariable(envVar, CStringLock(s, result), result);
 
-     return s;
 
- }
 
- /// Replace path root with environment variable
 
- /// If the beginning of \c s matches the value of the environment variable %envVar%,
 
- /// it is replaced with the %envVar% value
 
- /// (e.g. "C:\Windows" with "%windir%"
 
- /// \param s [CString &, in/out]: the string to modify
 
- /// \param envVar [LPCTSTR]: name of the environment variable
 
- /// \returns true if s was modified, false otherwise.
 
- bool EnvUnsubstRoot(CString & s, LPCTSTR envVar)
 
- {
 
-     // get environment value string
 
-     CString envValue = GetEnvVar(envVar);
 
-     if (!envValue.GetLength())
 
-         return false;
 
-     if (s.GetLength() >= envValue.GetLength() && 
 
-         _tcsnicmp(s, envValue, envValue.GetLength())==0)
 
-     {
 
-         CString modified = CString('%');
 
-         modified += envVar;
 
-         modified += '%';
 
-         modified += s.Mid(envValue.GetLength());
 
-         s = modified;
 
-         return true;
 
-     }
 
-     return false;
 
- }
 
-     
 
- } // namespace nsPath::nsDetail
 
- using namespace nsDetail;
 
- const TCHAR Backslash = '\\';
 
- // ==============================================
 
- //  Trim
 
- // ----------------------------------------------
 
- /// Trims whitespaces from left and right side. 
 
- /// \param s [CString]: String to modify in-place.
 
- void Trim(CString & string)
 
- {
 
-     if (_istspace(GetFirstChar(string)))
 
-         string.TrimLeft();
 
-     if (_istspace(GetLastChar(string)))
 
-         string.TrimRight();
 
- }
 
- // ==================================================================
 
- //  GetDriveLetter(ch)
 
- // ------------------------------------------------------------------
 
- /// checks if the specified letter \c ch is a drive letter, and casts it to uppercase
 
- /// 
 
- /// \returns [TCHAR]: if \c is a valid drive letter (A..Z, or a..z), returns the drive letter
 
- ///     cast to uppercase (A..Z). >Otherwise, returns 0
 
- TCHAR GetDriveLetter(TCHAR ch)
 
- {
 
-     if ( (ch >= 'A' && ch <= 'Z'))
 
-         return ch;
 
-     if (ch >= 'a' && ch <= 'z')
 
-             return (TCHAR) (ch - 'a' + 'A');
 
-     return 0;
 
- }
 
- // ==================================================================
 
- //  GetDriveLetter(string)
 
- // ------------------------------------------------------------------
 
- /// returnd the drive letter of a path.
 
- /// The drive letter returned is always uppercase ('A'.`'Z'). 
 
- /// \param s [LPCTSTR]: the path string
 
- /// \returns [TCHAR]: the drive letter, converted to uppercase, if the path starts with an 
 
- ///                     X: drive specification. Otherwise, returns 0
 
- // 
 
- TCHAR GetDriveLetter(LPCTSTR s)
 
- {
 
-     if (s == NULL || *s == 0 || s[1] != ':')
 
-         return 0;
 
-     return GetDriveLetter(s[0]);
 
- }
 
- // ==================================================================
 
- //  QuoteSpaces
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Quotes the string if it is not already quoted, and contains spaces
 
- /// see also MSDN: \c PathQuoteSpaces
 
- /// \note If the string is already quoted, an additional pair of quotes is added.
 
- /// \param str [CString const &]: path string to add quotes to
 
- /// \returns [CString]: path string with quotes added if required
 
- // 
 
- CString QuoteSpaces(CString const & str)
 
- {
 
-     // preserve refcounting if no changes will be made
 
-     if (str.Find(' ')>=0)  // if the string contains any spaces...
 
-     {
 
-         CString copy(str);
 
-         CStringLock buffer(copy, copy.GetLength() + 2);
 
-         PathQuoteSpaces(buffer);
 
-         buffer.Release();
 
-         return copy;
 
-     }
 
-     return str; // unmodified
 
- }
 
- /// helper function for GetRootType
 
- inline ERootType GRT_Return(ERootType type, int len, int * pLen)
 
- {
 
-    if (pLen)
 
-       *pLen = len;
 
-    return type;
 
- }
 
- // ==================================================================
 
- //  GetRootType
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// returns the type of the path root, and it's length.
 
- /// For supported root types, see \ref nsPath::ERootType "ERootType" enumeration
 
- /// 
 
- /// \param path [LPCTSTR]: The path to analyze
 
- /// \param pLen [int *, out]: if not NULL, receives the length of the root part (in characters)
 
- /// \param greedy [bool=true]: Affects len and type of the following root types:
 
- ///     - \c "\\server\share" : with greedy=true, it is treated as one \c rtServerShare root,
 
- ///       otherwise, it is treated as \c rtServer root
 
- ///     
 
- /// \returns [ERootType]: type of the root element 
 
- ///         
 
- /// 
 
- ERootType GetRootType(LPCTSTR path, int * pLen, bool greedy)
 
- {
 
- //   ERootType   type = rtNoRoot;
 
- //   int len = 0;
 
-    const TCHAR * invalidChars = _T("\\/:*/\"<>|");
 
-    const TCHAR bk = '\\';
 
-    if (!path || !*path)
 
-       return GRT_Return(rtNoRoot, 0, pLen);
 
-    // drive spec
 
-    if (_istalpha(*path) && path[1] == ':')
 
-    {
 
-       if (path[2] == bk) { return GRT_Return(rtDriveRoot, 3, pLen); }
 
-       else               { return GRT_Return(rtDriveCur, 2, pLen); }
 
-    }
 
-    // anything starting with two backslashes
 
-    if (path[0] == bk && path[1] == bk)
 
-    {
 
-       // UNC long path?
 
-       if (path[2] == '?' && path[3] == bk)
 
-       {
 
-          int extraLen = 0;
 
-          GetRootType(path+4, &extraLen) ;
 
-          return GRT_Return(rtLongPath, 4 + extraLen, pLen);
 
-       }
 
-       // position of next backslash or colon
 
-       int len = 2 + (int)_tcscspn(path+2, invalidChars);
 
-       TCHAR const * end = path+len;
 
-       // server only, no backslash
 
-       if (*end == 0) 
 
-          return GRT_Return(rtServerOnly, len, pLen);
 
-       // server only, terminated with backslash
 
-       if (*end == bk && end[1] == 0) 
 
-          return GRT_Return(rtServerOnly, len+1, pLen); 
 
-       // server, backslash, and more...
 
-       if (*end == bk)
 
-       {
 
-           if (!greedy)  // return server only
 
-               return GRT_Return(rtServer, len, pLen);
 
-          len += 1 + (int)_tcscspn(end+1, invalidChars);
 
-          end = path + len;
 
-          // server, share, no backslash
 
-          if (*end == 0) 
 
-             return GRT_Return(rtServerShare, len, pLen); 
 
-          // server, share, backslash
 
-          if (*end == '\\') 
 
-             return GRT_Return(rtServerShare, len+1, pLen);
 
-       }
 
-       // fall through to other tests
 
-    }
 
-    int len = (int)_tcscspn(path, invalidChars);
 
-    TCHAR const * end = path + len;
 
-    // (pseudo) protocol:
 
-    if (len > 0 && *end == ':')
 
-    {
 
-       if (end[1] == '/' && end[2] == '/') 
 
-          return GRT_Return(rtProtocol, len+3, pLen);
 
-       else 
 
-          return GRT_Return(rtPseudoProtocol, len+1, pLen); 
 
-    }
 
-    return GRT_Return(rtNoRoot, 0, pLen);
 
- }
 
- // ==================================================================
 
- //  CPath::Trim
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// removes leading and trailing spaces.
 
- // 
 
- CPath &  CPath::Trim()
 
- {
 
-     nsPath::Trim(m_path);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::Unquote
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// removes (double) quotes from around the string
 
- // 
 
- CPath & CPath::Unquote()
 
- {
 
-     if (GetFirstChar(m_path) == '"' && GetLastChar(m_path) == '"')
 
-         m_path = m_path.Mid(1, m_path.GetLength()-2);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::Canonicalize
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Collapses "\\..\\" and "\\.\\" path parts.
 
- /// see also MSDN: PathCanonicalize
 
- /// \note
 
- /// PathCanonicalize works strange on relative paths like "..\\..\\x" -
 
- /// it is changed to "\x", which is clearly not correct. SearchAndQualify is affected
 
- /// by the same problem
 
- /// \todo handle this separately?
 
- /// 
 
- /// \par Implementation Differences
 
- /// \c PathCanonicalize does turn an empty path into a single backspace.
 
- /// CPath::Canonicalize does not modify an empty path.
 
- // 
 
- CPath & CPath::Canonicalize()
 
- {
 
-     if (!m_path.GetLength())  // PathCanonicalize turns an empty path into "\\" - I don't want this..
 
-         return *this;
 
-     if (m_path.Find(_T("\\."))>=0)
 
-     {
 
-         CString target = m_path;  // PathCanonicalize requires a copy to work with
 
-         CStringLock buffer(target, m_path.GetLength()+2); // might add a backslash sometimes !
 
-         PathCanonicalize(buffer, m_path);
 
-         buffer.Release();
 
-         m_path = target;
 
-     }
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::ShrinkXXLPath
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Removes an "Extra long file name" specification
 
- /// Unicode API allows pathes longer than MAX_PATH, if they start with "\\\\?\\". This function
 
- /// removes such a specification if present. See also MSDN: "File Name Conventions".
 
- // 
 
- CPath & CPath::ShrinkXXLPath()
 
- {
 
-     if (m_path.GetLength() >= 6 &&   // at least 6 chars for [\\?\C:]
 
-         _tcsncmp(m_path, _T("\\\\?\\"), 4) == 0)
 
-     {
 
-         LPCTSTR path = m_path;
 
-         if (nsPath::GetDriveLetter(path[4]) != 0 && path[5] == ':')
 
-             m_path = m_path.Mid(4);
 
-         else if (m_path.GetLength() >= 8)  // at least 8 chars for [\\?\UNC\]
 
-         {
 
-             if (_tcsnicmp(path + 4, _T("UNC\\"), 4) == 0)
 
-             {
 
-                 // remove chars [2]..[7]
 
-                 int len = m_path.GetLength() - 8; // 
 
-                 CStringLock buffer(m_path);
 
-                 memmove(buffer+2, buffer+8, len * sizeof(TCHAR));
 
-                 buffer.Release(len+2);
 
-             }
 
-         }
 
-     }
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::Assign
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// Assigns a string to the path object, optionally applying cleanup of the path name
 
- ///
 
- /// \param str [CString const &]: The string to assign to the path
 
- /// \param cleanup [DWORD, default = epc_Default]: operations to apply to the path
 
- /// \returns [CPath &]: reference to the path object itself
 
- /// 
 
- /// see CPath::Clean for a description of the cleanup options
 
- // 
 
- CPath & CPath::Assign(CString const & str, DWORD cleanup)
 
- {
 
-     m_path = str;
 
-     Clean(cleanup);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::MakePretty
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Turns an all-uppercase path to all -lowercase. A path containing any lowercase 
 
- /// character is not modified.
 
- /// (This is Microsoft#s idea of prettyfying a path. I don't know what to say)
 
- /// 
 
- CPath & CPath::MakePretty()
 
- {
 
-     CStringLock buffer(m_path);
 
-     PathMakePretty(buffer);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::Clean
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Applies multiple "path cleanup" operations
 
- /// 
 
- /// \param cleanup [DWORD]: a combination of zero or more nsPath::EPathCleanup flags (see below)
 
- /// \returns [CPath &]: a reference to the path object
 
- /// 
 
- /// The following cleanup operations are defined:
 
- ///     - \c epcRemoveArgs: call PathRemoveArgs to remove arguments
 
- ///     - \c epcRemoveIconLocation: call to PathParseIconLocation to remove icon location
 
- ///     - \c \b epcTrim: trim leading and trailing whitespaces
 
- ///     - \c \b epcUnquote: remove quotes 
 
- ///     - \c \b epcTrimInQuote: trim whitespaces again after unqouting.
 
- ///     - \c \b epcCanonicalize: collapse "\\.\\" and "\\..\\" segments
 
- ///     - \c \b epcRemoveXXL: remove an "\\\\?\\" prefix for path lengths exceeding \c MAX_PATH
 
- ///     - \c epcSlashToBackslash: (not implemented)  change forward slashes to back slashes (does not modify a "xyz://" protocol root)
 
- ///     - \c epcMakePretty: applies PathMakePretty
 
- ///     - \c epcRemoveArgs: remove command line arguments
 
- ///     - \c epcRemoveIconLocation: remove icon location number
 
- ///     - \c \b epcExpandEnvStrings: Expand environment strings
 
- /// 
 
- /// This function is called by most assignment constructors and assignment operators, using
 
- /// the \c epc_Default cleanup options (typically those set in bold above, but check the enum 
 
- /// documentation in case I forgot to update this one).
 
- /// 
 
- /// Constructors and Assignment operators that take a string (\c LPCTSTR, \c LPCTSTR, \c CString) call
 
- /// this function. Copy or assignment from another \c CPath object does not call this function.
 
- /// 
 
- /// 
 
- //
 
- CPath & CPath::Clean(DWORD cleanup)
 
- {
 
-     if (cleanup & epcRemoveArgs)
 
-     {
 
-        // remove leading spaces, otherwise PathRemoveArgs considers everything a space
 
-        if (cleanup & epcTrim)
 
-           m_path.TrimLeft();  
 
-        
 
-         PathRemoveArgs(CStringLock(m_path));
 
-     }
 
-     if (cleanup & epcRemoveIconLocation)
 
-         PathParseIconLocation(CStringLock(m_path));
 
-     if (cleanup & epcTrim)
 
-         Trim();
 
-     if (cleanup & epcUnquote)
 
-     {
 
-         Unquote();
 
-         if (cleanup & epcTrimInQuote)
 
-             Trim();
 
-     }
 
-     if (cleanup & epcExpandEnvStrings)
 
-         ExpandEnvStrings();
 
-     if (cleanup & epcCanonicalize)
 
-         Canonicalize();
 
-     if (cleanup & epcRemoveXXL)
 
-         ShrinkXXLPath();
 
-     if (cleanup & epcSlashToBackslash)
 
-        m_path.Replace('/', '\\');
 
-     if (cleanup & epcMakePretty)
 
-         MakePretty();
 
-     return *this;
 
- }
 
- // Extractors
 
- CString CPath::GetStr(DWORD packing) const
 
- {
 
-     CString str = m_path;
 
- //    _ASSERTE(!(packing & eppAutoXXL));   // TODO
 
-     if (packing & eppAutoQuote)
 
-         str = QuoteSpaces(str);
 
-     if (packing & eppBackslashToSlash)
 
-         str.Replace('\\', '/');  // TODO: suport server-share and protocol correctly
 
-     return str;
 
- }
 
- _bstr_t CPath::GetBStr(DWORD packing) const
 
- {
 
-     return _bstr_t( GetStr(packing).operator LPCTSTR());
 
- }
 
- // ==================================================================
 
- //  Constructors 
 
- // ------------------------------------------------------------------
 
- // 
 
- CPath::CPath(LPCSTR path) : m_path(path)    
 
- { 
 
-     Clean();
 
- }
 
- CPath::CPath(LPCWSTR path) : m_path(path)
 
- {
 
-     Clean();
 
- }
 
- CPath::CPath(CString const & path) : m_path(path)
 
- {
 
-     Clean();
 
- }
 
- CPath::CPath(CPath const & path) : m_path(path.m_path) {}  // we assume it is already cleaned
 
- CPath::CPath(CString const & path,DWORD cleanup) : m_path(path)
 
- {
 
-     Clean(cleanup);
 
- }
 
- // ==================================================================
 
- //  Assignment
 
- // ------------------------------------------------------------------
 
- // 
 
- CPath & CPath::operator=(LPCSTR rhs)
 
- {
 
- #ifndef _UNICODE    // avoidf self-assignment
 
-     if (rhs == m_path.operator LPCTSTR())
 
-         return *this;
 
- #endif
 
-     m_path = rhs;
 
-     Clean();
 
-     return *this;
 
- }
 
- CPath & CPath::operator=(LPCWSTR rhs)
 
- {
 
- #ifdef _UNICODE // avoid self-assignment
 
-     if (rhs == m_path.operator LPCTSTR())
 
-         return *this;
 
- #endif
 
-     m_path = rhs;
 
-     Clean();
 
-     return *this;
 
- }
 
- CPath & CPath::operator=(CString const & rhs)
 
- {
 
-     // our own test for self-assignment, so we can skip CClean in this case
 
-     if (rhs.operator LPCTSTR() == m_path.operator LPCTSTR())
 
-         return *this;
 
-     m_path = rhs;
 
-     Clean();
 
-     return *this;
 
- }
 
- CPath & CPath::operator=(CPath const & rhs)
 
- {
 
-     if (rhs.m_path.operator LPCTSTR() == m_path.operator LPCTSTR())
 
-         return *this;
 
-     m_path = rhs;
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::operator &=
 
- // ------------------------------------------------------------------
 
- ///
 
- /// appends a path segment, making sure it is separated by exactly one backslash
 
- /// \returns reference to the modified \c CPath instance.
 
- // 
 
- CPath & CPath::operator &=(LPCTSTR rhs)
 
- {
 
-     return CPath::Append(rhs);
 
- }
 
- // ==================================================================
 
- //  CPath::AddBackslash
 
- // ------------------------------------------------------------------
 
- ///
 
- /// makes sure the contained path is terminated with a backslash
 
- /// \returns [CPath &]: reference to the modified path
 
- /// see also: \c PathAddBackslash Shell Lightweight Utility API
 
- // 
 
- CPath & CPath::AddBackslash()
 
- {
 
-     if (GetLastChar(m_path) != Backslash)
 
-     {
 
-         CStringLock buffer(m_path, m_path.GetLength()+1);
 
-         PathAddBackslash(buffer);
 
-     }
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::RemoveBackslash
 
- // ------------------------------------------------------------------
 
- ///
 
- /// If the path ends with a backslash, it is removed.
 
- /// \returns [CPath &]: a reference to the modified path.
 
- // 
 
- CPath & CPath::RemoveBackslash()
 
- {
 
-     if (GetLastChar(m_path) == Backslash)
 
-     {
 
-         CStringLock buffer(m_path, m_path.GetLength()+1);
 
-         PathRemoveBackslash(buffer);
 
-     }
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::Append
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Concatenates two paths
 
- /// \par Differences to \c PathAddBackslash:
 
- /// Unlike \c PathAddBackslash, \c CPath::Append appends a single backslash if rhs is empty (and
 
- /// the path does not already end with a backslash)
 
- /// 
 
- /// \param rhs [LPCTSTR]: the path component to append
 
- /// \returns [CPath &]: reference to \c *this
 
- // 
 
- CPath & CPath::Append(LPCTSTR rhs)
 
- {
 
-     if (rhs == NULL || *rhs == '\0')
 
-     {
 
-         AddBackslash();
 
-     }
 
-     else
 
-     {
 
-         int rhsLen = rhs ? (int)_tcslen(rhs) : 0;
 
-         CStringLock buffer(m_path, m_path.GetLength()+rhsLen+1);
 
-         PathAppend(buffer, rhs);
 
-     }
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::ShellGetRoot
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Retrieves the Root of the path, as returned by \c PathSkipRoot.
 
- /// 
 
- /// \note For a more detailed (but "hand-made") implementation see GetRoot and GetRootType.
 
- /// 
 
- /// The functionality of \c PathSkipRoot is pretty much limited:
 
- ///     - Drives ("C:\\" but not "C:")
 
- ///     - UNC Shares ("\\\\server\\share\\", but neither "\\\\server" nor "\\\\server\\share")
 
- ///     - UNC long drive ("\\\\?\\C:\\")
 
- /// 
 
- /// \returns [CString]: the rot part of the string
 
- /// 
 
- CString CPath::ShellGetRoot() const
 
- {
 
-     LPCTSTR path = m_path;
 
-     LPCTSTR rootEnd = PathSkipRoot(path);
 
-     if (rootEnd == NULL)
 
-         return CString();
 
-     return m_path.Left((int)(rootEnd - path));
 
- }
 
- // ==================================================================
 
- //  GetRootType
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// returns the type of the root, and it's length.
 
- /// For supported tpyes, see \ref nsPath::ERootType "ERootType".
 
- /// see also \ref nsPath::GetRootType
 
- /// 
 
- ERootType CPath::GetRootType(int * len, bool greedy) const
 
- {
 
-    return nsPath::GetRootType(m_path, len, greedy);
 
- }
 
- // ==================================================================
 
- //  GetRoot
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// \param rt [ERootType * =NULL]: if given, receives the type of the root segment.
 
- /// \return [CString]: the root, as a string.
 
- /// 
 
- /// For details which root types are supported, and how the length is calculated, see
 
- /// \ref nsPath::ERootType "ERootType" and \ref nsPath::GetRootType
 
- /// 
 
- CString CPath::GetRoot(ERootType * rt, bool greedy) const
 
- {
 
-    int len = 0;
 
-    ERootType rt_ = nsPath::GetRootType(m_path, &len, greedy);
 
-    if (rt)
 
-       *rt = rt_;
 
-    return m_path.Left(len);
 
- }
 
- // ==================================================================
 
- //  CPath::SplitRoot
 
- // ------------------------------------------------------------------
 
- ///
 
- /// removes and returns the root element from the contained path
 
- /// You can call SplitRoot repeatedly to retrieve the path segments in order
 
- /// 
 
- /// \param rt [ERootType * =NULL] if not NULL, receives the type of the root element
 
- ///     note: the root element type can be recognized correctly only for the first segment
 
- /// \returns [CString]: the root element of the original path
 
- /// \par Side Effects: root element removed from contained path
 
- /// 
 
- CString CPath::SplitRoot(ERootType * rt)
 
- {
 
-     CString head;
 
-  
 
-    if (!m_path.GetLength())
 
-       return head;
 
-    int rootLen = 0;
 
-    ERootType rt_ = nsPath::GetRootType(m_path, &rootLen, false);
 
-    if (rt)
 
-        *rt = rt_;
 
-     if (rt_ == rtNoRoot) // not a typical root element
 
-     {
 
-         int start = 0;
 
-         if (GetFirstChar(m_path) == '\\') // skip leading backslash (double backslas handled before)
 
-             ++start;
 
-         int ipos = m_path.Find('\\', start);
 
-         if (ipos < 0)
 
-         {
 
-             head = start ? m_path.Mid(start) : m_path;
 
-             m_path.Empty();
 
-         }
 
-         else
 
-         {
 
-             head = m_path.Mid(start, ipos-start);
 
-             m_path = m_path.Mid(ipos+1);
 
-         }
 
-     }
 
-     else
 
-     {
 
-         head = m_path.Left(rootLen);
 
-         if (rootLen < m_path.GetLength() && m_path[rootLen] == '\\')
 
-             ++rootLen;
 
-         m_path = m_path.Mid(rootLen);
 
-     }
 
-     return head;
 
- }
 
- // ==================================================================
 
- //  CPath::GetPath
 
- // ------------------------------------------------------------------
 
- ///
 
- /// returns the path (without file name and extension)
 
- /// \param includeRoot [bool=true]: if \c true (default), the root is included in the retuerned path.
 
- /// \returns [CPath]: the path, excluding file name and 
 
- /// \par Implementation:
 
- ///     Uses \c PathFindFileName, and \c PathSkipRoot
 
- /// \todo
 
- ///     - in "c:\\temp\\", \c PathFindFileName considers "temp\\" to be a file name and returns
 
- ///       "c:\\" only. This is clearly not my idea of a path
 
- ///     - when Extending \c CPath::GetRoot, this function should be adjusted as well
 
- /// 
 
- // 
 
- CPath CPath::GetPath(bool includeRoot ) const
 
- {
 
-     LPCTSTR path = m_path;
 
-     LPCTSTR fileName = PathFindFileName(path);
 
-     if (fileName == NULL) // seems to find something in any way!
 
-         fileName = path + m_path.GetLength();
 
-     LPCTSTR rootEnd = includeRoot ? NULL : PathSkipRoot(path);
 
-     CPath ret;
 
-     if (rootEnd == NULL)  // NULL if root should be included, or no root is found
 
-         return m_path.Left((int)(fileName-path));
 
-     else
 
-         return m_path.Mid((int)(rootEnd-path), (int)(fileName-rootEnd));
 
- }
 
- // ==================================================================
 
- //  CPath::GetName
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [CString]: the file name of the path
 
- /// \par Differences to \c PathFindFileName:
 
- /// \c PathFindFileName, always treats the last path segment as file name, even if it ends with a backslash.
 
- /// \c GetName treats such a string as not containing a file name\n
 
- /// \b Example: for "c:\\temp\\", \c PathFindFileName finds "temp\\" as file name. \c GetName returns
 
- /// an empty string.
 
- CString CPath::GetName() const
 
- {
 
-     // fix treating final path segments as file name
 
-     if (GetLastChar(m_path) == '\\')
 
-         return CString();
 
-     LPCTSTR path = m_path;
 
-     LPCTSTR fileName = PathFindFileName(path);
 
-     if (fileName == NULL)
 
-         return CString();
 
-     return m_path.Mid((int)(fileName-path));
 
- }
 
- // ==================================================================
 
- //  CPath::GetTitle
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [CString]: the file title, without path or extension
 
- // 
 
- CString CPath::GetTitle() const
 
- {
 
-     LPCTSTR path = m_path;
 
-     LPCTSTR fileName = PathFindFileName(path);
 
-     LPCTSTR ext      = PathFindExtension(path);
 
-     if (fileName == NULL)
 
-         return CString();
 
-     if (ext == NULL)
 
-         return m_path.Mid((int)(fileName-path));
 
-     return m_path.Mid((int)(fileName-path), (int)(ext-fileName));
 
- }
 
- // ==================================================================
 
- //  CPath::GetExtension
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [CString]: file extension
 
- /// \par Differences to \c PathFindExtension
 
- /// Unlike \c PathFindExtension, the period is not included in the extension string
 
- // 
 
- CString CPath::GetExtension() const
 
- {
 
-     LPCTSTR path = m_path;
 
-     LPCTSTR ext      = PathFindExtension(path);
 
-     if (ext == NULL)
 
-         return CString();
 
-     if (*ext == '.')        // skip "."
 
-         ++ext;
 
-     return m_path.Mid((int)(ext-path));
 
- }
 
- // ==================================================================
 
- //  CPath::AddExtension
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Appends the specified extension to the path. The path remains unmodified if it already contains
 
- /// an extension (that is, the part behind the last backslash contains a period)
 
- /// \par Difference to \c PathAddExtension
 
- /// Unlike CPath::AddExtension adds a period, if \c extension does not start with one
 
- /// \param extension [LPCTSTR]: the extension to append
 
- /// \param len [int, default=-1]: (optional) length of \c extension in characters, not counting the
 
- ///     terminating zero. This argument is only for avoiding a call to _tcslen if the caller already
 
- ///     knows the length of the string. The string still needs to be zero-terminated and contain exactly 
 
- ///     \c len characters. 
 
- /// \returns [CPath &]: reference to the modified Path object
 
- // 
 
- CPath & CPath::AddExtension(LPCTSTR extension, int len)
 
- {
 
-     if (!extension)
 
-         return AddExtension(_T(""), 0);
 
-     if (*extension != '.')
 
-     {
 
-         CString s = CString('.') + extension;
 
-         return AddExtension( s, s.GetLength());
 
-     }
 
-     if (len < 0)
 
-         return AddExtension(extension, (int)_tcslen(extension));
 
-     int totalLen = m_path.GetLength() + len;  // already counts the period
 
-     PathAddExtension(CStringLock(m_path, totalLen), extension);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::RemoveExtension
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Removes the extension of the path, if it has any.
 
- /// \returns [CPath &]: reference to the modified path object
 
- // 
 
- CPath& CPath::RemoveExtension()
 
- {
 
-     PathRemoveExtension(CStringLock(m_path));
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::RenameExtension
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Replaces an existing extension with the new given extension.
 
- /// If the path has no extension, it is appended.
 
- /// \par Difference  to \c PathRenameExtension:
 
- /// Unlike PathRenameExtension, \c newExt needs not start with a period.
 
- /// \param newExt [LPCTSTR ]: newextension
 
- /// \returns [CPath &]: reference to the modified path string
 
- // 
 
- CPath & CPath::RenameExtension(LPCTSTR newExt)
 
- {
 
-     if (newExt == NULL || *newExt != '.')
 
-     {
 
-         RemoveExtension();
 
-         return AddExtension(newExt);
 
-     }
 
-     int maxLen = m_path.GetLength() + (int)_tcslen(newExt) + 1;
 
-     PathRenameExtension(CStringLock(m_path, maxLen), newExt);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  ::RemoveFileSpec
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Removes the file specification (amd extension) from the path.
 
- /// \returns [CPath &]: a reference to the modified path object
 
- // 
 
- CPath & CPath::RemoveFileSpec()
 
- {
 
-     PathRemoveFileSpec(CStringLock(m_path));
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::SplitArgs
 
- // ------------------------------------------------------------------
 
- ///
 
- /// (static) Separates a path string from command line arguments
 
- /// 
 
- /// \param path_args [CString const &]: the path string with additional command line arguments
 
- /// \param args [CString *, out]: if not \c NULL, receives the arguments separated from the path
 
- /// \param cleanup [DWORD, = epc_Default]: the "cleanup" treatment to apply to the path, see \c CPath::Clean
 
- /// \returns [CPath]: a new path without the arguments
 
- // 
 
- CPath SplitArgs(CString const & path_args, CString * args, DWORD cleanup)
 
- {
 
-    CString pathWithArgs = path_args;
 
-    // when "trim" is given, trim left spaces, so PathRemoveArgsworks correctly and returns 
 
-    // the path part with the correct length
 
-    if (cleanup & epcTrim)
 
-       pathWithArgs.TrimLeft();
 
-     // assign with only removing the arguments
 
-     CPath path(pathWithArgs, epcRemoveArgs);
 
-     // cut non-argument part away from s
 
-     if (args)
 
-     {
 
-         *args = pathWithArgs.Mid(path.GetLength());
 
-         args->TrimLeft();
 
-     }
 
-     // now, clean the contained string (last since it might shorten the path string)
 
-     path.Clean(cleanup &~ epcRemoveArgs);
 
-     return path;
 
- }
 
- // ==================================================================
 
- //  CPath::SplitIconLocation
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// (static) Splits a path string containing an icon location into path and icon index
 
- ///
 
- /// \param path_icon [CString const &]: the string containing an icon location
 
- /// \param pIcon [int *, NULL]: if not NULL, receives the icon index 
 
- /// \param cleanup [DWORD, epc_Default]: additional cleanup to apply to the returned path
 
- /// \returns [CPath]: the path contained in \c path_icon (without the icon location)
 
- // 
 
- CPath SplitIconLocation(CString const & path_icon, int * pIcon, DWORD cleanup)
 
- {
 
-     CString strpath = path_icon;
 
-     int icon = PathParseIconLocation( CStringLock(strpath) );
 
-     if (pIcon)
 
-         *pIcon = icon;
 
-     return CPath(strpath, cleanup & ~epcRemoveIconLocation);
 
- }
 
- // ==================================================================
 
- //  CPath::BuildRoot
 
- // ------------------------------------------------------------------
 
- ///
 
- /// (static) Creates a root path from a drive index (0..25)
 
- /// \param driveNumber [int]: Number of the drive, 0 == 'A', etc.
 
- /// \returns [CPath]: a path consisitng only of a drive root
 
- // 
 
- CPath BuildRoot(int driveNumber)
 
- {
 
-     CString strDriveRoot;
 
-     ::PathBuildRoot(CStringLock(strDriveRoot, 3), driveNumber);
 
-     return CPath(strDriveRoot, 0);
 
- }
 
- // ==================================================================
 
- //  CPath::GetModuleFileName
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Returns the path of the dspecified module handle
 
- /// Path is limited to \c MAX_PATH characters
 
- /// see Win32: GetModuleFileName for more information
 
- /// \param module [HMODULE =NULL ]: DLL module handle, or NULL for path to exe
 
- /// \returns [CPath]: path to the specified module, or to the application exe if \c module==NULL.
 
- ///         If an error occurs, the function returrns an empty string. 
 
- ///         Call \c GetLastError() for more information.
 
- /// 
 
- CPath GetModuleFileName(HMODULE module)
 
- {
 
-     CString path;
 
-     DWORD ok = ::GetModuleFileName(module, CStringLock(path, MAX_PATH), MAX_PATH+1);
 
-     if (ok == 0)
 
-         return CPath();
 
-     return CPath(path);
 
- }
 
- // ==================================================================
 
- //  CPath::GetCurrentDirectory
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [CPath]: the current directory, se Win32: \c GetCurrentDirectory.
 
- /// \remarks
 
- /// If an error occurs the function returns an empty string. More information is available
 
- /// through \c GetLastError.
 
- /// 
 
- CPath GetCurrentDirectory()
 
- {
 
-     CString path;
 
-     CStringLock buffer(path, MAX_PATH);
 
-     DWORD chars = ::GetCurrentDirectory(MAX_PATH+1, buffer);
 
-     buffer.Release(chars);
 
-     return CPath(path);
 
- }
 
- // ==================================================================
 
- //  CPath::GetCommonPrefix
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Returns the common prefix of this path and the given other path,
 
- /// e.g. for "c:\temp\foo\foo.txt" and "c:\temp\bar\bar.txt", it returns
 
- /// "c:\temp".
 
- /// \param secondPath [LPCTSTR]: the path to compare to
 
- /// \returns [CPath]: a new path, containing the part that is identical
 
- // 
 
- CPath CPath::GetCommonPrefix(LPCTSTR secondPath)
 
- {
 
-     CString prefix;
 
-     PathCommonPrefix(m_path, secondPath, CStringLock(prefix, m_path.GetLength()));
 
-     return CPath(prefix, 0);
 
- }
 
- // ==================================================================
 
- //  CPath::GetDriveNumber
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [int]: the driver number (0..25 for A..Z), or -1 if the 
 
- ///     path does not start with a drive letter
 
- // 
 
- int CPath::GetDriveNumber()
 
- {
 
-     return PathGetDriveNumber(m_path);
 
- }
 
- // ==================================================================
 
- //  CPath::GetDriveLetter
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [TCHAR]: the drive letter in uppercase, or 0
 
- // 
 
- TCHAR CPath::GetDriveLetter()
 
- {
 
-     int driveNum = GetDriveNumber();
 
-     if (driveNum < 0)
 
-         return 0;
 
-     return (TCHAR)(driveNum + 'A');
 
- }
 
- // ==================================================================
 
- //  CPath::RelativePathTo
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Determines a relative path from the contained path to the specified \c pathTo
 
- /// \par Difference to \c PathRelativeTo:
 
- ///  - instead of a file attributes value, you specify a flag (this is a probelm only
 
- ///     if the function supports other attribues than FILE_ATTRIBUTE_DIRECTORY in the future)
 
- ///  - no flag / attribute is specified for the destination (it does not seem to make a difference)
 
- /// \param pathTo [LPCTSTR]: the target path or drive
 
- /// \param srcIsDir [bool =false]: determines whether the current path is as a directory or a file
 
- /// \returns [CPath]: a relative path from this to \c pathTo
 
- // 
 
- CPath CPath::RelativePathTo(LPCTSTR pathTo,bool srcIsDir)
 
- {
 
-     CString path;
 
-     if (!pathTo)
 
-         return CPath();
 
-     // maximum length estimate: 
 
-     // going up to the root of a path like "c:\a\b\c\d\e", and then append the entire to path
 
-     int maxLen = 3*m_path.GetLength() / 2 +1  + (int)_tcslen(pathTo); 
 
-     PathRelativePathTo( CStringLock(path, maxLen), 
 
-                         m_path, 
 
-                         srcIsDir ? FILE_ATTRIBUTE_DIRECTORY : 0,
 
-                         pathTo, 0);
 
-     return CPath(path, 0);
 
- }
 
- // ==================================================================
 
- //  MakeRelative
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// Of the path contained is below \c basePath, it is made relative.
 
- /// Otherwise, it remains unmodified. 
 
- /// 
 
- /// Unlike RelativePathTo, the path is made relative only if the base path
 
- /// matches completely, and does not generate ".." elements.
 
- /// 
 
- /// \return [bool] true if the path was modified, false otherwise.
 
- /// 
 
- bool CPath::MakeRelative(CPath const & basePath)
 
- {
 
-    CPath basePathBS = basePath;
 
-    basePathBS.AddBackslash(); // add backslash so that "c:\a" is not a base path for "c:\alqueida\files"
 
-    if (m_path.GetLength() > basePathBS.GetLength())
 
-    {
 
-       if (0 == _tcsnicmp(basePathBS, m_path, basePathBS.GetLength()))
 
-       {
 
-          m_path = m_path.Mid(basePathBS.GetLength());
 
-          return true;
 
-       }
 
-    }
 
-    return false;
 
- }
 
- // ==================================================================
 
- //  MakeAbsolute
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// If the contained path is relative, it is prefixed by \c basePath.
 
- /// Otherwise it remains unmodified.
 
- /// 
 
- /// Use: as anti-MakeRelative.
 
- /// 
 
- bool CPath::MakeAbsolute(CPath const & basePath)
 
- {
 
-    if (IsRelative())
 
-    {
 
-       m_path = basePath & m_path;
 
-       return true;
 
-    }
 
-    return false;
 
- }
 
- // ==================================================================
 
- //  CPath::MatchSpec
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Checks if the path matches a certain specification
 
- /// \param spec [LPCTSTR]: File specification (like "*.txt")
 
- /// \returns [bool]: true if the path matches the specification
 
- // 
 
- bool CPath::MatchSpec(LPCTSTR spec)
 
- {
 
-     return PathMatchSpec(m_path, spec) != 0;
 
- }
 
- // ==================================================================
 
- //  CPath::ExpandEnvStrings
 
- // ------------------------------------------------------------------
 
- ///
 
- /// replaces environment string references with their current value.
 
- /// See MSDN: \c ExpandEnvironmentStrings for more information
 
- /// \returns [CPath &]: reference to the modified path
 
- // 
 
- CPath & CPath::ExpandEnvStrings()
 
- {
 
-     CString target;
 
-     DWORD len = m_path.GetLength();
 
-     DWORD required = ExpandEnvironmentStrings(
 
-                 m_path,
 
-                 CStringLock(target, len), len+1);
 
-     if (required > len+1)
 
-         ExpandEnvironmentStrings(
 
-                 m_path,
 
-                 CStringLock(target, required), required+1);
 
-     m_path = CPath(target, 0);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::GetCompactStr
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Inserts ellipses so the path fits into the specified number of pixels
 
- /// See also SMDN: \c PathCompactPath 
 
- /// \param dc [HDC]: device context where the path is displayed
 
- /// \param dx [UINT]: number of pixels where to display
 
- /// \param eppFlags [DWORD, =0]: combination of \c EPathPacking flags indicating how to prepare the path
 
- /// \returns [CString]: path string prepared for display
 
- // 
 
- CString CPath::GetCompactStr(HDC dc,UINT dx, DWORD eppFlags)
 
- {
 
-     CString ret = GetStr(eppFlags);
 
- 	PathCompactPath(dc, CStringLock(ret), dx);
 
-     return ret;
 
- }
 
- // ==================================================================
 
- //  CPath::GetCompactStr
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Inserts ellipses so the path does not exceed the given number of characters
 
- /// \param cchMax [UINT]: maximum number of characters
 
- /// \param eppFlags [DWORD, =0]: combination of \c EPathPacking flags indicating how to prepare the path
 
- /// \param flags [DWORD, =0]: reserved, must be 0
 
- /// \returns [CString]: path string prepared for display
 
- // 
 
- CString CPath::GetCompactStr(UINT cchMax,DWORD flags, DWORD eppFlags )
 
- {
 
-     CString cleanPath = GetStr(eppFlags);
 
-     CString ret;
 
-     PathCompactPathEx(CStringLock(ret, cleanPath.GetLength()), cleanPath, cchMax, flags);
 
-     return ret;
 
- }
 
- // ==================================================================
 
- //  CPath::SetDlgItem
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Sets the text of a child control of a window or dialog, 
 
- /// PathCompactPath to make it fit
 
- /// 
 
- /// \param dlg [HWND]: the window handle of the parent window
 
- /// \param dlgCtrlID [UINT]: ID of the child control
 
- /// \param eppFlags [DWORD, =0]: combination of \c EPathPacking flags indicating how to prepare the path
 
- /// \returns [void]:
 
- // 
 
- void CPath::SetDlgItem(HWND dlg,UINT dlgCtrlID, DWORD eppFlags)
 
- {
 
-     CString cleanPath = GetStr(eppFlags);
 
-     PathSetDlgItemPath(dlg, dlgCtrlID, cleanPath);
 
- }
 
- // ==================================================================
 
- //  ::SearchAndQualify
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Searches for the given file on the search path. If it exists in the search path, 
 
- /// it is qualified with the full path of the first occurence found. Otherwise, it is 
 
- /// qualified with the current path. An absolute file paths remains unchanged. 
 
- /// 
 
- /// \note
 
- /// SearchAndQualify seems to be affected by the same problem
 
- /// as \ref nsPath::CPath::Canonicalize "Canonicalize" : a path like "..\\..\\x" 
 
- /// is changed to "\\x", which is clearly not correct (IMHO).
 
- /// \n
 
- /// compare also: \ref nsPath::CPath::FindOnPath "FindOnPath":
 
- /// FindOnPath allows to specify custom directories to be searched before the search path, and
 
- /// behaves differently in some cases.
 
- /// If the file is not found on the search path, \c FindOnPath leaves the file name unchanged.
 
- /// SearchAndQualify qualifies the path with the current directory in this case
 
- /// 
 
- // 
 
- CPath & CPath::SearchAndQualify()
 
- {
 
-     if (!m_path.GetLength())
 
-         return *this;
 
-     CString qualified;
 
-     DWORD len = m_path.GetLength();
 
-     while (qualified.GetLength() == 0)
 
-     {
 
-         PathSearchAndQualify(m_path, CStringLock(qualified, len), len+1);
 
-         len *= 2;
 
-     }
 
-     m_path = qualified;
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::FindOnPath
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Similar to SearchAndQualify, but 
 
- /// \note
 
- ///  -# the return value of PathFindOnPath does \b not indicate whether the file
 
- ///     exisits, that's why we don't return it either. If you want to check if the file
 
- ///     really is there use \c FileExists
 
- ///  -# PathFindOnPath does not check for string overflow. Documentation recommends to use a buffer
 
- ///     of length MAX_PATH. I don't trust it to be fail safe in case a path plus the string
 
- ///     exceeds this length (note that the file would not be found in this case - but the shell
 
- ///     API might be tempted to build the string inside the buffer)\n
 
- ///     If you don't need the "additional Dirs" functionality, it is recommended to use
 
- ///     SearchAndQualify instead
 
- /// 
 
- /// \param additionalDirs [LPCTSTR *, = NULL]: Additional NULL-terminated array of directories 
 
- ///                                            to search first
 
- /// \returns [CPath &]: a reference to the fully qualified file path
 
- /// 
 
- /// \par error handling:
 
- ///   If the function succeeds, \c GetLastError returns 0. Otherwise, \c GetLastError returns a Win32 error code.
 
- /// 
 
- CPath & CPath::FindOnPath(LPCTSTR * additionalDirs)
 
- {
 
-     DWORD len = m_path.GetLength() + 1 + MAX_PATH;
 
-     bool ok = PathFindOnPath(CStringLock(m_path, len), additionalDirs) != 0;
 
-     if (ok)
 
-        SetLastError(0);
 
-     return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::Exists
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [bool]: true if the file exists on the file system, false otherwise.
 
- // 
 
- bool CPath::Exists() const
 
- {
 
-     return PathFileExists(m_path) != 0;
 
- }
 
- // ==================================================================
 
- //  CPath::IsDirectory
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [bool]: true if the contained path specifies a directory 
 
- ///                  that exists on the file system
 
- // 
 
- bool CPath::IsDirectory() const
 
- {
 
-     return PathIsDirectory(m_path) != 0;
 
- }
 
- // ==================================================================
 
- //  CPath::IsSystemFolder
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \param  attrib [DWORD, default = FILE_ATTRIBUTE_SYSTEM]: the attributes that 
 
- ///                 identify a system folder
 
- /// \returns [bool]: true if the specified path exists and is a system folder
 
- // 
 
- bool CPath::IsSystemFolder(DWORD attrib) const
 
- {
 
-     return PathIsSystemFolder(m_path, attrib) != 0;
 
- }
 
- // ==================================================================
 
- //  CPath::MakeSystemFolder
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \param make [bool, default=true]: true to set the "System Folder" state, false to reset it
 
- /// \par error handling:
 
- ///  If the function succeeds, \c GetLastError returns 0. Otherwise, \c GetLastError returns a Win32 error code.
 
- // 
 
- CPath & CPath::MakeSystemFolder(bool make)
 
- {
 
-    bool ok = make ? PathMakeSystemFolder(m_path) != 0 : PathUnmakeSystemFolder(m_path) != 0;
 
-    if (ok) 
 
-       SetLastError(0);
 
-    return *this;
 
- }
 
- // ==================================================================
 
- //  MakeFullPath
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// Makes a absolute path from a relative path, using the current working directory.
 
- /// 
 
- /// If the path is already absolute, it is not changed.
 
- /// 
 
- CPath & CPath::MakeFullPath()
 
- {
 
-    if (!IsRelative())
 
-       return *this;
 
-    LPTSTR dummy = NULL;
 
-    DWORD chars = GetFullPathName(m_path, 0, NULL, &dummy);
 
-    _ASSERTE(chars > 0);
 
-    CString fullStr;
 
-    chars = GetFullPathName(m_path, chars, CStringLock(fullStr, chars), &dummy);
 
-    m_path = fullStr;
 
-    return *this;
 
- }
 
- // ==================================================================
 
- //  CPath::GetAttributes
 
- // ------------------------------------------------------------------
 
- ///
 
- /// \returns [DWORD]: the file attributes of the specified path or file, or -1 if it 
 
- ///                   does not exist.
 
- // 
 
- DWORD CPath::GetAttributes()
 
- {
 
-     return ::GetFileAttributes(m_path);
 
- 	// 
 
- }
 
- // ==================================================================
 
- //  CPath::GetAttributes
 
- // ------------------------------------------------------------------
 
- ///
 
- /// retrives the \c GetFileExInfoStandard File Attribute information
 
- /// 
 
- /// \param fad [WIN32_FILE_ATTRIBUTE_DATA &, out]: receives the extended file attribute
 
- ///     information (like size, timestamps) for the specified file
 
- /// \returns [bool]: true if the file is found and the query was successful, false otherwise
 
- // 
 
- bool CPath::GetAttributes(WIN32_FILE_ATTRIBUTE_DATA & fad)
 
- {
 
-     ZeroMemory(&fad, sizeof(fad));
 
-     return ::GetFileAttributesEx(m_path, GetFileExInfoStandard, &fad) != 0;
 
- }
 
- // ==================================================================
 
- //  CPath::EnvUnexpandRoot
 
- // ------------------------------------------------------------------
 
- ///
 
- /// replaces path start with matching environment variable
 
- /// If the path starts with the value of the environment variable %envVar%,
 
- /// The beginning of the path is replaced with the environment variable.
 
- /// 
 
- /// e.g. when specifying "WinDir" as \c envVar, "C:\\Windows\\foo.dll" is replaced by
 
- /// "%WINDIR%\foo.dll"
 
- /// 
 
- /// \param envVar [LPCTSTR]: environment variable to check 
 
- /// \returns \c true if the path was modified.
 
- /// 
 
- /// If the environment variable does not exist, or the value of the environment variable
 
- /// does not match the beginning of the path, the path is unmodified and the function returns 
 
- /// false.
 
- /// 
 
- bool CPath::EnvUnexpandRoot(LPCTSTR envVar)
 
- {
 
-     return nsDetail::EnvUnsubstRoot(m_path, envVar);
 
- }
 
- // ==================================================================
 
- //  CPath::EnvUnexpandDefaultRoots
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Tries to replace the path root with a matching environment variable.
 
- /// 
 
- /// 
 
- /// Checks a set of default environment variables, if they match the beginning of the path.
 
- /// If one of them matches, the beginning of the path is replaced with the environment
 
- /// variable specification, and the function returns true.
 
- /// Otherwise, the path remains unmodified and the function returns false.
 
- /// 
 
- /// see EnvUnexpandRoot for details.
 
- /// 
 
- bool CPath::EnvUnexpandDefaultRoots()
 
- {
 
-     // note: Order is important
 
-     return EnvUnexpandRoot(_T("APPDATA")) ||
 
-            EnvUnexpandRoot(_T("USERPROFILE")) ||
 
-            EnvUnexpandRoot(_T("ALLUSERSPROFILE")) ||
 
-            EnvUnexpandRoot(_T("ProgramFiles")) ||
 
-            EnvUnexpandRoot(_T("SystemRoot")) ||
 
-            EnvUnexpandRoot(_T("WinDir")) ||
 
-            EnvUnexpandRoot(_T("SystemDrive"));
 
- }
 
- // ==================================================================
 
- //  CPath::FromRegistry
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// Reads a path string from the registry.
 
- /// \param baseKey [HKEY]: base key for registry path
 
- /// \param subkey [LPCTSTR]: registry path
 
- /// \param name [LPCTSTR] name of the value
 
- /// \returns [CPath]: a path string read from the specified location
 
- /// 
 
- /// If the path is stored as REG_EXPAND_SZ, environment strings are expanded.
 
- /// Otherwise, the string remains unmodified.
 
- /// 
 
- /// \par Error Handling:
 
- ///   If an error occurs, the return value is an empty string, and GetLastError() returns the respective
 
- ///   error code. In particular, if the registry value exists but does not contain a string, GetLastError()
 
- ///   returns ERROR_INVALID_DATA
 
- ///   \n\n
 
- ///   If the function succeeds, GetLastError() returns zero.
 
- /// 
 
- /// See also nsPath::ToRegistry
 
- ///             
 
- CPath FromRegistry(HKEY baseKey, LPCTSTR subkey, LPCTSTR name)
 
- {
 
-    SetLastError(0);
 
-    CAutoHKEY key;
 
-    DWORD ok = RegOpenKeyEx(baseKey, subkey, 0, KEY_READ, key.OutArg());
 
-    if (ok != ERROR_SUCCESS)
 
-    {
 
-        SetLastError(ok);
 
-        return CPath();
 
-    }
 
-    DWORD len = 256;
 
-    DWORD type = 0;
 
-    CString path;
 
-    do 
 
-    {
 
-        CStringLock buffer(path, len);
 
-        if (!buffer)
 
-        {
 
-            SetLastError(ERROR_OUTOFMEMORY);
 
-            return CPath();
 
-        }
 
-        DWORD size = (len+1) * sizeof(TCHAR); // size includes terminating zero
 
-        ok = RegQueryValueEx(key, name, NULL, &type, 
 
-                             (LPBYTE) buffer.operator LPTSTR(), &size );
 
-        // read successfully:
 
-        if (ok == ERROR_SUCCESS)
 
-        {
 
-            if (type != REG_SZ && type != REG_EXPAND_SZ)
 
-            {
 
-                SetLastError(ERROR_INVALID_DATA);
 
-                return CPath();
 
-            }
 
-            break; // accept string
 
-        }
 
-        // buffer to small
 
-        if (ok == ERROR_MORE_DATA)
 
-        {
 
-            len = (size + sizeof(TCHAR) - 1) / sizeof(TCHAR);
 
-            continue;
 
-        }
 
-        // otherwise, an error occured
 
-        SetLastError(ok);
 
-        return CPath();
 
-    } while(1);
 
-    DWORD cleanup = epc_Default;
 
-    if (type == REG_SZ)
 
-        cleanup &= ~epcExpandEnvStrings;
 
-    else
 
-        cleanup |= epcExpandEnvStrings; // on by default, but I might change my mind..
 
-    return CPath(path, cleanup);
 
- }
 
- // ==================================================================
 
- //  CPath::ToRegistry
 
- // ------------------------------------------------------------------
 
- ///
 
- /// Writes the path to the registry
 
- /// 
 
- /// \param baseKey: root of registry path
 
- /// \param subkey: registry path where to store
 
- /// \param name: name to store the key under
 
- /// \param replaceEnv [bool=true]: If true (default), environment strings will be replaced
 
- ///     with environment variables, and the string is stored as REG_EXPAND_SZ. 
 
- ///     Otherwise, the string is stored unmodified as REG_SZ.
 
- /// 
 
- /// See also nsPath::FromRegistry
 
- /// 
 
- long CPath::ToRegistry(HKEY baseKey,LPCTSTR subkey,LPCTSTR name,bool replaceEnv)
 
- {
 
-     CAutoHKEY key;
 
-     DWORD ok = RegCreateKeyEx(baseKey, subkey, NULL, NULL, 0, KEY_WRITE, NULL, key.OutArg(), NULL);
 
-     if (ok != ERROR_SUCCESS)
 
-         return ok;
 
-     CString path;
 
-     if (replaceEnv)
 
-     { 
 
-         CPath ptemp = path;
 
-         ptemp.EnvUnexpandDefaultRoots();
 
-         path = ptemp.GetStr();
 
-     }
 
-     else
 
-         path = GetStr();
 
-     ok = RegSetValueEx(key, name, 0, replaceEnv ? REG_EXPAND_SZ : REG_SZ,
 
-                         (BYTE *) path.operator LPCTSTR(), 
 
-                         (path.GetLength()+1) * sizeof(TCHAR) );
 
-     return ok;
 
- }
 
- // ==================================================================
 
- //  IsDot, IsDotDot, IsDotty
 
- // ------------------------------------------------------------------
 
- ///
 
- bool CPath::IsDot() const 
 
- { 
 
-     return m_path.GetLength() == 1 && m_path[0] == '.';
 
- }
 
- bool CPath::IsDotDot() const
 
- {
 
-     return m_path.GetLength() == 2 && m_path[0] == '.' && m_path[1] == '.';
 
- }
 
- bool CPath::IsDotty() const
 
- {
 
-     return IsDot() || IsDotDot();
 
- }
 
- const LPCTSTR InvalidChars_Windows =
 
- 	_T("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F")
 
- 	_T("\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")
 
- 	_T("\\/:*?\"<>|");
 
- // ==================================================================
 
- //  IsValid
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// returns true if the path satisfies Windows naming conventions
 
- ///
 
- bool CPath::IsValid() const
 
- {
 
-     if (!m_path.GetLength()) return false;
 
-     if (m_path.FindOneOf(InvalidChars_Windows) >= 0) return false;
 
-     if (GetLastChar(m_path) == '.') // may not end in '.', except "." and ".."
 
-     {
 
-         if (m_path.GetLength() > 2 || m_path[0] != '.')
 
-             return false;
 
-     }
 
-     return true;
 
- }
 
- // ==================================================================
 
- //  ReplaceInvalid
 
- // ------------------------------------------------------------------
 
- /// 
 
- /// replaces all invalid file name characters  inc \c s with \c replaceChar
 
- /// This is helpful when generating names based on user input
 
- /// 
 
- CString ReplaceInvalid(CString const & str, TCHAR replaceChar)
 
- {
 
-     if (!str.GetLength() || CPath(str).IsDotty())
 
-         return str;
 
-     CString s = str;
 
-     for(int i=0; i<s.GetLength(); ++i)
 
-     {
 
-         TCHAR ch = s.GetAt(i);
 
-         if (_tcschr(InvalidChars_Windows, ch))
 
-             s.SetAt(i, replaceChar);
 
-     }
 
-     // last one may not be a dot
 
-     int len = s.GetLength();
 
-     if (s[len-1] == '.')
 
-         s.SetAt(len-1, replaceChar);
 
-     return s;
 
- }
 
- // ==================================================================
 
- } // namespace nsPath
 
 
  |