medit/moo/mooterm/mootermpt-unix.c

896 lines
21 KiB
C
Raw Normal View History

2005-06-22 15:13:12 -07:00
/*
2007-06-24 10:56:20 -07:00
* mootermpt-unix.c
2005-06-22 15:13:12 -07:00
*
2007-04-07 01:21:52 -07:00
* Copyright (C) 2004-2007 by Yevgen Muntyan <muntyan@math.tamu.edu>
2005-06-22 15:13:12 -07:00
*
2007-06-24 10:56:20 -07:00
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
2007-09-23 09:47:28 -07:00
* License version 2.1 as published by the Free Software Foundation.
2005-06-22 15:13:12 -07:00
*
* See COPYING file that comes with this distribution.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define MOOTERM_COMPILATION
#include "mooterm/mootermpt-private.h"
#include "mooterm/mooterm-private.h"
2005-06-22 15:13:12 -07:00
#include "mooterm/pty.h"
#include "mooutils/moomarshals.h"
2006-11-01 22:38:00 -08:00
#include "mooutils/mooutils-misc.h"
2007-08-20 20:15:58 -07:00
#include "mooutils/mooutils-debug.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>
#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
2007-06-20 20:09:28 -07:00
#undef WATCH_CHILD
#if defined(MOO_OS_DARWIN)
#define WATCH_CHILD
#endif
#define TERM_EMULATION "xterm"
#define WRITE_CHUNK_SIZE 4096
#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;
gboolean non_block;
2005-06-22 15:13:12 -07:00
GIOChannel *io;
guint io_watch_id;
2007-06-20 20:09:28 -07:00
#ifdef WATCH_CHILD
guint child_watch_id;
#endif
2005-06-22 15:13:12 -07:00
};
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 void set_echo_input (MooTermPt *pt,
gboolean echo);
2005-07-26 04:12:40 -07:00
static gboolean fork_command (MooTermPt *pt,
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,
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);
static void send_intr (MooTermPt *pt);
static char get_erase_char (MooTermPt *pt);
2007-01-18 01:21:30 -08:00
static gboolean set_fd (MooTermPt *pt,
int master);
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-06-22 15:13:12 -07:00
2005-07-07 04:06:23 -07:00
/* MOO_TYPE_TERM_PT_UNIX */
G_DEFINE_TYPE (MooTermPtUnix, _moo_term_pt_unix, MOO_TYPE_TERM_PT)
2005-06-22 15:13:12 -07: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->set_echo_input = set_echo_input;
2005-07-07 04:06:23 -07:00
pt_class->fork_command = fork_command;
pt_class->write = pt_write;
pt_class->kill_child = kill_child;
pt_class->get_erase_char = get_erase_char;
pt_class->send_intr = send_intr;
2007-01-18 01:21:30 -08:00
pt_class->set_fd = set_fd;
2005-06-22 15:13:12 -07:00
}
static void
_moo_term_pt_unix_init (MooTermPtUnix *pt)
2005-06-22 15:13:12 -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-07 04:06:23 -07:00
pt->master = -1;
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;
2007-06-20 20:09:28 -07:00
#ifdef WATCH_CHILD
pt->child_watch_id = 0;
#endif
2005-06-22 15:13:12 -07: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));
G_OBJECT_CLASS (_moo_term_pt_unix_parent_class)->finalize (object);
2005-06-22 15:13:12 -07: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
if (pt->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
pt->width = width;
pt->height = height;
}
static void
set_echo_input (MooTermPt *pt,
gboolean echo)
{
MooTermPtUnix *ptu;
ptu = MOO_TERM_PT_UNIX (pt);
if (pt->alive)
_vte_pty_set_echo_input (ptu->master, echo);
pt->echo = echo;
2005-06-22 15:13:12 -07:00
}
2007-01-18 01:21:30 -08:00
static gboolean
setup_master_fd (MooTermPtUnix *pt,
int master)
{
int flags;
g_return_val_if_fail (master >= 0, FALSE);
2007-07-04 19:17:40 -07:00
#if 0
2007-01-18 01:21:30 -08:00
_moo_message ("%s: attached to fd %d",
G_STRLOC, pt->master);
#endif
pt->non_block = FALSE;
if ((flags = fcntl (master, F_GETFL)) < 0)
g_critical ("%s: F_GETFL on master", G_STRLOC);
else if (-1 == fcntl (master, F_SETFL, O_NONBLOCK | flags))
g_critical ("%s: F_SETFL on master", G_STRLOC);
else
pt->non_block = TRUE;
pt->io = g_io_channel_unix_new (master);
g_return_val_if_fail (pt->io != NULL, FALSE);
g_io_channel_set_encoding (pt->io, NULL, NULL);
g_io_channel_set_buffered (pt->io, FALSE);
pt->io_watch_id = _moo_io_add_watch_full (pt->io,
MOO_TERM_PT(pt)->priority,
2007-06-20 20:09:28 -07:00
G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR,
(GIOFunc) read_child_out,
pt, NULL);
2007-01-18 01:21:30 -08:00
pt->master = master;
MOO_TERM_PT(pt)->alive = TRUE;
_vte_pty_set_echo_input (pt->master, MOO_TERM_PT(pt)->echo);
2007-01-18 01:21:30 -08:00
return TRUE;
}
2007-06-20 20:09:28 -07:00
#ifdef WATCH_CHILD
static void
child_died (GPid pid,
G_GNUC_UNUSED int status,
MooTermPtUnix *pt)
{
g_return_if_fail (pid == pt->child_pid);
_moo_message ("%s: child exited", G_STRLOC);
gdk_threads_enter ();
kill_child (MOO_TERM_PT (pt));
gdk_threads_leave ();
}
#endif
2007-01-18 01:21:30 -08: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;
2007-06-20 20:34:09 -07:00
int status, ret;
2005-06-22 15:13:12 -07:00
int i;
2007-01-18 01:21:30 -08:00
int master;
2005-06-22 15:13:12 -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
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
g_return_val_if_fail (!pt_gen->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;
2007-01-18 01:21:30 -08:00
master = _vte_pty_open (&pt->child_pid, new_env,
argv[0],
argv,
working_dir,
pt_gen->width, pt_gen->height,
2007-01-18 01:21:30 -08:00
FALSE, FALSE, FALSE);
2005-06-22 15:13:12 -07:00
g_strfreev (new_env);
2007-01-18 01:21:30 -08:00
if (master == -1)
2005-06-22 15:13:12 -07:00
{
g_critical ("%s: could not fork child", G_STRLOC);
if (errno)
g_set_error (error, MOO_TERM_ERROR,
MOO_TERM_ERROR_FAILED,
"could not fork command: %s",
g_strerror (errno));
else
g_set_error (error, MOO_TERM_ERROR,
MOO_TERM_ERROR_FAILED,
"could not fork command");
2005-06-22 15:13:12 -07:00
return FALSE;
}
2007-06-20 20:34:09 -07:00
ret = waitpid (pt->child_pid, &status, WNOHANG);
if (ret == -1 || ret > 0)
2005-06-22 15:13:12 -07:00
{
2007-06-20 20:34:09 -07:00
if (ret < 0)
g_critical ("%s: error in waitpid", G_STRLOC);
else
_moo_message ("%s: child died already", G_STRLOC);
pt->child_pid = -1;
kill_child (pt_gen);
return FALSE;
}
2007-07-04 19:17:40 -07:00
if (0)
2006-11-01 22:38:00 -08:00
_moo_message ("%s: forked child pid %d on fd %d",
2007-01-18 01:21:30 -08:00
G_STRLOC, pt->child_pid, master);
2005-06-22 15:13:12 -07:00
2007-06-20 20:34:09 -07:00
pt_gen->child_alive = TRUE;
pt_gen->alive = TRUE;
2005-06-22 15:13:12 -07:00
2007-06-20 20:09:28 -07:00
#ifdef WATCH_CHILD
pt->child_watch_id = g_child_watch_add_full (pt_gen->priority,
pt->child_pid,
(GChildWatchFunc) child_died,
pt, NULL);
#endif
2007-01-18 01:21:30 -08:00
setup_master_fd (pt, master);
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
return TRUE;
}
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
static gboolean
set_fd (MooTermPt *pt_gen,
int master)
{
kill_child (pt_gen);
2007-01-18 01:21:30 -08:00
if (master >= 0)
return setup_master_fd (MOO_TERM_PT_UNIX (pt_gen), master);
else
2007-01-18 01:21:30 -08:00
return TRUE;
2005-06-22 15:13:12 -07:00
}
static gboolean
fork_command (MooTermPt *pt_gen,
const MooTermCommand *cmd,
GError **error)
2005-07-26 04:12:40 -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);
return fork_argv (pt_gen, cmd->argv, cmd->working_dir, cmd->envp, error);
2005-07-26 04:12:40 -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
2007-06-20 20:09:28 -07:00
#ifdef WATCH_CHILD
if (pt->child_watch_id)
{
g_source_remove (pt->child_watch_id);
pt->child_watch_id = 0;
}
#endif
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);
pt_discard_pending_write (pt_gen);
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-07-07 04:06:23 -07:00
pt->child_pid = (GPid)-1;
pt_gen->alive = FALSE;
2005-06-22 15:13:12 -07:00
if (pt_gen->child_alive)
2005-06-22 15:13:12 -07:00
{
pt_gen->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);
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));
to_read = _moo_term_pt_get_input_chunk_len (MOO_TERM_PT (pt), sizeof buf);
2006-06-09 12:28:17 -07:00
if (!to_read)
{
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)
{
2006-06-09 12:28:17 -07:00
int bytes;
2007-06-20 20:34:09 -07:00
if (0)
_moo_message ("reading %" G_GSIZE_FORMAT " bytes\n", to_read);
errno = 0;
2006-06-09 12:28:17 -07:00
bytes = read (pt->master, buf, to_read);
2006-06-09 12:28:17 -07:00
switch (bytes)
{
case -1:
if (errno != EAGAIN && errno != EINTR)
{
error_occured = TRUE;
error_no = errno;
goto error;
}
break;
case 0:
break;
default:
#if 0
{
char *s = _moo_term_nice_bytes (buf, r);
g_print ("got '%s'\n", s);
g_free (s);
}
#endif
2006-06-09 12:28:17 -07:00
feed_buffer (pt, buf, bytes);
break;
}
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-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
{
_moo_term_pt_process_data (MOO_TERM_PT (pt), string, len);
2005-06-22 15:13:12 -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 */
2007-01-18 01:21:30 -08:00
static gboolean
do_write (MooTermPt *pt_gen,
const char **string,
guint *plen,
int *err)
2005-06-22 15:13:12 -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);
written = write (pt->master, *string,
*plen > WRITE_CHUNK_SIZE ? WRITE_CHUNK_SIZE : *plen);
2005-06-22 15:13:12 -07:00
if (written == -1)
{
*err = errno;
switch (errno)
{
case EAGAIN:
case EINTR:
g_warning ("%s", G_STRLOC);
return TRUE;
default:
return FALSE;
}
}
else
2005-06-22 15:13:12 -07:00
{
*string += written;
*plen -= written;
2005-06-22 15:13:12 -07:00
if (!*plen)
*string = NULL;
return TRUE;
}
}
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
static void
append (MooTermPt *pt,
const char *data,
guint len)
{
GByteArray *ar = g_byte_array_sized_new (len);
g_byte_array_append (ar, (const guint8*)data, len);
g_queue_push_tail (pt->pending_write, ar);
}
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
static gboolean
write_cb (MooTermPt *pt)
{
2005-07-07 04:06:23 -07:00
pt_write (pt, NULL, 0);
return TRUE;
}
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
static void
start_writer (MooTermPt *pt)
{
if (!pt->pending_write_id)
pt->pending_write_id =
_moo_idle_add_full (PT_WRITER_PRIORITY,
(GSourceFunc) write_cb,
pt, NULL);
}
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
static void
stop_writer (MooTermPt *pt)
{
if (pt->pending_write_id)
{
g_source_remove (pt->pending_write_id);
pt->pending_write_id = 0;
}
}
2005-06-22 15:13:12 -07:00
2007-01-18 01:21:30 -08:00
static void
pt_write (MooTermPt *pt,
const char *data,
gssize data_len)
{
g_return_if_fail (data == NULL || data_len != 0);
g_return_if_fail (pt->alive);
while (data || !g_queue_is_empty (pt->pending_write))
{
int err = 0;
const char *string;
guint len;
GByteArray *freeme = NULL;
if (!g_queue_is_empty (pt->pending_write))
{
if (data)
{
2005-07-07 04:06:23 -07:00
append (pt, data, data_len > 0 ? (guint)data_len : strlen (data));
data = NULL;
2005-06-22 15:13:12 -07:00
}
freeme = g_queue_peek_head (pt->pending_write);
2005-07-19 07:01:41 -07:00
string = (const char *) freeme->data;
len = freeme->len;
2005-06-22 15:13:12 -07:00
}
else
2005-06-22 15:13:12 -07:00
{
string = data;
len = data_len > 0 ? (guint)data_len : strlen (data);
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))
{
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);
}
break;
}
else if (freeme)
{
g_byte_array_free (freeme, TRUE);
g_queue_pop_head (pt->pending_write);
}
}
else
{
2006-11-01 22:38:00 -08:00
_moo_message ("%s: stopping writing to child", G_STRLOC);
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-06-22 15:13:12 -07:00
}
if (!g_queue_is_empty (pt->pending_write))
2005-07-07 04:06:23 -07:00
start_writer (pt);
else
2005-07-07 04:06:23 -07:00
stop_writer (pt);
2005-06-22 15:13:12 -07:00
}
static char
get_erase_char (MooTermPt *pt_gen)
{
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;
}
}
static void
send_intr (MooTermPt *pt)
{
g_return_if_fail (pt->alive);
pt_discard_pending_write (pt);
pt_write (pt, "\003", 1);
}
2005-07-26 04:12:40 -07:00
/* TODO: it should be in glib */
MooTermCommand *
_moo_term_get_default_shell (void)
2005-07-26 04:12:40 -07:00
{
static char *argv[2] = {NULL, NULL};
2005-07-26 04:12:40 -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
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 */
void
moo_term_set_helper_directory (G_GNUC_UNUSED const char *dir)
2005-07-30 23:20:59 -07:00
{
g_return_if_reached ();
}
static char *argv_to_cmd_line (char **argv)
{
GString *cmd = NULL;
char **p;
g_return_val_if_fail (argv && argv[0], NULL);
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;
}
gboolean
_moo_term_check_cmd (MooTermCommand *cmd,
GError **error)
{
g_return_val_if_fail (cmd != NULL, FALSE);
g_return_val_if_fail (cmd->cmd_line != NULL || cmd->argv != NULL, FALSE);
if (cmd->argv)
{
if (!cmd->argv[0])
{
g_set_error (error, MOO_TERM_ERROR, MOO_TERM_ERROR_INVAL,
"empty arguments array");
return FALSE;
}
g_free (cmd->cmd_line);
cmd->cmd_line = argv_to_cmd_line (cmd->argv);
g_return_val_if_fail (cmd->cmd_line != NULL, FALSE);
return TRUE;
}
else
{
cmd->argv = cmd_line_to_argv (cmd->cmd_line, error);
return cmd->argv != NULL;
}
}