#include <stdlib.h>
#include "gskio.h"
#include "gskmacros.h"
#include "debug.h"
/* always use 'notify_connected' from the outside */
#define gsk_io_clear_is_connecting(io) _GSK_IO_CLEAR_FIELD (io, is_connecting)
static GObjectClass *parent_class = NULL;
/* signals */
static guint on_connect_signal = 0;
static guint on_error_signal = 0;
/* ugh... */
#define DEBUG_PRINT_HEADER(flags, fctname) \
_GSK_DEBUG_PRINTF((flags), ("running %s on %s[%p]", \
fctname, g_type_name(G_OBJECT_TYPE(io)), io))
/**
* gsk_io_error_cause_to_string:
* @cause: the cause code.
*
* Convert the GskIOErrorCause code into a human-readable lowercase string.
*
* returns: the error as a string.
*/
const char *
gsk_io_error_cause_to_string (GskIOErrorCause cause)
{
switch (cause)
{
case GSK_IO_ERROR_NONE: return "none";
case GSK_IO_ERROR_OPEN: return "open";
case GSK_IO_ERROR_READ: return "read";
case GSK_IO_ERROR_WRITE: return "write";
case GSK_IO_ERROR_POLL_READ: return "poll-read";
case GSK_IO_ERROR_POLL_WRITE: return "poll-write";
case GSK_IO_ERROR_SHUTDOWN_READ: return "shutdown-read";
case GSK_IO_ERROR_SHUTDOWN_WRITE: return "shutdown-write";
case GSK_IO_ERROR_CLOSE: return "close";
case GSK_IO_ERROR_SYNC: return "sync";
case GSK_IO_ERROR_POLL: return "poll";
default: return "*unknown*";
}
}
static gboolean default_print_errors = FALSE;
static gboolean has_default_print_errors = FALSE;
void
gsk_io_set_default_print_errors (gboolean print_errors)
{
has_default_print_errors = TRUE;
default_print_errors = print_errors;
}
static void
gsk_io_set_error_literal (GskIO *io,
GskIOErrorCause cause,
GError *error)
{
g_assert (error != NULL);
if (io->error != NULL)
g_error_free (io->error);
io->error = error;
io->error_cause = cause;
if (GSK_IS_DEBUGGING (IO) || io->print_errors)
g_message ("I/O Error [%s,%p]: cause=%s: %s",
G_OBJECT_TYPE_NAME (io), io,
gsk_io_error_cause_to_string (cause),
error->message);
g_signal_emit (io, on_error_signal, 0);
if (io->error != NULL
&& gsk_io_get_shutdown_on_error (io))
{
gsk_io_shutdown (io, NULL);
}
}
/**
* gsk_io_set_error:
* @io: the object whose GError member should be set.
* @cause: what kind of situation triggered the error
* @error_code: an error code.
* @format: a printf-like format string.
* @Varargs: values to be embedded in the format string.
*
* Set the error member of the #GskIO.
*/
void
gsk_io_set_error (GskIO *io,
GskIOErrorCause cause,
GskErrorCode error_code,
const char *format,
...)
{
va_list args;
guint len;
char *buf;
GError *error;
va_start (args, format);
len = g_printf_string_upper_bound (format, args);
buf = alloca (len + 1);
g_vsnprintf (buf, len + 1, format, args);
va_end (args);
error = g_error_new_literal (GSK_G_ERROR_DOMAIN, error_code, buf);
gsk_io_set_error_literal (io, cause, error);
}
/**
* gsk_io_set_gerror:
* @io: the IO whose error member should be set.
* @cause: the operation which caused the error.
* @error: gerror which will be freed by the IO automatically now.
*
* Set the IO's error member, taking ownership of
* the @error parameter.
*/
void
gsk_io_set_gerror (GskIO *io,
GskIOErrorCause cause,
GError *error)
{
g_return_if_fail (error->domain == GSK_G_ERROR_DOMAIN);
gsk_io_set_error_literal (io, cause, error);
}
/**
* gsk_io_shutdown:
* @io: the object which should be shut down.
* @error: optional error to set upon failure.
*
* Shutdown the read and write ends of a #GskIO.
*/
void
gsk_io_shutdown (GskIO *io, GError **error)
{
g_object_ref (io);
gsk_io_read_shutdown (io, error);
gsk_io_write_shutdown (io, error);
g_object_unref (io);
}
/* --- implement poll-read/write for nonblocking ios --- */
/**
* gsk_io_notify_shutdown:
* @io: the object which is shut-down.
*
* This function is called by an implementation
* when the read- and write- ends of the i/o object
* have both shut-down.
*/
void
gsk_io_notify_shutdown (GskIO *io)
{
g_object_ref (io);
gsk_io_notify_read_shutdown (io);
gsk_io_notify_write_shutdown (io);
g_object_unref (io);
}
/**
* gsk_io_notify_connected:
* @io: the #GskIO that finished connecting to the remote side.
*
* Trigger an is-connected event. This should only be
* called by derived implementations.
* Called to indicate that the connection has been made.
*/
void
gsk_io_notify_connected (GskIO *io)
{
g_return_if_fail (gsk_io_get_is_connecting (io));
DEBUG_PRINT_HEADER (GSK_DEBUG_IO, "gsk_io_notify_connected");
gsk_io_clear_is_connecting (io);
g_signal_emit (io, on_connect_signal, 0);
}
/**
* gsk_io_close:
* @io: the #GskIO to close.
*
* Close an open #GskIO.
*/
void
gsk_io_close (GskIO *io)
{
GskIOClass *class = GSK_IO_GET_CLASS (io);
g_return_if_fail (io->is_open);
if (class->close != NULL)
(*class->close) (io);
io->is_open = FALSE;
}
static GObject *
gsk_io_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *rv = parent_class->constructor (type, n_construct_properties,
construct_properties);
GskIO *io = GSK_IO (rv);
GskIOClass *class = GSK_IO_GET_CLASS (io);
_GSK_DEBUG_PRINTF(GSK_DEBUG_LIFETIME,
("constructing %s [%p] [num_construct_properties=%d]",
g_type_name (type), rv, n_construct_properties));
if (class->open != NULL)
{
GError *error = NULL;
if (class->open (io, &error))
{
io->is_open = 1;
}
else
{
if (error == NULL)
gsk_io_set_error (io, GSK_IO_ERROR_OPEN,
GSK_ERROR_OPEN_FAILED,
_("open failed for %s (no explanation given)"),
g_type_name (G_OBJECT_CLASS_TYPE (class)));
else
gsk_io_set_error_literal (io, GSK_IO_ERROR_OPEN, error);
io->open_failed = 1;
}
}
else
{
io->is_open = 1;
}
return rv;
}
static void
gsk_io_finalize (GObject *object)
{
GskIO *io = GSK_IO (object);
DEBUG_PRINT_HEADER(GSK_DEBUG_IO | GSK_DEBUG_LIFETIME, "gsk_io_finalize");
/* TO CONSIDER: should we close then destruct the hooks, instead? */
gsk_hook_destruct (&io->read_hook);
gsk_hook_destruct (&io->write_hook);
if (io->error)
g_error_free (io->error);
gsk_io_close (io);
(*parent_class->finalize) (object);
}
/* --- functions --- */
static void
gsk_io_init (GskIO *io)
{
gsk_io_mark_shutdown_on_error (io);
io->print_errors = default_print_errors;
GSK_HOOK_INIT (io, GskIO, read_hook,
GSK_HOOK_CAN_HAVE_SHUTDOWN_ERROR,
set_poll_read, shutdown_read);
GSK_HOOK_INIT (io, GskIO, write_hook,
GSK_HOOK_CAN_HAVE_SHUTDOWN_ERROR,
set_poll_write, shutdown_write);
}
static void
gsk_io_class_init (GskIOClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GType type = G_OBJECT_CLASS_TYPE (object_class);
parent_class = g_type_class_peek_parent (class);
object_class->constructor = gsk_io_constructor;
object_class->finalize = gsk_io_finalize;
GSK_HOOK_CLASS_INIT (object_class, "read", GskIO, read_hook);
GSK_HOOK_CLASS_INIT (object_class, "write", GskIO, write_hook);
on_connect_signal
= g_signal_new ("on-connect",
type,
G_SIGNAL_NO_RECURSE,
G_STRUCT_OFFSET (GskIOClass, on_connect),
NULL, /* accumulator */
NULL, /* accu_data */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
on_error_signal
= g_signal_new ("on-error",
type,
G_SIGNAL_NO_RECURSE,
G_STRUCT_OFFSET (GskIOClass, on_error),
NULL, /* accumulator */
NULL, /* accu_data */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
if (!has_default_print_errors)
{
const char *env = getenv ("GSK_PRINT_ERRORS");
if (env)
gsk_io_set_default_print_errors (atoi (env) ? 1 : 0);
}
}
GType gsk_io_get_type()
{
static GType io_type = 0;
if (!io_type)
{
static const GTypeInfo io_info =
{
sizeof(GskIOClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_io_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskIO),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_io_init,
NULL /* value_table */
};
io_type = g_type_register_static (G_TYPE_OBJECT, "GskIO",
&io_info, G_TYPE_FLAG_ABSTRACT);
}
return io_type;
}
syntax highlighted by Code2HTML, v. 0.9.1