/********************************************************************\ * engine-helpers.c -- gnucash engine helper functions * * Copyright (C) 2000 Linas Vepstas * * Copyright (C) 2001 Linux Developers Group, Inc. * * * * 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, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * * * \********************************************************************/ #include #include "swig-runtime.h" #include #include #include "Account.h" #include "engine-helpers.h" #include "engine-helpers-guile.h" #include "glib-helpers.h" #include "gnc-date.h" #include "gnc-engine.h" #include "gnc-session.h" #include "guile-mappings.h" #include "gnc-guile-utils.h" #include #include /** \todo Code dependent on the private query headers qofquery-p.h and qofquerycore-p.h may need to be modified. These files are temporarily exported for QOF 0.6.0 but cannot be considered "standard" or public parts of QOF. */ #include "qofquery-p.h" #include "qofquerycore-p.h" #define FUNC_NAME G_STRFUNC static QofLogModule log_module = GNC_MOD_ENGINE; Timespec gnc_transaction_get_date_posted(const Transaction *t) { Timespec ret = {xaccTransRetDatePosted(t), 0}; return ret; } Timespec gnc_transaction_get_date_entered(const Transaction *t) { Timespec result = {xaccTransRetDateEntered(t), 0}; return result; } Timespec gnc_split_get_date_reconciled(const Split *s) { Timespec result; xaccSplitGetDateReconciledTS(s, &result); return(result); } void gnc_transaction_set_date(Transaction *t, Timespec ts) { xaccTransSetDatePostedSecs(t, ts.tv_sec); } /** Gets the transaction Number or split Action based on book option: * if the book option is TRUE (split action is used for NUM) and a * split is provided, split-action is returned; if book option is FALSE * (tran-num is used for NUM) and a trans is provided, transaction-num * is returned; if split is provided and tran is NULL, split-action is * returned; if tran is provided and split is NULL, transaction-num is * returned. Otherwise NULL is returned.*/ const char * gnc_get_num_action (const Transaction *trans, const Split *split) { gboolean num_action = qof_book_use_split_action_for_num_field (qof_session_get_book(gnc_get_current_session ())); if (trans && !split) return xaccTransGetNum(trans); if (split && !trans) return xaccSplitGetAction(split); if (trans && split) { if (num_action) return xaccSplitGetAction(split); else return xaccTransGetNum(trans); } else return NULL; } /** Opposite of 'gnc_get_num_action'; if the book option is TRUE (split action * is used for NUM) and a trans is provided, transaction-num is returned; if * book option is FALSE (tran-num is used for NUM) and a split is provided, * split-action is returned; if split is provided and tran is NULL, * split-action is returned; if tran is provided and split is NULL, * transaction-num is returned. Otherwise NULL is returned.*/ const char * gnc_get_action_num (const Transaction *trans, const Split *split) { gboolean num_action = qof_book_use_split_action_for_num_field (qof_session_get_book(gnc_get_current_session ())); if (trans && !split) return xaccTransGetNum(trans); if (split && !trans) return xaccSplitGetAction(split); if (trans && split) { if (num_action) return xaccTransGetNum(trans); else return xaccSplitGetAction(split); } else return NULL; } /** Sets the transaction Number and/or split Action based on book option: * if the book option is TRUE (split action is to be used for NUM) then 'num' * sets split-action and, if 'tran' and 'action' are provided, 'action' * sets transaction-num; if book option is FALSE (tran-num is to be used for NUM) * then 'num' sets transaction-num and, if 'split' and 'action' are * provided, 'action' sets 'split-action'. If any arguments are NULL (#f, for * the guile version), no change is made to the field that would otherwise be * affected. If 'tran' and 'num' are passed with 'split and 'action' set to * NULL, it is xaccTransSetNum (trans, num). Likewise, if 'split and 'action' * are passed with 'tran' and 'num' set to NULL, it is xaccSplitSetAction (split, * action). */ void gnc_set_num_action (Transaction *trans, Split *split, const char *num, const char *action) { gboolean num_action = qof_book_use_split_action_for_num_field (qof_session_get_book(gnc_get_current_session ())); if (trans && num && !split && !action) { xaccTransSetNum (trans, num); return; } if (!trans && !num && split && action) { xaccSplitSetAction (split, action); return; } if (trans) { if (!num_action && num) xaccTransSetNum (trans, num); if (num_action && action) xaccTransSetNum (trans, action); } if (split) { if (!num_action && action) xaccSplitSetAction (split, action); if (num_action && num) xaccSplitSetAction (split, num); } } /************************************************************/ /* Notification of Book Option Changes */ /************************************************************/ static GOnce bo_init_once = G_ONCE_INIT; static GHashTable *bo_callback_hash = NULL; static GHookList *bo_final_hook_list = NULL; static gpointer bo_init (gpointer unused) { bo_callback_hash = g_hash_table_new(g_str_hash, g_str_equal); bo_final_hook_list = g_malloc(sizeof(GHookList)); g_hook_list_init(bo_final_hook_list, sizeof(GHook)); return NULL; } static void bo_call_hook (GHook *hook, gpointer data) { ((GFunc)hook->func)(data, hook->data); } /** Calls registered callbacks when num_field_source book option changes so that * registers/reports can update themselves */ void gnc_book_option_num_field_source_change (gboolean num_action) { GHookList *hook_list; const gchar *key = OPTION_NAME_NUM_FIELD_SOURCE; g_once(&bo_init_once, bo_init, NULL); hook_list = g_hash_table_lookup(bo_callback_hash, key); if (hook_list != NULL) g_hook_list_marshal(hook_list, TRUE, bo_call_hook, &num_action); g_hook_list_invoke(bo_final_hook_list, TRUE); } /** Calls registered callbacks when book_currency book option changes so that * registers/reports can update themselves */ void gnc_book_option_book_currency_selected (gboolean use_book_currency) { GHookList *hook_list; const gchar *key = OPTION_NAME_BOOK_CURRENCY; g_once(&bo_init_once, bo_init, NULL); hook_list = g_hash_table_lookup(bo_callback_hash, key); if (hook_list != NULL) g_hook_list_marshal(hook_list, TRUE, bo_call_hook, &use_book_currency); g_hook_list_invoke(bo_final_hook_list, TRUE); } void gnc_book_option_register_cb (gchar *key, GncBOCb func, gpointer user_data) { GHookList *hook_list; GHook *hook; g_once(&bo_init_once, bo_init, NULL); hook_list = g_hash_table_lookup(bo_callback_hash, key); if (hook_list == NULL) { hook_list = g_malloc(sizeof(GHookList)); g_hook_list_init(hook_list, sizeof(GHook)); g_hash_table_insert(bo_callback_hash, (gpointer)key, hook_list); } hook = g_hook_find_func_data(hook_list, TRUE, func, user_data); if (hook != NULL) { return; } hook = g_hook_alloc(hook_list); hook->func = func; hook->data = user_data; g_hook_append(hook_list, hook); } void gnc_book_option_remove_cb (gchar *key, GncBOCb func, gpointer user_data) { GHookList *hook_list; GHook *hook; g_once(&bo_init_once, bo_init, NULL); hook_list = g_hash_table_lookup(bo_callback_hash, key); if (hook_list == NULL) return; hook = g_hook_find_func_data(hook_list, TRUE, func, user_data); if (hook == NULL) return; g_hook_destroy_link(hook_list, hook); if (hook_list->hooks == NULL) { g_hash_table_remove(bo_callback_hash, key); g_free(hook_list); } } GDate gnc_time64_to_GDate(SCM x) { time64 time = scm_to_int64 (x); return time64_to_gdate(time); } SCM gnc_guid2scm(GncGUID guid) { char string[GUID_ENCODING_LENGTH + 1]; if (!guid_to_string_buff(&guid, string)) return SCM_BOOL_F; return scm_from_utf8_string(string); } GncGUID gnc_scm2guid(SCM guid_scm) { GncGUID guid; gchar * str; if (!scm_is_string(guid_scm) || (GUID_ENCODING_LENGTH != scm_c_string_length (guid_scm))) { return *guid_null(); } str = gnc_scm_to_utf8_string (guid_scm); string_to_guid(str, &guid); g_free (str); return guid; } int gnc_guid_p(SCM guid_scm) { GncGUID guid; gchar * str; int return_int; if (!scm_is_string(guid_scm)) return FALSE; if (GUID_ENCODING_LENGTH != scm_c_string_length (guid_scm)) { return FALSE; } str = gnc_scm_to_utf8_string (guid_scm); return_int = string_to_guid(str, &guid); g_free (str); return return_int; } /******************************************************************** * type converters for query API ********************************************************************/ /* The query scm representation is a list of pairs, where the * car of each pair is one of the following symbols: * * Symbol cdr * 'terms list of OR terms * 'primary-sort scm rep of sort_type_t * 'secondary-sort scm rep of sort_type_t * 'tertiary-sort scm rep of sort_type_t * 'primary-increasing boolean * 'secondary-increasing boolean * 'tertiary-increasing boolean * 'max-splits integer * * Each OR term is a list of AND terms. * Each AND term is a list of one of the following forms: * * ('pd-amount pr-type sense-bool amt-match-how amt-match-sign amount) * ('pd-account pr-type sense-bool acct-match-how list-of-account-guids) * ('pd-string pr-type sense-bool case-sense-bool use-regexp-bool string) * ('pd-cleared pr-type sense-bool cleared-field) * ('pd-balance pr-type sense-bool balance-field) */ typedef enum { gnc_QUERY_v1 = 1, gnc_QUERY_v2 } query_version_t; /* QofCompareFunc */ static QofQueryCompare gnc_query_scm2compare (SCM how_scm) { return scm_to_int(how_scm); } /* QofStringMatch */ static QofStringMatch gnc_query_scm2string (SCM how_scm) { return scm_to_int(how_scm); } /* QofDateMatch */ static QofDateMatch gnc_query_scm2date (SCM how_scm) { return scm_to_int(how_scm); } /* QofNumericMatch */ static QofNumericMatch gnc_query_scm2numericop (SCM how_scm) { return scm_to_int(how_scm); } /* QofGuidMatch */ static QofGuidMatch gnc_query_scm2guid (SCM how_scm) { return scm_to_int(how_scm); } /* QofCharMatch */ static QofCharMatch gnc_query_scm2char (SCM how_scm) { return scm_to_int(how_scm); } static QofGuidMatch gnc_scm2acct_match_how (SCM how_scm) { QofGuidMatch res; gchar *how = gnc_scm_symbol_to_locale_string (how_scm); if (!g_strcmp0 (how, "acct-match-all")) res = QOF_GUID_MATCH_ALL; else if (!g_strcmp0 (how, "acct-match-any")) res = QOF_GUID_MATCH_ANY; else if (!g_strcmp0 (how, "acct-match-none")) res = QOF_GUID_MATCH_NONE; else { PINFO ("invalid account match: %s", how); res = QOF_GUID_MATCH_NULL; } g_free (how); return res; } static QofQueryCompare gnc_scm2amt_match_how (SCM how_scm) { QofQueryCompare res; gchar *how = gnc_scm_symbol_to_locale_string (how_scm); if (!g_strcmp0 (how, "amt-match-atleast")) res = QOF_COMPARE_GTE; else if (!g_strcmp0 (how, "amt-match-atmost")) res = QOF_COMPARE_LTE; else if (!g_strcmp0 (how, "amt-match-exactly")) res = QOF_COMPARE_EQUAL; else { PINFO ("invalid amount match: %s", how); res = QOF_COMPARE_EQUAL; } g_free (how); return res; } static int gnc_scm2bitfield (SCM field_scm) { int field = 0; if (!scm_is_list (field_scm)) return 0; while (!scm_is_null (field_scm)) { SCM scm; int bit; scm = SCM_CAR (field_scm); field_scm = SCM_CDR (field_scm); bit = scm_to_int(scm); field |= bit; } return field; } static cleared_match_t gnc_scm2cleared_match_how (SCM how_scm) { return gnc_scm2bitfield (how_scm); } static gboolean gnc_scm2balance_match_how (SCM how_scm, gboolean *resp) { gchar *how; if (!scm_is_list (how_scm)) return FALSE; if (scm_is_null (how_scm)) return FALSE; /* Only allow a single-entry list */ if (!scm_is_null (SCM_CDR (how_scm))) return FALSE; how = gnc_scm_symbol_to_locale_string (SCM_CAR(how_scm)); if (!g_strcmp0 (how, "balance-match-balanced")) *resp = TRUE; else *resp = FALSE; g_free (how); return TRUE; } static SCM gnc_guid_glist2scm (const GList *account_guids) { SCM guids = SCM_EOL; const GList *node; for (node = account_guids; node; node = node->next) { GncGUID *guid = node->data; if (guid) guids = scm_cons (gnc_guid2scm (*guid), guids); } return scm_reverse (guids); } static GList * gnc_scm2guid_glist (SCM guids_scm) { GList *guids = NULL; if (!scm_is_list (guids_scm)) return NULL; while (!scm_is_null (guids_scm)) { SCM guid_scm = SCM_CAR (guids_scm); GncGUID *guid = NULL; if (guid_scm != SCM_BOOL_F) { guid = guid_malloc (); *guid = gnc_scm2guid (guid_scm); } guids = g_list_prepend (guids, guid); guids_scm = SCM_CDR (guids_scm); } return g_list_reverse (guids); } static void gnc_guid_glist_free (GList *guids) { GList *node; for (node = guids; node; node = node->next) guid_free (node->data); g_list_free (guids); } static SCM gnc_query_numeric2scm (gnc_numeric val) { return scm_cons (scm_from_int64 (val.num), scm_from_int64 (val.denom)); } static gboolean gnc_query_numeric_p (SCM pair) { return (scm_is_pair (pair)); } static gnc_numeric gnc_query_scm2numeric (SCM pair) { SCM denom; SCM num; num = SCM_CAR (pair); denom = SCM_CDR (pair); return gnc_numeric_create (scm_to_int64 (num), scm_to_int64 (denom)); } static SCM gnc_query_path2scm (const GSList *path) { SCM path_scm = SCM_EOL; const GSList *node; for (node = path; node; node = node->next) { const char *key = node->data; if (key) path_scm = scm_cons (scm_from_utf8_string (key), path_scm); } return scm_reverse (path_scm); } GSList * gnc_query_scm2path (SCM path_scm) { GSList *path = NULL; if (!scm_is_list (path_scm)) return NULL; while (!scm_is_null (path_scm)) { SCM key_scm = SCM_CAR (path_scm); char *key; if (!scm_is_string (key_scm)) break; key = gnc_scm_to_utf8_string(key_scm); path = g_slist_prepend (path, key); path_scm = SCM_CDR (path_scm); } return g_slist_reverse (path); } static void gnc_query_path_free (GSList *path) { GSList *node; for (node = path; node; node = node->next) g_free (node->data); g_slist_free (path); } static SCM gnc_queryterm2scm (const QofQueryTerm *qt) { SCM qt_scm = SCM_EOL; QofQueryPredData *pd = NULL; qt_scm = scm_cons (gnc_query_path2scm (qof_query_term_get_param_path (qt)), qt_scm); qt_scm = scm_cons (SCM_BOOL (qof_query_term_is_inverted (qt)), qt_scm); pd = qof_query_term_get_pred_data (qt); qt_scm = scm_cons (scm_from_locale_symbol (pd->type_name), qt_scm); qt_scm = scm_cons (scm_from_long (pd->how), qt_scm); if (!g_strcmp0 (pd->type_name, QOF_TYPE_STRING)) { query_string_t pdata = (query_string_t) pd; qt_scm = scm_cons (scm_from_long (pdata->options), qt_scm); qt_scm = scm_cons (SCM_BOOL (pdata->is_regex), qt_scm); qt_scm = scm_cons (pdata->matchstring ? scm_from_utf8_string (pdata->matchstring) : SCM_BOOL_F, qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_DATE)) { query_date_t pdata = (query_date_t) pd; qt_scm = scm_cons (scm_from_long (pdata->options), qt_scm); qt_scm = scm_cons (scm_from_int64 (pdata->date), qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_NUMERIC)) { query_numeric_t pdata = (query_numeric_t) pd; qt_scm = scm_cons (scm_from_long (pdata->options), qt_scm); qt_scm = scm_cons (gnc_query_numeric2scm (pdata->amount), qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_GUID)) { query_guid_t pdata = (query_guid_t) pd; qt_scm = scm_cons (scm_from_long (pdata->options), qt_scm); qt_scm = scm_cons (gnc_guid_glist2scm (pdata->guids), qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_INT64)) { query_int64_t pdata = (query_int64_t) pd; qt_scm = scm_cons (scm_from_int64 (pdata->val), qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_DOUBLE)) { query_double_t pdata = (query_double_t) pd; qt_scm = scm_cons (scm_from_double (pdata->val), qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_BOOLEAN)) { query_boolean_t pdata = (query_boolean_t) pd; qt_scm = scm_cons (SCM_BOOL (pdata->val), qt_scm); } else if (!g_strcmp0 (pd->type_name, QOF_TYPE_CHAR)) { query_char_t pdata = (query_char_t) pd; qt_scm = scm_cons (scm_from_long (pdata->options), qt_scm); qt_scm = scm_cons (pdata->char_list ? scm_from_utf8_string (pdata->char_list) : SCM_BOOL_F, qt_scm); } else { PWARN ("query core type %s not supported", pd->type_name); return SCM_BOOL_F; } return scm_reverse (qt_scm); } static QofQuery * gnc_scm2query_term_query_v2 (SCM qt_scm) { QofQuery *q = NULL; QofQueryPredData *pd = NULL; SCM scm; gchar *type = NULL; GSList *path = NULL; gboolean inverted = FALSE; QofQueryCompare compare_how; if (!scm_is_list (qt_scm) || scm_is_null (qt_scm)) return NULL; do { /* param path */ scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_list (scm)) break; path = gnc_query_scm2path (scm); /* inverted */ scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_bool (scm)) break; inverted = scm_is_true (scm); /* type */ scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_symbol (scm)) break; type = gnc_scm_symbol_to_locale_string (scm); /* QofCompareFunc */ scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; compare_how = gnc_query_scm2compare (scm); /* Now compute the predicate */ if (!g_strcmp0 (type, QOF_TYPE_STRING)) { QofStringMatch options; gboolean is_regex; gchar *matchstring; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; options = gnc_query_scm2string (scm); scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_bool (scm)) break; is_regex = scm_is_true (scm); scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_string (scm)) break; matchstring = gnc_scm_to_utf8_string (scm); pd = qof_query_string_predicate (compare_how, matchstring, options, is_regex); g_free (matchstring); } else if (!g_strcmp0 (type, QOF_TYPE_DATE)) { QofDateMatch options; time64 date; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; options = gnc_query_scm2date (scm); scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; date = scm_to_int64 (scm); pd = qof_query_date_predicate (compare_how, options, date); } else if (!g_strcmp0 (type, QOF_TYPE_NUMERIC)) { QofNumericMatch options; gnc_numeric val; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; options = gnc_query_scm2numericop (scm); scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!gnc_query_numeric_p (scm)) break; val = gnc_query_scm2numeric (scm); pd = qof_query_numeric_predicate (compare_how, options, val); } else if (!g_strcmp0 (type, QOF_TYPE_GUID)) { QofGuidMatch options; GList *guids; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; options = gnc_query_scm2guid (scm); scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_list (scm)) break; guids = gnc_scm2guid_glist (scm); pd = qof_query_guid_predicate (options, guids); gnc_guid_glist_free (guids); } else if (!g_strcmp0 (type, QOF_TYPE_INT64)) { gint64 val; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; val = scm_to_int64 (scm); pd = qof_query_int64_predicate (compare_how, val); } else if (!g_strcmp0 (type, QOF_TYPE_DOUBLE)) { double val; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_number (scm)) break; val = scm_to_double (scm); pd = qof_query_double_predicate (compare_how, val); } else if (!g_strcmp0 (type, QOF_TYPE_BOOLEAN)) { gboolean val; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_bool (scm)) break; val = scm_is_true (scm); pd = qof_query_boolean_predicate (compare_how, val); } else if (!g_strcmp0 (type, QOF_TYPE_CHAR)) { QofCharMatch options; gchar *char_list; scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (scm_is_null (scm)) break; options = gnc_query_scm2char (scm); scm = SCM_CAR (qt_scm); qt_scm = SCM_CDR (qt_scm); if (!scm_is_string (scm)) break; char_list = gnc_scm_to_utf8_string (scm); pd = qof_query_char_predicate (options, char_list); g_free (char_list); } else { PWARN ("query core type %s not supported", type); break; } g_free (type); } while (FALSE); if (pd) { q = qof_query_create (); qof_query_add_term (q, path, pd, QOF_QUERY_OR); if (inverted) { QofQuery *outq = qof_query_invert (q); qof_query_destroy (q); q = outq; } } else { gnc_query_path_free (path); } return q; } static QofQuery * gnc_scm2query_term_query_v1 (SCM query_term_scm) { gboolean ok = FALSE; gchar * pd_type = NULL; gchar * pr_type = NULL; gboolean sense = FALSE; QofQuery *q = NULL; SCM scm; if (!scm_is_list (query_term_scm) || scm_is_null (query_term_scm)) { PINFO ("null term"); return NULL; } do { /* pd_type */ scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); pd_type = gnc_scm_symbol_to_locale_string (scm); /* pr_type */ if (scm_is_null (query_term_scm)) { PINFO ("null pr_type"); break; } scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); pr_type = gnc_scm_symbol_to_locale_string (scm); /* sense */ if (scm_is_null (query_term_scm)) { PINFO ("null sense"); break; } scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); sense = scm_is_true (scm); q = qof_query_create_for(GNC_ID_SPLIT); if (!g_strcmp0 (pd_type, "pd-date")) { gboolean use_start; gboolean use_end; time64 start; time64 end; /* use_start */ if (scm_is_null (query_term_scm)) { PINFO ("null use_start"); break; } scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); use_start = scm_is_true (scm); /* start */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); start = scm_to_int64 (scm); /* use_end */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); use_end = scm_is_true (scm); /* end */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); end = scm_to_int64 (scm); xaccQueryAddDateMatchTT (q, use_start, start, use_end, end, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pd_type, "pd-amount")) { QofQueryCompare how; QofNumericMatch amt_sgn; double amount; gnc_numeric val; /* how */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); how = gnc_scm2amt_match_how (scm); /* amt_sgn */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); amt_sgn = gnc_query_scm2numericop (scm); /* amount */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); val = gnc_numeric_create (scm_to_int64(scm_numerator(scm)), scm_to_int64(scm_denominator(scm))); if (!g_strcmp0 (pr_type, "pr-price")) { xaccQueryAddSharePriceMatch (q, val, how, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pr_type, "pr-shares")) { xaccQueryAddSharesMatch (q, val, how, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pr_type, "pr-value")) { xaccQueryAddValueMatch (q, val, amt_sgn, how, QOF_QUERY_OR); ok = TRUE; } else { PINFO ("unknown amount predicate: %s", pr_type); } } else if (!g_strcmp0 (pd_type, "pd-account")) { QofGuidMatch how; GList *account_guids; /* how */ if (scm_is_null (query_term_scm)) { PINFO ("pd-account: null how"); break; } scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); how = gnc_scm2acct_match_how (scm); /* account guids */ if (scm_is_null (query_term_scm)) { PINFO ("pd-account: null guids"); break; } scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); account_guids = gnc_scm2guid_glist (scm); xaccQueryAddAccountGUIDMatch (q, account_guids, how, QOF_QUERY_OR); gnc_guid_glist_free (account_guids); ok = TRUE; } else if (!g_strcmp0 (pd_type, "pd-string")) { gboolean case_sens; gboolean use_regexp; gchar *matchstring; /* case_sens */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); case_sens = scm_is_true (scm); /* use_regexp */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); use_regexp = scm_is_true (scm); /* matchstring */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); matchstring = gnc_scm_to_utf8_string (scm); if (!g_strcmp0 (pr_type, "pr-action")) { xaccQueryAddActionMatch (q, matchstring, case_sens, use_regexp, QOF_COMPARE_CONTAINS, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pr_type, "pr-desc")) { xaccQueryAddDescriptionMatch (q, matchstring, case_sens, use_regexp, QOF_COMPARE_CONTAINS, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pr_type, "pr-memo")) { xaccQueryAddMemoMatch (q, matchstring, case_sens, use_regexp, QOF_COMPARE_CONTAINS, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pr_type, "pr-num")) { xaccQueryAddNumberMatch (q, matchstring, case_sens, use_regexp, QOF_COMPARE_CONTAINS, QOF_QUERY_OR); ok = TRUE; } else { PINFO ("Unknown string predicate: %s", pr_type); } g_free (matchstring); } else if (!g_strcmp0 (pd_type, "pd-cleared")) { cleared_match_t how; /* how */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); how = gnc_scm2cleared_match_how (scm); xaccQueryAddClearedMatch (q, how, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pd_type, "pd-balance")) { gboolean how; /* how */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); if (gnc_scm2balance_match_how (scm, &how) == FALSE) break; xaccQueryAddBalanceMatch (q, how, QOF_QUERY_OR); ok = TRUE; } else if (!g_strcmp0 (pd_type, "pd-guid")) { GncGUID guid; QofIdType id_type; /* guid */ if (scm_is_null (query_term_scm)) break; scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); guid = gnc_scm2guid (scm); /* id type */ scm = SCM_CAR (query_term_scm); query_term_scm = SCM_CDR (query_term_scm); id_type = (QofIdType) gnc_scm_to_utf8_string (scm); xaccQueryAddGUIDMatch (q, &guid, id_type, QOF_QUERY_OR); g_free ((void *) id_type); ok = TRUE; } else { PINFO ("Unknown Predicate: %s", pd_type); } g_free (pd_type); g_free (pr_type); } while (FALSE); if (ok) { QofQuery *out_q; if (sense) out_q = q; else { out_q = qof_query_invert (q); qof_query_destroy (q); } return out_q; } qof_query_destroy (q); return NULL; } static QofQuery * gnc_scm2query_term_query (SCM query_term_scm, query_version_t vers) { switch (vers) { case gnc_QUERY_v1: return gnc_scm2query_term_query_v1 (query_term_scm); case gnc_QUERY_v2: return gnc_scm2query_term_query_v2 (query_term_scm); default: return NULL; } } static SCM gnc_query_terms2scm (const GList *terms) { SCM or_terms = SCM_EOL; const GList *or_node; for (or_node = terms; or_node; or_node = or_node->next) { SCM and_terms = SCM_EOL; GList *and_node; for (and_node = or_node->data; and_node; and_node = and_node->next) { QofQueryTerm *qt = and_node->data; SCM qt_scm; qt_scm = gnc_queryterm2scm (qt); and_terms = scm_cons (qt_scm, and_terms); } and_terms = scm_reverse (and_terms); or_terms = scm_cons (and_terms, or_terms); } return scm_reverse (or_terms); } static QofQuery * gnc_scm2query_and_terms (SCM and_terms, query_version_t vers) { QofQuery *q = NULL; if (!scm_is_list (and_terms)) return NULL; while (!scm_is_null (and_terms)) { SCM term; term = SCM_CAR (and_terms); and_terms = SCM_CDR (and_terms); if (!q) q = gnc_scm2query_term_query (term, vers); else { QofQuery *q_and; QofQuery *q_new; q_and = gnc_scm2query_term_query (term, vers); if (q_and) { q_new = qof_query_merge (q, q_and, QOF_QUERY_AND); if (q_new) { qof_query_destroy (q); q = q_new; } } } } return q; } static QofQuery * gnc_scm2query_or_terms (SCM or_terms, query_version_t vers) { QofQuery *q = NULL; if (!scm_is_list (or_terms)) return NULL; q = qof_query_create_for(GNC_ID_SPLIT); while (!scm_is_null (or_terms)) { SCM and_terms; and_terms = SCM_CAR (or_terms); or_terms = SCM_CDR (or_terms); if (!q) q = gnc_scm2query_and_terms (and_terms, vers); else { QofQuery *q_or; QofQuery *q_new; q_or = gnc_scm2query_and_terms (and_terms, vers); if (q_or) { q_new = qof_query_merge (q, q_or, QOF_QUERY_OR); if (q_new) { qof_query_destroy (q); q = q_new; } } } } return q; } static SCM gnc_query_sort2scm (const QofQuerySort *qs) { SCM sort_scm = SCM_EOL; GSList *path; path = qof_query_sort_get_param_path (qs); if (path == NULL) return SCM_BOOL_F; sort_scm = scm_cons (gnc_query_path2scm (path), sort_scm); sort_scm = scm_cons (scm_from_int (qof_query_sort_get_sort_options (qs)), sort_scm); sort_scm = scm_cons (SCM_BOOL (qof_query_sort_get_increasing (qs)), sort_scm); return scm_reverse (sort_scm); } static gboolean gnc_query_scm2sort (SCM sort_scm, GSList **path, gint *options, gboolean *inc) { SCM val; GSList *p; gint o; gboolean i; g_return_val_if_fail (path && options && inc, FALSE); g_return_val_if_fail (*path == NULL, FALSE); /* This is ok -- it means we have an empty sort. Don't do anything */ if (scm_is_bool (sort_scm)) return TRUE; /* Ok, this had better be a list */ if (!scm_is_list (sort_scm)) return FALSE; /* Parse the path, options, and increasing */ val = SCM_CAR (sort_scm); sort_scm = SCM_CDR (sort_scm); if (!scm_is_list (val)) return FALSE; p = gnc_query_scm2path (val); /* options */ val = SCM_CAR (sort_scm); sort_scm = SCM_CDR (sort_scm); if (!scm_is_number (val)) { gnc_query_path_free (p); return FALSE; } o = scm_to_int (val); /* increasing */ val = SCM_CAR (sort_scm); sort_scm = SCM_CDR (sort_scm); if (!scm_is_bool (val)) { gnc_query_path_free (p); return FALSE; } i = scm_is_true (val); /* EOL */ if (!scm_is_null (sort_scm)) { gnc_query_path_free (p); return FALSE; } *path = p; *options = o; *inc = i; return TRUE; } SCM gnc_query2scm (QofQuery *q) { SCM query_scm = SCM_EOL; SCM pair; QofQuerySort *s1, *s2, *s3; if (!q) return SCM_BOOL_F; /* terms */ pair = scm_cons (gnc_query_terms2scm (qof_query_get_terms (q)), SCM_EOL); pair = scm_cons (scm_from_locale_symbol ("terms"), pair); query_scm = scm_cons (pair, query_scm); /* search-for */ pair = scm_cons (scm_from_locale_symbol (qof_query_get_search_for (q)), SCM_EOL); pair = scm_cons (scm_from_locale_symbol ("search-for"), pair); query_scm = scm_cons (pair, query_scm); /* sorts... */ qof_query_get_sorts (q, &s1, &s2, &s3); /* primary-sort */ pair = scm_cons (gnc_query_sort2scm (s1), SCM_EOL); pair = scm_cons (scm_from_locale_symbol ("primary-sort"), pair); query_scm = scm_cons (pair, query_scm); /* secondary-sort */ pair = scm_cons (gnc_query_sort2scm (s2), SCM_EOL); pair = scm_cons (scm_from_locale_symbol ("secondary-sort"), pair); query_scm = scm_cons (pair, query_scm); /* tertiary-sort */ pair = scm_cons (gnc_query_sort2scm (s3), SCM_EOL); pair = scm_cons (scm_from_locale_symbol ("tertiary-sort"), pair); query_scm = scm_cons (pair, query_scm); /* max results */ pair = scm_cons (scm_from_int (qof_query_get_max_results (q)), SCM_EOL); pair = scm_cons (scm_from_locale_symbol ("max-results"), pair); query_scm = scm_cons (pair, query_scm); /* Reverse this list; tag it as 'query-v2' */ pair = scm_reverse (query_scm); return scm_cons (scm_from_locale_symbol ("query-v2"), pair); } static GSList * gnc_query_sort_to_list (const gchar * symbol) { GSList *path = NULL; if (!symbol) return NULL; if (!g_strcmp0 (symbol, "by-none")) { path = NULL; } else if (!g_strcmp0 (symbol, "by-standard")) { path = g_slist_prepend (path, QUERY_DEFAULT_SORT); } else if (!g_strcmp0 (symbol, "by-date") || !g_strcmp0 (symbol, "by-date-rounded")) { path = g_slist_prepend (path, TRANS_DATE_POSTED); path = g_slist_prepend (path, SPLIT_TRANS); } else if (!g_strcmp0 (symbol, "by-date-entered") || !g_strcmp0 (symbol, "by-date-entered-rounded")) { path = g_slist_prepend (path, TRANS_DATE_ENTERED); path = g_slist_prepend (path, SPLIT_TRANS); } else if (!g_strcmp0 (symbol, "by-date-reconciled") || !g_strcmp0 (symbol, "by-date-reconciled-rounded")) { path = g_slist_prepend (path, SPLIT_DATE_RECONCILED); } else if (!g_strcmp0 (symbol, "by-num")) { path = g_slist_prepend (path, TRANS_NUM); path = g_slist_prepend (path, SPLIT_TRANS); } else if (!g_strcmp0 (symbol, "by-amount")) { path = g_slist_prepend (path, SPLIT_VALUE); } else if (!g_strcmp0 (symbol, "by-memo")) { path = g_slist_prepend (path, SPLIT_MEMO); } else if (!g_strcmp0 (symbol, "by-desc")) { path = g_slist_prepend (path, TRANS_DESCRIPTION); path = g_slist_prepend (path, SPLIT_TRANS); } else if (!g_strcmp0 (symbol, "by-reconcile")) { path = g_slist_prepend (path, SPLIT_RECONCILE); } else if (!g_strcmp0 (symbol, "by-account-full-name")) { path = g_slist_prepend (path, SPLIT_ACCT_FULLNAME); } else if (!g_strcmp0 (symbol, "by-account-code")) { path = g_slist_prepend (path, ACCOUNT_CODE_); path = g_slist_prepend (path, SPLIT_ACCOUNT); } else if (!g_strcmp0 (symbol, "by-corr-account-full-name")) { path = g_slist_prepend (path, SPLIT_CORR_ACCT_NAME); } else if (!g_strcmp0 (symbol, "by-corr-account-code")) { path = g_slist_prepend (path, SPLIT_CORR_ACCT_CODE); } else { PERR ("Unknown sort-type, %s", symbol); } return path; } static QofQuery * gnc_scm2query_v1 (SCM query_scm) { QofQuery *q = NULL; gboolean ok = TRUE; gchar * primary_sort = NULL; gchar * secondary_sort = NULL; gchar * tertiary_sort = NULL; gboolean primary_increasing = TRUE; gboolean secondary_increasing = TRUE; gboolean tertiary_increasing = TRUE; int max_splits = -1; while (!scm_is_null (query_scm)) { gchar *symbol; SCM sym_scm; SCM value; SCM pair; pair = SCM_CAR (query_scm); query_scm = SCM_CDR (query_scm); if (!scm_is_pair (pair)) { PERR ("Not a Pair"); ok = FALSE; break; } sym_scm = SCM_CAR (pair); value = SCM_CADR (pair); if (!scm_is_symbol (sym_scm)) { PERR ("Not a symbol"); ok = FALSE; break; } symbol = gnc_scm_symbol_to_locale_string (sym_scm); if (!symbol) { PERR ("No string found"); ok = FALSE; break; } if (g_strcmp0 ("terms", symbol) == 0) { if (q) qof_query_destroy (q); q = gnc_scm2query_or_terms (value, gnc_QUERY_v1); if (!q) { PINFO ("invalid terms"); ok = FALSE; break; } } else if (g_strcmp0 ("primary-sort", symbol) == 0) { if (!scm_is_symbol (value)) { PINFO ("Invalid primary sort"); ok = FALSE; break; } primary_sort = gnc_scm_symbol_to_locale_string (value); } else if (g_strcmp0 ("secondary-sort", symbol) == 0) { if (!scm_is_symbol (value)) { PINFO ("Invalid secondary sort"); ok = FALSE; break; } secondary_sort = gnc_scm_symbol_to_locale_string (value); } else if (g_strcmp0 ("tertiary-sort", symbol) == 0) { if (!scm_is_symbol (value)) { PINFO ("Invalid tertiary sort"); ok = FALSE; break; } tertiary_sort = gnc_scm_symbol_to_locale_string (value); } else if (g_strcmp0 ("primary-increasing", symbol) == 0) { primary_increasing = scm_is_true (value); } else if (g_strcmp0 ("secondary-increasing", symbol) == 0) { secondary_increasing = scm_is_true (value); } else if (g_strcmp0 ("tertiary-increasing", symbol) == 0) { tertiary_increasing = scm_is_true (value); } else if (g_strcmp0 ("max-splits", symbol) == 0) { if (!scm_is_number (value)) { PERR ("invalid max-splits"); ok = FALSE; break; } max_splits = scm_to_int (value); } else { PERR ("Unknown symbol: %s", symbol); ok = FALSE; break; } g_free (symbol); } if (ok) { GSList *s1, *s2, *s3; s1 = gnc_query_sort_to_list (primary_sort); s2 = gnc_query_sort_to_list (secondary_sort); s3 = gnc_query_sort_to_list (tertiary_sort); qof_query_set_sort_order (q, s1, s2, s3); qof_query_set_sort_increasing (q, primary_increasing, secondary_increasing, tertiary_increasing); qof_query_set_max_results (q, max_splits); } else { qof_query_destroy (q); q = NULL; } g_free (primary_sort); g_free (secondary_sort); g_free (tertiary_sort); return q; } static QofQuery * gnc_scm2query_v2 (SCM query_scm) { QofQuery *q = NULL; gboolean ok = TRUE; gchar * search_for = NULL; GSList *sp1 = NULL, *sp2 = NULL, *sp3 = NULL; gint so1 = 0, so2 = 0, so3 = 0; gboolean si1 = TRUE, si2 = TRUE, si3 = TRUE; int max_results = -1; while (!scm_is_null (query_scm)) { gchar *symbol; SCM sym_scm; SCM value; SCM pair; pair = SCM_CAR (query_scm); query_scm = SCM_CDR (query_scm); if (!scm_is_pair (pair)) { ok = FALSE; break; } sym_scm = SCM_CAR (pair); value = SCM_CADR (pair); if (!scm_is_symbol (sym_scm)) { ok = FALSE; break; } symbol = gnc_scm_symbol_to_locale_string (sym_scm); if (!symbol) { ok = FALSE; break; } if (!g_strcmp0 ("terms", symbol)) { if (q) qof_query_destroy (q); q = gnc_scm2query_or_terms (value, gnc_QUERY_v2); if (!q) { ok = FALSE; break; } } else if (!g_strcmp0 ("search-for", symbol)) { if (!scm_is_symbol (value)) { ok = FALSE; break; } search_for = gnc_scm_symbol_to_locale_string (value); } else if (g_strcmp0 ("primary-sort", symbol) == 0) { if (! gnc_query_scm2sort (value, &sp1, &so1, &si1)) { ok = FALSE; break; } } else if (!g_strcmp0 ("secondary-sort", symbol)) { if (! gnc_query_scm2sort (value, &sp2, &so2, &si2)) { ok = FALSE; break; } } else if (!g_strcmp0 ("tertiary-sort", symbol)) { if (! gnc_query_scm2sort (value, &sp3, &so3, &si3)) { ok = FALSE; break; } } else if (!g_strcmp0 ("max-results", symbol)) { if (!scm_is_number (value)) { ok = FALSE; break; } max_results = scm_to_int (value); } else { ok = FALSE; break; } g_free (symbol); } if (ok && search_for) { qof_query_search_for (q, search_for); qof_query_set_sort_order (q, sp1, sp2, sp3); qof_query_set_sort_options (q, so1, so2, so3); qof_query_set_sort_increasing (q, si1, si2, si3); qof_query_set_max_results (q, max_results); } else { qof_query_destroy (q); q = NULL; } g_free (search_for); return q; } QofQuery * gnc_scm2query (SCM query_scm) { SCM q_type; gchar *type; QofQuery *q = NULL; /* Not a list or NULL? No need to go further */ if (!scm_is_list (query_scm) || scm_is_null (query_scm)) return NULL; /* Grab the 'type' (for v2 and above) */ q_type = SCM_CAR (query_scm); if (!scm_is_symbol (q_type)) { if (scm_is_pair (q_type)) { /* Version-1 queries are just a list */ return gnc_scm2query_v1 (query_scm); } else { return NULL; } } /* Ok, the LHS is the version and the RHS is the actual query list */ type = gnc_scm_symbol_to_locale_string (q_type); if (!type) return NULL; if (!g_strcmp0 (type, "query-v2")) q = gnc_scm2query_v2 (SCM_CDR (query_scm)); g_free (type); return q; } gnc_numeric gnc_scm_to_numeric(SCM gncnum) { if (scm_is_signed_integer(scm_numerator(gncnum), INT64_MIN, INT64_MAX) && scm_is_signed_integer(scm_denominator(gncnum), INT64_MIN, INT64_MAX)) return gnc_numeric_create(scm_to_int64(scm_numerator(gncnum)), scm_to_int64(scm_denominator(gncnum))); return gnc_numeric_create(0, GNC_ERROR_OVERFLOW); } SCM gnc_numeric_to_scm(gnc_numeric arg) { return scm_divide(scm_from_int64(arg.num), scm_from_int64(arg.denom)); } static SCM gnc_generic_to_scm(const void *cx, const gchar *type_str) { swig_type_info * stype = NULL; void *x = (void*) cx; if (!x) return SCM_BOOL_F; stype = SWIG_TypeQuery(type_str); if (!stype) { PERR("Unknown SWIG Type: %s ", type_str); return SCM_BOOL_F; } return SWIG_NewPointerObj(x, stype, 0); } static void * gnc_scm_to_generic(SCM scm, const gchar *type_str) { swig_type_info * stype = NULL; stype = SWIG_TypeQuery(type_str); if (!stype) { PERR("Unknown SWIG Type: %s ", type_str); return NULL; } if (!SWIG_IsPointerOfType(scm, stype)) return NULL; return SWIG_MustGetPtr(scm, stype, 1, 0); } gnc_commodity * gnc_scm_to_commodity(SCM scm) { return gnc_scm_to_generic(scm, "_p_gnc_commodity"); } SCM gnc_commodity_to_scm (const gnc_commodity *commodity) { return gnc_generic_to_scm(commodity, "_p_gnc_commodity"); } SCM gnc_book_to_scm (const QofBook *book) { return gnc_generic_to_scm(book, "_p_QofBook"); }