Update Spanish translation
[gnumeric.git] / src / ssconvert.c
blob4f0af7e9f71524d439c440a308c1d5f1b7ba593d
1 /*
2 * ssconvert.c: A wrapper application to convert spreadsheet formats
4 * Author:
5 * Jon Kåre Hellan <hellan@acm.org>
6 * Morten Welinder <terra@gnome.org>
7 * Jody Goldberg <jody@gnome.org>
9 * Copyright (C) 2002-2003 Jody Goldberg
10 * Copyright (C) 2006-2018 Morten Welinder (terra@gnome.org)
12 #include <gnumeric-config.h>
13 #include <glib/gi18n.h>
14 #include <gnumeric.h>
15 #include <position.h>
16 #include <parse-util.h>
17 #include <application.h>
18 #include <workbook.h>
19 #include <workbook-priv.h>
20 #include <workbook-control.h>
21 #include <sheet.h>
22 #include <dependent.h>
23 #include <expr-name.h>
24 #include <libgnumeric.h>
25 #include <gutils.h>
26 #include <value.h>
27 #include <ranges.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 <gnumeric-conf.h>
35 #include <gui-clipboard.h>
36 #include <tools/analysis-tools.h>
37 #include <dialogs/dialogs.h>
38 #include <goffice/goffice.h>
39 #include <gsf/gsf-utils.h>
40 #include <string.h>
41 #ifdef HAVE_SYS_RESOURCE_H
42 #include <sys/resource.h>
43 #endif
45 // Sheets that an exporter should export.
46 #define SHEET_SELECTION_KEY "sheet-selection"
48 // Sheets user has specified as export options
49 #define SSCONVERT_SHEET_SET_KEY "ssconvert-sheets"
51 static gboolean ssconvert_show_version = FALSE;
52 static gboolean ssconvert_verbose = FALSE;
53 static gboolean ssconvert_list_exporters = FALSE;
54 static gboolean ssconvert_list_importers = FALSE;
55 static gboolean ssconvert_one_file_per_sheet = FALSE;
56 static gboolean ssconvert_recalc = FALSE;
57 static gboolean ssconvert_solve = FALSE;
58 static char *ssconvert_resize = NULL;
59 static char *ssconvert_clipboard = NULL;
60 static char *ssconvert_range = NULL;
61 static char *ssconvert_import_encoding = NULL;
62 static char *ssconvert_import_id = NULL;
63 static char *ssconvert_export_id = NULL;
64 static char *ssconvert_export_options = NULL;
65 static char *ssconvert_merge_target = NULL;
66 static char **ssconvert_goal_seek = NULL;
67 static char **ssconvert_tool_test = NULL;
69 static const GOptionEntry ssconvert_options [] = {
71 "version", 0,
72 0, G_OPTION_ARG_NONE, &ssconvert_show_version,
73 N_("Display program version"),
74 NULL
78 "verbose", 'v',
79 0, G_OPTION_ARG_NONE, &ssconvert_verbose,
80 N_("Be somewhat more verbose during conversion"),
81 NULL
84 /* ---------------------------------------- */
87 "import-encoding", 'E',
88 0, G_OPTION_ARG_STRING, &ssconvert_import_encoding,
89 N_("Optionally specify an encoding for imported content"),
90 N_("ENCODING")
94 "import-type", 'I',
95 0, G_OPTION_ARG_STRING, &ssconvert_import_id,
96 N_("Optionally specify which importer to use"),
97 N_("ID")
101 "list-importers", 0,
102 0, G_OPTION_ARG_NONE, &ssconvert_list_importers,
103 N_("List the available importers"),
104 NULL
107 /* ---------------------------------------- */
110 "merge-to", 'M',
111 0, G_OPTION_ARG_STRING, &ssconvert_merge_target,
112 N_("Merge listed files (all same format) to make this file"),
113 N_("file")
117 "export-type", 'T',
118 0, G_OPTION_ARG_STRING, &ssconvert_export_id,
119 N_("Optionally specify which exporter to use"),
120 N_("ID")
124 "export-options", 'O',
125 0, G_OPTION_ARG_STRING, &ssconvert_export_options,
126 N_("Detailed instructions for the chosen exporter"),
127 N_("string")
131 "list-exporters", 0,
132 0, G_OPTION_ARG_NONE, &ssconvert_list_exporters,
133 N_("List the available exporters"),
134 NULL
138 "export-file-per-sheet", 'S',
139 0, G_OPTION_ARG_NONE, &ssconvert_one_file_per_sheet,
140 N_("Export a file for each sheet if the exporter only supports one sheet at a time"),
141 NULL
145 "recalc", 0,
146 0, G_OPTION_ARG_NONE, &ssconvert_recalc,
147 N_("Recalculate all cells before writing the result"),
148 NULL
152 "resize", 0,
153 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &ssconvert_resize,
154 N_("Resize to given ROWSxCOLS"),
155 NULL
159 /* ---------------------------------------- */
161 // For now these are for INTERNAL GNUMERIC USE ONLY. They are used
162 // by the test suite.
164 "clipboard", 0,
165 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &ssconvert_clipboard,
166 N_("Output via the clipboard"),
167 NULL
171 "export-range", 0,
172 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &ssconvert_range,
173 N_("The range to export"),
174 NULL
178 "goal-seek", 0,
179 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &ssconvert_goal_seek,
180 N_("Goal seek areas"),
181 NULL
185 "solve", 0,
186 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &ssconvert_solve,
187 N_("Run the solver"),
188 NULL
192 "tool-test", 0,
193 G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &ssconvert_tool_test,
194 N_("Tool test specs"),
195 NULL
198 /* ---------------------------------------- */
200 { NULL }
203 static GnmRangeRef const *
204 setup_range (GObject *obj, const char *key, Workbook *wb, const char *rtxt)
206 GnmParsePos pp;
207 const char *end;
208 GnmRangeRef rr, *rrc;
210 pp.wb = wb;
211 pp.sheet = workbook_sheet_by_index (wb, 0);
212 pp.eval.col = 0;
213 pp.eval.row = 0;
215 end = rangeref_parse (&rr, rtxt, &pp, gnm_conventions_default);
216 if (!end || end == rtxt || *end != 0) {
217 g_printerr ("Invalid range specified.\n");
218 exit (1);
221 rrc = g_memdup (&rr, sizeof (rr));
222 g_object_set_data_full (obj, key, rrc, g_free);
224 return rrc;
227 struct cb_handle_export_options {
228 GOFileSaver *fs;
229 Workbook const *wb;
232 static gboolean
233 cb_handle_export_options (const char *key, const char *value,
234 GError **err, gpointer user_)
236 struct cb_handle_export_options *user = user_;
237 return gnm_file_saver_common_export_option (user->fs, user->wb,
238 key, value, err);
241 static int
242 handle_export_options (GOFileSaver *fs, Workbook *wb)
244 GError *err = NULL;
245 gboolean fail;
246 guint sig;
248 if (!ssconvert_export_options)
249 return 0;
251 sig = g_signal_lookup ("set-export-options", G_TYPE_FROM_INSTANCE (fs));
252 if (g_signal_handler_find (fs, G_SIGNAL_MATCH_ID,
253 sig, 0, NULL, NULL, NULL))
254 fail = go_file_saver_set_export_options
255 (fs, GO_DOC (wb),
256 ssconvert_export_options,
257 &err);
258 else {
259 struct cb_handle_export_options data;
260 data.fs = fs;
261 data.wb = wb;
262 fail = go_parse_key_value (ssconvert_export_options, &err,
263 cb_handle_export_options, &data);
266 if (fail) {
267 g_printerr ("ssconvert: %s\n", err
268 ? err->message
269 : _("Cannot parse export options."));
270 return 1;
273 return 0;
276 // Check that the sheet selection, if any, matches the file saver's
277 // capabilities.
278 static int
279 validate_sheet_selection (GOFileSaver *fs, Workbook *wb)
281 GOFileSaveScope fsscope = go_file_saver_get_save_scope (fs);
282 gboolean fs_sheet_selection;
284 g_object_get (G_OBJECT (fs),
285 "sheet-selection", &fs_sheet_selection, NULL);
287 if (ssconvert_one_file_per_sheet) {
288 gboolean ok;
289 switch (fsscope) {
290 case GO_FILE_SAVE_WORKBOOK:
291 ok = fs_sheet_selection;
292 break;
293 case GO_FILE_SAVE_SHEET:
294 ok = TRUE;
295 break;
296 case GO_FILE_SAVE_RANGE:
297 default:
298 ok = FALSE;
299 break;
301 if (!ok) {
302 g_printerr (_("Selected exporter (%s) does not have the ability to split a workbook into sheets.\n"),
303 go_file_saver_get_id (fs));
304 return 1;
306 } else {
307 GPtrArray *sheets = g_object_get_data (G_OBJECT (wb),
308 SSCONVERT_SHEET_SET_KEY);
309 switch (fsscope) {
310 case GO_FILE_SAVE_WORKBOOK:
311 case GO_FILE_SAVE_RANGE:
312 default:
313 if (sheets && !fs_sheet_selection) {
314 g_printerr (_("Selected exporter (%s) does not have the ability to export a subset of sheets.\n"),
315 go_file_saver_get_id (fs));
316 return 1;
318 break;
319 case GO_FILE_SAVE_SHEET:
320 if (sheets && sheets->len != 1) {
321 g_printerr (_("Selected exporter (%s) can only export one sheet at a time.\n"),
322 go_file_saver_get_id (fs));
323 return 1;
325 break;
329 return 0;
333 typedef gchar const *(*get_desc_f)(const void *);
335 static int
336 by_his_id (gconstpointer a, gconstpointer b, gpointer user)
338 get_desc_f get_his_id = user;
339 return strcmp (get_his_id (a), get_his_id (b));
342 static void
343 list_them (GList *them,
344 get_desc_f get_his_id,
345 get_desc_f get_his_description)
347 GList *them_copy = g_list_copy (them);
348 GList *ptr;
349 guint len = 0;
350 gboolean interactive;
352 them_copy = g_list_sort_with_data (them_copy, by_his_id, get_his_id);
354 for (ptr = them_copy; ptr; ptr = ptr->next) {
355 GObject *obj = ptr->data;
356 char const *id;
358 g_object_get (obj, "interactive-only", &interactive, NULL);
359 if (interactive)
360 continue;
362 id = get_his_id (obj);
363 if (!id) id = "";
364 len = MAX (len, strlen (id));
367 g_printerr ("%-*s | %s\n", len,
368 /* Translate these? */
369 _("ID"),
370 _("Description"));
371 for (ptr = them_copy; ptr ; ptr = ptr->next) {
372 GObject *obj = ptr->data;
373 char const *id;
375 g_object_get (obj, "interactive-only", &interactive, NULL);
376 if (interactive)
377 continue;
379 id = get_his_id (obj);
380 if (!id) id = "";
381 g_printerr ("%-*s | %s\n", len,
383 (*get_his_description) (ptr->data));
386 g_list_free (them_copy);
390 * Read the files we're going to merge and return a list of Workbooks.
392 static GSList *
393 read_files_to_merge (const char *inputs[], GOFileOpener *fo,
394 GOIOContext *io_context, GOCmdContext *cc)
396 GSList *wbs = NULL;
398 while (*inputs) {
399 const char *fname = *inputs;
400 char *uri = go_shell_arg_to_uri (fname);
401 WorkbookView *wbv =
402 workbook_view_new_from_uri (uri, fo, io_context,
403 ssconvert_import_encoding);
404 g_free (uri);
405 inputs++;
407 if (go_io_error_occurred (io_context)) {
408 g_slist_free_full (wbs, g_object_unref);
409 return NULL;
412 if (!wbv)
413 continue;
415 wbs = g_slist_prepend (wbs, wb_view_get_workbook (wbv));
418 return g_slist_reverse (wbs);
422 * Look at a set of workbooks, and pick a sheet size that would
423 * be good for sheets in a workbook merging them all.
425 static void
426 suggest_size (GSList *wbs, int *csuggest, int *rsuggest)
428 GSList *l;
429 int rmax = 0;
430 int cmax = 0;
432 for (l = wbs; l; l = l->next) {
433 Workbook *wb = l->data;
435 WORKBOOK_FOREACH_SHEET (wb, sheet, {
436 int r = gnm_sheet_get_max_rows (sheet);
437 int c = gnm_sheet_get_max_cols (sheet);
438 if (r > rmax) rmax = r;
439 if (c > cmax) cmax = c;
443 gnm_sheet_suggest_size (&cmax, &rmax);
444 *csuggest = cmax;
445 *rsuggest = rmax;
448 static void
449 cb_collect_names (G_GNUC_UNUSED gconstpointer key,
450 GnmNamedExpr *nexpr,
451 GSList **plist)
453 if (!expr_name_is_active (nexpr))
454 return;
455 *plist = g_slist_prepend (*plist, expr_name_ref (nexpr));
458 /* Append the sheets of workbook wb2 to workbook wb. Resize sheets
459 if necessary. Fix workbook links in sheet if necessary.
460 Merge names in workbook scope (conflicts result in an error). */
461 static gboolean
462 merge_single (Workbook *wb, Workbook *wb2,
463 int cmax, int rmax,
464 GOCmdContext *cc)
466 /* Move names with workbook scope in wb2 over to wb */
467 GSList *names = g_slist_sort (gnm_named_expr_collection_list (wb2->names),
468 (GCompareFunc)expr_name_cmp_by_name);
469 GSList *p;
471 for (p = names; p; p = p->next) {
472 GnmNamedExpr *nexpr = p->data;
473 const char *name = expr_name_name (nexpr);
474 GnmNamedExpr *nexpr2;
475 GnmParsePos pp;
476 GnmParsePos newpos = nexpr->pos;
478 if (!expr_name_is_active (nexpr))
479 continue;
481 if (nexpr->pos.wb != wb2 || nexpr->pos.sheet != NULL)
482 continue;
484 /* Check for clash with existing name */
486 parse_pos_init (&pp, wb, NULL, 0, 0);
487 nexpr2 = expr_name_lookup (&pp, name);
488 if (nexpr2 /* FIXME: && nexpr2-is-not-the-same-as-nexpr */) {
489 g_printerr (_("Name conflict during merge: '%s' appears twice at workbook scope.\n"),
490 name);
491 g_slist_free (names);
492 return TRUE;
495 /* Move name scope to workbook wb */
496 newpos.wb = wb;
497 expr_name_set_pos (nexpr, &newpos);
499 g_slist_free (names);
501 while (workbook_sheet_count (wb2) > 0) {
502 /* Remove sheet from incoming workbook */
503 Sheet *sheet = workbook_sheet_by_index (wb2, 0);
504 int loc = workbook_sheet_count (wb);
505 GOUndo *undo;
506 char *sheet_name;
507 gboolean err;
508 GSList *names = NULL;
510 g_object_ref (sheet);
511 workbook_sheet_delete (sheet);
512 sheet->workbook = wb;
514 /* Fix names that reference the old workbook */
515 gnm_sheet_foreach_name (sheet, (GHFunc)cb_collect_names, &names);
516 while (names) {
517 GnmNamedExpr *nexpr = names->data;
518 names = g_slist_delete_link (names, names);
520 if (nexpr->pos.wb) {
521 GnmParsePos newpos = nexpr->pos;
522 newpos.wb = wb;
523 expr_name_set_pos (nexpr, &newpos);
525 expr_name_unref (nexpr);
528 undo = gnm_sheet_resize (sheet, cmax, rmax, cc, &err);
529 if (undo)
530 g_object_unref (undo);
532 /* Pick a free sheet name */
533 sheet_name = workbook_sheet_get_free_name
534 (wb, sheet->name_unquoted, FALSE, TRUE);
535 g_object_set (sheet, "name", sheet_name, NULL);
536 g_free (sheet_name);
538 /* Insert and revive the sheet */
539 workbook_sheet_attach_at_pos (wb, sheet, loc);
540 dependents_revive_sheet (sheet);
541 g_object_unref (sheet);
544 return FALSE;
547 /* Merge a collection of workbooks into one. */
548 static gboolean
549 merge (Workbook *wb, char const *inputs[],
550 GOFileOpener *fo, GOIOContext *io_context, GOCmdContext *cc)
552 GSList *wbs, *l;
553 int result = 0;
554 int cmax, rmax;
556 wbs = read_files_to_merge (inputs, fo, io_context, cc);
557 if (go_io_error_occurred (io_context)) {
558 go_io_error_display (io_context);
559 return TRUE;
562 suggest_size (wbs, &cmax, &rmax);
564 for (l = wbs; l; l = l->next) {
565 Workbook *wb2 = l->data;
566 const char *uri = go_doc_get_uri (GO_DOC (wb2));
568 g_printerr (_("Adding sheets from %s\n"), uri);
570 result = merge_single (wb, wb2, cmax, rmax, cc);
571 if (result)
572 break;
575 g_slist_free_full (wbs, g_object_unref);
576 return result;
579 static char *
580 resolve_template (const char *template, Sheet *sheet, unsigned n)
582 GString *s = g_string_new (NULL);
583 while (1) {
584 switch (*template) {
585 done:
586 case 0: {
587 char *res = go_shell_arg_to_uri (s->str);
588 g_string_free (s, TRUE);
589 return res;
591 case '%':
592 template++;
593 switch (*template) {
594 case 0:
595 goto done;
596 case 'n':
597 g_string_append_printf (s, "%u", n);
598 break;
599 case 's':
600 g_string_append (s, sheet->name_unquoted);
601 break;
602 case '%':
603 g_string_append_c (s, '%');
604 break;
606 template++;
607 break;
608 default:
609 g_string_append_c (s, *template);
610 template++;
615 static void
616 run_solver (Sheet *sheet, WorkbookView *wbv)
618 GnmSolverParameters *params = sheet->solver_parameters;
619 GError *err = NULL;
620 WorkbookControl *wbc;
621 GnmSolver *sol = NULL;
623 wbc = g_object_new (GNM_WBC_TYPE, NULL);
624 wb_control_set_view (wbc, wbv, NULL);
626 /* Pick a functional algorithm. */
627 if (!gnm_solver_factory_functional (params->options.algorithm,
628 NULL)) {
629 GSList *l;
630 for (l = gnm_solver_db_get (); l; l = l->next) {
631 GnmSolverFactory *factory = l->data;
632 if (params->options.model_type != factory->type)
633 continue;
634 if (gnm_solver_factory_functional (factory, NULL)) {
635 gnm_solver_param_set_algorithm (params,
636 factory);
637 break;
642 if (!gnm_solver_param_valid (params, &err))
643 goto done;
645 sol = params->options.algorithm
646 ? gnm_solver_factory_create (params->options.algorithm, params)
647 : NULL;
648 if (!sol) {
649 g_set_error (&err, go_error_invalid (), 0,
650 _("Failed to create solver"));
651 goto done;
654 if (!gnm_solver_start (sol, wbc, &err))
655 goto done;
657 while (!gnm_solver_finished (sol)) {
658 g_main_context_iteration (NULL, TRUE);
661 switch (sol->status) {
662 case GNM_SOLVER_STATUS_DONE:
663 break;
664 case GNM_SOLVER_STATUS_CANCELLED:
665 g_printerr (_("Solver reached time or iteration limit\n"));
666 break;
667 default:
668 g_set_error (&err, go_error_invalid (), 0,
669 _("Solver ran, but failed"));
670 goto done;
673 gnm_solver_store_result (sol);
675 gnm_solver_create_report (sol, "Solver");
677 done:
678 if (sol)
679 g_object_unref (sol);
680 if (err) {
681 g_printerr (_("Solver: %s\n"), err->message);
682 g_error_free (err);
686 #define GET_ARG(conv_,name_,def_) (g_hash_table_lookup_extended(args,(name_),NULL,&arg) ? conv_((const char *)arg) : (def_))
687 #define RANGE_ARG(s_) value_new_cellrange_str(sheet,(s_))
688 #define RANGE_LIST_ARG(s_) g_slist_prepend (NULL, value_new_cellrange_str(sheet,(s_)))
689 #define SHEET_ARG(s_) workbook_sheet_by_name(wb,(s_))
691 static void
692 run_tool_test (const char *tool, char **argv, WorkbookView *wbv)
694 int i;
695 WorkbookControl *wbc;
696 gpointer specs;
697 data_analysis_output_t *dao;
698 analysis_tool_engine engine;
699 Workbook *wb;
700 Sheet *sheet;
701 GHashTable *args;
702 gpointer arg;
705 * Arguments in argv are of the form key:value.
706 * Make a hash for those.
708 args = g_hash_table_new_full (g_str_hash, g_str_equal,
709 (GDestroyNotify)g_free,
710 (GDestroyNotify)g_free);
711 for (i = 0; argv[i]; i++) {
712 const char *s = argv[i];
713 const char *colon = strchr (s, ':');
714 if (!colon) {
715 g_printerr ("Ignoring tool test argument \"%s\"\n", s);
716 continue;
718 g_hash_table_replace (args, g_strndup (s, colon - s),
719 g_strdup (colon + 1));
722 wb = wb_view_get_workbook (wbv);
723 wbc = g_object_new (GNM_WBC_TYPE, NULL);
724 wb_control_set_view (wbc, wbv, NULL);
726 sheet = GET_ARG (SHEET_ARG, "sheet", wb_view_cur_sheet (wbv));
728 if (g_str_equal (tool, "regression")) {
729 analysis_tools_data_regression_t *data =
730 g_new0 (analysis_tools_data_regression_t, 1);
732 data->base.wbc = wbc;
733 data->base.range_1 = GET_ARG (RANGE_ARG, "x", value_new_error_REF (NULL));
734 data->base.range_2 = GET_ARG (RANGE_ARG, "y", value_new_error_REF (NULL));
735 data->base.labels = GET_ARG (atoi, "labels", FALSE);
736 data->base.alpha = GET_ARG (atof, "alpha", 0.05);
737 data->group_by = GET_ARG ((group_by_t), "grouped-by", GROUPED_BY_COL);
738 data->intercept = GET_ARG (atoi, "intercept", TRUE);
739 data->multiple_regression = GET_ARG (atoi, "multiple", TRUE);
740 data->multiple_y = GET_ARG (atoi, "multiple-y", FALSE);
741 data->residual = GET_ARG (atoi, "residual", TRUE);
743 engine = analysis_tool_regression_engine;
744 specs = data;
745 } else if (g_str_equal (tool, "anova")) {
746 analysis_tools_data_anova_single_t *data =
747 g_new0 (analysis_tools_data_anova_single_t, 1);
749 data->base.input = GET_ARG (RANGE_LIST_ARG, "data", NULL);
750 data->base.labels = GET_ARG (atoi, "labels", FALSE);
751 data->base.group_by = GET_ARG ((group_by_t), "grouped-by", GROUPED_BY_COL);
752 data->alpha = GET_ARG (atof, "alpha", 0.05);
754 engine = analysis_tool_anova_single_engine;
755 specs = data;
756 } else {
757 g_printerr ("no test for tool \"%s\"\n", tool);
758 return;
761 dao = dao_init_new_sheet (NULL);
762 dao->put_formulas = TRUE;
763 cmd_analysis_tool (wbc, sheet, dao, specs, engine, TRUE);
765 g_hash_table_destroy (args);
768 #undef GET_ARG
769 #undef RANGE_ARG
770 #undef RANGE_LISTARG
771 #undef SHEET_ARG
774 static int
775 do_split_save (GOFileSaver *fs, WorkbookView *wbv,
776 const char *outarg, GOCmdContext *cc)
778 Workbook *wb = wb_view_get_workbook (wbv);
779 char *template;
780 GPtrArray *sheets;
781 unsigned ui;
782 int res = 0;
783 GPtrArray *sheet_sel =
784 g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
785 gboolean fs_sheet_selection;
787 g_object_get (G_OBJECT (fs), "sheet-selection", &fs_sheet_selection, NULL);
789 template = strchr (outarg, '%')
790 ? g_strdup (outarg)
791 : g_strconcat (outarg, ".%n", NULL);
793 sheets = g_object_get_data (G_OBJECT (wb),
794 SSCONVERT_SHEET_SET_KEY);
795 if (sheets)
796 g_ptr_array_ref (sheets);
797 else {
798 int i;
799 sheets = g_ptr_array_new ();
800 for (i = 0; i < workbook_sheet_count (wb); i++) {
801 Sheet *sheet = workbook_sheet_by_index (wb, i);
802 g_ptr_array_add (sheets, sheet);
806 for (ui = 0; ui < sheets->len; ui++) {
807 Sheet *sheet = g_ptr_array_index (sheets, ui);
808 char *tmpfile = resolve_template (template, sheet, ui);
809 int oldn = sheet->index_in_wb;
811 g_ptr_array_set_size (sheet_sel, 0);
812 g_ptr_array_add (sheet_sel, sheet);
814 if (!fs_sheet_selection) {
816 * HACK: (bug 694408).
818 * We don't have a good way of specifying the
819 * sheet. Move it to the front and select
820 * it. That will at least make cvs and txt
821 * exporters reliably find it.
823 workbook_sheet_move (sheet, -oldn);
824 wb_view_sheet_focus (wbv, sheet);
827 res = !workbook_view_save_as (wbv, fs, tmpfile, cc);
829 if (!fs_sheet_selection)
830 workbook_sheet_move (sheet, +oldn);
832 g_free (tmpfile);
833 if (res)
834 break;
837 g_free (template);
838 g_ptr_array_unref (sheets);
840 return res;
843 static int
844 convert (char const *inarg, char const *outarg, char const *mergeargs[],
845 GOCmdContext *cc)
847 int res = 0;
848 GOFileSaver *fs = NULL;
849 GOFileOpener *fo = NULL;
850 char *infile = go_shell_arg_to_uri (inarg);
851 char *outfile = outarg ? go_shell_arg_to_uri (outarg) : NULL;
852 WorkbookView *wbv;
853 GOIOContext *io_context = NULL;
854 Workbook *wb = NULL;
855 GOFileSaveScope fsscope;
856 GPtrArray *sheet_sel = NULL;
857 GnmRangeRef const *range = NULL;
859 if (ssconvert_export_id != NULL) {
860 fs = go_file_saver_for_id (ssconvert_export_id);
861 if (fs == NULL) {
862 res = 1;
863 g_printerr (_("Unknown exporter '%s'.\n"
864 "Try --list-exporters to see a list of possibilities.\n"),
865 ssconvert_export_id);
866 goto out;
867 } else if (outfile == NULL &&
868 !ssconvert_one_file_per_sheet &&
869 go_file_saver_get_extension (fs) != NULL) {
870 char const *ext = gsf_extension_pointer (infile);
871 if (*infile) {
872 GString *res = g_string_new (NULL);
873 g_string_append_len (res, infile, ext - infile);
874 g_string_append (res, go_file_saver_get_extension(fs));
875 outfile = g_string_free (res, FALSE);
878 } else {
879 if (outfile != NULL) {
880 fs = go_file_saver_for_file_name (outfile);
881 if (fs == NULL) {
882 res = 2;
883 g_printerr (_("Unable to guess exporter to use for '%s'.\n"
884 "Try --list-exporters to see a list of possibilities.\n"),
885 outfile);
886 goto out;
888 if (ssconvert_verbose)
889 g_printerr (_("Using exporter %s\n"),
890 go_file_saver_get_id (fs));
894 if (outfile == NULL) {
895 g_printerr (_("An output file name or an explicit export type is required.\n"
896 "Try --list-exporters to see a list of possibilities.\n"));
897 res = 1;
898 goto out;
901 if (ssconvert_import_id != NULL) {
902 fo = go_file_opener_for_id (ssconvert_import_id);
903 if (fo == NULL) {
904 res = 1;
905 g_printerr (_("Unknown importer '%s'.\n"
906 "Try --list-importers to see a list of possibilities.\n"),
907 ssconvert_import_id);
908 goto out;
912 if (!fs)
913 goto out;
914 fsscope = go_file_saver_get_save_scope (fs);
916 io_context = go_io_context_new (cc);
917 if (mergeargs == NULL) {
918 wbv = workbook_view_new_from_uri (infile, fo,
919 io_context,
920 ssconvert_import_encoding);
921 } else {
922 wbv = workbook_view_new (NULL);
925 if (go_io_error_occurred (io_context)) {
926 go_io_error_display (io_context);
927 res = 1;
928 goto out;
929 } else if (wbv == NULL) {
930 g_printerr (_("Loading %s failed\n"), infile);
931 res = 1;
932 goto out;
935 wb = wb_view_get_workbook (wbv);
937 res = handle_export_options (fs, wb);
938 if (res)
939 goto out;
941 res = validate_sheet_selection (fs, wb);
942 if (res)
943 goto out;
945 if (mergeargs != NULL) {
946 if (merge (wb, mergeargs, fo, io_context, cc))
947 goto out;
950 if (ssconvert_goal_seek) {
951 int i;
952 Sheet *sheet = wb_view_cur_sheet (wbv);
954 for (i = 0; ssconvert_goal_seek[i]; i++) {
955 setup_range (G_OBJECT (sheet),
956 "ssconvert-goal-seek",
958 ssconvert_goal_seek[i]);
959 dialog_goal_seek (NULL, sheet);
963 if (ssconvert_solve) {
964 Sheet *sheet = wb_view_cur_sheet (wbv);
965 run_solver (sheet, wbv);
968 if (ssconvert_tool_test && ssconvert_tool_test[0]) {
969 run_tool_test (ssconvert_tool_test[0],
970 ssconvert_tool_test + 1,
971 wbv);
974 if (ssconvert_resize) {
975 int rows, cols;
976 if (sscanf (ssconvert_resize, "%dx%d", &rows, &cols) == 2) {
977 int n;
979 if (ssconvert_verbose)
980 g_printerr (_("Resizing to %dx%d\n"),
981 rows, cols);
983 for (n = workbook_sheet_count (wb) - 1;
984 n >= 0;
985 n--) {
986 gboolean err;
987 Sheet *sheet = workbook_sheet_by_index (wb, n);
988 GOUndo *undo =
989 gnm_sheet_resize (sheet, cols, rows,
990 NULL, &err);
991 if (err)
992 g_printerr (_("Resizing of sheet %s failed\n"),
993 sheet->name_unquoted);
994 g_object_unref (undo);
999 if (ssconvert_recalc)
1000 workbook_recalc_all (wb);
1001 gnm_app_recalc ();
1003 if (ssconvert_range)
1004 range = setup_range (G_OBJECT (wb),
1005 "ssconvert-range",
1007 ssconvert_range);
1009 if (ssconvert_one_file_per_sheet ||
1010 fsscope == GO_FILE_SAVE_SHEET ||
1011 range) {
1012 Sheet *def_sheet = NULL;
1014 if (range && range->a.sheet)
1015 def_sheet = range->a.sheet;
1016 else if (fsscope == GO_FILE_SAVE_SHEET || range)
1017 def_sheet = wb_view_cur_sheet (wbv);
1019 sheet_sel = g_ptr_array_new ();
1020 if (def_sheet)
1021 g_ptr_array_add (sheet_sel, def_sheet);
1022 g_object_set_data (G_OBJECT (wb),
1023 SHEET_SELECTION_KEY, sheet_sel);
1026 if (ssconvert_one_file_per_sheet) {
1027 res = do_split_save (fs, wbv, outarg, cc);
1028 } else {
1029 res = !workbook_view_save_as (wbv, fs, outfile, cc);
1032 if (sheet_sel) {
1033 g_object_set_data (G_OBJECT (wb),
1034 SHEET_SELECTION_KEY, NULL);
1035 g_ptr_array_free (sheet_sel, TRUE);
1038 out:
1039 if (wb)
1040 g_object_unref (wb);
1041 if (io_context)
1042 g_object_unref (io_context);
1043 g_free (infile);
1044 g_free (outfile);
1046 return res;
1049 static int
1050 clipboard_export (const char *inarg, char const *outarg, GOCmdContext *cc)
1052 GOFileOpener *fo = NULL;
1053 GOIOContext *io_context = NULL;
1054 WorkbookView *wbv;
1055 Workbook *wb = NULL;
1056 char *infile = go_shell_arg_to_uri (inarg);
1057 char *outfile = go_shell_arg_to_uri (outarg);
1058 int res = 0;
1059 GnmRangeRef const *range;
1060 GnmRange r;
1061 WorkbookControl *wbc = NULL;
1062 GBytes *data = NULL;
1063 GsfOutput *dst;
1065 io_context = go_io_context_new (cc);
1066 wbv = workbook_view_new_from_uri (infile, fo,
1067 io_context,
1068 ssconvert_import_encoding);
1070 if (go_io_error_occurred (io_context)) {
1071 go_io_error_display (io_context);
1072 res = 1;
1073 goto out;
1074 } else if (wbv == NULL) {
1075 g_printerr (_("Loading %s failed\n"), infile);
1076 res = 1;
1077 goto out;
1080 wb = wb_view_get_workbook (wbv);
1082 range = setup_range (G_OBJECT (wb),
1083 "ssconvert-range",
1085 ssconvert_range);
1086 range_init_rangeref (&r, range);
1087 if (range->a.sheet)
1088 wb_view_sheet_focus (wbv, range->a.sheet);
1090 gnm_app_clipboard_cut_copy (wbc, FALSE,
1091 wb_view_cur_sheet_view (wbv),
1092 &r, FALSE);
1094 data = gui_clipboard_test (ssconvert_clipboard);
1095 if (!data) {
1096 g_printerr ("Failed to get clipboard data.\n");
1097 res = 1;
1098 goto out;
1101 dst = go_file_create (outfile, NULL);
1102 if (!dst) {
1103 g_printerr ("Failed to write to %s\n", outfile);
1104 res = 1;
1105 goto out;
1108 gsf_output_write (dst, g_bytes_get_size (data),
1109 g_bytes_get_data (data, NULL));
1110 gsf_output_close (dst);
1111 g_object_unref (dst);
1113 out:
1114 if (data)
1115 g_bytes_unref (data);
1116 if (wb)
1117 g_object_unref (wb);
1118 if (io_context)
1119 g_object_unref (io_context);
1120 g_free (infile);
1121 g_free (outfile);
1123 return res;
1127 main (int argc, char const **argv)
1129 GOErrorInfo *plugin_errs;
1130 int res = 0;
1131 GOCmdContext *cc;
1132 GOptionContext *ocontext;
1133 GError *error = NULL;
1134 gboolean do_usage = FALSE;
1136 /* No code before here, we need to init threads */
1137 argv = gnm_pre_parse_init (argc, argv);
1139 gnm_conf_set_persistence (FALSE);
1141 ocontext = g_option_context_new (_("INFILE [OUTFILE]"));
1142 g_option_context_add_main_entries (ocontext, ssconvert_options, GETTEXT_PACKAGE);
1143 g_option_context_add_group (ocontext, gnm_get_option_group ());
1145 * The printing code uses gtk+ stuff, so we need to init gtk+. We
1146 * do that without opening any displays.
1148 g_option_context_add_group (ocontext, gtk_get_option_group (FALSE));
1149 g_option_context_parse (ocontext, &argc, (char ***)&argv, &error);
1150 g_option_context_free (ocontext);
1152 if (error) {
1153 g_printerr (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
1154 error->message, argv[0]);
1155 g_error_free (error);
1156 return 1;
1159 if (ssconvert_show_version) {
1160 g_print (_("ssconvert version '%s'\ndatadir := '%s'\nlibdir := '%s'\n"),
1161 GNM_VERSION_FULL, gnm_sys_data_dir (), gnm_sys_lib_dir ());
1162 return 0;
1165 if (ssconvert_one_file_per_sheet && ssconvert_merge_target) {
1166 g_printerr (_("--export-file-per-sheet and --merge-to are incompatible\n"));
1167 return 1;
1170 gnm_init ();
1172 cc = gnm_cmd_context_stderr_new ();
1173 gnm_plugins_init (GO_CMD_CONTEXT (cc));
1174 go_plugin_db_activate_plugin_list (
1175 go_plugins_get_available_plugins (), &plugin_errs);
1176 if (plugin_errs) {
1177 /* FIXME: What do we want to do here? */
1178 go_error_info_free (plugin_errs);
1180 go_component_set_default_command_context (cc);
1182 if (ssconvert_list_exporters)
1183 list_them (go_get_file_savers (),
1184 (get_desc_f) &go_file_saver_get_id,
1185 (get_desc_f) &go_file_saver_get_description);
1186 else if (ssconvert_list_importers)
1187 list_them (go_get_file_openers (),
1188 (get_desc_f) &go_file_opener_get_id,
1189 (get_desc_f) &go_file_opener_get_description);
1190 else if (ssconvert_clipboard)
1191 if (argc == 3 && ssconvert_range)
1192 res = clipboard_export (argv[1], argv[2], cc);
1193 else
1194 do_usage = TRUE;
1195 else if (ssconvert_merge_target) {
1196 if (argc >= 3)
1197 res = convert (argv[1], ssconvert_merge_target,
1198 argv + 1, cc);
1199 else
1200 do_usage = TRUE;
1201 } else if (argc == 2 || argc == 3) {
1202 res = convert (argv[1], argv[2], NULL, cc);
1203 } else
1204 do_usage = TRUE;
1206 if (do_usage) {
1207 g_printerr (_("Usage: %s [OPTION...] %s\n"),
1208 g_get_prgname (),
1209 _("INFILE [OUTFILE]"));
1210 res = 1;
1213 go_component_set_default_command_context (NULL);
1214 g_object_unref (cc);
1215 gnm_shutdown ();
1216 gnm_pre_parse_shutdown ();
1218 return res;