4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* type.c - code for dealing with filetypes */
31 #include <sys/param.h>
33 #include <sys/types.h>
37 # include <libgnomevfs/gnome-vfs.h>
38 # include <libgnomevfs/gnome-vfs-mime.h>
39 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
40 # include <libgnomevfs/gnome-vfs-application-registry.h>
50 #include "gui_support.h"
58 #include "action.h" /* (for action_chmod) */
62 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
63 enum {SET_MEDIA
, SET_TYPE
};
65 /* Colours for file types (same order as base types) */
66 static gchar
*opt_type_colours
[][2] = {
67 {"display_err_colour", "#ff0000"},
68 {"display_unkn_colour", "#000000"},
69 {"display_dir_colour", "#000080"},
70 {"display_pipe_colour", "#444444"},
71 {"display_sock_colour", "#ff00ff"},
72 {"display_file_colour", "#000000"},
73 {"display_cdev_colour", "#000000"},
74 {"display_bdev_colour", "#000000"},
75 {"display_door_colour", "#ff00ff"},
76 {"display_exec_colour", "#006000"},
77 {"display_adir_colour", "#006000"}
79 #define NUM_TYPE_COLOURS\
80 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
82 /* Parsed colours for file types */
83 static Option o_type_colours
[NUM_TYPE_COLOURS
];
84 static GdkColor type_colours
[NUM_TYPE_COLOURS
];
86 /* Static prototypes */
87 static void load_mime_types(void);
88 static void alloc_type_colours(void);
89 static char *get_action_save_path(GtkWidget
*dialog
);
90 static void edit_mime_types(guchar
*unused
);
91 static MIME_type
*get_mime_type(const gchar
*type_name
, gboolean can_create
);
92 static GList
*build_type_edit(Option
*none
, xmlNode
*node
, guchar
*label
);
93 static gboolean
remove_handler_with_confirm(const guchar
*path
);
95 /* When working out the type for a file, this hash table is checked
98 static GHashTable
*literal_hash
= NULL
; /* name -> MIME-type */
100 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
101 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
102 * The hash table is consulted from each dot in the string in turn
103 * (First .ps.gz, then try .gz)
105 static GHashTable
*extension_hash
= NULL
;
107 /* The first pattern in the list which matches is used */
108 typedef struct pattern
{
109 gint len
; /* Used for sorting */
113 static GPtrArray
*glob_patterns
= NULL
; /* [Pattern] */
115 /* Hash of all allocated MIME types, indexed by "media/subtype".
116 * MIME_type structs are never freed; this table prevents memory leaks
117 * when rereading the config files.
119 static GHashTable
*type_hash
= NULL
;
121 /* Most things on Unix are text files, so this is the default type */
122 MIME_type
*text_plain
;
123 MIME_type
*inode_directory
;
124 MIME_type
*inode_pipe
;
125 MIME_type
*inode_socket
;
126 MIME_type
*inode_block_dev
;
127 MIME_type
*inode_char_dev
;
128 MIME_type
*application_executable
;
129 MIME_type
*inode_unknown
;
130 MIME_type
*inode_door
;
132 static Option o_display_colour_types
;
138 extension_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
139 type_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
141 text_plain
= get_mime_type("text/plain", TRUE
);
142 inode_directory
= get_mime_type("inode/directory", TRUE
);
143 inode_pipe
= get_mime_type("inode/fifo", TRUE
);
144 inode_socket
= get_mime_type("inode/socket", TRUE
);
145 inode_block_dev
= get_mime_type("inode/blockdevice", TRUE
);
146 inode_char_dev
= get_mime_type("inode/chardevice", TRUE
);
147 application_executable
= get_mime_type("application/x-executable", TRUE
);
148 inode_unknown
= get_mime_type("inode/unknown", TRUE
);
149 inode_door
= get_mime_type("inode/door", TRUE
);
153 option_register_widget("type-edit", build_type_edit
);
155 option_add_int(&o_display_colour_types
, "display_colour_types", TRUE
);
157 for (i
= 0; i
< NUM_TYPE_COLOURS
; i
++)
158 option_add_string(&o_type_colours
[i
],
159 opt_type_colours
[i
][0],
160 opt_type_colours
[i
][1]);
161 alloc_type_colours();
163 option_add_notify(alloc_type_colours
);
166 /* Read-load all the glob patterns */
167 void reread_mime_files(void)
172 /* Returns the MIME_type structure for the given type name. It is looked
173 * up in type_hash and returned if found. If not found (and can_create is
174 * TRUE) then a new MIME_type is made, added to type_hash and returned.
175 * NULL is returned if type_name is not in type_hash and can_create is
176 * FALSE, or if type_name does not contain a '/' character.
178 static MIME_type
*get_mime_type(const gchar
*type_name
, gboolean can_create
)
183 mtype
= g_hash_table_lookup(type_hash
, type_name
);
184 if (mtype
|| !can_create
)
187 slash
= strchr(type_name
, '/');
188 g_return_val_if_fail(slash
!= NULL
, NULL
); /* XXX: Report nicely */
190 mtype
= g_new(MIME_type
, 1);
191 mtype
->media_type
= g_strndup(type_name
, slash
- type_name
);
192 mtype
->subtype
= g_strdup(slash
+ 1);
194 mtype
->comment
= NULL
;
196 g_hash_table_insert(type_hash
, g_strdup(type_name
), mtype
);
201 const char *basetype_name(DirItem
*item
)
203 if (item
->flags
& ITEM_FLAG_SYMLINK
)
204 return _("Sym link");
205 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
206 return _("Mount point");
207 else if (item
->flags
& ITEM_FLAG_APPDIR
)
210 switch (item
->base_type
)
216 case TYPE_CHAR_DEVICE
:
217 return _("Char dev");
218 case TYPE_BLOCK_DEVICE
:
219 return _("Block dev");
231 /* MIME-type guessing */
233 /* Get the type of this file - stats the file and uses that if
234 * possible. For regular or missing files, uses the pathname.
236 MIME_type
*type_get_type(const guchar
*path
)
239 MIME_type
*type
= NULL
;
240 int base
= TYPE_FILE
;
241 gboolean exec
= FALSE
;
243 if (mc_stat(path
, &info
) == 0)
245 base
= mode_to_base_type(info
.st_mode
);
246 if (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
250 if (base
== TYPE_FILE
)
251 type
= type_from_path(path
);
255 if (base
== TYPE_FILE
&& exec
)
256 type
= application_executable
;
258 type
= mime_type_from_base_type(base
);
264 /* Returns a pointer to the MIME-type.
265 * NULL if we can't think of anything.
267 MIME_type
*type_from_path(const char *path
)
269 const char *ext
, *dot
, *leafname
;
271 MIME_type
*type
= NULL
;
275 # ifdef WITH_GNOMEVFS
276 if (o_use_gnomevfs
.int_value
)
277 return get_mime_type(gnome_vfs_mime_type_from_name(path
), TRUE
);
281 leafname
= g_basename(path
);
283 type
= g_hash_table_lookup(literal_hash
, leafname
);
286 lower
= g_utf8_strdown(leafname
, -1);
287 type
= g_hash_table_lookup(literal_hash
, lower
);
293 while ((dot
= strchr(ext
, '.')))
297 type
= g_hash_table_lookup(extension_hash
, ext
);
302 type
= g_hash_table_lookup(extension_hash
,
303 lower
+ (ext
- leafname
));
308 for (i
= 0; i
< glob_patterns
->len
; i
++)
310 Pattern
*p
= glob_patterns
->pdata
[i
];
312 if (fnmatch(p
->glob
, leafname
, 0) == 0 ||
313 fnmatch(p
->glob
, lower
, 0) == 0)
326 /* Returns the file/dir in Choices for handling this type.
327 * NULL if there isn't one. g_free() the result.
329 static char *handler_for(MIME_type
*type
)
334 type_name
= g_strconcat(type
->media_type
, "_", type
->subtype
, NULL
);
335 open
= choices_find_path_load(type_name
, "MIME-types");
339 open
= choices_find_path_load(type
->media_type
, "MIME-types");
344 /* Actions for types */
346 gboolean
type_open(const char *path
, MIME_type
*type
)
348 gchar
*argv
[] = {NULL
, NULL
, NULL
};
353 argv
[1] = (char *) path
;
355 open
= handler_for(type
);
359 if (stat(open
, &info
))
361 report_error("stat(%s): %s", open
, g_strerror(errno
));
366 if (info
.st_mode
& S_IWOTH
)
371 report_error(_("Executable '%s' is world-writeable! Refusing "
372 "to run. Please change the permissions now (this "
373 "problem may have been caused by a bug in earlier "
374 "versions of the filer).\n\n"
375 "Having (non-symlink) run actions world-writeable "
376 "means that other people who use your computer can "
377 "replace your run actions with malicious versions.\n\n"
378 "If you trust everyone who could write to these files "
379 "then you needn't worry. Otherwise, you should check, "
380 "or even just delete, all the existing run actions."),
382 choices_dir
= g_path_get_dirname(open
);
383 paths
= g_list_append(NULL
, choices_dir
);
384 action_chmod(paths
, TRUE
, _("go-w (Fix security problem)"));
391 if (S_ISDIR(info
.st_mode
))
392 argv
[0] = g_strconcat(open
, "/AppRun", NULL
);
396 retval
= rox_spawn(home_dir
, (const gchar
**) argv
);
406 /* Return the image for this type, loading it if needed.
407 * Places to check are: (eg type="text_plain", base="text")
408 * 1. Choices:MIME-icons/<type>
409 * 2. Choices:MIME-icons/<base>
410 * 3. Unknown type icon.
412 * Note: You must g_object_unref() the image afterwards.
414 MaskedPixmap
*type_to_icon(MIME_type
*type
)
422 g_object_ref(im_unknown
);
427 /* Already got an image? */
430 /* Yes - don't recheck too often */
431 if (abs(now
- type
->image_time
) < 2)
433 g_object_ref(type
->image
);
436 g_object_unref(type
->image
);
440 type_name
= g_strconcat(type
->media_type
, "_",
441 type
->subtype
, ".png", NULL
);
442 path
= choices_find_path_load(type_name
, "MIME-icons");
445 strcpy(type_name
+ strlen(type
->media_type
), ".png");
446 path
= choices_find_path_load(type_name
, "MIME-icons");
453 type
->image
= g_fscache_lookup(pixmap_cache
, path
);
459 /* One ref from the type structure, one returned */
460 type
->image
= im_unknown
;
461 g_object_ref(im_unknown
);
464 type
->image_time
= now
;
466 g_object_ref(type
->image
);
470 GdkAtom
type_to_atom(MIME_type
*type
)
475 g_return_val_if_fail(type
!= NULL
, GDK_NONE
);
477 str
= g_strconcat(type
->media_type
, "/", type
->subtype
, NULL
);
478 retval
= gdk_atom_intern(str
, FALSE
);
484 static void show_shell_help(gpointer data
)
486 info_message(_("Enter a shell command which will load \"$@\" into "
487 "a suitable program. Eg:\n\n"
491 /* Called if the user clicks on the OK button. Returns FALSE if an error
492 * was displayed instead of performing the action.
494 static gboolean
set_shell_action(GtkWidget
*dialog
)
497 const guchar
*command
;
502 entry
= g_object_get_data(G_OBJECT(dialog
), "shell_command");
504 g_return_val_if_fail(entry
!= NULL
, FALSE
);
506 command
= gtk_entry_get_text(entry
);
508 if (!strchr(command
, '$'))
510 show_shell_help(NULL
);
514 path
= get_action_save_path(dialog
);
518 tmp
= g_strdup_printf("#! /bin/sh\nexec %s\n", command
);
521 fd
= open(path
, O_CREAT
| O_WRONLY
, 0755);
528 file
= fdopen(fd
, "w");
531 if (fwrite(tmp
, 1, len
, file
) < len
)
533 if (fclose(file
) && error
== 0)
541 report_error(g_strerror(error
));
546 gtk_widget_destroy(dialog
);
551 static void set_action_response(GtkWidget
*dialog
, gint response
, gpointer data
)
553 if (response
== GTK_RESPONSE_OK
)
554 if (!set_shell_action(dialog
))
556 gtk_widget_destroy(dialog
);
559 /* Return the path of the file in choices that handles this type and
561 * NULL if nothing is defined for it.
563 static guchar
*handler_for_radios(GObject
*dialog
)
568 radios
= g_object_get_data(G_OBJECT(dialog
), "rox-radios");
569 type
= g_object_get_data(G_OBJECT(dialog
), "mime_type");
571 g_return_val_if_fail(radios
!= NULL
, NULL
);
572 g_return_val_if_fail(type
!= NULL
, NULL
);
574 switch (radios_get_value(radios
))
577 return choices_find_path_load(type
->media_type
,
581 gchar
*tmp
, *handler
;
582 tmp
= g_strconcat(type
->media_type
, "_",
583 type
->subtype
, NULL
);
584 handler
= choices_find_path_load(tmp
, "MIME-types");
589 g_warning("Bad type");
594 static void run_action_update(gpointer data
)
598 GObject
*dialog
= G_OBJECT(data
);
600 drop_box
= g_object_get_data(dialog
, "rox-dropbox");
602 g_return_if_fail(drop_box
!= NULL
);
604 handler
= handler_for_radios(dialog
);
610 handler
= readlink_dup(old
);
617 drop_box_set_path(DROP_BOX(drop_box
), handler
);
621 static void clear_run_action(GtkWidget
*drop_box
, GtkWidget
*dialog
)
625 handler
= handler_for_radios(G_OBJECT(dialog
));
628 remove_handler_with_confirm(handler
);
630 run_action_update(dialog
);
633 /* Called when a URI list is dropped onto the box in the Set Run Action
634 * dialog. Make sure it's an application, and make that the default
637 static void drag_app_dropped(GtkWidget
*drop_box
,
643 item
= diritem_new("");
644 diritem_restat(app
, item
, NULL
);
645 if (item
->flags
& (ITEM_FLAG_APPDIR
| ITEM_FLAG_EXEC_FILE
))
649 path
= get_action_save_path(dialog
);
653 if (symlink(app
, path
))
654 delayed_error("symlink: %s",
657 destroy_on_idle(dialog
);
664 _("This is not a program! Give me an application "
670 /* Find the current command which is used to run files of this type.
671 * Returns NULL on failure. g_free() the result.
673 static guchar
*get_current_command(MIME_type
*type
)
676 char *handler
, *nl
, *data
= NULL
;
678 guchar
*command
= NULL
;
680 handler
= handler_for(type
);
683 return NULL
; /* No current handler */
685 if (stat(handler
, &info
))
686 goto out
; /* Can't stat */
688 if ((!S_ISREG(info
.st_mode
)) || info
.st_size
> 256)
689 goto out
; /* Only use small regular files */
691 if (!load_file(handler
, &data
, &len
))
692 goto out
; /* Didn't load OK */
694 if (strncmp(data
, "#! /bin/sh\nexec ", 16) != 0)
695 goto out
; /* Not one of ours */
697 nl
= strchr(data
+ 16, '\n');
699 goto out
; /* No newline! */
701 command
= g_strndup(data
+ 16, nl
- data
- 16);
708 /* Find the current command which is used to run files of this type,
709 * and return a textual description of it.
710 * g_free() the result.
712 gchar
*describe_current_command(MIME_type
*type
)
719 g_return_val_if_fail(type
!= NULL
, NULL
);
721 if (type
== application_executable
)
722 return g_strdup(_("Execute file"));
724 handler
= handler_for(type
);
727 return g_strdup(_("No run action defined"));
729 target
= readlink_dup(handler
);
732 /* Cope with relative paths (shouldn't normally be needed) */
734 if (target
[0] == '/')
743 dir
= g_path_get_dirname(handler
);
745 handler
= g_strconcat(dir
, "/", target
, NULL
);
751 if (mc_stat(handler
, &info
) !=0 )
753 desc
= g_strdup_printf(_("Error in handler %s: %s"), handler
,
758 if (S_ISDIR(info
.st_mode
))
761 uid_t dir_uid
= info
.st_uid
;
763 tmp
= make_path(handler
, "AppRun");
765 if (mc_lstat(tmp
, &info
) != 0 || info
.st_uid
!= dir_uid
766 || !(info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)))
767 desc
= g_strdup_printf(
768 _("Invalid application %s (bad AppRun)"),
770 /* Else, just report handler... */
775 /* It's not an application directory, and it's not a symlink... */
777 if (access(handler
, X_OK
) != 0)
779 desc
= g_strdup_printf(_("Non-executable %s"), handler
);
783 desc
= get_current_command(type
);
793 /* Display a dialog box allowing the user to set the default run action
796 void type_set_handler_dialog(MIME_type
*type
)
800 GtkWidget
*frame
, *entry
, *label
;
804 g_return_if_fail(type
!= NULL
);
806 dialog
= GTK_DIALOG(gtk_dialog_new());
807 gtk_dialog_set_has_separator(dialog
, FALSE
);
808 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
810 g_object_set_data(G_OBJECT(dialog
), "mime_type", type
);
812 gtk_window_set_title(GTK_WINDOW(dialog
), _("Set run action"));
814 radios
= radios_new(run_action_update
, dialog
);
815 g_object_set_data(G_OBJECT(dialog
), "rox-radios", radios
);
818 _("If a handler for the specific type isn't set up, "
819 "use this as the default."), SET_MEDIA
,
820 _("Set default for all `%s/<anything>'"),
824 _("Use this application for all files with this MIME "
826 _("Only for the type `%s' (%s/%s)"),
827 mime_type_comment(type
),
828 type
->media_type
, type
->subtype
);
830 radios_set_value(radios
, SET_TYPE
);
832 frame
= drop_box_new(_("Drop a suitable application here"));
834 g_object_set_data(G_OBJECT(dialog
), "rox-dropbox", frame
);
836 radios_pack(radios
, GTK_BOX(dialog
->vbox
));
837 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), frame
, TRUE
, TRUE
, 0);
839 g_signal_connect(frame
, "path_dropped",
840 G_CALLBACK(drag_app_dropped
), dialog
);
841 g_signal_connect(frame
, "clear",
842 G_CALLBACK(clear_run_action
), dialog
);
844 hbox
= gtk_hbox_new(FALSE
, 4);
845 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 4);
846 gtk_box_pack_start(GTK_BOX(hbox
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
847 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_("OR")),
849 gtk_box_pack_start(GTK_BOX(hbox
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
851 hbox
= gtk_hbox_new(FALSE
, 4);
852 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 0);
854 label
= gtk_label_new(_("Enter a shell command:")),
855 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
856 gtk_box_pack_start(GTK_BOX(hbox
), label
, TRUE
, TRUE
, 4);
858 gtk_box_pack_start(GTK_BOX(hbox
),
859 new_help_button(show_shell_help
, NULL
), FALSE
, TRUE
, 0);
861 entry
= gtk_entry_new();
862 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), entry
, FALSE
, TRUE
, 0);
863 gtk_widget_grab_focus(entry
);
864 g_object_set_data(G_OBJECT(dialog
), "shell_command", entry
);
865 gtk_entry_set_activates_default(GTK_ENTRY(entry
), TRUE
);
867 /* If possible, fill in the entry box with the current command */
868 tmp
= get_current_command(type
);
871 gtk_entry_set_text(GTK_ENTRY(entry
), tmp
);
872 gtk_editable_set_position(GTK_EDITABLE(entry
), -1);
877 gtk_entry_set_text(GTK_ENTRY(entry
), " \"$@\"");
878 gtk_editable_set_position(GTK_EDITABLE(entry
), 0);
881 gtk_dialog_add_buttons(dialog
,
882 GTK_STOCK_CANCEL
, GTK_RESPONSE_DELETE_EVENT
,
883 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
886 hbox
= gtk_hbox_new(TRUE
, 4);
887 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 0);
889 gtk_dialog_set_default_response(dialog
, GTK_RESPONSE_OK
);
891 g_signal_connect(dialog
, "response",
892 G_CALLBACK(set_action_response
), NULL
);
894 gtk_widget_show_all(GTK_WIDGET(dialog
));
897 /* path is an entry in Choices. If it's a symlink or a very small executable
898 * then just get rid of it, otherwise confirm first. It it doesn't exist,
901 * FALSE on error (abort operation).
903 static gboolean
remove_handler_with_confirm(const guchar
*path
)
907 if (lstat(path
, &info
) == 0)
909 /* A binding already exists... */
910 if (S_ISREG(info
.st_mode
) && info
.st_size
> 256)
912 if (!confirm(_("A run action already exists and is "
913 "quite a big program - are you sure "
914 "you want to delete it?"),
915 GTK_STOCK_DELETE
, NULL
))
923 report_error(_("Can't remove %s: %s"),
924 path
, g_strerror(errno
));
932 /* The user wants to set a new default action for files of this type (or just
933 * clear the action). Removes the current binding if possible and returns the
934 * path to save the new one to. NULL means cancel. g_free() the result.
936 static char *get_action_save_path(GtkWidget
*dialog
)
939 guchar
*type_name
= NULL
;
943 g_return_val_if_fail(dialog
!= NULL
, NULL
);
945 type
= g_object_get_data(G_OBJECT(dialog
), "mime_type");
946 radios
= g_object_get_data(G_OBJECT(dialog
), "rox-radios");
948 g_return_val_if_fail(radios
!= NULL
&& type
!= NULL
, NULL
);
950 if (radios_get_value(radios
) == SET_MEDIA
)
951 type_name
= g_strdup(type
->media_type
);
953 type_name
= g_strconcat(type
->media_type
, "_",
954 type
->subtype
, NULL
);
956 path
= choices_find_path_save("", PROJECT
, FALSE
);
960 _("Choices saving is disabled by CHOICESPATH variable"));
965 path
= choices_find_path_save(type_name
, "MIME-types", TRUE
);
967 if (!remove_handler_with_confirm(path
))
974 MIME_type
*mime_type_from_base_type(int base_type
)
981 return inode_directory
;
986 case TYPE_BLOCK_DEVICE
:
987 return inode_block_dev
;
988 case TYPE_CHAR_DEVICE
:
989 return inode_char_dev
;
993 return inode_unknown
;
996 /* Takes the st_mode field from stat() and returns the base type.
997 * Should not be a symlink.
999 int mode_to_base_type(int st_mode
)
1001 if (S_ISREG(st_mode
))
1003 else if (S_ISDIR(st_mode
))
1004 return TYPE_DIRECTORY
;
1005 else if (S_ISBLK(st_mode
))
1006 return TYPE_BLOCK_DEVICE
;
1007 else if (S_ISCHR(st_mode
))
1008 return TYPE_CHAR_DEVICE
;
1009 else if (S_ISFIFO(st_mode
))
1011 else if (S_ISSOCK(st_mode
))
1013 else if (S_ISDOOR(st_mode
))
1019 /* Returns TRUE is this is something that is run by looking up its type
1020 * in MIME-types and, hence, can have its run action set.
1022 gboolean
can_set_run_action(DirItem
*item
)
1024 g_return_val_if_fail(item
!= NULL
, FALSE
);
1026 return item
->base_type
== TYPE_FILE
&&
1027 !(item
->mime_type
== application_executable
);
1030 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
1031 static void edit_mime_types(guchar
*unused
)
1035 mkdir(make_path(home_dir
, ".mime"), 0700);
1036 path
= make_path(home_dir
, ".mime/packages");
1038 filer_opendir(path
, NULL
, NULL
);
1040 path
= "/usr/local/share/mime/packages";
1041 if (file_exists(path
))
1042 filer_opendir(path
, NULL
, NULL
);
1044 path
= "/usr/share/mime/packages";
1045 if (file_exists(path
))
1046 filer_opendir(path
, NULL
, NULL
);
1049 static GList
*build_type_edit(Option
*none
, xmlNode
*node
, guchar
*label
)
1051 GtkWidget
*button
, *align
;
1053 g_return_val_if_fail(none
== NULL
, NULL
);
1055 align
= gtk_alignment_new(0.1, 0, 0.1, 0);
1056 button
= gtk_button_new_with_label(_(label
));
1057 gtk_container_add(GTK_CONTAINER(align
), button
);
1059 g_signal_connect_swapped(button
, "clicked",
1060 G_CALLBACK(edit_mime_types
), NULL
);
1062 return g_list_append(NULL
, align
);
1065 /* Parse file type colours and allocate/free them as necessary */
1066 static void alloc_type_colours(void)
1068 gboolean success
[NUM_TYPE_COLOURS
];
1069 int change_count
= 0; /* No. needing realloc */
1071 static gboolean allocated
= FALSE
;
1074 for (i
= 0; i
< NUM_TYPE_COLOURS
; i
++)
1076 GdkColor
*c
= &type_colours
[i
];
1078 gushort g
= c
->green
;
1079 gushort b
= c
->blue
;
1081 gdk_color_parse(o_type_colours
[i
].value
, &type_colours
[i
]);
1083 if (allocated
&& (c
->red
!= r
|| c
->green
!= g
|| c
->blue
!= b
))
1087 /* Free colours if they were previously allocated and
1088 * have changed or become unneeded.
1090 if (allocated
&& (change_count
|| !o_display_colour_types
.int_value
))
1092 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1093 type_colours
, NUM_TYPE_COLOURS
);
1097 /* Allocate colours, unless they are still allocated (=> they didn't
1098 * change) or we don't want them anymore.
1099 * XXX: what should be done if allocation fails?
1101 if (!allocated
&& o_display_colour_types
.int_value
)
1103 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1104 type_colours
, NUM_TYPE_COLOURS
,
1105 FALSE
, TRUE
, success
);
1110 /* Return a pointer to a (static) colour for this item. If colouring is
1111 * off, returns normal.
1113 GdkColor
*type_get_colour(DirItem
*item
, GdkColor
*normal
)
1115 int type
= item
->base_type
;
1117 if (!o_display_colour_types
.int_value
)
1120 if (item
->flags
& ITEM_FLAG_EXEC_FILE
)
1122 else if (item
->flags
& ITEM_FLAG_APPDIR
)
1125 g_return_val_if_fail(type
>= 0 && type
< NUM_TYPE_COLOURS
, normal
);
1127 return &type_colours
[type
];
1130 /* Process the 'Patterns' value */
1131 static void add_pattern(MIME_type
*type
, const char *pattern
, GHashTable
*globs
)
1133 if (pattern
[0] == '*' && pattern
[1] == '.' &&
1134 strpbrk(pattern
+ 2, "*?[") == NULL
)
1136 g_hash_table_insert(extension_hash
,
1137 g_strdup(pattern
+ 2),
1140 else if (strpbrk(pattern
, "*?[") == NULL
)
1141 g_hash_table_insert(literal_hash
, g_strdup(pattern
), type
);
1143 g_hash_table_insert(globs
, g_strdup(pattern
), type
);
1146 /* Load and parse this file. literal_hash and extension_hash are updated
1147 * directly. Other patterns are added to 'globs'.
1149 static void import_file(const gchar
*file
, GHashTable
*globs
)
1151 MIME_type
*type
= NULL
;
1152 GError
*error
= NULL
;
1155 if (access(file
, F_OK
) != 0)
1156 return; /* Doesn't exist. No problem. */
1158 if (!g_file_get_contents(file
, &data
, NULL
, &error
))
1160 delayed_error(_("Error loading MIME database:\n%s"),
1162 g_error_free(error
);
1168 while (line
&& *line
)
1172 nl
= strchr(line
, '\n');
1182 colon
= strchr(line
, ':');
1185 delayed_error(_("File '%s' corrupted!"), file
);
1189 name
= g_strndup(line
, colon
- line
);
1190 type
= get_mime_type(name
, TRUE
);
1193 g_warning("Invalid type in '%s'", file
);
1195 add_pattern(type
, colon
+ 1, globs
);
1204 static void add_to_glob_patterns(gpointer key
, gpointer value
, gpointer unused
)
1208 pattern
= g_new(Pattern
, 1);
1209 pattern
->glob
= g_strdup((gchar
*) key
);
1210 pattern
->type
= (MIME_type
*) value
;
1211 pattern
->len
= strlen(pattern
->glob
);
1213 g_ptr_array_add(glob_patterns
, pattern
);
1216 static gint
sort_by_strlen(gconstpointer a
, gconstpointer b
)
1218 const Pattern
*pa
= *(const Pattern
**) a
;
1219 const Pattern
*pb
= *(const Pattern
**) b
;
1221 if (pa
->len
> pb
->len
)
1223 else if (pa
->len
== pb
->len
)
1228 /* Clear all currently stored information and re-read everything */
1229 static void load_mime_types(void)
1235 glob_patterns
= g_ptr_array_new();
1240 for (i
= glob_patterns
->len
- 1; i
>= 0; i
--)
1242 Pattern
*p
= glob_patterns
->pdata
[i
];
1246 g_ptr_array_set_size(glob_patterns
, 0);
1250 g_hash_table_destroy(literal_hash
);
1252 g_hash_table_destroy(extension_hash
);
1253 literal_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1255 extension_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1257 globs
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
1259 import_file("/usr/share/mime/globs", globs
);
1260 import_file("/usr/local/share/mime/globs", globs
);
1262 tmp
= g_strconcat(home_dir
, "/.mime/globs", NULL
);
1263 import_file(tmp
, globs
);
1266 /* Turn the globs hash into a pointer array */
1267 g_hash_table_foreach(globs
, add_to_glob_patterns
, NULL
);
1268 g_hash_table_destroy(globs
);
1271 if (glob_patterns
->len
)
1272 g_ptr_array_sort(glob_patterns
, sort_by_strlen
);
1274 if (g_hash_table_size(extension_hash
) == 0)
1276 delayed_error(_("The standard MIME type database "
1277 "(version 0.9 or later) was not found. "
1278 "The filer will probably not show the correct "
1279 "types for different files. You should download and "
1280 "install the 'shared-mime-info-0.9' package from "
1282 "http://www.freedesktop.org/standards/shared-mime-info.html\n\n"
1283 "If you have already installed this package, check that the "
1284 "permissions allow the files to be read (check "
1285 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1291 #define MIME_TRY doc = xml_cache_load(path); g_free(path); if (doc) return doc;
1293 /* Find the media/subtype.xml file for a MIME_type and load it, returning
1294 * an XML document (NULL if not found)
1296 * g_object_unref() the result.
1298 static XMLwrapper
*load_type_file(MIME_type
*type
)
1303 path
= g_strdup_printf("%s/.mime/%s/%s.xml", home_dir
,
1304 type
->media_type
, type
->subtype
);
1307 path
= g_strdup_printf("/usr/local/share/mime/%s/%s.xml",
1308 type
->media_type
, type
->subtype
);
1311 path
= g_strdup_printf("/usr/share/mime/%s/%s.xml",
1312 type
->media_type
, type
->subtype
);
1318 static void find_comment(MIME_type
*type
)
1320 XMLwrapper
*typedoc
;
1323 typedoc
= load_type_file(type
);
1327 node
= xml_get_section(typedoc
, TYPE_NS
, "comment");
1332 g_free(type
->comment
);
1333 type
->comment
= xmlNodeListGetString(node
->doc
,
1334 node
->xmlChildrenNode
, 1);
1337 g_object_unref(typedoc
);
1340 type
->comment
= g_strdup(_("No description"));
1343 const char *mime_type_comment(MIME_type
*type
)
1348 return type
->comment
;