Ticket #3243: "User menu -> View manual page" doesn't do coloring
[midnight-commander.git] / lib / utilunix.c
blob338bf2de7c740bd4a733273f6b0f367a2415ca05
1 /*
2 Various utilities - Unix variants
4 Copyright (C) 1994-2014
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1994, 1995, 1996
9 Janne Kukonlehto, 1994, 1995, 1996
10 Dugan Porter, 1994, 1995, 1996
11 Jakub Jelinek, 1994, 1995, 1996
12 Mauricio Plaza, 1994, 1995, 1996
14 The mc_realpath routine is mostly from uClibc package, written
15 by Rick Sladkey <jrs@world.std.com>
17 This file is part of the Midnight Commander.
19 The Midnight Commander is free software: you can redistribute it
20 and/or modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation, either version 3 of the License,
22 or (at your option) any later version.
24 The Midnight Commander is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program. If not, see <http://www.gnu.org/licenses/>.
33 /** \file utilunix.c
34 * \brief Source: various utilities - Unix variant
37 #include <config.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #ifdef HAVE_SYS_PARAM_H
49 #include <sys/param.h>
50 #endif
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/wait.h>
54 #ifdef HAVE_SYS_IOCTL_H
55 #include <sys/ioctl.h>
56 #endif
57 #ifdef HAVE_GET_PROCESS_STATS
58 #include <sys/procstats.h>
59 #endif
60 #include <pwd.h>
61 #include <grp.h>
63 #include "lib/global.h"
65 #include "lib/unixcompat.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 memset (&ignore, 0, sizeof (ignore));
184 ignore.sa_handler = SIG_IGN;
185 sigemptyset (&ignore.sa_mask);
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 (STDERR_FILENO);
520 if (old_error < 0 || close (STDERR_FILENO) != 0 || dup (error_pipe[1]) != STDERR_FILENO)
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, STDERR_FILENO) == -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 /* --------------------------------------------------------------------------------------------- */