X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/246c704fca3a07ee5ba89da3a4cc87370f6c8cc9..d323d966dd6f73d11cad1094c086ad31f648b9a3:/src/common/filename.cpp diff --git a/src/common/filename.cpp b/src/common/filename.cpp index d3f34e5aa2..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 // ============================================================================ @@ -53,6 +91,14 @@ #ifdef __UNIX_LIKE__ #include #include + #include + #include +#endif + +#ifdef __MWERKS__ + #include + #include + #include #endif // ---------------------------------------------------------------------------- @@ -61,7 +107,7 @@ // 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 -#ifdef __WIN32__ +#if defined(__WIN32__) && !defined(__WXMICROWIN__) class wxFileHandle { @@ -113,19 +159,37 @@ private: // private functions // ---------------------------------------------------------------------------- -#ifdef __WIN32__ +#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. -static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt) +// 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) { - // TODO + 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 ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft) +static void ConvertWxToFileTime(FILETIME *ft, const wxDateTime& dt) { - // TODO + // 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__ @@ -140,32 +204,33 @@ static void ConvertFileTimeToWx(wxDateTime *dt, const FILETIME &ft) 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); - int i = 0; + wxStringTokenizer tn(path, GetPathSeparators(format)); + m_dirs.Clear(); while ( tn.HasMoreTokens() ) { wxString token = tn.GetNextToken(); - // If the path starts with a slash (or two for a network path), - // we need the first dir entry to be an empty for later reassembly. - if ((i < 2) || !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 ); - - i ++; } + m_volume = volume; m_ext = ext; m_name = name; } @@ -173,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, @@ -192,6 +257,8 @@ void wxFileName::Assign(const wxString& path, void wxFileName::Clear() { m_dirs.Clear(); + + m_volume = m_name = m_ext = wxEmptyString; } @@ -477,29 +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; - // Hack to cope with e.g. c:\thing - need something better - wxChar driveSep = _T('\0'); - if (!m_dirs.IsEmpty() && m_dirs[0].Length() > 1) - driveSep = m_dirs[0u][1u]; + 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 - // the path is absolute if it starts with a path separator or, only for - // Unix filenames, with "~" or "~user" - return IsPathSeparator(ch, format) || - driveSep == _T(':') || - (GetFormat(format) == wxPATH_UNIX && ch == _T('~') ); + return sepVol; } /* static */ @@ -509,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: @@ -524,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; @@ -532,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; } @@ -539,7 +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; + (void)format; return m_name.find_first_of(_T("*?")) != wxString::npos; } @@ -605,44 +711,71 @@ wxString wxFileName::GetPath( bool add_separator, wxPathFormat format ) const wxString wxFileName::GetFullPath( wxPathFormat format ) const { - format = GetFormat( format ); - - wxString ret; - if (format == wxPATH_DOS) + format = GetFormat(format); + + wxString fullpath; + + // first put the volume + if ( !m_volume.empty() ) { - for (size_t i = 0; i < m_dirs.GetCount(); i++) + // 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 ) { - ret += m_dirs[i]; - ret += '\\'; + fullpath << wxFILE_SEP_PATH_DOS << wxFILE_SEP_PATH_DOS << m_volume; } - } - else - if (format == wxPATH_UNIX) - { - for (size_t i = 0; i < m_dirs.GetCount(); i++) + else // !UNC { - ret += m_dirs[i]; - ret += '/'; + fullpath << m_volume << GetVolumeSeparator(format); } } - else + + // then concatenate all the path components using the path separator + size_t dirCount = m_dirs.GetCount(); + if ( dirCount ) { - for (size_t i = 0; i < m_dirs.GetCount(); i++) + // 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() ) { - ret += m_dirs[i]; - ret += ':'; + 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; } } - - ret += m_name; - - if (!m_ext.IsEmpty()) - { - ret += '.'; - ret += m_ext; - } - - return ret; + + // finally add the file name and extension + fullpath += GetFullName(); + + return fullpath; } // Return the short form of the path (returns identity on non-Windows platforms) @@ -693,7 +826,7 @@ wxString wxFileName::GetLongPath() const if ( dllKernel ) { // may succeed or fail depending on the Windows version - static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL; + static GET_LONG_PATH_NAME s_pfnGetLongPathName = NULL; #ifdef _UNICODE s_pfnGetLongPathName = (GET_LONG_PATH_NAME) wxDllLoader::GetSymbol(dllKernel, _T("GetLongPathNameW")); #else @@ -791,8 +924,10 @@ wxPathFormat wxFileName::GetFormat( wxPathFormat format ) { #if defined(__WXMSW__) || defined(__WXPM__) format = wxPATH_DOS; -#elif defined(__WXMAC__) - format = wxPATH_MAC; +#elif defined(__WXMAC__) && !defined(__DARWIN__) + format = wxPATH_MAC; +#elif defined(__VMS) + format = wxPATH_VMS; #else format = wxPATH_UNIX; #endif @@ -804,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, @@ -812,18 +948,63 @@ 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; } } @@ -847,8 +1028,21 @@ 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); + } } } @@ -980,6 +1174,19 @@ bool wxFileName::GetTimes(wxDateTime *dtAccess, 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() )