dlangide/workspaces/sample1/sampleproject2/source/exlib/files.d

341 lines
9.2 KiB
D

// Written in the D programming language.
/**
This module contains cross-platform file access utilities
Synopsis:
----
import exlib.files;
----
Copyright: Vadim Lopatin, 2014
License: Boost License 1.0
Authors: Vadim Lopatin, coolreader.org@gmail.com
*/
module dlangui.core.files;
import std.algorithm;
private import dlangui.core.logger;
private import std.process;
private import std.path;
private import std.file;
private import std.utf;
version (Windows) {
/// path delimiter (\ for windows, / for others)
immutable char PATH_DELIMITER = '\\';
} else {
/// path delimiter (\ for windows, / for others)
immutable char PATH_DELIMITER = '/';
}
/// Filesystem root entry / bookmark types
enum RootEntryType : uint {
/// filesystem root
ROOT,
/// current user home
HOME,
/// removable drive
REMOVABLE,
/// fixed drive
FIXED,
/// network
NETWORK,
/// cd rom
CDROM,
/// sd card
SDCARD,
/// custom bookmark
BOOKMARK,
}
/// Filesystem root entry item
struct RootEntry {
private RootEntryType _type;
private string _path;
private dstring _display;
this(RootEntryType type, string path, dstring display = null) {
_type = type;
_path = path;
_display = display;
if (display is null) {
_display = toUTF32(baseName(path));
}
}
/// Returns type
@property RootEntryType type() { return _type; }
/// Returns path
@property string path() { return _path; }
/// Returns display label
@property dstring label() { return _display; }
/// Returns icon resource id
@property string icon() {
switch (type) {
case RootEntryType.NETWORK:
return "folder-network";
case RootEntryType.BOOKMARK:
return "folder-bookmark";
case RootEntryType.CDROM:
return "drive-optical";
case RootEntryType.FIXED:
return "drive-harddisk";
case RootEntryType.HOME:
return "user-home";
case RootEntryType.ROOT:
return "computer";
case RootEntryType.SDCARD:
return "media-flash-sd-mmc";
case RootEntryType.REMOVABLE:
return "device-removable-media";
default:
return "folder-blue";
}
}
}
/// Returns
@property RootEntry homeEntry() {
return RootEntry(RootEntryType.HOME, homePath);
}
/// returns array of system root entries
@property RootEntry[] getRootPaths() {
RootEntry[] res;
res ~= RootEntry(RootEntryType.HOME, homePath);
version (posix) {
res ~= RootEntry(RootEntryType.ROOT, "/", "File System"d);
}
version (Windows) {
import win32.windows;
uint mask = GetLogicalDrives();
for (int i = 0; i < 26; i++) {
if (mask & (1 << i)) {
char letter = cast(char)('A' + i);
string path = "" ~ letter ~ ":\\";
dstring display = ""d ~ letter ~ ":"d;
// detect drive type
RootEntryType type;
uint wtype = GetDriveTypeA(("" ~ path).ptr);
//Log.d("Drive ", path, " type ", wtype);
switch (wtype) {
case DRIVE_REMOVABLE:
type = RootEntryType.REMOVABLE;
break;
case DRIVE_REMOTE:
type = RootEntryType.NETWORK;
break;
case DRIVE_CDROM:
type = RootEntryType.CDROM;
break;
default:
type = RootEntryType.FIXED;
break;
}
res ~= RootEntry(type, path, display);
}
}
}
return res;
}
/// returns true if directory is root directory (e.g. / or C:\)
bool isRoot(string path) {
string root = rootName(path);
if (path.equal(root))
return true;
return false;
}
/// returns parent directory for specified path
string parentDir(string path) {
return buildNormalizedPath(path, "..");
}
/// check filename with pattern (currently only *.ext pattern is supported)
bool filterFilename(string filename, string pattern) {
if (pattern.equal("*.*"))
return true; // matches any
if (pattern.length < 3)
return false;
if (pattern[0] != '*' || pattern[1] != '.')
return false;
return filename.endsWith(pattern[1..$]);
}
/// Filters file name by pattern list
bool filterFilename(string filename, string[] filters) {
if (filters.length == 0)
return true; // no filters - show all
foreach(pattern; filters) {
if (filterFilename(filename, pattern))
return true;
}
return false;
}
/** List directory content
Optionally filters file names by filter.
Result will be placed into entries array.
Returns true if directory exists and listed successfully, false otherwise.
*/
bool listDirectory(string dir, bool includeDirs, bool includeFiles, bool showHiddenFiles, string[] filters, ref DirEntry[] entries) {
entries.length = 0;
if (!isDir(dir)) {
return false;
}
if (!isRoot(dir) && includeDirs) {
entries ~= DirEntry(appendPath(dir, ".."));
}
try {
DirEntry[] dirs;
DirEntry[] files;
foreach (DirEntry e; dirEntries(dir, SpanMode.shallow)) {
string fn = baseName(e.name);
if (!showHiddenFiles && fn.startsWith("."))
continue;
if (e.isDir) {
dirs ~= e;
} else if (e.isFile) {
files ~= e;
}
}
if (includeDirs)
foreach(DirEntry e; dirs)
entries ~= e;
if (includeFiles)
foreach(DirEntry e; files)
if (filterFilename(e.name, filters))
entries ~= e;
return true;
} catch (FileException e) {
return false;
}
}
/** Returns true if char ch is / or \ slash */
bool isPathDelimiter(char ch) {
return ch == '/' || ch == '\\';
}
/// Returns current directory
@property string currentDir() {
return getcwd();
}
/** Returns current executable path only, including last path delimiter - removes executable name from result of std.file.thisExePath() */
@property string exePath() {
string path = thisExePath();
int lastSlash = 0;
for (int i = 0; i < path.length; i++)
if (path[i] == PATH_DELIMITER)
lastSlash = i;
return path[0 .. lastSlash + 1];
}
/// Returns user's home directory
@property string homePath() {
string path;
version (Windows) {
path = environment.get("USERPROFILE");
if (path is null)
path = environment.get("HOME");
} else {
path = environment.get("HOME");
}
if (path is null)
path = "."; // fallback to current directory
return path;
}
/**
Returns application data directory
On unix, it will return path to subdirectory in home directory - e.g. /home/user/.subdir if ".subdir" is passed as a paramter.
On windows, it will return path to subdir in APPDATA directory - e.g. C:\Users\User\AppData\Roaming\.subdir.
*/
string appDataPath(string subdir = null) {
string path;
version (Windows) {
path = environment.get("APPDATA");
}
if (path is null)
path = homePath;
if (subdir !is null) {
path ~= PATH_DELIMITER;
path ~= subdir;
}
return path;
}
/// Converts path delimiters to standard for platform inplace in buffer(e.g. / to \ on windows, \ to / on posix), returns buf
char[] convertPathDelimiters(char[] buf) {
foreach(ref ch; buf) {
version (Windows) {
if (ch == '/')
ch = '\\';
} else {
if (ch == '\\')
ch = '/';
}
}
return buf;
}
/** Converts path delimiters to standard for platform (e.g. / to \ on windows, \ to / on posix) */
string convertPathDelimiters(string src) {
char[] buf = src.dup;
return cast(string)convertPathDelimiters(buf);
}
/** Appends file path parts with proper delimiters e.g. appendPath("/home/user", ".myapp", "config") => "/home/user/.myapp/config" */
string appendPath(string[] pathItems ...) {
char[] buf;
foreach (s; pathItems) {
if (buf.length && !isPathDelimiter(buf[$-1]))
buf ~= PATH_DELIMITER;
buf ~= s;
}
return convertPathDelimiters(buf).dup;
}
/** Appends file path parts with proper delimiters (as well converts delimiters inside path to system) to buffer e.g. appendPath("/home/user", ".myapp", "config") => "/home/user/.myapp/config" */
char[] appendPath(char[] buf, string[] pathItems ...) {
foreach (s; pathItems) {
if (buf.length && !isPathDelimiter(buf[$-1]))
buf ~= PATH_DELIMITER;
buf ~= s;
}
return convertPathDelimiters(buf);
}
/** Split path into elements, e.g. /home/user/dir1 -> ["home", "user", "dir1"], "c:\dir1\dir2" -> ["c:", "dir1", "dir2"] */
string[] splitPath(string path) {
string[] res;
int start = 0;
for (int i = 0; i <= path.length; i++) {
char ch = i < path.length ? path[i] : 0;
if (ch == '\\' || ch == '/' || ch == 0) {
if (start < i)
res ~= path[start .. i].dup;
start = i + 1;
}
}
return res;
}