r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / diritem.c
blobd16fc544bc7d12bdc05a545038d825837a4e6cb6
1 /*
2 * $Id$
4 * Copyright (C) 2002, the ROX-Filer team.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 * Place, Suite 330, Boston, MA 02111-1307 USA
21 /* diritem.c - get details about files */
23 /* Don't load icons larger than 400K (this is rather excessive, basically
24 * we just want to stop people crashing the filer with huge icons).
26 #define MAX_ICON_SIZE (400 * 1024)
28 #include "config.h"
30 #include <gtk/gtk.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
36 #include "global.h"
38 #include "diritem.h"
39 #include "support.h"
40 #include "gui_support.h"
41 #include "mount.h"
42 #include "type.h"
43 #include "usericons.h"
44 #include "options.h"
45 #include "fscache.h"
46 #include "pixmaps.h"
48 static Option o_ignore_exec;
50 /* Static prototypes */
51 static void examine_dir(const guchar *path, DirItem *item);
54 /****************************************************************
55 * EXTERNAL INTERFACE *
56 ****************************************************************/
58 void diritem_init(void)
60 option_add_int(&o_ignore_exec, "display_ignore_exec", FALSE);
62 read_globicons();
65 /* Bring this item's structure uptodate.
66 * 'parent' is optional; it saves one stat() for directories.
68 void diritem_restat(const guchar *path, DirItem *item, struct stat *parent)
70 struct stat info;
72 if (item->image)
74 g_object_unref(item->image);
75 item->image = NULL;
77 item->flags = 0;
78 item->mime_type = NULL;
80 if (mc_lstat(path, &info) == -1)
82 item->lstat_errno = errno;
83 item->base_type = TYPE_ERROR;
84 item->size = 0;
85 item->mode = 0;
86 item->mtime = item->ctime = item->atime = 0;
87 item->uid = (uid_t) -1;
88 item->gid = (gid_t) -1;
90 else
92 item->lstat_errno = 0;
93 item->size = info.st_size;
94 item->mode = info.st_mode;
95 item->atime = info.st_atime;
96 item->ctime = info.st_ctime;
97 item->mtime = info.st_mtime;
98 item->uid = info.st_uid;
99 item->gid = info.st_gid;
101 if (S_ISLNK(info.st_mode))
103 if (mc_stat(path, &info))
104 item->base_type = TYPE_ERROR;
105 else
106 item->base_type =
107 mode_to_base_type(info.st_mode);
109 item->flags |= ITEM_FLAG_SYMLINK;
111 else
113 item->base_type = mode_to_base_type(info.st_mode);
115 if (item->base_type == TYPE_DIRECTORY)
117 if (mount_is_mounted(path, &info, parent))
118 item->flags |= ITEM_FLAG_MOUNT_POINT
119 | ITEM_FLAG_MOUNTED;
120 else if (g_hash_table_lookup(fstab_mounts,
121 path))
122 item->flags |= ITEM_FLAG_MOUNT_POINT;
127 if (item->base_type == TYPE_DIRECTORY)
128 examine_dir(path, item);
129 else if (item->base_type == TYPE_FILE)
131 /* Type determined from path before checking for executables
132 * because some mounts show everything as executable and we
133 * still want to use the file name to set the mime type.
135 if (item->flags & ITEM_FLAG_SYMLINK)
137 guchar *link_path;
138 link_path = readlink_dup(path);
139 item->mime_type = type_from_path(link_path
140 ? link_path
141 : path);
142 g_free(link_path);
144 else
145 item->mime_type = type_from_path(path);
147 /* Note: for symlinks we need the mode of the target */
148 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
150 /* Note that the flag is set for ALL executable
151 * files, but the mime_type is only application_executable
152 * if the file doesn't have a known extension when
153 * that option is in force.
155 item->flags |= ITEM_FLAG_EXEC_FILE;
157 if (!o_ignore_exec.int_value)
158 item->mime_type = application_executable;
161 if (!item->mime_type)
162 item->mime_type = item->flags & ITEM_FLAG_EXEC_FILE
163 ? application_executable
164 : text_plain;
166 check_globicon(path, item);
168 else
169 check_globicon(path, item);
172 if (!item->mime_type)
173 item->mime_type = mime_type_from_base_type(item->base_type);
175 if (!item->image)
177 if (item->base_type == TYPE_ERROR)
179 item->image = im_error;
180 g_object_ref(im_error);
182 else
183 item->image = type_to_icon(item->mime_type);
187 DirItem *diritem_new(const guchar *leafname)
189 DirItem *item;
190 const gchar *i;
191 gboolean all_alpha = TRUE;
193 item = g_new(DirItem, 1);
194 item->leafname = g_strdup(leafname);
195 item->may_delete = FALSE;
196 item->image = NULL;
197 item->base_type = TYPE_UNKNOWN;
198 item->flags = 0;
199 item->mime_type = NULL;
201 for (i = leafname; *i; i++)
203 if (!isalnum(*i))
205 all_alpha = FALSE;
206 break;
210 if (all_alpha)
211 item->leafname_collate = item->leafname;
212 else
214 gchar *o;
215 gboolean need_digit_spacer = FALSE;
216 gboolean may_need_digit_spacer = FALSE;
218 item->leafname_collate = g_malloc(strlen(item->leafname) + 1);
219 o = item->leafname_collate;
221 /* TODO: Fix for UTF-8 */
223 for (i = leafname; *i; i++)
225 if (isdigit(*i))
227 if (need_digit_spacer)
229 need_digit_spacer = FALSE;
230 *(o++) = '-';
232 may_need_digit_spacer = TRUE;
233 *(o++) = *i;
235 else if (isalpha(*i))
237 need_digit_spacer = FALSE;
238 may_need_digit_spacer = FALSE;
239 *(o++) = *i;
241 else if (may_need_digit_spacer)
243 /* We had a digit more recently than
244 * an alpha. If this thing following this
245 * punctuation is another digit, add a spacer.
247 need_digit_spacer = TRUE;
250 *o = '\0';
253 return item;
256 void diritem_free(DirItem *item)
258 g_return_if_fail(item != NULL);
260 if (item->image)
261 g_object_unref(item->image);
262 item->image = NULL;
263 if (item->leafname_collate != item->leafname)
264 g_free(item->leafname_collate);
265 null_g_free(&item->leafname);
266 g_free(item);
270 /****************************************************************
271 * INTERNAL FUNCTIONS *
272 ****************************************************************/
274 /* Fill in more details of the DirItem for a directory item.
275 * - Looks for an image (but maybe still NULL on error)
276 * - Updates ITEM_FLAG_APPDIR
278 static void examine_dir(const guchar *path, DirItem *item)
280 uid_t uid = item->uid;
281 struct stat info;
282 static GString *tmp = NULL;
284 if (!tmp)
285 tmp = g_string_new(NULL);
287 check_globicon(path, item);
289 if (item->flags & ITEM_FLAG_MOUNT_POINT)
290 return; /* Try to avoid automounter problems */
292 /* Finding the icon:
294 * - If it contains a .DirIcon then that's the icon
295 * - If it contains an AppRun then it's an application
296 * - If it contains an AppRun but no .DirIcon then try to
297 * use AppIcon.xpm as the icon.
299 * .DirIcon and AppRun must have the same owner as the
300 * directory itself, to prevent abuse of /tmp, etc.
301 * For symlinks, we want the symlink's owner.
304 g_string_sprintf(tmp, "%s/.DirIcon", path);
306 if (item->image)
307 goto no_diricon; /* Already got an icon */
309 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
310 goto no_diricon; /* Missing, or wrong owner */
312 if (S_ISLNK(info.st_mode) && mc_stat(tmp->str, &info) != 0)
313 goto no_diricon; /* Bad symlink */
315 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
316 goto no_diricon; /* Too big, or non-regular file */
318 /* Try to load image; may still get NULL... */
319 item->image = g_fscache_lookup(pixmap_cache, tmp->str);
321 no_diricon:
323 /* Try to find AppRun... */
324 g_string_truncate(tmp, tmp->len - 8);
325 g_string_append(tmp, "AppRun");
327 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
328 goto out; /* Missing, or wrong owner */
330 if (!(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
331 goto out; /* Not executable */
333 item->flags |= ITEM_FLAG_APPDIR;
335 /* Try to load AppIcon.xpm... */
337 if (item->image)
338 goto out; /* Already got an icon */
340 g_string_truncate(tmp, tmp->len - 3);
341 g_string_append(tmp, "Icon.xpm");
343 /* Note: since AppRun is valid we don't need to check AppIcon.xpm
344 * so carefully.
347 if (mc_stat(tmp->str, &info) != 0)
348 goto out; /* Missing, or broken symlink */
350 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
351 goto out; /* Too big, or non-regular file */
353 /* Try to load image; may still get NULL... */
354 item->image = g_fscache_lookup(pixmap_cache, tmp->str);
356 out:
358 if ((item->flags & ITEM_FLAG_APPDIR) && !item->image)
360 /* This is an application without an icon */
361 item->image = im_appdir;
362 g_object_ref(item->image);