CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/File/DirListing.cpp
Views: 1401
#include "ppsspp_config.h"12#if PPSSPP_PLATFORM(WINDOWS)3#define WIN32_LEAN_AND_MEAN4#include <Windows.h>5#include <direct.h>6#if PPSSPP_PLATFORM(UWP)7#include <fileapifromapp.h>8#include <UWP/UWPHelpers/StorageManager.h>9#endif10#else11#include <strings.h>12#include <dirent.h>13#include <unistd.h>14#include <errno.h>15#endif16#include <cstring>17#include <string>18#include <set>19#include <algorithm>20#include <cstdio>21#include <sys/stat.h>22#include <ctype.h>2324#include "Common/Data/Encoding/Utf8.h"25#include "Common/StringUtils.h"26#include "Common/Net/URL.h"27#include "Common/File/DirListing.h"28#include "Common/File/FileUtil.h"29#include "Common/File/AndroidStorage.h"3031#if !defined(__linux__) && !defined(_WIN32) && !defined(__QNX__)32#define stat64 stat33#endif3435#ifdef HAVE_LIBNX36// Far from optimal, but I guess it works...37#define fseeko fseek38#define ftello ftell39#define fileno40#endif // HAVE_LIBNX4142namespace File {4344#if PPSSPP_PLATFORM(WINDOWS)45static uint64_t FiletimeToStatTime(FILETIME ft) {46const int windowsTickResolution = 10000000;47const int64_t secToUnixEpoch = 11644473600LL;48int64_t ticks = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;49return (int64_t)(ticks / windowsTickResolution - secToUnixEpoch);50};51#endif5253bool GetFileInfo(const Path &path, FileInfo * fileInfo) {54switch (path.Type()) {55case PathType::NATIVE:56break; // OK57case PathType::CONTENT_URI:58return Android_GetFileInfo(path.ToString(), fileInfo);59default:60return false;61}6263// TODO: Expand relative paths?64fileInfo->fullName = path;6566#if PPSSPP_PLATFORM(WINDOWS)67WIN32_FILE_ATTRIBUTE_DATA attrs;68#if PPSSPP_PLATFORM(UWP)69if (!GetFileAttributesExFromAppW(path.ToWString().c_str(), GetFileExInfoStandard, &attrs)) {70#else71if (!GetFileAttributesExW(path.ToWString().c_str(), GetFileExInfoStandard, &attrs)) {72#endif73fileInfo->size = 0;74fileInfo->isDirectory = false;75fileInfo->exists = false;76return false;77}78fileInfo->size = (uint64_t)attrs.nFileSizeLow | ((uint64_t)attrs.nFileSizeHigh << 32);79fileInfo->isDirectory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;80fileInfo->isWritable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;81fileInfo->exists = true;82fileInfo->atime = FiletimeToStatTime(attrs.ftLastAccessTime);83fileInfo->mtime = FiletimeToStatTime(attrs.ftLastWriteTime);84fileInfo->ctime = FiletimeToStatTime(attrs.ftCreationTime);85if (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {86fileInfo->access = 0444; // Read87} else {88fileInfo->access = 0666; // Read/Write89}90if (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {91fileInfo->access |= 0111; // Execute92}93#else9495#if (defined __ANDROID__) && (__ANDROID_API__ < 21)96struct stat file_info;97int result = stat(path.c_str(), &file_info);98#else99struct stat64 file_info;100int result = stat64(path.c_str(), &file_info);101#endif102if (result < 0) {103fileInfo->exists = false;104return false;105}106107fileInfo->isDirectory = S_ISDIR(file_info.st_mode);108fileInfo->isWritable = false;109fileInfo->size = file_info.st_size;110fileInfo->exists = true;111fileInfo->atime = file_info.st_atime;112fileInfo->mtime = file_info.st_mtime;113fileInfo->ctime = file_info.st_ctime;114fileInfo->access = file_info.st_mode & 0x1ff;115// HACK: approximation116if (file_info.st_mode & 0200)117fileInfo->isWritable = true;118#endif119return true;120}121122bool GetModifTime(const Path &filename, tm & return_time) {123memset(&return_time, 0, sizeof(return_time));124FileInfo info;125if (GetFileInfo(filename, &info)) {126time_t t = info.mtime;127localtime_r((time_t*)&t, &return_time);128return true;129} else {130return false;131}132}133134bool FileInfo::operator <(const FileInfo & other) const {135if (isDirectory && !other.isDirectory)136return true;137else if (!isDirectory && other.isDirectory)138return false;139if (strcasecmp(name.c_str(), other.name.c_str()) < 0)140return true;141else142return false;143}144145std::vector<File::FileInfo> ApplyFilter(std::vector<File::FileInfo> files, const char *filter) {146std::set<std::string> filters;147if (filter) {148std::string tmp;149while (*filter) {150if (*filter == ':') {151filters.emplace("." + tmp);152tmp.clear();153} else {154tmp.push_back(*filter);155}156filter++;157}158if (!tmp.empty())159filters.emplace("." + tmp);160}161162auto pred = [&](const File::FileInfo &info) {163if (info.isDirectory || !filter)164return false;165std::string ext = info.fullName.GetFileExtension();166return filters.find(ext) == filters.end();167};168files.erase(std::remove_if(files.begin(), files.end(), pred), files.end());169return files;170}171172bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const char *filter, int flags) {173if (directory.Type() == PathType::CONTENT_URI) {174bool exists = false;175std::vector<File::FileInfo> fileList = Android_ListContentUri(directory.ToString(), &exists);176*files = ApplyFilter(fileList, filter);177std::sort(files->begin(), files->end());178return exists;179}180181std::set<std::string> filters;182if (filter) {183std::string tmp;184while (*filter) {185if (*filter == ':') {186filters.insert(tmp);187tmp.clear();188} else {189tmp.push_back(*filter);190}191filter++;192}193if (!tmp.empty())194filters.insert(tmp);195}196197#if PPSSPP_PLATFORM(WINDOWS)198if (directory.IsRoot()) {199// Special path that means root of file system.200std::vector<std::string> drives = File::GetWindowsDrives();201for (auto drive = drives.begin(); drive != drives.end(); ++drive) {202if (*drive == "A:/" || *drive == "B:/")203continue;204File::FileInfo fake;205fake.fullName = Path(*drive);206fake.name = *drive;207fake.isDirectory = true;208fake.exists = true;209fake.size = 0;210fake.isWritable = false;211files->push_back(fake);212}213return files->size();214}215// Find the first file in the directory.216WIN32_FIND_DATA ffd;217std::wstring wpath = directory.ToWString();218wpath += L"\\*";219#if PPSSPP_PLATFORM(UWP)220HANDLE hFind = FindFirstFileExFromAppW(wpath.c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0);221#else222HANDLE hFind = FindFirstFileEx(wpath.c_str(), FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0);223#endif224if (hFind == INVALID_HANDLE_VALUE) {225#if PPSSPP_PLATFORM(UWP)226// This step just to avoid empty results by adding fake folders227// it will help also to navigate back between selected folder228// we must ignore this function for any request other than UI navigation229if (GetFakeFolders(directory, files, filter, filters))230return true;231#endif232return false;233}234do {235const std::string virtualName = ConvertWStringToUTF8(ffd.cFileName);236// check for "." and ".."237if (!(flags & GETFILES_GET_NAVIGATION_ENTRIES) && (virtualName == "." || virtualName == ".."))238continue;239// Remove dotfiles (optional with flag.)240if (!(flags & GETFILES_GETHIDDEN)) {241if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0)242continue;243}244245FileInfo info;246info.name = virtualName;247info.fullName = directory / virtualName;248info.exists = true;249info.size = ((uint64_t)ffd.nFileSizeHigh << 32) | ffd.nFileSizeLow;250info.isDirectory = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;251info.isWritable = (ffd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;252info.atime = FiletimeToStatTime(ffd.ftLastAccessTime);253info.mtime = FiletimeToStatTime(ffd.ftLastWriteTime);254info.ctime = FiletimeToStatTime(ffd.ftCreationTime);255if (ffd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {256info.access = 0444; // Read257} else {258info.access = 0666; // Read/Write259}260if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {261info.access |= 0111; // Execute262}263if (!info.isDirectory) {264std::string ext = info.fullName.GetFileExtension();265if (!ext.empty()) {266ext = ext.substr(1); // Remove the dot.267if (filter && filters.find(ext) == filters.end()) {268continue;269}270}271}272files->push_back(info);273} while (FindNextFile(hFind, &ffd) != 0);274FindClose(hFind);275#else276struct dirent *result = NULL;277DIR *dirp = opendir(directory.c_str());278if (!dirp)279return false;280while ((result = readdir(dirp))) {281const std::string virtualName(result->d_name);282// check for "." and ".."283if (!(flags & GETFILES_GET_NAVIGATION_ENTRIES) && (virtualName == "." || virtualName == ".."))284continue;285286// Remove dotfiles (optional with flag.)287if (!(flags & GETFILES_GETHIDDEN)) {288if (virtualName[0] == '.')289continue;290}291292// Let's just reuse GetFileInfo. We're calling stat anyway to get isDirectory information.293Path fullName = directory / virtualName;294295FileInfo info;296info.name = virtualName;297if (!GetFileInfo(fullName, &info)) {298continue;299}300if (!info.isDirectory) {301std::string ext = info.fullName.GetFileExtension();302if (!ext.empty()) {303ext = ext.substr(1); // Remove the dot.304if (filter && filters.find(ext) == filters.end()) {305continue;306}307}308}309files->push_back(info);310}311closedir(dirp);312#endif313std::sort(files->begin(), files->end());314return true;315}316317#if PPSSPP_PLATFORM(WINDOWS)318// Returns a vector with the device names319std::vector<std::string> GetWindowsDrives()320{321std::vector<std::string> drives;322323#if PPSSPP_PLATFORM(UWP)324DWORD logicaldrives = GetLogicalDrives();325for (int i = 0; i < 26; i++)326{327if (logicaldrives & (1 << i))328{329CHAR driveName[] = { (CHAR)(TEXT('A') + i), TEXT(':'), TEXT('\\'), TEXT('\0') };330std::string str(driveName);331drives.push_back(driveName);332}333}334return drives;335#else336const DWORD buffsize = GetLogicalDriveStrings(0, NULL);337std::vector<wchar_t> buff(buffsize);338if (GetLogicalDriveStrings(buffsize, buff.data()) == buffsize - 1)339{340auto drive = buff.data();341while (*drive)342{343std::string str(ConvertWStringToUTF8(drive));344str.pop_back(); // we don't want the final backslash345str += "/";346drives.push_back(str);347348// advance to next drive349while (*drive++) {}350}351}352return drives;353#endif // PPSSPP_PLATFORM(UWP)354}355#endif // PPSSPP_PLATFORM(WINDOWS)356357} // namespace File358359360