You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libfm-qt-packaging/src/core/fileinfo.cpp

379 lines
13 KiB

#include "fileinfo.h"
#include "fileinfo_p.h"
#include <gio/gio.h>
namespace Fm {
const char gfile_info_query_attribs[] = "standard::*,"
"unix::*,"
"time::*,"
"access::*,"
"id::filesystem,"
"metadata::emblems";
FileInfo::FileInfo() {
// FIXME: initialize numeric data members
}
FileInfo::FileInfo(const GFileInfoPtr& inf, const FilePath& parentDirPath) {
setFromGFileInfo(inf, parentDirPath);
}
FileInfo::~FileInfo() {
}
void FileInfo::setFromGFileInfo(const GObjectPtr<GFileInfo>& inf, const FilePath& parentDirPath) {
dirPath_ = parentDirPath;
const char* tmp, *uri;
GIcon* gicon;
GFileType type;
name_ = g_file_info_get_name(inf.get());
dispName_ = g_file_info_get_display_name(inf.get());
size_ = g_file_info_get_size(inf.get());
tmp = g_file_info_get_content_type(inf.get());
if(!tmp) {
tmp = "application/octet-stream";
}
mimeType_ = MimeType::fromName(tmp);
mode_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_MODE);
uid_ = gid_ = -1;
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_UNIX_UID)) {
uid_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_UID);
}
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_UNIX_GID)) {
gid_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_GID);
}
type = g_file_info_get_file_type(inf.get());
if(0 == mode_) { /* if UNIX file mode is not available, compose a fake one. */
switch(type) {
case G_FILE_TYPE_REGULAR:
mode_ |= S_IFREG;
break;
case G_FILE_TYPE_DIRECTORY:
mode_ |= S_IFDIR;
break;
case G_FILE_TYPE_SYMBOLIC_LINK:
mode_ |= S_IFLNK;
break;
case G_FILE_TYPE_SHORTCUT:
break;
case G_FILE_TYPE_MOUNTABLE:
break;
case G_FILE_TYPE_SPECIAL:
if(mode_) {
break;
}
/* if it's a special file but it doesn't have UNIX mode, compose a fake one. */
if(strcmp(tmp, "inode/chardevice") == 0) {
mode_ |= S_IFCHR;
}
else if(strcmp(tmp, "inode/blockdevice") == 0) {
mode_ |= S_IFBLK;
}
else if(strcmp(tmp, "inode/fifo") == 0) {
mode_ |= S_IFIFO;
}
#ifdef S_IFSOCK
else if(strcmp(tmp, "inode/socket") == 0) {
mode_ |= S_IFSOCK;
}
#endif
break;
case G_FILE_TYPE_UNKNOWN:
;
}
}
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
isAccessible_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
}
else
/* assume it's accessible */
{
isAccessible_ = true;
}
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
isWritable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
}
else
/* assume it's writable */
{
isWritable_ = true;
}
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) {
isDeletable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
}
else
/* assume it's deletable */
{
isDeletable_ = true;
}
/* special handling for symlinks */
if(g_file_info_get_is_symlink(inf.get())) {
mode_ &= ~S_IFMT; /* reset type */
mode_ |= S_IFLNK; /* set type to symlink */
goto _file_is_symlink;
}
isShortcut_ = false;
switch(type) {
case G_FILE_TYPE_SHORTCUT:
isShortcut_ = true;
case G_FILE_TYPE_MOUNTABLE:
uri = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if(uri) {
if(g_str_has_prefix(uri, "file:///")) {
auto filename = CStrPtr{g_filename_from_uri(uri, nullptr, nullptr)};
target_ = filename.get();
}
else {
target_ = uri;
}
if(!mimeType_) {
mimeType_ = MimeType::guessFromFileName(target_.c_str());
}
}
/* if the mime-type is not determined or is unknown */
if(G_UNLIKELY(!mimeType_ || mimeType_->isUnknownType())) {
/* FIXME: is this appropriate? */
if(type == G_FILE_TYPE_SHORTCUT) {
mimeType_ = MimeType::inodeShortcut();
}
else {
mimeType_ = MimeType::inodeMountPoint();
}
}
break;
case G_FILE_TYPE_DIRECTORY:
if(!mimeType_) {
mimeType_ = MimeType::inodeDirectory();
}
isReadOnly_ = false; /* default is R/W */
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_READONLY)) {
isReadOnly_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
}
/* directories should be writable to be deleted by user */
if(isReadOnly_ || !isWritable_) {
isDeletable_ = false;
}
break;
case G_FILE_TYPE_SYMBOLIC_LINK:
_file_is_symlink:
uri = g_file_info_get_symlink_target(inf.get());
if(uri) {
if(g_str_has_prefix(uri, "file:///")) {
auto filename = CStrPtr{g_filename_from_uri(uri, nullptr, nullptr)};
target_ = filename.get();
}
else {
target_ = uri;
}
if(!mimeType_) {
mimeType_ = MimeType::guessFromFileName(target_.c_str());
}
}
/* continue with absent mime type */
default: /* G_FILE_TYPE_UNKNOWN G_FILE_TYPE_REGULAR G_FILE_TYPE_SPECIAL */
if(G_UNLIKELY(!mimeType_)) {
if(!mimeType_) {
mimeType_ = MimeType::guessFromFileName(name_.c_str());
}
}
}
/* if there is a custom folder icon, use it */
if(isNative() && type == G_FILE_TYPE_DIRECTORY) {
auto local_path = path().localPath();
auto dot_dir = CStrPtr{g_build_filename(local_path.get(), ".directory", nullptr)};
if(g_file_test(dot_dir.get(), G_FILE_TEST_IS_REGULAR)) {
GKeyFile* kf = g_key_file_new();
if(g_key_file_load_from_file(kf, dot_dir.get(), G_KEY_FILE_NONE, nullptr)) {
CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)};
if(icon_name) {
auto dot_icon = IconInfo::fromName(icon_name.get());
if(dot_icon && dot_icon->isValid()) {
icon_ = dot_icon;
}
}
}
g_key_file_free(kf);
}
}
if(!icon_) {
/* try file-specific icon first */
gicon = g_file_info_get_icon(inf.get());
if(gicon) {
icon_ = IconInfo::fromGIcon(gicon);
}
}
#if 0
/* set "locked" icon on unaccesible folder */
else if(!accessible && type == G_FILE_TYPE_DIRECTORY) {
icon = g_object_ref(icon_locked_folder);
}
else {
icon = g_object_ref(fm_mime_type_get_icon(mime_type));
}
#endif
/* if the file has emblems, add them to the icon */
auto emblem_names = g_file_info_get_attribute_stringv(inf.get(), "metadata::emblems");
if(emblem_names) {
auto n_emblems = g_strv_length(emblem_names);
for(int i = n_emblems - 1; i >= 0; --i) {
emblems_.emplace_front(Fm::IconInfo::fromName(emblem_names[i]));
}
}
tmp = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_ID_FILESYSTEM);
filesystemId_ = g_intern_string(tmp);
mtime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_MODIFIED);
atime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_ACCESS);
ctime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_CHANGED);
isHidden_ = g_file_info_get_is_hidden(inf.get());
isBackup_ = g_file_info_get_is_backup(inf.get());
isNameChangeable_ = true; /* GVFS tends to ignore this attribute */
isIconChangeable_ = isHiddenChangeable_ = false;
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) {
isNameChangeable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME);
}
// special handling for desktop entry files (show the name and icon defined in the desktop entry instead)
if(isNative() && G_UNLIKELY(isDesktopEntry())) {
auto local_path = path().localPath();
GKeyFile* kf = g_key_file_new();
if(g_key_file_load_from_file(kf, local_path.get(), G_KEY_FILE_NONE, nullptr)) {
/* check if type is correct and supported */
CStrPtr type{g_key_file_get_string(kf, "Desktop Entry", "Type", nullptr)};
if(type) {
// Type == "Link"
if(strcmp(type.get(), G_KEY_FILE_DESKTOP_TYPE_LINK) == 0) {
CStrPtr uri{g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, nullptr)};
if(uri) {
isShortcut_ = true;
target_ = uri.get();
}
}
}
CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)};
if(icon_name) {
icon_ = IconInfo::fromName(icon_name.get());
}
/* Use title of the desktop entry for display */
CStrPtr displayName{g_key_file_get_locale_string(kf, "Desktop Entry", "Name", nullptr, nullptr)};
if(displayName) {
dispName_ = displayName.get();
}
/* handle 'Hidden' key to set hidden attribute */
if(!isHidden_) {
isHidden_ = g_key_file_get_boolean(kf, "Desktop Entry", "Hidden", nullptr);
}
}
g_key_file_free(kf);
}
if(!icon_ && mimeType_)
icon_ = mimeType_->icon();
#if 0
GFile* _gf = nullptr;
GFileAttributeInfoList* list;
auto list = g_file_query_settable_attributes(gf, nullptr, nullptr);
if(G_LIKELY(list)) {
if(g_file_attribute_info_list_lookup(list, G_FILE_ATTRIBUTE_STANDARD_ICON)) {
icon_is_changeable = true;
}
if(g_file_attribute_info_list_lookup(list, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)) {
hidden_is_changeable = true;
}
g_file_attribute_info_list_unref(list);
}
if(G_UNLIKELY(_gf)) {
g_object_unref(_gf);
}
#endif
}
void FileInfo::bindCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet) {
cutFilesHashSet_ = cutFilesHashSet;
}
bool FileInfo::canThumbnail() const {
/* We cannot use S_ISREG here as this exclude all symlinks */
if(size_ == 0 || /* don't generate thumbnails for empty files */
!(mode_ & S_IFREG) ||
isDesktopEntry() ||
isUnknownType()) {
return false;
}
return true;
}
/* full path of the file is required by this function */
bool FileInfo::isExecutableType() const {
if(isText()) { /* g_content_type_can_be_executable reports text files as executables too */
/* We don't execute remote files nor files in trash */
if(isNative() && (mode_ & (S_IXOTH | S_IXGRP | S_IXUSR))) {
/* it has executable bits so lets check shell-bang */
auto pathStr = path().toString();
int fd = open(pathStr.get(), O_RDONLY);
if(fd >= 0) {
char buf[2];
ssize_t rdlen = read(fd, &buf, 2);
close(fd);
if(rdlen == 2 && buf[0] == '#' && buf[1] == '!') {
return true;
}
}
}
return false;
}
return mimeType_->canBeExecutable();
}
bool FileInfoList::isSameType() const {
if(!empty()) {
auto& item = front();
for(auto it = cbegin() + 1; it != cend(); ++it) {
auto& item2 = *it;
if(item->mimeType() != item2->mimeType()) {
return false;
}
}
}
return true;
}
bool FileInfoList::isSameFilesystem() const {
if(!empty()) {
auto& item = front();
for(auto it = cbegin() + 1; it != cend(); ++it) {
auto& item2 = *it;
if(item->filesystemId() != item2->filesystemId()) {
return false;
}
}
}
return true;
}
} // namespace Fm