00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00061
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;
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
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 *date_param_list, *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
00284
00285
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
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;
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, all_month;
00477 gint adding_days;
00478 gchar *info;
00479
00480
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
00491 qof_date_set_day_start (qd);
00492 data->min_qt = qof_date_to_qtime (qd);
00493
00494 qof_time_add_secs (data->min_qt,
00495 qof_mod_get_local_offset());
00496
00497 if (strlen (date_time) == 4)
00498 {
00499 PINFO (" match entire year %s", date_time);
00500
00501 adding_days = qof_date_isleap(qd->qd_year) ? 365 : 364;
00502 qof_date_adddays (qd, adding_days);
00503 }
00504
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
00512 qof_date_set_day_end (qd);
00513 data->max_qt = qof_date_to_qtime (qd);
00514
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 QofQuery *q;
00545 regex_t *r;
00546 gint reg_exp_check;
00547 const gchar *fmt;
00548 static const gchar *pattern = QOF_SQL_SUPPORTED;
00549
00550 ENTER (" ");
00551 data->sql_file = g_strdup (sql_file);
00552 n = 0;
00553 q = NULL;
00554 data->sql_list = NULL;
00555 filehandle = fopen (sql_file, "r");
00556 if (!filehandle)
00557 {
00558 fmt = _("%s: There was an error reading the file '%s'.\n");
00559 qof_main_wrap_line (stderr, ERR_INDENT, fmt, PACKAGE, sql_file);
00560 return;
00561 }
00562 r = g_new (regex_t, 1);
00563 #ifndef HAVE_GETLINE
00564 while (NULL != (fgets (lineptr, sizeof (lineptr), filehandle)))
00565 #else
00566 lineptr = NULL;
00567 while (0 < getline (&lineptr, &n, filehandle))
00568 #endif
00569 {
00570 reg_exp_check =
00571 regcomp (r, pattern, REG_ICASE | REG_NOSUB | REG_EXTENDED);
00572 g_return_if_fail (reg_exp_check == 0);
00573 if (0 != regexec (r, lineptr, 0, NULL, 0))
00574 continue;
00575 buf = g_strdup (g_strchomp (lineptr));
00576 data->sql_list = g_list_prepend (data->sql_list, buf);
00577 }
00578 regfree (r);
00579 g_free (r);
00580 fclose (filehandle);
00581 LEAVE (" sql_list=%d", g_list_length (data->sql_list));
00582 }
00583
00584 void
00585 qof_mod_write (const gchar * write_file, QofMainContext * data)
00586 {
00587 data->write_file = g_strdup (write_file);
00588 }
00589
00590 void
00591 qof_main_show_error (QofSession * session)
00592 {
00593 gchar *newfile;
00594 const gchar *fmt;
00595 QofErrorId id;
00596
00597 newfile = g_strdup (qof_session_get_file_path (session));
00598 id = qof_error_check (session);
00599 if (id != QOF_SUCCESS)
00600 {
00601 fmt = "%s: %d %s\n";
00602 qof_main_wrap_line (stderr, ERR_INDENT, fmt, PACKAGE,
00603 id, qof_error_get_message (session));
00604 qof_error_clear (session);
00605 }
00606 g_free (newfile);
00607 }
00608
00611