Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
xremote_gui_test.c 56.33 KiB
/*  File: xremote_gui_test.c
 *  Author: Roy Storey (rds@sanger.ac.uk)
 *  Copyright (c) 2006-2010: Genome Research Ltd.
 *-------------------------------------------------------------------
 * ZMap 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.
 * 
 * This program 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
 *-------------------------------------------------------------------
 * This file is part of the ZMap genome database package
 * originally written by:
 *
 * 	Ed Griffiths (Sanger Institute, UK) edgrif@sanger.ac.uk,
 *      Roy Storey (Sanger Institute, UK) rds@sanger.ac.uk
 *
 * Description: Test program for ZMap Xremote API.
 *
 * Exported functions: None
 *              
 * HISTORY:
 * Last edited: Mar 26 09:20 2010 (edgrif)
 * Created: Thu Feb 15 11:25:20 2007 (rds)
 * CVS info:   $Id: xremote_gui_test.c,v 1.21 2010-03-29 09:54:55 edgrif Exp $
 *-------------------------------------------------------------------
 */

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <gtk/gtk.h>
#include <string.h>
#include <ZMap/zmapConfigIni.h>
#include <ZMap/zmapConfigDir.h>
#include <ZMap/zmapUtils.h>
#include <ZMap/zmapUtilsGUI.h>
#include <ZMap/zmapXRemote.h>
#include <ZMap/zmapXML.h>
#include <ZMap/zmapUtilsXRemote.h>
#include <zmapControl/remote/zmapXRemoteAPI.h>


/* command line args defines */
#define XREMOTEARG_NO_ARG "<none>"
#define XREMOTEARG_FILE_ARG "file path"

#define XREMOTEARG_VERSION "version"
#define XREMOTEARG_VERSION_DESC "version number"

#define XREMOTEARG_CONFIG "config-file"
#define XREMOTEARG_CONFIG_DESC "file location for config"

#define XREMOTEARG_COMMAND "command-file"
#define XREMOTEARG_COMMAND_DESC "file location for commands"

#define XREMOTEARG_DEBUGGER "in-debugger"
#define XREMOTEARG_DEBUGGER_DESC "Start zmap within debugger (Totalview)."

#define XREMOTEARG_XREMOTE_DEBUG "xremote-debug"
#define XREMOTEARG_XREMOTE_DEBUG_DESC "Display xremote debugging output (default false)."
#define XREMOTEARG_CMD_DEBUG "command-debug"
#define XREMOTEARG_CMD_DEBUG_DESC "Display command debugging output (default true)."


#define XREMOTEARG_SEQUENCE "sequence"
#define XREMOTEARG_SEQUENCE_DESC "Set up xremote with supplied sequence."

#define XREMOTEARG_NO_TIMEOUT "no_timeout"
#define XREMOTEARG_NO_TIMEOUT_DESC "Never timeout waiting for a response."

#define XREMOTEARG_DEFAULT_SEQUENCE "< Enter your sequence here >"


/* config file defines */
#define XREMOTE_PROG_CONFIG "programs"

#define XREMOTE_PROG_ZMAP      "zmap-exe"
#define XREMOTE_PROG_ZMAP_OPTS "zmap-options"
#define XREMOTE_PROG_SERVER      "sgifaceserver-exe"
#define XREMOTE_PROG_SERVER_OPTS "sgifaceserver-options"


typedef struct
{
  int argc;
  char **argv;
  GOptionContext *opt_context;
  GError *error;

  /* and the commandline args */
  gboolean version;
  char *config_file;
  char *command_file;
  gboolean debugger ;
  gboolean xremote_debug ;
  gboolean cmd_debug ;
  char *sequence ;
  gboolean timeout ;
} XRemoteCmdLineArgsStruct, *XRemoteCmdLineArgs;

typedef struct
{
  GtkWidget *app_toplevel, *vbox, *menu, *text_area, 
    *buttons, *sequence, *zmap_path, *client_entry;

  GtkWidget *response_text_area ;


  char *window_id ;

  GtkTextBuffer *text_buffer;
  GtkTextBuffer *response_text_buffer ;

  ZMapXRemoteObj xremote_server;
  GHashTable *xremote_clients;
  gulong register_client_id;
  gboolean is_register_client;
  int zmap_pid, server_pid;
  XRemoteCmdLineArgs cmd_line_args;
  ZMapConfigIniContext config_context;
  GQueue *queue;
} XRemoteTestSuiteDataStruct, *XRemoteTestSuiteData;

typedef struct
{
  XRemoteTestSuiteData suite;
  GHashTable *new_clients;
  char *xml;
  int hash_size;
  char *action;
  gboolean sent;
}SendCommandDataStruct, *SendCommandData;

typedef struct
{
  ZMapXRemoteObj client;
  GList *actions;
  gboolean is_main_window;
}HashEntryStruct, *HashEntry;

typedef struct
{
  GSourceFunc main_runner;
  GSourceFunc runner_done;
  gpointer    runner_data;
  GDestroyNotify destroy;
  gboolean    runnable;
} DependRunnerStruct, *DependRunner;


static int zmapXremoteTestSuite(int argc, char *argv[]);
static void installPropertyNotify(GtkWidget *ignored, GdkEvent *event, XRemoteTestSuiteData suite);
static char *handle_register_client(char *command_text, gpointer user_data, int *statusCode);
static GtkWidget *entry_box_widgets(XRemoteTestSuiteData suite);
static GtkWidget *menubar(XRemoteTestSuiteData suite);
static GtkWidget *message_box(XRemoteTestSuiteData suite);
static GtkWidget *button_bar(XRemoteTestSuiteData suite);

static int send_command_cb(gpointer key, gpointer hash_data, gpointer user_data);

static int events_to_text_buffer(ZMapXMLWriter writer, char *xml, int len, gpointer user_data);

static void cmdCB( gpointer data, guint callback_action, GtkWidget *w );
static void menuQuitCB(gpointer data, guint callback_action, GtkWidget *w);

static void addClientCB(GtkWidget *button, gpointer user_data);
static void quitCB(GtkWidget *button, gpointer user_data);
static void clearCB(GtkWidget *button, gpointer user_data);
static void parseCB(GtkWidget *button, gpointer user_data);
static void sendCommandCB(GtkWidget *button, gpointer user_data);
static void listClientsCB(GtkWidget *button, gpointer user_data);
static void runZMapCB(GtkWidget *button, gpointer user_data);

static gboolean api_zmap_start_cb(gpointer user_data, ZMapXMLElement zmap_element, ZMapXMLParser parser) ;
static gboolean api_zmap_end_cb(gpointer user_data, ZMapXMLElement zmap_element, ZMapXMLParser parser) ;
static gboolean api_request_start_cb(gpointer user_data, ZMapXMLElement zmap_element, ZMapXMLParser parser) ;
static gboolean api_request_end_cb(gpointer user_data, ZMapXMLElement zmap_element, ZMapXMLParser parser) ;


static gboolean xml_zmap_start_cb(gpointer user_data, ZMapXMLElement element, 
                                  ZMapXMLParser parser);
static gboolean xml_response_start_cb(gpointer user_data, ZMapXMLElement element, 
                                      ZMapXMLParser parser);
static gboolean xml_client_start_cb(gpointer user_data, ZMapXMLElement client_element,
                                    ZMapXMLParser parser);
static gboolean xml_client_end_cb(gpointer user_data, ZMapXMLElement client_element,
				  ZMapXMLParser parser);
static gboolean xml_error_end_cb(gpointer user_data, ZMapXMLElement element, 
                                 ZMapXMLParser parser);
static gboolean xml_zmap_end_cb(gpointer user_data, ZMapXMLElement element, 
                                ZMapXMLParser parser);

static gboolean copy_hash_to_hash(gpointer key, gpointer value, gpointer user_data);
static char *getXML(XRemoteTestSuiteData suite, int *length_out);
static HashEntry clientToHashEntry(ZMapXRemoteObj client, gboolean is_main);
static gboolean addClientToHash(GHashTable *table, ZMapXRemoteObj client, Window id, GList *actions, gboolean is_main);
static void destroyHashEntry(gpointer entry_data);
static void appExit(int exit_rc);

static void internal_send_command(SendCommandData send_data);
static gboolean run_command(char **command, int *pid);

