Introspection update
[gnumeric.git] / src / ssconvert.c
blobaddb9a4920bfd001fef877d9111adfe809938ea9
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * ssconvert.c: A wrapper application to convert spreadsheet formats
5 * Author:
6 * Jon Kåre Hellan <hellan@acm.org>
7 * Morten Welinder <terra@gnome.org>
8 * Jody Goldberg <jody@gnome.org>
10 * Copyright (C) 2002-2003 Jody Goldberg
11 * Copyright (C) 2006-2009 Morten Welinder (terra@gnome.org)
13 #include <gnumeric-config.h>
14 #include <glib/gi18n.h>
15 #include "gnumeric.h"
16 #include "position.h"
17 #include "parse-util.h"
18 #include "application.h"
19 #include "workbook.h"
20 #include "workbook-priv.h"
21 #include "workbook-control.h"
22 #include "sheet.h"
23 #include "dependent.h"
24 #include "expr-name.h"
25 #include "libgnumeric.h"
26 #include "gutils.h"
27 #include "value.h"
28 #include "commands.h"
29 #include "gnumeric-paths.h"
30 #include "gnm-plugin.h"
31 #include "command-context.h"
32 #include "command-context-stderr.h"
33 #include "workbook-view.h"
34 #include "tools/analysis-tools.h"
35 #include <dialogs/dialogs.h>
36 #include <goffice/goffice.h>
37 #include <gsf/gsf-utils.h>
38 #include <string.h>
39 #ifdef HAVE_SYS_RESOURCE_H
40 #include <sys/resource.h>
41 #endif
43 static gboolean ssconvert_show_version = FALSE;
44 static gboolean ssconvert_verbose = FALSE;
45 static gboolean ssconvert_list_exporters = FALSE;
46 static gboolean ssconvert_list_importers = FALSE;
47 static gboolean ssconvert_one_file_per_sheet = FALSE;
48 static gboolean ssconvert_recalc = FALSE;
49 static gboolean ssconvert_solve = FALSE;
50 static char *ssconvert_resize = NULL;
51 static char *ssconvert_range = NULL;
52 static char *ssconvert_import_encoding = NULL;
53 static char *ssconvert_import_id = NULL;
54 static char *ssconvert_export_id = NULL;
55 static char *ssconvert_export_options = NULL;
56 static char *ssconvert_merge_target = NULL;
57 static char **ssconvert_goal_seek = NULL;
58 static char **ssconvert_tool_test = NULL;
60 static const GOptionEntry ssconvert_options [] = {
62 "version", 0,
63 0, G_OPTION_ARG_NONE, &ssconvert_show_version,
64 N_("Display program version"),
65 NULL
69 "verbose", 'v',
70 0, G_OPTION_ARG_NONE, &ssconvert_verbose,
71 N_("Be somewhat more verbose during conversion"),
72 NULL
75 /* ---------------------------------------- */
78 "import-encoding", 'E',
79 0, G_OPTION_ARG_STRING, &ssconvert_import_encoding,
80 N_("Optionally specify an encoding for imported content"),
81 N_("ENCODING")
85 "import-type", 'I',
86 0, G_OPTION_ARG_STRING, &ssconvert_import_id,
87 N_("Optionally specify which importer to use"),
88 N_("ID")
92 "list-importers", 0,
93 0, G_OPTION_ARG_NONE, &ssconvert_list_importers,
94 N_("List the available importers"),
95 NULL
98 /* ---------------------------------------- */
101 "merge-to", 'M',
102 0, G_OPTION_ARG_STRING, &ssconvert_merge_target,
103 N_("Merge listed files (all same format) to make this file"),
104 N_("file")
108 "export-type", 'T',
109 0, G_OPTION_ARG_STRING, &ssconvert_export_id,
110 N_("Optionally specify which exporter to use"),
111 N_("ID")
115 "export-options", 'O',
116 0, G_OPTION_ARG_STRING, &ssconvert_export_options,
117 N_("Detailed instructions for the chosen exporter"),
118 N_("string")
122 "list-exporters", 0,
123 0, G_OPTION_ARG_NONE, &ssconvert_list_exporters,
124 N_("List the available exporters"),
125 NULL
129 "export-file-per-sheet", 'S',
130 0, G_OPTION_ARG_NONE, &ssconvert_one_file_per_sheet,
131 N_("Export a file for each sheet if the exporter only supports one sheet at a time"),
132 NULL
136 "recalc", 0,
137 0, G_OPTION_ARG_NONE, &ssconvert_recalc,
138 N_("Recalculate all cells before writing the result"),
139 NULL
143 "resize", 0,
144 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &ssconvert_resize,
145 N_("Resize to given ROWSxCOLS"),
146 NULL
150 /* ---------------------------------------- */
152 /* For now these are for INTERNAL GNUMERIC USE ONLY. */
154 "export-range", 0,
155 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &ssconvert_range,
156 N_("The range to export"),
157 NULL
161 "goal-seek", 0,
162 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &ssconvert_goal_seek,
163 N_("Goal seek areas"),
164 NULL
168 "solve", 0,
169 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &ssconvert_solve,
170 N_("Run the solver"),
171 NULL
175 "tool-test", 0,
176 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &ssconvert_tool_test,
177 N_("Tool test specs"),
178 NULL
181 /* ---------------------------------------- */
183 { NULL }
186 static void
187 setup_range (GObject *obj, const char *key, Workbook *wb, const char *rtxt)
189 GnmParsePos pp;
190 const char *end;
191 GnmRangeRef rr;
193 pp.wb = wb;
194 pp.sheet = workbook_sheet_by_index (wb, 0);
195 pp.eval.col = 0;
196 pp.eval.row = 0;
198 end = rangeref_parse (&rr, rtxt, &pp, gnm_conventions_default);
199 if (!end || end == rtxt || *end != 0) {
200 g_printerr ("Invalid range specified.\n");
201 exit (1);
204 g_object_set_data_full (obj, key,
205 g_memdup (&rr, sizeof (rr)),
206 g_free);
209 static int
210 handle_export_options (GOFileSaver *fs, GODoc *doc)
212 guint sig = g_signal_lookup ("set-export-options",
213 G_TYPE_FROM_INSTANCE (fs));
215 if (!ssconvert_export_options)
216 return 0;
218 if (g_signal_handler_find (fs, G_SIGNAL_MATCH_ID,
219 sig, 0, NULL, NULL, NULL)) {
220 GError *err = NULL;
221 gboolean fail =
222 go_file_saver_set_export_options
223 (fs, doc,
224 ssconvert_export_options,
225 &err);
227 if (fail) {
228 g_printerr ("ssconvert: %s\n", err
229 ? err->message
230 : _("Cannot parse export options."));
231 return 1;
234 return 0;
235 } else {
236 g_printerr (_("The file saver does not take options\n"));
237 return 1;
242 typedef gchar const *(*get_desc_f)(void *);
244 static void
245 list_them (GList *them,
246 get_desc_f get_his_id,
247 get_desc_f get_his_description)
249 GList *ptr;
250 guint len = 0;
251 gboolean interactive;
253 for (ptr = them; ptr ; ptr = ptr->next) {
254 GObject *obj = ptr->data;
255 char const *id;
257 g_object_get (obj, "interactive-only", &interactive, NULL);
258 if (interactive)
259 continue;
261 id = get_his_id (obj);
262 if (!id) id = "";
263 len = MAX (len, strlen (id));
266 g_printerr ("%-*s | %s\n", len,
267 /* Translate these? */
268 "ID",
269 "Description");
270 for (ptr = them; ptr ; ptr = ptr->next) {
271 GObject *obj = ptr->data;
272 char const *id;
274 g_object_get (obj, "interactive-only", &interactive, NULL);
275 if (interactive)
276 continue;
278 id = get_his_id (obj);
279 if (!id) id = "";
280 g_printerr ("%-*s | %s\n", len,
282 (*get_his_description) (ptr->data));
287 * Read the files we're going to merge and return a list of Workbooks.
289 static GSList *
290 read_files_to_merge (const char *inputs[], GOFileOpener *fo,
291 GOIOContext *io_context, GOCmdContext *cc)
293 GSList *wbs = NULL;
295 while (*inputs) {
296 const char *fname = *inputs;
297 char *uri = go_shell_arg_to_uri (fname);
298 WorkbookView *wbv =
299 workbook_view_new_from_uri (uri, fo, io_context,
300 ssconvert_import_encoding);
301 g_free (uri);
302 inputs++;
304 if (go_io_error_occurred (io_context)) {
305 g_slist_free_full (wbs, g_object_unref);
306 return NULL;
309 if (!wbv)
310 continue;
312 wbs = g_slist_prepend (wbs, wb_view_get_workbook (wbv));
315 return g_slist_reverse (wbs);
319 * Look at a set of workbooks, and pick a sheet size that would
320 * be good for sheets in a workbook merging them all.
322 static void
323 suggest_size (GSList *wbs, int *csuggest, int *rsuggest)
325 GSList *l;
326 int rmax = 0;
327 int cmax = 0;
329 for (l = wbs; l; l = l->next) {
330 Workbook *wb = l->data;
332 WORKBOOK_FOREACH_SHEET (wb, sheet, {
333 int r = gnm_sheet_get_max_rows (sheet);
334 int c = gnm_sheet_get_max_cols (sheet);
335 if (r > rmax) rmax = r;
336 if (c > cmax) cmax = c;
340 gnm_sheet_suggest_size (&cmax, &rmax);
341 *csuggest = cmax;
342 *rsuggest = rmax;
345 static void
346 cb_fixup_name_wb (G_GNUC_UNUSED gconstpointer key,
347 GnmNamedExpr *nexpr,
348 Workbook *wb)
350 GnmParsePos newpos = nexpr->pos;
352 if (!expr_name_is_active (nexpr))
353 return;
355 if (nexpr->pos.wb) {
356 newpos.wb = wb;
357 expr_name_set_pos (nexpr, &newpos);
362 /* Append the sheets of workbook wb2 to workbook wb. Resize sheets
363 if necessary. Fix workbook links in sheet if necessary.
364 Merge names in workbook scope (conflicts result in an error). */
365 static gboolean
366 merge_single (Workbook *wb, Workbook *wb2,
367 int cmax, int rmax,
368 GOCmdContext *cc)
370 /* Move names with workbook scope in wb2 over to wb */
371 GSList *names = g_slist_sort (gnm_named_expr_collection_list (wb2->names),
372 (GCompareFunc)expr_name_cmp_by_name);
373 GSList *p;
375 for (p = names; p; p = p->next) {
376 GnmNamedExpr *nexpr = p->data;
377 const char *name = expr_name_name (nexpr);
378 GnmNamedExpr *nexpr2;
379 GnmParsePos pp;
380 GnmParsePos newpos = nexpr->pos;
382 if (!expr_name_is_active (nexpr))
383 continue;
385 if (nexpr->pos.wb != wb2 || nexpr->pos.sheet != NULL)
386 continue;
388 /* Check for clash with existing name */
390 parse_pos_init (&pp, wb, NULL, 0, 0);
391 nexpr2 = expr_name_lookup (&pp, name);
392 if (nexpr2 /* FIXME: && nexpr2-is-not-the-same-as-nexpr */) {
393 g_printerr (_("Name conflict during merge: '%s' appears twice at workbook scope.\n"),
394 name);
395 g_slist_free (names);
396 return TRUE;
399 /* Move name scope to workbook wb */
400 newpos.wb = wb;
401 expr_name_set_pos (nexpr, &newpos);
403 g_slist_free (names);
405 while (workbook_sheet_count (wb2) > 0) {
406 /* Remove sheet from incoming workbook */
407 Sheet *sheet = workbook_sheet_by_index (wb2, 0);
408 int loc = workbook_sheet_count (wb);
409 GOUndo *undo;
410 char *sheet_name;
411 gboolean err;
413 g_object_ref (sheet);
414 workbook_sheet_delete (sheet);
415 sheet->workbook = wb;
417 /* Fix names that reference the old workbook */
418 gnm_sheet_foreach_name (sheet, (GHFunc)cb_fixup_name_wb, wb);
420 undo = gnm_sheet_resize (sheet, cmax, rmax, cc, &err);
421 if (undo)
422 g_object_unref (undo);
424 /* Pick a free sheet name */
425 sheet_name = workbook_sheet_get_free_name
426 (wb, sheet->name_unquoted, FALSE, TRUE);
427 g_object_set (sheet, "name", sheet_name, NULL);
428 g_free (sheet_name);
430 /* Insert and revive the sheet */
431 workbook_sheet_attach_at_pos (wb, sheet, loc);
432 dependents_revive_sheet (sheet);
433 g_object_unref (sheet);
436 return FALSE;
439 /* Merge a collection of workbooks into one. */
440 static gboolean
441 merge (Workbook *wb, char const *inputs[],
442 GOFileOpener *fo, GOIOContext *io_context, GOCmdContext *cc)
444 GSList *wbs, *l;
445 int result = 0;
446 int cmax, rmax;
448 wbs = read_files_to_merge (inputs, fo, io_context, cc);
449 if (go_io_error_occurred (io_context)) {
450 go_io_error_display (io_context);
451 return TRUE;
454 suggest_size (wbs, &cmax, &rmax);
456 for (l = wbs; l; l = l->next) {
457 Workbook *wb2 = l->data;
458 const char *uri = go_doc_get_uri (GO_DOC (wb2));
460 g_printerr ("Adding sheets from %s\n", uri);
462 result = merge_single (wb, wb2, cmax, rmax, cc);
463 if (result)
464 break;
467 g_slist_free_full (wbs, g_object_unref);
468 return result;
471 static char *
472 resolve_template (const char *template, Sheet *sheet)
474 GString *s = g_string_new (NULL);
475 while (1) {
476 switch (*template) {
477 done:
478 case 0: {
479 char *res = go_shell_arg_to_uri (s->str);
480 g_string_free (s, TRUE);
481 return res;
483 case '%':
484 template++;
485 switch (*template) {
486 case 0:
487 goto done;
488 case 'n':
489 g_string_append_printf (s, "%d", sheet->index_in_wb);
490 break;
491 case 's':
492 g_string_append (s, sheet->name_unquoted);
493 break;
494 case '%':
495 g_string_append_c (s, '%');
496 break;
498 template++;
499 break;
500 default:
501 g_string_append_c (s, *template);
502 template++;
507 static void
508 run_solver (Sheet *sheet, WorkbookView *wbv)
510 GnmSolverParameters *params = sheet->solver_parameters;
511 GError *err = NULL;
512 WorkbookControl *wbc;
513 GnmSolver *sol = NULL;
515 wbc = g_object_new (GNM_WBC_TYPE, NULL);
516 wb_control_set_view (wbc, wbv, NULL);
518 /* Pick a functional algorithm. */
519 if (!gnm_solver_factory_functional (params->options.algorithm,
520 NULL)) {
521 GSList *l;
522 for (l = gnm_solver_db_get (); l; l = l->next) {
523 GnmSolverFactory *factory = l->data;
524 if (params->options.model_type != factory->type)
525 continue;
526 if (gnm_solver_factory_functional (factory, NULL)) {
527 gnm_solver_param_set_algorithm (params,
528 factory);
529 break;
534 if (!gnm_solver_param_valid (params, &err))
535 goto done;
537 sol = params->options.algorithm
538 ? gnm_solver_factory_create (params->options.algorithm, params)
539 : NULL;
540 if (!sol) {
541 g_set_error (&err, go_error_invalid (), 0,
542 _("Failed to create solver"));
543 goto done;
546 if (!gnm_solver_start (sol, wbc, &err))
547 goto done;
549 while (!gnm_solver_finished (sol)) {
550 g_main_context_iteration (NULL, TRUE);
553 switch (sol->status) {
554 case GNM_SOLVER_STATUS_DONE:
555 break;
556 case GNM_SOLVER_STATUS_CANCELLED:
557 g_printerr ("Solver reached time or iteration limit\n");
558 break;
559 default:
560 g_set_error (&err, go_error_invalid (), 0,
561 _("Solver ran, but failed"));
562 goto done;
565 gnm_solver_store_result (sol);
567 gnm_solver_create_report (sol, "Solver");
569 done:
570 if (sol)
571 g_object_unref (sol);
572 if (err) {
573 g_printerr (_("Solver: %s\n"), err->message);
574 g_error_free (err);
578 #define GET_ARG(conv_,name_,def_) (g_hash_table_lookup_extended(args,(name_),NULL,&arg) ? conv_((const char *)arg) : (def_))
579 #define RANGE_ARG(s_) value_new_cellrange_str(sheet,(s_))
580 #define RANGE_LIST_ARG(s_) g_slist_prepend (NULL, value_new_cellrange_str(sheet,(s_)))
581 #define SHEET_ARG(s_) workbook_sheet_by_name(wb,(s_))
583 static void
584 run_tool_test (const char *tool, char **argv, WorkbookView *wbv)
586 int i;
587 WorkbookControl *wbc;
588 gpointer specs;
589 data_analysis_output_t *dao;
590 analysis_tool_engine engine;
591 Workbook *wb;
592 Sheet *sheet;
593 GHashTable *args;
594 gpointer arg;
597 * Arguments in argv are of the form key:value.
598 * Make a hash for those.
600 args = g_hash_table_new_full (g_str_hash, g_str_equal,
601 (GDestroyNotify)g_free,
602 (GDestroyNotify)g_free);
603 for (i = 0; argv[i]; i++) {
604 const char *s = argv[i];
605 const char *colon = strchr (s, ':');
606 if (!colon) {
607 g_printerr ("Ignoring tool test argument \"%s\"\n", s);
608 continue;
610 g_hash_table_replace (args, g_strndup (s, colon - s),
611 g_strdup (colon + 1));
614 wb = wb_view_get_workbook (wbv);
615 wbc = g_object_new (GNM_WBC_TYPE, NULL);
616 wb_control_set_view (wbc, wbv, NULL);
618 sheet = GET_ARG (SHEET_ARG, "sheet", wb_view_cur_sheet (wbv));
620 if (g_str_equal (tool, "regression")) {
621 analysis_tools_data_regression_t *data =
622 g_new0 (analysis_tools_data_regression_t, 1);
624 data->base.wbc = wbc;
625 data->base.range_1 = GET_ARG (RANGE_ARG, "x", value_new_error_REF (NULL));
626 data->base.range_2 = GET_ARG (RANGE_ARG, "y", value_new_error_REF (NULL));
627 data->base.labels = GET_ARG (atoi, "labels", FALSE);
628 data->base.alpha = GET_ARG (atof, "alpha", 0.05);
629 data->group_by = GET_ARG ((group_by_t), "grouped-by", GROUPED_BY_COL);
630 data->intercept = GET_ARG (atoi, "intercept", TRUE);
631 data->multiple_regression = GET_ARG (atoi, "multiple", TRUE);
632 data->multiple_y = GET_ARG (atoi, "multiple-y", FALSE);
633 data->residual = GET_ARG (atoi, "residual", TRUE);
635 engine = analysis_tool_regression_engine;
636 specs = data;
637 } else if (g_str_equal (tool, "anova")) {
638 analysis_tools_data_anova_single_t *data =
639 g_new0 (analysis_tools_data_anova_single_t, 1);
641 data->base.input = GET_ARG (RANGE_LIST_ARG, "data", NULL);
642 data->base.labels = GET_ARG (atoi, "labels", FALSE);
643 data->base.group_by = GET_ARG ((group_by_t), "grouped-by", GROUPED_BY_COL);
644 data->alpha = GET_ARG (atof, "alpha", 0.05);
646 engine = analysis_tool_anova_single_engine;
647 specs = data;
648 } else {
649 g_printerr ("no test for tool \"%s\"\n", tool);
650 return;
653 dao = dao_init_new_sheet (NULL);
654 dao->put_formulas = TRUE;
655 cmd_analysis_tool (wbc, sheet, dao, specs, engine, TRUE);
657 g_hash_table_destroy (args);
660 #undef GET_ARG
661 #undef RANGE_ARG
662 #undef RANGE_LISTARG
663 #undef SHEET_ARG
665 static int
666 convert (char const *inarg, char const *outarg, char const *mergeargs[],
667 GOCmdContext *cc)
669 int res = 0;
670 GOFileSaver *fs = NULL;
671 GOFileOpener *fo = NULL;
672 char *infile = go_shell_arg_to_uri (inarg);
673 char *outfile = outarg ? go_shell_arg_to_uri (outarg) : NULL;
674 WorkbookView *wbv;
675 GOIOContext *io_context = NULL;
676 Workbook *wb = NULL;
678 if (ssconvert_export_id != NULL) {
679 fs = go_file_saver_for_id (ssconvert_export_id);
680 if (fs == NULL) {
681 res = 1;
682 g_printerr (_("Unknown exporter '%s'.\n"
683 "Try --list-exporters to see a list of possibilities.\n"),
684 ssconvert_export_id);
685 goto out;
686 } else if (outfile == NULL &&
687 !ssconvert_one_file_per_sheet &&
688 go_file_saver_get_extension (fs) != NULL) {
689 char const *ext = gsf_extension_pointer (infile);
690 if (*infile) {
691 GString *res = g_string_new (NULL);
692 g_string_append_len (res, infile, ext - infile);
693 g_string_append (res, go_file_saver_get_extension(fs));
694 outfile = g_string_free (res, FALSE);
697 } else {
698 if (outfile != NULL) {
699 fs = go_file_saver_for_file_name (outfile);
700 if (fs == NULL) {
701 res = 2;
702 g_printerr (_("Unable to guess exporter to use for '%s'.\n"
703 "Try --list-exporters to see a list of possibilities.\n"),
704 outfile);
705 goto out;
707 if (ssconvert_verbose)
708 g_printerr ("Using exporter %s\n",
709 go_file_saver_get_id (fs));
713 if (outfile == NULL) {
714 g_printerr (_("An output file name or an explicit export type is required.\n"
715 "Try --list-exporters to see a list of possibilities.\n"));
716 res = 1;
717 goto out;
720 if (ssconvert_import_id != NULL) {
721 fo = go_file_opener_for_id (ssconvert_import_id);
722 if (fo == NULL) {
723 res = 1;
724 g_printerr (_("Unknown importer '%s'.\n"
725 "Try --list-importers to see a list of possibilities.\n"),
726 ssconvert_import_id);
727 goto out;
731 if (!fs)
732 goto out;
734 io_context = go_io_context_new (cc);
735 if (mergeargs == NULL) {
736 wbv = workbook_view_new_from_uri (infile, fo,
737 io_context,
738 ssconvert_import_encoding);
739 } else {
740 wbv = workbook_view_new (NULL);
743 if (go_io_error_occurred (io_context)) {
744 go_io_error_display (io_context);
745 res = 1;
746 goto out;
747 } else if (wbv == NULL) {
748 g_printerr (_("Loading %s failed\n"), infile);
749 res = 1;
750 goto out;
753 wb = wb_view_get_workbook (wbv);
755 res = handle_export_options (fs, GO_DOC (wb));
756 if (res)
757 goto out;
759 if (mergeargs != NULL) {
760 if (merge (wb, mergeargs, fo, io_context, cc))
761 goto out;
764 if (ssconvert_goal_seek) {
765 int i;
766 Sheet *sheet = wb_view_cur_sheet (wbv);
768 for (i = 0; ssconvert_goal_seek[i]; i++) {
769 setup_range (G_OBJECT (sheet),
770 "ssconvert-goal-seek",
772 ssconvert_goal_seek[i]);
773 dialog_goal_seek (NULL, sheet);
777 if (ssconvert_solve) {
778 Sheet *sheet = wb_view_cur_sheet (wbv);
779 run_solver (sheet, wbv);
782 if (ssconvert_tool_test && ssconvert_tool_test[0]) {
783 run_tool_test (ssconvert_tool_test[0],
784 ssconvert_tool_test + 1,
785 wbv);
788 if (ssconvert_resize) {
789 int rows, cols;
790 if (sscanf (ssconvert_resize, "%dx%d", &rows, &cols) == 2) {
791 int n;
793 if (ssconvert_verbose)
794 g_printerr ("Resizing to %dx%d\n", rows, cols);
796 for (n = workbook_sheet_count (wb) - 1;
797 n >= 0;
798 n--) {
799 gboolean err;
800 Sheet *sheet = workbook_sheet_by_index (wb, n);
801 GOUndo *undo =
802 gnm_sheet_resize (sheet, cols, rows,
803 NULL, &err);
804 if (err)
805 g_printerr ("Resizing of sheet %s failed\n",
806 sheet->name_unquoted);
807 g_object_unref (undo);
812 if (ssconvert_recalc)
813 workbook_recalc_all (wb);
814 gnm_app_recalc ();
816 if (ssconvert_range)
817 setup_range (G_OBJECT (wb),
818 "ssconvert-range",
820 ssconvert_range);
821 else if (ssconvert_one_file_per_sheet ||
822 (workbook_sheet_count (wb) > 1 &&
823 go_file_saver_get_save_scope (fs) != GO_FILE_SAVE_WORKBOOK)) {
824 if (ssconvert_one_file_per_sheet) {
825 GSList *ptr, *sheets;
826 char *template;
828 res = 0;
830 template = strchr (outarg, '%')
831 ? g_strdup (outarg)
832 : g_strconcat (outarg, ".%n", NULL);
834 sheets = workbook_sheets (wb);
835 for (ptr = sheets; ptr; ptr = ptr->next) {
836 Sheet *sheet = ptr->data;
837 char *tmpfile = resolve_template (template, sheet);
838 int oldn = sheet->index_in_wb;
841 * HACK: (bug 694408).
843 * We don't have a good way of specifying the
844 * sheet. Move it to the front and select
845 * it. That will at least make cvs and txt
846 * exporters reliable find it.
848 workbook_sheet_move (sheet, -oldn);
849 wb_view_sheet_focus (wbv, sheet);
851 res = !workbook_view_save_as (wbv, fs, tmpfile, cc);
852 workbook_sheet_move (sheet, +oldn);
853 g_free (tmpfile);
854 if (res)
855 break;
858 g_free (template);
859 g_slist_free (sheets);
860 goto out;
861 } else
862 g_printerr (_("Selected exporter (%s) does not support saving multiple sheets in one file.\n"
863 "Only the current sheet will be saved. To get around this limitation, use -S.\n"),
864 go_file_saver_get_id (fs));
866 res = !workbook_view_save_as (wbv, fs, outfile, cc);
868 out:
869 if (wb)
870 g_object_unref (wb);
871 if (io_context)
872 g_object_unref (io_context);
873 g_free (infile);
874 g_free (outfile);
876 return res;
880 main (int argc, char const **argv)
882 GOErrorInfo *plugin_errs;
883 int res = 0;
884 GOCmdContext *cc;
885 GOptionContext *ocontext;
886 GError *error = NULL;
888 /* No code before here, we need to init threads */
889 argv = gnm_pre_parse_init (argc, argv);
891 ocontext = g_option_context_new (_("INFILE [OUTFILE]"));
892 g_option_context_add_main_entries (ocontext, ssconvert_options, GETTEXT_PACKAGE);
893 g_option_context_add_group (ocontext, gnm_get_option_group ());
895 * The printing code uses gtk+ stuff, so we need to init gtk+. We
896 * do that without opening any displays.
898 g_option_context_add_group (ocontext, gtk_get_option_group (FALSE));
899 g_option_context_parse (ocontext, &argc, (char ***)&argv, &error);
900 g_option_context_free (ocontext);
902 if (error) {
903 g_printerr (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
904 error->message, argv[0]);
905 g_error_free (error);
906 return 1;
909 if (ssconvert_show_version) {
910 g_print (_("ssconvert version '%s'\ndatadir := '%s'\nlibdir := '%s'\n"),
911 GNM_VERSION_FULL, gnm_sys_data_dir (), gnm_sys_lib_dir ());
912 return 0;
915 gnm_init ();
917 cc = gnm_cmd_context_stderr_new ();
918 gnm_plugins_init (GO_CMD_CONTEXT (cc));
919 go_plugin_db_activate_plugin_list (
920 go_plugins_get_available_plugins (), &plugin_errs);
921 if (plugin_errs) {
922 /* FIXME: What do we want to do here? */
923 go_error_info_free (plugin_errs);
925 go_component_set_default_command_context (cc);
927 if (ssconvert_list_exporters)
928 list_them (go_get_file_savers (),
929 (get_desc_f) &go_file_saver_get_id,
930 (get_desc_f) &go_file_saver_get_description);
931 else if (ssconvert_list_importers)
932 list_them (go_get_file_openers (),
933 (get_desc_f) &go_file_opener_get_id,
934 (get_desc_f) &go_file_opener_get_description);
935 else if (ssconvert_merge_target!=NULL && argc>=3) {
936 res = convert (argv[1], ssconvert_merge_target, argv+1, cc);
937 } else if (argc == 2 || argc == 3) {
938 res = convert (argv[1], argv[2], NULL, cc);
939 } else {
940 g_printerr (_("Usage: %s [OPTION...] %s\n"),
941 g_get_prgname (),
942 _("INFILE [OUTFILE]"));
943 res = 1;
946 go_component_set_default_command_context (NULL);
947 g_object_unref (cc);
948 gnm_shutdown ();
949 gnm_pre_parse_shutdown ();
951 return res;