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