r2634: Updates for the new shared MIME database.
[rox-filer.git] / ROX-Filer / src / type.c
blob76ddd76fb6ad6e474351092661e59d6323abd363
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"
60 #include "dropbox.h"
62 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
63 enum {SET_MEDIA, SET_TYPE};
65 /* Colours for file types (same order as base types) */
66 static gchar *opt_type_colours[][2] = {
67 {"display_err_colour", "#ff0000"},
68 {"display_unkn_colour", "#000000"},
69 {"display_dir_colour", "#000080"},
70 {"display_pipe_colour", "#444444"},
71 {"display_sock_colour", "#ff00ff"},
72 {"display_file_colour", "#000000"},
73 {"display_cdev_colour", "#000000"},
74 {"display_bdev_colour", "#000000"},
75 {"display_door_colour", "#ff00ff"},
76 {"display_exec_colour", "#006000"},
77 {"display_adir_colour", "#006000"}
79 #define NUM_TYPE_COLOURS\
80 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
82 /* Parsed colours for file types */
83 static Option o_type_colours[NUM_TYPE_COLOURS];
84 static GdkColor type_colours[NUM_TYPE_COLOURS];
86 /* Static prototypes */
87 static void load_mime_types(void);
88 static void alloc_type_colours(void);
89 static char *get_action_save_path(GtkWidget *dialog);
90 static void edit_mime_types(guchar *unused);
91 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
92 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label);
93 static gboolean remove_handler_with_confirm(const guchar *path);
95 /* When working out the type for a file, this hash table is checked
96 * first...
98 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
100 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
101 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
102 * The hash table is consulted from each dot in the string in turn
103 * (First .ps.gz, then try .gz)
105 static GHashTable *extension_hash = NULL;
107 /* The first pattern in the list which matches is used */
108 typedef struct pattern {
109 gint len; /* Used for sorting */
110 gchar *glob;
111 MIME_type *type;
112 } Pattern;
113 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
115 /* Hash of all allocated MIME types, indexed by "media/subtype".
116 * MIME_type structs are never freed; this table prevents memory leaks
117 * when rereading the config files.
119 static GHashTable *type_hash = NULL;
121 /* Most things on Unix are text files, so this is the default type */
122 MIME_type *text_plain;
123 MIME_type *inode_directory;
124 MIME_type *inode_pipe;
125 MIME_type *inode_socket;
126 MIME_type *inode_block_dev;
127 MIME_type *inode_char_dev;
128 MIME_type *application_executable;
129 MIME_type *inode_unknown;
130 MIME_type *inode_door;
132 static Option o_display_colour_types;
134 void type_init(void)
136 int i;
138 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
139 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
141 text_plain = get_mime_type("text/plain", TRUE);
142 inode_directory = get_mime_type("inode/directory", TRUE);
143 inode_pipe = get_mime_type("inode/fifo", TRUE);
144 inode_socket = get_mime_type("inode/socket", TRUE);
145 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
146 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
147 application_executable = get_mime_type("application/x-executable", TRUE);
148 inode_unknown = get_mime_type("inode/unknown", TRUE);
149 inode_door = get_mime_type("inode/door", TRUE);
151 load_mime_types();
153 option_register_widget("type-edit", build_type_edit);
155 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
157 for (i = 0; i < NUM_TYPE_COLOURS; i++)
158 option_add_string(&o_type_colours[i],
159 opt_type_colours[i][0],
160 opt_type_colours[i][1]);
161 alloc_type_colours();
163 option_add_notify(alloc_type_colours);
166 /* Read-load all the glob patterns.
167 * Note: calls filer_update_all.
169 void reread_mime_files(void)
171 load_mime_types();
174 /* Returns the MIME_type structure for the given type name. It is looked
175 * up in type_hash and returned if found. If not found (and can_create is
176 * TRUE) then a new MIME_type is made, added to type_hash and returned.
177 * NULL is returned if type_name is not in type_hash and can_create is
178 * FALSE, or if type_name does not contain a '/' character.
180 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
182 MIME_type *mtype;
183 gchar *slash;
185 mtype = g_hash_table_lookup(type_hash, type_name);
186 if (mtype || !can_create)
187 return mtype;
189 slash = strchr(type_name, '/');
190 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
192 mtype = g_new(MIME_type, 1);
193 mtype->media_type = g_strndup(type_name, slash - type_name);
194 mtype->subtype = g_strdup(slash + 1);
195 mtype->image = NULL;
196 mtype->comment = NULL;
198 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
200 return mtype;
203 const char *basetype_name(DirItem *item)
205 if (item->flags & ITEM_FLAG_SYMLINK)
206 return _("Sym link");
207 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
208 return _("Mount point");
209 else if (item->flags & ITEM_FLAG_APPDIR)
210 return _("App dir");
212 switch (item->base_type)
214 case TYPE_FILE:
215 return _("File");
216 case TYPE_DIRECTORY:
217 return _("Dir");
218 case TYPE_CHAR_DEVICE:
219 return _("Char dev");
220 case TYPE_BLOCK_DEVICE:
221 return _("Block dev");
222 case TYPE_PIPE:
223 return _("Pipe");
224 case TYPE_SOCKET:
225 return _("Socket");
226 case TYPE_DOOR:
227 return _("Door");
230 return _("Unknown");
233 /* MIME-type guessing */
235 /* Get the type of this file - stats the file and uses that if
236 * possible. For regular or missing files, uses the pathname.
238 MIME_type *type_get_type(const guchar *path)
240 struct stat info;
241 MIME_type *type = NULL;
242 int base = TYPE_FILE;
243 gboolean exec = FALSE;
245 if (mc_stat(path, &info) == 0)
247 base = mode_to_base_type(info.st_mode);
248 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
249 exec = TRUE;
252 if (base == TYPE_FILE)
253 type = type_from_path(path);
255 if (!type)
257 if (base == TYPE_FILE && exec)
258 type = application_executable;
259 else
260 type = mime_type_from_base_type(base);
263 return type;
266 /* Returns a pointer to the MIME-type.
267 * NULL if we can't think of anything.
269 MIME_type *type_from_path(const char *path)
271 const char *ext, *dot, *leafname;
272 char *lower;
273 MIME_type *type = NULL;
274 int i;
276 #if 0
277 # ifdef WITH_GNOMEVFS
278 if (o_use_gnomevfs.int_value)
279 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
280 # endif
281 #endif
283 leafname = g_basename(path);
285 type = g_hash_table_lookup(literal_hash, leafname);
286 if (type)
287 return type;
288 lower = g_utf8_strdown(leafname, -1);
289 type = g_hash_table_lookup(literal_hash, lower);
290 if (type)
291 goto out;
293 ext = leafname;
295 while ((dot = strchr(ext, '.')))
297 ext = dot + 1;
299 type = g_hash_table_lookup(extension_hash, ext);
301 if (type)
302 goto out;
304 type = g_hash_table_lookup(extension_hash,
305 lower + (ext - leafname));
306 if (type)
307 goto out;
310 for (i = 0; i < glob_patterns->len; i++)
312 Pattern *p = glob_patterns->pdata[i];
314 if (fnmatch(p->glob, leafname, 0) == 0 ||
315 fnmatch(p->glob, lower, 0) == 0)
317 type = p->type;
318 goto out;
322 out:
323 g_free(lower);
325 return type;
328 /* Returns the file/dir in Choices for handling this type.
329 * NULL if there isn't one. g_free() the result.
331 static char *handler_for(MIME_type *type)
333 char *type_name;
334 char *open;
336 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
337 open = choices_find_path_load(type_name, "MIME-types");
338 g_free(type_name);
340 if (!open)
341 open = choices_find_path_load(type->media_type, "MIME-types");
343 return open;
346 /* Actions for types */
348 gboolean type_open(const char *path, MIME_type *type)
350 gchar *argv[] = {NULL, NULL, NULL};
351 char *open;
352 gboolean retval;
353 struct stat info;
355 argv[1] = (char *) path;
357 open = handler_for(type);
358 if (!open)
359 return FALSE;
361 if (stat(open, &info))
363 report_error("stat(%s): %s", open, g_strerror(errno));
364 g_free(open);
365 return FALSE;
368 if (info.st_mode & S_IWOTH)
370 gchar *choices_dir;
371 GList *paths;
373 report_error(_("Executable '%s' is world-writeable! Refusing "
374 "to run. Please change the permissions now (this "
375 "problem may have been caused by a bug in earlier "
376 "versions of the filer).\n\n"
377 "Having (non-symlink) run actions world-writeable "
378 "means that other people who use your computer can "
379 "replace your run actions with malicious versions.\n\n"
380 "If you trust everyone who could write to these files "
381 "then you needn't worry. Otherwise, you should check, "
382 "or even just delete, all the existing run actions."),
383 open);
384 choices_dir = g_path_get_dirname(open);
385 paths = g_list_append(NULL, choices_dir);
386 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
387 g_free(choices_dir);
388 g_list_free(paths);
389 g_free(open);
390 return TRUE;
393 if (S_ISDIR(info.st_mode))
394 argv[0] = g_strconcat(open, "/AppRun", NULL);
395 else
396 argv[0] = open;
398 retval = rox_spawn(home_dir, (const gchar **) argv);
400 if (argv[0] != open)
401 g_free(argv[0]);
403 g_free(open);
405 return retval;
408 /* Return the image for this type, loading it if needed.
409 * Places to check are: (eg type="text_plain", base="text")
410 * 1. Choices:MIME-icons/<type>
411 * 2. Choices:MIME-icons/<base>
412 * 3. Unknown type icon.
414 * Note: You must g_object_unref() the image afterwards.
416 MaskedPixmap *type_to_icon(MIME_type *type)
418 char *path;
419 char *type_name;
420 time_t now;
422 if (type == NULL)
424 g_object_ref(im_unknown);
425 return im_unknown;
428 now = time(NULL);
429 /* Already got an image? */
430 if (type->image)
432 /* Yes - don't recheck too often */
433 if (abs(now - type->image_time) < 2)
435 g_object_ref(type->image);
436 return type->image;
438 g_object_unref(type->image);
439 type->image = NULL;
442 type_name = g_strconcat(type->media_type, "_",
443 type->subtype, ".png", NULL);
444 path = choices_find_path_load(type_name, "MIME-icons");
445 if (!path)
447 strcpy(type_name + strlen(type->media_type), ".png");
448 path = choices_find_path_load(type_name, "MIME-icons");
451 g_free(type_name);
453 if (path)
455 type->image = g_fscache_lookup(pixmap_cache, path);
456 g_free(path);
459 if (!type->image)
461 /* One ref from the type structure, one returned */
462 type->image = im_unknown;
463 g_object_ref(im_unknown);
466 type->image_time = now;
468 g_object_ref(type->image);
469 return type->image;
472 GdkAtom type_to_atom(MIME_type *type)
474 char *str;
475 GdkAtom retval;
477 g_return_val_if_fail(type != NULL, GDK_NONE);
479 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
480 retval = gdk_atom_intern(str, FALSE);
481 g_free(str);
483 return retval;
486 static void show_shell_help(gpointer data)
488 info_message(_("Enter a shell command which will load \"$@\" into "
489 "a suitable program. Eg:\n\n"
490 "gimp \"$@\""));
493 /* Called if the user clicks on the OK button. Returns FALSE if an error
494 * was displayed instead of performing the action.
496 static gboolean set_shell_action(GtkWidget *dialog)
498 GtkEntry *entry;
499 const guchar *command;
500 gchar *tmp, *path;
501 int error = 0, len;
502 int fd;
504 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
506 g_return_val_if_fail(entry != NULL, FALSE);
508 command = gtk_entry_get_text(entry);
510 if (!strchr(command, '$'))
512 show_shell_help(NULL);
513 return FALSE;
516 path = get_action_save_path(dialog);
517 if (!path)
518 return FALSE;
520 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
521 len = strlen(tmp);
523 fd = open(path, O_CREAT | O_WRONLY, 0755);
524 if (fd == -1)
525 error = errno;
526 else
528 FILE *file;
530 file = fdopen(fd, "w");
531 if (file)
533 if (fwrite(tmp, 1, len, file) < len)
534 error = errno;
535 if (fclose(file) && error == 0)
536 error = errno;
538 else
539 error = errno;
542 if (error)
543 report_error(g_strerror(error));
545 g_free(tmp);
546 g_free(path);
548 gtk_widget_destroy(dialog);
550 return TRUE;
553 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
555 if (response == GTK_RESPONSE_OK)
556 if (!set_shell_action(dialog))
557 return;
558 gtk_widget_destroy(dialog);
561 /* Return the path of the file in choices that handles this type and
562 * radio setting.
563 * NULL if nothing is defined for it.
565 static guchar *handler_for_radios(GObject *dialog)
567 Radios *radios;
568 MIME_type *type;
570 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
571 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
573 g_return_val_if_fail(radios != NULL, NULL);
574 g_return_val_if_fail(type != NULL, NULL);
576 switch (radios_get_value(radios))
578 case SET_MEDIA:
579 return choices_find_path_load(type->media_type,
580 "MIME-types");
581 case SET_TYPE:
583 gchar *tmp, *handler;
584 tmp = g_strconcat(type->media_type, "_",
585 type->subtype, NULL);
586 handler = choices_find_path_load(tmp, "MIME-types");
587 g_free(tmp);
588 return handler;
590 default:
591 g_warning("Bad type");
592 return NULL;
596 static void run_action_update(gpointer data)
598 guchar *handler;
599 DropBox *drop_box;
600 GObject *dialog = G_OBJECT(data);
602 drop_box = g_object_get_data(dialog, "rox-dropbox");
604 g_return_if_fail(drop_box != NULL);
606 handler = handler_for_radios(dialog);
608 if (handler)
610 char *old = handler;
612 handler = readlink_dup(old);
613 if (handler)
614 g_free(old);
615 else
616 handler = old;
619 drop_box_set_path(DROP_BOX(drop_box), handler);
620 g_free(handler);
623 static void clear_run_action(GtkWidget *drop_box, GtkWidget *dialog)
625 guchar *handler;
627 handler = handler_for_radios(G_OBJECT(dialog));
629 if (handler)
630 remove_handler_with_confirm(handler);
632 run_action_update(dialog);
635 /* Called when a URI list is dropped onto the box in the Set Run Action
636 * dialog. Make sure it's an application, and make that the default
637 * handler.
639 static void drag_app_dropped(GtkWidget *drop_box,
640 const guchar *app,
641 GtkWidget *dialog)
643 DirItem *item;
645 item = diritem_new("");
646 diritem_restat(app, item, NULL);
647 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
649 guchar *path;
651 path = get_action_save_path(dialog);
653 if (path)
655 if (symlink(app, path))
656 delayed_error("symlink: %s",
657 g_strerror(errno));
658 else
659 destroy_on_idle(dialog);
661 g_free(path);
664 else
665 delayed_error(
666 _("This is not a program! Give me an application "
667 "instead!"));
669 diritem_free(item);
672 /* Find the current command which is used to run files of this type.
673 * Returns NULL on failure. g_free() the result.
675 static guchar *get_current_command(MIME_type *type)
677 struct stat info;
678 char *handler, *nl, *data = NULL;
679 long len;
680 guchar *command = NULL;
682 handler = handler_for(type);
684 if (!handler)
685 return NULL; /* No current handler */
687 if (stat(handler, &info))
688 goto out; /* Can't stat */
690 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
691 goto out; /* Only use small regular files */
693 if (!load_file(handler, &data, &len))
694 goto out; /* Didn't load OK */
696 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
697 goto out; /* Not one of ours */
699 nl = strchr(data + 16, '\n');
700 if (!nl)
701 goto out; /* No newline! */
703 command = g_strndup(data + 16, nl - data - 16);
704 out:
705 g_free(handler);
706 g_free(data);
707 return command;
710 /* Find the current command which is used to run files of this type,
711 * and return a textual description of it.
712 * g_free() the result.
714 gchar *describe_current_command(MIME_type *type)
716 char *handler;
717 char *desc = NULL;
718 struct stat info;
719 char *target;
721 g_return_val_if_fail(type != NULL, NULL);
723 if (type == application_executable)
724 return g_strdup(_("Execute file"));
726 handler = handler_for(type);
728 if (!handler)
729 return g_strdup(_("No run action defined"));
731 target = readlink_dup(handler);
732 if (target)
734 /* Cope with relative paths (shouldn't normally be needed) */
736 if (target[0] == '/')
738 g_free(handler);
739 handler = target;
741 else
743 gchar *dir;
745 dir = g_path_get_dirname(handler);
746 g_free(handler);
747 handler = g_strconcat(dir, "/", target, NULL);
748 g_free(target);
749 g_free(dir);
753 if (mc_stat(handler, &info) !=0 )
755 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
756 g_strerror(errno));
757 goto out;
760 if (S_ISDIR(info.st_mode))
762 const guchar *tmp;
763 uid_t dir_uid = info.st_uid;
765 tmp = make_path(handler, "AppRun");
767 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
768 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
769 desc = g_strdup_printf(
770 _("Invalid application %s (bad AppRun)"),
771 handler);
772 /* Else, just report handler... */
774 goto out;
777 /* It's not an application directory, and it's not a symlink... */
779 if (access(handler, X_OK) != 0)
781 desc = g_strdup_printf(_("Non-executable %s"), handler);
782 goto out;
785 desc = get_current_command(type);
786 out:
787 if (!desc)
788 desc = handler;
789 else
790 g_free(handler);
792 return desc;
795 /* Display a dialog box allowing the user to set the default run action
796 * for this type.
798 void type_set_handler_dialog(MIME_type *type)
800 guchar *tmp;
801 GtkDialog *dialog;
802 GtkWidget *frame, *entry, *label;
803 GtkWidget *hbox;
804 Radios *radios;
806 g_return_if_fail(type != NULL);
808 dialog = GTK_DIALOG(gtk_dialog_new());
809 gtk_dialog_set_has_separator(dialog, FALSE);
810 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
812 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
814 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
816 radios = radios_new(run_action_update, dialog);
817 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
819 radios_add(radios,
820 _("If a handler for the specific type isn't set up, "
821 "use this as the default."), SET_MEDIA,
822 _("Set default for all `%s/<anything>'"),
823 type->media_type);
825 radios_add(radios,
826 _("Use this application for all files with this MIME "
827 "type."), SET_TYPE,
828 _("Only for the type `%s' (%s/%s)"),
829 mime_type_comment(type),
830 type->media_type, type->subtype);
832 radios_set_value(radios, SET_TYPE);
834 frame = drop_box_new(_("Drop a suitable application here"));
836 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
838 radios_pack(radios, GTK_BOX(dialog->vbox));
839 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
841 g_signal_connect(frame, "path_dropped",
842 G_CALLBACK(drag_app_dropped), dialog);
843 g_signal_connect(frame, "clear",
844 G_CALLBACK(clear_run_action), dialog);
846 hbox = gtk_hbox_new(FALSE, 4);
847 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
848 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
849 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
850 FALSE, TRUE, 0);
851 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
853 hbox = gtk_hbox_new(FALSE, 4);
854 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
856 label = gtk_label_new(_("Enter a shell command:")),
857 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
858 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
860 gtk_box_pack_start(GTK_BOX(hbox),
861 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
863 entry = gtk_entry_new();
864 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
865 gtk_widget_grab_focus(entry);
866 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
867 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
869 /* If possible, fill in the entry box with the current command */
870 tmp = get_current_command(type);
871 if (tmp)
873 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
874 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
875 g_free(tmp);
877 else
879 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
880 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
883 gtk_dialog_add_buttons(dialog,
884 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
885 GTK_STOCK_OK, GTK_RESPONSE_OK,
886 NULL);
888 hbox = gtk_hbox_new(TRUE, 4);
889 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
891 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
893 g_signal_connect(dialog, "response",
894 G_CALLBACK(set_action_response), NULL);
896 gtk_widget_show_all(GTK_WIDGET(dialog));
899 /* path is an entry in Choices. If it's a symlink or a very small executable
900 * then just get rid of it, otherwise confirm first. It it doesn't exist,
901 * do nothing.
903 * FALSE on error (abort operation).
905 static gboolean remove_handler_with_confirm(const guchar *path)
907 struct stat info;
909 if (lstat(path, &info) == 0)
911 /* A binding already exists... */
912 if (S_ISREG(info.st_mode) && info.st_size > 256)
914 if (!confirm(_("A run action already exists and is "
915 "quite a big program - are you sure "
916 "you want to delete it?"),
917 GTK_STOCK_DELETE, NULL))
919 return FALSE;
923 if (unlink(path))
925 report_error(_("Can't remove %s: %s"),
926 path, g_strerror(errno));
927 return FALSE;
931 return TRUE;
934 /* The user wants to set a new default action for files of this type (or just
935 * clear the action). Removes the current binding if possible and returns the
936 * path to save the new one to. NULL means cancel. g_free() the result.
938 static char *get_action_save_path(GtkWidget *dialog)
940 guchar *path = NULL;
941 guchar *type_name = NULL;
942 MIME_type *type;
943 Radios *radios;
945 g_return_val_if_fail(dialog != NULL, NULL);
947 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
948 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
950 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
952 if (radios_get_value(radios) == SET_MEDIA)
953 type_name = g_strdup(type->media_type);
954 else
955 type_name = g_strconcat(type->media_type, "_",
956 type->subtype, NULL);
958 path = choices_find_path_save("", PROJECT, FALSE);
959 if (!path)
961 report_error(
962 _("Choices saving is disabled by CHOICESPATH variable"));
963 goto out;
965 g_free(path);
967 path = choices_find_path_save(type_name, "MIME-types", TRUE);
969 if (!remove_handler_with_confirm(path))
970 null_g_free(&path);
971 out:
972 g_free(type_name);
973 return path;
976 MIME_type *mime_type_from_base_type(int base_type)
978 switch (base_type)
980 case TYPE_FILE:
981 return text_plain;
982 case TYPE_DIRECTORY:
983 return inode_directory;
984 case TYPE_PIPE:
985 return inode_pipe;
986 case TYPE_SOCKET:
987 return inode_socket;
988 case TYPE_BLOCK_DEVICE:
989 return inode_block_dev;
990 case TYPE_CHAR_DEVICE:
991 return inode_char_dev;
992 case TYPE_DOOR:
993 return inode_door;
995 return inode_unknown;
998 /* Takes the st_mode field from stat() and returns the base type.
999 * Should not be a symlink.
1001 int mode_to_base_type(int st_mode)
1003 if (S_ISREG(st_mode))
1004 return TYPE_FILE;
1005 else if (S_ISDIR(st_mode))
1006 return TYPE_DIRECTORY;
1007 else if (S_ISBLK(st_mode))
1008 return TYPE_BLOCK_DEVICE;
1009 else if (S_ISCHR(st_mode))
1010 return TYPE_CHAR_DEVICE;
1011 else if (S_ISFIFO(st_mode))
1012 return TYPE_PIPE;
1013 else if (S_ISSOCK(st_mode))
1014 return TYPE_SOCKET;
1015 else if (S_ISDOOR(st_mode))
1016 return TYPE_DOOR;
1018 return TYPE_ERROR;
1021 /* Returns TRUE is this is something that is run by looking up its type
1022 * in MIME-types and, hence, can have its run action set.
1024 gboolean can_set_run_action(DirItem *item)
1026 g_return_val_if_fail(item != NULL, FALSE);
1028 return item->base_type == TYPE_FILE &&
1029 !(item->mime_type == application_executable);
1032 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
1033 static void edit_mime_types(guchar *unused)
1035 const guchar *path;
1037 mkdir(make_path(home_dir, ".mime"), 0700);
1038 path = make_path(home_dir, ".mime/packages");
1039 mkdir(path, 0700);
1040 filer_opendir(path, NULL, NULL);
1042 path = "/usr/local/share/mime/packages";
1043 if (file_exists(path))
1044 filer_opendir(path, NULL, NULL);
1046 path = "/usr/share/mime/packages";
1047 if (file_exists(path))
1048 filer_opendir(path, NULL, NULL);
1051 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
1053 GtkWidget *button, *align;
1055 g_return_val_if_fail(none == NULL, NULL);
1057 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1058 button = gtk_button_new_with_label(_(label));
1059 gtk_container_add(GTK_CONTAINER(align), button);
1061 g_signal_connect_swapped(button, "clicked",
1062 G_CALLBACK(edit_mime_types), NULL);
1064 return g_list_append(NULL, align);
1067 /* Parse file type colours and allocate/free them as necessary */
1068 static void alloc_type_colours(void)
1070 gboolean success[NUM_TYPE_COLOURS];
1071 int change_count = 0; /* No. needing realloc */
1072 int i;
1073 static gboolean allocated = FALSE;
1075 /* Parse colours */
1076 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1078 GdkColor *c = &type_colours[i];
1079 gushort r = c->red;
1080 gushort g = c->green;
1081 gushort b = c->blue;
1083 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1085 if (allocated && (c->red != r || c->green != g || c->blue != b))
1086 change_count++;
1089 /* Free colours if they were previously allocated and
1090 * have changed or become unneeded.
1092 if (allocated && (change_count || !o_display_colour_types.int_value))
1094 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1095 type_colours, NUM_TYPE_COLOURS);
1096 allocated = FALSE;
1099 /* Allocate colours, unless they are still allocated (=> they didn't
1100 * change) or we don't want them anymore.
1101 * XXX: what should be done if allocation fails?
1103 if (!allocated && o_display_colour_types.int_value)
1105 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1106 type_colours, NUM_TYPE_COLOURS,
1107 FALSE, TRUE, success);
1108 allocated = TRUE;
1112 /* Return a pointer to a (static) colour for this item. If colouring is
1113 * off, returns normal.
1115 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1117 int type = item->base_type;
1119 if (!o_display_colour_types.int_value)
1120 return normal;
1122 if (item->flags & ITEM_FLAG_EXEC_FILE)
1123 type = TYPE_EXEC;
1124 else if (item->flags & ITEM_FLAG_APPDIR)
1125 type = TYPE_APPDIR;
1127 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1129 return &type_colours[type];
1132 /* Process the 'Patterns' value */
1133 static void add_pattern(MIME_type *type, const char *pattern, GHashTable *globs)
1135 if (pattern[0] == '*' && pattern[1] == '.' &&
1136 strpbrk(pattern + 2, "*?[") == NULL)
1138 g_hash_table_insert(extension_hash,
1139 g_strdup(pattern + 2),
1140 type);
1142 else if (strpbrk(pattern, "*?[") == NULL)
1143 g_hash_table_insert(literal_hash, g_strdup(pattern), type);
1144 else
1145 g_hash_table_insert(globs, g_strdup(pattern), type);
1148 /* Load and parse this file. literal_hash and extension_hash are updated
1149 * directly. Other patterns are added to 'globs'.
1151 static void import_file(const gchar *file, GHashTable *globs)
1153 MIME_type *type = NULL;
1154 GError *error = NULL;
1155 gchar *data, *line;
1157 if (access(file, F_OK) != 0)
1158 return; /* Doesn't exist. No problem. */
1160 if (!g_file_get_contents(file, &data, NULL, &error))
1162 delayed_error(_("Error loading MIME database:\n%s"),
1163 error->message);
1164 g_error_free(error);
1165 return;
1168 line = data;
1170 while (line && *line)
1172 char *nl;
1174 nl = strchr(line, '\n');
1175 if (!nl)
1176 break;
1177 *nl = '\0';
1179 if (*line != '#')
1181 const gchar *colon;
1182 gchar *name;
1184 colon = strchr(line, ':');
1185 if (!colon)
1187 delayed_error(_("File '%s' corrupted!"), file);
1188 break;
1191 name = g_strndup(line, colon - line);
1192 type = get_mime_type(name, TRUE);
1193 g_free(name);
1194 if (!type)
1195 g_warning("Invalid type in '%s'", file);
1197 add_pattern(type, colon + 1, globs);
1200 line = nl + 1;
1203 g_free(data);
1206 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1208 Pattern *pattern;
1210 pattern = g_new(Pattern, 1);
1211 pattern->glob = g_strdup((gchar *) key);
1212 pattern->type = (MIME_type *) value;
1213 pattern->len = strlen(pattern->glob);
1215 g_ptr_array_add(glob_patterns, pattern);
1218 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1220 const Pattern *pa = *(const Pattern **) a;
1221 const Pattern *pb = *(const Pattern **) b;
1223 if (pa->len > pb->len)
1224 return -1;
1225 else if (pa->len == pb->len)
1226 return 0;
1227 return 1;
1230 static char **get_xdg_data_dirs(int *n_dirs)
1232 const char *env;
1233 char **dirs;
1234 int i, n;
1236 env = getenv("XDG_DATA_DIRS");
1237 if (!env)
1238 env = "/usr/local/share/:/usr/share/";
1239 dirs = g_strsplit(env, ":", 0);
1240 g_return_val_if_fail(dirs != NULL, NULL);
1241 for (n = 0; dirs[n]; n++)
1243 for (i = n; i > 0; i--)
1244 dirs[i] = dirs[i - 1];
1245 env = getenv("XDG_DATA_HOME");
1246 if (env)
1247 dirs[0] = g_strdup(env);
1248 else
1249 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1250 "share", NULL);
1251 *n_dirs = n + 1;
1252 return dirs;
1255 /* Clear all currently stored information and re-read everything.
1256 * Note: calls filer_update_all.
1258 static void load_mime_types(void)
1260 GHashTable *globs;
1261 char **dirs;
1262 int n_dirs;
1263 int i;
1265 dirs = get_xdg_data_dirs(&n_dirs);
1266 g_return_if_fail(dirs != NULL);
1269 struct stat info;
1270 if (lstat(make_path(home_dir, ".mime"), &info) == 0 &&
1271 S_ISDIR(info.st_mode))
1273 delayed_error(_("The ~/.mime directory has moved. "
1274 "It should now be ~/.local/share/mime. You "
1275 "should move it there (and make a symlink "
1276 "from ~/.mime to it for older applications)."));
1280 if (!glob_patterns)
1281 glob_patterns = g_ptr_array_new();
1282 else
1284 int i;
1286 for (i = glob_patterns->len - 1; i >= 0; i--)
1288 Pattern *p = glob_patterns->pdata[i];
1289 g_free(p->glob);
1290 g_free(p);
1292 g_ptr_array_set_size(glob_patterns, 0);
1295 if (literal_hash)
1296 g_hash_table_destroy(literal_hash);
1297 if (extension_hash)
1298 g_hash_table_destroy(extension_hash);
1299 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1300 g_free, NULL);
1301 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1302 g_free, NULL);
1303 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1305 for (i = n_dirs - 1; i >= 0; i--)
1307 char *path;
1308 path = g_build_filename(dirs[i], "mime", "globs", NULL);
1309 import_file(path, globs);
1310 g_free(path);
1311 g_free(dirs[i]);
1313 g_free(dirs);
1315 /* Turn the globs hash into a pointer array */
1316 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1317 g_hash_table_destroy(globs);
1318 globs = NULL;
1320 if (glob_patterns->len)
1321 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1323 if (g_hash_table_size(extension_hash) == 0)
1325 delayed_error(_("The standard MIME type database "
1326 "(version 0.9 or later) was not found. "
1327 "The filer will probably not show the correct "
1328 "types for different files. You should download and "
1329 "install the 'shared-mime-info-0.9' package from "
1330 "here:\n"
1331 "http://www.freedesktop.org/standards/shared-mime-info.html\n\n"
1332 "If you have already installed this package, check that the "
1333 "permissions allow the files to be read (check "
1334 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1337 filer_update_all();
1340 /* Try to fill in 'type->comment' from this document.
1341 * Frees 'path'.
1343 static void get_comment(MIME_type *type, guchar *path)
1345 xmlNode *node;
1346 XMLwrapper *doc;
1348 doc = xml_cache_load(path);
1349 g_free(path);
1350 if (!doc)
1351 return;
1353 node = xml_get_section(doc, TYPE_NS, "comment");
1355 if (node)
1357 char *val;
1358 g_return_if_fail(type->comment == NULL);
1359 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1360 type->comment = g_strdup(val);
1361 xmlFree(val);
1364 g_object_unref(doc);
1367 /* Fill in the comment field for this MIME type.
1369 * g_object_unref() the result.
1371 static void find_comment(MIME_type *type)
1373 guchar *path;
1375 if (type->comment)
1377 g_free(type->comment);
1378 type->comment = NULL;
1381 path = g_strdup_printf("%s/.mime/%s/%s.xml", home_dir,
1382 type->media_type, type->subtype);
1383 get_comment(type, path);
1384 if (type->comment)
1385 return;
1387 path = g_strdup_printf("/usr/local/share/mime/%s/%s.xml",
1388 type->media_type, type->subtype);
1389 get_comment(type, path);
1390 if (type->comment)
1391 return;
1393 path = g_strdup_printf("/usr/share/mime/%s/%s.xml",
1394 type->media_type, type->subtype);
1395 get_comment(type, path);
1396 if (type->comment)
1397 return;
1399 type->comment = g_strdup(_("No description"));
1402 const char *mime_type_comment(MIME_type *type)
1404 if (!type->comment)
1405 find_comment(type);
1407 return type->comment;