2005-06-22 15:13:12 -07:00
|
|
|
/*
|
2005-07-07 04:06:23 -07:00
|
|
|
* mooterm/mootermpt-unix.c
|
2005-06-22 15:13:12 -07:00
|
|
|
*
|
2006-02-23 06:03:17 -08:00
|
|
|
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
2005-06-22 15:13:12 -07:00
|
|
|
*
|
|
|
|
* This program 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* See COPYING file that comes with this distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2005-07-01 02:53:39 -07:00
|
|
|
#define MOOTERM_COMPILATION
|
2005-07-10 19:54:58 -07:00
|
|
|
#include "mooterm/mootermpt-private.h"
|
2005-07-01 02:53:39 -07:00
|
|
|
#include "mooterm/mooterm-private.h"
|
2005-06-22 15:13:12 -07:00
|
|
|
#include "mooterm/pty.h"
|
|
|
|
#include "mooutils/moomarshals.h"
|
|
|
|
#include "mooutils/moocompat.h"
|
2006-11-01 22:38:00 -08:00
|
|
|
#include "mooutils/mooutils-misc.h"
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
2005-07-14 23:34:31 -07:00
|
|
|
#include <termios.h>
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
#ifdef HAVE_POLL_H
|
2005-07-26 04:12:40 -07:00
|
|
|
# include <poll.h>
|
2005-06-22 15:13:12 -07:00
|
|
|
#elif HAVE_SYS_POLL_H
|
2005-07-26 04:12:40 -07:00
|
|
|
# include <sys/poll.h>
|
2005-06-22 15:13:12 -07:00
|
|
|
#endif
|
|
|
|
|
2005-06-23 06:50:25 -07:00
|
|
|
#define TERM_EMULATION "xterm"
|
2005-07-10 19:54:58 -07:00
|
|
|
#define WRITE_CHUNK_SIZE 4096
|
2005-06-23 06:50:25 -07:00
|
|
|
#define POLL_TIME 5
|
|
|
|
#define POLL_NUM 1
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
#define MOO_TERM_PT_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOO_TYPE_TERM_PT_UNIX, MooTermPtUnix))
|
|
|
|
#define MOO_TERM_PT_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOO_TYPE_TERM_PT_UNIX, MooTermPtUnixClass))
|
|
|
|
#define MOO_IS_TERM_PT_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOO_TYPE_TERM_PT_UNIX))
|
|
|
|
#define MOO_IS_TERM_PT_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOO_TYPE_TERM_PT_UNIX))
|
|
|
|
#define MOO_TERM_PT_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOO_TYPE_TERM_PT_UNIX, MooTermPtUnixClass))
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
typedef struct _MooTermPtUnix MooTermPtUnix;
|
|
|
|
typedef struct _MooTermPtUnixClass MooTermPtUnixClass;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
struct _MooTermPtUnix {
|
|
|
|
MooTermPt parent;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
GPid child_pid;
|
|
|
|
|
|
|
|
int master;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
|
2005-06-23 06:50:25 -07:00
|
|
|
gboolean non_block;
|
2005-06-22 15:13:12 -07:00
|
|
|
GIOChannel *io;
|
|
|
|
guint io_watch_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
struct _MooTermPtUnixClass {
|
|
|
|
MooTermPtClass parent_class;
|
2005-06-22 15:13:12 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
static void moo_term_pt_unix_finalize (GObject *object);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-26 04:12:40 -07:00
|
|
|
static void set_size (MooTermPt *pt,
|
|
|
|
guint width,
|
|
|
|
guint height);
|
|
|
|
static gboolean fork_command (MooTermPt *pt,
|
2005-07-31 07:52:24 -07:00
|
|
|
const MooTermCommand *cmd,
|
|
|
|
GError **error);
|
2005-07-26 04:12:40 -07:00
|
|
|
static gboolean fork_argv (MooTermPt *pt,
|
|
|
|
char **argv,
|
|
|
|
const char *working_dir,
|
2005-07-31 07:52:24 -07:00
|
|
|
char **envp,
|
|
|
|
GError **error);
|
2005-07-26 04:12:40 -07:00
|
|
|
static void pt_write (MooTermPt *pt,
|
|
|
|
const char *string,
|
|
|
|
gssize len);
|
|
|
|
static void kill_child (MooTermPt *pt);
|
2006-04-03 00:56:05 -07:00
|
|
|
static void send_intr (MooTermPt *pt);
|
|
|
|
static char get_erase_char (MooTermPt *pt);
|
2005-07-26 04:12:40 -07:00
|
|
|
|
|
|
|
static gboolean read_child_out (GIOChannel *source,
|
|
|
|
GIOCondition condition,
|
|
|
|
MooTermPtUnix *pt);
|
|
|
|
static void feed_buffer (MooTermPtUnix *pt,
|
|
|
|
const char *string,
|
|
|
|
int len);
|
|
|
|
|
|
|
|
static void start_writer (MooTermPt *pt);
|
|
|
|
static void stop_writer (MooTermPt *pt);
|
2005-07-01 02:53:39 -07:00
|
|
|
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
/* MOO_TYPE_TERM_PT_UNIX */
|
2006-12-31 02:53:45 -08:00
|
|
|
G_DEFINE_TYPE (MooTermPtUnix, _moo_term_pt_unix, MOO_TYPE_TERM_PT)
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
|
2006-12-31 02:53:45 -08:00
|
|
|
static void
|
|
|
|
_moo_term_pt_unix_class_init (MooTermPtUnixClass *klass)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
2005-07-07 04:06:23 -07:00
|
|
|
MooTermPtClass *pt_class = MOO_TERM_PT_CLASS (klass);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
gobject_class->finalize = moo_term_pt_unix_finalize;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt_class->set_size = set_size;
|
|
|
|
pt_class->fork_command = fork_command;
|
|
|
|
pt_class->write = pt_write;
|
|
|
|
pt_class->kill_child = kill_child;
|
2006-04-03 00:56:05 -07:00
|
|
|
pt_class->get_erase_char = get_erase_char;
|
|
|
|
pt_class->send_intr = send_intr;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-31 02:53:45 -08:00
|
|
|
static void
|
|
|
|
_moo_term_pt_unix_init (MooTermPtUnix *pt)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-14 23:34:31 -07:00
|
|
|
MOO_TERM_PT(pt)->priv->child_alive = FALSE;
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->child_pid = (GPid)-1;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->master = -1;
|
|
|
|
pt->width = 80;
|
|
|
|
pt->height = 24;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->non_block = FALSE;
|
|
|
|
pt->io = NULL;
|
|
|
|
pt->io_watch_id = 0;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-31 02:53:45 -08:00
|
|
|
static void
|
|
|
|
moo_term_pt_unix_finalize (GObject *object)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (object);
|
|
|
|
kill_child (MOO_TERM_PT (pt));
|
2006-12-31 02:53:45 -08:00
|
|
|
G_OBJECT_CLASS (_moo_term_pt_unix_parent_class)->finalize (object);
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-12-31 02:53:45 -08:00
|
|
|
static void
|
|
|
|
set_size (MooTermPt *pt,
|
|
|
|
guint width,
|
|
|
|
guint height)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
MooTermPtUnix *ptu;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
g_return_if_fail (MOO_IS_TERM_PT_UNIX (pt));
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
ptu = MOO_TERM_PT_UNIX (pt);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-14 23:34:31 -07:00
|
|
|
if (pt->priv->child_alive)
|
2005-07-07 04:06:23 -07:00
|
|
|
_vte_pty_set_size (ptu->master, width, height);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
ptu->width = width;
|
|
|
|
ptu->height = height;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-31 20:58:15 -07:00
|
|
|
static gboolean
|
|
|
|
fork_argv (MooTermPt *pt_gen,
|
|
|
|
char **argv,
|
|
|
|
const char *working_dir,
|
|
|
|
char **envp,
|
|
|
|
GError **error)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
MooTermPtUnix *pt;
|
2005-06-22 15:13:12 -07:00
|
|
|
int env_len = 0;
|
|
|
|
char **new_env;
|
|
|
|
int status, flags;
|
|
|
|
int i;
|
2005-06-23 06:50:25 -07:00
|
|
|
GSource *src;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2006-05-31 20:58:15 -07:00
|
|
|
g_return_val_if_fail (argv != NULL, FALSE);
|
2005-07-07 04:06:23 -07:00
|
|
|
g_return_val_if_fail (MOO_IS_TERM_PT_UNIX (pt_gen), FALSE);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2006-05-31 20:58:15 -07:00
|
|
|
if (!argv[0])
|
|
|
|
{
|
|
|
|
g_set_error (error, MOO_TERM_ERROR, MOO_TERM_ERROR_INVAL,
|
|
|
|
"empty arguments array");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt = MOO_TERM_PT_UNIX (pt_gen);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-14 23:34:31 -07:00
|
|
|
g_return_val_if_fail (!pt_gen->priv->child_alive, FALSE);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
if (envp)
|
|
|
|
{
|
|
|
|
char **e;
|
|
|
|
for (e = envp; *e != NULL; ++e)
|
|
|
|
++env_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_env = g_new (char*, env_len + 2);
|
|
|
|
|
|
|
|
for (i = 0; i < env_len; ++i)
|
|
|
|
new_env[i] = g_strdup (envp[i]);
|
|
|
|
|
|
|
|
new_env[env_len] = g_strdup ("TERM=" TERM_EMULATION);
|
|
|
|
new_env[env_len + 1] = NULL;
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->master = _vte_pty_open (&pt->child_pid, new_env,
|
2005-06-22 15:13:12 -07:00
|
|
|
argv[0],
|
|
|
|
argv,
|
|
|
|
working_dir,
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->width, pt->height,
|
2005-06-22 15:13:12 -07:00
|
|
|
FALSE, FALSE, FALSE);
|
|
|
|
g_strfreev (new_env);
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->master == -1)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
|
|
|
g_critical ("%s: could not fork child", G_STRLOC);
|
2005-07-31 07:52:24 -07:00
|
|
|
|
|
|
|
if (errno)
|
2006-05-31 20:58:15 -07:00
|
|
|
g_set_error (error, MOO_TERM_ERROR,
|
|
|
|
MOO_TERM_ERROR_FAILED,
|
2005-07-31 07:52:24 -07:00
|
|
|
"could not fork command: %s",
|
|
|
|
g_strerror (errno));
|
|
|
|
else
|
2006-05-31 20:58:15 -07:00
|
|
|
g_set_error (error, MOO_TERM_ERROR,
|
|
|
|
MOO_TERM_ERROR_FAILED,
|
2005-07-31 07:52:24 -07:00
|
|
|
"could not fork command");
|
|
|
|
|
2005-06-22 15:13:12 -07:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-10-23 23:26:10 -07:00
|
|
|
#if 1
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("%s: forked child pid %d on fd %d",
|
|
|
|
G_STRLOC, pt->child_pid, pt->master);
|
2005-10-13 07:08:18 -07:00
|
|
|
#endif
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (waitpid (-1, &status, WNOHANG) == -1)
|
|
|
|
g_critical ("%s: error in waitpid", G_STRLOC);
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if ((flags = fcntl (pt->master, F_GETFL)) < 0)
|
2005-06-22 15:13:12 -07:00
|
|
|
g_critical ("%s: F_GETFL on master", G_STRLOC);
|
2005-07-07 04:06:23 -07:00
|
|
|
else if (-1 == fcntl (pt->master, F_SETFL, O_NONBLOCK | flags))
|
2005-06-22 15:13:12 -07:00
|
|
|
g_critical ("%s: F_SETFL on master", G_STRLOC);
|
2005-06-23 06:50:25 -07:00
|
|
|
else
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->non_block = TRUE;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->io = g_io_channel_unix_new (pt->master);
|
|
|
|
g_return_val_if_fail (pt->io != NULL, FALSE);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
g_io_channel_set_encoding (pt->io, NULL, NULL);
|
|
|
|
g_io_channel_set_buffered (pt->io, FALSE);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
pt->io_watch_id = g_io_add_watch_full (pt->io,
|
|
|
|
G_PRIORITY_DEFAULT_IDLE,
|
|
|
|
G_IO_IN | G_IO_PRI | G_IO_HUP,
|
|
|
|
(GIOFunc) read_child_out,
|
|
|
|
pt, NULL);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
src = g_main_context_find_source_by_id (NULL, pt->io_watch_id);
|
2005-06-23 06:50:25 -07:00
|
|
|
|
|
|
|
if (src)
|
2005-07-07 04:06:23 -07:00
|
|
|
g_source_set_priority (src, PT_READER_PRIORITY);
|
2005-06-23 06:50:25 -07:00
|
|
|
else
|
|
|
|
g_warning ("%s: could not find io_watch_id source", G_STRLOC);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-14 23:34:31 -07:00
|
|
|
pt_gen->priv->child_alive = TRUE;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-07-31 22:54:24 -07:00
|
|
|
static gboolean
|
|
|
|
fork_command (MooTermPt *pt_gen,
|
|
|
|
const MooTermCommand *cmd,
|
|
|
|
GError **error)
|
2005-07-26 04:12:40 -07:00
|
|
|
{
|
2005-07-31 07:52:24 -07:00
|
|
|
g_return_val_if_fail (cmd != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (cmd->argv != NULL, FALSE);
|
2005-07-26 04:12:40 -07:00
|
|
|
g_return_val_if_fail (MOO_IS_TERM_PT_UNIX (pt_gen), FALSE);
|
|
|
|
|
2006-07-31 22:54:24 -07:00
|
|
|
return fork_argv (pt_gen, cmd->argv, cmd->working_dir, cmd->envp, error);
|
2005-07-26 04:12:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-07-31 22:54:24 -07:00
|
|
|
static void
|
|
|
|
kill_child (MooTermPt *pt_gen)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (pt_gen);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->io_watch_id)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
g_source_remove (pt->io_watch_id);
|
|
|
|
pt->io_watch_id = 0;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->io)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
g_io_channel_shutdown (pt->io, TRUE, NULL);
|
|
|
|
g_io_channel_unref (pt->io);
|
|
|
|
pt->io = NULL;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
stop_writer (pt_gen);
|
2006-04-03 00:56:05 -07:00
|
|
|
pt_discard_pending_write (pt_gen);
|
2005-07-02 23:01:57 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->master != -1)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
_vte_pty_close (pt->master);
|
|
|
|
pt->master = -1;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->non_block = FALSE;
|
2005-06-23 06:50:25 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->child_pid = (GPid)-1;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-14 23:34:31 -07:00
|
|
|
if (pt_gen->priv->child_alive)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-14 23:34:31 -07:00
|
|
|
pt_gen->priv->child_alive = FALSE;
|
2005-07-07 04:06:23 -07:00
|
|
|
g_signal_emit_by_name (pt, "child-died");
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
static gboolean
|
|
|
|
read_child_out (G_GNUC_UNUSED GIOChannel *source,
|
|
|
|
GIOCondition condition,
|
|
|
|
MooTermPtUnix *pt)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
|
|
|
gboolean error_occured = FALSE;
|
|
|
|
int error_no = 0;
|
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
char buf[INPUT_CHUNK_SIZE];
|
2005-06-22 15:13:12 -07:00
|
|
|
guint again = POLL_NUM;
|
2006-06-09 12:28:17 -07:00
|
|
|
gsize to_read;
|
|
|
|
gsize current = 0;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
/* try to read leftover from the pipe */
|
2005-06-22 15:13:12 -07:00
|
|
|
if (condition & G_IO_HUP)
|
|
|
|
{
|
2006-06-09 12:28:17 -07:00
|
|
|
int bytes;
|
2006-03-16 00:47:59 -08:00
|
|
|
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("%s: G_IO_HUP", G_STRLOC);
|
2005-06-22 15:13:12 -07:00
|
|
|
error_no = errno;
|
2006-03-16 00:47:59 -08:00
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
bytes = read (pt->master, buf, sizeof (buf));
|
2006-03-16 00:47:59 -08:00
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
if (bytes > 0)
|
|
|
|
feed_buffer (pt, buf, bytes);
|
2006-03-16 00:47:59 -08:00
|
|
|
|
2005-06-22 15:13:12 -07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else if (condition & G_IO_ERR)
|
|
|
|
{
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("%s: G_IO_ERR", G_STRLOC);
|
2005-06-22 15:13:12 -07:00
|
|
|
error_no = errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_assert (condition & (G_IO_IN | G_IO_PRI));
|
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
to_read = _moo_term_get_input_chunk_len (MOO_TERM_PT(pt)->priv->term);
|
|
|
|
g_assert (to_read <= sizeof (buf));
|
|
|
|
|
|
|
|
if (!to_read)
|
|
|
|
{
|
2006-10-17 22:24:35 -07:00
|
|
|
if (0)
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("read_child_out: skipping\n");
|
2006-06-09 12:28:17 -07:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->non_block)
|
2005-06-23 06:50:25 -07:00
|
|
|
{
|
2006-06-09 12:28:17 -07:00
|
|
|
int bytes;
|
|
|
|
|
|
|
|
bytes = read (pt->master, buf, to_read);
|
2005-06-23 06:50:25 -07:00
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
switch (bytes)
|
2005-06-23 06:50:25 -07:00
|
|
|
{
|
|
|
|
case -1:
|
2005-07-15 02:48:18 -07:00
|
|
|
if (errno != EAGAIN && errno != EINTR)
|
|
|
|
{
|
|
|
|
error_occured = TRUE;
|
|
|
|
error_no = errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
2005-06-23 06:50:25 -07:00
|
|
|
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2005-07-12 07:23:08 -07:00
|
|
|
#if 0
|
2005-07-10 19:54:58 -07:00
|
|
|
{
|
|
|
|
char *s = _moo_term_nice_bytes (buf, r);
|
|
|
|
g_print ("got '%s'\n", s);
|
|
|
|
g_free (s);
|
|
|
|
}
|
2005-07-04 12:22:02 -07:00
|
|
|
#endif
|
2006-06-09 12:28:17 -07:00
|
|
|
feed_buffer (pt, buf, bytes);
|
2005-06-23 06:50:25 -07:00
|
|
|
break;
|
|
|
|
}
|
2005-07-04 12:22:02 -07:00
|
|
|
|
2005-06-23 06:50:25 -07:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
while (again && !error_occured && current < to_read)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2007-01-11 18:28:22 -08:00
|
|
|
int res;
|
|
|
|
struct pollfd fd;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2007-01-11 18:28:22 -08:00
|
|
|
fd.fd = pt->master;
|
|
|
|
fd.events = POLLIN | POLLPRI;
|
|
|
|
fd.revents = 0;
|
|
|
|
res = poll (&fd, 1, POLL_TIME);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
switch (res)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
--again;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
if (fd.revents & (POLLNVAL | POLLERR | POLLHUP))
|
|
|
|
{
|
|
|
|
again = 0;
|
|
|
|
if (errno != EAGAIN && errno != EINTR)
|
|
|
|
{
|
|
|
|
error_occured = TRUE;
|
|
|
|
error_no = errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (fd.revents & (POLLIN | POLLPRI))
|
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
int r = read (pt->master, buf + current, 1);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
switch (r)
|
|
|
|
{
|
|
|
|
case -1:
|
|
|
|
error_occured = TRUE;
|
|
|
|
error_no = errno;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
--again;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
++current;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
}
|
2005-07-04 12:22:02 -07:00
|
|
|
|
2005-06-22 15:13:12 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
again = 0;
|
|
|
|
|
|
|
|
if (errno != EAGAIN && errno != EINTR)
|
|
|
|
{
|
|
|
|
error_occured = TRUE;
|
|
|
|
error_no = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current > 0)
|
2005-07-07 04:06:23 -07:00
|
|
|
feed_buffer (pt, buf, current);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
if (error_occured)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (error_occured)
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("error in %s", G_STRLOC);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
if (error_no)
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("%s: %s", G_STRLOC, g_strerror (error_no));
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->io)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
_vte_pty_close (pt->master);
|
|
|
|
pt->master = -1;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
g_io_channel_shutdown (pt->io, TRUE, NULL);
|
|
|
|
g_io_channel_unref (pt->io);
|
|
|
|
pt->io = NULL;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
pt->io_watch_id = 0;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
kill_child (MOO_TERM_PT (pt));
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-09 12:28:17 -07:00
|
|
|
static void
|
|
|
|
feed_buffer (MooTermPtUnix *pt,
|
|
|
|
const char *string,
|
|
|
|
int len)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2006-06-09 12:28:17 -07:00
|
|
|
moo_term_feed (MOO_TERM_PT(pt)->priv->term, string, len);
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-07-01 02:53:39 -07:00
|
|
|
/* writes given data to file, returns TRUE on successful write,
|
|
|
|
FALSE when could not write al teh data, puts start of leftover
|
|
|
|
to string, length of it to len, and fills err in case of error */
|
2005-07-07 04:06:23 -07:00
|
|
|
static gboolean do_write (MooTermPt *pt_gen,
|
2005-07-01 02:53:39 -07:00
|
|
|
const char **string,
|
2005-07-06 09:39:02 -07:00
|
|
|
guint *plen,
|
2005-07-02 23:01:57 -07:00
|
|
|
int *err)
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-06 09:39:02 -07:00
|
|
|
int written;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (pt_gen);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
g_return_val_if_fail (pt->io != NULL, FALSE);
|
2005-07-01 02:53:39 -07:00
|
|
|
|
2005-07-10 19:54:58 -07:00
|
|
|
written = write (pt->master, *string,
|
|
|
|
*plen > WRITE_CHUNK_SIZE ? WRITE_CHUNK_SIZE : *plen);
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-02 23:01:57 -07:00
|
|
|
if (written == -1)
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
2005-07-02 23:01:57 -07:00
|
|
|
*err = errno;
|
|
|
|
|
|
|
|
switch (errno)
|
|
|
|
{
|
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
g_warning ("%s", G_STRLOC);
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
|
|
}
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
|
|
|
else
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-01 02:53:39 -07:00
|
|
|
*string += written;
|
|
|
|
*plen -= written;
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-02 23:01:57 -07:00
|
|
|
if (!*plen)
|
|
|
|
*string = NULL;
|
2005-07-04 12:22:02 -07:00
|
|
|
|
2005-07-02 23:01:57 -07:00
|
|
|
return TRUE;
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
|
|
|
}
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
static void append (MooTermPt *pt,
|
2005-07-01 02:53:39 -07:00
|
|
|
const char *data,
|
|
|
|
guint len)
|
|
|
|
{
|
|
|
|
GByteArray *ar = g_byte_array_sized_new (len);
|
|
|
|
g_byte_array_append (ar, (const guint8*)data, len);
|
2005-07-07 04:06:23 -07:00
|
|
|
g_queue_push_tail (pt->priv->pending_write, ar);
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
2005-06-22 15:13:12 -07:00
|
|
|
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
static gboolean write_cb (MooTermPt *pt)
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
pt_write (pt, NULL, 0);
|
2005-07-01 02:53:39 -07:00
|
|
|
return TRUE;
|
|
|
|
}
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
static void start_writer (MooTermPt *pt)
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
if (!pt->priv->pending_write_id)
|
|
|
|
pt->priv->pending_write_id =
|
|
|
|
g_idle_add_full (PT_WRITER_PRIORITY,
|
2005-07-01 02:53:39 -07:00
|
|
|
(GSourceFunc) write_cb,
|
2005-07-07 04:06:23 -07:00
|
|
|
pt, NULL);
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
static void stop_writer (MooTermPt *pt)
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
if (pt->priv->pending_write_id)
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
g_source_remove (pt->priv->pending_write_id);
|
|
|
|
pt->priv->pending_write_id = 0;
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
|
|
|
}
|
2005-06-22 15:13:12 -07:00
|
|
|
|
2005-07-01 02:53:39 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
static void pt_write (MooTermPt *pt,
|
2005-07-01 02:53:39 -07:00
|
|
|
const char *data,
|
2006-04-30 11:53:04 -07:00
|
|
|
gssize data_len)
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
|
|
|
g_return_if_fail (data == NULL || data_len != 0);
|
2005-07-14 23:34:31 -07:00
|
|
|
g_return_if_fail (pt->priv->child_alive);
|
2005-07-01 02:53:39 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
while (data || !g_queue_is_empty (pt->priv->pending_write))
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
2005-07-02 23:01:57 -07:00
|
|
|
int err = 0;
|
2005-07-01 02:53:39 -07:00
|
|
|
const char *string;
|
2005-07-06 09:39:02 -07:00
|
|
|
guint len;
|
2005-07-01 02:53:39 -07:00
|
|
|
GByteArray *freeme = NULL;
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (!g_queue_is_empty (pt->priv->pending_write))
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
|
|
|
if (data)
|
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
append (pt, data, data_len > 0 ? (guint)data_len : strlen (data));
|
2005-07-01 02:53:39 -07:00
|
|
|
data = NULL;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
freeme = g_queue_peek_head (pt->priv->pending_write);
|
2005-07-19 07:01:41 -07:00
|
|
|
string = (const char *) freeme->data;
|
2005-07-01 02:53:39 -07:00
|
|
|
len = freeme->len;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
2005-07-01 02:53:39 -07:00
|
|
|
else
|
2005-06-22 15:13:12 -07:00
|
|
|
{
|
2005-07-01 02:53:39 -07:00
|
|
|
string = data;
|
2005-07-06 09:39:02 -07:00
|
|
|
len = data_len > 0 ? (guint)data_len : strlen (data);
|
2005-07-01 02:53:39 -07:00
|
|
|
data = NULL;
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (do_write (pt, &string, &len, &err))
|
2005-07-01 02:53:39 -07:00
|
|
|
{
|
|
|
|
if (len)
|
|
|
|
{
|
|
|
|
if (freeme)
|
|
|
|
{
|
|
|
|
memmove (freeme->data, freeme->data + (freeme->len - len), len);
|
|
|
|
g_byte_array_set_size (freeme, len);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-07-07 04:06:23 -07:00
|
|
|
append (pt, string, len);
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (freeme)
|
|
|
|
{
|
|
|
|
g_byte_array_free (freeme, TRUE);
|
2005-07-07 04:06:23 -07:00
|
|
|
g_queue_pop_head (pt->priv->pending_write);
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("%s: stopping writing to child", G_STRLOC);
|
2005-07-01 02:53:39 -07:00
|
|
|
if (err)
|
2006-11-01 22:38:00 -08:00
|
|
|
_moo_message ("%s: %s", G_STRLOC, g_strerror (err));
|
2005-07-07 04:06:23 -07:00
|
|
|
kill_child (pt);
|
2005-07-01 02:53:39 -07:00
|
|
|
}
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
2005-07-01 02:53:39 -07:00
|
|
|
|
2005-07-07 04:06:23 -07:00
|
|
|
if (!g_queue_is_empty (pt->priv->pending_write))
|
|
|
|
start_writer (pt);
|
2005-07-01 02:53:39 -07:00
|
|
|
else
|
2005-07-07 04:06:23 -07:00
|
|
|
stop_writer (pt);
|
2005-06-22 15:13:12 -07:00
|
|
|
}
|
2005-07-14 23:34:31 -07:00
|
|
|
|
|
|
|
|
2006-04-03 00:56:05 -07:00
|
|
|
static char
|
|
|
|
get_erase_char (MooTermPt *pt_gen)
|
2005-07-14 23:34:31 -07:00
|
|
|
{
|
|
|
|
MooTermPtUnix *pt = MOO_TERM_PT_UNIX (pt_gen);
|
|
|
|
struct termios tio;
|
|
|
|
|
|
|
|
g_return_val_if_fail (pt->master != -1, 0);
|
|
|
|
|
|
|
|
if (!tcgetattr (pt->master, &tio))
|
|
|
|
{
|
|
|
|
return tio.c_cc[VERASE];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_warning ("%s: %s", G_STRLOC,
|
|
|
|
g_strerror (errno));
|
2005-07-26 04:12:40 -07:00
|
|
|
return 127;
|
2005-07-14 23:34:31 -07:00
|
|
|
}
|
|
|
|
}
|
2005-07-15 02:48:18 -07:00
|
|
|
|
|
|
|
|
2006-04-03 00:56:05 -07:00
|
|
|
static void
|
|
|
|
send_intr (MooTermPt *pt)
|
2005-07-15 02:48:18 -07:00
|
|
|
{
|
|
|
|
g_return_if_fail (pt->priv->child_alive);
|
2006-04-03 00:56:05 -07:00
|
|
|
pt_discard_pending_write (pt);
|
2005-07-15 02:48:18 -07:00
|
|
|
pt_write (pt, "\003", 1);
|
|
|
|
}
|
2005-07-26 04:12:40 -07:00
|
|
|
|
|
|
|
|
|
|
|
/* TODO: it should be in glib */
|
2006-07-31 22:54:24 -07:00
|
|
|
MooTermCommand *
|
2005-10-13 07:08:18 -07:00
|
|
|
_moo_term_get_default_shell (void)
|
2005-07-26 04:12:40 -07:00
|
|
|
{
|
2005-07-31 07:52:24 -07:00
|
|
|
static char *argv[2] = {NULL, NULL};
|
2005-07-26 04:12:40 -07:00
|
|
|
|
2005-07-31 07:52:24 -07:00
|
|
|
if (!argv[0])
|
|
|
|
{
|
|
|
|
argv[0] = g_strdup (g_getenv ("SHELL"));
|
|
|
|
if (!argv[0]) argv[0] = g_strdup ("/bin/sh");
|
|
|
|
}
|
2005-07-26 04:12:40 -07:00
|
|
|
|
2006-07-31 22:54:24 -07:00
|
|
|
return moo_term_command_new_argv (argv, NULL, NULL);
|
2005-07-26 04:12:40 -07:00
|
|
|
}
|
2005-07-30 23:20:59 -07:00
|
|
|
|
|
|
|
|
|
|
|
/* it's a win32 function */
|
2005-10-13 07:08:18 -07:00
|
|
|
void
|
|
|
|
moo_term_set_helper_directory (G_GNUC_UNUSED const char *dir)
|
2005-07-30 23:20:59 -07:00
|
|
|
{
|
|
|
|
g_return_if_reached ();
|
|
|
|
}
|
2005-07-31 07:52:24 -07:00
|
|
|
|
|
|
|
|
|
|
|
static char *argv_to_cmd_line (char **argv)
|
|
|
|
{
|
|
|
|
GString *cmd = NULL;
|
|
|
|
char **p;
|
|
|
|
|
2006-05-31 20:58:15 -07:00
|
|
|
g_return_val_if_fail (argv && argv[0], NULL);
|
2005-07-31 07:52:24 -07:00
|
|
|
|
|
|
|
for (p = argv; *p != NULL; ++p)
|
|
|
|
{
|
|
|
|
char *quoted = g_shell_quote (*p);
|
|
|
|
/* TODO: may not happen? */
|
|
|
|
g_return_val_if_fail (quoted != NULL,
|
|
|
|
g_string_free (cmd, FALSE));
|
|
|
|
if (cmd)
|
|
|
|
g_string_append_printf (cmd, " %s", quoted);
|
|
|
|
else
|
|
|
|
cmd = g_string_new (quoted);
|
|
|
|
g_free (quoted);
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_string_free (cmd, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char **cmd_line_to_argv (const char *cmd_line,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
int argc;
|
|
|
|
char **argv;
|
|
|
|
g_shell_parse_argv (cmd_line, &argc, &argv, error);
|
|
|
|
return argv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-13 07:08:18 -07:00
|
|
|
gboolean
|
|
|
|
_moo_term_check_cmd (MooTermCommand *cmd,
|
|
|
|
GError **error)
|
2005-07-31 07:52:24 -07:00
|
|
|
{
|
|
|
|
g_return_val_if_fail (cmd != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (cmd->cmd_line != NULL || cmd->argv != NULL, FALSE);
|
|
|
|
|
|
|
|
if (cmd->argv)
|
|
|
|
{
|
2006-05-31 20:58:15 -07:00
|
|
|
if (!cmd->argv[0])
|
|
|
|
{
|
|
|
|
g_set_error (error, MOO_TERM_ERROR, MOO_TERM_ERROR_INVAL,
|
|
|
|
"empty arguments array");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2005-07-31 07:52:24 -07:00
|
|
|
g_free (cmd->cmd_line);
|
|
|
|
cmd->cmd_line = argv_to_cmd_line (cmd->argv);
|
2006-05-31 20:58:15 -07:00
|
|
|
|
2005-07-31 07:52:24 -07:00
|
|
|
g_return_val_if_fail (cmd->cmd_line != NULL, FALSE);
|
2006-05-31 20:58:15 -07:00
|
|
|
|
2005-07-31 07:52:24 -07:00
|
|
|
return TRUE;
|
|
|
|
}
|
2006-05-31 20:58:15 -07:00
|
|
|
|
|
|
|
cmd->argv = cmd_line_to_argv (cmd->cmd_line, error);
|
|
|
|
return cmd->argv != NULL;
|
2005-07-31 07:52:24 -07:00
|
|
|
}
|