Path.cpp 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961
  1. // ==================================================================
  2. //
  3. // Path.cpp
  4. //
  5. // Created: 20.06.2004
  6. //
  7. // Copyright (C) Peter Hauptmann
  8. //
  9. // ------------------------------------------------------------------
  10. ///
  11. /// for copyright & disclaimer see accompanying Path.h
  12. ///
  13. /// \page pgChangeLog Change Log
  14. ///
  15. /// June 20, 2004: Initial release
  16. ///
  17. /// June 22, 2004
  18. /// - \c fixed: nsPath::CPath::MakeSystemFolder implements unmake correctly
  19. /// - \c added: nsPath::CPath::MakeSystemFolder and nsPath::CPath::SearchOnPath
  20. /// set the windows error code to zero if the function succeeds (thanks Hans Dietrich)
  21. /// - \c fixed: nsPath::CPath compiles correctly with warning level -W4
  22. ///
  23. /// Mar 3, 2005
  24. /// - fixed eppAutoQuote bug in GetStr (thanks Stlan)
  25. /// - Added:
  26. /// - \ref nsPath::FromRegistry "FromRegistry"
  27. /// - \ref nsPath::CPath::ToRegistry "ToRegistry"
  28. /// - \ref nsPath::CPath::GetRootType, "GetRootType"
  29. /// - \ref nsPath::CPath::GetRoot "GetRoot" has a new implementation
  30. /// - \ref nsPath::CPath::MakeAbsolute "MakeAbsolute"
  31. /// - \ref nsPath::CPath::MakeRelative "MakeRelative"
  32. /// - \ref nsPath::CPath::MakeFullPath "MakeFullPath"
  33. /// - \ref nsPath::CPath::EnvUnexpandRoot "EnvUnexpandRoot"
  34. /// - \ref nsPath::CPath::EnvUnexpandDefaultRoots "EnvUnexpandDefaultRoots"
  35. /// - \b Breaking \b Changes (sorry)
  36. /// - GetRoot -> ShellGetRoot (to distinct from extended GetRoot implementation)
  37. /// - GetFileName --> GetName (consistency)
  38. /// - GetFileTitle --> GetTitle (consistency)
  39. /// - made the creation functions independent functions in the nsPath namespace
  40. /// (they are well tugged away in the namespace so conflicts are easy to avoid)
  41. ///
  42. /// Mar 17, 2005
  43. /// - fixed bug in GetFileName (now: GetName): If the path ends in a backslash,
  44. /// GetFileName did return the entire path instead of an empty string. (thanks woodland)
  45. ///
  46. /// Aug 21, 2005
  47. /// - fixed bug in GetStr(): re-quoting wasn't applied (doh!)
  48. /// - fixed incompatibility with CStlString
  49. /// - Added IsDot, IsDotDot, IsDotty (better names?)
  50. /// - Added IsValid, ReplaceInvalid
  51. /// - Added SplitRoot
  52. /// -
  53. ///
  54. ///
  55. ///
  56. // ------ Main Page --------------------
  57. /// @mainpage
  58. ///
  59. /// \ref pgDisclaimer, \ref pgChangeLog (recent changes August 2005, \b breaking \b changes Mar 2005)
  60. ///
  61. /// \par Introduction
  62. ///
  63. /// \ref nsPath::CPath "CPath" is a helper class to make manipulating file system path strings easier.
  64. /// It is complementedby a few non-class functions (see nsPath namespace documentation)
  65. ///
  66. /// CPath is based on the Shell Lightweight Utility API, but modifies / and extends this functionality
  67. /// (and removes some quirks). It requires CString (see below why, and why this is not too bad).
  68. ///
  69. /// \par Main Features:
  70. ///
  71. /// CPath acts as a string class with special "understanding" and operations for path strings.
  72. ///
  73. /// \code CPath path("c:\\temp"); \endcode
  74. /// constructs a path string, and does some default cleanup (trimming white space, removing quotes, etc.)
  75. /// The cleanup can be customized (see \ref nsPath::CPath::CPath "CPath Constructor",
  76. /// \ref nsPath::EPathCleanup "EPathCleanup"). You can pass an ANSI or UNICODE string.
  77. ///
  78. /// \code path = path & "file.txt"; \endcode
  79. /// Appends "foo" to the path, making sure that the two segments are separated by exactly one backslash
  80. /// no matter if the parts end/begin with a backslash.
  81. ///
  82. /// The following functions give you access to the individual elements of the path:
  83. ///
  84. /// - \ref nsPath::CPath::GetRoot "GetRoot"
  85. /// - \ref nsPath::CPath::GetPath "GetPath"
  86. /// - \ref nsPath::CPath::GetName "GetName"
  87. /// - \ref nsPath::CPath::GetTitle "GetTitle"
  88. /// - \ref nsPath::CPath::GetExtension "GetExtension"
  89. ///
  90. /// \code CString s = path.GetStr() \endcode
  91. /// returns a CString that is cleaned up again (re-quoted if necessary, etc). GetBStr() returns an _bstr_t
  92. /// with the same features (that automatically casts to either ANSI or UNICODE).
  93. /// To retrieve the unmodified CPath string, you can rely on the \c operator \c LPCTSTR
  94. ///
  95. /// There's much more - see the full nsPath documentation for details!
  96. ///
  97. /// @sa MSDN library: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/shlwapi/path/pathappend.asp
  98. ///
  99. /// \par Why not CPathT ?
  100. /// -# the class is intended for a VC6 project that won't move to VC7 to soon
  101. /// -# CPathT contains the same quirks that made me almost give up on the Shell Helper functions.
  102. /// -# I wanted the class to have additional features (such as the & operator, and automatic cleanup)
  103. ///
  104. /// \par Why CString ?
  105. /// -# The CString implementation provides a known performance (due to the guaranteed reference counted "copy
  106. /// on write" implementation). I consider this preferrable over the weaker guarantees made b STL, especially
  107. /// when designing a "convenient" class interface.
  108. /// -# CString's ref counting mechanism is automatically reused by CPath, constructing a CPath from a CString
  109. /// does not involve a copy operation.
  110. /// -# CString is widely availble independent of MFC (WTL, custom implementations, "extract" macros are
  111. /// available, and VC7 makes CString part of the ATL)
  112. ///
  113. /// \note if you want to port to STL, it's probably easier to use a vector<TCHAR> instead of std:string
  114. /// to hold the data internally
  115. ///
  116. /// \par Why _bstr_t ?
  117. /// To make implementation easier, the class internally works with "application native" strings (that is,
  118. /// TCHAR strings - which are either ANSI or UNICODE depending on a compile setting). GetBStr provides
  119. /// conversion to ANSI or UNICODE, whichever is required.\n
  120. /// An independent implementation would return a temporary object with cast operators to LPCSTR and LPWSTR
  121. /// - BUT _bstr_t does exactly that (admittedly, with some overhead).
  122. ///
  123. #include "stdafx.h"
  124. #include "Path.h"
  125. #include <shlwapi.h> // Link to Shell Helper API
  126. #pragma comment(lib, "shlwapi.lib")
  127. #ifdef _DEBUG
  128. #undef THIS_FILE
  129. static char THIS_FILE[]=__FILE__;
  130. #define new DEBUG_NEW
  131. #endif
  132. namespace nsPath
  133. {
  134. /// contains helper classes for nsPath namespace
  135. ///
  136. namespace nsDetail
  137. {
  138. // ==================================================================
  139. // CStringLock
  140. // ------------------------------------------------------------------
  141. //
  142. // Helper class for CString::GetBuffer and CString::ReleaseBuffer
  143. // \todo: consider moving to common utility
  144. // \todo: additional debug verification on release
  145. //
  146. class CStringLock
  147. {
  148. public:
  149. CString * m_string;
  150. LPTSTR m_buffer;
  151. static LPTSTR NullBuffer;
  152. public:
  153. CStringLock(CString & s) : m_string(&s)
  154. {
  155. m_buffer = m_string->GetBuffer(0);
  156. // fixes an incompatibility with CStdString, see "NullBuffer" in .cpp
  157. if (!s.GetLength())
  158. m_buffer = NullBuffer;
  159. }
  160. CStringLock(CString & s, int minChars) : m_string(&s)
  161. {
  162. m_buffer = m_string->GetBuffer(minChars);
  163. // fixes an incompatibility with CStdString, see "NullBuffer" in .cpp
  164. if (!s.GetLength() && !minChars)
  165. m_buffer = NullBuffer;
  166. }
  167. operator LPTSTR() { return m_buffer; }
  168. void Release(int newLen = -1)
  169. {
  170. if (m_string)
  171. {
  172. m_string->ReleaseBuffer(newLen);
  173. m_string = NULL;
  174. m_buffer = NULL;
  175. }
  176. }
  177. ~CStringLock() { Release(); }
  178. };
  179. /// CStdString incompatibility:
  180. /// http://www.codeproject.com/string/stdstring.asp
  181. /// If the contained string is empty, CStdString.GetBuffer returns a pointer to a constant
  182. /// empty string, which may cause an access violation when I write the terminating zero
  183. /// (which is in my understanding implicitely allowed the way I read the MSDN docs)
  184. /// Solution: we return a pointer to another buffer
  185. TCHAR NullBufferData[1] = { 0 };
  186. LPTSTR CStringLock::NullBuffer = NullBufferData;
  187. // Helper class for Close-On-Return HKEY
  188. // \todo migrate template class
  189. class CAutoHKEY
  190. {
  191. private:
  192. CAutoHKEY const & operator =(CAutoHKEY const & ); // not implemented
  193. CAutoHKEY(CAutoHKEY const &); // not implemented
  194. protected:
  195. HKEY key;
  196. public:
  197. CAutoHKEY() : key(0) {}
  198. CAutoHKEY(HKEY key_) : key(key_) {}
  199. ~CAutoHKEY() { Close(); }
  200. void Close()
  201. {
  202. if (key)
  203. {
  204. RegCloseKey(key);
  205. key = 0;
  206. }
  207. }
  208. HKEY * OutArg()
  209. {
  210. Close();
  211. return &key;
  212. }
  213. operator HKEY() const { return key; }
  214. }; // CAutoHKEY
  215. /// Reads an environment variable into a CString
  216. CString GetEnvVar(LPCTSTR envVar)
  217. {
  218. SetLastError(0);
  219. // get length of buffer
  220. DWORD result = GetEnvironmentVariable(envVar, _T(""), 0);
  221. if (!result)
  222. return CString();
  223. CString s;
  224. result = GetEnvironmentVariable(envVar, CStringLock(s, result), result);
  225. return s;
  226. }
  227. /// Replace path root with environment variable
  228. /// If the beginning of \c s matches the value of the environment variable %envVar%,
  229. /// it is replaced with the %envVar% value
  230. /// (e.g. "C:\Windows" with "%windir%"
  231. /// \param s [CString &, in/out]: the string to modify
  232. /// \param envVar [LPCTSTR]: name of the environment variable
  233. /// \returns true if s was modified, false otherwise.
  234. bool EnvUnsubstRoot(CString & s, LPCTSTR envVar)
  235. {
  236. // get environment value string
  237. CString envValue = GetEnvVar(envVar);
  238. if (!envValue.GetLength())
  239. return false;
  240. if (s.GetLength() >= envValue.GetLength() &&
  241. _tcsnicmp(s, envValue, envValue.GetLength())==0)
  242. {
  243. CString modified = CString('%');
  244. modified += envVar;
  245. modified += '%';
  246. modified += s.Mid(envValue.GetLength());
  247. s = modified;
  248. return true;
  249. }
  250. return false;
  251. }
  252. } // namespace nsPath::nsDetail
  253. using namespace nsDetail;
  254. const TCHAR Backslash = '\\';
  255. // ==============================================
  256. // Trim
  257. // ----------------------------------------------
  258. /// Trims whitespaces from left and right side.
  259. /// \param s [CString]: String to modify in-place.
  260. void Trim(CString & string)
  261. {
  262. if (_istspace(GetFirstChar(string)))
  263. string.TrimLeft();
  264. if (_istspace(GetLastChar(string)))
  265. string.TrimRight();
  266. }
  267. // ==================================================================
  268. // GetDriveLetter(ch)
  269. // ------------------------------------------------------------------
  270. /// checks if the specified letter \c ch is a drive letter, and casts it to uppercase
  271. ///
  272. /// \returns [TCHAR]: if \c is a valid drive letter (A..Z, or a..z), returns the drive letter
  273. /// cast to uppercase (A..Z). >Otherwise, returns 0
  274. TCHAR GetDriveLetter(TCHAR ch)
  275. {
  276. if ( (ch >= 'A' && ch <= 'Z'))
  277. return ch;
  278. if (ch >= 'a' && ch <= 'z')
  279. return (TCHAR) (ch - 'a' + 'A');
  280. return 0;
  281. }
  282. // ==================================================================
  283. // GetDriveLetter(string)
  284. // ------------------------------------------------------------------
  285. /// returnd the drive letter of a path.
  286. /// The drive letter returned is always uppercase ('A'.`'Z').
  287. /// \param s [LPCTSTR]: the path string
  288. /// \returns [TCHAR]: the drive letter, converted to uppercase, if the path starts with an
  289. /// X: drive specification. Otherwise, returns 0
  290. //
  291. TCHAR GetDriveLetter(LPCTSTR s)
  292. {
  293. if (s == NULL || *s == 0 || s[1] != ':')
  294. return 0;
  295. return GetDriveLetter(s[0]);
  296. }
  297. // ==================================================================
  298. // QuoteSpaces
  299. // ------------------------------------------------------------------
  300. ///
  301. /// Quotes the string if it is not already quoted, and contains spaces
  302. /// see also MSDN: \c PathQuoteSpaces
  303. /// \note If the string is already quoted, an additional pair of quotes is added.
  304. /// \param str [CString const &]: path string to add quotes to
  305. /// \returns [CString]: path string with quotes added if required
  306. //
  307. CString QuoteSpaces(CString const & str)
  308. {
  309. // preserve refcounting if no changes will be made
  310. if (str.Find(' ')>=0) // if the string contains any spaces...
  311. {
  312. CString copy(str);
  313. CStringLock buffer(copy, copy.GetLength() + 2);
  314. PathQuoteSpaces(buffer);
  315. buffer.Release();
  316. return copy;
  317. }
  318. return str; // unmodified
  319. }
  320. /// helper function for GetRootType
  321. inline ERootType GRT_Return(ERootType type, int len, int * pLen)
  322. {
  323. if (pLen)
  324. *pLen = len;
  325. return type;
  326. }
  327. // ==================================================================
  328. // GetRootType
  329. // ------------------------------------------------------------------
  330. ///
  331. /// returns the type of the path root, and it's length.
  332. /// For supported root types, see \ref nsPath::ERootType "ERootType" enumeration
  333. ///
  334. /// \param path [LPCTSTR]: The path to analyze
  335. /// \param pLen [int *, out]: if not NULL, receives the length of the root part (in characters)
  336. /// \param greedy [bool=true]: Affects len and type of the following root types:
  337. /// - \c "\\server\share" : with greedy=true, it is treated as one \c rtServerShare root,
  338. /// otherwise, it is treated as \c rtServer root
  339. ///
  340. /// \returns [ERootType]: type of the root element
  341. ///
  342. ///
  343. ERootType GetRootType(LPCTSTR path, int * pLen, bool greedy)
  344. {
  345. // ERootType type = rtNoRoot;
  346. // int len = 0;
  347. const TCHAR * invalidChars = _T("\\/:*/\"<>|");
  348. const TCHAR bk = '\\';
  349. if (!path || !*path)
  350. return GRT_Return(rtNoRoot, 0, pLen);
  351. // drive spec
  352. if (_istalpha(*path) && path[1] == ':')
  353. {
  354. if (path[2] == bk) { return GRT_Return(rtDriveRoot, 3, pLen); }
  355. else { return GRT_Return(rtDriveCur, 2, pLen); }
  356. }
  357. // anything starting with two backslashes
  358. if (path[0] == bk && path[1] == bk)
  359. {
  360. // UNC long path?
  361. if (path[2] == '?' && path[3] == bk)
  362. {
  363. int extraLen = 0;
  364. GetRootType(path+4, &extraLen) ;
  365. return GRT_Return(rtLongPath, 4 + extraLen, pLen);
  366. }
  367. // position of next backslash or colon
  368. int len = 2 + (int)_tcscspn(path+2, invalidChars);
  369. TCHAR const * end = path+len;
  370. // server only, no backslash
  371. if (*end == 0)
  372. return GRT_Return(rtServerOnly, len, pLen);
  373. // server only, terminated with backslash
  374. if (*end == bk && end[1] == 0)
  375. return GRT_Return(rtServerOnly, len+1, pLen);
  376. // server, backslash, and more...
  377. if (*end == bk)
  378. {
  379. if (!greedy) // return server only
  380. return GRT_Return(rtServer, len, pLen);
  381. len += 1 + (int)_tcscspn(end+1, invalidChars);
  382. end = path + len;
  383. // server, share, no backslash
  384. if (*end == 0)
  385. return GRT_Return(rtServerShare, len, pLen);
  386. // server, share, backslash
  387. if (*end == '\\')
  388. return GRT_Return(rtServerShare, len+1, pLen);
  389. }
  390. // fall through to other tests
  391. }
  392. int len = (int)_tcscspn(path, invalidChars);
  393. TCHAR const * end = path + len;
  394. // (pseudo) protocol:
  395. if (len > 0 && *end == ':')
  396. {
  397. if (end[1] == '/' && end[2] == '/')
  398. return GRT_Return(rtProtocol, len+3, pLen);
  399. else
  400. return GRT_Return(rtPseudoProtocol, len+1, pLen);
  401. }
  402. return GRT_Return(rtNoRoot, 0, pLen);
  403. }
  404. // ==================================================================
  405. // CPath::Trim
  406. // ------------------------------------------------------------------
  407. ///
  408. /// removes leading and trailing spaces.
  409. //
  410. CPath & CPath::Trim()
  411. {
  412. nsPath::Trim(m_path);
  413. return *this;
  414. }
  415. // ==================================================================
  416. // CPath::Unquote
  417. // ------------------------------------------------------------------
  418. ///
  419. /// removes (double) quotes from around the string
  420. //
  421. CPath & CPath::Unquote()
  422. {
  423. if (GetFirstChar(m_path) == '"' && GetLastChar(m_path) == '"')
  424. m_path = m_path.Mid(1, m_path.GetLength()-2);
  425. return *this;
  426. }
  427. // ==================================================================
  428. // CPath::Canonicalize
  429. // ------------------------------------------------------------------
  430. ///
  431. /// Collapses "\\..\\" and "\\.\\" path parts.
  432. /// see also MSDN: PathCanonicalize
  433. /// \note
  434. /// PathCanonicalize works strange on relative paths like "..\\..\\x" -
  435. /// it is changed to "\x", which is clearly not correct. SearchAndQualify is affected
  436. /// by the same problem
  437. /// \todo handle this separately?
  438. ///
  439. /// \par Implementation Differences
  440. /// \c PathCanonicalize does turn an empty path into a single backspace.
  441. /// CPath::Canonicalize does not modify an empty path.
  442. //
  443. CPath & CPath::Canonicalize()
  444. {
  445. if (!m_path.GetLength()) // PathCanonicalize turns an empty path into "\\" - I don't want this..
  446. return *this;
  447. if (m_path.Find(_T("\\."))>=0)
  448. {
  449. CString target = m_path; // PathCanonicalize requires a copy to work with
  450. CStringLock buffer(target, m_path.GetLength()+2); // might add a backslash sometimes !
  451. PathCanonicalize(buffer, m_path);
  452. buffer.Release();
  453. m_path = target;
  454. }
  455. return *this;
  456. }
  457. // ==================================================================
  458. // CPath::ShrinkXXLPath
  459. // ------------------------------------------------------------------
  460. ///
  461. /// Removes an "Extra long file name" specification
  462. /// Unicode API allows pathes longer than MAX_PATH, if they start with "\\\\?\\". This function
  463. /// removes such a specification if present. See also MSDN: "File Name Conventions".
  464. //
  465. CPath & CPath::ShrinkXXLPath()
  466. {
  467. if (m_path.GetLength() >= 6 && // at least 6 chars for [\\?\C:]
  468. _tcsncmp(m_path, _T("\\\\?\\"), 4) == 0)
  469. {
  470. LPCTSTR path = m_path;
  471. if (nsPath::GetDriveLetter(path[4]) != 0 && path[5] == ':')
  472. m_path = m_path.Mid(4);
  473. else if (m_path.GetLength() >= 8) // at least 8 chars for [\\?\UNC\]
  474. {
  475. if (_tcsnicmp(path + 4, _T("UNC\\"), 4) == 0)
  476. {
  477. // remove chars [2]..[7]
  478. int len = m_path.GetLength() - 8; //
  479. CStringLock buffer(m_path);
  480. memmove(buffer+2, buffer+8, len * sizeof(TCHAR));
  481. buffer.Release(len+2);
  482. }
  483. }
  484. }
  485. return *this;
  486. }
  487. // ==================================================================
  488. // CPath::Assign
  489. // ------------------------------------------------------------------
  490. ///
  491. /// Assigns a string to the path object, optionally applying cleanup of the path name
  492. ///
  493. /// \param str [CString const &]: The string to assign to the path
  494. /// \param cleanup [DWORD, default = epc_Default]: operations to apply to the path
  495. /// \returns [CPath &]: reference to the path object itself
  496. ///
  497. /// see CPath::Clean for a description of the cleanup options
  498. //
  499. CPath & CPath::Assign(CString const & str, DWORD cleanup)
  500. {
  501. m_path = str;
  502. Clean(cleanup);
  503. return *this;
  504. }
  505. // ==================================================================
  506. // CPath::MakePretty
  507. // ------------------------------------------------------------------
  508. ///
  509. /// Turns an all-uppercase path to all -lowercase. A path containing any lowercase
  510. /// character is not modified.
  511. /// (This is Microsoft#s idea of prettyfying a path. I don't know what to say)
  512. ///
  513. CPath & CPath::MakePretty()
  514. {
  515. CStringLock buffer(m_path);
  516. PathMakePretty(buffer);
  517. return *this;
  518. }
  519. // ==================================================================
  520. // CPath::Clean
  521. // ------------------------------------------------------------------
  522. ///
  523. /// Applies multiple "path cleanup" operations
  524. ///
  525. /// \param cleanup [DWORD]: a combination of zero or more nsPath::EPathCleanup flags (see below)
  526. /// \returns [CPath &]: a reference to the path object
  527. ///
  528. /// The following cleanup operations are defined:
  529. /// - \c epcRemoveArgs: call PathRemoveArgs to remove arguments
  530. /// - \c epcRemoveIconLocation: call to PathParseIconLocation to remove icon location
  531. /// - \c \b epcTrim: trim leading and trailing whitespaces
  532. /// - \c \b epcUnquote: remove quotes
  533. /// - \c \b epcTrimInQuote: trim whitespaces again after unqouting.
  534. /// - \c \b epcCanonicalize: collapse "\\.\\" and "\\..\\" segments
  535. /// - \c \b epcRemoveXXL: remove an "\\\\?\\" prefix for path lengths exceeding \c MAX_PATH
  536. /// - \c epcSlashToBackslash: (not implemented) change forward slashes to back slashes (does not modify a "xyz://" protocol root)
  537. /// - \c epcMakePretty: applies PathMakePretty
  538. /// - \c epcRemoveArgs: remove command line arguments
  539. /// - \c epcRemoveIconLocation: remove icon location number
  540. /// - \c \b epcExpandEnvStrings: Expand environment strings
  541. ///
  542. /// This function is called by most assignment constructors and assignment operators, using
  543. /// the \c epc_Default cleanup options (typically those set in bold above, but check the enum
  544. /// documentation in case I forgot to update this one).
  545. ///
  546. /// Constructors and Assignment operators that take a string (\c LPCTSTR, \c LPCTSTR, \c CString) call
  547. /// this function. Copy or assignment from another \c CPath object does not call this function.
  548. ///
  549. ///
  550. //
  551. CPath & CPath::Clean(DWORD cleanup)
  552. {
  553. if (cleanup & epcRemoveArgs)
  554. {
  555. // remove leading spaces, otherwise PathRemoveArgs considers everything a space
  556. if (cleanup & epcTrim)
  557. m_path.TrimLeft();
  558. PathRemoveArgs(CStringLock(m_path));
  559. }
  560. if (cleanup & epcRemoveIconLocation)
  561. PathParseIconLocation(CStringLock(m_path));
  562. if (cleanup & epcTrim)
  563. Trim();
  564. if (cleanup & epcUnquote)
  565. {
  566. Unquote();
  567. if (cleanup & epcTrimInQuote)
  568. Trim();
  569. }
  570. if (cleanup & epcExpandEnvStrings)
  571. ExpandEnvStrings();
  572. if (cleanup & epcCanonicalize)
  573. Canonicalize();
  574. if (cleanup & epcRemoveXXL)
  575. ShrinkXXLPath();
  576. if (cleanup & epcSlashToBackslash)
  577. m_path.Replace('/', '\\');
  578. if (cleanup & epcMakePretty)
  579. MakePretty();
  580. return *this;
  581. }
  582. // Extractors
  583. CString CPath::GetStr(DWORD packing) const
  584. {
  585. CString str = m_path;
  586. // _ASSERTE(!(packing & eppAutoXXL)); // TODO
  587. if (packing & eppAutoQuote)
  588. str = QuoteSpaces(str);
  589. if (packing & eppBackslashToSlash)
  590. str.Replace('\\', '/'); // TODO: suport server-share and protocol correctly
  591. return str;
  592. }
  593. _bstr_t CPath::GetBStr(DWORD packing) const
  594. {
  595. return _bstr_t( GetStr(packing).operator LPCTSTR());
  596. }
  597. // ==================================================================
  598. // Constructors
  599. // ------------------------------------------------------------------
  600. //
  601. CPath::CPath(LPCSTR path) : m_path(path)
  602. {
  603. Clean();
  604. }
  605. CPath::CPath(LPCWSTR path) : m_path(path)
  606. {
  607. Clean();
  608. }
  609. CPath::CPath(CString const & path) : m_path(path)
  610. {
  611. Clean();
  612. }
  613. CPath::CPath(CPath const & path) : m_path(path.m_path) {} // we assume it is already cleaned
  614. CPath::CPath(CString const & path,DWORD cleanup) : m_path(path)
  615. {
  616. Clean(cleanup);
  617. }
  618. // ==================================================================
  619. // Assignment
  620. // ------------------------------------------------------------------
  621. //
  622. CPath & CPath::operator=(LPCSTR rhs)
  623. {
  624. #ifndef _UNICODE // avoidf self-assignment
  625. if (rhs == m_path.operator LPCTSTR())
  626. return *this;
  627. #endif
  628. m_path = rhs;
  629. Clean();
  630. return *this;
  631. }
  632. CPath & CPath::operator=(LPCWSTR rhs)
  633. {
  634. #ifdef _UNICODE // avoid self-assignment
  635. if (rhs == m_path.operator LPCTSTR())
  636. return *this;
  637. #endif
  638. m_path = rhs;
  639. Clean();
  640. return *this;
  641. }
  642. CPath & CPath::operator=(CString const & rhs)
  643. {
  644. // our own test for self-assignment, so we can skip CClean in this case
  645. if (rhs.operator LPCTSTR() == m_path.operator LPCTSTR())
  646. return *this;
  647. m_path = rhs;
  648. Clean();
  649. return *this;
  650. }
  651. CPath & CPath::operator=(CPath const & rhs)
  652. {
  653. if (rhs.m_path.operator LPCTSTR() == m_path.operator LPCTSTR())
  654. return *this;
  655. m_path = rhs;
  656. return *this;
  657. }
  658. // ==================================================================
  659. // CPath::operator &=
  660. // ------------------------------------------------------------------
  661. ///
  662. /// appends a path segment, making sure it is separated by exactly one backslash
  663. /// \returns reference to the modified \c CPath instance.
  664. //
  665. CPath & CPath::operator &=(LPCTSTR rhs)
  666. {
  667. return CPath::Append(rhs);
  668. }
  669. // ==================================================================
  670. // CPath::AddBackslash
  671. // ------------------------------------------------------------------
  672. ///
  673. /// makes sure the contained path is terminated with a backslash
  674. /// \returns [CPath &]: reference to the modified path
  675. /// see also: \c PathAddBackslash Shell Lightweight Utility API
  676. //
  677. CPath & CPath::AddBackslash()
  678. {
  679. if (GetLastChar(m_path) != Backslash)
  680. {
  681. CStringLock buffer(m_path, m_path.GetLength()+1);
  682. PathAddBackslash(buffer);
  683. }
  684. return *this;
  685. }
  686. // ==================================================================
  687. // CPath::RemoveBackslash
  688. // ------------------------------------------------------------------
  689. ///
  690. /// If the path ends with a backslash, it is removed.
  691. /// \returns [CPath &]: a reference to the modified path.
  692. //
  693. CPath & CPath::RemoveBackslash()
  694. {
  695. if (GetLastChar(m_path) == Backslash)
  696. {
  697. CStringLock buffer(m_path, m_path.GetLength()+1);
  698. PathRemoveBackslash(buffer);
  699. }
  700. return *this;
  701. }
  702. // ==================================================================
  703. // CPath::Append
  704. // ------------------------------------------------------------------
  705. ///
  706. /// Concatenates two paths
  707. /// \par Differences to \c PathAddBackslash:
  708. /// Unlike \c PathAddBackslash, \c CPath::Append appends a single backslash if rhs is empty (and
  709. /// the path does not already end with a backslash)
  710. ///
  711. /// \param rhs [LPCTSTR]: the path component to append
  712. /// \returns [CPath &]: reference to \c *this
  713. //
  714. CPath & CPath::Append(LPCTSTR rhs)
  715. {
  716. if (rhs == NULL || *rhs == '\0')
  717. {
  718. AddBackslash();
  719. }
  720. else
  721. {
  722. int rhsLen = rhs ? (int)_tcslen(rhs) : 0;
  723. CStringLock buffer(m_path, m_path.GetLength()+rhsLen+1);
  724. PathAppend(buffer, rhs);
  725. }
  726. return *this;
  727. }
  728. // ==================================================================
  729. // CPath::ShellGetRoot
  730. // ------------------------------------------------------------------
  731. ///
  732. /// Retrieves the Root of the path, as returned by \c PathSkipRoot.
  733. ///
  734. /// \note For a more detailed (but "hand-made") implementation see GetRoot and GetRootType.
  735. ///
  736. /// The functionality of \c PathSkipRoot is pretty much limited:
  737. /// - Drives ("C:\\" but not "C:")
  738. /// - UNC Shares ("\\\\server\\share\\", but neither "\\\\server" nor "\\\\server\\share")
  739. /// - UNC long drive ("\\\\?\\C:\\")
  740. ///
  741. /// \returns [CString]: the rot part of the string
  742. ///
  743. CString CPath::ShellGetRoot() const
  744. {
  745. LPCTSTR path = m_path;
  746. LPCTSTR rootEnd = PathSkipRoot(path);
  747. if (rootEnd == NULL)
  748. return CString();
  749. return m_path.Left((int)(rootEnd - path));
  750. }
  751. // ==================================================================
  752. // GetRootType
  753. // ------------------------------------------------------------------
  754. ///
  755. /// returns the type of the root, and it's length.
  756. /// For supported tpyes, see \ref nsPath::ERootType "ERootType".
  757. /// see also \ref nsPath::GetRootType
  758. ///
  759. ERootType CPath::GetRootType(int * len, bool greedy) const
  760. {
  761. return nsPath::GetRootType(m_path, len, greedy);
  762. }
  763. // ==================================================================
  764. // GetRoot
  765. // ------------------------------------------------------------------
  766. ///
  767. /// \param rt [ERootType * =NULL]: if given, receives the type of the root segment.
  768. /// \return [CString]: the root, as a string.
  769. ///
  770. /// For details which root types are supported, and how the length is calculated, see
  771. /// \ref nsPath::ERootType "ERootType" and \ref nsPath::GetRootType
  772. ///
  773. CString CPath::GetRoot(ERootType * rt, bool greedy) const
  774. {
  775. int len = 0;
  776. ERootType rt_ = nsPath::GetRootType(m_path, &len, greedy);
  777. if (rt)
  778. *rt = rt_;
  779. return m_path.Left(len);
  780. }
  781. // ==================================================================
  782. // CPath::SplitRoot
  783. // ------------------------------------------------------------------
  784. ///
  785. /// removes and returns the root element from the contained path
  786. /// You can call SplitRoot repeatedly to retrieve the path segments in order
  787. ///
  788. /// \param rt [ERootType * =NULL] if not NULL, receives the type of the root element
  789. /// note: the root element type can be recognized correctly only for the first segment
  790. /// \returns [CString]: the root element of the original path
  791. /// \par Side Effects: root element removed from contained path
  792. ///
  793. CString CPath::SplitRoot(ERootType * rt)
  794. {
  795. CString head;
  796. if (!m_path.GetLength())
  797. return head;
  798. int rootLen = 0;
  799. ERootType rt_ = nsPath::GetRootType(m_path, &rootLen, false);
  800. if (rt)
  801. *rt = rt_;
  802. if (rt_ == rtNoRoot) // not a typical root element
  803. {
  804. int start = 0;
  805. if (GetFirstChar(m_path) == '\\') // skip leading backslash (double backslas handled before)
  806. ++start;
  807. int ipos = m_path.Find('\\', start);
  808. if (ipos < 0)
  809. {
  810. head = start ? m_path.Mid(start) : m_path;
  811. m_path.Empty();
  812. }
  813. else
  814. {
  815. head = m_path.Mid(start, ipos-start);
  816. m_path = m_path.Mid(ipos+1);
  817. }
  818. }
  819. else
  820. {
  821. head = m_path.Left(rootLen);
  822. if (rootLen < m_path.GetLength() && m_path[rootLen] == '\\')
  823. ++rootLen;
  824. m_path = m_path.Mid(rootLen);
  825. }
  826. return head;
  827. }
  828. // ==================================================================
  829. // CPath::GetPath
  830. // ------------------------------------------------------------------
  831. ///
  832. /// returns the path (without file name and extension)
  833. /// \param includeRoot [bool=true]: if \c true (default), the root is included in the retuerned path.
  834. /// \returns [CPath]: the path, excluding file name and
  835. /// \par Implementation:
  836. /// Uses \c PathFindFileName, and \c PathSkipRoot
  837. /// \todo
  838. /// - in "c:\\temp\\", \c PathFindFileName considers "temp\\" to be a file name and returns
  839. /// "c:\\" only. This is clearly not my idea of a path
  840. /// - when Extending \c CPath::GetRoot, this function should be adjusted as well
  841. ///
  842. //
  843. CPath CPath::GetPath(bool includeRoot ) const
  844. {
  845. LPCTSTR path = m_path;
  846. LPCTSTR fileName = PathFindFileName(path);
  847. if (fileName == NULL) // seems to find something in any way!
  848. fileName = path + m_path.GetLength();
  849. LPCTSTR rootEnd = includeRoot ? NULL : PathSkipRoot(path);
  850. CPath ret;
  851. if (rootEnd == NULL) // NULL if root should be included, or no root is found
  852. return m_path.Left((int)(fileName-path));
  853. else
  854. return m_path.Mid((int)(rootEnd-path), (int)(fileName-rootEnd));
  855. }
  856. // ==================================================================
  857. // CPath::GetName
  858. // ------------------------------------------------------------------
  859. ///
  860. /// \returns [CString]: the file name of the path
  861. /// \par Differences to \c PathFindFileName:
  862. /// \c PathFindFileName, always treats the last path segment as file name, even if it ends with a backslash.
  863. /// \c GetName treats such a string as not containing a file name\n
  864. /// \b Example: for "c:\\temp\\", \c PathFindFileName finds "temp\\" as file name. \c GetName returns
  865. /// an empty string.
  866. CString CPath::GetName() const
  867. {
  868. // fix treating final path segments as file name
  869. if (GetLastChar(m_path) == '\\')
  870. return CString();
  871. LPCTSTR path = m_path;
  872. LPCTSTR fileName = PathFindFileName(path);
  873. if (fileName == NULL)
  874. return CString();
  875. return m_path.Mid((int)(fileName-path));
  876. }
  877. // ==================================================================
  878. // CPath::GetTitle
  879. // ------------------------------------------------------------------
  880. ///
  881. /// \returns [CString]: the file title, without path or extension
  882. //
  883. CString CPath::GetTitle() const
  884. {
  885. LPCTSTR path = m_path;
  886. LPCTSTR fileName = PathFindFileName(path);
  887. LPCTSTR ext = PathFindExtension(path);
  888. if (fileName == NULL)
  889. return CString();
  890. if (ext == NULL)
  891. return m_path.Mid((int)(fileName-path));
  892. return m_path.Mid((int)(fileName-path), (int)(ext-fileName));
  893. }
  894. // ==================================================================
  895. // CPath::GetExtension
  896. // ------------------------------------------------------------------
  897. ///
  898. /// \returns [CString]: file extension
  899. /// \par Differences to \c PathFindExtension
  900. /// Unlike \c PathFindExtension, the period is not included in the extension string
  901. //
  902. CString CPath::GetExtension() const
  903. {
  904. LPCTSTR path = m_path;
  905. LPCTSTR ext = PathFindExtension(path);
  906. if (ext == NULL)
  907. return CString();
  908. if (*ext == '.') // skip "."
  909. ++ext;
  910. return m_path.Mid((int)(ext-path));
  911. }
  912. // ==================================================================
  913. // CPath::AddExtension
  914. // ------------------------------------------------------------------
  915. ///
  916. /// Appends the specified extension to the path. The path remains unmodified if it already contains
  917. /// an extension (that is, the part behind the last backslash contains a period)
  918. /// \par Difference to \c PathAddExtension
  919. /// Unlike CPath::AddExtension adds a period, if \c extension does not start with one
  920. /// \param extension [LPCTSTR]: the extension to append
  921. /// \param len [int, default=-1]: (optional) length of \c extension in characters, not counting the
  922. /// terminating zero. This argument is only for avoiding a call to _tcslen if the caller already
  923. /// knows the length of the string. The string still needs to be zero-terminated and contain exactly
  924. /// \c len characters.
  925. /// \returns [CPath &]: reference to the modified Path object
  926. //
  927. CPath & CPath::AddExtension(LPCTSTR extension, int len)
  928. {
  929. if (!extension)
  930. return AddExtension(_T(""), 0);
  931. if (*extension != '.')
  932. {
  933. CString s = CString('.') + extension;
  934. return AddExtension( s, s.GetLength());
  935. }
  936. if (len < 0)
  937. return AddExtension(extension, (int)_tcslen(extension));
  938. int totalLen = m_path.GetLength() + len; // already counts the period
  939. PathAddExtension(CStringLock(m_path, totalLen), extension);
  940. return *this;
  941. }
  942. // ==================================================================
  943. // CPath::RemoveExtension
  944. // ------------------------------------------------------------------
  945. ///
  946. /// Removes the extension of the path, if it has any.
  947. /// \returns [CPath &]: reference to the modified path object
  948. //
  949. CPath& CPath::RemoveExtension()
  950. {
  951. PathRemoveExtension(CStringLock(m_path));
  952. return *this;
  953. }
  954. // ==================================================================
  955. // CPath::RenameExtension
  956. // ------------------------------------------------------------------
  957. ///
  958. /// Replaces an existing extension with the new given extension.
  959. /// If the path has no extension, it is appended.
  960. /// \par Difference to \c PathRenameExtension:
  961. /// Unlike PathRenameExtension, \c newExt needs not start with a period.
  962. /// \param newExt [LPCTSTR ]: newextension
  963. /// \returns [CPath &]: reference to the modified path string
  964. //
  965. CPath & CPath::RenameExtension(LPCTSTR newExt)
  966. {
  967. if (newExt == NULL || *newExt != '.')
  968. {
  969. RemoveExtension();
  970. return AddExtension(newExt);
  971. }
  972. int maxLen = m_path.GetLength() + (int)_tcslen(newExt) + 1;
  973. PathRenameExtension(CStringLock(m_path, maxLen), newExt);
  974. return *this;
  975. }
  976. // ==================================================================
  977. // ::RemoveFileSpec
  978. // ------------------------------------------------------------------
  979. ///
  980. /// Removes the file specification (amd extension) from the path.
  981. /// \returns [CPath &]: a reference to the modified path object
  982. //
  983. CPath & CPath::RemoveFileSpec()
  984. {
  985. PathRemoveFileSpec(CStringLock(m_path));
  986. return *this;
  987. }
  988. // ==================================================================
  989. // CPath::SplitArgs
  990. // ------------------------------------------------------------------
  991. ///
  992. /// (static) Separates a path string from command line arguments
  993. ///
  994. /// \param path_args [CString const &]: the path string with additional command line arguments
  995. /// \param args [CString *, out]: if not \c NULL, receives the arguments separated from the path
  996. /// \param cleanup [DWORD, = epc_Default]: the "cleanup" treatment to apply to the path, see \c CPath::Clean
  997. /// \returns [CPath]: a new path without the arguments
  998. //
  999. CPath SplitArgs(CString const & path_args, CString * args, DWORD cleanup)
  1000. {
  1001. CString pathWithArgs = path_args;
  1002. // when "trim" is given, trim left spaces, so PathRemoveArgsworks correctly and returns
  1003. // the path part with the correct length
  1004. if (cleanup & epcTrim)
  1005. pathWithArgs.TrimLeft();
  1006. // assign with only removing the arguments
  1007. CPath path(pathWithArgs, epcRemoveArgs);
  1008. // cut non-argument part away from s
  1009. if (args)
  1010. {
  1011. *args = pathWithArgs.Mid(path.GetLength());
  1012. args->TrimLeft();
  1013. }
  1014. // now, clean the contained string (last since it might shorten the path string)
  1015. path.Clean(cleanup &~ epcRemoveArgs);
  1016. return path;
  1017. }
  1018. // ==================================================================
  1019. // CPath::SplitIconLocation
  1020. // ------------------------------------------------------------------
  1021. ///
  1022. /// (static) Splits a path string containing an icon location into path and icon index
  1023. ///
  1024. /// \param path_icon [CString const &]: the string containing an icon location
  1025. /// \param pIcon [int *, NULL]: if not NULL, receives the icon index
  1026. /// \param cleanup [DWORD, epc_Default]: additional cleanup to apply to the returned path
  1027. /// \returns [CPath]: the path contained in \c path_icon (without the icon location)
  1028. //
  1029. CPath SplitIconLocation(CString const & path_icon, int * pIcon, DWORD cleanup)
  1030. {
  1031. CString strpath = path_icon;
  1032. int icon = PathParseIconLocation( CStringLock(strpath) );
  1033. if (pIcon)
  1034. *pIcon = icon;
  1035. return CPath(strpath, cleanup & ~epcRemoveIconLocation);
  1036. }
  1037. // ==================================================================
  1038. // CPath::BuildRoot
  1039. // ------------------------------------------------------------------
  1040. ///
  1041. /// (static) Creates a root path from a drive index (0..25)
  1042. /// \param driveNumber [int]: Number of the drive, 0 == 'A', etc.
  1043. /// \returns [CPath]: a path consisitng only of a drive root
  1044. //
  1045. CPath BuildRoot(int driveNumber)
  1046. {
  1047. CString strDriveRoot;
  1048. ::PathBuildRoot(CStringLock(strDriveRoot, 3), driveNumber);
  1049. return CPath(strDriveRoot, 0);
  1050. }
  1051. // ==================================================================
  1052. // CPath::GetModuleFileName
  1053. // ------------------------------------------------------------------
  1054. ///
  1055. /// Returns the path of the dspecified module handle
  1056. /// Path is limited to \c MAX_PATH characters
  1057. /// see Win32: GetModuleFileName for more information
  1058. /// \param module [HMODULE =NULL ]: DLL module handle, or NULL for path to exe
  1059. /// \returns [CPath]: path to the specified module, or to the application exe if \c module==NULL.
  1060. /// If an error occurs, the function returrns an empty string.
  1061. /// Call \c GetLastError() for more information.
  1062. ///
  1063. CPath GetModuleFileName(HMODULE module)
  1064. {
  1065. CString path;
  1066. DWORD ok = ::GetModuleFileName(module, CStringLock(path, MAX_PATH), MAX_PATH+1);
  1067. if (ok == 0)
  1068. return CPath();
  1069. return CPath(path);
  1070. }
  1071. // ==================================================================
  1072. // CPath::GetCurrentDirectory
  1073. // ------------------------------------------------------------------
  1074. ///
  1075. /// \returns [CPath]: the current directory, se Win32: \c GetCurrentDirectory.
  1076. /// \remarks
  1077. /// If an error occurs the function returns an empty string. More information is available
  1078. /// through \c GetLastError.
  1079. ///
  1080. CPath GetCurrentDirectory()
  1081. {
  1082. CString path;
  1083. CStringLock buffer(path, MAX_PATH);
  1084. DWORD chars = ::GetCurrentDirectory(MAX_PATH+1, buffer);
  1085. buffer.Release(chars);
  1086. return CPath(path);
  1087. }
  1088. // ==================================================================
  1089. // CPath::GetCommonPrefix
  1090. // ------------------------------------------------------------------
  1091. ///
  1092. /// Returns the common prefix of this path and the given other path,
  1093. /// e.g. for "c:\temp\foo\foo.txt" and "c:\temp\bar\bar.txt", it returns
  1094. /// "c:\temp".
  1095. /// \param secondPath [LPCTSTR]: the path to compare to
  1096. /// \returns [CPath]: a new path, containing the part that is identical
  1097. //
  1098. CPath CPath::GetCommonPrefix(LPCTSTR secondPath)
  1099. {
  1100. CString prefix;
  1101. PathCommonPrefix(m_path, secondPath, CStringLock(prefix, m_path.GetLength()));
  1102. return CPath(prefix, 0);
  1103. }
  1104. // ==================================================================
  1105. // CPath::GetDriveNumber
  1106. // ------------------------------------------------------------------
  1107. ///
  1108. /// \returns [int]: the driver number (0..25 for A..Z), or -1 if the
  1109. /// path does not start with a drive letter
  1110. //
  1111. int CPath::GetDriveNumber()
  1112. {
  1113. return PathGetDriveNumber(m_path);
  1114. }
  1115. // ==================================================================
  1116. // CPath::GetDriveLetter
  1117. // ------------------------------------------------------------------
  1118. ///
  1119. /// \returns [TCHAR]: the drive letter in uppercase, or 0
  1120. //
  1121. TCHAR CPath::GetDriveLetter()
  1122. {
  1123. int driveNum = GetDriveNumber();
  1124. if (driveNum < 0)
  1125. return 0;
  1126. return (TCHAR)(driveNum + 'A');
  1127. }
  1128. // ==================================================================
  1129. // CPath::RelativePathTo
  1130. // ------------------------------------------------------------------
  1131. ///
  1132. /// Determines a relative path from the contained path to the specified \c pathTo
  1133. /// \par Difference to \c PathRelativeTo:
  1134. /// - instead of a file attributes value, you specify a flag (this is a probelm only
  1135. /// if the function supports other attribues than FILE_ATTRIBUTE_DIRECTORY in the future)
  1136. /// - no flag / attribute is specified for the destination (it does not seem to make a difference)
  1137. /// \param pathTo [LPCTSTR]: the target path or drive
  1138. /// \param srcIsDir [bool =false]: determines whether the current path is as a directory or a file
  1139. /// \returns [CPath]: a relative path from this to \c pathTo
  1140. //
  1141. CPath CPath::RelativePathTo(LPCTSTR pathTo,bool srcIsDir)
  1142. {
  1143. CString path;
  1144. if (!pathTo)
  1145. return CPath();
  1146. // maximum length estimate:
  1147. // going up to the root of a path like "c:\a\b\c\d\e", and then append the entire to path
  1148. int maxLen = 3*m_path.GetLength() / 2 +1 + (int)_tcslen(pathTo);
  1149. PathRelativePathTo( CStringLock(path, maxLen),
  1150. m_path,
  1151. srcIsDir ? FILE_ATTRIBUTE_DIRECTORY : 0,
  1152. pathTo, 0);
  1153. return CPath(path, 0);
  1154. }
  1155. // ==================================================================
  1156. // MakeRelative
  1157. // ------------------------------------------------------------------
  1158. ///
  1159. /// Of the path contained is below \c basePath, it is made relative.
  1160. /// Otherwise, it remains unmodified.
  1161. ///
  1162. /// Unlike RelativePathTo, the path is made relative only if the base path
  1163. /// matches completely, and does not generate ".." elements.
  1164. ///
  1165. /// \return [bool] true if the path was modified, false otherwise.
  1166. ///
  1167. bool CPath::MakeRelative(CPath const & basePath)
  1168. {
  1169. CPath basePathBS = basePath;
  1170. basePathBS.AddBackslash(); // add backslash so that "c:\a" is not a base path for "c:\alqueida\files"
  1171. if (m_path.GetLength() > basePathBS.GetLength())
  1172. {
  1173. if (0 == _tcsnicmp(basePathBS, m_path, basePathBS.GetLength()))
  1174. {
  1175. m_path = m_path.Mid(basePathBS.GetLength());
  1176. return true;
  1177. }
  1178. }
  1179. return false;
  1180. }
  1181. // ==================================================================
  1182. // MakeAbsolute
  1183. // ------------------------------------------------------------------
  1184. ///
  1185. /// If the contained path is relative, it is prefixed by \c basePath.
  1186. /// Otherwise it remains unmodified.
  1187. ///
  1188. /// Use: as anti-MakeRelative.
  1189. ///
  1190. bool CPath::MakeAbsolute(CPath const & basePath)
  1191. {
  1192. if (IsRelative())
  1193. {
  1194. m_path = basePath & m_path;
  1195. return true;
  1196. }
  1197. return false;
  1198. }
  1199. // ==================================================================
  1200. // CPath::MatchSpec
  1201. // ------------------------------------------------------------------
  1202. ///
  1203. /// Checks if the path matches a certain specification
  1204. /// \param spec [LPCTSTR]: File specification (like "*.txt")
  1205. /// \returns [bool]: true if the path matches the specification
  1206. //
  1207. bool CPath::MatchSpec(LPCTSTR spec)
  1208. {
  1209. return PathMatchSpec(m_path, spec) != 0;
  1210. }
  1211. // ==================================================================
  1212. // CPath::ExpandEnvStrings
  1213. // ------------------------------------------------------------------
  1214. ///
  1215. /// replaces environment string references with their current value.
  1216. /// See MSDN: \c ExpandEnvironmentStrings for more information
  1217. /// \returns [CPath &]: reference to the modified path
  1218. //
  1219. CPath & CPath::ExpandEnvStrings()
  1220. {
  1221. CString target;
  1222. DWORD len = m_path.GetLength();
  1223. DWORD required = ExpandEnvironmentStrings(
  1224. m_path,
  1225. CStringLock(target, len), len+1);
  1226. if (required > len+1)
  1227. ExpandEnvironmentStrings(
  1228. m_path,
  1229. CStringLock(target, required), required+1);
  1230. m_path = CPath(target, 0);
  1231. return *this;
  1232. }
  1233. // ==================================================================
  1234. // CPath::GetCompactStr
  1235. // ------------------------------------------------------------------
  1236. ///
  1237. /// Inserts ellipses so the path fits into the specified number of pixels
  1238. /// See also SMDN: \c PathCompactPath
  1239. /// \param dc [HDC]: device context where the path is displayed
  1240. /// \param dx [UINT]: number of pixels where to display
  1241. /// \param eppFlags [DWORD, =0]: combination of \c EPathPacking flags indicating how to prepare the path
  1242. /// \returns [CString]: path string prepared for display
  1243. //
  1244. CString CPath::GetCompactStr(HDC dc,UINT dx, DWORD eppFlags)
  1245. {
  1246. CString ret = GetStr(eppFlags);
  1247. PathCompactPath(dc, CStringLock(ret), dx);
  1248. return ret;
  1249. }
  1250. // ==================================================================
  1251. // CPath::GetCompactStr
  1252. // ------------------------------------------------------------------
  1253. ///
  1254. /// Inserts ellipses so the path does not exceed the given number of characters
  1255. /// \param cchMax [UINT]: maximum number of characters
  1256. /// \param eppFlags [DWORD, =0]: combination of \c EPathPacking flags indicating how to prepare the path
  1257. /// \param flags [DWORD, =0]: reserved, must be 0
  1258. /// \returns [CString]: path string prepared for display
  1259. //
  1260. CString CPath::GetCompactStr(UINT cchMax,DWORD flags, DWORD eppFlags )
  1261. {
  1262. CString cleanPath = GetStr(eppFlags);
  1263. CString ret;
  1264. PathCompactPathEx(CStringLock(ret, cleanPath.GetLength()), cleanPath, cchMax, flags);
  1265. return ret;
  1266. }
  1267. // ==================================================================
  1268. // CPath::SetDlgItem
  1269. // ------------------------------------------------------------------
  1270. ///
  1271. /// Sets the text of a child control of a window or dialog,
  1272. /// PathCompactPath to make it fit
  1273. ///
  1274. /// \param dlg [HWND]: the window handle of the parent window
  1275. /// \param dlgCtrlID [UINT]: ID of the child control
  1276. /// \param eppFlags [DWORD, =0]: combination of \c EPathPacking flags indicating how to prepare the path
  1277. /// \returns [void]:
  1278. //
  1279. void CPath::SetDlgItem(HWND dlg,UINT dlgCtrlID, DWORD eppFlags)
  1280. {
  1281. CString cleanPath = GetStr(eppFlags);
  1282. PathSetDlgItemPath(dlg, dlgCtrlID, cleanPath);
  1283. }
  1284. // ==================================================================
  1285. // ::SearchAndQualify
  1286. // ------------------------------------------------------------------
  1287. ///
  1288. /// Searches for the given file on the search path. If it exists in the search path,
  1289. /// it is qualified with the full path of the first occurence found. Otherwise, it is
  1290. /// qualified with the current path. An absolute file paths remains unchanged.
  1291. ///
  1292. /// \note
  1293. /// SearchAndQualify seems to be affected by the same problem
  1294. /// as \ref nsPath::CPath::Canonicalize "Canonicalize" : a path like "..\\..\\x"
  1295. /// is changed to "\\x", which is clearly not correct (IMHO).
  1296. /// \n
  1297. /// compare also: \ref nsPath::CPath::FindOnPath "FindOnPath":
  1298. /// FindOnPath allows to specify custom directories to be searched before the search path, and
  1299. /// behaves differently in some cases.
  1300. /// If the file is not found on the search path, \c FindOnPath leaves the file name unchanged.
  1301. /// SearchAndQualify qualifies the path with the current directory in this case
  1302. ///
  1303. //
  1304. CPath & CPath::SearchAndQualify()
  1305. {
  1306. if (!m_path.GetLength())
  1307. return *this;
  1308. CString qualified;
  1309. DWORD len = m_path.GetLength();
  1310. while (qualified.GetLength() == 0)
  1311. {
  1312. PathSearchAndQualify(m_path, CStringLock(qualified, len), len+1);
  1313. len *= 2;
  1314. }
  1315. m_path = qualified;
  1316. return *this;
  1317. }
  1318. // ==================================================================
  1319. // CPath::FindOnPath
  1320. // ------------------------------------------------------------------
  1321. ///
  1322. /// Similar to SearchAndQualify, but
  1323. /// \note
  1324. /// -# the return value of PathFindOnPath does \b not indicate whether the file
  1325. /// exisits, that's why we don't return it either. If you want to check if the file
  1326. /// really is there use \c FileExists
  1327. /// -# PathFindOnPath does not check for string overflow. Documentation recommends to use a buffer
  1328. /// of length MAX_PATH. I don't trust it to be fail safe in case a path plus the string
  1329. /// exceeds this length (note that the file would not be found in this case - but the shell
  1330. /// API might be tempted to build the string inside the buffer)\n
  1331. /// If you don't need the "additional Dirs" functionality, it is recommended to use
  1332. /// SearchAndQualify instead
  1333. ///
  1334. /// \param additionalDirs [LPCTSTR *, = NULL]: Additional NULL-terminated array of directories
  1335. /// to search first
  1336. /// \returns [CPath &]: a reference to the fully qualified file path
  1337. ///
  1338. /// \par error handling:
  1339. /// If the function succeeds, \c GetLastError returns 0. Otherwise, \c GetLastError returns a Win32 error code.
  1340. ///
  1341. CPath & CPath::FindOnPath(LPCTSTR * additionalDirs)
  1342. {
  1343. DWORD len = m_path.GetLength() + 1 + MAX_PATH;
  1344. bool ok = PathFindOnPath(CStringLock(m_path, len), additionalDirs) != 0;
  1345. if (ok)
  1346. SetLastError(0);
  1347. return *this;
  1348. }
  1349. // ==================================================================
  1350. // CPath::Exists
  1351. // ------------------------------------------------------------------
  1352. ///
  1353. /// \returns [bool]: true if the file exists on the file system, false otherwise.
  1354. //
  1355. bool CPath::Exists() const
  1356. {
  1357. return PathFileExists(m_path) != 0;
  1358. }
  1359. // ==================================================================
  1360. // CPath::IsDirectory
  1361. // ------------------------------------------------------------------
  1362. ///
  1363. /// \returns [bool]: true if the contained path specifies a directory
  1364. /// that exists on the file system
  1365. //
  1366. bool CPath::IsDirectory() const
  1367. {
  1368. return PathIsDirectory(m_path) != 0;
  1369. }
  1370. // ==================================================================
  1371. // CPath::IsSystemFolder
  1372. // ------------------------------------------------------------------
  1373. ///
  1374. /// \param attrib [DWORD, default = FILE_ATTRIBUTE_SYSTEM]: the attributes that
  1375. /// identify a system folder
  1376. /// \returns [bool]: true if the specified path exists and is a system folder
  1377. //
  1378. bool CPath::IsSystemFolder(DWORD attrib) const
  1379. {
  1380. return PathIsSystemFolder(m_path, attrib) != 0;
  1381. }
  1382. // ==================================================================
  1383. // CPath::MakeSystemFolder
  1384. // ------------------------------------------------------------------
  1385. ///
  1386. /// \param make [bool, default=true]: true to set the "System Folder" state, false to reset it
  1387. /// \par error handling:
  1388. /// If the function succeeds, \c GetLastError returns 0. Otherwise, \c GetLastError returns a Win32 error code.
  1389. //
  1390. CPath & CPath::MakeSystemFolder(bool make)
  1391. {
  1392. bool ok = make ? PathMakeSystemFolder(m_path) != 0 : PathUnmakeSystemFolder(m_path) != 0;
  1393. if (ok)
  1394. SetLastError(0);
  1395. return *this;
  1396. }
  1397. // ==================================================================
  1398. // MakeFullPath
  1399. // ------------------------------------------------------------------
  1400. ///
  1401. /// Makes a absolute path from a relative path, using the current working directory.
  1402. ///
  1403. /// If the path is already absolute, it is not changed.
  1404. ///
  1405. CPath & CPath::MakeFullPath()
  1406. {
  1407. if (!IsRelative())
  1408. return *this;
  1409. LPTSTR dummy = NULL;
  1410. DWORD chars = GetFullPathName(m_path, 0, NULL, &dummy);
  1411. _ASSERTE(chars > 0);
  1412. CString fullStr;
  1413. chars = GetFullPathName(m_path, chars, CStringLock(fullStr, chars), &dummy);
  1414. m_path = fullStr;
  1415. return *this;
  1416. }
  1417. // ==================================================================
  1418. // CPath::GetAttributes
  1419. // ------------------------------------------------------------------
  1420. ///
  1421. /// \returns [DWORD]: the file attributes of the specified path or file, or -1 if it
  1422. /// does not exist.
  1423. //
  1424. DWORD CPath::GetAttributes()
  1425. {
  1426. return ::GetFileAttributes(m_path);
  1427. //
  1428. }
  1429. // ==================================================================
  1430. // CPath::GetAttributes
  1431. // ------------------------------------------------------------------
  1432. ///
  1433. /// retrives the \c GetFileExInfoStandard File Attribute information
  1434. ///
  1435. /// \param fad [WIN32_FILE_ATTRIBUTE_DATA &, out]: receives the extended file attribute
  1436. /// information (like size, timestamps) for the specified file
  1437. /// \returns [bool]: true if the file is found and the query was successful, false otherwise
  1438. //
  1439. bool CPath::GetAttributes(WIN32_FILE_ATTRIBUTE_DATA & fad)
  1440. {
  1441. ZeroMemory(&fad, sizeof(fad));
  1442. return ::GetFileAttributesEx(m_path, GetFileExInfoStandard, &fad) != 0;
  1443. }
  1444. // ==================================================================
  1445. // CPath::EnvUnexpandRoot
  1446. // ------------------------------------------------------------------
  1447. ///
  1448. /// replaces path start with matching environment variable
  1449. /// If the path starts with the value of the environment variable %envVar%,
  1450. /// The beginning of the path is replaced with the environment variable.
  1451. ///
  1452. /// e.g. when specifying "WinDir" as \c envVar, "C:\\Windows\\foo.dll" is replaced by
  1453. /// "%WINDIR%\foo.dll"
  1454. ///
  1455. /// \param envVar [LPCTSTR]: environment variable to check
  1456. /// \returns \c true if the path was modified.
  1457. ///
  1458. /// If the environment variable does not exist, or the value of the environment variable
  1459. /// does not match the beginning of the path, the path is unmodified and the function returns
  1460. /// false.
  1461. ///
  1462. bool CPath::EnvUnexpandRoot(LPCTSTR envVar)
  1463. {
  1464. return nsDetail::EnvUnsubstRoot(m_path, envVar);
  1465. }
  1466. // ==================================================================
  1467. // CPath::EnvUnexpandDefaultRoots
  1468. // ------------------------------------------------------------------
  1469. ///
  1470. /// Tries to replace the path root with a matching environment variable.
  1471. ///
  1472. ///
  1473. /// Checks a set of default environment variables, if they match the beginning of the path.
  1474. /// If one of them matches, the beginning of the path is replaced with the environment
  1475. /// variable specification, and the function returns true.
  1476. /// Otherwise, the path remains unmodified and the function returns false.
  1477. ///
  1478. /// see EnvUnexpandRoot for details.
  1479. ///
  1480. bool CPath::EnvUnexpandDefaultRoots()
  1481. {
  1482. // note: Order is important
  1483. return EnvUnexpandRoot(_T("APPDATA")) ||
  1484. EnvUnexpandRoot(_T("USERPROFILE")) ||
  1485. EnvUnexpandRoot(_T("ALLUSERSPROFILE")) ||
  1486. EnvUnexpandRoot(_T("ProgramFiles")) ||
  1487. EnvUnexpandRoot(_T("SystemRoot")) ||
  1488. EnvUnexpandRoot(_T("WinDir")) ||
  1489. EnvUnexpandRoot(_T("SystemDrive"));
  1490. }
  1491. // ==================================================================
  1492. // CPath::FromRegistry
  1493. // ------------------------------------------------------------------
  1494. ///
  1495. /// Reads a path string from the registry.
  1496. /// \param baseKey [HKEY]: base key for registry path
  1497. /// \param subkey [LPCTSTR]: registry path
  1498. /// \param name [LPCTSTR] name of the value
  1499. /// \returns [CPath]: a path string read from the specified location
  1500. ///
  1501. /// If the path is stored as REG_EXPAND_SZ, environment strings are expanded.
  1502. /// Otherwise, the string remains unmodified.
  1503. ///
  1504. /// \par Error Handling:
  1505. /// If an error occurs, the return value is an empty string, and GetLastError() returns the respective
  1506. /// error code. In particular, if the registry value exists but does not contain a string, GetLastError()
  1507. /// returns ERROR_INVALID_DATA
  1508. /// \n\n
  1509. /// If the function succeeds, GetLastError() returns zero.
  1510. ///
  1511. /// See also nsPath::ToRegistry
  1512. ///
  1513. CPath FromRegistry(HKEY baseKey, LPCTSTR subkey, LPCTSTR name)
  1514. {
  1515. SetLastError(0);
  1516. CAutoHKEY key;
  1517. DWORD ok = RegOpenKeyEx(baseKey, subkey, 0, KEY_READ, key.OutArg());
  1518. if (ok != ERROR_SUCCESS)
  1519. {
  1520. SetLastError(ok);
  1521. return CPath();
  1522. }
  1523. DWORD len = 256;
  1524. DWORD type = 0;
  1525. CString path;
  1526. do
  1527. {
  1528. CStringLock buffer(path, len);
  1529. if (!buffer)
  1530. {
  1531. SetLastError(ERROR_OUTOFMEMORY);
  1532. return CPath();
  1533. }
  1534. DWORD size = (len+1) * sizeof(TCHAR); // size includes terminating zero
  1535. ok = RegQueryValueEx(key, name, NULL, &type,
  1536. (LPBYTE) buffer.operator LPTSTR(), &size );
  1537. // read successfully:
  1538. if (ok == ERROR_SUCCESS)
  1539. {
  1540. if (type != REG_SZ && type != REG_EXPAND_SZ)
  1541. {
  1542. SetLastError(ERROR_INVALID_DATA);
  1543. return CPath();
  1544. }
  1545. break; // accept string
  1546. }
  1547. // buffer to small
  1548. if (ok == ERROR_MORE_DATA)
  1549. {
  1550. len = (size + sizeof(TCHAR) - 1) / sizeof(TCHAR);
  1551. continue;
  1552. }
  1553. // otherwise, an error occured
  1554. SetLastError(ok);
  1555. return CPath();
  1556. } while(1);
  1557. DWORD cleanup = epc_Default;
  1558. if (type == REG_SZ)
  1559. cleanup &= ~epcExpandEnvStrings;
  1560. else
  1561. cleanup |= epcExpandEnvStrings; // on by default, but I might change my mind..
  1562. return CPath(path, cleanup);
  1563. }
  1564. // ==================================================================
  1565. // CPath::ToRegistry
  1566. // ------------------------------------------------------------------
  1567. ///
  1568. /// Writes the path to the registry
  1569. ///
  1570. /// \param baseKey: root of registry path
  1571. /// \param subkey: registry path where to store
  1572. /// \param name: name to store the key under
  1573. /// \param replaceEnv [bool=true]: If true (default), environment strings will be replaced
  1574. /// with environment variables, and the string is stored as REG_EXPAND_SZ.
  1575. /// Otherwise, the string is stored unmodified as REG_SZ.
  1576. ///
  1577. /// See also nsPath::FromRegistry
  1578. ///
  1579. long CPath::ToRegistry(HKEY baseKey,LPCTSTR subkey,LPCTSTR name,bool replaceEnv)
  1580. {
  1581. CAutoHKEY key;
  1582. DWORD ok = RegCreateKeyEx(baseKey, subkey, NULL, NULL, 0, KEY_WRITE, NULL, key.OutArg(), NULL);
  1583. if (ok != ERROR_SUCCESS)
  1584. return ok;
  1585. CString path;
  1586. if (replaceEnv)
  1587. {
  1588. CPath ptemp = path;
  1589. ptemp.EnvUnexpandDefaultRoots();
  1590. path = ptemp.GetStr();
  1591. }
  1592. else
  1593. path = GetStr();
  1594. ok = RegSetValueEx(key, name, 0, replaceEnv ? REG_EXPAND_SZ : REG_SZ,
  1595. (BYTE *) path.operator LPCTSTR(),
  1596. (path.GetLength()+1) * sizeof(TCHAR) );
  1597. return ok;
  1598. }
  1599. // ==================================================================
  1600. // IsDot, IsDotDot, IsDotty
  1601. // ------------------------------------------------------------------
  1602. ///
  1603. bool CPath::IsDot() const
  1604. {
  1605. return m_path.GetLength() == 1 && m_path[0] == '.';
  1606. }
  1607. bool CPath::IsDotDot() const
  1608. {
  1609. return m_path.GetLength() == 2 && m_path[0] == '.' && m_path[1] == '.';
  1610. }
  1611. bool CPath::IsDotty() const
  1612. {
  1613. return IsDot() || IsDotDot();
  1614. }
  1615. const LPCTSTR InvalidChars_Windows =
  1616. _T("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F")
  1617. _T("\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")
  1618. _T("\\/:*?\"<>|");
  1619. // ==================================================================
  1620. // IsValid
  1621. // ------------------------------------------------------------------
  1622. ///
  1623. /// returns true if the path satisfies Windows naming conventions
  1624. ///
  1625. bool CPath::IsValid() const
  1626. {
  1627. if (!m_path.GetLength()) return false;
  1628. if (m_path.FindOneOf(InvalidChars_Windows) >= 0) return false;
  1629. if (GetLastChar(m_path) == '.') // may not end in '.', except "." and ".."
  1630. {
  1631. if (m_path.GetLength() > 2 || m_path[0] != '.')
  1632. return false;
  1633. }
  1634. return true;
  1635. }
  1636. // ==================================================================
  1637. // ReplaceInvalid
  1638. // ------------------------------------------------------------------
  1639. ///
  1640. /// replaces all invalid file name characters inc \c s with \c replaceChar
  1641. /// This is helpful when generating names based on user input
  1642. ///
  1643. CString ReplaceInvalid(CString const & str, TCHAR replaceChar)
  1644. {
  1645. if (!str.GetLength() || CPath(str).IsDotty())
  1646. return str;
  1647. CString s = str;
  1648. for(int i=0; i<s.GetLength(); ++i)
  1649. {
  1650. TCHAR ch = s.GetAt(i);
  1651. if (_tcschr(InvalidChars_Windows, ch))
  1652. s.SetAt(i, replaceChar);
  1653. }
  1654. // last one may not be a dot
  1655. int len = s.GetLength();
  1656. if (s[len-1] == '.')
  1657. s.SetAt(len-1, replaceChar);
  1658. return s;
  1659. }
  1660. // ==================================================================
  1661. } // namespace nsPath