static gboolean start_zmap_cb(gpointer suite_data);

static GOptionEntry *get_main_entries(XRemoteCmdLineArgs arg_context);
static gboolean makeOptionContext(XRemoteCmdLineArgs arg_context);
static XRemoteCmdLineArgs process_command_line_args(int argc, char *argv[]);
static void process_command_file(XRemoteTestSuiteData suite, char *command_file);
static ZMapConfigIniContext get_configuration(XRemoteTestSuiteData suite);
static ZMapConfigIniContextKeyEntry get_programs_group_data(char **stanza_name, char **stanza_type);

enum
  {  
    XREMOTE_NEW_ZMAP,
    XREMOTE_NEW_VIEW,
    XREMOTE_REGISTER_CLIENT,
    XREMOTE_ZOOMIN,
    XREMOTE_ZOOMOUT,
    XREMOTE_CREATE,
    XREMOTE_DELETE,
    XREMOTE_ZOOMTO,
    XREMOTE_SHUTDOWN
  };



/* Testbed for ZMapXRemoteAPI */
/* ...Roys remark....BUT I'M NOT SURE IF THIS IS ACTUALLY USED ANYWHERE..... */

typedef struct
{
  XRemoteMessage message_out;
  ZMapXMLParser xml_parser;
  char *xml_message;
  unsigned int arrest_processing;
  unsigned int full_processing;
  unsigned int xml_length;
} APIProcessingStruct, *APIProcessing;





static gboolean command_debug_G = TRUE ;


static GtkItemFactoryEntry menu_items_G[] =
  {
    {"/_File",                   NULL,         NULL,       0,                "<Branch>", NULL},
    {"/File/Read",               NULL,         NULL,       0,                NULL,       NULL},  
    {"/File/Quit",               "<control>Q", menuQuitCB, 0,                NULL,       NULL},  
    {"/_Commands",               NULL,         NULL,       0,                "<Branch>", NULL},
    {"/Commands/new_zmap",            NULL,         cmdCB,      XREMOTE_NEW_ZMAP,      NULL,       NULL},
    {"/Commands/new_view",            NULL,         cmdCB,      XREMOTE_NEW_VIEW,      NULL,       NULL},
    {"/Commands/register client",     NULL,         cmdCB,      XREMOTE_REGISTER_CLIENT,   NULL,       NULL},
    {"/Commands/Zoom In",        NULL,         cmdCB,      XREMOTE_ZOOMIN,   NULL,       NULL},
    {"/Commands/Zoom Out",       NULL,         cmdCB,      XREMOTE_ZOOMOUT,  NULL,       NULL},
    {"/Commands/zoom_to",   NULL,         cmdCB,      XREMOTE_ZOOMTO,   NULL,       NULL},
    {"/Commands/Feature Create", NULL,         cmdCB,      XREMOTE_CREATE,   NULL,       NULL},
    {"/Commands/Feature Delete", NULL,         cmdCB,      XREMOTE_DELETE,   NULL,       NULL},
    {"/Commands/shutdown",       NULL,         cmdCB,      XREMOTE_SHUTDOWN, NULL,       NULL}
  };


static ZMapXMLObjTagFunctionsStruct api_message_starts_G[] =
  {
    {"zmap", api_zmap_start_cb },
    {"request", api_request_start_cb },
    { NULL,  NULL }
  };

static ZMapXMLObjTagFunctionsStruct api_message_ends_G[] =
  {
    { "zmap", api_zmap_end_cb },
    { "request", api_request_end_cb },
    { NULL,   NULL }
  };


/* Gets used in debug messages embedded in the xremote lib. */
char *ZMAP_X_PROGRAM_G  = "xremote_gui" ;



/* main */
int main(int argc, char *argv[])
{
  int main_rc;

  main_rc = zmapXremoteTestSuite(argc, argv);

  return main_rc;
}


/* Is this actually called anywhere ???? */
gboolean zMapXRemoteAPIMessageProcess(char           *message_xml_in, 
				      gboolean        full_process, 
				      char          **action_out,
				      XRemoteMessage *message_out)
{
  APIProcessingStruct process_data = { NULL };
  gboolean success = FALSE;

  if (full_process && message_out)
    {
      /* Do further processing */
      process_data.full_processing = full_process;
    }

  /* No point doing anything if there's no where to stick it */
  if (action_out || message_out)
    {
      process_data.xml_parser  = zMapXMLParserCreate(&process_data, FALSE, FALSE);
      process_data.xml_message = message_xml_in;
      process_data.xml_length  = strlen(message_xml_in);
      
      process_data.message_out = g_new0(XRemoteMessageStruct, 1);

      zMapXMLParserSetMarkupObjectTagHandlers(process_data.xml_parser,
					      api_message_starts_G,
					      api_message_ends_G);
      
      if((success = zMapXMLParserParseBuffer(process_data.xml_parser,
					     process_data.xml_message,
					     process_data.xml_length)))
	{
	  /* Need to make sure everything is good */
	  
	  if(action_out)
	    *action_out = g_strdup(process_data.message_out->action);
	  
	  if(message_out)
	    *message_out = process_data.message_out;
	  else
	    g_free(process_data.message_out);
	}
      else
	g_free(process_data.message_out);
    }

  return success;
}




/* 
 *                Internal functions.
 */


/* rest of the code. */

/* called by main to do the creation for the rest of the app and run gtk_main() */
static int zmapXremoteTestSuite(int argc, char *argv[])
{
  XRemoteTestSuiteData suite;
  XRemoteCmdLineArgs cmd_args;
  GtkWidget *toplevel, *vbox, *req_hbox, *our_vbox, *their_vbox,
    *menu_bar, *buttons, *textarea, *frame, *response_frame, *entry_box ;

  gtk_init(&argc, &argv);

  cmd_args = process_command_line_args(argc, argv);

  if (cmd_args->xremote_debug)
    zMapXRemoteSetDebug(cmd_args->xremote_debug) ;

  suite = g_new0(XRemoteTestSuiteDataStruct, 1) ;

  suite->xremote_clients = g_hash_table_new_full(NULL, NULL, NULL, destroyHashEntry);
  suite->cmd_line_args   = cmd_args;
  suite->config_context  = get_configuration(suite);

  suite->app_toplevel = toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  /* Only after map-event are we guaranteed that there's a window for us to work with. */
  g_signal_connect(G_OBJECT(toplevel), "map-event",
		   G_CALLBACK(installPropertyNotify), suite);
  g_signal_connect(G_OBJECT(toplevel), "destroy", 
		   G_CALLBACK(quitCB), (gpointer)suite) ;

  gtk_window_set_policy(GTK_WINDOW(toplevel), FALSE, TRUE, FALSE);
  gtk_window_set_title(GTK_WINDOW(toplevel), "ZMap XRemote Test Suite");
  gtk_container_border_width(GTK_CONTAINER(toplevel), 10);

  suite->vbox = vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(toplevel), vbox);

  suite->menu = menu_bar = menubar(suite);

  suite->text_area = textarea = message_box(suite) ;
  suite->text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(suite->text_area)) ;
  gtk_text_buffer_set_text( suite->text_buffer, "Enter xml here...", -1) ;


  suite->buttons = buttons  = button_bar(suite);

  entry_box = entry_box_widgets(suite) ;


  req_hbox = gtk_hbox_new(FALSE, 0) ;
  our_vbox = gtk_vbox_new(FALSE, 0);
  their_vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(req_hbox), our_vbox, TRUE, TRUE, 5) ;
  gtk_box_pack_start(GTK_BOX(req_hbox), their_vbox, TRUE, TRUE, 5) ;

  
  frame = gtk_frame_new("Our Request");
  gtk_box_pack_start(GTK_BOX(our_vbox), frame, TRUE, TRUE, 5);
  gtk_container_add(GTK_CONTAINER(frame), textarea);



  response_frame = gtk_frame_new("ZMaps Response") ;
  gtk_box_pack_start(GTK_BOX(our_vbox), response_frame, TRUE, TRUE, 5);
  suite->response_text_area = message_box(suite);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(suite->response_text_area), FALSE) ;
  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(suite->response_text_area), FALSE) ;
  suite->response_text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(suite->response_text_area)) ;

  gtk_container_add(GTK_CONTAINER(response_frame), suite->response_text_area) ;


  gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, TRUE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), req_hbox, TRUE, TRUE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), entry_box, TRUE, TRUE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), buttons, TRUE, TRUE, 5);


  gtk_widget_show_all(toplevel);

  gtk_main();

  appExit(EXIT_SUCCESS);

  return EXIT_FAILURE ;
}

