2018-12-31 20:04:05 +01:00
|
|
|
//
|
2018-01-02 14:42:31 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
|
2018-12-31 20:04:05 +01:00
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
#include "td/utils/filesystem.h"
|
|
|
|
|
|
|
|
#include "td/utils/buffer.h"
|
|
|
|
#include "td/utils/logging.h"
|
|
|
|
#include "td/utils/PathView.h"
|
|
|
|
#include "td/utils/port/FileFd.h"
|
|
|
|
#include "td/utils/Slice.h"
|
|
|
|
#include "td/utils/Status.h"
|
|
|
|
#include "td/utils/unicode.h"
|
|
|
|
#include "td/utils/utf8.h"
|
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
|
|
|
Result<BufferSlice> read_file(CSlice path, int64 size) {
|
|
|
|
TRY_RESULT(from_file, FileFd::open(path, FileFd::Read));
|
|
|
|
if (size == -1) {
|
|
|
|
size = from_file.get_size();
|
|
|
|
}
|
|
|
|
BufferWriter content{static_cast<size_t>(size), 0, 0};
|
|
|
|
TRY_RESULT(got_size, from_file.read(content.as_slice()));
|
|
|
|
if (got_size != static_cast<size_t>(size)) {
|
|
|
|
return Status::Error("Failed to read file");
|
|
|
|
}
|
|
|
|
from_file.close();
|
|
|
|
return content.as_buffer_slice();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Very straightforward function. Don't expect much of it.
|
|
|
|
Status copy_file(CSlice from, CSlice to, int64 size) {
|
|
|
|
TRY_RESULT(content, read_file(from, size));
|
|
|
|
return write_file(to, content.as_slice());
|
|
|
|
}
|
|
|
|
|
|
|
|
Status write_file(CSlice to, Slice data) {
|
|
|
|
auto size = data.size();
|
|
|
|
TRY_RESULT(to_file, FileFd::open(to, FileFd::Truncate | FileFd::Create | FileFd::Write));
|
|
|
|
TRY_RESULT(written, to_file.write(data));
|
|
|
|
if (written != static_cast<size_t>(size)) {
|
|
|
|
return Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size);
|
|
|
|
}
|
|
|
|
to_file.close();
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string clean_filename_part(Slice name, int max_length) {
|
|
|
|
auto is_ok = [](uint32 code) {
|
|
|
|
if (code < 32) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (code < 127) {
|
|
|
|
switch (code) {
|
|
|
|
case '<':
|
|
|
|
case '>':
|
|
|
|
case ':':
|
|
|
|
case '"':
|
|
|
|
case '/':
|
|
|
|
case '\\':
|
|
|
|
case '|':
|
|
|
|
case '?':
|
|
|
|
case '*':
|
|
|
|
case '&':
|
|
|
|
case '`':
|
|
|
|
case '\'':
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto category = get_unicode_simple_category(code);
|
|
|
|
|
|
|
|
return category == UnicodeSimpleCategory::Letter || category == UnicodeSimpleCategory::DecimalNumber ||
|
|
|
|
category == UnicodeSimpleCategory::Number;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string new_name;
|
|
|
|
int size = 0;
|
|
|
|
for (auto *it = name.ubegin(); it != name.uend() && size < max_length;) {
|
|
|
|
uint32 code;
|
2018-10-08 14:53:05 +02:00
|
|
|
it = next_utf8_unsafe(it, &code, "clean_filename_part");
|
2018-12-31 20:04:05 +01:00
|
|
|
if (!is_ok(code)) {
|
2018-11-27 02:24:43 +01:00
|
|
|
if (prepare_search_character(code) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
code = ' ';
|
|
|
|
}
|
|
|
|
if (new_name.empty() && (code == ' ' || code == '.')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
append_utf8_character(new_name, code);
|
|
|
|
size++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!new_name.empty() && (new_name.back() == ' ' || new_name.back() == '.')) {
|
|
|
|
new_name.pop_back();
|
|
|
|
}
|
|
|
|
return new_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string clean_filename(CSlice name) {
|
|
|
|
if (!check_utf8(name)) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
PathView path_view(name);
|
|
|
|
auto filename = clean_filename_part(path_view.file_stem(), 60);
|
|
|
|
auto extension = clean_filename_part(path_view.extension(), 20);
|
|
|
|
if (!extension.empty()) {
|
|
|
|
if (filename.empty()) {
|
|
|
|
filename = std::move(extension);
|
|
|
|
} else {
|
|
|
|
filename.reserve(filename.size() + 1 + extension.size());
|
|
|
|
filename += '.';
|
|
|
|
filename += extension;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace td
|