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