/* install the property notify, receives the requests from zmap, when started with --win_id option */
static void installPropertyNotify(GtkWidget *widget, GdkEvent *event, XRemoteTestSuiteData suite)
{
  zMapXRemoteInitialiseWidget(widget, "xremote_gui_test", 
                              "_CLIENT_REQUEST_NAME", "_CLIENT_RESPONSE_NAME", 
                              handle_register_client, suite);
  externalPerl = TRUE;

  if (suite->cmd_line_args && suite->cmd_line_args->command_file)
    {
      process_command_file(suite, suite->cmd_line_args->command_file);
    }

  suite->window_id = g_strdup_printf("0x%lx", GDK_DRAWABLE_XID(suite->app_toplevel->window)) ;
  
  return ;
}

/* The property notify, receives the requests from zmap, when started with --win_id option */
static char *handle_register_client(char *command_text, gpointer user_data, int *statusCode)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  ZMapXMLObjTagFunctionsStruct starts[] = {
    {"zmap",     xml_zmap_start_cb   },
    {"client",   xml_client_start_cb },
    { NULL, NULL}
  };
  ZMapXMLObjTagFunctionsStruct ends[] = {
    {"zmap",   xml_zmap_end_cb  },
    {"client", xml_client_end_cb },
    { NULL, NULL}
  };
  SendCommandDataStruct parser_data = {NULL};
  ZMapXMLParser parser;
  gboolean parse_ok;
  char *reply;
  int status = 500;

  parser_data.suite = suite;

  parser = zMapXMLParserCreate(&parser_data, FALSE, FALSE);

  zMapXMLParserSetMarkupObjectTagHandlers(parser, &starts[0], &ends[0]);

  reply = g_strdup_printf("%s", "** no reply text **");

  if((parse_ok = zMapXMLParserParseBuffer(parser, command_text, strlen(command_text))))
    {
      status = 200;
    }
  else
    status = 403;

  if(statusCode)
    *statusCode = status;


  return reply ;
}

/* create the entry box widgets */
static GtkWidget *entry_box_widgets(XRemoteTestSuiteData suite)
{
  GtkWidget *entry_box, *sequence, *label, *path;

  entry_box = gtk_hbox_new(FALSE, 0);

  label = gtk_label_new("zmap path :");
  suite->zmap_path = path = gtk_entry_new();

  gtk_box_pack_start(GTK_BOX(entry_box), label, FALSE, FALSE, 5);
  gtk_box_pack_start(GTK_BOX(entry_box), path, FALSE, FALSE, 5);

  label = gtk_label_new("sequence :");
  suite->sequence = sequence = gtk_entry_new();

  /* Set the default text. The current zmap_test_suite.sh sequence */
  gtk_entry_set_text(GTK_ENTRY(sequence), suite->cmd_line_args->sequence);

  gtk_box_pack_start(GTK_BOX(entry_box), label, FALSE, FALSE, 5);
  gtk_box_pack_start(GTK_BOX(entry_box), sequence, FALSE, FALSE, 5);

  label = gtk_label_new("client :");
  gtk_box_pack_start(GTK_BOX(entry_box), label, FALSE, FALSE, 5);
  suite->client_entry = sequence = gtk_entry_new();
  gtk_box_pack_start(GTK_BOX(entry_box), sequence, FALSE, FALSE, 5);  

  return entry_box;
}

/* create the menu bar */
static GtkWidget *menubar(XRemoteTestSuiteData suite)
{
  GtkWidget *menubar;
  GtkItemFactory *factory;
  GtkAccelGroup *accel;
  gint nmenu_items = sizeof (menu_items_G) / sizeof (menu_items_G[0]);
  
  accel = gtk_accel_group_new();
  
  factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel);

  gtk_item_factory_create_items(factory, nmenu_items, menu_items_G, suite);

  gtk_window_add_accel_group(GTK_WINDOW(suite->app_toplevel), accel);

  menubar = gtk_item_factory_get_widget(factory, "<main>");

  return menubar;
}
/* the message box where xml is entered */
static GtkWidget *message_box(XRemoteTestSuiteData suite)
{
  GtkWidget *message_box;

  message_box = gtk_text_view_new() ;
  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(message_box), GTK_WRAP_WORD) ;

  gtk_widget_set_size_request(message_box, -1, 200) ;

  return message_box;
}

/* All the buttons of the button bar */
static GtkWidget *button_bar(XRemoteTestSuiteData suite)
{
  GtkWidget *button_bar, *run_zmap, *send_command, 
    *clear, *parse, *list_clients, *add_client, *exit;

  button_bar   = gtk_hbutton_box_new();
  run_zmap     = gtk_button_new_with_label("Run ZMap");
  clear        = gtk_button_new_with_label("Clear XML");
  parse        = gtk_button_new_with_label("Check XML");
  send_command = gtk_button_new_with_label("Send Command");
  list_clients = gtk_button_new_with_label("List Clients");
  add_client   = gtk_button_new_with_label("Add Client");
  exit         = gtk_button_new_with_label("Exit");

  gtk_container_add(GTK_CONTAINER(button_bar), clear);
  gtk_container_add(GTK_CONTAINER(button_bar), parse);
  gtk_container_add(GTK_CONTAINER(button_bar), run_zmap);
  gtk_container_add(GTK_CONTAINER(button_bar), send_command);
  gtk_container_add(GTK_CONTAINER(button_bar), list_clients);
  gtk_container_add(GTK_CONTAINER(button_bar), add_client);
  gtk_container_add(GTK_CONTAINER(button_bar), exit);

  g_signal_connect(G_OBJECT(clear), "clicked",
                   G_CALLBACK(clearCB), suite);

  g_signal_connect(G_OBJECT(parse), "clicked",
                   G_CALLBACK(parseCB), suite);

  g_signal_connect(G_OBJECT(run_zmap), "clicked",
                   G_CALLBACK(runZMapCB), suite);

  g_signal_connect(G_OBJECT(send_command), "clicked",
                   G_CALLBACK(sendCommandCB), suite);

  g_signal_connect(G_OBJECT(list_clients), "clicked",
                   G_CALLBACK(listClientsCB), suite);

  g_signal_connect(G_OBJECT(add_client), "clicked",
                   G_CALLBACK(addClientCB), suite);

  g_signal_connect(G_OBJECT(exit), "clicked",
                   G_CALLBACK(quitCB), suite);

  return button_bar;
}

/* ---------------- */
/* Widget callbacks */
/* ---------------- */

/* in case you want to add a x window as a client */
static void addClientCB(GtkWidget *button, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  ZMapXRemoteObj client;
  char *client_text = NULL;
  Window wxid = 0;

  client_text = (char *)gtk_entry_get_text(GTK_ENTRY(suite->client_entry));
  wxid = (Window)(strtoul(client_text, (char **)NULL, 16));

  if ((client = zMapXRemoteNew(NULL)))
    {
      zMapXRemoteInitClient(client, wxid);
      addClientToHash(suite->xremote_clients, client, wxid, NULL, TRUE);

      if ((suite->cmd_line_args->timeout))
	zMapXRemoteSetTimeout(client, 0.0) ;
    }
  
  return ;
}

/* time to clean up */
static void quitCB(GtkWidget *unused_button, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;

  if(command_debug_G)
    printf("Free/Destroying suite data\n");

  if(suite->zmap_pid != 0)
    {
      kill(suite->zmap_pid, SIGKILL);
      suite->zmap_pid = 0;
    }

  if(suite->server_pid != 0)
    {
      kill(suite->server_pid, SIGKILL);
      suite->server_pid = 0;
    }

  g_hash_table_destroy(suite->xremote_clients);

  g_free(suite->window_id) ;

  g_free(suite);

  appExit(EXIT_SUCCESS);

  return ;
}


