mirror of https://github.com/microsoft/vcpkg.git
[vcpkg] Initial addition of Filesystem abstraction to enable testing.
This commit is contained in:
parent
19123677d1
commit
c434dc9986
|
|
@ -11,16 +11,16 @@ namespace vcpkg::Paragraphs
|
|||
{
|
||||
using ParagraphDataMap = std::unordered_map<std::string, std::string>;
|
||||
|
||||
Expected<ParagraphDataMap> get_single_paragraph(const fs::path& control_path);
|
||||
Expected<std::vector<ParagraphDataMap>> get_paragraphs(const fs::path& control_path);
|
||||
Expected<ParagraphDataMap> get_single_paragraph(Files::Filesystem& fs, const fs::path& control_path);
|
||||
Expected<std::vector<ParagraphDataMap>> get_paragraphs(Files::Filesystem& fs, const fs::path& control_path);
|
||||
Expected<ParagraphDataMap> parse_single_paragraph(const std::string& str);
|
||||
Expected<std::vector<ParagraphDataMap>> parse_paragraphs(const std::string& str);
|
||||
|
||||
Expected<SourceParagraph> try_load_port(const fs::path& control_path);
|
||||
Expected<SourceParagraph> try_load_port(Files::Filesystem& fs, const fs::path& control_path);
|
||||
|
||||
Expected<BinaryParagraph> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec);
|
||||
|
||||
std::vector<SourceParagraph> load_all_ports(const fs::path& ports_dir);
|
||||
std::vector<SourceParagraph> load_all_ports(Files::Filesystem& fs, const fs::path& ports_dir);
|
||||
|
||||
std::map<std::string, VersionT> extract_port_names_and_versions(const std::vector<SourceParagraph>& source_paragraphs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "filesystem_fs.h"
|
||||
#include "vcpkg_Files.h"
|
||||
#include "PostBuildLint_BuildPolicies.h"
|
||||
#include "OptBool.h"
|
||||
#include "PostBuildLint_LinkageType.h"
|
||||
|
|
@ -17,5 +18,5 @@ namespace vcpkg::PostBuildLint
|
|||
std::map<BuildPolicies::Type, OptBoolT> policies;
|
||||
};
|
||||
|
||||
BuildInfo read_build_info(const fs::path& filepath);
|
||||
BuildInfo read_build_info(Files::Filesystem& fs, const fs::path& filepath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "PackageSpec.h"
|
||||
#include "BinaryParagraph.h"
|
||||
#include "Lazy.h"
|
||||
#include "vcpkg_Files.h"
|
||||
|
||||
namespace vcpkg
|
||||
{
|
||||
|
|
@ -49,6 +50,8 @@ namespace vcpkg
|
|||
const fs::path& get_nuget_exe() const;
|
||||
const Toolset& get_toolset() const;
|
||||
|
||||
Files::Filesystem& get_filesystem() const;
|
||||
|
||||
private:
|
||||
Lazy<fs::path> cmake_exe;
|
||||
Lazy<fs::path> git_exe;
|
||||
|
|
|
|||
|
|
@ -6,11 +6,17 @@
|
|||
|
||||
namespace vcpkg::Files
|
||||
{
|
||||
__interface Filesystem
|
||||
{
|
||||
virtual Expected<std::string> read_contents(const fs::path& file_path) noexcept = 0;
|
||||
};
|
||||
|
||||
Filesystem& get_real_filesystem();
|
||||
|
||||
static const char* FILESYSTEM_INVALID_CHARACTERS = R"(\/:*?"<>|)";
|
||||
|
||||
bool has_invalid_chars_for_filesystem(const std::string& s);
|
||||
|
||||
Expected<std::string> read_contents(const fs::path& file_path) noexcept;
|
||||
|
||||
Expected<std::vector<std::string>> read_all_lines(const fs::path& file_path);
|
||||
|
||||
|
|
|
|||
|
|
@ -172,9 +172,9 @@ namespace vcpkg::Paragraphs
|
|||
}
|
||||
};
|
||||
|
||||
Expected<std::unordered_map<std::string, std::string>> get_single_paragraph(const fs::path& control_path)
|
||||
Expected<std::unordered_map<std::string, std::string>> get_single_paragraph(Files::Filesystem& fs, const fs::path& control_path)
|
||||
{
|
||||
const Expected<std::string> contents = Files::read_contents(control_path);
|
||||
const Expected<std::string> contents = fs.read_contents(control_path);
|
||||
if (auto spgh = contents.get())
|
||||
{
|
||||
return parse_single_paragraph(*spgh);
|
||||
|
|
@ -183,9 +183,9 @@ namespace vcpkg::Paragraphs
|
|||
return contents.error_code();
|
||||
}
|
||||
|
||||
Expected<std::vector<std::unordered_map<std::string, std::string>>> get_paragraphs(const fs::path& control_path)
|
||||
Expected<std::vector<std::unordered_map<std::string, std::string>>> get_paragraphs(Files::Filesystem& fs, const fs::path& control_path)
|
||||
{
|
||||
const Expected<std::string> contents = Files::read_contents(control_path);
|
||||
const Expected<std::string> contents = fs.read_contents(control_path);
|
||||
if (auto spgh = contents.get())
|
||||
{
|
||||
return parse_paragraphs(*spgh);
|
||||
|
|
@ -211,9 +211,9 @@ namespace vcpkg::Paragraphs
|
|||
return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
|
||||
}
|
||||
|
||||
Expected<SourceParagraph> try_load_port(const fs::path& path)
|
||||
Expected<SourceParagraph> try_load_port(Files::Filesystem& fs, const fs::path& path)
|
||||
{
|
||||
Expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(path / "CONTROL");
|
||||
Expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(fs, path / "CONTROL");
|
||||
if (auto p = pghs.get())
|
||||
{
|
||||
return SourceParagraph(*p);
|
||||
|
|
@ -224,7 +224,7 @@ namespace vcpkg::Paragraphs
|
|||
|
||||
Expected<BinaryParagraph> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
|
||||
{
|
||||
Expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(paths.package_dir(spec) / "CONTROL");
|
||||
Expected<std::unordered_map<std::string, std::string>> pghs = get_single_paragraph(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
|
||||
|
||||
if (auto p = pghs.get())
|
||||
{
|
||||
|
|
@ -234,13 +234,13 @@ namespace vcpkg::Paragraphs
|
|||
return pghs.error_code();
|
||||
}
|
||||
|
||||
std::vector<SourceParagraph> load_all_ports(const fs::path& ports_dir)
|
||||
std::vector<SourceParagraph> load_all_ports(Files::Filesystem& fs, const fs::path& ports_dir)
|
||||
{
|
||||
std::vector<SourceParagraph> output;
|
||||
for (auto it = fs::directory_iterator(ports_dir); it != fs::directory_iterator(); ++it)
|
||||
{
|
||||
const fs::path& path = it->path();
|
||||
Expected<SourceParagraph> source_paragraph = try_load_port(path);
|
||||
Expected<SourceParagraph> source_paragraph = try_load_port(fs, path);
|
||||
if (auto srcpgh = source_paragraph.get())
|
||||
{
|
||||
output.emplace_back(std::move(*srcpgh));
|
||||
|
|
|
|||
|
|
@ -624,7 +624,7 @@ namespace vcpkg::PostBuildLint
|
|||
// for dumpbin
|
||||
const Toolset& toolset = paths.get_toolset();
|
||||
|
||||
BuildInfo build_info = read_build_info(paths.build_info_file_path(spec));
|
||||
BuildInfo build_info = read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec));
|
||||
const fs::path package_dir = paths.package_dir(spec);
|
||||
|
||||
size_t error_count = 0;
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ namespace vcpkg::PostBuildLint
|
|||
return build_info;
|
||||
}
|
||||
|
||||
BuildInfo read_build_info(const fs::path& filepath)
|
||||
BuildInfo read_build_info(Files::Filesystem& fs, const fs::path& filepath)
|
||||
{
|
||||
const Expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(filepath);
|
||||
const Expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(fs, filepath);
|
||||
Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package");
|
||||
return BuildInfo::create(*pghs.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "PackageSpec.h"
|
||||
#include "vcpkg_Files.h"
|
||||
#include "vcpkg_Util.h"
|
||||
#include "..\include\VcpkgPaths.h"
|
||||
|
||||
namespace vcpkg
|
||||
{
|
||||
|
|
@ -335,4 +336,8 @@ namespace vcpkg
|
|||
{
|
||||
return this->toolset.get_lazy([this]() { return find_toolset_instance(*this); });
|
||||
}
|
||||
Files::Filesystem & VcpkgPaths::get_filesystem() const
|
||||
{
|
||||
return Files::get_real_filesystem();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ namespace vcpkg::Commands::Build
|
|||
Checks::exit_success(VCPKG_LINE_INFO);
|
||||
}
|
||||
|
||||
const Expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(port_dir);
|
||||
const Expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(paths.get_filesystem(), port_dir);
|
||||
Checks::check_exit(VCPKG_LINE_INFO, !maybe_spgh.error_code(), "Could not find package named %s: %s", spec, maybe_spgh.error_code().message());
|
||||
const SourceParagraph& spgh = *maybe_spgh.get();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace vcpkg::Commands::Cache
|
|||
for (auto it = fs::directory_iterator(paths.packages); it != fs::directory_iterator(); ++it)
|
||||
{
|
||||
const fs::path& path = it->path();
|
||||
const Expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(path / "CONTROL");
|
||||
const Expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(paths.get_filesystem(), path / "CONTROL");
|
||||
if (auto p = pghs.get())
|
||||
{
|
||||
const BinaryParagraph binary_paragraph = BinaryParagraph(*p);
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ namespace vcpkg::Commands::CI
|
|||
using Dependencies::InstallPlanType;
|
||||
using Build::BuildResult;
|
||||
|
||||
static std::vector<PackageSpec> load_all_package_specs(const fs::path& ports_directory, const Triplet& target_triplet)
|
||||
static std::vector<PackageSpec> load_all_package_specs(Files::Filesystem& fs, const fs::path& ports_directory, const Triplet& target_triplet)
|
||||
{
|
||||
std::vector<SourceParagraph> ports = Paragraphs::load_all_ports(ports_directory);
|
||||
std::vector<SourceParagraph> ports = Paragraphs::load_all_ports(fs, ports_directory);
|
||||
std::vector<PackageSpec> specs;
|
||||
for (const SourceParagraph& p : ports)
|
||||
{
|
||||
|
|
@ -33,7 +33,7 @@ namespace vcpkg::Commands::CI
|
|||
const Triplet target_triplet = args.command_arguments.size() == 1 ? Triplet::from_canonical_name(args.command_arguments.at(0)) : default_target_triplet;
|
||||
Input::check_triplet(target_triplet, paths);
|
||||
args.check_and_get_optional_command_arguments({});
|
||||
const std::vector<PackageSpec> specs = load_all_package_specs(paths.ports, target_triplet);
|
||||
const std::vector<PackageSpec> specs = load_all_package_specs(paths.get_filesystem(), paths.ports, target_triplet);
|
||||
|
||||
StatusParagraphs status_db = database_load_check(paths);
|
||||
const std::vector<PackageSpecWithInstallPlan> install_plan = Dependencies::create_install_plan(paths, specs, status_db);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace vcpkg::Commands::DependInfo
|
|||
args.check_exact_arg_count(0, example);
|
||||
args.check_and_get_optional_command_arguments({});
|
||||
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.ports);
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
|
||||
|
||||
for (const SourceParagraph& source_paragraph : source_paragraphs)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace vcpkg::Commands::Import
|
|||
const fs::path include_directory(args.command_arguments[1]);
|
||||
const fs::path project_directory(args.command_arguments[2]);
|
||||
|
||||
const Expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(control_file_path);
|
||||
const Expected<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_single_paragraph(paths.get_filesystem(), control_file_path);
|
||||
Checks::check_exit(VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid control file %s for package", control_file_path.generic_string());
|
||||
|
||||
StatusParagraph spgh;
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ namespace vcpkg::Commands::Integrate
|
|||
fs::create_directory(tmp_dir);
|
||||
|
||||
bool should_install_system = true;
|
||||
const Expected<std::string> system_wide_file_contents = Files::read_contents(system_wide_targets_file);
|
||||
const Expected<std::string> system_wide_file_contents = paths.get_filesystem().read_contents(system_wide_targets_file);
|
||||
if (auto contents_data = system_wide_file_contents.get())
|
||||
{
|
||||
std::regex re(R"###(<!-- version (\d+) -->)###");
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ namespace vcpkg::Commands::PortsDiff
|
|||
L".vcpkg-root",
|
||||
git_exe.native());
|
||||
System::cmd_execute_clean(cmd);
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(temp_checkout_path / ports_dir_name_as_string);
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string);
|
||||
const std::map<std::string, VersionT> names_and_versions = Paragraphs::extract_port_names_and_versions(source_paragraphs);
|
||||
fs::remove_all(temp_checkout_path);
|
||||
return names_and_versions;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace vcpkg::Commands::Search
|
|||
args.check_max_arg_count(1, example);
|
||||
const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({ OPTION_GRAPH });
|
||||
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.ports);
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
|
||||
if (options.find(OPTION_GRAPH) != options.cend())
|
||||
{
|
||||
const std::string graph_as_string = create_graph_as_string(source_paragraphs);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace vcpkg::Commands::Update
|
|||
|
||||
std::vector<OutdatedPackage> find_outdated_packages(const VcpkgPaths& paths, const StatusParagraphs& status_db)
|
||||
{
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.ports);
|
||||
const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports);
|
||||
const std::map<std::string, VersionT> src_names_to_versions = Paragraphs::extract_port_names_and_versions(source_paragraphs);
|
||||
const std::vector<StatusParagraph*> installed_packages = get_installed_ports(status_db);
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ namespace vcpkg::Commands::Update
|
|||
" vcpkg install <pkgs>...");
|
||||
}
|
||||
|
||||
auto version_file = Files::read_contents(paths.root / "toolsrc" / "VERSION.txt");
|
||||
auto version_file = paths.get_filesystem().read_contents(paths.root / "toolsrc" / "VERSION.txt");
|
||||
if (auto version_contents = version_file.get())
|
||||
{
|
||||
int maj1, min1, rev1;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ static void loadConfig()
|
|||
|
||||
try
|
||||
{
|
||||
auto maybe_pghs = Paragraphs::get_paragraphs(localappdata / "vcpkg" / "config");
|
||||
auto maybe_pghs = Paragraphs::get_paragraphs(Files::get_real_filesystem(), localappdata / "vcpkg" / "config");
|
||||
if (auto p_pghs = maybe_pghs.get())
|
||||
{
|
||||
const auto& pghs = *p_pghs;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ namespace vcpkg::Dependencies
|
|||
continue;
|
||||
}
|
||||
|
||||
Expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(paths.port_dir(spec));
|
||||
Expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec));
|
||||
if (auto spgh = maybe_spgh.get())
|
||||
{
|
||||
process_dependencies(filter_dependencies(spgh->depends, spec.target_triplet()));
|
||||
|
|
|
|||
|
|
@ -6,36 +6,45 @@ namespace vcpkg::Files
|
|||
{
|
||||
static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])");
|
||||
|
||||
struct RealFilesystem : Filesystem
|
||||
{
|
||||
Expected<std::string> read_contents(const fs::path& file_path) noexcept override
|
||||
{
|
||||
std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
|
||||
if (file_stream.fail())
|
||||
{
|
||||
return std::errc::no_such_file_or_directory;
|
||||
}
|
||||
|
||||
file_stream.seekg(0, file_stream.end);
|
||||
auto length = file_stream.tellg();
|
||||
file_stream.seekg(0, file_stream.beg);
|
||||
|
||||
if (length > SIZE_MAX)
|
||||
{
|
||||
return std::errc::file_too_large;
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output.resize(static_cast<size_t>(length));
|
||||
file_stream.read(&output[0], length);
|
||||
file_stream.close();
|
||||
|
||||
return std::move(output);
|
||||
}
|
||||
};
|
||||
|
||||
Filesystem & get_real_filesystem()
|
||||
{
|
||||
static RealFilesystem real_fs;
|
||||
return real_fs;
|
||||
}
|
||||
|
||||
bool has_invalid_chars_for_filesystem(const std::string& s)
|
||||
{
|
||||
return std::regex_search(s, FILESYSTEM_INVALID_CHARACTERS_REGEX);
|
||||
}
|
||||
|
||||
Expected<std::string> read_contents(const fs::path& file_path) noexcept
|
||||
{
|
||||
std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
|
||||
if (file_stream.fail())
|
||||
{
|
||||
return std::errc::no_such_file_or_directory;
|
||||
}
|
||||
|
||||
file_stream.seekg(0, file_stream.end);
|
||||
auto length = file_stream.tellg();
|
||||
file_stream.seekg(0, file_stream.beg);
|
||||
|
||||
if (length > SIZE_MAX)
|
||||
{
|
||||
return std::errc::file_too_large;
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output.resize(static_cast<size_t>(length));
|
||||
file_stream.read(&output[0], length);
|
||||
file_stream.close();
|
||||
|
||||
return std::move(output);
|
||||
}
|
||||
|
||||
Expected<std::vector<std::string>> read_all_lines(const fs::path& file_path)
|
||||
{
|
||||
std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ int WINAPI WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int)
|
|||
LPWSTR * szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
|
||||
|
||||
Checks::check_exit(VCPKG_LINE_INFO, argCount == 2, "Requires exactly one argument, the path to the payload file");
|
||||
Metrics::upload(Files::read_contents(szArgList[1]).value_or_exit(VCPKG_LINE_INFO));
|
||||
Metrics::upload(Files::get_real_filesystem().read_contents(szArgList[1]).value_or_exit(VCPKG_LINE_INFO));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace vcpkg
|
|||
{
|
||||
bool g_debugging = false;
|
||||
|
||||
static StatusParagraphs load_current_database(const fs::path& vcpkg_dir_status_file, const fs::path& vcpkg_dir_status_file_old)
|
||||
static StatusParagraphs load_current_database(Files::Filesystem& fs, const fs::path& vcpkg_dir_status_file, const fs::path& vcpkg_dir_status_file_old)
|
||||
{
|
||||
if (!fs::exists(vcpkg_dir_status_file))
|
||||
{
|
||||
|
|
@ -22,7 +22,7 @@ namespace vcpkg
|
|||
fs::rename(vcpkg_dir_status_file_old, vcpkg_dir_status_file);
|
||||
}
|
||||
|
||||
auto pghs = Paragraphs::get_paragraphs(vcpkg_dir_status_file).value_or_exit(VCPKG_LINE_INFO);
|
||||
auto pghs = Paragraphs::get_paragraphs(fs, vcpkg_dir_status_file).value_or_exit(VCPKG_LINE_INFO);
|
||||
|
||||
std::vector<std::unique_ptr<StatusParagraph>> status_pghs;
|
||||
for (auto&& p : pghs)
|
||||
|
|
@ -47,7 +47,7 @@ namespace vcpkg
|
|||
const fs::path status_file_old = status_file.parent_path() / "status-old";
|
||||
const fs::path status_file_new = status_file.parent_path() / "status-new";
|
||||
|
||||
StatusParagraphs current_status_db = load_current_database(status_file, status_file_old);
|
||||
StatusParagraphs current_status_db = load_current_database(paths.get_filesystem(), status_file, status_file_old);
|
||||
|
||||
auto b = fs::directory_iterator(updates_dir);
|
||||
auto e = fs::directory_iterator();
|
||||
|
|
@ -64,7 +64,7 @@ namespace vcpkg
|
|||
if (b->path().filename() == "incomplete")
|
||||
continue;
|
||||
|
||||
auto pghs = Paragraphs::get_paragraphs(b->path()).value_or_exit(VCPKG_LINE_INFO);
|
||||
auto pghs = Paragraphs::get_paragraphs(paths.get_filesystem(), b->path()).value_or_exit(VCPKG_LINE_INFO);
|
||||
for (auto&& p : pghs)
|
||||
{
|
||||
current_status_db.insert(std::make_unique<StatusParagraph>(p));
|
||||
|
|
|
|||
Loading…
Reference in New Issue