+2005-08-28 Benedikt Meurer <benny@xfce.org>
+
+ * thunar/thunar-file.{c,h}, thunar/thunar-local-file.c,
+ thunar/thunar-standard-view.c: Rename can_execute(), can_read() and
+ can_write() to is_executable(), is_readable() and is_writable() to
+ get consistent naming.
+ * thunar-vfs/thunar-vfs-info.{c,h}: Add THUNAR_VFS_FILE_FLAGS_EXECUTABLE
+ to the ThunarVfsFileFlags, which will be set if a ThunarVfsInfo
+ can be executed, either as regular binary or as .desktop file.
+ * thunar-vfs/thunar-vfs-mime-application.c,
+ thunar-vfs/thunar-vfs-sysdep.{c,h}: Move the Exec parsing code from
+ ThunarVfsMimeApplication to thunar-vfs-sysdep, so it can be used by
+ other modules as well.
+ * thunar-vfs/thunar-vfs-info.{c,h}, thunar-vfs/thunar-vfs.symbols: Add
+ new method thunar_vfs_info_execute(), which is used to execute
+ files with a list of URIs. These method can handle both regular
+ executable files as well as .desktop files.
+ * thunar/thunar-file.{c,h}, thunar/thunar-launcher.c,
+ thunar/thunar-local-file.c: Add support to execute files that are
+ marked as executable by the ThunarVfsInfo module.
+ * thunar-vfs/thunar-vfs-mime-database.c
+ (thunar_vfs_mime_database_get_info_locked),
+ (thunar_vfs_mime_database_get_infos_for_info_locked): Be sure to
+ always unalias MIME-types prior to returning them from the mime
+ database instance. This way we don't need to care for unaliasing
+ when determining the MIME-type comment or MIME-type icon.
+ * thunar-vfs/thunar-vfs-mime-database.{c,h},
+ thunar-vfs/thunar-vfs.symbols: Add new method
+ thunar_vfs_mime_database_get_infos_for_info() to the public API, to
+ allow other components to access the subclassing information.
+ * FAQ, Makefile.am: Add initial items for the list of frequently asked
+ questions.
+ * TODO: Remove obsolete items.
+
2005-08-27 Benedikt Meurer <benny@xfce.org>
* thunar-vfs/thunar-vfs-info.{c,h}: Add support to pass hints from the
--- /dev/null
+This file contains a list of frequently asked questions about Thunar and the
+appropriate answers to these questions.
+
+
+1. What is Thunar?
+==================
+
+ Thunar is a fast and easy-to-use file manager for the X Window System, with a
+ special focus on the Xfce Desktop Environment.
+
+
+2. Why doesn't Thunar execute files marked as executable?
+=========================================================
+
+ For security reasons Thunar only executes files of type application/x-desktop,
+ application/x-executable and application/x-shellscript. For desktop files
+ the execution feature will only be enabled if the desktop file is of type
+ Application and a valid Exec line is given. For the other types the feature
+ is available if the file is marked executable for the current user.
+
+ Also note that for application/x-executable and application/x-shellscript, the
+ types of the file don't really need to match these types exactly, but it is
+ suffice if the detected type has a parent that matches one of the two types
+ listed above, or if the MIME-type is an alias for one of the above.
@INTLTOOL_DESKTOP_RULE@
EXTRA_DIST = \
+ FAQ \
HACKING \
intltool-extract.in \
intltool-merge.in \
- We need a way to "refresh" folders after a "Cut"-operation with Nautilus.
With local folders - with not many files inside - the move is too fast!
- - ThunarVfsURI should have it's own parameter specification, that
- can be used for GValue handling.
-
- The ThunarTrashFile constructor needs some rework, as it's currently
mostly brain-dead. Should probably be splitted into several functions.
In addition the ThunarTrashFile should enable the monitoring on the
development of Thunar core modules is to be done, and what
material each developer in addition to the source code.
- - The ThunarVfsMonitor framework must be designed and implemented.
- The list of supported backends for 1.0 is kqueue, fam and
- the stat-thread. Backend technologies must be thread-safe. If
- there's a lot of time or manpower in the end, additional
- backends can be added, as long as they fit into the framework.
#ifdef HAVE_STRING_H
#include <string.h>
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#include <thunar-vfs/thunar-vfs-info.h>
#include <thunar-vfs/thunar-vfs-mime-database.h>
+#include <thunar-vfs/thunar-vfs-sysdep.h>
#include <thunar-vfs/thunar-vfs-alias.h>
+/* Use g_access() if possible */
+#if GLIB_CHECK_VERSION(2,8,0)
+#include <glib/gstdio.h>
+#else
+#define g_access(path, mode) (access ((path), (mode)))
+#endif
+
/**
GError **error)
{
ThunarVfsMimeDatabase *database;
+ ThunarVfsMimeInfo *mime_info;
ThunarVfsInfo *info;
const gchar *path;
const gchar *str;
struct stat lsb;
struct stat sb;
XfceRc *rc;
+ GList *mime_infos;
+ GList *lp;
g_return_val_if_fail (THUNAR_VFS_IS_URI (uri), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
info->display_name = thunar_vfs_uri_get_display_name (uri);
info->hints = NULL;
+ /* determine the POSIX file attributes */
if (G_LIKELY (!S_ISLNK (lsb.st_mode)))
{
info->type = (lsb.st_mode & S_IFMT) >> 12;
}
}
+ /* determine the file's mime type */
database = thunar_vfs_mime_database_get_default ();
switch (info->type)
{
break;
case THUNAR_VFS_FILE_TYPE_REGULAR:
+ /* determine the MIME-type for the regular file */
info->mime_info = thunar_vfs_mime_database_get_info_for_file (database, path, info->display_name);
+
+ /* check if the file is executable (for security reasons
+ * we only allow execution of well known file types).
+ */
+ if (G_LIKELY (info->type == THUNAR_VFS_FILE_TYPE_REGULAR)
+ && (info->mode & 0444) != 0 && g_access (path, X_OK) == 0)
+ {
+ mime_infos = thunar_vfs_mime_database_get_infos_for_info (database, info->mime_info);
+ for (lp = mime_infos; lp != NULL; lp = lp->next)
+ {
+ mime_info = THUNAR_VFS_MIME_INFO (lp->data);
+ if (strcmp (mime_info->name, "application/x-executable") == 0
+ || strcmp (mime_info->name, "application/x-shellscript") == 0)
+ {
+ info->flags |= THUNAR_VFS_FILE_FLAGS_EXECUTABLE;
+ break;
+ }
+ }
+ thunar_vfs_mime_info_list_free (mime_infos);
+ }
break;
default:
if (G_LIKELY (str != NULL))
info->hints[THUNAR_VFS_FILE_HINT_NAME] = g_strdup (str);
+ /* check if the desktop file refers to an application
+ * and has a non-NULL Exec field set.
+ */
+ str = xfce_rc_read_entry (rc, "Type", "Application");
+ if (G_LIKELY (exo_str_is_equal (str, "Application"))
+ && xfce_rc_read_entry (rc, "Exec", NULL) != NULL)
+ {
+ info->flags |= THUNAR_VFS_FILE_FLAGS_EXECUTABLE;
+ }
+
/* close the file */
xfce_rc_close (rc);
}
/**
+ * thunar_vfs_info_execute:
+ * @info : a #ThunarVfsInfo.
+ * @screen : a #GdkScreen or %NULL to use the default #GdkScreen.
+ * @uris : the list of #ThunarVfsURI<!---->s to give as parameters
+ * to the file referred to by @info on execution.
+ * @error : return location for errors or %NULL.
+ *
+ * Executes the file referred to by @info, given @uris as parameters,
+ * on the specified @screen. @info may refer to either a regular,
+ * executable file, or a <filename>.desktop</filename> file, whose
+ * type is <literal>Application</literal>.
+ *
+ * Return value: %TRUE on success, else %FALSE.
+ **/
+gboolean
+thunar_vfs_info_execute (const ThunarVfsInfo *info,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error)
+{
+ const gchar *icon;
+ const gchar *name;
+ const gchar *path;
+ gboolean terminal;
+ gboolean result = FALSE;
+ XfceRc *rc;
+ gchar *working_directory;
+ gchar *path_escaped;
+ gchar **argv = NULL;
+ gchar *exec;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (info->ref_count > 0, FALSE);
+ g_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* fallback to the default screen if none given */
+ if (G_UNLIKELY (screen == NULL))
+ screen = gdk_screen_get_default ();
+
+ /* determine the path */
+ path = thunar_vfs_uri_get_path (info->uri);
+
+ /* check if we have a .desktop file here */
+ if (G_UNLIKELY (strcmp (info->mime_info->name, "application/x-desktop") == 0))
+ {
+ rc = xfce_rc_simple_open (path, TRUE);
+ if (G_LIKELY (rc != NULL))
+ {
+ /* check if we have a valid Exec field */
+ exec = (gchar *) xfce_rc_read_entry_untranslated (rc, "Exec", NULL);
+ if (G_LIKELY (exec != NULL))
+ {
+ /* parse the Exec field */
+ name = xfce_rc_read_entry (rc, "Name", NULL);
+ icon = xfce_rc_read_entry_untranslated (rc, "Icon", NULL);
+ terminal = xfce_rc_read_bool_entry (rc, "Terminal", FALSE);
+ result = _thunar_vfs_sysdep_parse_exec (exec, uris, icon, name, path,
+ terminal, NULL, &argv, error);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, _("No Exec field specified"));
+ }
+
+ /* close the rc file */
+ xfce_rc_close (rc);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, _("Unable to parse file"));
+ }
+ }
+ else
+ {
+ /* fake the Exec line */
+ path_escaped = g_shell_quote (path);
+ exec = g_strconcat (path_escaped, " %F", NULL);
+ result = _thunar_vfs_sysdep_parse_exec (exec, uris, NULL, NULL, NULL, FALSE, NULL, &argv, error);
+ g_free (path_escaped);
+ g_free (exec);
+ }
+
+ if (G_LIKELY (result))
+ {
+ /* determine the working directory */
+ working_directory = g_path_get_dirname ((uris != NULL) ? thunar_vfs_uri_get_path (uris->data) : path);
+
+ /* execute the command */
+ result = gdk_spawn_on_screen (screen, working_directory, argv,
+ NULL, G_SPAWN_SEARCH_PATH, NULL,
+ NULL, NULL, error);
+
+ /* release the working directory */
+ g_free (working_directory);
+ }
+
+ /* clean up */
+ g_strfreev (argv);
+
+ return result;
+}
+
+
+
+/**
* thunar_vfs_info_get_hint:
* @info : a #ThunarVfsInfo.
* @hint : a #ThunarVfsFileHint.
/**
* ThunarVfsFileFlags:
- * @THUNAR_VFS_FILE_FLAGS_NONE : No additional information available.
- * @THUNAR_VFS_FILE_FLAGS_SYMLINK : The file is a symlink. Whether or not
- * the info fields refer to the symlink
- * itself or the linked file, depends on
- * whether the symlink is broken or not.
+ * @THUNAR_VFS_FILE_FLAGS_NONE : No additional information available.
+ * @THUNAR_VFS_FILE_FLAGS_SYMLINK : The file is a symlink. Whether or not
+ * the info fields refer to the symlink
+ * itself or the linked file, depends on
+ * whether the symlink is broken or not.
+ * @THUNAR_VFS_FILE_FLAGS_EXECUTABLE : The file can most probably be executed
+ * by #thunar_vfs_info_execute().
*
* Flags providing additional information about the
* file system entity.
**/
typedef enum { /*< flags >*/
- THUNAR_VFS_FILE_FLAGS_NONE = 0,
- THUNAR_VFS_FILE_FLAGS_SYMLINK = 1 << 0,
+ THUNAR_VFS_FILE_FLAGS_NONE = 0,
+ THUNAR_VFS_FILE_FLAGS_SYMLINK = 1L << 0,
+ THUNAR_VFS_FILE_FLAGS_EXECUTABLE = 1L << 1,
} ThunarVfsFileFlags;
/**
ThunarVfsInfo *thunar_vfs_info_ref (ThunarVfsInfo *info);
void thunar_vfs_info_unref (ThunarVfsInfo *info);
+gboolean thunar_vfs_info_execute (const ThunarVfsInfo *info,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error);
+
const gchar *thunar_vfs_info_get_hint (const ThunarVfsInfo *info,
ThunarVfsFileHint hint);
#endif
#include <thunar-vfs/thunar-vfs-mime-application.h>
+#include <thunar-vfs/thunar-vfs-sysdep.h>
+#include <thunar-vfs/thunar-vfs-util.h>
#include <thunar-vfs/thunar-vfs-alias.h>
gchar ***argv,
GError **error)
{
- const gchar *p;
- gboolean result;
- GString *command_line = g_string_new (NULL);
- GList *lp;
- gchar *uri_string;
- gchar *quoted;
-
- /* prepend terminal command if required */
- if (G_UNLIKELY (application->requires_terminal))
- {
- quoted = g_shell_quote (application->name);
- g_string_append (command_line, "Terminal -T ");
- g_string_append (command_line, quoted);
- g_string_append (command_line, " -x ");
- g_free (quoted);
- }
-
- for (p = application->exec; *p != '\0'; ++p)
- {
- if (p[0] == '%' && p[1] != '\0')
- {
- switch (*++p)
- {
- case 'f':
- quoted = g_shell_quote (thunar_vfs_uri_get_path (uris->data));
- g_string_append (command_line, quoted);
- g_free (quoted);
- break;
-
- case 'F':
- for (lp = uris; lp != NULL; lp = lp->next)
- {
- if (G_LIKELY (lp != uris))
- g_string_append_c (command_line, ' ');
- quoted = g_shell_quote (thunar_vfs_uri_get_path (lp->data));
- g_string_append (command_line, quoted);
- g_free (quoted);
- }
- break;
-
- case 'u':
- /* we need to hide the host parameter here, because there are quite a few
- * applications out there (namely GNOME applications), which cannot handle
- * 'file:'-URIs with host names.
- */
- uri_string = thunar_vfs_uri_to_string (uris->data, THUNAR_VFS_URI_HIDE_HOST);
- quoted = g_shell_quote (uri_string);
- g_string_append (command_line, quoted);
- g_free (uri_string);
- g_free (quoted);
- break;
-
- case 'U':
- for (lp = uris; lp != NULL; lp = lp->next)
- {
- if (G_LIKELY (lp != uris))
- g_string_append_c (command_line, ' ');
- uri_string = thunar_vfs_uri_to_string (lp->data, THUNAR_VFS_URI_HIDE_HOST);
- quoted = g_shell_quote (uri_string);
- g_string_append (command_line, quoted);
- g_free (uri_string);
- g_free (quoted);
- }
- break;
-
- case 'd':
- uri_string = g_path_get_dirname (thunar_vfs_uri_get_path (uris->data));
- quoted = g_shell_quote (uri_string);
- g_string_append (command_line, quoted);
- g_free (uri_string);
- g_free (quoted);
- break;
-
- case 'D':
- for (lp = uris; lp != NULL; lp = lp->next)
- {
- if (G_LIKELY (lp != uris))
- g_string_append_c (command_line, ' ');
- uri_string = g_path_get_dirname (thunar_vfs_uri_get_path (lp->data));
- quoted = g_shell_quote (uri_string);
- g_string_append (command_line, quoted);
- g_free (uri_string);
- g_free (quoted);
- }
- break;
-
- case 'n':
- quoted = g_shell_quote (thunar_vfs_uri_get_name (uris->data));
- g_string_append (command_line, quoted);
- g_free (quoted);
- break;
-
- case 'N':
- for (lp = uris; lp != NULL; lp = lp->next)
- {
- if (G_LIKELY (lp != uris))
- g_string_append_c (command_line, ' ');
- quoted = g_shell_quote (thunar_vfs_uri_get_name (lp->data));
- g_string_append (command_line, quoted);
- g_free (quoted);
- }
- break;
-
- case 'i':
- if (G_LIKELY (application->icon != NULL))
- {
- quoted = g_shell_quote (application->icon);
- g_string_append (command_line, "--icon ");
- g_string_append (command_line, quoted);
- g_free (quoted);
- }
- break;
-
- case 'c':
- quoted = g_shell_quote (application->name);
- g_string_append (command_line, quoted);
- g_free (quoted);
- break;
-
- case '%':
- g_string_append_c (command_line, '%');
- break;
- }
- }
- else
- {
- g_string_append_c (command_line, *p);
- }
- }
-
- result = g_shell_parse_argv (command_line->str, argc, argv, NULL);
-
- g_string_free (command_line, TRUE);
-
- return result;
+ return _thunar_vfs_sysdep_parse_exec (application->exec, uris, application->icon, application->name, NULL, application->requires_terminal, argc, argv, error);
}
thunar_vfs_mime_database_get_info_locked (ThunarVfsMimeDatabase *database,
const gchar *mime_type)
{
- ThunarVfsMimeInfo *info;
- const gchar *s;
- const gchar *t;
- const gchar *u;
- gchar *v;
- guint n;
+ ThunarVfsMimeProvider *provider;
+ ThunarVfsMimeInfo *info;
+ const gchar *s;
+ const gchar *t;
+ const gchar *u;
+ GList *lp;
+ gchar *v;
+ guint n;
+
+ /* unalias the mime type */
+ for (lp = database->providers; lp != NULL; lp = lp->next)
+ {
+ provider = THUNAR_VFS_MIME_PROVIDER_DATA (lp->data)->provider;
+ if (G_LIKELY (provider != NULL))
+ {
+ t = thunar_vfs_mime_provider_lookup_alias (provider, mime_type);
+ if (G_UNLIKELY (t != NULL && strcmp (mime_type, t) != 0))
+ {
+ mime_type = t;
+ break;
+ }
+ }
+ }
/* check if we have a cached version of the mime type */
info = g_hash_table_lookup (database->infos, mime_type);
ThunarVfsMimeProvider *provider;
ThunarVfsMimeInfo *parent_info;
const gchar *name = thunar_vfs_mime_info_get_name (info);
- const gchar *type;
gchar *parents[128];
GList *infos;
GList *lp;
/* the info itself is of course on the list */
infos = g_list_prepend (NULL, thunar_vfs_mime_info_ref (info));
- /* check if the info is an alias */
- for (lp = database->providers; lp != NULL; lp = lp->next)
- {
- provider = THUNAR_VFS_MIME_PROVIDER_DATA (lp->data)->provider;
- if (G_LIKELY (provider != NULL))
- {
- type = thunar_vfs_mime_provider_lookup_alias (provider, name);
- if (G_UNLIKELY (type != NULL && strcmp (name, type) != 0))
- {
- /* it is indeed an alias, so we use the unaliased type instead */
- info = thunar_vfs_mime_database_get_info_locked (database, type);
- name = thunar_vfs_mime_info_get_name (info);
- infos = g_list_append (infos, info);
- }
- }
- }
-
/* lookup all parents on every provider */
for (lp = database->providers; lp != NULL; lp = lp->next)
{
/**
+ * thunar_vfs_mime_database_get_infos_for_info:
+ * @database : a #ThunarVfsMimeDatabase.
+ * @info : a #ThunarVfsMimeInfo.
+ *
+ * Returns a list of all #ThunarVfsMimeInfo<!---->s,
+ * that are related to @info in @database. Currently
+ * this is the list of parent MIME-types for @info,
+ * as defined in the Shared Mime Database.
+ *
+ * Note that the returned list will also include
+ * a reference @info itself. In addition, this
+ * method also handles details specified by the
+ * Shared Mime Database Specification like the
+ * fact that every "text/xxxx" MIME-type is a
+ * subclass of "text/plain" and every MIME-type
+ * is a subclass of "application/octet-stream".
+ *
+ * The caller is responsible to free the returned
+ * list using #thunar_vfs_mime_info_list_free()
+ * when done with it.
+ *
+ * Return value: the list of #ThunarVfsMimeInfo<!---->s
+ * related to @info.
+ **/
+GList*
+thunar_vfs_mime_database_get_infos_for_info (ThunarVfsMimeDatabase *database,
+ ThunarVfsMimeInfo *info)
+{
+ GList *infos;
+
+ g_return_val_if_fail (THUNAR_VFS_IS_MIME_DATABASE (database), NULL);
+ g_return_val_if_fail (THUNAR_VFS_IS_MIME_INFO (info), NULL);
+
+ g_mutex_lock (database->lock);
+ infos = thunar_vfs_mime_database_get_infos_for_info_locked (database, info);
+ g_mutex_unlock (database->lock);
+
+ return infos;
+}
+
+
+
+/**
* thunar_vfs_mime_database_get_applications:
* @database : a #ThunarVfsMimeDatabase.
* @info : a #ThunarVfsMimeInfo.
const gchar *path,
const gchar *name);
+GList *thunar_vfs_mime_database_get_infos_for_info (ThunarVfsMimeDatabase *database,
+ ThunarVfsMimeInfo *info);
+
GList *thunar_vfs_mime_database_get_applications (ThunarVfsMimeDatabase *database,
ThunarVfsMimeInfo *info);
ThunarVfsMimeApplication *thunar_vfs_mime_database_get_default_application (ThunarVfsMimeDatabase *database,
#endif
#include <thunar-vfs/thunar-vfs-sysdep.h>
+#include <thunar-vfs/thunar-vfs-uri.h>
+#include <thunar-vfs/thunar-vfs-alias.h>
+/**
+ * _thunar_vfs_sysdep_parse_exec:
+ * @exec : the value of the <literal>Exec</literal> field.
+ * @uris : the list of #ThunarVfsURI<!---->s.
+ * @icon : value of the <literal>Icon</literal> field or %NULL.
+ * @name : translated value for the <literal>Name</literal> field or %NULL.
+ * @path : full path to the desktop file or %NULL.
+ * @terminal : whether to execute the command in a terminal.
+ * @argc : return location for the number of items placed into @argv.
+ * @argv : return location for the argument vector.
+ * @error : return location for errors or %NULL.
+ *
+ * Substitutes <literal>Exec</literal> parameter variables according
+ * to the <ulink href="http://freedesktop.org/wiki/Standards_2fdesktop_2dentry_2dspec"
+ * type="http">Desktop Entry Specification</ulink> and returns the
+ * parsed argument vector (in @argv) and the number of items placed
+ * into @argv (in @argc).
+ *
+ * The @icon, @name and @path fields are optional and may be %NULL
+ * if you don't know their values. The @icon parameter should be
+ * the value of the <literal>Icon</literal> field from the desktop
+ * file, the @name parameter should be the translated <literal>Name</literal>
+ * value, while the @path parameter should refer to the full path
+ * to the desktop file, whose <literal>Exec</literal> field is
+ * being parsed here.
+ *
+ * Return value: %TRUE on success, else %FALSE.
+ **/
+gboolean
+_thunar_vfs_sysdep_parse_exec (const gchar *exec,
+ GList *uris,
+ const gchar *icon,
+ const gchar *name,
+ const gchar *path,
+ gboolean terminal,
+ gint *argc,
+ gchar ***argv,
+ GError **error)
+{
+ const gchar *p;
+ gboolean result;
+ GString *command_line = g_string_new (NULL);
+ GList *lp;
+ gchar *uri_string;
+ gchar *quoted;
+
+ /* prepend terminal command if required */
+ if (G_UNLIKELY (terminal))
+ {
+ g_string_append (command_line, "Terminal ");
+ if (G_LIKELY (name != NULL))
+ {
+ quoted = g_shell_quote (name);
+ g_string_append (command_line, "-T ");
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ g_string_append (command_line, "-x ");
+ }
+
+ for (p = exec; *p != '\0'; ++p)
+ {
+ if (p[0] == '%' && p[1] != '\0')
+ {
+ switch (*++p)
+ {
+ case 'f':
+ if (G_LIKELY (uris != NULL))
+ {
+ quoted = g_shell_quote (thunar_vfs_uri_get_path (uris->data));
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ break;
+
+ case 'F':
+ for (lp = uris; lp != NULL; lp = lp->next)
+ {
+ if (G_LIKELY (lp != uris))
+ g_string_append_c (command_line, ' ');
+ quoted = g_shell_quote (thunar_vfs_uri_get_path (lp->data));
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ break;
+
+ case 'u':
+ /* we need to hide the host parameter here, because there are quite a few
+ * applications out there (namely GNOME applications), which cannot handle
+ * 'file:'-URIs with host names.
+ */
+ if (G_LIKELY (uris != NULL))
+ {
+ uri_string = thunar_vfs_uri_to_string (uris->data, THUNAR_VFS_URI_HIDE_HOST);
+ quoted = g_shell_quote (uri_string);
+ g_string_append (command_line, quoted);
+ g_free (uri_string);
+ g_free (quoted);
+ }
+ break;
+
+ case 'U':
+ for (lp = uris; lp != NULL; lp = lp->next)
+ {
+ if (G_LIKELY (lp != uris))
+ g_string_append_c (command_line, ' ');
+ uri_string = thunar_vfs_uri_to_string (lp->data, THUNAR_VFS_URI_HIDE_HOST);
+ quoted = g_shell_quote (uri_string);
+ g_string_append (command_line, quoted);
+ g_free (uri_string);
+ g_free (quoted);
+ }
+ break;
+
+ case 'd':
+ if (G_LIKELY (uris != NULL))
+ {
+ uri_string = g_path_get_dirname (thunar_vfs_uri_get_path (uris->data));
+ quoted = g_shell_quote (uri_string);
+ g_string_append (command_line, quoted);
+ g_free (uri_string);
+ g_free (quoted);
+ }
+ break;
+
+ case 'D':
+ for (lp = uris; lp != NULL; lp = lp->next)
+ {
+ if (G_LIKELY (lp != uris))
+ g_string_append_c (command_line, ' ');
+ uri_string = g_path_get_dirname (thunar_vfs_uri_get_path (lp->data));
+ quoted = g_shell_quote (uri_string);
+ g_string_append (command_line, quoted);
+ g_free (uri_string);
+ g_free (quoted);
+ }
+ break;
+
+ case 'n':
+ if (G_LIKELY (uris != NULL))
+ {
+ quoted = g_shell_quote (thunar_vfs_uri_get_name (uris->data));
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ break;
+
+ case 'N':
+ for (lp = uris; lp != NULL; lp = lp->next)
+ {
+ if (G_LIKELY (lp != uris))
+ g_string_append_c (command_line, ' ');
+ quoted = g_shell_quote (thunar_vfs_uri_get_name (lp->data));
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ break;
+
+ case 'i':
+ if (G_LIKELY (icon != NULL))
+ {
+ quoted = g_shell_quote (icon);
+ g_string_append (command_line, "--icon ");
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ break;
+
+ case 'c':
+ if (G_LIKELY (name != NULL))
+ {
+ quoted = g_shell_quote (name);
+ g_string_append (command_line, quoted);
+ g_free (quoted);
+ }
+ break;
+
+ case 'k':
+ if (G_LIKELY (path != NULL))
+ {
+ quoted = g_shell_quote (path);
+ g_string_append (command_line, path);
+ g_free (quoted);
+ }
+ break;
+
+ case '%':
+ g_string_append_c (command_line, '%');
+ break;
+ }
+ }
+ else
+ {
+ g_string_append_c (command_line, *p);
+ }
+ }
+
+ result = g_shell_parse_argv (command_line->str, argc, argv, error);
+
+ g_string_free (command_line, TRUE);
+
+ return result;
+}
+
+
+
+
G_BEGIN_DECLS;
-gboolean _thunar_vfs_sysdep_readdir (gpointer dirp,
- struct dirent *entry,
- struct dirent **result,
- GError **error);
+/* forward declarations */
+struct dirent;
+
+gboolean _thunar_vfs_sysdep_readdir (gpointer dirp,
+ struct dirent *entry,
+ struct dirent **result,
+ GError **error);
+
+gboolean _thunar_vfs_sysdep_parse_exec (const gchar *exec,
+ GList *uris,
+ const gchar *icon,
+ const gchar *name,
+ const gchar *path,
+ gboolean terminal,
+ gint *argc,
+ gchar ***argv,
+ GError **error)
G_END_DECLS;
thunar_vfs_info_new_for_uri
thunar_vfs_info_ref
thunar_vfs_info_unref
+thunar_vfs_info_execute
thunar_vfs_info_get_hint
thunar_vfs_info_matches
thunar_vfs_info_list_free
thunar_vfs_mime_database_get_info_for_data
thunar_vfs_mime_database_get_info_for_name
thunar_vfs_mime_database_get_info_for_file
+thunar_vfs_mime_database_get_infos_for_info
thunar_vfs_mime_database_get_applications
thunar_vfs_mime_database_get_default_application
#endif
static void thunar_file_finalize (GObject *object);
static ThunarFile *thunar_file_real_get_parent (ThunarFile *file,
GError **error);
+static gboolean thunar_file_real_execute (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error);
static ThunarFolder *thunar_file_real_open_as_folder (ThunarFile *file,
GError **error);
static const gchar *thunar_file_real_get_special_name (ThunarFile *file);
-static gboolean thunar_file_real_can_execute (ThunarFile *file);
-static gboolean thunar_file_real_can_read (ThunarFile *file);
-static gboolean thunar_file_real_can_write (ThunarFile *file);
+static gboolean thunar_file_real_is_readable (ThunarFile *file);
+static gboolean thunar_file_real_is_writable (ThunarFile *file);
static void thunar_file_real_changed (ThunarFile *file);
static ThunarFile *thunar_file_new_internal (ThunarVfsURI *uri,
GError **error);
klass->has_parent = (gpointer) exo_noop_true;
klass->get_parent = thunar_file_real_get_parent;
+ klass->execute = thunar_file_real_execute;
klass->open_as_folder = thunar_file_real_open_as_folder;
klass->get_mime_info = (gpointer) exo_noop_null;
klass->get_special_name = thunar_file_real_get_special_name;
klass->get_volume = (gpointer) exo_noop_null;
klass->get_group = (gpointer) exo_noop_null;
klass->get_user = (gpointer) exo_noop_null;
- klass->can_execute = thunar_file_real_can_execute;
- klass->can_read = thunar_file_real_can_read;
- klass->can_write = thunar_file_real_can_write;
+ klass->is_executable = (gpointer) exo_noop_false;
+ klass->is_readable = thunar_file_real_is_readable;
+ klass->is_writable = thunar_file_real_is_writable;
klass->get_emblem_names = (gpointer) exo_noop_null;
klass->reload = (gpointer) exo_noop;
klass->changed = thunar_file_real_changed;
+static gboolean
+thunar_file_real_execute (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error)
+{
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (ENOEXEC), g_strerror (ENOEXEC));
+ return FALSE;
+}
+
+
+
static ThunarFolder*
thunar_file_real_open_as_folder (ThunarFile *file,
GError **error)
static gboolean
-thunar_file_real_can_execute (ThunarFile *file)
-{
- return !thunar_file_denies_access_permission (file,
- THUNAR_VFS_FILE_MODE_USR_EXEC,
- THUNAR_VFS_FILE_MODE_GRP_EXEC,
- THUNAR_VFS_FILE_MODE_OTH_EXEC);
-}
-
-
-
-static gboolean
-thunar_file_real_can_read (ThunarFile *file)
+thunar_file_real_is_readable (ThunarFile *file)
{
return !thunar_file_denies_access_permission (file,
THUNAR_VFS_FILE_MODE_USR_READ,
static gboolean
-thunar_file_real_can_write (ThunarFile *file)
+thunar_file_real_is_writable (ThunarFile *file)
{
return !thunar_file_denies_access_permission (file,
THUNAR_VFS_FILE_MODE_USR_WRITE,
/**
+ * thunar_file_execute:
+ * @file : a #ThunarFile instance.
+ * @screen : a #GdkScreen.
+ * @uris : the list of #ThunarVfsURI<!---->s to supply to @file on
+ * execution.
+ * @error : return location for errors or %NULL.
+ *
+ * Tries to execute @file on the specified @screen. If @file is executable
+ * and could have been spawned successfully, %TRUE is returned, else %FALSE
+ * will be returned and @error will be set to point to the error location.
+ *
+ * Return value: %TRUE on success, else %FALSE.
+ **/
+gboolean
+thunar_file_execute (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error)
+{
+ g_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ return (*THUNAR_FILE_GET_CLASS (file)->execute) (file, screen, uris, error);
+}
+
+
+
+/**
* thunar_file_open_as_folder:
* @file : a #ThunarFile instance.
* @error : return location for errors or %NULL.
/**
- * thunar_file_can_execute:
+ * thunar_file_is_executable:
* @file : a #ThunarFile instance.
*
* Determines whether the owner of the current process is allowed
* @file).
*
* If the specific #ThunarFile implementation does not provide
- * a custom #thunar_file_can_execute() method, the fallback
+ * a custom #thunar_file_is_executable() method, the fallback
* method provided by #ThunarFile is used, which determines
* whether the @file can be executed based on the data provided
* by #thunar_file_get_mode(), #thunar_file_get_user() and
* Return value: %TRUE if @file can be executed.
**/
gboolean
-thunar_file_can_execute (ThunarFile *file)
+thunar_file_is_executable (ThunarFile *file)
{
g_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
- return THUNAR_FILE_GET_CLASS (file)->can_execute (file);
+ return THUNAR_FILE_GET_CLASS (file)->is_executable (file);
}
/**
- * thunar_file_can_read:
+ * thunar_file_is_readable:
* @file : a #ThunarFile instance.
*
* Determines whether the owner of the current process is allowed
* to read the @file.
*
* If the specific #ThunarFile implementation does not provide
- * a custom #thunar_file_can_read() method, the fallback
+ * a custom #thunar_file_is_readable() method, the fallback
* method provided by #ThunarFile is used, which determines
* whether the @file can be read based on the data provided
* by #thunar_file_get_mode(), #thunar_file_get_user() and
* Return value: %TRUE if @file can be read.
**/
gboolean
-thunar_file_can_read (ThunarFile *file)
+thunar_file_is_readable (ThunarFile *file)
{
g_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
- return THUNAR_FILE_GET_CLASS (file)->can_read (file);
+ return THUNAR_FILE_GET_CLASS (file)->is_readable (file);
}
/**
- * thunar_file_can_write:
+ * thunar_file_is_writable:
* @file : a #ThunarFile instance.
*
* Determines whether the owner of the current process is allowed
* to write the @file.
*
* If the specific #ThunarFile implementation does not provide
- * a custom #thunar_file_can_write() method, the fallback
+ * a custom #thunar_file_is_writable() method, the fallback
* method provided by #ThunarFile is used, which determines
* whether the @file can be written based on the data provided
* by #thunar_file_get_mode(), #thunar_file_get_user() and
* Return value: %TRUE if @file can be read.
**/
gboolean
-thunar_file_can_write (ThunarFile *file)
+thunar_file_is_writable (ThunarFile *file)
{
g_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
- return THUNAR_FILE_GET_CLASS (file)->can_write (file);
+ return THUNAR_FILE_GET_CLASS (file)->is_writable (file);
}
ThunarFile *(*get_parent) (ThunarFile *file,
GError **error);
+ gboolean (*execute) (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error);
+
ThunarFolder *(*open_as_folder) (ThunarFile *file,
GError **error);
ThunarVfsGroup *(*get_group) (ThunarFile *file);
ThunarVfsUser *(*get_user) (ThunarFile *file);
- gboolean (*can_execute) (ThunarFile *file);
- gboolean (*can_read) (ThunarFile *file);
- gboolean (*can_write) (ThunarFile *file);
+ gboolean (*is_executable) (ThunarFile *file);
+ gboolean (*is_readable) (ThunarFile *file);
+ gboolean (*is_writable) (ThunarFile *file);
GList *(*get_emblem_names) (ThunarFile *file);
const gchar *(*get_icon_name) (ThunarFile *file,
ThunarFile *thunar_file_get_parent (ThunarFile *file,
GError **error);
+gboolean thunar_file_execute (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error);
+
ThunarFolder *thunar_file_open_as_folder (ThunarFile *file,
GError **error);
ThunarVfsGroup *thunar_file_get_group (ThunarFile *file);
ThunarVfsUser *thunar_file_get_user (ThunarFile *file);
-gboolean thunar_file_can_execute (ThunarFile *file);
-gboolean thunar_file_can_read (ThunarFile *file);
-gboolean thunar_file_can_write (ThunarFile *file);
+gboolean thunar_file_is_executable (ThunarFile *file);
+gboolean thunar_file_is_readable (ThunarFile *file);
+gboolean thunar_file_is_writable (ThunarFile *file);
GList *thunar_file_get_emblem_names (ThunarFile *file);
GdkPixbuf *thunar_file_load_icon (ThunarFile *file,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
+static void thunar_launcher_execute_files (ThunarLauncher *launcher,
+ GList *files);
static void thunar_launcher_open_uris (ThunarVfsMimeApplication *application,
GList *uri_list,
ThunarLauncher *launcher);
static void
+thunar_launcher_execute_files (ThunarLauncher *launcher,
+ GList *files)
+{
+ ThunarFile *file;
+ GtkWidget *message;
+ GtkWidget *window;
+ GdkScreen *screen;
+ GError *error = NULL;
+ GList *lp;
+
+ /* determine the screen on which to run the file(s) */
+ screen = (launcher->widget != NULL)
+ ? gtk_widget_get_screen (launcher->widget)
+ : gdk_screen_get_default ();
+
+ /* execute all selected files */
+ for (lp = files; lp != NULL; lp = lp->next)
+ {
+ file = THUNAR_FILE (lp->data);
+ if (!thunar_file_execute (file, screen, NULL, &error))
+ {
+ window = (launcher->widget != NULL) ? gtk_widget_get_toplevel (launcher->widget) : NULL;
+ message = gtk_message_dialog_new ((GtkWindow *) window,
+ GTK_DIALOG_MODAL
+ | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Unable to execute file \"%s\"."),
+ thunar_file_get_display_name (file));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message), "%s.", error->message);
+ gtk_dialog_run (GTK_DIALOG (message));
+ gtk_widget_destroy (message);
+ g_error_free (error);
+ break;
+ }
+ }
+}
+
+
+
+static void
thunar_launcher_open_uris (ThunarVfsMimeApplication *application,
GList *uri_list,
ThunarLauncher *launcher)
GList *lp;
gchar text[256];
gint n_directories = 0;
+ gint n_executables = 0;
gint n_files = 0;
gint n_items;
for (lp = items; lp != NULL; lp = lp->next)
{
if (thunar_file_is_directory (lp->data))
- ++n_directories;
+ {
+ ++n_directories;
+ }
else
- ++n_files;
+ {
+ if (thunar_file_is_executable (lp->data))
+ ++n_executables;
+ ++n_files;
+ }
}
if (G_UNLIKELY (n_directories == n_items))
}
else
{
+ /* we turn the "Open" label into "Execute" if we have only one executable file */
+ if (G_UNLIKELY (n_executables == 1 && n_items == 1))
+ g_object_set (G_OBJECT (launcher->action_open), "label", _("Execute"), NULL);
+ else
+ g_object_set (G_OBJECT (launcher->action_open), "label", _("Open"), NULL);
+
#if GTK_CHECK_VERSION(2,6,0)
gtk_action_set_sensitive (launcher->action_open, (n_items > 0));
gtk_action_set_visible (launcher->action_open_in_new_window, FALSE);
/* open the files */
if (files != NULL)
{
- thunar_launcher_open_files (launcher, files);
+ if (g_list_length (files) == 1 && thunar_file_is_executable (files->data))
+ thunar_launcher_execute_files (launcher, files);
+ else
+ thunar_launcher_open_files (launcher, files);
thunar_file_list_free (files);
}
}
static void thunar_local_file_finalize (GObject *object);
static ThunarFile *thunar_local_file_get_parent (ThunarFile *file,
GError **error);
+static gboolean thunar_local_file_execute (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error);
static ThunarFolder *thunar_local_file_open_as_folder (ThunarFile *file,
GError **error);
static ThunarVfsURI *thunar_local_file_get_uri (ThunarFile *file);
ThunarVfsVolumeManager *volume_manager);
static ThunarVfsGroup *thunar_local_file_get_group (ThunarFile *file);
static ThunarVfsUser *thunar_local_file_get_user (ThunarFile *file);
+static gboolean thunar_local_file_is_executable (ThunarFile *file);
static GList *thunar_local_file_get_emblem_names (ThunarFile *file);
static const gchar *thunar_local_file_get_icon_name (ThunarFile *file,
GtkIconTheme *icon_theme);
thunarfile_class = THUNAR_FILE_CLASS (klass);
thunarfile_class->get_parent = thunar_local_file_get_parent;
+ thunarfile_class->execute = thunar_local_file_execute;
thunarfile_class->open_as_folder = thunar_local_file_open_as_folder;
thunarfile_class->get_uri = thunar_local_file_get_uri;
thunarfile_class->get_mime_info = thunar_local_file_get_mime_info;
thunarfile_class->get_volume = thunar_local_file_get_volume;
thunarfile_class->get_group = thunar_local_file_get_group;
thunarfile_class->get_user = thunar_local_file_get_user;
+ thunarfile_class->is_executable = thunar_local_file_is_executable;
thunarfile_class->get_emblem_names = thunar_local_file_get_emblem_names;
thunarfile_class->get_icon_name = thunar_local_file_get_icon_name;
thunarfile_class->watch = thunar_local_file_watch;
+static gboolean
+thunar_local_file_execute (ThunarFile *file,
+ GdkScreen *screen,
+ GList *uris,
+ GError **error)
+{
+ ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
+ return thunar_vfs_info_execute (local_file->info, screen, uris, error);
+}
+
+
+
static ThunarFolder*
thunar_local_file_open_as_folder (ThunarFile *file,
GError **error)
+static gboolean
+thunar_local_file_is_executable (ThunarFile *file)
+{
+ ThunarLocalFile *local_file = THUNAR_LOCAL_FILE (file);
+ return ((local_file->info->flags & THUNAR_VFS_FILE_FLAGS_EXECUTABLE) != 0);
+}
+
+
+
static GList*
thunar_local_file_get_emblem_names (ThunarFile *file)
{
if ((info->flags & THUNAR_VFS_FILE_FLAGS_SYMLINK) != 0)
emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_SYMBOLIC_LINK);
- if (!thunar_file_can_read (file))
+ if (!thunar_file_is_readable (file))
emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_CANT_READ);
return emblems;
/* check whether the folder displayed by the view is writable */
current_directory = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (standard_view));
- writable = (current_directory != NULL && thunar_file_can_write (current_directory));
+ writable = (current_directory != NULL && thunar_file_is_writable (current_directory));
/* check whether the clipboard contains data that can be pasted here */
pastable = (standard_view->clipboard != NULL && thunar_clipboard_manager_get_can_paste (standard_view->clipboard));
* folder to which we can paste to */
can_paste_into_folder = (n_selected_files == 1)
&& thunar_file_is_directory (selected_files->data)
- && thunar_file_can_write (selected_files->data);
+ && thunar_file_is_writable (selected_files->data);
/* update the "Properties" action */
gtk_action_set_sensitive (standard_view->priv->action_properties, (n_selected_files == 1));