Add function lib/utilunix.c:my_systemv_flags()
[midnight-commander.git] / lib / utilunix.c
blob78e5827fec9ca039cc826ac22761ebf90a9dd958
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 /* --------------------------------------------------------------------------------------------- */
207 static GPtrArray *
208 my_system_make_arg_array (int flags, const char *shell, char **execute_name)
210 GPtrArray *args_array;
212 args_array = g_ptr_array_new ();
214 if ((flags & EXECUTE_AS_SHELL) != 0)
216 g_ptr_array_add (args_array, g_strdup (shell));
217 g_ptr_array_add (args_array, g_strdup ("-c"));
218 *execute_name = g_strdup (shell);
220 else
222 char *shell_token;
224 shell_token = shell != NULL ? strchr (shell, ' ') : NULL;
225 if (shell_token == NULL)
226 *execute_name = g_strdup (shell);
227 else
228 *execute_name = g_strndup (shell, (gsize) (shell_token - shell));
230 g_ptr_array_add (args_array, g_strdup (shell));
232 return args_array;
235 /* --------------------------------------------------------------------------------------------- */
236 /*** public functions ****************************************************************************/
237 /* --------------------------------------------------------------------------------------------- */
239 char *
240 get_owner (int uid)
242 struct passwd *pwd;
243 static char ibuf[10];
244 char *name;
245 static int uid_last;
247 name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE);
248 if (name != NULL)
249 return name;
251 pwd = getpwuid (uid);
252 if (pwd != NULL)
254 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
255 return pwd->pw_name;
257 else
259 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
260 return ibuf;
264 /* --------------------------------------------------------------------------------------------- */
266 char *
267 get_group (int gid)
269 struct group *grp;
270 static char gbuf[10];
271 char *name;
272 static int gid_last;
274 name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE);
275 if (name != NULL)
276 return name;
278 grp = getgrgid (gid);
279 if (grp != NULL)
281 i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
282 return grp->gr_name;
284 else
286 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
287 return gbuf;
291 /* --------------------------------------------------------------------------------------------- */
292 /* Since ncurses uses a handler that automatically refreshes the */
293 /* screen after a SIGCONT, and we don't want this behavior when */
294 /* spawning a child, we save the original handler here */
296 void
297 save_stop_handler (void)
299 sigaction (SIGTSTP, NULL, &startup_handler);
302 /* --------------------------------------------------------------------------------------------- */
304 * Wrapper for _exit() system call.
305 * The _exit() function has gcc's attribute 'noreturn', and this is reason why we can't
306 * mock the call.
308 * @param status exit code
311 void
312 my_exit (int status)
314 _exit (status);
317 /* --------------------------------------------------------------------------------------------- */
319 * Call external programs.
321 * @parameter flags addition conditions for running external programs.
322 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
323 * Shell (or command) will be found in paths described in PATH variable
324 * (if shell parameter doesn't begin from path delimiter)
325 * @parameter command Command for shell (or first parameter for command, if flags contain EXECUTE_AS_SHELL)
326 * @return 0 if successfull, -1 otherwise
330 my_system (int flags, const char *shell, const char *command)
332 return my_systeml (flags, shell, command, NULL);
335 /* --------------------------------------------------------------------------------------------- */
337 * Call external programs with various parameters number.
339 * @parameter flags addition conditions for running external programs.
340 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
341 * Shell (or command) will be found in pathes described in PATH variable
342 * (if shell parameter doesn't begin from path delimiter)
343 * @parameter ... Command for shell with addition parameters for shell
344 * (or parameters for command, if flags contain EXECUTE_AS_SHELL).
345 * Should be NULL terminated.
346 * @return 0 if successfull, -1 otherwise
351 my_systeml (int flags, const char *shell, ...)
353 GPtrArray *args_array;
354 int status = 0;
355 va_list vargs;
356 char *one_arg;
358 args_array = g_ptr_array_new ();
360 va_start (vargs, shell);
361 while ((one_arg = va_arg (vargs, char *)) != NULL)
362 g_ptr_array_add (args_array, one_arg);
363 va_end (vargs);
365 g_ptr_array_add (args_array, NULL);
366 status = my_systemv_flags (flags, shell, (char *const *) args_array->pdata);
368 g_ptr_array_free (args_array, TRUE);
370 return status;
373 /* --------------------------------------------------------------------------------------------- */
375 * Call external programs with array of strings as parameters.
377 * @parameter command command to run. Command will be found in paths described in PATH variable
378 * (if command parameter doesn't begin from path delimiter)
379 * @parameter argv Array of strings (NULL-terminated) with parameters for command
380 * @return 0 if successfull, -1 otherwise
384 my_systemv (const char *command, char *const argv[])
386 my_fork_state_t fork_state;
387 int status = 0;
388 my_system_sigactions_t sigactions;
390 my_system__save_sigaction_handlers (&sigactions);
392 fork_state = my_fork ();
393 switch (fork_state)
395 case FORK_ERROR:
396 status = -1;
397 break;
398 case FORK_CHILD:
400 signal (SIGINT, SIG_DFL);
401 signal (SIGQUIT, SIG_DFL);
402 signal (SIGTSTP, SIG_DFL);
403 signal (SIGCHLD, SIG_DFL);
405 execvp (command, argv);
406 my_exit (127); /* Exec error */
408 break;
409 default:
410 status = 0;
411 break;
413 my_system__restore_sigaction_handlers (&sigactions);
415 return status;
418 /* --------------------------------------------------------------------------------------------- */
420 * Call external programs with flags and with array of strings as parameters.
422 * @parameter flags addition conditions for running external programs.
423 * @parameter command shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
424 * Shell (or command) will be found in paths described in PATH variable
425 * (if shell parameter doesn't begin from path delimiter)
426 * @parameter argv Array of strings (NULL-terminated) with parameters for command
427 * @return 0 if successfull, -1 otherwise
431 my_systemv_flags (int flags, const char *command, char *const argv[])
433 char *execute_name = NULL;
434 GPtrArray *args_array;
435 int status = 0;
437 args_array = my_system_make_arg_array (flags, command, &execute_name);
439 for (; argv != NULL && *argv != NULL; argv++)
440 g_ptr_array_add (args_array, *argv);
442 g_ptr_array_add (args_array, NULL);
443 status = my_systemv (execute_name, (char *const *) args_array->pdata);
445 g_free (execute_name);
446 g_ptr_array_free (args_array, TRUE);
448 return status;
451 /* --------------------------------------------------------------------------------------------- */
454 * Perform tilde expansion if possible.
456 * @param directory pointer to the path
458 * @return newly allocated string, even if it's unchanged.
461 char *
462 tilde_expand (const char *directory)
464 struct passwd *passwd;
465 const char *p, *q;
466 char *name;
468 if (*directory != '~')
469 return g_strdup (directory);
471 p = directory + 1;
473 /* d = "~" or d = "~/" */
474 if (!(*p) || (*p == PATH_SEP))
476 passwd = getpwuid (geteuid ());
477 q = (*p == PATH_SEP) ? p + 1 : "";
479 else
481 q = strchr (p, PATH_SEP);
482 if (!q)
484 passwd = getpwnam (p);
486 else
488 name = g_strndup (p, q - p);
489 passwd = getpwnam (name);
490 q++;
491 g_free (name);
495 /* If we can't figure the user name, leave tilde unexpanded */
496 if (!passwd)
497 return g_strdup (directory);
499 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
502 /* --------------------------------------------------------------------------------------------- */
504 * Creates a pipe to hold standard error for a later analysis.
505 * The pipe can hold 4096 bytes. Make sure no more is written
506 * or a deadlock might occur.
509 void
510 open_error_pipe (void)
512 if (pipe (error_pipe) < 0)
514 message (D_NORMAL, _("Warning"), _("Pipe failed"));
516 old_error = dup (2);
517 if (old_error < 0 || close (2) || dup (error_pipe[1]) != 2)
519 message (D_NORMAL, _("Warning"), _("Dup failed"));
521 close (error_pipe[0]);
522 error_pipe[0] = -1;
524 else
527 * Settng stderr in nonblocking mode as we close it earlier, than
528 * program stops. We try to read some error at program startup,
529 * but we should not block on it.
531 * TODO: make piped stdin/stderr poll()/select()able to get rid
532 * of following hack.
534 int fd_flags;
535 fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
536 if (fd_flags != -1)
538 fd_flags |= O_NONBLOCK;
539 if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
541 /* TODO: handle it somehow */
545 /* we never write there */
546 close (error_pipe[1]);
547 error_pipe[1] = -1;
550 /* --------------------------------------------------------------------------------------------- */
552 * Close a pipe
554 * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
555 * @param text is prepended to the error message from the pipe
557 * @return not 0 if an error was displayed
561 close_error_pipe (int error, const char *text)
563 const char *title;
564 char msg[MAX_PIPE_SIZE];
565 int len = 0;
567 /* already closed */
568 if (error_pipe[0] == -1)
569 return 0;
571 if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
572 title = MSG_ERROR;
573 else
574 title = _("Warning");
575 if (old_error >= 0)
577 if (dup2 (old_error, 2) == -1)
579 if (error < 0)
580 error = D_ERROR;
582 message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
583 return 1;
585 close (old_error);
586 len = read (error_pipe[0], msg, MAX_PIPE_SIZE - 1);
588 if (len >= 0)
589 msg[len] = 0;
590 close (error_pipe[0]);
591 error_pipe[0] = -1;
593 if (error < 0)
594 return 0; /* Just ignore error message */
595 if (text == NULL)
597 if (len <= 0)
598 return 0; /* Nothing to show */
600 /* Show message from pipe */
601 message (error, title, "%s", msg);
603 else
605 /* Show given text and possible message from pipe */
606 message (error, title, "%s\n%s", text, msg);
608 return 1;
611 /* --------------------------------------------------------------------------------------------- */
613 * Canonicalize path, and return a new path. Do everything in place.
614 * The new path differs from path in:
615 * Multiple `/'s are collapsed to a single `/'.
616 * Leading `./'s and trailing `/.'s are removed.
617 * Trailing `/'s are removed.
618 * Non-leading `../'s and trailing `..'s are handled by removing
619 * portions of the path.
620 * Well formed UNC paths are modified only in the local part.
623 void
624 custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
626 char *p, *s;
627 size_t len;
628 char *lpath = path; /* path without leading UNC part */
629 const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
631 /* Detect and preserve UNC paths: //server/... */
632 if ((flags & CANON_PATH_GUARDUNC) && path[0] == PATH_SEP && path[1] == PATH_SEP)
634 p = path + 2;
635 while (p[0] && p[0] != '/')
636 p++;
637 if (p[0] == '/' && p > path + 2)
638 lpath = p;
641 if (!lpath[0] || !lpath[1])
642 return;
644 if (flags & CANON_PATH_JOINSLASHES)
646 /* Collapse multiple slashes */
647 p = lpath;
648 while (*p)
650 if (p[0] == PATH_SEP && p[1] == PATH_SEP && (p == lpath || *(p - 1) != ':'))
652 s = p + 1;
653 while (*(++s) == PATH_SEP);
654 str_move (p + 1, s);
656 p++;
660 if (flags & CANON_PATH_JOINSLASHES)
662 /* Collapse "/./" -> "/" */
663 p = lpath;
664 while (*p)
666 if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
667 str_move (p, p + 2);
668 else
669 p++;
673 if (flags & CANON_PATH_REMSLASHDOTS)
675 /* Remove trailing slashes */
676 p = lpath + strlen (lpath) - 1;
677 while (p > lpath && *p == PATH_SEP)
679 if (p >= lpath - (url_delim_len + 1)
680 && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
681 break;
682 *p-- = 0;
685 /* Remove leading "./" */
686 if (lpath[0] == '.' && lpath[1] == PATH_SEP)
688 if (lpath[2] == 0)
690 lpath[1] = 0;
691 return;
693 else
695 str_move (lpath, lpath + 2);
699 /* Remove trailing "/" or "/." */
700 len = strlen (lpath);
701 if (len < 2)
702 return;
703 if (lpath[len - 1] == PATH_SEP
704 && (len < url_delim_len
705 || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
706 url_delim_len) != 0))
708 lpath[len - 1] = '\0';
710 else
712 if (lpath[len - 1] == '.' && lpath[len - 2] == PATH_SEP)
714 if (len == 2)
716 lpath[1] = '\0';
717 return;
719 else
721 lpath[len - 2] = '\0';
727 if (flags & CANON_PATH_REMDOUBLEDOTS)
729 #ifdef HAVE_CHARSET
730 const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
731 #endif /* HAVE_CHARSET */
733 /* Collapse "/.." with the previous part of path */
734 p = lpath;
735 while (p[0] && p[1] && p[2])
737 if ((p[0] != PATH_SEP || p[1] != '.' || p[2] != '.') || (p[3] != PATH_SEP && p[3] != 0))
739 p++;
740 continue;
743 /* search for the previous token */
744 s = p - 1;
745 if (s >= lpath + url_delim_len - 2
746 && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
748 s -= (url_delim_len - 2);
749 while (s >= lpath && *s-- != PATH_SEP);
752 while (s >= lpath)
754 if (s - url_delim_len > lpath
755 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
757 char *vfs_prefix = s - url_delim_len;
758 struct vfs_class *vclass;
760 while (vfs_prefix > lpath && *--vfs_prefix != PATH_SEP);
761 if (*vfs_prefix == PATH_SEP)
762 vfs_prefix++;
763 *(s - url_delim_len) = '\0';
765 vclass = vfs_prefix_to_class (vfs_prefix);
766 *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
768 if (vclass != NULL)
770 struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data;
771 if (sub != NULL && sub->flags & VFS_S_REMOTE)
773 s = vfs_prefix;
774 continue;
779 if (*s == PATH_SEP)
780 break;
782 s--;
785 s++;
787 /* If the previous token is "..", we cannot collapse it */
788 if (s[0] == '.' && s[1] == '.' && s + 2 == p)
790 p += 3;
791 continue;
794 if (p[3] != 0)
796 if (s == lpath && *s == PATH_SEP)
798 /* "/../foo" -> "/foo" */
799 str_move (s + 1, p + 4);
801 else
803 /* "token/../foo" -> "foo" */
804 #ifdef HAVE_CHARSET
805 if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
806 && (is_supported_encoding (s + enc_prefix_len)))
807 /* special case: remove encoding */
808 str_move (s, p + 1);
809 else
810 #endif /* HAVE_CHARSET */
811 str_move (s, p + 4);
813 p = (s > lpath) ? s - 1 : s;
814 continue;
817 /* trailing ".." */
818 if (s == lpath)
820 /* "token/.." -> "." */
821 if (lpath[0] != PATH_SEP)
823 lpath[0] = '.';
825 lpath[1] = 0;
827 else
829 /* "foo/token/.." -> "foo" */
830 if (s == lpath + 1)
831 s[0] = 0;
832 #ifdef HAVE_CHARSET
833 else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
834 && (is_supported_encoding (s + enc_prefix_len)))
836 /* special case: remove encoding */
837 s[0] = '.';
838 s[1] = '.';
839 s[2] = '\0';
841 /* search for the previous token */
842 /* s[-1] == PATH_SEP */
843 p = s - 1;
844 while (p >= lpath && *p != PATH_SEP)
845 p--;
847 if (p != NULL)
848 continue;
850 #endif /* HAVE_CHARSET */
851 else
853 if (s >= lpath + url_delim_len
854 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
855 *s = '\0';
856 else
857 s[-1] = '\0';
859 break;
862 break;
867 /* --------------------------------------------------------------------------------------------- */
869 void
870 canonicalize_pathname (char *path)
872 custom_canonicalize_pathname (path, CANON_PATH_ALL);
875 /* --------------------------------------------------------------------------------------------- */
877 #ifdef HAVE_GET_PROCESS_STATS
879 gettimeofday (struct timeval *tp, void *tzp)
881 return get_process_stats (tp, PS_SELF, 0, 0);
883 #endif /* HAVE_GET_PROCESS_STATS */
885 /* --------------------------------------------------------------------------------------------- */
887 #ifndef HAVE_REALPATH
888 char *
889 mc_realpath (const char *path, char *resolved_path)
891 char copy_path[PATH_MAX];
892 char link_path[PATH_MAX];
893 char got_path[PATH_MAX];
894 char *new_path = got_path;
895 char *max_path;
896 int readlinks = 0;
897 int n;
899 /* Make a copy of the source path since we may need to modify it. */
900 if (strlen (path) >= PATH_MAX - 2)
902 errno = ENAMETOOLONG;
903 return NULL;
905 strcpy (copy_path, path);
906 path = copy_path;
907 max_path = copy_path + PATH_MAX - 2;
908 /* If it's a relative pathname use getwd for starters. */
909 if (*path != '/')
912 new_path = g_get_current_dir ();
913 if (new_path == NULL)
915 strcpy (got_path, "");
917 else
919 g_snprintf (got_path, PATH_MAX, "%s", new_path);
920 g_free (new_path);
921 new_path = got_path;
924 new_path += strlen (got_path);
925 if (new_path[-1] != '/')
926 *new_path++ = '/';
928 else
930 *new_path++ = '/';
931 path++;
933 /* Expand each slash-separated pathname component. */
934 while (*path != '\0')
936 /* Ignore stray "/". */
937 if (*path == '/')
939 path++;
940 continue;
942 if (*path == '.')
944 /* Ignore ".". */
945 if (path[1] == '\0' || path[1] == '/')
947 path++;
948 continue;
950 if (path[1] == '.')
952 if (path[2] == '\0' || path[2] == '/')
954 path += 2;
955 /* Ignore ".." at root. */
956 if (new_path == got_path + 1)
957 continue;
958 /* Handle ".." by backing up. */
959 while ((--new_path)[-1] != '/');
960 continue;
964 /* Safely copy the next pathname component. */
965 while (*path != '\0' && *path != '/')
967 if (path > max_path)
969 errno = ENAMETOOLONG;
970 return NULL;
972 *new_path++ = *path++;
974 #ifdef S_IFLNK
975 /* Protect against infinite loops. */
976 if (readlinks++ > MAXSYMLINKS)
978 errno = ELOOP;
979 return NULL;
981 /* See if latest pathname component is a symlink. */
982 *new_path = '\0';
983 n = readlink (got_path, link_path, PATH_MAX - 1);
984 if (n < 0)
986 /* EINVAL means the file exists but isn't a symlink. */
987 if (errno != EINVAL)
989 /* Make sure it's null terminated. */
990 *new_path = '\0';
991 strcpy (resolved_path, got_path);
992 return NULL;
995 else
997 /* Note: readlink doesn't add the null byte. */
998 link_path[n] = '\0';
999 if (*link_path == '/')
1000 /* Start over for an absolute symlink. */
1001 new_path = got_path;
1002 else
1003 /* Otherwise back up over this component. */
1004 while (*(--new_path) != '/');
1005 /* Safe sex check. */
1006 if (strlen (path) + n >= PATH_MAX - 2)
1008 errno = ENAMETOOLONG;
1009 return NULL;
1011 /* Insert symlink contents into path. */
1012 strcat (link_path, path);
1013 strcpy (copy_path, link_path);
1014 path = copy_path;
1016 #endif /* S_IFLNK */
1017 *new_path++ = '/';
1019 /* Delete trailing slash but don't whomp a lone slash. */
1020 if (new_path != got_path + 1 && new_path[-1] == '/')
1021 new_path--;
1022 /* Make sure it's null terminated. */
1023 *new_path = '\0';
1024 strcpy (resolved_path, got_path);
1025 return resolved_path;
1027 #endif /* HAVE_REALPATH */
1029 /* --------------------------------------------------------------------------------------------- */
1031 * Return the index of the permissions triplet
1036 get_user_permissions (struct stat *st)
1038 static gboolean initialized = FALSE;
1039 static gid_t *groups;
1040 static int ngroups;
1041 static uid_t uid;
1042 int i;
1044 if (!initialized)
1046 uid = geteuid ();
1048 ngroups = getgroups (0, NULL);
1049 if (ngroups == -1)
1050 ngroups = 0; /* ignore errors */
1052 /* allocate space for one element in addition to what
1053 * will be filled by getgroups(). */
1054 groups = g_new (gid_t, ngroups + 1);
1056 if (ngroups != 0)
1058 ngroups = getgroups (ngroups, groups);
1059 if (ngroups == -1)
1060 ngroups = 0; /* ignore errors */
1063 /* getgroups() may or may not return the effective group ID,
1064 * so we always include it at the end of the list. */
1065 groups[ngroups++] = getegid ();
1067 initialized = TRUE;
1070 if (st->st_uid == uid || uid == 0)
1071 return 0;
1073 for (i = 0; i < ngroups; i++)
1075 if (st->st_gid == groups[i])
1076 return 1;
1079 return 2;
1082 /* --------------------------------------------------------------------------------------------- */
1084 * Build filename from arguments.
1085 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1088 char *
1089 mc_build_filenamev (const char *first_element, va_list args)
1091 gboolean absolute;
1092 const char *element = first_element;
1093 GString *path;
1094 char *ret;
1096 if (element == NULL)
1097 return NULL;
1099 path = g_string_new ("");
1101 absolute = (*first_element != '\0' && *first_element == PATH_SEP);
1105 if (*element == '\0')
1106 element = va_arg (args, char *);
1107 else
1109 char *tmp_element;
1110 size_t len;
1111 const char *start;
1113 tmp_element = g_strdup (element);
1115 element = va_arg (args, char *);
1117 canonicalize_pathname (tmp_element);
1118 len = strlen (tmp_element);
1119 start = (tmp_element[0] == PATH_SEP) ? tmp_element + 1 : tmp_element;
1121 g_string_append (path, start);
1122 if (tmp_element[len - 1] != PATH_SEP && element != NULL)
1123 g_string_append_c (path, PATH_SEP);
1125 g_free (tmp_element);
1128 while (element != NULL);
1130 if (absolute)
1131 g_string_prepend_c (path, PATH_SEP);
1133 ret = g_string_free (path, FALSE);
1134 canonicalize_pathname (ret);
1136 return ret;
1139 /* --------------------------------------------------------------------------------------------- */
1141 * Build filename from arguments.
1142 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1145 char *
1146 mc_build_filename (const char *first_element, ...)
1148 va_list args;
1149 char *ret;
1151 if (first_element == NULL)
1152 return NULL;
1154 va_start (args, first_element);
1155 ret = mc_build_filenamev (first_element, args);
1156 va_end (args);
1157 return ret;
1160 /* --------------------------------------------------------------------------------------------- */