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 /* --------------------------------------------------------------------------------------------- */
290 my_system (int flags
, const char *shell
, const char *command
)
292 my_fork_state_t fork_state
;
294 my_system_sigactions_t sigactions
;
296 my_system__save_sigaction_handlers (&sigactions
);
298 fork_state
= my_fork ();
306 signal (SIGINT
, SIG_DFL
);
307 signal (SIGQUIT
, SIG_DFL
);
308 signal (SIGTSTP
, SIG_DFL
);
309 signal (SIGCHLD
, SIG_DFL
);
311 if (flags
& EXECUTE_AS_SHELL
)
312 execl (shell
, shell
, "-c", command
, (char *) NULL
);
315 gchar
**shell_tokens
;
316 const gchar
*only_cmd
;
318 shell_tokens
= g_strsplit (shell
, " ", 2);
319 if (shell_tokens
== NULL
)
322 only_cmd
= (*shell_tokens
!= NULL
) ? *shell_tokens
: shell
;
324 execlp (only_cmd
, shell
, command
, (char *) NULL
);
327 execlp will replace current process,
328 therefore no sence in call of g_strfreev().
329 But this keeped for estetic reason :)
331 g_strfreev (shell_tokens
);
333 my_exit (127); /* Exec error */
340 my_system__restore_sigaction_handlers (&sigactions
);
345 /* --------------------------------------------------------------------------------------------- */
348 * Perform tilde expansion if possible.
350 * @param directory pointer to the path
352 * @return newly allocated string, even if it's unchanged.
356 tilde_expand (const char *directory
)
358 struct passwd
*passwd
;
362 if (*directory
!= '~')
363 return g_strdup (directory
);
367 /* d = "~" or d = "~/" */
368 if (!(*p
) || (*p
== PATH_SEP
))
370 passwd
= getpwuid (geteuid ());
371 q
= (*p
== PATH_SEP
) ? p
+ 1 : "";
375 q
= strchr (p
, PATH_SEP
);
378 passwd
= getpwnam (p
);
382 name
= g_strndup (p
, q
- p
);
383 passwd
= getpwnam (name
);
389 /* If we can't figure the user name, leave tilde unexpanded */
391 return g_strdup (directory
);
393 return g_strconcat (passwd
->pw_dir
, PATH_SEP_STR
, q
, (char *) NULL
);
396 /* --------------------------------------------------------------------------------------------- */
398 * Creates a pipe to hold standard error for a later analysis.
399 * The pipe can hold 4096 bytes. Make sure no more is written
400 * or a deadlock might occur.
404 open_error_pipe (void)
406 if (pipe (error_pipe
) < 0)
408 message (D_NORMAL
, _("Warning"), _("Pipe failed"));
411 if (old_error
< 0 || close (2) || dup (error_pipe
[1]) != 2)
413 message (D_NORMAL
, _("Warning"), _("Dup failed"));
415 close (error_pipe
[0]);
421 * Settng stderr in nonblocking mode as we close it earlier, than
422 * program stops. We try to read some error at program startup,
423 * but we should not block on it.
425 * TODO: make piped stdin/stderr poll()/select()able to get rid
429 fd_flags
= fcntl (error_pipe
[0], F_GETFL
, NULL
);
432 fd_flags
|= O_NONBLOCK
;
433 if (fcntl (error_pipe
[0], F_SETFL
, fd_flags
) == -1)
435 /* TODO: handle it somehow */
439 /* we never write there */
440 close (error_pipe
[1]);
444 /* --------------------------------------------------------------------------------------------- */
448 * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
449 * @param text is prepended to the error message from the pipe
451 * @return not 0 if an error was displayed
455 close_error_pipe (int error
, const char *text
)
458 char msg
[MAX_PIPE_SIZE
];
462 if (error_pipe
[0] == -1)
465 if (error
< 0 || (error
> 0 && (error
& D_ERROR
) != 0))
468 title
= _("Warning");
471 if (dup2 (old_error
, 2) == -1)
476 message (error
, MSG_ERROR
, _("Error dup'ing old error pipe"));
480 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
- 1);
484 close (error_pipe
[0]);
488 return 0; /* Just ignore error message */
492 return 0; /* Nothing to show */
494 /* Show message from pipe */
495 message (error
, title
, "%s", msg
);
499 /* Show given text and possible message from pipe */
500 message (error
, title
, "%s\n%s", text
, msg
);
505 /* --------------------------------------------------------------------------------------------- */
507 * Canonicalize path, and return a new path. Do everything in place.
508 * The new path differs from path in:
509 * Multiple `/'s are collapsed to a single `/'.
510 * Leading `./'s and trailing `/.'s are removed.
511 * Trailing `/'s are removed.
512 * Non-leading `../'s and trailing `..'s are handled by removing
513 * portions of the path.
514 * Well formed UNC paths are modified only in the local part.
518 custom_canonicalize_pathname (char *path
, CANON_PATH_FLAGS flags
)
522 char *lpath
= path
; /* path without leading UNC part */
523 const size_t url_delim_len
= strlen (VFS_PATH_URL_DELIMITER
);
525 /* Detect and preserve UNC paths: //server/... */
526 if ((flags
& CANON_PATH_GUARDUNC
) && path
[0] == PATH_SEP
&& path
[1] == PATH_SEP
)
529 while (p
[0] && p
[0] != '/')
531 if (p
[0] == '/' && p
> path
+ 2)
535 if (!lpath
[0] || !lpath
[1])
538 if (flags
& CANON_PATH_JOINSLASHES
)
540 /* Collapse multiple slashes */
544 if (p
[0] == PATH_SEP
&& p
[1] == PATH_SEP
&& (p
== lpath
|| *(p
- 1) != ':'))
547 while (*(++s
) == PATH_SEP
);
554 if (flags
& CANON_PATH_JOINSLASHES
)
556 /* Collapse "/./" -> "/" */
560 if (p
[0] == PATH_SEP
&& p
[1] == '.' && p
[2] == PATH_SEP
)
567 if (flags
& CANON_PATH_REMSLASHDOTS
)
569 /* Remove trailing slashes */
570 p
= lpath
+ strlen (lpath
) - 1;
571 while (p
> lpath
&& *p
== PATH_SEP
)
573 if (p
>= lpath
- (url_delim_len
+ 1)
574 && strncmp (p
- url_delim_len
+ 1, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
579 /* Remove leading "./" */
580 if (lpath
[0] == '.' && lpath
[1] == PATH_SEP
)
589 str_move (lpath
, lpath
+ 2);
593 /* Remove trailing "/" or "/." */
594 len
= strlen (lpath
);
597 if (lpath
[len
- 1] == PATH_SEP
598 && (len
< url_delim_len
599 || strncmp (lpath
+ len
- url_delim_len
, VFS_PATH_URL_DELIMITER
,
600 url_delim_len
) != 0))
602 lpath
[len
- 1] = '\0';
606 if (lpath
[len
- 1] == '.' && lpath
[len
- 2] == PATH_SEP
)
615 lpath
[len
- 2] = '\0';
621 if (flags
& CANON_PATH_REMDOUBLEDOTS
)
624 const size_t enc_prefix_len
= strlen (VFS_ENCODING_PREFIX
);
625 #endif /* HAVE_CHARSET */
627 /* Collapse "/.." with the previous part of path */
629 while (p
[0] && p
[1] && p
[2])
631 if ((p
[0] != PATH_SEP
|| p
[1] != '.' || p
[2] != '.') || (p
[3] != PATH_SEP
&& p
[3] != 0))
637 /* search for the previous token */
639 if (s
>= lpath
+ url_delim_len
- 2
640 && strncmp (s
- url_delim_len
+ 2, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
642 s
-= (url_delim_len
- 2);
643 while (s
>= lpath
&& *s
-- != PATH_SEP
);
648 if (s
- url_delim_len
> lpath
649 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
651 char *vfs_prefix
= s
- url_delim_len
;
652 struct vfs_class
*vclass
;
654 while (vfs_prefix
> lpath
&& *--vfs_prefix
!= PATH_SEP
);
655 if (*vfs_prefix
== PATH_SEP
)
657 *(s
- url_delim_len
) = '\0';
659 vclass
= vfs_prefix_to_class (vfs_prefix
);
660 *(s
- url_delim_len
) = *VFS_PATH_URL_DELIMITER
;
664 struct vfs_s_subclass
*sub
= (struct vfs_s_subclass
*) vclass
->data
;
665 if (sub
!= NULL
&& sub
->flags
& VFS_S_REMOTE
)
681 /* If the previous token is "..", we cannot collapse it */
682 if (s
[0] == '.' && s
[1] == '.' && s
+ 2 == p
)
690 if (s
== lpath
&& *s
== PATH_SEP
)
692 /* "/../foo" -> "/foo" */
693 str_move (s
+ 1, p
+ 4);
697 /* "token/../foo" -> "foo" */
699 if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
700 && (is_supported_encoding (s
+ enc_prefix_len
)))
701 /* special case: remove encoding */
704 #endif /* HAVE_CHARSET */
707 p
= (s
> lpath
) ? s
- 1 : s
;
714 /* "token/.." -> "." */
715 if (lpath
[0] != PATH_SEP
)
723 /* "foo/token/.." -> "foo" */
727 else if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
728 && (is_supported_encoding (s
+ enc_prefix_len
)))
730 /* special case: remove encoding */
735 /* search for the previous token */
736 /* s[-1] == PATH_SEP */
738 while (p
>= lpath
&& *p
!= PATH_SEP
)
744 #endif /* HAVE_CHARSET */
747 if (s
>= lpath
+ url_delim_len
748 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
761 /* --------------------------------------------------------------------------------------------- */
764 canonicalize_pathname (char *path
)
766 custom_canonicalize_pathname (path
, CANON_PATH_ALL
);
769 /* --------------------------------------------------------------------------------------------- */
771 #ifdef HAVE_GET_PROCESS_STATS
773 gettimeofday (struct timeval
*tp
, void *tzp
)
775 return get_process_stats (tp
, PS_SELF
, 0, 0);
777 #endif /* HAVE_GET_PROCESS_STATS */
779 /* --------------------------------------------------------------------------------------------- */
781 #ifndef HAVE_REALPATH
783 mc_realpath (const char *path
, char *resolved_path
)
785 char copy_path
[PATH_MAX
];
786 char link_path
[PATH_MAX
];
787 char got_path
[PATH_MAX
];
788 char *new_path
= got_path
;
793 /* Make a copy of the source path since we may need to modify it. */
794 if (strlen (path
) >= PATH_MAX
- 2)
796 errno
= ENAMETOOLONG
;
799 strcpy (copy_path
, path
);
801 max_path
= copy_path
+ PATH_MAX
- 2;
802 /* If it's a relative pathname use getwd for starters. */
806 new_path
= g_get_current_dir ();
807 if (new_path
== NULL
)
809 strcpy (got_path
, "");
813 g_snprintf (got_path
, PATH_MAX
, "%s", new_path
);
818 new_path
+= strlen (got_path
);
819 if (new_path
[-1] != '/')
827 /* Expand each slash-separated pathname component. */
828 while (*path
!= '\0')
830 /* Ignore stray "/". */
839 if (path
[1] == '\0' || path
[1] == '/')
846 if (path
[2] == '\0' || path
[2] == '/')
849 /* Ignore ".." at root. */
850 if (new_path
== got_path
+ 1)
852 /* Handle ".." by backing up. */
853 while ((--new_path
)[-1] != '/');
858 /* Safely copy the next pathname component. */
859 while (*path
!= '\0' && *path
!= '/')
863 errno
= ENAMETOOLONG
;
866 *new_path
++ = *path
++;
869 /* Protect against infinite loops. */
870 if (readlinks
++ > MAXSYMLINKS
)
875 /* See if latest pathname component is a symlink. */
877 n
= readlink (got_path
, link_path
, PATH_MAX
- 1);
880 /* EINVAL means the file exists but isn't a symlink. */
883 /* Make sure it's null terminated. */
885 strcpy (resolved_path
, got_path
);
891 /* Note: readlink doesn't add the null byte. */
893 if (*link_path
== '/')
894 /* Start over for an absolute symlink. */
897 /* Otherwise back up over this component. */
898 while (*(--new_path
) != '/');
899 /* Safe sex check. */
900 if (strlen (path
) + n
>= PATH_MAX
- 2)
902 errno
= ENAMETOOLONG
;
905 /* Insert symlink contents into path. */
906 strcat (link_path
, path
);
907 strcpy (copy_path
, link_path
);
913 /* Delete trailing slash but don't whomp a lone slash. */
914 if (new_path
!= got_path
+ 1 && new_path
[-1] == '/')
916 /* Make sure it's null terminated. */
918 strcpy (resolved_path
, got_path
);
919 return resolved_path
;
921 #endif /* HAVE_REALPATH */
923 /* --------------------------------------------------------------------------------------------- */
925 * Return the index of the permissions triplet
930 get_user_permissions (struct stat
*st
)
932 static gboolean initialized
= FALSE
;
933 static gid_t
*groups
;
942 ngroups
= getgroups (0, NULL
);
944 ngroups
= 0; /* ignore errors */
946 /* allocate space for one element in addition to what
947 * will be filled by getgroups(). */
948 groups
= g_new (gid_t
, ngroups
+ 1);
952 ngroups
= getgroups (ngroups
, groups
);
954 ngroups
= 0; /* ignore errors */
957 /* getgroups() may or may not return the effective group ID,
958 * so we always include it at the end of the list. */
959 groups
[ngroups
++] = getegid ();
964 if (st
->st_uid
== uid
|| uid
== 0)
967 for (i
= 0; i
< ngroups
; i
++)
969 if (st
->st_gid
== groups
[i
])
976 /* --------------------------------------------------------------------------------------------- */
978 * Build filename from arguments.
979 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
983 mc_build_filenamev (const char *first_element
, va_list args
)
986 const char *element
= first_element
;
993 path
= g_string_new ("");
995 absolute
= (*first_element
!= '\0' && *first_element
== PATH_SEP
);
999 if (*element
== '\0')
1000 element
= va_arg (args
, char *);
1007 tmp_element
= g_strdup (element
);
1009 element
= va_arg (args
, char *);
1011 canonicalize_pathname (tmp_element
);
1012 len
= strlen (tmp_element
);
1013 start
= (tmp_element
[0] == PATH_SEP
) ? tmp_element
+ 1 : tmp_element
;
1015 g_string_append (path
, start
);
1016 if (tmp_element
[len
- 1] != PATH_SEP
&& element
!= NULL
)
1017 g_string_append_c (path
, PATH_SEP
);
1019 g_free (tmp_element
);
1022 while (element
!= NULL
);
1025 g_string_prepend_c (path
, PATH_SEP
);
1027 ret
= g_string_free (path
, FALSE
);
1028 canonicalize_pathname (ret
);
1033 /* --------------------------------------------------------------------------------------------- */
1035 * Build filename from arguments.
1036 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1040 mc_build_filename (const char *first_element
, ...)
1045 if (first_element
== NULL
)
1048 va_start (args
, first_element
);
1049 ret
= mc_build_filenamev (first_element
, args
);
1054 /* --------------------------------------------------------------------------------------------- */