pilot-qof  0.2.3
qof-main.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *            qof-main.c
00003  *
00004  *  Thu Jan 13 10:55:44 2005
00005  *  Copyright  2005-2006  Neil Williams
00006  *  linux@codehelp.co.uk
00007  ****************************************************************************/
00008 /*
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 3 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00021  */
00022 
00034 #define _GNU_SOURCE
00035 #include "config.h"
00036 #include <glib.h>
00037 #include <glib/gi18n.h>
00038 #include <glib/gprintf.h>
00039 #include <qof.h>
00040 #include <stdlib.h>
00041 #include <stdio.h>
00042 #include <regex.h>
00043 #include <time.h>
00044 #include "qof-main.h"
00045 
00046 #define MAX_LINE 79
00047 
00048 /* the debugging module for this file. */
00049 static QofLogModule log_module = QOF_MAIN_CLI;
00050 
00051 void
00052 qof_main_wrap_line (FILE * fp, gint indent, 
00053                     const gchar * template, ...)
00054 {
00055     gint line_length, msg_length;
00056     va_list wraps;
00057     gchar *message;
00058 
00059     line_length = MAX_LINE;
00060     /* note the modulus. Don't use CLAMP here */
00061     /* indent != line_length or particularly close to it. */
00062     indent = indent >= line_length ? indent % line_length : indent;
00063     indent = indent < 0 ? 0 : indent;
00064     message = NULL;
00065     g_return_if_fail (template);
00066     va_start (wraps, template);
00067     message = g_strdup_vprintf (template, wraps);
00068     va_end (wraps);
00069     g_return_if_fail (message);
00070     msg_length = strlen (message);
00071     while (msg_length > line_length)
00072     {
00073         gchar *chunk;
00074         gchar format[16];
00075 
00076         chunk = message + line_length - 1;
00077         while (chunk > message && !g_ascii_isspace (*chunk))
00078             chunk--;
00079         if (chunk == message)
00080             break;              /* give up */
00081         while (chunk > (message + 1) && g_ascii_isspace (*chunk))
00082             chunk--;
00083         chunk++;
00084         g_sprintf (format, "%%.%ds\n%%%ds", (gint) (chunk - message),
00085             indent);
00086         g_fprintf (fp, format, message, "");
00087         message = chunk;
00088         while (g_ascii_isspace (*message) && *message)
00089             message++;
00090         msg_length = strlen (message);
00091         if (line_length == MAX_LINE)
00092             line_length -= indent;
00093     }
00094     if (msg_length)
00095         g_fprintf (fp, "%s\n", message);
00096 }
00097 
00098 static void
00099 qof_main_run_sql (QofMainContext * context)
00100 {
00101     QofSqlQuery *q;
00102     QofBook *book;
00103     gchar *sql;
00104 
00105     ENTER (" ");
00106     q = qof_sql_query_new ();
00107     sql = g_strdup (context->sql_str);
00108     book = qof_session_get_book (context->input_session);
00109     qof_sql_query_set_book (q, book);
00110     qof_sql_query_run (q, sql);
00111     context->query = qof_sql_query_get_query (q);
00112     LEAVE (" ");
00113 }
00114 
00115 static void
00116 qof_main_run_query (QofMainContext * context)
00117 {
00118     QofBook *book;
00119     GList *results;
00120 
00121     ENTER (" ");
00122     results = NULL;
00123     book = qof_session_get_book (context->input_session);
00124     qof_query_set_book (context->query, book);
00125     results = qof_query_run (context->query);
00126     if (results != NULL)
00127         qof_entity_copy_list (context->export_session, results);
00128     LEAVE (" ");
00129 }
00130 
00131 void
00132 qof_main_free (QofMainContext * context)
00133 {
00134     g_free (context->filename);
00135     g_free (context->write_file);
00136     g_free (context->sql_file);
00137     g_free (context->database);
00138     g_free (context->category);
00139 }
00140 
00141 static void
00142 find_param_cb (QofParam * param, gpointer user_data)
00143 {
00144     QofMainContext *context;
00145     QofQueryPredData *time_pred_data;
00146 
00147     context = (QofMainContext *) user_data;
00148     if ((param->param_getfcn == NULL) || 
00149         (param->param_setfcn == NULL))
00150         return;
00151     if (0 == safe_strcmp (context->param_type, param->param_type))
00152     {
00153         time_pred_data = qof_query_time_predicate (QOF_COMPARE_GTE,
00154             QOF_DATE_MATCH_NORMAL, context->min_qt);
00155         qof_query_add_term (context->query, 
00156             qof_query_build_param_list ((gchar*)param->param_name, 
00157             NULL), time_pred_data, QOF_QUERY_AND);
00158         time_pred_data = qof_query_time_predicate (QOF_COMPARE_LTE,
00159             QOF_DATE_MATCH_NORMAL, context->max_qt);
00160         qof_query_add_term (context->query, 
00161             qof_query_build_param_list ((gchar*)param->param_name, 
00162             NULL), time_pred_data, QOF_QUERY_AND);
00163         qof_main_run_query (context);
00164         qof_query_purge_terms (context->query, 
00165             qof_query_build_param_list ((gchar*)param->param_name, 
00166             QOF_ID_BOOK, QOF_TYPE_GUID, NULL));
00167         PINFO (" param_name=%s added", param->param_name);
00168     }
00169     LEAVE (" ");
00170 }
00171 
00172 /* takes one database name and runs -c and -t queries against it. */
00173 static void
00174 build_database_list (QofIdTypeConst obj_type, QofMainContext * context)
00175 {
00176     if (!obj_type || !context)
00177         return;
00178     if (!qof_class_is_registered (obj_type))
00179         return;
00180     ENTER (" object_type=%s", obj_type);
00181     context->query = qof_query_create_for (obj_type);
00182     if (context->category != NULL)
00183     {
00184         QofQueryPredData *category_pred;
00185 
00186         category_pred =
00187             qof_query_string_predicate (QOF_COMPARE_EQUAL,
00188             context->category, QOF_STRING_MATCH_CASEINSENSITIVE, 
00189             FALSE);
00190         qof_query_add_term (context->query, 
00191             qof_query_build_param_list (CATEGORY_NAME, NULL),
00192             category_pred, QOF_QUERY_AND);
00193     }
00194     if (context->min_qt)
00195     {
00196         PINFO (" Preparing a time based queryset.");
00197         context->param_type = QOF_TYPE_TIME;
00198         qof_class_param_foreach (obj_type, find_param_cb, context);
00199     }
00200     else
00201     {
00202         qof_main_run_query (context);
00203         if (context->query)
00204             qof_query_clear (context->query);
00205     }
00206     LEAVE (" ");
00207 }
00208 
00209 static void
00210 select_cb (QofObject * obj, gpointer data)
00211 {
00212     QofMainContext *context;
00213 
00214     context = (QofMainContext *) data;
00215     g_return_if_fail (context);
00216     if (0 != safe_strcmp (context->exclude, obj->e_type))
00217         build_database_list (obj->e_type, context);
00218 }
00219 
00220 void
00221 qof_main_moderate_query (QofMainContext * context)
00222 {
00223     GSList * G_GNUC_UNUSED date_param_list, * G_GNUC_UNUSED category_param_list;
00224     gboolean all;
00225 
00226     ENTER (" ");
00227     all = TRUE;
00228     context->query = qof_query_create ();
00229     date_param_list = NULL;
00230     category_param_list = NULL;
00231     while (context->sql_list)
00232     {
00233         PINFO ("running sql_list");
00234         context->sql_str = g_strdup (context->sql_list->data);
00235         qof_main_run_sql (context);
00236         qof_main_run_query (context);
00237         if (context->query)
00238             qof_query_clear (context->query);
00239         g_free (context->sql_str);
00240         context->sql_str = NULL;
00241         all = FALSE;
00242         context->sql_list = g_list_next (context->sql_list);
00243     }
00244     if (0 < g_list_length (context->sql_list))
00245     {
00246         context->sql_str = NULL;
00247         g_list_free (context->sql_list);
00248         all = FALSE;
00249     }
00250     if (context->sql_str != NULL)
00251     {
00252         PINFO ("running sql_str");
00253         qof_main_run_sql (context);
00254         qof_main_run_query (context);
00255         if (context->query)
00256             qof_query_clear (context->query);
00257         all = FALSE;
00258     }
00259     if ((context->exclude != NULL)
00260         && (qof_class_is_registered (context->exclude)))
00261     {
00262         qof_object_foreach_type (select_cb, context);
00263         all = FALSE;
00264     }
00265     if ((context->database != NULL)
00266         && (qof_class_is_registered (context->database)))
00267     {
00268         build_database_list (context->database, context);
00269         all = FALSE;
00270     }
00271     if (all == TRUE)
00272         qof_object_foreach_type (select_cb, context);
00273     LEAVE (" ");
00274 }
00275 
00276 static void
00277 option_cb (QofBackendOption * option, gpointer data)
00278 {
00279     QofMainContext *context;
00280 
00281     context = (QofMainContext *) data;
00282     g_return_if_fail (context);
00283 /* Normally, I'd use GPOINTER_TO_INT but internally, 
00284 the QofBackendOption uses gint64 which gets mangled by
00285 the 32-bit macro. */
00286     ENTER (" compression=%" G_GINT64_FORMAT " encoding=%s",
00287         context->gz_level, context->encoding);
00288     if (0 == safe_strcmp (QSF_COMPRESS, option->option_name))
00289         option->value = (gpointer) & context->gz_level;
00290     if (0 == safe_strcmp (QSF_ENCODING, option->option_name))
00291         option->value = (gpointer) context->encoding;
00292     if (0 == safe_strcmp (QSF_DATE_CONVERT, option->option_name))
00293         option->value = (gpointer) & context->convert;
00294     LEAVE (" ");
00295 }
00296 
00297 void
00298 qof_mod_compression (gint64 gz_level, QofMainContext * context)
00299 {
00300     KvpFrame *be_config;
00301     QofBook *book;
00302     QofBackend *be;
00303 
00304     ENTER (" compression=%" G_GINT64_FORMAT, gz_level);
00305     if ((gz_level > 0) && (gz_level <= 9))
00306     {
00307         book = qof_session_get_book (context->export_session);
00308         be = qof_book_get_backend (book);
00309         be_config = qof_backend_get_config (be);
00310         context->gz_level = gz_level;
00311         qof_backend_option_foreach (be_config, option_cb, context);
00312         qof_backend_load_config (be, be_config);
00313     }
00314     LEAVE (" ");
00315 }
00316 
00317 void
00318 qof_mod_encoding (const gchar * encoding, QofMainContext * context)
00319 {
00320     KvpFrame *be_config;
00321     QofBook *book;
00322     QofBackend *be;
00323 
00324     ENTER (" encode to %s", encoding);
00325     book = qof_session_get_book (context->export_session);
00326     be = qof_book_get_backend (book);
00327     be_config = qof_backend_get_config (be);
00328     context->encoding = encoding;
00329     qof_backend_option_foreach (be_config, option_cb, context);
00330     qof_backend_load_config (be, be_config);
00331     LEAVE (" ");
00332 }
00333 
00334 void
00335 qof_mod_convert_deprecated (gint64 convert, QofMainContext * context)
00336 {
00337     KvpFrame *be_config;
00338     QofBook *book;
00339     QofBackend *be;
00340     gboolean set;
00341 
00342     set = (convert == 0) ? FALSE : TRUE;
00343     ENTER (" convert deprecated date values? %i No if 0.", set);
00344     book = qof_session_get_book (context->export_session);
00345     be = qof_book_get_backend (book);
00346     be_config = qof_backend_get_config (be);
00347     context->convert = convert;
00348     qof_backend_option_foreach (be_config, option_cb, context);
00349     qof_backend_load_config (be, be_config);
00350     LEAVE (" ");
00351 }
00352 
00353 void
00354 qof_cmd_xmlfile (QofMainContext * context)
00355 {
00356     QofSession *input_session, *export_session;
00357 
00358     ENTER (" ");
00359     input_session = context->input_session;
00360     if (0 == safe_strcmp (context->exclude, context->database)
00361         && (context->exclude != NULL))
00362     {
00363         qof_main_wrap_line (stderr, ERR_INDENT,
00364             _("%s: Error: Cannot exclude database \"%s\" with option -e "
00365                 "because option -d is set to include the database: \"%s\". "
00366                 "Use the \'-l\' command to see the full list of supported "
00367                 "databases.\n"), PACKAGE, context->exclude,
00368             context->database);
00369         qof_session_end (input_session);
00370         LEAVE (" conflicting options");
00371         return;
00372     }
00373     qof_session_begin (input_session, context->filename, TRUE, FALSE);
00374     if (0 != safe_strcmp (QOF_STDOUT, context->filename))
00375         qof_session_load (input_session, NULL);
00376     export_session = qof_session_new ();
00377     context->export_session = export_session;
00378     if (context->write_file)
00379     {
00380         qof_session_begin (export_session, context->write_file, TRUE,
00381             TRUE);
00382         qof_mod_compression (context->gz_level, context);
00383     }
00384     else
00385         qof_session_begin (export_session, QOF_STDOUT, TRUE, FALSE);
00386     /* ensure encoding value is set in the new export_session */
00387     qof_mod_encoding (context->encoding, context);
00388     qof_main_moderate_query (context);
00389     qof_session_save (export_session, NULL);
00390     qof_main_show_error (export_session);
00391     qof_main_show_error (input_session);
00392     qof_session_end (input_session);
00393     qof_session_end (export_session);
00394     LEAVE (" ");
00395 }
00396 
00397 static void
00398 qof_main_list (QofObject * obj, gpointer data)
00399 {
00400     fprintf (stdout, "%-20s%-20s\n", obj->e_type, obj->type_label);
00401 }
00402 
00403 void
00404 qof_main_select (QofMainContext * context)
00405 {
00406     g_return_if_fail (context);
00407     qof_object_foreach_type (select_cb, context);
00408 }
00409 
00410 void
00411 qof_cmd_list (void)
00412 {
00413     qof_main_wrap_line (stdout, 0,
00414         _("\n%s: You can use the supported database names with '%s -d' "
00415             "and in SQL queries (as the table name) with '%s -s|f'. "
00416             "Descriptions are shown only for readability.\n"),
00417         PACKAGE, PACKAGE, PACKAGE);
00418     fprintf (stdout, "%-20s%-20s\n", _("Name"), _("Description"));
00419     qof_object_foreach_type (qof_main_list, NULL);
00420     qof_main_wrap_line (stdout, 0,
00421         _("\nUse '%s -d <database> --explain' to see the list of fields "
00422             "within any supported database."), PACKAGE);
00423     fprintf (stdout, _("\nThank you for using %s\n\n"), PACKAGE);
00424 }
00425 
00426 static void
00427 explain_cb (QofParam * param, gpointer user_data)
00428 {
00429     if (param->param_getfcn && param->param_setfcn)
00430         fprintf (stdout, _("Type: %s\tName: %s\n"),
00431             param->param_type, param->param_name);
00432 }
00433 
00434 void
00435 qof_cmd_explain (QofMainContext * context)
00436 {
00437     if (context->error)
00438         return;
00439     fprintf (stdout, _("\nParameters of the %s database:\n\n"),
00440         context->database);
00441     qof_class_param_foreach (context->database, explain_cb, NULL);
00442     fprintf (stdout, _("\nThank you for using %s\n\n"), PACKAGE);
00443 }
00444 
00445 void
00446 qof_mod_category (const gchar * category, QofMainContext * data)
00447 {
00448     data->category = g_strdup (category);
00449 }
00450 
00451 glong
00452 qof_mod_get_local_offset (void)
00453 {
00454     glong local_offset;
00455     struct tm local;
00456     time_t now;
00457 
00458     local_offset = 0; /* UTC */
00459     now = time (NULL);
00460     local = *localtime_r (&now, &local);
00461     local_offset -= local.tm_gmtoff;
00462     return local_offset;
00463 }
00464 
00465 void
00466 qof_mod_database (const gchar * database, QofMainContext * data)
00467 {
00468     if (qof_class_is_registered (database))
00469         data->database = g_strdup (database);
00470 }
00471 
00472 void
00473 qof_mod_time (const gchar * date_time, QofMainContext * data)
00474 {
00475     QofDate *qd;
00476     gboolean all_year, G_GNUC_UNUSED all_month;
00477     gint adding_days;
00478     gchar *info;
00479 
00480     /* incoming date is assumed to be localtime */
00481     ENTER (" date_time=%s", date_time);
00482     all_month = all_year = FALSE;
00483     g_return_if_fail (date_time);
00484     qd = qof_date_parse (date_time, QOF_DATE_FORMAT_ISO);
00485     if (!qd)
00486         qd = qof_date_parse (date_time, QOF_DATE_FORMAT_UTC);
00487     info = qof_date_print (qd, QOF_DATE_FORMAT_ISO8601);
00488     PINFO (" parsed start_time=%s", info);
00489     g_free (info);
00490     /* set first second of day, UTC */
00491     qof_date_set_day_start (qd);
00492     data->min_qt = qof_date_to_qtime (qd);
00493     /* adjust for incoming localtime */
00494     qof_time_add_secs (data->min_qt, 
00495         qof_mod_get_local_offset());
00496     /* year specified but no month or day, select the entire year */
00497     if (strlen (date_time) == 4)
00498     {
00499         PINFO (" match entire year %s", date_time);
00500         /* go to end of this year, not first day of next. */
00501         adding_days = qof_date_isleap(qd->qd_year) ? 365 : 364;
00502         qof_date_adddays (qd, adding_days);
00503     }
00504     /* month specified, but no day, select entire month */
00505     if (strlen (date_time) == 7)
00506     {
00507         PINFO (" match entire month %s", date_time);
00508         adding_days = qof_date_get_mday (qd->qd_mon, qd->qd_year);
00509         qof_date_adddays (qd, adding_days - 1);
00510     }
00511     /* set last second of day */
00512     qof_date_set_day_end (qd);
00513     data->max_qt = qof_date_to_qtime (qd);
00514     /* adjust for incoming localtime */
00515     qof_time_add_secs (data->max_qt, 
00516         qof_mod_get_local_offset());
00517     LEAVE (" ");
00518 }
00519 
00520 void
00521 qof_mod_exclude (const gchar * exclude, QofMainContext * data)
00522 {
00523     if (qof_class_is_registered (exclude))
00524         data->exclude = g_strdup (exclude);
00525 }
00526 
00527 void
00528 qof_mod_sql (const gchar * sql_query, QofMainContext * data)
00529 {
00530     data->sql_str = g_strdup (sql_query);
00531 }
00532 
00533 void
00534 qof_mod_sql_file (const gchar * sql_file, QofMainContext * data)
00535 {
00536     FILE *filehandle;
00537 #ifndef HAVE_GETLINE
00538     gchar lineptr[1024];
00539 #else
00540     gchar *lineptr;
00541 #endif
00542     gchar *buf;
00543     size_t n;
00544     regex_t *r;
00545     gint reg_exp_check;
00546     const gchar *fmt;
00547     static const gchar *pattern = QOF_SQL_SUPPORTED;
00548 
00549     ENTER (" ");
00550     data->sql_file = g_strdup (sql_file);
00551     n = 0;
00552     data->sql_list = NULL;
00553     filehandle = fopen (sql_file, "r");
00554     if (!filehandle)
00555     {
00556         fmt = _("%s: There was an error reading the file '%s'.\n");
00557         qof_main_wrap_line (stderr, ERR_INDENT, fmt, PACKAGE, sql_file);
00558         return;
00559     }
00560     r = g_new (regex_t, 1);
00561 #ifndef HAVE_GETLINE
00562     while (NULL != (fgets (lineptr, sizeof (lineptr), filehandle)))
00563 #else
00564     lineptr = NULL;
00565     while (0 < getline (&lineptr, &n, filehandle))
00566 #endif
00567     {
00568         reg_exp_check =
00569             regcomp (r, pattern, REG_ICASE | REG_NOSUB | REG_EXTENDED);
00570         g_return_if_fail (reg_exp_check == 0);
00571         if (0 != regexec (r, lineptr, 0, NULL, 0))
00572             continue;
00573         buf = g_strdup (g_strchomp (lineptr));
00574         data->sql_list = g_list_prepend (data->sql_list, buf);
00575     }
00576     regfree (r);
00577     g_free (r);
00578     fclose (filehandle);
00579     LEAVE (" sql_list=%d", g_list_length (data->sql_list));
00580 }
00581 
00582 void
00583 qof_mod_write (const gchar * write_file, QofMainContext * data)
00584 {
00585     data->write_file = g_strdup (write_file);
00586 }
00587 
00588 void
00589 qof_main_show_error (QofSession * session)
00590 {
00591     gchar *newfile;
00592     const gchar *fmt;
00593     QofErrorId id;
00594 
00595     newfile = g_strdup (qof_session_get_file_path (session));
00596     id = qof_error_check (session);
00597     if (id != QOF_SUCCESS)
00598     {
00599         fmt = "%s: %d %s\n";
00600         qof_main_wrap_line (stderr, ERR_INDENT, fmt, PACKAGE,
00601             id, qof_error_get_message (session));
00602         qof_error_clear (session);
00603     }
00604     g_free (newfile);
00605 }
00606 
00609 /*==================== END OF FILE ======================*/