mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-06 13:51:33 -06:00
KWSys 2020-03-25 (4380f1ae)
Code extracted from:
https://gitlab.kitware.com/utils/kwsys.git
at commit 4380f1ae99f3206938251393e94055a3e4120b2c (master).
Upstream Shortlog
-----------------
Rolf Eike Beer (6):
25b61c12 Directory: make it move constructible and assignable
8b1a29e1 optimize SystemToolsParseRegistryKey()
420c3b04 call std::string::clear() instead of assigning ""
bc9a4256 avoid inefficient usage of std::string::substr()
e3c051e2 SystemTools: create directories with the right permissions on Un*x
0085096e avoid std::string::find() to check for prefix
This commit is contained in:
committed by
Brad King
parent
ec33e3600c
commit
9d3b9ec4ab
@@ -35,6 +35,18 @@ Directory::Directory()
|
||||
this->Internal = new DirectoryInternals;
|
||||
}
|
||||
|
||||
Directory::Directory(Directory&& other)
|
||||
{
|
||||
this->Internal = other.Internal;
|
||||
other.Internal = nullptr;
|
||||
}
|
||||
|
||||
Directory& Directory::operator=(Directory&& other)
|
||||
{
|
||||
std::swap(this->Internal, other.Internal);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Directory::~Directory()
|
||||
{
|
||||
delete this->Internal;
|
||||
|
||||
@@ -23,6 +23,11 @@ class @KWSYS_NAMESPACE@_EXPORT Directory
|
||||
{
|
||||
public:
|
||||
Directory();
|
||||
Directory(Directory&& other);
|
||||
Directory(const Directory&) = delete;
|
||||
Directory& operator=(const Directory&) = delete;
|
||||
Directory& operator=(Directory&& other);
|
||||
bool operator==(const Directory&) = delete;
|
||||
~Directory();
|
||||
|
||||
/**
|
||||
@@ -62,10 +67,7 @@ public:
|
||||
private:
|
||||
// Private implementation details.
|
||||
DirectoryInternals* Internal;
|
||||
|
||||
Directory(const Directory&); // Not implemented.
|
||||
void operator=(const Directory&); // Not implemented.
|
||||
}; // End Class: Directory
|
||||
}; // End Class: Directory
|
||||
|
||||
} // namespace @KWSYS_NAMESPACE@
|
||||
|
||||
|
||||
3
Glob.cxx
3
Glob.cxx
@@ -385,10 +385,9 @@ bool Glob::FindFiles(const std::string& inexpr, GlobMessages* messages)
|
||||
}
|
||||
|
||||
if (skip > 0) {
|
||||
expr = expr.substr(skip);
|
||||
expr.erase(0, skip);
|
||||
}
|
||||
|
||||
cexpr = "";
|
||||
for (cc = 0; cc < expr.size(); cc++) {
|
||||
int ch = expr[cc];
|
||||
if (ch == '/') {
|
||||
|
||||
@@ -1367,7 +1367,7 @@ std::string SymbolProperties::GetFileName(const std::string& path) const
|
||||
if (!this->ReportPath) {
|
||||
size_t at = file.rfind("/");
|
||||
if (at != std::string::npos) {
|
||||
file = file.substr(at + 1);
|
||||
file.erase(0, at + 1);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
@@ -2170,7 +2170,7 @@ void SystemInformationImplementation::FindManufacturer(
|
||||
this->ChipManufacturer = HP; // Hewlett-Packard
|
||||
else if (this->ChipID.Vendor == "Motorola")
|
||||
this->ChipManufacturer = Motorola; // Motorola Microelectronics
|
||||
else if (family.substr(0, 7) == "PA-RISC")
|
||||
else if (family.compare(0, 7, "PA-RISC") == 0)
|
||||
this->ChipManufacturer = HP; // Hewlett-Packard
|
||||
else
|
||||
this->ChipManufacturer = UnknownManufacturer; // Unknown manufacturer
|
||||
@@ -2885,7 +2885,7 @@ static void SystemInformationStripLeadingSpace(std::string& str)
|
||||
// post-process the name.
|
||||
std::string::size_type pos = str.find_first_not_of(" ");
|
||||
if (pos != std::string::npos) {
|
||||
str = str.substr(pos);
|
||||
str.erase(0, pos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -3400,7 +3400,9 @@ std::string SystemInformationImplementation::ExtractValueFromCpuInfoFile(
|
||||
return this->ExtractValueFromCpuInfoFile(buffer, word, pos2);
|
||||
}
|
||||
}
|
||||
return buffer.substr(pos + 2, pos2 - pos - 2);
|
||||
buffer.erase(0, pos + 2);
|
||||
buffer.resize(pos2 - pos - 2);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
this->CurrentPositionInFile = std::string::npos;
|
||||
@@ -3549,7 +3551,7 @@ bool SystemInformationImplementation::RetreiveInformationFromCpuInfoFile()
|
||||
if (!cacheSize.empty()) {
|
||||
pos = cacheSize.find(" KB");
|
||||
if (pos != std::string::npos) {
|
||||
cacheSize = cacheSize.substr(0, pos);
|
||||
cacheSize.resize(pos);
|
||||
}
|
||||
this->Features.L1CacheSize += atoi(cacheSize.c_str());
|
||||
}
|
||||
@@ -4774,7 +4776,8 @@ std::string SystemInformationImplementation::ParseValueFromKStat(
|
||||
}
|
||||
pos = command.find(' ', pos + 1);
|
||||
}
|
||||
args_string.push_back(command.substr(start + 1, command.size() - start - 1));
|
||||
command.erase(0, start + 1);
|
||||
args_string.push_back(command);
|
||||
|
||||
std::vector<const char*> args;
|
||||
args.reserve(3 + args_string.size());
|
||||
@@ -4963,7 +4966,9 @@ bool SystemInformationImplementation::QueryQNXMemory()
|
||||
while (buffer[pos] == ' ')
|
||||
pos++;
|
||||
|
||||
this->TotalPhysicalMemory = atoi(buffer.substr(pos, pos2 - pos).c_str());
|
||||
buffer.erase(0, pos);
|
||||
buffer.resize(pos2);
|
||||
this->TotalPhysicalMemory = atoi(buffer.c_str());
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
|
||||
160
SystemTools.cxx
160
SystemTools.cxx
@@ -221,11 +221,17 @@ static time_t windows_filetime_to_posix_time(const FILETIME& ft)
|
||||
|
||||
#ifdef KWSYS_WINDOWS_DIRS
|
||||
# include <wctype.h>
|
||||
# ifdef _MSC_VER
|
||||
typedef KWSYS_NAMESPACE::SystemTools::mode_t mode_t;
|
||||
# endif
|
||||
|
||||
inline int Mkdir(const std::string& dir)
|
||||
inline int Mkdir(const std::string& dir, const mode_t* mode)
|
||||
{
|
||||
return _wmkdir(
|
||||
KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
|
||||
int ret =
|
||||
_wmkdir(KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
|
||||
if (ret == 0 && mode)
|
||||
KWSYS_NAMESPACE::SystemTools::SetPermissions(dir, *mode);
|
||||
return ret;
|
||||
}
|
||||
inline int Rmdir(const std::string& dir)
|
||||
{
|
||||
@@ -295,9 +301,9 @@ inline void Realpath(const std::string& path, std::string& resolved_path,
|
||||
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
inline int Mkdir(const std::string& dir)
|
||||
inline int Mkdir(const std::string& dir, const mode_t* mode)
|
||||
{
|
||||
return mkdir(dir.c_str(), 00777);
|
||||
return mkdir(dir.c_str(), mode ? *mode : 00777);
|
||||
}
|
||||
inline int Rmdir(const std::string& dir)
|
||||
{
|
||||
@@ -912,16 +918,17 @@ bool SystemTools::MakeDirectory(const std::string& path, const mode_t* mode)
|
||||
std::string::size_type pos = 0;
|
||||
std::string topdir;
|
||||
while ((pos = dir.find('/', pos)) != std::string::npos) {
|
||||
topdir = dir.substr(0, pos);
|
||||
// all underlying functions use C strings, so temporarily
|
||||
// end the string here
|
||||
dir[pos] = '\0';
|
||||
|
||||
if (Mkdir(topdir) == 0 && mode != nullptr) {
|
||||
SystemTools::SetPermissions(topdir, *mode);
|
||||
}
|
||||
Mkdir(dir, mode);
|
||||
dir[pos] = '/';
|
||||
|
||||
++pos;
|
||||
}
|
||||
topdir = dir;
|
||||
if (Mkdir(topdir) != 0) {
|
||||
if (Mkdir(topdir, mode) != 0) {
|
||||
// There is a bug in the Borland Run time library which makes MKDIR
|
||||
// return EACCES when it should return EEXISTS
|
||||
// if it is some other error besides directory exists
|
||||
@@ -933,8 +940,6 @@ bool SystemTools::MakeDirectory(const std::string& path, const mode_t* mode)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
} else if (mode != nullptr) {
|
||||
SystemTools::SetPermissions(topdir, *mode);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1010,38 +1015,40 @@ void SystemToolsStatic::ReplaceString(std::string& source, const char* replace,
|
||||
# define KWSYS_ST_KEY_WOW64_64KEY 0x0100
|
||||
# endif
|
||||
|
||||
static bool SystemToolsParseRegistryKey(const std::string& key,
|
||||
HKEY& primaryKey, std::string& second,
|
||||
std::string& valuename)
|
||||
static bool hasPrefix(const std::string& s, const char* pattern,
|
||||
std::string::size_type spos)
|
||||
{
|
||||
std::string primary = key;
|
||||
size_t plen = strlen(pattern);
|
||||
if (spos != plen)
|
||||
return false;
|
||||
return s.compare(0, plen, pattern) == 0;
|
||||
}
|
||||
|
||||
size_t start = primary.find('\\');
|
||||
static bool SystemToolsParseRegistryKey(const std::string& key,
|
||||
HKEY& primaryKey, std::wstring& second,
|
||||
std::string* valuename)
|
||||
{
|
||||
size_t start = key.find('\\');
|
||||
if (start == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t valuenamepos = primary.find(';');
|
||||
if (valuenamepos != std::string::npos) {
|
||||
valuename = primary.substr(valuenamepos + 1);
|
||||
size_t valuenamepos = key.find(';');
|
||||
if (valuenamepos != std::string::npos && valuename) {
|
||||
*valuename = key.substr(valuenamepos + 1);
|
||||
}
|
||||
|
||||
second = primary.substr(start + 1, valuenamepos - start - 1);
|
||||
primary = primary.substr(0, start);
|
||||
second = Encoding::ToWide(key.substr(start + 1, valuenamepos - start - 1));
|
||||
|
||||
if (primary == "HKEY_CURRENT_USER") {
|
||||
if (hasPrefix(key, "HKEY_CURRENT_USER", start)) {
|
||||
primaryKey = HKEY_CURRENT_USER;
|
||||
}
|
||||
if (primary == "HKEY_CURRENT_CONFIG") {
|
||||
} else if (hasPrefix(key, "HKEY_CURRENT_CONFIG", start)) {
|
||||
primaryKey = HKEY_CURRENT_CONFIG;
|
||||
}
|
||||
if (primary == "HKEY_CLASSES_ROOT") {
|
||||
} else if (hasPrefix(key, "HKEY_CLASSES_ROOT", start)) {
|
||||
primaryKey = HKEY_CLASSES_ROOT;
|
||||
}
|
||||
if (primary == "HKEY_LOCAL_MACHINE") {
|
||||
} else if (hasPrefix(key, "HKEY_LOCAL_MACHINE", start)) {
|
||||
primaryKey = HKEY_LOCAL_MACHINE;
|
||||
}
|
||||
if (primary == "HKEY_USERS") {
|
||||
} else if (hasPrefix(key, "HKEY_USERS", start)) {
|
||||
primaryKey = HKEY_USERS;
|
||||
}
|
||||
|
||||
@@ -1073,14 +1080,13 @@ bool SystemTools::GetRegistrySubKeys(const std::string& key,
|
||||
KeyWOW64 view)
|
||||
{
|
||||
HKEY primaryKey = HKEY_CURRENT_USER;
|
||||
std::string second;
|
||||
std::string valuename;
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) {
|
||||
std::wstring second;
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0,
|
||||
if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
|
||||
SystemToolsMakeRegistryMode(KEY_READ, view),
|
||||
&hKey) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
@@ -1120,14 +1126,14 @@ bool SystemTools::ReadRegistryValue(const std::string& key, std::string& value,
|
||||
{
|
||||
bool valueset = false;
|
||||
HKEY primaryKey = HKEY_CURRENT_USER;
|
||||
std::string second;
|
||||
std::wstring second;
|
||||
std::string valuename;
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) {
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0,
|
||||
if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
|
||||
SystemToolsMakeRegistryMode(KEY_READ, view),
|
||||
&hKey) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
@@ -1174,16 +1180,16 @@ bool SystemTools::WriteRegistryValue(const std::string& key,
|
||||
const std::string& value, KeyWOW64 view)
|
||||
{
|
||||
HKEY primaryKey = HKEY_CURRENT_USER;
|
||||
std::string second;
|
||||
std::wstring second;
|
||||
std::string valuename;
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) {
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HKEY hKey;
|
||||
DWORD dwDummy;
|
||||
wchar_t lpClass[] = L"";
|
||||
if (RegCreateKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0, lpClass,
|
||||
if (RegCreateKeyExW(primaryKey, second.c_str(), 0, lpClass,
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
SystemToolsMakeRegistryMode(KEY_WRITE, view), nullptr,
|
||||
&hKey, &dwDummy) != ERROR_SUCCESS) {
|
||||
@@ -1218,14 +1224,14 @@ bool SystemTools::WriteRegistryValue(const std::string&, const std::string&,
|
||||
bool SystemTools::DeleteRegistryValue(const std::string& key, KeyWOW64 view)
|
||||
{
|
||||
HKEY primaryKey = HKEY_CURRENT_USER;
|
||||
std::string second;
|
||||
std::wstring second;
|
||||
std::string valuename;
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, valuename)) {
|
||||
if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HKEY hKey;
|
||||
if (RegOpenKeyExW(primaryKey, Encoding::ToWide(second).c_str(), 0,
|
||||
if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
|
||||
SystemToolsMakeRegistryMode(KEY_WRITE, view),
|
||||
&hKey) != ERROR_SUCCESS) {
|
||||
return false;
|
||||
@@ -1866,7 +1872,7 @@ std::string SystemTools::CropString(const std::string& s, size_t max_len)
|
||||
|
||||
size_t middle = max_len / 2;
|
||||
|
||||
n += s.substr(0, middle);
|
||||
n.assign(s, 0, middle);
|
||||
n += s.substr(s.size() - (max_len - middle));
|
||||
|
||||
if (max_len > 2) {
|
||||
@@ -2064,8 +2070,10 @@ void SystemTools::ConvertToUnixSlashes(std::string& path)
|
||||
#ifdef HAVE_GETPWNAM
|
||||
else if (pathCString[0] == '~') {
|
||||
std::string::size_type idx = path.find_first_of("/\0");
|
||||
std::string user = path.substr(1, idx - 1);
|
||||
passwd* pw = getpwnam(user.c_str());
|
||||
char oldch = path[idx];
|
||||
path[idx] = '\0';
|
||||
passwd* pw = getpwnam(path.c_str() + 1);
|
||||
path[idx] = oldch;
|
||||
if (pw) {
|
||||
path.replace(0, idx, pw->pw_dir);
|
||||
}
|
||||
@@ -3131,17 +3139,17 @@ bool SystemTools::SplitProgramPath(const std::string& in_name,
|
||||
std::string& dir, std::string& file, bool)
|
||||
{
|
||||
dir = in_name;
|
||||
file = "";
|
||||
file.clear();
|
||||
SystemTools::ConvertToUnixSlashes(dir);
|
||||
|
||||
if (!SystemTools::FileIsDirectory(dir)) {
|
||||
std::string::size_type slashPos = dir.rfind("/");
|
||||
if (slashPos != std::string::npos) {
|
||||
file = dir.substr(slashPos + 1);
|
||||
dir = dir.substr(0, slashPos);
|
||||
dir.resize(slashPos);
|
||||
} else {
|
||||
file = dir;
|
||||
dir = "";
|
||||
dir.clear();
|
||||
}
|
||||
}
|
||||
if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) {
|
||||
@@ -3268,7 +3276,7 @@ void SystemTools::CheckTranslationPath(std::string& path)
|
||||
// Now convert any path found in the table back to the one desired:
|
||||
for (auto const& pair : SystemTools::Statics->TranslationMap) {
|
||||
// We need to check of the path is a substring of the other path
|
||||
if (path.find(pair.first) == 0) {
|
||||
if (path.compare(0, pair.first.size(), pair.first) == 0) {
|
||||
path = path.replace(0, pair.first.size(), pair.second);
|
||||
}
|
||||
}
|
||||
@@ -3540,7 +3548,7 @@ void SystemTools::SplitPath(const std::string& p,
|
||||
// Expand home directory references if requested.
|
||||
if (expand_home_dir && !root.empty() && root[0] == '~') {
|
||||
std::string homedir;
|
||||
root = root.substr(0, root.size() - 1);
|
||||
root.resize(root.size() - 1);
|
||||
if (root.size() == 1) {
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
if (!SystemTools::GetEnv("USERPROFILE", homedir))
|
||||
@@ -3685,18 +3693,19 @@ std::string SystemTools::GetFilenamePath(const std::string& filename)
|
||||
SystemTools::ConvertToUnixSlashes(fn);
|
||||
|
||||
std::string::size_type slash_pos = fn.rfind("/");
|
||||
if (slash_pos != std::string::npos) {
|
||||
std::string ret = fn.substr(0, slash_pos);
|
||||
if (ret.size() == 2 && ret[1] == ':') {
|
||||
return ret + '/';
|
||||
}
|
||||
if (ret.empty()) {
|
||||
return "/";
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
if (slash_pos == 0) {
|
||||
return "/";
|
||||
}
|
||||
if (slash_pos == 2 && fn[1] == ':') {
|
||||
// keep the / after a drive letter
|
||||
fn.resize(3);
|
||||
return fn;
|
||||
}
|
||||
if (slash_pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
fn.resize(slash_pos);
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3726,7 +3735,8 @@ std::string SystemTools::GetFilenameExtension(const std::string& filename)
|
||||
std::string name = SystemTools::GetFilenameName(filename);
|
||||
std::string::size_type dot_pos = name.find('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
return name.substr(dot_pos);
|
||||
name.erase(0, dot_pos);
|
||||
return name;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@@ -3741,7 +3751,8 @@ std::string SystemTools::GetFilenameLastExtension(const std::string& filename)
|
||||
std::string name = SystemTools::GetFilenameName(filename);
|
||||
std::string::size_type dot_pos = name.rfind('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
return name.substr(dot_pos);
|
||||
name.erase(0, dot_pos);
|
||||
return name;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
@@ -3757,10 +3768,9 @@ std::string SystemTools::GetFilenameWithoutExtension(
|
||||
std::string name = SystemTools::GetFilenameName(filename);
|
||||
std::string::size_type dot_pos = name.find('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
return name.substr(0, dot_pos);
|
||||
} else {
|
||||
return name;
|
||||
name.resize(dot_pos);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3774,10 +3784,9 @@ std::string SystemTools::GetFilenameWithoutLastExtension(
|
||||
std::string name = SystemTools::GetFilenameName(filename);
|
||||
std::string::size_type dot_pos = name.rfind('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
return name.substr(0, dot_pos);
|
||||
} else {
|
||||
return name;
|
||||
name.resize(dot_pos);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool SystemTools::FileHasSignature(const char* filename, const char* signature,
|
||||
@@ -3999,7 +4008,8 @@ bool SystemTools::GetShortPath(const std::string& path, std::string& shortPath)
|
||||
|
||||
// if the path passed in has quotes around it, first remove the quotes
|
||||
if (!path.empty() && path[0] == '"' && path.back() == '"') {
|
||||
tempPath = path.substr(1, path.length() - 2);
|
||||
tempPath.resize(path.length() - 1);
|
||||
tempPath.erase(0, 1);
|
||||
}
|
||||
|
||||
std::wstring wtempPath = Encoding::ToWide(tempPath);
|
||||
@@ -4218,8 +4228,8 @@ bool SystemTools::IsSubDirectory(const std::string& cSubdir,
|
||||
if (subdir[expectedSlashPosition] != '/') {
|
||||
return false;
|
||||
}
|
||||
std::string s = subdir.substr(0, dir.size());
|
||||
return SystemTools::ComparePath(s, dir);
|
||||
subdir.resize(dir.size());
|
||||
return SystemTools::ComparePath(subdir, dir);
|
||||
}
|
||||
|
||||
void SystemTools::Delay(unsigned int msec)
|
||||
@@ -4580,8 +4590,8 @@ std::string SystemTools::DecodeURL(const std::string& url)
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < url.length(); i++) {
|
||||
if (urlByteRe.find(url.substr(i, 3))) {
|
||||
ret +=
|
||||
static_cast<char>(strtoul(url.substr(i + 1, 2).c_str(), nullptr, 16));
|
||||
char bytes[] = { url[i + 1], url[i + 2], '\0' };
|
||||
ret += static_cast<char>(strtoul(bytes, nullptr, 16));
|
||||
i += 2;
|
||||
} else {
|
||||
ret += url[i];
|
||||
|
||||
Reference in New Issue
Block a user