r293: Updated the docs, plus a few minor changes.
[rox-filer/ma.git] / ROX-Filer / src / type.c
blob8009fc3df39d6ec971d105afe65da29fc2c30cd8
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 MIME_type special_directory = {"special", "directory", NULL};
59 MIME_type special_pipe = {"special", "pipe", NULL};
60 MIME_type special_socket = {"special", "socket", NULL};
61 MIME_type special_block_dev = {"special", "block-device", NULL};
62 MIME_type special_char_dev = {"special", "char-device", NULL};
63 MIME_type special_unknown = {"special", "unknown", NULL};
65 void type_init()
67 int i;
68 GPtrArray *list;
70 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
72 current_type = NULL;
74 list = choices_list_dirs("MIME-info");
75 for (i = 0; i < list->len; i++)
76 import_for_dir((gchar *) g_ptr_array_index(list, i));
77 choices_free_list(list);
80 /* Parse every file in 'dir' */
81 static void import_for_dir(guchar *path)
83 DIR *dir;
84 struct dirent *item;
86 dir = opendir(path);
87 if (!dir)
88 return;
90 while ((item = readdir(dir)))
92 if (item->d_name[0] == '.')
93 continue;
95 current_type = NULL;
96 parse_file(make_path(path, item->d_name)->str,
97 import_extensions);
100 closedir(dir);
103 /* Add one entry to the extension_hash table */
104 static void add_ext(char *type_name, char *ext)
106 MIME_type *new;
107 char *slash;
108 int len;
110 slash = strchr(type_name, '/');
111 g_return_if_fail(slash != NULL); /* XXX: Report nicely */
112 len = slash - type_name;
114 new = g_new(MIME_type, 1);
115 new->media_type = g_malloc(sizeof(char) * (len + 1));
116 memcpy(new->media_type, type_name, len);
117 new->media_type[len] = '\0';
119 new->subtype = g_strdup(slash + 1);
120 new->image = NULL;
122 g_hash_table_insert(extension_hash, g_strdup(ext), new);
125 /* Parse one line from the file and add entries to extension_hash */
126 static char *import_extensions(guchar *line)
129 if (*line == '\0' || *line == '#')
130 return NULL; /* Comment */
132 if (isspace(*line))
134 if (!current_type)
135 return _("Missing MIME-type");
136 while (*line && isspace(*line))
137 line++;
139 if (strncmp(line, "ext:", 4) == 0)
141 char *ext;
142 line += 4;
144 for (;;)
146 while (*line && isspace(*line))
147 line++;
148 if (*line == '\0')
149 break;
150 ext = line;
151 while (*line && !isspace(*line))
152 line++;
153 if (*line)
154 *line++ = '\0';
155 add_ext(current_type, ext);
158 /* else ignore */
160 else
162 char *type = line;
163 while (*line && *line != ':' && !isspace(*line))
164 line++;
165 if (*line)
166 *line++ = '\0';
167 while (*line && isspace(*line))
168 line++;
169 if (*line)
170 return _("Trailing chars after MIME-type");
171 current_type = g_strdup(type);
173 return NULL;
176 char *basetype_name(DirItem *item)
178 if (item->flags & ITEM_FLAG_SYMLINK)
179 return _("Sym link");
180 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
181 return _("Mount point");
182 else if (item->flags & ITEM_FLAG_APPDIR)
183 return _("App dir");
185 switch (item->base_type)
187 case TYPE_FILE:
188 return _("File");
189 case TYPE_DIRECTORY:
190 return _("Dir");
191 case TYPE_CHAR_DEVICE:
192 return _("Char dev");
193 case TYPE_BLOCK_DEVICE:
194 return _("Block dev");
195 case TYPE_PIPE:
196 return _("Pipe");
197 case TYPE_SOCKET:
198 return _("Socket");
201 return _("Unknown");
204 /* MIME-type guessing */
206 /* Returns a pointer to the MIME-type. Defaults to text/plain if we have
207 * no opinion.
209 MIME_type *type_from_path(char *path)
211 char *dot;
213 dot = strrchr(path, '.');
214 if (dot)
216 MIME_type *type;
217 type = g_hash_table_lookup(extension_hash, dot + 1);
218 if (type)
219 return type;
222 return &text_plain;
225 /* Actions for types */
227 gboolean type_open(char *path, MIME_type *type)
229 char *argv[] = {NULL, NULL, NULL};
230 char *open;
231 char *type_name;
232 gboolean retval = TRUE;
233 struct stat info;
235 argv[1] = path;
237 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
238 open = choices_find_path_load(type_name, "MIME-types");
239 g_free(type_name);
240 if (!open)
242 open = choices_find_path_load(type->media_type,
243 "MIME-types");
244 if (!open)
245 return FALSE;
248 if (stat(open, &info))
250 report_error(PROJECT, g_strerror(errno));
251 return FALSE;
254 if (S_ISDIR(info.st_mode))
255 argv[0] = g_strconcat(open, "/AppRun", NULL);
256 else
257 argv[0] = open;
259 if (!spawn_full(argv, home_dir))
261 report_error(PROJECT,
262 _("Failed to fork() child process"));
263 retval = FALSE;
266 if (argv[0] != open)
267 g_free(argv[0]);
269 return retval;
272 /* Return the image for this type, loading it if needed.
273 * Places to check are: (eg type="text_plain", base="text")
274 * 1. Choices:MIME-icons/<type>
275 * 2. Choices:MIME-icons/<base>
276 * 3. Unknown type icon.
278 * Note: You must pixmap_unref() the image afterwards.
280 MaskedPixmap *type_to_icon(MIME_type *type)
282 char *path;
283 char *type_name;
284 time_t now;
286 g_return_val_if_fail(type != NULL, im_unknown);
288 now = time(NULL);
289 /* Already got an image? */
290 if (type->image)
292 /* Yes - don't recheck too often */
293 if (abs(now - type->image_time) < 2)
295 pixmap_ref(type->image);
296 return type->image;
298 pixmap_unref(type->image);
299 type->image = NULL;
302 type_name = g_strconcat(type->media_type, "_",
303 type->subtype, ".xpm", NULL);
304 path = choices_find_path_load(type_name, "MIME-icons");
305 if (!path)
307 strcpy(type_name + strlen(type->media_type), ".xpm");
308 path = choices_find_path_load(type_name, "MIME-icons");
311 g_free(type_name);
313 if (path)
314 type->image = g_fscache_lookup(pixmap_cache, path);
316 if (!type->image)
318 type->image = im_unknown;
319 pixmap_ref(type->image);
322 type->image_time = now;
324 pixmap_ref(type->image);
325 return type->image;
328 GdkAtom type_to_atom(MIME_type *type)
330 char *str;
331 GdkAtom retval;
333 g_return_val_if_fail(type != NULL, GDK_NONE);
335 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
336 retval = gdk_atom_intern(str, FALSE);
337 g_free(str);
339 return retval;
342 /* The user wants to set a new default action for files of this type.
343 * Ask the user if they want to set eg 'text/plain' or just 'text'.
344 * Removes the current binding if possible and returns the path to
345 * save the new one to. NULL means cancel.
347 char *type_ask_which_action(guchar *media_type, guchar *subtype)
349 int r;
350 guchar *tmp, *type_name, *path;
351 struct stat info;
353 g_return_val_if_fail(media_type != NULL, NULL);
354 g_return_val_if_fail(subtype != NULL, NULL);
356 if (!choices_find_path_save("", PROJECT, FALSE))
358 report_error(PROJECT,
359 _("Choices saving is disabled by CHOICESPATH variable"));
360 return NULL;
363 type_name = g_strconcat(media_type, "/", subtype, NULL);
364 tmp = g_strdup_printf(
365 _("You can choose to set the action for just '%s' files, or "
366 "the default action for all '%s' files which don't already "
367 "have a run action:"), type_name, media_type);
368 r = get_choice(PROJECT, tmp, 3, type_name, media_type, "Cancel");
369 g_free(tmp);
370 g_free(type_name);
372 if (r == 0)
374 type_name = g_strconcat(media_type, "_", subtype, NULL);
375 path = choices_find_path_save(type_name, "MIME-types", TRUE);
376 g_free(type_name);
378 else if (r == 1)
379 path = choices_find_path_save(media_type, "MIME-types", TRUE);
380 else
381 return NULL;
383 if (lstat(path, &info) == 0)
385 /* A binding already exists... */
386 if (S_ISREG(info.st_mode) && info.st_size > 256)
388 if (get_choice(PROJECT,
389 _("A run action already exists and is quite "
390 "a big program - are you sure you want to "
391 "delete it?"), 2, "Delete", "Cancel") != 0)
392 return NULL;
395 if (unlink(path))
397 tmp = g_strdup_printf( _("Can't remove %s: %s"),
398 path, g_strerror(errno));
399 report_error(PROJECT, tmp);
400 g_free(tmp);
401 return NULL;
405 return path;
408 MIME_type *mime_type_from_base_type(int base_type)
410 switch (base_type)
412 case TYPE_DIRECTORY:
413 return &special_directory;
414 case TYPE_PIPE:
415 return &special_pipe;
416 case TYPE_SOCKET:
417 return &special_socket;
418 case TYPE_BLOCK_DEVICE:
419 return &special_block_dev;
420 case TYPE_CHAR_DEVICE:
421 return &special_char_dev;
423 return &special_unknown;