r339: The menu key bindings are now only saved if they actually changed.
[rox-filer.git] / ROX-Filer / src / support.c
blobd6160e729c586ffc467fd5dc5c93d05e169970c7
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 <errno.h>
29 #include <ctype.h>
30 #include <sys/param.h>
31 #include <pwd.h>
32 #include <grp.h>
33 #include <fcntl.h>
34 #include <sys/wait.h>
35 #include <string.h>
36 #include <time.h>
38 #include "global.h"
40 #include "main.h"
41 #include "support.h"
42 #include "my_vfs.h"
44 static GHashTable *uid_hash = NULL; /* UID -> User name */
45 static GHashTable *gid_hash = NULL; /* GID -> Group name */
47 /* Static prototypes */
50 /* Like g_strdup, but does realpath() too (if possible) */
51 char *pathdup(char *path)
53 char real[MAXPATHLEN];
55 g_return_val_if_fail(path != NULL, NULL);
57 if (realpath(path, real))
58 return g_strdup(real);
60 return g_strdup(path);
63 /* Join the path to the leaf (adding a / between them) and
64 * return a pointer to a buffer with the result. Buffer is valid until
65 * the next call to make_path.
67 GString *make_path(char *dir, char *leaf)
69 static GString *buffer = NULL;
71 if (!buffer)
72 buffer = g_string_new(NULL);
74 g_return_val_if_fail(dir != NULL, buffer);
75 g_return_val_if_fail(leaf != NULL, buffer);
77 g_string_sprintf(buffer, "%s%s%s",
78 dir,
79 dir[0] == '/' && dir[1] == '\0' ? "" : "/",
80 leaf);
82 return buffer;
85 /* Return our complete host name */
86 char *our_host_name()
88 static char *name = NULL;
90 if (!name)
92 char buffer[4096];
94 g_return_val_if_fail(gethostname(buffer, 4096) == 0,
95 "localhost");
97 buffer[4095] = '\0';
98 name = g_strdup(buffer);
101 return name;
104 /* fork() and run a new program.
105 * Returns the new PID, or 0 on failure.
107 pid_t spawn(char **argv)
109 return spawn_full(argv, NULL);
112 /* As spawn(), but cd to dir first (if dir is non-NULL) */
113 pid_t spawn_full(char **argv, char *dir)
115 int child;
117 child = fork();
119 if (child == -1)
120 return 0; /* Failure */
121 else if (child == 0)
123 /* We are the child process */
124 if (dir)
125 if (chdir(dir))
126 fprintf(stderr, "chdir() failed: %s\n",
127 g_strerror(errno));
128 dup2(to_error_log, STDERR_FILENO);
129 close_on_exec(STDERR_FILENO, FALSE);
130 execvp(argv[0], argv);
131 fprintf(stderr, "execvp(%s, ...) failed: %s\n",
132 argv[0],
133 g_strerror(errno));
134 _exit(0);
137 /* We are the parent */
138 return child;
141 void debug_free_string(void *data)
143 g_print("Freeing string '%s'\n", (char *) data);
144 g_free(data);
147 char *user_name(uid_t uid)
149 char *retval;
151 if (!uid_hash)
152 uid_hash = g_hash_table_new(NULL, NULL);
154 retval = g_hash_table_lookup(uid_hash, (gpointer) uid);
156 if (!retval)
158 struct passwd *passwd;
160 passwd = getpwuid(uid);
161 retval = passwd ? g_strdup(passwd->pw_name)
162 : g_strdup_printf("[%d]", (int) uid);
163 g_hash_table_insert(uid_hash, (gpointer) uid, retval);
166 return retval;
169 char *group_name(gid_t gid)
171 char *retval;
173 if (!gid_hash)
174 gid_hash = g_hash_table_new(NULL, NULL);
176 retval = g_hash_table_lookup(gid_hash, (gpointer) gid);
178 if (!retval)
180 struct group *group;
182 group = getgrgid(gid);
183 retval = group ? g_strdup(group->gr_name)
184 : g_strdup_printf("[%d]", (int) gid);
185 g_hash_table_insert(gid_hash, (gpointer) gid, retval);
188 return retval;
191 /* Return a string in the form '23Mb' in a static buffer valid until
192 * the next call.
194 char *format_size(unsigned long size)
196 static char *buffer = NULL;
197 char *units;
199 if (size >= PRETTY_SIZE_LIMIT)
201 size += 1023;
202 size >>= 10;
203 if (size >= PRETTY_SIZE_LIMIT)
205 size += 1023;
206 size >>= 10;
207 if (size >= PRETTY_SIZE_LIMIT)
209 size += 1023;
210 size >>= 10;
211 units = "Gb";
213 else
214 units = "Mb";
216 else
217 units = "K";
219 else if (size == 1)
220 units = _("byte");
221 else
222 units = _("bytes");
224 if (buffer)
225 g_free(buffer);
226 buffer = g_strdup_printf("%ld %s", size, units);
228 return buffer;
231 /* Return a string in the form '23Mb' in a static buffer valid until
232 * the next call. Aligned to the right.
234 char *format_size_aligned(unsigned long size)
236 static char *buffer = NULL;
237 char units;
239 if (size >= PRETTY_SIZE_LIMIT)
241 size += 1023;
242 size >>= 10;
243 if (size >= PRETTY_SIZE_LIMIT)
245 size += 1023;
246 size >>= 10;
247 if (size >= PRETTY_SIZE_LIMIT)
249 size += 1023;
250 size >>= 10;
251 units = 'G';
253 else
254 units = 'M';
256 else
257 units = 'K';
259 else
260 units = ' ';
262 if (buffer)
263 g_free(buffer);
264 buffer = g_strdup_printf("%4ld%c", size, units);
266 return buffer;
269 /* Fork and exec argv. Wait and return the child's exit status.
270 * -1 if spawn fails.
272 int fork_exec_wait(char **argv)
274 pid_t child;
275 int status = -1;
277 child = spawn_full(argv, NULL);
279 while (child)
281 if (waitpid(child, &status, 0) == -1)
283 if (errno != EINTR)
284 return -1;
286 else
287 break;
290 return status;
293 /* If a file has this UID and GID, which permissions apply to us?
294 * 0 = User, 1 = Group, 2 = World
296 gint applicable(uid_t uid, gid_t gid)
298 int i;
300 if (uid == euid)
301 return 0;
303 if (gid == egid)
304 return 1;
306 for (i = 0; i < ngroups; i++)
308 if (supplemental_groups[i] == gid)
309 return 1;
312 return 2;
315 /* Converts a file's mode to a string. Result is a pointer
316 * to a static buffer, valid until the next call.
318 char *pretty_permissions(mode_t m)
320 static char buffer[] = "rwx,rwx,rwx/UGT";
322 buffer[0] = m & S_IRUSR ? 'r' : '-';
323 buffer[1] = m & S_IWUSR ? 'w' : '-';
324 buffer[2] = m & S_IXUSR ? 'x' : '-';
326 buffer[4] = m & S_IRGRP ? 'r' : '-';
327 buffer[5] = m & S_IWGRP ? 'w' : '-';
328 buffer[6] = m & S_IXGRP ? 'x' : '-';
330 buffer[8] = m & S_IROTH ? 'r' : '-';
331 buffer[9] = m & S_IWOTH ? 'w' : '-';
332 buffer[10] = m & S_IXOTH ? 'x' : '-';
334 buffer[12] = m & S_ISUID ? 'U' : '-';
335 buffer[13] = m & S_ISGID ? 'G' : '-';
336 #ifdef S_ISVTX
337 buffer[14] = m & S_ISVTX ? 'T' : '-';
338 buffer[15] = 0;
339 #else
340 buffer[14] = 0;
341 #endif
343 return buffer;
346 /* Convert a URI to a local pathname (or NULL if it isn't local).
347 * The returned pointer points inside the input string.
348 * Possible formats:
349 * /path
350 * ///path
351 * //host/path
352 * file://host/path
354 char *get_local_path(char *uri)
356 char *host;
358 host = our_host_name();
360 if (*uri == '/')
362 char *path;
364 if (uri[1] != '/')
365 return uri; /* Just a local path - no host part */
367 path = strchr(uri + 2, '/');
368 if (!path)
369 return NULL; /* //something */
371 if (path - uri == 2)
372 return path; /* ///path */
373 if (strlen(host) == path - uri - 2 &&
374 strncmp(uri + 2, host, path - uri - 2) == 0)
375 return path; /* //myhost/path */
377 return NULL; /* From a different host */
379 else
381 if (strncasecmp(uri, "file:", 5))
382 return NULL; /* Don't know this format */
384 uri += 5;
386 if (*uri == '/')
387 return get_local_path(uri);
389 return NULL;
393 /* Set the close-on-exec flag for this FD.
394 * TRUE means that an exec()'d process will not get the FD.
396 void close_on_exec(int fd, gboolean close)
398 if (fcntl(fd, F_SETFD, close))
399 g_warning("fcntl() failed: %s\n", g_strerror(errno));
402 void set_blocking(int fd, gboolean blocking)
404 if (fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK))
405 g_warning("fcntl() failed: %s\n", g_strerror(errno));
408 /* Format this time nicely. The result is a pointer to a static buffer,
409 * valid until the next call.
411 char *pretty_time(time_t *time)
413 static char time_buf[32];
415 if (strftime(time_buf, sizeof(time_buf),
416 TIME_FORMAT, localtime(time)) == 0)
417 time_buf[0]= 0;
419 return time_buf;
422 #ifndef O_NOFOLLOW
423 # define O_NOFOLLOW 0x0
424 #endif
426 #ifdef HAVE_LIBVFS
427 /* Copy data from 'read_fd' FD to 'write_fd' FD until EOF or error.
428 * Returns 0 on success, -1 on error (and sets errno).
430 static int copy_fd(int read_fd, int write_fd)
432 char buffer[4096];
433 int got;
435 while ((got = mc_read(read_fd, buffer, sizeof(buffer))) > 0)
437 int sent = 0;
439 while (sent < got)
441 int c;
443 c = mc_write(write_fd, buffer + sent, got - sent);
444 if (c < 0)
445 return -1;
446 sent += c;
450 return got;
452 #endif
454 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
455 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
456 * do the copy manually using vfs.
458 * Returns an error string, or NULL on success.
460 guchar *copy_file(guchar *from, guchar *to)
462 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
464 #ifdef HAVE_LIBVFS
465 struct stat info;
467 if (lstat(from, &info))
469 int read_fd = -1;
470 int write_fd = -1;
471 int error = 0;
473 if (mc_lstat(from, &info))
474 return g_strerror(errno);
476 /* Regular lstat() can't find it, but mc_lstat() can,
477 * so try reading it with VFS.
480 if (!S_ISREG(info.st_mode))
481 goto err;
483 read_fd = mc_open(from, O_RDONLY);
484 if (read_fd == -1)
485 goto err;
486 write_fd = mc_open(to,
487 O_NOFOLLOW | O_WRONLY | O_CREAT | O_TRUNC,
488 info.st_mode & 0777);
489 if (write_fd == -1)
490 goto err;
492 if (copy_fd(read_fd, write_fd))
493 goto err;
495 /* (yes, the single | is right) */
496 if (mc_close(read_fd) | mc_close(write_fd))
497 return g_strerror(errno);
498 return NULL;
499 err:
500 error = errno;
501 if (read_fd != -1)
502 mc_close(read_fd);
503 if (write_fd != -1)
504 mc_close(write_fd);
505 return error ? g_strerror(error) : _("Copy error");
507 #endif
509 argv[2] = from;
510 argv[3] = to;
512 if (fork_exec_wait(argv))
513 return _("Copy failed");
514 return NULL;
517 /* 'word' has all special characters escaped so that it may be inserted
518 * into a shell command.
519 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
521 guchar *shell_escape(guchar *word)
523 GString *tmp;
524 guchar *retval;
526 tmp = g_string_new(NULL);
528 while (*word)
530 if (strchr(" ?*['\"$~\\|();!`&", *word))
531 g_string_append_c(tmp, '\\');
532 g_string_append_c(tmp, *word);
533 word++;
536 retval = tmp->str;
537 g_string_free(tmp, FALSE);
538 return retval;