r2918: Doing a Refresh in a directory under /uri/0install triggers a remote refresh.
[rox-filer.git] / ROX-Filer / src / type.c
blob590247c43f3e257d2862ed888233642f26ebb946
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 MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
91 static gboolean remove_handler_with_confirm(const guchar *path);
93 /* When working out the type for a file, this hash table is checked
94 * first...
96 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
98 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
99 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
100 * The hash table is consulted from each dot in the string in turn
101 * (First .ps.gz, then try .gz)
103 static GHashTable *extension_hash = NULL;
105 /* The first pattern in the list which matches is used */
106 typedef struct pattern {
107 gint len; /* Used for sorting */
108 gchar *glob;
109 MIME_type *type;
110 } Pattern;
111 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
113 /* Hash of all allocated MIME types, indexed by "media/subtype".
114 * MIME_type structs are never freed; this table prevents memory leaks
115 * when rereading the config files.
117 static GHashTable *type_hash = NULL;
119 /* Most things on Unix are text files, so this is the default type */
120 MIME_type *text_plain;
121 MIME_type *inode_directory;
122 MIME_type *inode_pipe;
123 MIME_type *inode_socket;
124 MIME_type *inode_block_dev;
125 MIME_type *inode_char_dev;
126 MIME_type *application_executable;
127 MIME_type *inode_unknown;
128 MIME_type *inode_door;
130 static Option o_display_colour_types;
132 void type_init(void)
134 int i;
136 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
137 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
139 text_plain = get_mime_type("text/plain", TRUE);
140 inode_directory = get_mime_type("inode/directory", TRUE);
141 inode_pipe = get_mime_type("inode/fifo", TRUE);
142 inode_socket = get_mime_type("inode/socket", TRUE);
143 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
144 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
145 application_executable = get_mime_type("application/x-executable", TRUE);
146 inode_unknown = get_mime_type("inode/unknown", TRUE);
147 inode_door = get_mime_type("inode/door", TRUE);
149 load_mime_types();
151 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
153 for (i = 0; i < NUM_TYPE_COLOURS; i++)
154 option_add_string(&o_type_colours[i],
155 opt_type_colours[i][0],
156 opt_type_colours[i][1]);
157 alloc_type_colours();
159 option_add_notify(alloc_type_colours);
162 /* Read-load all the glob patterns.
163 * Note: calls filer_update_all.
165 void reread_mime_files(void)
167 load_mime_types();
170 /* Returns the MIME_type structure for the given type name. It is looked
171 * up in type_hash and returned if found. If not found (and can_create is
172 * TRUE) then a new MIME_type is made, added to type_hash and returned.
173 * NULL is returned if type_name is not in type_hash and can_create is
174 * FALSE, or if type_name does not contain a '/' character.
176 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
178 MIME_type *mtype;
179 gchar *slash;
181 mtype = g_hash_table_lookup(type_hash, type_name);
182 if (mtype || !can_create)
183 return mtype;
185 slash = strchr(type_name, '/');
186 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
188 mtype = g_new(MIME_type, 1);
189 mtype->media_type = g_strndup(type_name, slash - type_name);
190 mtype->subtype = g_strdup(slash + 1);
191 mtype->image = NULL;
192 mtype->comment = NULL;
194 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
196 return mtype;
199 const char *basetype_name(DirItem *item)
201 if (item->flags & ITEM_FLAG_SYMLINK)
202 return _("Sym link");
203 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
204 return _("Mount point");
205 else if (item->flags & ITEM_FLAG_APPDIR)
206 return _("App dir");
208 switch (item->base_type)
210 case TYPE_FILE:
211 return _("File");
212 case TYPE_DIRECTORY:
213 return _("Dir");
214 case TYPE_CHAR_DEVICE:
215 return _("Char dev");
216 case TYPE_BLOCK_DEVICE:
217 return _("Block dev");
218 case TYPE_PIPE:
219 return _("Pipe");
220 case TYPE_SOCKET:
221 return _("Socket");
222 case TYPE_DOOR:
223 return _("Door");
226 return _("Unknown");
229 /* MIME-type guessing */
231 /* Get the type of this file - stats the file and uses that if
232 * possible. For regular or missing files, uses the pathname.
234 MIME_type *type_get_type(const guchar *path)
236 struct stat info;
237 MIME_type *type = NULL;
238 int base = TYPE_FILE;
239 gboolean exec = FALSE;
241 if (mc_stat(path, &info) == 0)
243 base = mode_to_base_type(info.st_mode);
244 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
245 exec = TRUE;
248 if (base == TYPE_FILE)
249 type = type_from_path(path);
251 if (!type)
253 if (base == TYPE_FILE && exec)
254 type = application_executable;
255 else
256 type = mime_type_from_base_type(base);
259 return type;
262 /* Returns a pointer to the MIME-type.
263 * NULL if we can't think of anything.
265 MIME_type *type_from_path(const char *path)
267 const char *ext, *dot, *leafname;
268 char *lower;
269 MIME_type *type = NULL;
270 int i;
272 #if 0
273 # ifdef WITH_GNOMEVFS
274 if (o_use_gnomevfs.int_value)
275 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
276 # endif
277 #endif
279 leafname = g_basename(path);
281 type = g_hash_table_lookup(literal_hash, leafname);
282 if (type)
283 return type;
284 lower = g_utf8_strdown(leafname, -1);
285 type = g_hash_table_lookup(literal_hash, lower);
286 if (type)
287 goto out;
289 ext = leafname;
291 while ((dot = strchr(ext, '.')))
293 ext = dot + 1;
295 type = g_hash_table_lookup(extension_hash, ext);
297 if (type)
298 goto out;
300 type = g_hash_table_lookup(extension_hash,
301 lower + (ext - leafname));
302 if (type)
303 goto out;
306 for (i = 0; i < glob_patterns->len; i++)
308 Pattern *p = glob_patterns->pdata[i];
310 if (fnmatch(p->glob, leafname, 0) == 0 ||
311 fnmatch(p->glob, lower, 0) == 0)
313 type = p->type;
314 goto out;
318 out:
319 g_free(lower);
321 return type;
324 /* Returns the file/dir in Choices for handling this type.
325 * NULL if there isn't one. g_free() the result.
327 static char *handler_for(MIME_type *type)
329 char *type_name;
330 char *open;
332 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
333 open = choices_find_path_load(type_name, "MIME-types");
334 g_free(type_name);
336 if (!open)
337 open = choices_find_path_load(type->media_type, "MIME-types");
339 return open;
342 /* Actions for types */
344 gboolean type_open(const char *path, MIME_type *type)
346 gchar *argv[] = {NULL, NULL, NULL};
347 char *open;
348 gboolean retval;
349 struct stat info;
351 argv[1] = (char *) path;
353 open = handler_for(type);
354 if (!open)
355 return FALSE;
357 if (stat(open, &info))
359 report_error("stat(%s): %s", open, g_strerror(errno));
360 g_free(open);
361 return FALSE;
364 if (info.st_mode & S_IWOTH)
366 gchar *choices_dir;
367 GList *paths;
369 report_error(_("Executable '%s' is world-writeable! Refusing "
370 "to run. Please change the permissions now (this "
371 "problem may have been caused by a bug in earlier "
372 "versions of the filer).\n\n"
373 "Having (non-symlink) run actions world-writeable "
374 "means that other people who use your computer can "
375 "replace your run actions with malicious versions.\n\n"
376 "If you trust everyone who could write to these files "
377 "then you needn't worry. Otherwise, you should check, "
378 "or even just delete, all the existing run actions."),
379 open);
380 choices_dir = g_path_get_dirname(open);
381 paths = g_list_append(NULL, choices_dir);
382 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
383 g_free(choices_dir);
384 g_list_free(paths);
385 g_free(open);
386 return TRUE;
389 if (S_ISDIR(info.st_mode))
390 argv[0] = g_strconcat(open, "/AppRun", NULL);
391 else
392 argv[0] = open;
394 retval = rox_spawn(home_dir, (const gchar **) argv) != 0;
396 if (argv[0] != open)
397 g_free(argv[0]);
399 g_free(open);
401 return retval;
404 /* Return the image for this type, loading it if needed.
405 * Places to check are: (eg type="text_plain", base="text")
406 * 1. Choices:MIME-icons/<type>
407 * 2. Choices:MIME-icons/<base>
408 * 3. Unknown type icon.
410 * Note: You must g_object_unref() the image afterwards.
412 MaskedPixmap *type_to_icon(MIME_type *type)
414 char *path;
415 char *type_name;
416 time_t now;
418 if (type == NULL)
420 g_object_ref(im_unknown);
421 return im_unknown;
424 now = time(NULL);
425 /* Already got an image? */
426 if (type->image)
428 /* Yes - don't recheck too often */
429 if (abs(now - type->image_time) < 2)
431 g_object_ref(type->image);
432 return type->image;
434 g_object_unref(type->image);
435 type->image = NULL;
438 type_name = g_strconcat(type->media_type, "_",
439 type->subtype, ".png", NULL);
440 path = choices_find_path_load(type_name, "MIME-icons");
441 if (!path)
443 strcpy(type_name + strlen(type->media_type), ".png");
444 path = choices_find_path_load(type_name, "MIME-icons");
447 g_free(type_name);
449 if (path)
451 type->image = g_fscache_lookup(pixmap_cache, path);
452 g_free(path);
455 if (!type->image)
457 /* One ref from the type structure, one returned */
458 type->image = im_unknown;
459 g_object_ref(im_unknown);
462 type->image_time = now;
464 g_object_ref(type->image);
465 return type->image;
468 GdkAtom type_to_atom(MIME_type *type)
470 char *str;
471 GdkAtom retval;
473 g_return_val_if_fail(type != NULL, GDK_NONE);
475 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
476 retval = gdk_atom_intern(str, FALSE);
477 g_free(str);
479 return retval;
482 static void show_shell_help(gpointer data)
484 info_message(_("Enter a shell command which will load \"$@\" into "
485 "a suitable program. Eg:\n\n"
486 "gimp \"$@\""));
489 /* Called if the user clicks on the OK button. Returns FALSE if an error
490 * was displayed instead of performing the action.
492 static gboolean set_shell_action(GtkWidget *dialog)
494 GtkEntry *entry;
495 const guchar *command;
496 gchar *tmp, *path;
497 int error = 0, len;
498 int fd;
500 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
502 g_return_val_if_fail(entry != NULL, FALSE);
504 command = gtk_entry_get_text(entry);
506 if (!strchr(command, '$'))
508 show_shell_help(NULL);
509 return FALSE;
512 path = get_action_save_path(dialog);
513 if (!path)
514 return FALSE;
516 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
517 len = strlen(tmp);
519 fd = open(path, O_CREAT | O_WRONLY, 0755);
520 if (fd == -1)
521 error = errno;
522 else
524 FILE *file;
526 file = fdopen(fd, "w");
527 if (file)
529 if (fwrite(tmp, 1, len, file) < len)
530 error = errno;
531 if (fclose(file) && error == 0)
532 error = errno;
534 else
535 error = errno;
538 if (error)
539 report_error(g_strerror(error));
541 g_free(tmp);
542 g_free(path);
544 gtk_widget_destroy(dialog);
546 return TRUE;
549 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
551 if (response == GTK_RESPONSE_OK)
552 if (!set_shell_action(dialog))
553 return;
554 gtk_widget_destroy(dialog);
557 /* Return the path of the file in choices that handles this type and
558 * radio setting.
559 * NULL if nothing is defined for it.
561 static guchar *handler_for_radios(GObject *dialog)
563 Radios *radios;
564 MIME_type *type;
566 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
567 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
569 g_return_val_if_fail(radios != NULL, NULL);
570 g_return_val_if_fail(type != NULL, NULL);
572 switch (radios_get_value(radios))
574 case SET_MEDIA:
575 return choices_find_path_load(type->media_type,
576 "MIME-types");
577 case SET_TYPE:
579 gchar *tmp, *handler;
580 tmp = g_strconcat(type->media_type, "_",
581 type->subtype, NULL);
582 handler = choices_find_path_load(tmp, "MIME-types");
583 g_free(tmp);
584 return handler;
586 default:
587 g_warning("Bad type");
588 return NULL;
592 static void run_action_update(gpointer data)
594 guchar *handler;
595 DropBox *drop_box;
596 GObject *dialog = G_OBJECT(data);
598 drop_box = g_object_get_data(dialog, "rox-dropbox");
600 g_return_if_fail(drop_box != NULL);
602 handler = handler_for_radios(dialog);
604 if (handler)
606 char *old = handler;
608 handler = readlink_dup(old);
609 if (handler)
610 g_free(old);
611 else
612 handler = old;
615 drop_box_set_path(DROP_BOX(drop_box), handler);
616 g_free(handler);
619 static void clear_run_action(GtkWidget *drop_box, GtkWidget *dialog)
621 guchar *handler;
623 handler = handler_for_radios(G_OBJECT(dialog));
625 if (handler)
626 remove_handler_with_confirm(handler);
628 run_action_update(dialog);
631 /* Called when a URI list is dropped onto the box in the Set Run Action
632 * dialog. Make sure it's an application, and make that the default
633 * handler.
635 static void drag_app_dropped(GtkWidget *drop_box,
636 const guchar *app,
637 GtkWidget *dialog)
639 DirItem *item;
641 item = diritem_new("");
642 diritem_restat(app, item, NULL);
643 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
645 guchar *path;
647 path = get_action_save_path(dialog);
649 if (path)
651 if (symlink(app, path))
652 delayed_error("symlink: %s",
653 g_strerror(errno));
654 else
655 destroy_on_idle(dialog);
657 g_free(path);
660 else
661 delayed_error(
662 _("This is not a program! Give me an application "
663 "instead!"));
665 diritem_free(item);
668 /* Find the current command which is used to run files of this type.
669 * Returns NULL on failure. g_free() the result.
671 static guchar *get_current_command(MIME_type *type)
673 struct stat info;
674 char *handler, *nl, *data = NULL;
675 long len;
676 guchar *command = NULL;
678 handler = handler_for(type);
680 if (!handler)
681 return NULL; /* No current handler */
683 if (stat(handler, &info))
684 goto out; /* Can't stat */
686 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
687 goto out; /* Only use small regular files */
689 if (!load_file(handler, &data, &len))
690 goto out; /* Didn't load OK */
692 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
693 goto out; /* Not one of ours */
695 nl = strchr(data + 16, '\n');
696 if (!nl)
697 goto out; /* No newline! */
699 command = g_strndup(data + 16, nl - data - 16);
700 out:
701 g_free(handler);
702 g_free(data);
703 return command;
706 /* Find the current command which is used to run files of this type,
707 * and return a textual description of it.
708 * g_free() the result.
710 gchar *describe_current_command(MIME_type *type)
712 char *handler;
713 char *desc = NULL;
714 struct stat info;
715 char *target;
717 g_return_val_if_fail(type != NULL, NULL);
719 if (type == application_executable)
720 return g_strdup(_("Execute file"));
722 handler = handler_for(type);
724 if (!handler)
725 return g_strdup(_("No run action defined"));
727 target = readlink_dup(handler);
728 if (target)
730 /* Cope with relative paths (shouldn't normally be needed) */
732 if (target[0] == '/')
734 g_free(handler);
735 handler = target;
737 else
739 gchar *dir;
741 dir = g_path_get_dirname(handler);
742 g_free(handler);
743 handler = g_strconcat(dir, "/", target, NULL);
744 g_free(target);
745 g_free(dir);
749 if (mc_stat(handler, &info) !=0 )
751 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
752 g_strerror(errno));
753 goto out;
756 if (S_ISDIR(info.st_mode))
758 const guchar *tmp;
759 uid_t dir_uid = info.st_uid;
761 tmp = make_path(handler, "AppRun");
763 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
764 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
765 desc = g_strdup_printf(
766 _("Invalid application %s (bad AppRun)"),
767 handler);
768 /* Else, just report handler... */
770 goto out;
773 /* It's not an application directory, and it's not a symlink... */
775 if (access(handler, X_OK) != 0)
777 desc = g_strdup_printf(_("Non-executable %s"), handler);
778 goto out;
781 desc = get_current_command(type);
782 out:
783 if (!desc)
784 desc = handler;
785 else
786 g_free(handler);
788 return desc;
791 /* Display a dialog box allowing the user to set the default run action
792 * for this type.
794 void type_set_handler_dialog(MIME_type *type)
796 guchar *tmp;
797 GtkDialog *dialog;
798 GtkWidget *frame, *entry, *label;
799 GtkWidget *hbox;
800 Radios *radios;
802 g_return_if_fail(type != NULL);
804 dialog = GTK_DIALOG(gtk_dialog_new());
805 gtk_dialog_set_has_separator(dialog, FALSE);
806 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
808 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
810 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
812 radios = radios_new(run_action_update, dialog);
813 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
815 radios_add(radios,
816 _("If a handler for the specific type isn't set up, "
817 "use this as the default."), SET_MEDIA,
818 _("Set default for all `%s/<anything>'"),
819 type->media_type);
821 radios_add(radios,
822 _("Use this application for all files with this MIME "
823 "type."), SET_TYPE,
824 _("Only for the type `%s' (%s/%s)"),
825 mime_type_comment(type),
826 type->media_type, type->subtype);
828 radios_set_value(radios, SET_TYPE);
830 frame = drop_box_new(_("Drop a suitable application here"));
832 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
834 radios_pack(radios, GTK_BOX(dialog->vbox));
835 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
837 g_signal_connect(frame, "path_dropped",
838 G_CALLBACK(drag_app_dropped), dialog);
839 g_signal_connect(frame, "clear",
840 G_CALLBACK(clear_run_action), dialog);
842 hbox = gtk_hbox_new(FALSE, 4);
843 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
844 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
845 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
846 FALSE, TRUE, 0);
847 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
849 hbox = gtk_hbox_new(FALSE, 4);
850 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
852 label = gtk_label_new(_("Enter a shell command:")),
853 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
854 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
856 gtk_box_pack_start(GTK_BOX(hbox),
857 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
859 entry = gtk_entry_new();
860 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
861 gtk_widget_grab_focus(entry);
862 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
863 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
865 /* If possible, fill in the entry box with the current command */
866 tmp = get_current_command(type);
867 if (tmp)
869 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
870 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
871 g_free(tmp);
873 else
875 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
876 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
879 gtk_dialog_add_buttons(dialog,
880 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
881 GTK_STOCK_OK, GTK_RESPONSE_OK,
882 NULL);
884 hbox = gtk_hbox_new(TRUE, 4);
885 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
887 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
889 g_signal_connect(dialog, "response",
890 G_CALLBACK(set_action_response), NULL);
892 gtk_widget_show_all(GTK_WIDGET(dialog));
895 /* path is an entry in Choices. If it's a symlink or a very small executable
896 * then just get rid of it, otherwise confirm first. It it doesn't exist,
897 * do nothing.
899 * FALSE on error (abort operation).
901 static gboolean remove_handler_with_confirm(const guchar *path)
903 struct stat info;
905 if (lstat(path, &info) == 0)
907 /* A binding already exists... */
908 if (S_ISREG(info.st_mode) && info.st_size > 256)
910 if (!confirm(_("A run action already exists and is "
911 "quite a big program - are you sure "
912 "you want to delete it?"),
913 GTK_STOCK_DELETE, NULL))
915 return FALSE;
919 if (unlink(path))
921 report_error(_("Can't remove %s: %s"),
922 path, g_strerror(errno));
923 return FALSE;
927 return TRUE;
930 /* The user wants to set a new default action for files of this type (or just
931 * clear the action). Removes the current binding if possible and returns the
932 * path to save the new one to. NULL means cancel. g_free() the result.
934 static char *get_action_save_path(GtkWidget *dialog)
936 guchar *path = NULL;
937 guchar *type_name = NULL;
938 MIME_type *type;
939 Radios *radios;
941 g_return_val_if_fail(dialog != NULL, NULL);
943 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
944 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
946 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
948 if (radios_get_value(radios) == SET_MEDIA)
949 type_name = g_strdup(type->media_type);
950 else
951 type_name = g_strconcat(type->media_type, "_",
952 type->subtype, NULL);
954 path = choices_find_path_save("", PROJECT, FALSE);
955 if (!path)
957 report_error(
958 _("Choices saving is disabled by CHOICESPATH variable"));
959 goto out;
961 g_free(path);
963 path = choices_find_path_save(type_name, "MIME-types", TRUE);
965 if (!remove_handler_with_confirm(path))
966 null_g_free(&path);
967 out:
968 g_free(type_name);
969 return path;
972 MIME_type *mime_type_from_base_type(int base_type)
974 switch (base_type)
976 case TYPE_FILE:
977 return text_plain;
978 case TYPE_DIRECTORY:
979 return inode_directory;
980 case TYPE_PIPE:
981 return inode_pipe;
982 case TYPE_SOCKET:
983 return inode_socket;
984 case TYPE_BLOCK_DEVICE:
985 return inode_block_dev;
986 case TYPE_CHAR_DEVICE:
987 return inode_char_dev;
988 case TYPE_DOOR:
989 return inode_door;
991 return inode_unknown;
994 /* Takes the st_mode field from stat() and returns the base type.
995 * Should not be a symlink.
997 int mode_to_base_type(int st_mode)
999 if (S_ISREG(st_mode))
1000 return TYPE_FILE;
1001 else if (S_ISDIR(st_mode))
1002 return TYPE_DIRECTORY;
1003 else if (S_ISBLK(st_mode))
1004 return TYPE_BLOCK_DEVICE;
1005 else if (S_ISCHR(st_mode))
1006 return TYPE_CHAR_DEVICE;
1007 else if (S_ISFIFO(st_mode))
1008 return TYPE_PIPE;
1009 else if (S_ISSOCK(st_mode))
1010 return TYPE_SOCKET;
1011 else if (S_ISDOOR(st_mode))
1012 return TYPE_DOOR;
1014 return TYPE_ERROR;
1017 /* Returns TRUE is this is something that is run by looking up its type
1018 * in MIME-types and, hence, can have its run action set.
1020 gboolean can_set_run_action(DirItem *item)
1022 g_return_val_if_fail(item != NULL, FALSE);
1024 return item->base_type == TYPE_FILE &&
1025 !(item->mime_type == application_executable);
1028 /* Parse file type colours and allocate/free them as necessary */
1029 static void alloc_type_colours(void)
1031 gboolean success[NUM_TYPE_COLOURS];
1032 int change_count = 0; /* No. needing realloc */
1033 int i;
1034 static gboolean allocated = FALSE;
1036 /* Parse colours */
1037 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1039 GdkColor *c = &type_colours[i];
1040 gushort r = c->red;
1041 gushort g = c->green;
1042 gushort b = c->blue;
1044 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1046 if (allocated && (c->red != r || c->green != g || c->blue != b))
1047 change_count++;
1050 /* Free colours if they were previously allocated and
1051 * have changed or become unneeded.
1053 if (allocated && (change_count || !o_display_colour_types.int_value))
1055 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1056 type_colours, NUM_TYPE_COLOURS);
1057 allocated = FALSE;
1060 /* Allocate colours, unless they are still allocated (=> they didn't
1061 * change) or we don't want them anymore.
1062 * XXX: what should be done if allocation fails?
1064 if (!allocated && o_display_colour_types.int_value)
1066 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1067 type_colours, NUM_TYPE_COLOURS,
1068 FALSE, TRUE, success);
1069 allocated = TRUE;
1073 /* Return a pointer to a (static) colour for this item. If colouring is
1074 * off, returns normal.
1076 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1078 int type = item->base_type;
1080 if (!o_display_colour_types.int_value)
1081 return normal;
1083 if (item->flags & ITEM_FLAG_EXEC_FILE)
1084 type = TYPE_EXEC;
1085 else if (item->flags & ITEM_FLAG_APPDIR)
1086 type = TYPE_APPDIR;
1088 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1090 return &type_colours[type];
1093 /* Process the 'Patterns' value */
1094 static void add_pattern(MIME_type *type, const char *pattern, GHashTable *globs)
1096 if (pattern[0] == '*' && pattern[1] == '.' &&
1097 strpbrk(pattern + 2, "*?[") == NULL)
1099 g_hash_table_insert(extension_hash,
1100 g_strdup(pattern + 2),
1101 type);
1103 else if (strpbrk(pattern, "*?[") == NULL)
1104 g_hash_table_insert(literal_hash, g_strdup(pattern), type);
1105 else
1106 g_hash_table_insert(globs, g_strdup(pattern), type);
1109 /* Load and parse this file. literal_hash and extension_hash are updated
1110 * directly. Other patterns are added to 'globs'.
1112 static void import_file(const gchar *file, GHashTable *globs)
1114 MIME_type *type = NULL;
1115 GError *error = NULL;
1116 gchar *data, *line;
1118 if (access(file, F_OK) != 0)
1119 return; /* Doesn't exist. No problem. */
1121 if (!g_file_get_contents(file, &data, NULL, &error))
1123 delayed_error(_("Error loading MIME database:\n%s"),
1124 error->message);
1125 g_error_free(error);
1126 return;
1129 line = data;
1131 while (line && *line)
1133 char *nl;
1135 nl = strchr(line, '\n');
1136 if (!nl)
1137 break;
1138 *nl = '\0';
1140 if (*line != '#')
1142 const gchar *colon;
1143 gchar *name;
1145 colon = strchr(line, ':');
1146 if (!colon)
1148 delayed_error(_("File '%s' corrupted!"), file);
1149 break;
1152 name = g_strndup(line, colon - line);
1153 type = get_mime_type(name, TRUE);
1154 g_free(name);
1155 if (!type)
1156 g_warning("Invalid type in '%s'", file);
1158 add_pattern(type, colon + 1, globs);
1161 line = nl + 1;
1164 g_free(data);
1167 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1169 Pattern *pattern;
1171 pattern = g_new(Pattern, 1);
1172 pattern->glob = g_strdup((gchar *) key);
1173 pattern->type = (MIME_type *) value;
1174 pattern->len = strlen(pattern->glob);
1176 g_ptr_array_add(glob_patterns, pattern);
1179 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1181 const Pattern *pa = *(const Pattern **) a;
1182 const Pattern *pb = *(const Pattern **) b;
1184 if (pa->len > pb->len)
1185 return -1;
1186 else if (pa->len == pb->len)
1187 return 0;
1188 return 1;
1191 static char **get_xdg_data_dirs(int *n_dirs)
1193 const char *env;
1194 char **dirs;
1195 int i, n;
1197 env = getenv("XDG_DATA_DIRS");
1198 if (!env)
1199 env = "/usr/local/share/:/usr/share/";
1200 dirs = g_strsplit(env, ":", 0);
1201 g_return_val_if_fail(dirs != NULL, NULL);
1202 for (n = 0; dirs[n]; n++)
1204 for (i = n; i > 0; i--)
1205 dirs[i] = dirs[i - 1];
1206 env = getenv("XDG_DATA_HOME");
1207 if (env)
1208 dirs[0] = g_strdup(env);
1209 else
1210 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1211 "share", NULL);
1212 *n_dirs = n + 1;
1213 return dirs;
1216 /* Clear all currently stored information and re-read everything.
1217 * Note: calls filer_update_all.
1219 static void load_mime_types(void)
1221 GHashTable *globs;
1222 char **dirs;
1223 int n_dirs;
1224 int i;
1226 dirs = get_xdg_data_dirs(&n_dirs);
1227 g_return_if_fail(dirs != NULL);
1230 struct stat info;
1231 if (lstat(make_path(home_dir, ".mime"), &info) == 0 &&
1232 S_ISDIR(info.st_mode))
1234 delayed_error(_("The ~/.mime directory has moved. "
1235 "It should now be ~/.local/share/mime. You "
1236 "should move it there (and make a symlink "
1237 "from ~/.mime to it for older applications)."));
1241 if (!glob_patterns)
1242 glob_patterns = g_ptr_array_new();
1243 else
1245 int i;
1247 for (i = glob_patterns->len - 1; i >= 0; i--)
1249 Pattern *p = glob_patterns->pdata[i];
1250 g_free(p->glob);
1251 g_free(p);
1253 g_ptr_array_set_size(glob_patterns, 0);
1256 if (literal_hash)
1257 g_hash_table_destroy(literal_hash);
1258 if (extension_hash)
1259 g_hash_table_destroy(extension_hash);
1260 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1261 g_free, NULL);
1262 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1263 g_free, NULL);
1264 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1266 for (i = n_dirs - 1; i >= 0; i--)
1268 char *path;
1269 path = g_build_filename(dirs[i], "mime", "globs", NULL);
1270 import_file(path, globs);
1271 g_free(path);
1272 g_free(dirs[i]);
1274 g_free(dirs);
1276 /* Turn the globs hash into a pointer array */
1277 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1278 g_hash_table_destroy(globs);
1279 globs = NULL;
1281 if (glob_patterns->len)
1282 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1284 if (g_hash_table_size(extension_hash) == 0)
1286 delayed_error(_("The standard MIME type database "
1287 "(version 0.9 or later) was not found. "
1288 "The filer will probably not show the correct "
1289 "types for different files. You should download and "
1290 "install the 'shared-mime-info-0.9' package from "
1291 "here:\n"
1292 "http://www.freedesktop.org/standards/shared-mime-info.html\n\n"
1293 "If you have already installed this package, check that the "
1294 "permissions allow the files to be read (check "
1295 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1298 filer_update_all();
1301 /* Try to fill in 'type->comment' from this document.
1302 * Frees 'path'.
1304 static void get_comment(MIME_type *type, guchar *path)
1306 xmlNode *node;
1307 XMLwrapper *doc;
1309 doc = xml_cache_load(path);
1310 g_free(path);
1311 if (!doc)
1312 return;
1314 node = xml_get_section(doc, TYPE_NS, "comment");
1316 if (node)
1318 char *val;
1319 g_return_if_fail(type->comment == NULL);
1320 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1321 type->comment = g_strdup(val);
1322 xmlFree(val);
1325 g_object_unref(doc);
1328 /* Fill in the comment field for this MIME type.
1330 * g_object_unref() the result.
1332 static void find_comment(MIME_type *type)
1334 guchar *path;
1336 if (type->comment)
1338 g_free(type->comment);
1339 type->comment = NULL;
1342 path = g_strdup_printf("%s/.mime/%s/%s.xml", home_dir,
1343 type->media_type, type->subtype);
1344 get_comment(type, path);
1345 if (type->comment)
1346 return;
1348 path = g_strdup_printf("/usr/local/share/mime/%s/%s.xml",
1349 type->media_type, type->subtype);
1350 get_comment(type, path);
1351 if (type->comment)
1352 return;
1354 path = g_strdup_printf("/usr/share/mime/%s/%s.xml",
1355 type->media_type, type->subtype);
1356 get_comment(type, path);
1357 if (type->comment)
1358 return;
1360 type->comment = g_strdup(_("No description"));
1363 const char *mime_type_comment(MIME_type *type)
1365 if (!type->comment)
1366 find_comment(type);
1368 return type->comment;