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