Shutdown: help the style leak printer out a bit.
[gnumeric.git] / plugins / qpro / qpro-read.c
blob30696803d9bb49e9f4dd8095f620517049d0a51e
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * qpro-read.c: Read Quatro Pro files
6 * Copyright (C) 2002 Jody Goldberg (jody@gnome.org)
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.
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.
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
23 * Docs for the format used to be at
25 * www.corel.com/partners_developers/ds/CO32SDK/docs/qp7/Qpf3recd.htm
26 * www.corel.com/partners_developers/ds/CO32SDK/docs/qp7/Qpf2intr.htm
28 * Try Wayback!
30 #include <gnumeric-config.h>
31 #include <glib/gi18n-lib.h>
32 #include <gnumeric.h>
33 #include <string.h>
34 #include "qpro.h"
36 #include <gutils.h>
37 #include <func.h>
38 #include <goffice/goffice.h>
39 #include <workbook-view.h>
40 #include <workbook.h>
41 #include <sheet.h>
42 #include <cell.h>
43 #include <value.h>
44 #include <expr.h>
45 #include <mstyle.h>
46 #include <sheet-style.h>
47 #include <style-color.h>
48 #include <parse-util.h>
49 #include <gnm-plugin.h>
51 #include <gsf/gsf-utils.h>
52 #include <gsf/gsf-input.h>
53 #include <gsf/gsf-infile.h>
54 #include <gsf/gsf-infile-msole.h>
56 GNM_PLUGIN_MODULE_HEADER;
58 gboolean qpro_file_probe (GOFileOpener const *fo, GsfInput *input,
59 GOFileProbeLevel pl);
60 void qpro_file_open (GOFileOpener const *fo, GOIOContext *context,
61 WorkbookView *new_wb_view, GsfInput *input);
63 static gboolean
64 qpro_check_signature (GsfInput *input)
66 guint8 const *header;
67 guint16 version;
69 if (gsf_input_seek (input, 0, G_SEEK_SET) ||
70 NULL == (header = gsf_input_read (input, 2+2+2, NULL)) ||
71 GSF_LE_GET_GUINT16 (header + 0) != 0 ||
72 GSF_LE_GET_GUINT16 (header + 2) != 2)
73 return FALSE;
74 version = GSF_LE_GET_GUINT16 (header + 4);
75 return (version == 0x1001 || /* 'WB1' format, documented */
76 version == 0x1002 || /* 'WB2' format, documented */
77 version == 0x1006 || /* qpro 6.0 ?? */
78 version == 0x1007); /* qpro 7.0 ?? */
81 gboolean
82 qpro_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl)
84 GsfInfile *ole;
85 GsfInput *stream;
86 gboolean res = FALSE;
88 /* check for >= QPro 6.0 which is OLE based */
89 ole = gsf_infile_msole_new (input, NULL);
90 if (ole != NULL) {
91 stream = gsf_infile_child_by_name (GSF_INFILE (ole),
92 "PerfectOffice_MAIN");
93 if (stream != NULL) {
94 res = qpro_check_signature (stream);
95 g_object_unref (stream);
97 g_object_unref (ole);
98 } else
99 res = qpro_check_signature (input);
101 return res;
104 typedef struct {
105 GsfInput *input;
106 GOIOContext *io_context;
107 WorkbookView *wbv;
108 Workbook *wb;
109 Sheet *cur_sheet;
110 GIConv converter;
111 gboolean corrupted;
112 } QProReadState;
114 static void
115 corrupted (QProReadState *state)
117 if (!state->corrupted) {
118 state->corrupted = TRUE;
119 g_printerr (_("File is most likely corrupted.\n"));
123 static void
124 q_condition_barf (QProReadState *state, const char *cond)
126 corrupted (state);
127 /* Translation is screwed here. */
128 g_printerr ("Condition \"%s\" failed.\n", cond);
131 #define Q_CHECK_CONDITION(cond_) \
132 do { \
133 if (!(cond_)) { \
134 q_condition_barf (state, #cond_); \
135 goto error; \
137 } while (0)
140 static GnmValue *
141 qpro_new_string (QProReadState *state, gchar const *data)
143 return value_new_string_nocopy (
144 g_convert_with_iconv (data, -1, state->converter,
145 NULL, NULL, NULL));
149 static guint8 const *
150 qpro_get_record (QProReadState *state, guint16 *id, guint16 *len)
152 guint8 const *data;
154 data = gsf_input_read (state->input, 4, NULL);
155 Q_CHECK_CONDITION (data != NULL);
157 *id = GSF_LE_GET_GUINT16 (data + 0);
158 *len = GSF_LE_GET_GUINT16 (data + 2);
160 #if 0
161 g_printerr ("%hd with %hd\n", *id, *len);
162 #endif
164 if (*len == 0)
165 return "";
167 data = gsf_input_read (state->input, *len, NULL);
169 switch (*id) {
170 case QPRO_UNDOCUMENTED_837:
171 case QPRO_UNDOCUMENTED_907:
172 break; /* Nothing. */
173 default:
174 Q_CHECK_CONDITION (*len < 0x2000);
177 Q_CHECK_CONDITION (data != NULL);
179 return data;
181 error:
182 return NULL;
185 #define validate(f,expected) qpro_validate_len (state, #f, len, expected)
187 static gboolean
188 qpro_validate_len (QProReadState *state, char const *id, guint16 len, int expected_len)
190 if (expected_len >= 0 && len != expected_len) {
191 corrupted (state);
192 g_printerr ("Invalid '%s' record of length %hd instead of %d\n",
193 id, len, expected_len);
194 return FALSE;
197 return TRUE;
200 enum { ARGS_UNKNOWN = -1, ARGS_COUNT_FOLLOWS = -2 };
202 static struct {
203 char const *name;
204 int args;
205 } const qpro_functions[QPRO_OP_LAST_FUNC - QPRO_OP_FIRST_FUNC + 1] = {
206 { "err", ARGS_UNKNOWN }, /* No args -- returns error. */
207 { "abs", 1 },
208 { "int", 1 },
209 { "sqrt", 1 },
210 { "log", 1 },
211 { "ln", 1 },
212 { "pi", 0 },
213 { "sin", 1 },
214 { "cos", 1 },
215 { "tan", 1 },
216 { "atan2", 2 },
217 { "atan", 1 },
218 { "asin", 1 },
219 { "acos", 1 },
220 { "exp", 1 },
221 { "mod", 2 },
222 { "choose", ARGS_COUNT_FOLLOWS },
223 { "isna", 1 },
224 { "iserr", 1 },
225 { "false", 0 },
226 { "true", 0 },
227 { "rand", 0 },
228 { "date", 1 },
229 { "now", 0 },
230 { "pmt", ARGS_UNKNOWN }, /* (pv,Rate,Nper) */
231 { "pv", ARGS_UNKNOWN }, /* (pmt, Rate, Nper) */
232 { "fv", ARGS_UNKNOWN }, /* (pmt, Rate, Nper) */
233 { "if", 3 },
234 { "day", 1 },
235 { "month", 1 },
236 { "year", 1 },
237 { "round", 2 },
238 { "time", 1 },
239 { "hour", 1 },
240 { "minute", 1 },
241 { "second", 1 },
242 { "isnumber", 1 },
243 { "istext", 1 },
244 { "len", 1 },
245 { "n", 1 },
246 { "fixed", 2 },
247 { "mid", 3 },
248 { "char", 1 },
249 { "code", 1 },
250 { "find", ARGS_UNKNOWN }, /* (subString, String,StartNumber) */
251 { "dateval", ARGS_UNKNOWN }, /* @datevalue(datestring) */
252 { "timeval", ARGS_UNKNOWN }, /* @timevalue(timestring) */
253 { "cellptr", ARGS_UNKNOWN }, /* @cellpointer(attribute) -- the requested attribute of the current cell */
254 { "sum", ARGS_COUNT_FOLLOWS },
255 { "avg", ARGS_COUNT_FOLLOWS },
256 { "count", ARGS_COUNT_FOLLOWS },
257 { "min", ARGS_COUNT_FOLLOWS },
258 { "max", ARGS_COUNT_FOLLOWS },
259 { "vlookup", 3 },
260 { "npv1", ARGS_UNKNOWN },
261 { "var", ARGS_COUNT_FOLLOWS },
262 { "std", ARGS_COUNT_FOLLOWS },
263 { "irr", ARGS_UNKNOWN }, /* @irr(guess,block) */
264 { "hlookup", 3 }, /* @hlookup(X,Block,Row) */
265 { "dsum", 3 },
266 { "davg", 3 },
267 { "dcount", 3 },
268 { "dmin", 3 },
269 { "dmax", 3 },
270 { "dvar", 3 },
271 { "dstdev", 3 },
272 { "index2d", ARGS_UNKNOWN }, /* Possibly @index(Block, Column, Row, <Page>) */
273 { "columns", 1 },
274 { "rows", 1 },
275 { "rept", 1 },
276 { "upper", 1 },
277 { "lower", 1 },
278 { "left", 2 },
279 { "right", 2 },
280 { "replace", 4 },
281 { "proper", 1 },
282 { "cell", ARGS_UNKNOWN }, /* @cell(attribute,Block) */
283 { "trim", 1 },
284 { "clean", 1 },
285 { "s", ARGS_UNKNOWN }, /* @s(Block) -- The string value of the upper left cell in Block (blank, if it is a value entry). */
286 { "n", ARGS_UNKNOWN }, /* @n(block) -- The numberic vlue of the upper lwft cell in Block (0, if it is a label or blank). */
287 { "exact", 2 },
288 { "call", ARGS_UNKNOWN },
289 { "at", ARGS_UNKNOWN }, /* @@(Cell) */
290 { "rate", ARGS_UNKNOWN }, /* @rate(Fv,Pv,Nper) */
291 { "term", ARGS_UNKNOWN }, /* @term(Pmt,Rate,Fv) */
292 { "cterm", ARGS_UNKNOWN }, /* @cterm(Rate,Fv,Pv) */
293 { "sln", ARGS_UNKNOWN }, /* @sln(cost,salvage,life) */
294 { "syd", ARGS_UNKNOWN }, /* (cost,salvage,life,period) */
295 { "ddb", ARGS_UNKNOWN }, /* (cost,salvage,life,period) */
296 { "stdp", ARGS_COUNT_FOLLOWS },
297 { "varp", ARGS_COUNT_FOLLOWS },
298 { "dstdevp", 3 },
299 { "dvarp", 3 },
300 { "pval", ARGS_UNKNOWN }, /* (Rate,Nper,Pmt,<Fv>,<Type>) */
301 { "paymt", ARGS_UNKNOWN }, /* (Rate,Nper,Pv,<Fv>,<Type>) */
302 { "fval", ARGS_UNKNOWN }, /* (Rate,Nper,Pmt,<Fv>,<Type>) */
303 { "nper", ARGS_UNKNOWN }, /* (Rate,Pmt,Pv,<Fv><Type>) */
304 { "irate", ARGS_UNKNOWN }, /* (Nper,Pmt,Pv,<Fv>,<Type>) */
305 { "ipaymt", ARGS_UNKNOWN }, /* (Rate,Per,Nper,Pv,<Fv>,<Type>) */
306 { "ppaymt", ARGS_UNKNOWN }, /* (Rate,Per,Nper,Pv,<Fv>,<Type>) */
307 { "sumproduct", 2 },
308 { "memavail", ARGS_UNKNOWN }, /* No args. */
309 { "mememsavail", ARGS_UNKNOWN }, /* No args. Returns NA. */
310 { "fileexists", ARGS_UNKNOWN }, /* @fileexists(FileName) */
311 { "curvalue", ARGS_UNKNOWN },
312 { "degrees", 1 },
313 { "radians", 1 },
314 { "hex2dec", 1 },
315 { "dec2hex", 2 },
316 { "today", 0 },
317 { "npv2", ARGS_UNKNOWN },
318 { "cellindex2d", ARGS_UNKNOWN },
319 { "version", ARGS_UNKNOWN },
320 { NULL, ARGS_UNKNOWN },
321 { NULL, ARGS_UNKNOWN },
322 { NULL, ARGS_UNKNOWN },
323 { NULL, ARGS_UNKNOWN },
324 { NULL, ARGS_UNKNOWN },
325 { NULL, ARGS_UNKNOWN },
326 { NULL, ARGS_UNKNOWN },
327 { NULL, ARGS_UNKNOWN },
328 { "sheets", ARGS_UNKNOWN },
329 { NULL, ARGS_UNKNOWN },
330 { NULL, ARGS_UNKNOWN },
331 { "index3d", ARGS_UNKNOWN },
332 { "cellindex3d", ARGS_UNKNOWN },
333 { "property", ARGS_UNKNOWN },
334 { "ddelink", ARGS_UNKNOWN },
335 { "command", ARGS_UNKNOWN }
338 #ifdef DEBUG_MISSING
339 static void
340 dump_missing_functions (void)
342 static gboolean done = FALSE;
343 int i;
345 if (!done) {
346 for (i = QPRO_OP_FIRST_FUNC; i <= QPRO_OP_LAST_FUNC; i++) {
347 char const *name = qpro_functions[i - QPRO_OP_FIRST_FUNC].name;
348 int args = qpro_functions[i - QPRO_OP_FIRST_FUNC].args;
349 GnmFunc *f;
350 int dummy;
352 if (!name || args != ARGS_UNKNOWN)
353 continue;
355 f = gnm_func_lookup (name, NULL);
356 if (f == 0) {
357 g_warning ("%s is not known.", name);
358 continue;
361 gnm_func_count_args (f, &dummy, &dummy);
363 g_warning ("Function %s has args %s.",
364 name, f->arg_names ? f->arg_names : "?");
366 done = TRUE;
369 #endif
371 static const GnmExpr *
372 expr_stack_pop (GSList **pstack)
374 const GnmExpr *expr;
375 GSList *next;
377 g_return_val_if_fail (pstack != NULL, NULL);
379 expr = (*pstack)->data;
380 next = (*pstack)->next;
382 g_slist_free_1 (*pstack);
384 *pstack = next;
385 return expr;
388 static void
389 qpro_parse_formula (QProReadState *state, int col, int row,
390 guint8 const *data, guint8 const *end)
392 guint16 magic, ref_offset;
393 #if 0
394 int flags = GSF_LE_GET_GUINT16 (data + 8);
395 int length = GSF_LE_GET_GUINT16 (data + 10);
396 #endif
397 GnmValue *val;
398 GSList *stack = NULL;
399 GnmExprTop const *texpr = NULL;
400 guint8 const *refs, *fmla;
402 #ifdef DEBUG_MISSING
403 dump_missing_functions ();
404 #endif
406 Q_CHECK_CONDITION (end - data >= 14);
407 magic = GSF_LE_GET_GUINT16 (data + 6) & 0x7ff8;
408 ref_offset = GSF_LE_GET_GUINT16 (data + 12);
410 fmla = data + 14;
411 refs = fmla + ref_offset;
412 Q_CHECK_CONDITION (refs <= end);
414 #if 0
415 puts (cell_coord_name (col, row));
416 gsf_mem_dump (data, 14);
417 gsf_mem_dump (fmla, refs-fmla);
418 gsf_mem_dump (refs, end-refs);
419 #endif
421 while (fmla < refs && *fmla != QPRO_OP_EOF) {
422 QProOperators op = *fmla++;
423 GnmExpr const *expr = NULL;
424 #if 0
425 g_print ("Operator %d.\n", op);
426 #endif
427 switch (op) {
428 case QPRO_OP_CONST_FLOAT:
429 Q_CHECK_CONDITION (refs - fmla >= 8);
430 expr = gnm_expr_new_constant (value_new_float (
431 gsf_le_get_double (fmla)));
432 fmla += 8;
433 break;
435 case QPRO_OP_CELLREF: {
436 GnmCellRef ref;
437 guint16 tmp;
439 Q_CHECK_CONDITION (end - refs >= 6);
440 tmp = GSF_LE_GET_GUINT16 (refs + 4);
441 ref.sheet = NULL;
442 ref.col = *((gint8 *)(refs + 2));
443 ref.col_relative = (tmp & 0x4000) ? TRUE : FALSE;
444 ref.row_relative = (tmp & 0x2000) ? TRUE : FALSE;
445 if (ref.row_relative)
446 ref.row = (int)(((gint16)((tmp & 0x1fff) << 3)) >> 3);
447 else
448 ref.row = tmp & 0x1fff;
449 expr = gnm_expr_new_cellref (&ref);
450 refs += 6;
451 break;
454 case QPRO_OP_RANGEREF: {
455 GnmCellRef a, b;
456 guint16 tmp;
458 Q_CHECK_CONDITION (end - refs >= 10);
460 tmp = GSF_LE_GET_GUINT16 (refs + 4);
461 a.sheet = NULL;
462 a.col = *((gint8 *)(refs + 2));
463 a.col_relative = (tmp & 0x4000) ? TRUE : FALSE;
464 a.row_relative = (tmp & 0x2000) ? TRUE : FALSE;
465 if (a.row_relative)
466 a.row = (int)(((gint16)((tmp & 0x1fff) << 3)) >> 3);
467 else
468 a.row = tmp & 0x1fff;
470 tmp = GSF_LE_GET_GUINT16 (refs + 8);
471 b.sheet = NULL;
472 b.col = *((gint8 *)(refs + 6));
473 b.col_relative = (tmp & 0x4000) ? TRUE : FALSE;
474 b.row_relative = (tmp & 0x2000) ? TRUE : FALSE;
475 if (b.row_relative)
476 b.row = (int)(((gint16)((tmp & 0x1fff) << 3)) >> 3);
477 else
478 b.row = tmp & 0x1fff;
480 expr = gnm_expr_new_constant (
481 value_new_cellrange_unsafe (&a, &b));
482 refs += 10;
483 break;
485 case QPRO_OP_EOF:
486 break; /* exit */
488 case QPRO_OP_PAREN:
489 break; /* Currently just ignore. */
491 case QPRO_OP_CONST_INT:
492 Q_CHECK_CONDITION (refs - fmla >= 2);
493 expr = gnm_expr_new_constant (
494 value_new_int ((gint16)GSF_LE_GET_GUINT16 (fmla)));
495 fmla += 2;
496 break;
498 case QPRO_OP_CONST_STR:
499 expr = gnm_expr_new_constant (qpro_new_string (state, fmla));
500 fmla += strlen (fmla) + 1;
501 break;
503 case QPRO_OP_DEFAULT_ARG:
504 expr = gnm_expr_new_constant (value_new_empty ());
505 break;
507 case QPRO_OP_ADD: case QPRO_OP_SUB:
508 case QPRO_OP_MULT: case QPRO_OP_DIV:
509 case QPRO_OP_EXP:
510 case QPRO_OP_EQ: case QPRO_OP_NE:
511 case QPRO_OP_LE: case QPRO_OP_GE:
512 case QPRO_OP_LT: case QPRO_OP_GT:
513 case QPRO_OP_CONCAT: Q_CHECK_CONDITION (stack && stack->next); {
514 static GnmExprOp const binop_map[] = {
515 GNM_EXPR_OP_ADD, GNM_EXPR_OP_SUB,
516 GNM_EXPR_OP_MULT, GNM_EXPR_OP_DIV,
517 GNM_EXPR_OP_EXP,
518 GNM_EXPR_OP_EQUAL, GNM_EXPR_OP_NOT_EQUAL,
519 GNM_EXPR_OP_LTE, GNM_EXPR_OP_GTE,
520 GNM_EXPR_OP_LT, GNM_EXPR_OP_GT,
521 0, 0, 0, 0,
522 GNM_EXPR_OP_CAT
524 GnmExpr const *r = expr_stack_pop (&stack);
525 GnmExpr const *l = expr_stack_pop (&stack);
526 expr = gnm_expr_new_binary (
527 l, binop_map [op - QPRO_OP_ADD], r);
528 break;
531 case QPRO_OP_AND:
532 case QPRO_OP_OR: Q_CHECK_CONDITION (stack && stack->next); {
533 GnmFunc *f = gnm_func_lookup (op == QPRO_OP_OR ? "or" : "and",
534 NULL);
535 GnmExpr const *r = expr_stack_pop (&stack);
536 GnmExpr const *l = expr_stack_pop (&stack);
537 expr = gnm_expr_new_funcall2 (f, l, r);
538 break;
541 case QPRO_OP_NOT: Q_CHECK_CONDITION (stack); {
542 GnmFunc *f = gnm_func_lookup ("NOT", NULL);
543 GnmExpr const *a = expr_stack_pop (&stack);
544 expr = gnm_expr_new_funcall1 (f, a);
545 break;
548 case QPRO_OP_UNARY_NEG:
549 case QPRO_OP_UNARY_PLUS:
550 Q_CHECK_CONDITION (stack);
551 expr = expr_stack_pop (&stack);
552 expr = gnm_expr_new_unary ((op == QPRO_OP_UNARY_NEG)
553 ? GNM_EXPR_OP_UNARY_NEG
554 : GNM_EXPR_OP_UNARY_PLUS,
555 expr);
556 break;
558 default:
559 if (QPRO_OP_FIRST_FUNC <= op && op <= QPRO_OP_LAST_FUNC) {
560 int idx = op - QPRO_OP_FIRST_FUNC;
561 char const *name = qpro_functions[idx].name;
562 int args = qpro_functions[idx].args;
563 GnmExprList *arglist = NULL;
564 GnmFunc *f;
566 if (name == NULL) {
567 g_warning ("QPRO function %d is not known.", op);
568 break;
570 /* FIXME : Add support for workbook local functions */
571 f = gnm_func_lookup (name, NULL);
572 if (f == NULL) {
573 g_warning ("QPRO function %s is not supported!",
574 name);
575 break;
578 if (args == ARGS_UNKNOWN) {
579 g_warning ("QPRO function %s is not supported.",
580 name);
581 goto error;
584 if (args == ARGS_COUNT_FOLLOWS) {
585 Q_CHECK_CONDITION (refs - fmla >= 1);
586 args = fmla[0];
587 fmla++;
590 while (stack && args > 0) {
591 arglist = g_slist_prepend (arglist,
592 (gpointer)expr_stack_pop (&stack));
593 args--;
595 if (args > 0) {
596 g_printerr ("File is probably corrupted.\n"
597 "(Expression stack is short by %d arguments)",
598 args);
599 while (args > 0) {
600 GnmExpr const *e = gnm_expr_new_constant (value_new_empty ());
601 arglist = g_slist_prepend (arglist,
602 (gpointer)e);
603 args--;
606 expr = gnm_expr_new_funcall (f, arglist);
607 break;
608 } else {
609 corrupted (state);
610 g_printerr ("Operator %d encountered.\n", op);
613 if (expr != NULL) {
614 stack = g_slist_prepend (stack, (gpointer)expr);
617 Q_CHECK_CONDITION (fmla != refs);
618 Q_CHECK_CONDITION (stack != NULL);
619 Q_CHECK_CONDITION (stack->next == NULL);
621 texpr = gnm_expr_top_new (expr_stack_pop (&stack));
623 switch (magic) {
624 case 0x7ff0:
625 val = value_new_error_VALUE (NULL);
626 break;
627 case 0x7ff8: {
628 guint16 id, len;
629 int new_row, new_col;
631 again:
632 data = qpro_get_record (state, &id, &len);
633 Q_CHECK_CONDITION (data != NULL);
635 if (id == QPRO_UNDOCUMENTED_270) {
637 * poker.wk3 has a few of these. They seem to be
638 * more or less embedding a copy of the formula
639 * record.
641 #if 0
642 g_warning ("Encountered 270 record.");
643 #endif
644 goto again;
647 Q_CHECK_CONDITION (id == QPRO_FORMULA_STRING);
648 Q_CHECK_CONDITION (len >= 7);
650 new_col = data[0];
651 new_row = GSF_LE_GET_GUINT16 (data + 2);
653 /* Be anal */
654 Q_CHECK_CONDITION (col == new_col && row == new_row);
656 val = qpro_new_string (state, data + 7);
657 break;
659 default:
660 val = value_new_float (gsf_le_get_double (data));
663 gnm_cell_set_expr_and_value
664 (sheet_cell_fetch (state->cur_sheet, col, row),
665 texpr, val, TRUE);
666 gnm_expr_top_unref (texpr);
667 return;
669 error:
671 GSList *tmp;
673 for (tmp = stack; tmp; tmp = tmp->next) {
674 GnmExpr *expr = tmp->data;
675 #ifdef DEBUG_EXPR_STACK
676 GnmParsePos pp;
677 char *p;
679 pp.wb = state->wb;
680 pp.sheet = state->cur_sheet;
681 pp.eval.col = col;
682 pp.eval.row = row;
684 p = gnm_expr_as_string (expr, &pp,
685 gnm_conventions_default);
686 g_printerr ("Expr: %s\n", p);
687 g_free (p);
688 #endif
689 gnm_expr_free (expr);
691 g_slist_free (stack);
694 if (texpr)
695 gnm_expr_top_unref (texpr);
698 static GnmStyle *
699 qpro_get_style (QProReadState *state, guint8 const *data)
701 #if 0
702 unsigned attr_id = GSF_LE_GET_GUINT16 (data) >> 3;
703 g_printerr ("Get Attr %u\n", attr_id);
704 #endif
705 return sheet_style_default (state->cur_sheet);
708 static void
709 qpro_read_sheet (QProReadState *state)
711 guint16 id, len;
712 guint8 const *data;
714 /* We can use col_name as a quick proxy for the defaul q-pro sheet names */
715 char const *def_name = col_name (workbook_sheet_count (state->wb));
716 Sheet *sheet = sheet_new (state->wb, def_name, 256, 65536);
718 state->cur_sheet = sheet;
719 workbook_sheet_attach (state->wb, sheet);
720 sheet_flag_recompute_spans (sheet);
721 #if 0
722 g_printerr ("----------> start %s\n", def_name);
723 #endif
724 while (NULL != (data = qpro_get_record (state, &id, &len))) {
725 switch (id) {
726 case QPRO_BLANK_CELL:
727 if (validate (QPRO_BLANK_CELL, 6))
728 sheet_style_set_pos (sheet,
729 data[0], GSF_LE_GET_GUINT16 (data + 2),
730 qpro_get_style (state, data + 4));
731 break;
733 case QPRO_INTEGER_CELL:
734 if (validate (QPRO_INTEGER_CELL, 8)) {
735 int col = data[0];
736 int row = GSF_LE_GET_GUINT16 (data + 2);
737 sheet_style_set_pos (sheet, col, row,
738 qpro_get_style (state, data + 4));
739 gnm_cell_assign_value (sheet_cell_fetch (sheet, col, row),
740 value_new_int (GSF_LE_GET_GUINT16 (data + 6)));
742 break;
744 case QPRO_FLOATING_POINT_CELL:
745 if (validate (QPRO_FLOATING_POINT_CELL, 14)) {
746 int col = data[0];
747 int row = GSF_LE_GET_GUINT16 (data + 2);
748 sheet_style_set_pos (sheet, col, row,
749 qpro_get_style (state, data + 4));
750 gnm_cell_assign_value (sheet_cell_fetch (sheet, col, row),
751 value_new_float (gsf_le_get_double (data + 6)));
753 break;
755 case QPRO_LABEL_CELL:
756 if (validate (QPRO_LABEL_CELL, -1)) {
757 int col = data[0];
758 int row = GSF_LE_GET_GUINT16 (data + 2);
759 GnmHAlign align = GNM_HALIGN_GENERAL;
760 GnmStyle *as = qpro_get_style (state, data + 4);
761 GnmHAlign asa = gnm_style_is_element_set (as, MSTYLE_ALIGN_H)
762 ? gnm_style_get_align_h (as)
763 : GNM_HALIGN_GENERAL;
764 if (asa == GNM_HALIGN_GENERAL)
765 asa = GNM_HALIGN_LEFT;
767 sheet_style_set_pos (sheet, col, row, as);
768 switch (data[6]) {
769 case '\'': align = GNM_HALIGN_LEFT; break;
770 case '^': align = GNM_HALIGN_CENTER; break;
771 case '"': align = GNM_HALIGN_RIGHT; break;
772 case '\\': break; /* Repeat */
773 case '|': break; /* Page break */
774 case 0: break; /* Nothing */
775 default:
776 g_printerr ("Ignoring unknown alignment\n");
778 if (align != GNM_HALIGN_GENERAL && align != asa) {
779 GnmStyle *s = gnm_style_new ();
780 gnm_style_set_align_h (s, align);
781 sheet_style_apply_pos (sheet, col, row,
785 gnm_cell_assign_value (sheet_cell_fetch (sheet, col, row),
786 qpro_new_string (state, data + 7));
788 break;
790 case QPRO_FORMULA_CELL:
791 if (validate (QPRO_FORMULA_CELL, -1)) {
792 int col = data[0];
793 int row = GSF_LE_GET_GUINT16 (data + 2);
794 sheet_style_set_pos (sheet, col, row,
795 qpro_get_style (state, data + 4));
797 qpro_parse_formula (state, col, row,
798 data + 6, data + len);
800 break;
802 case QPRO_END_OF_PAGE:
803 break;
805 case QPRO_COLUMN_SIZE:
806 /* ignore this, we auto generate this info */
807 break;
809 case QPRO_PROTECTION:
810 if (validate (QPRO_PROTECTION, 1))
811 g_object_set (sheet,
812 "protected", (data[0] == 0xff),
813 NULL);
814 break;
816 case QPRO_PAGE_NAME:
817 if (validate (QPRO_PAGE_NAME, -1)) {
818 char *utf8name =
819 g_convert_with_iconv (data, -1,
820 state->converter,
821 NULL, NULL, NULL);
822 #warning "This is wrong, but the workbook interface is confused and needs a control."
823 g_object_set (sheet, "name", utf8name, NULL);
824 g_free (utf8name);
826 break;
828 case QPRO_PAGE_ATTRIBUTE:
829 /* Documented at 30. Observed at 34. */
830 if (validate (QPRO_PAGE_ATTRIBUTE, -1)) {
831 #warning TODO, mostly simple
833 break;
835 case QPRO_DEFAULT_ROW_HEIGHT_PANE1:
836 case QPRO_DEFAULT_ROW_HEIGHT_PANE2:
837 if (validate (QPRO_DEFAULT_ROW_HEIGHT, 2)) {
839 break;
841 case QPRO_DEFAULT_COL_WIDTH_PANE1:
842 case QPRO_DEFAULT_COL_WIDTH_PANE2:
843 if (validate (QPRO_DEFAULT_COL_WIDTH, 2)) {
845 break;
847 case QPRO_MAX_FONT_PANE1:
848 case QPRO_MAX_FONT_PANE2 :
849 /* just ignore for now */
850 break;
852 case QPRO_PAGE_TAB_COLOR :
853 if (validate (QPRO_PAGE_TAB_COLOR, 4)) {
854 GnmColor *bc = gnm_color_new_rgb8 (
855 data[0], data[1], data[2]);
856 g_object_set (sheet,
857 "tab-background", bc,
858 NULL);
859 style_color_unref (bc);
861 break;
863 case QPRO_PAGE_ZOOM_FACTOR :
864 if (validate (QPRO_PAGE_ZOOM_FACTOR, 4)) {
865 guint16 low = GSF_LE_GET_GUINT16 (data);
866 guint16 high = GSF_LE_GET_GUINT16 (data + 2);
868 if (low == 100) {
869 if (high < 10 || high > 400)
870 go_io_warning (state->io_context,
871 _("Invalid zoom %hd %%"), high);
872 else
873 g_object_set (sheet, "zoom-factor", high / 100.0, NULL);
876 break;
879 if (id == QPRO_END_OF_PAGE)
880 break;
882 #if 0
883 g_printerr ("----------< end\n");
884 #endif
885 state->cur_sheet = NULL;
888 static void
889 qpro_read_workbook (QProReadState *state, GsfInput *input)
891 guint16 id, len;
892 guint8 const *data;
894 state->input = input;
895 gsf_input_seek (input, 0, G_SEEK_SET);
897 while (NULL != (data = qpro_get_record (state, &id, &len))) {
898 switch (id) {
899 case QPRO_BEGINNING_OF_FILE:
900 if (validate (QPRO_BEGINNING_OF_FILE, 2)) {
901 guint16 version;
902 version = GSF_LE_GET_GUINT16 (data);
903 (void)version;
905 break;
906 case QPRO_BEGINNING_OF_PAGE:
907 qpro_read_sheet (state);
908 break;
910 default :
911 if (id > QPRO_LAST_SANE_ID)
912 go_io_warning (state->io_context,
913 _("Invalid record %d of length %hd"),
914 id, len);
916 if (id == QPRO_END_OF_FILE)
917 break;
921 void
922 qpro_file_open (GOFileOpener const *fo, GOIOContext *context,
923 WorkbookView *new_wb_view, GsfInput *input)
925 QProReadState state;
926 GsfInput *stream = NULL;
927 GsfInfile *ole;
929 state.io_context = context;
930 state.wbv = new_wb_view;
931 state.wb = wb_view_get_workbook (new_wb_view);
932 state.cur_sheet = NULL;
933 state.converter = g_iconv_open ("UTF-8", "ISO-8859-1");
934 state.corrupted = FALSE;
936 /* check for >= QPro 6.0 which is OLE based */
937 ole = gsf_infile_msole_new (input, NULL);
938 if (ole != NULL) {
939 stream = gsf_infile_child_by_name (GSF_INFILE (ole),
940 "PerfectOffice_MAIN");
941 if (stream != NULL) {
942 qpro_read_workbook (&state, stream);
943 g_object_unref (stream);
944 } else
945 go_io_warning (context,
946 _("Unable to find the PerfectOffice_MAIN stream. Is this really a Quattro Pro file?"));
947 g_object_unref (ole);
948 } else
949 qpro_read_workbook (&state, input);
951 gsf_iconv_close (state.converter);