/* nautilus-file-utilities.c - implementation of file manipulation routines.
*
* Copyright (C) 1999, 2000, 2001 Eazel, Inc.
*
* The Gnome Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The Gnome Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Gnome Library; see the file COPYING.LIB. If not,
* see .
*
* Authors: John Sullivan
*/
#include
#include "nautilus-file-utilities.h"
#include "nautilus-application.h"
#include "nautilus-file.h"
#include "nautilus-file-operations.h"
#include "nautilus-filename-utilities.h"
#include "nautilus-global-preferences.h"
#include "nautilus-icon-names.h"
#include "nautilus-metadata.h"
#include "nautilus-network-directory.h"
#include "nautilus-scheme.h"
#include "nautilus-search-directory.h"
#include "nautilus-starred-directory.h"
#include "nautilus-ui-utilities.h"
#include
#include
#include
#include
#include
#include
#include
#include
#define NAUTILUS_USER_DIRECTORY_NAME "nautilus"
#define DEFAULT_NAUTILUS_DIRECTORY_MODE (0755)
/* From linux/magic.h */
#ifndef AUTOFS_SUPER_MAGIC
#define AUTOFS_SUPER_MAGIC 0x0187
#endif
char *
nautilus_compute_title_for_location (GFile *location)
{
NautilusFile *file;
g_autoptr (GMount) mount = NULL;
char *title;
/* TODO-gio: This doesn't really work all that great if the
* info about the file isn't known atm... */
if (nautilus_is_home_directory (location))
{
return g_strdup (_("Home"));
}
mount = nautilus_get_mounted_mount_for_root (location);
if (mount != NULL)
{
return g_mount_get_name (mount);
}
title = NULL;
if (location)
{
file = nautilus_file_get (location);
title = g_strdup (nautilus_file_get_display_name (file));
nautilus_file_unref (file);
}
if (title == NULL)
{
title = g_file_get_basename (location);
}
return title;
}
/**
* nautilus_get_user_directory:
*
* Get the path for the directory containing nautilus settings.
*
* Return value: the directory path.
**/
char *
nautilus_get_user_directory (void)
{
char *user_directory = NULL;
user_directory = g_build_filename (g_get_user_config_dir (),
NAUTILUS_USER_DIRECTORY_NAME,
NULL);
if (!g_file_test (user_directory, G_FILE_TEST_EXISTS))
{
g_mkdir_with_parents (user_directory, DEFAULT_NAUTILUS_DIRECTORY_MODE);
/* FIXME bugzilla.gnome.org 41286:
* How should we handle the case where this mkdir fails?
* Note that nautilus_application_startup will refuse to launch if this
* directory doesn't get created, so that case is OK. But the directory
* could be deleted after Nautilus was launched, and perhaps
* there is some bad side-effect of not handling that case.
*/
}
return user_directory;
}
/**
* nautilus_get_scripts_directory_path:
*
* Get the path for the directory containing nautilus scripts.
*
* Return value: the directory path containing nautilus scripts
**/
char *
nautilus_get_scripts_directory_path (void)
{
if (nautilus_application_is_sandboxed ())
{
/* g_get_user_data_dir() leads to a different path then expected under
* the flatpak sandbox */
return g_build_filename (g_get_home_dir (), ".local", "share", "nautilus", "scripts", NULL);
}
else
{
return g_build_filename (g_get_user_data_dir (), "nautilus", "scripts", NULL);
}
}
char *
nautilus_get_home_directory_uri (void)
{
return g_filename_to_uri (g_get_home_dir (), NULL, NULL);
}
gboolean
nautilus_should_use_templates_directory (void)
{
const char *templates_dir = g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES);
if (templates_dir == NULL || *templates_dir == '\0')
{
return FALSE;
}
g_autoptr (GFile) templates_location = g_file_new_for_path (templates_dir);
return !nautilus_is_home_directory (templates_location) &&
!nautilus_is_root_directory (templates_location);
}
char *
nautilus_get_templates_directory (void)
{
return g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES));
}
char *
nautilus_get_templates_directory_uri (void)
{
char *directory, *uri;
directory = nautilus_get_templates_directory ();
uri = g_filename_to_uri (directory, NULL, NULL);
g_free (directory);
return uri;
}
gboolean
nautilus_is_home_directory_file (GFile *dir,
const char *filename)
{
char *dirname;
static GFile *home_dir_dir = NULL;
static char *home_dir_filename = NULL;
if (home_dir_dir == NULL)
{
dirname = g_path_get_dirname (g_get_home_dir ());
home_dir_dir = g_file_new_for_path (dirname);
g_free (dirname);
home_dir_filename = g_path_get_basename (g_get_home_dir ());
}
return (g_file_equal (dir, home_dir_dir) &&
strcmp (filename, home_dir_filename) == 0);
}
gboolean
nautilus_is_home_directory (GFile *dir)
{
static GFile *home_dir = NULL;
if (home_dir == NULL)
{
home_dir = g_file_new_for_path (g_get_home_dir ());
}
return g_file_equal (dir, home_dir);
}
gboolean
nautilus_is_root_directory (GFile *dir)
{
static GFile *root_dir = NULL;
if (root_dir == NULL)
{
root_dir = g_file_new_for_path ("/");
}
return g_file_equal (dir, root_dir);
}
gboolean
nautilus_is_root_for_scheme (GFile *dir,
const char *scheme)
{
return (g_file_has_uri_scheme (dir, scheme) &&
!g_file_has_parent (dir, NULL));
}
typedef struct
{
EmptyCheckCallback callback;
gpointer callback_data;
} EmptyCheck;
static void
directory_file_info_callback (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autofree EmptyCheck *data = user_data;
GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object);
g_autoptr (GError) error = NULL;
g_autolist (GFile) files = g_file_enumerator_next_files_finish (enumerator,
res, &error);
/* Consider directory empty when no files can be listed */
gboolean is_empty = (error != NULL || files == NULL);
data->callback (data->callback_data, is_empty);
}
static void
directory_empty_check_callback (GFile *directory,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) error = NULL;
g_autoptr (GFileEnumerator) enumerator = g_file_enumerate_children_finish (directory,
res, &error);
if (error != NULL)
{
g_autofree EmptyCheck *data = user_data;
/* Consider non-existing directories empty */
data->callback (data->callback_data, TRUE);
}
else
{
g_file_enumerator_next_files_async (enumerator,
1, /* one item is enough for empty test */
G_PRIORITY_DEFAULT,
NULL,
directory_file_info_callback,
user_data);
}
}
void
nautilus_is_directory_empty (GFile *directory,
EmptyCheckCallback callback,
gpointer callback_data)
{
EmptyCheck *data = g_new0 (EmptyCheck, 1);
data->callback = callback;
data->callback_data = callback_data;
g_file_enumerate_children_async (directory, G_FILE_ATTRIBUTE_STANDARD_NAME, 0,
G_PRIORITY_DEFAULT, NULL,
(GAsyncReadyCallback) directory_empty_check_callback, data);
}
GMount *
nautilus_get_mounted_mount_for_root (GFile *location)
{
g_autoptr (GVolumeMonitor) volume_monitor = NULL;
GList *mounts;
GList *l;
GMount *mount;
GMount *result = NULL;
volume_monitor = g_volume_monitor_get ();
mounts = g_volume_monitor_get_mounts (volume_monitor);
for (l = mounts; l != NULL; l = l->next)
{
g_autoptr (GFile) root = NULL;
mount = l->data;
if (g_mount_is_shadowed (mount))
{
continue;
}
root = g_mount_get_root (mount);
if (g_file_equal (location, root))
{
result = g_object_ref (mount);
break;
}
}
g_list_free_full (mounts, g_object_unref);
return result;
}
GFile *
nautilus_generate_unique_file_in_directory (GFile *directory,
const char *basename,
gboolean ignore_extension)
{
g_return_val_if_fail (directory != NULL, NULL);
g_return_val_if_fail (basename != NULL, NULL);
g_return_val_if_fail (g_file_query_exists (directory, NULL), NULL);
GFile *child = g_file_get_child (directory, basename);
for (size_t counter = 1; g_file_query_exists (child, NULL); counter += 1)
{
g_autofree char *filename = nautilus_filename_for_conflict (basename,
counter,
-1,
ignore_extension);
g_object_unref (child);
child = g_file_get_child (directory, filename);
}
return child;
}
GFile *
nautilus_find_existing_uri_in_hierarchy (GFile *location)
{
GFile *tmp;
g_assert (location != NULL);
location = g_object_ref (location);
while (location != NULL)
{
g_autoptr (GFileInfo) info = NULL;
info = g_file_query_info (location,
G_FILE_ATTRIBUTE_STANDARD_NAME,
G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (info != NULL)
{
return location;
}
tmp = location;
location = g_file_get_parent (location);
g_object_unref (tmp);
}
return location;
}
static gboolean
have_program_in_path (const char *name)
{
gchar *path;
gboolean result;
path = g_find_program_in_path (name);
result = (path != NULL);
g_free (path);
return result;
}
static GIcon *
special_directory_get_icon (GUserDirectory directory,
gboolean symbolic)
{
#define ICON_CASE(x) \
case G_USER_DIRECTORY_ ## x: \
return (symbolic) ? g_themed_icon_new (NAUTILUS_ICON_FOLDER_ ## x) : g_themed_icon_new (NAUTILUS_ICON_FULLCOLOR_FOLDER_ ## x);
switch (directory)
{
ICON_CASE (DOCUMENTS);
ICON_CASE (DOWNLOAD);
ICON_CASE (MUSIC);
ICON_CASE (PICTURES);
ICON_CASE (PROJECTS);
ICON_CASE (PUBLIC_SHARE);
ICON_CASE (TEMPLATES);
ICON_CASE (VIDEOS);
default:
{
return (symbolic) ? g_themed_icon_new (NAUTILUS_ICON_FOLDER) : g_themed_icon_new (NAUTILUS_ICON_FULLCOLOR_FOLDER);
}
}
#undef ICON_CASE
}
GIcon *
nautilus_special_directory_get_symbolic_icon (GUserDirectory directory)
{
return special_directory_get_icon (directory, TRUE);
}
GIcon *
nautilus_special_directory_get_icon (GUserDirectory directory)
{
return special_directory_get_icon (directory, FALSE);
}
gboolean
nautilus_is_file_roller_installed (void)
{
static int installed = -1;
if (installed < 0)
{
if (have_program_in_path ("file-roller"))
{
installed = 1;
}
else
{
installed = 0;
}
}
return installed > 0 ? TRUE : FALSE;
}
GHashTable *
nautilus_trashed_files_get_original_directories (GList *files,
GList **unhandled_files)
{
GHashTable *directories;
NautilusFile *file, *original_file, *original_dir;
GList *l, *m;
directories = NULL;
if (unhandled_files != NULL)
{
*unhandled_files = NULL;
}
for (l = files; l != NULL; l = l->next)
{
file = NAUTILUS_FILE (l->data);
original_file = nautilus_file_get_trash_original_file (file);
original_dir = NULL;
if (original_file != NULL)
{
original_dir = nautilus_file_get_parent (original_file);
}
if (original_dir != NULL)
{
if (directories == NULL)
{
directories = g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) nautilus_file_unref,
(GDestroyNotify) nautilus_file_list_free);
}
nautilus_file_ref (original_dir);
m = g_hash_table_lookup (directories, original_dir);
if (m != NULL)
{
g_hash_table_steal (directories, original_dir);
nautilus_file_unref (original_dir);
}
m = g_list_append (m, nautilus_file_ref (file));
g_hash_table_insert (directories, original_dir, m);
}
else if (unhandled_files != NULL)
{
*unhandled_files = g_list_append (*unhandled_files, nautilus_file_ref (file));
}
nautilus_file_unref (original_file);
nautilus_file_unref (original_dir);
}
return directories;
}
GList *
nautilus_file_list_from_uri_list (GList *uris)
{
GList *l;
GList *result = NULL;
for (l = uris; l != NULL; l = l->next)
{
g_autoptr (GFile) location = NULL;
location = g_file_new_for_uri (l->data);
result = g_list_prepend (result, nautilus_file_get (location));
}
return g_list_reverse (result);
}
GList *
nautilus_location_list_from_file_list (GList *files)
{
GList *locations = NULL;
for (GList *l = files; l != NULL; l = l->next)
{
NautilusFile *file = l->data;
locations = g_list_prepend (locations, nautilus_file_get_location (file));
}
return g_list_reverse (locations);
}
typedef struct
{
GHashTable *original_dirs_hash;
GtkWindow *parent_window;
} RestoreFilesData;
static void
ensure_dirs_task_ready_cb (GObject *_source,
GAsyncResult *res,
gpointer user_data)
{
NautilusFile *original_dir;
GFile *original_dir_location;
GList *original_dirs, *files, *locations, *l;
RestoreFilesData *data = user_data;
original_dirs = g_hash_table_get_keys (data->original_dirs_hash);
for (l = original_dirs; l != NULL; l = l->next)
{
original_dir = NAUTILUS_FILE (l->data);
original_dir_location = nautilus_file_get_location (original_dir);
files = g_hash_table_lookup (data->original_dirs_hash, original_dir);
locations = nautilus_location_list_from_file_list (files);
nautilus_file_operations_move_async (locations,
original_dir_location,
data->parent_window,
NULL, NULL, NULL);
g_list_free_full (locations, g_object_unref);
g_object_unref (original_dir_location);
}
g_list_free (original_dirs);
g_hash_table_unref (data->original_dirs_hash);
g_slice_free (RestoreFilesData, data);
}
static void
ensure_dirs_task_thread_func (GTask *task,
gpointer source,
gpointer task_data,
GCancellable *cancellable)
{
RestoreFilesData *data = task_data;
NautilusFile *original_dir;
GFile *original_dir_location;
g_autoptr (GList) original_dirs = g_hash_table_get_keys (data->original_dirs_hash);
for (GList *l = original_dirs; l != NULL; l = l->next)
{
original_dir = NAUTILUS_FILE (l->data);
original_dir_location = nautilus_file_get_location (original_dir);
g_file_make_directory_with_parents (original_dir_location, cancellable, NULL);
g_object_unref (original_dir_location);
}
g_task_return_pointer (task, NULL, NULL);
}
static void
restore_files_ensure_parent_directories (GHashTable *original_dirs_hash,
GtkWindow *parent_window)
{
RestoreFilesData *data;
GTask *ensure_dirs_task;
data = g_slice_new0 (RestoreFilesData);
data->parent_window = parent_window;
data->original_dirs_hash = g_hash_table_ref (original_dirs_hash);
ensure_dirs_task = g_task_new (NULL, NULL, ensure_dirs_task_ready_cb, data);
g_task_set_task_data (ensure_dirs_task, data, NULL);
g_task_run_in_thread (ensure_dirs_task, ensure_dirs_task_thread_func);
g_object_unref (ensure_dirs_task);
}
void
nautilus_restore_files_from_trash (GList *files,
GtkWindow *parent_window)
{
GHashTable *original_dirs_hash;
g_autolist (NautilusFile) unhandled_files = NULL;
original_dirs_hash = nautilus_trashed_files_get_original_directories (files, &unhandled_files);
for (GList *l = unhandled_files; l != NULL; l = l->next)
{
NautilusFile *file = NAUTILUS_FILE (l->data);
g_autofree char *message = g_strdup_printf (_("Could not determine original location of ā%sā "),
nautilus_file_get_display_name (file));
nautilus_show_ok_dialog (message,
_("The item cannot be restored from trash"),
GTK_WIDGET (parent_window));
}
if (original_dirs_hash != NULL)
{
restore_files_ensure_parent_directories (original_dirs_hash, parent_window);
g_hash_table_unref (original_dirs_hash);
}
}
typedef struct
{
NautilusMountGetContent callback;
gpointer user_data;
} GetContentTypesData;
static void
get_types_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GetContentTypesData *data;
char **types;
data = user_data;
types = g_mount_guess_content_type_finish (G_MOUNT (source_object), res, NULL);
g_object_set_data_full (source_object,
"nautilus-content-type-cache",
g_strdupv (types),
(GDestroyNotify) g_strfreev);
if (data->callback)
{
data->callback ((const char **) types, data->user_data);
}
g_strfreev (types);
g_slice_free (GetContentTypesData, data);
}
void
nautilus_get_x_content_types_for_mount_async (GMount *mount,
NautilusMountGetContent callback,
GCancellable *cancellable,
gpointer user_data)
{
char **cached;
GetContentTypesData *data;
if (mount == NULL)
{
if (callback)
{
callback (NULL, user_data);
}
return;
}
cached = g_object_get_data (G_OBJECT (mount), "nautilus-content-type-cache");
if (cached != NULL)
{
if (callback)
{
callback ((const char **) cached, user_data);
}
return;
}
data = g_slice_new0 (GetContentTypesData);
data->callback = callback;
data->user_data = user_data;
g_mount_guess_content_type (mount,
FALSE,
cancellable,
get_types_cb,
data);
}
char **
nautilus_get_cached_x_content_types_for_mount (GMount *mount)
{
char **cached;
if (mount == NULL)
{
return NULL;
}
cached = g_object_get_data (G_OBJECT (mount), "nautilus-content-type-cache");
if (cached != NULL)
{
return g_strdupv (cached);
}
return NULL;
}
char *
get_message_for_content_type (const char *content_type)
{
char *message;
const GStrv known_types = (char *[])
{
"x-content/audio-cdda",
"x-content/audio-dvd",
"x-content/video-vcd",
"x-content/video-dvd",
"x-content/video-svcd",
"x-content/image-picturecd",
NULL,
};
/* Customize greeting for well-known content types */
if (g_strv_contains ((const gchar **) known_types, content_type))
{
message = g_content_type_get_description (content_type);
}
else if (strcmp (content_type, "x-content/image-photocd") == 0)
{
/* translators: these describe the contents of removable media */
message = g_strdup (_("Photo CD"));
}
else if (strcmp (content_type, "x-content/image-dcf") == 0)
{
message = g_strdup (_("Contains digital photos"));
}
else if (strcmp (content_type, "x-content/audio-player") == 0)
{
message = g_strdup (_("Contains music"));
}
else if (strcmp (content_type, "x-content/unix-software") == 0)
{
message = g_strdup (_("Contains software to run"));
}
else if (strcmp (content_type, "x-content/ostree-repository") == 0)
{
message = g_strdup (_("Contains software to install"));
}
else
{
/* fallback to generic greeting */
g_autofree char *description = g_content_type_get_description (content_type);
message = g_strdup_printf (_("Detected as ā%sā"), description);
}
return message;
}
char *
get_message_for_two_content_types (const char * const *content_types)
{
char *message;
g_assert (content_types[0] != NULL);
g_assert (content_types[1] != NULL);
/* few combinations make sense */
if (strcmp (content_types[0], "x-content/image-dcf") == 0
|| strcmp (content_types[1], "x-content/image-dcf") == 0)
{
if (strcmp (content_types[0], "x-content/audio-player") == 0)
{
/* translators: these describe the contents of removable media */
message = g_strdup (_("Contains music and photos"));
}
else if (strcmp (content_types[1], "x-content/audio-player") == 0)
{
message = g_strdup (_("Contains photos and music"));
}
else
{
message = g_strdup (_("Contains digital photos"));
}
}
else if ((strcmp (content_types[0], "x-content/video-vcd") == 0
|| strcmp (content_types[1], "x-content/video-vcd") == 0)
&& (strcmp (content_types[0], "x-content/video-dvd") == 0
|| strcmp (content_types[1], "x-content/video-dvd") == 0))
{
message = g_strdup_printf ("%s/%s",
get_message_for_content_type (content_types[0]),
get_message_for_content_type (content_types[1]));
}
else
{
message = get_message_for_content_type (content_types[0]);
}
return message;
}
gboolean
should_handle_content_type (const char *content_type)
{
g_autoptr (GAppInfo) default_app = NULL;
default_app = g_app_info_get_default_for_type (content_type, FALSE);
return !g_str_has_prefix (content_type, "x-content/blank-") &&
!g_content_type_is_a (content_type, "x-content/win32-software") &&
default_app != NULL;
}
gboolean
should_handle_content_types (const char * const *content_types)
{
int i;
for (i = 0; content_types[i] != NULL; i++)
{
if (should_handle_content_type (content_types[i]))
{
return TRUE;
}
}
return FALSE;
}
static char *
trim_whitespace (const gchar *string)
{
glong space_count;
glong length;
gchar *offset;
space_count = 0;
length = g_utf8_strlen (string, -1);
offset = g_utf8_offset_to_pointer (string, length);
while (space_count <= length)
{
gunichar character;
offset = g_utf8_prev_char (offset);
character = g_utf8_get_char (offset);
if (!g_unichar_isspace (character))
{
break;
}
space_count++;
}
if (space_count == 0)
{
return g_strdup (string);
}
return g_utf8_substring (string, 0, length - space_count);
}
char *
nautilus_get_common_filename_prefix (GList *file_list,
int min_required_len)
{
g_autoptr (GPtrArray) file_names = NULL;
g_autoptr (GPtrArray) directory_names = NULL;
g_autofree char *result_files = NULL;
g_autofree char *result = NULL;
g_autofree char *result_trimmed = NULL;
if (file_list == NULL)
{
return NULL;
}
file_names = g_ptr_array_new_null_terminated (0, g_free, TRUE);
directory_names = g_ptr_array_new_null_terminated (0, g_free, TRUE);
for (GList *l = file_list; l != NULL; l = l->next)
{
char *name;
g_return_val_if_fail (NAUTILUS_IS_FILE (l->data), NULL);
name = g_strdup (nautilus_file_get_display_name (l->data));
/* Since the concept of file extensions does not apply to directories,
* we filter those out.
*/
if (nautilus_file_is_directory (l->data))
{
g_ptr_array_add (directory_names, name);
}
else
{
g_ptr_array_add (file_names, name);
}
}
result_files = nautilus_get_common_filename_prefix_from_filenames ((const char * const *) file_names->pdata,
min_required_len);
if (directory_names->len == 0)
{
return g_steal_pointer (&result_files);
}
if (result_files != NULL)
{
g_ptr_array_add (directory_names, g_steal_pointer (&result_files));
}
result = nautilus_filename_get_common_prefix ((const char * const *) directory_names->pdata,
min_required_len);
if (result == NULL)
{
return NULL;
}
result_trimmed = trim_whitespace (result);
if (g_utf8_strlen (result_trimmed, -1) < min_required_len)
{
return NULL;
}
return g_steal_pointer (&result_trimmed);
}
char *
nautilus_get_common_filename_prefix_from_filenames (const char * const *filenames,
int min_required_len)
{
g_autoptr (GPtrArray) stripped_filenames = NULL;
g_autofree char *common_prefix = NULL;
g_autofree char *truncated = NULL;
int common_prefix_len;
if (filenames == NULL || filenames[0] == NULL)
{
return NULL;
}
stripped_filenames = g_ptr_array_new_from_null_terminated_array ((gpointer *) filenames,
(GCopyFunc) nautilus_filename_strip_extension,
NULL,
g_free);
common_prefix = nautilus_filename_get_common_prefix ((const char * const *) stripped_filenames->pdata,
min_required_len);
if (common_prefix == NULL)
{
return NULL;
}
truncated = trim_whitespace (common_prefix);
common_prefix_len = g_utf8_strlen (truncated, -1);
if (common_prefix_len < min_required_len)
{
return NULL;
}
return g_steal_pointer (&truncated);
}
glong
nautilus_get_max_child_name_length_for_location (GFile *location)
{
glong name_max;
glong path_max;
glong max_child_name_length;
g_return_val_if_fail (G_IS_FILE (location), -1);
if (!g_file_has_uri_scheme (location, "file"))
{
/* FIXME: Can we query length limits for non-"file://" locations? */
return -1;
}
const gchar *path = g_file_peek_path (location);
g_return_val_if_fail (path != NULL, -1);
name_max = pathconf (path, _PC_NAME_MAX);
path_max = pathconf (path, _PC_PATH_MAX);
max_child_name_length = -1;
if (name_max == -1)
{
if (path_max != -1)
{
/* We don't know the name max, but we know the name can't make the
* path longer than this.
* Subtracting 1 because PATH_MAX includes the terminating null
* character, as per limits.h(0P). */
max_child_name_length = MAX ((path_max - 1) - strlen (path), 0);
}
}
else
{
/* No need to subtract 1, because NAME_MAX excludes the terminating null
* character, as per limits.h(0P) */
max_child_name_length = name_max;
if (path_max != -1)
{
max_child_name_length = CLAMP ((path_max - 1) - (glong) strlen (path),
0,
max_child_name_length);
}
}
return max_child_name_length;
}
void
nautilus_ensure_extension_builtins (void)
{
/* Please add new extension types here, even if you can guarantee
* that they will be registered by the time the extension point
* is iterating over its extensions.
*/
g_type_ensure (NAUTILUS_TYPE_NETWORK_DIRECTORY);
g_type_ensure (NAUTILUS_TYPE_SEARCH_DIRECTORY);
g_type_ensure (NAUTILUS_TYPE_STARRED_DIRECTORY);
}
void
nautilus_ensure_extension_points (void)
{
static gsize once_init_value = 0;
if (g_once_init_enter (&once_init_value))
{
GIOExtensionPoint *extension_point;
extension_point = g_io_extension_point_register (NAUTILUS_DIRECTORY_PROVIDER_EXTENSION_POINT_NAME);
g_io_extension_point_set_required_type (extension_point, NAUTILUS_TYPE_DIRECTORY);
g_once_init_leave (&once_init_value, 1);
}
}
gboolean
nautilus_file_can_rename_files (GList *files)
{
GList *l;
NautilusFile *file;
for (l = files; l != NULL; l = l->next)
{
file = NAUTILUS_FILE (l->data);
if (!nautilus_file_can_rename (file))
{
return FALSE;
}
}
return TRUE;
}
/* check_schema_available() was copied from GNOME Settings */
gboolean
check_schema_available (const gchar *schema_id)
{
GSettingsSchemaSource *source;
g_autoptr (GSettingsSchema) schema = NULL;
if (nautilus_application_is_sandboxed ())
{
return FALSE;
}
source = g_settings_schema_source_get_default ();
if (!source)
{
return FALSE;
}
schema = g_settings_schema_source_lookup (source, schema_id, TRUE);
if (!schema)
{
return FALSE;
}
return TRUE;
}
gboolean
is_external_volume (GVolume *volume)
{
gboolean is_external;
GDrive *drive;
char *id;
drive = g_volume_get_drive (volume);
id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS);
is_external = g_volume_can_eject (volume);
/* NULL volume identifier only happens on removable devices */
is_external |= !id;
if (drive)
{
is_external |= g_drive_is_removable (drive);
}
g_clear_object (&drive);
g_free (id);
return is_external;
}
/* Checks if the location is an autofs mountpoint without triggering mount.*/
gboolean
nautilus_location_is_autofs_mountpoint (GFile *location)
{
g_return_val_if_fail (G_IS_FILE (location), FALSE);
g_autofree char *path = g_file_get_path (location);
struct statfs buf;
gint fd;
if (path == NULL)
{
return FALSE;
}
fd = g_open (path, O_PATH | O_NOFOLLOW, 0);
if (fd < 0)
{
return FALSE;
}
if (fstatfs (fd, &buf) < 0)
{
close (fd);
return FALSE;
}
close (fd);
return (buf.f_type == AUTOFS_SUPER_MAGIC);
}
gboolean
selection_source_is_intentional (NautilusSelectionSource selection_source)
{
return selection_source == NAUTILUS_SELECTION_SOURCE_MANUAL ||
selection_source == NAUTILUS_SELECTION_SOURCE_AFTER_SEARCH;
}
/*** begin NautilusRecursiveDirectoryIterator ***/
typedef struct
{
GFile *directory;
GHashTable *visited;
char *attributes;
guint max_depth;
gboolean local_only;
GCancellable *cancellable;
IterationFileCallback file_callback;
gpointer user_data;
} NautilusRecursiveDirectoryIterator;
static NautilusRecursiveDirectoryIterator *
nautilus_recursive_directory_iterator_new (GFile *directory,
guint max_depth,
gboolean local_only,
const char *attributes,
GCancellable *cancellable,
IterationFileCallback file_callback,
gpointer user_data)
{
NautilusRecursiveDirectoryIterator *self = g_new0 (NautilusRecursiveDirectoryIterator, 1);
self->directory = g_object_ref (directory);
self->visited = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
self->attributes = g_strdup (attributes != NULL
? attributes : G_FILE_ATTRIBUTE_STANDARD_NAME);
self->max_depth = max_depth;
self->local_only = local_only;
self->cancellable = (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
self->file_callback = file_callback;
self->user_data = user_data;
return self;
}
static void
nautilus_recursive_directory_iterator_free (NautilusRecursiveDirectoryIterator *self)
{
g_clear_object (&self->directory);
g_hash_table_unref (self->visited);
g_free (self->attributes);
g_clear_object (&self->cancellable);
g_free (self);
}
static gboolean
is_remote (GFile *location)
{
g_autoptr (GFileInfo) file_system_info =
g_file_query_filesystem_info (location, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, NULL, NULL);
return file_system_info != NULL &&
g_file_info_get_attribute_boolean (file_system_info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE);
}
static void
recursive_directory_iterator_thread (GTask *task,
GObject *source_object,
gpointer task_data,
GCancellable *cancellable)
{
NautilusRecursiveDirectoryIterator *self = task_data;
g_autoqueue (GFile) pending_directories = g_queue_new ();
g_queue_push_tail (pending_directories, g_object_ref (self->directory));
for (guint depth = 0;
depth <= self->max_depth && g_queue_get_length (pending_directories) > 0;
depth++)
{
/* Breadth-first iteration, all stored directories are for next depth level */
g_autoqueue (GFile) directories_at_depth = g_steal_pointer (&pending_directories);
pending_directories = g_queue_new ();
while (!g_queue_is_empty (directories_at_depth))
{
g_autoptr (GFile) directory = g_queue_pop_head (directories_at_depth);
g_autoptr (GError) error = NULL;
g_autoptr (GFileEnumerator) enumerator =
g_file_enumerate_children (directory,
self->attributes,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
self->cancellable,
&error);
GFileInfo *info = NULL;
GFile *location = NULL;
if (error != NULL)
{
g_warning ("Error enumerating directory '%s': %s",
g_file_get_path (directory), error->message);
continue;
}
while (g_file_enumerator_iterate (enumerator, &info, &location, self->cancellable, &error) && info != NULL)
{
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY &&
(!self->local_only || !is_remote (location)))
{
const char *id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE);
if (id != NULL)
{
if (g_hash_table_contains (self->visited, id))
{
/* Already visited, don't call callback a second time */
continue;
}
else
{
g_hash_table_add (self->visited, g_strdup (id));
}
}
g_queue_push_tail (pending_directories, g_object_ref (location));
}
self->file_callback (info, location, self->user_data);
}
if (g_cancellable_is_cancelled (self->cancellable))
{
g_task_return_boolean (task, FALSE);
return;
}
if (error != NULL)
{
g_warning ("Error iterating directory '%s': %s",
g_file_get_path (directory), error->message);
}
}
}
g_task_return_boolean (task, TRUE);
}
/**
* Asynchronously iterates a directory recursively, calling @file_callback
* for each encountered file. Calls @done_callback when iterating finished.
*/
gboolean
nautilus_iterate_directory_recursive (GFile *directory,
guint max_depth,
const char *attributes,
gboolean local_only,
GCancellable *cancellable,
IterationFileCallback file_callback,
GAsyncReadyCallback done_callback,
gpointer user_data)
{
g_return_val_if_fail (G_IS_FILE (directory), FALSE);
g_return_val_if_fail (file_callback != NULL, FALSE);
g_autoptr (GError) error = NULL;
NautilusRecursiveDirectoryIterator *self =
nautilus_recursive_directory_iterator_new (directory,
max_depth,
local_only,
attributes,
cancellable,
file_callback,
user_data);
g_autoptr (GTask) task = g_task_new (NULL, cancellable, done_callback, user_data);
g_task_set_task_data (task, self, (GDestroyNotify) nautilus_recursive_directory_iterator_free);
g_task_run_in_thread (task, (GTaskThreadFunc) recursive_directory_iterator_thread);
return TRUE;
}
/*** end NautilusRecursiveDirectoryIterator ***/