Include sys/param.h conditionally.
[midnight-commander.git] / lib / utilunix.c
blob909f7acb8aa91e13f12f1da6ba27d2e7cc88e8aa
1 /*
2 Various utilities - Unix variants
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2007, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Miguel de Icaza, 1994, 1995, 1996
10 Janne Kukonlehto, 1994, 1995, 1996
11 Dugan Porter, 1994, 1995, 1996
12 Jakub Jelinek, 1994, 1995, 1996
13 Mauricio Plaza, 1994, 1995, 1996
15 The mc_realpath routine is mostly from uClibc package, written
16 by Rick Sladkey <jrs@world.std.com>
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /** \file utilunix.c
35 * \brief Source: various utilities - Unix variant
38 #include <config.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include <signal.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <fcntl.h>
49 #ifdef HAVE_SYS_PARAM_H
50 #include <sys/param.h>
51 #endif
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/wait.h>
55 #ifdef HAVE_SYS_IOCTL_H
56 #include <sys/ioctl.h>
57 #endif
58 #ifdef HAVE_GET_PROCESS_STATS
59 #include <sys/procstats.h>
60 #endif
61 #include <unistd.h>
62 #include <pwd.h>
63 #include <grp.h>
65 #include "lib/global.h"
66 #include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
67 #include "lib/strutil.h" /* str_move() */
68 #include "lib/util.h"
69 #include "lib/widget.h" /* message() */
70 #include "lib/vfs/xdirentry.h"
72 #ifdef HAVE_CHARSET
73 #include "lib/charsets.h"
74 #endif
76 #include "utilunix.h"
78 /*** global variables ****************************************************************************/
80 struct sigaction startup_handler;
82 /*** file scope macro definitions ****************************************************************/
84 #define UID_CACHE_SIZE 200
85 #define GID_CACHE_SIZE 30
87 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
88 /* More than that would be unportable */
89 #define MAX_PIPE_SIZE 4096
91 /*** file scope type declarations ****************************************************************/
93 typedef struct
95 int index;
96 char *string;
97 } int_cache;
99 /*** file scope variables ************************************************************************/
101 static int_cache uid_cache[UID_CACHE_SIZE];
102 static int_cache gid_cache[GID_CACHE_SIZE];
104 static int error_pipe[2]; /* File descriptors of error pipe */
105 static int old_error; /* File descriptor of old standard error */
107 /*** file scope functions ************************************************************************/
108 /* --------------------------------------------------------------------------------------------- */
110 static char *
111 i_cache_match (int id, int_cache * cache, int size)
113 int i;
115 for (i = 0; i < size; i++)
116 if (cache[i].index == id)
117 return cache[i].string;
118 return 0;
121 /* --------------------------------------------------------------------------------------------- */
123 static void
124 i_cache_add (int id, int_cache * cache, int size, char *text, int *last)
126 g_free (cache[*last].string);
127 cache[*last].string = g_strdup (text);
128 cache[*last].index = id;
129 *last = ((*last) + 1) % size;
132 /* --------------------------------------------------------------------------------------------- */
133 /*** public functions ****************************************************************************/
134 /* --------------------------------------------------------------------------------------------- */
136 char *
137 get_owner (int uid)
139 struct passwd *pwd;
140 static char ibuf[10];
141 char *name;
142 static int uid_last;
144 name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE);
145 if (name != NULL)
146 return name;
148 pwd = getpwuid (uid);
149 if (pwd != NULL)
151 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
152 return pwd->pw_name;
154 else
156 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
157 return ibuf;
161 /* --------------------------------------------------------------------------------------------- */
163 char *
164 get_group (int gid)
166 struct group *grp;
167 static char gbuf[10];
168 char *name;
169 static int gid_last;
171 name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE);
172 if (name != NULL)
173 return name;
175 grp = getgrgid (gid);
176 if (grp != NULL)
178 i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
179 return grp->gr_name;
181 else
183 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
184 return gbuf;
188 /* --------------------------------------------------------------------------------------------- */
189 /* Since ncurses uses a handler that automatically refreshes the */
190 /* screen after a SIGCONT, and we don't want this behavior when */
191 /* spawning a child, we save the original handler here */
193 void
194 save_stop_handler (void)
196 sigaction (SIGTSTP, NULL, &startup_handler);
199 /* --------------------------------------------------------------------------------------------- */
202 my_system (int flags, const char *shell, const char *command)
204 struct sigaction ignore, save_intr, save_quit, save_stop;
205 pid_t pid;
206 int status = 0;
208 ignore.sa_handler = SIG_IGN;
209 sigemptyset (&ignore.sa_mask);
210 ignore.sa_flags = 0;
212 sigaction (SIGINT, &ignore, &save_intr);
213 sigaction (SIGQUIT, &ignore, &save_quit);
215 /* Restore the original SIGTSTP handler, we don't want ncurses' */
216 /* handler messing the screen after the SIGCONT */
217 sigaction (SIGTSTP, &startup_handler, &save_stop);
219 pid = fork ();
220 if (pid < 0)
222 fprintf (stderr, "\n\nfork () = -1\n");
223 status = -1;
225 else if (pid == 0)
227 signal (SIGINT, SIG_DFL);
228 signal (SIGQUIT, SIG_DFL);
229 signal (SIGTSTP, SIG_DFL);
230 signal (SIGCHLD, SIG_DFL);
232 if (flags & EXECUTE_AS_SHELL)
233 execl (shell, shell, "-c", command, (char *) NULL);
234 else
236 gchar **shell_tokens;
237 const gchar *only_cmd;
239 shell_tokens = g_strsplit (shell, " ", 2);
240 if (shell_tokens == NULL)
241 only_cmd = shell;
242 else
243 only_cmd = (*shell_tokens != NULL) ? *shell_tokens : shell;
245 execlp (only_cmd, shell, command, (char *) NULL);
248 execlp will replace current process,
249 therefore no sence in call of g_strfreev().
250 But this keeped for estetic reason :)
252 g_strfreev (shell_tokens);
256 _exit (127); /* Exec error */
258 else
260 while (TRUE)
262 if (waitpid (pid, &status, 0) > 0)
264 status = WEXITSTATUS (status);
265 break;
267 if (errno != EINTR)
269 status = -1;
270 break;
274 sigaction (SIGINT, &save_intr, NULL);
275 sigaction (SIGQUIT, &save_quit, NULL);
276 sigaction (SIGTSTP, &save_stop, NULL);
278 return status;
282 /* --------------------------------------------------------------------------------------------- */
284 * Perform tilde expansion if possible.
285 * Always return a newly allocated string, even if it's unchanged.
288 char *
289 tilde_expand (const char *directory)
291 struct passwd *passwd;
292 const char *p, *q;
293 char *name;
295 if (*directory != '~')
296 return g_strdup (directory);
298 p = directory + 1;
300 /* d = "~" or d = "~/" */
301 if (!(*p) || (*p == PATH_SEP))
303 passwd = getpwuid (geteuid ());
304 q = (*p == PATH_SEP) ? p + 1 : "";
306 else
308 q = strchr (p, PATH_SEP);
309 if (!q)
311 passwd = getpwnam (p);
313 else
315 name = g_strndup (p, q - p);
316 passwd = getpwnam (name);
317 q++;
318 g_free (name);
322 /* If we can't figure the user name, leave tilde unexpanded */
323 if (!passwd)
324 return g_strdup (directory);
326 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
329 /* --------------------------------------------------------------------------------------------- */
331 * Return the directory where mc should keep its temporary files.
332 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}/mc-$USER"
333 * When called the first time, the directory is created if needed.
334 * The first call should be done early, since we are using fprintf()
335 * and not message() to report possible problems.
338 const char *
339 mc_tmpdir (void)
341 static char buffer[64];
342 static const char *tmpdir = NULL;
343 const char *sys_tmp;
344 struct passwd *pwd;
345 struct stat st;
346 const char *error = NULL;
348 /* Check if already correctly initialized */
349 if (tmpdir && lstat (tmpdir, &st) == 0 && S_ISDIR (st.st_mode) &&
350 st.st_uid == getuid () && (st.st_mode & 0777) == 0700)
351 return tmpdir;
353 sys_tmp = getenv ("TMPDIR");
354 if (!sys_tmp || sys_tmp[0] != '/')
356 sys_tmp = TMPDIR_DEFAULT;
359 pwd = getpwuid (getuid ());
361 if (pwd)
362 g_snprintf (buffer, sizeof (buffer), "%s/mc-%s", sys_tmp, pwd->pw_name);
363 else
364 g_snprintf (buffer, sizeof (buffer), "%s/mc-%lu", sys_tmp, (unsigned long) getuid ());
366 canonicalize_pathname (buffer);
368 if (lstat (buffer, &st) == 0)
370 /* Sanity check for existing directory */
371 if (!S_ISDIR (st.st_mode))
372 error = _("%s is not a directory\n");
373 else if (st.st_uid != getuid ())
374 error = _("Directory %s is not owned by you\n");
375 else if (((st.st_mode & 0777) != 0700) && (chmod (buffer, 0700) != 0))
376 error = _("Cannot set correct permissions for directory %s\n");
378 else
380 /* Need to create directory */
381 if (mkdir (buffer, S_IRWXU) != 0)
383 fprintf (stderr,
384 _("Cannot create temporary directory %s: %s\n"),
385 buffer, unix_error_string (errno));
386 error = "";
390 if (error != NULL)
392 int test_fd;
393 char *test_fn, *fallback_prefix;
394 int fallback_ok = 0;
396 if (*error)
397 fprintf (stderr, error, buffer);
399 /* Test if sys_tmp is suitable for temporary files */
400 fallback_prefix = g_strdup_printf ("%s/mctest", sys_tmp);
401 test_fd = mc_mkstemps (&test_fn, fallback_prefix, NULL);
402 g_free (fallback_prefix);
403 if (test_fd != -1)
405 close (test_fd);
406 test_fd = open (test_fn, O_RDONLY);
407 if (test_fd != -1)
409 close (test_fd);
410 unlink (test_fn);
411 fallback_ok = 1;
415 if (fallback_ok)
417 fprintf (stderr, _("Temporary files will be created in %s\n"), sys_tmp);
418 g_snprintf (buffer, sizeof (buffer), "%s", sys_tmp);
419 error = NULL;
421 else
423 fprintf (stderr, _("Temporary files will not be created\n"));
424 g_snprintf (buffer, sizeof (buffer), "%s", "/dev/null/");
427 fprintf (stderr, "%s\n", _("Press any key to continue..."));
428 getc (stdin);
431 tmpdir = buffer;
433 if (!error)
434 g_setenv ("MC_TMPDIR", tmpdir, TRUE);
436 return tmpdir;
439 /* --------------------------------------------------------------------------------------------- */
441 * Creates a pipe to hold standard error for a later analysis.
442 * The pipe can hold 4096 bytes. Make sure no more is written
443 * or a deadlock might occur.
446 void
447 open_error_pipe (void)
449 if (pipe (error_pipe) < 0)
451 message (D_NORMAL, _("Warning"), _("Pipe failed"));
453 old_error = dup (2);
454 if (old_error < 0 || close (2) || dup (error_pipe[1]) != 2)
456 message (D_NORMAL, _("Warning"), _("Dup failed"));
458 close (error_pipe[0]);
459 error_pipe[0] = -1;
461 else
464 * Settng stderr in nonblocking mode as we close it earlier, than
465 * program stops. We try to read some error at program startup,
466 * but we should not block on it.
468 * TODO: make piped stdin/stderr poll()/select()able to get rid
469 * of following hack.
471 int fd_flags;
472 fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
473 if (fd_flags != -1)
475 fd_flags |= O_NONBLOCK;
476 if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
478 /* TODO: handle it somehow */
482 /* we never write there */
483 close (error_pipe[1]);
484 error_pipe[1] = -1;
487 /* --------------------------------------------------------------------------------------------- */
489 * Returns true if an error was displayed
490 * error: -1 - ignore errors, 0 - display warning, 1 - display error
491 * text is prepended to the error message from the pipe
495 close_error_pipe (int error, const char *text)
497 const char *title;
498 char msg[MAX_PIPE_SIZE];
499 int len = 0;
501 /* already closed */
502 if (error_pipe[0] == -1)
503 return 0;
505 if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
506 title = MSG_ERROR;
507 else
508 title = _("Warning");
509 if (old_error >= 0)
511 if (dup2 (old_error, 2) == -1)
513 if (error < 0)
514 error = D_ERROR;
516 message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
517 return 1;
519 close (old_error);
520 len = read (error_pipe[0], msg, MAX_PIPE_SIZE - 1);
522 if (len >= 0)
523 msg[len] = 0;
524 close (error_pipe[0]);
525 error_pipe[0] = -1;
527 if (error < 0)
528 return 0; /* Just ignore error message */
529 if (text == NULL)
531 if (len <= 0)
532 return 0; /* Nothing to show */
534 /* Show message from pipe */
535 message (error, title, "%s", msg);
537 else
539 /* Show given text and possible message from pipe */
540 message (error, title, "%s\n%s", text, msg);
542 return 1;
545 /* --------------------------------------------------------------------------------------------- */
547 * Canonicalize path, and return a new path. Do everything in place.
548 * The new path differs from path in:
549 * Multiple `/'s are collapsed to a single `/'.
550 * Leading `./'s and trailing `/.'s are removed.
551 * Trailing `/'s are removed.
552 * Non-leading `../'s and trailing `..'s are handled by removing
553 * portions of the path.
554 * Well formed UNC paths are modified only in the local part.
557 void
558 custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
560 char *p, *s;
561 size_t len;
562 char *lpath = path; /* path without leading UNC part */
563 const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
565 /* Detect and preserve UNC paths: //server/... */
566 if ((flags & CANON_PATH_GUARDUNC) && path[0] == PATH_SEP && path[1] == PATH_SEP)
568 p = path + 2;
569 while (p[0] && p[0] != '/')
570 p++;
571 if (p[0] == '/' && p > path + 2)
572 lpath = p;
575 if (!lpath[0] || !lpath[1])
576 return;
578 if (flags & CANON_PATH_JOINSLASHES)
580 /* Collapse multiple slashes */
581 p = lpath;
582 while (*p)
584 if (p[0] == PATH_SEP && p[1] == PATH_SEP && (p == lpath || *(p - 1) != ':'))
586 s = p + 1;
587 while (*(++s) == PATH_SEP);
588 str_move (p + 1, s);
590 p++;
594 if (flags & CANON_PATH_JOINSLASHES)
596 /* Collapse "/./" -> "/" */
597 p = lpath;
598 while (*p)
600 if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
601 str_move (p, p + 2);
602 else
603 p++;
607 if (flags & CANON_PATH_REMSLASHDOTS)
609 /* Remove trailing slashes */
610 p = lpath + strlen (lpath) - 1;
611 while (p > lpath && *p == PATH_SEP)
613 if (p >= lpath - (url_delim_len + 1)
614 && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
615 break;
616 *p-- = 0;
619 /* Remove leading "./" */
620 if (lpath[0] == '.' && lpath[1] == PATH_SEP)
622 if (lpath[2] == 0)
624 lpath[1] = 0;
625 return;
627 else
629 str_move (lpath, lpath + 2);
633 /* Remove trailing "/" or "/." */
634 len = strlen (lpath);
635 if (len < 2)
636 return;
637 if (lpath[len - 1] == PATH_SEP
638 && (len < url_delim_len
639 || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
640 url_delim_len) != 0))
642 lpath[len - 1] = '\0';
644 else
646 if (lpath[len - 1] == '.' && lpath[len - 2] == PATH_SEP)
648 if (len == 2)
650 lpath[1] = '\0';
651 return;
653 else
655 lpath[len - 2] = '\0';
661 if (flags & CANON_PATH_REMDOUBLEDOTS)
663 const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
665 /* Collapse "/.." with the previous part of path */
666 p = lpath;
667 while (p[0] && p[1] && p[2])
669 if ((p[0] != PATH_SEP || p[1] != '.' || p[2] != '.') || (p[3] != PATH_SEP && p[3] != 0))
671 p++;
672 continue;
675 /* search for the previous token */
676 s = p - 1;
677 if (s >= lpath + url_delim_len - 2
678 && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
680 s -= (url_delim_len - 2);
681 while (s >= lpath && *s-- != PATH_SEP);
684 while (s >= lpath)
686 if (s - url_delim_len > lpath
687 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
689 char *vfs_prefix = s - url_delim_len;
690 struct vfs_class *vclass;
692 while (vfs_prefix > lpath && *--vfs_prefix != PATH_SEP);
693 if (*vfs_prefix == PATH_SEP)
694 vfs_prefix++;
695 *(s - url_delim_len) = '\0';
697 vclass = vfs_prefix_to_class (vfs_prefix);
698 *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
700 if (vclass != NULL)
702 struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data;
703 if (sub != NULL && sub->flags & VFS_S_REMOTE)
705 s = vfs_prefix;
706 continue;
711 if (*s == PATH_SEP)
712 break;
714 s--;
717 s++;
719 /* If the previous token is "..", we cannot collapse it */
720 if (s[0] == '.' && s[1] == '.' && s + 2 == p)
722 p += 3;
723 continue;
726 if (p[3] != 0)
728 if (s == lpath && *s == PATH_SEP)
730 /* "/../foo" -> "/foo" */
731 str_move (s + 1, p + 4);
733 else
735 /* "token/../foo" -> "foo" */
736 #if HAVE_CHARSET
737 if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
738 && (is_supported_encoding (s + enc_prefix_len)))
739 /* special case: remove encoding */
740 str_move (s, p + 1);
741 else
742 #endif /* HAVE_CHARSET */
743 str_move (s, p + 4);
745 p = (s > lpath) ? s - 1 : s;
746 continue;
749 /* trailing ".." */
750 if (s == lpath)
752 /* "token/.." -> "." */
753 if (lpath[0] != PATH_SEP)
755 lpath[0] = '.';
757 lpath[1] = 0;
759 else
761 /* "foo/token/.." -> "foo" */
762 if (s == lpath + 1)
763 s[0] = 0;
764 #if HAVE_CHARSET
765 else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
766 && (is_supported_encoding (s + enc_prefix_len)))
768 /* special case: remove encoding */
769 s[0] = '.';
770 s[1] = '.';
771 s[2] = '\0';
773 /* search for the previous token */
774 /* s[-1] == PATH_SEP */
775 p = s - 1;
776 while (p >= lpath && *p != PATH_SEP)
777 p--;
779 if (p != NULL)
780 continue;
782 #endif /* HAVE_CHARSET */
783 else
785 if (s >= lpath + url_delim_len
786 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
787 *s = '\0';
788 else
789 s[-1] = '\0';
791 break;
794 break;
799 /* --------------------------------------------------------------------------------------------- */
801 void
802 canonicalize_pathname (char *path)
804 custom_canonicalize_pathname (path, CANON_PATH_ALL);
807 /* --------------------------------------------------------------------------------------------- */
809 #ifdef HAVE_GET_PROCESS_STATS
811 gettimeofday (struct timeval *tp, void *tzp)
813 return get_process_stats (tp, PS_SELF, 0, 0);
815 #endif /* HAVE_GET_PROCESS_STATS */
817 /* --------------------------------------------------------------------------------------------- */
819 #ifndef HAVE_REALPATH
820 char *
821 mc_realpath (const char *path, char *resolved_path)
823 char copy_path[PATH_MAX];
824 char link_path[PATH_MAX];
825 char got_path[PATH_MAX];
826 char *new_path = got_path;
827 char *max_path;
828 int readlinks = 0;
829 int n;
831 /* Make a copy of the source path since we may need to modify it. */
832 if (strlen (path) >= PATH_MAX - 2)
834 errno = ENAMETOOLONG;
835 return NULL;
837 strcpy (copy_path, path);
838 path = copy_path;
839 max_path = copy_path + PATH_MAX - 2;
840 /* If it's a relative pathname use getwd for starters. */
841 if (*path != '/')
844 new_path = g_get_current_dir ();
845 if (new_path == NULL)
847 strcpy (got_path, "");
849 else
851 g_snprintf (got_path, PATH_MAX, "%s", new_path);
852 g_free (new_path);
853 new_path = got_path;
856 new_path += strlen (got_path);
857 if (new_path[-1] != '/')
858 *new_path++ = '/';
860 else
862 *new_path++ = '/';
863 path++;
865 /* Expand each slash-separated pathname component. */
866 while (*path != '\0')
868 /* Ignore stray "/". */
869 if (*path == '/')
871 path++;
872 continue;
874 if (*path == '.')
876 /* Ignore ".". */
877 if (path[1] == '\0' || path[1] == '/')
879 path++;
880 continue;
882 if (path[1] == '.')
884 if (path[2] == '\0' || path[2] == '/')
886 path += 2;
887 /* Ignore ".." at root. */
888 if (new_path == got_path + 1)
889 continue;
890 /* Handle ".." by backing up. */
891 while ((--new_path)[-1] != '/');
892 continue;
896 /* Safely copy the next pathname component. */
897 while (*path != '\0' && *path != '/')
899 if (path > max_path)
901 errno = ENAMETOOLONG;
902 return NULL;
904 *new_path++ = *path++;
906 #ifdef S_IFLNK
907 /* Protect against infinite loops. */
908 if (readlinks++ > MAXSYMLINKS)
910 errno = ELOOP;
911 return NULL;
913 /* See if latest pathname component is a symlink. */
914 *new_path = '\0';
915 n = readlink (got_path, link_path, PATH_MAX - 1);
916 if (n < 0)
918 /* EINVAL means the file exists but isn't a symlink. */
919 if (errno != EINVAL)
921 /* Make sure it's null terminated. */
922 *new_path = '\0';
923 strcpy (resolved_path, got_path);
924 return NULL;
927 else
929 /* Note: readlink doesn't add the null byte. */
930 link_path[n] = '\0';
931 if (*link_path == '/')
932 /* Start over for an absolute symlink. */
933 new_path = got_path;
934 else
935 /* Otherwise back up over this component. */
936 while (*(--new_path) != '/');
937 /* Safe sex check. */
938 if (strlen (path) + n >= PATH_MAX - 2)
940 errno = ENAMETOOLONG;
941 return NULL;
943 /* Insert symlink contents into path. */
944 strcat (link_path, path);
945 strcpy (copy_path, link_path);
946 path = copy_path;
948 #endif /* S_IFLNK */
949 *new_path++ = '/';
951 /* Delete trailing slash but don't whomp a lone slash. */
952 if (new_path != got_path + 1 && new_path[-1] == '/')
953 new_path--;
954 /* Make sure it's null terminated. */
955 *new_path = '\0';
956 strcpy (resolved_path, got_path);
957 return resolved_path;
959 #endif /* HAVE_REALPATH */
961 /* --------------------------------------------------------------------------------------------- */
963 * Return the index of the permissions triplet
968 get_user_permissions (struct stat *st)
970 static gboolean initialized = FALSE;
971 static gid_t *groups;
972 static int ngroups;
973 static uid_t uid;
974 int i;
976 if (!initialized)
978 uid = geteuid ();
980 ngroups = getgroups (0, NULL);
981 if (ngroups == -1)
982 ngroups = 0; /* ignore errors */
984 /* allocate space for one element in addition to what
985 * will be filled by getgroups(). */
986 groups = g_new (gid_t, ngroups + 1);
988 if (ngroups != 0)
990 ngroups = getgroups (ngroups, groups);
991 if (ngroups == -1)
992 ngroups = 0; /* ignore errors */
995 /* getgroups() may or may not return the effective group ID,
996 * so we always include it at the end of the list. */
997 groups[ngroups++] = getegid ();
999 initialized = TRUE;
1002 if (st->st_uid == uid || uid == 0)
1003 return 0;
1005 for (i = 0; i < ngroups; i++)
1007 if (st->st_gid == groups[i])
1008 return 1;
1011 return 2;
1014 /* --------------------------------------------------------------------------------------------- */
1016 * Build filename from arguments.
1017 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1020 char *
1021 mc_build_filename (const char *first_element, ...)
1023 gboolean absolute;
1024 va_list args;
1025 const char *element = first_element;
1026 GString *path;
1027 char *ret;
1029 if (element == NULL)
1030 return NULL;
1032 path = g_string_new ("");
1033 va_start (args, first_element);
1035 absolute = (*first_element != '\0' && *first_element == PATH_SEP);
1039 if (*element == '\0')
1040 element = va_arg (args, char *);
1041 else
1043 char *tmp_element;
1044 size_t len;
1045 const char *start;
1047 tmp_element = g_strdup (element);
1049 element = va_arg (args, char *);
1051 canonicalize_pathname (tmp_element);
1052 len = strlen (tmp_element);
1053 start = (tmp_element[0] == PATH_SEP) ? tmp_element + 1 : tmp_element;
1055 g_string_append (path, start);
1056 if (tmp_element[len - 1] != PATH_SEP && element != NULL)
1057 g_string_append_c (path, PATH_SEP);
1059 g_free (tmp_element);
1062 while (element != NULL);
1064 va_end (args);
1066 if (absolute)
1067 g_string_prepend_c (path, PATH_SEP);
1069 ret = g_string_free (path, FALSE);
1070 canonicalize_pathname (ret);
1072 return ret;
1075 /* --------------------------------------------------------------------------------------------- */