/* Menu commands internal to put xml events into the text buffer as text (xml) */
static int events_to_text_buffer(ZMapXMLWriter writer, char *xml, int len, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  GtkTextIter end; 
  GtkTextBuffer *buffer;

  buffer = suite->text_buffer;
  
  gtk_text_buffer_get_end_iter(buffer, &end);

  gtk_text_buffer_insert(buffer, &end, xml, len);

  return len;
}

/* The menu command to handle the commands menu items */
static void cmdCB( gpointer data, guint callback_action, GtkWidget *w )
{
  static ZMapXMLUtilsEventStackStruct
    start[] = {{ZMAPXML_START_ELEMENT_EVENT, "zmap",   ZMAPXML_EVENT_DATA_NONE,  {0}},
	       {0}},
    end[] = {{ZMAPXML_END_ELEMENT_EVENT,   "zmap",   ZMAPXML_EVENT_DATA_NONE,  {0}},
	     {0}},
    req_start[] = {{ZMAPXML_START_ELEMENT_EVENT, "request",   ZMAPXML_EVENT_DATA_NONE,  {0}},
	       {ZMAPXML_ATTRIBUTE_EVENT,     "action", ZMAPXML_EVENT_DATA_QUARK, {NULL}},
		   {0}},
    req_end[] = {{ZMAPXML_END_ELEMENT_EVENT,   "request",   ZMAPXML_EVENT_DATA_NONE,  {0}},
		 {0}},
      align_start[] = {{ZMAPXML_START_ELEMENT_EVENT, "align",   ZMAPXML_EVENT_DATA_NONE,  {0}},
		       {ZMAPXML_ATTRIBUTE_EVENT,     "name", ZMAPXML_EVENT_DATA_QUARK, {""}},
		       {0}},
	align_end[] = {{ZMAPXML_END_ELEMENT_EVENT,   "align",   ZMAPXML_EVENT_DATA_NONE,  {0}},
		       {0}},
      block_start[] = {{ZMAPXML_START_ELEMENT_EVENT, "block",   ZMAPXML_EVENT_DATA_NONE,  {0}},
		       {ZMAPXML_ATTRIBUTE_EVENT,     "name", ZMAPXML_EVENT_DATA_QUARK, {""}},
		       {0}},
	block_end[] = {{ZMAPXML_END_ELEMENT_EVENT,   "block",   ZMAPXML_EVENT_DATA_NONE,  {0}},
		       {0}},
    feature[] = {{ZMAPXML_START_ELEMENT_EVENT, "featureset", ZMAPXML_EVENT_DATA_NONE,    {0}},
		   {ZMAPXML_START_ELEMENT_EVENT, "feature",    ZMAPXML_EVENT_DATA_NONE,    {0}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "name",       ZMAPXML_EVENT_DATA_QUARK,   {""}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "start",      ZMAPXML_EVENT_DATA_INTEGER, {0}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "end",        ZMAPXML_EVENT_DATA_INTEGER, {0}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "strand",     ZMAPXML_EVENT_DATA_QUARK,   {"+|-"}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "style",      ZMAPXML_EVENT_DATA_QUARK,   {""}},
		   {ZMAPXML_START_ELEMENT_EVENT, "subfeature", ZMAPXML_EVENT_DATA_NONE,    {0}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "start",      ZMAPXML_EVENT_DATA_INTEGER, {0}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "end",        ZMAPXML_EVENT_DATA_INTEGER, {0}},
		   {ZMAPXML_ATTRIBUTE_EVENT,     "ontology",   ZMAPXML_EVENT_DATA_QUARK,   {""}},
		   {ZMAPXML_END_ELEMENT_EVENT,   "subfeature", ZMAPXML_EVENT_DATA_NONE,    {0}},
		   {ZMAPXML_END_ELEMENT_EVENT,   "feature",    ZMAPXML_EVENT_DATA_NONE,    {0}},
		   {ZMAPXML_END_ELEMENT_EVENT,   "featureset", ZMAPXML_EVENT_DATA_NONE,    {0}},
		   {0}},
    segment[] = {{ZMAPXML_START_ELEMENT_EVENT, "segment",  ZMAPXML_EVENT_DATA_NONE,    {0}},
		     {ZMAPXML_ATTRIBUTE_EVENT,     "sequence", ZMAPXML_EVENT_DATA_QUARK,   {NULL}},
		     {ZMAPXML_ATTRIBUTE_EVENT,     "start",    ZMAPXML_EVENT_DATA_INTEGER, {(char *)1}},
		     {ZMAPXML_ATTRIBUTE_EVENT,     "end",      ZMAPXML_EVENT_DATA_INTEGER, {0}},
		     {ZMAPXML_END_ELEMENT_EVENT,   "segment",  ZMAPXML_EVENT_DATA_NONE,    {0}},
		 {0}},
    client[] = {{ZMAPXML_START_ELEMENT_EVENT, "client",  ZMAPXML_EVENT_DATA_NONE,    {0}},
		{ZMAPXML_ATTRIBUTE_EVENT,     "xwid", ZMAPXML_EVENT_DATA_QUARK,   {NULL}},
		{ZMAPXML_ATTRIBUTE_EVENT,     "request_atom",    ZMAPXML_EVENT_DATA_QUARK, {NULL}},
		{ZMAPXML_ATTRIBUTE_EVENT,     "response_atom",      ZMAPXML_EVENT_DATA_QUARK, {NULL}},
		{ZMAPXML_END_ELEMENT_EVENT,   "client",  ZMAPXML_EVENT_DATA_NONE,    {0}},
		{0}};
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)data;
  ZMapXMLUtilsEventStack data_ptr = NULL ;
  ZMapXMLWriter writer;
  GArray *events;
  char **action;
  gboolean do_feature_xml = FALSE ;


  action = &(req_start[1].value.s);

  switch(callback_action)
    {
    case XREMOTE_NEW_ZMAP:
      *action  = "new_zmap";

      break;

    case XREMOTE_NEW_VIEW:
      *action  = "new_view";
      data_ptr = &segment[0];
      segment[1].value.s = (char *)gtk_entry_get_text(GTK_ENTRY(suite->sequence));

      break;

    case XREMOTE_REGISTER_CLIENT:
      {
	char *client_id ;

	*action  = "register_client" ;
	data_ptr = &client[0] ;

	client_id = suite->window_id ;
	client[1].value.s = client_id ;
	client[2].value.s = ZMAP_CLIENT_REQUEST_ATOM_NAME ;
	client[3].value.s = ZMAP_CLIENT_RESPONSE_ATOM_NAME ;

	break;
      }

    case XREMOTE_ZOOMTO:
      *action  = "zoom_to";
      data_ptr = &feature[0];

      do_feature_xml = TRUE ;
      break;
    case XREMOTE_DELETE:
      *action  = "delete_feature";
      data_ptr = &feature[0];

      do_feature_xml = TRUE ;
      break;
    case XREMOTE_CREATE:
      *action  = "create_feature";
      data_ptr = &feature[0];

      do_feature_xml = TRUE ;
      break;
    case XREMOTE_ZOOMIN:
      *action  = "zoom_in";
      data_ptr = NULL;

      do_feature_xml = TRUE ;
      break;
    case XREMOTE_ZOOMOUT:
      *action  = "zoom_out";
      data_ptr = NULL;

      do_feature_xml = TRUE ;
      break;

    case XREMOTE_SHUTDOWN:
      *action  = "shutdown";
      data_ptr = NULL;
      break;
    default:
      zMapAssertNotReached();
      break;
    }

  /* Create the xml for the request. */
  events = zMapXMLUtilsStackToEventsArray(&start[0]);

  events = zMapXMLUtilsAddStackToEventsArray(&req_start[0], events);

  if (do_feature_xml)
    {
      events = zMapXMLUtilsAddStackToEventsArray(&align_start[0], events);

      events = zMapXMLUtilsAddStackToEventsArray(&block_start[0], events);
    }

  if (data_ptr)
    events = zMapXMLUtilsAddStackToEventsArray(data_ptr, events);

  if (do_feature_xml)
    {
      events = zMapXMLUtilsAddStackToEventsArray(&block_end[0], events);

      events = zMapXMLUtilsAddStackToEventsArray(&align_end[0], events);
    }

  events = zMapXMLUtilsAddStackToEventsArray(&req_end[0], events);

  events = zMapXMLUtilsAddStackToEventsArray(&end[0], events);

  if ((writer = zMapXMLWriterCreate(events_to_text_buffer, suite)))
    {
      ZMapXMLWriterErrorCode xml_status ;

      gtk_text_buffer_set_text(suite->text_buffer, "", -1) ;
      gtk_text_buffer_set_text(suite->response_text_buffer, "", -1) ;

      if ((xml_status = zMapXMLWriterProcessEvents(writer, events)) != ZMAPXMLWRITER_OK)
        zMapGUIShowMsg(ZMAP_MSG_WARNING, zMapXMLWriterErrorMsg(writer));
    }

  return ;
}

