Code refactoring: my_system was split by few functions.
[midnight-commander.git] / lib / utilunix.c
bloba14f6e57ddfd31b89c1dff1f22d0e5f91d5e5d25
1 /*
2 Various utilities - Unix variants
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2007, 2011, 2012, 2013
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 typedef enum
101 FORK_ERROR = -1,
102 FORK_CHILD,
103 FORK_PARENT,
104 } my_fork_state_t;
106 typedef struct
108 struct sigaction intr;
109 struct sigaction quit;
110 struct sigaction stop;
111 } my_system_sigactions_t;
113 /*** file scope variables ************************************************************************/
115 static int_cache uid_cache[UID_CACHE_SIZE];
116 static int_cache gid_cache[GID_CACHE_SIZE];
118 static int error_pipe[2]; /* File descriptors of error pipe */
119 static int old_error; /* File descriptor of old standard error */
121 /*** file scope functions ************************************************************************/
122 /* --------------------------------------------------------------------------------------------- */
124 static char *
125 i_cache_match (int id, int_cache * cache, int size)
127 int i;
129 for (i = 0; i < size; i++)
130 if (cache[i].index == id)
131 return cache[i].string;
132 return 0;
135 /* --------------------------------------------------------------------------------------------- */
137 static void
138 i_cache_add (int id, int_cache * cache, int size, char *text, int *last)
140 g_free (cache[*last].string);
141 cache[*last].string = g_strdup (text);
142 cache[*last].index = id;
143 *last = ((*last) + 1) % size;
146 /* --------------------------------------------------------------------------------------------- */
148 static my_fork_state_t
149 my_fork (void)
151 pid_t pid;
153 pid = fork ();
155 if (pid < 0)
157 fprintf (stderr, "\n\nfork () = -1\n");
158 return FORK_ERROR;
161 if (pid == 0)
162 return FORK_CHILD;
164 while (TRUE)
166 int status = 0;
168 if (waitpid (pid, &status, 0) > 0)
169 return WEXITSTATUS (status) == 0 ? FORK_PARENT : FORK_ERROR;
171 if (errno != EINTR)
172 return FORK_ERROR;
176 /* --------------------------------------------------------------------------------------------- */
178 static void
179 my_system__save_sigaction_handlers (my_system_sigactions_t * sigactions)
181 struct sigaction ignore;
183 ignore.sa_handler = SIG_IGN;
184 sigemptyset (&ignore.sa_mask);
185 ignore.sa_flags = 0;
187 sigaction (SIGINT, &ignore, &sigactions->intr);
188 sigaction (SIGQUIT, &ignore, &sigactions->quit);
190 /* Restore the original SIGTSTP handler, we don't want ncurses' */
191 /* handler messing the screen after the SIGCONT */
192 sigaction (SIGTSTP, &startup_handler, &sigactions->stop);
195 /* --------------------------------------------------------------------------------------------- */
197 static void
198 my_system__restore_sigaction_handlers (my_system_sigactions_t * sigactions)
200 sigaction (SIGINT, &sigactions->intr, NULL);
201 sigaction (SIGQUIT, &sigactions->quit, NULL);
202 sigaction (SIGTSTP, &sigactions->stop, NULL);
205 /* --------------------------------------------------------------------------------------------- */
206 /*** public functions ****************************************************************************/
207 /* --------------------------------------------------------------------------------------------- */
209 char *
210 get_owner (int uid)
212 struct passwd *pwd;
213 static char ibuf[10];
214 char *name;
215 static int uid_last;
217 name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE);
218 if (name != NULL)
219 return name;
221 pwd = getpwuid (uid);
222 if (pwd != NULL)
224 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
225 return pwd->pw_name;
227 else
229 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
230 return ibuf;
234 /* --------------------------------------------------------------------------------------------- */
236 char *
237 get_group (int gid)
239 struct group *grp;
240 static char gbuf[10];
241 char *name;
242 static int gid_last;
244 name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE);
245 if (name != NULL)
246 return name;
248 grp = getgrgid (gid);
249 if (grp != NULL)
251 i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
252 return grp->gr_name;
254 else
256 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
257 return gbuf;
261 /* --------------------------------------------------------------------------------------------- */
262 /* Since ncurses uses a handler that automatically refreshes the */
263 /* screen after a SIGCONT, and we don't want this behavior when */
264 /* spawning a child, we save the original handler here */
266 void
267 save_stop_handler (void)
269 sigaction (SIGTSTP, NULL, &startup_handler);
272 /* --------------------------------------------------------------------------------------------- */
274 * Wrapper for _exit() system call.
275 * The _exit() function has gcc's attribute 'noreturn', and this is reason why we can't
276 * mock the call.
278 * @param status exit code
281 void
282 my_exit (int status)
284 _exit (status);
287 /* --------------------------------------------------------------------------------------------- */
290 my_system (int flags, const char *shell, const char *command)
292 my_fork_state_t fork_state;
293 int status = 0;
294 my_system_sigactions_t sigactions;
296 my_system__save_sigaction_handlers (&sigactions);
298 fork_state = my_fork ();
299 switch (fork_state)
301 case FORK_ERROR:
302 status = -1;
303 break;
304 case FORK_CHILD:
306 signal (SIGINT, SIG_DFL);
307 signal (SIGQUIT, SIG_DFL);
308 signal (SIGTSTP, SIG_DFL);
309 signal (SIGCHLD, SIG_DFL);
311 if (flags & EXECUTE_AS_SHELL)
312 execl (shell, shell, "-c", command, (char *) NULL);
313 else
315 gchar **shell_tokens;
316 const gchar *only_cmd;
318 shell_tokens = g_strsplit (shell, " ", 2);
319 if (shell_tokens == NULL)
320 only_cmd = shell;
321 else
322 only_cmd = (*shell_tokens != NULL) ? *shell_tokens : shell;
324 execlp (only_cmd, shell, command, (char *) NULL);
327 execlp will replace current process,
328 therefore no sence in call of g_strfreev().
329 But this keeped for estetic reason :)
331 g_strfreev (shell_tokens);
333 my_exit (127); /* Exec error */
335 break;
336 default:
337 status = 0;
338 break;
340 my_system__restore_sigaction_handlers (&sigactions);
342 return status;
345 /* --------------------------------------------------------------------------------------------- */
348 * Perform tilde expansion if possible.
350 * @param directory pointer to the path
352 * @return newly allocated string, even if it's unchanged.
355 char *
356 tilde_expand (const char *directory)
358 struct passwd *passwd;
359 const char *p, *q;
360 char *name;
362 if (*directory != '~')
363 return g_strdup (directory);
365 p = directory + 1;
367 /* d = "~" or d = "~/" */
368 if (!(*p) || (*p == PATH_SEP))
370 passwd = getpwuid (geteuid ());
371 q = (*p == PATH_SEP) ? p + 1 : "";
373 else
375 q = strchr (p, PATH_SEP);
376 if (!q)
378 passwd = getpwnam (p);
380 else
382 name = g_strndup (p, q - p);
383 passwd = getpwnam (name);
384 q++;
385 g_free (name);
389 /* If we can't figure the user name, leave tilde unexpanded */
390 if (!passwd)
391 return g_strdup (directory);
393 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
396 /* --------------------------------------------------------------------------------------------- */
398 * Creates a pipe to hold standard error for a later analysis.
399 * The pipe can hold 4096 bytes. Make sure no more is written
400 * or a deadlock might occur.
403 void
404 open_error_pipe (void)
406 if (pipe (error_pipe) < 0)
408 message (D_NORMAL, _("Warning"), _("Pipe failed"));
410 old_error = dup (2);
411 if (old_error < 0 || close (2) || dup (error_pipe[1]) != 2)
413 message (D_NORMAL, _("Warning"), _("Dup failed"));
415 close (error_pipe[0]);
416 error_pipe[0] = -1;
418 else
421 * Settng stderr in nonblocking mode as we close it earlier, than
422 * program stops. We try to read some error at program startup,
423 * but we should not block on it.
425 * TODO: make piped stdin/stderr poll()/select()able to get rid
426 * of following hack.
428 int fd_flags;
429 fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
430 if (fd_flags != -1)
432 fd_flags |= O_NONBLOCK;
433 if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
435 /* TODO: handle it somehow */
439 /* we never write there */
440 close (error_pipe[1]);
441 error_pipe[1] = -1;
444 /* --------------------------------------------------------------------------------------------- */
446 * Close a pipe
448 * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
449 * @param text is prepended to the error message from the pipe
451 * @return not 0 if an error was displayed
455 close_error_pipe (int error, const char *text)
457 const char *title;
458 char msg[MAX_PIPE_SIZE];
459 int len = 0;
461 /* already closed */
462 if (error_pipe[0] == -1)
463 return 0;
465 if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
466 title = MSG_ERROR;
467 else
468 title = _("Warning");
469 if (old_error >= 0)
471 if (dup2 (old_error, 2) == -1)
473 if (error < 0)
474 error = D_ERROR;
476 message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
477 return 1;
479 close (old_error);
480 len = read (error_pipe[0], msg, MAX_PIPE_SIZE - 1);
482 if (len >= 0)
483 msg[len] = 0;
484 close (error_pipe[0]);
485 error_pipe[0] = -1;
487 if (error < 0)
488 return 0; /* Just ignore error message */
489 if (text == NULL)
491 if (len <= 0)
492 return 0; /* Nothing to show */
494 /* Show message from pipe */
495 message (error, title, "%s", msg);
497 else
499 /* Show given text and possible message from pipe */
500 message (error, title, "%s\n%s", text, msg);
502 return 1;
505 /* --------------------------------------------------------------------------------------------- */
507 * Canonicalize path, and return a new path. Do everything in place.
508 * The new path differs from path in:
509 * Multiple `/'s are collapsed to a single `/'.
510 * Leading `./'s and trailing `/.'s are removed.
511 * Trailing `/'s are removed.
512 * Non-leading `../'s and trailing `..'s are handled by removing
513 * portions of the path.
514 * Well formed UNC paths are modified only in the local part.
517 void
518 custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
520 char *p, *s;
521 size_t len;
522 char *lpath = path; /* path without leading UNC part */
523 const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
525 /* Detect and preserve UNC paths: //server/... */
526 if ((flags & CANON_PATH_GUARDUNC) && path[0] == PATH_SEP && path[1] == PATH_SEP)
528 p = path + 2;
529 while (p[0] && p[0] != '/')
530 p++;
531 if (p[0] == '/' && p > path + 2)
532 lpath = p;
535 if (!lpath[0] || !lpath[1])
536 return;
538 if (flags & CANON_PATH_JOINSLASHES)
540 /* Collapse multiple slashes */
541 p = lpath;
542 while (*p)
544 if (p[0] == PATH_SEP && p[1] == PATH_SEP && (p == lpath || *(p - 1) != ':'))
546 s = p + 1;
547 while (*(++s) == PATH_SEP);
548 str_move (p + 1, s);
550 p++;
554 if (flags & CANON_PATH_JOINSLASHES)
556 /* Collapse "/./" -> "/" */
557 p = lpath;
558 while (*p)
560 if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
561 str_move (p, p + 2);
562 else
563 p++;
567 if (flags & CANON_PATH_REMSLASHDOTS)
569 /* Remove trailing slashes */
570 p = lpath + strlen (lpath) - 1;
571 while (p > lpath && *p == PATH_SEP)
573 if (p >= lpath - (url_delim_len + 1)
574 && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
575 break;
576 *p-- = 0;
579 /* Remove leading "./" */
580 if (lpath[0] == '.' && lpath[1] == PATH_SEP)
582 if (lpath[2] == 0)
584 lpath[1] = 0;
585 return;
587 else
589 str_move (lpath, lpath + 2);
593 /* Remove trailing "/" or "/." */
594 len = strlen (lpath);
595 if (len < 2)
596 return;
597 if (lpath[len - 1] == PATH_SEP
598 && (len < url_delim_len
599 || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
600 url_delim_len) != 0))
602 lpath[len - 1] = '\0';
604 else
606 if (lpath[len - 1] == '.' && lpath[len - 2] == PATH_SEP)
608 if (len == 2)
610 lpath[1] = '\0';
611 return;
613 else
615 lpath[len - 2] = '\0';
621 if (flags & CANON_PATH_REMDOUBLEDOTS)
623 #ifdef HAVE_CHARSET
624 const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
625 #endif /* HAVE_CHARSET */
627 /* Collapse "/.." with the previous part of path */
628 p = lpath;
629 while (p[0] && p[1] && p[2])
631 if ((p[0] != PATH_SEP || p[1] != '.' || p[2] != '.') || (p[3] != PATH_SEP && p[3] != 0))
633 p++;
634 continue;
637 /* search for the previous token */
638 s = p - 1;
639 if (s >= lpath + url_delim_len - 2
640 && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
642 s -= (url_delim_len - 2);
643 while (s >= lpath && *s-- != PATH_SEP);
646 while (s >= lpath)
648 if (s - url_delim_len > lpath
649 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
651 char *vfs_prefix = s - url_delim_len;
652 struct vfs_class *vclass;
654 while (vfs_prefix > lpath && *--vfs_prefix != PATH_SEP);
655 if (*vfs_prefix == PATH_SEP)
656 vfs_prefix++;
657 *(s - url_delim_len) = '\0';
659 vclass = vfs_prefix_to_class (vfs_prefix);
660 *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
662 if (vclass != NULL)
664 struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data;
665 if (sub != NULL && sub->flags & VFS_S_REMOTE)
667 s = vfs_prefix;
668 continue;
673 if (*s == PATH_SEP)
674 break;
676 s--;
679 s++;
681 /* If the previous token is "..", we cannot collapse it */
682 if (s[0] == '.' && s[1] == '.' && s + 2 == p)
684 p += 3;
685 continue;
688 if (p[3] != 0)
690 if (s == lpath && *s == PATH_SEP)
692 /* "/../foo" -> "/foo" */
693 str_move (s + 1, p + 4);
695 else
697 /* "token/../foo" -> "foo" */
698 #ifdef HAVE_CHARSET
699 if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
700 && (is_supported_encoding (s + enc_prefix_len)))
701 /* special case: remove encoding */
702 str_move (s, p + 1);
703 else
704 #endif /* HAVE_CHARSET */
705 str_move (s, p + 4);
707 p = (s > lpath) ? s - 1 : s;
708 continue;
711 /* trailing ".." */
712 if (s == lpath)
714 /* "token/.." -> "." */
715 if (lpath[0] != PATH_SEP)
717 lpath[0] = '.';
719 lpath[1] = 0;
721 else
723 /* "foo/token/.." -> "foo" */
724 if (s == lpath + 1)
725 s[0] = 0;
726 #ifdef HAVE_CHARSET
727 else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
728 && (is_supported_encoding (s + enc_prefix_len)))
730 /* special case: remove encoding */
731 s[0] = '.';
732 s[1] = '.';
733 s[2] = '\0';
735 /* search for the previous token */
736 /* s[-1] == PATH_SEP */
737 p = s - 1;
738 while (p >= lpath && *p != PATH_SEP)
739 p--;
741 if (p != NULL)
742 continue;
744 #endif /* HAVE_CHARSET */
745 else
747 if (s >= lpath + url_delim_len
748 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
749 *s = '\0';
750 else
751 s[-1] = '\0';
753 break;
756 break;
761 /* --------------------------------------------------------------------------------------------- */
763 void
764 canonicalize_pathname (char *path)
766 custom_canonicalize_pathname (path, CANON_PATH_ALL);
769 /* --------------------------------------------------------------------------------------------- */
771 #ifdef HAVE_GET_PROCESS_STATS
773 gettimeofday (struct timeval *tp, void *tzp)
775 return get_process_stats (tp, PS_SELF, 0, 0);
777 #endif /* HAVE_GET_PROCESS_STATS */
779 /* --------------------------------------------------------------------------------------------- */
781 #ifndef HAVE_REALPATH
782 char *
783 mc_realpath (const char *path, char *resolved_path)
785 char copy_path[PATH_MAX];
786 char link_path[PATH_MAX];
787 char got_path[PATH_MAX];
788 char *new_path = got_path;
789 char *max_path;
790 int readlinks = 0;
791 int n;
793 /* Make a copy of the source path since we may need to modify it. */
794 if (strlen (path) >= PATH_MAX - 2)
796 errno = ENAMETOOLONG;
797 return NULL;
799 strcpy (copy_path, path);
800 path = copy_path;
801 max_path = copy_path + PATH_MAX - 2;
802 /* If it's a relative pathname use getwd for starters. */
803 if (*path != '/')
806 new_path = g_get_current_dir ();
807 if (new_path == NULL)
809 strcpy (got_path, "");
811 else
813 g_snprintf (got_path, PATH_MAX, "%s", new_path);
814 g_free (new_path);
815 new_path = got_path;
818 new_path += strlen (got_path);
819 if (new_path[-1] != '/')
820 *new_path++ = '/';
822 else
824 *new_path++ = '/';
825 path++;
827 /* Expand each slash-separated pathname component. */
828 while (*path != '\0')
830 /* Ignore stray "/". */
831 if (*path == '/')
833 path++;
834 continue;
836 if (*path == '.')
838 /* Ignore ".". */
839 if (path[1] == '\0' || path[1] == '/')
841 path++;
842 continue;
844 if (path[1] == '.')
846 if (path[2] == '\0' || path[2] == '/')
848 path += 2;
849 /* Ignore ".." at root. */
850 if (new_path == got_path + 1)
851 continue;
852 /* Handle ".." by backing up. */
853 while ((--new_path)[-1] != '/');
854 continue;
858 /* Safely copy the next pathname component. */
859 while (*path != '\0' && *path != '/')
861 if (path > max_path)
863 errno = ENAMETOOLONG;
864 return NULL;
866 *new_path++ = *path++;
868 #ifdef S_IFLNK
869 /* Protect against infinite loops. */
870 if (readlinks++ > MAXSYMLINKS)
872 errno = ELOOP;
873 return NULL;
875 /* See if latest pathname component is a symlink. */
876 *new_path = '\0';
877 n = readlink (got_path, link_path, PATH_MAX - 1);
878 if (n < 0)
880 /* EINVAL means the file exists but isn't a symlink. */
881 if (errno != EINVAL)
883 /* Make sure it's null terminated. */
884 *new_path = '\0';
885 strcpy (resolved_path, got_path);
886 return NULL;
889 else
891 /* Note: readlink doesn't add the null byte. */
892 link_path[n] = '\0';
893 if (*link_path == '/')
894 /* Start over for an absolute symlink. */
895 new_path = got_path;
896 else
897 /* Otherwise back up over this component. */
898 while (*(--new_path) != '/');
899 /* Safe sex check. */
900 if (strlen (path) + n >= PATH_MAX - 2)
902 errno = ENAMETOOLONG;
903 return NULL;
905 /* Insert symlink contents into path. */
906 strcat (link_path, path);
907 strcpy (copy_path, link_path);
908 path = copy_path;
910 #endif /* S_IFLNK */
911 *new_path++ = '/';
913 /* Delete trailing slash but don't whomp a lone slash. */
914 if (new_path != got_path + 1 && new_path[-1] == '/')
915 new_path--;
916 /* Make sure it's null terminated. */
917 *new_path = '\0';
918 strcpy (resolved_path, got_path);
919 return resolved_path;
921 #endif /* HAVE_REALPATH */
923 /* --------------------------------------------------------------------------------------------- */
925 * Return the index of the permissions triplet
930 get_user_permissions (struct stat *st)
932 static gboolean initialized = FALSE;
933 static gid_t *groups;
934 static int ngroups;
935 static uid_t uid;
936 int i;
938 if (!initialized)
940 uid = geteuid ();
942 ngroups = getgroups (0, NULL);
943 if (ngroups == -1)
944 ngroups = 0; /* ignore errors */
946 /* allocate space for one element in addition to what
947 * will be filled by getgroups(). */
948 groups = g_new (gid_t, ngroups + 1);
950 if (ngroups != 0)
952 ngroups = getgroups (ngroups, groups);
953 if (ngroups == -1)
954 ngroups = 0; /* ignore errors */
957 /* getgroups() may or may not return the effective group ID,
958 * so we always include it at the end of the list. */
959 groups[ngroups++] = getegid ();
961 initialized = TRUE;
964 if (st->st_uid == uid || uid == 0)
965 return 0;
967 for (i = 0; i < ngroups; i++)
969 if (st->st_gid == groups[i])
970 return 1;
973 return 2;
976 /* --------------------------------------------------------------------------------------------- */
978 * Build filename from arguments.
979 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
982 char *
983 mc_build_filenamev (const char *first_element, va_list args)
985 gboolean absolute;
986 const char *element = first_element;
987 GString *path;
988 char *ret;
990 if (element == NULL)
991 return NULL;
993 path = g_string_new ("");
995 absolute = (*first_element != '\0' && *first_element == PATH_SEP);
999 if (*element == '\0')
1000 element = va_arg (args, char *);
1001 else
1003 char *tmp_element;
1004 size_t len;
1005 const char *start;
1007 tmp_element = g_strdup (element);
1009 element = va_arg (args, char *);
1011 canonicalize_pathname (tmp_element);
1012 len = strlen (tmp_element);
1013 start = (tmp_element[0] == PATH_SEP) ? tmp_element + 1 : tmp_element;
1015 g_string_append (path, start);
1016 if (tmp_element[len - 1] != PATH_SEP && element != NULL)
1017 g_string_append_c (path, PATH_SEP);
1019 g_free (tmp_element);
1022 while (element != NULL);
1024 if (absolute)
1025 g_string_prepend_c (path, PATH_SEP);
1027 ret = g_string_free (path, FALSE);
1028 canonicalize_pathname (ret);
1030 return ret;
1033 /* --------------------------------------------------------------------------------------------- */
1035 * Build filename from arguments.
1036 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1039 char *
1040 mc_build_filename (const char *first_element, ...)
1042 va_list args;
1043 char *ret;
1045 if (first_element == NULL)
1046 return NULL;
1048 va_start (args, first_element);
1049 ret = mc_build_filenamev (first_element, args);
1050 va_end (args);
1051 return ret;
1054 /* --------------------------------------------------------------------------------------------- */