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 QofMainContext *context;
00175 QofQueryPredData *time_pred_data;
00176
00177 context = (QofMainContext *) user_data;
00178 if ((param->param_getfcn == NULL) ||
00179 (param->param_setfcn == NULL))
00180 return;
00181 if (0 == safe_strcmp (context->param_type, param->param_type))
00182 {
00183 time_pred_data = qof_query_time_predicate (QOF_COMPARE_GTE,
00184 QOF_DATE_MATCH_NORMAL, context->min_qt);
00185 qof_query_add_term (context->query,
00186 qof_query_build_param_list ((gchar*)param->param_name,
00187 NULL), time_pred_data, QOF_QUERY_AND);
00188 time_pred_data = qof_query_time_predicate (QOF_COMPARE_LTE,
00189 QOF_DATE_MATCH_NORMAL, context->max_qt);
00190 qof_query_add_term (context->query,
00191 qof_query_build_param_list ((gchar*)param->param_name,
00192 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 ((gchar*)param->param_name,
00196 QOF_ID_BOOK, QOF_TYPE_GUID, NULL));
00197 PINFO (" param_name=%s added", param->param_name);
00198 }
00199 LEAVE (" ");
00200 }
00201
00202
00203 static void
00204 build_database_list (QofIdTypeConst obj_type, QofMainContext * context)
00205 {
00206 if (!obj_type || !context)
00207 return;
00208 if (!qof_class_is_registered (obj_type))
00209 return;
00210 ENTER (" object_type=%s", obj_type);
00211 context->query = qof_query_create_for (obj_type);
00212 if (context->category != NULL)
00213 {
00214 QofQueryPredData *category_pred;
00215
00216 category_pred =
00217 qof_query_string_predicate (QOF_COMPARE_EQUAL,
00218 context->category, QOF_STRING_MATCH_CASEINSENSITIVE,
00219 FALSE);
00220 qof_query_add_term (context->query,
00221 qof_query_build_param_list (CATEGORY_NAME, NULL),
00222 category_pred, QOF_QUERY_AND);
00223 }
00224 if (context->min_qt)
00225 {
00226 PINFO (" Preparing a time based queryset.");
00227 context->param_type = QOF_TYPE_TIME;
00228 qof_class_param_foreach (obj_type, find_param_cb, context);
00229 }
00230 else
00231 {
00232 qof_main_run_query (context);
00233 if (context->query)
00234 qof_query_clear (context->query);
00235 }
00236 LEAVE (" ");
00237 }
00238
00239 static void
00240 select_cb (QofObject * obj, gpointer data)
00241 {
00242 QofMainContext *context;
00243
00244 context = (QofMainContext *) data;
00245 g_return_if_fail (context);
00246 if (0 != safe_strcmp (context->exclude, obj->e_type))
00247 build_database_list (obj->e_type, context);
00248 }
00249
00250 void
00251 qof_main_moderate_query (QofMainContext * context)
00252 {
00253 GSList *date_param_list, *category_param_list;
00254 gboolean all;
00255
00256 ENTER (" ");
00257 all = TRUE;
00258 context->query = qof_query_create ();
00259 date_param_list = NULL;
00260 category_param_list = NULL;
00261 while (context->sql_list)
00262 {
00263 PINFO ("running sql_list");
00264 context->sql_str = g_strdup (context->sql_list->data);
00265 qof_main_run_sql (context);
00266 qof_main_run_query (context);
00267 if (context->query)
00268 qof_query_clear (context->query);
00269 g_free (context->sql_str);
00270 context->sql_str = NULL;
00271 all = FALSE;
00272 context->sql_list = g_list_next (context->sql_list);
00273 }
00274 if (0 < g_list_length (context->sql_list))
00275 {
00276 context->sql_str = NULL;
00277 g_list_free (context->sql_list);
00278 all = FALSE;
00279 }
00280 if (context->sql_str != NULL)
00281 {
00282 PINFO ("running sql_str");
00283 qof_main_run_sql (context);
00284 qof_main_run_query (context);
00285 if (context->query)
00286 qof_query_clear (context->query);
00287 all = FALSE;
00288 }
00289 if ((context->exclude != NULL)
00290 && (qof_class_is_registered (context->exclude)))
00291 {
00292 qof_object_foreach_type (select_cb, context);
00293 all = FALSE;
00294 }
00295 if ((context->database != NULL)
00296 && (qof_class_is_registered (context->database)))
00297 {
00298 build_database_list (context->database, context);
00299 all = FALSE;
00300 }
00301 if (all == TRUE)
00302 qof_object_foreach_type (select_cb, context);
00303 LEAVE (" ");
00304 }
00305
00306 static void
00307 option_cb (QofBackendOption * option, gpointer data)
00308 {
00309 QofMainContext *context;
00310
00311 context = (QofMainContext *) data;
00312 g_return_if_fail (context);
00313
00314
00315
00316 ENTER (" compression=%" G_GINT64_FORMAT " encoding=%s",
00317 context->gz_level, context->encoding);
00318 if (0 == safe_strcmp (QSF_COMPRESS, option->option_name))
00319 option->value = (gpointer) & context->gz_level;
00320 if (0 == safe_strcmp (QSF_ENCODING, option->option_name))
00321 option->value = (gpointer) context->encoding;
00322 if (0 == safe_strcmp (QSF_DATE_CONVERT, option->option_name))
00323 option->value = (gpointer) & context->convert;
00324 LEAVE (" ");
00325 }
00326
00327 void
00328 qof_mod_compression (gint64 gz_level, QofMainContext * context)
00329 {
00330 KvpFrame *be_config;
00331 QofBook *book;
00332 QofBackend *be;
00333
00334 ENTER (" compression=%" G_GINT64_FORMAT, gz_level);
00335 if ((gz_level > 0) && (gz_level <= 9))
00336 {
00337 book = qof_session_get_book (context->export_session);
00338 be = qof_book_get_backend (book);
00339 be_config = qof_backend_get_config (be);
00340 context->gz_level = gz_level;
00341 qof_backend_option_foreach (be_config, option_cb, context);
00342 qof_backend_load_config (be, be_config);
00343 }
00344 LEAVE (" ");
00345 }
00346
00347 void
00348 qof_mod_encoding (const gchar * encoding, QofMainContext * context)
00349 {
00350 KvpFrame *be_config;
00351 QofBook *book;
00352 QofBackend *be;
00353
00354 ENTER (" encode to %s", encoding);
00355 book = qof_session_get_book (context->export_session);
00356 be = qof_book_get_backend (book);
00357 be_config = qof_backend_get_config (be);
00358 context->encoding = encoding;
00359 qof_backend_option_foreach (be_config, option_cb, context);
00360 qof_backend_load_config (be, be_config);
00361 LEAVE (" ");
00362 }
00363
00364 void
00365 qof_mod_convert_deprecated (gint64 convert, QofMainContext * context)
00366 {
00367 KvpFrame *be_config;
00368 QofBook *book;
00369 QofBackend *be;
00370 gboolean set;
00371
00372 set = (convert == 0) ? FALSE : TRUE;
00373 ENTER (" convert deprecated date values? %i No if 0.", set);
00374 book = qof_session_get_book (context->export_session);
00375 be = qof_book_get_backend (book);
00376 be_config = qof_backend_get_config (be);
00377 context->convert = convert;
00378 qof_backend_option_foreach (be_config, option_cb, context);
00379 qof_backend_load_config (be, be_config);
00380 LEAVE (" ");
00381 }
00382
00383 void
00384 qof_cmd_xmlfile (QofMainContext * context)
00385 {
00386 QofSession *input_session, *export_session;
00387
00388 ENTER (" ");
00389 input_session = context->input_session;
00390 if (0 == safe_strcmp (context->exclude, context->database)
00391 && (context->exclude != NULL))
00392 {
00393 qof_main_wrap_line (stderr, ERR_INDENT,
00394 _("%s: Error: Cannot exclude database \"%s\" with option -e "
00395 "because option -d is set to include the database: \"%s\". "
00396 "Use the \'-l\' command to see the full list of supported "
00397 "databases.\n"), PACKAGE, context->exclude,
00398 context->database);
00399 qof_session_end (input_session);
00400 LEAVE (" conflicting options");
00401 return;
00402 }
00403 qof_session_begin (input_session, context->filename, TRUE, FALSE);
00404 if (0 != safe_strcmp (QOF_STDOUT, context->filename))
00405 qof_session_load (input_session, NULL);
00406 export_session = qof_session_new ();
00407 context->export_session = export_session;
00408 if (context->write_file)
00409 {
00410 qof_session_begin (export_session, context->write_file, TRUE,
00411 TRUE);
00412 qof_mod_compression (context->gz_level, context);
00413 }
00414 else
00415 qof_session_begin (export_session, QOF_STDOUT, TRUE, TRUE);
00416
00417 qof_mod_encoding (context->encoding, context);
00418 qof_main_moderate_query (context);
00419 qof_session_save (export_session, NULL);
00420 qof_main_show_error (export_session);
00421 qof_main_show_error (input_session);
00422 qof_session_end (input_session);
00423 qof_session_end (export_session);
00424 LEAVE (" ");
00425 }
00426
00427 static void
00428 qof_main_list (QofObject * obj, gpointer data)
00429 {
00430 fprintf (stdout, "%-20s%-20s\n", obj->e_type, obj->type_label);
00431 }
00432
00433 void
00434 qof_main_select (QofMainContext * context)
00435 {
00436 g_return_if_fail (context);
00437 qof_object_foreach_type (select_cb, context);
00438 }
00439
00440 void
00441 qof_cmd_list (void)
00442 {
00443 qof_main_wrap_line (stdout, 0,
00444 _("\n%s: You can use the supported database names with '%s -d' "
00445 "and in SQL queries (as the table name) with '%s -s|f'. "
00446 "Descriptions are shown only for readability.\n"),
00447 PACKAGE, PACKAGE, PACKAGE);
00448 fprintf (stdout, "%-20s%-20s\n", _("Name"), _("Description"));
00449 qof_object_foreach_type (qof_main_list, NULL);
00450 qof_main_wrap_line (stdout, 0,
00451 _("\nUse '%s -d <database> --explain' to see the list of fields "
00452 "within any supported database."), PACKAGE);
00453 fprintf (stdout, _("\nThank you for using %s\n\n"), PACKAGE);
00454 }
00455
00456 static void
00457 explain_cb (QofParam * param, gpointer user_data)
00458 {
00459 if (param->param_getfcn && param->param_setfcn)
00460 fprintf (stdout, _("Type: %s\tName: %s\n"),
00461 param->param_type, param->param_name);
00462 }
00463
00464 void
00465 qof_cmd_explain (QofMainContext * context)
00466 {
00467 if (context->error)
00468 return;
00469 fprintf (stdout, _("\nParameters of the %s database:\n\n"),
00470 context->database);
00471 qof_class_param_foreach (context->database, explain_cb, NULL);
00472 fprintf (stdout, _("\nThank you for using %s\n\n"), PACKAGE);
00473 }
00474
00475 void
00476 qof_mod_category (const gchar * category, QofMainContext * data)
00477 {
00478 data->category = g_strdup (category);
00479 }
00480
00481 glong
00482 qof_mod_get_local_offset (void)
00483 {
00484 glong local_offset;
00485 struct tm local;
00486 time_t now;
00487
00488 local_offset = 0;
00489 now = time (NULL);
00490 local = *localtime_r (&now, &local);
00491 local_offset -= local.tm_gmtoff;
00492 return local_offset;
00493 }
00494
00495 void
00496 qof_mod_database (const gchar * database, QofMainContext * data)
00497 {
00498 if (qof_class_is_registered (database))
00499 data->database = g_strdup (database);
00500 }
00501
00502 void
00503 qof_mod_time (const gchar * date_time, QofMainContext * data)
00504 {
00505 QofDate *qd;
00506 gboolean all_year, all_month;
00507 gint adding_days;
00508 gchar *info;
00509
00510
00511 ENTER (" date_time=%s", date_time);
00512 all_month = all_year = FALSE;
00513 g_return_if_fail (date_time);
00514 qd = qof_date_parse (date_time, QOF_DATE_FORMAT_ISO);
00515 if (!qd)
00516 qd = qof_date_parse (date_time, QOF_DATE_FORMAT_UTC);
00517 info = qof_date_print (qd, QOF_DATE_FORMAT_ISO8601);
00518 PINFO (" parsed start_time=%s", info);
00519 g_free (info);
00520
00521 qof_date_set_day_start (qd);
00522 data->min_qt = qof_date_to_qtime (qd);
00523
00524 qof_time_add_secs (data->min_qt,
00525 qof_mod_get_local_offset());
00526
00527 if (strlen (date_time) == 4)
00528 {
00529 PINFO (" match entire year %s", date_time);
00530
00531 adding_days = qof_date_isleap(qd->qd_year) ? 365 : 364;
00532 qof_date_adddays (qd, adding_days);
00533 }
00534
00535 if (strlen (date_time) == 7)
00536 {
00537 PINFO (" match entire month %s", date_time);
00538 adding_days = qof_date_get_mday (qd->qd_mon, qd->qd_year);
00539 qof_date_adddays (qd, adding_days - 1);
00540 }
00541
00542 qof_date_set_day_end (qd);
00543 data->max_qt = qof_date_to_qtime (qd);
00544
00545 qof_time_add_secs (data->max_qt,
00546 qof_mod_get_local_offset());
00547 LEAVE (" ");
00548 }
00549
00550 void
00551 qof_mod_exclude (const gchar * exclude, QofMainContext * data)
00552 {
00553 if (qof_class_is_registered (exclude))
00554 data->exclude = g_strdup (exclude);
00555 }
00556
00557 void
00558 qof_mod_sql (const gchar * sql_query, QofMainContext * data)
00559 {
00560 data->sql_str = g_strdup (sql_query);
00561 }
00562
00563 void
00564 qof_mod_sql_file (const gchar * sql_file, QofMainContext * data)
00565 {
00566 FILE *filehandle;
00567 #ifndef HAVE_GETLINE
00568 gchar lineptr[1024];
00569 #else
00570 gchar *lineptr;
00571 #endif
00572 gchar *buf;
00573 size_t n;
00574 QofQuery *q;
00575 regex_t *r;
00576 gint reg_exp_check;
00577 const gchar *fmt;
00578 static gchar *pattern = QOF_SQL_SUPPORTED;
00579
00580 ENTER (" ");
00581 data->sql_file = g_strdup (sql_file);
00582 n = 0;
00583 q = NULL;
00584 data->sql_list = NULL;
00585 filehandle = fopen (sql_file, "r");
00586 if (!filehandle)
00587 {
00588 fmt = _("%s: There was an error reading the file '%s'.\n");
00589 qof_main_wrap_line (stderr, ERR_INDENT, fmt, PACKAGE, sql_file);
00590 return;
00591 }
00592 r = g_new (regex_t, 1);
00593 #ifndef HAVE_GETLINE
00594 while (NULL != (fgets (lineptr, sizeof (lineptr), filehandle)))
00595 #else
00596 lineptr = NULL;
00597 while (0 < getline (&lineptr, &n, filehandle))
00598 #endif
00599 {
00600 reg_exp_check =
00601 regcomp (r, pattern, REG_ICASE | REG_NOSUB | REG_EXTENDED);
00602 g_return_if_fail (reg_exp_check == 0);
00603 if (0 != regexec (r, lineptr, 0, NULL, 0))
00604 continue;
00605 buf = g_strdup (g_strchomp (lineptr));
00606 data->sql_list = g_list_prepend (data->sql_list, buf);
00607 }
00608 regfree (r);
00609 g_free (r);
00610 fclose (filehandle);
00611 LEAVE (" sql_list=%d", g_list_length (data->sql_list));
00612 }
00613
00614 void
00615 qof_mod_write (const gchar * write_file, QofMainContext * data)
00616 {
00617 data->write_file = g_strdup (write_file);
00618 }
00619
00620 void
00621 qof_main_show_error (QofSession * session)
00622 {
00623 gchar *newfile;
00624 const gchar *fmt;
00625 QofErrorId id;
00626
00627 newfile = g_strdup (qof_session_get_file_path (session));
00628 id = qof_error_check (session);
00629 if (id != QOF_SUCCESS)
00630 {
00633 fmt = _("%s: %d %s\n");
00634 qof_main_wrap_line (stderr, ERR_INDENT, fmt, PACKAGE,
00635 id, qof_error_get_message (session));
00636 qof_error_clear (session);
00637 }
00638 g_free (newfile);
00639 }
00640
00643