X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/da2fd5acd884129f58a5b394994ace8c22b55b6d..d323d966dd6f73d11cad1094c086ad31f648b9a3:/src/common/filename.cpp diff --git a/src/common/filename.cpp b/src/common/filename.cpp index 91318e43bf..7aef28bdda 100644 --- a/src/common/filename.cpp +++ b/src/common/filename.cpp @@ -9,6 +9,44 @@ // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// +/* + Here are brief descriptions of the filename formats supported by this class: + + wxPATH_UNIX: standard Unix format, absolute file names have the form + /dir1/dir2/.../dirN/filename, "." and ".." stand for the + current and parent directory respectively, "~" is parsed as the + user HOME and "~username" as the HOME of that user + + wxPATH_DOS: DOS/Windows format, absolute file names have the form + drive:\dir1\dir2\...\dirN\filename.ext where drive is a single + letter. "." and ".." as for Unix but no "~". + + There are also UNC names of the form \\share\fullpath + + wxPATH_MAC: Mac OS 8/9 format, absolute file names have the form + volume:dir1:...:dirN:filename + and the relative file names are either + :dir1:...:dirN:filename + or just + filename + (although :filename works as well). + + wxPATH_VMS: VMS native format, absolute file names have the form + :[dir1.dir2.dir3]file.txt + or + :[000000.dir1.dir2.dir3]file.txt + + the is the physical device (i.e. disk). 000000 is the + root directory on the device which can be omitted. + + Note that VMS uses different separators unlike Unix: + : always after the device. If the path does not contain : than + the default (the device of the current directory) is assumed. + [ start of directory specyfication + . separator between directory and subdirectory + ] between directory and file + */ + // ============================================================================ // declarations // ============================================================================ @@ -38,6 +76,124 @@ #include "wx/config.h" // for wxExpandEnvVars #include "wx/utils.h" +#if wxUSE_DYNLIB_CLASS + #include "wx/dynlib.h" +#endif + +// For GetShort/LongPathName +#ifdef __WIN32__ + #include + + #include "wx/msw/winundef.h" +#endif + +// utime() is POSIX so should normally be available on all Unices +#ifdef __UNIX_LIKE__ + #include + #include + #include + #include +#endif + +#ifdef __MWERKS__ + #include + #include + #include +#endif + +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- + +// small helper class which opens and closes the file - we use it just to get +// a file handle for the given file name to pass it to some Win32 API function +#if defined(__WIN32__) && !defined(__WXMICROWIN__) + +class wxFileHandle +{ +public: + wxFileHandle(const wxString& filename) + { + m_hFile = ::CreateFile + ( + filename, // name + GENERIC_READ, // access mask + 0, // no sharing + NULL, // no secutity attr + OPEN_EXISTING, // creation disposition + 0, // no flags + NULL // no template file + ); + + if ( m_hFile == INVALID_HANDLE_VALUE ) + { + wxLogSysError(_("Failed to open '%s' for reading"), + filename.c_str()); + } + } + + ~wxFileHandle() + { + if ( m_hFile != INVALID_HANDLE_VALUE ) + { + if ( !::CloseHandle(m_hFile) ) + { + wxLogSysError(_("Failed to close file handle")); + } + } + } + + // return TRUE only if the file could be opened successfully + bool IsOk() const { return m_hFile != INVALID_HANDLE_VALUE; } + + // get the handle + operator HANDLE() const { return m_hFile; } + +private: + HANDLE m_hFile; +}; + +#endif // __WIN32__ + +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +#if defined(__WIN32__) && !defined(__WXMICROWIN__) + +// convert between wxDateTime and FILETIME which is a 64-bit value representing +// the number of 100-nanosecond intervals since January 1, 1601. + +// the number of milliseconds between the Unix Epoch (January 1, 1970) and the +// FILETIME reference point (January 1, 1601) +static const wxLongLong FILETIME_EPOCH_OFFSET = wxLongLong(0xa97, 0x30b66800); + +static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft) +{ + wxLongLong ll(ft.dwHighDateTime, ft.dwLowDateTime); + + // convert 100ns to ms + ll /= 10000; + + // move it to our Epoch + ll -= FILETIME_EPOCH_OFFSET; + + *dt = wxDateTime(ll); +} + +static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt) +{ + // do the reverse of ConvertFileTimeToWx() + wxLongLong ll = dt.GetValue(); + ll *= 10000; + ll += FILETIME_EPOCH_OFFSET; + + ft->dwHighDateTime = ll.GetHi(); + ft->dwLowDateTime = ll.GetLo(); +} + +#endif // __WIN32__ + // ============================================================================ // implementation // ============================================================================ @@ -48,32 +204,33 @@ void wxFileName::Assign( const wxFileName &filepath ) { - m_ext = filepath.GetExt(); - m_name = filepath.GetName(); + m_volume = filepath.GetVolume(); m_dirs = filepath.GetDirs(); + m_name = filepath.GetName(); + m_ext = filepath.GetExt(); } -void wxFileName::Assign( const wxString& path, - const wxString& name, - const wxString& ext, - wxPathFormat format ) +void wxFileName::Assign(const wxString& volume, + const wxString& path, + const wxString& name, + const wxString& ext, + wxPathFormat format ) { - wxStringTokenizer tn(path, GetPathSeparators(format), - wxTOKEN_RET_EMPTY_ALL); - bool first = TRUE; + wxStringTokenizer tn(path, GetPathSeparators(format)); + m_dirs.Clear(); while ( tn.HasMoreTokens() ) { wxString token = tn.GetNextToken(); - // If the path starts with a slash, we need the first - // dir entry to be an empty for later reassembly. - if (first || !token.IsEmpty()) + // if the path starts with a slash, we do need the first empty dir + // entry to be able to tell later that it was an absolute path, but + // otherwise ignore the double slashes + if ( m_dirs.IsEmpty() || !token.IsEmpty() ) m_dirs.Add( token ); - - first = FALSE; } + m_volume = volume; m_ext = ext; m_name = name; } @@ -81,10 +238,10 @@ void wxFileName::Assign( const wxString& path, void wxFileName::Assign(const wxString& fullpath, wxPathFormat format) { - wxString path, name, ext; - SplitPath(fullpath, &path, &name, &ext, format); + wxString volume, path, name, ext; + SplitPath(fullpath, &volume, &path, &name, &ext, format); - Assign(path, name, ext, format); + Assign(volume, path, name, ext, format); } void wxFileName::Assign(const wxString& path, @@ -100,6 +257,8 @@ void wxFileName::Assign(const wxString& path, void wxFileName::Clear() { m_dirs.Clear(); + + m_volume = m_name = m_ext = wxEmptyString; } @@ -194,14 +353,47 @@ void wxFileName::AssignTempFileName( const wxString &prefix ) // directory operations // ---------------------------------------------------------------------------- -bool wxFileName::Mkdir( int perm ) +bool wxFileName::Mkdir( int perm, bool full ) { - return wxFileName::Mkdir( GetFullPath(), perm ); + return wxFileName::Mkdir( GetFullPath(), perm, full ); } -bool wxFileName::Mkdir( const wxString &dir, int perm ) +bool wxFileName::Mkdir( const wxString &dir, int perm, bool full ) { - return ::wxMkdir( dir, perm ); + if (full) + { + wxFileName filename(dir); + wxArrayString dirs = filename.GetDirs(); + dirs.Add(filename.GetName()); + + size_t count = dirs.GetCount(); + size_t i; + wxString currPath; + int noErrors = 0; + for ( i = 0; i < count; i++ ) + { + currPath += dirs[i]; + + if (currPath.Last() == wxT(':')) + { + // Can't create a root directory so continue to next dir + currPath += wxFILE_SEP_PATH; + continue; + } + + if (!DirExists(currPath)) + if (!wxMkdir(currPath, perm)) + noErrors ++; + + if ( (i < (count-1)) ) + currPath += wxFILE_SEP_PATH; + } + + return (noErrors == 0); + + } + else + return ::wxMkdir( dir, perm ); } bool wxFileName::Rmdir() @@ -316,6 +508,13 @@ bool wxFileName::Normalize(wxPathNormalize flags, m_ext.MakeLower(); } +#if defined(__WXMSW__) && defined(__WIN32__) + if (flags & wxPATH_NORM_LONG) + { + Assign(GetLongPath()); + } +#endif + return TRUE; } @@ -345,23 +544,59 @@ bool wxFileName::SameAs( const wxFileName &filepath, wxPathFormat format) /* static */ bool wxFileName::IsCaseSensitive( wxPathFormat format ) { - // only DOS filenames are case-sensitive - return GetFormat(format) != wxPATH_DOS; + // only Unix filenames are truely case-sensitive + return GetFormat(format) == wxPATH_UNIX; } -bool wxFileName::IsRelative( wxPathFormat format ) +bool wxFileName::IsAbsolute( wxPathFormat format ) { - return !IsAbsolute(format); + // if we have no path, we can't be an abs filename + if ( m_dirs.IsEmpty() ) + { + return FALSE; + } + + switch ( GetFormat(format) ) + { + case wxPATH_DOS: + case wxPATH_VMS: + case wxPATH_MAC: + // must have the drive + return !m_volume.empty(); + + default: + wxFAIL_MSG( _T("unknown wxPATH_XXX style") ); + // fall through + + case wxPATH_UNIX: + const wxString& str = m_dirs[0u]; + if ( str.empty() ) + { + // the path started with '/', it's an absolute one + return TRUE; + } + + // the path is absolute if it starts with a path separator or + // with "~" or "~user" + wxChar ch = str[0u]; + + return IsPathSeparator(ch, format) || ch == _T('~'); + } } -bool wxFileName::IsAbsolute( wxPathFormat format ) +/* static */ +wxString wxFileName::GetVolumeSeparator(wxPathFormat format) { - wxChar ch = m_dirs.IsEmpty() ? _T('\0') : m_dirs[0u][0u]; + wxString sepVol; - // the path is absolute if it starts with a path separator or, only for - // Unix filenames, with "~" or "~user" - return IsPathSeparator(ch, format) || - (GetFormat(format) == wxPATH_UNIX && ch == _T('~') ); + if ( GetFormat(format) != wxPATH_UNIX ) + { + // so far it is the same for all systems which have it + sepVol = wxFILE_SEP_DSK; + } + //else: leave empty, no volume separators under Unix + + return sepVol; } /* static */ @@ -371,8 +606,9 @@ wxString wxFileName::GetPathSeparators(wxPathFormat format) switch ( GetFormat(format) ) { case wxPATH_DOS: - // accept both as native APIs do - seps << wxFILE_SEP_PATH_UNIX << wxFILE_SEP_PATH_DOS; + // accept both as native APIs do but put the native one first as + // this is the one we use in GetFullPath() + seps << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_UNIX; break; default: @@ -386,6 +622,10 @@ wxString wxFileName::GetPathSeparators(wxPathFormat format) case wxPATH_MAC: seps = wxFILE_SEP_PATH_MAC; break; + + case wxPATH_VMS: + seps = wxFILE_SEP_PATH_VMS; + break; } return seps; @@ -394,6 +634,10 @@ wxString wxFileName::GetPathSeparators(wxPathFormat format) /* static */ bool wxFileName::IsPathSeparator(wxChar ch, wxPathFormat format) { + // wxString::Find() doesn't work as expected with NUL - it will always find + // it, so it is almost surely a bug if this function is called with NUL arg + wxASSERT_MSG( ch != _T('\0'), _T("shouldn't be called with NUL") ); + return GetPathSeparators(format).Find(ch) != wxNOT_FOUND; } @@ -401,6 +645,7 @@ bool wxFileName::IsWild( wxPathFormat format ) { // FIXME: this is probably false for Mac and this is surely wrong for most // of Unix shells (think about "[...]") + (void)format; return m_name.find_first_of(_T("*?")) != wxString::npos; } @@ -466,7 +711,211 @@ wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const wxString wxFileName::GetFullPath( wxPathFormat format ) const { - return GetPathWithSep() + GetFullName(); + format = GetFormat(format); + + wxString fullpath; + + // first put the volume + if ( !m_volume.empty() ) + { + // special Windows UNC paths hack, part 2: undo what we did in + // SplitPath() and make an UNC path if we have a drive which is not a + // single letter (hopefully the network shares can't be one letter only + // although I didn't find any authoritative docs on this) + if ( format == wxPATH_DOS && m_volume.length() > 1 ) + { + fullpath << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << m_volume; + } + else // !UNC + { + fullpath << m_volume << GetVolumeSeparator(format); + } + } + + // then concatenate all the path components using the path separator + size_t dirCount = m_dirs.GetCount(); + if ( dirCount ) + { + // under Mac, we must have a path separator in the beginning of the + // relative path - otherwise it would be parsed as an absolute one + if ( format == wxPATH_MAC && m_volume.empty() && !m_dirs[0].empty() ) + { + fullpath += wxFILE_SEP_PATH_MAC; + } + + wxChar chPathSep = GetPathSeparators(format)[0u]; + if ( format == wxPATH_VMS ) + { + fullpath += _T('['); + } + + for ( size_t i = 0; i < dirCount; i++ ) + { + // under VMS, we shouldn't have a leading dot + if ( i && (format != wxPATH_VMS || !m_dirs[i - 1].empty()) ) + fullpath += chPathSep; + + fullpath += m_dirs[i]; + } + + if ( format == wxPATH_VMS ) + { + fullpath += _T(']'); + } + else // !VMS + { + // separate the file name from the last directory, notice that we + // intentionally do it even if the name and extension are empty as + // this allows us to distinguish the directories from the file + // names (the directories have the trailing slash) + fullpath += chPathSep; + } + } + + // finally add the file name and extension + fullpath += GetFullName(); + + return fullpath; +} + +// Return the short form of the path (returns identity on non-Windows platforms) +wxString wxFileName::GetShortPath() const +{ +#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) + wxString path(GetFullPath()); + wxString pathOut; + DWORD sz = ::GetShortPathName(path, NULL, 0); + bool ok = sz != 0; + if ( ok ) + { + ok = ::GetShortPathName + ( + path, + pathOut.GetWriteBuf(sz), + sz + ) != 0; + pathOut.UngetWriteBuf(); + } + if (ok) + return pathOut; + + return path; +#else + return GetFullPath(); +#endif +} + +// Return the long form of the path (returns identity on non-Windows platforms) +wxString wxFileName::GetLongPath() const +{ +#if defined(__WXMSW__) && defined(__WIN32__) && !defined(__WXMICROWIN__) + wxString path(GetFullPath()); + wxString pathOut; + bool success = FALSE; + + // VZ: this code was disabled, why? +#if 0 // wxUSE_DYNLIB_CLASS + typedef DWORD (*GET_LONG_PATH_NAME)(const wxChar *, wxChar *, DWORD); + + static bool s_triedToLoad = FALSE; + + if ( !s_triedToLoad ) + { + s_triedToLoad = TRUE; + wxDllType dllKernel = wxDllLoader::LoadLibrary(_T("kernel32")); + if ( dllKernel ) + { + // may succeed or fail depending on the Windows version + static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL; +#ifdef _UNICODE + s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW")); +#else + s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameA")); +#endif + + wxDllLoader::UnloadLibrary(dllKernel); + + if ( s_pfnGetLongPathName ) + { + DWORD dwSize = (*s_pfnGetLongPathName)(path, NULL, 0); + bool ok = dwSize > 0; + + if ( ok ) + { + DWORD sz = (*s_pfnGetLongPathName)(path, NULL, 0); + ok = sz != 0; + if ( ok ) + { + ok = (*s_pfnGetLongPathName) + ( + path, + pathOut.GetWriteBuf(sz), + sz + ) != 0; + pathOut.UngetWriteBuf(); + + success = TRUE; + } + } + } + } + } + if (success) + return pathOut; +#endif // wxUSE_DYNLIB_CLASS + + if (!success) + { + // The OS didn't support GetLongPathName, or some other error. + // We need to call FindFirstFile on each component in turn. + + WIN32_FIND_DATA findFileData; + HANDLE hFind; + pathOut = wxEmptyString; + + wxArrayString dirs = GetDirs(); + dirs.Add(GetFullName()); + + size_t count = dirs.GetCount(); + size_t i; + wxString tmpPath; + + for ( i = 0; i < count; 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.Last() == wxT(':')) + { + // 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); + if (hFind == INVALID_HANDLE_VALUE) + { + // Error: return immediately with the original path + return path; + } + else + { + pathOut += findFileData.cFileName; + if ( (i < (count-1)) ) + pathOut += wxFILE_SEP_PATH; + + ::FindClose(hFind); + } + } + } + + return pathOut; +#else + return GetFullPath(); +#endif } wxPathFormat wxFileName::GetFormat( wxPathFormat format ) @@ -475,8 +924,10 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format ) { #if defined(__WXMSW__) || defined(__WXPM__) format = wxPATH_DOS; -#elif defined(__WXMAC__) +#elif defined(__WXMAC__) && !defined(__DARWIN__) format = wxPATH_MAC; +#elif defined(__VMS) + format = wxPATH_VMS; #else format = wxPATH_UNIX; #endif @@ -488,7 +939,8 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format ) // path splitting function // ---------------------------------------------------------------------------- -void wxFileName::SplitPath(const wxString& fullpath, +void wxFileName::SplitPath(const wxString& fullpathWithVolume, + wxString *pstrVolume, wxString *pstrPath, wxString *pstrName, wxString *pstrExt, @@ -496,23 +948,71 @@ void wxFileName::SplitPath(const wxString& fullpath, { format = GetFormat(format); + wxString fullpath = fullpathWithVolume; + + // under VMS the end of the path is ']', not the path separator used to + // separate the components + wxString sepPath = format == wxPATH_VMS ? _T(']') + : GetPathSeparators(format); + + // special Windows UNC paths hack: transform \\share\path into share:path + if ( format == wxPATH_DOS ) + { + if ( fullpath.length() >= 4 && + fullpath[0u] == wxFILE_SEP_PATH_DOS && + fullpath[1u] == wxFILE_SEP_PATH_DOS ) + { + fullpath.erase(0, 2); + + size_t posFirstSlash = fullpath.find_first_of(sepPath); + if ( posFirstSlash != wxString::npos ) + { + fullpath[posFirstSlash] = wxFILE_SEP_DSK; + + // UNC paths are always absolute, right? (FIXME) + fullpath.insert(posFirstSlash + 1, wxFILE_SEP_PATH_DOS); + } + } + } + + // do we have the volume name in the beginning? + wxString sepVol = GetVolumeSeparator(format); + if ( !sepVol.empty() ) + { + size_t posFirstColon = fullpath.find_first_of(sepVol); + if ( posFirstColon != wxString::npos ) + { + if ( pstrVolume ) + { + *pstrVolume = fullpath.Left(posFirstColon); + } + + // remove the volume name and the separator from the full path + fullpath.erase(0, posFirstColon + sepVol.length()); + } + } + // find the positions of the last dot and last path separator in the path size_t posLastDot = fullpath.find_last_of(wxFILE_SEP_EXT); - size_t posLastSlash = fullpath.find_last_of(GetPathSeparators(format)); + size_t posLastSlash = fullpath.find_last_of(sepPath); - if ( (posLastDot != wxString::npos) && (format == wxPATH_UNIX) ) + if ( (posLastDot != wxString::npos) && + ((format == wxPATH_UNIX) || (format == wxPATH_VMS)) ) { if ( (posLastDot == 0) || - (fullpath[posLastDot - 1] == wxFILE_SEP_PATH_UNIX) ) + (fullpath[posLastDot - 1] == sepPath[0u] ) ) { - // under Unix, dot may be (and commonly is) the first character of - // the filename, don't treat the entire filename as extension in - // this case + // under Unix and VMS, dot may be (and commonly is) the first + // character of the filename, don't treat the entire filename as + // extension in this case posLastDot = wxString::npos; } } - if ( (posLastDot != wxString::npos) && (posLastDot < posLastSlash) ) + // if we do have a dot and a slash, check that the dot is in the name part + if ( (posLastDot != wxString::npos) && + (posLastSlash != wxString::npos) && + (posLastDot < posLastSlash) ) { // the dot is part of the path, not the start of the extension posLastDot = wxString::npos; @@ -528,16 +1028,43 @@ void wxFileName::SplitPath(const wxString& fullpath, } else { - // take all until the separator - *pstrPath = fullpath.Left(posLastSlash); + // take everything up to the path separator but take care to make + // tha path equal to something like '/', not empty, for the files + // immediately under root directory + size_t len = posLastSlash; + if ( !len ) + len++; + + *pstrPath = fullpath.Left(len); + + // special VMS hack: remove the initial bracket + if ( format == wxPATH_VMS ) + { + if ( (*pstrPath)[0u] == _T('[') ) + pstrPath->erase(0, 1); + } } } if ( pstrName ) { + // take all characters starting from the one after the last slash and + // up to, but excluding, the last dot size_t nStart = posLastSlash == wxString::npos ? 0 : posLastSlash + 1; - size_t count = posLastDot == wxString::npos ? wxString::npos - : posLastDot - posLastSlash; + size_t count; + if ( posLastDot == wxString::npos ) + { + // take all until the end + count = wxString::npos; + } + else if ( posLastSlash == wxString::npos ) + { + count = posLastDot; + } + else // have both dot and slash + { + count = posLastDot - posLastSlash - 1; + } *pstrName = fullpath.Mid(nStart, count); } @@ -556,3 +1083,137 @@ void wxFileName::SplitPath(const wxString& fullpath, } } } + +// ---------------------------------------------------------------------------- +// time functions +// ---------------------------------------------------------------------------- + +bool wxFileName::SetTimes(const wxDateTime *dtCreate, + const wxDateTime *dtAccess, + const wxDateTime *dtMod) +{ +#if defined(__UNIX_LIKE__) + if ( !dtAccess && !dtMod ) + { + // can't modify the creation time anyhow, don't try + return TRUE; + } + + // if dtAccess or dtMod is not specified, use the other one (which must be + // non NULL because of the test above) for both times + utimbuf utm; + utm.actime = dtAccess ? dtAccess->GetTicks() : dtMod->GetTicks(); + utm.modtime = dtMod ? dtMod->GetTicks() : dtAccess->GetTicks(); + if ( utime(GetFullPath(), &utm) == 0 ) + { + return TRUE; + } +#elif defined(__WIN32__) + wxFileHandle fh(GetFullPath()); + if ( fh.IsOk() ) + { + FILETIME ftAccess, ftCreate, ftWrite; + + if ( dtCreate ) + ConvertWxToFileTime(&ftCreate, *dtCreate); + if ( dtAccess ) + ConvertWxToFileTime(&ftAccess, *dtAccess); + if ( dtMod ) + ConvertWxToFileTime(&ftWrite, *dtMod); + + if ( ::SetFileTime(fh, + dtCreate ? &ftCreate : NULL, + dtAccess ? &ftAccess : NULL, + dtMod ? &ftWrite : NULL) ) + { + return TRUE; + } + } +#else // other platform +#endif // platforms + + wxLogSysError(_("Failed to modify file times for '%s'"), + GetFullPath().c_str()); + + return FALSE; +} + +bool wxFileName::Touch() +{ +#if defined(__UNIX_LIKE__) + // under Unix touching file is simple: just pass NULL to utime() + if ( utime(GetFullPath(), NULL) == 0 ) + { + return TRUE; + } + + wxLogSysError(_("Failed to touch the file '%s'"), GetFullPath().c_str()); + + return FALSE; +#else // other platform + wxDateTime dtNow = wxDateTime::Now(); + + return SetTimes(NULL /* don't change create time */, &dtNow, &dtNow); +#endif // platforms +} + +bool wxFileName::GetTimes(wxDateTime *dtAccess, + wxDateTime *dtMod, + wxDateTime *dtChange) const +{ +#if defined(__UNIX_LIKE__) + wxStructStat stBuf; + if ( wxStat(GetFullPath(), &stBuf) == 0 ) + { + if ( dtAccess ) + dtAccess->Set(stBuf.st_atime); + if ( dtMod ) + dtMod->Set(stBuf.st_mtime); + if ( dtChange ) + dtChange->Set(stBuf.st_ctime); + + return TRUE; + } +#elif defined(__WXMAC__) + wxStructStat stBuf; + if ( wxStat(GetFullPath(), &stBuf) == 0 ) + { + if ( dtAccess ) + dtAccess->Set(stBuf.st_atime); + if ( dtMod ) + dtMod->Set(stBuf.st_mtime); + if ( dtChange ) + dtChange->Set(stBuf.st_ctime); + + return TRUE; + } +#elif defined(__WIN32__) + wxFileHandle fh(GetFullPath()); + if ( fh.IsOk() ) + { + FILETIME ftAccess, ftCreate, ftWrite; + + if ( ::GetFileTime(fh, + dtMod ? &ftCreate : NULL, + dtAccess ? &ftAccess : NULL, + dtChange ? &ftWrite : NULL) ) + { + if ( dtMod ) + ConvertFileTimeToWx(dtMod, ftCreate); + if ( dtAccess ) + ConvertFileTimeToWx(dtAccess, ftAccess); + if ( dtChange ) + ConvertFileTimeToWx(dtChange, ftWrite); + + return TRUE; + } + } +#else // other platform +#endif // platforms + + wxLogSysError(_("Failed to retrieve file times for '%s'"), + GetFullPath().c_str()); + + return FALSE; +} +