pilot-qof
0.2.3
|
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 ======================*/