r345: Fixed a bug that prevented i18n from working.
[rox-filer/dt.git] / ROX-Filer / src / type.c
blob5d4a5a33c55246643c18cd533ddeb0aaf0636763
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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 <errno.h>
28 #include <ctype.h>
29 #include <time.h>
30 #include <sys/param.h>
32 #include "global.h"
34 #include "string.h"
35 #include "main.h"
36 #include "pixmaps.h"
37 #include "run.h"
38 #include "gui_support.h"
39 #include "choices.h"
40 #include "type.h"
41 #include "support.h"
42 #include "dir.h"
44 /* Static prototypes */
45 static char *import_extensions(guchar *line);
46 static void import_for_dir(guchar *path);
48 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *) */
49 static GHashTable *extension_hash = NULL;
50 static char *current_type = NULL; /* (used while reading file) */
52 /* Most things on Unix are text files, so this is the default type */
53 MIME_type text_plain = {"text", "plain", NULL};
55 MIME_type special_directory = {"special", "directory", NULL};
56 MIME_type special_pipe = {"special", "pipe", NULL};
57 MIME_type special_socket = {"special", "socket", NULL};
58 MIME_type special_block_dev = {"special", "block-device", NULL};
59 MIME_type special_char_dev = {"special", "char-device", NULL};
60 MIME_type special_unknown = {"special", "unknown", NULL};
62 void type_init()
64 int i;
65 GPtrArray *list;
67 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
69 current_type = NULL;
71 list = choices_list_dirs("MIME-info");
72 for (i = 0; i < list->len; i++)
73 import_for_dir((gchar *) g_ptr_array_index(list, i));
74 choices_free_list(list);
77 /* Parse every file in 'dir' */
78 static void import_for_dir(guchar *path)
80 DIR *dir;
81 struct dirent *item;
83 dir = opendir(path);
84 if (!dir)
85 return;
87 while ((item = readdir(dir)))
89 if (item->d_name[0] == '.')
90 continue;
92 current_type = NULL;
93 parse_file(make_path(path, item->d_name)->str,
94 import_extensions);
97 closedir(dir);
100 /* Add one entry to the extension_hash table */
101 static void add_ext(char *type_name, char *ext)
103 MIME_type *new;
104 char *slash;
105 int len;
107 slash = strchr(type_name, '/');
108 g_return_if_fail(slash != NULL); /* XXX: Report nicely */
109 len = slash - type_name;
111 new = g_new(MIME_type, 1);
112 new->media_type = g_malloc(sizeof(char) * (len + 1));
113 memcpy(new->media_type, type_name, len);
114 new->media_type[len] = '\0';
116 new->subtype = g_strdup(slash + 1);
117 new->image = NULL;
119 g_hash_table_insert(extension_hash, g_strdup(ext), new);
122 /* Parse one line from the file and add entries to extension_hash */
123 static char *import_extensions(guchar *line)
126 if (*line == '\0' || *line == '#')
127 return NULL; /* Comment */
129 if (isspace(*line))
131 if (!current_type)
132 return _("Missing MIME-type");
133 while (*line && isspace(*line))
134 line++;
136 if (strncmp(line, "ext:", 4) == 0)
138 char *ext;
139 line += 4;
141 for (;;)
143 while (*line && isspace(*line))
144 line++;
145 if (*line == '\0')
146 break;
147 ext = line;
148 while (*line && !isspace(*line))
149 line++;
150 if (*line)
151 *line++ = '\0';
152 add_ext(current_type, ext);
155 /* else ignore */
157 else
159 char *type = line;
160 while (*line && *line != ':' && !isspace(*line))
161 line++;
162 if (*line)
163 *line++ = '\0';
164 while (*line && isspace(*line))
165 line++;
166 if (*line)
167 return _("Trailing chars after MIME-type");
168 current_type = g_strdup(type);
170 return NULL;
173 char *basetype_name(DirItem *item)
175 if (item->flags & ITEM_FLAG_SYMLINK)
176 return _("Sym link");
177 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
178 return _("Mount point");
179 else if (item->flags & ITEM_FLAG_APPDIR)
180 return _("App dir");
182 switch (item->base_type)
184 case TYPE_FILE:
185 return _("File");
186 case TYPE_DIRECTORY:
187 return _("Dir");
188 case TYPE_CHAR_DEVICE:
189 return _("Char dev");
190 case TYPE_BLOCK_DEVICE:
191 return _("Block dev");
192 case TYPE_PIPE:
193 return _("Pipe");
194 case TYPE_SOCKET:
195 return _("Socket");
198 return _("Unknown");
201 /* MIME-type guessing */
203 /* Returns a pointer to the MIME-type. Defaults to text/plain if we have
204 * no opinion.
206 MIME_type *type_from_path(char *path)
208 char *dot;
210 dot = strrchr(path, '.');
211 if (dot)
213 MIME_type *type;
214 type = g_hash_table_lookup(extension_hash, dot + 1);
215 if (type)
216 return type;
219 return &text_plain;
222 /* Actions for types */
224 gboolean type_open(char *path, MIME_type *type)
226 char *argv[] = {NULL, NULL, NULL};
227 char *open;
228 char *type_name;
229 gboolean retval = TRUE;
230 struct stat info;
232 argv[1] = path;
234 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
235 open = choices_find_path_load(type_name, "MIME-types");
236 g_free(type_name);
237 if (!open)
239 open = choices_find_path_load(type->media_type,
240 "MIME-types");
241 if (!open)
242 return FALSE;
245 if (stat(open, &info))
247 report_error(PROJECT, g_strerror(errno));
248 return FALSE;
251 if (S_ISDIR(info.st_mode))
252 argv[0] = g_strconcat(open, "/AppRun", NULL);
253 else
254 argv[0] = open;
256 if (!spawn_full(argv, home_dir))
258 report_error(PROJECT,
259 _("Failed to fork() child process"));
260 retval = FALSE;
263 if (argv[0] != open)
264 g_free(argv[0]);
266 return retval;
269 /* Return the image for this type, loading it if needed.
270 * Places to check are: (eg type="text_plain", base="text")
271 * 1. Choices:MIME-icons/<type>
272 * 2. Choices:MIME-icons/<base>
273 * 3. Unknown type icon.
275 * Note: You must pixmap_unref() the image afterwards.
277 MaskedPixmap *type_to_icon(MIME_type *type)
279 char *path;
280 char *type_name;
281 time_t now;
283 if (type == NULL)
285 pixmap_ref(im_unknown);
286 return im_unknown;
289 now = time(NULL);
290 /* Already got an image? */
291 if (type->image)
293 /* Yes - don't recheck too often */
294 if (abs(now - type->image_time) < 2)
296 pixmap_ref(type->image);
297 return type->image;
299 pixmap_unref(type->image);
300 type->image = NULL;
303 type_name = g_strconcat(type->media_type, "_",
304 type->subtype, ".xpm", NULL);
305 path = choices_find_path_load(type_name, "MIME-icons");
306 if (!path)
308 strcpy(type_name + strlen(type->media_type), ".xpm");
309 path = choices_find_path_load(type_name, "MIME-icons");
312 g_free(type_name);
314 if (path)
315 type->image = g_fscache_lookup(pixmap_cache, path);
317 if (!type->image)
319 type->image = im_unknown;
320 pixmap_ref(type->image);
323 type->image_time = now;
325 pixmap_ref(type->image);
326 return type->image;
329 GdkAtom type_to_atom(MIME_type *type)
331 char *str;
332 GdkAtom retval;
334 g_return_val_if_fail(type != NULL, GDK_NONE);
336 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
337 retval = gdk_atom_intern(str, FALSE);
338 g_free(str);
340 return retval;
343 /* The user wants to set a new default action for files of this type.
344 * Ask the user if they want to set eg 'text/plain' or just 'text'.
345 * Removes the current binding if possible and returns the path to
346 * save the new one to. NULL means cancel.
348 char *type_ask_which_action(guchar *media_type, guchar *subtype)
350 int r;
351 guchar *tmp, *type_name, *path;
352 struct stat info;
354 g_return_val_if_fail(media_type != NULL, NULL);
355 g_return_val_if_fail(subtype != NULL, NULL);
357 if (!choices_find_path_save("", PROJECT, FALSE))
359 report_error(PROJECT,
360 _("Choices saving is disabled by CHOICESPATH variable"));
361 return NULL;
364 type_name = g_strconcat(media_type, "/", subtype, NULL);
365 tmp = g_strdup_printf(
366 _("You can choose to set the action for just '%s' files, or "
367 "the default action for all '%s' files which don't already "
368 "have a run action:"), type_name, media_type);
369 r = get_choice(PROJECT, tmp, 3, type_name, media_type, "Cancel");
370 g_free(tmp);
371 g_free(type_name);
373 if (r == 0)
375 type_name = g_strconcat(media_type, "_", subtype, NULL);
376 path = choices_find_path_save(type_name, "MIME-types", TRUE);
377 g_free(type_name);
379 else if (r == 1)
380 path = choices_find_path_save(media_type, "MIME-types", TRUE);
381 else
382 return NULL;
384 if (lstat(path, &info) == 0)
386 /* A binding already exists... */
387 if (S_ISREG(info.st_mode) && info.st_size > 256)
389 if (get_choice(PROJECT,
390 _("A run action already exists and is quite "
391 "a big program - are you sure you want to "
392 "delete it?"), 2, "Delete", "Cancel") != 0)
393 return NULL;
396 if (unlink(path))
398 tmp = g_strdup_printf( _("Can't remove %s: %s"),
399 path, g_strerror(errno));
400 report_error(PROJECT, tmp);
401 g_free(tmp);
402 return NULL;
406 return path;
409 MIME_type *mime_type_from_base_type(int base_type)
411 switch (base_type)
413 case TYPE_DIRECTORY:
414 return &special_directory;
415 case TYPE_PIPE:
416 return &special_pipe;
417 case TYPE_SOCKET:
418 return &special_socket;
419 case TYPE_BLOCK_DEVICE:
420 return &special_block_dev;
421 case TYPE_CHAR_DEVICE:
422 return &special_char_dev;
424 return &special_unknown;
427 /* Takes the st_mode field from stat() and returns the base type.
428 * Should not be a symlink.
430 int mode_to_base_type(int st_mode)
432 if (S_ISREG(st_mode))
433 return TYPE_FILE;
434 else if (S_ISDIR(st_mode))
435 return TYPE_DIRECTORY;
436 else if (S_ISBLK(st_mode))
437 return TYPE_BLOCK_DEVICE;
438 else if (S_ISCHR(st_mode))
439 return TYPE_CHAR_DEVICE;
440 else if (S_ISFIFO(st_mode))
441 return TYPE_PIPE;
442 else if (S_ISSOCK(st_mode))
443 return TYPE_SOCKET;
445 return TYPE_UNKNOWN;