Converted README to markdown
[rox-filer.git] / ROX-Filer / src / diritem.c
blob1503e42515cf60746278b7a9d69f391a79cab8eb
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* diritem.c - get details about files */
22 /* Don't load icons larger than 400K (this is rather excessive, basically
23 * we just want to stop people crashing the filer with huge icons).
25 #define MAX_ICON_SIZE (400 * 1024)
27 #include "config.h"
29 #include <gtk/gtk.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
35 #include "global.h"
37 #include "diritem.h"
38 #include "support.h"
39 #include "gui_support.h"
40 #include "mount.h"
41 #include "type.h"
42 #include "usericons.h"
43 #include "options.h"
44 #include "fscache.h"
45 #include "pixmaps.h"
46 #include "xtypes.h"
48 #define RECENT_DELAY (5 * 60) /* Time in seconds to consider a file recent */
49 #define ABOUT_NOW(time) (diritem_recent_time - time < RECENT_DELAY)
50 /* If you want to make use of the RECENT flag, make sure this is set to
51 * the current time before calling diritem_restat().
53 time_t diritem_recent_time;
55 /* Static prototypes */
56 static void examine_dir(const guchar *path, DirItem *item,
57 struct stat *link_target);
59 /****************************************************************
60 * EXTERNAL INTERFACE *
61 ****************************************************************/
63 void diritem_init(void)
65 read_globicons();
68 /* Bring this item's structure uptodate.
69 * 'parent' is optional; it saves one stat() for directories.
71 void diritem_restat(const guchar *path, DirItem *item, struct stat *parent)
73 struct stat info;
75 if (item->_image)
77 g_object_unref(item->_image);
78 item->_image = NULL;
80 item->flags = 0;
81 item->mime_type = NULL;
83 if (mc_lstat(path, &info) == -1)
85 item->lstat_errno = errno;
86 item->base_type = TYPE_ERROR;
87 item->size = 0;
88 item->mode = 0;
89 item->mtime = item->ctime = item->atime = 0;
90 item->uid = (uid_t) -1;
91 item->gid = (gid_t) -1;
93 else
95 guchar *target_path;
97 item->lstat_errno = 0;
98 item->size = info.st_size;
99 item->mode = info.st_mode;
100 item->atime = info.st_atime;
101 item->ctime = info.st_ctime;
102 item->mtime = info.st_mtime;
103 item->uid = info.st_uid;
104 item->gid = info.st_gid;
105 if (ABOUT_NOW(item->mtime) || ABOUT_NOW(item->ctime))
106 item->flags |= ITEM_FLAG_RECENT;
108 if (xattr_have(path))
109 item->flags |= ITEM_FLAG_HAS_XATTR;
111 if (S_ISLNK(info.st_mode))
113 if (mc_stat(path, &info))
114 item->base_type = TYPE_ERROR;
115 else
116 item->base_type =
117 mode_to_base_type(info.st_mode);
119 item->flags |= ITEM_FLAG_SYMLINK;
121 target_path = pathdup(path);
123 else
125 item->base_type = mode_to_base_type(info.st_mode);
126 target_path = (guchar *) path;
129 if (item->base_type == TYPE_DIRECTORY)
131 if (mount_is_mounted(target_path, &info,
132 target_path == path ? parent : NULL))
133 item->flags |= ITEM_FLAG_MOUNT_POINT
134 | ITEM_FLAG_MOUNTED;
135 else if (g_hash_table_lookup(fstab_mounts,
136 target_path))
137 item->flags |= ITEM_FLAG_MOUNT_POINT;
140 if (path != target_path)
141 g_free(target_path);
144 if (item->base_type == TYPE_DIRECTORY)
146 /* KRJW: info.st_uid will be the uid of the dir, regardless
147 * of whether `path' is a dir or a symlink to one. Note that
148 * if path is a symlink to a dir, item->uid will be the uid
149 * of the *symlink*, but we really want the uid of the dir
150 * to which the symlink points.
152 examine_dir(path, item, &info);
154 else if (item->base_type == TYPE_FILE)
156 if (item->flags & ITEM_FLAG_SYMLINK)
158 guchar *link_path;
159 link_path = pathdup(path);
160 item->mime_type = type_from_path(link_path
161 ? link_path
162 : path);
163 g_free(link_path);
165 else
166 item->mime_type = type_from_path(path);
168 /* Note: for symlinks we need the mode of the target */
169 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
171 /* Note that the flag is set for ALL executable
172 * files, but the mime_type must also be executable
173 * for clicking on the file to run it.
175 item->flags |= ITEM_FLAG_EXEC_FILE;
177 if (item->mime_type == NULL ||
178 item->mime_type == application_octet_stream)
180 item->mime_type = application_executable;
182 else if (item->mime_type == text_plain &&
183 !strchr(item->leafname, '.'))
185 item->mime_type = application_x_shellscript;
188 else if (item->mime_type == application_x_desktop)
190 item->flags |= ITEM_FLAG_EXEC_FILE;
193 if (!item->mime_type)
194 item->mime_type = text_plain;
196 check_globicon(path, item);
198 if (item->mime_type == application_x_desktop && item->_image == NULL)
200 item->_image = g_fscache_lookup(desktop_icon_cache, path);
203 else
204 check_globicon(path, item);
206 if (!item->mime_type)
207 item->mime_type = mime_type_from_base_type(item->base_type);
210 DirItem *diritem_new(const guchar *leafname)
212 DirItem *item;
214 item = g_new(DirItem, 1);
215 item->leafname = g_strdup(leafname);
216 item->may_delete = FALSE;
217 item->_image = NULL;
218 item->base_type = TYPE_UNKNOWN;
219 item->flags = ITEM_FLAG_NEED_RESCAN_QUEUE;
220 item->mime_type = NULL;
221 item->leafname_collate = collate_key_new(leafname);
223 return item;
226 void diritem_free(DirItem *item)
228 g_return_if_fail(item != NULL);
230 if (item->_image)
231 g_object_unref(item->_image);
232 item->_image = NULL;
233 collate_key_free(item->leafname_collate);
234 g_free(item->leafname);
235 g_free(item);
238 /* For use by di_image() only. Sets item->_image. */
239 void _diritem_get_image(DirItem *item)
241 g_return_if_fail(item->_image == NULL);
243 if (item->base_type == TYPE_ERROR)
245 item->_image = im_error;
246 g_object_ref(im_error);
248 else
249 item->_image = type_to_icon(item->mime_type);
252 /****************************************************************
253 * INTERNAL FUNCTIONS *
254 ****************************************************************/
256 /* Fill in more details of the DirItem for a directory item.
257 * - Looks for an image (but maybe still NULL on error)
258 * - Updates ITEM_FLAG_APPDIR
260 * link_target contains stat info for the link target for symlinks (or for the
261 * item itself if not a link).
263 static void examine_dir(const guchar *path, DirItem *item,
264 struct stat *link_target)
266 struct stat info;
267 static GString *tmp = NULL;
268 uid_t uid = link_target->st_uid;
270 if (!tmp)
271 tmp = g_string_new(NULL);
273 check_globicon(path, item);
275 if (item->flags & ITEM_FLAG_MOUNT_POINT)
277 item->mime_type = inode_mountpoint;
278 return; /* Try to avoid automounter problems */
281 if (link_target->st_mode & S_IWOTH)
282 return; /* Don't trust world-writable dirs */
284 /* Finding the icon:
286 * - If it contains a .DirIcon then that's the icon
287 * - If it contains an AppRun then it's an application
288 * - If it contains an AppRun but no .DirIcon then try to
289 * use AppIcon.xpm as the icon.
291 * .DirIcon and AppRun must have the same owner as the
292 * directory itself, to prevent abuse of /tmp, etc.
293 * For symlinks, we want the symlink's owner.
296 g_string_printf(tmp, "%s/.DirIcon", path);
298 if (item->_image)
299 goto no_diricon; /* Already got an icon */
301 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
302 goto no_diricon; /* Missing, or wrong owner */
304 if (S_ISLNK(info.st_mode) && mc_stat(tmp->str, &info) != 0)
305 goto no_diricon; /* Bad symlink */
307 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
308 goto no_diricon; /* Too big, or non-regular file */
310 /* Try to load image; may still get NULL... */
311 item->_image = g_fscache_lookup(pixmap_cache, tmp->str);
313 no_diricon:
315 /* Try to find AppRun... */
316 g_string_truncate(tmp, tmp->len - 8);
317 g_string_append(tmp, "AppRun");
319 if (mc_lstat(tmp->str, &info) != 0 || info.st_uid != uid)
320 goto out; /* Missing, or wrong owner */
322 if (!(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
323 goto out; /* Not executable */
325 item->flags |= ITEM_FLAG_APPDIR;
327 /* Try to load AppIcon.xpm... */
329 if (item->_image)
330 goto out; /* Already got an icon */
332 g_string_truncate(tmp, tmp->len - 3);
333 g_string_append(tmp, "Icon.xpm");
335 /* Note: since AppRun is valid we don't need to check AppIcon.xpm
336 * so carefully.
339 if (mc_stat(tmp->str, &info) != 0)
340 goto out; /* Missing, or broken symlink */
342 if (info.st_size > MAX_ICON_SIZE || !S_ISREG(info.st_mode))
343 goto out; /* Too big, or non-regular file */
345 /* Try to load image; may still get NULL... */
346 item->_image = g_fscache_lookup(pixmap_cache, tmp->str);
348 out:
350 if ((item->flags & ITEM_FLAG_APPDIR) && !item->_image)
352 /* This is an application without an icon */
353 item->_image = im_appdir;
354 g_object_ref(item->_image);