1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * ssconvert.c: A wrapper application to convert spreadsheet formats
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>
17 #include "parse-util.h"
18 #include "application.h"
20 #include "workbook-priv.h"
21 #include "workbook-control.h"
23 #include "dependent.h"
24 #include "expr-name.h"
25 #include "libgnumeric.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>
39 #ifdef HAVE_SYS_RESOURCE_H
40 #include <sys/resource.h>
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
[] = {
63 0, G_OPTION_ARG_NONE
, &ssconvert_show_version
,
64 N_("Display program version"),
70 0, G_OPTION_ARG_NONE
, &ssconvert_verbose
,
71 N_("Be somewhat more verbose during conversion"),
75 /* ---------------------------------------- */
78 "import-encoding", 'E',
79 0, G_OPTION_ARG_STRING
, &ssconvert_import_encoding
,
80 N_("Optionally specify an encoding for imported content"),
86 0, G_OPTION_ARG_STRING
, &ssconvert_import_id
,
87 N_("Optionally specify which importer to use"),
93 0, G_OPTION_ARG_NONE
, &ssconvert_list_importers
,
94 N_("List the available importers"),
98 /* ---------------------------------------- */
102 0, G_OPTION_ARG_STRING
, &ssconvert_merge_target
,
103 N_("Merge listed files (all same format) to make this file"),
109 0, G_OPTION_ARG_STRING
, &ssconvert_export_id
,
110 N_("Optionally specify which exporter to use"),
115 "export-options", 'O',
116 0, G_OPTION_ARG_STRING
, &ssconvert_export_options
,
117 N_("Detailed instructions for the chosen exporter"),
123 0, G_OPTION_ARG_NONE
, &ssconvert_list_exporters
,
124 N_("List the available exporters"),
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"),
137 0, G_OPTION_ARG_NONE
, &ssconvert_recalc
,
138 N_("Recalculate all cells before writing the result"),
144 G_OPTION_FLAG_HIDDEN
, G_OPTION_ARG_STRING
, &ssconvert_resize
,
145 N_("Resize to given ROWSxCOLS"),
150 /* ---------------------------------------- */
152 /* For now these are for INTERNAL GNUMERIC USE ONLY. */
155 G_OPTION_FLAG_HIDDEN
, G_OPTION_ARG_STRING
, &ssconvert_range
,
156 N_("The range to export"),
162 G_OPTION_FLAG_HIDDEN
, G_OPTION_ARG_STRING_ARRAY
, &ssconvert_goal_seek
,
163 N_("Goal seek areas"),
169 G_OPTION_FLAG_HIDDEN
, G_OPTION_ARG_NONE
, &ssconvert_solve
,
170 N_("Run the solver"),
176 G_OPTION_FLAG_HIDDEN
, G_OPTION_ARG_STRING_ARRAY
, &ssconvert_tool_test
,
177 N_("Tool test specs"),
181 /* ---------------------------------------- */
187 setup_range (GObject
*obj
, const char *key
, Workbook
*wb
, const char *rtxt
)
194 pp
.sheet
= workbook_sheet_by_index (wb
, 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");
204 g_object_set_data_full (obj
, key
,
205 g_memdup (&rr
, sizeof (rr
)),
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
)
218 if (g_signal_handler_find (fs
, G_SIGNAL_MATCH_ID
,
219 sig
, 0, NULL
, NULL
, NULL
)) {
222 go_file_saver_set_export_options
224 ssconvert_export_options
,
228 g_printerr ("ssconvert: %s\n", err
230 : _("Cannot parse export options."));
236 g_printerr (_("The file saver does not take options\n"));
242 typedef gchar
const *(*get_desc_f
)(void *);
245 list_them (GList
*them
,
246 get_desc_f get_his_id
,
247 get_desc_f get_his_description
)
251 gboolean interactive
;
253 for (ptr
= them
; ptr
; ptr
= ptr
->next
) {
254 GObject
*obj
= ptr
->data
;
257 g_object_get (obj
, "interactive-only", &interactive
, NULL
);
261 id
= get_his_id (obj
);
263 len
= MAX (len
, strlen (id
));
266 g_printerr ("%-*s | %s\n", len
,
267 /* Translate these? */
270 for (ptr
= them
; ptr
; ptr
= ptr
->next
) {
271 GObject
*obj
= ptr
->data
;
274 g_object_get (obj
, "interactive-only", &interactive
, NULL
);
278 id
= get_his_id (obj
);
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.
290 read_files_to_merge (const char *inputs
[], GOFileOpener
*fo
,
291 GOIOContext
*io_context
, GOCmdContext
*cc
)
296 const char *fname
= *inputs
;
297 char *uri
= go_shell_arg_to_uri (fname
);
299 workbook_view_new_from_uri (uri
, fo
, io_context
,
300 ssconvert_import_encoding
);
304 if (go_io_error_occurred (io_context
)) {
305 g_slist_free_full (wbs
, g_object_unref
);
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.
323 suggest_size (GSList
*wbs
, int *csuggest
, int *rsuggest
)
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
);
346 cb_collect_names (G_GNUC_UNUSED gconstpointer key
,
350 if (!expr_name_is_active (nexpr
))
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). */
359 merge_single (Workbook
*wb
, Workbook
*wb2
,
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
);
368 for (p
= names
; p
; p
= p
->next
) {
369 GnmNamedExpr
*nexpr
= p
->data
;
370 const char *name
= expr_name_name (nexpr
);
371 GnmNamedExpr
*nexpr2
;
373 GnmParsePos newpos
= nexpr
->pos
;
375 if (!expr_name_is_active (nexpr
))
378 if (nexpr
->pos
.wb
!= wb2
|| nexpr
->pos
.sheet
!= NULL
)
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"),
388 g_slist_free (names
);
392 /* Move name scope to workbook 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
);
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
);
414 GnmNamedExpr
*nexpr
= names
->data
;
415 names
= g_slist_delete_link (names
, names
);
418 GnmParsePos newpos
= nexpr
->pos
;
420 expr_name_set_pos (nexpr
, &newpos
);
422 expr_name_unref (nexpr
);
425 undo
= gnm_sheet_resize (sheet
, cmax
, rmax
, cc
, &err
);
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
);
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
);
444 /* Merge a collection of workbooks into one. */
446 merge (Workbook
*wb
, char const *inputs
[],
447 GOFileOpener
*fo
, GOIOContext
*io_context
, GOCmdContext
*cc
)
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
);
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
);
472 g_slist_free_full (wbs
, g_object_unref
);
477 resolve_template (const char *template, Sheet
*sheet
)
479 GString
*s
= g_string_new (NULL
);
484 char *res
= go_shell_arg_to_uri (s
->str
);
485 g_string_free (s
, TRUE
);
494 g_string_append_printf (s
, "%d", sheet
->index_in_wb
);
497 g_string_append (s
, sheet
->name_unquoted
);
500 g_string_append_c (s
, '%');
506 g_string_append_c (s
, *template);
513 run_solver (Sheet
*sheet
, WorkbookView
*wbv
)
515 GnmSolverParameters
*params
= sheet
->solver_parameters
;
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
,
527 for (l
= gnm_solver_db_get (); l
; l
= l
->next
) {
528 GnmSolverFactory
*factory
= l
->data
;
529 if (params
->options
.model_type
!= factory
->type
)
531 if (gnm_solver_factory_functional (factory
, NULL
)) {
532 gnm_solver_param_set_algorithm (params
,
539 if (!gnm_solver_param_valid (params
, &err
))
542 sol
= params
->options
.algorithm
543 ? gnm_solver_factory_create (params
->options
.algorithm
, params
)
546 g_set_error (&err
, go_error_invalid (), 0,
547 _("Failed to create solver"));
551 if (!gnm_solver_start (sol
, wbc
, &err
))
554 while (!gnm_solver_finished (sol
)) {
555 g_main_context_iteration (NULL
, TRUE
);
558 switch (sol
->status
) {
559 case GNM_SOLVER_STATUS_DONE
:
561 case GNM_SOLVER_STATUS_CANCELLED
:
562 g_printerr ("Solver reached time or iteration limit\n");
565 g_set_error (&err
, go_error_invalid (), 0,
566 _("Solver ran, but failed"));
570 gnm_solver_store_result (sol
);
572 gnm_solver_create_report (sol
, "Solver");
576 g_object_unref (sol
);
578 g_printerr (_("Solver: %s\n"), err
->message
);
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_))
589 run_tool_test (const char *tool
, char **argv
, WorkbookView
*wbv
)
592 WorkbookControl
*wbc
;
594 data_analysis_output_t
*dao
;
595 analysis_tool_engine engine
;
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
, ':');
612 g_printerr ("Ignoring tool test argument \"%s\"\n", s
);
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
;
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
;
654 g_printerr ("no test for tool \"%s\"\n", tool
);
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
);
671 convert (char const *inarg
, char const *outarg
, char const *mergeargs
[],
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
;
680 GOIOContext
*io_context
= NULL
;
683 if (ssconvert_export_id
!= NULL
) {
684 fs
= go_file_saver_for_id (ssconvert_export_id
);
687 g_printerr (_("Unknown exporter '%s'.\n"
688 "Try --list-exporters to see a list of possibilities.\n"),
689 ssconvert_export_id
);
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
);
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
);
703 if (outfile
!= NULL
) {
704 fs
= go_file_saver_for_file_name (outfile
);
707 g_printerr (_("Unable to guess exporter to use for '%s'.\n"
708 "Try --list-exporters to see a list of possibilities.\n"),
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"));
725 if (ssconvert_import_id
!= NULL
) {
726 fo
= go_file_opener_for_id (ssconvert_import_id
);
729 g_printerr (_("Unknown importer '%s'.\n"
730 "Try --list-importers to see a list of possibilities.\n"),
731 ssconvert_import_id
);
739 io_context
= go_io_context_new (cc
);
740 if (mergeargs
== NULL
) {
741 wbv
= workbook_view_new_from_uri (infile
, fo
,
743 ssconvert_import_encoding
);
745 wbv
= workbook_view_new (NULL
);
748 if (go_io_error_occurred (io_context
)) {
749 go_io_error_display (io_context
);
752 } else if (wbv
== NULL
) {
753 g_printerr (_("Loading %s failed\n"), infile
);
758 wb
= wb_view_get_workbook (wbv
);
760 res
= handle_export_options (fs
, GO_DOC (wb
));
764 if (mergeargs
!= NULL
) {
765 if (merge (wb
, mergeargs
, fo
, io_context
, cc
))
769 if (ssconvert_goal_seek
) {
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,
793 if (ssconvert_resize
) {
795 if (sscanf (ssconvert_resize
, "%dx%d", &rows
, &cols
) == 2) {
798 if (ssconvert_verbose
)
799 g_printerr ("Resizing to %dx%d\n", rows
, cols
);
801 for (n
= workbook_sheet_count (wb
) - 1;
805 Sheet
*sheet
= workbook_sheet_by_index (wb
, n
);
807 gnm_sheet_resize (sheet
, cols
, rows
,
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
);
822 setup_range (G_OBJECT (wb
),
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
;
835 template = strchr (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
);
864 g_slist_free (sheets
);
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
);
877 g_object_unref (io_context
);
885 main (int argc
, char const **argv
)
887 GOErrorInfo
*plugin_errs
;
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
);
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
);
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 ());
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
);
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
);
945 g_printerr (_("Usage: %s [OPTION...] %s\n"),
947 _("INFILE [OUTFILE]"));
951 go_component_set_default_command_context (NULL
);
954 gnm_pre_parse_shutdown ();