cppcheck: reduce variable scope.
[midnight-commander.git] / lib / utilunix.c
blob715561dce734caa0a3a7354511bc3d7d87997c37
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 char *name;
244 static int uid_last;
246 name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE);
247 if (name != NULL)
248 return name;
250 pwd = getpwuid (uid);
251 if (pwd != NULL)
253 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
254 return pwd->pw_name;
256 else
258 static char ibuf[10];
260 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
261 return ibuf;
265 /* --------------------------------------------------------------------------------------------- */
267 char *
268 get_group (int gid)
270 struct group *grp;
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 static char gbuf[10];
288 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
289 return gbuf;
293 /* --------------------------------------------------------------------------------------------- */
294 /* Since ncurses uses a handler that automatically refreshes the */
295 /* screen after a SIGCONT, and we don't want this behavior when */
296 /* spawning a child, we save the original handler here */
298 void
299 save_stop_handler (void)
301 sigaction (SIGTSTP, NULL, &startup_handler);
304 /* --------------------------------------------------------------------------------------------- */
306 * Wrapper for _exit() system call.
307 * The _exit() function has gcc's attribute 'noreturn', and this is reason why we can't
308 * mock the call.
310 * @param status exit code
313 void
314 my_exit (int status)
316 _exit (status);
319 /* --------------------------------------------------------------------------------------------- */
321 * Call external programs.
323 * @parameter flags addition conditions for running external programs.
324 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
325 * Shell (or command) will be found in paths described in PATH variable
326 * (if shell parameter doesn't begin from path delimiter)
327 * @parameter command Command for shell (or first parameter for command, if flags contain EXECUTE_AS_SHELL)
328 * @return 0 if successfull, -1 otherwise
332 my_system (int flags, const char *shell, const char *command)
334 return my_systeml (flags, shell, command, NULL);
337 /* --------------------------------------------------------------------------------------------- */
339 * Call external programs with various parameters number.
341 * @parameter flags addition conditions for running external programs.
342 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
343 * Shell (or command) will be found in pathes described in PATH variable
344 * (if shell parameter doesn't begin from path delimiter)
345 * @parameter ... Command for shell with addition parameters for shell
346 * (or parameters for command, if flags contain EXECUTE_AS_SHELL).
347 * Should be NULL terminated.
348 * @return 0 if successfull, -1 otherwise
353 my_systeml (int flags, const char *shell, ...)
355 GPtrArray *args_array;
356 int status = 0;
357 va_list vargs;
358 char *one_arg;
360 args_array = g_ptr_array_new ();
362 va_start (vargs, shell);
363 while ((one_arg = va_arg (vargs, char *)) != NULL)
364 g_ptr_array_add (args_array, one_arg);
365 va_end (vargs);
367 g_ptr_array_add (args_array, NULL);
368 status = my_systemv_flags (flags, shell, (char *const *) args_array->pdata);
370 g_ptr_array_free (args_array, TRUE);
372 return status;
375 /* --------------------------------------------------------------------------------------------- */
377 * Call external programs with array of strings as parameters.
379 * @parameter command command to run. Command will be found in paths described in PATH variable
380 * (if command parameter doesn't begin from path delimiter)
381 * @parameter argv Array of strings (NULL-terminated) with parameters for command
382 * @return 0 if successfull, -1 otherwise
386 my_systemv (const char *command, char *const argv[])
388 my_fork_state_t fork_state;
389 int status = 0;
390 my_system_sigactions_t sigactions;
392 my_system__save_sigaction_handlers (&sigactions);
394 fork_state = my_fork ();
395 switch (fork_state)
397 case FORK_ERROR:
398 status = -1;
399 break;
400 case FORK_CHILD:
402 signal (SIGINT, SIG_DFL);
403 signal (SIGQUIT, SIG_DFL);
404 signal (SIGTSTP, SIG_DFL);
405 signal (SIGCHLD, SIG_DFL);
407 execvp (command, argv);
408 my_exit (127); /* Exec error */
410 break;
411 default:
412 status = 0;
413 break;
415 my_system__restore_sigaction_handlers (&sigactions);
417 return status;
420 /* --------------------------------------------------------------------------------------------- */
422 * Call external programs with flags and with array of strings as parameters.
424 * @parameter flags addition conditions for running external programs.
425 * @parameter command shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
426 * Shell (or command) will be found in paths described in PATH variable
427 * (if shell parameter doesn't begin from path delimiter)
428 * @parameter argv Array of strings (NULL-terminated) with parameters for command
429 * @return 0 if successfull, -1 otherwise
433 my_systemv_flags (int flags, const char *command, char *const argv[])
435 char *execute_name = NULL;
436 GPtrArray *args_array;
437 int status = 0;
439 args_array = my_system_make_arg_array (flags, command, &execute_name);
441 for (; argv != NULL && *argv != NULL; argv++)
442 g_ptr_array_add (args_array, *argv);
444 g_ptr_array_add (args_array, NULL);
445 status = my_systemv (execute_name, (char *const *) args_array->pdata);
447 g_free (execute_name);
448 g_ptr_array_free (args_array, TRUE);
450 return status;
453 /* --------------------------------------------------------------------------------------------- */
456 * Perform tilde expansion if possible.
458 * @param directory pointer to the path
460 * @return newly allocated string, even if it's unchanged.
463 char *
464 tilde_expand (const char *directory)
466 struct passwd *passwd;
467 const char *p, *q;
469 if (*directory != '~')
470 return g_strdup (directory);
472 p = directory + 1;
474 /* d = "~" or d = "~/" */
475 if (!(*p) || (*p == PATH_SEP))
477 passwd = getpwuid (geteuid ());
478 q = (*p == PATH_SEP) ? p + 1 : "";
480 else
482 q = strchr (p, PATH_SEP);
483 if (!q)
485 passwd = getpwnam (p);
487 else
489 char *name;
491 name = g_strndup (p, q - p);
492 passwd = getpwnam (name);
493 q++;
494 g_free (name);
498 /* If we can't figure the user name, leave tilde unexpanded */
499 if (!passwd)
500 return g_strdup (directory);
502 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
505 /* --------------------------------------------------------------------------------------------- */
507 * Creates a pipe to hold standard error for a later analysis.
508 * The pipe can hold 4096 bytes. Make sure no more is written
509 * or a deadlock might occur.
512 void
513 open_error_pipe (void)
515 if (pipe (error_pipe) < 0)
517 message (D_NORMAL, _("Warning"), _("Pipe failed"));
519 old_error = dup (2);
520 if (old_error < 0 || close (2) || dup (error_pipe[1]) != 2)
522 message (D_NORMAL, _("Warning"), _("Dup failed"));
524 close (error_pipe[0]);
525 error_pipe[0] = -1;
527 else
530 * Settng stderr in nonblocking mode as we close it earlier, than
531 * program stops. We try to read some error at program startup,
532 * but we should not block on it.
534 * TODO: make piped stdin/stderr poll()/select()able to get rid
535 * of following hack.
537 int fd_flags;
538 fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
539 if (fd_flags != -1)
541 fd_flags |= O_NONBLOCK;
542 if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
544 /* TODO: handle it somehow */
548 /* we never write there */
549 close (error_pipe[1]);
550 error_pipe[1] = -1;
553 /* --------------------------------------------------------------------------------------------- */
555 * Close a pipe
557 * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
558 * @param text is prepended to the error message from the pipe
560 * @return not 0 if an error was displayed
564 close_error_pipe (int error, const char *text)
566 const char *title;
567 char msg[MAX_PIPE_SIZE];
568 int len = 0;
570 /* already closed */
571 if (error_pipe[0] == -1)
572 return 0;
574 if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
575 title = MSG_ERROR;
576 else
577 title = _("Warning");
578 if (old_error >= 0)
580 if (dup2 (old_error, 2) == -1)
582 if (error < 0)
583 error = D_ERROR;
585 message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
586 return 1;
588 close (old_error);
589 len = read (error_pipe[0], msg, MAX_PIPE_SIZE - 1);
591 if (len >= 0)
592 msg[len] = 0;
593 close (error_pipe[0]);
594 error_pipe[0] = -1;
596 if (error < 0)
597 return 0; /* Just ignore error message */
598 if (text == NULL)
600 if (len <= 0)
601 return 0; /* Nothing to show */
603 /* Show message from pipe */
604 message (error, title, "%s", msg);
606 else
608 /* Show given text and possible message from pipe */
609 message (error, title, "%s\n%s", text, msg);
611 return 1;
614 /* --------------------------------------------------------------------------------------------- */
616 * Canonicalize path, and return a new path. Do everything in place.
617 * The new path differs from path in:
618 * Multiple '/'s are collapsed to a single '/'.
619 * Leading './'s and trailing '/.'s are removed.
620 * Trailing '/'s are removed.
621 * Non-leading '../'s and trailing '..'s are handled by removing
622 * portions of the path.
623 * Well formed UNC paths are modified only in the local part.
626 void
627 custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
629 char *p, *s;
630 char *lpath = path; /* path without leading UNC part */
631 const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
633 /* Detect and preserve UNC paths: //server/... */
634 if ((flags & CANON_PATH_GUARDUNC) && path[0] == PATH_SEP && path[1] == PATH_SEP)
636 p = path + 2;
637 while (p[0] && p[0] != '/')
638 p++;
639 if (p[0] == '/' && p > path + 2)
640 lpath = p;
643 if (!lpath[0] || !lpath[1])
644 return;
646 if (flags & CANON_PATH_JOINSLASHES)
648 /* Collapse multiple slashes */
649 p = lpath;
650 while (*p)
652 if (p[0] == PATH_SEP && p[1] == PATH_SEP && (p == lpath || *(p - 1) != ':'))
654 s = p + 1;
655 while (*(++s) == PATH_SEP);
656 str_move (p + 1, s);
658 p++;
662 if (flags & CANON_PATH_JOINSLASHES)
664 /* Collapse "/./" -> "/" */
665 p = lpath;
666 while (*p)
668 if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
669 str_move (p, p + 2);
670 else
671 p++;
675 if (flags & CANON_PATH_REMSLASHDOTS)
677 size_t len;
679 /* Remove trailing slashes */
680 p = lpath + strlen (lpath) - 1;
681 while (p > lpath && *p == PATH_SEP)
683 if (p >= lpath - (url_delim_len + 1)
684 && strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
685 break;
686 *p-- = 0;
689 /* Remove leading "./" */
690 if (lpath[0] == '.' && lpath[1] == PATH_SEP)
692 if (lpath[2] == 0)
694 lpath[1] = 0;
695 return;
697 else
699 str_move (lpath, lpath + 2);
703 /* Remove trailing "/" or "/." */
704 len = strlen (lpath);
705 if (len < 2)
706 return;
707 if (lpath[len - 1] == PATH_SEP
708 && (len < url_delim_len
709 || strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
710 url_delim_len) != 0))
712 lpath[len - 1] = '\0';
714 else
716 if (lpath[len - 1] == '.' && lpath[len - 2] == PATH_SEP)
718 if (len == 2)
720 lpath[1] = '\0';
721 return;
723 else
725 lpath[len - 2] = '\0';
731 if (flags & CANON_PATH_REMDOUBLEDOTS)
733 #ifdef HAVE_CHARSET
734 const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
735 #endif /* HAVE_CHARSET */
737 /* Collapse "/.." with the previous part of path */
738 p = lpath;
739 while (p[0] && p[1] && p[2])
741 if ((p[0] != PATH_SEP || p[1] != '.' || p[2] != '.') || (p[3] != PATH_SEP && p[3] != 0))
743 p++;
744 continue;
747 /* search for the previous token */
748 s = p - 1;
749 if (s >= lpath + url_delim_len - 2
750 && strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
752 s -= (url_delim_len - 2);
753 while (s >= lpath && *s-- != PATH_SEP);
756 while (s >= lpath)
758 if (s - url_delim_len > lpath
759 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
761 char *vfs_prefix = s - url_delim_len;
762 struct vfs_class *vclass;
764 while (vfs_prefix > lpath && *--vfs_prefix != PATH_SEP);
765 if (*vfs_prefix == PATH_SEP)
766 vfs_prefix++;
767 *(s - url_delim_len) = '\0';
769 vclass = vfs_prefix_to_class (vfs_prefix);
770 *(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
772 if (vclass != NULL)
774 struct vfs_s_subclass *sub = (struct vfs_s_subclass *) vclass->data;
775 if (sub != NULL && sub->flags & VFS_S_REMOTE)
777 s = vfs_prefix;
778 continue;
783 if (*s == PATH_SEP)
784 break;
786 s--;
789 s++;
791 /* If the previous token is "..", we cannot collapse it */
792 if (s[0] == '.' && s[1] == '.' && s + 2 == p)
794 p += 3;
795 continue;
798 if (p[3] != 0)
800 if (s == lpath && *s == PATH_SEP)
802 /* "/../foo" -> "/foo" */
803 str_move (s + 1, p + 4);
805 else
807 /* "token/../foo" -> "foo" */
808 #ifdef HAVE_CHARSET
809 if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
810 && (is_supported_encoding (s + enc_prefix_len)))
811 /* special case: remove encoding */
812 str_move (s, p + 1);
813 else
814 #endif /* HAVE_CHARSET */
815 str_move (s, p + 4);
817 p = (s > lpath) ? s - 1 : s;
818 continue;
821 /* trailing ".." */
822 if (s == lpath)
824 /* "token/.." -> "." */
825 if (lpath[0] != PATH_SEP)
827 lpath[0] = '.';
829 lpath[1] = 0;
831 else
833 /* "foo/token/.." -> "foo" */
834 if (s == lpath + 1)
835 s[0] = 0;
836 #ifdef HAVE_CHARSET
837 else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
838 && (is_supported_encoding (s + enc_prefix_len)))
840 /* special case: remove encoding */
841 s[0] = '.';
842 s[1] = '.';
843 s[2] = '\0';
845 /* search for the previous token */
846 /* s[-1] == PATH_SEP */
847 p = s - 1;
848 while (p >= lpath && *p != PATH_SEP)
849 p--;
851 if (p >= lpath)
852 continue;
854 #endif /* HAVE_CHARSET */
855 else
857 if (s >= lpath + url_delim_len
858 && strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
859 *s = '\0';
860 else
861 s[-1] = '\0';
865 break;
870 /* --------------------------------------------------------------------------------------------- */
872 void
873 canonicalize_pathname (char *path)
875 custom_canonicalize_pathname (path, CANON_PATH_ALL);
878 /* --------------------------------------------------------------------------------------------- */
880 #ifdef HAVE_GET_PROCESS_STATS
882 gettimeofday (struct timeval *tp, void *tzp)
884 return get_process_stats (tp, PS_SELF, 0, 0);
886 #endif /* HAVE_GET_PROCESS_STATS */
888 /* --------------------------------------------------------------------------------------------- */
890 #ifndef HAVE_REALPATH
891 char *
892 mc_realpath (const char *path, char *resolved_path)
894 char copy_path[PATH_MAX];
895 char link_path[PATH_MAX];
896 char got_path[PATH_MAX];
897 char *new_path = got_path;
898 char *max_path;
899 int readlinks = 0;
900 int n;
902 /* Make a copy of the source path since we may need to modify it. */
903 if (strlen (path) >= PATH_MAX - 2)
905 errno = ENAMETOOLONG;
906 return NULL;
908 strcpy (copy_path, path);
909 path = copy_path;
910 max_path = copy_path + PATH_MAX - 2;
911 /* If it's a relative pathname use getwd for starters. */
912 if (*path != '/')
915 new_path = g_get_current_dir ();
916 if (new_path == NULL)
918 strcpy (got_path, "");
920 else
922 g_snprintf (got_path, PATH_MAX, "%s", new_path);
923 g_free (new_path);
924 new_path = got_path;
927 new_path += strlen (got_path);
928 if (new_path[-1] != '/')
929 *new_path++ = '/';
931 else
933 *new_path++ = '/';
934 path++;
936 /* Expand each slash-separated pathname component. */
937 while (*path != '\0')
939 /* Ignore stray "/". */
940 if (*path == '/')
942 path++;
943 continue;
945 if (*path == '.')
947 /* Ignore ".". */
948 if (path[1] == '\0' || path[1] == '/')
950 path++;
951 continue;
953 if (path[1] == '.')
955 if (path[2] == '\0' || path[2] == '/')
957 path += 2;
958 /* Ignore ".." at root. */
959 if (new_path == got_path + 1)
960 continue;
961 /* Handle ".." by backing up. */
962 while ((--new_path)[-1] != '/');
963 continue;
967 /* Safely copy the next pathname component. */
968 while (*path != '\0' && *path != '/')
970 if (path > max_path)
972 errno = ENAMETOOLONG;
973 return NULL;
975 *new_path++ = *path++;
977 #ifdef S_IFLNK
978 /* Protect against infinite loops. */
979 if (readlinks++ > MAXSYMLINKS)
981 errno = ELOOP;
982 return NULL;
984 /* See if latest pathname component is a symlink. */
985 *new_path = '\0';
986 n = readlink (got_path, link_path, PATH_MAX - 1);
987 if (n < 0)
989 /* EINVAL means the file exists but isn't a symlink. */
990 if (errno != EINVAL)
992 /* Make sure it's null terminated. */
993 *new_path = '\0';
994 strcpy (resolved_path, got_path);
995 return NULL;
998 else
1000 /* Note: readlink doesn't add the null byte. */
1001 link_path[n] = '\0';
1002 if (*link_path == '/')
1003 /* Start over for an absolute symlink. */
1004 new_path = got_path;
1005 else
1006 /* Otherwise back up over this component. */
1007 while (*(--new_path) != '/');
1008 /* Safe sex check. */
1009 if (strlen (path) + n >= PATH_MAX - 2)
1011 errno = ENAMETOOLONG;
1012 return NULL;
1014 /* Insert symlink contents into path. */
1015 strcat (link_path, path);
1016 strcpy (copy_path, link_path);
1017 path = copy_path;
1019 #endif /* S_IFLNK */
1020 *new_path++ = '/';
1022 /* Delete trailing slash but don't whomp a lone slash. */
1023 if (new_path != got_path + 1 && new_path[-1] == '/')
1024 new_path--;
1025 /* Make sure it's null terminated. */
1026 *new_path = '\0';
1027 strcpy (resolved_path, got_path);
1028 return resolved_path;
1030 #endif /* HAVE_REALPATH */
1032 /* --------------------------------------------------------------------------------------------- */
1034 * Return the index of the permissions triplet
1039 get_user_permissions (struct stat *st)
1041 static gboolean initialized = FALSE;
1042 static gid_t *groups;
1043 static int ngroups;
1044 static uid_t uid;
1045 int i;
1047 if (!initialized)
1049 uid = geteuid ();
1051 ngroups = getgroups (0, NULL);
1052 if (ngroups == -1)
1053 ngroups = 0; /* ignore errors */
1055 /* allocate space for one element in addition to what
1056 * will be filled by getgroups(). */
1057 groups = g_new (gid_t, ngroups + 1);
1059 if (ngroups != 0)
1061 ngroups = getgroups (ngroups, groups);
1062 if (ngroups == -1)
1063 ngroups = 0; /* ignore errors */
1066 /* getgroups() may or may not return the effective group ID,
1067 * so we always include it at the end of the list. */
1068 groups[ngroups++] = getegid ();
1070 initialized = TRUE;
1073 if (st->st_uid == uid || uid == 0)
1074 return 0;
1076 for (i = 0; i < ngroups; i++)
1078 if (st->st_gid == groups[i])
1079 return 1;
1082 return 2;
1085 /* --------------------------------------------------------------------------------------------- */
1087 * Build filename from arguments.
1088 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1091 char *
1092 mc_build_filenamev (const char *first_element, va_list args)
1094 gboolean absolute;
1095 const char *element = first_element;
1096 GString *path;
1097 char *ret;
1099 if (element == NULL)
1100 return NULL;
1102 path = g_string_new ("");
1104 absolute = (*first_element != '\0' && *first_element == PATH_SEP);
1108 if (*element == '\0')
1109 element = va_arg (args, char *);
1110 else
1112 char *tmp_element;
1113 size_t len;
1114 const char *start;
1116 tmp_element = g_strdup (element);
1118 element = va_arg (args, char *);
1120 canonicalize_pathname (tmp_element);
1121 len = strlen (tmp_element);
1122 start = (tmp_element[0] == PATH_SEP) ? tmp_element + 1 : tmp_element;
1124 g_string_append (path, start);
1125 if (tmp_element[len - 1] != PATH_SEP && element != NULL)
1126 g_string_append_c (path, PATH_SEP);
1128 g_free (tmp_element);
1131 while (element != NULL);
1133 if (absolute)
1134 g_string_prepend_c (path, PATH_SEP);
1136 ret = g_string_free (path, FALSE);
1137 canonicalize_pathname (ret);
1139 return ret;
1142 /* --------------------------------------------------------------------------------------------- */
1144 * Build filename from arguments.
1145 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1148 char *
1149 mc_build_filename (const char *first_element, ...)
1151 va_list args;
1152 char *ret;
1154 if (first_element == NULL)
1155 return NULL;
1157 va_start (args, first_element);
1158 ret = mc_build_filenamev (first_element, args);
1159 va_end (args);
1160 return ret;
1163 /* --------------------------------------------------------------------------------------------- */