mirror of
https://github.com/MidnightCommander/mc
synced 2025-01-18 17:29:28 +03:00
Merge branch '4191_tar.xz'
* 4191_tar.xz: (open_error_pipe, close_error_pipe): remove. (extfs_read_archive): move file adding to separate function. extfs: reimplement using mc_pipe. (sfs_vfmake): reimplement using mc_pipe. (do_external_panelize): reimplement using mc_pipe. (mc_pstream_get_string): new API. (mc_popen): add two parameters: Ticket #4191: unable to browse tar.xz archives.
This commit is contained in:
commit
318981535f
20
lib/util.h
20
lib/util.h
@ -87,11 +87,12 @@ typedef struct
|
|||||||
int fd;
|
int fd;
|
||||||
/* data read from fd */
|
/* data read from fd */
|
||||||
char buf[MC_PIPE_BUFSIZE];
|
char buf[MC_PIPE_BUFSIZE];
|
||||||
/* positive: length of data in buf as before read as after;
|
/* current position in @buf (used by mc_pstream_get_string()) */
|
||||||
* zero or negative before read: do not read drom fd;
|
size_t pos;
|
||||||
* MC_PIPE_STREAM_EOF after read: EOF of fd;
|
/* positive: length of data in buf;
|
||||||
* MC_PIPE_STREAM_UNREAD after read: there was not read from fd;
|
* MC_PIPE_STREAM_EOF: EOF of fd;
|
||||||
* MC_PIPE_ERROR_READ after read: reading error from fd.
|
* MC_PIPE_STREAM_UNREAD: there was not read from fd;
|
||||||
|
* MC_PIPE_ERROR_READ: reading error from fd.
|
||||||
*/
|
*/
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
/* whether buf is null-terminated or not */
|
/* whether buf is null-terminated or not */
|
||||||
@ -207,21 +208,18 @@ const char *get_owner (uid_t uid);
|
|||||||
/* Returns a copy of *s until a \n is found and is below top */
|
/* Returns a copy of *s until a \n is found and is below top */
|
||||||
const char *extract_line (const char *s, const char *top);
|
const char *extract_line (const char *s, const char *top);
|
||||||
|
|
||||||
/* Error pipes */
|
|
||||||
void open_error_pipe (void);
|
|
||||||
void check_error_pipe (void);
|
|
||||||
int close_error_pipe (int error, const char *text);
|
|
||||||
|
|
||||||
/* Process spawning */
|
/* Process spawning */
|
||||||
int my_system (int flags, const char *shell, const char *command);
|
int my_system (int flags, const char *shell, const char *command);
|
||||||
int my_systeml (int flags, const char *shell, ...);
|
int my_systeml (int flags, const char *shell, ...);
|
||||||
int my_systemv (const char *command, char *const argv[]);
|
int my_systemv (const char *command, char *const argv[]);
|
||||||
int my_systemv_flags (int flags, const char *command, char *const argv[]);
|
int my_systemv_flags (int flags, const char *command, char *const argv[]);
|
||||||
|
|
||||||
mc_pipe_t *mc_popen (const char *command, GError ** error);
|
mc_pipe_t *mc_popen (const char *command, gboolean read_out, gboolean read_err, GError ** error);
|
||||||
void mc_pread (mc_pipe_t * p, GError ** error);
|
void mc_pread (mc_pipe_t * p, GError ** error);
|
||||||
void mc_pclose (mc_pipe_t * p, GError ** error);
|
void mc_pclose (mc_pipe_t * p, GError ** error);
|
||||||
|
|
||||||
|
GString *mc_pstream_get_string (mc_pipe_stream_t * ps);
|
||||||
|
|
||||||
void my_exit (int status);
|
void my_exit (int status);
|
||||||
void save_stop_handler (void);
|
void save_stop_handler (void);
|
||||||
|
|
||||||
|
192
lib/utilunix.c
192
lib/utilunix.c
@ -53,12 +53,6 @@
|
|||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#ifdef HAVE_SYS_IOCTL_H
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_GET_PROCESS_STATS
|
|
||||||
#include <sys/procstats.h>
|
|
||||||
#endif
|
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
|
||||||
@ -117,9 +111,6 @@ typedef struct
|
|||||||
static int_cache uid_cache[UID_CACHE_SIZE];
|
static int_cache uid_cache[UID_CACHE_SIZE];
|
||||||
static int_cache gid_cache[GID_CACHE_SIZE];
|
static int_cache gid_cache[GID_CACHE_SIZE];
|
||||||
|
|
||||||
static int error_pipe[2]; /* File descriptors of error pipe */
|
|
||||||
static int old_error; /* File descriptor of old standard error */
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
/*** file scope functions ************************************************************************/
|
/*** file scope functions ************************************************************************/
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
@ -277,6 +268,8 @@ mc_pread_stream (mc_pipe_stream_t * ps, const fd_set * fds)
|
|||||||
if (ps->null_term)
|
if (ps->null_term)
|
||||||
ps->buf[(size_t) ps->len] = '\0';
|
ps->buf[(size_t) ps->len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ps->pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
@ -503,13 +496,15 @@ my_systemv_flags (int flags, const char *command, char *const argv[])
|
|||||||
* Create pipe and run child process.
|
* Create pipe and run child process.
|
||||||
*
|
*
|
||||||
* @parameter command command line of child process
|
* @parameter command command line of child process
|
||||||
|
* @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
|
||||||
* @paremeter error contains pointer to object to handle error code and message
|
* @paremeter error contains pointer to object to handle error code and message
|
||||||
*
|
*
|
||||||
* @return newly created object of mc_pipe_t class in success, NULL otherwise
|
* @return newly created object of mc_pipe_t class in success, NULL otherwise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mc_pipe_t *
|
mc_pipe_t *
|
||||||
mc_popen (const char *command, GError ** error)
|
mc_popen (const char *command, gboolean read_out, gboolean read_err, GError ** error)
|
||||||
{
|
{
|
||||||
mc_pipe_t *p;
|
mc_pipe_t *p;
|
||||||
const char *const argv[] = { "/bin/sh", "sh", "-c", command, NULL };
|
const char *const argv[] = { "/bin/sh", "sh", "-c", command, NULL };
|
||||||
@ -522,9 +517,13 @@ mc_popen (const char *command, GError ** error)
|
|||||||
goto ret_err;
|
goto ret_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->out.fd = -1;
|
||||||
|
p->err.fd = -1;
|
||||||
|
|
||||||
if (!g_spawn_async_with_pipes
|
if (!g_spawn_async_with_pipes
|
||||||
(NULL, (gchar **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_FILE_AND_ARGV_ZERO,
|
(NULL, (gchar **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_FILE_AND_ARGV_ZERO, NULL,
|
||||||
NULL, NULL, &p->child_pid, NULL, &p->out.fd, &p->err.fd, error))
|
NULL, &p->child_pid, NULL, read_out ? &p->out.fd : NULL, read_err ? &p->err.fd : NULL,
|
||||||
|
error))
|
||||||
{
|
{
|
||||||
mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE_STREAM, "%s",
|
mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE_STREAM, "%s",
|
||||||
_("Cannot create pipe streams"));
|
_("Cannot create pipe streams"));
|
||||||
@ -553,9 +552,9 @@ mc_popen (const char *command, GError ** error)
|
|||||||
* @parameter p pipe descriptor
|
* @parameter p pipe descriptor
|
||||||
*
|
*
|
||||||
* The lengths of read data contain in p->out.len and p->err.len.
|
* The lengths of read data contain in p->out.len and p->err.len.
|
||||||
* Before read, p->xxx.len is an input:
|
*
|
||||||
* p->xxx.len > 0: do read stream p->xxx and store data in p->xxx.buf;
|
* Before read, p->xxx.len is an input. It defines the number of data to read.
|
||||||
* p->xxx.len <= 0: do not read stream p->xxx.
|
* Should not be greater than MC_PIPE_BUFSIZE.
|
||||||
*
|
*
|
||||||
* After read, p->xxx.len is an output and contains the following:
|
* 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 > 0: an actual length of read data stored in p->xxx.buf;
|
||||||
@ -577,8 +576,8 @@ mc_pread (mc_pipe_t * p, GError ** error)
|
|||||||
if (error != NULL)
|
if (error != NULL)
|
||||||
*error = NULL;
|
*error = NULL;
|
||||||
|
|
||||||
read_out = p->out.fd >= 0 && p->out.len > 0;
|
read_out = p->out.fd >= 0;
|
||||||
read_err = p->err.fd >= 0 && p->err.len > 0;
|
read_err = p->err.fd >= 0;
|
||||||
|
|
||||||
if (!read_out && !read_err)
|
if (!read_out && !read_err)
|
||||||
{
|
{
|
||||||
@ -622,6 +621,50 @@ mc_pread (mc_pipe_t * p, GError ** error)
|
|||||||
p->err.len = MC_PIPE_STREAM_UNREAD;
|
p->err.len = MC_PIPE_STREAM_UNREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* 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 *
|
||||||
|
mc_pstream_get_string (mc_pipe_stream_t * ps)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
/**
|
/**
|
||||||
* Close pipe and destroy pipe descriptor.
|
* Close pipe and destroy pipe descriptor.
|
||||||
@ -705,121 +748,6 @@ tilde_expand (const char *directory)
|
|||||||
return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
|
return g_strconcat (passwd->pw_dir, PATH_SEP_STR, q, (char *) NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
|
||||||
/**
|
|
||||||
* Creates a pipe to hold standard error for a later analysis.
|
|
||||||
* The pipe can hold 4096 bytes. Make sure no more is written
|
|
||||||
* or a deadlock might occur.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
open_error_pipe (void)
|
|
||||||
{
|
|
||||||
int error_fd = -1;
|
|
||||||
|
|
||||||
if (pipe (error_pipe) < 0)
|
|
||||||
message (D_NORMAL, _("Warning"), _("Pipe failed"));
|
|
||||||
|
|
||||||
old_error = dup (STDERR_FILENO);
|
|
||||||
if (old_error < 0 || close (STDERR_FILENO) != 0
|
|
||||||
|| (error_fd = dup (error_pipe[1])) != STDERR_FILENO)
|
|
||||||
{
|
|
||||||
message (D_NORMAL, _("Warning"), _("Dup failed"));
|
|
||||||
|
|
||||||
close (error_pipe[0]);
|
|
||||||
error_pipe[0] = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Settng stderr in nonblocking mode as we close it earlier, than
|
|
||||||
* program stops. We try to read some error at program startup,
|
|
||||||
* but we should not block on it.
|
|
||||||
*
|
|
||||||
* TODO: make piped stdin/stderr poll()/select()able to get rid
|
|
||||||
* of following hack.
|
|
||||||
*/
|
|
||||||
int fd_flags;
|
|
||||||
|
|
||||||
fd_flags = fcntl (error_pipe[0], F_GETFL, NULL);
|
|
||||||
if (fd_flags != -1)
|
|
||||||
{
|
|
||||||
fd_flags |= O_NONBLOCK;
|
|
||||||
|
|
||||||
if (fcntl (error_pipe[0], F_SETFL, fd_flags) == -1)
|
|
||||||
{
|
|
||||||
/* TODO: handle it somehow */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error_fd >= 0)
|
|
||||||
close (error_fd);
|
|
||||||
/* we never write there */
|
|
||||||
close (error_pipe[1]);
|
|
||||||
error_pipe[1] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
|
||||||
/**
|
|
||||||
* Close a pipe
|
|
||||||
*
|
|
||||||
* @param error '-1' - ignore errors, '0' - display warning, '1' - display error
|
|
||||||
* @param text is prepended to the error message from the pipe
|
|
||||||
*
|
|
||||||
* @return not 0 if an error was displayed
|
|
||||||
*/
|
|
||||||
|
|
||||||
int
|
|
||||||
close_error_pipe (int error, const char *text)
|
|
||||||
{
|
|
||||||
const char *title;
|
|
||||||
char msg[MAX_PIPE_SIZE];
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
/* already closed */
|
|
||||||
if (error_pipe[0] == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (error < 0 || (error > 0 && (error & D_ERROR) != 0))
|
|
||||||
title = MSG_ERROR;
|
|
||||||
else
|
|
||||||
title = _("Warning");
|
|
||||||
if (old_error >= 0)
|
|
||||||
{
|
|
||||||
if (dup2 (old_error, STDERR_FILENO) == -1)
|
|
||||||
{
|
|
||||||
if (error < 0)
|
|
||||||
error = D_ERROR;
|
|
||||||
|
|
||||||
message (error, MSG_ERROR, _("Error dup'ing old error pipe"));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
close (old_error);
|
|
||||||
len = read (error_pipe[0], msg, sizeof (msg) - 1);
|
|
||||||
|
|
||||||
if (len >= 0)
|
|
||||||
msg[len] = 0;
|
|
||||||
close (error_pipe[0]);
|
|
||||||
error_pipe[0] = -1;
|
|
||||||
}
|
|
||||||
if (error < 0)
|
|
||||||
return 0; /* Just ignore error message */
|
|
||||||
if (text == NULL)
|
|
||||||
{
|
|
||||||
if (len <= 0)
|
|
||||||
return 0; /* Nothing to show */
|
|
||||||
|
|
||||||
/* Show message from pipe */
|
|
||||||
message (error, title, "%s", msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Show given text and possible message from pipe */
|
|
||||||
message (error, title, "%s\n%s", text, msg);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
/**
|
/**
|
||||||
* Canonicalize path, and return a new path. Do everything in place.
|
* Canonicalize path, and return a new path. Do everything in place.
|
||||||
|
@ -109,7 +109,7 @@ clipboard_file_from_ext_clip (const gchar * event_group_name, const gchar * even
|
|||||||
if (clipboard_paste_path == NULL || clipboard_paste_path[0] == '\0')
|
if (clipboard_paste_path == NULL || clipboard_paste_path[0] == '\0')
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
p = mc_popen (clipboard_paste_path, NULL);
|
p = mc_popen (clipboard_paste_path, TRUE, TRUE, NULL);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return TRUE; /* don't show error message */
|
return TRUE; /* don't show error message */
|
||||||
|
|
||||||
|
@ -306,15 +306,18 @@ static void
|
|||||||
do_external_panelize (char *command)
|
do_external_panelize (char *command)
|
||||||
{
|
{
|
||||||
dir_list *list = ¤t_panel->dir;
|
dir_list *list = ¤t_panel->dir;
|
||||||
FILE *external;
|
mc_pipe_t *external;
|
||||||
|
GError *error = NULL;
|
||||||
|
GString *remain_file_name = NULL;
|
||||||
|
|
||||||
open_error_pipe ();
|
external = mc_popen (command, TRUE, TRUE, &error);
|
||||||
external = popen (command, "r");
|
|
||||||
if (external == NULL)
|
if (external == NULL)
|
||||||
{
|
{
|
||||||
close_error_pipe (D_ERROR, _("Cannot invoke command."));
|
message (D_ERROR, _("External panelize"), "%s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the counters and the directory list */
|
/* Clear the counters and the directory list */
|
||||||
panel_clean_dir (current_panel);
|
panel_clean_dir (current_panel);
|
||||||
|
|
||||||
@ -324,48 +327,109 @@ do_external_panelize (char *command)
|
|||||||
|
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
{
|
{
|
||||||
char line[MC_MAXPATHLEN];
|
GString *line;
|
||||||
size_t len;
|
gboolean ok;
|
||||||
char *name;
|
|
||||||
gboolean link_to_dir, stale_link;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
clearerr (external);
|
/* init buffers before call of mc_pread() */
|
||||||
if (fgets (line, sizeof (line), external) == NULL)
|
external->out.len = MC_PIPE_BUFSIZE;
|
||||||
|
external->err.len = MC_PIPE_BUFSIZE;
|
||||||
|
|
||||||
|
mc_pread (external, &error);
|
||||||
|
|
||||||
|
if (error != NULL)
|
||||||
{
|
{
|
||||||
if (ferror (external) != 0 && errno == EINTR)
|
message (D_ERROR, MSG_ERROR, _("External panelize:\n%s"), error->message);
|
||||||
continue;
|
g_error_free (error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = strlen (line);
|
if (external->err.len > 0)
|
||||||
if (line[len - 1] == '\n')
|
message (D_ERROR, MSG_ERROR, _("External panelize:\n%s"), external->err.buf);
|
||||||
line[len - 1] = '\0';
|
|
||||||
if (line[0] == '\0')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
name = line;
|
if (external->out.len == MC_PIPE_STREAM_EOF)
|
||||||
if (line[0] == '.' && IS_PATH_SEP (line[1]))
|
|
||||||
name += 2;
|
|
||||||
|
|
||||||
if (!handle_path (name, &st, &link_to_dir, &stale_link))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!dir_list_append (list, name, &st, link_to_dir, stale_link))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
file_mark (current_panel, list->len - 1, 0);
|
if (external->out.len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
if ((list->len & 31) == 0)
|
if (external->out.len == MC_PIPE_ERROR_READ)
|
||||||
rotate_dash (TRUE);
|
{
|
||||||
|
message (D_ERROR, MSG_ERROR,
|
||||||
|
_("External panelize:\nfailed to read data from child stdout:\n%s"),
|
||||||
|
unix_error_string (external->out.error));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = TRUE;
|
||||||
|
|
||||||
|
while (ok && (line = mc_pstream_get_string (&external->out)) != NULL)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
gboolean link_to_dir, stale_link;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
/* handle a \n-separated file list */
|
||||||
|
|
||||||
|
if (line->str[line->len - 1] == '\n')
|
||||||
|
{
|
||||||
|
/* entire file name or last chunk */
|
||||||
|
|
||||||
|
g_string_truncate (line, line->len - 1);
|
||||||
|
|
||||||
|
/* join filename chunks */
|
||||||
|
if (remain_file_name != NULL)
|
||||||
|
{
|
||||||
|
g_string_append_len (remain_file_name, line->str, line->len);
|
||||||
|
g_string_free (line, TRUE);
|
||||||
|
line = remain_file_name;
|
||||||
|
remain_file_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* first or middle chunk of file name */
|
||||||
|
|
||||||
|
if (remain_file_name == NULL)
|
||||||
|
remain_file_name = line;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_string_append_len (remain_file_name, line->str, line->len);
|
||||||
|
g_string_free (line, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = line->str;
|
||||||
|
|
||||||
|
if (name[0] == '.' && IS_PATH_SEP (name[1]))
|
||||||
|
name += 2;
|
||||||
|
|
||||||
|
if (handle_path (name, &st, &link_to_dir, &stale_link))
|
||||||
|
{
|
||||||
|
ok = dir_list_append (list, name, &st, link_to_dir, stale_link);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
file_mark (current_panel, list->len - 1, 0);
|
||||||
|
|
||||||
|
if ((list->len & 31) == 0)
|
||||||
|
rotate_dash (TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_string_free (line, TRUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (remain_file_name != NULL)
|
||||||
|
g_string_free (remain_file_name, TRUE);
|
||||||
|
|
||||||
|
mc_pclose (external, NULL);
|
||||||
|
|
||||||
current_panel->is_panelized = TRUE;
|
current_panel->is_panelized = TRUE;
|
||||||
panelize_absolutize_if_needed (current_panel);
|
panelize_absolutize_if_needed (current_panel);
|
||||||
|
|
||||||
if (pclose (external) < 0)
|
|
||||||
message (D_NORMAL, _("External panelize"), _("Pipe close failed"));
|
|
||||||
close_error_pipe (D_NORMAL, NULL);
|
|
||||||
try_to_select (current_panel, NULL);
|
try_to_select (current_panel, NULL);
|
||||||
panel_re_sort (current_panel);
|
panel_re_sort (current_panel);
|
||||||
rotate_dash (FALSE);
|
rotate_dash (FALSE);
|
||||||
|
@ -392,12 +392,128 @@ extfs_skip_leading_dotslash (char *s)
|
|||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static FILE *
|
static int
|
||||||
extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc)
|
extfs_add_file (struct extfs_super_t *archive, const char *file_name)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super = VFS_SUPER (archive);
|
||||||
|
struct stat hstat;
|
||||||
|
char *current_file_name = NULL, *current_link_name = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (vfs_parse_ls_lga (file_name, &hstat, ¤t_file_name, ¤t_link_name, NULL))
|
||||||
|
{
|
||||||
|
char *cfn = current_file_name;
|
||||||
|
|
||||||
|
if (*cfn != '\0')
|
||||||
|
{
|
||||||
|
struct vfs_s_entry *entry;
|
||||||
|
struct vfs_s_entry *pent = NULL;
|
||||||
|
struct vfs_s_inode *inode;
|
||||||
|
char *p, *q;
|
||||||
|
|
||||||
|
cfn = extfs_skip_leading_dotslash (cfn);
|
||||||
|
if (IS_PATH_SEP (*cfn))
|
||||||
|
cfn++;
|
||||||
|
p = strchr (cfn, '\0');
|
||||||
|
if (p != cfn && IS_PATH_SEP (p[-1]))
|
||||||
|
p[-1] = '\0';
|
||||||
|
p = strrchr (cfn, PATH_SEP);
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
p = cfn;
|
||||||
|
q = strchr (cfn, '\0');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(p++) = '\0';
|
||||||
|
q = cfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*q != '\0')
|
||||||
|
{
|
||||||
|
pent = extfs_find_entry (super->root, q, FL_MKDIR);
|
||||||
|
if (pent == NULL)
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pent != NULL)
|
||||||
|
{
|
||||||
|
entry = extfs_entry_new (super->me, p, pent->ino);
|
||||||
|
entry->dir = pent->ino;
|
||||||
|
g_queue_push_tail (pent->ino->subdir, entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry = extfs_entry_new (super->me, p, super->root);
|
||||||
|
entry->dir = super->root;
|
||||||
|
g_queue_push_tail (super->root->subdir, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
|
||||||
|
{
|
||||||
|
pent = extfs_find_entry (super->root, current_link_name, FL_NONE);
|
||||||
|
if (pent == NULL)
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
pent->ino->st.st_nlink++;
|
||||||
|
entry->ino = pent->ino;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
memset (&st, 0, sizeof (st));
|
||||||
|
st.st_ino = super->ino_usage++;
|
||||||
|
st.st_nlink = 1;
|
||||||
|
st.st_dev = archive->rdev;
|
||||||
|
st.st_mode = hstat.st_mode;
|
||||||
|
#ifdef HAVE_STRUCT_STAT_ST_RDEV
|
||||||
|
st.st_rdev = hstat.st_rdev;
|
||||||
|
#endif
|
||||||
|
st.st_uid = hstat.st_uid;
|
||||||
|
st.st_gid = hstat.st_gid;
|
||||||
|
st.st_size = hstat.st_size;
|
||||||
|
st.st_mtime = hstat.st_mtime;
|
||||||
|
st.st_atime = hstat.st_atime;
|
||||||
|
st.st_ctime = hstat.st_ctime;
|
||||||
|
|
||||||
|
if (current_link_name == NULL && S_ISLNK (hstat.st_mode))
|
||||||
|
st.st_mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
|
||||||
|
|
||||||
|
inode = vfs_s_new_inode (super->me, super, &st);
|
||||||
|
inode->ent = entry;
|
||||||
|
entry->ino = inode;
|
||||||
|
|
||||||
|
if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
|
||||||
|
{
|
||||||
|
inode->linkname = current_link_name;
|
||||||
|
current_link_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
g_free (current_file_name);
|
||||||
|
g_free (current_link_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static mc_pipe_t *
|
||||||
|
extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc, GError ** error)
|
||||||
{
|
{
|
||||||
const extfs_plugin_info_t *info;
|
const extfs_plugin_info_t *info;
|
||||||
static dev_t archive_counter = 0;
|
static dev_t archive_counter = 0;
|
||||||
FILE *result = NULL;
|
mc_pipe_t *result = NULL;
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
struct stat mystat;
|
struct stat mystat;
|
||||||
@ -432,12 +548,11 @@ extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc)
|
|||||||
vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
|
vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
|
||||||
g_free (tmp);
|
g_free (tmp);
|
||||||
|
|
||||||
open_error_pipe ();
|
result = mc_popen (cmd, TRUE, TRUE, error);
|
||||||
result = popen (cmd, "r");
|
|
||||||
g_free (cmd);
|
g_free (cmd);
|
||||||
|
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
{
|
{
|
||||||
close_error_pipe (D_ERROR, NULL);
|
|
||||||
if (local_name_vpath != NULL)
|
if (local_name_vpath != NULL)
|
||||||
{
|
{
|
||||||
mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
|
mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
|
||||||
@ -446,10 +561,6 @@ extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc)
|
|||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ___QNXNTO__
|
|
||||||
setvbuf (result, NULL, _IONBF, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
current_archive = extfs_super_new (vfs_extfs_ops, name, local_name_vpath, fstype);
|
current_archive = extfs_super_new (vfs_extfs_ops, name, local_name_vpath, fstype);
|
||||||
current_archive->rdev = archive_counter++;
|
current_archive->rdev = archive_counter++;
|
||||||
vfs_path_free (local_name_vpath);
|
vfs_path_free (local_name_vpath);
|
||||||
@ -486,120 +597,96 @@ extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
extfs_read_archive (FILE * extfsd, struct extfs_super_t *current_archive)
|
extfs_read_archive (mc_pipe_t * pip, struct extfs_super_t *archive, GError ** error)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
char *buffer;
|
GString *buffer;
|
||||||
struct vfs_s_super *super = VFS_SUPER (current_archive);
|
GString *err_msg = NULL;
|
||||||
|
GString *remain_file_name = NULL;
|
||||||
|
|
||||||
buffer = g_malloc (BUF_4K);
|
while (ret != -1)
|
||||||
|
|
||||||
while (fgets (buffer, BUF_4K, extfsd) != NULL)
|
|
||||||
{
|
{
|
||||||
struct stat hstat;
|
/* init buffers before call of mc_pread() */
|
||||||
char *current_file_name = NULL, *current_link_name = NULL;
|
pip->out.len = MC_PIPE_BUFSIZE;
|
||||||
|
pip->err.len = MC_PIPE_BUFSIZE;
|
||||||
|
|
||||||
if (vfs_parse_ls_lga (buffer, &hstat, ¤t_file_name, ¤t_link_name, NULL))
|
mc_pread (pip, error);
|
||||||
|
|
||||||
|
if (*error != NULL)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if (pip->err.len > 0)
|
||||||
{
|
{
|
||||||
struct vfs_s_entry *entry, *pent = NULL;
|
/* join errors/warnings */
|
||||||
struct vfs_s_inode *inode;
|
if (err_msg == NULL)
|
||||||
char *p, *q, *cfn = current_file_name;
|
err_msg = g_string_new_len (pip->err.buf, pip->err.len);
|
||||||
|
else
|
||||||
if (*cfn != '\0')
|
|
||||||
{
|
{
|
||||||
cfn = extfs_skip_leading_dotslash (cfn);
|
if (err_msg->str[err_msg->len - 1] != '\n')
|
||||||
if (IS_PATH_SEP (*cfn))
|
g_string_append_c (err_msg, '\n');
|
||||||
cfn++;
|
g_string_append_len (err_msg, pip->err.buf, pip->err.len);
|
||||||
p = strchr (cfn, '\0');
|
}
|
||||||
if (p != cfn && IS_PATH_SEP (p[-1]))
|
}
|
||||||
p[-1] = '\0';
|
|
||||||
p = strrchr (cfn, PATH_SEP);
|
if (pip->out.len == MC_PIPE_STREAM_EOF)
|
||||||
if (p == NULL)
|
break;
|
||||||
|
|
||||||
|
if (pip->out.len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (pip->out.len == MC_PIPE_ERROR_READ)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
while (ret != -1 && (buffer = mc_pstream_get_string (&pip->out)) != NULL)
|
||||||
|
{
|
||||||
|
/* handle a \n-separated file list */
|
||||||
|
|
||||||
|
if (buffer->str[buffer->len - 1] == '\n')
|
||||||
|
{
|
||||||
|
/* entire file name or last chunk */
|
||||||
|
|
||||||
|
g_string_truncate (buffer, buffer->len - 1);
|
||||||
|
|
||||||
|
/* join filename chunks */
|
||||||
|
if (remain_file_name != NULL)
|
||||||
{
|
{
|
||||||
p = cfn;
|
g_string_append_len (remain_file_name, buffer->str, buffer->len);
|
||||||
q = strchr (cfn, '\0');
|
g_string_free (buffer, TRUE);
|
||||||
}
|
buffer = remain_file_name;
|
||||||
else
|
remain_file_name = NULL;
|
||||||
{
|
|
||||||
*(p++) = '\0';
|
|
||||||
q = cfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*q != '\0')
|
|
||||||
{
|
|
||||||
pent = extfs_find_entry (super->root, q, FL_MKDIR);
|
|
||||||
if (pent == NULL)
|
|
||||||
{
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pent != NULL)
|
|
||||||
{
|
|
||||||
entry = extfs_entry_new (super->me, p, pent->ino);
|
|
||||||
entry->dir = pent->ino;
|
|
||||||
g_queue_push_tail (pent->ino->subdir, entry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
entry = extfs_entry_new (super->me, p, super->root);
|
|
||||||
entry->dir = super->root;
|
|
||||||
g_queue_push_tail (super->root->subdir, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
|
|
||||||
{
|
|
||||||
pent = extfs_find_entry (super->root, current_link_name, FL_NONE);
|
|
||||||
if (pent == NULL)
|
|
||||||
{
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pent->ino->st.st_nlink++;
|
|
||||||
entry->ino = pent->ino;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
memset (&st, 0, sizeof (st));
|
|
||||||
st.st_ino = super->ino_usage++;
|
|
||||||
st.st_nlink = 1;
|
|
||||||
st.st_dev = current_archive->rdev;
|
|
||||||
st.st_mode = hstat.st_mode;
|
|
||||||
#ifdef HAVE_STRUCT_STAT_ST_RDEV
|
|
||||||
st.st_rdev = hstat.st_rdev;
|
|
||||||
#endif
|
|
||||||
st.st_uid = hstat.st_uid;
|
|
||||||
st.st_gid = hstat.st_gid;
|
|
||||||
st.st_size = hstat.st_size;
|
|
||||||
st.st_mtime = hstat.st_mtime;
|
|
||||||
st.st_atime = hstat.st_atime;
|
|
||||||
st.st_ctime = hstat.st_ctime;
|
|
||||||
|
|
||||||
if (current_link_name == NULL && S_ISLNK (hstat.st_mode))
|
|
||||||
st.st_mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
|
|
||||||
|
|
||||||
inode = vfs_s_new_inode (super->me, super, &st);
|
|
||||||
inode->ent = entry;
|
|
||||||
entry->ino = inode;
|
|
||||||
|
|
||||||
if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
|
|
||||||
{
|
|
||||||
inode->linkname = current_link_name;
|
|
||||||
current_link_name = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* first or middle chunk of file name */
|
||||||
|
|
||||||
g_free (current_file_name);
|
if (remain_file_name == NULL)
|
||||||
g_free (current_link_name);
|
remain_file_name = buffer;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_string_append_len (remain_file_name, buffer->str, buffer->len);
|
||||||
|
g_string_free (buffer, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = extfs_add_file (archive, buffer->str);
|
||||||
|
|
||||||
|
g_string_free (buffer, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free (buffer);
|
if (remain_file_name != NULL)
|
||||||
|
g_string_free (remain_file_name, TRUE);
|
||||||
|
|
||||||
|
if (err_msg != NULL)
|
||||||
|
{
|
||||||
|
if (*error == NULL)
|
||||||
|
mc_propagate_error (error, 0, "%s", err_msg->str);
|
||||||
|
|
||||||
|
g_string_free (err_msg, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -635,35 +722,39 @@ static int
|
|||||||
extfs_open_and_read_archive (int fstype, const char *name, struct extfs_super_t **archive)
|
extfs_open_and_read_archive (int fstype, const char *name, struct extfs_super_t **archive)
|
||||||
{
|
{
|
||||||
int result = -1;
|
int result = -1;
|
||||||
FILE *extfsd;
|
|
||||||
struct extfs_super_t *a;
|
struct extfs_super_t *a;
|
||||||
|
mc_pipe_t *pip;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
pip = extfs_open_archive (fstype, name, archive, &error);
|
||||||
|
|
||||||
extfsd = extfs_open_archive (fstype, name, archive);
|
|
||||||
a = *archive;
|
a = *archive;
|
||||||
|
|
||||||
if (extfsd == NULL)
|
if (pip == NULL)
|
||||||
{
|
{
|
||||||
const extfs_plugin_info_t *info;
|
const extfs_plugin_info_t *info;
|
||||||
|
|
||||||
info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
|
info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
|
||||||
message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
|
message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s:\n%s"), info->prefix, name,
|
||||||
}
|
error->message);
|
||||||
else if (extfs_read_archive (extfsd, a) != 0)
|
g_error_free (error);
|
||||||
{
|
|
||||||
pclose (extfsd);
|
|
||||||
close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
|
|
||||||
}
|
|
||||||
else if (pclose (extfsd) != 0)
|
|
||||||
{
|
|
||||||
VFS_SUPER (a)->me->free (VFS_SUPER (a));
|
|
||||||
close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
close_error_pipe (D_ERROR, NULL);
|
result = extfs_read_archive (pip, a, &error);
|
||||||
result = 0;
|
|
||||||
|
if (result != 0)
|
||||||
|
VFS_SUPER (a)->me->free (VFS_SUPER (a));
|
||||||
|
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mc_pclose (pip, NULL);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,7 +918,9 @@ extfs_cmd (const char *str_extfs_cmd, const struct extfs_super_t *archive,
|
|||||||
char *archive_name, *quoted_archive_name;
|
char *archive_name, *quoted_archive_name;
|
||||||
const extfs_plugin_info_t *info;
|
const extfs_plugin_info_t *info;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int retval;
|
int retval = 0;
|
||||||
|
GError *error = NULL;
|
||||||
|
mc_pipe_t *pip;
|
||||||
|
|
||||||
file = extfs_get_path_from_entry (entry);
|
file = extfs_get_path_from_entry (entry);
|
||||||
quoted_file = name_quote (file, FALSE);
|
quoted_file = name_quote (file, FALSE);
|
||||||
@ -847,10 +940,29 @@ extfs_cmd (const char *str_extfs_cmd, const struct extfs_super_t *archive,
|
|||||||
g_free (quoted_localname);
|
g_free (quoted_localname);
|
||||||
g_free (quoted_archive_name);
|
g_free (quoted_archive_name);
|
||||||
|
|
||||||
open_error_pipe ();
|
/* don't read stdout */
|
||||||
retval = my_system (EXECUTE_AS_SHELL, mc_global.shell->path, cmd);
|
pip = mc_popen (cmd, FALSE, TRUE, &error);
|
||||||
g_free (cmd);
|
g_free (cmd);
|
||||||
close_error_pipe (D_ERROR, NULL);
|
|
||||||
|
if (pip == NULL)
|
||||||
|
{
|
||||||
|
message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mc_pread (pip, &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
retval = -1;
|
||||||
|
}
|
||||||
|
else if (pip->err.len > 0)
|
||||||
|
message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), pip->err.buf);
|
||||||
|
|
||||||
|
mc_pclose (pip, NULL);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +140,8 @@ sfs_vfmake (const vfs_path_t * vpath, vfs_path_t * cache_vpath)
|
|||||||
vfs_path_t *pname; /* name of parent archive */
|
vfs_path_t *pname; /* name of parent archive */
|
||||||
char *pqname; /* name of parent archive, quoted */
|
char *pqname; /* name of parent archive, quoted */
|
||||||
const vfs_path_element_t *path_element;
|
const vfs_path_element_t *path_element;
|
||||||
|
mc_pipe_t *pip;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
path_element = vfs_path_get_by_index (vpath, -1);
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
pname = vfs_path_clone (vpath);
|
pname = vfs_path_clone (vpath);
|
||||||
@ -216,14 +218,29 @@ sfs_vfmake (const vfs_path_t * vpath, vfs_path_t * cache_vpath)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_free (pqname);
|
g_free (pqname);
|
||||||
open_error_pipe ();
|
|
||||||
if (my_system (EXECUTE_AS_SHELL, "/bin/sh", pad))
|
/* don't read stdout */
|
||||||
|
pip = mc_popen (pad, FALSE, TRUE, &error);
|
||||||
|
if (pip == NULL)
|
||||||
{
|
{
|
||||||
close_error_pipe (D_ERROR, NULL);
|
message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), error->message);
|
||||||
|
g_error_free (error);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
close_error_pipe (D_NORMAL, NULL);
|
mc_pread (pip, &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
mc_pclose (pip, NULL);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pip->err.len > 0)
|
||||||
|
message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), pip->err.buf);
|
||||||
|
|
||||||
|
mc_pclose (pip, NULL);
|
||||||
return 0; /* OK */
|
return 0; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@ mcview_load_command_output (WView * view, const char *command)
|
|||||||
|
|
||||||
mcview_close_datasource (view);
|
mcview_close_datasource (view);
|
||||||
|
|
||||||
p = mc_popen (command, &error);
|
p = mc_popen (command, TRUE, TRUE, &error);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
{
|
{
|
||||||
mcview_display (view);
|
mcview_display (view);
|
||||||
|
@ -20,6 +20,7 @@ TESTS = \
|
|||||||
mc_build_filename \
|
mc_build_filename \
|
||||||
name_quote \
|
name_quote \
|
||||||
serialize \
|
serialize \
|
||||||
|
utilunix__mc_pstream_get_string \
|
||||||
utilunix__my_system_fork_fail \
|
utilunix__my_system_fork_fail \
|
||||||
utilunix__my_system_fork_child_shell \
|
utilunix__my_system_fork_child_shell \
|
||||||
utilunix__my_system_fork_child \
|
utilunix__my_system_fork_child \
|
||||||
@ -46,6 +47,9 @@ name_quote_SOURCES = \
|
|||||||
serialize_SOURCES = \
|
serialize_SOURCES = \
|
||||||
serialize.c
|
serialize.c
|
||||||
|
|
||||||
|
utilunix__mc_pstream_get_string_SOURCES = \
|
||||||
|
utilunix__mc_pstream_get_string.c
|
||||||
|
|
||||||
utilunix__my_system_fork_fail_SOURCES = \
|
utilunix__my_system_fork_fail_SOURCES = \
|
||||||
utilunix__my_system-fork_fail.c
|
utilunix__my_system-fork_fail.c
|
||||||
|
|
||||||
|
396
tests/lib/utilunix__mc_pstream_get_string.c
Normal file
396
tests/lib/utilunix__mc_pstream_get_string.c
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
/*
|
||||||
|
lib - Read string from mc_pipe_stream
|
||||||
|
|
||||||
|
Copyright (C) 2021
|
||||||
|
Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Andrew Borodin <aborodin@vmail.ru>, 2021
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TEST_SUITE_NAME "/lib/util"
|
||||||
|
|
||||||
|
#include "tests/mctest.h"
|
||||||
|
|
||||||
|
#include "lib/util.h"
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#define MAX_CHUNKS 8
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static mc_pipe_stream_t stream;
|
||||||
|
|
||||||
|
static char etalon_long_file_list[BUF_1K];
|
||||||
|
static size_t etalon_long_file_list_pos;
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* @Before */
|
||||||
|
static void
|
||||||
|
setup (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @After */
|
||||||
|
static void
|
||||||
|
teardown (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* @DataSource("data_source") */
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
static const struct data_source
|
||||||
|
{
|
||||||
|
/* input */
|
||||||
|
const char *buf; /* string to read */
|
||||||
|
|
||||||
|
/* output */
|
||||||
|
int pos[MAX_CHUNKS]; /* ps.pos values */
|
||||||
|
const char *str[MAX_CHUNKS]; /* chunks */
|
||||||
|
size_t len[MAX_CHUNKS]; /* chunk lengths */
|
||||||
|
}
|
||||||
|
data_source[] =
|
||||||
|
{
|
||||||
|
/* 0 */
|
||||||
|
{
|
||||||
|
.buf = "",
|
||||||
|
.pos = { 0 },
|
||||||
|
.str = { "" },
|
||||||
|
.len = { 0 }
|
||||||
|
},
|
||||||
|
/* 1 */
|
||||||
|
{
|
||||||
|
.buf = "\n",
|
||||||
|
.pos = { 0, 1 },
|
||||||
|
.str = { "\n" },
|
||||||
|
.len = { 1, 0 }
|
||||||
|
},
|
||||||
|
/* 2 */
|
||||||
|
{
|
||||||
|
.buf = "\\\n",
|
||||||
|
.pos = { 0, 2 },
|
||||||
|
.str = { "\\\n" },
|
||||||
|
.len = { 2, 0 }
|
||||||
|
},
|
||||||
|
/* 3 */
|
||||||
|
{
|
||||||
|
.buf = "\\\\\n",
|
||||||
|
.pos = { 0, 3 },
|
||||||
|
.str = { "\\\\\n" },
|
||||||
|
.len = { 3, 0 }
|
||||||
|
},
|
||||||
|
/* 4 */
|
||||||
|
{
|
||||||
|
.buf = "\\\\\\\n",
|
||||||
|
.pos = { 0, 4 },
|
||||||
|
.str = { "\\\\\\\n" },
|
||||||
|
.len = { 4, 0 }
|
||||||
|
},
|
||||||
|
/* 5 */
|
||||||
|
{
|
||||||
|
.buf = "\\\\\\\\\n",
|
||||||
|
.pos = { 0, 5 },
|
||||||
|
.str = { "\\\\\\\\\n" },
|
||||||
|
.len = { 5, 0 }
|
||||||
|
},
|
||||||
|
/* 6 */
|
||||||
|
{
|
||||||
|
.buf = "12345",
|
||||||
|
.pos = { 0, 5 },
|
||||||
|
.str = { "12345" },
|
||||||
|
.len = { 5, 0 }
|
||||||
|
},
|
||||||
|
/* 7 */
|
||||||
|
{
|
||||||
|
.buf = "12345\n",
|
||||||
|
.pos = { 0, 6 },
|
||||||
|
.str = { "12345\n" },
|
||||||
|
.len = { 6, 0 }
|
||||||
|
},
|
||||||
|
/* 8 */
|
||||||
|
{
|
||||||
|
.buf = "12345\\\n",
|
||||||
|
.pos = { 0, 7 },
|
||||||
|
.str = { "12345\\\n" },
|
||||||
|
.len = { 7, 0 }
|
||||||
|
},
|
||||||
|
/* 9 */
|
||||||
|
{
|
||||||
|
.buf = "12345\\\\\n",
|
||||||
|
.pos = { 0, 8 },
|
||||||
|
.str = { "12345\\\\\n" },
|
||||||
|
.len = { 8, 0 }
|
||||||
|
},
|
||||||
|
/* 10 */
|
||||||
|
{
|
||||||
|
.buf = "12345\nabcd",
|
||||||
|
.pos = { 0, 6, 10 },
|
||||||
|
.str = { "12345\n", "abcd" },
|
||||||
|
.len = { 6, 4, 0 }
|
||||||
|
},
|
||||||
|
/* 11 */
|
||||||
|
{
|
||||||
|
.buf = "12345\\\nabcd",
|
||||||
|
.pos = { 0, 11 },
|
||||||
|
.str = { "12345\\\nabcd" },
|
||||||
|
.len = { 11, 0 }
|
||||||
|
},
|
||||||
|
/* 12 */
|
||||||
|
{
|
||||||
|
.buf = "12345\\\\\nabcd",
|
||||||
|
.pos = { 0, 8, 12 },
|
||||||
|
.str = { "12345\\\\\n", "abcd" },
|
||||||
|
.len = { 8, 4, 0 }
|
||||||
|
},
|
||||||
|
/* 13 */
|
||||||
|
{
|
||||||
|
.buf = "12345\\\\\\\nabcd",
|
||||||
|
.pos = { 0, 13 },
|
||||||
|
.str = { "12345\\\\\\\nabcd" },
|
||||||
|
.len = { 13, 0 }
|
||||||
|
},
|
||||||
|
/* 14 */
|
||||||
|
{
|
||||||
|
.buf = "12345\\\\\\\\\nabcd",
|
||||||
|
.pos = { 0, 10, 14 },
|
||||||
|
.str = { "12345\\\\\\\\\n", "abcd" },
|
||||||
|
.len = { 10, 4, 0 }
|
||||||
|
},
|
||||||
|
/* 15 */
|
||||||
|
{
|
||||||
|
.buf = "12345\nabcd\n",
|
||||||
|
.pos = { 0, 6, 11 },
|
||||||
|
.str = { "12345\n", "abcd\n" },
|
||||||
|
.len = { 6, 5, 0 }
|
||||||
|
},
|
||||||
|
/* 16 */
|
||||||
|
{
|
||||||
|
.buf = "12345\nabcd\n~!@#$%^",
|
||||||
|
.pos = { 0, 6, 11, 18 },
|
||||||
|
.str = { "12345\n", "abcd\n", "~!@#$%^" },
|
||||||
|
.len = { 6, 5, 7, 0 }
|
||||||
|
},
|
||||||
|
/* 17 */
|
||||||
|
{
|
||||||
|
.buf = "12345\nabcd\n~!@#$%^\n",
|
||||||
|
.pos = { 0, 6, 11, 19 },
|
||||||
|
.str = { "12345\n", "abcd\n", "~!@#$%^\n" },
|
||||||
|
.len = { 6, 5, 8, 0 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/* @Test(dataSource = "data_source") */
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
START_PARAMETRIZED_TEST (mc_pstream_get_string_test, data_source)
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
{
|
||||||
|
/* given */
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
/* when */
|
||||||
|
memset (&stream, 0, sizeof (stream));
|
||||||
|
stream.len = strlen (data->buf);
|
||||||
|
memmove (&stream.buf, data->buf, stream.len);
|
||||||
|
|
||||||
|
/* then */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
GString *ret;
|
||||||
|
|
||||||
|
mctest_assert_int_eq (stream.pos, data->pos[j]);
|
||||||
|
|
||||||
|
ret = mc_pstream_get_string (&stream);
|
||||||
|
if (ret == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
mctest_assert_int_eq (ret->len, data->len[j]);
|
||||||
|
mctest_assert_str_eq (ret->str, data->str[j]);
|
||||||
|
|
||||||
|
g_string_free (ret, TRUE);
|
||||||
|
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
while (TRUE);
|
||||||
|
}
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
END_PARAMETRIZED_TEST
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static mc_pipe_t *
|
||||||
|
test_mc_popen (void)
|
||||||
|
{
|
||||||
|
mc_pipe_t *p;
|
||||||
|
|
||||||
|
p = g_try_new0 (mc_pipe_t, 1);
|
||||||
|
/* make less than sizeof (etalon_long_file_list) */
|
||||||
|
p->out.len = 128;
|
||||||
|
|
||||||
|
etalon_long_file_list_pos = 0;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_mc_pread (mc_pipe_t * p)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
p->out.pos = 0;
|
||||||
|
|
||||||
|
if (etalon_long_file_list_pos >= sizeof (etalon_long_file_list))
|
||||||
|
{
|
||||||
|
etalon_long_file_list_pos = sizeof (etalon_long_file_list);
|
||||||
|
p->out.len = MC_PIPE_STREAM_EOF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = sizeof (etalon_long_file_list) - etalon_long_file_list_pos;
|
||||||
|
len = MIN (len, (size_t) p->out.len);
|
||||||
|
memmove (p->out.buf, etalon_long_file_list + etalon_long_file_list_pos, len);
|
||||||
|
p->out.len = (ssize_t) len;
|
||||||
|
|
||||||
|
etalon_long_file_list_pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
START_TEST (mc_pstream_get_long_file_list_test)
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
{
|
||||||
|
/* given */
|
||||||
|
GString *result_long_file_list = NULL;
|
||||||
|
mc_pipe_t *pip;
|
||||||
|
GString *remain_file_name = NULL;
|
||||||
|
|
||||||
|
/* when */
|
||||||
|
/* fill the list */
|
||||||
|
memset (etalon_long_file_list, 'a', sizeof (etalon_long_file_list) - 1);
|
||||||
|
/* create an \n-separated list */
|
||||||
|
etalon_long_file_list[5] = '\n';
|
||||||
|
etalon_long_file_list[25] = '\n';
|
||||||
|
etalon_long_file_list[50] = '\n';
|
||||||
|
etalon_long_file_list[75] = '\n';
|
||||||
|
etalon_long_file_list[127] = '\n';
|
||||||
|
etalon_long_file_list[200] = '\n';
|
||||||
|
etalon_long_file_list[310] = '\n';
|
||||||
|
etalon_long_file_list[325] = '\n';
|
||||||
|
etalon_long_file_list[360] = '\n';
|
||||||
|
etalon_long_file_list[512] = '\n';
|
||||||
|
etalon_long_file_list[701] = '\n';
|
||||||
|
etalon_long_file_list[725] = '\n';
|
||||||
|
etalon_long_file_list[800] = '\n';
|
||||||
|
etalon_long_file_list[sizeof (etalon_long_file_list) - 2] = '\n';
|
||||||
|
etalon_long_file_list[sizeof (etalon_long_file_list) - 1] = '\0';
|
||||||
|
|
||||||
|
/* then */
|
||||||
|
/* read file list */
|
||||||
|
pip = test_mc_popen ();
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
GString *line;
|
||||||
|
|
||||||
|
test_mc_pread (pip);
|
||||||
|
|
||||||
|
if (pip->out.len == MC_PIPE_STREAM_EOF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
while ((line = mc_pstream_get_string (&pip->out)) != NULL)
|
||||||
|
{
|
||||||
|
/* handle an \n-separated file list */
|
||||||
|
|
||||||
|
if (line->str[line->len - 1] == '\n')
|
||||||
|
{
|
||||||
|
/* entire file name or last chunk */
|
||||||
|
|
||||||
|
g_string_truncate (line, line->len - 1);
|
||||||
|
|
||||||
|
/* join filename chunks */
|
||||||
|
if (remain_file_name != NULL)
|
||||||
|
{
|
||||||
|
g_string_append_len (remain_file_name, line->str, line->len);
|
||||||
|
g_string_free (line, TRUE);
|
||||||
|
line = remain_file_name;
|
||||||
|
remain_file_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* first or middle chunk of file name */
|
||||||
|
if (remain_file_name == NULL)
|
||||||
|
remain_file_name = line;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_string_append_len (remain_file_name, line->str, line->len);
|
||||||
|
g_string_free (line, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* collect file names to assemble the result string */
|
||||||
|
if (line == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (result_long_file_list == NULL)
|
||||||
|
result_long_file_list = line;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_string_append_len (result_long_file_list, line->str, line->len);
|
||||||
|
g_string_free (line, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_string_append_c (result_long_file_list, '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mctest_assert_str_eq (etalon_long_file_list, result_long_file_list->str);
|
||||||
|
g_string_free (result_long_file_list, TRUE);
|
||||||
|
|
||||||
|
}
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
END_TEST
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
TCase *tc_core;
|
||||||
|
|
||||||
|
tc_core = tcase_create ("Core");
|
||||||
|
|
||||||
|
tcase_add_checked_fixture (tc_core, setup, teardown);
|
||||||
|
|
||||||
|
/* Add new tests here: *************** */
|
||||||
|
mctest_add_parameterized_test (tc_core, mc_pstream_get_string_test, data_source);
|
||||||
|
tcase_add_test (tc_core, mc_pstream_get_long_file_list_test);
|
||||||
|
/* *********************************** */
|
||||||
|
|
||||||
|
return mctest_run_all (tc_core);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
Loading…
Reference in New Issue
Block a user