#include <string.h>
#include <signal.h>
#include "gskinit.h"
#include "gskerror.h"
#include "gskbuffer.h"
#include "gsknameresolver.h"
#include "gskmainloop.h"
#include "gskdebug.h"

_GskInitFlags gsk_init_flags = 0;
gpointer gsk_main_thread = NULL;

/**
 * gsk_init_info_get_defaults:
 * @info: the #GskInitInfo to fill.
 *
 * Obtain the default initialization information.
 * This should be run before gsk_init() or gsk_init_info_parse_args().
 *
 * This API has been deprecated for public use,
 * because it doesn't allow us to expand
 * GskInitInfo without breaking binary-compatibility.
 *
 * Use gsk_init_info_new() instead.
 */
void
gsk_init_info_get_defaults (GskInitInfo *info)
{
  info->prgname = NULL;
  info->needs_threads = 1;
}

/**
 * gsk_init_info_new:
 *
 * Create a new, default initialization-configuration object.
 *
 * returns: the newly allocated #GskInitInfo.
 */
GskInitInfo *
gsk_init_info_new (void)
{
  GskInitInfo *info = g_new (GskInitInfo, 1);
  gsk_init_info_get_defaults (info);
  return info;
}

/**
 * gsk_init_info_free:
 * @info: the object to free.
 *
 * Free a initialization-configuration object.
 */
void
gsk_init_info_free (GskInitInfo *info)
{
  g_free (info);
}

/**
 * gsk_init:
 * @argc: a reference to main()'s argc;
 * this will be decreased if arguments are parsed
 * out of the argument array.
 * @argv: a reference to main()'s argc;
 * this may have arguments removed.
 * @info: the #GskInitInfo to use as hints,
 * which will be filled with the
 * actual initialization information used.
 * If NULL, default initialization parameters
 * will be used.
 *
 * Initialize the GSK library.
 */
void
gsk_init                   (int         *argc,
			    char      ***argv,
			    GskInitInfo *info)
{
  g_type_init ();
  if (info == NULL)
    {
      info = g_newa (GskInitInfo, 1);
      gsk_init_info_get_defaults (info);
    }
  gsk_init_info_parse_args (info, argc, argv);
  gsk_init_raw (info);
}

/**
 * gsk_init_without_threads:
 * @argc: a reference to main()'s argc;
 * this will be decreased if arguments are parsed
 * out of the argument array.
 * @argv: a reference to main()'s argc;
 * this may have arguments removed.
 *
 * Initialize the GSK library indicating that you will not use threads.
 */
void
gsk_init_without_threads   (int         *argc,
			    char      ***argv)
{
  GskInitInfo info;
  g_type_init ();
  gsk_init_info_get_defaults (&info);
  info.needs_threads = FALSE;
  gsk_init_info_parse_args (&info, argc, argv);
  gsk_init_raw (&info);
}

static struct
{
  const char *name;
  guint flag;
} debug_flag_names[] = {
  { "io", GSK_DEBUG_IO },
  { "stream-data", GSK_DEBUG_STREAM_DATA },
  { "stream", GSK_DEBUG_STREAM },
  { "listener", GSK_DEBUG_STREAM_LISTENER },
  { "lifetime", GSK_DEBUG_LIFETIME },
  { "mainloop", GSK_DEBUG_MAIN_LOOP },
  { "dns", GSK_DEBUG_DNS },
  { "hook", GSK_DEBUG_HOOK },
  { "request", GSK_DEBUG_REQUEST },
  { "fd", GSK_DEBUG_FD },
  { "ssl", GSK_DEBUG_SSL },
  { "all", GSK_DEBUG_ALL },
  { NULL, 0 }
};

static void
handle_debug_flags (const char *opts)
{
  for (;;)
    {
      guint k;
      for (k = 0; debug_flag_names[k].name != NULL; k++)
	{
	  const char *o = debug_flag_names[k].name;
	  if (strncmp (opts, o, strlen (o)) == 0)
	    {
	      gsk_debug_add_flags (debug_flag_names[k].flag);
	      break;
	    }
	}
      if (debug_flag_names[k].name == NULL)
	{
	  char *tmp = g_strdup (opts);
	  char *comma = strchr (tmp, ',');
	  if (comma)
	    *comma = 0;
	  g_warning ("no debugging option `%s' found", tmp);
	  g_free (tmp);
	}
      opts = strchr (opts, ',');
      if (!opts)
	break;
      opts++;
    }
}

/**
 * gsk_init_info_parse_args:
 * @in_out: the #GskInitInfo to fill.
 * @argc: the argument count (may be modified)
 * @argv: the arguments (may be modified)
 *
 * Parse/modify arguments and return their values in @in_out.
 *
 * The only currently supported argument is --gsk-debug=FLAGS.
 */
void
gsk_init_info_parse_args (GskInitInfo *in_out,
			  int         *argc,
			  char      ***argv)
{
  gint i;
  g_type_init ();
  if (in_out->prgname == NULL && argv != NULL)
    in_out->prgname = (*argv)[0];

  for (i = 1; i < *argc; )
    {
      const char *arg = (*argv)[i];
      guint num_to_swallow = 0;

      /* handle --gsk-debug= */
      if (strncmp (arg, "--gsk-debug=", 12) == 0)
	{
	  const char *opts = arg + 12;
	  handle_debug_flags (opts);
	  num_to_swallow = 1;
	}

      if (strcmp (arg, "--g-fatal-warnings") == 0)
	{
	  g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
	  num_to_swallow = 1;
	}

      if (num_to_swallow)
	{
	  memcpy ((*argv) + i, (*argv) + i + num_to_swallow,
		  (*argc - num_to_swallow - i + 1) * sizeof (char *));
	  *argc -= num_to_swallow;
	}
      else
	{
	  i++;
	}
    }

  {
    const char *debug_flags = g_getenv ("GSK_DEBUG");
    if (debug_flags)
      handle_debug_flags (debug_flags);
  }
}

static void
gsk_socket_address_family_init (void)
{
  g_type_class_ref (GSK_TYPE_SOCKET_ADDRESS_IPV4);
  g_type_class_ref (GSK_TYPE_SOCKET_ADDRESS_IPV6);
}

void _gsk_hook_init(void);
void _gsk_name_resolver_init (void);
void _gsk_url_transfer_register_builtins (void);

/**
 * gsk_init_raw:
 * @info: information to use for initializing.
 *
 * Initialize GSK.
 */
void
gsk_init_raw (GskInitInfo *info)
{
  static gboolean has_initialized = FALSE;

  if (has_initialized)
    return;
  has_initialized = TRUE;

  if (info->prgname != NULL && g_get_prgname () == NULL)
    g_set_prgname (info->prgname);

  gsk_init_flags = 0;
  if (info->needs_threads)
    gsk_init_flags |= _GSK_INIT_SUPPORT_THREADS;

  g_type_init ();
  if (info->needs_threads)
    {
      g_thread_init (NULL);
      gsk_main_thread = g_thread_self ();
    }
  _gsk_hook_init ();
  _gsk_error_init ();
  _gsk_name_resolver_init ();
  _gsk_main_loop_init ();
  _gsk_url_transfer_register_builtins ();
  gsk_socket_address_family_init ();

  /* we always want to ignore SIGPIPE;
     we just handle errno==EPIPE properly. */
  signal (SIGPIPE, SIG_IGN);
}


syntax highlighted by Code2HTML, v. 0.9.1