GUI: Dead kittens.
[gnumeric.git] / src / ssconvert.c
blob6bcfe785c4d4725a6092f7a945adcc079be2e875
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-2018 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_collect_names (G_GNUC_UNUSED gconstpointer key,
347 GnmNamedExpr *nexpr,
348 GSList **plist)
350 if (!expr_name_is_active (nexpr))
351 return;
352 *plist = g_slist_prepend (*plist, expr_name_ref (nexpr));
355 /* Append the sheets of workbook wb2 to workbook wb. Resize sheets
356 if necessary. Fix workbook links in sheet if necessary.
357 Merge names in workbook scope (conflicts result in an error). */
358 static gboolean
359 merge_single (Workbook *wb, Workbook *wb2,
360 int cmax, int rmax,
361 GOCmdContext *cc)
363 /* Move names with workbook scope in wb2 over to wb */
364 GSList *names = g_slist_sort (gnm_named_expr_collection_list (wb2->names),
365 (GCompareFunc)expr_name_cmp_by_name);
366 GSList *p;
368 for (p = names; p; p = p->next) {
369 GnmNamedExpr *nexpr = p->data;
370 const char *name = expr_name_name (nexpr);
371 GnmNamedExpr *nexpr2;
372 GnmParsePos pp;
373 GnmParsePos newpos = nexpr->pos;
375 if (!expr_name_is_active (nexpr))
376 continue;
378 if (nexpr->pos.wb != wb2 || nexpr->pos.sheet != NULL)
379 continue;
381 /* Check for clash with existing name */
383 parse_pos_init (&pp, wb, NULL, 0, 0);
384 nexpr2 = expr_name_lookup (&pp, name);
385 if (nexpr2 /* FIXME: && nexpr2-is-not-the-same-as-nexpr */) {
386 g_printerr (_("Name conflict during merge: '%s' appears twice at workbook scope.\n"),
387 name);
388 g_slist_free (names);
389 return TRUE;
392 /* Move name scope to workbook wb */
393 newpos.wb = wb;
394 expr_name_set_pos (nexpr, &newpos);
396 g_slist_free (names);
398 while (workbook_sheet_count (wb2) > 0) {
399 /* Remove sheet from incoming workbook */
400 Sheet *sheet = workbook_sheet_by_index (wb2, 0);
401 int loc = workbook_sheet_count (wb);
402 GOUndo *undo;
403 char *sheet_name;
404 gboolean err;
405 GSList *names = NULL;
407 g_object_ref (sheet);
408 workbook_sheet_delete (sheet);
409 sheet->workbook = wb;
411 /* Fix names that reference the old workbook */
412 gnm_sheet_foreach_name (sheet, (GHFunc)cb_collect_names, &names);
413 while (names) {
414 GnmNamedExpr *nexpr = names->data;
415 names = g_slist_delete_link (names, names);
417 if (nexpr->pos.wb) {
418 GnmParsePos newpos = nexpr->pos;
419 newpos.wb = wb;
420 expr_name_set_pos (nexpr, &newpos);
422 expr_name_unref (nexpr);
425 undo = gnm_sheet_resize (sheet, cmax, rmax, cc, &err);
426 if (undo)
427 g_object_unref (undo);
429 /* Pick a free sheet name */
430 sheet_name = workbook_sheet_get_free_name
431 (wb, sheet->name_unquoted, FALSE, TRUE);
432 g_object_set (sheet, "name", sheet_name, NULL);
433 g_free (sheet_name);
435 /* Insert and revive the sheet */
436 workbook_sheet_attach_at_pos (wb, sheet, loc);
437 dependents_revive_sheet (sheet);
438 g_object_unref (sheet);
441 return FALSE;
444 /* Merge a collection of workbooks into one. */
445 static gboolean
446 merge (Workbook *wb, char const *inputs[],
447 GOFileOpener *fo, GOIOContext *io_context, GOCmdContext *cc)
449 GSList *wbs, *l;
450 int result = 0;
451 int cmax, rmax;
453 wbs = read_files_to_merge (inputs, fo, io_context, cc);
454 if (go_io_error_occurred (io_context)) {
455 go_io_error_display (io_context);
456 return TRUE;
459 suggest_size (wbs, &cmax, &rmax);
461 for (l = wbs; l; l = l->next) {
462 Workbook *wb2 = l->data;
463 const char *uri = go_doc_get_uri (GO_DOC (wb2));
465 g_printerr ("Adding sheets from %s\n", uri);
467 result = merge_single (wb, wb2, cmax, rmax, cc);
468 if (result)
469 break;
472 g_slist_free_full (wbs, g_object_unref);
473 return result;
476 static char *
477 resolve_template (const char *template, Sheet *sheet)
479 GString *s = g_string_new (NULL);
480 while (1) {
481 switch (*template) {
482 done:
483 case 0: {
484 char *res = go_shell_arg_to_uri (s->str);
485 g_string_free (s, TRUE);
486 return res;
488 case '%':
489 template++;
490 switch (*template) {
491 case 0:
492 goto done;
493 case 'n':
494 g_string_append_printf (s, "%d", sheet->index_in_wb);
495 break;
496 case 's':
497 g_string_append (s, sheet->name_unquoted);
498 break;
499 case '%':
500 g_string_append_c (s, '%');
501 break;
503 template++;
504 break;
505 default:
506 g_string_append_c (s, *template);
507 template++;
512 static void
513 run_solver (Sheet *sheet, WorkbookView *wbv)
515 GnmSolverParameters *params = sheet->solver_parameters;
516 GError *err = NULL;
517 WorkbookControl *wbc;
518 GnmSolver *sol = NULL;
520 wbc = g_object_new (GNM_WBC_TYPE, NULL);
521 wb_control_set_view (wbc, wbv, NULL);
523 /* Pick a functional algorithm. */
524 if (!gnm_solver_factory_functional (params->options.algorithm,
525 NULL)) {
526 GSList *l;
527 for (l = gnm_solver_db_get (); l; l = l->next) {
528 GnmSolverFactory *factory = l->data;
529 if (params->options.model_type != factory->type)
530 continue;
531 if (gnm_solver_factory_functional (factory, NULL)) {
532 gnm_solver_param_set_algorithm (params,
533 factory);
534 break;
539 if (!gnm_solver_param_valid (params, &err))
540 goto done;
542 sol = params->options.algorithm
543 ? gnm_solver_factory_create (params->options.algorithm, params)
544 : NULL;
545 if (!sol) {
546 g_set_error (&err, go_error_invalid (), 0,
547 _("Failed to create solver"));
548 goto done;
551 if (!gnm_solver_start (sol, wbc, &err))
552 goto done;
554 while (!gnm_solver_finished (sol)) {
555 g_main_context_iteration (NULL, TRUE);
558 switch (sol->status) {
559 case GNM_SOLVER_STATUS_DONE:
560 break;
561 case GNM_SOLVER_STATUS_CANCELLED:
562 g_printerr ("Solver reached time or iteration limit\n");
563 break;
564 default:
565 g_set_error (&err, go_error_invalid (), 0,
566 _("Solver ran, but failed"));
567 goto done;
570 gnm_solver_store_result (sol);
572 gnm_solver_create_report (sol, "Solver");
574 done:
575 if (sol)
576 g_object_unref (sol);
577 if (err) {
578 g_printerr (_("Solver: %s\n"), err->message);
579 g_error_free (err);
583 #define GET_ARG(conv_,name_,def_) (g_hash_table_lookup_extended(args,(name_),NULL,&arg) ? conv_((const char *)arg) : (def_))
584 #define RANGE_ARG(s_) value_new_cellrange_str(sheet,(s_))
585 #define RANGE_LIST_ARG(s_) g_slist_prepend (NULL, value_new_cellrange_str(sheet,(s_)))
586 #define SHEET_ARG(s_) workbook_sheet_by_name(wb,(s_))
588 static void
589 run_tool_test (const char *tool, char **argv, WorkbookView *wbv)
591 int i;
592 WorkbookControl *wbc;
593 gpointer specs;
594 data_analysis_output_t *dao;
595 analysis_tool_engine engine;
596 Workbook *wb;
597 Sheet *sheet;
598 GHashTable *args;
599 gpointer arg;
602 * Arguments in argv are of the form key:value.
603 * Make a hash for those.
605 args = g_hash_table_new_full (g_str_hash, g_str_equal,
606 (GDestroyNotify)g_free,
607 (GDestroyNotify)g_free);
608 for (i = 0; argv[i]; i++) {
609 const char *s = argv[i];
610 const char *colon = strchr (s, ':');
611 if (!colon) {
612 g_printerr ("Ignoring tool test argument \"%s\"\n", s);
613 continue;
615 g_hash_table_replace (args, g_strndup (s, colon - s),
616 g_strdup (colon + 1));
619 wb = wb_view_get_workbook (wbv);
620 wbc = g_object_new (GNM_WBC_TYPE, NULL);
621 wb_control_set_view (wbc, wbv, NULL);
623 sheet = GET_ARG (SHEET_ARG, "sheet", wb_view_cur_sheet (wbv));
625 if (g_str_equal (tool, "regression")) {
626 analysis_tools_data_regression_t *data =
627 g_new0 (analysis_tools_data_regression_t, 1);
629 data->base.wbc = wbc;
630 data->base.range_1 = GET_ARG (RANGE_ARG, "x", value_new_error_REF (NULL));
631 data->base.range_2 = GET_ARG (RANGE_ARG, "y", value_new_error_REF (NULL));
632 data->base.labels = GET_ARG (atoi, "labels", FALSE);
633 data->base.alpha = GET_ARG (atof, "alpha", 0.05);
634 data->group_by = GET_ARG ((group_by_t), "grouped-by", GROUPED_BY_COL);
635 data->intercept = GET_ARG (atoi, "intercept", TRUE);
636 data->multiple_regression = GET_ARG (atoi, "multiple", TRUE);
637 data->multiple_y = GET_ARG (atoi, "multiple-y", FALSE);
638 data->residual = GET_ARG (atoi, "residual", TRUE);
640 engine = analysis_tool_regression_engine;
641 specs = data;
642 } else if (g_str_equal (tool, "anova")) {
643 analysis_tools_data_anova_single_t *data =
644 g_new0 (analysis_tools_data_anova_single_t, 1);
646 data->base.input = GET_ARG (RANGE_LIST_ARG, "data", NULL);
647 data->base.labels = GET_ARG (atoi, "labels", FALSE);
648 data->base.group_by = GET_ARG ((group_by_t), "grouped-by", GROUPED_BY_COL);
649 data->alpha = GET_ARG (atof, "alpha", 0.05);
651 engine = analysis_tool_anova_single_engine;
652 specs = data;
653 } else {
654 g_printerr ("no test for tool \"%s\"\n", tool);
655 return;
658 dao = dao_init_new_sheet (NULL);
659 dao->put_formulas = TRUE;
660 cmd_analysis_tool (wbc, sheet, dao, specs, engine, TRUE);
662 g_hash_table_destroy (args);
665 #undef GET_ARG
666 #undef RANGE_ARG
667 #undef RANGE_LISTARG
668 #undef SHEET_ARG
670 static int
671 convert (char const *inarg, char const *outarg, char const *mergeargs[],
672 GOCmdContext *cc)
674 int res = 0;
675 GOFileSaver *fs = NULL;
676 GOFileOpener *fo = NULL;
677 char *infile = go_shell_arg_to_uri (inarg);
678 char *outfile = outarg ? go_shell_arg_to_uri (outarg) : NULL;
679 WorkbookView *wbv;
680 GOIOContext *io_context = NULL;
681 Workbook *wb = NULL;
683 if (ssconvert_export_id != NULL) {
684 fs = go_file_saver_for_id (ssconvert_export_id);
685 if (fs == NULL) {
686 res = 1;
687 g_printerr (_("Unknown exporter '%s'.\n"
688 "Try --list-exporters to see a list of possibilities.\n"),
689 ssconvert_export_id);
690 goto out;
691 } else if (outfile == NULL &&
692 !ssconvert_one_file_per_sheet &&
693 go_file_saver_get_extension (fs) != NULL) {
694 char const *ext = gsf_extension_pointer (infile);
695 if (*infile) {
696 GString *res = g_string_new (NULL);
697 g_string_append_len (res, infile, ext - infile);
698 g_string_append (res, go_file_saver_get_extension(fs));
699 outfile = g_string_free (res, FALSE);
702 } else {
703 if (outfile != NULL) {
704 fs = go_file_saver_for_file_name (outfile);
705 if (fs == NULL) {
706 res = 2;
707 g_printerr (_("Unable to guess exporter to use for '%s'.\n"
708 "Try --list-exporters to see a list of possibilities.\n"),
709 outfile);
710 goto out;
712 if (ssconvert_verbose)
713 g_printerr ("Using exporter %s\n",
714 go_file_saver_get_id (fs));
718 if (outfile == NULL) {
719 g_printerr (_("An output file name or an explicit export type is required.\n"
720 "Try --list-exporters to see a list of possibilities.\n"));
721 res = 1;
722 goto out;
725 if (ssconvert_import_id != NULL) {
726 fo = go_file_opener_for_id (ssconvert_import_id);
727 if (fo == NULL) {
728 res = 1;
729 g_printerr (_("Unknown importer '%s'.\n"
730 "Try --list-importers to see a list of possibilities.\n"),
731 ssconvert_import_id);
732 goto out;
736 if (!fs)
737 goto out;
739 io_context = go_io_context_new (cc);
740 if (mergeargs == NULL) {
741 wbv = workbook_view_new_from_uri (infile, fo,
742 io_context,
743 ssconvert_import_encoding);
744 } else {
745 wbv = workbook_view_new (NULL);
748 if (go_io_error_occurred (io_context)) {
749 go_io_error_display (io_context);
750 res = 1;
751 goto out;
752 } else if (wbv == NULL) {
753 g_printerr (_("Loading %s failed\n"), infile);
754 res = 1;
755 goto out;
758 wb = wb_view_get_workbook (wbv);
760 res = handle_export_options (fs, GO_DOC (wb));
761 if (res)
762 goto out;
764 if (mergeargs != NULL) {
765 if (merge (wb, mergeargs, fo, io_context, cc))
766 goto out;
769 if (ssconvert_goal_seek) {
770 int i;
771 Sheet *sheet = wb_view_cur_sheet (wbv);
773 for (i = 0; ssconvert_goal_seek[i]; i++) {
774 setup_range (G_OBJECT (sheet),
775 "ssconvert-goal-seek",
777 ssconvert_goal_seek[i]);
778 dialog_goal_seek (NULL, sheet);
782 if (ssconvert_solve) {
783 Sheet *sheet = wb_view_cur_sheet (wbv);
784 run_solver (sheet, wbv);
787 if (ssconvert_tool_test && ssconvert_tool_test[0]) {
788 run_tool_test (ssconvert_tool_test[0],
789 ssconvert_tool_test + 1,
790 wbv);
793 if (ssconvert_resize) {
794 int rows, cols;
795 if (sscanf (ssconvert_resize, "%dx%d", &rows, &cols) == 2) {
796 int n;
798 if (ssconvert_verbose)
799 g_printerr ("Resizing to %dx%d\n", rows, cols);
801 for (n = workbook_sheet_count (wb) - 1;
802 n >= 0;
803 n--) {
804 gboolean err;
805 Sheet *sheet = workbook_sheet_by_index (wb, n);
806 GOUndo *undo =
807 gnm_sheet_resize (sheet, cols, rows,
808 NULL, &err);
809 if (err)
810 g_printerr ("Resizing of sheet %s failed\n",
811 sheet->name_unquoted);
812 g_object_unref (undo);
817 if (ssconvert_recalc)
818 workbook_recalc_all (wb);
819 gnm_app_recalc ();
821 if (ssconvert_range)
822 setup_range (G_OBJECT (wb),
823 "ssconvert-range",
825 ssconvert_range);
826 else if (ssconvert_one_file_per_sheet ||
827 (workbook_sheet_count (wb) > 1 &&
828 go_file_saver_get_save_scope (fs) != GO_FILE_SAVE_WORKBOOK)) {
829 if (ssconvert_one_file_per_sheet) {
830 GSList *ptr, *sheets;
831 char *template;
833 res = 0;
835 template = strchr (outarg, '%')
836 ? g_strdup (outarg)
837 : g_strconcat (outarg, ".%n", NULL);
839 sheets = workbook_sheets (wb);
840 for (ptr = sheets; ptr; ptr = ptr->next) {
841 Sheet *sheet = ptr->data;
842 char *tmpfile = resolve_template (template, sheet);
843 int oldn = sheet->index_in_wb;
846 * HACK: (bug 694408).
848 * We don't have a good way of specifying the
849 * sheet. Move it to the front and select
850 * it. That will at least make cvs and txt
851 * exporters reliable find it.
853 workbook_sheet_move (sheet, -oldn);
854 wb_view_sheet_focus (wbv, sheet);
856 res = !workbook_view_save_as (wbv, fs, tmpfile, cc);
857 workbook_sheet_move (sheet, +oldn);
858 g_free (tmpfile);
859 if (res)
860 break;
863 g_free (template);
864 g_slist_free (sheets);
865 goto out;
866 } else
867 g_printerr (_("Selected exporter (%s) does not support saving multiple sheets in one file.\n"
868 "Only the current sheet will be saved. To get around this limitation, use -S.\n"),
869 go_file_saver_get_id (fs));
871 res = !workbook_view_save_as (wbv, fs, outfile, cc);
873 out:
874 if (wb)
875 g_object_unref (wb);
876 if (io_context)
877 g_object_unref (io_context);
878 g_free (infile);
879 g_free (outfile);
881 return res;
885 main (int argc, char const **argv)
887 GOErrorInfo *plugin_errs;
888 int res = 0;
889 GOCmdContext *cc;
890 GOptionContext *ocontext;
891 GError *error = NULL;
893 /* No code before here, we need to init threads */
894 argv = gnm_pre_parse_init (argc, argv);
896 ocontext = g_option_context_new (_("INFILE [OUTFILE]"));
897 g_option_context_add_main_entries (ocontext, ssconvert_options, GETTEXT_PACKAGE);
898 g_option_context_add_group (ocontext, gnm_get_option_group ());
900 * The printing code uses gtk+ stuff, so we need to init gtk+. We
901 * do that without opening any displays.
903 g_option_context_add_group (ocontext, gtk_get_option_group (FALSE));
904 g_option_context_parse (ocontext, &argc, (char ***)&argv, &error);
905 g_option_context_free (ocontext);
907 if (error) {
908 g_printerr (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
909 error->message, argv[0]);
910 g_error_free (error);
911 return 1;
914 if (ssconvert_show_version) {
915 g_print (_("ssconvert version '%s'\ndatadir := '%s'\nlibdir := '%s'\n"),
916 GNM_VERSION_FULL, gnm_sys_data_dir (), gnm_sys_lib_dir ());
917 return 0;
920 gnm_init ();
922 cc = gnm_cmd_context_stderr_new ();
923 gnm_plugins_init (GO_CMD_CONTEXT (cc));
924 go_plugin_db_activate_plugin_list (
925 go_plugins_get_available_plugins (), &plugin_errs);
926 if (plugin_errs) {
927 /* FIXME: What do we want to do here? */
928 go_error_info_free (plugin_errs);
930 go_component_set_default_command_context (cc);
932 if (ssconvert_list_exporters)
933 list_them (go_get_file_savers (),
934 (get_desc_f) &go_file_saver_get_id,
935 (get_desc_f) &go_file_saver_get_description);
936 else if (ssconvert_list_importers)
937 list_them (go_get_file_openers (),
938 (get_desc_f) &go_file_opener_get_id,
939 (get_desc_f) &go_file_opener_get_description);
940 else if (ssconvert_merge_target!=NULL && argc>=3) {
941 res = convert (argv[1], ssconvert_merge_target, argv+1, cc);
942 } else if (argc == 2 || argc == 3) {
943 res = convert (argv[1], argv[2], NULL, cc);
944 } else {
945 g_printerr (_("Usage: %s [OPTION...] %s\n"),
946 g_get_prgname (),
947 _("INFILE [OUTFILE]"));
948 res = 1;
951 go_component_set_default_command_context (NULL);
952 g_object_unref (cc);
953 gnm_shutdown ();
954 gnm_pre_parse_shutdown ();
956 return res;