r845: Require AppRun to be executable before showing a directory as an
[rox-filer.git] / ROX-Filer / src / support.c
blobab8f1520f68d7a345b74d2ffe908fb00cf18c90b
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, the ROX-Filer team.
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 /* support.c - (non-GUI) useful routines */
24 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <fcntl.h>
35 #include <sys/wait.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
40 #include "global.h"
42 #include "choices.h"
43 #include "main.h"
44 #include "options.h"
45 #include "support.h"
46 #include "my_vfs.h"
48 static GHashTable *uid_hash = NULL; /* UID -> User name */
49 static GHashTable *gid_hash = NULL; /* GID -> Group name */
51 /* Static prototypes */
54 /* Like g_strdup, but does realpath() too (if possible) */
55 char *pathdup(char *path)
57 char real[MAXPATHLEN];
59 g_return_val_if_fail(path != NULL, NULL);
61 if (realpath(path, real))
62 return g_strdup(real);
64 return g_strdup(path);
67 /* Join the path to the leaf (adding a / between them) and
68 * return a pointer to a buffer with the result. Buffer is valid until
69 * the next call to make_path.
71 GString *make_path(char *dir, char *leaf)
73 static GString *buffer = NULL;
75 if (!buffer)
76 buffer = g_string_new(NULL);
78 g_return_val_if_fail(dir != NULL, buffer);
79 g_return_val_if_fail(leaf != NULL, buffer);
81 g_string_sprintf(buffer, "%s%s%s",
82 dir,
83 dir[0] == '/' && dir[1] == '\0' ? "" : "/",
84 leaf);
86 return buffer;
89 /* Return our complete host name for DND */
90 char *our_host_name_for_dnd(void)
92 if (option_get_int("dnd_no_hostnames"))
93 return "";
94 return our_host_name();
97 /* Return our complete host name, unconditionally */
98 char *our_host_name(void)
100 static char *name = NULL;
102 if (!name)
104 char buffer[4096];
106 if (gethostname(buffer, 4096) == 0)
108 /* gethostname doesn't always return the full name... */
109 struct hostent *ent;
111 buffer[4095] = '\0';
112 ent = gethostbyname(buffer);
113 name = g_strdup(ent ? ent->h_name : buffer);
115 else
117 g_warning("gethostname() failed - using localhost\n");
118 name = g_strdup("localhost");
122 return name;
125 /* fork() and run a new program.
126 * Returns the new PID, or 0 on failure.
128 pid_t spawn(char **argv)
130 return spawn_full(argv, NULL);
133 /* As spawn(), but cd to dir first (if dir is non-NULL) */
134 pid_t spawn_full(char **argv, char *dir)
136 int child;
138 child = fork();
140 if (child == -1)
141 return 0; /* Failure */
142 else if (child == 0)
144 /* We are the child process */
145 if (dir)
146 if (chdir(dir))
147 fprintf(stderr, "chdir() failed: %s\n",
148 g_strerror(errno));
149 execvp(argv[0], argv);
150 fprintf(stderr, "execvp(%s, ...) failed: %s\n",
151 argv[0],
152 g_strerror(errno));
153 _exit(0);
156 /* We are the parent */
157 return child;
160 void debug_free_string(void *data)
162 g_print("Freeing string '%s'\n", (char *) data);
163 g_free(data);
166 char *user_name(uid_t uid)
168 char *retval;
170 if (!uid_hash)
171 uid_hash = g_hash_table_new(NULL, NULL);
173 retval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(uid));
175 if (!retval)
177 struct passwd *passwd;
179 passwd = getpwuid(uid);
180 retval = passwd ? g_strdup(passwd->pw_name)
181 : g_strdup_printf("[%d]", (int) uid);
182 g_hash_table_insert(uid_hash, GINT_TO_POINTER(uid), retval);
185 return retval;
188 char *group_name(gid_t gid)
190 char *retval;
192 if (!gid_hash)
193 gid_hash = g_hash_table_new(NULL, NULL);
195 retval = g_hash_table_lookup(gid_hash, GINT_TO_POINTER(gid));
197 if (!retval)
199 struct group *group;
201 group = getgrgid(gid);
202 retval = group ? g_strdup(group->gr_name)
203 : g_strdup_printf("[%d]", (int) gid);
204 g_hash_table_insert(gid_hash, GINT_TO_POINTER(gid), retval);
207 return retval;
210 /* Return a string in the form '23Mb' in a static buffer valid until
211 * the next call.
213 char *format_size(unsigned long size)
215 static char *buffer = NULL;
216 char *units;
218 if (size >= PRETTY_SIZE_LIMIT)
220 size += 1023;
221 size >>= 10;
222 if (size >= PRETTY_SIZE_LIMIT)
224 size += 1023;
225 size >>= 10;
226 if (size >= PRETTY_SIZE_LIMIT)
228 size += 1023;
229 size >>= 10;
230 units = "Gb";
232 else
233 units = "Mb";
235 else
236 units = "K";
238 else if (size == 1)
239 units = _("byte");
240 else
241 units = _("bytes");
243 if (buffer)
244 g_free(buffer);
245 buffer = g_strdup_printf("%ld %s", size, units);
247 return buffer;
250 /* Return a string in the form '23Mb' in a static buffer valid until
251 * the next call. Aligned to the right.
253 char *format_size_aligned(unsigned long size)
255 static char *buffer = NULL;
256 char units;
258 if (size >= PRETTY_SIZE_LIMIT)
260 size += 1023;
261 size >>= 10;
262 if (size >= PRETTY_SIZE_LIMIT)
264 size += 1023;
265 size >>= 10;
266 if (size >= PRETTY_SIZE_LIMIT)
268 size += 1023;
269 size >>= 10;
270 units = 'G';
272 else
273 units = 'M';
275 else
276 units = 'K';
278 else
279 units = ' ';
281 if (buffer)
282 g_free(buffer);
283 buffer = g_strdup_printf("%4ld%c", size, units);
285 return buffer;
289 * Similar to format_size(), but this one uses a double argument since
290 * unsigned long isn't wide enough on all platforms and we must be able to
291 * sum sizes above 4 GB.
293 gchar *format_double_size(double size)
295 static gchar *buf = NULL;
296 char *units;
298 if (size >= PRETTY_SIZE_LIMIT)
300 size += 1023;
301 size /= 1024;
302 if (size >= PRETTY_SIZE_LIMIT)
304 size += 1023;
305 size /= 1024;
306 if (size >= PRETTY_SIZE_LIMIT)
308 size += 1023;
309 size /= 1024;
310 units = "Gb";
312 else
313 units = "Mb";
315 else
316 units = "K";
319 else if (size != 1)
320 units = _("bytes");
321 else
322 units = _("byte");
324 if (buf)
325 g_free(buf);
326 buf = g_strdup_printf("%.0f %s", size, units);
328 return buf;
331 /* Fork and exec argv. Wait and return the child's exit status.
332 * -1 if spawn fails.
334 int fork_exec_wait(char **argv)
336 pid_t child;
337 int status = -1;
339 child = spawn_full(argv, NULL);
341 while (child)
343 if (waitpid(child, &status, 0) == -1)
345 if (errno != EINTR)
346 return -1;
348 else
349 break;
352 return status;
355 /* If a file has this UID and GID, which permissions apply to us?
356 * 0 = User, 1 = Group, 2 = World
358 gint applicable(uid_t uid, gid_t gid)
360 int i;
362 if (uid == euid)
363 return 0;
365 if (gid == egid)
366 return 1;
368 for (i = 0; i < ngroups; i++)
370 if (supplemental_groups[i] == gid)
371 return 1;
374 return 2;
377 /* Converts a file's mode to a string. Result is a pointer
378 * to a static buffer, valid until the next call.
380 char *pretty_permissions(mode_t m)
382 static char buffer[] = "rwx,rwx,rwx/UGT";
384 buffer[0] = m & S_IRUSR ? 'r' : '-';
385 buffer[1] = m & S_IWUSR ? 'w' : '-';
386 buffer[2] = m & S_IXUSR ? 'x' : '-';
388 buffer[4] = m & S_IRGRP ? 'r' : '-';
389 buffer[5] = m & S_IWGRP ? 'w' : '-';
390 buffer[6] = m & S_IXGRP ? 'x' : '-';
392 buffer[8] = m & S_IROTH ? 'r' : '-';
393 buffer[9] = m & S_IWOTH ? 'w' : '-';
394 buffer[10] = m & S_IXOTH ? 'x' : '-';
396 buffer[12] = m & S_ISUID ? 'U' : '-';
397 buffer[13] = m & S_ISGID ? 'G' : '-';
398 #ifdef S_ISVTX
399 buffer[14] = m & S_ISVTX ? 'T' : '-';
400 buffer[15] = 0;
401 #else
402 buffer[14] = 0;
403 #endif
405 return buffer;
408 /* Gets the canonical name for address and compares to our_host_name() */
409 static gboolean is_local_address(char *address)
411 struct hostent *ent;
413 ent = gethostbyname(address);
415 return strcmp(our_host_name(), ent ? ent->h_name : address) == 0;
418 /* Convert a URI to a local pathname (or NULL if it isn't local).
419 * The returned pointer points inside the input string.
420 * Possible formats:
421 * /path
422 * ///path
423 * //host/path
424 * file://host/path
426 char *get_local_path(char *uri)
428 if (*uri == '/')
430 char *path, *uri_host;
432 if (uri[1] != '/')
433 return uri; /* Just a local path - no host part */
435 path = strchr(uri + 2, '/');
436 if (!path)
437 return NULL; /* //something */
439 if (path - uri == 2)
440 return path; /* ///path */
442 uri_host = g_strndup(uri + 2, path - uri - 2);
443 if (is_local_address(uri_host))
445 g_free(uri_host);
446 return path; /* //myhost/path */
448 g_free(uri_host);
450 return NULL; /* From a different host */
452 else
454 if (strncasecmp(uri, "file:", 5))
455 return NULL; /* Don't know this format */
457 uri += 5;
459 if (*uri == '/')
460 return get_local_path(uri);
462 return NULL;
466 /* Set the close-on-exec flag for this FD.
467 * TRUE means that an exec()'d process will not get the FD.
469 void close_on_exec(int fd, gboolean close)
471 if (fcntl(fd, F_SETFD, close))
472 g_warning("fcntl() failed: %s\n", g_strerror(errno));
475 void set_blocking(int fd, gboolean blocking)
477 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
478 g_warning("fcntl() failed: %s\n", g_strerror(errno));
481 /* Format this time nicely. The result is a pointer to a static buffer,
482 * valid until the next call.
484 char *pretty_time(time_t *time)
486 static char time_buf[32];
488 if (strftime(time_buf, sizeof(time_buf),
489 TIME_FORMAT, localtime(time)) == 0)
490 time_buf[0]= 0;
492 return time_buf;
495 #ifndef O_NOFOLLOW
496 # define O_NOFOLLOW 0x0
497 #endif
499 #ifdef HAVE_LIBVFS
500 /* Copy data from 'read_fd' FD to 'write_fd' FD until EOF or error.
501 * Returns 0 on success, -1 on error (and sets errno).
503 static int copy_fd(int read_fd, int write_fd)
505 char buffer[4096];
506 int got;
508 while ((got = mc_read(read_fd, buffer, sizeof(buffer))) > 0)
510 int sent = 0;
512 while (sent < got)
514 int c;
516 c = mc_write(write_fd, buffer + sent, got - sent);
517 if (c < 0)
518 return -1;
519 sent += c;
523 return got;
525 #endif
527 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
528 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
529 * do the copy manually using vfs.
531 * Returns an error string, or NULL on success.
533 guchar *copy_file(guchar *from, guchar *to)
535 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
537 #ifdef HAVE_LIBVFS
538 struct stat info;
540 if (lstat(from, &info))
542 int read_fd = -1;
543 int write_fd = -1;
544 int error = 0;
546 if (mc_lstat(from, &info))
547 return g_strerror(errno);
549 /* Regular lstat() can't find it, but mc_lstat() can,
550 * so try reading it with VFS.
553 if (!S_ISREG(info.st_mode))
554 goto err;
556 read_fd = mc_open(from, O_RDONLY);
557 if (read_fd == -1)
558 goto err;
559 write_fd = mc_open(to,
560 O_NOFOLLOW | O_WRONLY | O_CREAT | O_TRUNC,
561 info.st_mode & 0777);
562 if (write_fd == -1)
563 goto err;
565 if (copy_fd(read_fd, write_fd))
566 goto err;
568 /* (yes, the single | is right) */
569 if (mc_close(read_fd) | mc_close(write_fd))
570 return g_strerror(errno);
571 return NULL;
572 err:
573 error = errno;
574 if (read_fd != -1)
575 mc_close(read_fd);
576 if (write_fd != -1)
577 mc_close(write_fd);
578 return error ? g_strerror(error) : _("Copy error");
580 #endif
582 argv[2] = from;
583 argv[3] = to;
585 if (fork_exec_wait(argv))
586 return _("Copy failed");
587 return NULL;
590 /* 'word' has all special characters escaped so that it may be inserted
591 * into a shell command.
592 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
594 guchar *shell_escape(guchar *word)
596 GString *tmp;
597 guchar *retval;
599 tmp = g_string_new(NULL);
601 while (*word)
603 if (strchr(" ?*['\"$~\\|();!`&", *word))
604 g_string_append_c(tmp, '\\');
605 g_string_append_c(tmp, *word);
606 word++;
609 retval = tmp->str;
610 g_string_free(tmp, FALSE);
611 return retval;
614 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
615 * (or the two are the same item/directory).
616 * FALSE if parent doesn't exist.
618 gboolean is_sub_dir(char *sub, char *parent)
620 struct stat parent_info;
622 /* These were pathdup()s, but that follows symlinks!
623 * I guess they were here for a reason - something is probably
624 * broken now...
626 sub = g_strdup(sub);
628 if (mc_stat(parent, &parent_info))
629 return FALSE; /* Parent doesn't exist */
631 while (1)
633 char *slash;
634 struct stat info;
636 if (mc_stat(sub, &info) == 0)
638 if (info.st_dev == parent_info.st_dev &&
639 info.st_ino == parent_info.st_ino)
641 g_free(sub);
642 return TRUE;
646 slash = strrchr(sub, '/');
647 if (!slash)
648 break;
649 if (slash == sub)
651 if (sub[1])
652 sub[1] = '\0';
653 else
654 break;
656 else
657 *slash = '\0';
660 g_free(sub);
662 return FALSE;
665 /* True if the string 'list' contains 'item'.
666 * Eg ("close", "close, help") -> TRUE
668 gboolean in_list(guchar *item, guchar *list)
670 int len;
672 len = strlen(item);
674 while (*list)
676 if (strncmp(item, list, len) == 0 && !isalpha(list[len]))
677 return TRUE;
678 list = strchr(list, ',');
679 if (!list)
680 return FALSE;
681 while (isspace(*++list))
685 return FALSE;
688 /* Split a path into its components. Eg:
690 * /bob/fred -> ["bob", "fred"]
691 * ///a//b// -> ["a", "b"]
692 * / -> []
694 * The array and the strings in it must be freed after use.
696 GPtrArray *split_path(guchar *path)
698 GPtrArray *array;
699 guchar *slash;
701 g_return_val_if_fail(path != NULL, NULL);
703 array = g_ptr_array_new();
705 while (1)
707 while (path[0] == '/')
708 path++;
709 if (path[0] == '\0')
710 break;
712 slash = strchr(path, '/');
713 if (slash)
715 g_ptr_array_add(array, g_strndup(path, slash - path));
716 path = slash + 1;
717 continue;
719 g_ptr_array_add(array, g_strdup(path));
720 break;
723 return array;
726 /* Return the shortest path from 'from' to 'to'.
727 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
729 guchar *get_relative_path(guchar *from, guchar *to)
731 GString *path;
732 guchar *retval;
733 GPtrArray *src, *dst;
734 int i, j;
736 src = split_path(from);
737 dst = split_path(to);
739 /* The last component of src doesn't matter... */
740 if (src->len)
742 g_free(src->pdata[src->len - 1]);
743 g_ptr_array_remove_index(src, src->len - 1);
746 /* Strip off common path elements... */
747 i = 0;
748 while (i < src->len && i < dst->len)
750 guchar *a = (guchar *) src->pdata[i];
751 guchar *b = (guchar *) dst->pdata[i];
753 if (strcmp(a, b) != 0)
754 break;
755 i++;
758 /* Go up one dir for each element remaining in src */
759 path = g_string_new(NULL);
760 for (j = i; j < src->len; j++)
761 g_string_append(path, "../");
763 /* Go down one dir for each element remaining in dst */
764 for (j = i; j < dst->len; j++)
766 g_string_append(path, (guchar *) dst->pdata[j]);
767 g_string_append_c(path, '/');
770 if (path->str[path->len - 1] == '/')
771 g_string_truncate(path, path->len - 1);
772 if (path->len == 0)
773 g_string_assign(path, ".");
775 /* Free the arrays */
776 for (i = 0; i < src->len; i++)
777 g_free(src->pdata[i]);
778 g_ptr_array_free(src, TRUE);
779 for (i = 0; i < dst->len; i++)
780 g_free(dst->pdata[i]);
781 g_ptr_array_free(dst, TRUE);
783 retval = path->str;
784 g_string_free(path, FALSE);
786 return retval;
789 /* Called before gtk_init(). Override default styles with our defaults,
790 * and override them with user choices, if any.
792 void add_default_styles(void)
794 gchar *rc_file;
796 rc_file = g_strconcat(app_dir, "/Styles", NULL);
797 gtk_rc_add_default_file(rc_file);
798 g_free(rc_file);
800 rc_file = choices_find_path_load("Styles", "ROX-Filer");
801 if (rc_file)
803 gtk_rc_add_default_file(rc_file);
804 g_free(rc_file);