Merge pull request #362 from FreeSlave/files_and_filedialog

Fix crash when listDirectory can't access parent path. Add listDirect…
This commit is contained in:
Vadim Lopatin 2017-06-05 12:24:01 +03:00 committed by GitHub
commit 420118d885
3 changed files with 186 additions and 90 deletions

View File

@ -131,29 +131,36 @@ version(OSX) {} else version(Posix)
}
return false;
}
private string getDeviceLabelFallback(in char[] type, in char[] fsName)
private string getDeviceLabelFallback(in char[] type, in char[] fsName, in char[] mountDir)
{
import std.format : format;
import std.string : startsWith;
if (type == "vboxsf") {
return "VirtualBox shared folder";
}
if (fsName.startsWith("gvfsd")) {
return "GNOME virtual file system";
if (type == "fuse.gvfsd-fuse") {
return "GNOME Virtual file system";
}
return format("%s volume", type);
return format("%s (%s)", mountDir.baseName, type);
}
private RootEntryType getDeviceRootEntryType(in char[] type)
{
if (type == "iso9660") {
return RootEntryType.CDROM;
}
if (type == "vfat") {
return RootEntryType.REMOVABLE;
}
return RootEntryType.FIXED;
switch(type)
{
case "iso9660":
return RootEntryType.CDROM;
case "vfat":
return RootEntryType.REMOVABLE;
case "cifs":
case "davfs":
case "fuse.sshfs":
case "nfs":
case "nfs4":
return RootEntryType.NETWORK;
default:
return RootEntryType.FIXED;
}
}
}
@ -247,6 +254,7 @@ private:
// do nothing
} else version(linux) {
import std.string : fromStringz;
import std.exception : collectException;
mntent ent;
char[1024] buf;
@ -267,22 +275,25 @@ private:
string label;
enum byLabel = "/dev/disk/by-label";
try {
foreach(entry; dirEntries(byLabel, SpanMode.shallow))
{
if (entry.isSymlink) {
auto normalized = buildNormalizedPath(byLabel, entry.readLink);
if (normalized == fsName) {
label = entry.name.baseName.unescapeLabel();
if (fsName.isAbsolute) {
try {
foreach(entry; dirEntries(byLabel, SpanMode.shallow))
{
string resolvedLink;
if (entry.isSymlink && collectException(entry.readLink, resolvedLink) is null) {
auto normalized = buildNormalizedPath(byLabel, resolvedLink);
if (normalized == fsName) {
label = entry.name.baseName.unescapeLabel();
}
}
}
} catch(Exception e) {
}
} catch(Exception e) {
}
if (!label.length) {
label = getDeviceLabelFallback(type, fsName);
label = getDeviceLabelFallback(type, fsName, mountDir);
}
auto entryType = getDeviceRootEntryType(type);
res ~= RootEntry(entryType, mountDir.idup, label.toUTF32);
@ -307,7 +318,7 @@ private:
continue;
}
string label = getDeviceLabelFallback(type, fsName);
string label = getDeviceLabelFallback(type, fsName, mountDir);
res ~= RootEntry(getDeviceRootEntryType(type), mountDir.idup, label.toUTF32);
}
}
@ -515,6 +526,7 @@ bool isHidden(in string path) nothrow {
return false;
}
} else version(Posix) {
//TODO: check for hidden attribute on macOS
return path.baseName.startsWith(".");
} else {
return false;
@ -530,6 +542,49 @@ unittest
}
}
private bool isReadable(in string filePath) nothrow
{
version(Posix) {
import core.sys.posix.unistd : access, R_OK;
import std.string : toStringz;
return access(toStringz(filePath), R_OK) == 0;
} else {
// TODO: Windows version
return true;
}
}
private bool isWritable(in string filePath) nothrow
{
version(Posix) {
import core.sys.posix.unistd : access, W_OK;
import std.string : toStringz;
return access(toStringz(filePath), W_OK) == 0;
} else {
// TODO: Windows version
return true;
}
}
private bool isExecutable(in string filePath) nothrow
{
version(Windows) {
//TODO: Use GetEffectiveRightsFromAclW? For now just check extension
string extension = filePath.extension;
foreach(ext; [".exe", ".com", ".bat", ".cmd"]) {
if (filenameCmp(extension, ext) == 0)
return true;
}
return false;
} else version(Posix) {
import core.sys.posix.unistd : access, X_OK;
import std.string : toStringz;
return access(toStringz(filePath), X_OK) == 0;
} else {
return false;
}
}
/// returns parent directory for specified path
string parentDir(in string path) pure nothrow {
return buildNormalizedPath(path, "..");
@ -550,71 +605,92 @@ bool filterFilename(in string filename, in string[] filters) pure nothrow {
return false;
}
/** List directory content
Optionally filters file names by filter.
enum AttrFilter
{
none = 0,
files = 1 << 0, /// Include regular files that match the filters.
dirs = 1 << 1, /// Include directories.
hidden = 1 << 2, /// Include hidden files and directoroies.
parent = 1 << 3, /// Include parent directory (..). Takes effect only with includeDirs.
thisDir = 1 << 4, /// Include this directory (.). Takes effect only with includeDirs.
special = 1 << 5, /// Include special files (On Unix: socket and device files, FIFO) that match the filters.
readable = 1 << 6, /// Listing only readable files and directories.
writable = 1 << 7, /// Listing only writable files and directories.
executable = 1 << 8, /// Include only executable files. This filter does not affect directories.
allVisible = AttrFilter.files | AttrFilter.dirs, /// Include all non-hidden files and directories without parent directory, this directory and special files.
all = AttrFilter.allVisible | AttrFilter.hidden /// Include all files and directories including hidden ones but without parent directory, this directory and special files.
}
/** List directory content
Optionally filters file names by filter (not applied to directories).
Returns true if directory exists and listed successfully, false otherwise.
Throws: Exception if $(D dir) is not directory or some error occured during directory listing.
*/
DirEntry[] listDirectory(in string dir, AttrFilter attrFilter = AttrFilter.all, in string[] filters = [])
{
DirEntry[] entries;
DirEntry[] dirs;
DirEntry[] files;
foreach (DirEntry e; dirEntries(dir, SpanMode.shallow)) {
if (!(attrFilter & AttrFilter.hidden) && e.name.isHidden())
continue;
if ((attrFilter & AttrFilter.readable) && !e.name.isReadable())
continue;
if ((attrFilter & AttrFilter.writable) && !e.name.isWritable())
continue;
if (!e.isDir && (attrFilter & AttrFilter.executable) && !e.name.isExecutable())
continue;
if (e.isDir && (attrFilter & AttrFilter.dirs)) {
dirs ~= e;
} else if ((attrFilter & AttrFilter.files) && filterFilename(e.name, filters)) {
if (e.isFile) {
files ~= e;
} else if (attrFilter & AttrFilter.special) {
files ~= e;
}
}
}
if ((attrFilter & AttrFilter.dirs) && (attrFilter & AttrFilter.thisDir) ) {
entries ~= DirEntry(appendPath(dir, ".")) ~ entries;
}
if (!isRoot(dir) && (attrFilter & AttrFilter.dirs) && (attrFilter & AttrFilter.parent)) {
entries ~= DirEntry(appendPath(dir, ".."));
}
dirs.sort!((a,b) => filenameCmp!(std.path.CaseSensitive.no)(a.name,b.name) < 0);
files.sort!((a,b) => filenameCmp!(std.path.CaseSensitive.no)(a.name,b.name) < 0);
entries ~= dirs;
entries ~= files;
return entries;
}
/** List directory content
Optionally filters file names by filter (not applied to directories).
Result will be placed into entries array.
Returns true if directory exists and listed successfully, false otherwise.
*/
bool listDirectory(in string dir, in bool includeDirs, in bool includeFiles, in bool showHiddenFiles, in string[] filters, ref DirEntry[] entries, in bool showExecutables = false) {
deprecated bool listDirectory(in string dir, in bool includeDirs, in bool includeFiles, in bool showHiddenFiles, in string[] filters, ref DirEntry[] entries, in bool showExecutables = false) {
entries.length = 0;
AttrFilter attrFilter;
if (includeDirs) {
attrFilter |= AttrFilter.dirs;
attrFilter |= AttrFilter.parent;
}
if (includeFiles)
attrFilter |= AttrFilter.files;
if (showHiddenFiles)
attrFilter |= AttrFilter.hidden;
if (showExecutables)
attrFilter |= AttrFilter.executable;
import std.exception : collectException;
bool dirExists;
collectException(dir.isDir, dirExists);
if (!dirExists) {
return false;
}
if (!isRoot(dir) && includeDirs) {
entries ~= DirEntry(appendPath(dir, ".."));
}
try {
DirEntry[] dirs;
DirEntry[] files;
foreach (DirEntry e; dirEntries(dir, SpanMode.shallow)) {
if (!showHiddenFiles && e.name.isHidden())
continue;
if (e.isDir) {
dirs ~= e;
} else if (e.isFile) {
files ~= e;
}
}
dirs.sort!((a,b) => filenameCmp!(std.path.CaseSensitive.no)(a.name,b.name) < 0);
files.sort!((a,b) => filenameCmp!(std.path.CaseSensitive.no)(a.name,b.name) < 0);
if (includeDirs)
foreach(DirEntry e; dirs)
entries ~= e;
if (includeFiles)
foreach(DirEntry e; files) {
bool passed = false;
if (showExecutables) {
uint attr_mask = (1 << 0) | (1 << 3) | (1 << 6);
version(Windows) {
passed = e.name.endsWith(".exe") || e.name.endsWith(".EXE")
|| e.name.endsWith(".cmd") || e.name.endsWith(".CMD")
|| e.name.endsWith(".bat") || e.name.endsWith(".BAT");
} else version (Posix) {
// execute permission for others
passed = (e.attributes & attr_mask) != 0;
} else version(OSX) {
passed = (e.attributes & attr_mask) != 0;
}
} else {
passed = filterFilename(e.name, filters);
}
if (passed)
entries ~= e;
}
return true;
} catch (FileException e) {
return false;
}
return collectException(listDirectory(dir, attrFilter, filters), entries) is null;
}
/// Returns true if char ch is / or \ slash

View File

@ -256,9 +256,21 @@ class FileDialog : Dialog, CustomGridCellAdapter {
protected bool openDirectory(string dir, string selectedItemPath) {
dir = buildNormalizedPath(dir);
Log.d("FileDialog.openDirectory(", dir, ")");
_fileList.rows = 0;
if (!listDirectory(dir, true, true, _showHiddenFiles, selectedFilter, _entries, executableFilterSelected))
DirEntry[] entries;
auto attrFilter = (showHiddenFiles ? AttrFilter.all : AttrFilter.allVisible) | AttrFilter.special | AttrFilter.parent;
if (executableFilterSelected()) {
attrFilter |= AttrFilter.executable;
}
try {
_entries = listDirectory(dir, attrFilter, selectedFilter());
} catch(Exception e) {
import dlangui.dialogs.msgbox;
auto msgBox = new MessageBox(UIString("Error"d), UIString(e.msg.toUTF32), window());
msgBox.show();
return false;
}
_fileList.rows = 0;
_path = dir;
_isRoot = isRoot(dir);
_edPath.path = _path; //toUTF32(_path);
@ -689,8 +701,12 @@ class FilePathPanelItem : HorizontalLayout {
// show popup menu with subdirs
string[] filters;
DirEntry[] entries;
if (!listDirectory(_path, true, false, false, filters, entries))
try {
AttrFilter attrFilter = AttrFilter.dirs | AttrFilter.parent;
entries = listDirectory(_path, attrFilter);
} catch(Exception e) {
return false;
}
if (entries.length == 0)
return false;
MenuItem dirs = new MenuItem();
@ -699,10 +715,11 @@ class FilePathPanelItem : HorizontalLayout {
string fullPath = e.name;
string d = baseName(fullPath);
Action a = new Action(itemId++, toUTF32(d));
a.stringParam = fullPath;
MenuItem item = new MenuItem(a);
item.menuItemClick = delegate(MenuItem item) {
item.menuItemAction = delegate(const Action action) {
if (onPathSelectionListener.assigned)
return onPathSelectionListener(fullPath);
return onPathSelectionListener(action.stringParam);
return false;
};
dirs.add(item);

View File

@ -113,8 +113,11 @@ version (Windows) {
import dlangui.core.files;
import std.file : DirEntry;
DirEntry[] entries;
if (!listDirectory(dir, false, true, true, ["*.ttf"], entries))
return null;
try {
entries = listDirectory(dir, AttrFilter.files, ["*.ttf"]);
} catch(Exception e) {
return null;
}
string[] res;
foreach(entry; entries) {