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