Use application properties instead of ugly dual-use global variable
[gnumeric.git] / plugins / excel / boot.c
blob37a4fa9d19d19a4f185f87109a3db0a6e6a28f6f
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * boot.c: the external interface to the MS Excel import/export
6 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 1998-2001 Michael Meeks (miguel@kernel.org)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) version 3.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
24 #include <gnumeric-config.h>
25 #include <gnumeric.h>
27 #include "libgnumeric.h"
28 #include "command-context.h"
29 #include "workbook-view.h"
30 #include "workbook.h"
31 #include "gnm-plugin.h"
33 #include "excel.h"
34 #include "ms-excel-write.h"
35 #include "boot.h"
36 #include "ms-excel-util.h"
37 #include "ms-excel-read.h"
39 #include <goffice/goffice.h>
40 #include <gsf/gsf-input.h>
41 #include <gsf/gsf-infile.h>
42 #include <gsf/gsf-infile-msole.h>
43 #include <gsf/gsf-infile-msvba.h>
44 #include <gsf/gsf-msole-utils.h>
45 #include <gsf/gsf-output-stdio.h>
46 #include <gsf/gsf-outfile.h>
47 #include <gsf/gsf-outfile-msole.h>
48 #include <gsf/gsf-structured-blob.h>
49 #include <glib/gi18n-lib.h>
50 #include <string.h>
52 GNM_PLUGIN_MODULE_HEADER;
54 /* Used to toggle debug messages on & off */
56 * As a convention
57 * 0 = quiet, no experimental features.
58 * 1 = enable experimental features
59 * >1 increasing levels of detail.
61 gint ms_excel_read_debug = 0;
62 gint ms_excel_pivot_debug = 0;
63 gint ms_excel_escher_debug = 0;
64 gint ms_excel_formula_debug = 0;
65 gint ms_excel_chart_debug = 0;
66 gint ms_excel_write_debug = 0;
67 gint ms_excel_object_debug = 0;
69 gboolean excel_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl);
70 void excel_file_open (GOFileOpener const *fo, GOIOContext *context, WorkbookView *wbv, GsfInput *input);
71 void excel_enc_file_open (GOFileOpener const *fo, char const *enc, GOIOContext *context, WorkbookView *wbv, GsfInput *input);
72 void excel_biff7_file_save (GOFileSaver const *fs, GOIOContext *context, WorkbookView const *wbv, GsfOutput *output);
73 void excel_biff8_file_save (GOFileSaver const *fs, GOIOContext *context, WorkbookView const *wbv, GsfOutput *output);
74 void excel_dsf_file_save (GOFileSaver const *fs, GOIOContext *context, WorkbookView const *wbv, GsfOutput *output);
76 static GsfInput *
77 find_content_stream (GsfInfile *ole, gboolean *is_97)
79 static char const * const stream_names[] = {
80 "Workbook", "WORKBOOK", "workbook",
81 "Book", "BOOK", "book"
83 GsfInput *stream;
84 unsigned i;
86 for (i = 0 ; i < G_N_ELEMENTS (stream_names) ; i++) {
87 stream = gsf_infile_child_by_name (ole, stream_names[i]);
88 if (stream != NULL) {
89 if (is_97 != NULL)
90 *is_97 = (i < 3);
91 return stream;
95 return NULL;
98 gboolean
99 excel_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl)
101 GsfInfile *ole;
102 GsfInput *stream;
103 gboolean res = FALSE;
105 if (input == NULL)
106 return FALSE;
107 ole = gsf_infile_msole_new (input, NULL);
108 if (ole == NULL) { /* Test for non-OLE BIFF file */
109 guint8 const *data;
110 gsf_input_seek (input, 0, G_SEEK_SET);
111 data = gsf_input_read (input, 2, NULL);
112 return data && data[0] == 0x09 && (data[1] & 0xf1) == 0;
115 stream = find_content_stream (ole, NULL);
116 if (stream != NULL) {
117 g_object_unref (stream);
118 res = TRUE;
120 g_object_unref (ole);
122 return res;
125 static void
126 excel_read_metadata (GsfDocMetaData *meta_data, GsfInfile *ole, char const *name,
127 GOIOContext *context)
129 GsfInput *stream = gsf_infile_child_by_name (ole, name);
131 if (stream != NULL) {
132 GError *err = gsf_doc_meta_data_read_from_msole (meta_data, stream);
133 if (err != NULL) {
134 go_io_warning (context, "%s", err->message);
135 g_error_free (err);
138 g_object_unref (stream);
142 #ifdef SPEW_VBA
143 static void
144 cb_dump_vba (char const *name, guint8 const *src_code)
146 g_printerr ("<module name=\"%s\">\n<![CDATA[%s]]>\n</module>\n", name, src_code);
148 #endif
150 /* Service entry point */
151 void
152 excel_enc_file_open (GOFileOpener const *fo, char const *enc, GOIOContext *context,
153 WorkbookView *wbv, GsfInput *input)
155 GsfInput *stream = NULL;
156 GError *err = NULL;
157 GsfInfile *ole = gsf_infile_msole_new (input, &err);
158 Workbook *wb = wb_view_get_workbook (wbv);
159 gboolean is_double_stream_file, is_97;
160 GsfDocMetaData *meta_data;
162 if (ole == NULL) {
163 guint8 const *data;
165 /* Test for non-OLE BIFF file */
166 gsf_input_seek (input, 0, G_SEEK_SET);
167 data = gsf_input_read (input, 2, NULL);
168 if (data && data[0] == 0x09 && (data[1] & 0xf1) == 0) {
169 gsf_input_seek (input, -2, G_SEEK_CUR);
170 excel_read_workbook (context, wbv, input,
171 &is_double_stream_file, enc);
172 /* NOTE : we lack a saver for the early formats */
173 g_clear_error (&err);
174 return;
177 /* OK, it really isn't an Excel file */
178 g_return_if_fail (err != NULL);
179 go_cmd_context_error_import (GO_CMD_CONTEXT (context),
180 err->message);
181 g_error_free (err);
182 return;
185 stream = find_content_stream (ole, &is_97);
186 if (stream == NULL) {
187 go_cmd_context_error_import (GO_CMD_CONTEXT (context),
188 _("No Workbook or Book streams found."));
189 g_object_unref (ole);
190 return;
193 excel_read_workbook (context, wbv, stream, &is_double_stream_file, enc);
194 g_object_unref (stream);
196 meta_data = gsf_doc_meta_data_new ();
197 excel_read_metadata (meta_data, ole, "\05SummaryInformation", context);
198 excel_read_metadata (meta_data, ole, "\05DocumentSummaryInformation", context);
199 go_doc_set_meta_data (GO_DOC (wb), meta_data);
200 g_object_unref (meta_data);
202 /* See if there are any macros to keep around */
203 stream = gsf_infile_child_by_name (ole, "\01CompObj");
204 if (stream != NULL) {
205 GsfInput *macros = gsf_infile_child_by_name (ole, "_VBA_PROJECT_CUR");
206 if (macros != NULL) {
207 GsfInput *vba_child = gsf_infile_child_by_name (GSF_INFILE (macros), "VBA");
208 GsfInfile *vba = vba_child
209 ? gsf_infile_msvba_new (GSF_INFILE (vba_child), NULL)
210 : NULL;
211 GsfStructuredBlob *blob;
213 if (NULL != vba) {
214 GHashTable *modules =
215 gsf_infile_msvba_steal_modules (GSF_INFILE_MSVBA (vba));
216 if (NULL != modules) {
217 #ifdef SPEW_VBA
218 g_hash_table_foreach (modules,
219 (GHFunc) cb_dump_vba, NULL);
220 #endif
221 g_object_set_data_full (G_OBJECT (wb), "VBA",
222 modules, (GDestroyNotify) g_hash_table_destroy);
224 g_object_unref (vba);
226 if (vba_child)
227 g_object_unref (vba_child);
229 blob = gsf_structured_blob_read (stream);
230 if (blob)
231 g_object_set_data_full (G_OBJECT (wb),
232 "MS_EXCEL_COMPOBJ_STREAM",
233 blob, g_object_unref);
235 blob = gsf_structured_blob_read (macros);
236 if (blob)
237 g_object_set_data_full (G_OBJECT (wb),
238 "MS_EXCEL_MACROS",
239 blob, g_object_unref);
241 g_object_unref (macros);
243 g_object_unref (stream);
246 stream = gsf_infile_child_by_name (ole, "\01Ole");
247 if (stream) {
248 GsfStructuredBlob *blob = gsf_structured_blob_read (stream);
249 if (blob)
250 g_object_set_data_full (G_OBJECT (wb), "MS_EXCEL_OLE_STREAM",
251 blob, g_object_unref);
252 g_object_unref (stream);
255 g_object_unref (ole);
257 /* simple guess of format based on stream names */
258 if (is_double_stream_file)
259 workbook_set_saveinfo (wb, GO_FILE_FL_AUTO,
260 go_file_saver_for_id ("Gnumeric_Excel:excel_dsf"));
261 else if (is_97)
262 workbook_set_saveinfo (wb, GO_FILE_FL_AUTO,
263 go_file_saver_for_id ("Gnumeric_Excel:excel_biff8"));
264 else
265 workbook_set_saveinfo (wb, GO_FILE_FL_AUTO,
266 go_file_saver_for_id ("Gnumeric_Excel:excel_biff7"));
269 void
270 excel_file_open (GOFileOpener const *fo, GOIOContext *context,
271 WorkbookView *wbv, GsfInput *input)
273 excel_enc_file_open (fo, NULL, context, wbv, input);
276 static void
277 excel_save (GOIOContext *context, WorkbookView const *wbv, GsfOutput *output,
278 gboolean biff7, gboolean biff8)
280 Workbook *wb;
281 GsfOutput *content;
282 GsfOutfile *outfile;
283 ExcelWriteState *ewb = NULL;
284 GsfStructuredBlob *blob;
285 GsfDocMetaData *meta_data;
287 go_io_progress_message (context, _("Preparing to save..."));
288 go_io_progress_range_push (context, 0.0, 0.1);
289 ewb = excel_write_state_new (context, wbv, biff7, biff8);
290 go_io_progress_range_pop (context);
291 if (ewb == NULL)
292 return;
294 wb = wb_view_get_workbook (wbv);
295 outfile = gsf_outfile_msole_new (output);
296 ewb->export_macros = (biff8 &&
297 NULL != g_object_get_data (G_OBJECT (wb), "MS_EXCEL_MACROS"));
299 go_io_progress_message (context, _("Saving file..."));
300 go_io_progress_range_push (context, 0.1, 1.0);
301 if (biff7)
302 excel_write_v7 (ewb, outfile);
303 if (biff8)
304 excel_write_v8 (ewb, outfile);
305 excel_write_state_free (ewb);
306 go_io_progress_range_pop (context);
308 meta_data = go_doc_get_meta_data (GO_DOC (wb));
309 if (meta_data != NULL) {
310 content = gsf_outfile_new_child (outfile,
311 "\05DocumentSummaryInformation", FALSE);
312 gsf_doc_meta_data_write_to_msole (meta_data, content, TRUE);
313 gsf_output_close (content);
314 g_object_unref (content);
316 content = gsf_outfile_new_child (outfile,
317 "\05SummaryInformation", FALSE);
318 gsf_doc_meta_data_write_to_msole (meta_data, content, FALSE);
319 gsf_output_close (content);
320 g_object_unref (content);
323 /* restore the macros we loaded */
324 blob = g_object_get_data (G_OBJECT (wb), "MS_EXCEL_COMPOBJ_STREAM");
325 if (blob != NULL)
326 gsf_structured_blob_write (blob, outfile);
328 blob = g_object_get_data (G_OBJECT (wb), "MS_EXCEL_OLE_STREAM");
329 if (blob != NULL)
330 gsf_structured_blob_write (blob, outfile);
332 blob = g_object_get_data (G_OBJECT (wb), "MS_EXCEL_MACROS");
333 if (blob)
334 gsf_structured_blob_write (blob, outfile);
336 gsf_output_close (GSF_OUTPUT (outfile));
337 g_object_unref (outfile);
340 void
341 excel_dsf_file_save (GOFileSaver const *fs, GOIOContext *context,
342 WorkbookView const *wbv, GsfOutput *output)
344 excel_save (context, wbv, output, TRUE, TRUE);
346 void
347 excel_biff8_file_save (GOFileSaver const *fs, GOIOContext *context,
348 WorkbookView const *wbv, GsfOutput *output)
350 excel_save (context, wbv, output, FALSE, TRUE);
353 void
354 excel_biff7_file_save (GOFileSaver const *fs, GOIOContext *context,
355 WorkbookView const *wbv, GsfOutput *output)
357 excel_save (context, wbv, output, TRUE, FALSE);
361 #include <formula-types.h>
362 G_MODULE_EXPORT void
363 go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
365 excel_read_init ();
367 #if 0
369 int i;
370 char const *name;
372 for (i = 0 ; i < excel_func_desc_size; i++) {
373 ExcelFuncDesc const *fd = excel_func_desc + i;
374 name = fd->name;
375 if (fd->flags & (XL_UNKNOWN | XL_MAGIC))
376 continue;
377 if (fd->flags & XL_XLM) {
378 if (fd->flags != XL_XLM)
379 g_printerr ("%s : flags in addition to XLM\n", name);
380 if (fd->min_args != fd->max_args)
381 g_printerr ("%s : min != max\n", name);
382 continue;
384 if (fd->min_args < 0)
385 g_printerr ("%s : min_args < 0\n", name);
386 if (fd->max_args < 0)
387 g_printerr ("%s : min_args < 0\n", name);
388 if (fd->known_args != NULL &&
389 fd->num_known_args != strlen (fd->known_args))
390 g_printerr ("%s : num_expected_args inconsistent\n", name);
393 #endif
397 * Cleanup allocations made by this plugin.
398 * (Called right before we are unloaded.)
400 G_MODULE_EXPORT void
401 go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
403 destroy_xl_font_widths ();
404 excel_read_cleanup ();