/* quit from the menu */
static void menuQuitCB(gpointer data, guint callback_action, GtkWidget *w)
{
  quitCB(w, data);

  return ;
}

/* clear the xml messages from all the buffers */
static void clearCB(GtkWidget *button, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;

  gtk_text_buffer_set_text(suite->text_buffer, "", -1);
  gtk_text_buffer_set_text(suite->response_text_buffer, "", -1);

  return ;
}

/* check the xml in the message input buffer parses as valid xml */
static void parseCB(GtkWidget *button, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  ZMapXMLParser parser;
  gboolean parse_ok;
  int xml_length = 0;
  char *xml;

  parser = zMapXMLParserCreate(user_data, FALSE, FALSE);
  
  xml = getXML(suite, &xml_length);

  if((parse_ok = zMapXMLParserParseBuffer(parser, xml, xml_length)) == TRUE)
    {
      /* message dialog, all ok */
      zMapGUIShowMsg(ZMAP_MSG_INFORMATION, "Info: XML ok");
    }
  else
    {
      char *error;
      /* message dialog, something wrong. position cursor? */
      error = zMapXMLParserLastErrorMsg(parser);
      zMapGUIShowMsg(ZMAP_MSG_WARNING, error);
    }

  zMapXMLParserDestroy(parser);

  return ;
}

/* button -> send command */
static void sendCommandCB(GtkWidget *button, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  char *xml;
  int xml_length;
  SendCommandDataStruct send_data = {NULL};
 
  if((xml = getXML(suite, &xml_length)) && xml_length > 0)
    {
      send_data.suite = suite;
      send_data.xml   = xml;

      zMapXRemoteAPIMessageProcess(xml, FALSE, &(send_data.action), NULL);

      internal_send_command(&send_data);
    }

  return ;
}

/* button -> run a zmap */
static void runZMapCB(GtkWidget *button, gpointer user_data)
{

  start_zmap_cb(user_data);

  return ;
}

static void list_clients_cb(gpointer key, gpointer hash_data, gpointer user_data)
{
  GString *string = (GString *)user_data;

  g_string_append_printf(string, "Window id = 0x%x\n", GPOINTER_TO_INT(key));

  return ;
}

static void listClientsCB(GtkWidget *button, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  GString *client_list;
  client_list = g_string_sized_new(512);
  g_string_append_printf(client_list, "%s", "Clients:\n");

  g_hash_table_foreach(suite->xremote_clients, list_clients_cb, client_list);

  /* This is a bit simple at the moment, we should really have a list
   * store which does a similar thing to the manager bit of zmap. windows
   * could then be raised on selection and a little more info displayed.
   * To do this XQueryTree, gdk_window_foreign_new_for_display, 
   * gdk_window_raise or similar should be used.  The id is not neccessarily
   * the toplevel I guess.
   */

  zMapGUIShowMsg(ZMAP_MSG_INFORMATION, client_list->str);

  g_string_free(client_list, TRUE);

  return ;
}


/* Because you can't iterate over a hash and alter it we need to have
 * a hash we alter while iterating over the main one, which then
 * alters the main one at the end */

/* Just results in the entry being removed from the hash table, the destroy func
 * does the real work. */
static gboolean remove_cb(gpointer key, gpointer hash_data, gpointer user_data)
{
  return TRUE ;
}

static gint action_lookup_cb(gconstpointer list_data, gconstpointer user_data)
{
  char *action_to_find = (char *)user_data;
  char *action_current = (char *)list_data;
  gint match = -1;

  if(strcmp(action_to_find, action_current) == 0)
    match = 0;

  return match;
}

static void action_elements_to_list(gpointer list_data, gpointer user_data)
{
  ZMapXMLElement element = (ZMapXMLElement)list_data;
  GList **list_ptr = (GList **)user_data;
  
  if(list_ptr)
    {
      char *action = NULL;
      if((element->name == g_quark_from_string("action")))
	action = zMapXMLElementStealContent(element);

      if(action != NULL)
	*list_ptr = g_list_append(*list_ptr, action);
    }

  return ;
}

/* ------------------ */
/* XML event handlers */
/* ------------------ */

static gboolean xml_client_end_cb(gpointer user_data, ZMapXMLElement client_element,
				  ZMapXMLParser parser)
{
  SendCommandData send_data  = (SendCommandData)user_data;
  XRemoteTestSuiteData suite = send_data->suite;
  ZMapXRemoteObj new_client;
  ZMapXMLAttribute attr;
  Window wxid = 0;
  char *xid = NULL, *req = NULL, *resp = NULL;

  if((attr = zMapXMLElementGetAttributeByName(client_element, "xwid")) != NULL)
    {
      xid  = (char *)g_quark_to_string(zMapXMLAttributeGetValue(attr));
      wxid = (Window)(strtoul(xid, (char **)NULL, 16));
    }
  else
    zMapXMLParserRaiseParsingError(parser, "id is a required attribute for client.");
  
  if((attr  = zMapXMLElementGetAttributeByName(client_element, "request_atom")) != NULL)
    req = (char *)g_quark_to_string(zMapXMLAttributeGetValue(attr));
  if((attr  = zMapXMLElementGetAttributeByName(client_element, "response_atom")) != NULL)
    resp = (char *)g_quark_to_string(zMapXMLAttributeGetValue(attr));
  
  if(wxid && req && resp && (new_client = zMapXRemoteNew(NULL)))
    {
      GList *actions = NULL;
      zMapXRemoteInitClient(new_client, wxid);
      zMapXRemoteSetRequestAtomName(new_client, req);
      zMapXRemoteSetResponseAtomName(new_client, resp);

      if ((suite->cmd_line_args->timeout))
	zMapXRemoteSetTimeout(new_client, 0.0) ;

      /* make actions list */
      g_list_foreach(client_element->children, action_elements_to_list, &actions);

      addClientToHash(suite->xremote_clients, new_client, wxid, actions, TRUE);
    }

  return TRUE;
}


static gboolean xml_zmap_start_cb(gpointer user_data, ZMapXMLElement zmap_element,
                                  ZMapXMLParser parser)
{
  SendCommandData send_data  = (SendCommandData)user_data;
  XRemoteTestSuiteData suite = send_data->suite;
  ZMapXMLAttribute attr = NULL;

  if((attr = zMapXMLElementGetAttributeByName(zmap_element, "action")) != NULL)
    {
      GQuark action = zMapXMLAttributeGetValue(attr);

      if (action == g_quark_from_string("register_client"))
        suite->is_register_client = TRUE;
      else
        suite->is_register_client = FALSE;

      /* When we get this it means the zmap is exitting so clean up the client
       * connection. */
      if (action == g_quark_from_string("finalised"))
	{
	  g_hash_table_foreach_remove(suite->xremote_clients, remove_cb, NULL) ;
	}
    }

  return FALSE;
}

