X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/53a410d1b242fce6ef061c7e6d24f54f8e38664d..acd32ffcdb319f162633c20e0202db3f8542998a:/src/common/filename.cpp diff --git a/src/common/filename.cpp b/src/common/filename.cpp index 451effbc3c..618fdac037 100644 --- a/src/common/filename.cpp +++ b/src/common/filename.cpp @@ -68,24 +68,25 @@ #endif #ifndef WX_PRECOMP -#include "wx/intl.h" -#include "wx/log.h" -#include "wx/file.h" + #ifdef __WXMSW__ + #include "wx/msw/wrapwin.h" // For GetShort/LongPathName + #endif + #include "wx/dynarray.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/utils.h" + #include "wx/crt.h" #endif #include "wx/filename.h" +#include "wx/private/filename.h" #include "wx/tokenzr.h" #include "wx/config.h" // for wxExpandEnvVars -#include "wx/utils.h" -#include "wx/file.h" #include "wx/dynlib.h" +#include "wx/dir.h" -// For GetShort/LongPathName -#ifdef __WIN32__ -#include "wx/msw/wrapwin.h" -#if defined(__MINGW32__) -#include "wx/msw/gccpriv.h" -#endif +#if defined(__WIN32__) && defined(__MINGW32__) + #include "wx/msw/gccpriv.h" #endif #ifdef __WXWINCE__ @@ -93,7 +94,7 @@ #endif #if defined(__WXMAC__) - #include "wx/mac/private.h" // includes mac headers + #include "wx/osx/private.h" // includes mac headers #endif // utime() is POSIX so should normally be available on all Unices @@ -138,6 +139,12 @@ #define MAX_PATH _MAX_PATH #endif + +#if wxUSE_LONGLONG +extern const wxULongLong wxInvalidSize = (unsigned)-1; +#endif // wxUSE_LONGLONG + + // ---------------------------------------------------------------------------- // private classes // ---------------------------------------------------------------------------- @@ -155,26 +162,29 @@ public: Write }; - wxFileHandle(const wxString& filename, OpenMode mode) + wxFileHandle(const wxString& filename, OpenMode mode, int flags = 0) { m_hFile = ::CreateFile ( - filename, // name + filename.fn_str(), // name mode == Read ? GENERIC_READ // access mask : GENERIC_WRITE, FILE_SHARE_READ | // sharing mode FILE_SHARE_WRITE, // (allow everything) NULL, // no secutity attr OPEN_EXISTING, // creation disposition - 0, // no flags + flags, // flags NULL // no template file ); if ( m_hFile == INVALID_HANDLE_VALUE ) { - wxLogSysError(_("Failed to open '%s' for %s"), - filename.c_str(), - mode == Read ? _("reading") : _("writing")); + if ( mode == Read ) + wxLogSysError(_("Failed to open '%s' for reading"), + filename.c_str()); + else + wxLogSysError(_("Failed to open '%s' for writing"), + filename.c_str()); } } @@ -281,6 +291,17 @@ static wxString wxGetVolumeString(const wxString& volume, wxPathFormat format) return path; } +// return true if the format used is the DOS/Windows one and the string looks +// like a UNC path +static bool IsUNCPath(const wxString& path, wxPathFormat format) +{ + return format == wxPATH_DOS && + path.length() >= 4 && // "\\a" can't be a UNC path + path[0u] == wxFILE_SEP_PATH_DOS && + path[1u] == wxFILE_SEP_PATH_DOS && + path[2u] != wxFILE_SEP_PATH_DOS; +} + // ============================================================================ // implementation // ============================================================================ @@ -304,9 +325,28 @@ void wxFileName::Assign(const wxString& volume, const wxString& name, const wxString& ext, bool hasExt, - wxPathFormat format ) + wxPathFormat format) { - SetPath( path, format ); + // we should ignore paths which look like UNC shares because we already + // have the volume here and the UNC notation (\\server\path) is only valid + // for paths which don't start with a volume, so prevent SetPath() from + // recognizing "\\foo\bar" in "c:\\foo\bar" as an UNC path + // + // note also that this is a rather ugly way to do what we want (passing + // some kind of flag telling to ignore UNC paths to SetPath() would be + // better) but this is the safest thing to do to avoid breaking backwards + // compatibility in 2.8 + if ( IsUNCPath(path, format) ) + { + // remove one of the 2 leading backslashes to ensure that it's not + // recognized as an UNC path by SetPath() + wxString pathNonUNC(path, 1, wxString::npos); + SetPath(pathNonUNC, format); + } + else // no UNC complications + { + SetPath(path, format); + } m_volume = volume; m_ext = ext; @@ -424,7 +464,7 @@ void wxFileName::Assign(const wxString& fullpathOrig, // always recognize fullpath as directory, even if it doesn't end with a // slash wxString fullpath = fullpathOrig; - if ( !wxEndsWithPathSeparator(fullpath) ) + if ( !fullpath.empty() && !wxEndsWithPathSeparator(fullpath) ) { fullpath += GetPathSeparator(format); } @@ -518,7 +558,7 @@ bool wxFileName::FileExists( const wxString &file ) bool wxFileName::DirExists() const { - return wxFileName::DirExists( GetFullPath() ); + return wxFileName::DirExists( GetPath() ); } bool wxFileName::DirExists( const wxString &dir ) @@ -561,7 +601,7 @@ wxString wxFileName::GetCwd(const wxString& volume) bool wxFileName::SetCwd() { - return wxFileName::SetCwd( GetFullPath() ); + return wxFileName::SetCwd( GetPath() ); } bool wxFileName::SetCwd( const wxString &cwd ) @@ -579,40 +619,128 @@ wxString wxFileName::GetHomeDir() return ::wxGetHomeDir(); } -#if wxUSE_FILE -void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp) +// ---------------------------------------------------------------------------- +// CreateTempFileName +// ---------------------------------------------------------------------------- + +#if wxUSE_FILE || wxUSE_FFILE + + +#if !defined wx_fdopen && defined HAVE_FDOPEN + #define wx_fdopen fdopen +#endif + +// NB: GetTempFileName() under Windows creates the file, so using +// O_EXCL there would fail +#ifdef __WINDOWS__ + #define wxOPEN_EXCL 0 +#else + #define wxOPEN_EXCL O_EXCL +#endif + + +#ifdef wxOpenOSFHandle +#define WX_HAVE_DELETE_ON_CLOSE +// On Windows create a file with the FILE_FLAGS_DELETE_ON_CLOSE flags. +// +static int wxOpenWithDeleteOnClose(const wxString& filename) { - wxString tempname = CreateTempFileName(prefix, fileTemp); - if ( tempname.empty() ) - { - // error, failed to get temp file name - Clear(); - } - else // ok - { - Assign(tempname); - } + DWORD access = GENERIC_READ | GENERIC_WRITE; + + DWORD disposition = OPEN_ALWAYS; + + DWORD attributes = FILE_ATTRIBUTE_TEMPORARY | + FILE_FLAG_DELETE_ON_CLOSE; + + HANDLE h = ::CreateFile(filename.fn_str(), access, 0, NULL, + disposition, attributes, NULL); + + return wxOpenOSFHandle(h, wxO_BINARY); } +#endif // wxOpenOSFHandle -/* static */ -wxString -wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) + +// Helper to open the file +// +static int wxTempOpen(const wxString& path, bool *deleteOnClose) +{ +#ifdef WX_HAVE_DELETE_ON_CLOSE + if (*deleteOnClose) + return wxOpenWithDeleteOnClose(path); +#endif + + *deleteOnClose = false; + + return wxOpen(path, wxO_BINARY | O_RDWR | O_CREAT | wxOPEN_EXCL, 0600); +} + + +#if wxUSE_FFILE +// Helper to open the file and attach it to the wxFFile +// +static bool wxTempOpen(wxFFile *file, const wxString& path, bool *deleteOnClose) +{ +#ifndef wx_fdopen + *deleteOnClose = false; + return file->Open(path, _T("w+b")); +#else // wx_fdopen + int fd = wxTempOpen(path, deleteOnClose); + if (fd == -1) + return false; + file->Attach(wx_fdopen(fd, "w+b")); + return file->IsOpened(); +#endif // wx_fdopen +} +#endif // wxUSE_FFILE + + +#if !wxUSE_FILE + #define WXFILEARGS(x, y) y +#elif !wxUSE_FFILE + #define WXFILEARGS(x, y) x +#else + #define WXFILEARGS(x, y) x, y +#endif + + +// Implementation of wxFileName::CreateTempFileName(). +// +static wxString wxCreateTempImpl( + const wxString& prefix, + WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp), + bool *deleteOnClose = NULL) { +#if wxUSE_FILE && wxUSE_FFILE + wxASSERT(fileTemp == NULL || ffileTemp == NULL); +#endif wxString path, dir, name; + bool wantDeleteOnClose = false; + + if (deleteOnClose) + { + // set the result to false initially + wantDeleteOnClose = *deleteOnClose; + *deleteOnClose = false; + } + else + { + // easier if it alwasys points to something + deleteOnClose = &wantDeleteOnClose; + } // use the directory specified by the prefix - SplitPath(prefix, &dir, &name, NULL /* extension */); + wxFileName::SplitPath(prefix, &dir, &name, NULL /* extension */); -#if defined(__WXWINCE__) if (dir.empty()) { - // FIXME. Create \temp dir? - dir = wxT("\\"); + dir = wxFileName::GetTempDir(); } + +#if defined(__WXWINCE__) path = dir + wxT("\\") + name; int i = 1; - while (FileExists(path)) + while (wxFileName::FileExists(path)) { path = dir + wxT("\\") + name ; path << i; @@ -620,28 +748,8 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) } #elif defined(__WINDOWS__) && !defined(__WXMICROWIN__) - - if ( dir.empty() ) - { - if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) ) - { - wxLogLastError(_T("GetTempPath")); - } - - if ( dir.empty() ) - { - // GetTempFileName() fails if we pass it an empty string - dir = _T('.'); - } - } - else // we have a dir to create the file in - { - // ensure we use only the back slashes as GetTempFileName(), unlike all - // the other APIs, is picky and doesn't accept the forward ones - dir.Replace(_T("/"), _T("\\")); - } - - if ( !::GetTempFileName(dir, name, 0, wxStringBuffer(path, MAX_PATH + 1)) ) + if ( !::GetTempFileName(dir.fn_str(), name.fn_str(), 0, + wxStringBuffer(path, MAX_PATH + 1)) ) { wxLogLastError(_T("GetTempFileName")); @@ -649,31 +757,6 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) } #else // !Windows - if ( dir.empty() ) - { -#if defined(__WXMAC__) && !defined(__DARWIN__) - dir = wxMacFindFolder( (short) kOnSystemDisk, kTemporaryFolderType, kCreateFolder ) ; -#else // !Mac - dir = wxGetenv(_T("TMP")); - if ( dir.empty() ) - { - dir = wxGetenv(_T("TEMP")); - } - - if ( dir.empty() ) - { - // default - #if defined(__DOS__) || defined(__OS2__) - dir = _T("."); - #elif defined(__WXMAC__) - dir = wxMacFindFolder( (short) kOnSystemDisk, kTemporaryFolderType, kCreateFolder ) ; - #else - dir = _T("/tmp"); - #endif - } -#endif // Mac/!Mac - } - path = dir; if ( !wxEndsWithPathSeparator(dir) && @@ -689,7 +772,7 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) path += _T("XXXXXX"); // we need to copy the path to the buffer in which mkstemp() can modify it - wxCharBuffer buf( wxConvFile.cWX2MB( path ) ); + wxCharBuffer buf(path.fn_str()); // cast is safe because the string length doesn't change int fdTemp = mkstemp( (char*)(const char*) buf ); @@ -703,12 +786,28 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) { path = wxConvFile.cMB2WX( (const char*) buf ); + #if wxUSE_FILE // avoid leaking the fd if ( fileTemp ) { fileTemp->Attach(fdTemp); } else + #endif + + #if wxUSE_FFILE + if ( ffileTemp ) + { + #ifdef wx_fdopen + ffileTemp->Attach(wx_fdopen(fdTemp, "r+b")); + #else + ffileTemp->Open(path, _T("r+b")); + close(fdTemp); + #endif + } + else + #endif + { close(fdTemp); } @@ -720,7 +819,7 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) path += _T("XXXXXX"); wxCharBuffer buf = wxConvFile.cWX2MB( path ); - if ( !mktemp( (const char*) buf ) ) + if ( !mktemp( (char*)(const char*) buf ) ) { path.clear(); } @@ -741,7 +840,7 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) { // 3 hex digits is enough for numTries == 1000 < 4096 pathTry = path + wxString::Format(_T("%.03x"), (unsigned int) n); - if ( !FileExists(pathTry) ) + if ( !wxFileName::FileExists(pathTry) ) { break; } @@ -752,9 +851,6 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) path = pathTry; #endif // HAVE_MKTEMP/!HAVE_MKTEMP - if ( !path.empty() ) - { - } #endif // HAVE_MKSTEMP/!HAVE_MKSTEMP #endif // Windows/!Windows @@ -763,20 +859,33 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) { wxLogSysError(_("Failed to create a temporary file name")); } - else if ( fileTemp && !fileTemp->IsOpened() ) + else { + bool ok = true; + // open the file - of course, there is a race condition here, this is // why we always prefer using mkstemp()... - // - // NB: GetTempFileName() under Windows creates the file, so using - // write_excl there would fail - if ( !fileTemp->Open(path, -#if defined(__WINDOWS__) && !defined(__WXMICROWIN__) - wxFile::write, -#else - wxFile::write_excl, -#endif - wxS_IRUSR | wxS_IWUSR) ) + #if wxUSE_FILE + if ( fileTemp && !fileTemp->IsOpened() ) + { + *deleteOnClose = wantDeleteOnClose; + int fd = wxTempOpen(path, deleteOnClose); + if (fd != -1) + fileTemp->Attach(fd); + else + ok = false; + } + #endif + + #if wxUSE_FFILE + if ( ffileTemp && !ffileTemp->IsOpened() ) + { + *deleteOnClose = wantDeleteOnClose; + ok = wxTempOpen(ffileTemp, path, deleteOnClose); + } + #endif + + if ( !ok ) { // FIXME: If !ok here should we loop and try again with another // file name? That is the standard recourse if open(O_EXCL) @@ -792,15 +901,188 @@ wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) return path; } + +static bool wxCreateTempImpl( + const wxString& prefix, + WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp), + wxString *name) +{ + bool deleteOnClose = true; + + *name = wxCreateTempImpl(prefix, + WXFILEARGS(fileTemp, ffileTemp), + &deleteOnClose); + + bool ok = !name->empty(); + + if (deleteOnClose) + name->clear(); +#ifdef __UNIX__ + else if (ok && wxRemoveFile(*name)) + name->clear(); +#endif + + return ok; +} + + +static void wxAssignTempImpl( + wxFileName *fn, + const wxString& prefix, + WXFILEARGS(wxFile *fileTemp, wxFFile *ffileTemp)) +{ + wxString tempname; + tempname = wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, ffileTemp)); + + if ( tempname.empty() ) + { + // error, failed to get temp file name + fn->Clear(); + } + else // ok + { + fn->Assign(tempname); + } +} + + +void wxFileName::AssignTempFileName(const wxString& prefix) +{ + wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, NULL)); +} + +/* static */ +wxString wxFileName::CreateTempFileName(const wxString& prefix) +{ + return wxCreateTempImpl(prefix, WXFILEARGS(NULL, NULL)); +} + +#endif // wxUSE_FILE || wxUSE_FFILE + + +#if wxUSE_FILE + +wxString wxCreateTempFileName(const wxString& prefix, + wxFile *fileTemp, + bool *deleteOnClose) +{ + return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), deleteOnClose); +} + +bool wxCreateTempFile(const wxString& prefix, + wxFile *fileTemp, + wxString *name) +{ + return wxCreateTempImpl(prefix, WXFILEARGS(fileTemp, NULL), name); +} + +void wxFileName::AssignTempFileName(const wxString& prefix, wxFile *fileTemp) +{ + wxAssignTempImpl(this, prefix, WXFILEARGS(fileTemp, NULL)); +} + +/* static */ +wxString +wxFileName::CreateTempFileName(const wxString& prefix, wxFile *fileTemp) +{ + return wxCreateTempFileName(prefix, fileTemp); +} + #endif // wxUSE_FILE + +#if wxUSE_FFILE + +wxString wxCreateTempFileName(const wxString& prefix, + wxFFile *fileTemp, + bool *deleteOnClose) +{ + return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), deleteOnClose); +} + +bool wxCreateTempFile(const wxString& prefix, + wxFFile *fileTemp, + wxString *name) +{ + return wxCreateTempImpl(prefix, WXFILEARGS(NULL, fileTemp), name); + +} + +void wxFileName::AssignTempFileName(const wxString& prefix, wxFFile *fileTemp) +{ + wxAssignTempImpl(this, prefix, WXFILEARGS(NULL, fileTemp)); +} + +/* static */ +wxString +wxFileName::CreateTempFileName(const wxString& prefix, wxFFile *fileTemp) +{ + return wxCreateTempFileName(prefix, fileTemp); +} + +#endif // wxUSE_FFILE + + // ---------------------------------------------------------------------------- // directory operations // ---------------------------------------------------------------------------- +// helper of GetTempDir(): check if the given directory exists and return it if +// it does or an empty string otherwise +namespace +{ + +wxString CheckIfDirExists(const wxString& dir) +{ + return wxFileName::DirExists(dir) ? dir : wxString(); +} + +} // anonymous namespace + +wxString wxFileName::GetTempDir() +{ + // first try getting it from environment: this allows overriding the values + // used by default if the user wants to create temporary files in another + // directory + wxString dir = CheckIfDirExists(wxGetenv("TMPDIR")); + if ( dir.empty() ) + { + dir = CheckIfDirExists(wxGetenv("TMP")); + if ( dir.empty() ) + dir = CheckIfDirExists(wxGetenv("TEMP")); + } + + // if no environment variables are set, use the system default + if ( dir.empty() ) + { +#if defined(__WXWINCE__) + dir = CheckIfDirExists(wxT("\\temp")); +#elif defined(__WINDOWS__) && !defined(__WXMICROWIN__) + if ( !::GetTempPath(MAX_PATH, wxStringBuffer(dir, MAX_PATH + 1)) ) + { + wxLogLastError(_T("GetTempPath")); + } +#elif defined(__WXMAC__) && wxOSX_USE_CARBON + dir = wxMacFindFolder(short(kOnSystemDisk), kTemporaryFolderType, kCreateFolder); +#endif // systems with native way + } + + // fall back to hard coded value + if ( dir.empty() ) + { +#ifdef __UNIX_LIKE__ + dir = CheckIfDirExists("/tmp"); + if ( dir.empty() ) +#endif // __UNIX_LIKE__ + dir = "."; + } + + return dir; +} + bool wxFileName::Mkdir( int perm, int flags ) { - return wxFileName::Mkdir( GetFullPath(), perm, flags ); + return wxFileName::Mkdir(GetPath(), perm, flags); } bool wxFileName::Mkdir( const wxString& dir, int perm, int flags ) @@ -821,14 +1103,7 @@ bool wxFileName::Mkdir( const wxString& dir, int perm, int flags ) size_t count = dirs.GetCount(); for ( size_t i = 0; i < count; i++ ) { - if ( i > 0 || -#if defined(__WXMAC__) && !defined(__DARWIN__) - // relative pathnames are exactely the other way round under mac... - !filename.IsAbsolute() -#else - filename.IsAbsolute() -#endif - ) + if ( i > 0 || filename.IsAbsolute() ) currPath += wxFILE_SEP_PATH; currPath += dirs[i]; @@ -849,14 +1124,83 @@ bool wxFileName::Mkdir( const wxString& dir, int perm, int flags ) return ::wxMkdir( dir, perm ); } -bool wxFileName::Rmdir() +bool wxFileName::Rmdir(int flags) { - return wxFileName::Rmdir( GetFullPath() ); + return wxFileName::Rmdir( GetPath(), flags ); } -bool wxFileName::Rmdir( const wxString &dir ) +bool wxFileName::Rmdir(const wxString& dir, int flags) { - return ::wxRmdir( dir ); +#ifdef __WXMSW__ + if ( flags & wxPATH_RMDIR_RECURSIVE ) + { + // SHFileOperation needs double null termination string + // but without separator at the end of the path + wxString path(dir); + if ( path.Last() == wxFILE_SEP_PATH ) + path.RemoveLast(); + path += _T('\0'); + + SHFILEOPSTRUCT fileop; + wxZeroMemory(fileop); + fileop.wFunc = FO_DELETE; + fileop.pFrom = path.fn_str(); + fileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION; + #ifndef __WXWINCE__ + // FOF_NOERRORUI is not defined in WinCE + fileop.fFlags |= FOF_NOERRORUI; + #endif + + int ret = SHFileOperation(&fileop); + if ( ret != 0 ) + { + // SHFileOperation may return non-Win32 error codes, so the error + // message can be incorrect + wxLogApiError(_T("SHFileOperation"), ret); + return false; + } + + return true; + } + else if ( flags & wxPATH_RMDIR_FULL ) +#else // !__WXMSW__ + if ( flags != 0 ) // wxPATH_RMDIR_FULL or wxPATH_RMDIR_RECURSIVE +#endif // !__WXMSW__ + { + wxString path(dir); + if ( path.Last() != wxFILE_SEP_PATH ) + path += wxFILE_SEP_PATH; + + wxDir d(path); + + if ( !d.IsOpened() ) + return false; + + wxString filename; + + // first delete all subdirectories + bool cont = d.GetFirst(&filename, "", wxDIR_DIRS | wxDIR_HIDDEN); + while ( cont ) + { + wxFileName::Rmdir(path + filename, flags); + cont = d.GetNext(&filename); + } + +#ifndef __WXMSW__ + if ( flags & wxPATH_RMDIR_RECURSIVE ) + { + // delete all files too + cont = d.GetFirst(&filename, "", wxDIR_FILES | wxDIR_HIDDEN); + while ( cont ) + { + ::wxRemoveFile(path + filename); + cont = d.GetNext(&filename); + } + } +#endif // !__WXMSW__ + } + + return ::wxRmdir(dir); } // ---------------------------------------------------------------------------- @@ -878,7 +1222,6 @@ bool wxFileName::Normalize(int flags, } } - // the existing path components wxArrayString dirs = GetDirs(); @@ -887,7 +1230,7 @@ bool wxFileName::Normalize(int flags, format = GetFormat(format); - // make the path absolute + // set up the directory to use for making the path absolute later if ( (flags & wxPATH_NORM_ABSOLUTE) && !IsAbsolute(format) ) { if ( cwd.empty() ) @@ -898,20 +1241,6 @@ bool wxFileName::Normalize(int flags, { curDir.AssignDir(cwd); } - - // the path may be not absolute because it doesn't have the volume name - // but in this case we shouldn't modify the directory components of it - // but just set the current volume - if ( !HasVolume() && curDir.HasVolume() ) - { - SetVolume(curDir.GetVolume()); - - if ( !m_relative ) - { - // yes, it was the case - we don't need curDir then - curDir.Clear(); - } - } } // handle ~ stuff under Unix only @@ -922,8 +1251,18 @@ bool wxFileName::Normalize(int flags, wxString dir = dirs[0u]; if ( !dir.empty() && dir[0u] == _T('~') ) { + // to make the path absolute use the home directory curDir.AssignDir(wxGetUserHome(dir.c_str() + 1)); + // if we are expanding the tilde, then this path + // *should* be already relative (since we checked for + // the tilde only in the first char of the first dir); + // if m_relative==false, it's because it was initialized + // from a string which started with /~; in that case + // we reach this point but then need m_relative=true + // for relative->absolute expansion later + m_relative = true; + dirs.RemoveAt(0u); } } @@ -932,14 +1271,34 @@ bool wxFileName::Normalize(int flags, // transform relative path into abs one if ( curDir.IsOk() ) { - wxArrayString dirsNew = curDir.GetDirs(); - size_t count = dirs.GetCount(); - for ( size_t n = 0; n < count; n++ ) + // this path may be relative because it doesn't have the volume name + // and still have m_relative=true; in this case we shouldn't modify + // our directory components but just set the current volume + if ( !HasVolume() && curDir.HasVolume() ) { - dirsNew.Add(dirs[n]); + SetVolume(curDir.GetVolume()); + + if ( !m_relative ) + { + // yes, it was the case - we don't need curDir then + curDir.Clear(); + } } - dirs = dirsNew; + // finally, prepend curDir to the dirs array + wxArrayString dirsNew = curDir.GetDirs(); + WX_PREPEND_ARRAY(dirs, dirsNew); + + // if we used e.g. tilde expansion previously and wxGetUserHome didn't + // return for some reason an absolute path, then curDir maybe not be absolute! + if ( curDir.IsAbsolute(format) ) + { + // we have prepended an absolute path and thus we are now an absolute + // file name too + m_relative = false; + } + // else if (flags & wxPATH_NORM_ABSOLUTE): + // should we warn the user that we didn't manage to make the path absolute? } // now deal with ".", ".." and the rest @@ -971,11 +1330,6 @@ bool wxFileName::Normalize(int flags, } } - if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) ) - { - dir.MakeLower(); - } - m_dirs.Add(dir); } @@ -985,37 +1339,77 @@ bool wxFileName::Normalize(int flags, wxString filename; if (GetShortcutTarget(GetFullPath(format), filename)) { - // Repeat this since we may now have a new path - if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) ) - { - filename.MakeLower(); - } m_relative = false; Assign(filename); } } #endif - if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) ) +#if defined(__WIN32__) + if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) ) { - // VZ: expand env vars here too? + Assign(GetLongPath()); + } +#endif // Win32 + // Change case (this should be kept at the end of the function, to ensure + // that the path doesn't change any more after we normalize its case) + if ( (flags & wxPATH_NORM_CASE) && !IsCaseSensitive(format) ) + { m_volume.MakeLower(); m_name.MakeLower(); m_ext.MakeLower(); + + // directory entries must be made lower case as well + count = m_dirs.GetCount(); + for ( size_t i = 0; i < count; i++ ) + { + m_dirs[i].MakeLower(); + } } - // we do have the path now - // - // NB: need to do this before (maybe) calling Assign() below - m_relative = false; + return true; +} -#if defined(__WIN32__) - if ( (flags & wxPATH_NORM_LONG) && (format == wxPATH_DOS) ) - { - Assign(GetLongPath()); - } -#endif // Win32 +#ifndef __WXWINCE__ +bool wxFileName::ReplaceEnvVariable(const wxString& envname, + const wxString& replacementFmtString, + wxPathFormat format) +{ + // look into stringForm for the contents of the given environment variable + wxString val; + if (envname.empty() || + !wxGetEnv(envname, &val)) + return false; + if (val.empty()) + return false; + + wxString stringForm = GetPath(wxPATH_GET_VOLUME, format); + // do not touch the file name and the extension + + wxString replacement = wxString::Format(replacementFmtString, envname); + stringForm.Replace(val, replacement); + + // Now assign ourselves the modified path: + Assign(stringForm, GetFullName(), format); + + return true; +} +#endif + +bool wxFileName::ReplaceHomeDir(wxPathFormat format) +{ + wxString homedir = wxGetHomeDir(); + if (homedir.empty()) + return false; + + wxString stringForm = GetPath(wxPATH_GET_VOLUME, format); + // do not touch the file name and the extension + + stringForm.Replace(homedir, "~"); + + // Now assign ourselves the modified path: + Assign(stringForm, GetFullName(), format); return true; } @@ -1044,10 +1438,12 @@ bool wxFileName::Normalize(int flags, #include #endif -bool wxFileName::GetShortcutTarget(const wxString& shortcutPath, wxString& targetFilename, wxString* arguments) +bool wxFileName::GetShortcutTarget(const wxString& shortcutPath, + wxString& targetFilename, + wxString* arguments) { wxString path, file, ext; - wxSplitPath(shortcutPath, & path, & file, & ext); + wxFileName::SplitPath(shortcutPath, & path, & file, & ext); HRESULT hres; IShellLink* psl; @@ -1073,6 +1469,8 @@ bool wxFileName::GetShortcutTarget(const wxString& shortcutPath, wxString& targe MAX_PATH); hres = ppf->Load(wsz, 0); + ppf->Release(); + if (SUCCEEDED(hres)) { wxChar buf[2048]; @@ -1093,11 +1491,13 @@ bool wxFileName::GetShortcutTarget(const wxString& shortcutPath, wxString& targe } } } + + psl->Release(); } - psl->Release(); return success; } -#endif + +#endif // __WIN32__ && !__WXWINCE__ // ---------------------------------------------------------------------------- @@ -1241,8 +1641,11 @@ wxString wxFileName::GetForbiddenChars(wxPathFormat format) } /* static */ -wxString wxFileName::GetVolumeSeparator(wxPathFormat format) +wxString wxFileName::GetVolumeSeparator(wxPathFormat WXUNUSED_IN_WINCE(format)) { +#ifdef __WXWINCE__ + return wxEmptyString; +#else wxString sepVol; if ( (GetFormat(format) == wxPATH_DOS) || @@ -1253,6 +1656,7 @@ wxString wxFileName::GetVolumeSeparator(wxPathFormat format) //else: leave empty return sepVol; +#endif } /* static */ @@ -1503,13 +1907,13 @@ wxString wxFileName::GetShortPath() const wxString path(GetFullPath()); #if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) && !defined(__WXWINCE__) - DWORD sz = ::GetShortPathName(path, NULL, 0); + DWORD sz = ::GetShortPathName(path.fn_str(), NULL, 0); if ( sz != 0 ) { wxString pathOut; if ( ::GetShortPathName ( - path, + path.fn_str(), wxStringBuffer(pathOut, sz), sz ) != 0 ) @@ -1528,9 +1932,9 @@ wxString wxFileName::GetLongPath() const wxString pathOut, path = GetFullPath(); -#if defined(__WIN32__) && !defined(__WXMICROWIN__) +#if defined(__WIN32__) && !defined(__WXWINCE__) && !defined(__WXMICROWIN__) -#if wxUSE_DYNAMIC_LOADER +#if wxUSE_DYNLIB_CLASS typedef DWORD (WINAPI *GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD); // this is MT-safe as in the worst case we're going to resolve the function @@ -1567,12 +1971,12 @@ wxString wxFileName::GetLongPath() const if ( s_pfnGetLongPathName ) { - DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0); + DWORD dwSize = (*s_pfnGetLongPathName)(path.fn_str(), NULL, 0); if ( dwSize > 0 ) { if ( (*s_pfnGetLongPathName) ( - path, + path.fn_str(), wxStringBuffer(pathOut, dwSize), dwSize ) != 0 ) @@ -1581,7 +1985,7 @@ wxString wxFileName::GetLongPath() const } } } -#endif // wxUSE_DYNAMIC_LOADER +#endif // wxUSE_DYNLIB_CLASS // The OS didn't support GetLongPathName, or some other error. // We need to call FindFirstFile on each component in turn. @@ -1604,25 +2008,27 @@ wxString wxFileName::GetLongPath() const size_t count = dirs.GetCount(); for ( size_t i = 0; i < count; i++ ) { + const wxString& dir = dirs[i]; + // We're using pathOut to collect the long-name path, but using a // temporary for appending the last path component which may be // short-name - tmpPath = pathOut + dirs[i]; - - if ( tmpPath.empty() ) - continue; + tmpPath = pathOut + dir; - // can't see this being necessary? MF - if ( tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) ) + // We must not process "." or ".." here as they would be (unexpectedly) + // replaced by the corresponding directory names so just leave them + // alone + // + // And we can't pass a drive and root dir to FindFirstFile (VZ: why?) + if ( tmpPath.empty() || dir == '.' || dir == ".." || + tmpPath.Last() == GetVolumeSeparator(wxPATH_DOS) ) { - // Can't pass a drive and root dir to FindFirstFile, - // so continue to next dir tmpPath += wxFILE_SEP_PATH; pathOut = tmpPath; continue; } - hFind = ::FindFirstFile(tmpPath, &findFileData); + hFind = ::FindFirstFile(tmpPath.fn_str(), &findFileData); if (hFind == INVALID_HANDLE_VALUE) { // Error: most likely reason is that path doesn't exist, so @@ -1652,8 +2058,6 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format ) { #if defined(__WXMSW__) || defined(__OS2__) || defined(__DOS__) format = wxPATH_DOS; -#elif defined(__WXMAC__) && !defined(__DARWIN__) - format = wxPATH_MAC; #elif defined(__VMS) format = wxPATH_VMS; #else @@ -1663,6 +2067,23 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format ) return format; } +#ifdef wxHAS_FILESYSTEM_VOLUMES + +/* static */ +wxString wxFileName::GetVolumeString(char drive, int flags) +{ + wxASSERT_MSG( !(flags & ~wxPATH_GET_SEPARATOR), "invalid flag specified" ); + + wxString vol(drive); + vol += wxFILE_SEP_DSK; + if ( flags & wxPATH_GET_SEPARATOR ) + vol += wxFILE_SEP_PATH; + + return vol; +} + +#endif // wxHAS_FILESYSTEM_VOLUMES + // ---------------------------------------------------------------------------- // path splitting function // ---------------------------------------------------------------------------- @@ -1679,23 +2100,18 @@ wxFileName::SplitVolume(const wxString& fullpathWithVolume, wxString fullpath = fullpathWithVolume; // special Windows UNC paths hack: transform \\share\path into share:path - if ( format == wxPATH_DOS ) + if ( IsUNCPath(fullpath, format) ) { - if ( fullpath.length() >= 4 && - fullpath[0u] == wxFILE_SEP_PATH_DOS && - fullpath[1u] == wxFILE_SEP_PATH_DOS ) - { - fullpath.erase(0, 2); + fullpath.erase(0, 2); - size_t posFirstSlash = - fullpath.find_first_of(GetPathTerminators(format)); - if ( posFirstSlash != wxString::npos ) - { - fullpath[posFirstSlash] = wxFILE_SEP_DSK; + size_t posFirstSlash = + fullpath.find_first_of(GetPathTerminators(format)); + if ( posFirstSlash != wxString::npos ) + { + fullpath[posFirstSlash] = wxFILE_SEP_DSK; - // UNC paths are always absolute, right? (FIXME) - fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS); - } + // UNC paths are always absolute, right? (FIXME) + fullpath.insert(posFirstSlash + 1, 1, wxFILE_SEP_PATH_DOS); } } @@ -1863,32 +2279,44 @@ bool wxFileName::SetTimes(const wxDateTime *dtAccess, const wxDateTime *dtCreate) { #if defined(__WIN32__) + FILETIME ftAccess, ftCreate, ftWrite; + + if ( dtCreate ) + ConvertWxToFileTime(&ftCreate, *dtCreate); + if ( dtAccess ) + ConvertWxToFileTime(&ftAccess, *dtAccess); + if ( dtMod ) + ConvertWxToFileTime(&ftWrite, *dtMod); + + wxString path; + int flags; if ( IsDir() ) { - // VZ: please let me know how to do this if you can - wxFAIL_MSG( _T("SetTimes() not implemented for the directories") ); + if ( wxGetOsVersion() == wxOS_WINDOWS_9X ) + { + wxLogError(_("Setting directory access times is not supported " + "under this OS version")); + return false; + } + + path = GetPath(); + flags = FILE_FLAG_BACKUP_SEMANTICS; } else // file { - wxFileHandle fh(GetFullPath(), wxFileHandle::Write); - if ( fh.IsOk() ) - { - FILETIME ftAccess, ftCreate, ftWrite; - - if ( dtCreate ) - ConvertWxToFileTime(&ftCreate, *dtCreate); - if ( dtAccess ) - ConvertWxToFileTime(&ftAccess, *dtAccess); - if ( dtMod ) - ConvertWxToFileTime(&ftWrite, *dtMod); + path = GetFullPath(); + flags = 0; + } - if ( ::SetFileTime(fh, - dtCreate ? &ftCreate : NULL, - dtAccess ? &ftAccess : NULL, - dtMod ? &ftWrite : NULL) ) - { - return true; - } + wxFileHandle fh(path, wxFileHandle::Write, flags); + if ( fh.IsOk() ) + { + if ( ::SetFileTime(fh, + dtCreate ? &ftCreate : NULL, + dtAccess ? &ftAccess : NULL, + dtMod ? &ftWrite : NULL) ) + { + return true; } } #elif defined(__UNIX_LIKE__) || (defined(__DOS__) && defined(__WATCOMC__)) @@ -1989,7 +2417,8 @@ bool wxFileName::GetTimes(wxDateTime *dtAccess, return true; } -#elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || (defined(__DOS__) && defined(__WATCOMC__)) +#elif defined(__UNIX_LIKE__) || defined(__WXMAC__) || defined(__OS2__) || (defined(__DOS__) && defined(__WATCOMC__)) + // no need to test for IsDir() here wxStructStat stBuf; if ( wxStat( GetFullPath().c_str(), &stBuf) == 0 ) { @@ -2016,55 +2445,134 @@ bool wxFileName::GetTimes(wxDateTime *dtAccess, #endif // wxUSE_DATETIME -#ifdef __WXMAC__ -const short kMacExtensionMaxLength = 16 ; +// ---------------------------------------------------------------------------- +// file size functions +// ---------------------------------------------------------------------------- + +#if wxUSE_LONGLONG + +/* static */ +wxULongLong wxFileName::GetSize(const wxString &filename) +{ + if (!wxFileExists(filename)) + return wxInvalidSize; + +#if defined(__WXPALMOS__) + // TODO + return wxInvalidSize; +#elif defined(__WIN32__) + wxFileHandle f(filename, wxFileHandle::Read); + if (!f.IsOk()) + return wxInvalidSize; + + DWORD lpFileSizeHigh; + DWORD ret = GetFileSize(f, &lpFileSizeHigh); + if ( ret == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR ) + return wxInvalidSize; + + return wxULongLong(lpFileSizeHigh, ret); +#else // ! __WIN32__ + wxStructStat st; +#ifndef wxNEED_WX_UNISTD_H + if (wxStat( filename.fn_str() , &st) != 0) +#else + if (wxStat( filename, &st) != 0) +#endif + return wxInvalidSize; + return wxULongLong(st.st_size); +#endif +} + +/* static */ +wxString wxFileName::GetHumanReadableSize(const wxULongLong &bs, + const wxString &nullsize, + int precision) +{ + static const double KILOBYTESIZE = 1024.0; + static const double MEGABYTESIZE = 1024.0*KILOBYTESIZE; + static const double GIGABYTESIZE = 1024.0*MEGABYTESIZE; + static const double TERABYTESIZE = 1024.0*GIGABYTESIZE; + + if (bs == 0 || bs == wxInvalidSize) + return nullsize; + + double bytesize = bs.ToDouble(); + if (bytesize < KILOBYTESIZE) + return wxString::Format(_("%s B"), bs.ToString().c_str()); + if (bytesize < MEGABYTESIZE) + return wxString::Format(_("%.*f kB"), precision, bytesize/KILOBYTESIZE); + if (bytesize < GIGABYTESIZE) + return wxString::Format(_("%.*f MB"), precision, bytesize/MEGABYTESIZE); + if (bytesize < TERABYTESIZE) + return wxString::Format(_("%.*f GB"), precision, bytesize/GIGABYTESIZE); + + return wxString::Format(_("%.*f TB"), precision, bytesize/TERABYTESIZE); +} + +wxULongLong wxFileName::GetSize() const +{ + return GetSize(GetFullPath()); +} + +wxString wxFileName::GetHumanReadableSize(const wxString &failmsg, int precision) const +{ + return GetHumanReadableSize(GetSize(), failmsg, precision); +} + +#endif // wxUSE_LONGLONG + +// ---------------------------------------------------------------------------- +// Mac-specific functions +// ---------------------------------------------------------------------------- + +#if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON + +namespace +{ + class MacDefaultExtensionRecord { -public : - MacDefaultExtensionRecord() - { - m_ext[0] = 0 ; - m_type = m_creator = 0 ; - } - MacDefaultExtensionRecord( const MacDefaultExtensionRecord& from ) - { - wxStrcpy( m_ext , from.m_ext ) ; - m_type = from.m_type ; - m_creator = from.m_creator ; - } - MacDefaultExtensionRecord( const wxChar * extension , OSType type , OSType creator ) - { - wxStrncpy( m_ext , extension , kMacExtensionMaxLength ) ; - m_ext[kMacExtensionMaxLength] = 0 ; - m_type = type ; - m_creator = creator ; - } - wxChar m_ext[kMacExtensionMaxLength] ; - OSType m_type ; - OSType m_creator ; -} ; +public: + MacDefaultExtensionRecord() + { + m_type = + m_creator = 0 ; + } + + // default copy ctor, assignment operator and dtor are ok + + MacDefaultExtensionRecord(const wxString& ext, OSType type, OSType creator) + : m_ext(ext) + { + m_type = type; + m_creator = creator; + } + + wxString m_ext; + OSType m_type; + OSType m_creator; +}; -#include "wx/dynarray.h" -WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray) ; +WX_DECLARE_OBJARRAY(MacDefaultExtensionRecord, MacDefaultExtensionArray); -bool gMacDefaultExtensionsInited = false ; +bool gMacDefaultExtensionsInited = false; #include "wx/arrimpl.cpp" -WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray) ; +WX_DEFINE_EXPORTED_OBJARRAY(MacDefaultExtensionArray); -MacDefaultExtensionArray gMacDefaultExtensions ; +MacDefaultExtensionArray gMacDefaultExtensions; // load the default extensions -MacDefaultExtensionRecord gDefaults[] = +const MacDefaultExtensionRecord gDefaults[] = { - MacDefaultExtensionRecord( wxT("txt") , 'TEXT' , 'ttxt' ) , - MacDefaultExtensionRecord( wxT("tif") , 'TIFF' , '****' ) , - MacDefaultExtensionRecord( wxT("jpg") , 'JPEG' , '****' ) , -} ; + MacDefaultExtensionRecord( "txt", 'TEXT', 'ttxt' ), + MacDefaultExtensionRecord( "tif", 'TIFF', '****' ), + MacDefaultExtensionRecord( "jpg", 'JPEG', '****' ), +}; -static void MacEnsureDefaultExtensionsLoaded() +void MacEnsureDefaultExtensionsLoaded() { if ( !gMacDefaultExtensionsInited ) { @@ -2073,10 +2581,12 @@ static void MacEnsureDefaultExtensionsLoaded() { gMacDefaultExtensions.Add( gDefaults[i] ) ; } - gMacDefaultExtensionsInited = true ; + gMacDefaultExtensionsInited = true; } } +} // anonymous namespace + bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator ) { FSRef fsRef ; @@ -2145,11 +2655,9 @@ bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *t void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator ) { - MacEnsureDefaultExtensionsLoaded() ; - MacDefaultExtensionRecord rec ; - rec.m_type = type ; - rec.m_creator = creator ; - wxStrncpy( rec.m_ext , ext.Lower().c_str() , kMacExtensionMaxLength ) ; - gMacDefaultExtensions.Add( rec ) ; + MacEnsureDefaultExtensionsLoaded(); + MacDefaultExtensionRecord rec(ext.Lower(), type, creator); + gMacDefaultExtensions.Add( rec ); } -#endif + +#endif // defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON