2011-10-15 14:56:47 +04:00
|
|
|
/*
|
|
|
|
Various utilities - Unix variants
|
|
|
|
|
2024-01-01 09:46:17 +03:00
|
|
|
Copyright (C) 1994-2024
|
2014-02-12 10:33:10 +04:00
|
|
|
Free Software Foundation, Inc.
|
2011-10-15 14:56:47 +04:00
|
|
|
|
|
|
|
Written by:
|
|
|
|
Miguel de Icaza, 1994, 1995, 1996
|
|
|
|
Janne Kukonlehto, 1994, 1995, 1996
|
|
|
|
Dugan Porter, 1994, 1995, 1996
|
|
|
|
Jakub Jelinek, 1994, 1995, 1996
|
|
|
|
Mauricio Plaza, 1994, 1995, 1996
|
2024-03-31 18:53:59 +03:00
|
|
|
Andrew Borodin <aborodin@vmail.ru> 2010-2024
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2005-01-13 22:37:46 +03:00
|
|
|
The mc_realpath routine is mostly from uClibc package, written
|
|
|
|
by Rick Sladkey <jrs@world.std.com>
|
|
|
|
|
2011-10-15 14:56:47 +04:00
|
|
|
This file is part of the Midnight Commander.
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2011-10-15 14:56:47 +04:00
|
|
|
The Midnight Commander is free software: you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation, either version 3 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
The Midnight Commander is distributed in the hope that it will be useful,
|
1998-02-27 07:54:42 +03:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2011-10-15 14:56:47 +04:00
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2009-02-05 21:28:18 +03:00
|
|
|
/** \file utilunix.c
|
|
|
|
* \brief Source: various utilities - Unix variant
|
|
|
|
*/
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <config.h>
|
2005-02-08 12:04:03 +03:00
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdarg.h>
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <stdio.h>
|
2005-02-08 12:04:03 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2012-01-04 15:44:01 +04:00
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
2005-02-08 12:04:03 +03:00
|
|
|
#include <sys/param.h>
|
2012-01-04 15:44:01 +04:00
|
|
|
#endif
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2014-07-03 09:58:57 +04:00
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
|
|
#include <sys/select.h>
|
|
|
|
#endif
|
2009-02-27 14:54:26 +03:00
|
|
|
#include <sys/wait.h>
|
2009-02-06 02:30:45 +03:00
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
2001-09-03 09:07:40 +04:00
|
|
|
|
2010-01-20 18:11:52 +03:00
|
|
|
#include "lib/global.h"
|
2013-12-20 11:26:56 +04:00
|
|
|
|
|
|
|
#include "lib/unixcompat.h"
|
2011-02-15 16:44:17 +03:00
|
|
|
#include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
|
2024-03-31 18:53:59 +03:00
|
|
|
#include "lib/strutil.h" /* str_move(), str_tokenize() */
|
2010-11-11 16:58:29 +03:00
|
|
|
#include "lib/util.h"
|
2010-11-12 11:03:57 +03:00
|
|
|
#include "lib/widget.h" /* message() */
|
2011-06-14 16:09:59 +04:00
|
|
|
#include "lib/vfs/xdirentry.h"
|
2010-09-10 11:27:21 +04:00
|
|
|
|
2010-09-13 13:32:27 +04:00
|
|
|
#ifdef HAVE_CHARSET
|
2011-02-18 16:11:57 +03:00
|
|
|
#include "lib/charsets.h"
|
2010-09-13 13:32:27 +04:00
|
|
|
#endif
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/*** global variables ****************************************************************************/
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
struct sigaction startup_handler;
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/*** file scope macro definitions ****************************************************************/
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
#define UID_CACHE_SIZE 200
|
|
|
|
#define GID_CACHE_SIZE 30
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/*** file scope type declarations ****************************************************************/
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int index;
|
1998-02-27 07:54:42 +03:00
|
|
|
char *string;
|
|
|
|
} int_cache;
|
|
|
|
|
2013-01-06 01:30:56 +04:00
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
FORK_ERROR = -1,
|
|
|
|
FORK_CHILD,
|
|
|
|
FORK_PARENT,
|
|
|
|
} my_fork_state_t;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
struct sigaction intr;
|
|
|
|
struct sigaction quit;
|
|
|
|
struct sigaction stop;
|
|
|
|
} my_system_sigactions_t;
|
|
|
|
|
Update template for .c files.
Add section for forward declarations of local functions. This section is
located before file scope variables because functions can be used in
strucutres (see find.c for example):
/*** forward declarations (file scope functions) *************************************************/
/* button callbacks */
static int start_stop (WButton * button, int action);
static int find_do_view_file (WButton * button, int action);
static int find_do_edit_file (WButton * button, int action);
/*** file scope variables ************************************************************************/
static struct
{
...
bcback_fn callback;
} fbuts[] =
{
...
{ B_STOP, NORMAL_BUTTON, N_("S&uspend"), 0, 0, NULL, start_stop },
...
{ B_VIEW, NORMAL_BUTTON, N_("&View - F3"), 0, 0, NULL, find_do_view_file },
{ B_VIEW, NORMAL_BUTTON, N_("&Edit - F4"), 0, 0, NULL, find_do_edit_file }
};
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
2023-02-24 09:27:11 +03:00
|
|
|
/*** forward declarations (file scope functions) *************************************************/
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/*** file scope variables ************************************************************************/
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
static int_cache uid_cache[UID_CACHE_SIZE];
|
|
|
|
static int_cache gid_cache[GID_CACHE_SIZE];
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
2010-11-08 13:21:45 +03:00
|
|
|
/*** file scope functions ************************************************************************/
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
static char *
|
2024-06-01 21:12:14 +03:00
|
|
|
i_cache_match (int id, int_cache *cache, int size)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++)
|
2010-04-30 17:31:06 +04:00
|
|
|
if (cache[i].index == id)
|
|
|
|
return cache[i].string;
|
1998-02-27 07:54:42 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
static void
|
2024-06-01 21:12:14 +03:00
|
|
|
i_cache_add (int id, int_cache *cache, int size, char *text, int *last)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2010-04-30 17:31:06 +04:00
|
|
|
g_free (cache[*last].string);
|
|
|
|
cache[*last].string = g_strdup (text);
|
|
|
|
cache[*last].index = id;
|
|
|
|
*last = ((*last) + 1) % size;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2013-01-06 01:30:56 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static my_fork_state_t
|
|
|
|
my_fork (void)
|
|
|
|
{
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
pid = fork ();
|
|
|
|
|
|
|
|
if (pid < 0)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "\n\nfork () = -1\n");
|
|
|
|
return FORK_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pid == 0)
|
|
|
|
return FORK_CHILD;
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (waitpid (pid, &status, 0) > 0)
|
|
|
|
return WEXITSTATUS (status) == 0 ? FORK_PARENT : FORK_ERROR;
|
|
|
|
|
|
|
|
if (errno != EINTR)
|
|
|
|
return FORK_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void
|
2024-06-01 21:12:14 +03:00
|
|
|
my_system__save_sigaction_handlers (my_system_sigactions_t *sigactions)
|
2013-01-06 01:30:56 +04:00
|
|
|
{
|
|
|
|
struct sigaction ignore;
|
|
|
|
|
2013-11-17 09:55:18 +04:00
|
|
|
memset (&ignore, 0, sizeof (ignore));
|
2013-01-06 01:30:56 +04:00
|
|
|
ignore.sa_handler = SIG_IGN;
|
|
|
|
sigemptyset (&ignore.sa_mask);
|
|
|
|
|
|
|
|
sigaction (SIGINT, &ignore, &sigactions->intr);
|
|
|
|
sigaction (SIGQUIT, &ignore, &sigactions->quit);
|
|
|
|
|
|
|
|
/* Restore the original SIGTSTP handler, we don't want ncurses' */
|
|
|
|
/* handler messing the screen after the SIGCONT */
|
|
|
|
sigaction (SIGTSTP, &startup_handler, &sigactions->stop);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void
|
2024-06-01 21:12:14 +03:00
|
|
|
my_system__restore_sigaction_handlers (my_system_sigactions_t *sigactions)
|
2013-01-06 01:30:56 +04:00
|
|
|
{
|
|
|
|
sigaction (SIGINT, &sigactions->intr, NULL);
|
|
|
|
sigaction (SIGQUIT, &sigactions->quit, NULL);
|
|
|
|
sigaction (SIGTSTP, &sigactions->stop, NULL);
|
|
|
|
}
|
|
|
|
|
2013-01-10 17:22:59 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static GPtrArray *
|
2024-03-31 18:53:59 +03:00
|
|
|
my_system_make_arg_array (int flags, const char *shell)
|
2013-01-10 17:22:59 +04:00
|
|
|
{
|
|
|
|
GPtrArray *args_array;
|
|
|
|
|
|
|
|
if ((flags & EXECUTE_AS_SHELL) != 0)
|
|
|
|
{
|
2024-03-31 18:53:59 +03:00
|
|
|
args_array = g_ptr_array_new ();
|
2015-05-09 15:53:16 +03:00
|
|
|
g_ptr_array_add (args_array, (gpointer) shell);
|
|
|
|
g_ptr_array_add (args_array, (gpointer) "-c");
|
2013-01-10 17:22:59 +04:00
|
|
|
}
|
2024-03-31 18:53:59 +03:00
|
|
|
else if (shell == NULL || *shell == '\0')
|
2013-01-10 17:22:59 +04:00
|
|
|
{
|
2024-03-31 18:53:59 +03:00
|
|
|
args_array = g_ptr_array_new ();
|
|
|
|
g_ptr_array_add (args_array, NULL);
|
2013-01-10 17:22:59 +04:00
|
|
|
}
|
2024-03-31 18:53:59 +03:00
|
|
|
else
|
|
|
|
args_array = str_tokenize (shell);
|
|
|
|
|
2013-01-10 17:22:59 +04:00
|
|
|
return args_array;
|
|
|
|
}
|
|
|
|
|
2014-07-03 09:58:57 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void
|
2024-06-01 21:12:14 +03:00
|
|
|
mc_pread_stream (mc_pipe_stream_t *ps, const fd_set *fds)
|
2014-07-03 09:58:57 +04:00
|
|
|
{
|
|
|
|
size_t buf_len;
|
|
|
|
ssize_t read_len;
|
|
|
|
|
|
|
|
if (!FD_ISSET (ps->fd, fds))
|
|
|
|
{
|
|
|
|
ps->len = MC_PIPE_STREAM_UNREAD;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf_len = (size_t) ps->len;
|
|
|
|
|
|
|
|
if (buf_len >= MC_PIPE_BUFSIZE)
|
|
|
|
buf_len = ps->null_term ? MC_PIPE_BUFSIZE - 1 : MC_PIPE_BUFSIZE;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
read_len = read (ps->fd, ps->buf, buf_len);
|
|
|
|
}
|
|
|
|
while (read_len < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
if (read_len < 0)
|
|
|
|
{
|
|
|
|
/* reading error */
|
|
|
|
ps->len = MC_PIPE_ERROR_READ;
|
|
|
|
ps->error = errno;
|
|
|
|
}
|
|
|
|
else if (read_len == 0)
|
|
|
|
/* EOF */
|
|
|
|
ps->len = MC_PIPE_STREAM_EOF;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* success */
|
|
|
|
ps->len = read_len;
|
|
|
|
|
|
|
|
if (ps->null_term)
|
|
|
|
ps->buf[(size_t) ps->len] = '\0';
|
|
|
|
}
|
2021-02-10 13:16:18 +03:00
|
|
|
|
|
|
|
ps->pos = 0;
|
2014-07-03 09:58:57 +04:00
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/*** public functions ****************************************************************************/
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
2015-06-24 16:14:36 +03:00
|
|
|
const char *
|
|
|
|
get_owner (uid_t uid)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
struct passwd *pwd;
|
2010-04-30 17:31:06 +04:00
|
|
|
char *name;
|
2015-06-24 16:14:36 +03:00
|
|
|
static uid_t uid_last;
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2015-06-24 16:14:36 +03:00
|
|
|
name = i_cache_match ((int) uid, uid_cache, UID_CACHE_SIZE);
|
2010-03-30 22:59:41 +04:00
|
|
|
if (name != NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
return name;
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
pwd = getpwuid (uid);
|
2010-03-30 22:59:41 +04:00
|
|
|
if (pwd != NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2015-06-24 16:14:36 +03:00
|
|
|
i_cache_add ((int) uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, (int *) &uid_last);
|
2010-04-30 17:31:06 +04:00
|
|
|
return pwd->pw_name;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
else
|
|
|
|
{
|
2013-10-15 10:34:04 +04:00
|
|
|
static char ibuf[10];
|
|
|
|
|
2015-06-24 16:14:36 +03:00
|
|
|
g_snprintf (ibuf, sizeof (ibuf), "%d", (int) uid);
|
2010-04-30 17:31:06 +04:00
|
|
|
return ibuf;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
|
2015-06-24 16:09:58 +03:00
|
|
|
const char *
|
|
|
|
get_group (gid_t gid)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
struct group *grp;
|
|
|
|
char *name;
|
2015-06-24 16:09:58 +03:00
|
|
|
static gid_t gid_last;
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2015-06-24 16:09:58 +03:00
|
|
|
name = i_cache_match ((int) gid, gid_cache, GID_CACHE_SIZE);
|
2010-03-30 22:59:41 +04:00
|
|
|
if (name != NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
return name;
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
grp = getgrgid (gid);
|
2010-03-30 22:59:41 +04:00
|
|
|
if (grp != NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2015-06-24 16:09:58 +03:00
|
|
|
i_cache_add ((int) gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, (int *) &gid_last);
|
2010-04-30 17:31:06 +04:00
|
|
|
return grp->gr_name;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-10-15 10:34:04 +04:00
|
|
|
static char gbuf[10];
|
|
|
|
|
2015-06-24 16:09:58 +03:00
|
|
|
g_snprintf (gbuf, sizeof (gbuf), "%d", (int) gid);
|
2010-04-30 17:31:06 +04:00
|
|
|
return gbuf;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Since ncurses uses a handler that automatically refreshes the */
|
|
|
|
/* screen after a SIGCONT, and we don't want this behavior when */
|
|
|
|
/* spawning a child, we save the original handler here */
|
2010-11-08 13:21:45 +03:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
void
|
|
|
|
save_stop_handler (void)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
sigaction (SIGTSTP, NULL, &startup_handler);
|
|
|
|
}
|
|
|
|
|
2013-01-05 14:48:50 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Wrapper for _exit() system call.
|
|
|
|
* The _exit() function has gcc's attribute 'noreturn', and this is reason why we can't
|
|
|
|
* mock the call.
|
|
|
|
*
|
|
|
|
* @param status exit code
|
|
|
|
*/
|
|
|
|
|
2017-03-04 20:27:02 +03:00
|
|
|
void
|
2017-03-04 20:33:15 +03:00
|
|
|
/* __attribute__ ((noreturn)) */
|
2017-03-04 20:27:02 +03:00
|
|
|
my_exit (int status)
|
2013-01-05 14:48:50 +04:00
|
|
|
{
|
|
|
|
_exit (status);
|
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
2013-01-09 21:25:04 +04:00
|
|
|
/**
|
|
|
|
* Call external programs.
|
|
|
|
*
|
|
|
|
* @parameter flags addition conditions for running external programs.
|
|
|
|
* @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
|
|
|
|
* Shell (or command) will be found in paths described in PATH variable
|
|
|
|
* (if shell parameter doesn't begin from path delimiter)
|
|
|
|
* @parameter command Command for shell (or first parameter for command, if flags contain EXECUTE_AS_SHELL)
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @return 0 if successful, -1 otherwise
|
2013-01-09 21:25:04 +04:00
|
|
|
*/
|
2010-11-08 13:21:45 +03:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
int
|
|
|
|
my_system (int flags, const char *shell, const char *command)
|
2013-01-09 21:25:04 +04:00
|
|
|
{
|
|
|
|
return my_systeml (flags, shell, command, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Call external programs with various parameters number.
|
|
|
|
*
|
|
|
|
* @parameter flags addition conditions for running external programs.
|
|
|
|
* @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* Shell (or command) will be found in paths described in PATH variable
|
2013-01-09 21:25:04 +04:00
|
|
|
* (if shell parameter doesn't begin from path delimiter)
|
|
|
|
* @parameter ... Command for shell with addition parameters for shell
|
|
|
|
* (or parameters for command, if flags contain EXECUTE_AS_SHELL).
|
|
|
|
* Should be NULL terminated.
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @return 0 if successful, -1 otherwise
|
2013-01-09 21:25:04 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
my_systeml (int flags, const char *shell, ...)
|
|
|
|
{
|
|
|
|
GPtrArray *args_array;
|
|
|
|
int status = 0;
|
|
|
|
va_list vargs;
|
|
|
|
char *one_arg;
|
|
|
|
|
|
|
|
args_array = g_ptr_array_new ();
|
|
|
|
|
|
|
|
va_start (vargs, shell);
|
|
|
|
while ((one_arg = va_arg (vargs, char *)) != NULL)
|
2013-01-10 17:22:59 +04:00
|
|
|
g_ptr_array_add (args_array, one_arg);
|
2013-01-09 21:25:04 +04:00
|
|
|
va_end (vargs);
|
|
|
|
|
|
|
|
g_ptr_array_add (args_array, NULL);
|
2013-01-10 17:22:59 +04:00
|
|
|
status = my_systemv_flags (flags, shell, (char *const *) args_array->pdata);
|
2013-01-09 21:25:04 +04:00
|
|
|
|
|
|
|
g_ptr_array_free (args_array, TRUE);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Call external programs with array of strings as parameters.
|
|
|
|
*
|
|
|
|
* @parameter command command to run. Command will be found in paths described in PATH variable
|
|
|
|
* (if command parameter doesn't begin from path delimiter)
|
|
|
|
* @parameter argv Array of strings (NULL-terminated) with parameters for command
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @return 0 if successful, -1 otherwise
|
2013-01-09 21:25:04 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
my_systemv (const char *command, char *const argv[])
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2013-01-06 01:30:56 +04:00
|
|
|
my_fork_state_t fork_state;
|
1998-02-27 07:54:42 +03:00
|
|
|
int status = 0;
|
2013-01-06 01:30:56 +04:00
|
|
|
my_system_sigactions_t sigactions;
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2013-01-06 01:30:56 +04:00
|
|
|
my_system__save_sigaction_handlers (&sigactions);
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2013-01-06 01:30:56 +04:00
|
|
|
fork_state = my_fork ();
|
|
|
|
switch (fork_state)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2013-01-06 01:30:56 +04:00
|
|
|
case FORK_ERROR:
|
2010-04-17 23:16:36 +04:00
|
|
|
status = -1;
|
2013-01-06 01:30:56 +04:00
|
|
|
break;
|
|
|
|
case FORK_CHILD:
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2013-01-06 01:30:56 +04:00
|
|
|
signal (SIGINT, SIG_DFL);
|
|
|
|
signal (SIGQUIT, SIG_DFL);
|
|
|
|
signal (SIGTSTP, SIG_DFL);
|
|
|
|
signal (SIGCHLD, SIG_DFL);
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2013-01-09 21:25:04 +04:00
|
|
|
execvp (command, argv);
|
2013-01-06 01:30:56 +04:00
|
|
|
my_exit (127); /* Exec error */
|
2010-04-17 23:16:36 +04:00
|
|
|
}
|
2018-01-04 01:39:07 +03:00
|
|
|
MC_FALLTHROUGH;
|
2016-09-17 23:23:26 +03:00
|
|
|
/* no break here, or unreachable-code warning by no returning my_exit() */
|
2013-01-06 01:30:56 +04:00
|
|
|
default:
|
|
|
|
status = 0;
|
|
|
|
break;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2013-01-06 01:30:56 +04:00
|
|
|
my_system__restore_sigaction_handlers (&sigactions);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2010-04-17 23:16:36 +04:00
|
|
|
return status;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2013-01-10 17:22:59 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Call external programs with flags and with array of strings as parameters.
|
|
|
|
*
|
|
|
|
* @parameter flags addition conditions for running external programs.
|
|
|
|
* @parameter command shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
|
|
|
|
* Shell (or command) will be found in paths described in PATH variable
|
|
|
|
* (if shell parameter doesn't begin from path delimiter)
|
|
|
|
* @parameter argv Array of strings (NULL-terminated) with parameters for command
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @return 0 if successful, -1 otherwise
|
2013-01-10 17:22:59 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
my_systemv_flags (int flags, const char *command, char *const argv[])
|
|
|
|
{
|
2024-03-31 18:53:59 +03:00
|
|
|
const char *execute_name;
|
2013-01-10 17:22:59 +04:00
|
|
|
GPtrArray *args_array;
|
|
|
|
int status = 0;
|
|
|
|
|
2024-03-31 18:53:59 +03:00
|
|
|
args_array = my_system_make_arg_array (flags, command);
|
|
|
|
|
|
|
|
execute_name = g_ptr_array_index (args_array, 0);
|
2013-01-10 17:22:59 +04:00
|
|
|
|
|
|
|
for (; argv != NULL && *argv != NULL; argv++)
|
|
|
|
g_ptr_array_add (args_array, *argv);
|
|
|
|
|
|
|
|
g_ptr_array_add (args_array, NULL);
|
|
|
|
status = my_systemv (execute_name, (char *const *) args_array->pdata);
|
|
|
|
|
|
|
|
g_ptr_array_free (args_array, TRUE);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2014-07-03 09:58:57 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Create pipe and run child process.
|
|
|
|
*
|
|
|
|
* @parameter command command line of child process
|
2021-02-24 14:10:52 +03:00
|
|
|
* @parameter read_out do or don't read the stdout of child process
|
|
|
|
* @parameter read_err do or don't read the stderr of child process
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @parameter error contains pointer to object to handle error code and message
|
2014-07-03 09:58:57 +04:00
|
|
|
*
|
|
|
|
* @return newly created object of mc_pipe_t class in success, NULL otherwise
|
|
|
|
*/
|
|
|
|
|
|
|
|
mc_pipe_t *
|
2024-06-01 21:12:14 +03:00
|
|
|
mc_popen (const char *command, gboolean read_out, gboolean read_err, GError **error)
|
2014-07-03 09:58:57 +04:00
|
|
|
{
|
|
|
|
mc_pipe_t *p;
|
2016-06-28 09:10:44 +03:00
|
|
|
const char *const argv[] = { "/bin/sh", "sh", "-c", command, NULL };
|
2014-07-03 09:58:57 +04:00
|
|
|
|
|
|
|
p = g_try_new (mc_pipe_t, 1);
|
|
|
|
if (p == NULL)
|
|
|
|
{
|
|
|
|
mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE, "%s",
|
|
|
|
_("Cannot create pipe descriptor"));
|
|
|
|
goto ret_err;
|
|
|
|
}
|
|
|
|
|
2021-02-24 14:10:52 +03:00
|
|
|
p->out.fd = -1;
|
|
|
|
p->err.fd = -1;
|
|
|
|
|
2015-04-12 06:44:40 +03:00
|
|
|
if (!g_spawn_async_with_pipes
|
2021-02-24 14:10:52 +03:00
|
|
|
(NULL, (gchar **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_FILE_AND_ARGV_ZERO, NULL,
|
|
|
|
NULL, &p->child_pid, NULL, read_out ? &p->out.fd : NULL, read_err ? &p->err.fd : NULL,
|
|
|
|
error))
|
2014-07-03 09:58:57 +04:00
|
|
|
{
|
|
|
|
mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE_STREAM, "%s",
|
|
|
|
_("Cannot create pipe streams"));
|
|
|
|
goto ret_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->out.buf[0] = '\0';
|
|
|
|
p->out.len = MC_PIPE_BUFSIZE;
|
|
|
|
p->out.null_term = FALSE;
|
|
|
|
|
|
|
|
p->err.buf[0] = '\0';
|
|
|
|
p->err.len = MC_PIPE_BUFSIZE;
|
|
|
|
p->err.null_term = FALSE;
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
|
|
|
ret_err:
|
|
|
|
g_free (p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Read stdout and stderr of pipe asynchronously.
|
|
|
|
*
|
|
|
|
* @parameter p pipe descriptor
|
|
|
|
*
|
|
|
|
* The lengths of read data contain in p->out.len and p->err.len.
|
2021-02-24 14:10:52 +03:00
|
|
|
*
|
|
|
|
* Before read, p->xxx.len is an input. It defines the number of data to read.
|
|
|
|
* Should not be greater than MC_PIPE_BUFSIZE.
|
2014-07-03 09:58:57 +04:00
|
|
|
*
|
|
|
|
* After read, p->xxx.len is an output and contains the following:
|
|
|
|
* p->xxx.len > 0: an actual length of read data stored in p->xxx.buf;
|
|
|
|
* p->xxx.len == MC_PIPE_STREAM_EOF: EOF of stream p->xxx;
|
|
|
|
* p->xxx.len == MC_PIPE_STREAM_UNREAD: stream p->xxx was not read;
|
|
|
|
* p->xxx.len == MC_PIPE_ERROR_READ: reading error, and p->xxx.errno is set appropriately.
|
|
|
|
*
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @parameter error contains pointer to object to handle error code and message
|
2014-07-03 09:58:57 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2024-06-01 21:12:14 +03:00
|
|
|
mc_pread (mc_pipe_t *p, GError **error)
|
2014-07-03 09:58:57 +04:00
|
|
|
{
|
|
|
|
gboolean read_out, read_err;
|
|
|
|
fd_set fds;
|
|
|
|
int maxfd = 0;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (error != NULL)
|
|
|
|
*error = NULL;
|
|
|
|
|
2021-02-24 14:10:52 +03:00
|
|
|
read_out = p->out.fd >= 0;
|
|
|
|
read_err = p->err.fd >= 0;
|
2014-07-03 09:58:57 +04:00
|
|
|
|
|
|
|
if (!read_out && !read_err)
|
|
|
|
{
|
|
|
|
p->out.len = MC_PIPE_STREAM_UNREAD;
|
|
|
|
p->err.len = MC_PIPE_STREAM_UNREAD;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FD_ZERO (&fds);
|
|
|
|
if (read_out)
|
|
|
|
{
|
|
|
|
FD_SET (p->out.fd, &fds);
|
|
|
|
maxfd = p->out.fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read_err)
|
|
|
|
{
|
|
|
|
FD_SET (p->err.fd, &fds);
|
2016-04-07 10:52:04 +03:00
|
|
|
maxfd = MAX (maxfd, p->err.fd);
|
2014-07-03 09:58:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* no timeout */
|
|
|
|
res = select (maxfd + 1, &fds, NULL, NULL, NULL);
|
|
|
|
if (res < 0 && errno != EINTR)
|
|
|
|
{
|
|
|
|
mc_propagate_error (error, MC_PIPE_ERROR_READ,
|
|
|
|
_
|
|
|
|
("Unexpected error in select() reading data from a child process:\n%s"),
|
|
|
|
unix_error_string (errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read_out)
|
|
|
|
mc_pread_stream (&p->out, &fds);
|
|
|
|
else
|
|
|
|
p->out.len = MC_PIPE_STREAM_UNREAD;
|
|
|
|
|
|
|
|
if (read_err)
|
|
|
|
mc_pread_stream (&p->err, &fds);
|
|
|
|
else
|
|
|
|
p->err.len = MC_PIPE_STREAM_UNREAD;
|
|
|
|
}
|
|
|
|
|
2021-02-10 13:16:18 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Reads a line from @stream. Reading stops after an EOL or a newline. If a newline is read,
|
|
|
|
* it is appended to the line.
|
|
|
|
*
|
|
|
|
* @stream mc_pipe_stream_t object
|
|
|
|
*
|
|
|
|
* @return newly created GString or NULL in case of EOL;
|
|
|
|
*/
|
|
|
|
|
|
|
|
GString *
|
2024-06-01 21:12:14 +03:00
|
|
|
mc_pstream_get_string (mc_pipe_stream_t *ps)
|
2021-02-10 13:16:18 +03:00
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
size_t size, i;
|
|
|
|
gboolean escape = FALSE;
|
|
|
|
|
|
|
|
g_return_val_if_fail (ps != NULL, NULL);
|
|
|
|
|
|
|
|
if (ps->len < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
size = ps->len - ps->pos;
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s = ps->buf + ps->pos;
|
|
|
|
|
|
|
|
if (s[0] == '\0')
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* find '\0' or unescaped '\n' */
|
|
|
|
for (i = 0; i < size && !(s[i] == '\0' || (s[i] == '\n' && !escape)); i++)
|
|
|
|
escape = s[i] == '\\' ? !escape : FALSE;
|
|
|
|
|
|
|
|
if (i != size && s[i] == '\n')
|
|
|
|
i++;
|
|
|
|
|
|
|
|
ps->pos += i;
|
|
|
|
|
|
|
|
return g_string_new_len (s, i);
|
|
|
|
}
|
|
|
|
|
2014-07-03 09:58:57 +04:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Close pipe and destroy pipe descriptor.
|
|
|
|
*
|
Fix various typos in the source code (closes MidnightCommander/mc#177).
Found via `codespell -S
po,doc,./misc/syntax,./src/vfs/extfs/helpers/README.it -L
parm,rouge,sav,ect,vie,te,dum,clen,wee,dynamc,childs,ths,fo,nin,unx,nd,iif,iterm,ser,makrs,wil`
Co-authored-by: Yury V. Zaytsev <yury@shurup.com>
Signed-off-by: Kian-Meng Ang <kianmeng@cpan.org>
Signed-off-by: Yury V. Zaytsev <yury@shurup.com>
2023-01-10 06:02:52 +03:00
|
|
|
* @parameter p pipe descriptor
|
|
|
|
* @parameter error contains pointer to object to handle error code and message
|
2014-07-03 09:58:57 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2024-06-01 21:12:14 +03:00
|
|
|
mc_pclose (mc_pipe_t *p, GError **error)
|
2014-07-03 09:58:57 +04:00
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
2022-08-19 14:35:20 +03:00
|
|
|
if (p == NULL)
|
|
|
|
{
|
|
|
|
mc_replace_error (error, MC_PIPE_ERROR_READ, "%s",
|
|
|
|
_("Cannot close pipe descriptor (p == NULL)"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-03 09:58:57 +04:00
|
|
|
if (p->out.fd >= 0)
|
|
|
|
res = close (p->out.fd);
|
|
|
|
if (p->err.fd >= 0)
|
|
|
|
res = close (p->err.fd);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
res = waitpid (p->child_pid, &status, 0);
|
|
|
|
}
|
|
|
|
while (res < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
if (res < 0)
|
|
|
|
mc_replace_error (error, MC_PIPE_ERROR_READ, _("Unexpected error in waitpid():\n%s"),
|
|
|
|
unix_error_string (errno));
|
|
|
|
|
|
|
|
g_free (p);
|
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
2012-08-31 18:05:29 +04:00
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/**
|
2003-11-27 12:45:22 +03:00
|
|
|
* Perform tilde expansion if possible.
|
2012-08-31 18:05:29 +04:00
|
|
|
*
|
|
|
|
* @param directory pointer to the path
|
|
|
|
*
|
2012-11-05 17:57:50 +04:00
|
|
|
* @return newly allocated string, even if it's unchanged.
|
2003-11-27 12:45:22 +03:00
|
|
|
*/
|
2010-11-08 13:21:45 +03:00
|
|
|
|
2003-11-27 12:45:22 +03:00
|
|
|
char *
|
|
|
|
tilde_expand (const char *directory)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
struct passwd *passwd;
|
2003-11-28 18:23:19 +03:00
|
|
|
const char *p, *q;
|
2003-11-27 12:45:22 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (*directory != '~')
|
2010-04-30 17:31:06 +04:00
|
|
|
return g_strdup (directory);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-11-28 18:23:19 +03:00
|
|
|
p = directory + 1;
|
2003-11-27 12:45:22 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* d = "~" or d = "~/" */
|
2015-01-07 11:34:53 +03:00
|
|
|
if (*p == '\0' || IS_PATH_SEP (*p))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
|
|
|
passwd = getpwuid (geteuid ());
|
2015-01-07 11:34:53 +03:00
|
|
|
q = IS_PATH_SEP (*p) ? p + 1 : "";
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
q = strchr (p, PATH_SEP);
|
2019-01-12 13:28:59 +03:00
|
|
|
if (q == NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
passwd = getpwnam (p);
|
|
|
|
else
|
|
|
|
{
|
2013-10-15 10:34:04 +04:00
|
|
|
char *name;
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
name = g_strndup (p, q - p);
|
|
|
|
passwd = getpwnam (name);
|
|
|
|
q++;
|
|
|
|
g_free (name);
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2003-11-27 12:45:22 +03:00
|
|
|
/* If we can't figure the user name, leave tilde unexpanded */
|
2019-01-12 13:28:59 +03:00
|
|
|
if (passwd == NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
return g_strdup (directory);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-09-24 19:05:28 +04:00
|
|
|
return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
2022-05-07 11:23:42 +03:00
|
|
|
* Canonicalize path.
|
|
|
|
*
|
|
|
|
* @param path path to file
|
|
|
|
* @param flags canonicalization flags
|
|
|
|
*
|
|
|
|
* All modifications of @path are made in place.
|
2004-01-24 03:17:34 +03:00
|
|
|
* Well formed UNC paths are modified only in the local part.
|
|
|
|
*/
|
2010-11-08 13:21:45 +03:00
|
|
|
|
2004-01-24 02:53:37 +03:00
|
|
|
void
|
2022-05-07 11:23:42 +03:00
|
|
|
canonicalize_pathname_custom (char *path, canon_path_flags_t flags)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2003-03-10 09:52:57 +03:00
|
|
|
char *p, *s;
|
2010-04-30 17:31:06 +04:00
|
|
|
char *lpath = path; /* path without leading UNC part */
|
2011-06-14 16:09:59 +04:00
|
|
|
const size_t url_delim_len = strlen (VFS_PATH_URL_DELIMITER);
|
2004-01-24 03:17:34 +03:00
|
|
|
|
|
|
|
/* Detect and preserve UNC paths: //server/... */
|
2015-01-07 11:34:53 +03:00
|
|
|
if ((flags & CANON_PATH_GUARDUNC) != 0 && IS_PATH_SEP (path[0]) && IS_PATH_SEP (path[1]))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2019-01-12 13:28:59 +03:00
|
|
|
for (p = path + 2; p[0] != '\0' && !IS_PATH_SEP (p[0]); p++)
|
|
|
|
;
|
2015-01-07 11:34:53 +03:00
|
|
|
if (IS_PATH_SEP (p[0]) && p > path + 2)
|
2010-04-30 17:31:06 +04:00
|
|
|
lpath = p;
|
2004-01-24 03:17:34 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
if (lpath[0] == '\0' || lpath[1] == '\0')
|
2010-04-30 17:31:06 +04:00
|
|
|
return;
|
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
if ((flags & CANON_PATH_JOINSLASHES) != 0)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
|
|
|
/* Collapse multiple slashes */
|
2019-01-12 13:28:59 +03:00
|
|
|
for (p = lpath; *p != '\0'; p++)
|
2015-01-07 11:34:53 +03:00
|
|
|
if (IS_PATH_SEP (p[0]) && IS_PATH_SEP (p[1]) && (p == lpath || *(p - 1) != ':'))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
|
|
|
s = p + 1;
|
2015-01-07 11:34:53 +03:00
|
|
|
while (IS_PATH_SEP (*(++s)))
|
|
|
|
;
|
2010-04-30 17:31:06 +04:00
|
|
|
str_move (p + 1, s);
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
/* Collapse "/./" -> "/" */
|
2019-01-12 13:28:59 +03:00
|
|
|
for (p = lpath; *p != '\0';)
|
2015-01-07 11:34:53 +03:00
|
|
|
if (IS_PATH_SEP (p[0]) && p[1] == '.' && IS_PATH_SEP (p[2]))
|
2010-04-30 17:31:06 +04:00
|
|
|
str_move (p, p + 2);
|
|
|
|
else
|
|
|
|
p++;
|
2003-03-10 09:52:57 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
if ((flags & CANON_PATH_REMSLASHDOTS) != 0)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2013-10-15 10:34:04 +04:00
|
|
|
size_t len;
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
/* Remove trailing slashes */
|
2019-01-12 13:28:59 +03:00
|
|
|
for (p = lpath + strlen (lpath) - 1; p > lpath && IS_PATH_SEP (*p); p--)
|
2011-06-14 16:09:59 +04:00
|
|
|
{
|
2015-04-11 12:47:52 +03:00
|
|
|
if (p >= lpath + url_delim_len - 1
|
2011-06-14 16:09:59 +04:00
|
|
|
&& strncmp (p - url_delim_len + 1, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
|
|
|
|
break;
|
2019-01-12 13:28:59 +03:00
|
|
|
*p = '\0';
|
2011-06-14 16:09:59 +04:00
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
|
|
|
|
/* Remove leading "./" */
|
2015-01-07 11:34:53 +03:00
|
|
|
if (lpath[0] == '.' && IS_PATH_SEP (lpath[1]))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2019-01-12 13:28:59 +03:00
|
|
|
if (lpath[2] == '\0')
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2019-01-12 13:28:59 +03:00
|
|
|
lpath[1] = '\0';
|
2010-04-30 17:31:06 +04:00
|
|
|
return;
|
|
|
|
}
|
2019-01-12 13:28:59 +03:00
|
|
|
|
|
|
|
str_move (lpath, lpath + 2);
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove trailing "/" or "/." */
|
|
|
|
len = strlen (lpath);
|
|
|
|
if (len < 2)
|
|
|
|
return;
|
2019-01-12 13:28:59 +03:00
|
|
|
|
2015-01-07 11:34:53 +03:00
|
|
|
if (IS_PATH_SEP (lpath[len - 1])
|
2011-06-14 16:09:59 +04:00
|
|
|
&& (len < url_delim_len
|
|
|
|
|| strncmp (lpath + len - url_delim_len, VFS_PATH_URL_DELIMITER,
|
|
|
|
url_delim_len) != 0))
|
|
|
|
lpath[len - 1] = '\0';
|
2019-01-12 13:28:59 +03:00
|
|
|
else if (lpath[len - 1] == '.' && IS_PATH_SEP (lpath[len - 2]))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2019-01-12 13:28:59 +03:00
|
|
|
if (len == 2)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2019-01-12 13:28:59 +03:00
|
|
|
lpath[1] = '\0';
|
|
|
|
return;
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2019-01-12 13:28:59 +03:00
|
|
|
|
|
|
|
lpath[len - 2] = '\0';
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2009-11-23 11:45:46 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
/* Collapse "/.." with the previous part of path */
|
|
|
|
if ((flags & CANON_PATH_REMDOUBLEDOTS) != 0)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2012-04-30 15:52:39 +04:00
|
|
|
#ifdef HAVE_CHARSET
|
2010-09-10 11:27:21 +04:00
|
|
|
const size_t enc_prefix_len = strlen (VFS_ENCODING_PREFIX);
|
2012-04-30 15:52:39 +04:00
|
|
|
#endif /* HAVE_CHARSET */
|
2010-09-10 11:27:21 +04:00
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
for (p = lpath; p[0] != '\0' && p[1] != '\0' && p[2] != '\0';)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2015-01-07 11:34:53 +03:00
|
|
|
if (!IS_PATH_SEP (p[0]) || p[1] != '.' || p[2] != '.'
|
|
|
|
|| (!IS_PATH_SEP (p[3]) && p[3] != '\0'))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
|
|
|
p++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* search for the previous token */
|
|
|
|
s = p - 1;
|
2011-06-14 16:09:59 +04:00
|
|
|
if (s >= lpath + url_delim_len - 2
|
|
|
|
&& strncmp (s - url_delim_len + 2, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
|
|
|
|
{
|
|
|
|
s -= (url_delim_len - 2);
|
2015-01-07 11:34:53 +03:00
|
|
|
while (s >= lpath && !IS_PATH_SEP (*s--))
|
|
|
|
;
|
2011-06-14 16:09:59 +04:00
|
|
|
}
|
|
|
|
|
2011-06-07 17:35:17 +04:00
|
|
|
while (s >= lpath)
|
|
|
|
{
|
2011-06-14 16:09:59 +04:00
|
|
|
if (s - url_delim_len > lpath
|
|
|
|
&& strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
|
2011-06-07 17:35:17 +04:00
|
|
|
{
|
2011-06-14 16:09:59 +04:00
|
|
|
char *vfs_prefix = s - url_delim_len;
|
2016-03-03 22:51:40 +03:00
|
|
|
vfs_class *vclass;
|
2011-06-14 16:09:59 +04:00
|
|
|
|
2015-01-07 11:34:53 +03:00
|
|
|
while (vfs_prefix > lpath && !IS_PATH_SEP (*--vfs_prefix))
|
|
|
|
;
|
|
|
|
if (IS_PATH_SEP (*vfs_prefix))
|
2011-06-14 16:09:59 +04:00
|
|
|
vfs_prefix++;
|
|
|
|
*(s - url_delim_len) = '\0';
|
|
|
|
|
|
|
|
vclass = vfs_prefix_to_class (vfs_prefix);
|
|
|
|
*(s - url_delim_len) = *VFS_PATH_URL_DELIMITER;
|
|
|
|
|
2019-11-18 21:24:13 +03:00
|
|
|
if (vclass != NULL && (vclass->flags & VFSF_REMOTE) != 0)
|
2011-06-14 16:09:59 +04:00
|
|
|
{
|
2018-09-11 14:54:28 +03:00
|
|
|
s = vfs_prefix;
|
|
|
|
continue;
|
2011-06-14 16:09:59 +04:00
|
|
|
}
|
2011-06-07 17:35:17 +04:00
|
|
|
}
|
2011-06-14 16:09:59 +04:00
|
|
|
|
2015-01-07 11:34:53 +03:00
|
|
|
if (IS_PATH_SEP (*s))
|
2011-06-30 14:26:03 +04:00
|
|
|
break;
|
2011-06-14 02:32:15 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
s--;
|
2011-06-07 17:35:17 +04:00
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
/* If the previous token is "..", we cannot collapse it */
|
|
|
|
if (s[0] == '.' && s[1] == '.' && s + 2 == p)
|
|
|
|
{
|
|
|
|
p += 3;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-01-12 13:28:59 +03:00
|
|
|
if (p[3] != '\0')
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2015-01-07 11:34:53 +03:00
|
|
|
if (s == lpath && IS_PATH_SEP (*s))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
|
|
|
/* "/../foo" -> "/foo" */
|
|
|
|
str_move (s + 1, p + 4);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* "token/../foo" -> "foo" */
|
2012-02-06 21:43:01 +04:00
|
|
|
#ifdef HAVE_CHARSET
|
2010-09-13 13:32:27 +04:00
|
|
|
if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
|
|
|
|
&& (is_supported_encoding (s + enc_prefix_len)))
|
2010-09-10 11:27:21 +04:00
|
|
|
/* special case: remove encoding */
|
2010-09-09 16:50:26 +04:00
|
|
|
str_move (s, p + 1);
|
|
|
|
else
|
|
|
|
#endif /* HAVE_CHARSET */
|
|
|
|
str_move (s, p + 4);
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2019-01-12 13:28:59 +03:00
|
|
|
|
|
|
|
p = s > lpath ? s - 1 : s;
|
2010-04-30 17:31:06 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* trailing ".." */
|
|
|
|
if (s == lpath)
|
|
|
|
{
|
|
|
|
/* "token/.." -> "." */
|
2015-01-07 11:34:53 +03:00
|
|
|
if (!IS_PATH_SEP (lpath[0]))
|
2010-04-30 17:31:06 +04:00
|
|
|
lpath[0] = '.';
|
2015-01-07 11:34:53 +03:00
|
|
|
lpath[1] = '\0';
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* "foo/token/.." -> "foo" */
|
|
|
|
if (s == lpath + 1)
|
2015-01-07 11:34:53 +03:00
|
|
|
s[0] = '\0';
|
2012-02-06 21:43:01 +04:00
|
|
|
#ifdef HAVE_CHARSET
|
2010-09-13 13:32:27 +04:00
|
|
|
else if ((strncmp (s, VFS_ENCODING_PREFIX, enc_prefix_len) == 0)
|
2010-11-08 13:21:45 +03:00
|
|
|
&& (is_supported_encoding (s + enc_prefix_len)))
|
2010-09-09 16:50:26 +04:00
|
|
|
{
|
|
|
|
/* special case: remove encoding */
|
|
|
|
s[0] = '.';
|
|
|
|
s[1] = '.';
|
|
|
|
s[2] = '\0';
|
|
|
|
|
|
|
|
/* search for the previous token */
|
2015-01-07 11:34:53 +03:00
|
|
|
/* IS_PATH_SEP (s[-1]) */
|
2019-01-12 13:28:59 +03:00
|
|
|
for (p = s - 1; p >= lpath && !IS_PATH_SEP (*p); p--)
|
|
|
|
;
|
2010-09-09 16:50:26 +04:00
|
|
|
|
2013-09-20 13:13:56 +04:00
|
|
|
if (p >= lpath)
|
2010-09-09 16:50:26 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_CHARSET */
|
2010-04-30 17:31:06 +04:00
|
|
|
else
|
2011-06-14 16:09:59 +04:00
|
|
|
{
|
|
|
|
if (s >= lpath + url_delim_len
|
|
|
|
&& strncmp (s - url_delim_len, VFS_PATH_URL_DELIMITER, url_delim_len) == 0)
|
|
|
|
*s = '\0';
|
|
|
|
else
|
|
|
|
s[-1] = '\0';
|
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
2002-07-30 03:12:31 +04:00
|
|
|
|
2005-01-13 22:37:46 +03:00
|
|
|
char *
|
2010-05-13 21:28:12 +04:00
|
|
|
mc_realpath (const char *path, char *resolved_path)
|
2005-01-13 22:37:46 +03:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
#ifdef HAVE_CHARSET
|
|
|
|
const char *p = path;
|
|
|
|
gboolean absolute_path = FALSE;
|
2005-01-13 22:37:46 +03:00
|
|
|
|
2017-12-30 15:29:27 +03:00
|
|
|
if (IS_PATH_SEP (*p))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
absolute_path = TRUE;
|
|
|
|
p++;
|
2005-01-13 22:37:46 +03:00
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
|
|
|
|
/* ignore encoding: skip "#enc:" */
|
|
|
|
if (g_str_has_prefix (p, VFS_ENCODING_PREFIX))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
p += strlen (VFS_ENCODING_PREFIX);
|
|
|
|
p = strchr (p, PATH_SEP);
|
|
|
|
if (p != NULL)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
if (!absolute_path && p[1] != '\0')
|
|
|
|
p++;
|
|
|
|
|
|
|
|
path = p;
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
}
|
|
|
|
#endif /* HAVE_CHARSET */
|
|
|
|
|
|
|
|
#ifdef HAVE_REALPATH
|
|
|
|
return realpath (path, resolved_path);
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
char copy_path[PATH_MAX];
|
|
|
|
char got_path[PATH_MAX];
|
|
|
|
char *new_path = got_path;
|
|
|
|
char *max_path;
|
|
|
|
#ifdef S_IFLNK
|
|
|
|
char link_path[PATH_MAX];
|
|
|
|
int readlinks = 0;
|
|
|
|
int n;
|
|
|
|
#endif /* S_IFLNK */
|
|
|
|
|
|
|
|
/* Make a copy of the source path since we may need to modify it. */
|
|
|
|
if (strlen (path) >= PATH_MAX - 2)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
return NULL;
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2019-01-12 13:28:59 +03:00
|
|
|
|
2017-12-30 15:29:27 +03:00
|
|
|
strcpy (copy_path, path);
|
|
|
|
path = copy_path;
|
|
|
|
max_path = copy_path + PATH_MAX - 2;
|
|
|
|
/* If it's a relative pathname use getwd for starters. */
|
|
|
|
if (!IS_PATH_SEP (*path))
|
|
|
|
{
|
|
|
|
new_path = g_get_current_dir ();
|
|
|
|
if (new_path == NULL)
|
|
|
|
strcpy (got_path, "");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_snprintf (got_path, sizeof (got_path), "%s", new_path);
|
|
|
|
g_free (new_path);
|
|
|
|
new_path = got_path;
|
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
|
2017-12-30 15:29:27 +03:00
|
|
|
new_path += strlen (got_path);
|
|
|
|
if (!IS_PATH_SEP (new_path[-1]))
|
|
|
|
*new_path++ = PATH_SEP;
|
|
|
|
}
|
|
|
|
else
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
*new_path++ = PATH_SEP;
|
2010-04-30 17:31:06 +04:00
|
|
|
path++;
|
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
/* Expand each slash-separated pathname component. */
|
|
|
|
while (*path != '\0')
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
/* Ignore stray "/". */
|
|
|
|
if (IS_PATH_SEP (*path))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
|
|
|
path++;
|
|
|
|
continue;
|
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
if (*path == '.')
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
/* Ignore ".". */
|
|
|
|
if (path[1] == '\0' || IS_PATH_SEP (path[1]))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
path++;
|
2010-04-30 17:31:06 +04:00
|
|
|
continue;
|
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
if (path[1] == '.')
|
|
|
|
{
|
|
|
|
if (path[2] == '\0' || IS_PATH_SEP (path[2]))
|
|
|
|
{
|
|
|
|
path += 2;
|
|
|
|
/* Ignore ".." at root. */
|
|
|
|
if (new_path == got_path + 1)
|
|
|
|
continue;
|
|
|
|
/* Handle ".." by backing up. */
|
|
|
|
while (!IS_PATH_SEP ((--new_path)[-1]))
|
|
|
|
;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
/* Safely copy the next pathname component. */
|
|
|
|
while (*path != '\0' && !IS_PATH_SEP (*path))
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
if (path > max_path)
|
|
|
|
{
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*new_path++ = *path++;
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
2005-01-13 22:37:46 +03:00
|
|
|
#ifdef S_IFLNK
|
2017-12-30 15:29:27 +03:00
|
|
|
/* Protect against infinite loops. */
|
|
|
|
if (readlinks++ > MAXSYMLINKS)
|
2010-04-30 17:31:06 +04:00
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
errno = ELOOP;
|
2010-04-30 17:31:06 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2017-12-30 15:29:27 +03:00
|
|
|
/* See if latest pathname component is a symlink. */
|
|
|
|
*new_path = '\0';
|
|
|
|
n = readlink (got_path, link_path, PATH_MAX - 1);
|
|
|
|
if (n < 0)
|
|
|
|
{
|
|
|
|
/* EINVAL means the file exists but isn't a symlink. */
|
|
|
|
if (errno != EINVAL)
|
|
|
|
{
|
|
|
|
/* Make sure it's null terminated. */
|
|
|
|
*new_path = '\0';
|
|
|
|
strcpy (resolved_path, got_path);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2010-04-30 17:31:06 +04:00
|
|
|
else
|
|
|
|
{
|
2017-12-30 15:29:27 +03:00
|
|
|
/* Note: readlink doesn't add the null byte. */
|
|
|
|
link_path[n] = '\0';
|
|
|
|
if (IS_PATH_SEP (*link_path))
|
|
|
|
/* Start over for an absolute symlink. */
|
|
|
|
new_path = got_path;
|
|
|
|
else
|
|
|
|
/* Otherwise back up over this component. */
|
|
|
|
while (!IS_PATH_SEP (*(--new_path)))
|
|
|
|
;
|
|
|
|
/* Safe sex check. */
|
|
|
|
if (strlen (path) + n >= PATH_MAX - 2)
|
|
|
|
{
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Insert symlink contents into path. */
|
|
|
|
strcat (link_path, path);
|
|
|
|
strcpy (copy_path, link_path);
|
|
|
|
path = copy_path;
|
2010-04-30 17:31:06 +04:00
|
|
|
}
|
|
|
|
#endif /* S_IFLNK */
|
2017-12-30 15:29:27 +03:00
|
|
|
*new_path++ = PATH_SEP;
|
|
|
|
}
|
|
|
|
/* Delete trailing slash but don't whomp a lone slash. */
|
|
|
|
if (new_path != got_path + 1 && IS_PATH_SEP (new_path[-1]))
|
|
|
|
new_path--;
|
|
|
|
/* Make sure it's null terminated. */
|
|
|
|
*new_path = '\0';
|
|
|
|
strcpy (resolved_path, got_path);
|
|
|
|
return resolved_path;
|
2005-01-13 22:37:46 +03:00
|
|
|
}
|
2010-05-13 21:28:12 +04:00
|
|
|
#endif /* HAVE_REALPATH */
|
2017-12-30 15:29:27 +03:00
|
|
|
}
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-11-08 13:21:45 +03:00
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
|
|
* Return the index of the permissions triplet
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2005-07-23 01:16:50 +04:00
|
|
|
int
|
2010-04-30 17:31:06 +04:00
|
|
|
get_user_permissions (struct stat *st)
|
|
|
|
{
|
2009-02-06 01:46:07 +03:00
|
|
|
static gboolean initialized = FALSE;
|
2005-07-23 01:16:50 +04:00
|
|
|
static gid_t *groups;
|
|
|
|
static int ngroups;
|
|
|
|
static uid_t uid;
|
|
|
|
int i;
|
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
if (!initialized)
|
|
|
|
{
|
|
|
|
uid = geteuid ();
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
ngroups = getgroups (0, NULL);
|
|
|
|
if (ngroups == -1)
|
|
|
|
ngroups = 0; /* ignore errors */
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
/* allocate space for one element in addition to what
|
|
|
|
* will be filled by getgroups(). */
|
|
|
|
groups = g_new (gid_t, ngroups + 1);
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
if (ngroups != 0)
|
|
|
|
{
|
|
|
|
ngroups = getgroups (ngroups, groups);
|
|
|
|
if (ngroups == -1)
|
|
|
|
ngroups = 0; /* ignore errors */
|
|
|
|
}
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
/* getgroups() may or may not return the effective group ID,
|
|
|
|
* so we always include it at the end of the list. */
|
|
|
|
groups[ngroups++] = getegid ();
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
initialized = TRUE;
|
2005-07-23 01:16:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (st->st_uid == uid || uid == 0)
|
2010-04-30 17:31:06 +04:00
|
|
|
return 0;
|
2005-07-23 01:16:50 +04:00
|
|
|
|
2010-04-30 17:31:06 +04:00
|
|
|
for (i = 0; i < ngroups; i++)
|
|
|
|
if (st->st_gid == groups[i])
|
|
|
|
return 1;
|
2005-07-23 01:16:50 +04:00
|
|
|
|
|
|
|
return 2;
|
|
|
|
}
|
2010-11-08 13:21:45 +03:00
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
2011-06-17 21:17:16 +04:00
|
|
|
/**
|
|
|
|
* Build filename from arguments.
|
|
|
|
* Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
|
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
2011-07-17 20:59:47 +04:00
|
|
|
mc_build_filenamev (const char *first_element, va_list args)
|
2011-06-17 21:17:16 +04:00
|
|
|
{
|
2011-09-20 09:21:11 +04:00
|
|
|
gboolean absolute;
|
2011-06-17 21:17:16 +04:00
|
|
|
const char *element = first_element;
|
|
|
|
GString *path;
|
|
|
|
char *ret;
|
|
|
|
|
2024-02-11 19:56:08 +03:00
|
|
|
if (first_element == NULL)
|
2011-06-17 21:17:16 +04:00
|
|
|
return NULL;
|
|
|
|
|
2015-01-07 11:34:53 +03:00
|
|
|
absolute = IS_PATH_SEP (*first_element);
|
2011-09-20 09:21:11 +04:00
|
|
|
|
2024-02-11 19:56:08 +03:00
|
|
|
path = g_string_new (absolute ? PATH_SEP_STR : "");
|
|
|
|
|
2011-06-17 21:17:16 +04:00
|
|
|
do
|
|
|
|
{
|
2011-09-19 14:43:05 +04:00
|
|
|
if (*element == '\0')
|
|
|
|
element = va_arg (args, char *);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *tmp_element;
|
|
|
|
const char *start;
|
2011-06-17 21:17:16 +04:00
|
|
|
|
2011-09-19 14:43:05 +04:00
|
|
|
tmp_element = g_strdup (element);
|
2011-06-17 21:17:16 +04:00
|
|
|
|
2011-09-19 14:43:05 +04:00
|
|
|
element = va_arg (args, char *);
|
2011-06-17 21:17:16 +04:00
|
|
|
|
2011-09-19 14:43:05 +04:00
|
|
|
canonicalize_pathname (tmp_element);
|
2015-01-07 11:34:53 +03:00
|
|
|
start = IS_PATH_SEP (tmp_element[0]) ? tmp_element + 1 : tmp_element;
|
2011-06-17 21:17:16 +04:00
|
|
|
|
2011-09-19 14:43:05 +04:00
|
|
|
g_string_append (path, start);
|
2024-02-11 19:56:08 +03:00
|
|
|
if (!IS_PATH_SEP (path->str[path->len - 1]) && element != NULL)
|
2011-09-19 14:43:05 +04:00
|
|
|
g_string_append_c (path, PATH_SEP);
|
2011-06-17 21:17:16 +04:00
|
|
|
|
2011-09-19 14:43:05 +04:00
|
|
|
g_free (tmp_element);
|
|
|
|
}
|
2011-06-17 21:17:16 +04:00
|
|
|
}
|
|
|
|
while (element != NULL);
|
|
|
|
|
|
|
|
ret = g_string_free (path, FALSE);
|
|
|
|
canonicalize_pathname (ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
2011-07-17 20:59:47 +04:00
|
|
|
/**
|
|
|
|
* Build filename from arguments.
|
|
|
|
* Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
|
|
|
|
*/
|
|
|
|
|
|
|
|
char *
|
|
|
|
mc_build_filename (const char *first_element, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
if (first_element == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
va_start (args, first_element);
|
|
|
|
ret = mc_build_filenamev (first_element, args);
|
|
|
|
va_end (args);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|