static gboolean xml_client_start_cb(gpointer user_data, ZMapXMLElement client_element,
                                    ZMapXMLParser parser)
{
  SendCommandData send_data  = (SendCommandData)user_data;
  XRemoteTestSuiteData suite = send_data->suite;
  ZMapXRemoteObj new_client;
  ZMapXMLAttribute attr;
  Window wxid = 0;
  char *xid = NULL, *req = NULL, *resp = NULL;

  if (suite->is_register_client)
    {
      if((attr = zMapXMLElementGetAttributeByName(client_element, "xwid")) != NULL)
        {
          xid  = (char *)g_quark_to_string(zMapXMLAttributeGetValue(attr));
          wxid = (Window)(strtoul(xid, (char **)NULL, 16));
        }
      else
        zMapXMLParserRaiseParsingError(parser, "id is a required attribute for client.");
      
      if((attr  = zMapXMLElementGetAttributeByName(client_element, "request_atom")) != NULL)
        req = (char *)g_quark_to_string(zMapXMLAttributeGetValue(attr));

      if((attr  = zMapXMLElementGetAttributeByName(client_element, "response_atom")) != NULL)
        resp = (char *)g_quark_to_string(zMapXMLAttributeGetValue(attr));
      
      if (wxid && req && resp && (new_client = zMapXRemoteNew(NULL)))
        {
	  GList *actions = NULL;

          zMapXRemoteInitClient(new_client, wxid);
          zMapXRemoteSetRequestAtomName(new_client, req);
          zMapXRemoteSetResponseAtomName(new_client, resp);

	  if ((suite->cmd_line_args->timeout))
	    zMapXRemoteSetTimeout(new_client, 0.0) ;

	  actions = g_list_append(actions, g_strdup("new_zmap"));
	  actions = g_list_append(actions, g_strdup("shutdown"));

          addClientToHash(suite->xremote_clients, new_client, wxid, actions, TRUE);
        }
    }

  return FALSE;
}

static gboolean xml_zmap_end_cb(gpointer user_data, ZMapXMLElement element, 
                                ZMapXMLParser parser)
{
  return TRUE;
}

static gboolean xml_error_end_cb(gpointer user_data, ZMapXMLElement element, 
                                 ZMapXMLParser parser)
{
  return TRUE;
}

static gboolean xml_response_start_cb(gpointer user_data, ZMapXMLElement element, 
                                      ZMapXMLParser parser)
{



  return FALSE;
}


/* really send an xremote command */
static gboolean send_command_cb(gpointer key, gpointer hash_data, gpointer user_data)
{
  HashEntry entry = (HashEntry)hash_data;
  ZMapXMLParser parser;
  ZMapXRemoteObj client;
  SendCommandData send_data = (SendCommandData)user_data;
  char *full_response, *xml;
  int result, code;
  gboolean parse_ok = FALSE, dead_client = FALSE;
  ZMapXMLObjTagFunctionsStruct starts[] = {
    {"zmap",     xml_zmap_start_cb     },
    {"response", xml_response_start_cb },
    { NULL, NULL}
  };
  ZMapXMLObjTagFunctionsStruct ends[] = {
    {"zmap",     xml_zmap_end_cb  },
    {"client",   xml_client_end_cb },
    {"error",    xml_error_end_cb },
    { NULL, NULL}
  };

  client = entry->client;

  /* Check the client will understand the command... */
  if(send_data->action && g_list_find_custom(entry->actions, send_data->action, action_lookup_cb))				/* find the action in the hash entry list */
    {
      /* send it to the client that understands */
      if((result = zMapXRemoteSendRemoteCommand(client, send_data->xml, &full_response)) == ZMAPXREMOTE_SENDCOMMAND_SUCCEED)
	{
	  send_data->sent = TRUE;

	  if (!zMapXRemoteResponseIsError(client, full_response))
	    {
	      gtk_text_buffer_set_text(send_data->suite->response_text_buffer, full_response, -1) ;

	      parser = zMapXMLParserCreate(send_data, FALSE, FALSE);
	      zMapXMLParserSetMarkupObjectTagHandlers(parser, &starts[0], &ends[0]);
	      
	      zMapXRemoteResponseSplit(client, full_response, &code, &xml);
	      
	      if((parse_ok = zMapXMLParserParseBuffer(parser, xml, strlen(xml))))
		{
		  
		}
	      else
		zMapGUIShowMsg(ZMAP_MSG_WARNING, zMapXMLParserLastErrorMsg(parser));
	    }
	  else
	    {
	      zMapGUIShowMsg(ZMAP_MSG_INFORMATION, full_response);
	    }

	}
      else
	{
	  char *message;
	  message = g_strdup_printf("send command failed, deleting client (0x%x)...", GPOINTER_TO_INT(key));
	  zMapGUIShowMsg(ZMAP_MSG_WARNING, message);
	  g_free(message);
	  message = zMapXRemoteGetResponse(NULL);
	  zMapGUIShowMsg(ZMAP_MSG_WARNING, message);
	  dead_client = TRUE;
	}
    }

  return dead_client;
}

/* handle the hashes so we can correctly iterate and remove/append to the hash of clients... */
static void internal_send_command(SendCommandData send_data)
{
  if(send_data->suite && send_data->xml && !send_data->sent)
    {
      send_data->new_clients = g_hash_table_new_full(NULL, NULL, NULL, destroyHashEntry);
  
      send_data->hash_size   = g_hash_table_size(send_data->suite->xremote_clients);
  
      g_hash_table_foreach_remove(send_data->suite->xremote_clients, 
				  send_command_cb, send_data);
  
      g_hash_table_foreach_steal(send_data->new_clients, 
				 copy_hash_to_hash, 
				 send_data->suite->xremote_clients);
  
      g_hash_table_destroy(send_data->new_clients);
    }

  return ;
}

/* Hash related functions */

static HashEntry clientToHashEntry(ZMapXRemoteObj client, gboolean is_main)
{
  HashEntry entry;
  if((entry = g_new0(HashEntryStruct, 1)))
    {
      entry->client = client;
      entry->is_main_window = is_main;
    }

  return entry;
}

static gboolean addClientToHash(GHashTable *table, ZMapXRemoteObj client, Window id, GList *actions, gboolean is_main)
{
  HashEntry new_entry;
  gboolean inserted = FALSE;

  if(!(g_hash_table_lookup(table, GINT_TO_POINTER(id))))
    {
      new_entry = clientToHashEntry(client, is_main);
      new_entry->actions = actions;
      g_hash_table_insert(table, GINT_TO_POINTER(id), new_entry);

      inserted = TRUE;
    }

  return inserted;
}

static gboolean copy_hash_to_hash(gpointer key, gpointer value, gpointer user_data)
{
  HashEntry hash_entry = (HashEntry)value;
  gboolean   copied_ok = FALSE;
  GHashTable *to = (GHashTable *)user_data;
  Window      id = GPOINTER_TO_INT(key);

  copied_ok = addClientToHash(to, hash_entry->client, 
                              id, hash_entry->actions, 
			      hash_entry->is_main_window);

  return copied_ok;
}

static void destroyHashEntry(gpointer entry_data)
{
  HashEntry entry = (HashEntry)entry_data;

  zMapXRemoteDestroy(entry->client);
  entry->client = NULL;
  g_free(entry);

  return ;
}


/* simple function to run a command and return its pid. */
static gboolean run_command(char **command, int *pid)
{
  int child_pid, *pid_ptr;
  char **ptr;
  char *cwd = NULL, **envp = NULL; /* inherit from parent */
  GSpawnFlags flags = G_SPAWN_SEARCH_PATH;
  GSpawnChildSetupFunc pre_exec = NULL;
  gpointer user_data = NULL;
  GError *error = NULL;
  gboolean success = FALSE;
  
  if(command_debug_G)
    {
      printf("Running command: ");
      ptr = &command[0];
      while(ptr && *ptr)
	{
	  printf("%s ", *ptr);
	  ptr++;
	}
      printf("\n");
    }

  if(pid)
    pid_ptr = pid;
  else
    pid_ptr = &child_pid;

  if(!(success = g_spawn_async(cwd, command, envp, 
			       flags, pre_exec, user_data, 
			       pid_ptr, &error)))
    {
      printf("Errror %s\n", error->message);
    }

  return success;
}

/* get the xml and its length from the text buffer */
static char *getXML(XRemoteTestSuiteData suite, int *length_out)
{
  GtkTextBuffer *buffer;
  GtkTextIter start, end;
  char *xml;
  
  buffer = suite->text_buffer;

  gtk_text_buffer_get_start_iter(buffer, &start);
  gtk_text_buffer_get_end_iter(buffer, &end);
  
  xml = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);

  if(length_out)
    *length_out = gtk_text_iter_get_offset(&end);

  return xml;
}

/* an app exit function to return a return code of meaning */
static void appExit(int exit_rc)
{
  int true_rc;

  if(exit_rc)
    true_rc = EXIT_FAILURE;
  else
    true_rc = EXIT_SUCCESS;

  gtk_exit(true_rc);

  return ;
}


