[Gnucash-changes] r12059 - gnucash/branches/cashutil/cashutil - cashutil: standardise qof-main.c|h to update centrally

Neil Williams codehelp at cvs.gnucash.org
Mon Nov 28 10:45:35 EST 2005


Author: codehelp
Date: 2005-11-28 10:45:33 -0500 (Mon, 28 Nov 2005)
New Revision: 12059
Trac: http://svn.gnucash.org/trac/changeset/12059

Added:
   gnucash/branches/cashutil/cashutil/src/qof-shell.c
   gnucash/branches/cashutil/cashutil/src/qof-shell.h
Modified:
   gnucash/branches/cashutil/cashutil/ChangeLog
   gnucash/branches/cashutil/cashutil/src/Makefile.am
   gnucash/branches/cashutil/cashutil/src/cashutil.c
   gnucash/branches/cashutil/cashutil/src/qof-main.c
   gnucash/branches/cashutil/cashutil/src/qof-main.h
Log:
cashutil: standardise qof-main.c|h to update centrally

Modified: gnucash/branches/cashutil/cashutil/ChangeLog
===================================================================
--- gnucash/branches/cashutil/cashutil/ChangeLog	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/ChangeLog	2005-11-28 15:45:33 UTC (rev 12059)
@@ -1,3 +1,16 @@
+2005-11-28  Neil Williams <linux at codehelp.co.uk>
+
+	Standardise qof-main.c|h so that it can
+	be updated centrally for pilot-qof, 
+	cashutil, gpe-expenses and gpe-cash.
+	* src/cashutil.c : 
+	* src/Makefile.am : Add new files. 
+	* src/qof-main.c : 
+	* src/qof-main.h : 
+	* src/qof-shell.c : New file for QOF shell.
+	* src/qof-shell.h : New file.
+
+
 2005-11-05  Neil Williams <linux at codehelp.co.uk>
 
 	* cashutil/src/qof-main.c : Skip objects not registered

Modified: gnucash/branches/cashutil/cashutil/src/Makefile.am
===================================================================
--- gnucash/branches/cashutil/cashutil/src/Makefile.am	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/src/Makefile.am	2005-11-28 15:45:33 UTC (rev 12059)
@@ -14,11 +14,13 @@
 
 cashutil_SOURCES = \
   qof-main.c \
+  qof-shell.c \
   qofundo.c \
   cashutil.c 
 
 EXTRA_DIST = \
   qof-main.h \
+  qof-shell.h \
   qofundo.h
 
 noinst_HEADERS = qofundo-p.h

Modified: gnucash/branches/cashutil/cashutil/src/cashutil.c
===================================================================
--- gnucash/branches/cashutil/cashutil/src/cashutil.c	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/src/cashutil.c	2005-11-28 15:45:33 UTC (rev 12059)
@@ -18,7 +18,7 @@
  *
  *  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.
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
 
 /** @addtogroup QOFCLI
@@ -88,15 +88,101 @@
 #include <sys/stat.h>
 #include "config.h"
 #include "qof-main.h"
+#include "qof-shell.h"
 #include "cashutil.h"
 #include "cashobjects.h"
 #include "gncla-dir.h"
 #include "qofundo-p.h"
 #include "backend-bus.h"
 
-static gchar* log_module = CU_MOD_CLI;
+static QofLogModule log_module = CU_MOD_CLI;
 static gboolean debug_on = FALSE;
+static GHashTable *backend_extensions;
+#define GNC_LIB_NAME "libgnc-backend-file.la"
+#define GNC_LIB_INIT "gnc_provider_init"
 
+/** \brief command line command options.*/
+typedef enum {
+	qof_op_noop = 0,
+	qof_op_input,
+	qof_op_offline,
+	qof_op_list,
+	qof_op_shell,
+	qof_op_vers,
+	qof_op_database,
+	qof_op_timespec,
+	qof_op_exclude,
+	qof_op_sql,
+	qof_op_sql_file,
+	qof_op_write,
+	qof_op_explain,
+	qof_op_compress,
+	qof_op_category,
+	qof_op_debug
+}qof_op_type;
+
+qof_shell_context*
+qof_create(void)
+{
+	qof_shell_context *context;
+
+	context = g_new0(qof_shell_context, 1);
+	return context;
+}
+
+/*static void
+print_config_cb (QofBackendOption *option, gpointer data)
+{
+	fprintf (stdout, "option name=%s\n", option->option_name);
+	fprintf (stdout, "option desc=%s\n", option->description);
+	fprintf (stdout, "option tip =%s\n", option->tooltip);
+	switch(option->type) {
+		case KVP_TYPE_GINT64   : {
+			fprintf (stdout, "option value=%" G_GINT64_FORMAT,
+				*(gint64*)option->value);
+			fprintf (stdout, "\noption type=%s\n", QOF_TYPE_INT64);
+			break;
+		}
+		case KVP_TYPE_DOUBLE   : {
+			break; 
+		}
+		case KVP_TYPE_NUMERIC  : {
+			break; 
+		}
+		case KVP_TYPE_STRING   : {
+			fprintf (stdout, "option value=%s\n", (char*)option->value);
+			fprintf (stdout, "option type=%s\n", QOF_TYPE_STRING);
+			break;
+		}
+		case KVP_TYPE_GUID     : { break; } // unsupported
+		case KVP_TYPE_TIMESPEC : {
+			break;
+		}
+		case KVP_TYPE_BINARY   : { break; } // unsupported
+		case KVP_TYPE_GLIST    : { break; } // unsupported
+		case KVP_TYPE_FRAME    : { break; } // unsupported
+	}		
+}*/
+
+void extensions_init(void)
+{
+	backend_extensions = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void qof_backend_extension_add(char *IDstring, gpointer data)
+{
+	g_hash_table_insert(backend_extensions, IDstring, data);
+}
+
+gpointer qof_backend_extension(const char* IDstring)
+{
+	gpointer func;
+
+	func = g_hash_table_lookup(backend_extensions, IDstring);
+	if(func) { return func; }
+	return NULL;
+}
+
 static gboolean
 load_bus_backend (const char *directory)
 {
@@ -129,11 +215,10 @@
 int
 main (int argc, const char *argv[])
 {
-	const char *exclude, *date_time, *database;
-	const char *sql_file, *write_file, *sql_query, *filename;
+	QOF_OP_VARS;
 	const char *help_header_text;
 	gboolean use_stdin;
-	qof_data *context;
+	qof_shell_context *context;
 	int optc, gz_level;
 	poptContext pc;
 	qof_op_type command;
@@ -142,8 +227,8 @@
 
 	struct poptOption options[] = {
 		QOF_CLI_OPTIONS
-		{"input", 'i', POPT_ARG_STRING, &filename, qof_op_input,
-		 _("Load a GnuCash or QSF book from <filename>"), "filename"},
+        {"shell", 0, POPT_ARG_NONE, NULL, qof_op_shell,
+         _("Enter the QOF interactive shell"), NULL},
 		POPT_TABLEEND
 	};
 	#ifdef ENABLE_NLS
@@ -236,37 +321,37 @@
 			/* optional modifiers - store to act on later. */
 			case qof_op_database:
 			{
-				qof_mod_database (database, context);
+				qof_mod_database (database, &context->qof);
 				break;
 			}
 			case qof_op_timespec:
 			{
-				qof_mod_timespec (date_time, context);
+				qof_mod_timespec (date_time, &context->qof);
 				break;
 			}
 			case qof_op_exclude:
 			{
-				qof_mod_exclude (exclude, context);
+				qof_mod_exclude (exclude, &context->qof);
 				break;
 			}
 			case qof_op_sql:
 			{
-				qof_mod_sql (sql_query, context);
+				qof_mod_sql (sql_query, &context->qof);
 				break;
 			}
 			case qof_op_sql_file:
 			{
-				qof_mod_sql_file (sql_file, context);
+				qof_mod_sql_file (sql_file, &context->qof);
 				break;
 			}
 			case qof_op_write:
 			{
-				qof_mod_write (write_file, context);
+				qof_mod_write (write_file, &context->qof);
 				break;
 			}
 			case qof_op_compress:
 			{
-				context->gz_level = gz_level;
+				context->qof.gz_level = gz_level;
 				break;
 			}
 			case qof_op_debug:
@@ -309,22 +394,21 @@
 		gnc_log_init();
 		qof_log_set_default(GNC_LOG_DETAIL);
 		gnc_set_log_level(CU_MOD_CLI, GNC_LOG_DETAIL);
-		gnc_set_log_level(CU_MOD_ENGINE, GNC_LOG_DETAIL);
+		gnc_set_log_level(QOF_MAIN_CLI, GNC_LOG_DETAIL);
 	}
 	g_return_val_if_fail((qof_load_backend_library 
 		(QOF_LIB_DIR, "libqof-backend-qsf.la", "qsf_provider_init")), -1);
 	g_return_val_if_fail((qof_load_backend_library
 		(GNC_LIBDIR, GNC_LIB_NAME, GNC_LIB_INIT)), -1);
 	g_return_val_if_fail(load_bus_backend(GNC_LIBDIR), -1);
-	context->input_session = session;
+	context->qof.input_session = session;
 	qof_book_clear_undo(context->book);
-	qof_object_foreach_type(qof_select_all, context);
 	switch (command)
 	{
 		case qof_op_input:
 		{
-			context->filename = g_strdup(filename);
-			qof_cmd_offline (context);
+			context->qof.filename = g_strdup(filename);
+			qof_cmd_xmlfile (&context->qof);
 			break;
 		}
 		case qof_op_list:
@@ -334,14 +418,14 @@
 		}
 		case qof_op_explain:
 		{
-			if(!context->database)
+			if(!context->qof.database)
 			{
 				fprintf (stderr, 
 					_("%s: Error. please specify the database to explain.\n\n"), 
 					PACKAGE);
 				break;
 			}
-			qof_cmd_explain(context);
+			qof_cmd_explain(&context->qof);
 			break;
 		}
 		case qof_op_shell:
@@ -356,7 +440,7 @@
 		}
 	}
 	poptFreeContext(pc);
-	qof_data_free(context);
+	qof_shell_free(context);
 	qof_close();
 	if(f) { fclose(f); }
 	return 0;

Modified: gnucash/branches/cashutil/cashutil/src/qof-main.c
===================================================================
--- gnucash/branches/cashutil/cashutil/src/qof-main.c	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/src/qof-main.c	2005-11-28 15:45:33 UTC (rev 12059)
@@ -1,11 +1,13 @@
 /***************************************************************************
  *            qof-main.c
  *
+ *  This is an auto-generated file. Patches are available from
+ *  http://qof-gen.sourceforge.net/
+ *
  *  Thu Jan 13 10:55:44 2005
  *  Copyright  2005  Neil Williams
  *  linux at codehelp.co.uk
  ****************************************************************************/
-
 /*
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -19,23 +21,13 @@
  *
  *  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.
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
-/** @addtogroup QOFCLI
 
-Includes common functions for all QOF CLI programs and provides generic
-functions to implement command line and interactive shell options.
-@{
-*/
-/** @file qof-main.c
-    @brief Common functions for the QOF external framework
-    @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
-*/
 #define _GNU_SOURCE
 #include "config.h"
 #include <stdio.h>
 #include <stdlib.h>
-#include <regex.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <errno.h>
@@ -43,431 +35,283 @@
 /** \todo temporary until undo goes into QofBook */
 #include "qofundo-p.h"
 
-#ifdef HAVE_LIBREADLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
+static QofLogModule log_module = QOF_MAIN_CLI;
 
-static GHashTable *backend_extensions;
-static gchar* log_module = CU_MOD_ENGINE;
-
-#define QSF_COMPRESS "compression_level"
-
-/** standardise tab output to help output line up nicely. */
-#define QOF_TAB "    "
-
-/** the maximum number of entities to offer for a single selection.
-
-\todo Improve the maximum to allow scrolling / next page.
-*/
-#define CLI_MAX_SELECT 20
-
-/** Relate the command to the function. */
-typedef int (*cmd_fcn) (qof_data *context);
-/** \todo Reorganise the C into multiple files. */
-static int qof_parse_command (const char *cmd, qof_data *context);
-
-/** Provide a simple numerical index for selectable objects.
-
- at param ent  each entity in turn of the selected type.
- at param data The qof_data context for the CLI.
-
-Ensure counter is reset to zero between runs.
-*/
-static void
-cli_select_cb (QofEntity *ent, gpointer data)
+struct param_ref_list
 {
-	qof_data *context;
-	gchar *temp;
-
-	context = (qof_data*)data;
-	g_return_if_fail(context);
-	context->counter++;
-	if(context->counter >= CLI_MAX_SELECT) { return; }
-	fprintf (stdout, "%d    %s\n", context->counter, 
-		qof_object_printable(context->inst_type, (QofInstance*)ent));
-	temp = g_strdup_printf("%d", context->counter);
-	g_hash_table_insert(context->select_table, temp, ent);
-}
-
-/** \brief Relate the commands to the functions within the shell. */
-struct shell_cmd
-{
-	const char *name;     /**< Name of the command. */
-	cmd_fcn func;         /**< Name of function to execute the command. */
+	GSList *slist;
+	QofType param_type;
+	int i;
 };
 
-/** command object param value */
-struct search_helper
-{
-	QofInstance *inst;
-	QofParam *param;  /**< the parameter in argv[2] */
-	const char *term; /**< argv[3] */
-	char *value;      /**< argv[4] */
-};
-
-/** check_for_single_instance */
-struct count_helper
-{
-	gint count;
-	QofInstance *first_inst;
-};
-
-/** pre-selects if only one entity of this type exists. */
 static void
-count_cb (QofEntity *ent, gpointer user_data)
+find_param_cb(QofParam *param, gpointer user_data)
 {
-	struct count_helper *ch;
+	struct param_ref_list *b;
+	char *buf;
 
-	ch = (struct count_helper*)user_data;
-	ch->count++;
-	if(ch->count == 1)
+	b = (struct param_ref_list*)user_data;
+	if((param->param_getfcn == NULL)||(param->param_setfcn == NULL)) { return; }
+	if(0 == safe_strcmp(b->param_type, param->param_type))
 	{
-		ch->first_inst = (QofInstance*)ent;
+		b->i++;
+		buf = g_strdup(param->param_name);
+		if(buf != NULL) {
+			b->slist = g_slist_append(b->slist, buf);
+		}
+		return;
 	}
 }
 
-/** select_table helper
+GSList*
+qof_main_get_param_list(QofIdTypeConst object_type, QofType param_type)
+{
+	GSList *param_list;
+	char *i;
+	struct param_ref_list p;
 
-The select_table hashtable relies on a conversion from
-the integer counter to a string. This frees the temporary
-conversion string when the hashtable is no longer needed.
-*/
-static void
-cli_free_select (gpointer key, gpointer value, gpointer data)
+	g_return_val_if_fail(object_type != NULL, NULL);
+	param_list = NULL;
+	p.slist = NULL;
+	p.i = 0;
+	p.param_type = g_strdup(param_type);
+	qof_class_param_foreach(object_type, find_param_cb, &p);
+	param_list = g_slist_copy(p.slist);
+	i = g_strdup(object_type);
+	return param_list;
+}
+
+void
+qof_main_free (qof_main_context *context)
 {
-	g_free(key);	
+	g_free(context->filename);
+	g_free(context->write_file);
+	g_free(context->sql_file);
+	g_free(context->database);
+//	g_free(context->shortname);
+	g_free(context->category);
 }
 
-static void option_cb (QofBackendOption *option, gpointer data)
+static void
+qof_main_run_sql (qof_main_context *context)
 {
-	gint gz_level;
+	QofSqlQuery *q;
+	gchar *sql;
 
-	gz_level = *(gint*)data;
-	if(0 == safe_strcmp(QSF_COMPRESS, option->option_name)) {
-		option->value = (gpointer)&gz_level;
-	}
+	q = qof_sql_query_new();
+	sql = g_strdup(context->sql_str);
+	qof_sql_query_parse(q, sql);
+	context->query = qof_sql_query_get_query(q);
 }
 
 static void
