diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-02-02 22:30:17 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-02-02 22:30:17 +0000 |
commit | 3c0d263abb7469f63c03df6ab21bfb5785457401 (patch) | |
tree | 1c0ad8f0f34b71ae8f8bd7595ba52db24f68d54f /clang/lib/Basic/FileManager.cpp | |
parent | e3773c2f511e1c2deb2f1128a14e98b386ca47c4 (diff) | |
download | bcm5719-llvm-3c0d263abb7469f63c03df6ab21bfb5785457401.tar.gz bcm5719-llvm-3c0d263abb7469f63c03df6ab21bfb5785457401.zip |
Canonicalize path names in the file manager before performing a lookup
on that name. Canonicalization eliminates silliness such as "." and
"foo/.." that breaks the uniquing of files in the presence of virtual
files or files whose inode numbers have changed during
parsing/re-parsing. c-index-test isn't able to create this crazy
situation, so I've resorted to testing outside of the Clang
tree. Fixes <rdar://problem/8928220>.
Note that this hackery will go away once we have a real virtual file
system on which we can layer FileManager; the virtual-files hack is
showing cracks.
llvm-svn: 124754
Diffstat (limited to 'clang/lib/Basic/FileManager.cpp')
-rw-r--r-- | clang/lib/Basic/FileManager.cpp | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index cbe90bfdc16..5bf1ae36892 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -271,10 +271,84 @@ const DirectoryEntry *FileManager::getDirectory(llvm::StringRef Filename) { return &UDE; } +/// \brief Canonicalize a file or path name by eliminating redundant +/// "foo/.." and "./" path components. +/// +/// Uses the given scratch space to store the resulting string, if needed. +static llvm::StringRef CanonicalizeFileName(llvm::StringRef Filename, + llvm::SmallVectorImpl<char> &Scratch) { + size_t Start = 0; + bool Changed = false; + do { + size_t FirstSlash = Filename.find('/', Start); + if (FirstSlash == llvm::StringRef::npos) { + // No more components. Just copy the rest of the file name, if + // we need to. + if (Changed) + Scratch.append(Filename.begin() + Start, Filename.end()); + break; + } + + if (Start + 1 == FirstSlash && Filename[Start] == '.') { + // We have './'; remove it. + + // If we haven't changed anything previously, copy the + // starting bits here. + if (!Changed) { + Scratch.clear(); + Scratch.append(Filename.begin(), Filename.begin() + Start); + Changed = true; + } + + // Skip over the './'. + Start = FirstSlash + 1; + continue; + } + + size_t SecondSlash = Filename.find('/', FirstSlash + 1); + if (SecondSlash != llvm::StringRef::npos && + SecondSlash - FirstSlash == 3 && + Filename[FirstSlash + 1] == '.' && + Filename[FirstSlash + 2] == '.') { + // We have 'foo/../'; remove it. + + // If we haven't changed anything previously, copy the + // starting bits here. + if (!Changed) { + Scratch.clear(); + Scratch.append(Filename.begin(), Filename.begin() + Start); + Changed = true; + } + + // Skip over the 'foo/..'. + Start = SecondSlash + 1; + continue; + } + + if (Changed) + Scratch.append(Filename.begin() + Start, + Filename.begin() + FirstSlash + 1); + Start = FirstSlash + 1; + } while (true); + + if (Changed) { +#if 0 + llvm::errs() << "Canonicalized \"" << Filename << "\" to \"" + << llvm::StringRef(Scratch.data(), Scratch.size()) << "\"\n"; +#endif + return llvm::StringRef(Scratch.data(), Scratch.size()); + } + + return Filename; +} + /// getFile - Lookup, cache, and verify the specified file. This returns null /// if the file doesn't exist. /// const FileEntry *FileManager::getFile(llvm::StringRef Filename) { + llvm::SmallString<128> FilenameScratch; + Filename = CanonicalizeFileName(Filename, FilenameScratch); + ++NumFileLookups; // See if there is already an entry in the map. @@ -343,6 +417,9 @@ const FileEntry *FileManager::getFile(llvm::StringRef Filename) { const FileEntry * FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size, time_t ModificationTime) { + llvm::SmallString<128> FilenameScratch; + Filename = CanonicalizeFileName(Filename, FilenameScratch); + ++NumFileLookups; // See if there is already an entry in the map. |