/* command line bits */
static GOptionEntry *get_main_entries(XRemoteCmdLineArgs arg_context)
{
  static GOptionEntry entries[] = {
    /* long_name, short_name, flags, arg, arg_data, description, arg_description */
    { XREMOTEARG_VERSION, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, 
      NULL, XREMOTEARG_VERSION_DESC, XREMOTEARG_NO_ARG },
    { XREMOTEARG_CONFIG, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_STRING, 
      NULL, XREMOTEARG_CONFIG_DESC, XREMOTEARG_FILE_ARG },
    { XREMOTEARG_COMMAND, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_STRING, 
      NULL, XREMOTEARG_COMMAND_DESC, XREMOTEARG_FILE_ARG },
    { XREMOTEARG_DEBUGGER, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, NULL,
      XREMOTEARG_DEBUGGER_DESC, XREMOTEARG_NO_ARG },
    { XREMOTEARG_XREMOTE_DEBUG, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, NULL,
      XREMOTEARG_XREMOTE_DEBUG_DESC, XREMOTEARG_NO_ARG },
    { XREMOTEARG_CMD_DEBUG, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, NULL,
      XREMOTEARG_CMD_DEBUG_DESC, XREMOTEARG_NO_ARG },
    { XREMOTEARG_SEQUENCE, 0, 0, G_OPTION_ARG_STRING, NULL,
      XREMOTEARG_COMMAND_DESC, "sequence" },
    { XREMOTEARG_NO_TIMEOUT, 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_NONE, NULL,
      XREMOTEARG_NO_TIMEOUT_DESC, XREMOTEARG_NO_ARG },
    { NULL }
  };

  if (entries[0].arg_data == NULL)
    {
      entries[0].arg_data = &(arg_context->version);
      entries[1].arg_data = &(arg_context->config_file);
      entries[2].arg_data = &(arg_context->command_file);
      entries[3].arg_data = &(arg_context->debugger) ;
      entries[4].arg_data = &(arg_context->xremote_debug) ;
      entries[5].arg_data = &(arg_context->cmd_debug) ;
      entries[6].arg_data = &(arg_context->sequence) ;
      entries[7].arg_data = &(arg_context->timeout) ;
    }
  
  return entries;
}

static gboolean makeOptionContext(XRemoteCmdLineArgs arg_context)
{
  GOptionEntry *main_entries;
  gboolean success = FALSE;

  arg_context->opt_context = g_option_context_new(NULL) ;

  main_entries = get_main_entries(arg_context);

  g_option_context_add_main_entries(arg_context->opt_context, main_entries, NULL);

  if (g_option_context_parse (arg_context->opt_context, 
			      &arg_context->argc, 
			      &arg_context->argv, 
			      &arg_context->error))
    {
      success = TRUE;
    }
  else
    {
      g_print ("option parsing failed: %s\n", arg_context->error->message);
      success = FALSE;
    }

  return success;
}

static XRemoteCmdLineArgs process_command_line_args(int argc, char *argv[])
{
  XRemoteCmdLineArgs arg_context = NULL;
  gboolean failure = FALSE;

  if ((arg_context = g_new0(XRemoteCmdLineArgsStruct, 1)))
    {
      arg_context->argc = argc;
      arg_context->argv = argv;

      arg_context->sequence = XREMOTEARG_DEFAULT_SEQUENCE ;

      if(!makeOptionContext(arg_context))
	failure = TRUE;
    }
  else
    failure = TRUE;

  /* If there was a failure  */
  if(failure)
    appExit(TRUE);

  return arg_context;
}

static char **build_command(char *params_as_string)
{
  char **command_out = NULL;
  char **ptr, **split = NULL;
  char *copy, *strip ;
  int i, c = 0;

  copy = g_strdup(params_as_string) ;
  strip = g_strstrip(copy) ;

  split = ptr = g_strsplit(strip, " ", 0);
  
  while(ptr && *ptr != '\0')
    {
      c++;
      ptr++;
    }

  command_out = g_new0(char *, c + 2);

  for (i = 0; i < c; i++)
    {
      if (split[i] && *(split[i]))
	command_out[i] = split[i]; 
    }

  g_free(copy) ;

  return command_out ;
}

static GList *read_command_file(XRemoteTestSuiteData suite, char *file_name)
{
  GIOChannel *io_channel;
  GError *open_error = NULL;
  GList *commands = NULL;
  /* do we 
   * a) parse the file using expat.
   * b) parse line by line expecting empty lines to separate commands.
   * c) something else.
   */

  /* lets try (b) */
  if((io_channel = g_io_channel_new_file(file_name, "r", &open_error)))
    {
      GIOStatus status;
      GString *string, *current;
      GError  *io_error = NULL;
      gsize term = 0;
      
      string  = g_string_sized_new(2000);
      current = g_string_sized_new(2000);

      while((status = g_io_channel_read_line_string(io_channel, string, 
						    &term, &io_error)) == G_IO_STATUS_NORMAL)
	{
	  /* separator = __EOC__ (End of Command)*/

	  if(g_ascii_strcasecmp(string->str, "__eoc__\n") == 0)			/* line is a separator */
	    {
	      /* append to list */
	      commands = g_list_append(commands, current);

	      /* clear up current string */
	      current = g_string_sized_new(2000);
	      g_string_truncate(string, 0);
	    }
	  else
	    {
	      /* append to current string */
	      g_string_append(current, string->str);
	      g_string_truncate(string, 0);
	    }
	}

      g_string_free(string, TRUE);

      g_io_channel_shutdown(io_channel, TRUE, &open_error);
    }


  return commands;
}

static void command_file_free_strings(gpointer list_data, gpointer unused_data)
{
  GString *command = (GString *)list_data;
  g_string_free(command, FALSE);
  return ;
}


static gboolean queue_send_command_cb(gpointer user_data)
{
  SendCommandData send_data = (SendCommandData)user_data;
  gboolean sent = TRUE;

  internal_send_command(send_data);

  return sent;
}

static void queue_destroy_send_data_cb(gpointer destroy_data)
{
  SendCommandData send_data = (SendCommandData)destroy_data;

  if(send_data->xml)
    g_free(send_data->xml);

  g_free(send_data);

  return ;
}

static gboolean queue_sent_command_cb(gpointer user_data)
{
  SendCommandData send_data = (SendCommandData)user_data;
  gboolean remove_when_true = FALSE;

  remove_when_true = send_data->sent;

  return remove_when_true;
}

static void command_file_run_command_cb(gpointer list_data, gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  GString *command = (GString *)list_data;

  if((command->str))
    {
      SendCommandData send_data = NULL;
      DependRunner depend_data = NULL;

      send_data        = g_new0(SendCommandDataStruct, 1);
      send_data->suite = suite;
      send_data->xml   = command->str;

      zMapXRemoteAPIMessageProcess(send_data->xml, FALSE, &(send_data->action), NULL);

      depend_data = g_new0(DependRunnerStruct, 1);
      depend_data->main_runner = queue_send_command_cb;
      depend_data->destroy     = queue_destroy_send_data_cb;
      depend_data->runner_data = send_data;
      depend_data->runnable    = TRUE;
      depend_data->runner_done = queue_sent_command_cb;

      g_queue_push_tail(suite->queue, depend_data);
      
    }


  return;
}

static gboolean command_source_cb(gpointer user_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)user_data;
  gboolean remove_when_false = TRUE;

  if(!g_queue_is_empty(suite->queue))
    {
      DependRunner depend_data = g_queue_peek_head(suite->queue);
      gboolean finished;

      if(!(finished = (depend_data->runner_done)(depend_data->runner_data)))
	{
	  if(depend_data->main_runner && depend_data->runnable)
	    depend_data->runnable = (depend_data->main_runner)(depend_data->runner_data);
	  else
	    finished = TRUE;
	}

      if(finished)
	{
	  depend_data = g_queue_pop_head(suite->queue);
	  if(depend_data->destroy)
	    (depend_data->destroy)(depend_data->runner_data);
	  g_free(depend_data);
	}
    }
  else
    remove_when_false = FALSE;

  return remove_when_false;
}