-qof_mod_compression (gint gz_level, qof_data *data)
+qof_main_run_query(qof_main_context *context)
 {
-	KvpFrame *be_config;
 	QofBook *book;
-	QofBackend *be;
+	GList *results;
 
-	if((gz_level > 0) && (gz_level <= 9))
-	{
-		book = qof_session_get_book(data->export_session);
-		be = qof_book_get_backend(book);
-		be_config = qof_backend_get_config(be);
-		qof_backend_option_foreach(be_config, option_cb, &gz_level);
-		qof_backend_load_config(be, be_config);
+	book = qof_session_get_book(context->input_session);
+	qof_query_set_book(context->query, book);
+	results = qof_query_run (context->query);
+	if(results != NULL) {
+		qof_entity_copy_list(context->export_session, results);
 	}
 }
 
-/** select an instance
-
-Offers multiple entities for selection. Ensure that
-check_for_single_instance has been run first.
-
-\todo Offer multiple listings of CLI_MAX_SELECT each.
-*/
-static int select_fcn (qof_data *context)
+/** takes one database name and runs -c and -t queries against it. */
+static void 
+build_database_list(QofIdTypeConst obj_type, qof_main_context *context)
 {
-	gint choice, max;
-	gchar *temp;
+	GSList *date_param_list, *category_param_list;
+	QofQueryPredData *date_pred_data;
+	QofQueryPredData *category_pred;
+	QofIdTypeConst find;
+	Timespec min_ts;
+	Timespec max_ts;
 
-	if(!context->inst_type) { return -1; }
-	choice = 0;
-	context->counter = 0;
-	context->instance = NULL;
-	context->select_table = g_hash_table_new(g_str_hash, g_str_equal);
-	qof_object_foreach(context->inst_type, context->book, cli_select_cb, context);
-	max = g_hash_table_size(context->select_table);
-	if(max > CLI_MAX_SELECT) { max = CLI_MAX_SELECT; }
-	/* Translators: %s is an object name, %d the number of objects in the book. */
-	switch (context->cli_mode)
-	{
-		case EDIT_MODE : {
-			fprintf (stdout, _("Choose the %s to edit: (1 - %d): "), 
-				context->inst_type, max);
-			break;
-		}
-		case DELETE_MODE : {
-			fprintf (stdout, _("Choose the %s to DELETE: (1 - %d): "), 
-				context->inst_type, max);
-			break;
-		}
-		case PRINT_MODE : {
-			fprintf (stdout, _("Choose the %s to print: (1 - %d): "), 
-				context->inst_type, max);
-			break;
-		}
-		case NO_OP : { break; }
+    if(!obj_type || !context) { return; }
+    context->query = qof_query_create_for(obj_type);
+    find = qof_query_get_search_for(context->query);
+	if(context->category != NULL) {
+		category_param_list = qof_query_build_param_list(CATEGORY_NAME, NULL);
+		category_pred = qof_query_string_predicate(QOF_COMPARE_EQUAL, 
+			context->category, QOF_STRING_MATCH_CASEINSENSITIVE, FALSE);
+        qof_query_add_term(context->query, category_param_list,
+            category_pred, QOF_QUERY_AND);
 	}
-	scanf("%2i", &choice);
-	fprintf (stdout, "\n");
-	while(choice <= 0 || choice > max)
-	{
-		char c;
-		c = getchar();
-		while (c != '\n') { c = getchar(); }
-		fprintf (stdout, _("Only %d objects are available of type '%s'.\n"), 
-			max, context->inst_type);
-		fprintf (stdout, _("Choose the %s to use: (1 - %d): "), context->inst_type, max);
-		scanf("%2i", &choice);
-		fprintf (stdout, "\n");
+	if(context->min_ts.tv_sec > 0) {
+		min_ts = context->min_ts;
+		max_ts = context->max_ts;
+		date_param_list = g_slist_copy(qof_main_get_param_list(find, QOF_TYPE_DATE));
+		if(!date_param_list) 
+		{
+            if(context->query) { qof_query_clear(context->query); }
+            PINFO (" no date_param_list");
+			return;
 	}
-	temp = g_strdup_printf("%d", choice);
-	context->instance = (QofInstance*)g_hash_table_lookup(context->select_table, temp);
-	g_free(temp);
-	g_hash_table_foreach(context->select_table, cli_free_select, NULL);
-	g_hash_table_destroy(context->select_table);
-	return 0;
+	date_pred_data = qof_query_date_predicate(QOF_COMPARE_GTE,
+		   QOF_DATE_MATCH_NORMAL, min_ts);
+	qof_query_add_term(context->query, date_param_list,
+		   date_pred_data, QOF_QUERY_AND);
+	date_param_list = qof_main_get_param_list(
+		   qof_query_get_search_for(context->query), QOF_TYPE_DATE);
+	date_pred_data = qof_query_date_predicate(QOF_COMPARE_LTE,
+		   QOF_DATE_MATCH_NORMAL, max_ts);
+	qof_query_add_term(context->query, date_param_list,
+		   date_pred_data, QOF_QUERY_AND);
+	}
+	qof_main_run_query(context);
+	if(context->query) { qof_query_clear(context->query); }
 }
 
-/** \brief Shorthand search routine.
-
-Rather than using a QofQuery, this shorthand version uses a
-string for all parameter types. It will support the existing
--t --date shorthand versions for compatibility with the non-interactive
-command set by actually using an underlying QofQuery.
-
-If this quick search fails, an interactive QofQuery could be used.
-A new query would have been needed anyway, with options enabled to
-isolate only one match. QofQuery relies on the term being the same
-type as the param_type. SQL queries will also be supported via the
-sql command in the shell. */
-
 static void
-cli_search_cb (QofEntity * ent, gpointer data)
+select_cb(QofObject *obj, gpointer data)
 {
-	struct search_helper *sh;
-	gchar *value;
-	const QofParam *param;
-	gboolean found;
-
-	found = FALSE;
-	sh = (struct search_helper*)data;
-	param = sh->param;
-/* if param_type == QOF_TYPE_DATE, process term as -t --date and skip to a QofQuery format. */
-	if(0 == safe_strcmp(param->param_type, QOF_TYPE_DATE)) { return; }
-	value = qof_book_merge_param_as_string((QofParam*)param, ent);
-	if(0 == safe_strcmp(value, sh->term))
+	qof_main_context *context;
+	
+	context = (qof_main_context*)data;
+	g_return_if_fail(context);
+	if(0 != safe_strcmp(context->exclude, obj->e_type))
 	{
-		 /* if term matches more than once, fallback to SQL */
-		if(found) { sh->inst = NULL; return; }
-		found = TRUE;
-		sh->inst = (QofInstance*)ent;
+		build_database_list(obj->e_type, context);
 	}
 }
 
-/** Shortcut if only one entity of this type exists. */
-static gboolean
-check_for_single_instance (qof_data *context)
+void
+qof_main_moderate_query(qof_main_context *context)
 {
-	QofCollection *coll;
-	struct count_helper ch;
-	struct search_helper sh;
+	GSList *date_param_list, *category_param_list;
+	gboolean all;
 
-	ch.count = 0;
-	qof_object_foreach(context->inst_type, context->book, count_cb, &ch);
-	if(!ch.count)
+	all = TRUE;
+    context->query = qof_query_create();
+	date_param_list = NULL;
+	category_param_list = NULL;
+	while (context->sql_list)
 	{
-		fprintf (stderr, _("%s: No objects of type '%s' found.\n"),
-			PACKAGE, context->inst_type);
-		context->inst_type = NULL;
-		return FALSE;
+		context->sql_str = g_strdup(context->sql_list->data);
+		qof_main_run_sql(context);
+		qof_main_run_query(context);
+		if(context->query) { qof_query_clear(context->query); }
+		g_free(context->sql_str);
+		all = FALSE;
+		context->sql_list = g_list_next(context->sql_list);
 	}
-	if(ch.count == 1) 
-	{ 
-		context->instance = ch.first_inst; 
-		if(!context->argv[2] || !context->argv[3]) { return TRUE; }
+	if(0 < g_list_length(context->sql_list)) { 
+		context->sql_str = NULL;
+		g_list_free(context->sql_list);
+		all = FALSE;
 	}
-	if(!context->argv[2] || !context->argv[3]) { return FALSE; }
-	sh.inst = NULL;
-	sh.param = NULL;
-	sh.term = context->argv[3];
-	sh.param = (QofParam*)qof_class_get_parameter(context->inst_type, context->argv[2]);
-	if(sh.param == NULL) { 
-		fprintf (stderr, _("No parameter named '%s' found for object '%s'\n"), 
-			sh.term, sh.param->param_name);
-		return FALSE;
+	if(context->sql_str != NULL) {
+		qof_main_run_sql(context);
+		qof_main_run_query( context);
+		if(context->query) { qof_query_clear(context->query); }
+		all = FALSE;
 	}
-	coll = qof_book_get_collection(context->book, context->inst_type);
-	if(!coll) { return FALSE; }
-	qof_collection_foreach(coll, cli_search_cb, &sh);
-	if(sh.inst)
+	if((context->exclude != NULL)&&(qof_class_is_registered(context->exclude)))
 	{
-		context->instance = sh.inst;
-		if(context->argv[4]) {
-			g_message("argv[4]=%s param=%s", context->argv[4], sh.param->param_name);
-			sh.value = g_strdup(context->argv[4]);
-			qof_begin_edit(context->instance);
-			undo_edit_record(context->instance, sh.param);
-			qof_entity_set_param(&context->instance->entity, (QofParam*)sh.param, sh.value);
-			qof_commit_edit(context->instance);
-			undo_edit_commit(context->instance, sh.param);
-		}
-		return TRUE;
+		qof_object_foreach_type(select_cb, context);
+		all = FALSE;
 	}
-	return FALSE;
+	if((context->database != NULL)&&(qof_class_is_registered(context->database)))
+	{
+		build_database_list(context->database, context);
+		all = FALSE;
+	}
+	if(all == TRUE)
+	{
+		qof_object_foreach_type(select_cb, context);
+	}
 }
 
-/** @name Top level shell functions.
-@{
-*/
-
-/** Run a sub-shell to either select or edit an instance. */
-static int
-qof_sub_shell (qof_data *context)
+static void option_cb (QofBackendOption *option, gpointer data)
 {
-#ifdef HAVE_LIBREADLINE
-	char *line;
-	char *prompt;
+	gint gz_level;
 
-	if(!context->inst_type) { return 0; }
-	line = (char*)malloc(256 * sizeof(char));
-	prompt = g_strdup_printf("%s/%s> ", context->shortname, context->inst_type);
-#else
-	char buf[256];
-
-	if(!context->inst_type) { return 0; }
-#endif
-	for (;;) {
-		fflush (stdout);
-#ifdef HAVE_LIBREADLINE
-		line = readline(prompt);
-		if (line == NULL) { break; }
-		if (*line) { add_history(line); }
-		if (qof_parse_command(line, context) < 0) { break; }
-		g_free (line);
-#else
-		fprintf (stdout, "%s/%s> ", context->shortname, context->inst_type);
-		if (qof_parse_command(buf, context) < 0) { break; }
-#endif
+	gz_level = GPOINTER_TO_INT(data);
+	if(0 == safe_strcmp(QSF_COMPRESS, option->option_name)) {
+		option->value = (gpointer)&gz_level;
 	}
-	fprintf(stdout, "\n");
-	return 0;
 }
 
-/** \brief add a new instance and edit the values
-
-Expects the second argument (context->argv[1]) to be the name of a registed object.
-*/
-static int add_fcn(qof_data *context)
+void
+qof_mod_compression (gint gz_level, qof_main_context *context)
 {
-	QofInstance *inst;
-	gint result;
-	gchar *temp;
+	KvpFrame *be_config;
+	QofBook *book;
+	QofBackend *be;
 
-	result = 0;
-	if(!qof_class_is_registered(context->argv[1]))
+	if((gz_level > 0) && (gz_level <= 9))
 	{
-		fprintf (stdout, _("%s: Cannot add '%s' - object name not found\n"),
-			PACKAGE, context->argv[1]);
-		return 0;
+		ENTER (" ");
+		book = qof_session_get_book(context->export_session);
+		be = qof_book_get_backend(book);
+		be_config = qof_backend_get_config(be);
+		qof_backend_option_foreach(be_config, option_cb, &gz_level);
+		qof_backend_load_config(be, be_config);
+		LEAVE (" ");
 	}
-	context->inst_type = g_strdup(context->argv[1]);
-	temp = g_strdup_printf("add-%s", context->inst_type);
-	qof_book_start_operation(context->book, temp);
-	inst = (QofInstance*)qof_object_new_instance(context->inst_type, context->book);
-	if(!inst) {
-		fprintf (stdout, _("Failed to add an instance of %s\n"), context->inst_type);
-		return 0;
-	}
-	fprintf (stdout, _("Added an instance of %s\n\n"), context->inst_type);
-	fprintf (stdout, _("Now edit the parameter details of this instance.\n"));
-	fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
-	undo_create_record(inst);
-	qof_book_end_operation(context->book);
-	context->shell_type = EDIT_SHELL;
-	context->instance = inst;
-	result = qof_sub_shell(context);
-	context->shell_type = TOP_SHELL;
-	return result;
 }
 
-/** \brief edit [object]
-
-\todo skip over user-friendly forms that may use
-where or =.
-
-*/
-static int edit_fcn(qof_data *context)
+void
+qof_cmd_xmlfile (qof_main_context *context)
 {
-	gint result;
+	QofSession *input_session, *export_session;
+	gchar current_work[PATH_MAX];
 	gchar *temp;
+	QofBackend *be;
+	QofBook *book;
+	KvpFrame *backend_config;
 
-	if(!qof_class_is_registered(context->argv[1]))
+	backend_config = NULL;
+	input_session = context->input_session;
+	if(0 == safe_strcmp(context->exclude, context->database)
+		&&(context->exclude != NULL))
 	{
-		fprintf (stdout, _("%s: Cannot edit '%s' - object name not found\n"),
-			PACKAGE, context->argv[1]);
-		return 0;
+		fprintf(stderr, _("%s: Error: Cannot exclude database \"%s\" with option -e\n"
+		"    because option -d is set to the include the same database: \"%s\"\n"
+		"Use the \'-l\' command to see the full list of supported databases.\n"),
+			PACKAGE, context->exclude, context->database);
+		qof_session_end(input_session);
+		return;
 	}
-	result = 0;
-	context->inst_type = g_strdup(context->argv[1]);
-	if(!check_for_single_instance(context)) {
-		fprintf (stdout, _("Select an instance of %s to edit:\n"), context->inst_type);
-		fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
-		context->cli_mode = EDIT_MODE;
-		result = select_fcn(context);
-		context->cli_mode = NO_OP;
-		if (result) { return result; }
-	}
-	context->shell_type = EDIT_SHELL;
-	temp = g_strdup_printf("modify-%s", context->inst_type);
-	qof_book_start_operation(context->book, temp);
-	fprintf (stdout, _("Edit the parameter details of the selected instance.\n"));
-	fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
-	if(context->instance) { result = qof_sub_shell(context); }
-	context->shell_type = TOP_SHELL;
-	context->inst_type = NULL;
-	qof_book_end_operation(context->book);
-	return result;
-}
-/** \brief delete an instance of an object.
-
-Selects one instance and frees the entity.
-Warns the user AFTER deletion. 
-
-\todo a bespoke undo command could be useful, rather
-than quitting the program completely! :-)
-*/
-static int delete_fcn(qof_data *context)
-{
-	gint result;
-	QofEntity *ent;
-	gchar *temp;
-
-	result = 0;
-	if(!qof_class_is_registered(context->argv[1]))
+	qof_session_begin(input_session, context->filename, FALSE, TRUE);
+	qof_session_load(input_session, NULL);
+	if(ERR_BACKEND_LOCKED == qof_session_get_error(input_session))
 	{
-		fprintf (stdout, _("%s: Cannot delete '%s' - object name not found\n"),
-		PACKAGE, context->argv[1]);
-		return 0;
+		/** \todo ask the user if it is OK to ignore the lock. */
+		qof_session_begin(input_session, context->filename, TRUE, FALSE);
+		qof_session_load(input_session, NULL);
 	}
-	context->inst_type = g_strdup(context->argv[1]);
-	if(!check_for_single_instance(context))
+	book = qof_session_get_book(input_session);
+	be = qof_book_get_backend(book);
+	backend_config = qof_backend_get_config(be);
+	PINFO (" trying to get backend config");
+	if(backend_config) 
 	{
-		fprintf (stdout, _("Select the instance of %s to delete\n"), context->inst_type);
-		fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
-		context->cli_mode = DELETE_MODE;
-		result = select_fcn(context);
-		context->cli_mode = NO_OP;
-		if ((result)||(!context->instance)) { return result; }
+/*		qof_backend_option_foreach(backend_config, 
+			print_config_cb, context);*/
 	}
-	temp = g_strdup_printf("delete_%s", context->inst_type);
-	qof_book_start_operation(context->book, temp);
-	undo_delete_record(context->instance);
-	ent = (QofEntity*)context->instance;
-	qof_collection_mark_dirty(qof_book_get_collection(context->book, ent->e_type));
-	qof_entity_release(ent);
-	qof_book_end_operation(context->book);
-	fprintf (stdout, _("The instance has been DELETED. Type 'quit' to exit and undo. "
-	"The file will not be changed until you type 'write'.\n"));
-	return 0;
+	else { PINFO (" failed"); }
+	export_session = qof_session_new();
+	context->export_session = export_session;
+	if(context->write_file != NULL) {
+		if(*context->write_file != '/')
+		{
+			getcwd(current_work, PATH_MAX);
+			temp = g_strconcat(current_work, "/", context->write_file, NULL);
+			context->write_file = temp;
+		}
+		qof_session_begin(export_session, context->write_file, FALSE, TRUE);
+	}
+	else { qof_session_begin(export_session, QOF_STDOUT, TRUE, FALSE); }
+	qof_session_set_current_session(input_session);
+	qof_main_moderate_query(context);
+	qof_session_save(export_session, NULL);
+	qof_main_show_error(export_session);
+	qof_main_show_error(input_session);
+	qof_session_end(input_session);
+	qof_session_end(export_session);
 }
 
 static void
@@ -478,725 +322,192 @@
 	}
 }
 
-static int list_fcn(qof_data *context)
+void
+qof_main_select(qof_main_context *context)
 {
-	fprintf (stdout, _("\nThe QOF shell supports these object names:\n"
-	"You can use the names with 'edit', 'print' or 'delete'\n"
-	"and in SQL queries (as the table name) with 'sql'\n"
-	"Descriptions are shown only for readability.\n\n"));
-	fprintf (stdout, "%-20s%s", _("Object Name"), _("Description\n"));
-	qof_object_foreach_type(qof_list_cb, NULL);
-	fprintf (stdout, _("\nUse 'explain <database>' to see the list of fields within\n"
-	"any supported database.\n"));
-	return 0;
-}
-
-static void
-qof_print_cb (QofParam *param, gpointer data)
-{
-	gchar *str;
-	qof_data *context;
-
-	context = (qof_data*)data;
 	g_return_if_fail(context);
-	str = qof_book_merge_param_as_string(param, (QofEntity*)context->instance);
-	fprintf (stdout, _("%-24s %-12s %s\n"), param->param_name, param->param_type, str);
+	qof_object_foreach_type(select_cb, context);
 }
 
-/** \brief print an instance to the terminal. */
-static int print_fcn(qof_data *context)
+void
+qof_cmd_list (void)
 {
-	gint result;
-
-	result = 0;
-	if(!qof_class_is_registered(context->argv[1]))
-	{
-		fprintf (stdout, _("%s: Cannot print '%s' - object name not found\n"),
-			PACKAGE, context->argv[1]);
-		return 0;
-	}
-	context->inst_type = g_strdup(context->argv[1]);
-	if(!check_for_single_instance(context))
-	{
-		if(!context->inst_type) { return 0; }
-		fprintf (stdout, _("Select the instance of '%s' to print.\n"), context->inst_type);
-		fprintf (stdout, _(" Type 'help' for available commands or parameters.\n\n"));
-		context->cli_mode = PRINT_MODE;
-		result = select_fcn(context);
-		context->cli_mode = NO_OP;
-		if ((result)||(!context->instance)) { return result; }
-	}
-	fprintf (stdout, "%-24s %-12s %s\n\n", _("Name"), _("Type"), _("Value"));
-	qof_class_param_foreach (context->inst_type, qof_print_cb, context);
-	fprintf(stdout, "\n");
-	return 0;
+	fprintf(stdout, _("\n%s currently supports these database names:\n"
+	"You can use the names with %s -d\n"
+	"and in SQL queries (as the table name) with %s -s|f\n"
+	"Descriptions are shown only for readability.\n\n"
+	"Name                    Description\n\n"
+	)
+	, PACKAGE, PACKAGE, PACKAGE);
+	qof_object_foreach_type(qof_list_cb, NULL);
+	fprintf(stdout, _("\nUse '-d <database> --explain' to see the list of fields within\n"
+	"any supported database.\n"));
+	fprintf(stdout, _("\nThank you for using %s\n\n"), PACKAGE);
 }
 
 static void
-qof_explain_cb (QofParam* param, gpointer user_data)
+explain_cb (QofParam* param, gpointer user_data)
 {
 	if(param->param_getfcn && param->param_setfcn)
 	{
-		fprintf (stdout, _("Type: %-12s\tName: %-12s\n"), param->param_type, param->param_name);
+		fprintf(stdout, _("Type: %s\tName: %s\n"), 
+			param->param_type, param->param_name);
 	}
 }
 
-/** print a list of available parameters for this object type */
-static int explain_fcn(qof_data *context)
+void
+qof_cmd_explain (qof_main_context *context)
 {
-	if(qof_class_is_registered(context->argv[1])) {
-		fprintf (stdout, _("\nParameters of the %s database:\n\n"), context->argv[1]);
-		qof_class_param_foreach(context->argv[1], qof_explain_cb, NULL);
-		fprintf (stdout, "\n\n");
-	}
-	else {
-		fprintf (stderr, _("\n%s: %s object not found.\n"), PACKAGE, context->argv[1]);
-	}
-	return 0;
+
+	if(context->error) { return; }
+	fprintf(stdout, _("\nParameters of the %s database:\n\n"), context->database);
+	qof_class_param_foreach(context->database, explain_cb, NULL);
+	fprintf(stdout, _("\nThank you for using %s\n\n"), PACKAGE);
 }
 
-/** \brief load [filename]
-
-Expects a readable filename in context->argv[1]
-*/
-static int load_fcn(qof_data *context)
+void
+qof_mod_category (const char *category, qof_main_context *data)
 {
-	char* answer;
+	data->category = g_strdup(category);
+}
 
-	answer = g_strdup(" ");
-	if(qof_book_not_saved(context->book))
-	{
-		fprintf (stderr, _("%s: The current book has not been saved.\n"
-		"Do you still want to overwrite it? [y/n]\n"), PACKAGE);
-		scanf("%1s", answer);
-		if((0 != safe_strcmp(answer, "y")) && (0 != safe_strcmp(answer, "Y"))) {
-			qof_session_save(context->input_session, NULL);
-		}
+void
+qof_mod_database (const char *database, qof_main_context *data)
+{
+	if(qof_class_is_registered(database)) {
+		data->database = g_strdup(database);
 	}
-	qof_session_end(context->input_session);
-	context->input_session = qof_session_new();
-	if(context->argv[1]) {
-		context->filename = g_strdup(context->argv[1]);
-	}
-	if(context->filename) {
-		qof_session_begin(context->input_session, context->filename, FALSE, TRUE);
-		qof_mod_compression(context->gz_level, context);
-	}
-	else {
-		qof_session_begin(context->input_session, QOF_STDOUT, FALSE, FALSE);
-	}
-	qof_session_load(context->input_session, NULL);
-	context->book = qof_session_get_book(context->input_session);
-	qof_show_error(context->input_session, context->filename);
-	/* implement in the backend eventually */
-	qof_book_clear_undo(context->book);
-	return 0;
 }
-/** \brief write [filename]
 
-write the current book to file.
-	If no filename is provided, save to the original file
-	(to STDOUT if STDIN used). Analagous to Save / Save As...
-
-\todo fix problems with writing to an alternative file.
-*/
-static int write_fcn(qof_data *context)
+void
+qof_mod_timespec (const char *date_time, qof_main_context *data)
 {
-	QofSession *save_as;
-	gchar current_work[PATH_MAX];
 	gchar *temp;
+	int year, month, day;
+	gboolean takemonth, takeyear, scanned;
+	char *first_field, *second_field, *third_field;
+	static char *delims = ".,-+/\\() ";
 
-	if(context->argv[1])
+	takemonth = takeyear = scanned = FALSE;
+	day = month = year = 0;
+	second_field = "";
+	third_field = "";
+	temp = g_strdup(date_time);
+	qof_date_format_set(QOF_DATE_FORMAT_UTC);
+	scanned = qof_scan_date(temp, &day, &month, &year);
+	if(scanned == FALSE)
 	{
-		save_as = qof_session_new();
-		if(*context->argv[1] != '/')
+		first_field = strtok (temp, delims);
+		if (first_field)
 		{
-			getcwd(current_work, PATH_MAX);
-			temp = g_strconcat(current_work, "/", context->argv[1], NULL);
-			context->argv[1] = temp;
-		}
-		qof_session_begin(save_as, context->argv[1], FALSE, TRUE);
-		qof_session_swap_data(save_as, context->input_session);
-		qof_session_save(save_as, NULL);
-		qof_show_error(save_as, context->argv[1]);
-		qof_session_end(save_as);
-		return 0;
-	}
-	if(context->write_file)
-	{
-		qof_session_save(context->export_session, NULL);
-		qof_show_error(context->export_session, context->write_file);
-	}
-	else {
-		qof_session_save(context->input_session, NULL);
-		qof_show_error(context->input_session, context->filename);
-	}
-	/* implement in the backend eventually */
-	if(qof_book_can_undo(context->book)) { qof_book_clear_undo(context->book); }
-	return 0;
-}
-
-/** \brief merge UI
-
-\todo the entire merge routine needs testing.
-*/
-static void
-qof_merge_loop (qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder)
-{
-	GSList *user_reports;
-	QofParam *one_param;
-	gchar *importstring, *targetstring, *buffer;
-	gint count, resolution;
-	gboolean input_ok;
-	gchar y;
-
-	buffer = "";
-	count = 0;
-	input_ok = FALSE;
-	user_reports = rule->mergeParam;
-	while(user_reports != NULL) {
-		one_param = user_reports->data;
-		buffer = g_strconcat(buffer, g_strdup_printf(_("%i:Parameter name: %s "),
-			count, one_param->param_name), NULL);
-		importstring = qof_book_merge_param_as_string(one_param, rule->importEnt);
-		buffer = g_strconcat(buffer, g_strdup_printf(_("Import data : %s "), importstring), NULL);
-		targetstring = qof_book_merge_param_as_string(one_param, rule->targetEnt);
-		buffer = g_strconcat(buffer, g_strdup_printf(_("Original data : %s\n"), targetstring), NULL);
-		user_reports = g_slist_next(user_reports);
-		count++;
-	}
-	while(!input_ok) {
-		resolution = 0;
-		fprintf (stdout, _("\nPlease resolve this conflict. Enter\n"
-		"    1 to use the import data or \n"
-		"    2 to keep the original data or"));
-		if(rule->mergeAbsolute == FALSE) {
-			fprintf (stdout, _("\n    3 to import the data as a NEW object or"));
-		}
-		fprintf (stdout, _("\n    9 to abort the entire merge operation.\n"));
-		fprintf (stdout, "> (1, 2");
-		if(rule->mergeAbsolute == FALSE) {
-			fprintf (stdout, ", 3 ");
-		}
-		fprintf (stdout, _("or 9) : "));
-		scanf("%1i", &resolution);
-		switch(resolution) {
-			case 1 : {
-				mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_UPDATE);
-				input_ok = TRUE;
-				break;
-				}
-			case 2 : {
-				if(rule->mergeAbsolute == FALSE) {
-					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_DUPLICATE);
-				}
-				if(rule->mergeAbsolute == TRUE) {
-					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_ABSOLUTE);
-				}
-				input_ok = TRUE;
-				break;
+			second_field = strtok (NULL, delims);
+			if (second_field)
+			{
+				third_field = strtok (NULL, delims);
 			}
-			case 3 : {
-				if(rule->mergeAbsolute == FALSE) {
-					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_NEW);
-					input_ok = TRUE;
-				}
-				break;
-			}
-			case 9 : {
-				fprintf (stdout, _("Are you sure you want to abort the entire merge operation?\n"
-				"The rest of the import data will not be processed.\n"
-				"Your original data will not be modified. Abort? y/n : "));
-				scanf("%1s", &y);
-				if((safe_strcmp(_("y"),&y) == 0)||(safe_strcmp("",&y) == 0)) {
-					fprintf (stdout, _("Aborting . . \n\n"));
-					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_INVALID);
-					input_ok = TRUE;
-				}
-				break;
-			}
-			default : break;
 		}
-	}
-}
-
-/** \brief merge [filename]
-
-Accept a filename from the shell, check it is readable and
-load it, then if the book is valid, merge it into the
-current book.
-
-If the current book is NULL, this is equivalent to 'load' but
-a LOT more longwinded. (Not recommended.)
-*/
-static int merge_fcn(qof_data *context)
-{
-	qof_book_mergeData *mergeData;
-	QofBook *importBook, *targetBook;
-	gint result;
-
-	/** \todo check the filename can be read. */
-	if(!context->argv[1]) { return 0; }
-	qof_session_begin(context->export_session, context->argv[1], FALSE, FALSE);
-	qof_session_load(context->export_session, NULL);
-	qof_show_error(context->export_session, context->write_file);
-	importBook = qof_session_get_book(context->export_session);
-	targetBook = context->book;
-	mergeData = qof_book_mergeInit(importBook, targetBook);
-	g_return_val_if_fail((mergeData != NULL), -1);
-	qof_book_mergeRuleForeach(mergeData, qof_merge_loop, MERGE_REPORT);
-	result = qof_book_mergeCommit(mergeData);
-	return result;
-}
-
-/** check if book is dirty, show errors (if any) and exit */
-static int exit_fcn(qof_data *context)
-{
-	char* answer;
-
-	answer = g_strdup(" ");
-	if(qof_book_not_saved(context->book))
-	{
-		fprintf (stdout, _("The current book has not been saved.\n"
-		"Do you still want to quit without saving? %s"), "[y/n]");
-		scanf("%1s", answer);
-		fprintf (stdout, "\n");
-		if(0 != safe_strcmp(answer, "y") && (0 != safe_strcmp(answer, "Y"))) {
-			qof_session_save(context->input_session, NULL);
+		if (third_field && second_field)
+		{
+			year = atoi(first_field);
+			month = atoi(second_field);
+			day = atoi(third_field);
+		} else if (second_field)
+		{
+			year = atoi(first_field);
+			month = atoi(second_field);
+			takemonth = TRUE;
+		} else if (first_field)
+		{
+			year = atoi(first_field);
+			takeyear = TRUE;
 		}
 	}
-	if(qof_book_not_saved(qof_session_get_book(context->export_session)))
-	{
-		qof_show_error(context->export_session, context->write_file);
+	if(takemonth) { day = 1; }
+	if(takeyear)  { day = 1; month = 1; }
+	data->min_ts = gnc_dmy2timespec(day, month, year);
+	if(takemonth) { day = gnc_date_my_last_mday(month, year); }
+	if(takeyear)  {
+		month = 12;
+		day = gnc_date_my_last_mday(month, year);
 	}
-	qof_show_error(context->input_session, context->filename);
-	qof_session_end(context->input_session);
-	if(context->export_session) { qof_session_end(context->export_session); }
-	fprintf (stdout, _("\nThank you for using %s.\n"), PACKAGE);
-	return -1;
+	data->max_ts = gnc_dmy2timespec_end(day, month, year);
 }
 
-static int help_fcn(qof_data *context)
+void
+qof_mod_exclude (const char *exclude, qof_main_context *data)
 {
-	fprintf (stdout, _("Commands available in the shell are:\n\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "add [object]", 
-		_("Add a new instance of the object.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "edit [object]",
-		_("Select one instance and edit (set) the parameter values.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "delete [object]", 
-		_("Select one instance for deletion.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "list", 
-		_("Synonym for the --list command outside the shell.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "print [object]", 
-		_("Select one instance and print the parameter values.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "explain [object]", 
-		_("Synonym for the --explain command outside the shell.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "load [filename]",
-		_("Replace the current book with data from the file.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, " ", 
-		_("Prompts to save the current book, if any.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "write [filename]", 
-		_("Write out the current book. If no filename is given,\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, " ", _("write to the original file.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, " ", _("(uses STDOUT if STDIN is used.)\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "merge [filename]", 
-		_("Merge data from the file into the current book.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "sql [sql_query]", 
-		_("Run a \"quoted\" SQL statement.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "help | ?", _("This screen.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "quit | q | exit", _("Quit the shell\n"));
-	fprintf (stdout, "\n");
-	return 0;
-}
-
-/** \brief sql - take a SQL statement.
-
-\todo improve the sql_parser - it is very noisy and
-yet not very helpful.
-*/
-static int sql_fcn(qof_data *context)
-{
-	QofSqlQuery *q;
-	gchar *sql, *temp;
-	gint result;
-	QofQuery *qq;
-	QofBook *book;
-	GList *results;
-
-	q = qof_sql_query_new();
-	sql = g_strdup(context->sql_str);
-	if(!sql) { sql = context->argv[1]; }
-	qof_sql_query_parse(q, sql);
-	qq = qof_sql_query_get_query(q);
-	book = qof_session_get_book(context->input_session);
-	qof_query_set_book(qq, book);
-	qof_query_set_sort_order(qq, NULL, NULL, NULL);
-	results = qof_query_run (qq);
-	if(!results) { return 0; }
-	if(g_list_length(results) == 1) { 
-		context->instance = (QofInstance*)results->data;
-		context->shell_type = EDIT_SHELL;
-		temp = g_strdup_printf("modify-%s", context->inst_type);
-		qof_book_start_operation(context->book, temp);
-		fprintf (stdout, _("Edit the parameter details of the selected instance.\n"));
-		fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
-		if(context->instance) { result = qof_sub_shell(context); }
-		context->shell_type = TOP_SHELL;
-		context->inst_type = NULL;
-		qof_book_end_operation(context->book);
-		return 0;
+	if(qof_class_is_registered(exclude)) {
+		data->exclude = g_strdup(exclude);
 	}
-	fprintf (stdout, _("Query returned %d entities\n"), g_list_length(results));
-	return 0;
 }
 
-/** @} */
-/** \name Edit shell functions
-@{
-*/
-
-/** \brief Set a value in a parameter
-
-\todo improve error handling - currently it is almost silent.
-
-Boolean values accept TRUE, 1, or translated values for Y, YES or TRUE
-and are matched case insensitively, so y, yes and true (and their translations)
-also match. If the match fails, false is set.
-
-*/
-static int set_edit_fcn (qof_data *context)
+void
+qof_mod_sql (const char *sql_query, qof_main_context *data)
 {
-	QofParam *param;
-	QofType type;
-	gchar *value;
-
-	if(!context->instance || !context->argv[1] || !context->argv[2]) { return 0; }
-	value = g_strdup(context->argv[2]);
-	param = (QofParam*)qof_class_get_parameter(context->inst_type, context->argv[1]);
-	if(!param)
-	{
-		fprintf (stderr, _("%s: parameter name '%s' of object '%s' not recognised.\n"),
-			PACKAGE, context->argv[1], context->inst_type);
-		fprintf (stderr, _("Type 'explain' for more information.\n"));
-		return 0;
-	}
-	type = qof_class_get_parameter_type(context->inst_type, context->argv[1]);
-	qof_begin_edit(context->instance);
-	/* undo_edit_record will be called by begin_edit eventually. */
-	undo_edit_record(context->instance, param);
-	qof_entity_set_param(&context->instance->entity, param, value);
-	qof_commit_edit(context->instance);
-	undo_edit_commit(context->instance, param);
-	g_free(value);
-	return 0;
+	if(!qof_check_sql(sql_query)) { return; }
+	data->sql_str = g_strdup(sql_query);
 }
 
-/** List the available parameters for this edit */
-static int explain_edit_fcn (qof_data *context)
-{
-	fprintf (stdout, _("\nEditable parameters of the %s object:\n\n"), context->inst_type);
-	qof_class_param_foreach(context->inst_type, qof_explain_cb, NULL);
-	fprintf (stdout, "\n\n");
-	return 0;
-}
-
-/** \brief Commit data to instance and leave the sub-shell.
-
-\todo Should this clear the undo??
-*/
-static int commit_edit_fcn (qof_data *context)
-{
-//	if(qof_book_can_undo(context->book)) { qof_book_clear_undo(context->book); }
-	return -1;
-}
-
-/** print the instance being edited.*/
-static int print_edit_fcn (qof_data *context)
-{
-	if(!context->inst_type) { return 0; }
-	fprintf (stdout, "%-24s %-12s %s\n\n", _("Name"), _("Type"), _("Value"));
-	qof_class_param_foreach (context->inst_type, qof_print_cb, context);
-	fprintf (stdout, _("\nNot all parameters are editable, use 'explain' "
-	"to see the list of editable parameters for the object '%s'.\n\n"), context->inst_type);
-	return 0;
-}
-
-static int help_edit_fcn (qof_data *context)
-{
-	gchar *temp;
-
-	fprintf (stdout, _("Commands available in this sub-shell are:\n\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "edit [Name] [Value]", 
-	_("Edit the parameter 'Name' to have the 'Value' given.\n"));
-	/* Translators: TRUE and numeral one are always acceptable for boolean values here. */
-	fprintf (stdout, QOF_SHELL_FORMAT, " ", 
-		_("Boolean values accept TRUE|true, 1, Y|y or yes|YES\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, " ", _("String values that include spaces should be quoted\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "set [Name] [Value]", _("Synonym for edit.\n"));
-	temp = g_strdup_printf(_("Print the current values for this '%s' instance.\n"), context->inst_type);
-	fprintf (stdout, QOF_SHELL_FORMAT, "print", temp);
-	g_free(temp);
-	temp = g_strdup_printf(_("Show the editable parameters for '%s'\n"), context->inst_type);
-	fprintf (stdout, QOF_SHELL_FORMAT, "explain", temp);
-	g_free(temp);
-	fprintf (stdout, QOF_SHELL_FORMAT, "commit",  _("Set the edited parameter values.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "help | ?", _("This screen.\n"));
-	fprintf (stdout, QOF_SHELL_FORMAT, "quit | q | exit", 
-		_("Return to the top shell without setting changes.\n"));
-	fprintf (stdout, "\n");
-	return 0;
-}
-
-static int quit_edit_fcn (qof_data *context)
-{
-	if(qof_book_can_undo(context->book)) { qof_book_undo(context->book); }
-	return -1;
-}
-
-/** @} */
-/** \name Shell control handlers
-@{
-*/
-struct shell_cmd shell_list[] = {
-	{ "add",     add_fcn          },
-	{ "edit",    edit_fcn         },
-	{ "delete",  delete_fcn       },
-	{ "list",    list_fcn         },
-	{ "print",   print_fcn        },
-	{ "explain", explain_fcn      },
-	{ "load",    load_fcn         },
-	{ "write",   write_fcn        },
-	{ "merge",   merge_fcn        },
-	{ "sql",     sql_fcn          },
-	{ "help",    help_fcn         },
-	{ "?",       help_fcn         },
-	{ "q",       exit_fcn         },
-	{ "quit",    exit_fcn         },
-	{ "exit",    exit_fcn         },
-	{ "bye",     exit_fcn         },
-	{ NULL, NULL }
-};
-
-struct shell_cmd edit_list[] = {
-	{ "edit",    set_edit_fcn     },
-	{ "set",     set_edit_fcn     },
-	{ "explain", explain_edit_fcn },
-	{ "commit",  commit_edit_fcn  },
-	{ "print",   print_edit_fcn   },
-	{ "quit",    quit_edit_fcn    },
-	{ "help",    help_edit_fcn    },
-	{ "?",       help_edit_fcn    },
-	{ "q",       quit_edit_fcn    },
-	{ "exit",    quit_edit_fcn    },
-	{ "bye",     quit_edit_fcn    },
-	{ NULL, NULL }
-};
-
-static char *strtoke(char *str, const char *ws, const char *delim)
-{
-	int inc;
-	static char *start, *s = NULL;
-
-	if (str != NULL) { s = str; }
-	inc = strspn(s, ws);
-	s += inc;
-	start = s;
-	if (*s == '\0') { return NULL; }
-	else if (strchr(delim, *s) != NULL) {
-		start++;
-		s = strchr(s + 1, *s);
-		*s = '\0';
-		s++;
-	}
-	else {
-		inc = strcspn(s, ws);
-		if (s[inc] == '\0') { s += inc; }
-		else {
-			s[inc] = '\0';
-			s += inc + 1;
-		}
-	}
-	return start;
-}
-
-gboolean
-qof_check_sql(const char *sql)
-{
-	regex_t *r;
-	int reg_exp_check;
-	static char *pattern = QOF_SQL_SUPPORTED;
-//	QofSqlQuery *q;
-	gboolean result;
-
-	result = FALSE;
-	r = g_new(regex_t, 1);
-	reg_exp_check = regcomp(r, pattern,
-		REG_ICASE | REG_NOSUB | REG_EXTENDED);
-	g_return_val_if_fail(reg_exp_check == 0, FALSE);
-	if(0 == regexec(r, sql, 0, NULL, 0)) { result = TRUE; }
-	regfree(r);
-	g_free(r);
-	return result;
-}
-
-/** \brief Relate the command to the function.
-
-\todo Stop changing the input with strtoke ?
-*/
-static int
-qof_parse_command (const char *cmd, qof_data *context)
-{
-	char *argv[32];
-	int inc, argc;
-	char *cmd_dup;
-	gboolean good;
-
-	argc = 0;
-	good = FALSE;
-	memset(argv, 0, sizeof(argv) / sizeof(char*));
-	cmd_dup = strdup(cmd);
-	argv[0] = strtoke(cmd_dup, " \t\n", "\"'");
-	while (argv[argc] != NULL) {
-		argc++;
-		argv[argc] = strtoke(NULL, " \t\n", "\"'");
-	}
-	if (argc == 0) {
-		free(cmd_dup);
-		return 0;
-	}
-	context->argc = argc;
-	context->argv = argv;
-	switch(context->shell_type)
-	{
-		case TOP_SHELL :
-		{
-			for (inc = 0; shell_list[inc].name != NULL; inc++) {
-				if (strcasecmp(argv[0], shell_list[inc].name) == 0) {
-					good = TRUE;
-					if((shell_list[inc].func(context)) < 0) { return -1; }
-				}
-			}
-			if(!good) {
-				fprintf(stderr, _("%s: bad option - %s, available commands are:\n"),
-					PACKAGE, argv[0]);
-				for (inc = 0; shell_list[inc].name != NULL; inc++) {
-					fprintf (stderr, "%s ", shell_list[inc].name);
-				}
-				fprintf (stderr, _("\nUse help for more information.\n\n"));
-			}
-			break;
-		}
-		case EDIT_SHELL :
-		{
-			for (inc = 0; edit_list[inc].name != NULL; inc++) {
-				if(strcasecmp(argv[0], edit_list[inc].name) == 0) {
-					good = TRUE;
-					if((edit_list[inc].func(context)) < 0) { return -1; }
-				}
-			}
-			if(!good) {
-				fprintf(stderr, _("%s: bad option - %s, available commands are:\n"),
-					PACKAGE, argv[0]);
-				for (inc = 0; edit_list[inc].name != NULL; inc++) {
-					fprintf (stderr, "%s ", edit_list[inc].name);
-				}
-				fprintf (stderr, _("\nUse help for more information.\n\n"));
-			}
-			break;
-		}
-		default : { break; }
-	}
-	free(cmd_dup);
-	return 0;
-}
-
 void
-qof_cmd_shell(qof_data *context)
+qof_mod_sql_file (const char *sql_file, qof_main_context *data)
 {
-	QofSession *input_session;
-#ifdef HAVE_LIBREADLINE
-	char *line;
-	char *prompt;
-
-	line = (char *)malloc(256*sizeof(char));
-	prompt = g_strdup_printf("%s> ", PACKAGE);
+	FILE *filehandle;
+#ifndef HAVE_GETLINE
+	char lineptr[1024];
 #else
-	char buf[256];
-
+	char *lineptr;
 #endif
-	context->argc = 0;
-	context->argv = NULL;
-	input_session = context->input_session;
-	if(0 == safe_strcmp(context->exclude, context->database)
-		&&(context->exclude != NULL))
-	{
-		fprintf(stderr, _("%s: Error: Cannot exclude database \"%s\" with option -e\n"
-		"    because option -d is set to the include the same database: \"%s\"\n"
-		"Use the \'-l\' command to see the full list of supported databases.\n"),
-			PACKAGE, context->exclude, context->database);
-		qof_session_end(input_session);
-		return;
-	}
-	if(context->filename) {
-		qof_session_begin(input_session, context->filename, TRUE, TRUE);
-		qof_session_load(input_session, NULL);
-	}
-	else { qof_session_begin(input_session, QOF_STDOUT, TRUE, FALSE); }
-	qof_show_error(input_session, context->filename);
-	context->book = qof_session_get_book(context->input_session);
-	context->shell_type = TOP_SHELL;
-	context->inst_type = NULL;
-	context->instance = NULL;
-	if(!context->shortname) { context->shortname = g_strndup(PACKAGE, 4); }
-	context->argc = 0;
-	fprintf (stdout, _("\nWelcome to the QOF interactive shell ...\n"));
-	fprintf (stdout, _(" Type 'help' for additional information\n\n"));
+	char *buf;
+	size_t n;
+	QofQuery *q;
+	struct stat sbuf;
 
-	for (;;) {
-		fflush(stdout);
-#ifdef HAVE_LIBREADLINE
-		line = readline(prompt);
-		/* user pressed ^d or so */
-		if (line == NULL) {
-			fprintf(stdout, _("\n\nThank you for using %s.\n"), PACKAGE);
-		       	break;
+	data->sql_file = g_strdup(sql_file);
+	n = 0;
+	q = NULL;
+	data->sql_list = NULL;
+	if (stat(sql_file, &sbuf) <0) {
+		fprintf(stderr,"%s: ERROR. Unable to open %s (%s)\n\n",
+			PACKAGE, sql_file, strerror(errno));
+		return;
 		}
-		/* skip blanks */
-		if (*line) { add_history(line); }
-		if(qof_parse_command(line, context) != 0) { break; }
-		free(line);
+	filehandle = fopen(sql_file, "r");
+#ifndef HAVE_GETLINE
+	while (NULL != (fgets(lineptr, sizeof(lineptr), filehandle)))
 #else
-		fprintf (stdout, "%s> ", PACKAGE);
-		if(qof_parse_command(buf, context) != 0) { break; }
-		if (fgets(buf, 256, stdin) == NULL) { break; }
+	lineptr = NULL;
+	while (0 < getline(&lineptr, &n, filehandle))
 #endif
+	{
+		if(!qof_check_sql(lineptr)) { continue; }
+		if(0 == safe_strcmp(lineptr, "\n")) { continue; }
+		buf = g_strdup(lineptr);
+		data->sql_list = g_list_append(data->sql_list, buf);
 	}
-	fprintf(stdout, "\n");
+
+	fclose(filehandle);
 }
-/** @} */
 
-qof_data*
-qof_create(void)
+void
+qof_mod_write (const char *write_file, qof_main_context *data)
 {
-	qof_data *context;
+	FILE *f;
 
-	context = g_new0(qof_data, 1);
-	return context;
+	data->write_file = g_strdup(write_file);
+	f = fopen(data->write_file, "a+");
+	if(f) {fclose(f); }
 }
 
-/** Prints helpful error messages
-
-\todo make sure the file is always available, some
-errors still print (null).
-*/
 void
-qof_show_error(QofSession *session, const char *newfile)
+qof_main_show_error(QofSession *session)
 {
 	QofBackendError io_error;
+	char *newfile;
 	gboolean uh_oh;
 	const char *fmt;
 
 	uh_oh = TRUE;
+	newfile = g_strdup(qof_session_get_file_path(session));
 	io_error = qof_session_get_error(session);
 	switch (io_error)
 	{
@@ -1405,473 +716,6 @@
 		fprintf (stderr, fmt, PACKAGE);
 	break;
 	}
-	fprintf (stderr, "\n");
 }
 
-struct param_ref_list
-{
-	GSList *slist;
-	QofType param_type;
-	int i;
-};
-
-static void
-find_param_cb(QofParam *param, gpointer user_data)
-{
-	struct param_ref_list *b;
-	char *buf;
-
-	b = (struct param_ref_list*)user_data;
-	if((param->param_getfcn == NULL)||(param->param_setfcn == NULL)) { return; }
-	if(0 == safe_strcmp(b->param_type, param->param_type))
-	{
-		b->i++;
-		buf = g_strdup(param->param_name);
-		if(buf != NULL) {
-			b->slist = g_slist_append(b->slist, buf);
-		}
-		return;
-	}
-}
-
-GSList*
-qof_get_param_list(QofIdTypeConst object_type, QofType param_type)
-{
-	GSList *param_list;
-	char *i;
-	struct param_ref_list p;
-
-	param_list = NULL;
-	p.slist = NULL;
-	p.i = 0;
-	g_return_val_if_fail(object_type != NULL, NULL);
-	p.param_type = g_strdup(param_type);
-	qof_class_param_foreach(object_type, find_param_cb, &p);
-	param_list = g_slist_copy(p.slist);
-	i = g_strdup(object_type);
-	return param_list;
-}
-
-void
-qof_data_free(qof_data *data)
-{
-	g_free(data->filename);
-	g_free(data->write_file);
-	g_free(data->sql_file);
-	g_free(data->sql_str);
-	g_free(data->database);
-	g_free(data->shortname);
-}
-
-/** \name SQL handlers
-@{
-*/
-static QofQuery*
-qof_main_run_sql(qof_data *context)
-{
-	QofSqlQuery *q;
-	gchar *sql;
-	QofQuery *qq;
-
-	q = qof_sql_query_new();
-	sql = g_strdup(context->sql_str);
-	qof_sql_query_parse(q, sql);
-	qq = qof_sql_query_get_query(q);
-	return qq;
-}
-
-static void
-qof_main_run_query(QofQuery *qq, qof_data *context)
-{
-	QofBook *book;
-	GList *results;
-
-	g_return_if_fail(qq);
-	results = NULL;
-	book = qof_session_get_book(context->input_session);
-	qof_query_set_book(qq, book);
-	qof_query_set_sort_order(qq, NULL, NULL, NULL);
-	results = qof_query_run (qq);
-#ifdef PRINT_DEBUG
-	qof_query_print(qq);
-#endif
-	if(results == NULL) { return; }
-	if(results != NULL) {
-		qof_entity_copy_list(context->export_session, results);
-	}
-}
-
-/** \brief Assemble the components of the query.
-
-If any SQL statements are found, run
-separately from any -c, -d or -t options.
-
-All queries are additive: Successive queries add
-more entities to the result set but no entity is
-set more than once.
-*/
-static void
-qof_moderate_query(qof_data *context)
-{
-	Timespec min_ts;
-	Timespec max_ts;
-	QofQueryPredData *date_pred_data;
-//	QofQueryPredData *category_pred;
-	QofQuery *q;
-	QofIdTypeConst find;
-//	char *buf;
-	GSList *date_param_list, *category_param_list;
-	GList *f;
-	gboolean all;
-
-	all = TRUE;
-	q = qof_query_create();
-	date_param_list = NULL;
-	category_param_list = NULL;
-	for (f = context->sql_list; f ; f = context->sql_list->next)
-	{
-		context->sql_str = g_strdup(f->data);
-		q = qof_main_run_sql(context);
-		qof_main_run_query(q, context);
-		if(q) { qof_query_clear(q); }
-		g_free(context->sql_str);
-		all = FALSE;
-	}
-	if(0 < g_list_length(context->sql_list)) {
-		context->sql_str = NULL;
-		g_list_free(context->sql_list);
-		all = FALSE;
-	}
-	if(context->sql_str != NULL) {
-		q = qof_main_run_sql(context);
-		qof_main_run_query(q, context);
-		if(q) { qof_query_clear(q); }
-		all = FALSE;
-	}
-	if((context->database != NULL)&&(qof_class_is_registered(context->database)))
-	{
-		qof_query_search_for(q, context->database);
-		find = qof_query_get_search_for(q);
-		if(context->min_ts.tv_sec > 0) {
-			min_ts = context->min_ts;
-			max_ts = context->max_ts;
-			date_param_list = g_slist_copy(qof_get_param_list(find, QOF_TYPE_DATE));
-			if(!date_param_list) { qof_query_clear(q); return;}
-			date_pred_data = qof_query_date_predicate(QOF_COMPARE_GTE, QOF_DATE_MATCH_NORMAL, min_ts);
-			qof_query_add_term(q, date_param_list, date_pred_data, QOF_QUERY_AND);
-			date_param_list = qof_get_param_list(qof_query_get_search_for(q), QOF_TYPE_DATE);
-			date_pred_data = qof_query_date_predicate(QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, max_ts);
-			qof_query_add_term(q, date_param_list, date_pred_data, QOF_QUERY_AND);
-		}
-		qof_main_run_query(q, context);
-		if(q) { qof_query_clear(q); }
-		all = FALSE;
-	}
-	if(all == TRUE)
-	{
-		while(context->all_objects)
-		{
-			q = qof_query_create_for(context->all_objects->data);
-		find = qof_query_get_search_for(q);
-			if(context->min_ts.tv_sec > 0) {
-				min_ts = context->min_ts;
-				max_ts = context->max_ts;
-				date_param_list = g_slist_copy(qof_get_param_list(find, QOF_TYPE_DATE));
-				if(!date_param_list) {
-					if(q) { qof_query_clear(q); }
-					context->all_objects = context->all_objects->next;
-					continue;
-				}
-				date_pred_data = qof_query_date_predicate(QOF_COMPARE_GTE, QOF_DATE_MATCH_NORMAL, min_ts);
-				qof_query_add_term(q, date_param_list, date_pred_data, QOF_QUERY_AND);
-				date_param_list = qof_get_param_list(qof_query_get_search_for(q), QOF_TYPE_DATE);
-				date_pred_data = qof_query_date_predicate(QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, max_ts);
-				qof_query_add_term(q, date_param_list, date_pred_data, QOF_QUERY_AND);
-			}
-			qof_main_run_query(q, context);
-			if(q) { qof_query_clear(q); }
-			context->all_objects = context->all_objects->next;
-		}
-	}
-}
-/** @} */
-
-static void
-print_config_cb (QofBackendOption *option, gpointer data)
-{
-	fprintf (stdout, "option name=%s\n", option->option_name);
-	fprintf (stdout, "option desc=%s\n", option->description);
-	fprintf (stdout, "option tip =%s\n", option->tooltip);
-	switch(option->type) {
-		case KVP_TYPE_GINT64   : {
-			fprintf (stdout, "option value=%" G_GINT64_FORMAT,
-				*(gint64*)option->value);
-			fprintf (stdout, "\noption type=%s\n", QOF_TYPE_INT64);
-			break;
-		}
-		case KVP_TYPE_DOUBLE   : {
-			break; 
-		}
-		case KVP_TYPE_NUMERIC  : {
-			break; 
-		}
-		case KVP_TYPE_STRING   : {
-			fprintf (stdout, "option value=%s\n", (char*)option->value);
-			fprintf (stdout, "option type=%s\n", QOF_TYPE_STRING);
-			break;
-		}
-		case KVP_TYPE_GUID     : { break; } /* unsupported */
-		case KVP_TYPE_TIMESPEC : {
-			break;
-		}
-		case KVP_TYPE_BINARY   : { break; } /* unsupported */
-		case KVP_TYPE_GLIST    : { break; } /* unsupported */
-		case KVP_TYPE_FRAME    : { break; } /* unsupported */
-	}		
-}
-
-void
-qof_cmd_offline (qof_data *context)
-{
-	QofSession *input_session, *export_session;
-	gchar current_work[PATH_MAX];
-	gchar *temp;
-	QofBackend *be;
-	KvpFrame *backend_config;
-
-	backend_config = NULL;
-	input_session = context->input_session;
-	if(0 == safe_strcmp(context->exclude, context->database)
-		&&(context->exclude != NULL))
-	{
-		fprintf(stderr, _("%s: Error: Cannot exclude database \"%s\" with option -e\n"
-		"    because option -d is set to the include the same database: \"%s\"\n"
-		"Use the \'-l\' command to see the full list of supported databases.\n"),
-			PACKAGE, context->exclude, context->database);
-		qof_session_end(input_session);
-		return;
-	}
-	qof_session_begin(input_session, context->filename, FALSE, TRUE);
-	qof_session_load(input_session, NULL);
-	if(ERR_BACKEND_LOCKED == qof_session_get_error(input_session))
-	{
-		/** \todo ask the user if it is OK to ignore the lock. */
-		qof_session_begin(input_session, context->filename, TRUE, FALSE);
-		qof_session_load(input_session, NULL);
-	}
-	context->book = qof_session_get_book(input_session);
-	be = qof_book_get_backend(context->book);
-	backend_config = qof_backend_get_config(be);
-	PINFO (" trying to get backend config");
-	if(backend_config) 
-	{
-		qof_backend_option_foreach(backend_config, 
-			print_config_cb, context);
-	}
-	else { PINFO (" failed"); }
-	export_session = qof_session_new();
-	context->export_session = export_session;
-	if(context->write_file != NULL) {
-		if(*context->write_file != '/')
-		{
-			getcwd(current_work, PATH_MAX);
-			temp = g_strconcat(current_work, "/", context->write_file, NULL);
-			context->write_file = temp;
-		}
-		qof_session_begin(export_session, context->write_file, FALSE, TRUE);
-	}
-	else { qof_session_begin(export_session, QOF_STDOUT, TRUE, FALSE); }
-	qof_session_set_current_session(input_session);
-	qof_moderate_query(context);
-	qof_session_save(export_session, NULL);
-	qof_show_error(export_session, context->write_file);
-	qof_show_error(input_session, context->filename);
-	qof_session_end(input_session);
-	qof_session_end(export_session);
-}
-
-
-void
-qof_cmd_list (void)
-{
-	fprintf(stdout, _("\n%s currently supports these database names:\n"
-	"You can use the names with %s -d\n"
-	"and in SQL queries (as the table name) with %s -s|f\n"
-	"Descriptions are shown only for readability.\n\n"
-	"Name                    Description\n\n"
-	)
-	, PACKAGE, PACKAGE, PACKAGE);
-	qof_object_foreach_type(qof_list_cb, NULL);
-	fprintf(stdout, _("\nUse '-d <database> --explain' to see the list of fields within\n"
-	"any supported database.\n"));
-	fprintf(stdout, _("\nThank you for using %s\n\n"), PACKAGE);
-}
-
-void
-qof_select_all(QofObject *obj, gpointer data)
-{
-	qof_data *context;
-	char* type;
-
-	context = (qof_data*)data;
-	type = g_strdup(obj->e_type);
-	if(!qof_class_is_registered(type)) { return; }
-	context->all_objects = g_list_prepend(context->all_objects, type);
-}
-
-void
-qof_cmd_explain (gpointer user_data)
-{
-	qof_data *context;
-
-	context = (qof_data*)user_data;
-	if(context->error) { return; }
-	fprintf(stdout, _("\nParameters of the %s database:\n\n"), context->database);
-	qof_class_param_foreach(context->database, qof_explain_cb, NULL);
-	fprintf(stdout, _("\nThank you for using %s\n\n"), PACKAGE);
-}
-
-void
-qof_mod_database (const char *database, qof_data *data)
-{
-	if(qof_class_is_registered(database)) {
-		data->database = g_strdup(database);
-	}
-}
-
-void
-qof_mod_timespec (const char *date_time, qof_data *data)
-{
-	gchar *temp;
-	int year, month, day;
-	gboolean takemonth, takeyear, scanned;
-	char *first_field, *second_field, *third_field;
-	static char *delims = ".,-+/\\() ";
-
-	takemonth = takeyear = scanned = FALSE;
-	day = month = year = 0;
-	second_field = "";
-	third_field = "";
-	temp = g_strdup(date_time);
-	qof_date_format_set(QOF_DATE_FORMAT_UTC);
-	scanned = qof_scan_date(temp, &day, &month, &year);
-	if(scanned == FALSE)
-	{
-		first_field = strtok (temp, delims);
-		if (first_field)
-		{
-			second_field = strtok (NULL, delims);
-			if (second_field)
-			{
-				third_field = strtok (NULL, delims);
-			}
-		}
-		if (third_field && second_field)
-		{
-			year = atoi(first_field);
-			month = atoi(second_field);
-			day = atoi(third_field);
-		} else if (second_field)
-		{
-			year = atoi(first_field);
-			month = atoi(second_field);
-			takemonth = TRUE;
-		} else if (first_field)
-		{
-			year = atoi(first_field);
-			takeyear = TRUE;
-		}
-	}
-	if(takemonth) { day = 1; }
-	if(takeyear)  { day = 1; month = 1; }
-	data->min_ts = gnc_dmy2timespec(day, month, year);
-	if(takemonth) { day = gnc_date_my_last_mday(month, year); }
-	if(takeyear)  {
-		month = 12;
-		day = gnc_date_my_last_mday(month, year);
-	}
-	data->max_ts = gnc_dmy2timespec_end(day, month, year);
-}
-
-void
-qof_mod_exclude (const char *exclude, qof_data *data)
-{
-	if(qof_class_is_registered(exclude)) {
-		data->exclude = g_strdup(exclude);
-	}
-}
-
-void
-qof_mod_sql (const char *sql_query, qof_data *data)
-{
-	if(!qof_check_sql(sql_query)) { return; }
-	data->sql_str = g_strdup(sql_query);
-}
-
-void
-qof_mod_sql_file (const char *sql_file, qof_data *data)
-{
-	FILE *filehandle;
-#ifndef HAVE_GETLINE
-	char lineptr[1024];
-#else
-	char *lineptr;
-#endif
-	char *buf;
-	size_t n;
-	QofQuery *q;
-	struct stat sbuf;
-
-	data->sql_file = g_strdup(sql_file);
-	n = 0;
-	q = NULL;
-	data->sql_list = NULL;
-	if (stat(sql_file, &sbuf) <0) {
-		fprintf(stderr,"%s: ERROR. Unable to open %s (%s)\n\n",
-			PACKAGE, sql_file, strerror(errno));
-		return;
-		}
-	filehandle = fopen(sql_file, "r");
-#ifndef HAVE_GETLINE
-	while (NULL != (fgets(lineptr, sizeof(lineptr), filehandle)))
-#else
-	lineptr = NULL;
-	while (0 < getline(&lineptr, &n, filehandle))
-#endif
-	{
-		if(!qof_check_sql(lineptr)) { continue; }
-		if(0 == safe_strcmp(lineptr, "\n")) { continue; }
-		buf = g_strdup(lineptr);
-		data->sql_list = g_list_append(data->sql_list, buf);
-	}
-
-	fclose(filehandle);
-}
-
-void
-qof_mod_write (const char *write_file, qof_data *data)
-{
-	data->write_file = g_strdup(write_file);
-}
-
-void extensions_init(void)
-{
-	backend_extensions = g_hash_table_new(g_str_hash, g_str_equal);
-}
-
-void qof_backend_extension_add(char *IDstring, gpointer data)
-{
-	g_hash_table_insert(backend_extensions, IDstring, data);
-}
-
-gpointer qof_backend_extension(const char* IDstring)
-{
-	gpointer func;
-
-	func = g_hash_table_lookup(backend_extensions, IDstring);
-	if(func) { return func; }
-	return NULL;
-}
-
-/** @} */
-/** @} */
+/*==================== END OF FILE ======================*/

Modified: gnucash/branches/cashutil/cashutil/src/qof-main.h
===================================================================
--- gnucash/branches/cashutil/cashutil/src/qof-main.h	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/src/qof-main.h	2005-11-28 15:45:33 UTC (rev 12059)
@@ -1,11 +1,13 @@
 /***************************************************************************
  *            qof-main.h
  *
+ *  This is an auto-generated file. Patches are available from
+ *  http://qof-gen.sourceforge.net/
+ *
  *  Thu Jan 13 12:15:41 2005
  *  Copyright  2005  Neil Williams
  *  linux at codehelp.co.uk
  ****************************************************************************/
-
 /*
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -19,34 +21,22 @@
  *
  *  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.
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
-/** @addtogroup QOFCLI Query Object Framework Command Line Interface and shell.
+/** @addtogroup QOFCLI Query Object Framework Command Line Interface.
 
-The CLI uses a top level shell and two sub shells. The top level shell provides
-general interactivity with object types, entire books and SQL support.
+QOF provides an outline CLI that is easily patched
+from the qof-generator project to make it easier
+to keep various QOF projects updated.
 
-The select sub-shell identifies a single instance of an object. e.g. the print 
-command in the top shell uses the select subshell to locate the instance to be
-printed and the delete command, the instance to be deleted. These commands then
-return to the top shell.
+This CLI is easily extended to support your own functions
+and options and includes macros to help you keep up to date
+with changes in main QOF options. It is recommended that you
+do not edit this file, instead please feed patches back to the
+QOF-devel mailing list at
+http://lists.sourceforge.net/mailman/listinfo/qof-devel
+so that other projects can be updated.
 
-The edit sub-shell allows data to be set in a selected instance. The edit command
-uses a select sub-shell to identify the instance then changes to an edit sub-shell
-to handle setting the individual parameters of that instance. Before returning to the
-top shell, the edited data can be saved to the instance using 'commit' or the edit can
-be aborted using 'quit'. 
-
-The add command creates a new instance and passes that instance, already selected,
-to the edit sub-shell for data entry.
-
-\note CashUtil relies on installed versions of the QOF, libcashobjects and
-libgnc-backend-file libraries. If you change any source files for these libraries,
-ensure you run 'make install' rather than just 'make' or your changes will have no
-effect. There is no support for loading local or 'test' versions of the libraries.
-You can usually run cashutil against a freshly installed set of QOF libraries without
-recompiling cashutil, depending on the level of changes in QOF.
-
 @{
 */
 /** @file qof-main.h
@@ -54,13 +44,12 @@
   @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
 */
 
-#define _GNU_SOURCE
-#include "config.h"
 #ifndef _QOF_MAIN_H
 #define _QOF_MAIN_H
+#include <qof.h>
+#include <stdlib.h>
+#include <time.h>
 #include <popt.h>
-#include "qof.h"
-#include "qofundo.h"
 
 #if defined(HAVE_GETTEXT)             /* HAVE_GETTEXT */
 
@@ -95,27 +84,15 @@
 #undef  N_
 #define N_(String) (String)
 
-/** gnc file backend library name */
-#define GNC_LIB_NAME "libgnc-backend-file.la"
-/** init_fcn for gnc file backend library. */
-#define GNC_LIB_INIT "gnc_provider_init"
+/** \brief List of all parameters for this object of one QOF type. 
 
-#define CU_MOD_ENGINE "cashutil-engine"
-
-gpointer qof_backend_extension(const char* IDstring);
-
-/** \name Control functions.
-@{
-*/
-/** \brief List of all parameters for this object of one type.
-
 Return a GSList of all parameters of this object that are a
 particular QOF type, QOF_TYPE_STRING, QOF_TYPE_BOOLEAN etc.
 
 The returned GSList should be freed by the caller.
 
 \note The return list is a singly linked list - GSList -
-\b not the doubly-linked list - GList - returned by
+\b not the doubly-linked list - GList - returned by 
 ::qof_class_get_referenceList.
 
 \param object_type  object->e_type for the relevant object.
@@ -124,17 +101,47 @@
 \return GSList of all matching parameters or NULL if none exist.
 */
 GSList*
-qof_get_param_list(QofIdTypeConst object_type, QofType param_type);
+qof_main_get_param_list(QofIdTypeConst object_type, QofType param_type);
 
-#define QOF_DATE_STRING_LENGTH  31 /**< Inherited from QSF */
-#define QOF_UTC_DATE_FORMAT     "%Y-%m-%dT%H:%M:%SZ" /**< Inherited from QSF */
+/** Maximum length of the UTC timestamp used by QSF
 
-/**  \brief The SQL commands supported by QOF
+QOF_UTC_DATE_FORMAT   "%Y-%m-%dT%H:%M:%SZ"
+*/
+#define QOF_DATE_STRING_LENGTH  MAX_DATE_LENGTH
 
+/** Debug module for qof-main */
+#define QOF_MAIN_CLI  "QOF-mod-command-line"
+
+/** \brief Category name.
+
+The name of the parameter that holds the category of the entity.
+
+Many CLI data sources categorise data by user-editable category
+strings. If your program does not, simply implement a modified
+QOF_CLI_OPTIONS in your code without the category option:
+\verbatim
+{"category", 'c', POPT_ARG_STRING, &category, qof_op_category,
+_("Shorthand to only query objects that are set to the specified category."),
+"string"},
+\endverbatim
+*/
+#define CATEGORY_NAME "category"
+
+/** backend configuration index string for QSF
+
+The identifier for the configuration option within
+QSF supported by the CLI. Matches the
+QofBackendOption->option_name in the KvpFrame
+holding the options.
+*/
+#define QSF_COMPRESS "compression_level"
+
+/** The SQL commands supported by QOF
+
 A regular expression used to exclude unsupported commands
 from SQL files. Anything that does \b not match the expression
-will be silently ignored by cashutil. This allows genuine
-SQL dump files to be parsed by cashutil without errors.
+will be silently ignored. This allows genuine
+SQL dump files to be parsed without errors.
 
  A QOF object is similar to a definition of a SQL table.\n
  A QOF entity is similar to an instance of a SQL record.\n
@@ -155,124 +162,49 @@
 */
 #define QOF_SQL_SUPPORTED  "^SELECT|INSERT"
 
-/** Indent and pad the shell output nicely.*/
-#define QOF_SHELL_FORMAT "    %-30s%s"
-
-/** \brief Common QOF CLI options
-
- * These are definitions for popt support in the CLI. Every program's
- * popt table should start with QOF_CLI_OPTIONS to insert
- * the standard options into it. Also enables autohelp.
- */
-#define QOF_CLI_OPTIONS POPT_AUTOHELP \
-	{"list", 'l', POPT_ARG_NONE, NULL, qof_op_list, \
-	 _("List all databases supported by the current QOF framework and exit."), \
-	 NULL}, \
-	{"explain", 0, POPT_ARG_NONE, NULL, qof_op_explain, \
-	 _("List the fields within the specified database and exit, requires -d."), \
-	 NULL}, \
-	{"date", 't', POPT_ARG_STRING, &date_time, qof_op_timespec, \
-	 _("Shorthand to only query objects that contain the specified date."), \
-	 "string"}, \
-	{"database", 'd', POPT_ARG_STRING, &database, qof_op_database, \
-	 _("Shorthand to only query objects within a specific supported database. "), \
-	 "string"}, \
-	{"exclude", 'e', POPT_ARG_STRING, &exclude, qof_op_exclude, \
-	 _("Shorthand to exclude a supported database from the query."), \
-	 "string"}, \
-	{"sql", 's', POPT_ARG_STRING, &sql_query, qof_op_sql, \
-	 _("Specify a SQL query on the command line."), "string"}, \
-	{"sql-file", 'f', POPT_ARG_STRING, &sql_file, qof_op_sql_file, \
-	 _("Specify one or more SQL queries contained in a file."), \
-	 "filename"}, \
-	{"write", 'w', POPT_ARG_STRING, &write_file, qof_op_write, \
-	 _("Write the results of any query to the file"), "filename"}, \
-	{"compress", 0, POPT_ARG_INT, &gz_level, qof_op_compress, \
-	 _("Compress output files, 0 for none, 9 for maximum"), "integer"}, \
-	{"debug", 0, POPT_ARG_NONE, NULL, qof_op_debug, \
-	 _("Print debugging information to a temporary file."), NULL}, \
-	{"shell", 0, POPT_ARG_NONE, NULL, qof_op_shell, \
-	 _("Enter the QOF interactive shell"), NULL}, \
-	{"version", 0, POPT_ARG_NONE, NULL, qof_op_vers, \
-	 _("Display version information"), NULL},
-
 /** \brief Output error messages from QOF
 
 QOF will set errors in the QofSession. The
 application determines how to output those
-messages and for CLI programw, this will be to
-stderr. Some of these error messages are not used in
-all CLI programs.
+messages and for the CLI, this will be to
+stderr. Not all these messages are implemented
+in any one QOF CLI.
+
+\param session Any current session.
 */
-void qof_show_error(QofSession *session, const char *file);
+void qof_main_show_error(QofSession *session);
 
-/** \brief Handle the type of each subshell. */
-typedef enum {
-	NO_SHELL,
-	TOP_SHELL,     /**< the first, top level shell. */
-	EDIT_SHELL,    /**< Edit the selected instance */
-}qof_subshell;
+/** \brief The qof-main context struct.
 
-typedef enum {
-	NO_OP,
-	PRINT_MODE,
-	DELETE_MODE,
-	EDIT_MODE,
-}qof_cli_mode;
-
-/** \brief The QOF CLI context struct */
-typedef struct qofdata_s {
-	gchar *filename;            /**< Input filename containing QSF XML, if any.*/
-	gchar *write_file;          /**< Export filename, if any.*/
-	gchar *sql_file;            /**< SQL file, if any. */
-	gchar *sql_str;             /**< The current SQL, overwritten each iteration if using a file.*/
-	gchar *database;            /**< The database to include with -d. */
-	gchar *exclude;             /**< The database to exclude with -e. */
-	gchar *shortname;           /**< A shortname for this program if truncation to first 4 characters is not suitable. */
-	Timespec min_ts;            /**< Matches objects above minimum time_t value. */
-	Timespec max_ts;            /**< Matches objects below maximum time_t value. */
-	QofSession *input_session;  /**< The input session. */
-	QofSession *export_session; /**< The query results session, for STDOUT or -w. */
+Intended as a core type for QOF-based CLI programs, wrap
+your own context struct around qof_main_context
+*/
+typedef struct qof_main_s {
+	gchar *filename;		    /**< Input filename containing QSF XML data, if any.*/
+	gchar *write_file; 		    /**< Export filename, if any.*/
+	gchar *input_file;		    /**< File containing data to upload, if any. */
+	gchar *sql_file;		    /**< SQL file, if any. */
+	gchar *sql_str;			    /**< The current SQL, overwritten each iteration
+								if using a file.*/
+	gchar *database;		    /**< The database to include with -d. */
+	gchar *exclude;			    /**< The database to exclude with -e. */
+	gchar *category;		    /**< The category to include with -c.*/
+	Timespec min_ts;		    /**< Holds the converted -t field - minimum.
+								Matches objects above min. */
+	Timespec max_ts;		    /**< Holds the converted -t field - maximum.
+								Matches objects below max. */
+	QofSession *input_session;  /**< The input session - hotsync or offline storage. */
+	QofSession *export_session; /**< The query results session, for STDOUT or -w,
+								both as QSF XML. */
 	gboolean error;             /**< general error, abort. */
-	GList *all_objects;         /**< List of all supported databases. */
+    QofQuery *query;            /**< The current QofQuery, converted from QofSqlQuery */
 	GList *sql_list;            /**< List of sql commands from a file. */
-	int argc;                   /**< Shell copy of argc */
-	char **argv;                /**< Shell copy of commands */
-	QofBook *book;              /**< the current book for the shell function. */
-	qof_subshell shell_type;    /**< the type of subshell, top or edit */
-	qof_cli_mode cli_mode;          /**< current operation mode. */
-	QofIdTypeConst inst_type;   /**< The current registered QofObject type. */
-	QofInstance *instance;      /**< The currently selected instance. */
-	gint counter;
-	GHashTable *select_table;
-	gint gz_level;
-}qof_data;
+	gint gz_level;              /**< Use compression (>0 <=9) or not (0)*/
+}qof_main_context;
 
-/** \brief Register all QOF objects.
+/** Free qof_main_context values when work is done. */
+void qof_main_free (qof_main_context *context);
 
-If new objects are added, call the register func()
-here.
-
-qof_init must be called by any program wanting to
-use the QOF framework with GnuCash objects.
-
-\return A usable qof_data* context.
-*/
-void  qof_init (void);
-
-/** \brief initialise the QOF CLI context. 
-
-All QOF CLI programs must create a context.
-*/
-qof_data* qof_create(void);
-
-/** \brief Shutdown the QOF framework
-*/
-void qof_close(void);
-
-/* \brief Clear up the qof_data context */
-void qof_data_free(qof_data *data);
-
 /** \brief Check that the SQL command is supported.*/
 gboolean qof_check_sql(const char *sql);
 
@@ -292,75 +224,34 @@
 
 List supported databases command.
 */
+/** \enum qof_op_type::qof_op_category
 
-/** \brief command line command options.*/
-typedef enum {
-	qof_op_noop = 0,
-	qof_op_input,
-	qof_op_list,
-	qof_op_shell,
-	qof_op_vers,
-	qof_op_database,
-	qof_op_timespec,
-	qof_op_exclude,
-	qof_op_sql,
-	qof_op_sql_file,
-	qof_op_write,
-	qof_op_explain,
-	qof_op_compress,
-	qof_op_debug
-}qof_op_type;
-
-/** \brief Build a list of all available objects */
-void qof_select_all(QofObject *obj, gpointer data);
-
-/** @} */
-/** @name Command handlers.
-@{
+Shorthand category option.
 */
 
-/** \brief load the QOF interactive shell
+/** query the QSF XML data */
+void qof_cmd_xmlfile (qof_main_context *context);
 
-Where available, uses READLINE to store a history of
-previous shell commands.
-*/
-void qof_cmd_shell(qof_data *context);
-
-/** \brief List each parameter for the selected object. */
-void qof_cmd_explain (gpointer user_data);
-
-/** \brief List the supported databases.
-
-Uses a callback to ::qof_class_is_registered.
-*/
-void qof_cmd_list (void);
-
-/** \brief query a QSF XML file
-
-Query the QSF XML in <filename>.
-*/
-void qof_cmd_offline (qof_data *context);
-
 /** \brief Lists all databases supported by the current QOF framework.
 
 Prints the name and description for each object type
 registered with this instance of QOF. No options are used.
 */
 void qof_cmd_list (void);
-/** @} */
 
-/** @name Command modulators.
-@{
+/** \brief Shorthand to only query objects that are set to the specified category.
+
+Modifies the QOF query to only query objects that are set to
+\a category.
 */
+void qof_mod_category (const char *category, qof_main_context *data);
 
 /** \brief Shorthand to only query objects within one specific supported database.
 
 Used to only query objects within the specified
-database. In a hotsync, all other supported databases are skipped
-and data is only read from the named database. Without a HotSync (using
-offline storage), only objects of this type are queried.
+database.
 */
-void qof_mod_database (const char *database, qof_data *data);
+void qof_mod_database (const char *database, qof_main_context *data);
 
 /** \brief Shorthand to only query objects that contain the specified date.
 
@@ -375,6 +266,9 @@
 For more precise time matches or to set a defined period that doesn't follow
 whole calendar months, (e.g. the UK financial year) use a SQL statement:
 
+"SELECT * from pilot_datebook where start_time > '2004-04-06T00:00Z'\n
+and end_time < '2005-04-05T23:59:59Z';"
+
 Partial matches are allowed, so YY-MM matches
 any object where a date is within the specified month and year,
 YY matches any object where a date is within the specified year.
@@ -382,15 +276,13 @@
 The query range starts at midnight on the first day of the range
 and ends at 1 second to midnight on the last day of the range.
 */
-void qof_mod_timespec (const char *date_time, qof_data *data);
+void qof_mod_timespec (const char *date_time, qof_main_context *data);
 
 /** \brief Shorthand to exclude a supported database from the query.
 
 Excludes the (single) specified database from the query.
-During a hotsync, data in that database is not read from the Palm.
-When working offline, the objects of that type are not queried.
 */
-void qof_mod_exclude (const char *exclude, qof_data *data);
+void qof_mod_exclude (const char *exclude, qof_main_context *data);
 
 /** \brief Specify a SQL query on the command line.
 
@@ -414,12 +306,8 @@
 a path '/some/kvp' and the value stored at that path is floating-point and that float
 value is less than 10.0.
 
- at param sql_query Examples:
-
-"select * from gncAddress"
-
 */
-void qof_mod_sql (const char *sql_query, qof_data *data);
+void qof_mod_sql (const char *sql_query, qof_main_context *data);
 
 /** \brief Specify one or more SQL queries contained in a file.
 
@@ -436,23 +324,127 @@
 http://www.gnu.org/software/libc/manual/html_node/Line-Input.html
 
 */
-void qof_mod_sql_file (const char *sql_file, qof_data *data);
+void qof_mod_sql_file (const char *sql_file, qof_main_context *data);
 
 /** \brief Write the results of any query to the file
 
-Sets the \a filename of the file to be written out using 
-the QSF XML QofBackend.
+\a filename of the file to be written out using the QSF XML
+QofBackend.
 
 */
-void qof_mod_write (const char *write_file, qof_data *data);
+void qof_mod_write (const char *write_file, qof_main_context *data);
 
-void extensions_init(void);
+/** Pass the requested compression to QSF
 
-void qof_backend_extension_add(char *IDstring, gpointer data);
+ at param gz_level Integer between 0 and 9, 9 highest compression, 0 for none.
+*/
+void
+qof_mod_compression (gint gz_level, qof_main_context *context);
 
-gpointer qof_backend_extension(const char* IDstring);
+/** \brief Assemble the components of the query.
 
+If any SQL statements are found, run
+separately from any -c, -d or -t options.
 
+All queries are additive: Successive queries add
+more entities to the result set but no entity is
+set more than once.
+*/
+void
+qof_main_moderate_query(qof_main_context *context);
+
+/** Print a list of available parameters for a database.
+
+Used with qof_mod_database to print a list of
+QofParam for the QofObject set in context->database.
+*/
+void
+qof_cmd_explain (qof_main_context *context);
+
+/** \brief Common QOF CLI options
+ *
+ * These are definitions for popt support in the CLI. Every program's
+ * popt table should start with QOF_CLI_OPTIONS or a replacement to insert
+ * the standard options into it. Also enables autohelp. End your
+ * popt option list with POPT_TABLEEND. If you want to remove any
+ * of these options, simply copy QOF_CLI_OPTIONS into a macro of
+ * your own and remove the options you do not need.
+*/
+#define QOF_CLI_OPTIONS POPT_AUTOHELP \
+	{"list", 'l', POPT_ARG_NONE, NULL, qof_op_list, \
+	 _("List all databases supported by the current QOF framework and exit."), \
+	 NULL}, \
+	{"explain", 0, POPT_ARG_NONE, NULL, qof_op_explain, \
+	 _("List the fields within the specified database and exit, requires -d."), \
+	 NULL}, \
+    {"xml-file", 'x', POPT_ARG_STRING, &filename, qof_op_offline, \
+     _("Query the QSF XML data in <filename>"), \
+     "filename"}, \
+	{"date", 't', POPT_ARG_STRING, &date_time, qof_op_timespec, \
+	 _("Shorthand to only query objects that contain the specified date."), \
+	 "string"}, \
+	{"database", 'd', POPT_ARG_STRING, &database, qof_op_database, \
+	 _("Shorthand to only query objects within a specific supported database. "), \
+	 "string"}, \
+	{"exclude", 'e', POPT_ARG_STRING, &exclude, qof_op_exclude, \
+	 _("Shorthand to exclude a supported database from the query."), \
+	 "string"}, \
+	{"sql", 's', POPT_ARG_STRING, &sql_query, qof_op_sql, \
+	 _("Specify a SQL query on the command line."), "string"}, \
+	{"sql-file", 'f', POPT_ARG_STRING, &sql_file, qof_op_sql_file, \
+	 _("Specify one or more SQL queries contained in a file."), \
+	 "filename"}, \
+	{"write", 'w', POPT_ARG_STRING, &write_file, qof_op_write, \
+	 _("Write the results of any query to the file"), "filename"}, \
+	{"compress", 0, POPT_ARG_INT, &gz_level, qof_op_compress, \
+	 _("Compress output files, 0 for none, 9 for maximum"), "integer"}, \
+	{"debug", 0, POPT_ARG_NONE, NULL, qof_op_debug, \
+	 _("Print debugging information to a temporary file."), NULL}, \
+	{"version", 0, POPT_ARG_NONE, NULL, qof_op_vers, \
+	 _("Display version information"), NULL}, \
+    {"category", 'c', POPT_ARG_STRING, &category, qof_op_category, \
+     _("Shorthand to only query objects that are set to the specified category."), \
+     "string"},
+
+
+/** use only if you have no extended options, otherwise use as a template. */
+#define QOF_MAIN_OP \
+ 	_(qof_op_noop, = 0) \
+	_(qof_op_list,)     \
+    _(qof_op_xmlfile,)  \
+	_(qof_op_category,) \
+	_(qof_op_database,) \
+	_(qof_op_timespec,) \
+	_(qof_op_exclude,)  \
+	_(qof_op_sql,)      \
+	_(qof_op_sql_file,) \
+	_(qof_op_write, )   \
+	_(qof_op_explain,)  \
+	_(qof_op_vers,)     \
+	_(qof_op_compress,) \
+	_(qof_op_debug,)
+
+/** Define the variables for the standard QOF CLI options.
+
+If you remove any QOF CLI options, ensure you also remove
+the option variable and it's initialiser.
+*/
+#define QOF_OP_VARS \
+       const char *exclude,  *date_time,  *category,  *database; \
+       const char *sql_file, *write_file, *sql_query, *filename;
+
+/** initialise the standard QOF CLI option variables.
+
+A simple convenience macro.
+*/
+#define QOF_OP_INIT    \
+       exclude = NULL;    \
+       category = NULL;   \
+       database = NULL;   \
+       sql_file = NULL;   \
+       write_file = NULL; \
+       sql_query = NULL;  \
+       filename = NULL;
 /** @} */
 /** @} */
 

Added: gnucash/branches/cashutil/cashutil/src/qof-shell.c
===================================================================
--- gnucash/branches/cashutil/cashutil/src/qof-shell.c	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/src/qof-shell.c	2005-11-28 15:45:33 UTC (rev 12059)
@@ -0,0 +1,1143 @@
+/***************************************************************************
+ *            qof-shell.c
+ *
+ *  Mon Nov 28 13:02:40 2005
+ *  Copyright  2005  Neil Williams
+ *  linux at codehelp.co.uk
+ ****************************************************************************/
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  */
+
+#include <unistd.h>
+#include <regex.h>
+#include "config.h"
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+#include "qof-shell.h"
+#include "qofundo-p.h"
+
+/* unused
+static QofLogModule log_module = QOF_SHELL_CLI;
+*/
+
+void
+qof_shell_free (qof_shell_context *context)
+{
+	qof_main_free(&context->qof);
+	context->shortname = NULL;
+	if(context->select_table)
+	{
+		g_hash_table_destroy(context->select_table);
+	}
+}
+
+/** standardise tab output to help output line up nicely. */
+#define QOF_TAB "    "
+
+/** the maximum number of entities to offer for a single selection.
+
+\todo Improve the maximum to allow scrolling / next page.
+*/
+#define CLI_MAX_SELECT 20
+
+/** Relate the command to the function. */
+typedef int (*cmd_fcn) (qof_shell_context *context);
+/** \todo Reorganise the C into multiple files. */
+static int qof_parse_command (const char *cmd, qof_shell_context *context);
+
+/** \brief write [filename]
+
+write the current book to file.
+	If no filename is provided, save to the original file
+	(to STDOUT if STDIN used). Analagous to Save / Save As...
+
+\todo fix problems with writing to an alternative file.
+*/
+static int write_fcn(qof_shell_context *context)
+{
+	QofSession *save_as;
+	gchar current_work[PATH_MAX];
+	gchar *temp;
+
+	if(context->argv[1])
+	{
+		save_as = qof_session_new();
+		if(*context->argv[1] != '/')
+		{
+			getcwd(current_work, PATH_MAX);
+			temp = g_strconcat(current_work, "/", context->argv[1], NULL);
+			context->argv[1] = temp;
+		}
+		qof_session_begin(save_as, context->argv[1], FALSE, TRUE);
+		qof_session_swap_data(save_as, context->qof.input_session);
+		qof_session_save(save_as, NULL);
+		qof_main_show_error(save_as);
+		qof_session_end(save_as);
+		return 0;
+	}
+	if(context->qof.write_file)
+	{
+		qof_session_save(context->qof.export_session, NULL);
+		qof_main_show_error(context->qof.export_session);
+	}
+	else {
+		qof_session_save(context->qof.input_session, NULL);
+		qof_main_show_error(context->qof.input_session);
+	}
+	/* implement in the backend eventually */
+	context->book = qof_session_get_book(context->qof.input_session);
+	if(qof_book_can_undo(context->book)) 
+	{ 
+		qof_book_clear_undo(context->book); 
+	}
+	return 0;
+}
+
+/** Run a sub-shell to either select or edit an instance. */
+static int
+qof_sub_shell (qof_shell_context *context)
+{
+#ifdef HAVE_LIBREADLINE
+	char *line;
+	char *prompt;
+
+	if(!context->inst_type) { return 0; }
+	line = (char*)malloc(256 * sizeof(char));
+	prompt = g_strdup_printf("%s/%s> ", context->shortname, context->inst_type);
+#else
+	char buf[256];
+
+	if(!context->inst_type) { return 0; }
+#endif
+	for (;;) {
+		fflush (stdout);
+#ifdef HAVE_LIBREADLINE
+		line = readline(prompt);
+		if (line == NULL) { break; }
+		if (*line) { add_history(line); }
+		if (qof_parse_command(line, context) < 0) { break; }
+		g_free (line);
+#else
+		fprintf (stdout, "%s/%s> ", context->shortname, context->inst_type);
+		if (qof_parse_command(buf, context) < 0) { break; }
+#endif
+	}
+	fprintf(stdout, "\n");
+	return 0;
+}
+
+/** \brief sql - take a SQL statement.
+
+\todo improve the sql_parser - it is very noisy and
+yet not very helpful.
+*/
+static int sql_fcn(qof_shell_context *context)
+{
+	QofSqlQuery *q;
+	gchar *sql, *temp;
+	gint result;
+	QofQuery *qq;
+	QofBook *book;
+	GList *results;
+
+	q = qof_sql_query_new();
+	sql = g_strdup(context->qof.sql_str);
+	if(!sql) { sql = context->argv[1]; }
+	qof_sql_query_parse(q, sql);
+	qq = qof_sql_query_get_query(q);
+	book = qof_session_get_book(context->qof.input_session);
+	qof_query_set_book(qq, book);
+	qof_query_set_sort_order(qq, NULL, NULL, NULL);
+	results = qof_query_run (qq);
+	if(!results) { return 0; }
+	if(g_list_length(results) == 1) { 
+		context->instance = (QofInstance*)results->data;
+		context->shell_type = EDIT_SHELL;
+		temp = g_strdup_printf("modify-%s", context->inst_type);
+		qof_book_start_operation(context->book, temp);
+		fprintf (stdout, _("Edit the parameter details of the selected instance.\n"));
+		fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
+		if(context->instance) { result = qof_sub_shell(context); }
+		context->shell_type = TOP_SHELL;
+		context->inst_type = NULL;
+		qof_book_end_operation(context->book);
+		return 0;
+	}
+	fprintf (stdout, _("Query returned %d entities\n"), g_list_length(results));
+	return 0;
+}
+
+gboolean
+qof_check_sql(const char *sql)
+{
+	regex_t *r;
+	int reg_exp_check;
+	static char *pattern = QOF_SQL_SUPPORTED;
+//	QofSqlQuery *q;
+	gboolean result;
+
+	result = FALSE;
+	r = g_new(regex_t, 1);
+	reg_exp_check = regcomp(r, pattern,
+		REG_ICASE | REG_NOSUB | REG_EXTENDED);
+	g_return_val_if_fail(reg_exp_check == 0, FALSE);
+	if(0 == regexec(r, sql, 0, NULL, 0)) { result = TRUE; }
+	regfree(r);
+	g_free(r);
+	return result;
+}
+
+/** Provide a simple numerical index for selectable objects.
+
+ at param ent  each entity in turn of the selected type.
+ at param data The qof_data context for the CLI.
+
+Ensure counter is reset to zero between runs.
+*/
+static void
+cli_select_cb (QofEntity *ent, gpointer data)
+{
+	qof_shell_context *context;
+	gchar *temp;
+
+	context = (qof_shell_context*)data;
+	g_return_if_fail(context);
+	context->counter++;
+	if(context->counter >= CLI_MAX_SELECT) { return; }
+	fprintf (stdout, "%d    %s\n", context->counter, 
+		qof_object_printable(context->inst_type, (QofInstance*)ent));
+	temp = g_strdup_printf("%d", context->counter);
+	g_hash_table_insert(context->select_table, temp, ent);
+}
+
+/** select_table helper
+
+The select_table hashtable relies on a conversion from
+the integer counter to a string. This frees the temporary
+conversion string when the hashtable is no longer needed.
+*/
+static void
+cli_free_select (gpointer key, gpointer value, gpointer data)
+{
+	g_free(key);	
+}
+
+/** select an instance
+
+Offers multiple entities for selection. Ensure that
+check_for_single_instance has been run first.
+
+\todo Offer multiple listings of CLI_MAX_SELECT each.
+*/
+static int select_fcn (qof_shell_context *context)
+{
+	gint choice, max;
+	gchar *temp;
+
+	if(!context->inst_type) { return -1; }
+	choice = 0;
+	context->counter = 0;
+	context->instance = NULL;
+	context->select_table = g_hash_table_new(g_str_hash, g_str_equal);
+	qof_object_foreach(context->inst_type, context->book, cli_select_cb, context);
+	max = g_hash_table_size(context->select_table);
+	if(max > CLI_MAX_SELECT) { max = CLI_MAX_SELECT; }
+	/* Translators: %s is an object name, %d the number of objects in the book. */
+	switch (context->cli_mode)
+	{
+		case EDIT_MODE : {
+			fprintf (stdout, _("Choose the %s to edit: (1 - %d): "), 
+				context->inst_type, max);
+			break;
+		}
+		case DELETE_MODE : {
+			fprintf (stdout, _("Choose the %s to DELETE: (1 - %d): "), 
+				context->inst_type, max);
+			break;
+		}
+		case PRINT_MODE : {
+			fprintf (stdout, _("Choose the %s to print: (1 - %d): "), 
+				context->inst_type, max);
+			break;
+		}
+		case NO_OP : { break; }
+	}
+	scanf("%2i", &choice);
+	fprintf (stdout, "\n");
+	while(choice <= 0 || choice > max)
+	{
+		char c;
+		c = getchar();
+		while (c != '\n') { c = getchar(); }
+		fprintf (stdout, _("Only %d objects are available of type '%s'.\n"), 
+			max, context->inst_type);
+		fprintf (stdout, _("Choose the %s to use: (1 - %d): "), context->inst_type, max);
+		scanf("%2i", &choice);
+		fprintf (stdout, "\n");
+	}
+	temp = g_strdup_printf("%d", choice);
+	context->instance = (QofInstance*)g_hash_table_lookup(context->select_table, temp);
+	g_free(temp);
+	g_hash_table_foreach(context->select_table, cli_free_select, NULL);
+	g_hash_table_destroy(context->select_table);
+	return 0;
+}
+
+/** \brief Relate the commands to the functions within the shell. */
+struct shell_cmd
+{
+	const char *name;     /**< Name of the command. */
+	cmd_fcn func;         /**< Name of function to execute the command. */
+};
+
+/** command object param value */
+struct search_helper
+{
+	QofInstance *inst;
+	QofParam *param;  /**< the parameter in argv[2] */
+	const char *term; /**< argv[3] */
+	char *value;      /**< argv[4] */
+};
+
+/** check_for_single_instance */
+struct count_helper
+{
+	gint count;
+	QofInstance *first_inst;
+};
+
+/** \brief Shorthand search routine.
+
+Rather than using a QofQuery, this shorthand version uses a
+string for all parameter types. It will support the existing
+-t --date shorthand versions for compatibility with the non-interactive
+command set by actually using an underlying QofQuery.
+
+If this quick search fails, an interactive QofQuery could be used.
+A new query would have been needed anyway, with options enabled to
+isolate only one match. QofQuery relies on the term being the same
+type as the param_type. SQL queries will also be supported via the
+sql command in the shell. */
+
+static void
+cli_search_cb (QofEntity * ent, gpointer data)
+{
+	struct search_helper *sh;
+	gchar *value;
+	const QofParam *param;
+	gboolean found;
+
+	found = FALSE;
+	sh = (struct search_helper*)data;
+	param = sh->param;
+/* if param_type == QOF_TYPE_DATE, process term as -t --date and skip to a QofQuery format. */
+	if(0 == safe_strcmp(param->param_type, QOF_TYPE_DATE)) { return; }
+	value = qof_book_merge_param_as_string((QofParam*)param, ent);
+	if(0 == safe_strcmp(value, sh->term))
+	{
+		 /* if term matches more than once, fallback to SQL */
+		if(found) { sh->inst = NULL; return; }
+		found = TRUE;
+		sh->inst = (QofInstance*)ent;
+	}
+}
+
+/** pre-selects if only one entity of this type exists. */
+static void
+count_cb (QofEntity *ent, gpointer user_data)
+{
+	struct count_helper *ch;
+
+	ch = (struct count_helper*)user_data;
+	ch->count++;
+	if(ch->count == 1)
+	{
+		ch->first_inst = (QofInstance*)ent;
+	}
+}
+
+/** Shortcut if only one entity of this type exists. */
+static gboolean
+check_for_single_instance (qof_shell_context *context)
+{
+	QofCollection *coll;
+	struct count_helper ch;
+	struct search_helper sh;
+
+	ch.count = 0;
+	qof_object_foreach(context->inst_type, context->book, count_cb, &ch);
+	if(!ch.count)
+	{
+		fprintf (stderr, _("%s: No objects of type '%s' found.\n"),
+			PACKAGE, context->inst_type);
+		context->inst_type = NULL;
+		return FALSE;
+	}
+	if(ch.count == 1) 
+	{ 
+		context->instance = ch.first_inst; 
+		if(!context->argv[2] || !context->argv[3]) { return TRUE; }
+	}
+	if(!context->argv[2] || !context->argv[3]) { return FALSE; }
+	sh.inst = NULL;
+	sh.param = NULL;
+	sh.term = context->argv[3];
+	sh.param = (QofParam*)qof_class_get_parameter(context->inst_type, context->argv[2]);
+	if(sh.param == NULL) { 
+		fprintf (stderr, _("No parameter named '%s' found for object '%s'\n"), 
+			sh.term, sh.param->param_name);
+		return FALSE;
+	}
+	coll = qof_book_get_collection(context->book, context->inst_type);
+	if(!coll) { return FALSE; }
+	qof_collection_foreach(coll, cli_search_cb, &sh);
+	if(sh.inst)
+	{
+		context->instance = sh.inst;
+		if(context->argv[4]) {
+			g_message("argv[4]=%s param=%s", context->argv[4], sh.param->param_name);
+			sh.value = g_strdup(context->argv[4]);
+			qof_begin_edit(context->instance);
+			undo_edit_record(context->instance, sh.param);
+			qof_entity_set_param(&context->instance->entity, (QofParam*)sh.param, sh.value);
+			qof_commit_edit(context->instance);
+			undo_edit_commit(context->instance, sh.param);
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/** @name Top level shell functions.
+@{
+*/
+
+/** \brief add a new instance and edit the values
+
+Expects the second argument (context->argv[1]) to be the name of a registed object.
+*/
+static int add_fcn(qof_shell_context *context)
+{
+	QofInstance *inst;
+	gint result;
+	gchar *temp;
+
+	result = 0;
+	if(!qof_class_is_registered(context->argv[1]))
+	{
+		fprintf (stdout, _("%s: Cannot add '%s' - object name not found\n"),
+			PACKAGE, context->argv[1]);
+		return 0;
+	}
+	context->inst_type = g_strdup(context->argv[1]);
+	temp = g_strdup_printf("add-%s", context->inst_type);
+	qof_book_start_operation(context->book, temp);
+	inst = (QofInstance*)qof_object_new_instance(context->inst_type, context->book);
+	if(!inst) {
+		fprintf (stdout, _("Failed to add an instance of %s\n"), context->inst_type);
+		return 0;
+	}
+	fprintf (stdout, _("Added an instance of %s\n\n"), context->inst_type);
+	fprintf (stdout, _("Now edit the parameter details of this instance.\n"));
+	fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
+	undo_create_record(inst);
+	qof_book_end_operation(context->book);
+	context->shell_type = EDIT_SHELL;
+	context->instance = inst;
+	result = qof_sub_shell(context);
+	context->shell_type = TOP_SHELL;
+	return result;
+}
+
+/** \brief edit [object]
+
+\todo skip over user-friendly forms that may use
+where or =.
+
+*/
+static int edit_fcn(qof_shell_context *context)
+{
+	gint result;
+	gchar *temp;
+
+	if(!qof_class_is_registered(context->argv[1]))
+	{
+		fprintf (stdout, _("%s: Cannot edit '%s' - object name not found\n"),
+			PACKAGE, context->argv[1]);
+		return 0;
+	}
+	result = 0;
+	context->inst_type = g_strdup(context->argv[1]);
+	if(!check_for_single_instance(context)) {
+		fprintf (stdout, _("Select an instance of %s to edit:\n"), context->inst_type);
+		fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
+		context->cli_mode = EDIT_MODE;
+		result = select_fcn(context);
+		context->cli_mode = NO_OP;
+		if (result) { return result; }
+	}
+	context->shell_type = EDIT_SHELL;
+	temp = g_strdup_printf("modify-%s", context->inst_type);
+	qof_book_start_operation(context->book, temp);
+	fprintf (stdout, _("Edit the parameter details of the selected instance.\n"));
+	fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
+	if(context->instance) { result = qof_sub_shell(context); }
+	context->shell_type = TOP_SHELL;
+	context->inst_type = NULL;
+	qof_book_end_operation(context->book);
+	return result;
+}
+/** \brief delete an instance of an object.
+
+Selects one instance and frees the entity.
+Warns the user AFTER deletion. 
+
+\todo a bespoke undo command could be useful, rather
+than quitting the program completely! :-)
+*/
+static int delete_fcn(qof_shell_context *context)
+{
+	gint result;
+	QofEntity *ent;
+	gchar *temp;
+
+	result = 0;
+	if(!qof_class_is_registered(context->argv[1]))
+	{
+		fprintf (stdout, _("%s: Cannot delete '%s' - object name not found\n"),
+		PACKAGE, context->argv[1]);
+		return 0;
+	}
+	context->inst_type = g_strdup(context->argv[1]);
+	if(!check_for_single_instance(context))
+	{
+		fprintf (stdout, _("Select the instance of %s to delete\n"), context->inst_type);
+		fprintf (stdout, _("Type 'help' for available commands or parameters.\n\n"));
+		context->cli_mode = DELETE_MODE;
+		result = select_fcn(context);
+		context->cli_mode = NO_OP;
+		if ((result)||(!context->instance)) { return result; }
+	}
+	temp = g_strdup_printf("delete_%s", context->inst_type);
+	qof_book_start_operation(context->book, temp);
+	undo_delete_record(context->instance);
+	ent = (QofEntity*)context->instance;
+	qof_collection_mark_dirty(qof_book_get_collection(context->book, ent->e_type));
+	qof_entity_release(ent);
+	qof_book_end_operation(context->book);
+	fprintf (stdout, _("The instance has been DELETED. Type 'quit' to exit and undo. "
+	"The file will not be changed until you type 'write'.\n"));
+	return 0;
+}
+
+/*static void
+qof_main_list(QofObject *obj, gpointer data)
+{
+	fprintf(stdout, "%s\t%s\n", obj->e_type, obj->type_label);
+}*/
+
+static int list_fcn(qof_shell_context *context)
+{
+	fprintf (stdout, _("\nThe QOF shell supports these object names:\n"
+	"You can use the names with 'edit', 'print' or 'delete'\n"
+	"and in SQL queries (as the table name) with 'sql'\n"
+	"Descriptions are shown only for readability.\n\n"));
+	fprintf (stdout, "%-20s%s", _("Object Name"), _("Description\n"));
+//	qof_object_foreach_type(qof_list_cb, NULL);
+	fprintf (stdout, _("\nUse 'explain <database>' to see the list of fields within\n"
+	"any supported database.\n"));
+	return 0;
+}
+
+static void
+qof_print_cb (QofParam *param, gpointer data)
+{
+	gchar *str;
+	qof_shell_context *context;
+
+	context = (qof_shell_context*)data;
+	g_return_if_fail(context);
+	str = qof_book_merge_param_as_string(param, (QofEntity*)context->instance);
+	fprintf (stdout, _("%-24s %-12s %s\n"), param->param_name, param->param_type, str);
+}
+
+/** \brief print an instance to the terminal. */
+static int print_fcn(qof_shell_context *context)
+{
+	gint result;
+
+	result = 0;
+	if(!qof_class_is_registered(context->argv[1]))
+	{
+		fprintf (stdout, _("%s: Cannot print '%s' - object name not found\n"),
+			PACKAGE, context->argv[1]);
+		return 0;
+	}
+	context->inst_type = g_strdup(context->argv[1]);
+	if(!check_for_single_instance(context))
+	{
+		if(!context->inst_type) { return 0; }
+		fprintf (stdout, _("Select the instance of '%s' to print.\n"), context->inst_type);
+		fprintf (stdout, _(" Type 'help' for available commands or parameters.\n\n"));
+		context->cli_mode = PRINT_MODE;
+		result = select_fcn(context);
+		context->cli_mode = NO_OP;
+		if ((result)||(!context->instance)) { return result; }
+	}
+	fprintf (stdout, "%-24s %-12s %s\n\n", _("Name"), _("Type"), _("Value"));
+	qof_class_param_foreach (context->inst_type, qof_print_cb, context);
+	fprintf(stdout, "\n");
+	return 0;
+}
+
+static void
+qof_explain_cb (QofParam* param, gpointer user_data)
+{
+	if(param->param_getfcn && param->param_setfcn)
+	{
+		fprintf (stdout, _("Type: %-12s\tName: %-12s\n"), param->param_type, param->param_name);
+	}
+}
+
+/** print a list of available parameters for this object type */
+static int explain_fcn(qof_shell_context *context)
+{
+	if(qof_class_is_registered(context->argv[1])) {
+		fprintf (stdout, _("\nParameters of the %s database:\n\n"), context->argv[1]);
+		qof_class_param_foreach(context->argv[1], qof_explain_cb, NULL);
+		fprintf (stdout, "\n\n");
+	}
+	else {
+		fprintf (stderr, _("\n%s: %s object not found.\n"), PACKAGE, context->argv[1]);
+	}
+	return 0;
+}
+
+/** \brief load [filename]
+
+Expects a readable filename in context->argv[1]
+*/
+static int load_fcn(qof_shell_context *context)
+{
+	char* answer;
+
+	answer = g_strdup(" ");
+	if(qof_book_not_saved(context->book))
+	{
+		fprintf (stderr, _("%s: The current book has not been saved.\n"
+		"Do you still want to overwrite it? [y/n]\n"), PACKAGE);
+		scanf("%1s", answer);
+		if((0 != safe_strcmp(answer, "y")) && (0 != safe_strcmp(answer, "Y"))) {
+			qof_session_save(context->qof.input_session, NULL);
+		}
+	}
+	qof_session_end(context->qof.input_session);
+	context->qof.input_session = qof_session_new();
+	if(context->argv[1]) {
+		context->qof.filename = g_strdup(context->argv[1]);
+	}
+	if(context->qof.filename) {
+		qof_session_begin(context->qof.input_session, context->qof.filename, FALSE, TRUE);
+//		qof_mod_compression(context->gz_level, context);
+	}
+	else {
+		qof_session_begin(context->qof.input_session, QOF_STDOUT, FALSE, FALSE);
+	}
+	qof_session_load(context->qof.input_session, NULL);
+	context->book = qof_session_get_book(context->qof.input_session);
+	qof_main_show_error(context->qof.input_session);
+	/* implement in the backend eventually */
+	qof_book_clear_undo(context->book);
+	return 0;
+}
+
+/** check if book is dirty, show errors (if any) and exit */
+static int exit_fcn(qof_shell_context *context)
+{
+	char* answer;
+
+	answer = g_strdup(" ");
+	if(qof_book_not_saved(context->book))
+	{
+		fprintf (stdout, _("The current book has not been saved.\n"
+		"Do you still want to quit without saving? %s"), "[y/n]");
+		scanf("%1s", answer);
+		fprintf (stdout, "\n");
+		if(0 != safe_strcmp(answer, "y") && (0 != safe_strcmp(answer, "Y"))) {
+			qof_session_save(context->qof.input_session, NULL);
+		}
+	}
+	if(qof_book_not_saved(qof_session_get_book(context->qof.export_session)))
+	{
+		qof_main_show_error(context->qof.export_session);
+	}
+	qof_main_show_error(context->qof.input_session);
+	qof_session_end(context->qof.input_session);
+	if(context->qof.export_session) { qof_session_end(context->qof.export_session); }
+	fprintf (stdout, _("\nThank you for using %s.\n"), PACKAGE);
+	return -1;
+}
+
+static int help_fcn(qof_shell_context *context)
+{
+	fprintf (stdout, _("Commands available in the shell are:\n\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "add [object]", 
+		_("Add a new instance of the object.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "edit [object]",
+		_("Select one instance and edit (set) the parameter values.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "delete [object]", 
+		_("Select one instance for deletion.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "list", 
+		_("Synonym for the --list command outside the shell.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "print [object]", 
+		_("Select one instance and print the parameter values.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "explain [object]", 
+		_("Synonym for the --explain command outside the shell.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "load [filename]",
+		_("Replace the current book with data from the file.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, " ", 
+		_("Prompts to save the current book, if any.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "write [filename]", 
+		_("Write out the current book. If no filename is given,\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, " ", _("write to the original file.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, " ", _("(uses STDOUT if STDIN is used.)\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "merge [filename]", 
+		_("Merge data from the file into the current book.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "sql [sql_query]", 
+		_("Run a \"quoted\" SQL statement.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "help | ?", _("This screen.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "quit | q | exit", _("Quit the shell\n"));
+	fprintf (stdout, "\n");
+	return 0;
+}
+
+/** \name Edit shell functions
+@{
+*/
+
+/** \brief Set a value in a parameter
+
+\todo improve error handling - currently it is almost silent.
+
+Boolean values accept TRUE, 1, or translated values for Y, YES or TRUE
+and are matched case insensitively, so y, yes and true (and their translations)
+also match. If the match fails, false is set.
+
+*/
+static int set_edit_fcn (qof_shell_context *context)
+{
+	QofParam *param;
+	QofType type;
+	gchar *value;
+
+	if(!context->instance || !context->argv[1] || !context->argv[2]) { return 0; }
+	value = g_strdup(context->argv[2]);
+	param = (QofParam*)qof_class_get_parameter(context->inst_type, context->argv[1]);
+	if(!param)
+	{
+		fprintf (stderr, _("%s: parameter name '%s' of object '%s' not recognised.\n"),
+			PACKAGE, context->argv[1], context->inst_type);
+		fprintf (stderr, _("Type 'explain' for more information.\n"));
+		return 0;
+	}
+	type = qof_class_get_parameter_type(context->inst_type, context->argv[1]);
+	qof_begin_edit(context->instance);
+	/* undo_edit_record will be called by begin_edit eventually. */
+	undo_edit_record(context->instance, param);
+	qof_entity_set_param(&context->instance->entity, param, value);
+	qof_commit_edit(context->instance);
+	undo_edit_commit(context->instance, param);
+	g_free(value);
+	return 0;
+}
+
+/** List the available parameters for this edit */
+static int explain_edit_fcn (qof_shell_context *context)
+{
+	fprintf (stdout, _("\nEditable parameters of the %s object:\n\n"), context->inst_type);
+	qof_class_param_foreach(context->inst_type, qof_explain_cb, NULL);
+	fprintf (stdout, "\n\n");
+	return 0;
+}
+
+/** \brief Commit data to instance and leave the sub-shell.
+
+\todo Should this clear the undo??
+*/
+static int commit_edit_fcn (qof_shell_context *context)
+{
+	if(qof_book_can_undo(context->book)) { qof_book_clear_undo(context->book); }
+	return -1;
+}
+
+/** print the instance being edited.*/
+static int print_edit_fcn (qof_shell_context *context)
+{
+	if(!context->inst_type) { return 0; }
+	fprintf (stdout, "%-24s %-12s %s\n\n", _("Name"), _("Type"), _("Value"));
+	qof_class_param_foreach (context->inst_type, qof_print_cb, context);
+	fprintf (stdout, _("\nNot all parameters are editable, use 'explain' "
+	"to see the list of editable parameters for the object '%s'.\n\n"), context->inst_type);
+	return 0;
+}
+
+static int help_edit_fcn (qof_shell_context *context)
+{
+	gchar *temp;
+
+	fprintf (stdout, _("Commands available in this sub-shell are:\n\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "edit [Name] [Value]", 
+	_("Edit the parameter 'Name' to have the 'Value' given.\n"));
+	/* Translators: TRUE and numeral one are always acceptable for boolean values here. */
+	fprintf (stdout, QOF_SHELL_FORMAT, " ", 
+		_("Boolean values accept TRUE|true, 1, Y|y or yes|YES\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, " ", _("String values that include spaces should be quoted\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "set [Name] [Value]", _("Synonym for edit.\n"));
+	temp = g_strdup_printf(_("Print the current values for this '%s' instance.\n"), context->inst_type);
+	fprintf (stdout, QOF_SHELL_FORMAT, "print", temp);
+	g_free(temp);
+	temp = g_strdup_printf(_("Show the editable parameters for '%s'\n"), context->inst_type);
+	fprintf (stdout, QOF_SHELL_FORMAT, "explain", temp);
+	g_free(temp);
+	fprintf (stdout, QOF_SHELL_FORMAT, "commit",  _("Set the edited parameter values.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "help | ?", _("This screen.\n"));
+	fprintf (stdout, QOF_SHELL_FORMAT, "quit | q | exit", 
+		_("Return to the top shell without setting changes.\n"));
+	fprintf (stdout, "\n");
+	return 0;
+}
+
+static int quit_edit_fcn (qof_shell_context *context)
+{
+	if(qof_book_can_undo(context->book)) { qof_book_undo(context->book); }
+	return -1;
+}
+
+/** @} */
+/** \brief merge UI
+
+\todo the entire merge routine needs testing.
+*/
+static void
+qof_merge_loop (qof_book_mergeData *mergeData, qof_book_mergeRule *rule, guint remainder)
+{
+	GSList *user_reports;
+	QofParam *one_param;
+	gchar *importstring, *targetstring, *buffer;
+	gint count, resolution;
+	gboolean input_ok;
+	gchar y;
+
+	buffer = "";
+	count = 0;
+	input_ok = FALSE;
+	user_reports = rule->mergeParam;
+	while(user_reports != NULL) {
+		one_param = user_reports->data;
+		buffer = g_strconcat(buffer, g_strdup_printf(_("%i:Parameter name: %s "),
+			count, one_param->param_name), NULL);
+		importstring = qof_book_merge_param_as_string(one_param, rule->importEnt);
+		buffer = g_strconcat(buffer, g_strdup_printf(_("Import data : %s "), importstring), NULL);
+		targetstring = qof_book_merge_param_as_string(one_param, rule->targetEnt);
+		buffer = g_strconcat(buffer, g_strdup_printf(_("Original data : %s\n"), targetstring), NULL);
+		user_reports = g_slist_next(user_reports);
+		count++;
+	}
+	while(!input_ok) {
+		resolution = 0;
+		fprintf (stdout, _("\nPlease resolve this conflict. Enter\n"
+		"    1 to use the import data or \n"
+		"    2 to keep the original data or"));
+		if(rule->mergeAbsolute == FALSE) {
+			fprintf (stdout, _("\n    3 to import the data as a NEW object or"));
+		}
+		fprintf (stdout, _("\n    9 to abort the entire merge operation.\n"));
+		fprintf (stdout, "> (1, 2");
+		if(rule->mergeAbsolute == FALSE) {
+			fprintf (stdout, ", 3 ");
+		}
+		fprintf (stdout, _("or 9) : "));
+		scanf("%1i", &resolution);
+		switch(resolution) {
+			case 1 : {
+				mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_UPDATE);
+				input_ok = TRUE;
+				break;
+				}
+			case 2 : {
+				if(rule->mergeAbsolute == FALSE) {
+					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_DUPLICATE);
+				}
+				if(rule->mergeAbsolute == TRUE) {
+					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_ABSOLUTE);
+				}
+				input_ok = TRUE;
+				break;
+			}
+			case 3 : {
+				if(rule->mergeAbsolute == FALSE) {
+					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_NEW);
+					input_ok = TRUE;
+				}
+				break;
+			}
+			case 9 : {
+				fprintf (stdout, _("Are you sure you want to abort the entire merge operation?\n"
+				"The rest of the import data will not be processed.\n"
+				"Your original data will not be modified. Abort? y/n : "));
+				scanf("%1s", &y);
+				if((safe_strcmp(_("y"),&y) == 0)||(safe_strcmp("",&y) == 0)) {
+					fprintf (stdout, _("Aborting . . \n\n"));
+					mergeData = qof_book_mergeUpdateResult(mergeData, MERGE_INVALID);
+					input_ok = TRUE;
+				}
+				break;
+			}
+			default : break;
+		}
+	}
+}
+
+/** \brief merge [filename]
+
+Accept a filename from the shell, check it is readable and
+load it, then if the book is valid, merge it into the
+current book.
+
+If the current book is NULL, this is equivalent to 'load' but
+a LOT more longwinded. (Not recommended.)
+*/
+static int merge_fcn(qof_shell_context *context)
+{
+	qof_book_mergeData *mergeData;
+	QofBook *importBook, *targetBook;
+	gint result;
+
+	/** \todo check the filename can be read. */
+	if(!context->argv[1]) { return 0; }
+	qof_session_begin(context->qof.export_session, context->argv[1], FALSE, FALSE);
+	qof_session_load(context->qof.export_session, NULL);
+	qof_main_show_error(context->qof.export_session);
+	importBook = qof_session_get_book(context->qof.export_session);
+	targetBook = context->book;
+	mergeData = qof_book_mergeInit(importBook, targetBook);
+	g_return_val_if_fail((mergeData != NULL), -1);
+	qof_book_mergeRuleForeach(mergeData, qof_merge_loop, MERGE_REPORT);
+	result = qof_book_mergeCommit(mergeData);
+	return result;
+}
+
+/** \name Shell control handlers
+@{
+*/
+struct shell_cmd shell_list[] = {
+	{ "add",     add_fcn          },
+	{ "edit",    edit_fcn         },
+	{ "delete",  delete_fcn       },
+	{ "list",    list_fcn         },
+	{ "print",   print_fcn        },
+	{ "explain", explain_fcn      },
+	{ "load",    load_fcn         },
+	{ "write",   write_fcn        },
+	{ "merge",   merge_fcn        },
+	{ "sql",     sql_fcn          },
+	{ "help",    help_fcn         },
+	{ "?",       help_fcn         },
+	{ "q",       exit_fcn         },
+	{ "quit",    exit_fcn         },
+	{ "exit",    exit_fcn         },
+	{ "bye",     exit_fcn         },
+	{ NULL, NULL }
+};
+
+struct shell_cmd edit_list[] = {
+	{ "edit",    set_edit_fcn     },
+	{ "set",     set_edit_fcn     },
+	{ "explain", explain_edit_fcn },
+	{ "commit",  commit_edit_fcn  },
+	{ "print",   print_edit_fcn   },
+	{ "quit",    quit_edit_fcn    },
+	{ "help",    help_edit_fcn    },
+	{ "?",       help_edit_fcn    },
+	{ "q",       quit_edit_fcn    },
+	{ "exit",    quit_edit_fcn    },
+	{ "bye",     quit_edit_fcn    },
+	{ NULL, NULL }
+};
+
+static char *strtoke(char *str, const char *ws, const char *delim)
+{
+	int inc;
+	static char *start, *s = NULL;
+
+	if (str != NULL) { s = str; }
+	inc = strspn(s, ws);
+	s += inc;
+	start = s;
+	if (*s == '\0') { return NULL; }
+	else if (strchr(delim, *s) != NULL) {
+		start++;
+		s = strchr(s + 1, *s);
+		*s = '\0';
+		s++;
+	}
+	else {
+		inc = strcspn(s, ws);
+		if (s[inc] == '\0') { s += inc; }
+		else {
+			s[inc] = '\0';
+			s += inc + 1;
+		}
+	}
+	return start;
+}
+
+/** \brief Relate the command to the function.
+
+\todo Stop changing the input with strtoke ?
+*/
+static int
+qof_parse_command (const char *cmd, qof_shell_context *context)
+{
+	char *argv[32];
+	int inc, argc;
+	char *cmd_dup;
+	gboolean good;
+
+	argc = 0;
+	good = FALSE;
+	memset(argv, 0, sizeof(argv) / sizeof(char*));
+	cmd_dup = strdup(cmd);
+	argv[0] = strtoke(cmd_dup, " \t\n", "\"'");
+	while (argv[argc] != NULL) {
+		argc++;
+		argv[argc] = strtoke(NULL, " \t\n", "\"'");
+	}
+	if (argc == 0) {
+		free(cmd_dup);
+		return 0;
+	}
+	context->argc = argc;
+	context->argv = argv;
+	switch(context->shell_type)
+	{
+		case TOP_SHELL :
+		{
+			for (inc = 0; shell_list[inc].name != NULL; inc++) {
+				if (strcasecmp(argv[0], shell_list[inc].name) == 0) {
+					good = TRUE;
+					if((shell_list[inc].func(context)) < 0) { return -1; }
+				}
+			}
+			if(!good) {
+				fprintf(stderr, _("%s: bad option - %s, available commands are:\n"),
+					PACKAGE, argv[0]);
+				for (inc = 0; shell_list[inc].name != NULL; inc++) {
+					fprintf (stderr, "%s ", shell_list[inc].name);
+				}
+				fprintf (stderr, _("\nUse help for more information.\n\n"));
+			}
+			break;
+		}
+		case EDIT_SHELL :
+		{
+			for (inc = 0; edit_list[inc].name != NULL; inc++) {
+				if(strcasecmp(argv[0], edit_list[inc].name) == 0) {
+					good = TRUE;
+					if((edit_list[inc].func(context)) < 0) { return -1; }
+				}
+			}
+			if(!good) {
+				fprintf(stderr, _("%s: bad option - %s, available commands are:\n"),
+					PACKAGE, argv[0]);
+				for (inc = 0; edit_list[inc].name != NULL; inc++) {
+					fprintf (stderr, "%s ", edit_list[inc].name);
+				}
+				fprintf (stderr, _("\nUse help for more information.\n\n"));
+			}
+			break;
+		}
+		default : { break; }
+	}
+	free(cmd_dup);
+	return 0;
+}
+
+void
+qof_cmd_shell(qof_shell_context *context)
+{
+	QofSession *input_session;
+#ifdef HAVE_LIBREADLINE
+	char *line;
+	char *prompt;
+
+	line = (char *)malloc(256*sizeof(char));
+	prompt = g_strdup_printf("%s> ", PACKAGE);
+#else
+	char buf[256];
+
+#endif
+	context->argc = 0;
+	context->argv = NULL;
+	input_session = context->qof.input_session;
+	if(0 == safe_strcmp(context->qof.exclude, context->qof.database)
+		&&(context->qof.exclude != NULL))
+	{
+		fprintf(stderr, _("%s: Error: Cannot exclude database \"%s\" with option -e\n"
+		"    because option -d is set to the include the same database: \"%s\"\n"
+		"Use the \'-l\' command to see the full list of supported databases.\n"),
+			PACKAGE, context->qof.exclude, context->qof.database);
+		qof_session_end(input_session);
+		return;
+	}
+	if(context->qof.filename) {
+		qof_session_begin(input_session, context->qof.filename, TRUE, TRUE);
+		qof_session_load(input_session, NULL);
+	}
+	else { qof_session_begin(input_session, QOF_STDOUT, TRUE, FALSE); }
+	qof_main_show_error(input_session);
+	context->book = qof_session_get_book(context->qof.input_session);
+	context->shell_type = TOP_SHELL;
+	context->inst_type = NULL;
+	context->instance = NULL;
+	if(!context->shortname) { context->shortname = g_strndup(PACKAGE, 4); }
+	context->argc = 0;
+	fprintf (stdout, _("\nWelcome to the QOF interactive shell ...\n"));
+	fprintf (stdout, _(" Type 'help' for additional information\n\n"));
+
+	for (;;) {
+		fflush(stdout);
+#ifdef HAVE_LIBREADLINE
+		line = readline(prompt);
+		/* user pressed ^d or so */
+		if (line == NULL) {
+			fprintf(stdout, _("\n\nThank you for using %s.\n"), PACKAGE);
+		       	break;
+		}
+		/* skip blanks */
+		if (*line) { add_history(line); }
+		if(qof_parse_command(line, context) != 0) { break; }
+		free(line);
+#else
+		fprintf (stdout, "%s> ", PACKAGE);
+		if(qof_parse_command(buf, context) != 0) { break; }
+		if (fgets(buf, 256, stdin) == NULL) { break; }
+#endif
+	}
+	fprintf(stdout, "\n");
+}

Added: gnucash/branches/cashutil/cashutil/src/qof-shell.h
===================================================================
--- gnucash/branches/cashutil/cashutil/src/qof-shell.h	2005-11-28 12:10:32 UTC (rev 12058)
+++ gnucash/branches/cashutil/cashutil/src/qof-shell.h	2005-11-28 15:45:33 UTC (rev 12059)
@@ -0,0 +1,109 @@
+/***************************************************************************
+ *            qof-shell.h
+ *
+ *  Mon Nov 28 13:03:44 2005
+ *  Copyright  2005  Neil Williams
+ *  linux at codehelp.co.uk
+ ****************************************************************************/
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+ /** @addtogroup QOFCLI Query Object Framework Command Line Interface.
+
+The CLI uses a top level shell and two sub shells. The top level shell provides
+general interactivity with object types, entire books and SQL support.
+
+The select sub-shell identifies a single instance of an object. e.g. the print 
+command in the top shell uses the select subshell to locate the instance to be
+printed and the delete command, the instance to be deleted. These commands then
+return to the top shell.
+
+The edit sub-shell allows data to be set in a selected instance. The edit command
+uses a select sub-shell to identify the instance then changes to an edit sub-shell
+to handle setting the individual parameters of that instance. Before returning to the
+top shell, the edited data can be saved to the instance using 'commit' or the edit can
+be aborted using 'quit'. 
+
+The add command creates a new instance and passes that instance, already selected,
+to the edit sub-shell for data entry.
+
+\note CashUtil relies on installed versions of the QOF, libcashobjects and
+libgnc-backend-file libraries. If you change any source files for these libraries,
+ensure you run 'make install' rather than just 'make' or your changes will have no
+effect. There is no support for loading local or 'test' versions of the libraries.
+You can usually run cashutil against a freshly installed set of QOF libraries without
+recompiling cashutil, depending on the level of changes in QOF.
+@{
+*/
+/** @file qof-shell.h
+  @brief Shell functions for the QOF external framework CLI
+  @author Copyright (c) 2005 Neil Williams <linux at codehelp.co.uk>
+*/
+
+#ifndef _QOF_SHELL_H
+#define _QOF_SHELL_H
+
+#include <qof.h>
+#include "qof-main.h"
+#include "qofundo.h"
+
+#define QOF_SHELL_CLI "qof-cli-shell"
+
+#define QOF_SHELL_OPTIONS \
+	{"shell", 0, POPT_ARG_NONE, NULL, qof_op_shell, \
+	 _("Enter the QOF interactive shell"), NULL},
+
+
+/** Indent and pad the shell output nicely.*/
+#define QOF_SHELL_FORMAT "    %-30s%s"
+
+/** \brief Handle the type of each subshell. */
+typedef enum {
+	NO_SHELL,
+	TOP_SHELL,     /**< the first, top level shell. */
+	EDIT_SHELL,    /**< Edit the selected instance */
+}qof_subshell;
+
+typedef enum {
+	NO_OP,
+	PRINT_MODE,
+	DELETE_MODE,
+	EDIT_MODE,
+}qof_cli_mode;
+
+typedef struct qof_shell_s {
+	qof_main_context qof;       /**< Wrap the CLI context */
+	gchar *shortname;           /**< A shortname for this program if truncation to first 4 characters is not suitable. */
+	int argc;                   /**< Shell copy of argc */
+	char **argv;                /**< Shell copy of commands */
+	QofBook *book;              /**< the current book for the shell function. */
+	qof_subshell shell_type;    /**< the type of subshell, top or edit */
+	qof_cli_mode cli_mode;      /**< current operation mode. */
+	QofIdTypeConst inst_type;   /**< The current registered QofObject type. */
+	QofInstance *instance;      /**< The currently selected instance. */
+	gint counter;
+	GHashTable *select_table;
+}qof_shell_context;
+
+/** \brief load the QOF interactive shell
+
+Where available, uses READLINE to store a history of
+previous shell commands.
+*/
+void qof_cmd_shell(qof_shell_context *context);
+
+void qof_shell_free (qof_shell_context *context);
+
+#endif /* _QOF_SHELL_H */



More information about the gnucash-changes mailing list