1 /* Various utilities - Unix variants
2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2007 Free Software Foundation, Inc.
4 Written 1994, 1995, 1996 by:
5 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
6 Jakub Jelinek, Mauricio Plaza.
8 The mc_realpath routine is mostly from uClibc package, written
9 by Rick Sladkey <jrs@world.std.com>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
26 * \brief Source: various utilities - Unix variant
40 #include <sys/param.h>
41 #include <sys/types.h>
44 #ifdef HAVE_SYS_IOCTL_H
45 #include <sys/ioctl.h>
47 #ifdef HAVE_GET_PROCESS_STATS
48 #include <sys/procstats.h>
54 #include "lib/global.h"
55 #include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
56 #include "lib/strutil.h" /* str_move() */
58 #include "lib/widget.h" /* message() */
59 #include "lib/vfs/xdirentry.h"
62 #include "lib/charsets.h"
67 /*** global variables ****************************************************************************/
69 struct sigaction startup_handler
;
71 /*** file scope macro definitions ****************************************************************/
73 #define UID_CACHE_SIZE 200
74 #define GID_CACHE_SIZE 30
76 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
77 /* More than that would be unportable */
78 #define MAX_PIPE_SIZE 4096
80 /*** file scope type declarations ****************************************************************/
88 /*** file scope variables ************************************************************************/
90 static int_cache uid_cache
[UID_CACHE_SIZE
];
91 static int_cache gid_cache
[GID_CACHE_SIZE
];
93 static int error_pipe
[2]; /* File descriptors of error pipe */
94 static int old_error
; /* File descriptor of old standard error */
96 /*** file scope functions ************************************************************************/
97 /* --------------------------------------------------------------------------------------------- */
100 i_cache_match (int id
, int_cache
* cache
, int size
)
104 for (i
= 0; i
< size
; i
++)
105 if (cache
[i
].index
== id
)
106 return cache
[i
].string
;
110 /* --------------------------------------------------------------------------------------------- */
113 i_cache_add (int id
, int_cache
* cache
, int size
, char *text
, int *last
)
115 g_free (cache
[*last
].string
);
116 cache
[*last
].string
= g_strdup (text
);
117 cache
[*last
].index
= id
;
118 *last
= ((*last
) + 1) % size
;
121 /* --------------------------------------------------------------------------------------------- */
122 /*** public functions ****************************************************************************/
123 /* --------------------------------------------------------------------------------------------- */
129 static char ibuf
[10];
133 name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
);
137 pwd
= getpwuid (uid
);
140 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
145 g_snprintf (ibuf
, sizeof (ibuf
), "%d", uid
);
150 /* --------------------------------------------------------------------------------------------- */
156 static char gbuf
[10];
160 name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
);
164 grp
= getgrgid (gid
);
167 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
172 g_snprintf (gbuf
, sizeof (gbuf
), "%d", gid
);
177 /* --------------------------------------------------------------------------------------------- */
178 /* Since ncurses uses a handler that automatically refreshes the */
179 /* screen after a SIGCONT, and we don't want this behavior when */
180 /* spawning a child, we save the original handler here */
183 save_stop_handler (void)
185 sigaction (SIGTSTP
, NULL
, &startup_handler
);
188 /* --------------------------------------------------------------------------------------------- */
191 my_system (int flags
, const char *shell
, const char *command
)
193 struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
197 ignore
.sa_handler
= SIG_IGN
;
198 sigemptyset (&ignore
.sa_mask
);
201 sigaction (SIGINT
, &ignore
, &save_intr
);
202 sigaction (SIGQUIT
, &ignore
, &save_quit
);
204 /* Restore the original SIGTSTP handler, we don't want ncurses' */
205 /* handler messing the screen after the SIGCONT */
206 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
211 fprintf (stderr
, "\n\nfork () = -1\n");
216 signal (SIGINT
, SIG_DFL
);
217 signal (SIGQUIT
, SIG_DFL
);
218 signal (SIGTSTP
, SIG_DFL
);
219 signal (SIGCHLD
, SIG_DFL
);
221 if (flags
& EXECUTE_AS_SHELL
)
222 execl (shell
, shell
, "-c", command
, (char *) NULL
);
225 gchar
**shell_tokens
;
226 const gchar
*only_cmd
;
228 shell_tokens
= g_strsplit (shell
, " ", 2);
229 if (shell_tokens
== NULL
)
232 only_cmd
= (*shell_tokens
!= NULL
) ? *shell_tokens
: shell
;
234 execlp (only_cmd
, shell
, command
, (char *) NULL
);
237 execlp will replace current process,
238 therefore no sence in call of g_strfreev().
239 But this keeped for estetic reason :)
241 g_strfreev (shell_tokens
);
245 _exit (127); /* Exec error */
251 if (waitpid (pid
, &status
, 0) > 0)
253 status
= WEXITSTATUS (status
);
263 sigaction (SIGINT
, &save_intr
, NULL
);
264 sigaction (SIGQUIT
, &save_quit
, NULL
);
265 sigaction (SIGTSTP
, &save_stop
, NULL
);
271 /* --------------------------------------------------------------------------------------------- */
273 * Perform tilde expansion if possible.
274 * Always return a newly allocated string, even if it's unchanged.
278 tilde_expand (const char *directory
)
280 struct passwd
*passwd
;
284 if (*directory
!= '~')
285 return g_strdup (directory
);
289 /* d = "~" or d = "~/" */
290 if (!(*p
) || (*p
== PATH_SEP
))
292 passwd
= getpwuid (geteuid ());
293 q
= (*p
== PATH_SEP
) ? p
+ 1 : "";
297 q
= strchr (p
, PATH_SEP
);
300 passwd
= getpwnam (p
);
304 name
= g_strndup (p
, q
- p
);
305 passwd
= getpwnam (name
);
311 /* If we can't figure the user name, leave tilde unexpanded */
313 return g_strdup (directory
);
315 return g_strconcat (passwd
->pw_dir
, PATH_SEP_STR
, q
, (char *) NULL
);
318 /* --------------------------------------------------------------------------------------------- */
320 * Return the directory where mc should keep its temporary files.
321 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}/mc-$USER"
322 * When called the first time, the directory is created if needed.
323 * The first call should be done early, since we are using fprintf()
324 * and not message() to report possible problems.
330 static char buffer
[64];
331 static const char *tmpdir
;
335 const char *error
= NULL
;
337 /* Check if already correctly initialized */
338 if (tmpdir
&& lstat (tmpdir
, &st
) == 0 && S_ISDIR (st
.st_mode
) &&
339 st
.st_uid
== getuid () && (st
.st_mode
& 0777) == 0700)
342 sys_tmp
= getenv ("TMPDIR");
343 if (!sys_tmp
|| sys_tmp
[0] != '/')
345 sys_tmp
= TMPDIR_DEFAULT
;
348 pwd
= getpwuid (getuid ());
351 g_snprintf (buffer
, sizeof (buffer
), "%s/mc-%s", sys_tmp
, pwd
->pw_name
);
353 g_snprintf (buffer
, sizeof (buffer
), "%s/mc-%lu", sys_tmp
, (unsigned long) getuid ());
355 canonicalize_pathname (buffer
);
357 if (lstat (buffer
, &st
) == 0)
359 /* Sanity check for existing directory */
360 if (!S_ISDIR (st
.st_mode
))
361 error
= _("%s is not a directory\n");
362 else if (st
.st_uid
!= getuid ())
363 error
= _("Directory %s is not owned by you\n");
364 else if (((st
.st_mode
& 0777) != 0700) && (chmod (buffer
, 0700) != 0))
365 error
= _("Cannot set correct permissions for directory %s\n");
369 /* Need to create directory */
370 if (mkdir (buffer
, S_IRWXU
) != 0)
373 _("Cannot create temporary directory %s: %s\n"),
374 buffer
, unix_error_string (errno
));
382 char *test_fn
, *fallback_prefix
;
386 fprintf (stderr
, error
, buffer
);
388 /* Test if sys_tmp is suitable for temporary files */
389 fallback_prefix
= g_strdup_printf ("%s/mctest", sys_tmp
);
390 test_fd
= mc_mkstemps (&test_fn
, fallback_prefix
, NULL
);
391 g_free (fallback_prefix
);
395 test_fd
= open (test_fn
, O_RDONLY
);
406 fprintf (stderr
, _("Temporary files will be created in %s\n"), sys_tmp
);
407 g_snprintf (buffer
, sizeof (buffer
), "%s", sys_tmp
);
412 fprintf (stderr
, _("Temporary files will not be created\n"));
413 g_snprintf (buffer
, sizeof (buffer
), "%s", "/dev/null/");
416 fprintf (stderr
, "%s\n", _("Press any key to continue..."));
423 g_setenv ("MC_TMPDIR", tmpdir
, TRUE
);
428 /* --------------------------------------------------------------------------------------------- */
430 * Creates a pipe to hold standard error for a later analysis.
431 * The pipe can hold 4096 bytes. Make sure no more is written
432 * or a deadlock might occur.
436 open_error_pipe (void)
438 if (pipe (error_pipe
) < 0)
440 message (D_NORMAL
, _("Warning"), _("Pipe failed"));
443 if (old_error
< 0 || close (2) || dup (error_pipe
[1]) != 2)
445 message (D_NORMAL
, _("Warning"), _("Dup failed"));
447 close (error_pipe
[0]);
453 * Settng stderr in nonblocking mode as we close it earlier, than
454 * program stops. We try to read some error at program startup,
455 * but we should not block on it.
457 * TODO: make piped stdin/stderr poll()/select()able to get rid
461 fd_flags
= fcntl (error_pipe
[0], F_GETFL
, NULL
);
464 fd_flags
|= O_NONBLOCK
;
465 if (fcntl (error_pipe
[0], F_SETFL
, fd_flags
) == -1)
467 /* TODO: handle it somehow */
471 /* we never write there */
472 close (error_pipe
[1]);
476 /* --------------------------------------------------------------------------------------------- */
478 * Returns true if an error was displayed
479 * error: -1 - ignore errors, 0 - display warning, 1 - display error
480 * text is prepended to the error message from the pipe
484 close_error_pipe (int error
, const char *text
)
487 char msg
[MAX_PIPE_SIZE
];
491 if (error_pipe
[0] == -1)
497 title
= _("Warning");
500 if (dup2 (old_error
, 2) == -1)
502 message (error
, MSG_ERROR
, _("Error dup'ing old error pipe"));
506 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
- 1);
510 close (error_pipe
[0]);
514 return 0; /* Just ignore error message */
518 return 0; /* Nothing to show */
520 /* Show message from pipe */
521 message (error
, title
, "%s", msg
);
525 /* Show given text and possible message from pipe */
526 message (error
, title
, "%s\n%s", text
, msg
);
531 /* --------------------------------------------------------------------------------------------- */
533 * Canonicalize path, and return a new path. Do everything in place.
534 * The new path differs from path in:
535 * Multiple `/'s are collapsed to a single `/'.
536 * Leading `./'s and trailing `/.'s are removed.
537 * Trailing `/'s are removed.
538 * Non-leading `../'s and trailing `..'s are handled by removing
539 * portions of the path.
540 * Well formed UNC paths are modified only in the local part.
544 custom_canonicalize_pathname (char *path
, CANON_PATH_FLAGS flags
)
548 char *lpath
= path
; /* path without leading UNC part */
549 const size_t url_delim_len
= strlen (VFS_PATH_URL_DELIMITER
);
551 /* Detect and preserve UNC paths: //server/... */
552 if ((flags
& CANON_PATH_GUARDUNC
) && path
[0] == PATH_SEP
&& path
[1] == PATH_SEP
)
555 while (p
[0] && p
[0] != '/')
557 if (p
[0] == '/' && p
> path
+ 2)
561 if (!lpath
[0] || !lpath
[1])
564 if (flags
& CANON_PATH_JOINSLASHES
)
566 /* Collapse multiple slashes */
570 if (p
[0] == PATH_SEP
&& p
[1] == PATH_SEP
&& (p
== lpath
|| *(p
- 1) != ':'))
573 while (*(++s
) == PATH_SEP
);
580 if (flags
& CANON_PATH_JOINSLASHES
)
582 /* Collapse "/./" -> "/" */
586 if (p
[0] == PATH_SEP
&& p
[1] == '.' && p
[2] == PATH_SEP
)
593 if (flags
& CANON_PATH_REMSLASHDOTS
)
595 /* Remove trailing slashes */
596 p
= lpath
+ strlen (lpath
) - 1;
597 while (p
> lpath
&& *p
== PATH_SEP
)
599 if (p
>= lpath
- (url_delim_len
+ 1)
600 && strncmp (p
- url_delim_len
+ 1, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
605 /* Remove leading "./" */
606 if (lpath
[0] == '.' && lpath
[1] == PATH_SEP
)
615 str_move (lpath
, lpath
+ 2);
619 /* Remove trailing "/" or "/." */
620 len
= strlen (lpath
);
623 if (lpath
[len
- 1] == PATH_SEP
624 && (len
< url_delim_len
625 || strncmp (lpath
+ len
- url_delim_len
, VFS_PATH_URL_DELIMITER
,
626 url_delim_len
) != 0))
628 lpath
[len
- 1] = '\0';
632 if (lpath
[len
- 1] == '.' && lpath
[len
- 2] == PATH_SEP
)
641 lpath
[len
- 2] = '\0';
647 if (flags
& CANON_PATH_REMDOUBLEDOTS
)
649 const size_t enc_prefix_len
= strlen (VFS_ENCODING_PREFIX
);
651 /* Collapse "/.." with the previous part of path */
653 while (p
[0] && p
[1] && p
[2])
655 if ((p
[0] != PATH_SEP
|| p
[1] != '.' || p
[2] != '.') || (p
[3] != PATH_SEP
&& p
[3] != 0))
661 /* search for the previous token */
663 if (s
>= lpath
+ url_delim_len
- 2
664 && strncmp (s
- url_delim_len
+ 2, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
666 s
-= (url_delim_len
- 2);
667 while (s
>= lpath
&& *s
-- != PATH_SEP
);
672 if (s
- url_delim_len
> lpath
673 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
675 char *vfs_prefix
= s
- url_delim_len
;
676 struct vfs_class
*vclass
;
678 while (vfs_prefix
> lpath
&& *--vfs_prefix
!= PATH_SEP
);
679 if (*vfs_prefix
== PATH_SEP
)
681 *(s
- url_delim_len
) = '\0';
683 vclass
= vfs_prefix_to_class (vfs_prefix
);
684 *(s
- url_delim_len
) = *VFS_PATH_URL_DELIMITER
;
688 struct vfs_s_subclass
*sub
= (struct vfs_s_subclass
*) vclass
->data
;
689 if (sub
!= NULL
&& sub
->flags
& VFS_S_REMOTE
)
699 /* skip VFS prefix */
701 struct vfs_class
*vclass
;
703 /* old parser mode */
707 path_sep
= strchr (s
+ 1, PATH_SEP
);
708 if (path_sep
!= NULL
)
711 vclass
= vfs_prefix_to_class (s
+ 2);
712 if (path_sep
!= NULL
)
713 *path_sep
= PATH_SEP
;
723 /* If the previous token is "..", we cannot collapse it */
724 if (s
[0] == '.' && s
[1] == '.' && s
+ 2 == p
)
732 if (s
== lpath
&& *s
== PATH_SEP
)
734 /* "/../foo" -> "/foo" */
735 str_move (s
+ 1, p
+ 4);
739 /* "token/../foo" -> "foo" */
741 if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
742 && (is_supported_encoding (s
+ enc_prefix_len
)))
743 /* special case: remove encoding */
746 #endif /* HAVE_CHARSET */
749 p
= (s
> lpath
) ? s
- 1 : s
;
756 /* "token/.." -> "." */
757 if (lpath
[0] != PATH_SEP
)
765 /* "foo/token/.." -> "foo" */
769 else if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
770 && (is_supported_encoding (s
+ enc_prefix_len
)))
772 /* special case: remove encoding */
777 /* search for the previous token */
778 /* s[-1] == PATH_SEP */
780 while (p
>= lpath
&& *p
!= PATH_SEP
)
786 #endif /* HAVE_CHARSET */
789 if (s
>= lpath
+ url_delim_len
790 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
803 /* --------------------------------------------------------------------------------------------- */
806 canonicalize_pathname (char *path
)
808 custom_canonicalize_pathname (path
, CANON_PATH_ALL
);
811 /* --------------------------------------------------------------------------------------------- */
813 #ifdef HAVE_GET_PROCESS_STATS
815 gettimeofday (struct timeval
*tp
, void *tzp
)
817 return get_process_stats (tp
, PS_SELF
, 0, 0);
819 #endif /* HAVE_GET_PROCESS_STATS */
821 /* --------------------------------------------------------------------------------------------- */
823 #ifndef HAVE_REALPATH
825 mc_realpath (const char *path
, char *resolved_path
)
827 char copy_path
[PATH_MAX
];
828 char link_path
[PATH_MAX
];
829 char got_path
[PATH_MAX
];
830 char *new_path
= got_path
;
835 /* Make a copy of the source path since we may need to modify it. */
836 if (strlen (path
) >= PATH_MAX
- 2)
838 errno
= ENAMETOOLONG
;
841 strcpy (copy_path
, path
);
843 max_path
= copy_path
+ PATH_MAX
- 2;
844 /* If it's a relative pathname use getwd for starters. */
848 new_path
= g_get_current_dir ();
849 if (new_path
== NULL
)
851 strcpy (got_path
, "");
855 g_snprintf (got_path
, PATH_MAX
, "%s", new_path
);
860 new_path
+= strlen (got_path
);
861 if (new_path
[-1] != '/')
869 /* Expand each slash-separated pathname component. */
870 while (*path
!= '\0')
872 /* Ignore stray "/". */
881 if (path
[1] == '\0' || path
[1] == '/')
888 if (path
[2] == '\0' || path
[2] == '/')
891 /* Ignore ".." at root. */
892 if (new_path
== got_path
+ 1)
894 /* Handle ".." by backing up. */
895 while ((--new_path
)[-1] != '/');
900 /* Safely copy the next pathname component. */
901 while (*path
!= '\0' && *path
!= '/')
905 errno
= ENAMETOOLONG
;
908 *new_path
++ = *path
++;
911 /* Protect against infinite loops. */
912 if (readlinks
++ > MAXSYMLINKS
)
917 /* See if latest pathname component is a symlink. */
919 n
= readlink (got_path
, link_path
, PATH_MAX
- 1);
922 /* EINVAL means the file exists but isn't a symlink. */
925 /* Make sure it's null terminated. */
927 strcpy (resolved_path
, got_path
);
933 /* Note: readlink doesn't add the null byte. */
935 if (*link_path
== '/')
936 /* Start over for an absolute symlink. */
939 /* Otherwise back up over this component. */
940 while (*(--new_path
) != '/');
941 /* Safe sex check. */
942 if (strlen (path
) + n
>= PATH_MAX
- 2)
944 errno
= ENAMETOOLONG
;
947 /* Insert symlink contents into path. */
948 strcat (link_path
, path
);
949 strcpy (copy_path
, link_path
);
955 /* Delete trailing slash but don't whomp a lone slash. */
956 if (new_path
!= got_path
+ 1 && new_path
[-1] == '/')
958 /* Make sure it's null terminated. */
960 strcpy (resolved_path
, got_path
);
961 return resolved_path
;
963 #endif /* HAVE_REALPATH */
965 /* --------------------------------------------------------------------------------------------- */
967 * Return the index of the permissions triplet
972 get_user_permissions (struct stat
*st
)
974 static gboolean initialized
= FALSE
;
975 static gid_t
*groups
;
984 ngroups
= getgroups (0, NULL
);
986 ngroups
= 0; /* ignore errors */
988 /* allocate space for one element in addition to what
989 * will be filled by getgroups(). */
990 groups
= g_new (gid_t
, ngroups
+ 1);
994 ngroups
= getgroups (ngroups
, groups
);
996 ngroups
= 0; /* ignore errors */
999 /* getgroups() may or may not return the effective group ID,
1000 * so we always include it at the end of the list. */
1001 groups
[ngroups
++] = getegid ();
1006 if (st
->st_uid
== uid
|| uid
== 0)
1009 for (i
= 0; i
< ngroups
; i
++)
1011 if (st
->st_gid
== groups
[i
])
1018 /* --------------------------------------------------------------------------------------------- */