367 lines
9.7 KiB
C++
367 lines
9.7 KiB
C++
|
/*
|
|||
|
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
|
|||
|
*
|
|||
|
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
|
|||
|
*
|
|||
|
* Use of this source code is governed by MIT license that can be found in the
|
|||
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|||
|
* may be found in the AUTHORS file in the root of the source tree.
|
|||
|
*/
|
|||
|
|
|||
|
#if defined(_WIN32)
|
|||
|
#include <io.h>
|
|||
|
#include <direct.h>
|
|||
|
#else
|
|||
|
#include <dirent.h>
|
|||
|
#include <limits.h>
|
|||
|
#endif // WIN32
|
|||
|
|
|||
|
#include <sys/stat.h>
|
|||
|
#include "File.h"
|
|||
|
#include "util.h"
|
|||
|
#include "logger.h"
|
|||
|
#include "uv_errno.h"
|
|||
|
|
|||
|
using namespace std;
|
|||
|
using namespace toolkit;
|
|||
|
|
|||
|
#if !defined(_WIN32)
|
|||
|
#define _unlink unlink
|
|||
|
#define _rmdir rmdir
|
|||
|
#define _access access
|
|||
|
#endif
|
|||
|
|
|||
|
#if defined(_WIN32)
|
|||
|
|
|||
|
int mkdir(const char *path, int mode) {
|
|||
|
return _mkdir(path);
|
|||
|
}
|
|||
|
|
|||
|
DIR *opendir(const char *name) {
|
|||
|
char namebuf[512];
|
|||
|
snprintf(namebuf, sizeof(namebuf), "%s\\*.*", name);
|
|||
|
|
|||
|
WIN32_FIND_DATAA FindData;
|
|||
|
auto hFind = FindFirstFileA(namebuf, &FindData);
|
|||
|
if (hFind == INVALID_HANDLE_VALUE) {
|
|||
|
return nullptr;
|
|||
|
}
|
|||
|
DIR *dir = (DIR *)malloc(sizeof(DIR));
|
|||
|
memset(dir, 0, sizeof(DIR));
|
|||
|
dir->dd_fd = 0; // simulate return
|
|||
|
dir->handle = hFind;
|
|||
|
return dir;
|
|||
|
}
|
|||
|
|
|||
|
struct dirent *readdir(DIR *d) {
|
|||
|
HANDLE hFind = d->handle;
|
|||
|
WIN32_FIND_DATAA FileData;
|
|||
|
//fail or end
|
|||
|
if (!FindNextFileA(hFind, &FileData)) {
|
|||
|
return nullptr;
|
|||
|
}
|
|||
|
struct dirent *dir = (struct dirent *)malloc(sizeof(struct dirent) + sizeof(FileData.cFileName));
|
|||
|
strcpy(dir->d_name, (char *)FileData.cFileName);
|
|||
|
dir->d_reclen = (uint16_t)strlen(dir->d_name);
|
|||
|
//check there is file or directory
|
|||
|
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|||
|
dir->d_type = 2;
|
|||
|
}
|
|||
|
else {
|
|||
|
dir->d_type = 1;
|
|||
|
}
|
|||
|
if (d->index) {
|
|||
|
//覆盖前释放内存
|
|||
|
free(d->index);
|
|||
|
d->index = nullptr;
|
|||
|
}
|
|||
|
d->index = dir;
|
|||
|
return dir;
|
|||
|
}
|
|||
|
|
|||
|
int closedir(DIR *d) {
|
|||
|
if (!d) {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
//关闭句柄
|
|||
|
if (d->handle != INVALID_HANDLE_VALUE) {
|
|||
|
FindClose(d->handle);
|
|||
|
d->handle = INVALID_HANDLE_VALUE;
|
|||
|
}
|
|||
|
//释放内存
|
|||
|
if (d->index) {
|
|||
|
free(d->index);
|
|||
|
d->index = nullptr;
|
|||
|
}
|
|||
|
free(d);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
#endif // defined(_WIN32)
|
|||
|
|
|||
|
namespace toolkit {
|
|||
|
|
|||
|
FILE *File::create_file(const std::string &file, const std::string &mode) {
|
|||
|
std::string path = file;
|
|||
|
std::string dir;
|
|||
|
size_t index = 1;
|
|||
|
FILE *ret = nullptr;
|
|||
|
while (true) {
|
|||
|
index = path.find('/', index) + 1;
|
|||
|
dir = path.substr(0, index);
|
|||
|
if (dir.length() == 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
if (_access(dir.data(), 0) == -1) { //access函数是查看是不是存在
|
|||
|
if (mkdir(dir.data(), 0777) == -1) { //如果不存在就用mkdir函数来创建
|
|||
|
WarnL << "mkdir " << dir << " failed: " << get_uv_errmsg();
|
|||
|
return nullptr;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (path[path.size() - 1] != '/') {
|
|||
|
ret = fopen(file.data(), mode.data());
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
bool File::create_path(const std::string &file, unsigned int mod) {
|
|||
|
std::string path = file;
|
|||
|
std::string dir;
|
|||
|
size_t index = 1;
|
|||
|
while (true) {
|
|||
|
index = path.find('/', index) + 1;
|
|||
|
dir = path.substr(0, index);
|
|||
|
if (dir.length() == 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
if (_access(dir.data(), 0) == -1) { //access函数是查看是不是存在
|
|||
|
if (mkdir(dir.data(), mod) == -1) { //如果不存在就用mkdir函数来创建
|
|||
|
WarnL << "mkdir " << dir << " failed: " << get_uv_errmsg();
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
//判断是否为目录
|
|||
|
bool File::is_dir(const std::string &path) {
|
|||
|
auto dir = opendir(path.data());
|
|||
|
if (!dir) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
closedir(dir);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
//判断是否为常规文件
|
|||
|
bool File::fileExist(const std::string &path) {
|
|||
|
auto fp = fopen(path.data(), "rb");
|
|||
|
if (!fp) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
fclose(fp);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
//判断是否是特殊目录
|
|||
|
bool File::is_special_dir(const std::string &path) {
|
|||
|
return path == "." || path == "..";
|
|||
|
}
|
|||
|
|
|||
|
static int delete_file_l(const std::string &path_in) {
|
|||
|
DIR *dir;
|
|||
|
dirent *dir_info;
|
|||
|
auto path = path_in;
|
|||
|
if (path.back() == '/') {
|
|||
|
path.pop_back();
|
|||
|
}
|
|||
|
if (File::is_dir(path)) {
|
|||
|
if ((dir = opendir(path.data())) == nullptr) {
|
|||
|
return _rmdir(path.data());
|
|||
|
}
|
|||
|
while ((dir_info = readdir(dir)) != nullptr) {
|
|||
|
if (File::is_special_dir(dir_info->d_name)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
File::delete_file(path + "/" + dir_info->d_name);
|
|||
|
}
|
|||
|
auto ret = _rmdir(path.data());
|
|||
|
closedir(dir);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
return remove(path.data()) ? _unlink(path.data()) : 0;
|
|||
|
}
|
|||
|
|
|||
|
int File::delete_file(const std::string &path, bool del_empty_dir, bool backtrace) {
|
|||
|
auto ret = delete_file_l(path);
|
|||
|
if (!ret && del_empty_dir) {
|
|||
|
// delete success
|
|||
|
File::deleteEmptyDir(parentDir(path), backtrace);
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
string File::loadFile(const std::string &path) {
|
|||
|
FILE *fp = fopen(path.data(), "rb");
|
|||
|
if (!fp) {
|
|||
|
return "";
|
|||
|
}
|
|||
|
fseek(fp, 0, SEEK_END);
|
|||
|
auto len = ftell(fp);
|
|||
|
fseek(fp, 0, SEEK_SET);
|
|||
|
string str(len, '\0');
|
|||
|
if (len != (decltype(len))fread((char *)str.data(), 1, str.size(), fp)) {
|
|||
|
WarnL << "fread " << path << " failed: " << get_uv_errmsg();
|
|||
|
}
|
|||
|
fclose(fp);
|
|||
|
return str;
|
|||
|
}
|
|||
|
|
|||
|
bool File::saveFile(const string &data, const std::string &path) {
|
|||
|
FILE *fp = fopen(path.data(), "wb");
|
|||
|
if (!fp) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
fwrite(data.data(), data.size(), 1, fp);
|
|||
|
fclose(fp);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
string File::parentDir(const std::string &path) {
|
|||
|
auto parent_dir = path;
|
|||
|
if (parent_dir.back() == '/') {
|
|||
|
parent_dir.pop_back();
|
|||
|
}
|
|||
|
auto pos = parent_dir.rfind('/');
|
|||
|
if (pos != string::npos) {
|
|||
|
parent_dir = parent_dir.substr(0, pos + 1);
|
|||
|
}
|
|||
|
return parent_dir;
|
|||
|
}
|
|||
|
|
|||
|
string File::absolutePath(const std::string &path, const std::string ¤t_path, bool can_access_parent) {
|
|||
|
string currentPath = current_path;
|
|||
|
if (!currentPath.empty()) {
|
|||
|
//当前目录不为空
|
|||
|
if (currentPath.front() == '.') {
|
|||
|
//如果当前目录是相对路径,那么先转换成绝对路径
|
|||
|
currentPath = absolutePath(current_path, exeDir(), true);
|
|||
|
}
|
|||
|
} else {
|
|||
|
currentPath = exeDir();
|
|||
|
}
|
|||
|
|
|||
|
if (path.empty()) {
|
|||
|
//相对路径为空,那么返回当前目录
|
|||
|
return currentPath;
|
|||
|
}
|
|||
|
|
|||
|
if (currentPath.back() != '/') {
|
|||
|
//确保当前目录最后字节为'/'
|
|||
|
currentPath.push_back('/');
|
|||
|
}
|
|||
|
auto rootPath = currentPath;
|
|||
|
auto dir_vec = split(path, "/");
|
|||
|
for (auto &dir : dir_vec) {
|
|||
|
if (dir.empty() || dir == ".") {
|
|||
|
//忽略空或本文件夹
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (dir == "..") {
|
|||
|
//访问上级目录
|
|||
|
if (!can_access_parent && currentPath.size() <= rootPath.size()) {
|
|||
|
//不能访问根目录之外的目录, 返回根目录
|
|||
|
return rootPath;
|
|||
|
}
|
|||
|
currentPath = parentDir(currentPath);
|
|||
|
continue;
|
|||
|
}
|
|||
|
currentPath.append(dir);
|
|||
|
currentPath.append("/");
|
|||
|
}
|
|||
|
|
|||
|
if (path.back() != '/' && currentPath.back() == '/') {
|
|||
|
//在路径是文件的情况下,防止转换成目录
|
|||
|
currentPath.pop_back();
|
|||
|
}
|
|||
|
return currentPath;
|
|||
|
}
|
|||
|
|
|||
|
void File::scanDir(const std::string &path_in, const function<bool(const string &path, bool is_dir)> &cb,
|
|||
|
bool enter_subdirectory, bool show_hidden_file) {
|
|||
|
string path = path_in;
|
|||
|
if (path.back() == '/') {
|
|||
|
path.pop_back();
|
|||
|
}
|
|||
|
|
|||
|
DIR *pDir;
|
|||
|
dirent *pDirent;
|
|||
|
if ((pDir = opendir(path.data())) == nullptr) {
|
|||
|
//文件夹无效
|
|||
|
return;
|
|||
|
}
|
|||
|
while ((pDirent = readdir(pDir)) != nullptr) {
|
|||
|
if (is_special_dir(pDirent->d_name)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (!show_hidden_file && pDirent->d_name[0] == '.') {
|
|||
|
//隐藏的文件
|
|||
|
continue;
|
|||
|
}
|
|||
|
string strAbsolutePath = path + "/" + pDirent->d_name;
|
|||
|
bool isDir = is_dir(strAbsolutePath);
|
|||
|
if (!cb(strAbsolutePath, isDir)) {
|
|||
|
//不再继续扫描
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (isDir && enter_subdirectory) {
|
|||
|
//如果是文件夹并且扫描子文件夹,那么递归扫描
|
|||
|
scanDir(strAbsolutePath, cb, enter_subdirectory);
|
|||
|
}
|
|||
|
}
|
|||
|
closedir(pDir);
|
|||
|
}
|
|||
|
|
|||
|
uint64_t File::fileSize(FILE *fp, bool remain_size) {
|
|||
|
if (!fp) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
auto current = ftell64(fp);
|
|||
|
fseek64(fp, 0L, SEEK_END); /* 定位到文件末尾 */
|
|||
|
auto end = ftell64(fp); /* 得到文件大小 */
|
|||
|
fseek64(fp, current, SEEK_SET);
|
|||
|
return end - (remain_size ? current : 0);
|
|||
|
}
|
|||
|
|
|||
|
uint64_t File::fileSize(const std::string &path) {
|
|||
|
if (path.empty()) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
auto fp = std::unique_ptr<FILE, decltype(&fclose)>(fopen(path.data(), "rb"), fclose);
|
|||
|
return fileSize(fp.get());
|
|||
|
}
|
|||
|
|
|||
|
static bool isEmptyDir(const std::string &path) {
|
|||
|
bool empty = true;
|
|||
|
File::scanDir(path,[&](const std::string &path, bool isDir) {
|
|||
|
empty = false;
|
|||
|
return false;
|
|||
|
}, true, true);
|
|||
|
return empty;
|
|||
|
}
|
|||
|
|
|||
|
void File::deleteEmptyDir(const std::string &dir, bool backtrace) {
|
|||
|
if (!File::is_dir(dir) || !isEmptyDir(dir)) {
|
|||
|
// 不是文件夹或者非空
|
|||
|
return;
|
|||
|
}
|
|||
|
File::delete_file(dir);
|
|||
|
if (backtrace) {
|
|||
|
deleteEmptyDir(File::parentDir(dir), true);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} /* namespace toolkit */
|