static gboolean start_server_cb(gpointer suite_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)suite_data;
  char *tmp_string = NULL;

  /* run sgifaceserver */
  if(zMapConfigIniContextGetString(suite->config_context, XREMOTE_PROG_CONFIG, XREMOTE_PROG_CONFIG,
				   XREMOTE_PROG_SERVER, &tmp_string))
    {
      char *server_path = tmp_string;
      char **command = NULL;
      tmp_string = NULL;
      char *cmd_string ;

      zMapConfigIniContextGetString(suite->config_context, XREMOTE_PROG_CONFIG, XREMOTE_PROG_CONFIG,
				    XREMOTE_PROG_SERVER_OPTS, &tmp_string);

      cmd_string = g_strdup_printf("%s %s", server_path, tmp_string) ;
      command = build_command(cmd_string) ;

      run_command(command, &(suite->server_pid)) ;

      g_free(command);
    }

  /* Make sure we only run once */
  return FALSE;
}

static gboolean server_started_cb(gpointer suite_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)suite_data;
  gboolean remove_when_true = FALSE;

  if(suite->server_pid)
    remove_when_true = TRUE;

  return remove_when_true;
}

static gboolean start_zmap_cb(gpointer suite_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)suite_data;
  char *zmap_path  = NULL;
  char *tmp_string = NULL;
  gboolean debugger = suite->cmd_line_args->debugger ;


  /* run zmap */
  if ((suite->config_context)
      && (zMapConfigIniContextGetString(suite->config_context,
					XREMOTE_PROG_CONFIG, XREMOTE_PROG_CONFIG, XREMOTE_PROG_ZMAP,
					&tmp_string)))
    {
      zmap_path = tmp_string ;
    }
  else if ((zmap_path = (char *)gtk_entry_get_text(GTK_ENTRY(suite->zmap_path))) == NULL || (*zmap_path == '\0'))
    {
      zmap_path = "./zmap";
    }

  if (zmap_path != NULL)
    {
      char **command = NULL;
      tmp_string = NULL;

      if ((suite->config_context == NULL) ||
	  !zMapConfigIniContextGetString(suite->config_context, XREMOTE_PROG_CONFIG, XREMOTE_PROG_CONFIG,
					 XREMOTE_PROG_ZMAP_OPTS, &tmp_string))
	tmp_string = "";

      tmp_string = g_strdup_printf("%s %s %s --win_id 0x%lx %s",
				   (debugger ? "totalview" : ""),
				   (debugger ? "-a" : ""),
				   zmap_path,
				   GDK_DRAWABLE_XID(suite->app_toplevel->window),
				   tmp_string) ;
      command = build_command(tmp_string) ;

      run_command(command, &(suite->zmap_pid)) ;

      g_free(command) ;

      g_free(tmp_string) ;
    }

  return FALSE;
}

static gboolean zmap_started_cb(gpointer suite_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)suite_data;
  gboolean remove_when_true = FALSE;

  if(suite->zmap_pid)
    remove_when_true = TRUE;

  return remove_when_true;
}

static gboolean quit_fin_cb(gpointer suite_data)
{
  return FALSE;			/* Keep on trying to quit! */
}

static gboolean invoke_quit_cb(gpointer suite_data)
{
  XRemoteTestSuiteData suite = (XRemoteTestSuiteData)suite_data;

  quitCB(NULL, suite);

  return FALSE;
}

static void process_command_file(XRemoteTestSuiteData suite, char *command_file)
{
  DependRunner depend_data;
  GList *command_list = NULL;

  suite->queue = g_queue_new();

  /* Start the server. */
  depend_data = g_new0(DependRunnerStruct, 1);
  depend_data->main_runner = start_server_cb;
  depend_data->runner_data = suite;
  depend_data->runner_done = server_started_cb;
  depend_data->runnable    = TRUE;

  g_queue_push_tail(suite->queue, depend_data);
  
  /* Start zmap */
  depend_data = g_new0(DependRunnerStruct, 1);
  depend_data->main_runner = start_zmap_cb;
  depend_data->runner_data = suite;
  depend_data->runner_done = zmap_started_cb;
  depend_data->runnable    = TRUE;

  g_queue_push_tail(suite->queue, depend_data);

  /* get individual commands (GList *) */
  command_list = read_command_file(suite, command_file);
  /* run individual commands, waiting 30 seconds between each command */
  g_list_foreach(command_list, command_file_run_command_cb, suite);
  g_list_foreach(command_list, command_file_free_strings, NULL);
  g_list_free(command_list);

  depend_data = g_new0(DependRunnerStruct, 1);
  depend_data->main_runner = invoke_quit_cb;
  depend_data->runner_data = suite;
  depend_data->runner_done = quit_fin_cb;
  depend_data->runnable    = TRUE;

  g_queue_push_tail(suite->queue, depend_data);

  g_timeout_add(5000, command_source_cb, suite);

  return ;
}

static ZMapConfigIniContextKeyEntry get_programs_group_data(char **stanza_name, char **stanza_type)
{
  static ZMapConfigIniContextKeyEntryStruct stanza_keys[] = {
    { XREMOTE_PROG_ZMAP,        G_TYPE_STRING,  NULL, FALSE },
    { XREMOTE_PROG_ZMAP_OPTS,   G_TYPE_STRING,  NULL, FALSE },
    { XREMOTE_PROG_SERVER,      G_TYPE_STRING,  NULL, FALSE },
    { XREMOTE_PROG_SERVER_OPTS, G_TYPE_STRING,  NULL, FALSE },
    { NULL }
  };
  static char *name = XREMOTE_PROG_CONFIG;
  static char *type = XREMOTE_PROG_CONFIG;

  if(stanza_name)
    *stanza_name = name;
  if(stanza_type)
    *stanza_type = type;

  return stanza_keys;
}

static ZMapConfigIniContext get_configuration(XRemoteTestSuiteData suite)
{
  ZMapConfigIniContext context = NULL;
  
  if(suite->cmd_line_args && suite->cmd_line_args->config_file)
    {
      char *dir  = NULL;
      char *base = NULL;

      dir  = g_path_get_dirname(suite->cmd_line_args->config_file);
      base = g_path_get_basename(suite->cmd_line_args->config_file);

      zMapConfigDirCreate(dir, base); 

      if((context = zMapConfigIniContextCreate()))
	{
	  ZMapConfigIniContextKeyEntry stanza_group = NULL;
	  char *stanza_name, *stanza_type;
	  
	  if((stanza_group = get_programs_group_data(&stanza_name, &stanza_type)))
	    zMapConfigIniContextAddGroup(context, stanza_name, 
					 stanza_type, stanza_group);
	}
      if(dir)
	g_free(dir);
      if(base)
	g_free(base);
    }

  return context;
}




/* THIS STUFF SEEMS NOT TO BE USED...NOT SURE WHAT THE INTENTION WAS.... */


static gboolean api_zmap_start_cb(gpointer user_data, ZMapXMLElement zmap_element,
                                  ZMapXMLParser parser)
{
#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
  APIProcessing process_data = (APIProcessing)user_data;

  ZMapXMLAttribute attr;
  if((attr = zMapXMLElementGetAttributeByName(zmap_element, "action")) != NULL)
    {
      GQuark action = zMapXMLAttributeGetValue(attr);
      process_data->message_out->action = (char *)g_quark_to_string(action);
    }

  if(!process_data->full_processing)
    zMapXMLParserPauseParsing(parser);
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */

  /* I DON'T UNDERSTAND WHY WE RETURN FALSE HERE..... */
  return FALSE;
}


static gboolean api_zmap_end_cb(gpointer user_data, ZMapXMLElement zmap_element, ZMapXMLParser parser)
{
  return TRUE;
}

static gboolean api_request_start_cb(gpointer user_data, ZMapXMLElement zmap_element,
				     ZMapXMLParser parser)
{
  APIProcessing process_data = (APIProcessing)user_data;
  ZMapXMLAttribute attr;

  if((attr = zMapXMLElementGetAttributeByName(zmap_element, "action")) != NULL)
    {
      GQuark action = zMapXMLAttributeGetValue(attr);
      process_data->message_out->action = (char *)g_quark_to_string(action);
    }

  if(!process_data->full_processing)
    zMapXMLParserPauseParsing(parser);

  return FALSE;
}


static gboolean api_request_end_cb(gpointer user_data, ZMapXMLElement zmap_element, ZMapXMLParser parser)
{
  return TRUE;
}