r496: Many internal changes to the Options system.
[rox-filer.git] / ROX-Filer / src / support.c
blob3a8720dfe0004451abf7b105d432928665925224
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 /* 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 "main.h"
43 #include "options.h"
44 #include "support.h"
45 #include "my_vfs.h"
47 static GHashTable *uid_hash = NULL; /* UID -> User name */
48 static GHashTable *gid_hash = NULL; /* GID -> Group name */
50 /* Static prototypes */
53 /* Like g_strdup, but does realpath() too (if possible) */
54 char *pathdup(char *path)
56 char real[MAXPATHLEN];
58 g_return_val_if_fail(path != NULL, NULL);
60 if (realpath(path, real))
61 return g_strdup(real);
63 return g_strdup(path);
66 /* Join the path to the leaf (adding a / between them) and
67 * return a pointer to a buffer with the result. Buffer is valid until
68 * the next call to make_path.
70 GString *make_path(char *dir, char *leaf)
72 static GString *buffer = NULL;
74 if (!buffer)
75 buffer = g_string_new(NULL);
77 g_return_val_if_fail(dir != NULL, buffer);
78 g_return_val_if_fail(leaf != NULL, buffer);
80 g_string_sprintf(buffer, "%s%s%s",
81 dir,
82 dir[0] == '/' && dir[1] == '\0' ? "" : "/",
83 leaf);
85 return buffer;
88 /* Return our complete host name */
89 char *our_host_name(void)
91 static char *name = NULL;
93 if (option_get_int("dnd_no_hostnames"))
94 return "";
96 if (!name)
98 char buffer[4096];
100 if (gethostname(buffer, 4096) == 0)
102 /* gethostname doesn't always return the full name... */
103 struct hostent *ent;
105 buffer[4095] = '\0';
106 ent = gethostbyname(buffer);
107 name = g_strdup(ent ? ent->h_name : buffer);
109 else
111 g_warning("gethostname() failed - using localhost\n");
112 name = g_strdup("localhost");
116 return name;
119 /* fork() and run a new program.
120 * Returns the new PID, or 0 on failure.
122 pid_t spawn(char **argv)
124 return spawn_full(argv, NULL);
127 /* As spawn(), but cd to dir first (if dir is non-NULL) */
128 pid_t spawn_full(char **argv, char *dir)
130 int child;
132 child = fork();
134 if (child == -1)
135 return 0; /* Failure */
136 else if (child == 0)
138 /* We are the child process */
139 if (dir)
140 if (chdir(dir))
141 fprintf(stderr, "chdir() failed: %s\n",
142 g_strerror(errno));
143 execvp(argv[0], argv);
144 fprintf(stderr, "execvp(%s, ...) failed: %s\n",
145 argv[0],
146 g_strerror(errno));
147 _exit(0);
150 /* We are the parent */
151 return child;
154 void debug_free_string(void *data)
156 g_print("Freeing string '%s'\n", (char *) data);
157 g_free(data);
160 char *user_name(uid_t uid)
162 char *retval;
164 if (!uid_hash)
165 uid_hash = g_hash_table_new(NULL, NULL);
167 retval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(uid));
169 if (!retval)
171 struct passwd *passwd;
173 passwd = getpwuid(uid);
174 retval = passwd ? g_strdup(passwd->pw_name)
175 : g_strdup_printf("[%d]", (int) uid);
176 g_hash_table_insert(uid_hash, GINT_TO_POINTER(uid), retval);
179 return retval;
182 char *group_name(gid_t gid)
184 char *retval;
186 if (!gid_hash)
187 gid_hash = g_hash_table_new(NULL, NULL);
189 retval = g_hash_table_lookup(gid_hash, GINT_TO_POINTER(gid));
191 if (!retval)
193 struct group *group;
195 group = getgrgid(gid);
196 retval = group ? g_strdup(group->gr_name)
197 : g_strdup_printf("[%d]", (int) gid);
198 g_hash_table_insert(gid_hash, GINT_TO_POINTER(gid), retval);
201 return retval;
204 /* Return a string in the form '23Mb' in a static buffer valid until
205 * the next call.
207 char *format_size(unsigned long size)
209 static char *buffer = NULL;
210 char *units;
212 if (size >= PRETTY_SIZE_LIMIT)
214 size += 1023;
215 size >>= 10;
216 if (size >= PRETTY_SIZE_LIMIT)
218 size += 1023;
219 size >>= 10;
220 if (size >= PRETTY_SIZE_LIMIT)
222 size += 1023;
223 size >>= 10;
224 units = "Gb";
226 else
227 units = "Mb";
229 else
230 units = "K";
232 else if (size == 1)
233 units = _("byte");
234 else
235 units = _("bytes");
237 if (buffer)
238 g_free(buffer);
239 buffer = g_strdup_printf("%ld %s", size, units);
241 return buffer;
244 /* Return a string in the form '23Mb' in a static buffer valid until
245 * the next call. Aligned to the right.
247 char *format_size_aligned(unsigned long size)
249 static char *buffer = NULL;
250 char units;
252 if (size >= PRETTY_SIZE_LIMIT)
254 size += 1023;
255 size >>= 10;
256 if (size >= PRETTY_SIZE_LIMIT)
258 size += 1023;
259 size >>= 10;
260 if (size >= PRETTY_SIZE_LIMIT)
262 size += 1023;
263 size >>= 10;
264 units = 'G';
266 else
267 units = 'M';
269 else
270 units = 'K';
272 else
273 units = ' ';
275 if (buffer)
276 g_free(buffer);
277 buffer = g_strdup_printf("%4ld%c", size, units);
279 return buffer;
282 /* Fork and exec argv. Wait and return the child's exit status.
283 * -1 if spawn fails.
285 int fork_exec_wait(char **argv)
287 pid_t child;
288 int status = -1;
290 child = spawn_full(argv, NULL);
292 while (child)
294 if (waitpid(child, &status, 0) == -1)
296 if (errno != EINTR)
297 return -1;
299 else
300 break;
303 return status;
306 /* If a file has this UID and GID, which permissions apply to us?
307 * 0 = User, 1 = Group, 2 = World
309 gint applicable(uid_t uid, gid_t gid)
311 int i;
313 if (uid == euid)
314 return 0;
316 if (gid == egid)
317 return 1;
319 for (i = 0; i < ngroups; i++)
321 if (supplemental_groups[i] == gid)
322 return 1;
325 return 2;
328 /* Converts a file's mode to a string. Result is a pointer
329 * to a static buffer, valid until the next call.
331 char *pretty_permissions(mode_t m)
333 static char buffer[] = "rwx,rwx,rwx/UGT";
335 buffer[0] = m & S_IRUSR ? 'r' : '-';
336 buffer[1] = m & S_IWUSR ? 'w' : '-';
337 buffer[2] = m & S_IXUSR ? 'x' : '-';
339 buffer[4] = m & S_IRGRP ? 'r' : '-';
340 buffer[5] = m & S_IWGRP ? 'w' : '-';
341 buffer[6] = m & S_IXGRP ? 'x' : '-';
343 buffer[8] = m & S_IROTH ? 'r' : '-';
344 buffer[9] = m & S_IWOTH ? 'w' : '-';
345 buffer[10] = m & S_IXOTH ? 'x' : '-';
347 buffer[12] = m & S_ISUID ? 'U' : '-';
348 buffer[13] = m & S_ISGID ? 'G' : '-';
349 #ifdef S_ISVTX
350 buffer[14] = m & S_ISVTX ? 'T' : '-';
351 buffer[15] = 0;
352 #else
353 buffer[14] = 0;
354 #endif
356 return buffer;
359 /* Gets the canonical name for address and compares to our_host_name() */
360 static gboolean is_local_address(char *address)
362 struct hostent *ent;
364 ent = gethostbyname(address);
366 return strcmp(our_host_name(), ent ? ent->h_name : address) == 0;
369 /* Convert a URI to a local pathname (or NULL if it isn't local).
370 * The returned pointer points inside the input string.
371 * Possible formats:
372 * /path
373 * ///path
374 * //host/path
375 * file://host/path
377 char *get_local_path(char *uri)
379 if (*uri == '/')
381 char *path, *uri_host;
383 if (uri[1] != '/')
384 return uri; /* Just a local path - no host part */
386 path = strchr(uri + 2, '/');
387 if (!path)
388 return NULL; /* //something */
390 if (path - uri == 2)
391 return path; /* ///path */
393 uri_host = g_strndup(uri + 2, path - uri - 2);
394 if (is_local_address(uri_host))
396 g_free(uri_host);
397 return path; /* //myhost/path */
399 g_free(uri_host);
401 return NULL; /* From a different host */
403 else
405 if (strncasecmp(uri, "file:", 5))
406 return NULL; /* Don't know this format */
408 uri += 5;
410 if (*uri == '/')
411 return get_local_path(uri);
413 return NULL;
417 /* Set the close-on-exec flag for this FD.
418 * TRUE means that an exec()'d process will not get the FD.
420 void close_on_exec(int fd, gboolean close)
422 if (fcntl(fd, F_SETFD, close))
423 g_warning("fcntl() failed: %s\n", g_strerror(errno));
426 void set_blocking(int fd, gboolean blocking)
428 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
429 g_warning("fcntl() failed: %s\n", g_strerror(errno));
432 /* Format this time nicely. The result is a pointer to a static buffer,
433 * valid until the next call.
435 char *pretty_time(time_t *time)
437 static char time_buf[32];
439 if (strftime(time_buf, sizeof(time_buf),
440 TIME_FORMAT, localtime(time)) == 0)
441 time_buf[0]= 0;
443 return time_buf;
446 #ifndef O_NOFOLLOW
447 # define O_NOFOLLOW 0x0
448 #endif
450 #ifdef HAVE_LIBVFS
451 /* Copy data from 'read_fd' FD to 'write_fd' FD until EOF or error.
452 * Returns 0 on success, -1 on error (and sets errno).
454 static int copy_fd(int read_fd, int write_fd)
456 char buffer[4096];
457 int got;
459 while ((got = mc_read(read_fd, buffer, sizeof(buffer))) > 0)
461 int sent = 0;
463 while (sent < got)
465 int c;
467 c = mc_write(write_fd, buffer + sent, got - sent);
468 if (c < 0)
469 return -1;
470 sent += c;
474 return got;
476 #endif
478 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
479 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
480 * do the copy manually using vfs.
482 * Returns an error string, or NULL on success.
484 guchar *copy_file(guchar *from, guchar *to)
486 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
488 #ifdef HAVE_LIBVFS
489 struct stat info;
491 if (lstat(from, &info))
493 int read_fd = -1;
494 int write_fd = -1;
495 int error = 0;
497 if (mc_lstat(from, &info))
498 return g_strerror(errno);
500 /* Regular lstat() can't find it, but mc_lstat() can,
501 * so try reading it with VFS.
504 if (!S_ISREG(info.st_mode))
505 goto err;
507 read_fd = mc_open(from, O_RDONLY);
508 if (read_fd == -1)
509 goto err;
510 write_fd = mc_open(to,
511 O_NOFOLLOW | O_WRONLY | O_CREAT | O_TRUNC,
512 info.st_mode & 0777);
513 if (write_fd == -1)
514 goto err;
516 if (copy_fd(read_fd, write_fd))
517 goto err;
519 /* (yes, the single | is right) */
520 if (mc_close(read_fd) | mc_close(write_fd))
521 return g_strerror(errno);
522 return NULL;
523 err:
524 error = errno;
525 if (read_fd != -1)
526 mc_close(read_fd);
527 if (write_fd != -1)
528 mc_close(write_fd);
529 return error ? g_strerror(error) : _("Copy error");
531 #endif
533 argv[2] = from;
534 argv[3] = to;
536 if (fork_exec_wait(argv))
537 return _("Copy failed");
538 return NULL;
541 /* 'word' has all special characters escaped so that it may be inserted
542 * into a shell command.
543 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
545 guchar *shell_escape(guchar *word)
547 GString *tmp;
548 guchar *retval;
550 tmp = g_string_new(NULL);
552 while (*word)
554 if (strchr(" ?*['\"$~\\|();!`&", *word))
555 g_string_append_c(tmp, '\\');
556 g_string_append_c(tmp, *word);
557 word++;
560 retval = tmp->str;
561 g_string_free(tmp, FALSE);
562 return retval;
565 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
566 * (or the two are the same item/directory).
567 * FALSE if parent doesn't exist.
569 gboolean is_sub_dir(char *sub, char *parent)
571 struct stat parent_info;
573 /* These were pathdup()s, but that follows symlinks!
574 * I guess they were here for a reason - something is probably
575 * broken now...
577 sub = g_strdup(sub);
579 if (mc_stat(parent, &parent_info))
580 return FALSE; /* Parent doesn't exist */
582 while (1)
584 char *slash;
585 struct stat info;
587 if (mc_stat(sub, &info) == 0)
589 if (info.st_dev == parent_info.st_dev &&
590 info.st_ino == parent_info.st_ino)
592 g_free(sub);
593 return TRUE;
597 slash = strrchr(sub, '/');
598 if (!slash)
599 break;
600 if (slash == sub)
602 if (sub[1])
603 sub[1] = '\0';
604 else
605 break;
607 else
608 *slash = '\0';
611 g_free(real_sub);
612 g_free(real_parent);
613 g_free(sub);
615 return retval;
618 /* True if the string 'list' contains 'item'.
619 * Eg ("close", "close, help") -> TRUE
621 gboolean in_list(guchar *item, guchar *list)
623 int len;
625 len = strlen(item);
627 while (*list)
629 if (strncmp(item, list, len) == 0 && !isalpha(list[len]))
630 return TRUE;
631 list = strchr(list, ',');
632 if (!list)
633 return FALSE;
634 while (isspace(*++list))
638 return FALSE;