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.
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/>.
35 * \brief Source: various utilities - Unix variant
49 #ifdef HAVE_SYS_PARAM_H
50 #include <sys/param.h>
52 #include <sys/types.h>
55 #ifdef HAVE_SYS_IOCTL_H
56 #include <sys/ioctl.h>
58 #ifdef HAVE_GET_PROCESS_STATS
59 #include <sys/procstats.h>
65 #include "lib/global.h"
66 #include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
67 #include "lib/strutil.h" /* str_move() */
69 #include "lib/widget.h" /* message() */
70 #include "lib/vfs/xdirentry.h"
73 #include "lib/charsets.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 ****************************************************************/
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 /* --------------------------------------------------------------------------------------------- */
125 i_cache_match (int id
, int_cache
* cache
, int size
)
129 for (i
= 0; i
< size
; i
++)
130 if (cache
[i
].index
== id
)
131 return cache
[i
].string
;
135 /* --------------------------------------------------------------------------------------------- */
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
157 fprintf (stderr
, "\n\nfork () = -1\n");
168 if (waitpid (pid
, &status
, 0) > 0)
169 return WEXITSTATUS (status
) == 0 ? FORK_PARENT
: FORK_ERROR
;
176 /* --------------------------------------------------------------------------------------------- */
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
);
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 /* --------------------------------------------------------------------------------------------- */
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 /* --------------------------------------------------------------------------------------------- */
213 static char ibuf
[10];
217 name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
);
221 pwd
= getpwuid (uid
);
224 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
229 g_snprintf (ibuf
, sizeof (ibuf
), "%d", uid
);
234 /* --------------------------------------------------------------------------------------------- */
240 static char gbuf
[10];
244 name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
);
248 grp
= getgrgid (gid
);
251 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
256 g_snprintf (gbuf
, sizeof (gbuf
), "%d", gid
);
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 */
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
278 * @param status exit code
287 /* --------------------------------------------------------------------------------------------- */
289 * Call external programs.
291 * @parameter flags addition conditions for running external programs.
292 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
293 * Shell (or command) will be found in paths described in PATH variable
294 * (if shell parameter doesn't begin from path delimiter)
295 * @parameter command Command for shell (or first parameter for command, if flags contain EXECUTE_AS_SHELL)
296 * @return 0 if successfull, -1 otherwise
300 my_system (int flags
, const char *shell
, const char *command
)
302 return my_systeml (flags
, shell
, command
, NULL
);
305 /* --------------------------------------------------------------------------------------------- */
307 * Call external programs with various parameters number.
309 * @parameter flags addition conditions for running external programs.
310 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
311 * Shell (or command) will be found in pathes described in PATH variable
312 * (if shell parameter doesn't begin from path delimiter)
313 * @parameter ... Command for shell with addition parameters for shell
314 * (or parameters for command, if flags contain EXECUTE_AS_SHELL).
315 * Should be NULL terminated.
316 * @return 0 if successfull, -1 otherwise
320 my_systeml (int flags
, const char *shell
, ...)
323 GPtrArray
*args_array
;
328 args_array
= g_ptr_array_new ();
330 if ((flags
& EXECUTE_AS_SHELL
) != 0)
332 g_ptr_array_add (args_array
, g_strdup (shell
));
333 g_ptr_array_add (args_array
, g_strdup ("-c"));
334 execute_name
= g_strdup (shell
);
340 shell_token
= shell
!= NULL
? strchr (shell
, ' ') : NULL
;
341 if (shell_token
== NULL
)
342 *execute_name
= g_strdup (shell
);
344 *execute_name
= g_strndup (shell
, (gsize
) (shell_token
- shell
));
345 g_ptr_array_add (args_array
, g_strdup (shell
));
348 va_start (vargs
, shell
);
349 while ((one_arg
= va_arg (vargs
, char *)) != NULL
)
350 g_ptr_array_add (args_array
, g_strdup (one_arg
));
353 g_ptr_array_add (args_array
, NULL
);
354 status
= my_systemv (execute_name
, (char *const *) args_array
->pdata
);
356 g_free (execute_name
);
357 g_ptr_array_free (args_array
, TRUE
);
362 /* --------------------------------------------------------------------------------------------- */
364 * Call external programs with array of strings as parameters.
366 * @parameter command command to run. Command will be found in paths described in PATH variable
367 * (if command parameter doesn't begin from path delimiter)
368 * @parameter argv Array of strings (NULL-terminated) with parameters for command
369 * @return 0 if successfull, -1 otherwise
373 my_systemv (const char *command
, char *const argv
[])
375 my_fork_state_t fork_state
;
377 my_system_sigactions_t sigactions
;
379 my_system__save_sigaction_handlers (&sigactions
);
381 fork_state
= my_fork ();
389 signal (SIGINT
, SIG_DFL
);
390 signal (SIGQUIT
, SIG_DFL
);
391 signal (SIGTSTP
, SIG_DFL
);
392 signal (SIGCHLD
, SIG_DFL
);
394 execvp (command
, argv
);
395 my_exit (127); /* Exec error */
402 my_system__restore_sigaction_handlers (&sigactions
);
407 /* --------------------------------------------------------------------------------------------- */
410 * Perform tilde expansion if possible.
412 * @param directory pointer to the path
414 * @return newly allocated string, even if it's unchanged.
418 tilde_expand (const char *directory
)
420 struct passwd
*passwd
;
424 if (*directory
!= '~')
425 return g_strdup (directory
);
429 /* d = "~" or d = "~/" */
430 if (!(*p
) || (*p
== PATH_SEP
))
432 passwd
= getpwuid (geteuid ());
433 q
= (*p
== PATH_SEP
) ? p
+ 1 : "";
437 q
= strchr (p
, PATH_SEP
);
440 passwd
= getpwnam (p
);
444 name
= g_strndup (p
, q
- p
);
445 passwd
= getpwnam (name
);
451 /* If we can't figure the user name, leave tilde unexpanded */
453 return g_strdup (directory
);
455 return g_strconcat (passwd
->pw_dir
, PATH_SEP_STR
, q
, (char *) NULL
);
458 /* --------------------------------------------------------------------------------------------- */
460 * Creates a pipe to hold standard error for a later analysis.
461 * The pipe can hold 4096 bytes. Make sure no more is written
462 * or a deadlock might occur.
466 open_error_pipe (void)
468 if (pipe (error_pipe
) < 0)
470 message (D_NORMAL
, _("Warning"), _("Pipe failed"));
473 if (old_error
< 0 || close (2) || dup (error_pipe
[1]) != 2)
475 message (D_NORMAL
, _("Warning"), _("Dup failed"));
477 close (error_pipe
[0]);
483 * Settng stderr in nonblocking mode as we close it earlier, than
484 * program stops. We try to read some error at program startup,
485 * but we should not block on it.
487 * TODO: make piped stdin/stderr poll()/select()able to get rid
491 fd_flags
= fcntl (error_pipe
[0], F_GETFL
, NULL
);
494 fd_flags
|= O_NONBLOCK
;
495 if (fcntl (error_pipe
[0], F_SETFL
, fd_flags
) == -1)
497 /* TODO: handle it somehow */
501 /* we never write there */
502 close (error_pipe
[1]);
506 /* --------------------------------------------------------------------------------------------- */
510 * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
511 * @param text is prepended to the error message from the pipe
513 * @return not 0 if an error was displayed
517 close_error_pipe (int error
, const char *text
)
520 char msg
[MAX_PIPE_SIZE
];
524 if (error_pipe
[0] == -1)
527 if (error
< 0 || (error
> 0 && (error
& D_ERROR
) != 0))
530 title
= _("Warning");
533 if (dup2 (old_error
, 2) == -1)
538 message (error
, MSG_ERROR
, _("Error dup'ing old error pipe"));
542 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
- 1);
546 close (error_pipe
[0]);
550 return 0; /* Just ignore error message */
554 return 0; /* Nothing to show */
556 /* Show message from pipe */
557 message (error
, title
, "%s", msg
);
561 /* Show given text and possible message from pipe */
562 message (error
, title
, "%s\n%s", text
, msg
);
567 /* --------------------------------------------------------------------------------------------- */
569 * Canonicalize path, and return a new path. Do everything in place.
570 * The new path differs from path in:
571 * Multiple `/'s are collapsed to a single `/'.
572 * Leading `./'s and trailing `/.'s are removed.
573 * Trailing `/'s are removed.
574 * Non-leading `../'s and trailing `..'s are handled by removing
575 * portions of the path.
576 * Well formed UNC paths are modified only in the local part.
580 custom_canonicalize_pathname (char *path
, CANON_PATH_FLAGS flags
)
584 char *lpath
= path
; /* path without leading UNC part */
585 const size_t url_delim_len
= strlen (VFS_PATH_URL_DELIMITER
);
587 /* Detect and preserve UNC paths: //server/... */
588 if ((flags
& CANON_PATH_GUARDUNC
) && path
[0] == PATH_SEP
&& path
[1] == PATH_SEP
)
591 while (p
[0] && p
[0] != '/')
593 if (p
[0] == '/' && p
> path
+ 2)
597 if (!lpath
[0] || !lpath
[1])
600 if (flags
& CANON_PATH_JOINSLASHES
)
602 /* Collapse multiple slashes */
606 if (p
[0] == PATH_SEP
&& p
[1] == PATH_SEP
&& (p
== lpath
|| *(p
- 1) != ':'))
609 while (*(++s
) == PATH_SEP
);
616 if (flags
& CANON_PATH_JOINSLASHES
)
618 /* Collapse "/./" -> "/" */
622 if (p
[0] == PATH_SEP
&& p
[1] == '.' && p
[2] == PATH_SEP
)
629 if (flags
& CANON_PATH_REMSLASHDOTS
)
631 /* Remove trailing slashes */
632 p
= lpath
+ strlen (lpath
) - 1;
633 while (p
> lpath
&& *p
== PATH_SEP
)
635 if (p
>= lpath
- (url_delim_len
+ 1)
636 && strncmp (p
- url_delim_len
+ 1, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
641 /* Remove leading "./" */
642 if (lpath
[0] == '.' && lpath
[1] == PATH_SEP
)
651 str_move (lpath
, lpath
+ 2);
655 /* Remove trailing "/" or "/." */
656 len
= strlen (lpath
);
659 if (lpath
[len
- 1] == PATH_SEP
660 && (len
< url_delim_len
661 || strncmp (lpath
+ len
- url_delim_len
, VFS_PATH_URL_DELIMITER
,
662 url_delim_len
) != 0))
664 lpath
[len
- 1] = '\0';
668 if (lpath
[len
- 1] == '.' && lpath
[len
- 2] == PATH_SEP
)
677 lpath
[len
- 2] = '\0';
683 if (flags
& CANON_PATH_REMDOUBLEDOTS
)
686 const size_t enc_prefix_len
= strlen (VFS_ENCODING_PREFIX
);
687 #endif /* HAVE_CHARSET */
689 /* Collapse "/.." with the previous part of path */
691 while (p
[0] && p
[1] && p
[2])
693 if ((p
[0] != PATH_SEP
|| p
[1] != '.' || p
[2] != '.') || (p
[3] != PATH_SEP
&& p
[3] != 0))
699 /* search for the previous token */
701 if (s
>= lpath
+ url_delim_len
- 2
702 && strncmp (s
- url_delim_len
+ 2, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
704 s
-= (url_delim_len
- 2);
705 while (s
>= lpath
&& *s
-- != PATH_SEP
);
710 if (s
- url_delim_len
> lpath
711 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
713 char *vfs_prefix
= s
- url_delim_len
;
714 struct vfs_class
*vclass
;
716 while (vfs_prefix
> lpath
&& *--vfs_prefix
!= PATH_SEP
);
717 if (*vfs_prefix
== PATH_SEP
)
719 *(s
- url_delim_len
) = '\0';
721 vclass
= vfs_prefix_to_class (vfs_prefix
);
722 *(s
- url_delim_len
) = *VFS_PATH_URL_DELIMITER
;
726 struct vfs_s_subclass
*sub
= (struct vfs_s_subclass
*) vclass
->data
;
727 if (sub
!= NULL
&& sub
->flags
& VFS_S_REMOTE
)
743 /* If the previous token is "..", we cannot collapse it */
744 if (s
[0] == '.' && s
[1] == '.' && s
+ 2 == p
)
752 if (s
== lpath
&& *s
== PATH_SEP
)
754 /* "/../foo" -> "/foo" */
755 str_move (s
+ 1, p
+ 4);
759 /* "token/../foo" -> "foo" */
761 if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
762 && (is_supported_encoding (s
+ enc_prefix_len
)))
763 /* special case: remove encoding */
766 #endif /* HAVE_CHARSET */
769 p
= (s
> lpath
) ? s
- 1 : s
;
776 /* "token/.." -> "." */
777 if (lpath
[0] != PATH_SEP
)
785 /* "foo/token/.." -> "foo" */
789 else if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
790 && (is_supported_encoding (s
+ enc_prefix_len
)))
792 /* special case: remove encoding */
797 /* search for the previous token */
798 /* s[-1] == PATH_SEP */
800 while (p
>= lpath
&& *p
!= PATH_SEP
)
806 #endif /* HAVE_CHARSET */
809 if (s
>= lpath
+ url_delim_len
810 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
823 /* --------------------------------------------------------------------------------------------- */
826 canonicalize_pathname (char *path
)
828 custom_canonicalize_pathname (path
, CANON_PATH_ALL
);
831 /* --------------------------------------------------------------------------------------------- */
833 #ifdef HAVE_GET_PROCESS_STATS
835 gettimeofday (struct timeval
*tp
, void *tzp
)
837 return get_process_stats (tp
, PS_SELF
, 0, 0);
839 #endif /* HAVE_GET_PROCESS_STATS */
841 /* --------------------------------------------------------------------------------------------- */
843 #ifndef HAVE_REALPATH
845 mc_realpath (const char *path
, char *resolved_path
)
847 char copy_path
[PATH_MAX
];
848 char link_path
[PATH_MAX
];
849 char got_path
[PATH_MAX
];
850 char *new_path
= got_path
;
855 /* Make a copy of the source path since we may need to modify it. */
856 if (strlen (path
) >= PATH_MAX
- 2)
858 errno
= ENAMETOOLONG
;
861 strcpy (copy_path
, path
);
863 max_path
= copy_path
+ PATH_MAX
- 2;
864 /* If it's a relative pathname use getwd for starters. */
868 new_path
= g_get_current_dir ();
869 if (new_path
== NULL
)
871 strcpy (got_path
, "");
875 g_snprintf (got_path
, PATH_MAX
, "%s", new_path
);
880 new_path
+= strlen (got_path
);
881 if (new_path
[-1] != '/')
889 /* Expand each slash-separated pathname component. */
890 while (*path
!= '\0')
892 /* Ignore stray "/". */
901 if (path
[1] == '\0' || path
[1] == '/')
908 if (path
[2] == '\0' || path
[2] == '/')
911 /* Ignore ".." at root. */
912 if (new_path
== got_path
+ 1)
914 /* Handle ".." by backing up. */
915 while ((--new_path
)[-1] != '/');
920 /* Safely copy the next pathname component. */
921 while (*path
!= '\0' && *path
!= '/')
925 errno
= ENAMETOOLONG
;
928 *new_path
++ = *path
++;
931 /* Protect against infinite loops. */
932 if (readlinks
++ > MAXSYMLINKS
)
937 /* See if latest pathname component is a symlink. */
939 n
= readlink (got_path
, link_path
, PATH_MAX
- 1);
942 /* EINVAL means the file exists but isn't a symlink. */
945 /* Make sure it's null terminated. */
947 strcpy (resolved_path
, got_path
);
953 /* Note: readlink doesn't add the null byte. */
955 if (*link_path
== '/')
956 /* Start over for an absolute symlink. */
959 /* Otherwise back up over this component. */
960 while (*(--new_path
) != '/');
961 /* Safe sex check. */
962 if (strlen (path
) + n
>= PATH_MAX
- 2)
964 errno
= ENAMETOOLONG
;
967 /* Insert symlink contents into path. */
968 strcat (link_path
, path
);
969 strcpy (copy_path
, link_path
);
975 /* Delete trailing slash but don't whomp a lone slash. */
976 if (new_path
!= got_path
+ 1 && new_path
[-1] == '/')
978 /* Make sure it's null terminated. */
980 strcpy (resolved_path
, got_path
);
981 return resolved_path
;
983 #endif /* HAVE_REALPATH */
985 /* --------------------------------------------------------------------------------------------- */
987 * Return the index of the permissions triplet
992 get_user_permissions (struct stat
*st
)
994 static gboolean initialized
= FALSE
;
995 static gid_t
*groups
;
1004 ngroups
= getgroups (0, NULL
);
1006 ngroups
= 0; /* ignore errors */
1008 /* allocate space for one element in addition to what
1009 * will be filled by getgroups(). */
1010 groups
= g_new (gid_t
, ngroups
+ 1);
1014 ngroups
= getgroups (ngroups
, groups
);
1016 ngroups
= 0; /* ignore errors */
1019 /* getgroups() may or may not return the effective group ID,
1020 * so we always include it at the end of the list. */
1021 groups
[ngroups
++] = getegid ();
1026 if (st
->st_uid
== uid
|| uid
== 0)
1029 for (i
= 0; i
< ngroups
; i
++)
1031 if (st
->st_gid
== groups
[i
])
1038 /* --------------------------------------------------------------------------------------------- */
1040 * Build filename from arguments.
1041 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1045 mc_build_filenamev (const char *first_element
, va_list args
)
1048 const char *element
= first_element
;
1052 if (element
== NULL
)
1055 path
= g_string_new ("");
1057 absolute
= (*first_element
!= '\0' && *first_element
== PATH_SEP
);
1061 if (*element
== '\0')
1062 element
= va_arg (args
, char *);
1069 tmp_element
= g_strdup (element
);
1071 element
= va_arg (args
, char *);
1073 canonicalize_pathname (tmp_element
);
1074 len
= strlen (tmp_element
);
1075 start
= (tmp_element
[0] == PATH_SEP
) ? tmp_element
+ 1 : tmp_element
;
1077 g_string_append (path
, start
);
1078 if (tmp_element
[len
- 1] != PATH_SEP
&& element
!= NULL
)
1079 g_string_append_c (path
, PATH_SEP
);
1081 g_free (tmp_element
);
1084 while (element
!= NULL
);
1087 g_string_prepend_c (path
, PATH_SEP
);
1089 ret
= g_string_free (path
, FALSE
);
1090 canonicalize_pathname (ret
);
1095 /* --------------------------------------------------------------------------------------------- */
1097 * Build filename from arguments.
1098 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1102 mc_build_filename (const char *first_element
, ...)
1107 if (first_element
== NULL
)
1110 va_start (args
, first_element
);
1111 ret
= mc_build_filenamev (first_element
, args
);
1116 /* --------------------------------------------------------------------------------------------- */