r2417: Merged icons and backdrop dropboxes into a single Ultimate DropBox widget,
[rox-filer.git] / ROX-Filer / src / type.c
bloba6cd6d11cb5abd43adc48fdb2e63e7e1678e5486
1 /*
2 * $Id$
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)
10 * any later version.
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
15 * more details.
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 */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <time.h>
31 #include <sys/param.h>
32 #include <fnmatch.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
36 #ifdef WITH_GNOMEVFS
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>
41 #endif
43 #include "global.h"
45 #include "string.h"
46 #include "fscache.h"
47 #include "main.h"
48 #include "pixmaps.h"
49 #include "run.h"
50 #include "gui_support.h"
51 #include "choices.h"
52 #include "type.h"
53 #include "support.h"
54 #include "diritem.h"
55 #include "dnd.h"
56 #include "options.h"
57 #include "filer.h"
58 #include "action.h" /* (for action_chmod) */
59 #include "xml.h"
61 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
62 enum {SET_MEDIA, SET_TYPE};
64 /* Colours for file types (same order as base types) */
65 static gchar *opt_type_colours[][2] = {
66 {"display_err_colour", "#ff0000"},
67 {"display_unkn_colour", "#000000"},
68 {"display_dir_colour", "#000080"},
69 {"display_pipe_colour", "#444444"},
70 {"display_sock_colour", "#ff00ff"},
71 {"display_file_colour", "#000000"},
72 {"display_cdev_colour", "#000000"},
73 {"display_bdev_colour", "#000000"},
74 {"display_door_colour", "#ff00ff"},
75 {"display_exec_colour", "#006000"},
76 {"display_adir_colour", "#006000"}
78 #define NUM_TYPE_COLOURS\
79 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
81 /* Parsed colours for file types */
82 static Option o_type_colours[NUM_TYPE_COLOURS];
83 static GdkColor type_colours[NUM_TYPE_COLOURS];
85 /* Static prototypes */
86 static void load_mime_types(void);
87 static void alloc_type_colours(void);
88 char *get_action_save_path(GtkWidget *dialog);
89 static void edit_mime_types(guchar *unused);
90 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
91 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label);
93 /* When working out the type for a file, this hash table is checked
94 * first...
96 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
98 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
99 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
100 * The hash table is consulted from each dot in the string in turn
101 * (First .ps.gz, then try .gz)
103 static GHashTable *extension_hash = NULL;
105 /* The first pattern in the list which matches is used */
106 typedef struct pattern {
107 gint len; /* Used for sorting */
108 gchar *glob;
109 MIME_type *type;
110 } Pattern;
111 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
113 /* Hash of all allocated MIME types, indexed by "media/subtype".
114 * MIME_type structs are never freed; this table prevents memory leaks
115 * when rereading the config files.
117 static GHashTable *type_hash = NULL;
119 /* Most things on Unix are text files, so this is the default type */
120 MIME_type *text_plain;
121 MIME_type *inode_directory;
122 MIME_type *inode_pipe;
123 MIME_type *inode_socket;
124 MIME_type *inode_block_dev;
125 MIME_type *inode_char_dev;
126 MIME_type *application_executable;
127 MIME_type *inode_unknown;
128 MIME_type *inode_door;
130 static Option o_display_colour_types;
132 void type_init(void)
134 int i;
136 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
137 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
139 text_plain = get_mime_type("text/plain", TRUE);
140 inode_directory = get_mime_type("inode/directory", TRUE);
141 inode_pipe = get_mime_type("inode/fifo", TRUE);
142 inode_socket = get_mime_type("inode/socket", TRUE);
143 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
144 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
145 application_executable = get_mime_type("application/x-executable", TRUE);
146 inode_unknown = get_mime_type("inode/unknown", TRUE);
147 inode_door = get_mime_type("inode/door", TRUE);
149 load_mime_types();
151 option_register_widget("type-edit", build_type_edit);
153 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
155 for (i = 0; i < NUM_TYPE_COLOURS; i++)
156 option_add_string(&o_type_colours[i],
157 opt_type_colours[i][0],
158 opt_type_colours[i][1]);
159 alloc_type_colours();
161 option_add_notify(alloc_type_colours);
164 /* Read-load all the glob patterns */
165 void reread_mime_files(void)
167 load_mime_types();
170 /* Returns the MIME_type structure for the given type name. It is looked
171 * up in type_hash and returned if found. If not found (and can_create is
172 * TRUE) then a new MIME_type is made, added to type_hash and returned.
173 * NULL is returned if type_name is not in type_hash and can_create is
174 * FALSE, or if type_name does not contain a '/' character.
176 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
178 MIME_type *mtype;
179 gchar *slash;
181 mtype = g_hash_table_lookup(type_hash, type_name);
182 if (mtype || !can_create)
183 return mtype;
185 slash = strchr(type_name, '/');
186 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
188 mtype = g_new(MIME_type, 1);
189 mtype->media_type = g_strndup(type_name, slash - type_name);
190 mtype->subtype = g_strdup(slash + 1);
191 mtype->image = NULL;
192 mtype->comment = NULL;
194 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
196 return mtype;
199 const char *basetype_name(DirItem *item)
201 if (item->flags & ITEM_FLAG_SYMLINK)
202 return _("Sym link");
203 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
204 return _("Mount point");
205 else if (item->flags & ITEM_FLAG_APPDIR)
206 return _("App dir");
208 switch (item->base_type)
210 case TYPE_FILE:
211 return _("File");
212 case TYPE_DIRECTORY:
213 return _("Dir");
214 case TYPE_CHAR_DEVICE:
215 return _("Char dev");
216 case TYPE_BLOCK_DEVICE:
217 return _("Block dev");
218 case TYPE_PIPE:
219 return _("Pipe");
220 case TYPE_SOCKET:
221 return _("Socket");
222 case TYPE_DOOR:
223 return _("Door");
226 return _("Unknown");
229 /* MIME-type guessing */
231 /* Get the type of this file - stats the file and uses that if
232 * possible. For regular or missing files, uses the pathname.
234 MIME_type *type_get_type(const guchar *path)
236 struct stat info;
237 MIME_type *type = NULL;
238 int base = TYPE_FILE;
239 gboolean exec = FALSE;
241 if (mc_stat(path, &info) == 0)
243 base = mode_to_base_type(info.st_mode);
244 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
245 exec = TRUE;
248 if (base == TYPE_FILE)
249 type = type_from_path(path);
251 if (!type)
253 if (base == TYPE_FILE && exec)
254 type = application_executable;
255 else
256 type = mime_type_from_base_type(base);
259 return type;
262 /* Returns a pointer to the MIME-type.
263 * NULL if we can't think of anything.
265 MIME_type *type_from_path(const char *path)
267 const char *ext, *dot, *leafname;
268 char *lower;
269 MIME_type *type = NULL;
270 int i;
272 #if 0
273 # ifdef WITH_GNOMEVFS
274 if (o_use_gnomevfs.int_value)
275 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
276 # endif
277 #endif
279 leafname = g_basename(path);
281 type = g_hash_table_lookup(literal_hash, leafname);
282 if (type)
283 return type;
284 lower = g_utf8_strdown(leafname, -1);
285 type = g_hash_table_lookup(literal_hash, lower);
286 if (type)
287 goto out;
289 ext = leafname;
291 while ((dot = strchr(ext, '.')))
293 ext = dot + 1;
295 type = g_hash_table_lookup(extension_hash, ext);
297 if (type)
298 goto out;
300 type = g_hash_table_lookup(extension_hash,
301 lower + (ext - leafname));
302 if (type)
303 goto out;
306 for (i = 0; i < glob_patterns->len; i++)
308 Pattern *p = glob_patterns->pdata[i];
310 if (fnmatch(p->glob, leafname, 0) == 0 ||
311 fnmatch(p->glob, lower, 0) == 0)
313 type = p->type;
314 goto out;
318 out:
319 g_free(lower);
321 return type;
324 /* Returns the file/dir in Choices for handling this type.
325 * NULL if there isn't one. g_free() the result.
327 static char *handler_for(MIME_type *type)
329 char *type_name;
330 char *open;
332 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
333 open = choices_find_path_load(type_name, "MIME-types");
334 g_free(type_name);
336 if (!open)
337 open = choices_find_path_load(type->media_type, "MIME-types");
339 return open;
342 /* Actions for types */
344 gboolean type_open(const char *path, MIME_type *type)
346 gchar *argv[] = {NULL, NULL, NULL};
347 char *open;
348 gboolean retval;
349 struct stat info;
351 argv[1] = (char *) path;
353 open = handler_for(type);
354 if (!open)
355 return FALSE;
357 if (stat(open, &info))
359 report_error("stat(%s): %s", open, g_strerror(errno));
360 g_free(open);
361 return FALSE;
364 if (info.st_mode & S_IWOTH)
366 gchar *choices_dir;
367 GList *paths;
369 report_error(_("Executable '%s' is world-writeable! Refusing "
370 "to run. Please change the permissions now (this "
371 "problem may have been caused by a bug in earlier "
372 "versions of the filer).\n\n"
373 "Having (non-symlink) run actions world-writeable "
374 "means that other people who use your computer can "
375 "replace your run actions with malicious versions.\n\n"
376 "If you trust everyone who could write to these files "
377 "then you needn't worry. Otherwise, you should check, "
378 "or even just delete, all the existing run actions."),
379 open);
380 choices_dir = g_path_get_dirname(open);
381 paths = g_list_append(NULL, choices_dir);
382 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
383 g_free(choices_dir);
384 g_list_free(paths);
385 g_free(open);
386 return TRUE;
389 if (S_ISDIR(info.st_mode))
390 argv[0] = g_strconcat(open, "/AppRun", NULL);
391 else
392 argv[0] = open;
394 retval = rox_spawn(home_dir, (const gchar **) argv);
396 if (argv[0] != open)
397 g_free(argv[0]);
399 g_free(open);
401 return retval;
404 /* Return the image for this type, loading it if needed.
405 * Places to check are: (eg type="text_plain", base="text")
406 * 1. Choices:MIME-icons/<type>
407 * 2. Choices:MIME-icons/<base>
408 * 3. Unknown type icon.
410 * Note: You must g_object_unref() the image afterwards.
412 MaskedPixmap *type_to_icon(MIME_type *type)
414 char *path;
415 char *type_name;
416 time_t now;
418 if (type == NULL)
420 g_object_ref(im_unknown);
421 return im_unknown;
424 now = time(NULL);
425 /* Already got an image? */
426 if (type->image)
428 /* Yes - don't recheck too often */
429 if (abs(now - type->image_time) < 2)
431 g_object_ref(type->image);
432 return type->image;
434 g_object_unref(type->image);
435 type->image = NULL;
438 type_name = g_strconcat(type->media_type, "_",
439 type->subtype, ".png", NULL);
440 path = choices_find_path_load(type_name, "MIME-icons");
441 if (!path)
443 strcpy(type_name + strlen(type->media_type), ".png");
444 path = choices_find_path_load(type_name, "MIME-icons");
447 g_free(type_name);
449 if (path)
451 type->image = g_fscache_lookup(pixmap_cache, path);
452 g_free(path);
455 if (!type->image)
457 /* One ref from the type structure, one returned */
458 type->image = im_unknown;
459 g_object_ref(im_unknown);
462 type->image_time = now;
464 g_object_ref(type->image);
465 return type->image;
468 GdkAtom type_to_atom(MIME_type *type)
470 char *str;
471 GdkAtom retval;
473 g_return_val_if_fail(type != NULL, GDK_NONE);
475 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
476 retval = gdk_atom_intern(str, FALSE);
477 g_free(str);
479 return retval;
482 static void show_shell_help(gpointer data)
484 info_message(_("Enter a shell command which will load \"$@\" into "
485 "a suitable program. Eg:\n\n"
486 "gimp \"$@\""));
489 /* Called if the user clicks on the OK button. Returns FALSE if an error
490 * was displayed instead of performing the action.
492 static gboolean set_shell_action(GtkWidget *dialog)
494 GtkEntry *entry;
495 const guchar *command;
496 gchar *tmp, *path;
497 int error = 0, len;
498 int fd;
500 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
502 g_return_val_if_fail(entry != NULL, FALSE);
504 command = gtk_entry_get_text(entry);
506 if (!strchr(command, '$'))
508 show_shell_help(NULL);
509 return FALSE;
512 path = get_action_save_path(dialog);
513 if (!path)
514 return FALSE;
516 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
517 len = strlen(tmp);
519 fd = open(path, O_CREAT | O_WRONLY, 0755);
520 if (fd == -1)
521 error = errno;
522 else
524 FILE *file;
526 file = fdopen(fd, "w");
527 if (file)
529 if (fwrite(tmp, 1, len, file) < len)
530 error = errno;
531 if (fclose(file) && error == 0)
532 error = errno;
534 else
535 error = errno;
538 if (error)
539 report_error(g_strerror(error));
541 g_free(tmp);
542 g_free(path);
544 gtk_widget_destroy(dialog);
546 return TRUE;
549 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
551 if (response == GTK_RESPONSE_OK)
552 if (!set_shell_action(dialog))
553 return;
554 gtk_widget_destroy(dialog);
557 /* Called when a URI list is dropped onto the box in the Set Run Action
558 * dialog. Make sure it's an application, and make that the default
559 * handler.
561 static void drag_app_dropped(GtkWidget *eb,
562 GdkDragContext *context,
563 gint x,
564 gint y,
565 GtkSelectionData *selection_data,
566 guint info,
567 guint32 time,
568 GtkWidget *dialog)
570 GList *uris;
571 const gchar *app = NULL;
572 DirItem *item;
574 if (!selection_data->data)
575 return; /* Timeout? */
577 uris = uri_list_to_glist(selection_data->data);
579 if (g_list_length(uris) == 1)
580 app = get_local_path((guchar *) uris->data);
581 g_list_free(uris);
583 if (!app)
585 delayed_error(
586 _("You should drop a single (local) application "
587 "onto the drop box - that application will be "
588 "used to load files of this type in future"));
589 return;
592 item = diritem_new("");
593 diritem_restat(app, item, NULL);
594 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
596 guchar *path;
598 path = get_action_save_path(dialog);
600 if (path)
602 if (symlink(app, path))
603 delayed_error("symlink: %s",
604 g_strerror(errno));
605 else
606 destroy_on_idle(dialog);
608 g_free(path);
611 else
612 delayed_error(
613 _("This is not a program! Give me an application "
614 "instead!"));
616 diritem_free(item);
619 /* Find the current command which is used to run files of this type.
620 * Returns NULL on failure. g_free() the result.
622 static guchar *get_current_command(MIME_type *type)
624 struct stat info;
625 char *handler, *nl, *data = NULL;
626 long len;
627 guchar *command = NULL;
629 handler = handler_for(type);
631 if (!handler)
632 return NULL; /* No current handler */
634 if (stat(handler, &info))
635 goto out; /* Can't stat */
637 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
638 goto out; /* Only use small regular files */
640 if (!load_file(handler, &data, &len))
641 goto out; /* Didn't load OK */
643 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
644 goto out; /* Not one of ours */
646 nl = strchr(data + 16, '\n');
647 if (!nl)
648 goto out; /* No newline! */
650 command = g_strndup(data + 16, nl - data - 16);
651 out:
652 g_free(handler);
653 g_free(data);
654 return command;
657 /* Find the current command which is used to run files of this type,
658 * and return a textual description of it.
659 * g_free() the result.
661 gchar *describe_current_command(MIME_type *type)
663 char *handler;
664 char *desc = NULL;
665 struct stat info;
666 char *target;
668 g_return_val_if_fail(type != NULL, NULL);
670 if (type == application_executable)
671 return g_strdup(_("Execute file"));
673 handler = handler_for(type);
675 if (!handler)
676 return g_strdup(_("No run action defined"));
678 target = readlink_dup(handler);
679 if (target)
681 /* Cope with relative paths (shouldn't normally be needed) */
683 if (target[0] == '/')
685 g_free(handler);
686 handler = target;
688 else
690 gchar *dir;
692 dir = g_path_get_dirname(handler);
693 g_free(handler);
694 handler = g_strconcat(dir, "/", target, NULL);
695 g_free(target);
696 g_free(dir);
700 if (mc_stat(handler, &info) !=0 )
702 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
703 g_strerror(errno));
704 goto out;
707 if (S_ISDIR(info.st_mode))
709 const guchar *tmp;
710 uid_t dir_uid = info.st_uid;
712 tmp = make_path(handler, "AppRun");
714 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
715 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
716 desc = g_strdup_printf(
717 _("Invalid application %s (bad AppRun)"),
718 handler);
719 /* Else, just report handler... */
721 goto out;
724 /* It's not an application directory, and it's not a symlink... */
726 if (access(handler, X_OK) != 0)
728 desc = g_strdup_printf(_("Non-executable %s"), handler);
729 goto out;
732 desc = get_current_command(type);
733 out:
734 if (!desc)
735 desc = handler;
736 else
737 g_free(handler);
739 return desc;
742 /* Display a dialog box allowing the user to set the default run action
743 * for this type.
745 void type_set_handler_dialog(MIME_type *type)
747 guchar *tmp;
748 gchar *handler;
749 GtkDialog *dialog;
750 GtkWidget *frame, *entry, *label;
751 GtkWidget *eb, *hbox;
752 Radios *radios;
753 GtkTargetEntry targets[] = {
754 {"text/uri-list", 0, TARGET_URI_LIST},
757 g_return_if_fail(type != NULL);
759 dialog = GTK_DIALOG(gtk_dialog_new());
760 gtk_dialog_set_has_separator(dialog, FALSE);
761 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
763 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
765 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
767 radios = radios_new(NULL, NULL);
768 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
770 radios_add(radios,
771 _("If a handler for the specific type isn't set up, "
772 "use this as the default."), SET_MEDIA,
773 _("Set default for all `%s/<anything>'"),
774 type->media_type);
776 radios_add(radios,
777 _("Use this application for all files with this MIME "
778 "type."), SET_TYPE,
779 _("Only for the type `%s' (%s/%s)"),
780 mime_type_comment(type),
781 type->media_type, type->subtype);
783 radios_set_value(radios, SET_TYPE);
785 radios_pack(radios, GTK_BOX(dialog->vbox));
787 frame = gtk_frame_new(NULL);
788 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 4);
789 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
790 eb = gtk_event_box_new();
791 gtk_container_add(GTK_CONTAINER(frame), eb);
793 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
795 handler = handler_for(type);
796 if (handler)
798 char *link;
800 link = readlink_dup(handler);
801 if (link)
803 char *msg;
805 msg = g_strdup_printf(_("Currently %s"), link);
806 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
807 g_free(link);
808 g_free(msg);
810 g_free(handler);
813 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
814 targets, sizeof(targets) / sizeof(*targets),
815 GDK_ACTION_COPY);
816 g_signal_connect(eb, "drag_data_received",
817 G_CALLBACK(drag_app_dropped), dialog);
819 label = gtk_label_new(_("Drop a suitable\napplication here"));
820 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
821 gtk_container_add(GTK_CONTAINER(eb), label);
823 hbox = gtk_hbox_new(FALSE, 4);
824 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
825 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
826 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
827 FALSE, TRUE, 0);
828 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
830 hbox = gtk_hbox_new(FALSE, 4);
831 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
833 label = gtk_label_new(_("Enter a shell command:")),
834 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
835 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
837 gtk_box_pack_start(GTK_BOX(hbox),
838 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
840 entry = gtk_entry_new();
841 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
842 gtk_widget_grab_focus(entry);
843 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
844 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
846 /* If possible, fill in the entry box with the current command */
847 tmp = get_current_command(type);
848 if (tmp)
850 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
851 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
852 g_free(tmp);
854 else
856 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
857 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
860 gtk_dialog_add_buttons(dialog,
861 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
862 GTK_STOCK_OK, GTK_RESPONSE_OK,
863 NULL);
865 hbox = gtk_hbox_new(TRUE, 4);
866 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
868 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
870 g_signal_connect(dialog, "response",
871 G_CALLBACK(set_action_response), NULL);
873 gtk_widget_show_all(GTK_WIDGET(dialog));
876 /* The user wants to set a new default action for files of this type.
877 * Removes the current binding if possible and returns the path to
878 * save the new one to. NULL means cancel. g_free() the result.
880 char *get_action_save_path(GtkWidget *dialog)
882 guchar *path = NULL;
883 struct stat info;
884 guchar *type_name = NULL;
885 MIME_type *type;
886 Radios *radios;
888 g_return_val_if_fail(dialog != NULL, NULL);
890 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
891 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
893 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
895 if (radios_get_value(radios) == SET_MEDIA)
896 type_name = g_strdup(type->media_type);
897 else
898 type_name = g_strconcat(type->media_type, "_",
899 type->subtype, NULL);
901 path = choices_find_path_save("", PROJECT, FALSE);
902 if (!path)
904 report_error(
905 _("Choices saving is disabled by CHOICESPATH variable"));
906 goto out;
908 g_free(path);
910 path = choices_find_path_save(type_name, "MIME-types", TRUE);
912 if (lstat(path, &info) == 0)
914 /* A binding already exists... */
915 if (S_ISREG(info.st_mode) && info.st_size > 256)
917 if (!confirm(_("A run action already exists and is "
918 "quite a big program - are you sure "
919 "you want to delete it?"),
920 GTK_STOCK_DELETE, NULL))
922 null_g_free(&path);
923 goto out;
927 if (unlink(path))
929 report_error(_("Can't remove %s: %s"),
930 path, g_strerror(errno));
931 null_g_free(&path);
932 goto out;
936 out:
937 g_free(type_name);
938 return path;
941 MIME_type *mime_type_from_base_type(int base_type)
943 switch (base_type)
945 case TYPE_FILE:
946 return text_plain;
947 case TYPE_DIRECTORY:
948 return inode_directory;
949 case TYPE_PIPE:
950 return inode_pipe;
951 case TYPE_SOCKET:
952 return inode_socket;
953 case TYPE_BLOCK_DEVICE:
954 return inode_block_dev;
955 case TYPE_CHAR_DEVICE:
956 return inode_char_dev;
957 case TYPE_DOOR:
958 return inode_door;
960 return inode_unknown;
963 /* Takes the st_mode field from stat() and returns the base type.
964 * Should not be a symlink.
966 int mode_to_base_type(int st_mode)
968 if (S_ISREG(st_mode))
969 return TYPE_FILE;
970 else if (S_ISDIR(st_mode))
971 return TYPE_DIRECTORY;
972 else if (S_ISBLK(st_mode))
973 return TYPE_BLOCK_DEVICE;
974 else if (S_ISCHR(st_mode))
975 return TYPE_CHAR_DEVICE;
976 else if (S_ISFIFO(st_mode))
977 return TYPE_PIPE;
978 else if (S_ISSOCK(st_mode))
979 return TYPE_SOCKET;
980 else if (S_ISDOOR(st_mode))
981 return TYPE_DOOR;
983 return TYPE_ERROR;
986 /* Returns TRUE is this is something that is run by looking up its type
987 * in MIME-types and, hence, can have its run action set.
989 gboolean can_set_run_action(DirItem *item)
991 g_return_val_if_fail(item != NULL, FALSE);
993 return item->base_type == TYPE_FILE &&
994 !(item->mime_type == application_executable);
997 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
998 static void edit_mime_types(guchar *unused)
1000 const guchar *path;
1002 mkdir(make_path(home_dir, ".mime"), 0700);
1003 path = make_path(home_dir, ".mime/packages");
1004 mkdir(path, 0700);
1005 filer_opendir(path, NULL, NULL);
1007 path = "/usr/local/share/mime/packages";
1008 if (file_exists(path))
1009 filer_opendir(path, NULL, NULL);
1011 path = "/usr/share/mime/packages";
1012 if (file_exists(path))
1013 filer_opendir(path, NULL, NULL);
1016 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
1018 GtkWidget *button, *align;
1020 g_return_val_if_fail(none == NULL, NULL);
1022 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1023 button = gtk_button_new_with_label(_(label));
1024 gtk_container_add(GTK_CONTAINER(align), button);
1026 g_signal_connect_swapped(button, "clicked",
1027 G_CALLBACK(edit_mime_types), NULL);
1029 return g_list_append(NULL, align);
1032 /* Parse file type colours and allocate/free them as necessary */
1033 static void alloc_type_colours(void)
1035 gboolean success[NUM_TYPE_COLOURS];
1036 int change_count = 0; /* No. needing realloc */
1037 int i;
1038 static gboolean allocated = FALSE;
1040 /* Parse colours */
1041 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1043 GdkColor *c = &type_colours[i];
1044 gushort r = c->red;
1045 gushort g = c->green;
1046 gushort b = c->blue;
1048 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1050 if (allocated && (c->red != r || c->green != g || c->blue != b))
1051 change_count++;
1054 /* Free colours if they were previously allocated and
1055 * have changed or become unneeded.
1057 if (allocated && (change_count || !o_display_colour_types.int_value))
1059 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1060 type_colours, NUM_TYPE_COLOURS);
1061 allocated = FALSE;
1064 /* Allocate colours, unless they are still allocated (=> they didn't
1065 * change) or we don't want them anymore.
1066 * XXX: what should be done if allocation fails?
1068 if (!allocated && o_display_colour_types.int_value)
1070 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1071 type_colours, NUM_TYPE_COLOURS,
1072 FALSE, TRUE, success);
1073 allocated = TRUE;
1077 /* Return a pointer to a (static) colour for this item. If colouring is
1078 * off, returns normal.
1080 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1082 int type = item->base_type;
1084 if (!o_display_colour_types.int_value)
1085 return normal;
1087 if (item->flags & ITEM_FLAG_EXEC_FILE)
1088 type = TYPE_EXEC;
1089 else if (item->flags & ITEM_FLAG_APPDIR)
1090 type = TYPE_APPDIR;
1092 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1094 return &type_colours[type];
1097 /* Process the 'Patterns' value */
1098 static void add_pattern(MIME_type *type, const char *pattern, GHashTable *globs)
1100 if (pattern[0] == '*' && pattern[1] == '.' &&
1101 strpbrk(pattern + 2, "*?[") == NULL)
1103 g_hash_table_insert(extension_hash,
1104 g_strdup(pattern + 2),
1105 type);
1107 else if (strpbrk(pattern, "*?[") == NULL)
1108 g_hash_table_insert(literal_hash, g_strdup(pattern), type);
1109 else
1110 g_hash_table_insert(globs, g_strdup(pattern), type);
1113 /* Load and parse this file. literal_hash and extension_hash are updated
1114 * directly. Other patterns are added to 'globs'.
1116 static void import_file(const gchar *file, GHashTable *globs)
1118 MIME_type *type = NULL;
1119 GError *error = NULL;
1120 gchar *data, *line;
1122 if (access(file, F_OK) != 0)
1123 return; /* Doesn't exist. No problem. */
1125 if (!g_file_get_contents(file, &data, NULL, &error))
1127 delayed_error(_("Error loading MIME database:\n%s"),
1128 error->message);
1129 g_error_free(error);
1130 return;
1133 line = data;
1135 while (line && *line)
1137 char *nl;
1139 nl = strchr(line, '\n');
1140 if (!nl)
1141 break;
1142 *nl = '\0';
1144 if (*line != '#')
1146 const gchar *colon;
1147 gchar *name;
1149 colon = strchr(line, ':');
1150 if (!colon)
1152 delayed_error(_("File '%s' corrupted!"), file);
1153 break;
1156 name = g_strndup(line, colon - line);
1157 type = get_mime_type(name, TRUE);
1158 g_free(name);
1159 if (!type)
1160 g_warning("Invalid type in '%s'", file);
1162 add_pattern(type, colon + 1, globs);
1165 line = nl + 1;
1168 g_free(data);
1171 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1173 Pattern *pattern;
1175 pattern = g_new(Pattern, 1);
1176 pattern->glob = g_strdup((gchar *) key);
1177 pattern->type = (MIME_type *) value;
1178 pattern->len = strlen(pattern->glob);
1180 g_ptr_array_add(glob_patterns, pattern);
1183 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1185 const Pattern *pa = *(const Pattern **) a;
1186 const Pattern *pb = *(const Pattern **) b;
1188 if (pa->len > pb->len)
1189 return -1;
1190 else if (pa->len == pb->len)
1191 return 0;
1192 return 1;
1195 /* Clear all currently stored information and re-read everything */
1196 static void load_mime_types(void)
1198 GHashTable *globs;
1199 gchar *tmp;
1201 if (!glob_patterns)
1202 glob_patterns = g_ptr_array_new();
1203 else
1205 int i;
1207 for (i = glob_patterns->len - 1; i >= 0; i--)
1209 Pattern *p = glob_patterns->pdata[i];
1210 g_free(p->glob);
1211 g_free(p);
1213 g_ptr_array_set_size(glob_patterns, 0);
1216 if (literal_hash)
1217 g_hash_table_destroy(literal_hash);
1218 if (extension_hash)
1219 g_hash_table_destroy(extension_hash);
1220 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1221 g_free, NULL);
1222 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1223 g_free, NULL);
1224 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1226 import_file("/usr/share/mime/globs", globs);
1227 import_file("/usr/local/share/mime/globs", globs);
1229 tmp = g_strconcat(home_dir, "/.mime/globs", NULL);
1230 import_file(tmp, globs);
1231 g_free(tmp);
1233 /* Turn the globs hash into a pointer array */
1234 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1235 g_hash_table_destroy(globs);
1236 globs = NULL;
1238 if (glob_patterns->len)
1239 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1241 if (g_hash_table_size(extension_hash) == 0)
1243 delayed_error(_("The standard MIME type database "
1244 "(version 0.9 or later) was not found. "
1245 "The filer will probably not show the correct "
1246 "types for different files. You should download and "
1247 "install the 'shared-mime-info-0.9' package from "
1248 "here:\n"
1249 "http://www.freedesktop.org/standards/shared-mime-info.html\n\n"
1250 "If you have already installed this package, check that the "
1251 "permissions allow the files to be read (check "
1252 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1255 filer_update_all();
1258 #define MIME_TRY doc = xml_cache_load(path); g_free(path); if (doc) return doc;
1260 /* Find the media/subtype.xml file for a MIME_type and load it, returning
1261 * an XML document (NULL if not found)
1263 * g_object_unref() the result.
1265 static XMLwrapper *load_type_file(MIME_type *type)
1267 guchar *path;
1268 XMLwrapper *doc;
1270 path = g_strdup_printf("%s/.mime/%s/%s.xml", home_dir,
1271 type->media_type, type->subtype);
1272 MIME_TRY;
1274 path = g_strdup_printf("/usr/local/share/mime/%s/%s.xml",
1275 type->media_type, type->subtype);
1276 MIME_TRY;
1278 path = g_strdup_printf("/usr/share/mime/%s/%s.xml",
1279 type->media_type, type->subtype);
1280 MIME_TRY;
1282 return NULL;
1285 static void find_comment(MIME_type *type)
1287 XMLwrapper *typedoc;
1288 xmlNode *node;
1290 typedoc = load_type_file(type);
1291 if (!typedoc)
1292 goto out;
1294 node = xml_get_section(typedoc, TYPE_NS, "comment");
1296 if (node)
1298 if (type->comment)
1299 g_free(type->comment);
1300 type->comment = xmlNodeListGetString(node->doc,
1301 node->xmlChildrenNode, 1);
1304 g_object_unref(typedoc);
1305 out:
1306 if (!type->comment)
1307 type->comment = g_strdup(_("No description"));
1310 const char *mime_type_comment(MIME_type *type)
1312 if (!type->comment)
1313 find_comment(type);
1315 return type->comment;