r279: Can now set run actions by dragging.
[rox-filer/ma.git] / ROX-Filer / src / type.c
blob014c6ad10104afe05a93407f46f579cf9a1db791
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
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 <sys/stat.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <sys/param.h>
34 #include <glib.h>
36 #include "string.h"
37 #include "main.h"
38 #include "filer.h"
39 #include "pixmaps.h"
40 #include "run.h"
41 #include "gui_support.h"
42 #include "choices.h"
43 #include "type.h"
44 #include "support.h"
45 #include "options.h"
47 /* Static prototypes */
48 static char *import_extensions(guchar *line);
49 static void import_for_dir(guchar *path);
51 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *) */
52 static GHashTable *extension_hash = NULL;
53 static char *current_type = NULL; /* (used while reading file) */
55 /* Most things on Unix are text files, so this is the default type */
56 MIME_type text_plain = {"text", "plain", NULL};
58 void type_init()
60 int i;
61 GPtrArray *list;
63 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
65 current_type = NULL;
66 parse_file(make_path(getenv("APP_DIR"), "MIME-info")->str,
67 import_extensions);
69 list = choices_list_dirs("MIME-info");
70 for (i = 0; i < list->len; i++)
71 import_for_dir((gchar *) g_ptr_array_index(list, i));
72 choices_free_list(list);
75 /* Parse every file in 'dir' */
76 static void import_for_dir(guchar *path)
78 DIR *dir;
79 struct dirent *item;
81 dir = opendir(path);
82 if (!dir)
83 return;
85 while ((item = readdir(dir)))
87 if (item->d_name[0] == '.')
88 continue;
90 current_type = NULL;
91 parse_file(make_path(path, item->d_name)->str,
92 import_extensions);
95 closedir(dir);
98 /* Add one entry to the extension_hash table */
99 static void add_ext(char *type_name, char *ext)
101 MIME_type *new;
102 char *slash;
103 int len;
105 slash = strchr(type_name, '/');
106 g_return_if_fail(slash != NULL); /* XXX: Report nicely */
107 len = slash - type_name;
109 new = g_new(MIME_type, 1);
110 new->media_type = g_malloc(sizeof(char) * (len + 1));
111 memcpy(new->media_type, type_name, len);
112 new->media_type[len] = '\0';
114 new->subtype = g_strdup(slash + 1);
115 new->image = NULL;
117 g_hash_table_insert(extension_hash, g_strdup(ext), new);
120 /* Parse one line from the file and add entries to extension_hash */
121 static char *import_extensions(guchar *line)
124 if (*line == '\0' || *line == '#')
125 return NULL; /* Comment */
127 if (isspace(*line))
129 if (!current_type)
130 return _("Missing MIME-type");
131 while (*line && isspace(*line))
132 line++;
134 if (strncmp(line, "ext:", 4) == 0)
136 char *ext;
137 line += 4;
139 for (;;)
141 while (*line && isspace(*line))
142 line++;
143 if (*line == '\0')
144 break;
145 ext = line;
146 while (*line && !isspace(*line))
147 line++;
148 if (*line)
149 *line++ = '\0';
150 add_ext(current_type, ext);
153 /* else ignore */
155 else
157 char *type = line;
158 while (*line && *line != ':' && !isspace(*line))
159 line++;
160 if (*line)
161 *line++ = '\0';
162 while (*line && isspace(*line))
163 line++;
164 if (*line)
165 return _("Trailing chars after MIME-type");
166 current_type = g_strdup(type);
168 return NULL;
171 char *basetype_name(DirItem *item)
173 if (item->flags & ITEM_FLAG_SYMLINK)
174 return _("Sym link");
175 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
176 return _("Mount point");
177 else if (item->flags & ITEM_FLAG_APPDIR)
178 return _("App dir");
180 switch (item->base_type)
182 case TYPE_FILE:
183 return _("File");
184 case TYPE_DIRECTORY:
185 return _("Dir");
186 case TYPE_CHAR_DEVICE:
187 return _("Char dev");
188 case TYPE_BLOCK_DEVICE:
189 return _("Block dev");
190 case TYPE_PIPE:
191 return _("Pipe");
192 case TYPE_SOCKET:
193 return _("Socket");
196 return _("Unknown");
199 /* MIME-type guessing */
201 /* Returns a pointer to the MIME-type. Defaults to text/plain if we have
202 * no opinion.
204 MIME_type *type_from_path(char *path)
206 char *dot;
208 dot = strrchr(path, '.');
209 if (dot)
211 MIME_type *type;
212 type = g_hash_table_lookup(extension_hash, dot + 1);
213 if (type)
214 return type;
217 return &text_plain;
220 /* Actions for types */
222 gboolean type_open(char *path, MIME_type *type)
224 char *argv[] = {NULL, NULL, NULL};
225 char *open;
226 char *type_name;
227 gboolean retval = TRUE;
228 struct stat info;
230 argv[1] = path;
232 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
233 open = choices_find_path_load(type_name, "MIME-types");
234 g_free(type_name);
235 if (!open)
237 open = choices_find_path_load(type->media_type,
238 "MIME-types");
239 if (!open)
240 return FALSE;
243 if (stat(open, &info))
245 report_error(PROJECT, g_strerror(errno));
246 return FALSE;
249 if (S_ISDIR(info.st_mode))
250 argv[0] = g_strconcat(open, "/AppRun", NULL);
251 else
252 argv[0] = open;
254 if (!spawn_full(argv, home_dir))
256 report_error(PROJECT,
257 _("Failed to fork() child process"));
258 retval = FALSE;
261 if (argv[0] != open)
262 g_free(argv[0]);
264 return retval;
267 /* Return the image for this type, loading it if needed.
268 * Places to check are: (eg type="text_plain", base="text")
269 * 1. Choices:MIME-icons/<type>
270 * 2. Choices:MIME-icons/<base>
271 * 3. Unknown type icon.
273 * Note: You must pixmap_unref() the image afterwards.
275 MaskedPixmap *type_to_icon(MIME_type *type)
277 char *path;
278 char *type_name;
279 time_t now;
281 g_return_val_if_fail(type != NULL, default_pixmap[TYPE_UNKNOWN]);
283 now = time(NULL);
284 /* Already got an image? */
285 if (type->image)
287 /* Yes - don't recheck too often */
288 if (abs(now - type->image_time) < 2)
290 pixmap_ref(type->image);
291 return type->image;
293 pixmap_unref(type->image);
294 type->image = NULL;
297 type_name = g_strconcat(type->media_type, "_",
298 type->subtype, ".xpm", NULL);
299 path = choices_find_path_load(type_name, "MIME-icons");
300 if (!path)
302 strcpy(type_name + strlen(type->media_type), ".xpm");
303 path = choices_find_path_load(type_name, "MIME-icons");
306 g_free(type_name);
308 if (path)
309 type->image = g_fscache_lookup(pixmap_cache, path);
311 if (!type->image)
313 type->image = default_pixmap[TYPE_UNKNOWN];
314 pixmap_ref(type->image);
317 type->image_time = now;
319 pixmap_ref(type->image);
320 return type->image;
323 GdkAtom type_to_atom(MIME_type *type)
325 char *str;
326 GdkAtom retval;
328 g_return_val_if_fail(type != NULL, GDK_NONE);
330 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
331 retval = gdk_atom_intern(str, FALSE);
332 g_free(str);
334 return retval;
337 /* The user wants to set a new default action for files of this type.
338 * Ask the user if they want to set eg 'text/plain' or just 'text'.
339 * Removes the current binding if possible and returns the path to
340 * save the new one to. NULL means cancel.
342 char *type_ask_which_action(guchar *media_type, guchar *subtype)
344 int r;
345 guchar *tmp, *type_name, *path;
347 g_return_val_if_fail(media_type != NULL, NULL);
348 g_return_val_if_fail(subtype != NULL, NULL);
350 if (!choices_find_path_save("", PROJECT, FALSE))
352 report_error(PROJECT,
353 _("Choices saving is disabled by CHOICESPATH variable"));
354 return NULL;
357 type_name = g_strconcat(media_type, "/", subtype, NULL);
358 tmp = g_strdup_printf(
359 _("You can choose to set the action for just '%s' files, or "
360 "the default action for all '%s' files which don't already "
361 "have a run action:"), type_name, media_type);
362 r = get_choice(PROJECT, tmp, 3, type_name, media_type, "Cancel");
363 g_free(tmp);
364 g_free(type_name);
366 if (r == 0)
368 type_name = g_strconcat(media_type, "_", subtype, NULL);
369 path = choices_find_path_save(type_name, "MIME-types", TRUE);
370 g_free(type_name);
372 else if (r == 1)
373 path = choices_find_path_save(media_type, "MIME-types", TRUE);
374 else
375 return NULL;
377 if (access(path, F_OK) == 0)
379 if (unlink(path))
381 tmp = g_strdup_printf( _("Can't remove %s: %s"),
382 path, g_strerror(errno));
383 report_error(PROJECT, tmp);
384 g_free(tmp);
385 return NULL;
389 return path;