📄 path.cpp
字号:
//// This file is part of the aMule Project.//// Copyright (c) 2008 aMule Team ( admin@amule.org / http://www.amule.org )//// Any parts of this program derived from the xMule, lMule or eMule project,// or contributed by third-party developers are copyrighted by their// respective authors.//// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.// // You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA//#include "Path.h"#include "MuleDebug.h"#include "StringFunctions.h"#include <wx/file.h>#ifdef __WXMSW__# include <wx/ffile.h>#endif#include <wx/utils.h>#include <wx/filename.h>// This is required in order to ensure that wx can "handle" filenames// using a different encoding than the current system-wide setting. If// this is not done, such filenames will fail during convertion to/from// multibyte (as in cWC2MB/cMB2WC).#if !wxUSE_GUI && !defined(__WXMSW__)void* setFNConv(){ // This uses the same method as wxApp::Initialize under GTK2 wxString encName = wxLocale::GetSystemEncodingName().Upper(); if (encName.IsEmpty() || (encName == wxT("US-ASCII"))) { encName = wxT("UTF-8"); } return wxConvFileName = new wxConvBrokenFileNames(encName);}// Ensure intializationstatic void* s_foo = setFNConv();#endif// Windows has case-insensitive paths, so we use a// case-insensitive cmp for that platform. TODO:// Perhaps it would be better to simply lowercase// m_filesystem in the contructor ...#ifdef __WXMSW__ #define PATHCMP(a, b) wxStricmp(a, b) #define PATHNCMP(a, b, n) wxStrnicmp(a, b, n)#else #define PATHCMP(a, b) wxStrcmp(a, b) #define PATHNCMP(a, b, n) wxStrncmp(a, b, n)#endif////////////////////////////////////////////////////////////// Helper functions/** Creates a deep copy of the string, avoiding its ref. counting. */inline wxString DeepCopy(const wxString& str){ return wxString(str.c_str(), str.Length());}wxString Demangle(const wxCharBuffer& fn, const wxString& filename){ wxString result = wxConvUTF8.cMB2WC(fn); // FIXME: Is this actually needed for osx/msw? if (!result) { // We only try to further demangle if the current locale is // UTF-8, C or POSIX. This is because in any other case, the // current locale is probably the best choice for printing. static wxFontEncoding enc = wxLocale::GetSystemEncoding(); switch (enc) { // SYSTEM is needed for ANSI encodings such as // "POSIX" and "C", which are only 7bit. case wxFONTENCODING_SYSTEM: case wxFONTENCODING_UTF8: result = wxConvISO8859_1.cMB2WC(fn); break; default: // Nothing to do, the filename is probably Ok. result = DeepCopy(filename); } } return result;}/** Splits a full path into its path and filename component. */inline void DoSplitPath(const wxString& strPath, wxString* path, wxString* name){ bool hasExt = false; wxString ext, vol; wxString* pVol = (path ? &vol : NULL); wxString* pExt = (name ? &ext : NULL); wxFileName::SplitPath(strPath, pVol, path, name, pExt, &hasExt); if (hasExt && pExt) { *name += wxT(".") + ext; } if (path && vol.Length()) { *path = vol + wxFileName::GetVolumeSeparator() + *path; }}/** Removes invalid chars from a filename. */wxString DoCleanup(const wxString& filename, bool keepSpaces, bool isFAT32){ wxString result; for (size_t i = 0; i < filename.Length(); i++) { const wxChar c = filename[i]; switch (c) { case wxT('/'): continue; case wxT('\"'): case wxT('*'): case wxT('<'): case wxT('>'): case wxT('?'): case wxT('|'): case wxT('\\'): case wxT(':'): if (isFAT32) { continue; } default: if ((c == wxT(' ')) && !keepSpaces) { result += wxT("%20"); } else if (c >= 32) { // Many illegal for filenames in windows // below the 32th char (which is space). result += filename[i]; } } } return result;}/** Does the actual work of adding a postfix ... */wxString DoAddPostfix(const wxString& src, const wxString& postfix){ const wxFileName srcFn(src); wxString result = srcFn.GetName() + postfix; if (srcFn.HasExt()) { result += wxT(".") + srcFn.GetExt(); } wxString path = srcFn.GetPath(); if (path.Length()) { return path + wxFileName::GetPathSeparator() + result; } return result;}/** Removes the last extension of a filename. */wxString DoRemoveExt(const wxString& path){ // Using wxFilename which handles paths, etc. wxFileName tmp(path); tmp.ClearExt(); return tmp.GetFullPath();}/** Readies a path for use with wxAccess.. */wxString DoCleanPath(const wxString& path){#ifdef __WXMSW__ // stat fails on windows if there are trailing path-separators. wxString cleanPath = StripSeparators(path, wxString::trailing); // Root paths must end with a separator (X:\ rather than X:). // See comments in wxDirExists. if ((cleanPath.Length() == 2) && (cleanPath.Last() == wxT(':'))) { cleanPath += wxFileName::GetPathSeparator(); } return cleanPath;#else return path;#endif}/** Returns true if the two paths are equal. */bool IsSameAs(const wxString& a, const wxString& b){ // Cache the current directory const wxString cwd = wxGetCwd(); // We normalize everything, except env. variables, which // can cause problems when the string is not encodable // using wxConvLibc which wxWidgets uses for the purpose. const int flags = (wxPATH_NORM_ALL | wxPATH_NORM_CASE) & ~wxPATH_NORM_ENV_VARS; // Let wxFileName handle the tricky stuff involved in actually // comparing two paths ... Currently, a path ending with a path- // seperator will be unequal to the same path without a path- // seperator, which is probably for the best, but can could // lead to some unexpected behavior. wxFileName fn1(a); wxFileName fn2(b); fn1.Normalize(flags, cwd); fn2.Normalize(flags, cwd); return (fn1.GetFullPath() == fn2.GetFullPath());}////////////////////////////////////////////////////////////// CPath implementationCPath::CPath(){}CPath::CPath(const wxString& filename){ // Equivalent to the default constructor ... if (!filename) { return; } wxCharBuffer fn = filename2char(filename); if (fn) { // Filename is valid in the current locale. This means that // it either originated from a (wx)system-call, or from a // user with a properly setup system. m_filesystem = DeepCopy(filename); m_printable = Demangle(fn, filename); } else { // It's not a valid filename in the current locale, so we'll // have to do some magic. This ensures that the filename is // saved as UTF8, even if the system is not unicode enabled, // preserving the original filename till the user has fixed // his system ... fn = wxConvUTF8.cWC2MB(filename); m_filesystem = wxConvFile.cMB2WC(fn); // There's no need to try to unmangle the filename here. m_printable = DeepCopy(filename); } wxASSERT(m_filesystem.Length()); wxASSERT(m_printable.Length());}CPath::CPath(const CPath& other) : CPrintable() , m_printable(DeepCopy(other.m_printable)) , m_filesystem(DeepCopy(other.m_filesystem)){}CPath::~CPath(){}CPath CPath::FromUniv(const wxString& path){ wxCharBuffer fn = wxConvISO8859_1.cWC2MB(path); return CPath(wxConvFile.cMB2WC(fn));}wxString CPath::ToUniv(const CPath& path){ // The logic behind this is that by saving the filename // as a raw bytestream (which is what ISO8859-1 amounts // to), we can always recreate the on-disk filename, as // if we had read it using wx functions. wxCharBuffer fn = wxConvFile.cWC2MB(path.m_filesystem); return wxConvISO8859_1.cMB2WC(fn);}CPath& CPath::operator=(const CPath& other){ if (this != &other) { m_printable = DeepCopy(other.m_printable); m_filesystem = DeepCopy(other.m_filesystem); } return *this;}bool CPath::operator==(const CPath& other) const{ return ::IsSameAs(m_filesystem, other.m_filesystem);}bool CPath::operator!=(const CPath& other) const{ return !(*this == other);}bool CPath::operator<(const CPath& other) const{ return PATHCMP(m_filesystem.c_str(), other.m_filesystem.c_str()) < 0;}bool CPath::IsOk() const{ // Something is very wrong if one of the two is empty. return m_printable.Length() && m_filesystem.Length();}bool CPath::FileExists() const{ return wxFileName::FileExists(m_filesystem);}bool CPath::DirExists() const{ return wxFileName::DirExists(DoCleanPath(m_filesystem));}bool CPath::IsDir(EAccess mode) const{ wxString path = DoCleanPath(m_filesystem); if (!wxFileName::DirExists(path)) { return false; } else if ((mode & writable) && !wxIsWritable(path)) { return false; } else if ((mode & readable) && !wxIsReadable(path)) { return false; } return true;}bool CPath::IsFile(EAccess mode) const{ if (!wxFileName::FileExists(m_filesystem)) { return false; } else if ((mode & writable) && !wxIsWritable(m_filesystem)) { return false; } else if ((mode & readable) && !wxIsReadable(m_filesystem)) { return false; } return true;}wxString CPath::GetRaw() const{ // Copy as c-strings to ensure that the CPath objects can safely // be passed across threads (avoiding wxString ref. counting). return DeepCopy(m_filesystem);}wxString CPath::GetPrintable() const{ // Copy as c-strings to ensure that the CPath objects can safely // be passed across threads (avoiding wxString ref. counting). return DeepCopy(m_printable);}wxString CPath::GetExt() const{ return wxFileName(m_filesystem).GetExt();}CPath CPath::GetPath() const{ CPath path; ::DoSplitPath(m_printable, &path.m_printable, NULL); ::DoSplitPath(m_filesystem, &path.m_filesystem, NULL); return path;}CPath CPath::GetFullName() const{ CPath path; ::DoSplitPath(m_printable, NULL, &path.m_printable); ::DoSplitPath(m_filesystem, NULL, &path.m_filesystem); return path;}sint64 CPath::GetFileSize() const{ if (FileExists()) { wxFile f(m_filesystem); if (f.IsOpened()) { return f.Length(); } } return wxInvalidOffset;}bool CPath::IsSameDir(const CPath& other) const{ wxString a = m_filesystem; wxString b = other.m_filesystem; // This check is needed to avoid trouble in the // case where one path is empty, and the other // points to the root dir. if (a.Length() && b.Length()) { a = StripSeparators(a, wxString::trailing); b = StripSeparators(b, wxString::trailing); } return ::IsSameAs(a, b);}CPath CPath::JoinPaths(const CPath& other) const{ if (!IsOk()) { return CPath(other); } else if (!other.IsOk()) { return CPath(*this); } CPath joinedPath; // DeepCopy shouldn't be needed, as JoinPaths results in the creation of a new string. joinedPath.m_printable = ::JoinPaths(m_printable, other.m_printable); joinedPath.m_filesystem = ::JoinPaths(m_filesystem, other.m_filesystem); return joinedPath;}CPath CPath::Cleanup(bool keepSpaces, bool isFAT32) const{ CPath result; result.m_printable = ::DoCleanup(m_printable, keepSpaces, isFAT32); result.m_filesystem = ::DoCleanup(m_filesystem, keepSpaces, isFAT32); return result;}CPath CPath::AddPostfix(const wxString& postfix) const{ wxASSERT(postfix.IsAscii()); CPath result; result.m_printable = ::DoAddPostfix(m_printable, postfix); result.m_filesystem = ::DoAddPostfix(m_filesystem, postfix); return result;}CPath CPath::AppendExt(const wxString& ext) const{ wxASSERT(ext.IsAscii()); // Though technically, and empty extension would simply // be another . at the end of the filename, we ignore them. if (ext.IsEmpty()) { return *this; } CPath result(*this); if (ext[0] == wxT('.')) { result.m_printable << ext; result.m_filesystem << ext; } else { result.m_printable << wxT(".") << ext; result.m_filesystem << wxT(".") << ext; } return result;}CPath CPath::RemoveExt() const{ CPath result; result.m_printable = DoRemoveExt(m_printable); result.m_filesystem = DoRemoveExt(m_filesystem); return result;}CPath CPath::RemoveAllExt() const{ CPath last, current = RemoveExt(); // Loop untill all extensions are removed do { last = current; current = last.RemoveExt(); } while (last != current); return current;}bool CPath::StartsWith(const CPath& other) const{ // It doesn't make sense comparing invalid paths, // especially since if 'other' was empty, it would // be considered a prefix of any path. if ((IsOk() && other.IsOk()) == false) { return false; } // Adding an seperator to avoid partial matches, such as // "/usr/bi" matching "/usr/bin". TODO: Paths should be // normalized first (in the constructor). const wxString a = StripSeparators(m_filesystem, wxString::trailing) + wxFileName::GetPathSeparator(); const wxString b = StripSeparators(other.m_filesystem, wxString::trailing) + wxFileName::GetPathSeparator(); if (a.Length() < b.Length()) { // Cannot possibly be a prefix. return false; } const size_t checkLen = std::min(a.Length(), b.Length()); return PATHNCMP(a.c_str(), b.c_str(), checkLen) == 0;}wxString CPath::GetPrintableString() const{ return DeepCopy(m_printable);}bool CPath::CloneFile(const CPath& src, const CPath& dst, bool overwrite){ return ::wxCopyFile(src.m_filesystem, dst.m_filesystem, overwrite);}bool CPath::RemoveFile(const CPath& file){ return ::wxRemoveFile(file.m_filesystem);}bool CPath::RenameFile(const CPath& src, const CPath& dst, bool overwrite){ return ::wxRenameFile(src.m_filesystem, dst.m_filesystem, overwrite);}bool CPath::BackupFile(const CPath& src, const wxString& appendix){ wxASSERT(appendix.IsAscii()); CPath dst = CPath(src.m_filesystem + appendix); if (CPath::CloneFile(src, dst, true)) { // Try to ensure that the backup gets physically written // Now - does this have any effect reopening a already closed file // to flush it ???#ifdef __WXMSW__ wxFFile backupFile;#else wxFile backupFile;#endif if (backupFile.Open(dst.m_filesystem)) { backupFile.Flush(); } return true; } return false;}bool CPath::RemoveDir(const CPath& file){ return ::wxRmdir(file.m_filesystem);}bool CPath::MakeDir(const CPath& file){ return ::wxMkdir(file.m_filesystem);}bool CPath::FileExists(const wxString& file){ return CPath(file).FileExists();}bool CPath::DirExists(const wxString& path){ return CPath(path).DirExists();}sint64 CPath::GetFileSize(const wxString& file){ return CPath(file).GetFileSize();}time_t CPath::GetModificationTime(const CPath& file){ return ::wxFileModificationTime(file.m_filesystem);}sint64 CPath::GetFreeSpaceAt(const CPath& path){ wxLongLong free; if (::wxGetDiskSpace(path.m_filesystem, NULL, &free)) { return free.GetValue(); } return wxInvalidOffset;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -