diff --git a/modules/audio_processing/test/conversational_speech/BUILD.gn b/modules/audio_processing/test/conversational_speech/BUILD.gn index 551781b8bc..721ebc7ee5 100644 --- a/modules/audio_processing/test/conversational_speech/BUILD.gn +++ b/modules/audio_processing/test/conversational_speech/BUILD.gn @@ -51,7 +51,6 @@ rtc_static_library("lib") { "../../../../common_audio", "../../../../rtc_base:checks", "../../../../rtc_base:rtc_base_approved", - "../../../../test:fileutils", "//third_party/abseil-cpp/absl/memory", ] visibility = [ ":*" ] # Only targets in this file can depend on this. diff --git a/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc b/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc index a45e3bbfa7..eb8e3bed3c 100644 --- a/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc +++ b/modules/audio_processing/test/conversational_speech/mock_wavreader_factory.cc @@ -12,6 +12,7 @@ #include "modules/audio_processing/test/conversational_speech/mock_wavreader.h" #include "rtc_base/logging.h" +#include "rtc_base/pathutils.h" #include "test/gmock.h" namespace webrtc { @@ -38,10 +39,9 @@ MockWavReaderFactory::~MockWavReaderFactory() = default; std::unique_ptr MockWavReaderFactory::CreateMock( const std::string& filepath) { // Search the parameters corresponding to filepath. - size_t delimiter = filepath.find_last_of("/\\"); // Either windows or posix - std::string filename = - filepath.substr(delimiter == std::string::npos ? 0 : delimiter + 1); - const auto it = audiotrack_names_params_.find(filename); + const rtc::Pathname audiotrack_file_path(filepath); + const auto it = + audiotrack_names_params_.find(audiotrack_file_path.filename()); // If not found, use default parameters. if (it == audiotrack_names_params_.end()) { diff --git a/modules/audio_processing/test/conversational_speech/multiend_call.cc b/modules/audio_processing/test/conversational_speech/multiend_call.cc index 4e2f54d150..d633d90e9c 100644 --- a/modules/audio_processing/test/conversational_speech/multiend_call.cc +++ b/modules/audio_processing/test/conversational_speech/multiend_call.cc @@ -14,7 +14,7 @@ #include #include "rtc_base/logging.h" -#include "test/testsupport/fileutils.h" +#include "rtc_base/pathutils.h" namespace webrtc { namespace test { @@ -50,13 +50,13 @@ bool MultiEndCall::CreateAudioTrackReaders() { if (it != audiotrack_readers_.end()) continue; - const std::string audiotrack_file_path = - test::JoinFilename(audiotracks_path_, turn.audiotrack_file_name); + // Instance Pathname to retrieve the full path to the audiotrack file. + const rtc::Pathname audiotrack_file_path(audiotracks_path_, + turn.audiotrack_file_name); // Map the audiotrack file name to a new instance of WavReaderInterface. std::unique_ptr wavreader = - wavreader_abstract_factory_->Create( - test::JoinFilename(audiotracks_path_, turn.audiotrack_file_name)); + wavreader_abstract_factory_->Create(audiotrack_file_path.pathname()); if (sample_rate_hz_ == 0) { sample_rate_hz_ = wavreader->SampleRate(); diff --git a/modules/audio_processing/test/conversational_speech/simulator.cc b/modules/audio_processing/test/conversational_speech/simulator.cc index c400499fd3..c2fb7808e0 100644 --- a/modules/audio_processing/test/conversational_speech/simulator.cc +++ b/modules/audio_processing/test/conversational_speech/simulator.cc @@ -25,7 +25,7 @@ #include "rtc_base/constructormagic.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" -#include "test/testsupport/fileutils.h" +#include "rtc_base/pathutils.h" namespace webrtc { namespace test { @@ -46,20 +46,21 @@ InitSpeakerOutputFilePaths(const std::set& speaker_names, // Add near-end and far-end output paths into the map. for (const auto& speaker_name : speaker_names) { - const std::string near_end_path = - test::JoinFilename(output_path, "s_" + speaker_name + "-near_end.wav"); + const rtc::Pathname near_end_path(output_path, + "s_" + speaker_name + "-near_end.wav"); RTC_LOG(LS_VERBOSE) << "The near-end audio track will be created in " - << near_end_path << "."; + << near_end_path.pathname() << "."; - const std::string far_end_path = - test::JoinFilename(output_path, "s_" + speaker_name + "-far_end.wav"); + const rtc::Pathname far_end_path(output_path, + "s_" + speaker_name + "-far_end.wav"); RTC_LOG(LS_VERBOSE) << "The far-end audio track will be created in " - << far_end_path << "."; + << far_end_path.pathname() << "."; // Add to map. speaker_output_file_paths_map->emplace( std::piecewise_construct, std::forward_as_tuple(speaker_name), - std::forward_as_tuple(near_end_path, far_end_path)); + std::forward_as_tuple(near_end_path.pathname(), + far_end_path.pathname())); } return speaker_output_file_paths_map; diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 1453bafd66..3f02fb7f0e 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -121,6 +121,8 @@ rtc_source_set("rtc_base_approved") { "numerics/sample_counter.cc", "numerics/sample_counter.h", "onetimeevent.h", + "pathutils.cc", + "pathutils.h", "platform_file.cc", "platform_file.h", "race_checker.cc", @@ -1103,6 +1105,7 @@ if (rtc_include_tests) { "numerics/safe_minmax_unittest.cc", "numerics/sample_counter_unittest.cc", "onetimeevent_unittest.cc", + "pathutils_unittest.cc", "platform_file_unittest.cc", "platform_thread_unittest.cc", "random_unittest.cc", diff --git a/rtc_base/filerotatingstream.cc b/rtc_base/filerotatingstream.cc index f0a17bf9fd..c28616d8f6 100644 --- a/rtc_base/filerotatingstream.cc +++ b/rtc_base/filerotatingstream.cc @@ -18,6 +18,7 @@ #include "rtc_base/checks.h" #include "rtc_base/fileutils.h" #include "rtc_base/logging.h" +#include "rtc_base/pathutils.h" // Note: We use fprintf for logging in the write paths of this stream to avoid // infinite loops when logging. @@ -55,6 +56,7 @@ FileRotatingStream::FileRotatingStream(const std::string& dir_path, rotation_index_(0), current_bytes_written_(0), disable_buffering_(false) { + RTC_DCHECK(Filesystem::IsFolder(dir_path)); switch (mode) { case kWrite: { file_names_.clear(); @@ -186,6 +188,7 @@ bool FileRotatingStream::GetSize(size_t* size) const { *size = 0; size_t total_size = 0; for (auto file_name : file_names_) { + Pathname pathname(file_name); size_t file_size = 0; if (Filesystem::GetFileSize(file_name, &file_size)) { total_size += file_size; @@ -304,14 +307,17 @@ std::vector FileRotatingStream::GetFilesWithPrefix() const { std::vector files; // Iterate over the files in the directory. DirectoryIterator it; - if (!it.Iterate(dir_path_)) { + Pathname dir_path; + dir_path.SetFolder(dir_path_); + if (!it.Iterate(dir_path)) { return files; } do { std::string current_name = it.Name(); if (current_name.size() && !it.IsDirectory() && current_name.compare(0, file_prefix_.size(), file_prefix_) == 0) { - files.push_back(it.PathName()); + Pathname path(dir_path_, current_name); + files.push_back(path.pathname()); } } while (it.Next()); return files; @@ -328,7 +334,8 @@ std::string FileRotatingStream::GetFilePath(size_t index, RTC_DCHECK_LT(1 + max_digits, buffer_size); std::snprintf(file_postfix, buffer_size, "_%0*zu", max_digits, index); - return dir_path_ + file_prefix_ + file_postfix; + Pathname file_path(dir_path_, file_prefix_ + file_postfix); + return file_path.pathname(); } CallSessionFileRotatingStream::CallSessionFileRotatingStream( diff --git a/rtc_base/filerotatingstream_unittest.cc b/rtc_base/filerotatingstream_unittest.cc index 84bf45c341..19055162fd 100644 --- a/rtc_base/filerotatingstream_unittest.cc +++ b/rtc_base/filerotatingstream_unittest.cc @@ -15,6 +15,7 @@ #include "rtc_base/filerotatingstream.h" #include "rtc_base/fileutils.h" #include "rtc_base/gunit.h" +#include "rtc_base/pathutils.h" #include "test/testsupport/fileutils.h" namespace rtc { @@ -50,7 +51,7 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test { // Append per-test output path in order to run within gtest parallel. dir_path_.append(dir_name); - dir_path_.append(webrtc::test::kPathDelimiter); + dir_path_.push_back(Pathname::DefaultFolderDelimiter()); ASSERT_TRUE(webrtc::test::CreateDir(dir_path_)); stream_.reset(new FileRotatingStream(dir_path_, file_prefix, max_file_size, num_log_files)); @@ -217,7 +218,7 @@ class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test { // Append per-test output path in order to run within gtest parallel. dir_path_.append(dir_name); - dir_path_.append(webrtc::test::kPathDelimiter); + dir_path_.push_back(Pathname::DefaultFolderDelimiter()); ASSERT_TRUE(webrtc::test::CreateDir(dir_path_)); stream_.reset( new CallSessionFileRotatingStream(dir_path_, max_total_log_size)); diff --git a/rtc_base/fileutils.cc b/rtc_base/fileutils.cc index 1086f3c7f7..0adbbac4b0 100644 --- a/rtc_base/fileutils.cc +++ b/rtc_base/fileutils.cc @@ -11,6 +11,7 @@ #include "rtc_base/fileutils.h" #include "rtc_base/checks.h" +#include "rtc_base/pathutils.h" #if defined(WEBRTC_WIN) #include "rtc_base/stringutils.h" // for ToUtf16 @@ -57,12 +58,12 @@ DirectoryIterator::~DirectoryIterator() { // Starts traversing a directory. // dir is the directory to traverse // returns true if the directory exists and is valid -bool DirectoryIterator::Iterate(const std::string& dir) { - directory_ = dir; +bool DirectoryIterator::Iterate(const Pathname& dir) { + directory_ = dir.pathname(); #if defined(WEBRTC_WIN) if (handle_ != INVALID_HANDLE_VALUE) ::FindClose(handle_); - std::string d = dir + '*'; + std::string d = dir.pathname() + '*'; handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_); if (handle_ == INVALID_HANDLE_VALUE) return false; @@ -76,7 +77,7 @@ bool DirectoryIterator::Iterate(const std::string& dir) { if (dirent_ == nullptr) return false; - if (::stat(PathName().c_str(), &stat_) != 0) + if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0) return false; #endif return true; @@ -92,7 +93,7 @@ bool DirectoryIterator::Next() { if (dirent_ == nullptr) return false; - return ::stat(PathName().c_str(), &stat_) == 0; + return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0; #endif } @@ -111,17 +112,7 @@ std::string DirectoryIterator::Name() const { return ToUtf8(data_.cFileName); #else RTC_DCHECK(dirent_); - return std::string(dirent_->d_name); -#endif -} - -// returns the name of the file currently pointed to -std::string DirectoryIterator::PathName() const { -#if defined(WEBRTC_WIN) - return directory_ + "\\" + ToUtf8(data_.cFileName); -#else - RTC_DCHECK(dirent_); - return directory_ + "/" + dirent_->d_name; + return dirent_->d_name; #endif } diff --git a/rtc_base/fileutils.h b/rtc_base/fileutils.h index d85b6b1787..deaf2e387a 100644 --- a/rtc_base/fileutils.h +++ b/rtc_base/fileutils.h @@ -25,6 +25,8 @@ namespace rtc { +class Pathname; + ////////////////////////// // Directory Iterator // ////////////////////////// @@ -42,25 +44,22 @@ class DirectoryIterator { // Destructor virtual ~DirectoryIterator(); - // Starts traversing a directory. - // |dir| is the directory to traverse. - // returns true if the directory exists and is valid. - // The iterator will point to the first entry in the directory. - virtual bool Iterate(const std::string& dir); + // Starts traversing a directory + // dir is the directory to traverse + // returns true if the directory exists and is valid + // The iterator will point to the first entry in the directory + virtual bool Iterate(const Pathname& path); // Advances to the next file // returns true if there were more files in the directory. virtual bool Next(); - // Returns true if the file currently pointed to is a directory. + // returns true if the file currently pointed to is a directory virtual bool IsDirectory() const; - // Returns the name of the file currently pointed to. + // returns the name of the file currently pointed to virtual std::string Name() const; - // Returns complete name of the file, including directory part. - virtual std::string PathName() const; - private: std::string directory_; #if defined(WEBRTC_WIN) @@ -80,37 +79,42 @@ class FilesystemInterface { // This will attempt to delete the path located at filename. // It DCHECKs and returns false if the path points to a folder or a // non-existent file. - virtual bool DeleteFile(const std::string& filename) = 0; + virtual bool DeleteFile(const Pathname& filename) = 0; // This moves a file from old_path to new_path, where "old_path" is a // plain file. This DCHECKs and returns false if old_path points to a // directory, and returns true if the function succeeds. - virtual bool MoveFile(const std::string& old_path, - const std::string& new_path) = 0; + virtual bool MoveFile(const Pathname& old_path, const Pathname& new_path) = 0; + + // Returns true if pathname refers to a directory + virtual bool IsFolder(const Pathname& pathname) = 0; // Returns true if pathname refers to a file - virtual bool IsFile(const std::string& pathname) = 0; + virtual bool IsFile(const Pathname& pathname) = 0; // Determines the size of the file indicated by path. - virtual bool GetFileSize(const std::string& path, size_t* size) = 0; + virtual bool GetFileSize(const Pathname& path, size_t* size) = 0; }; class Filesystem { public: - static bool DeleteFile(const std::string& filename) { + static bool DeleteFile(const Pathname& filename) { return GetFilesystem()->DeleteFile(filename); } - static bool MoveFile(const std::string& old_path, - const std::string& new_path) { + static bool MoveFile(const Pathname& old_path, const Pathname& new_path) { return GetFilesystem()->MoveFile(old_path, new_path); } - static bool IsFile(const std::string& pathname) { + static bool IsFolder(const Pathname& pathname) { + return GetFilesystem()->IsFolder(pathname); + } + + static bool IsFile(const Pathname& pathname) { return GetFilesystem()->IsFile(pathname); } - static bool GetFileSize(const std::string& path, size_t* size) { + static bool GetFileSize(const Pathname& path, size_t* size) { return GetFilesystem()->GetFileSize(path, size); } diff --git a/rtc_base/pathutils.cc b/rtc_base/pathutils.cc new file mode 100644 index 0000000000..07646716c2 --- /dev/null +++ b/rtc_base/pathutils.cc @@ -0,0 +1,154 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if defined(WEBRTC_WIN) +#include +#include +#include +#include +#endif // WEBRTC_WIN + +#include // for strchr + +#include "rtc_base/pathutils.h" + +namespace rtc { + +static const char EMPTY_STR[] = ""; + +// EXT_DELIM separates a file basename from extension +const char EXT_DELIM = '.'; + +// FOLDER_DELIMS separate folder segments and the filename +const char* const FOLDER_DELIMS = "/\\"; + +// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform +#ifdef WEBRTC_WIN +const char DEFAULT_FOLDER_DELIM = '\\'; +#else // !WEBRTC_WIN +const char DEFAULT_FOLDER_DELIM = '/'; +#endif // !WEBRTC_WIN + +/////////////////////////////////////////////////////////////////////////////// +// Pathname - parsing of pathnames into components, and vice versa +/////////////////////////////////////////////////////////////////////////////// + +bool Pathname::IsFolderDelimiter(char ch) { + return (nullptr != ::strchr(FOLDER_DELIMS, ch)); +} + +char Pathname::DefaultFolderDelimiter() { + return DEFAULT_FOLDER_DELIM; +} + +Pathname::Pathname() + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { +} + +Pathname::Pathname(const Pathname&) = default; +Pathname::Pathname(Pathname&&) = default; + +Pathname::Pathname(const std::string& pathname) + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { + SetPathname(pathname); +} + +Pathname::Pathname(const std::string& folder, const std::string& filename) + : folder_delimiter_(DEFAULT_FOLDER_DELIM) { + SetPathname(folder, filename); +} + +Pathname& Pathname::operator=(const Pathname&) = default; +Pathname& Pathname::operator=(Pathname&&) = default; + +std::string Pathname::pathname() const { + std::string pathname(folder_); + pathname.append(basename_); + pathname.append(extension_); + if (pathname.empty()) { + // Instead of the empty pathname, return the current working directory. + pathname.push_back('.'); + pathname.push_back(folder_delimiter_); + } + return pathname; +} + +void Pathname::SetPathname(const std::string& pathname) { + std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS); + if (pos != std::string::npos) { + SetFolder(pathname.substr(0, pos + 1)); + SetFilename(pathname.substr(pos + 1)); + } else { + SetFolder(EMPTY_STR); + SetFilename(pathname); + } +} + +void Pathname::SetPathname(const std::string& folder, + const std::string& filename) { + SetFolder(folder); + SetFilename(filename); +} + +void Pathname::SetFolder(const std::string& folder) { + folder_.assign(folder); + // Ensure folder ends in a path delimiter + if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { + folder_.push_back(folder_delimiter_); + } +} + +void Pathname::AppendFolder(const std::string& folder) { + folder_.append(folder); + // Ensure folder ends in a path delimiter + if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { + folder_.push_back(folder_delimiter_); + } +} + +bool Pathname::SetBasename(const std::string& basename) { + if(basename.find_first_of(FOLDER_DELIMS) != std::string::npos) { + return false; + } + basename_.assign(basename); + return true; +} + +bool Pathname::SetExtension(const std::string& extension) { + if (extension.find_first_of(FOLDER_DELIMS) != std::string::npos || + extension.find_first_of(EXT_DELIM, 1) != std::string::npos) { + return false; + } + extension_.assign(extension); + // Ensure extension begins with the extension delimiter + if (!extension_.empty() && (extension_[0] != EXT_DELIM)) { + extension_.insert(extension_.begin(), EXT_DELIM); + } + return true; +} + +std::string Pathname::filename() const { + std::string filename(basename_); + filename.append(extension_); + return filename; +} + +bool Pathname::SetFilename(const std::string& filename) { + std::string::size_type pos = filename.rfind(EXT_DELIM); + if ((pos == std::string::npos) || (pos == 0)) { + return SetExtension(EMPTY_STR) && SetBasename(filename); + } else { + return SetExtension(filename.substr(pos)) && SetBasename(filename.substr(0, pos)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace rtc diff --git a/rtc_base/pathutils.h b/rtc_base/pathutils.h new file mode 100644 index 0000000000..59f2a4ac29 --- /dev/null +++ b/rtc_base/pathutils.h @@ -0,0 +1,79 @@ +/* + * Copyright 2004 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTC_BASE_PATHUTILS_H_ +#define RTC_BASE_PATHUTILS_H_ + +#include + +namespace rtc { + +/////////////////////////////////////////////////////////////////////////////// +// Pathname - parsing of pathnames into components, and vice versa. +// +// To establish consistent terminology, a filename never contains a folder +// component. A folder never contains a filename. A pathname may include +// a folder and/or filename component. Here are some examples: +// +// pathname() /home/john/example.txt +// folder() /home/john/ +// filename() example.txt +// parent_folder() /home/ +// folder_name() john/ +// basename() example +// extension() .txt +// +// Basename may begin, end, and/or include periods, but no folder delimiters. +// If extension exists, it consists of a period followed by zero or more +// non-period/non-delimiter characters, and basename is non-empty. +/////////////////////////////////////////////////////////////////////////////// + +class Pathname { + public: + // Folder delimiters are slash and backslash + static bool IsFolderDelimiter(char ch); + static char DefaultFolderDelimiter(); + + Pathname(); + Pathname(const Pathname&); + Pathname(Pathname&&); + Pathname(const std::string& pathname); + Pathname(const std::string& folder, const std::string& filename); + + Pathname& operator=(const Pathname&); + Pathname& operator=(Pathname&&); + + // Returns the folder and filename components. If the pathname is empty, + // returns a string representing the current directory (as a relative path, + // i.e., "."). + std::string pathname() const; + void SetPathname(const std::string& pathname); + void SetPathname(const std::string& folder, const std::string& filename); + + // SetFolder and AppendFolder will append a folder delimiter, if needed. + void SetFolder(const std::string& folder); + void AppendFolder(const std::string& folder); + + bool SetBasename(const std::string& basename); + + // SetExtension will prefix a period, if needed. + bool SetExtension(const std::string& extension); + + std::string filename() const; + bool SetFilename(const std::string& filename); + + private: + std::string folder_, basename_, extension_; + char folder_delimiter_; +}; + +} // namespace rtc + +#endif // RTC_BASE_PATHUTILS_H_ diff --git a/rtc_base/pathutils_unittest.cc b/rtc_base/pathutils_unittest.cc new file mode 100644 index 0000000000..fae4f0aba5 --- /dev/null +++ b/rtc_base/pathutils_unittest.cc @@ -0,0 +1,37 @@ +/* + * Copyright 2007 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "rtc_base/pathutils.h" +#include "rtc_base/gunit.h" + +TEST(Pathname, ReturnsDotForEmptyPathname) { + const std::string kCWD = + std::string(".") + rtc::Pathname::DefaultFolderDelimiter(); + + rtc::Pathname path("/", ""); + EXPECT_TRUE (path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(std::string("/"), path.pathname()); + + path.SetPathname("", "foo"); + EXPECT_FALSE(path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(std::string("foo"), path.pathname()); + + path.SetPathname("", ""); + EXPECT_TRUE (path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(kCWD, path.pathname()); + + path.SetPathname(kCWD, ""); + EXPECT_TRUE (path.filename().empty()); + EXPECT_FALSE(path.pathname().empty()); + EXPECT_EQ(kCWD, path.pathname()); +} diff --git a/rtc_base/unixfilesystem.cc b/rtc_base/unixfilesystem.cc index ee9e3f069a..2a941e2b74 100644 --- a/rtc_base/unixfilesystem.cc +++ b/rtc_base/unixfilesystem.cc @@ -38,6 +38,7 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/pathutils.h" namespace rtc { @@ -45,39 +46,47 @@ UnixFilesystem::UnixFilesystem() {} UnixFilesystem::~UnixFilesystem() {} -bool UnixFilesystem::DeleteFile(const std::string& filename) { - RTC_LOG(LS_INFO) << "Deleting file:" << filename; +bool UnixFilesystem::DeleteFile(const Pathname& filename) { + RTC_LOG(LS_INFO) << "Deleting file:" << filename.pathname(); if (!IsFile(filename)) { RTC_DCHECK(IsFile(filename)); return false; } - return ::unlink(filename.c_str()) == 0; + return ::unlink(filename.pathname().c_str()) == 0; } -bool UnixFilesystem::MoveFile(const std::string& old_path, - const std::string& new_path) { +bool UnixFilesystem::MoveFile(const Pathname& old_path, + const Pathname& new_path) { if (!IsFile(old_path)) { RTC_DCHECK(IsFile(old_path)); return false; } - RTC_LOG(LS_VERBOSE) << "Moving " << old_path << " to " << new_path; - if (rename(old_path.c_str(), new_path.c_str()) != 0) { + RTC_LOG(LS_VERBOSE) << "Moving " << old_path.pathname() << " to " + << new_path.pathname(); + if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) { return false; } return true; } -bool UnixFilesystem::IsFile(const std::string& pathname) { +bool UnixFilesystem::IsFolder(const Pathname& path) { struct stat st; - int res = ::stat(pathname.c_str(), &st); + if (stat(path.pathname().c_str(), &st) < 0) + return false; + return S_ISDIR(st.st_mode); +} + +bool UnixFilesystem::IsFile(const Pathname& pathname) { + struct stat st; + int res = ::stat(pathname.pathname().c_str(), &st); // Treat symlinks, named pipes, etc. all as files. return res == 0 && !S_ISDIR(st.st_mode); } -bool UnixFilesystem::GetFileSize(const std::string& pathname, size_t* size) { +bool UnixFilesystem::GetFileSize(const Pathname& pathname, size_t* size) { struct stat st; - if (::stat(pathname.c_str(), &st) != 0) + if (::stat(pathname.pathname().c_str(), &st) != 0) return false; *size = st.st_size; return true; diff --git a/rtc_base/unixfilesystem.h b/rtc_base/unixfilesystem.h index 32af9b510a..d95132c0a5 100644 --- a/rtc_base/unixfilesystem.h +++ b/rtc_base/unixfilesystem.h @@ -14,6 +14,7 @@ #include #include "rtc_base/fileutils.h" +#include "rtc_base/pathutils.h" namespace rtc { @@ -24,18 +25,20 @@ class UnixFilesystem : public FilesystemInterface { // This will attempt to delete the file located at filename. // It will fail with VERIY if you pass it a non-existant file, or a directory. - bool DeleteFile(const std::string& filename) override; + bool DeleteFile(const Pathname& filename) override; // This moves a file from old_path to new_path, where "file" can be a plain // file or directory, which will be moved recursively. // Returns true if function succeeds. - bool MoveFile(const std::string& old_path, - const std::string& new_path) override; + bool MoveFile(const Pathname& old_path, const Pathname& new_path) override; + + // Returns true if a pathname is a directory + bool IsFolder(const Pathname& pathname) override; // Returns true of pathname represents an existing file - bool IsFile(const std::string& pathname) override; + bool IsFile(const Pathname& pathname) override; - bool GetFileSize(const std::string& path, size_t* size) override; + bool GetFileSize(const Pathname& path, size_t* size) override; }; } // namespace rtc diff --git a/rtc_base/win32filesystem.cc b/rtc_base/win32filesystem.cc index 5465a5cbb7..cd439660db 100644 --- a/rtc_base/win32filesystem.cc +++ b/rtc_base/win32filesystem.cc @@ -21,6 +21,7 @@ #include "rtc_base/checks.h" #include "rtc_base/fileutils.h" #include "rtc_base/logging.h" +#include "rtc_base/pathutils.h" #include "rtc_base/stream.h" #include "rtc_base/stringutils.h" @@ -33,37 +34,48 @@ namespace rtc { -bool Win32Filesystem::DeleteFile(const std::string& filename) { - RTC_LOG(LS_INFO) << "Deleting file " << filename; +bool Win32Filesystem::DeleteFile(const Pathname& filename) { + RTC_LOG(LS_INFO) << "Deleting file " << filename.pathname(); if (!IsFile(filename)) { RTC_DCHECK(IsFile(filename)); return false; } - return ::DeleteFile(ToUtf16(filename).c_str()) != 0; + return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0; } -bool Win32Filesystem::MoveFile(const std::string& old_path, - const std::string& new_path) { +bool Win32Filesystem::MoveFile(const Pathname& old_path, + const Pathname& new_path) { if (!IsFile(old_path)) { RTC_DCHECK(IsFile(old_path)); return false; } - RTC_LOG(LS_INFO) << "Moving " << old_path << " to " << new_path; - return ::MoveFile(ToUtf16(old_path).c_str(), ToUtf16(new_path).c_str()) != 0; + RTC_LOG(LS_INFO) << "Moving " << old_path.pathname() << " to " + << new_path.pathname(); + return ::MoveFile(ToUtf16(old_path.pathname()).c_str(), + ToUtf16(new_path.pathname()).c_str()) != 0; } -bool Win32Filesystem::IsFile(const std::string& path) { +bool Win32Filesystem::IsFolder(const Pathname& path) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; - if (0 == ::GetFileAttributesEx(ToUtf16(path).c_str(), GetFileExInfoStandard, - &data)) + if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(), + GetFileExInfoStandard, &data)) + return false; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == + FILE_ATTRIBUTE_DIRECTORY; +} + +bool Win32Filesystem::IsFile(const Pathname& path) { + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(), + GetFileExInfoStandard, &data)) return false; return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; } -bool Win32Filesystem::GetFileSize(const std::string& pathname, size_t* size) { +bool Win32Filesystem::GetFileSize(const Pathname& pathname, size_t* size) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; - if (::GetFileAttributesEx(ToUtf16(pathname).c_str(), GetFileExInfoStandard, - &data) == 0) + if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(), + GetFileExInfoStandard, &data) == 0) return false; *size = data.nFileSizeLow; return true; diff --git a/rtc_base/win32filesystem.h b/rtc_base/win32filesystem.h index 2c27f90575..d26741e67b 100644 --- a/rtc_base/win32filesystem.h +++ b/rtc_base/win32filesystem.h @@ -19,19 +19,21 @@ class Win32Filesystem : public FilesystemInterface { public: // This will attempt to delete the path located at filename. // If the path points to a folder, it will fail with VERIFY - bool DeleteFile(const std::string& filename) override; + bool DeleteFile(const Pathname& filename) override; // This moves a file from old_path to new_path. If the new path is on a // different volume than the old, it will attempt to copy and then delete // the folder // Returns true if the file is successfully moved - bool MoveFile(const std::string& old_path, - const std::string& new_path) override; + bool MoveFile(const Pathname& old_path, const Pathname& new_path) override; + + // Returns true if a pathname is a directory + bool IsFolder(const Pathname& pathname) override; // Returns true if a file exists at path - bool IsFile(const std::string& path) override; + bool IsFile(const Pathname& path) override; - bool GetFileSize(const std::string& path, size_t* size) override; + bool GetFileSize(const Pathname& path, size_t* size) override; }; } // namespace rtc diff --git a/test/testsupport/fileutils.cc b/test/testsupport/fileutils.cc index a71d8f8db5..0eac8d5e0f 100644 --- a/test/testsupport/fileutils.cc +++ b/test/testsupport/fileutils.cc @@ -64,12 +64,16 @@ namespace webrtc { namespace test { +namespace { + #if defined(WEBRTC_WIN) const char* kPathDelimiter = "\\"; #else const char* kPathDelimiter = "/"; #endif +} // namespace + std::string DirName(const std::string& path) { if (path.empty()) return ""; diff --git a/test/testsupport/fileutils.h b/test/testsupport/fileutils.h index 8a186988d6..693944e2ae 100644 --- a/test/testsupport/fileutils.h +++ b/test/testsupport/fileutils.h @@ -25,9 +25,6 @@ namespace test { // to find the project root. extern const char* kCannotFindProjectRootDir; -// Slash or backslash, depending on platform. NUL-terminated string. -extern const char* kPathDelimiter; - // Returns the absolute path to the output directory where log files and other // test artifacts should be put. The output directory is generally a directory // named "out" at the project root. This root is assumed to be two levels above diff --git a/test/testsupport/fileutils_unittest.cc b/test/testsupport/fileutils_unittest.cc index d39f468004..c6dc86deb2 100644 --- a/test/testsupport/fileutils_unittest.cc +++ b/test/testsupport/fileutils_unittest.cc @@ -24,6 +24,9 @@ #ifdef WIN32 #define chdir _chdir +static const char* kPathDelimiter = "\\"; +#else +static const char* kPathDelimiter = "/"; #endif using ::testing::EndsWith; diff --git a/test/testsupport/test_artifacts_unittest.cc b/test/testsupport/test_artifacts_unittest.cc index df02d27c67..267ea93676 100644 --- a/test/testsupport/test_artifacts_unittest.cc +++ b/test/testsupport/test_artifacts_unittest.cc @@ -16,6 +16,7 @@ #include "rtc_base/file.h" #include "rtc_base/flags.h" +#include "rtc_base/pathutils.h" #include "rtc_base/platform_file.h" #include "test/gtest.h" #include "test/testsupport/fileutils.h" diff --git a/video/video_analyzer.cc b/video/video_analyzer.cc index e4a3d56ea1..6d16b1a055 100644 --- a/video/video_analyzer.cc +++ b/video/video_analyzer.cc @@ -18,9 +18,9 @@ #include "rtc_base/flags.h" #include "rtc_base/format_macros.h" #include "rtc_base/memory_usage.h" +#include "rtc_base/pathutils.h" #include "system_wrappers/include/cpu_info.h" #include "test/call_test.h" -#include "test/testsupport/fileutils.h" #include "test/testsupport/frame_writer.h" #include "test/testsupport/perf_test.h" #include "test/testsupport/test_artifacts.h" @@ -607,7 +607,7 @@ void VideoAnalyzer::PrintResults() { std::string output_dir; test::GetTestArtifactsDir(&output_dir); std::string output_path = - test::JoinFilename(output_dir, test_label_ + ".jpg"); + rtc::Pathname(output_dir, test_label_ + ".jpg").pathname(); RTC_LOG(LS_INFO) << "Saving worst frame to " << output_path; test::JpegFrameWriter frame_writer(output_path); RTC_CHECK(