r1755: Updated to new shared MIME database format.
[rox-filer.git] / ROX-Filer / src / type.c
blobc33388eb16a04c48bc3149a7d4c30f17002b585c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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) */
60 /* Colours for file types (same order as base types) */
61 static gchar *opt_type_colours[][2] = {
62 {"display_err_colour", "#ff0000"},
63 {"display_unkn_colour", "#000000"},
64 {"display_dir_colour", "#000080"},
65 {"display_pipe_colour", "#444444"},
66 {"display_sock_colour", "#ff00ff"},
67 {"display_file_colour", "#000000"},
68 {"display_cdev_colour", "#000000"},
69 {"display_bdev_colour", "#000000"},
70 {"display_door_colour", "#ff00ff"},
71 {"display_exec_colour", "#006000"},
72 {"display_adir_colour", "#006000"}
74 #define NUM_TYPE_COLOURS\
75 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
77 /* Parsed colours for file types */
78 static Option o_type_colours[NUM_TYPE_COLOURS];
79 static GdkColor type_colours[NUM_TYPE_COLOURS];
81 /* Static prototypes */
82 static void load_mime_types(void);
83 static void alloc_type_colours(void);
84 char *get_action_save_path(GtkWidget *dialog);
85 static void edit_mime_types(guchar *unused);
86 static void reread_mime_files(guchar *unused);
87 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
88 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label);
89 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label);
91 /* When working out the type for a file, this hash table is checked
92 * first...
94 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
96 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
97 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
98 * The hash table is consulted from each dot in the string in turn
99 * (First .ps.gz, then try .gz)
101 static GHashTable *extension_hash = NULL;
103 /* The first pattern in the list which matches is used */
104 typedef struct pattern {
105 gint len; /* Used for sorting */
106 gchar *glob;
107 MIME_type *type;
108 } Pattern;
109 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
111 /* Hash of all allocated MIME types, indexed by "media/subtype".
112 * MIME_type structs are never freed; this table prevents memory leaks
113 * when rereading the config files.
115 static GHashTable *type_hash = NULL;
117 /* Most things on Unix are text files, so this is the default type */
118 MIME_type *text_plain;
119 MIME_type *inode_directory;
120 MIME_type *inode_pipe;
121 MIME_type *inode_socket;
122 MIME_type *inode_block_dev;
123 MIME_type *inode_char_dev;
124 MIME_type *application_executable;
125 MIME_type *inode_unknown;
126 MIME_type *inode_door;
128 static Option o_display_colour_types;
130 void type_init(void)
132 int i;
134 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
135 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
137 text_plain = get_mime_type("text/plain", TRUE);
138 inode_directory = get_mime_type("inode/directory", TRUE);
139 inode_pipe = get_mime_type("inode/fifo", TRUE);
140 inode_socket = get_mime_type("inode/socket", TRUE);
141 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
142 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
143 application_executable = get_mime_type("application/x-executable", TRUE);
144 inode_unknown = get_mime_type("inode/unknown", TRUE);
145 inode_door = get_mime_type("inode/door", TRUE);
147 load_mime_types();
149 option_register_widget("type-edit", build_type_edit);
150 option_register_widget("type-reread", build_type_reread);
152 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
154 for (i = 0; i < NUM_TYPE_COLOURS; i++)
155 option_add_string(&o_type_colours[i],
156 opt_type_colours[i][0],
157 opt_type_colours[i][1]);
158 alloc_type_colours();
160 option_add_notify(alloc_type_colours);
163 /* Returns the MIME_type structure for the given type name. It is looked
164 * up in type_hash and returned if found. If not found (and can_create is
165 * TRUE) then a new MIME_type is made, added to type_hash and returned.
166 * NULL is returned if type_name is not in type_hash and can_create is
167 * FALSE, or if type_name does not contain a '/' character.
169 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
171 MIME_type *mtype;
172 gchar *slash;
174 mtype = g_hash_table_lookup(type_hash, type_name);
175 if (mtype || !can_create)
176 return mtype;
178 slash = strchr(type_name, '/');
179 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
181 mtype = g_new(MIME_type, 1);
182 mtype->media_type = g_strndup(type_name, slash - type_name);
183 mtype->subtype = g_strdup(slash + 1);
184 mtype->image = NULL;
186 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
188 return mtype;
191 const char *basetype_name(DirItem *item)
193 if (item->flags & ITEM_FLAG_SYMLINK)
194 return _("Sym link");
195 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
196 return _("Mount point");
197 else if (item->flags & ITEM_FLAG_APPDIR)
198 return _("App dir");
200 switch (item->base_type)
202 case TYPE_FILE:
203 return _("File");
204 case TYPE_DIRECTORY:
205 return _("Dir");
206 case TYPE_CHAR_DEVICE:
207 return _("Char dev");
208 case TYPE_BLOCK_DEVICE:
209 return _("Block dev");
210 case TYPE_PIPE:
211 return _("Pipe");
212 case TYPE_SOCKET:
213 return _("Socket");
214 case TYPE_DOOR:
215 return _("Door");
218 return _("Unknown");
221 /* MIME-type guessing */
223 /* Get the type of this file - stats the file and uses that if
224 * possible. For regular or missing files, uses the pathname.
226 MIME_type *type_get_type(const guchar *path)
228 struct stat info;
229 MIME_type *type = NULL;
230 int base = TYPE_FILE;
231 gboolean exec = FALSE;
233 if (mc_stat(path, &info) == 0)
235 base = mode_to_base_type(info.st_mode);
236 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
237 exec = TRUE;
240 if (base == TYPE_FILE)
241 type = type_from_path(path);
243 if (!type)
245 if (base == TYPE_FILE && exec)
246 type = application_executable;
247 else
248 type = mime_type_from_base_type(base);
251 return type;
254 /* Returns a pointer to the MIME-type.
255 * NULL if we can't think of anything.
257 MIME_type *type_from_path(const char *path)
259 const char *ext, *dot, *leafname;
260 char *lower;
261 MIME_type *type = NULL;
262 int i;
264 #if 0
265 # ifdef WITH_GNOMEVFS
266 if (o_use_gnomevfs.int_value)
267 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
268 # endif
269 #endif
271 leafname = g_basename(path);
273 type = g_hash_table_lookup(literal_hash, leafname);
274 if (type)
275 return type;
276 lower = g_utf8_strdown(leafname, -1);
277 type = g_hash_table_lookup(literal_hash, lower);
278 if (type)
279 goto out;
281 ext = leafname;
283 while ((dot = strchr(ext, '.')))
285 ext = dot + 1;
287 type = g_hash_table_lookup(extension_hash, ext);
289 if (type)
290 goto out;
292 type = g_hash_table_lookup(extension_hash,
293 lower + (ext - leafname));
294 if (type)
295 goto out;
298 for (i = 0; i < glob_patterns->len; i++)
300 Pattern *p = glob_patterns->pdata[i];
302 if (fnmatch(p->glob, leafname, 0) == 0 ||
303 fnmatch(p->glob, lower, 0) == 0)
305 type = p->type;
306 goto out;
310 out:
311 g_free(lower);
313 return type;
316 /* Returns the file/dir in Choices for handling this type.
317 * NULL if there isn't one. g_free() the result.
319 static char *handler_for(MIME_type *type)
321 char *type_name;
322 char *open;
324 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
325 open = choices_find_path_load(type_name, "MIME-types");
326 g_free(type_name);
328 if (!open)
329 open = choices_find_path_load(type->media_type, "MIME-types");
331 return open;
334 /* Actions for types */
336 gboolean type_open(const char *path, MIME_type *type)
338 gchar *argv[] = {NULL, NULL, NULL};
339 char *open;
340 gboolean retval;
341 struct stat info;
343 argv[1] = (char *) path;
345 open = handler_for(type);
346 if (!open)
347 return FALSE;
349 if (stat(open, &info))
351 report_error("stat(%s): %s", open, g_strerror(errno));
352 g_free(open);
353 return FALSE;
356 if (info.st_mode & S_IWOTH)
358 gchar *choices_dir;
359 GList *paths;
361 report_error(_("Executable '%s' is world-writeable! Refusing "
362 "to run. Please change the permissions now (this "
363 "problem may have been caused by a bug in earlier "
364 "versions of the filer).\n\n"
365 "Having (non-symlink) run actions world-writeable "
366 "means that other people who use your computer can "
367 "replace your run actions with malicious versions.\n\n"
368 "If you trust everyone who could write to these files "
369 "then you needn't worry. Otherwise, you should check, "
370 "or even just delete, all the existing run actions."),
371 open);
372 choices_dir = g_dirname(open);
373 paths = g_list_append(NULL, choices_dir);
374 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
375 g_free(choices_dir);
376 g_list_free(paths);
377 g_free(open);
378 return TRUE;
381 if (S_ISDIR(info.st_mode))
382 argv[0] = g_strconcat(open, "/AppRun", NULL);
383 else
384 argv[0] = open;
386 retval = rox_spawn(home_dir, (const gchar **) argv);
388 if (argv[0] != open)
389 g_free(argv[0]);
391 g_free(open);
393 return retval;
396 /* Return the image for this type, loading it if needed.
397 * Places to check are: (eg type="text_plain", base="text")
398 * 1. Choices:MIME-icons/<type>
399 * 2. Choices:MIME-icons/<base>
400 * 3. Unknown type icon.
402 * Note: You must g_object_unref() the image afterwards.
404 MaskedPixmap *type_to_icon(MIME_type *type)
406 char *path;
407 char *type_name;
408 time_t now;
410 if (type == NULL)
412 g_object_ref(im_unknown);
413 return im_unknown;
416 now = time(NULL);
417 /* Already got an image? */
418 if (type->image)
420 /* Yes - don't recheck too often */
421 if (abs(now - type->image_time) < 2)
423 g_object_ref(type->image);
424 return type->image;
426 g_object_unref(type->image);
427 type->image = NULL;
430 type_name = g_strconcat(type->media_type, "_",
431 type->subtype, ".png", NULL);
432 path = choices_find_path_load(type_name, "MIME-icons");
433 if (!path)
435 strcpy(type_name + strlen(type->media_type), ".png");
436 path = choices_find_path_load(type_name, "MIME-icons");
439 g_free(type_name);
441 if (path)
443 type->image = g_fscache_lookup(pixmap_cache, path);
444 g_free(path);
447 if (!type->image)
449 /* One ref from the type structure, one returned */
450 type->image = im_unknown;
451 g_object_ref(im_unknown);
454 type->image_time = now;
456 g_object_ref(type->image);
457 return type->image;
460 GdkAtom type_to_atom(MIME_type *type)
462 char *str;
463 GdkAtom retval;
465 g_return_val_if_fail(type != NULL, GDK_NONE);
467 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
468 retval = gdk_atom_intern(str, FALSE);
469 g_free(str);
471 return retval;
474 static void show_shell_help(gpointer data)
476 info_message(_("Enter a shell command which will load \"$1\" into "
477 "a suitable program. Eg:\n\n"
478 "gimp \"$1\""));
481 /* Called if the user clicks on the OK button. Returns FALSE if an error
482 * was displayed instead of performing the action.
484 static gboolean set_shell_action(GtkWidget *dialog)
486 GtkEntry *entry;
487 GtkToggleButton *for_all;
488 const guchar *command;
489 gchar *tmp, *path;
490 int error = 0, len;
491 int fd;
493 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
494 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
495 g_return_val_if_fail(entry != NULL, FALSE);
497 command = gtk_entry_get_text(entry);
499 if (!strchr(command, '$'))
501 show_shell_help(NULL);
502 return FALSE;
505 path = get_action_save_path(dialog);
506 if (!path)
507 return FALSE;
509 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
510 len = strlen(tmp);
512 fd = open(path, O_CREAT | O_WRONLY, 0755);
513 if (fd == -1)
514 error = errno;
515 else
517 FILE *file;
519 file = fdopen(fd, "w");
520 if (file)
522 if (fwrite(tmp, 1, len, file) < len)
523 error = errno;
524 if (fclose(file) && error == 0)
525 error = errno;
527 else
528 error = errno;
531 if (error)
532 report_error(g_strerror(error));
534 g_free(tmp);
535 g_free(path);
537 gtk_widget_destroy(dialog);
539 return TRUE;
542 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
544 if (response == GTK_RESPONSE_OK)
545 if (!set_shell_action(dialog))
546 return;
547 gtk_widget_destroy(dialog);
550 /* Called when a URI list is dropped onto the box in the Set Run Action
551 * dialog. Make sure it's an application, and make that the default
552 * handler.
554 static void drag_app_dropped(GtkWidget *eb,
555 GdkDragContext *context,
556 gint x,
557 gint y,
558 GtkSelectionData *selection_data,
559 guint info,
560 guint32 time,
561 GtkWidget *dialog)
563 GList *uris;
564 const gchar *app = NULL;
565 DirItem *item;
567 if (!selection_data->data)
568 return; /* Timeout? */
570 uris = uri_list_to_glist(selection_data->data);
572 if (g_list_length(uris) == 1)
573 app = get_local_path((guchar *) uris->data);
574 g_list_free(uris);
576 if (!app)
578 delayed_error(
579 _("You should drop a single (local) application "
580 "onto the drop box - that application will be "
581 "used to load files of this type in future"));
582 return;
585 item = diritem_new("");
586 diritem_restat(app, item, NULL);
587 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
589 guchar *path;
591 path = get_action_save_path(dialog);
593 if (path)
595 if (symlink(app, path))
596 delayed_error("symlink: %s",
597 g_strerror(errno));
598 else
599 destroy_on_idle(dialog);
602 g_free(path);
604 else
605 delayed_error(
606 _("This is not a program! Give me an application "
607 "instead!"));
609 diritem_free(item);
612 /* Find the current command which is used to run files of this type.
613 * Returns NULL on failure. g_free() the result.
615 static guchar *get_current_command(MIME_type *type)
617 struct stat info;
618 char *handler, *nl, *data = NULL;
619 long len;
620 guchar *command = NULL;
622 handler = handler_for(type);
624 if (!handler)
625 return NULL; /* No current handler */
627 if (stat(handler, &info))
628 goto out; /* Can't stat */
630 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
631 goto out; /* Only use small regular files */
633 if (!load_file(handler, &data, &len))
634 goto out; /* Didn't load OK */
636 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
637 goto out; /* Not one of ours */
639 nl = strchr(data + 16, '\n');
640 if (!nl)
641 goto out; /* No newline! */
643 command = g_strndup(data + 16, nl - data - 16);
644 out:
645 g_free(handler);
646 g_free(data);
647 return command;
650 /* Find the current command which is used to run files of this type,
651 * and return a textual description of it.
652 * g_free() the result.
654 gchar *describe_current_command(MIME_type *type)
656 char *handler;
657 char *desc = NULL;
658 struct stat info;
659 char *target;
661 g_return_val_if_fail(type != NULL, NULL);
663 if (type == application_executable)
664 return g_strdup(_("Execute file"));
666 handler = handler_for(type);
668 if (!handler)
669 return g_strdup(_("No run action defined"));
671 target = readlink_dup(handler);
672 if (target)
674 /* Cope with relative paths (shouldn't normally be needed) */
676 if (target[0] == '/')
678 g_free(handler);
679 handler = target;
681 else
683 gchar *dir;
685 dir = g_dirname(handler);
686 g_free(handler);
687 handler = g_strconcat(dir, "/", target, NULL);
688 g_free(target);
689 g_free(dir);
693 if (mc_stat(handler, &info) !=0 )
695 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
696 g_strerror(errno));
697 goto out;
700 if (S_ISDIR(info.st_mode))
702 gchar *tmp;
703 uid_t dir_uid = info.st_uid;
705 tmp = make_path(handler, "AppRun")->str;
707 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
708 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
709 desc = g_strdup_printf(
710 _("Invalid application %s (bad AppRun)"),
711 handler);
712 /* Else, just report handler... */
714 goto out;
717 /* It's not an application directory, and it's not a symlink... */
719 if (access(handler, X_OK) != 0)
721 desc = g_strdup_printf(_("Non-executable %s"), handler);
722 goto out;
725 desc = get_current_command(type);
726 out:
727 if (!desc)
728 desc = handler;
729 else
730 g_free(handler);
732 return desc;
735 /* Display a dialog box allowing the user to set the default run action
736 * for this type.
738 void type_set_handler_dialog(MIME_type *type)
740 guchar *tmp;
741 gchar *handler;
742 GtkDialog *dialog;
743 GtkWidget *frame, *entry, *label;
744 GtkWidget *radio, *eb, *hbox;
745 GtkTargetEntry targets[] = {
746 {"text/uri-list", 0, TARGET_URI_LIST},
749 g_return_if_fail(type != NULL);
751 dialog = GTK_DIALOG(gtk_dialog_new());
752 gtk_dialog_set_has_separator(dialog, FALSE);
753 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
755 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
757 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
759 tmp = g_strdup_printf(_("Set default for all `%s/<anything>'"),
760 type->media_type);
761 radio = gtk_radio_button_new_with_label(NULL, tmp);
762 g_free(tmp);
763 g_object_set_data(G_OBJECT(dialog), "set_for_all", radio);
765 tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type,
766 type->subtype);
767 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
768 radio = gtk_radio_button_new_with_label(
769 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)),
770 tmp);
771 g_free(tmp);
772 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
773 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
775 frame = gtk_frame_new(NULL);
776 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 4);
777 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
778 eb = gtk_event_box_new();
779 gtk_container_add(GTK_CONTAINER(frame), eb);
781 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
783 handler = handler_for(type);
784 if (handler)
786 char *link;
788 link = readlink_dup(handler);
789 if (link)
791 char *msg;
793 msg = g_strdup_printf(_("Currently %s"), link);
794 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
795 g_free(link);
796 g_free(msg);
798 g_free(handler);
801 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
802 targets, sizeof(targets) / sizeof(*targets),
803 GDK_ACTION_COPY);
804 g_signal_connect(eb, "drag_data_received",
805 G_CALLBACK(drag_app_dropped), dialog);
807 label = gtk_label_new(_("Drop a suitable\napplication here"));
808 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
809 gtk_container_add(GTK_CONTAINER(eb), label);
811 hbox = gtk_hbox_new(FALSE, 4);
812 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
813 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
814 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
815 FALSE, TRUE, 0);
816 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
818 hbox = gtk_hbox_new(FALSE, 4);
819 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
821 label = gtk_label_new(_("Enter a shell command:")),
822 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
823 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
825 gtk_box_pack_start(GTK_BOX(hbox),
826 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
828 entry = gtk_entry_new();
829 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
830 gtk_widget_grab_focus(entry);
831 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
832 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
834 /* If possible, fill in the entry box with the current command */
835 tmp = get_current_command(type);
836 if (tmp)
838 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
839 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
840 g_free(tmp);
842 else
844 gtk_entry_set_text(GTK_ENTRY(entry), " \"$1\"");
845 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
848 gtk_dialog_add_buttons(dialog,
849 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
850 GTK_STOCK_OK, GTK_RESPONSE_OK,
851 NULL);
853 hbox = gtk_hbox_new(TRUE, 4);
854 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
856 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
858 g_signal_connect(dialog, "response",
859 G_CALLBACK(set_action_response), NULL);
861 gtk_widget_show_all(GTK_WIDGET(dialog));
864 /* The user wants to set a new default action for files of this type.
865 * Removes the current binding if possible and returns the path to
866 * save the new one to. NULL means cancel. g_free() the result.
868 char *get_action_save_path(GtkWidget *dialog)
870 guchar *path = NULL;
871 struct stat info;
872 guchar *type_name = NULL;
873 MIME_type *type;
874 GtkToggleButton *for_all;
876 g_return_val_if_fail(dialog != NULL, NULL);
877 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
878 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
879 g_return_val_if_fail(for_all != NULL && type != NULL, NULL);
881 if (gtk_toggle_button_get_active(for_all))
882 type_name = g_strdup(type->media_type);
883 else
884 type_name = g_strconcat(type->media_type, "_",
885 type->subtype, NULL);
887 path = choices_find_path_save("", PROJECT, FALSE);
888 if (!path)
890 report_error(
891 _("Choices saving is disabled by CHOICESPATH variable"));
892 goto out;
894 g_free(path);
896 path = choices_find_path_save(type_name, "MIME-types", TRUE);
898 if (lstat(path, &info) == 0)
900 /* A binding already exists... */
901 if (S_ISREG(info.st_mode) && info.st_size > 256)
903 if (get_choice(PROJECT,
904 _("A run action already exists and is quite "
905 "a big program - are you sure you want to "
906 "delete it?"), 2,
907 _("Cancel"), _("Delete")) != 1)
909 g_free(path);
910 path = NULL;
911 goto out;
915 if (unlink(path))
917 report_error(_("Can't remove %s: %s"),
918 path, g_strerror(errno));
919 g_free(path);
920 path = NULL;
921 goto out;
925 out:
926 g_free(type_name);
927 return path;
930 MIME_type *mime_type_from_base_type(int base_type)
932 switch (base_type)
934 case TYPE_FILE:
935 return text_plain;
936 case TYPE_DIRECTORY:
937 return inode_directory;
938 case TYPE_PIPE:
939 return inode_pipe;
940 case TYPE_SOCKET:
941 return inode_socket;
942 case TYPE_BLOCK_DEVICE:
943 return inode_block_dev;
944 case TYPE_CHAR_DEVICE:
945 return inode_char_dev;
946 case TYPE_DOOR:
947 return inode_door;
949 return inode_unknown;
952 /* Takes the st_mode field from stat() and returns the base type.
953 * Should not be a symlink.
955 int mode_to_base_type(int st_mode)
957 if (S_ISREG(st_mode))
958 return TYPE_FILE;
959 else if (S_ISDIR(st_mode))
960 return TYPE_DIRECTORY;
961 else if (S_ISBLK(st_mode))
962 return TYPE_BLOCK_DEVICE;
963 else if (S_ISCHR(st_mode))
964 return TYPE_CHAR_DEVICE;
965 else if (S_ISFIFO(st_mode))
966 return TYPE_PIPE;
967 else if (S_ISSOCK(st_mode))
968 return TYPE_SOCKET;
969 else if (S_ISDOOR(st_mode))
970 return TYPE_DOOR;
972 return TYPE_ERROR;
975 /* Returns TRUE is this is something that is run by looking up its type
976 * in MIME-types and, hence, can have its run action set.
978 gboolean can_set_run_action(DirItem *item)
980 g_return_val_if_fail(item != NULL, FALSE);
982 return item->base_type == TYPE_FILE &&
983 !(item->mime_type == application_executable);
986 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
987 static void edit_mime_types(guchar *unused)
989 const gchar *path;
990 struct stat info;
992 mkdir(make_path(home_dir, ".mime")->str, 0700);
993 path = make_path(home_dir, ".mime/mime-info")->str;
994 mkdir(path, 0700);
995 filer_opendir(path, NULL);
997 path = "/usr/local/share/mime/mime-info";
998 if (mc_stat(path, &info) == 0)
999 filer_opendir(path, NULL);
1001 path = "/usr/share/mime/mime-info";
1002 if (mc_stat(path, &info) == 0)
1003 filer_opendir(path, NULL);
1006 static void reread_mime_files(guchar *unused)
1008 load_mime_types();
1011 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label)
1013 GtkWidget *button, *align;
1015 g_return_val_if_fail(none == NULL, NULL);
1017 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1018 button = gtk_button_new_with_label(_(label));
1019 gtk_container_add(GTK_CONTAINER(align), button);
1021 g_signal_connect_swapped(button, "clicked",
1022 G_CALLBACK(reread_mime_files), NULL);
1024 return g_list_append(NULL, align);
1027 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
1029 GtkWidget *button, *align;
1031 g_return_val_if_fail(none == NULL, NULL);
1033 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1034 button = gtk_button_new_with_label(_(label));
1035 gtk_container_add(GTK_CONTAINER(align), button);
1037 g_signal_connect_swapped(button, "clicked",
1038 G_CALLBACK(edit_mime_types), NULL);
1040 return g_list_append(NULL, align);
1043 /* Parse file type colours and allocate/free them as necessary */
1044 static void alloc_type_colours(void)
1046 gboolean success[NUM_TYPE_COLOURS];
1047 int change_count = 0; /* No. needing realloc */
1048 int i;
1049 static gboolean allocated = FALSE;
1051 /* Parse colours */
1052 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1054 GdkColor *c = &type_colours[i];
1055 gushort r = c->red;
1056 gushort g = c->green;
1057 gushort b = c->blue;
1059 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1061 if (allocated && (c->red != r || c->green != g || c->blue != b))
1062 change_count++;
1065 /* Free colours if they were previously allocated and
1066 * have changed or become unneeded.
1068 if (allocated && (change_count || !o_display_colour_types.int_value))
1070 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1071 type_colours, NUM_TYPE_COLOURS);
1072 allocated = FALSE;
1075 /* Allocate colours, unless they are still allocated (=> they didn't
1076 * change) or we don't want them anymore.
1077 * XXX: what should be done if allocation fails?
1079 if (!allocated && o_display_colour_types.int_value)
1081 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1082 type_colours, NUM_TYPE_COLOURS,
1083 FALSE, TRUE, success);
1084 allocated = TRUE;
1088 /* Return a pointer to a (static) colour for this item. If colouring is
1089 * off, returns normal.
1091 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1093 int type = item->base_type;
1095 if (!o_display_colour_types.int_value)
1096 return normal;
1098 if (item->flags & ITEM_FLAG_EXEC_FILE)
1099 type = TYPE_EXEC;
1100 else if (item->flags & ITEM_FLAG_APPDIR)
1101 type = TYPE_APPDIR;
1103 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1105 return &type_colours[type];
1108 /* Process the 'Patterns' value */
1109 static void add_pattern(MIME_type *type, const char *pattern, GHashTable *globs)
1111 if (pattern[0] == '*' && pattern[1] == '.' &&
1112 strpbrk(pattern + 2, "*?[") == NULL)
1114 g_hash_table_insert(extension_hash,
1115 g_strdup(pattern + 2),
1116 type);
1118 else if (strpbrk(pattern, "*?[") == NULL)
1119 g_hash_table_insert(literal_hash, g_strdup(pattern), type);
1120 else
1121 g_hash_table_insert(globs, g_strdup(pattern), type);
1124 /* Load and parse this file. literal_hash and extension_hash are updated
1125 * directly. Other patterns are added to 'globs'.
1127 static void import_file(const gchar *file, GHashTable *globs)
1129 MIME_type *type = NULL;
1130 GError *error = NULL;
1131 gchar *data, *line;
1133 if (access(file, F_OK) != 0)
1134 return; /* Doesn't exist. No problem. */
1136 if (!g_file_get_contents(file, &data, NULL, &error))
1138 delayed_error("Error loading MIME database:\n%s",
1139 error->message);
1140 g_error_free(error);
1141 return;
1144 line = data;
1146 while (line && *line)
1148 char *nl;
1150 nl = strchr(line, '\n');
1151 if (!nl)
1152 break;
1153 *nl = '\0';
1155 if (*line != '#')
1157 const gchar *colon;
1158 gchar *name;
1160 colon = strchr(line, ':');
1161 if (!colon)
1163 delayed_error(_("File '%s' corrupted!"), file);
1164 break;
1167 name = g_strndup(line, colon - line);
1168 type = get_mime_type(name, TRUE);
1169 g_free(name);
1170 if (!type)
1171 g_warning("Invalid type in '%s'", file);
1173 add_pattern(type, colon + 1, globs);
1176 line = nl + 1;
1179 g_free(data);
1182 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1184 Pattern *pattern;
1186 pattern = g_new(Pattern, 1);
1187 pattern->glob = g_strdup((gchar *) key);
1188 pattern->type = (MIME_type *) value;
1189 pattern->len = strlen(pattern->glob);
1191 g_ptr_array_add(glob_patterns, pattern);
1194 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1196 const Pattern *pa = *(const Pattern **) a;
1197 const Pattern *pb = *(const Pattern **) b;
1199 if (pa->len > pb->len)
1200 return -1;
1201 else if (pa->len == pb->len)
1202 return 0;
1203 return 1;
1206 /* Clear all currently stored information and re-read everything */
1207 static void load_mime_types(void)
1209 GHashTable *globs;
1210 gchar *tmp;
1212 if (!glob_patterns)
1213 glob_patterns = g_ptr_array_new();
1214 else
1216 int i;
1218 for (i = glob_patterns->len - 1; i >= 0; i--)
1220 Pattern *p = glob_patterns->pdata[i];
1221 g_free(p->glob);
1222 g_free(p);
1224 g_ptr_array_set_size(glob_patterns, 0);
1227 if (literal_hash)
1228 g_hash_table_destroy(literal_hash);
1229 if (extension_hash)
1230 g_hash_table_destroy(extension_hash);
1231 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1232 g_free, NULL);
1233 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1234 g_free, NULL);
1235 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1237 import_file("/usr/share/mime/globs", globs);
1238 import_file("/usr/local/share/mime/globs", globs);
1240 tmp = g_strconcat(home_dir, "/.mime/globs", NULL);
1241 import_file(tmp, globs);
1242 g_free(tmp);
1244 /* Turn the globs hash into a pointer array */
1245 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1246 g_hash_table_destroy(globs);
1247 globs = NULL;
1249 if (glob_patterns->len)
1250 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1252 if (g_hash_table_size(extension_hash) == 0)
1254 delayed_error(_("The standard MIME type database "
1255 "(version 0.8 or later) was not found. "
1256 "The filer will probably not show the correct "
1257 "types for different files. You should download and "
1258 "install the 'shared-mime-info-0.8' package from "
1259 "here:\n"
1260 "http://www.freedesktop.org/standards/shared-mime-info.html"));
1263 filer_update_all();