r1621: Better error message.
[rox-filer.git] / ROX-Filer / src / type.c
blob0cf289f4bcc910da4da5233f05c26f081bbe070e
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_exec_colour", "#006000"},
71 {"display_adir_colour", "#006000"}
73 #define NUM_TYPE_COLOURS\
74 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
76 /* Parsed colours for file types */
77 static Option o_type_colours[NUM_TYPE_COLOURS];
78 static GdkColor type_colours[NUM_TYPE_COLOURS];
80 /* Static prototypes */
81 static void load_mime_types(void);
82 static void alloc_type_colours(void);
83 char *get_action_save_path(GtkWidget *dialog);
84 static void edit_mime_types(guchar *unused);
85 static void reread_mime_files(guchar *unused);
86 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
87 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label);
88 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label);
90 /* When working out the type for a file, this hash table is checked
91 * first...
93 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
95 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
96 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
97 * The hash table is consulted from each dot in the string in turn
98 * (First .ps.gz, then try .gz)
100 static GHashTable *extension_hash = NULL;
102 /* The first pattern in the list which matches is used */
103 typedef struct pattern {
104 gint len; /* Used for sorting */
105 gchar *glob;
106 MIME_type *type;
107 } Pattern;
108 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
110 /* Hash of all allocated MIME types, indexed by "media/subtype".
111 * MIME_type structs are never freed; this table prevents memory leaks
112 * when rereading the config files.
114 static GHashTable *type_hash = NULL;
116 /* Most things on Unix are text files, so this is the default type */
117 MIME_type *text_plain;
118 MIME_type *inode_directory;
119 MIME_type *inode_pipe;
120 MIME_type *inode_socket;
121 MIME_type *inode_block_dev;
122 MIME_type *inode_char_dev;
123 MIME_type *application_executable;
124 MIME_type *inode_unknown;
126 static Option o_display_colour_types;
128 void type_init(void)
130 int i;
132 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
133 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
135 text_plain = get_mime_type("text/plain", TRUE);
136 inode_directory = get_mime_type("inode/directory", TRUE);
137 inode_pipe = get_mime_type("inode/fifo", TRUE);
138 inode_socket = get_mime_type("inode/socket", TRUE);
139 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
140 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
141 application_executable = get_mime_type("application/x-executable", TRUE);
142 inode_unknown = get_mime_type("inode/unknown", TRUE);
144 load_mime_types();
146 option_register_widget("type-edit", build_type_edit);
147 option_register_widget("type-reread", build_type_reread);
149 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
151 for (i = 0; i < NUM_TYPE_COLOURS; i++)
152 option_add_string(&o_type_colours[i],
153 opt_type_colours[i][0],
154 opt_type_colours[i][1]);
155 alloc_type_colours();
157 option_add_notify(alloc_type_colours);
160 /* Returns the MIME_type structure for the given type name. It is looked
161 * up in type_hash and returned if found. If not found (and can_create is
162 * TRUE) then a new MIME_type is made, added to type_hash and returned.
163 * NULL is returned if type_name is not in type_hash and can_create is
164 * FALSE, or if type_name does not contain a '/' character.
166 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
168 MIME_type *mtype;
169 gchar *slash;
171 mtype = g_hash_table_lookup(type_hash, type_name);
172 if (mtype || !can_create)
173 return mtype;
175 slash = strchr(type_name, '/');
176 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
178 mtype = g_new(MIME_type, 1);
179 mtype->media_type = g_strndup(type_name, slash - type_name);
180 mtype->subtype = g_strdup(slash + 1);
181 mtype->image = NULL;
183 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
185 return mtype;
188 const char *basetype_name(DirItem *item)
190 if (item->flags & ITEM_FLAG_SYMLINK)
191 return _("Sym link");
192 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
193 return _("Mount point");
194 else if (item->flags & ITEM_FLAG_APPDIR)
195 return _("App dir");
197 switch (item->base_type)
199 case TYPE_FILE:
200 return _("File");
201 case TYPE_DIRECTORY:
202 return _("Dir");
203 case TYPE_CHAR_DEVICE:
204 return _("Char dev");
205 case TYPE_BLOCK_DEVICE:
206 return _("Block dev");
207 case TYPE_PIPE:
208 return _("Pipe");
209 case TYPE_SOCKET:
210 return _("Socket");
213 return _("Unknown");
216 /* MIME-type guessing */
218 /* Get the type of this file - stats the file and uses that if
219 * possible. For regular or missing files, uses the pathname.
221 MIME_type *type_get_type(const guchar *path)
223 struct stat info;
224 MIME_type *type = NULL;
225 int base = TYPE_FILE;
226 gboolean exec = FALSE;
228 if (mc_stat(path, &info) == 0)
230 base = mode_to_base_type(info.st_mode);
231 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
232 exec = TRUE;
235 if (base == TYPE_FILE)
236 type = type_from_path(path);
238 if (!type)
240 if (base == TYPE_FILE && exec)
241 type = application_executable;
242 else
243 type = mime_type_from_base_type(base);
246 return type;
249 /* Returns a pointer to the MIME-type.
250 * NULL if we can't think of anything.
252 MIME_type *type_from_path(const char *path)
254 const char *ext, *dot, *leafname;
255 char *lower;
256 MIME_type *type = NULL;
257 int i;
259 #if 0
260 # ifdef WITH_GNOMEVFS
261 if (o_use_gnomevfs.int_value)
262 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
263 # endif
264 #endif
266 leafname = g_basename(path);
268 type = g_hash_table_lookup(literal_hash, leafname);
269 if (type)
270 return type;
271 lower = g_utf8_strdown(leafname, -1);
272 type = g_hash_table_lookup(literal_hash, lower);
273 if (type)
274 goto out;
276 ext = leafname;
278 while ((dot = strchr(ext, '.')))
280 ext = dot + 1;
282 type = g_hash_table_lookup(extension_hash, ext);
284 if (type)
285 goto out;
287 type = g_hash_table_lookup(extension_hash,
288 lower + (ext - leafname));
289 if (type)
290 goto out;
293 for (i = 0; i < glob_patterns->len; i++)
295 Pattern *p = glob_patterns->pdata[i];
297 if (fnmatch(p->glob, leafname, 0) == 0 ||
298 fnmatch(p->glob, lower, 0) == 0)
300 type = p->type;
301 goto out;
305 out:
306 g_free(lower);
308 return type;
311 /* Returns the file/dir in Choices for handling this type.
312 * NULL if there isn't one. g_free() the result.
314 static char *handler_for(MIME_type *type)
316 char *type_name;
317 char *open;
319 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
320 open = choices_find_path_load(type_name, "MIME-types");
321 g_free(type_name);
323 if (!open)
324 open = choices_find_path_load(type->media_type, "MIME-types");
326 return open;
329 /* Actions for types */
331 gboolean type_open(const char *path, MIME_type *type)
333 gchar *argv[] = {NULL, NULL, NULL};
334 char *open;
335 gboolean retval;
336 struct stat info;
338 argv[1] = (char *) path;
340 open = handler_for(type);
341 if (!open)
342 return FALSE;
344 if (stat(open, &info))
346 report_error("stat(%s): %s", open, g_strerror(errno));
347 g_free(open);
348 return FALSE;
351 if (info.st_mode & S_IWOTH)
353 gchar *choices_dir;
354 GList *paths;
356 report_error(_("Executable '%s' is world-writeable! Refusing "
357 "to run. Please change the permissions now (this "
358 "problem may have been caused by a bug in earlier "
359 "versions of the filer).\n\n"
360 "Having (non-symlink) run actions world-writeable "
361 "means that other people who use your computer can "
362 "replace your run actions with malicious versions.\n\n"
363 "If you trust everyone who could write to these files "
364 "then you needn't worry. Otherwise, you should check, "
365 "or even just delete, all the existing run actions."),
366 open);
367 choices_dir = g_dirname(open);
368 paths = g_list_append(NULL, choices_dir);
369 action_chmod(paths, TRUE, "go-w (Fix security problem)");
370 g_free(choices_dir);
371 g_list_free(paths);
372 g_free(open);
373 return TRUE;
376 if (S_ISDIR(info.st_mode))
377 argv[0] = g_strconcat(open, "/AppRun", NULL);
378 else
379 argv[0] = open;
381 retval = rox_spawn(home_dir, (const gchar **) argv);
383 if (argv[0] != open)
384 g_free(argv[0]);
386 g_free(open);
388 return retval;
391 /* Return the image for this type, loading it if needed.
392 * Places to check are: (eg type="text_plain", base="text")
393 * 1. Choices:MIME-icons/<type>
394 * 2. Choices:MIME-icons/<base>
395 * 3. Unknown type icon.
397 * Note: You must g_object_unref() the image afterwards.
399 MaskedPixmap *type_to_icon(MIME_type *type)
401 char *path;
402 char *type_name;
403 time_t now;
405 if (type == NULL)
407 g_object_ref(im_unknown);
408 return im_unknown;
411 now = time(NULL);
412 /* Already got an image? */
413 if (type->image)
415 /* Yes - don't recheck too often */
416 if (abs(now - type->image_time) < 2)
418 g_object_ref(type->image);
419 return type->image;
421 g_object_unref(type->image);
422 type->image = NULL;
425 type_name = g_strconcat(type->media_type, "_",
426 type->subtype, ".png", NULL);
427 path = choices_find_path_load(type_name, "MIME-icons");
428 if (!path)
430 strcpy(type_name + strlen(type->media_type), ".png");
431 path = choices_find_path_load(type_name, "MIME-icons");
434 g_free(type_name);
436 if (path)
438 type->image = g_fscache_lookup(pixmap_cache, path);
439 g_free(path);
442 if (!type->image)
444 /* One ref from the type structure, one returned */
445 type->image = im_unknown;
446 g_object_ref(im_unknown);
449 type->image_time = now;
451 g_object_ref(type->image);
452 return type->image;
455 GdkAtom type_to_atom(MIME_type *type)
457 char *str;
458 GdkAtom retval;
460 g_return_val_if_fail(type != NULL, GDK_NONE);
462 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
463 retval = gdk_atom_intern(str, FALSE);
464 g_free(str);
466 return retval;
469 static void show_shell_help(gpointer data)
471 info_message(_("Enter a shell command which will load \"$1\" into "
472 "a suitable program. Eg:\n\n"
473 "gimp \"$1\""));
476 /* Called if the user clicks on the OK button */
477 static void set_shell_action(GtkWidget *dialog)
479 GtkEntry *entry;
480 GtkToggleButton *for_all;
481 const guchar *command;
482 gchar *tmp, *path;
483 int error = 0, len;
484 int fd;
486 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
487 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
488 g_return_if_fail(entry != NULL);
490 command = gtk_entry_get_text(entry);
492 if (!strchr(command, '$'))
494 show_shell_help(NULL);
495 return;
498 path = get_action_save_path(dialog);
499 if (!path)
500 return;
502 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
503 len = strlen(tmp);
505 fd = open(path, O_CREAT | O_WRONLY, 0755);
506 if (fd == -1)
507 error = errno;
508 else
510 FILE *file;
512 file = fdopen(fd, "w");
513 if (file)
515 if (fwrite(tmp, 1, len, file) < len)
516 error = errno;
517 if (fclose(file) && error == 0)
518 error = errno;
520 else
521 error = errno;
524 if (error)
525 report_error(g_strerror(error));
527 g_free(tmp);
528 g_free(path);
530 gtk_widget_destroy(dialog);
533 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
535 if (response == GTK_RESPONSE_OK)
536 set_shell_action(dialog);
537 gtk_widget_destroy(dialog);
540 /* Called when a URI list is dropped onto the box in the Set Run Action
541 * dialog. Make sure it's an application, and make that the default
542 * handler.
544 static void drag_app_dropped(GtkWidget *eb,
545 GdkDragContext *context,
546 gint x,
547 gint y,
548 GtkSelectionData *selection_data,
549 guint info,
550 guint32 time,
551 GtkWidget *dialog)
553 GList *uris;
554 const gchar *app = NULL;
555 DirItem *item;
557 if (!selection_data->data)
558 return; /* Timeout? */
560 uris = uri_list_to_glist(selection_data->data);
562 if (g_list_length(uris) == 1)
563 app = get_local_path((guchar *) uris->data);
564 g_list_free(uris);
566 if (!app)
568 delayed_error(
569 _("You should drop a single (local) application "
570 "onto the drop box - that application will be "
571 "used to load files of this type in future"));
572 return;
575 item = diritem_new("");
576 diritem_restat(app, item, NULL);
577 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
579 guchar *path;
581 path = get_action_save_path(dialog);
583 if (path)
585 if (symlink(app, path))
586 delayed_error("symlink: %s",
587 g_strerror(errno));
588 else
589 destroy_on_idle(dialog);
592 g_free(path);
594 else
595 delayed_error(
596 _("This is not a program! Give me an application "
597 "instead!"));
599 diritem_free(item);
602 /* Find the current command which is used to run files of this type.
603 * Returns NULL on failure. g_free() the result.
605 static guchar *get_current_command(MIME_type *type)
607 struct stat info;
608 char *handler, *nl, *data = NULL;
609 long len;
610 guchar *command = NULL;
612 handler = handler_for(type);
614 if (!handler)
615 return NULL; /* No current handler */
617 if (stat(handler, &info))
618 goto out; /* Can't stat */
620 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
621 goto out; /* Only use small regular files */
623 if (!load_file(handler, &data, &len))
624 goto out; /* Didn't load OK */
626 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
627 goto out; /* Not one of ours */
629 nl = strchr(data + 16, '\n');
630 if (!nl)
631 goto out; /* No newline! */
633 command = g_strndup(data + 16, nl - data - 16);
634 out:
635 g_free(handler);
636 g_free(data);
637 return command;
640 /* Find the current command which is used to run files of this type,
641 * and return a textual description of it.
642 * g_free() the result.
644 gchar *describe_current_command(MIME_type *type)
646 char *handler;
647 char *desc = NULL;
648 struct stat info;
649 char *target;
651 g_return_val_if_fail(type != NULL, NULL);
653 if (type == application_executable)
654 return g_strdup(_("Execute file"));
656 handler = handler_for(type);
658 if (!handler)
659 return g_strdup(_("No run action defined"));
661 target = readlink_dup(handler);
662 if (target)
664 /* Cope with relative paths (shouldn't normally be needed) */
666 if (target[0] == '/')
668 g_free(handler);
669 handler = target;
671 else
673 gchar *dir;
675 dir = g_dirname(handler);
676 g_free(handler);
677 handler = g_strconcat(dir, "/", target, NULL);
678 g_free(target);
679 g_free(dir);
683 if (mc_stat(handler, &info) !=0 )
685 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
686 g_strerror(errno));
687 goto out;
690 if (S_ISDIR(info.st_mode))
692 gchar *tmp;
693 uid_t dir_uid = info.st_uid;
695 tmp = make_path(handler, "AppRun")->str;
697 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
698 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
699 desc = g_strdup_printf(
700 _("Invalid application %s (bad AppRun)"),
701 handler);
702 /* Else, just report handler... */
704 goto out;
707 /* It's not an application directory, and it's not a symlink... */
709 if (access(handler, X_OK) != 0)
711 desc = g_strdup_printf(_("Non-executable %s"), handler);
712 goto out;
715 desc = get_current_command(type);
716 out:
717 if (!desc)
718 desc = handler;
719 else
720 g_free(handler);
722 return desc;
725 /* Display a dialog box allowing the user to set the default run action
726 * for this type.
728 void type_set_handler_dialog(MIME_type *type)
730 guchar *tmp;
731 gchar *handler;
732 GtkDialog *dialog;
733 GtkWidget *frame, *entry, *label;
734 GtkWidget *radio, *eb, *hbox;
735 GtkTargetEntry targets[] = {
736 {"text/uri-list", 0, TARGET_URI_LIST},
739 g_return_if_fail(type != NULL);
741 dialog = GTK_DIALOG(gtk_dialog_new());
742 gtk_dialog_set_has_separator(dialog, FALSE);
743 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
745 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
747 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
749 tmp = g_strdup_printf(_("Set default for all `%s/<anything>'"),
750 type->media_type);
751 radio = gtk_radio_button_new_with_label(NULL, tmp);
752 g_free(tmp);
753 g_object_set_data(G_OBJECT(dialog), "set_for_all", radio);
755 tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type,
756 type->subtype);
757 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
758 radio = gtk_radio_button_new_with_label(
759 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)),
760 tmp);
761 g_free(tmp);
762 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
763 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
765 frame = gtk_frame_new(NULL);
766 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 4);
767 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
768 eb = gtk_event_box_new();
769 gtk_container_add(GTK_CONTAINER(frame), eb);
771 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
773 handler = handler_for(type);
774 if (handler)
776 char *link;
778 link = readlink_dup(handler);
779 if (link)
781 char *msg;
783 msg = g_strdup_printf(_("Currently %s"), link);
784 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
785 g_free(link);
786 g_free(msg);
788 g_free(handler);
791 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
792 targets, sizeof(targets) / sizeof(*targets),
793 GDK_ACTION_COPY);
794 g_signal_connect(eb, "drag_data_received",
795 G_CALLBACK(drag_app_dropped), dialog);
797 label = gtk_label_new(_("Drop a suitable\napplication here"));
798 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
799 gtk_container_add(GTK_CONTAINER(eb), label);
801 hbox = gtk_hbox_new(FALSE, 4);
802 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
803 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
804 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
805 FALSE, TRUE, 0);
806 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
808 hbox = gtk_hbox_new(FALSE, 4);
809 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
811 label = gtk_label_new(_("Enter a shell command:")),
812 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
813 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
815 gtk_box_pack_start(GTK_BOX(hbox),
816 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
818 entry = gtk_entry_new();
819 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
820 gtk_widget_grab_focus(entry);
821 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
822 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
824 /* If possible, fill in the entry box with the current command */
825 tmp = get_current_command(type);
826 if (tmp)
828 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
829 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
830 g_free(tmp);
832 else
834 gtk_entry_set_text(GTK_ENTRY(entry), " \"$1\"");
835 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
838 gtk_dialog_add_buttons(dialog,
839 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
840 GTK_STOCK_OK, GTK_RESPONSE_OK,
841 NULL);
843 hbox = gtk_hbox_new(TRUE, 4);
844 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
846 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
848 g_signal_connect(dialog, "response",
849 G_CALLBACK(set_action_response), NULL);
851 gtk_widget_show_all(GTK_WIDGET(dialog));
854 /* The user wants to set a new default action for files of this type.
855 * Removes the current binding if possible and returns the path to
856 * save the new one to. NULL means cancel. g_free() the result.
858 char *get_action_save_path(GtkWidget *dialog)
860 guchar *path = NULL;
861 struct stat info;
862 guchar *type_name = NULL;
863 MIME_type *type;
864 GtkToggleButton *for_all;
866 g_return_val_if_fail(dialog != NULL, NULL);
867 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
868 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
869 g_return_val_if_fail(for_all != NULL && type != NULL, NULL);
871 if (gtk_toggle_button_get_active(for_all))
872 type_name = g_strdup(type->media_type);
873 else
874 type_name = g_strconcat(type->media_type, "_",
875 type->subtype, NULL);
877 path = choices_find_path_save("", PROJECT, FALSE);
878 if (!path)
880 report_error(
881 _("Choices saving is disabled by CHOICESPATH variable"));
882 goto out;
884 g_free(path);
886 path = choices_find_path_save(type_name, "MIME-types", TRUE);
888 if (lstat(path, &info) == 0)
890 /* A binding already exists... */
891 if (S_ISREG(info.st_mode) && info.st_size > 256)
893 if (get_choice(PROJECT,
894 _("A run action already exists and is quite "
895 "a big program - are you sure you want to "
896 "delete it?"), 2,
897 _("Cancel"), _("Delete")) != 1)
899 g_free(path);
900 path = NULL;
901 goto out;
905 if (unlink(path))
907 report_error(_("Can't remove %s: %s"),
908 path, g_strerror(errno));
909 g_free(path);
910 path = NULL;
911 goto out;
915 out:
916 g_free(type_name);
917 return path;
920 MIME_type *mime_type_from_base_type(int base_type)
922 switch (base_type)
924 case TYPE_FILE:
925 return text_plain;
926 case TYPE_DIRECTORY:
927 return inode_directory;
928 case TYPE_PIPE:
929 return inode_pipe;
930 case TYPE_SOCKET:
931 return inode_socket;
932 case TYPE_BLOCK_DEVICE:
933 return inode_block_dev;
934 case TYPE_CHAR_DEVICE:
935 return inode_char_dev;
937 return inode_unknown;
940 /* Takes the st_mode field from stat() and returns the base type.
941 * Should not be a symlink.
943 int mode_to_base_type(int st_mode)
945 if (S_ISREG(st_mode))
946 return TYPE_FILE;
947 else if (S_ISDIR(st_mode))
948 return TYPE_DIRECTORY;
949 else if (S_ISBLK(st_mode))
950 return TYPE_BLOCK_DEVICE;
951 else if (S_ISCHR(st_mode))
952 return TYPE_CHAR_DEVICE;
953 else if (S_ISFIFO(st_mode))
954 return TYPE_PIPE;
955 else if (S_ISSOCK(st_mode))
956 return TYPE_SOCKET;
958 return TYPE_ERROR;
961 /* Returns TRUE is this is something that is run by looking up its type
962 * in MIME-types and, hence, can have its run action set.
964 gboolean can_set_run_action(DirItem *item)
966 g_return_val_if_fail(item != NULL, FALSE);
968 return item->base_type == TYPE_FILE &&
969 !(item->mime_type == application_executable);
972 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
973 static void edit_mime_types(guchar *unused)
975 const gchar *path;
976 struct stat info;
978 mkdir(make_path(home_dir, ".mime")->str, 0700);
979 path = make_path(home_dir, ".mime/mime-info")->str;
980 mkdir(path, 0700);
981 filer_opendir(path, NULL);
983 path = "/usr/local/share/mime/mime-info";
984 if (mc_stat(path, &info) == 0)
985 filer_opendir(path, NULL);
987 path = "/usr/share/mime/mime-info";
988 if (mc_stat(path, &info) == 0)
989 filer_opendir(path, NULL);
992 static void reread_mime_files(guchar *unused)
994 load_mime_types();
997 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label)
999 GtkWidget *button, *align;
1001 g_return_val_if_fail(none == NULL, NULL);
1003 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1004 button = gtk_button_new_with_label(_(label));
1005 gtk_container_add(GTK_CONTAINER(align), button);
1007 g_signal_connect_swapped(button, "clicked",
1008 G_CALLBACK(reread_mime_files), NULL);
1010 return g_list_append(NULL, align);
1013 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
1015 GtkWidget *button, *align;
1017 g_return_val_if_fail(none == NULL, NULL);
1019 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1020 button = gtk_button_new_with_label(_(label));
1021 gtk_container_add(GTK_CONTAINER(align), button);
1023 g_signal_connect_swapped(button, "clicked",
1024 G_CALLBACK(edit_mime_types), NULL);
1026 return g_list_append(NULL, align);
1029 /* Parse file type colours and allocate/free them as necessary */
1030 static void alloc_type_colours(void)
1032 gboolean success[NUM_TYPE_COLOURS];
1033 int change_count = 0; /* No. needing realloc */
1034 int i;
1035 static gboolean allocated = FALSE;
1037 /* Parse colours */
1038 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1040 GdkColor *c = &type_colours[i];
1041 gushort r = c->red;
1042 gushort g = c->green;
1043 gushort b = c->blue;
1045 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1047 if (allocated && (c->red != r || c->green != g || c->blue != b))
1048 change_count++;
1051 /* Free colours if they were previously allocated and
1052 * have changed or become unneeded.
1054 if (allocated && (change_count || !o_display_colour_types.int_value))
1056 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1057 type_colours, NUM_TYPE_COLOURS);
1058 allocated = FALSE;
1061 /* Allocate colours, unless they are still allocated (=> they didn't
1062 * change) or we don't want them anymore.
1063 * XXX: what should be done if allocation fails?
1065 if (!allocated && o_display_colour_types.int_value)
1067 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1068 type_colours, NUM_TYPE_COLOURS,
1069 FALSE, TRUE, success);
1070 allocated = TRUE;
1074 /* Return a pointer to a (static) colour for this item. If colouring is
1075 * off, returns normal.
1077 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1079 if (!o_display_colour_types.int_value)
1080 return normal;
1082 if (item->flags & ITEM_FLAG_EXEC_FILE)
1083 return &type_colours[8];
1084 else if (item->flags & ITEM_FLAG_APPDIR)
1085 return &type_colours[9];
1086 else
1087 return &type_colours[item->base_type];
1090 /* Process the 'Patterns' value */
1091 static void add_patterns(MIME_type *type, gchar *patterns, GHashTable *globs)
1093 while (1)
1095 char *semi;
1097 semi = strchr(patterns, ';');
1098 if (semi)
1099 *semi = '\0';
1100 g_strstrip(patterns);
1101 if (patterns[0] == '*' && patterns[1] == '.' &&
1102 strpbrk(patterns + 2, "*?[") == NULL)
1104 g_hash_table_insert(extension_hash,
1105 g_strdup(patterns + 2), type);
1107 else if (strpbrk(patterns, "*?[") == NULL)
1108 g_hash_table_insert(literal_hash,
1109 g_strdup(patterns), type);
1110 else
1111 g_hash_table_insert(globs, g_strdup(patterns), type);
1112 if (!semi)
1113 return;
1114 patterns = semi + 1;
1118 /* Load and parse this file. literal_hash and extension_hash are updated
1119 * directly. Other patterns are added to 'globs'.
1121 static void import_file(const gchar *file, GHashTable *globs)
1123 MIME_type *type = NULL;
1124 GError *error = NULL;
1125 gchar *data, *line;
1127 if (!g_file_get_contents(file, &data, NULL, &error))
1129 delayed_error("Error loading MIME-Info database:\n%s",
1130 error->message);
1131 g_error_free(error);
1132 return;
1135 line = data;
1137 while (line && *line)
1139 char *nl;
1141 nl = strchr(line, '\n');
1142 if (!nl)
1143 break;
1144 *nl = '\0';
1146 if (*line == '[')
1148 const gchar *end;
1150 type = NULL;
1152 end = strchr(line, ']');
1153 if (!end)
1155 delayed_error(_("File '%s' corrupted!"), file);
1156 break;
1159 if (strncmp(line + 1, "MIME-Info ", 10) == 0)
1161 gchar *name;
1163 line += 11;
1164 while (*line == ' ' || *line == '\t')
1165 line++;
1166 name = g_strndup(line, end - line);
1167 g_strstrip(name);
1169 type = get_mime_type(name, TRUE);
1170 if (!type)
1172 delayed_error(
1173 _("Invalid type '%s' in '%s'"),
1174 name, file);
1175 break;
1177 g_free(name);
1180 else if (type)
1182 char *eq;
1183 eq = strchr(line, '=');
1184 if (eq)
1186 char *tmp = eq;
1188 while (tmp > line &&
1189 (tmp[-1] == ' ' || tmp[-1] == '\t'))
1190 tmp--;
1191 *tmp = '\0';
1193 eq++;
1194 while (*eq == ' ' || *eq == '\t')
1195 eq++;
1197 if (strcmp(line, "Patterns") == 0)
1198 add_patterns(type, eq, globs);
1202 line = nl + 1;
1205 g_free(data);
1208 /* Parse every .mimeinfo file in 'dir' */
1209 static void import_for_dir(guchar *path, GHashTable *globs, gboolean *freedesk)
1211 DIR *dir;
1212 struct dirent *item;
1214 dir = opendir(path);
1215 if (!dir)
1216 return;
1218 while ((item = readdir(dir)))
1220 gchar *dot;
1222 dot = strrchr(item->d_name, '.');
1223 if (!dot)
1224 continue;
1225 if (strcmp(dot + 1, "mimeinfo") != 0)
1226 continue;
1228 if (*freedesk == FALSE &&
1229 !strcmp(item->d_name, "freedesktop-shared.mimeinfo"))
1231 *freedesk = TRUE;
1234 import_file(make_path(path, item->d_name)->str, globs);
1235 #if 0
1237 struct timeval start, end;
1238 g_print("[ %s ]\n", item->d_name);
1239 gettimeofday(&start, NULL);
1240 import_file(make_path(path, item->d_name)->str, globs);
1241 gettimeofday(&end, NULL);
1243 g_print("Delay = %lf s\n",
1244 (end.tv_sec + ((double) end.tv_usec) / 1000000) -
1245 (start.tv_sec + ((double) start.tv_usec) / 1000000));
1247 #endif
1250 closedir(dir);
1253 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1255 Pattern *pattern;
1257 pattern = g_new(Pattern, 1);
1258 pattern->glob = g_strdup((gchar *) key);
1259 pattern->type = (MIME_type *) value;
1260 pattern->len = strlen(pattern->glob);
1262 g_ptr_array_add(glob_patterns, pattern);
1265 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1267 const Pattern *pa = *(const Pattern **) a;
1268 const Pattern *pb = *(const Pattern **) b;
1270 if (pa->len > pb->len)
1271 return -1;
1272 else if (pa->len == pb->len)
1273 return 0;
1274 return 1;
1277 /* Clear all currently stored information and re-read everything */
1278 static void load_mime_types(void)
1280 gboolean got_freedesk = FALSE;
1281 GHashTable *globs;
1282 gchar *tmp;
1284 if (!glob_patterns)
1285 glob_patterns = g_ptr_array_new();
1286 else
1288 int i;
1290 for (i = glob_patterns->len - 1; i >= 0; i--)
1292 Pattern *p = glob_patterns->pdata[i];
1293 g_free(p->glob);
1294 g_free(p);
1296 g_ptr_array_set_size(glob_patterns, 0);
1299 if (literal_hash)
1300 g_hash_table_destroy(literal_hash);
1301 if (extension_hash)
1302 g_hash_table_destroy(extension_hash);
1303 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1304 g_free, NULL);
1305 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1306 g_free, NULL);
1307 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1309 import_for_dir("/usr/share/mime/mime-info", globs, &got_freedesk);
1311 import_for_dir("/usr/local/share/mime/mime-info", globs, &got_freedesk);
1313 tmp = g_strconcat(home_dir, "/.mime/mime-info", NULL);
1314 import_for_dir(tmp, globs, &got_freedesk);
1315 g_free(tmp);
1317 /* Turn the globs hash into a pointer array */
1318 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1319 g_hash_table_destroy(globs);
1321 if (glob_patterns->len)
1322 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1324 if (!got_freedesk)
1326 delayed_error(_("The standard MIME type database was not "
1327 "found. The filer will probably not show the correct "
1328 "types for different files. You should download and "
1329 "install the 'Common types package' from here:\n"
1330 "http://www.freedesktop.org/standards/shared-mime-info.html"));
1333 filer_update_all();