1 
2 /*
3  * applix-read.c : Routines to read applix version 4 & 5 spreadsheets.
4  *
5  * Copyright (C) 2000-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) any later version.
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 
23 /*
24  * I do not have much in the way of useful docs.
25  * This is a guess based on some sample sheets with a few pointers from
26  *	http://www.vistasource.com/products/axware/fileformats/wptchc01.html
27  */
28 
29 #include <gnumeric-config.h>
30 #include <glib/gi18n-lib.h>
31 #include <gnumeric.h>
32 #include "applix.h"
33 
34 #include <application.h>
35 #include <expr.h>
36 #include <expr-name.h>
37 #include <func.h>
38 #include <value.h>
39 #include <sheet.h>
40 #include <sheet-view.h>
41 #include <number-match.h>
42 #include <cell.h>
43 #include <parse-util.h>
44 #include <sheet-style.h>
45 #include <style.h>
46 #include <style-border.h>
47 #include <style-color.h>
48 #include <selection.h>
49 #include <position.h>
50 #include <ranges.h>
51 #include <command-context.h>
52 #include <workbook-view.h>
53 #include <workbook.h>
54 #include <parse-util.h>
55 
56 #include <goffice/goffice.h>
57 
58 #include <gsf/gsf-input-textline.h>
59 
60 #include <string.h>
61 #include <stdlib.h>
62 
63 typedef struct {
64 	GsfInputTextline *input;
65 	GOErrorInfo     *parse_error;
66 	WorkbookView  *wb_view;
67 	Workbook      *wb;
68 	GHashTable    *exprs, *styles;
69 	GPtrArray     *colors;
70 	GPtrArray     *attrs;
71 	GPtrArray     *font_names;
72 
73 	unsigned char *buffer;
74 	size_t buffer_size;
75 	size_t line_len;
76 	int zoom;
77 	GSList *sheet_order;
78 	GSList *std_names, *real_names;
79 
80 	GnmConventions *convs;
81 	GIConv converter;
82 } ApplixReadState;
83 
84 /* #define NO_DEBUG_APPLIX */
85 #ifndef NO_DEBUG_APPLIX
86 #define d(level, code)	do { if (debug_applix_read > level) { code } } while (0)
87 static int debug_applix_read = 0;
88 #else
89 #define d(level, code)
90 #endif
91 
92 #define a_strncmp(buf, str) strncmp ((buf), str, sizeof (str) - 1)
93 
94 static long
au_strtol(const unsigned char * str,unsigned char ** end)95 au_strtol (const unsigned char *str, unsigned char **end)
96 {
97 	char *send;
98 	long res = strtol ((const char *)str, &send, 10);
99 	if (end) *end = (unsigned char *)send;
100 	return res;
101 }
102 
103 static long
a_strtol(const char * str,char ** end)104 a_strtol (const char *str, char **end)
105 {
106 	return strtol (str, end, 10);
107 }
108 
109 
110 /* The maximum numer of character potentially involved in a new line */
111 #define MAX_END_OF_LINE_SLOP	16
112 
113 static int applix_parse_error (ApplixReadState *, char const *format, ...)
114 	G_GNUC_PRINTF (2, 3);
115 
116 static int
applix_parse_error(ApplixReadState * state,char const * format,...)117 applix_parse_error (ApplixReadState *state, char const *format, ...)
118 {
119 	va_list args;
120 	char *err;
121 
122 	if (state->parse_error == NULL)
123 		state->parse_error = go_error_info_new_str (
124 			_("Parse error while reading Applix file."));
125 
126 	va_start (args, format);
127 	err = g_strdup_vprintf (format, args);
128 	va_end (args);
129 
130 	go_error_info_add_details (state->parse_error, go_error_info_new_str (err));
131 	g_free (err);
132 
133 	return -1;
134 }
135 
136 /**
137  * applix_parse_value : Parse applix's optionally quoted values.
138  *
139  * @follow: A pointer to a char * that is adjusted to point 2 chars AFTER the
140  *           end of the string.
141  *
142  * returns the strings and null terminates it.
143  */
144 static char *
applix_parse_value(char * buf,char ** follow)145 applix_parse_value (char *buf, char **follow)
146 {
147 	/* Is the value a quoted string */
148 	if (*buf == '"') {
149 		char *src = ++buf, *dest = src;
150 		while (*src && *src != '"') {
151 			if (*src == '\\')
152 				src++;
153 			*dest = *src++;
154 		}
155 		g_return_val_if_fail (*src == '"', NULL);
156 		*follow = src;
157 		**follow = '\0';
158 		*follow += 3;
159 	} else {
160 		*follow = strchr (buf, ' ');
161 		g_return_val_if_fail (*follow != NULL, NULL);
162 		**follow = '\0';
163 		*follow += 2;
164 	}
165 
166 	return buf;
167 }
168 
169 /* A..Z, AA..ZZ */
170 #define APPLIX_SHEET_MAX_COLS 702
171 #define APPLIX_SHEET_MAX_ROWS 65536
172 
173 static const GnmSheetSize applix_sheet_size = {
174   APPLIX_SHEET_MAX_COLS, APPLIX_SHEET_MAX_ROWS
175 };
176 
177 static gboolean
valid_col(Sheet const * sheet,int c)178 valid_col (Sheet const *sheet, int c)
179 {
180 	return c >= 0 && c < gnm_sheet_get_max_cols (sheet);
181 }
182 
183 static gboolean
valid_row(Sheet const * sheet,int r)184 valid_row (Sheet const *sheet, int r)
185 {
186 	return r >= 0 && r < gnm_sheet_get_max_rows (sheet);
187 }
188 
189 static gboolean
valid_cellpos(Sheet const * sheet,const GnmCellPos * cpos)190 valid_cellpos (Sheet const *sheet, const GnmCellPos *cpos)
191 {
192 	return (sheet &&
193 		valid_col (sheet, cpos->col) &&
194 		valid_row (sheet, cpos->row));
195 }
196 
197 static char const *
applix_col_parse(char const * str,int * res,unsigned char * relative)198 applix_col_parse (char const *str, int *res, unsigned char *relative)
199 {
200 	return col_parse (str, &applix_sheet_size, res, relative);
201 }
202 
203 static char const *
applix_row_parse(char const * str,int * res,unsigned char * relative)204 applix_row_parse (char const *str, int *res, unsigned char *relative)
205 {
206 	return row_parse (str, &applix_sheet_size, res, relative);
207 }
208 
209 static char const *
applix_cellpos_parse(char const * cell_str,Sheet const * sheet,GnmCellPos * res,gboolean strict)210 applix_cellpos_parse (char const *cell_str, Sheet const *sheet,
211 		      GnmCellPos *res, gboolean strict)
212 {
213 	unsigned char dummy_relative;
214 
215 	cell_str = applix_col_parse (cell_str, &res->col, &dummy_relative);
216 	if (!cell_str)
217 		return NULL;
218 
219 	cell_str = applix_row_parse (cell_str, &res->row, &dummy_relative);
220 	if (!cell_str)
221 		return NULL;
222 
223 	if (*cell_str != 0 && strict)
224 		return NULL;
225 
226 	return cell_str;
227 }
228 
229 static char const *
applix_sheetref_parse(char const * start,Sheet ** sheet,Workbook const * wb)230 applix_sheetref_parse (char const *start, Sheet **sheet, Workbook const *wb)
231 {
232 	char const *end, *begin;
233 	char *name;
234 
235 	begin = end = (*start == '$') ? start + 1 : start;
236 	while (*end && g_ascii_isalnum (*end))
237 		end++;
238 
239 	if (*end != ':') {
240 		*sheet = NULL;
241 		return start;
242 	}
243 
244 	name = g_strndup (begin, end - begin);
245 	*sheet = workbook_sheet_by_name (wb, name);
246 	g_free (name);
247 	return *sheet != NULL ? end : start;
248 }
249 
250 static char const *
applix_rangeref_parse(GnmRangeRef * res,char const * start,GnmParsePos const * pp,GnmConventions const * convention)251 applix_rangeref_parse (GnmRangeRef *res, char const *start, GnmParsePos const *pp,
252 		       GnmConventions const *convention)
253 {
254 	char const *ptr = start, *tmp1, *tmp2;
255 	Workbook *wb = pp->wb;
256 
257 	/* TODO : Does not handle external references */
258 
259 	ptr = applix_sheetref_parse (start, &res->a.sheet, wb);
260 	if (ptr == NULL)
261 		return start; /* TODO error unknown sheet */
262 	if (*ptr == ':') ptr++;
263 	tmp1 = applix_col_parse (ptr, &res->a.col, &res->a.col_relative);
264 	if (!tmp1)
265 		return start;
266 	tmp2 = applix_row_parse (tmp1, &res->a.row, &res->a.row_relative);
267 	if (!tmp2)
268 		return start;
269 	if (res->a.col_relative)
270 		res->a.col -= pp->eval.col;
271 	if (res->a.row_relative)
272 		res->a.row -= pp->eval.row;
273 	if (tmp2[0] != '.' || tmp2[1] != '.') {
274 		res->b = res->a;
275 		return tmp2;
276 	}
277 
278 	start = tmp2;
279 	ptr = applix_sheetref_parse (start+2, &res->b.sheet, wb);
280 	if (ptr == NULL)
281 		return start; /* TODO error unknown sheet */
282 	if (*ptr == ':') ptr++;
283 	tmp1 = applix_col_parse (ptr, &res->b.col, &res->b.col_relative);
284 	if (!tmp1)
285 		return start;
286 	tmp2 = applix_row_parse (tmp1, &res->b.row, &res->b.row_relative);
287 	if (!tmp2)
288 		return start;
289 	if (res->b.col_relative)
290 		res->b.col -= pp->eval.col;
291 	if (res->b.row_relative)
292 		res->b.row -= pp->eval.row;
293 	return tmp2;
294 }
295 
296 static unsigned char *
applix_get_line(ApplixReadState * state)297 applix_get_line (ApplixReadState *state)
298 {
299 	unsigned char *ptr, *end, *buf;
300 	GString *line = g_string_new (NULL);
301 	gboolean first = TRUE;
302 
303 	// Read line and continuation lines.
304 	while (NULL != (ptr = gsf_input_textline_ascii_gets (state->input))) {
305 		size_t len = strlen (ptr);
306 		// Clip at the state line length
307 		size_t uselen = MIN (len, state->line_len);
308 
309 		if (first) {
310 			first = FALSE;
311 			g_string_append_len (line, ptr, uselen);
312 		} else if (uselen > 0) {
313 			// Drop initial space from continuation line
314 			g_string_append_len (line, ptr + 1, uselen - 1);
315 		}
316 
317 		if (len < state->line_len)
318 			break;
319 	}
320 
321 	if (line->len > state->buffer_size) {
322 		state->buffer_size = line->len;
323 		state->buffer = g_realloc (state->buffer, state->buffer_size + 1);
324 	}
325 
326 	ptr = line->str;
327 	end = ptr + line->len;
328 	buf = state->buffer;
329 
330 	// g_printerr ("Pre [%s]\n", ptr);
331 
332 	while (ptr < end) {
333 		if (*ptr != '^') {
334 			*(buf++) = *(ptr++);
335 			continue;
336 		}
337 
338 		if (ptr[1] == '^') {
339 			// An encoded carat
340 			*(buf++) = '^', ptr += 2;
341 			continue;
342 		}
343 
344 		if (ptr[1] == '\0' || ptr[2] == '\0') {
345 			applix_parse_error (state, _("Missing characters for character encoding"));
346 			*(buf++) = *(ptr++);
347 		} else if (ptr[1] < 'a' || ptr[1] > 'p' ||
348 			   ptr[2] < 'a' || ptr[2] > 'p') {
349 			applix_parse_error (state, _("Invalid characters for encoding '%c%c'"),
350 					    ptr[1], ptr[2]);
351 			*(buf++) = *(ptr++);
352 		} else {
353 			guchar uc = ((ptr[1] - 'a') << 4) | (ptr[2] - 'a');
354 			gsize utf8_len;
355 			char *utf8buf = g_convert_with_iconv (&uc, 1, state->converter, NULL,
356 							      &utf8_len, NULL);
357 			memcpy (buf, utf8buf, utf8_len);
358 			buf += utf8_len;
359 			g_free (utf8buf);
360 			ptr += 3;
361 		}
362 	}
363 
364 	if (line->len == 0) {
365 		g_string_free (line, TRUE);
366 		return NULL;
367 	}
368 
369 	if (buf)
370 		*buf = 0;
371 
372 	g_string_free (line, TRUE);
373 
374 	//g_printerr ("Post: [%s]\n", state->buffer);
375 	return state->buffer;
376 }
377 
378 static gboolean
applix_read_colormap(ApplixReadState * state)379 applix_read_colormap (ApplixReadState *state)
380 {
381 	unsigned char *buffer, *pos, *iter, *end;
382 	int count;
383 	long numbers[6];
384 
385 
386 	while (NULL != (buffer = applix_get_line (state))) {
387 
388 		if (!a_strncmp (buffer, "END COLORMAP"))
389 			return FALSE;
390 
391 		iter = pos = buffer + strlen (buffer) - 1;
392 		for (count = 6; --count >= 0; pos = iter) {
393 			while (--iter > buffer && g_ascii_isdigit (*iter))
394 				;
395 
396 			if (iter <= buffer || *iter != ' ')
397 				return TRUE;
398 
399 			numbers[count] = au_strtol (iter + 1, &end);
400 			if (end != pos || numbers[count] < 0 || numbers[count] > 255)
401 				return TRUE;
402 		}
403 		if (numbers[0] != 0 || numbers[5] != 0)
404 			return TRUE;
405 
406 		*pos = '\0';
407 
408 		{
409 			int const c = numbers[1];
410 			int const m = numbers[2];
411 			int const y = numbers[3];
412 			int const k = numbers[4];
413 			guint8 r, g, b;
414 
415 			/* From Shelf-2.1 /gui/colorcom.c:1330 */
416 			/* cmyk to rgb */
417 			r = 255 - MIN(255, c+k); /* red */
418 			g = 255 - MIN(255, m+k); /* green */
419 			b = 255 - MIN(255, y+k); /* blue */
420 
421 			/* Store the result */
422 			g_ptr_array_add	(state->colors,
423 					 gnm_color_new_rgb8 (r, g, b));
424 #if 0
425 			g_printerr ("'%s' %ld %ld %ld %ld\n", buffer, numbers[1],
426 				    numbers[2], numbers[3], numbers[4]);
427 #endif
428 		}
429 	}
430 
431 	return TRUE;
432 }
433 
434 static gboolean
applix_read_typefaces(ApplixReadState * state)435 applix_read_typefaces (ApplixReadState *state)
436 {
437 	unsigned char *ptr;
438 
439 	while (NULL != (ptr = applix_get_line (state))) {
440 		if (!a_strncmp (ptr, "END TYPEFACE TABLE"))
441 			return FALSE;
442 		g_ptr_array_add	(state->font_names, g_strdup (ptr));
443 	}
444 
445 	return FALSE;
446 }
447 
448 static GnmColor *
applix_get_color(ApplixReadState * state,char ** buf)449 applix_get_color (ApplixReadState *state, char **buf)
450 {
451 	/* Skip 'FG' or 'BG' */
452 	char *start = *buf+2;
453 	int num = a_strtol (start, buf);
454 
455 	if (start == *buf) {
456 		applix_parse_error (state, "Invalid color");
457 		return NULL;
458 	}
459 
460 	if (num >= 0 && num < (int)state->colors->len)
461 		return style_color_ref (g_ptr_array_index(state->colors, num));
462 
463 	return style_color_black ();
464 }
465 
466 static int
applix_get_precision(char const * val)467 applix_get_precision (char const *val)
468 {
469 	if ('0' <= *val && *val <= '9')
470 		return *val - '0';
471 	if (*val != 'f')
472 		g_warning ("APPLIX : unknow number format %c", *val);
473 	return 2;
474 }
475 
476 static GnmStyle *
applix_parse_style(ApplixReadState * state,unsigned char ** buffer)477 applix_parse_style (ApplixReadState *state, unsigned char **buffer)
478 {
479 	GnmStyle *style;
480 	char *start = *buffer, *tmp = start;
481 	gboolean is_protected = FALSE, is_invisible = FALSE;
482 	char const *format_prefix = NULL, *format_suffix = NULL;
483 	int font_id = 0; /* default */
484 
485 	*buffer = NULL;
486 	if (*tmp == 'P') {
487 		is_protected = TRUE;
488 		tmp = ++start;
489 	}
490 	if (*tmp == 'I') {
491 		is_invisible = TRUE;
492 		tmp = ++start;
493 	}
494 	if ((is_protected || is_invisible)) {
495 		if (*tmp != ' ') {
496 			applix_parse_error (state, "Invalid format, protection problem");
497 			return NULL;
498 		}
499 		tmp = ++start;
500 	}
501 
502 	if (*tmp != '(') {
503 		applix_parse_error (state, "Invalid format, missing '('");
504 		return NULL;
505 	}
506 
507 	while (*(++tmp) && *tmp != ')')
508 		;
509 
510 	if (tmp[0] != ')' || tmp[1] != ' ') {
511 		applix_parse_error (state, "Invalid format missing ')'");
512 		return NULL;
513 	}
514 
515 	/* Look the descriptor string up in the hash of parsed styles */
516 	tmp[1] = '\0';
517 	style = g_hash_table_lookup (state->styles, start);
518 	if (style == NULL) {
519 		/* Parse the descriptor */
520 		char *sep = start;
521 
522 		/* Allocate the new style */
523 		style = gnm_style_new_default ();
524 
525 		gnm_style_set_contents_locked (style, is_protected);
526 		gnm_style_set_contents_hidden (style, is_invisible);
527 
528 		if (sep[1] == '\'')
529 			sep += 2;
530 		else
531 			++sep;
532 
533 		/* Formatting and alignment */
534 		for (; *sep && *sep != '|' && *sep != ')' ; ) {
535 
536 			if (*sep == ',') {
537 				++sep;
538 				continue;
539 			}
540 
541 			if (g_ascii_isdigit (*sep)) {
542 				GnmHAlign a;
543 				switch (*sep) {
544 				case '1' : a = GNM_HALIGN_LEFT; break;
545 				case '2' : a = GNM_HALIGN_RIGHT; break;
546 				case '3' : a = GNM_HALIGN_CENTER; break;
547 				case '4' : a = GNM_HALIGN_FILL; break;
548 				default :
549 					applix_parse_error (state, "Unknown horizontal alignment '%c'", *sep);
550 					return NULL;
551 				}
552 				gnm_style_set_align_h (style, a);
553 				++sep;
554 			} else if (*sep == 'V') {
555 				GnmVAlign a;
556 				switch (sep[1]) {
557 				case 'T' : a = GNM_VALIGN_TOP; break;
558 				case 'C' : a = GNM_VALIGN_CENTER; break;
559 				case 'B' : a = GNM_VALIGN_BOTTOM; break;
560 				default :
561 					applix_parse_error (state, "Unknown vertical alignment '%c'", *sep);
562 					return NULL;
563 				}
564 				gnm_style_set_align_v (style, a);
565 				sep += 2;
566 				break;
567 			} else {
568 				gboolean get_precision = FALSE;
569 				char const *format = NULL;
570 
571 				switch (*sep) {
572 				case 'D' : {
573 					int id = 0;
574 					char *end;
575 					static char const * const date_formats[] = {
576 						/*  1 */ "mmmm d, yyyy",
577 						/*  2 */ "mmm d, yyyy",
578 						/*  3 */ "d mmm yy",
579 						/*  4 */ "mm/dd/yy",
580 						/*  5 */ "dd.mm.yy",
581 						/*  6 */ "yyyy-mm-dd",
582 						/*  7 */ "yy-mm-dd",
583 						/*  8 */ "yyyy mm dd",
584 						/*  9 */ "yy mm dd",
585 						/* 10 */ "yyyymmdd",
586 						/* 11 */ "yymmdd",
587 						/* 12 */ "dd/mm/yy",
588 						/* 13 */ "dd.mm.yyyy",
589 						/* 14 */ "mmm dd, yyyy",
590 						/* 15 */ "mmmm yyyy",
591 						/* 16 */ "mmm.yyyy"
592 					};
593 
594 					/* General : do nothing */
595 					if (sep[1] == 'N') {
596 						sep += 2;
597 						break;
598 					}
599 
600 					if (!g_ascii_isdigit (sep[1]) ||
601 					    (0 == (id = a_strtol (sep+1, &end))) ||
602 					    sep+1 == end ||
603 					    id < 1 || id > 16) {
604 						applix_parse_error (state, "Unknown format %d", id);
605 						return NULL;
606 					}
607 					format = date_formats[id - 1];
608 					sep = end;
609 					break;
610 				}
611 
612 				case 'T' :
613 					switch (sep[1]) {
614 					case '0' : format = "hh:mm:ss AM/PM";	break;
615 					case '1' : format = "hh:mm AM/PM";	break;
616 					case '2' : format = "hh:mm:ss";		break;
617 					case '3' : format = "hh:mm";		break;
618 					default :
619 						applix_parse_error (state, "Unknown time format '%c'", sep[1]);
620 						return NULL;
621 					}
622 					sep += 2;
623 					break;
624 
625 				case 'G' : /* general */
626 					gnm_style_set_format (style, go_format_general ());
627 
628 					/* What is 'Gf' ? */
629 					if (sep[1] == 'f')
630 						sep += 2;
631 					else while (g_ascii_isdigit (*(++sep)))
632 						;
633 					break;
634 
635 				case 'C' : /* currency or comma */
636 					/* comma 'CO' */
637 					if (sep[1] == 'O') {
638 						++sep;
639 						format_prefix = "#,##0";
640 					} else
641 						/* FIXME : what currency to use for differnt locales */
642 						format_prefix = "$ #,##0";
643 
644 					format_suffix = "";
645 					get_precision = TRUE;
646 					break;
647 
648 				case 'S' : /* scientific */
649 					format_suffix = "E+00";
650 					get_precision = TRUE;
651 					break;
652 
653 				case 'P' : /* percentage */
654 					format_suffix = "%";
655 					get_precision = TRUE;
656 					break;
657 
658 				case 'F' : /* fixed */
659 					get_precision = TRUE;
660 					break;
661 
662 #if 0
663 				/* FIXME : Add these to gnumeric ? */
664 				case "GR0" : Graph ?  Seems like a truncated integer histogram
665 					     /* looks like crap, no need to support */
666 
667 #endif
668 				case 'B' : if (sep[1] == '0') {
669 						   /* TODO : support this in gnumeric */
670 						   sep += 2;
671 						   break;
672 					   }
673 					   /* Fall through */
674 				default :
675 					applix_parse_error (state, "Unknown format '%c'", *sep);
676 					return NULL;
677 				}
678 
679 				if (get_precision) {
680 					static char const *zeros = "000000000";
681 					char *tmp_format;
682 					char const *prec = "", *decimal = "";
683 					int n_prec = applix_get_precision (++sep);
684 
685 					sep++;
686 					if (n_prec > 0) {
687 						prec = zeros + 9 - n_prec;
688 						decimal = ".";
689 					}
690 
691 					if (!format_prefix)
692 						format_prefix = "0";
693 					tmp_format = g_strconcat (format_prefix, decimal, prec,
694 								  format_suffix, NULL);
695 
696 					gnm_style_set_format_text (style, tmp_format);
697 					g_free (tmp_format);
698 				} else if (NULL != format)
699 					gnm_style_set_format_text (style, format);
700 			}
701 		}
702 
703 		/* Font spec */
704 		for (++sep ; *sep && *sep != '|' && *sep != ')' ; ) {
705 
706 			/* check for the 1 character modifiers */
707 			switch (*sep) {
708 			case 'B' :
709 				gnm_style_set_font_bold (style, TRUE);
710 				++sep;
711 				break;
712 			case 'I' :
713 				gnm_style_set_font_italic (style, TRUE);
714 				++sep;
715 				break;
716 			case 'U' :
717 				gnm_style_set_font_uline (style, UNDERLINE_SINGLE);
718 				++sep;
719 				break;
720 			case 'D' :
721 				gnm_style_set_font_uline (style, UNDERLINE_DOUBLE);
722 				++sep;
723 				break;
724 			case 'f' :
725 				if (sep[1] == 'g' ) {
726 					/* TODO : what is this ?? */
727 					sep += 2;
728 					break;
729 				}
730 				applix_parse_error (state, "Unknown font modifier 'f%c'", sep[1]);
731 				return NULL;
732 
733 			case 'F' :
734 				if (sep[1] == 'G' ) {
735 					GnmColor *color = applix_get_color (state, &sep);
736 					if (color == NULL)
737 						return NULL;
738 					gnm_style_set_font_color (style, color);
739 					break;
740 				}
741 				applix_parse_error (state, "Unknown font modifier F%c", sep[1]);
742 				return NULL;
743 
744 			case 'P' : {
745 				char *start = ++sep;
746 				double size = go_strtod (start, &sep);
747 
748 				if (start != sep && size > 0.) {
749 					gnm_style_set_font_size (style, size / gnm_app_dpi_to_pixels ());
750 					break;
751 				}
752 				applix_parse_error (state, "Invalid font size '%s", start);
753 				return NULL;
754 			}
755 
756 			case 'W' :
757 				if (sep[1] == 'T') {
758 					/* FIXME : What is WTO ?? */
759 					if (sep[2] == 'O') {
760 						sep +=3;
761 						break;
762 					}
763 					gnm_style_set_wrap_text (style, TRUE);
764 					sep +=2;
765 					break;
766 				}
767 				applix_parse_error (state, "Unknown font modifier W%c", sep[1]);
768 				return NULL;
769 
770 			case 'T' :
771 				if (sep[1] == 'F') {
772 					/* be a font ID numbered from 0 */
773 					char *start = (sep += 2);
774 
775 					font_id = a_strtol (start, &sep);
776 					if (start == sep || font_id < 0 || font_id >= (int)state->font_names->len) {
777 						applix_parse_error (state, "Unknown font index %s", start);
778 						font_id = 0;
779 					}
780 					break;
781 				}
782 
783 
784 			default :
785 				applix_parse_error (state, "Unknown font modifier");
786 				return NULL;
787 			}
788 
789 			if (*sep == ',')
790 				++sep;
791 		}
792 
793 		if (*sep != '|' && *sep != ')') {
794 			applix_parse_error (state, "Invalid font specification");
795 			return NULL;
796 		}
797 
798 		if (font_id < (int)state->font_names->len)
799 			gnm_style_set_font_name (style, g_ptr_array_index (state->font_names, font_id));
800 
801 		/* Background, pattern, and borders */
802 		for (++sep ; *sep && *sep != ')' ; ) {
803 
804 			if (sep[0] == 'S' && sep[1] == 'H')  {
805 				/* A map from applix patten
806 				 * indicies to gnumeric.
807 				 */
808 				static int const map[] = { 0,
809 					1,  6, 5,  4,  3, 2, 24,
810 					24, 14, 13, 17, 16, 15, 11,
811 					19, 20, 21, 22, 23,
812 				};
813 				char *end;
814 				int num = a_strtol (sep += 2, &end);
815 
816 				if (sep == end || 0 >= num || num >= (int)G_N_ELEMENTS (map)) {
817 					applix_parse_error (state, "Unknown pattern %s", sep);
818 					goto error;
819 				}
820 
821 				num = map[num];
822 				gnm_style_set_pattern (style, num);
823 				sep = end;
824 
825 				if (sep[0] == 'F' && sep[1] == 'G' ) {
826 					GnmColor *color = applix_get_color (state, &sep);
827 					if (color == NULL)
828 						goto error;
829 					gnm_style_set_pattern_color (style, color);
830 				}
831 
832 				if (sep[0] == 'B' && sep[1] == 'G') {
833 					GnmColor *color = applix_get_color (state, &sep);
834 					if (color == NULL)
835 						goto error;
836 					gnm_style_set_back_color (style, color);
837 				}
838 			} else if (sep[0] == 'T' || sep[0] == 'B' || sep[0] == 'L' || sep[0] == 'R') {
839 				/* A map from applix border indicies to gnumeric. */
840 				static GnmStyleBorderType const map[] = {0,
841 					GNM_STYLE_BORDER_THIN,
842 					GNM_STYLE_BORDER_MEDIUM,
843 					GNM_STYLE_BORDER_THICK,
844 					GNM_STYLE_BORDER_DASHED,
845 					GNM_STYLE_BORDER_DOUBLE
846 				};
847 
848 				GnmColor *color;
849 				GnmStyleElement const type =
850 					(sep[0] == 'T') ? MSTYLE_BORDER_TOP :
851 					(sep[0] == 'B') ? MSTYLE_BORDER_BOTTOM :
852 					(sep[0] == 'L') ? MSTYLE_BORDER_LEFT : MSTYLE_BORDER_RIGHT;
853 				GnmStyleBorderOrientation const orient = (sep[0] == 'T' || sep[0] == 'B')
854 					? GNM_STYLE_BORDER_HORIZONTAL : GNM_STYLE_BORDER_VERTICAL;
855 				char *end;
856 				int num = a_strtol (++sep, &end);
857 
858 				if (sep == end || 0 >= num || num >= (int)G_N_ELEMENTS (map)) {
859 					applix_parse_error (state, "Unknown border style %s", sep);
860 					goto error;
861 				}
862 				sep = end;
863 
864 				if (sep[0] == 'F' && sep[1] == 'G' ) {
865 					color = applix_get_color (state, &sep);
866 					if (color == NULL)
867 						goto error;
868 				} else
869 					color = style_color_black ();
870 
871 				gnm_style_set_border (style, type,
872 						   gnm_style_border_fetch (map[num], color, orient));
873 			}
874 
875 			if (*sep == ',')
876 				++sep;
877 			else if (*sep != ')') {
878 				applix_parse_error (state, "Invalid pattern, background, or border");
879 				goto error;
880 			}
881 		}
882 
883 		if (*sep != ')') {
884 			applix_parse_error (state, "Invalid pattern or background");
885 			goto error;
886 		}
887 
888 		/* Store the newly parsed style along with its descriptor */
889 		g_hash_table_insert (state->styles, g_strdup (start), style);
890 	}
891 
892 	*buffer = tmp + 2;
893 	gnm_style_ref (style);
894 	return style;
895 
896 error:
897 	if (style)
898 		gnm_style_unref (style);
899 	return NULL;
900 }
901 
902 static gboolean
applix_read_attributes(ApplixReadState * state)903 applix_read_attributes (ApplixReadState *state)
904 {
905 	int count = 0;
906 	unsigned char *ptr, *tmp;
907 	GnmStyle *style;
908 
909 	while (NULL != (ptr = applix_get_line (state))) {
910 		if (!a_strncmp (ptr, "Attr Table End"))
911 			return FALSE;
912 
913 		if (ptr[0] != '<')
914 			return applix_parse_error (state, "Invalid attribute");
915 
916 		/* TODO : The first style seems to be a different format */
917 		if (count++) {
918 			tmp = ptr + 1;
919 			style = applix_parse_style (state, &tmp);
920 			if (style == NULL || *tmp != '>')
921 				return applix_parse_error (state, "Invalid attribute");
922 			g_ptr_array_add	(state->attrs, style);
923 		}
924 	}
925 
926 	/* NOTREACHED */
927 	return FALSE;
928 }
929 
930 static Sheet *
applix_fetch_sheet(ApplixReadState * state,char const * name)931 applix_fetch_sheet (ApplixReadState *state, char const *name)
932 {
933 	Sheet *sheet = workbook_sheet_by_name (state->wb, name);
934 
935 	if (sheet == NULL) {
936 		int cols = APPLIX_SHEET_MAX_COLS;
937 		int rows = APPLIX_SHEET_MAX_ROWS;
938 		gnm_sheet_suggest_size (&cols, &rows);
939 		sheet = sheet_new (state->wb, name, cols, rows);
940 		workbook_sheet_attach (state->wb, sheet);
941 		g_object_set (sheet, "zoom-factor", state->zoom / 100.0, NULL);
942 		sheet_flag_recompute_spans (sheet);
943 	}
944 
945 	return sheet;
946 }
947 
948 static Sheet *
applix_parse_sheet(ApplixReadState * state,unsigned char ** buffer,char const separator)949 applix_parse_sheet (ApplixReadState *state, unsigned char **buffer,
950 		    char const separator)
951 {
952 	Sheet *sheet;
953 
954 	/* Get sheet name */
955 	char *tmp = strchr (*buffer, separator);
956 
957 	if (tmp == NULL) {
958 		applix_parse_error (state, "Invalid sheet name.");
959 		return NULL;
960 	}
961 
962 	*tmp = '\0';
963 	sheet = applix_fetch_sheet (state, *buffer);
964 	*buffer = tmp+1;
965 	return sheet;
966 }
967 
968 static char *
applix_parse_cellref(ApplixReadState * state,unsigned char * buffer,Sheet ** sheet,GnmCellPos * pos,char const separator)969 applix_parse_cellref (ApplixReadState *state, unsigned char *buffer,
970 		      Sheet **sheet, GnmCellPos *pos,
971 		      char const separator)
972 {
973 	*sheet = applix_parse_sheet (state, &buffer, separator);
974 
975 	/* Get cell addr */
976 	if (*sheet) {
977 		buffer = (unsigned char *)applix_cellpos_parse
978 			(buffer, *sheet, pos, FALSE);
979 		if (buffer)
980 			return buffer;
981 	}
982 
983 	*sheet = NULL;
984 	pos->col = pos->row = -1;
985 	return NULL;
986 }
987 
988 static int
applix_height_to_pixels(int height)989 applix_height_to_pixels (int height)
990 {
991 	return height+4;
992 }
993 static int
applix_width_to_pixels(int width)994 applix_width_to_pixels (int width)
995 {
996 	return width*8 + 3;
997 }
998 
999 static int
applix_read_current_view(ApplixReadState * state,unsigned char * buffer)1000 applix_read_current_view (ApplixReadState *state, unsigned char *buffer)
1001 {
1002 	/* What is this ? */
1003 	unsigned char *ptr;
1004 	while (NULL != (ptr = applix_get_line (state)))
1005 	       if (!a_strncmp (ptr, "End View, Name: ~Current~"))
1006 		       return 0;
1007 	return -1;
1008 }
1009 
1010 static int
applix_read_view(ApplixReadState * state,unsigned char * buffer)1011 applix_read_view (ApplixReadState *state, unsigned char *buffer)
1012 {
1013 	Sheet *sheet = NULL;
1014 	unsigned char *name = buffer + 19;
1015 	unsigned char *tmp;
1016 	gboolean ignore;
1017 
1018 	tmp = strchr (name, ':');
1019 	if (tmp == NULL)
1020 		return 0;
1021 	*tmp =  '\0';
1022 
1023 	ignore = tmp[1] != '~';
1024 	if (!ignore)
1025 		state->sheet_order = g_slist_prepend (state->sheet_order,
1026 			applix_fetch_sheet (state, name));
1027 
1028 	while (NULL != (buffer = applix_get_line (state))) {
1029 		if (!a_strncmp (buffer, "View End, Name: ~"))
1030 			break;
1031 		if (ignore)
1032 			continue;
1033 
1034 		if (!a_strncmp (buffer, "View Top Left: ")) {
1035 			GnmCellPos pos;
1036 			if (applix_parse_cellref (state, buffer+15, &sheet, &pos, ':') &&
1037 			    valid_cellpos (sheet, &pos))
1038 				gnm_sheet_view_set_initial_top_left (sheet_get_view (sheet, state->wb_view),
1039 							 pos.col, pos.row);
1040 		} else if (!a_strncmp (buffer, "View Open Cell: ")) {
1041 			GnmCellPos pos;
1042 			if (applix_parse_cellref (state, buffer+16, &sheet, &pos, ':') &&
1043 			    valid_cellpos (sheet, &pos))
1044 				sv_selection_set (sheet_get_view (sheet, state->wb_view),
1045 						  &pos, pos.col, pos.row, pos.col, pos.row);
1046 		} else if (!a_strncmp (buffer, "View Default Column Width ")) {
1047 			char *ptr, *tmp = buffer + 26;
1048 			int width = a_strtol (tmp, &ptr);
1049 			if (tmp == ptr || width <= 0)
1050 				return applix_parse_error (state, "Invalid default column width");
1051 
1052 			sheet_col_set_default_size_pixels (sheet,
1053 				applix_width_to_pixels (width));
1054 		} else if (!a_strncmp (buffer, "View Default Row Height: ")) {
1055 			char *ptr, *tmp = buffer + 25;
1056 			int height = a_strtol (tmp, &ptr);
1057 			if (tmp == ptr || height <= 0)
1058 				return applix_parse_error (state, "Invalid default row height");
1059 
1060 			/* height + one for the grid line */
1061 			sheet_row_set_default_size_pixels (sheet,
1062 				applix_height_to_pixels (height));
1063 		} else if (!a_strncmp (buffer, "View Row Heights: ")) {
1064 			char *ptr = buffer + 17;
1065 			do {
1066 				int row, height;
1067 				char *tmp;
1068 
1069 				row = a_strtol (tmp = ptr + 1, &ptr) - 1;
1070 				if (tmp == ptr || row < 0 || *ptr != ':')
1071 					return applix_parse_error (state, "Invalid row size row number");
1072 				height = a_strtol (tmp = ptr + 1, &ptr);
1073 				if (height >= 32768)
1074 					height -= 32768;
1075 
1076 				if (tmp == ptr || height <= 0)
1077 					return applix_parse_error (state, "Invalid row size");
1078 
1079 				/* These seem to assume
1080 				 * top margin 2
1081 				 * bottom margin 1
1082 				 * size in pixels = val -32768 (sometimes ??)
1083 				 */
1084 				sheet_row_set_size_pixels (sheet, row,
1085 							  applix_height_to_pixels (height),
1086 							  TRUE);
1087 			} while (ptr[0] == ' ' && g_ascii_isdigit (ptr[1]));
1088 		} else if (!a_strncmp (buffer, "View Column Widths: ")) {
1089 			char const *ptr = buffer + 19;
1090 			char const *tmp;
1091 			int col, width;
1092 			unsigned char dummy;
1093 
1094 			do {
1095 				ptr = applix_col_parse (tmp = ptr + 1, &col, &dummy);
1096 				if (!ptr || *ptr != ':')
1097 					return applix_parse_error (state, "Invalid column");
1098 				width = a_strtol (tmp = ptr + 1, (char **)&ptr);
1099 				if (tmp == ptr || width <= 0)
1100 					return applix_parse_error (state, "Invalid column size");
1101 
1102 				/* These seem to assume
1103 				 * pixels = 8*width + 3 for the grid lines and margins
1104 				 */
1105 				sheet_col_set_size_pixels (sheet, col,
1106 							   applix_width_to_pixels (width),
1107 							   TRUE);
1108 			} while (ptr[0] == ' ' && g_ascii_isalpha (ptr[1]));
1109 		}
1110 	}
1111 
1112 	return 0;
1113 }
1114 
1115 static int
applix_read_cells(ApplixReadState * state)1116 applix_read_cells (ApplixReadState *state)
1117 {
1118 	Sheet *sheet;
1119 	GnmStyle *style;
1120 	GnmCell *cell;
1121 	GnmCellPos pos;
1122 	GnmParseError  perr;
1123 	unsigned char content_type, *tmp, *ptr;
1124 
1125 	while (NULL != (ptr = applix_get_line (state))) {
1126 		gboolean const val_is_string = (ptr[0] != '\0' && ptr[1] == '\'');
1127 
1128 	       if (!a_strncmp (ptr, "*END SPREADSHEETS"))
1129 		       break;
1130 
1131 		/* Parse formatting */
1132 		style = applix_parse_style (state, &ptr);
1133 		if (style == NULL)
1134 			return -1;
1135 		if (ptr == NULL) {
1136 			gnm_style_unref (style);
1137 			return -1;
1138 		}
1139 
1140 		/* Get cell */
1141 		ptr = applix_parse_cellref (state, ptr, &sheet, &pos, '!');
1142 		if (ptr == NULL) {
1143 			gnm_style_unref (style);
1144 			return applix_parse_error (state, "Expression did not specify target cell");
1145 		}
1146 
1147 		if (!valid_cellpos (sheet, &pos)) {
1148 			gnm_style_unref (style);
1149 			g_warning ("Ignoring sheet contents beyond allowed range.");
1150 			continue;
1151 		}
1152 
1153 		cell = sheet_cell_fetch (sheet, pos.col, pos.row);
1154 
1155 		/* Apply the formatting */
1156 		sheet_style_set_pos (sheet, pos.col, pos.row, style);
1157 		content_type = *ptr;
1158 		switch (content_type) {
1159 		case ';' : /* First of a shared formula */
1160 		case '.' : { /* instance of a shared formula */
1161 			GnmParsePos	 pos;
1162 			GnmValue		*val = NULL;
1163 			GnmRange		 r;
1164 			char *expr_string;
1165 
1166 			ptr = applix_parse_value (ptr+2, &expr_string);
1167 
1168 			/* Just in case something failed */
1169 			if (ptr == NULL)
1170 				return -1;
1171 
1172 			if (!val_is_string)
1173 				/* Does it match any formats (use default date convention) */
1174 				val = format_match (ptr, NULL, NULL);
1175 
1176 			if (val == NULL)
1177 				/* TODO : Could this happen ? */
1178 				val = value_new_string (ptr);
1179 
1180 #if 0
1181 			g_printerr ("\'%s\'\n\'%s\'\n", ptr, expr_string);
1182 #endif
1183 
1184 			if (content_type == ';') {
1185 				GnmExprTop const *texpr;
1186 				gboolean	is_array = FALSE;
1187 
1188 				if (*expr_string == '~') {
1189 					Sheet *start_sheet, *end_sheet;
1190 					tmp = applix_parse_cellref (state, expr_string+1, &start_sheet,
1191 								    &r.start, ':');
1192 					if (start_sheet == NULL || tmp == NULL || tmp[0] != '.' || tmp[1] != '.') {
1193 						applix_parse_error (state, "Invalid array expression");
1194 						continue;
1195 					}
1196 
1197 					tmp = applix_parse_cellref (state, tmp+2, &end_sheet,
1198 								    &r.end, ':');
1199 					if (end_sheet == NULL || tmp == NULL || tmp[0] != '~') {
1200 						applix_parse_error (state, "Invalid array expression");
1201 						continue;
1202 					}
1203 
1204 					if (start_sheet != end_sheet) {
1205 						applix_parse_error (state, "3D array functions are not supported.");
1206 						continue;
1207 					}
1208 
1209 					if (!valid_cellpos (start_sheet, &r.start) ||
1210 					    !valid_cellpos (end_sheet, &r.end)) {
1211 						g_warning ("Ignoring sheet contents beyond allowed range.");
1212 						continue;
1213 					}
1214 
1215 					is_array = TRUE;
1216 					expr_string = tmp+3; /* ~addr~<space><space>=expr */
1217 				}
1218 
1219 				/* We need to continue at all costs so that the
1220 				 * rest of the sheet can be parsed. If we quit, then trailing
1221 				 * 'Formula ' lines confuse the parser
1222 				 */
1223 				(void) parse_error_init(&perr);
1224 				if (*expr_string != '=' && *expr_string != '+') {
1225 					applix_parse_error (state, _("Expression did not start with '=' ? '%s'"),
1226 								   expr_string);
1227 					texpr = gnm_expr_top_new_constant (value_new_string (expr_string));
1228 				} else
1229 					texpr = gnm_expr_parse_str (expr_string+1,
1230 						parse_pos_init_cell (&pos, cell),
1231 								   GNM_EXPR_PARSE_DEFAULT,
1232 								   state->convs,
1233 								   &perr);
1234 
1235 				if (texpr == NULL) {
1236 					applix_parse_error (state, _("%s!%s : unable to parse '%s'\n     %s"),
1237 								   sheet->name_quoted, cell_name (cell),
1238 								   expr_string, perr.err->message);
1239 					texpr = gnm_expr_top_new_constant (value_new_string (expr_string));
1240 				} else if (is_array) {
1241 					gnm_cell_set_array (sheet,
1242 							    &r,
1243 							    texpr);
1244 					gnm_cell_assign_value (cell, val);
1245 					/* Leak? */
1246 				} else {
1247 					gnm_cell_set_expr_and_value (cell, texpr, val, TRUE);
1248 					/* Leak? */
1249 				}
1250 
1251 				if (!applix_get_line (state) ||
1252 				    a_strncmp (state->buffer, "Formula: ")) {
1253 					applix_parse_error (state, "Missing formula ID");
1254 					continue;
1255 				}
1256 
1257 				ptr = state->buffer + 9;
1258 
1259 				/* Store the newly parsed expression along with its descriptor */
1260 				g_hash_table_insert (state->exprs,
1261 						     g_strdup (ptr),
1262 						     (gpointer)texpr);
1263 
1264 				parse_error_free (&perr);
1265 			} else {
1266 				GnmExprTop const *texpr;
1267 				char const *key = expr_string + strlen (expr_string);
1268 				while (key > expr_string && !g_ascii_isspace (key[-1]))
1269 					key--;
1270 #if 0
1271 				g_printerr ("Shared '%s'\n", expr_string);
1272 #endif
1273 				texpr = g_hash_table_lookup (state->exprs, key);
1274 				gnm_cell_set_expr_and_value (cell, texpr, val, TRUE);
1275 			}
1276 			break;
1277 		}
1278 
1279 		case ':' : { /* simple value */
1280 			GnmValue *val = NULL;
1281 
1282 			ptr += 2;
1283 #if 0
1284 			g_printerr ("\"%s\" %d\n", ptr, val_is_string);
1285 #endif
1286 			/* Does it match any formats (use default date convention) */
1287 			if (!val_is_string)
1288 				val = format_match (ptr, NULL, NULL);
1289 			if (val == NULL)
1290 				val = value_new_string (ptr);
1291 
1292 			if (gnm_cell_is_array (cell))
1293 				gnm_cell_assign_value (cell, val);
1294 			else
1295 				gnm_cell_set_value (cell, val);
1296 			break;
1297 		}
1298 
1299 		default :
1300 			g_warning ("Unknown cell type '%c'", content_type);
1301 		}
1302 	}
1303 
1304 	return 0;
1305 }
1306 
1307 static int
applix_read_row_list(ApplixReadState * state,unsigned char * ptr)1308 applix_read_row_list (ApplixReadState *state, unsigned char *ptr)
1309 {
1310 	unsigned char *tmp;
1311 	GnmRange	r;
1312 	Sheet *sheet = applix_parse_sheet (state, &ptr, ' ');
1313 
1314 	if (ptr == NULL)
1315 		return -1;
1316 	if (*ptr != '!')
1317 		return applix_parse_error (state, "Invalid row format");
1318 
1319 	r.start.row = r.end.row = au_strtol (++ptr, &tmp) - 1;
1320 	if (tmp == ptr || r.start.row < 0 || tmp[0] != ':' || tmp[1] != ' ')
1321 		return applix_parse_error (state, "Invalid row format row number");
1322 
1323 	++tmp;
1324 	do {
1325 		unsigned attr_index;
1326 
1327 		r.start.col = au_strtol (ptr = tmp+1, &tmp);
1328 		if (tmp == ptr || r.start.col < 0 || tmp[0] != '-')
1329 			return applix_parse_error (state, "Invalid row format start col");
1330 		r.end.col = au_strtol (ptr = tmp+1, &tmp);
1331 		if (tmp == ptr || r.end.col < 0 || tmp[0] != ':')
1332 			return applix_parse_error (state, "Invalid row format end col");
1333 		attr_index = au_strtol (ptr = tmp+1, &tmp);
1334 		if (tmp != ptr && attr_index >= 2 && attr_index < state->attrs->len+2) {
1335 			GnmStyle *style = g_ptr_array_index(state->attrs, attr_index-2);
1336 			gnm_style_ref (style);
1337 			sheet_style_set_range (sheet, &r, style);
1338 		} else if (attr_index != 1) /* TODO : What the hell is attr 1 ?? */
1339 			return applix_parse_error (state, "Invalid row format attr index");
1340 
1341 	/* Just for kicks they added a trailing space */
1342 	} while (tmp[0] && g_ascii_isdigit (tmp[1]));
1343 
1344 	return 0;
1345 }
1346 
1347 static gboolean
applix_read_sheet_table(ApplixReadState * state)1348 applix_read_sheet_table (ApplixReadState *state)
1349 {
1350 	unsigned char *ptr;
1351 	unsigned char *std_name, *real_name;
1352 	while (NULL != (ptr = applix_get_line (state))) {
1353 	       if (!a_strncmp (ptr, "END SHEETS TABLE"))
1354 		       return FALSE;
1355 
1356 	       /* Sheet A: ~Foo~ */
1357 	       std_name = ptr + 6;
1358 	       ptr = strchr (std_name, ':');
1359 	       if (ptr == NULL)
1360 		       continue;
1361 	       *ptr = '\0';
1362 
1363 	       real_name = ptr + 3;
1364 	       ptr = strchr (real_name, '~');
1365 	       if (ptr == NULL)
1366 		       continue;
1367 	       *ptr = '\0';
1368 
1369 	       state->std_names  = g_slist_prepend (state->std_names,
1370 						    g_strdup (std_name));
1371 	       state->real_names = g_slist_prepend (state->real_names,
1372 						    g_strdup (real_name));
1373 	}
1374 	return TRUE;
1375 }
1376 
1377 static gboolean
applix_read_header_footer(ApplixReadState * state)1378 applix_read_header_footer (ApplixReadState *state)
1379 {
1380 	unsigned char *ptr;
1381 	while (NULL != (ptr = applix_get_line (state)))
1382 	       if (!a_strncmp (ptr, "Headers And Footers End"))
1383 		       return FALSE;
1384 	return TRUE;
1385 }
1386 
1387 static gboolean
applix_read_absolute_name(ApplixReadState * state,char * buffer)1388 applix_read_absolute_name (ApplixReadState *state, char *buffer)
1389 {
1390 	char *end;
1391 	GnmRangeRef ref;
1392 	GnmParsePos pp;
1393 	GnmExprTop const *texpr;
1394 
1395 	/* .ABCDe. Coordinate: A:B2..A:C4 */
1396 	/* Spec guarantees that there are no dots in the name */
1397 	buffer = strchr (buffer, '.');
1398 	if (buffer == NULL)
1399 		return TRUE;
1400 	end = strchr (++buffer, '.');
1401 	if (end == NULL)
1402 		return TRUE;
1403 	*end = '\0';
1404 	end = strchr (end + 1, ':');
1405 	if (end == NULL)
1406 		return TRUE;
1407 	applix_rangeref_parse (&ref, end+2,
1408 		parse_pos_init (&pp, state->wb, NULL, 0, 0),
1409 		state->convs);
1410 	ref.a.col_relative = ref.b.col_relative =
1411 		ref.a.row_relative = ref.b.row_relative = FALSE;
1412 
1413 	texpr = gnm_expr_top_new_constant
1414 		(value_new_cellrange_unsafe (&ref.a, &ref.b));
1415 	expr_name_add (&pp, buffer, texpr, NULL, TRUE, NULL);
1416 
1417 	return FALSE;
1418 }
1419 
1420 static gboolean
applix_read_relative_name(ApplixReadState * state,char * buffer)1421 applix_read_relative_name (ApplixReadState *state, char *buffer)
1422 {
1423 	int dummy;
1424 	char *end;
1425 	GnmRangeRef ref, flag;
1426 	GnmParsePos pp;
1427 	GnmExprTop const *texpr;
1428 
1429 	/* .abcdE. tCol:0 tRow:0 tSheet:0 bCol:1 bRow:2 bSheet: 0 tColAbs:0 tRowAbs:0 tSheetAbs:1 bColAbs:0 bRowAbs:0 bSheetAbs:1 */
1430 	/* Spec guarantees that there are no dots in the name */
1431 	buffer = strchr (buffer, '.');
1432 	if (buffer == NULL)
1433 		return TRUE;
1434 	end = strchr (++buffer, '.');
1435 	if (end == NULL)
1436 		return TRUE;
1437 	*end = '\0';
1438 	if (12 != sscanf (end + 2,
1439 			  " tCol:%d tRow:%d tSheet:%d bCol:%d bRow:%d bSheet: %d tColAbs:%d tRowAbs:%d tSheetAbs:%d bColAbs:%d bRowAbs:%d bSheetAbs:%d",
1440 			  &ref.a.col, &ref.a.row, &dummy, &ref.b.col, &ref.b.row, &dummy,
1441 			  &flag.a.col, &flag.a.row, &dummy, &flag.b.col, &flag.b.row, &dummy))
1442 		return TRUE;
1443 
1444 	ref.a.col_relative = (flag.a.col == 0);
1445 	ref.b.col_relative = (flag.b.col == 0);
1446 	ref.a.row_relative = (flag.a.row == 0);
1447 	ref.b.row_relative = (flag.b.row == 0);
1448 
1449 	ref.a.sheet = ref.b.sheet = NULL;
1450 	texpr = gnm_expr_top_new_constant
1451 		(value_new_cellrange_unsafe (&ref.a, &ref.b));
1452 	parse_pos_init (&pp, state->wb, NULL,
1453 		MAX (-ref.a.col, 0), MAX (-ref.a.row, 0));
1454 	expr_name_add (&pp, buffer, texpr, NULL, TRUE, NULL);
1455 
1456 	return FALSE;
1457 }
1458 
1459 #define ABS_NAMED_RANGE	"Named Range, Name:"
1460 #define REL_NAMED_RANGE	"Relative Named Range, Name:"
1461 
1462 static int
applix_read_impl(ApplixReadState * state)1463 applix_read_impl (ApplixReadState *state)
1464 {
1465 	Sheet *sheet;
1466 	GnmCellPos pos;
1467 	int ext_links = -1;
1468 	unsigned char *real_name = NULL;
1469 	char top_cell_addr[30] = "";
1470 	char cur_cell_addr[30] = "";
1471 	unsigned char *buffer;
1472 	char default_text_format[128] = "";
1473 	char default_number_format[128] = "";
1474 	int def_col_width = -1;
1475 	int win_width = -1;
1476 	int win_height = -1;
1477 
1478 	while (NULL != (buffer = applix_get_line (state))) {
1479 		if (!a_strncmp (buffer, "*BEGIN SPREADSHEETS VERSION=")) {
1480 			char encoding_buffer[32];
1481 			int v0, v1;
1482 			if (3 != sscanf (buffer, "*BEGIN SPREADSHEETS VERSION=%d/%d ENCODING=%31s",
1483 					 &v0, &v1, encoding_buffer))
1484 				return applix_parse_error (state, "Invalid header ");
1485 
1486 			/* FIXME : Guess that version 400 is a minimum */
1487 			if (v0 < 400)
1488 				return applix_parse_error (state, "Versions < 4.0 are not supported");
1489 
1490 			/* We only have a sample of '7BIT' right now */
1491 			if (strcmp (encoding_buffer, "7BIT"))
1492 				return applix_parse_error (state, "We only have samples of '7BIT' encoding, please send us this sample.");
1493 
1494 		} else if (!a_strncmp (buffer, "Num ExtLinks:")) {
1495 			if (1 != sscanf (buffer, "Num ExtLinks: %d", &ext_links))
1496 				return applix_parse_error (state, "Missing number of external links");
1497 
1498 		} else if (!a_strncmp (buffer, "Spreadsheet Dump Rev")) {
1499 			int major_rev, minor_rev, len;
1500 			if (3 != sscanf (buffer, "Spreadsheet Dump Rev %d.%d Line Length %d",
1501 					 &major_rev, &minor_rev, &len))
1502 				return applix_parse_error (state, "Missing dump revision");
1503 			if (len < 0 || 65535 < len) /* magic sanity check */
1504 				return applix_parse_error (state, "Invalid line length");
1505 			state->line_len = len;
1506 
1507 			d (0, g_printerr ("Applix load : Saved with revision %d.%d",
1508 					  major_rev, minor_rev););
1509 		} else if (!a_strncmp (buffer, "Current Doc Real Name:")) {
1510 			g_free (real_name);
1511 			real_name = NULL;  /* FIXME? g_strdup (buffer + 22); */
1512 
1513 		} else if (!strcmp (buffer, "COLORMAP")) {
1514 			if (applix_read_colormap (state))
1515 				return applix_parse_error (state, "invalid colormap");
1516 
1517 		} else if (!strcmp (buffer, "TYPEFACE TABLE")) {
1518 			if (applix_read_typefaces (state))
1519 				return applix_parse_error (state, "invalid typefaces");
1520 
1521 		} else if (!strcmp (buffer, "Attr Table Start")) {
1522 			if (applix_read_attributes (state))
1523 				return applix_parse_error (state, "Invalid attribute table");
1524 
1525 		} else if (!a_strncmp (buffer, "View, Name: ~Current~")) {
1526 			if (0 != applix_read_current_view (state, buffer))
1527 				return applix_parse_error (state, "Invalid view");
1528 
1529 		} else if (!a_strncmp (buffer, "View Start, Name: ~")) {
1530 			if (0 != applix_read_view (state, buffer))
1531 				return applix_parse_error (state, "Invalid view");
1532 
1533 		} else if (!a_strncmp (buffer, "Default Label Style")) {
1534 			if (1 != sscanf (buffer, "Default Label Style %127s", default_text_format))
1535 				return applix_parse_error (state, "invalid default label style");
1536 
1537 		} else if (!a_strncmp (buffer, "Default Number Style")) {
1538 			if (1 != sscanf (buffer, "Default Number Style %127s", default_number_format))
1539 				return applix_parse_error (state, "invalid default number style");
1540 
1541 		} else if (!a_strncmp (buffer, "Document Column Width:")) {
1542 			if (1 != sscanf (buffer, "Document Column Width: %d", &def_col_width))
1543 				return applix_parse_error (state, "invalid col width");
1544 
1545 		} else if (!a_strncmp (buffer, "Percent Zoom Factor:")) {
1546 			if (1 != sscanf (buffer, "Percent Zoom Factor: %d", &state->zoom) ||
1547 			    state->zoom <= 10 || 500 <= state->zoom)
1548 				return applix_parse_error (state, "invalid zoom");
1549 		} else if (!a_strncmp (buffer, "Window Width:")) {
1550 			if (1 != sscanf (buffer, "Window Width: %d", &win_width))
1551 				return applix_parse_error (state, "invalid win width");
1552 		} else if (!a_strncmp (buffer, "Window Height:")) {
1553 			if (1 != sscanf (buffer, "Window Height: %d", &win_height))
1554 				return applix_parse_error (state, "invalid win height");
1555 		} else if (!a_strncmp (buffer, "Top Left:")) {
1556 			if (1 != sscanf (buffer, "Top Left: %25s", top_cell_addr))
1557 				return applix_parse_error (state, "invalid top left");
1558 		} else if (!a_strncmp (buffer, "Open Cell:")) {
1559 			if (1 != sscanf (buffer, "Open Cell: %25s", cur_cell_addr))
1560 				return applix_parse_error (state, "invalid cur cell");
1561 		} else if (!a_strncmp (buffer, "SHEETS TABLE")) {
1562 			if (applix_read_sheet_table (state))
1563 				return applix_parse_error (state, "sheet table");
1564 		} else if (!a_strncmp (buffer, ABS_NAMED_RANGE)) {
1565 			if (applix_read_absolute_name (state, buffer + sizeof (ABS_NAMED_RANGE)))
1566 				return applix_parse_error (state, "Absolute named range");
1567 		} else if (!a_strncmp (buffer, REL_NAMED_RANGE)) {
1568 			if (applix_read_relative_name (state, buffer + sizeof (REL_NAMED_RANGE)))
1569 				return applix_parse_error (state, "Relative named range");
1570 		} else if (!a_strncmp (buffer, "Row List")) {
1571 			if (applix_read_row_list (state, buffer + sizeof ("Row List")))
1572 				return applix_parse_error (state, "row list");
1573 		} else if (!a_strncmp (buffer, "Headers And Footers")) {
1574 			if (applix_read_header_footer (state))
1575 				return applix_parse_error (state, "headers and footers");
1576 
1577 			break; /* BREAK OUT OF THE LOOP HERE */
1578 		}
1579 	}
1580 
1581 	if (applix_read_cells (state))
1582 		return -1;
1583 
1584 	/* We only need the sheet, the visible cell, and edit pos are already set */
1585 	if (applix_parse_cellref (state, cur_cell_addr, &sheet, &pos, ':') &&
1586 	    valid_cellpos (sheet, &pos))
1587 		wb_view_sheet_focus (state->wb_view, sheet);
1588 
1589 	return 0;
1590 }
1591 
1592 static gboolean
cb_remove_texpr(gpointer key,gpointer value,gpointer user_data)1593 cb_remove_texpr (gpointer key, gpointer value, gpointer user_data)
1594 {
1595 	g_free (key);
1596 	gnm_expr_top_unref (value);
1597 	return TRUE;
1598 }
1599 static gboolean
cb_remove_style(gpointer key,gpointer value,gpointer user_data)1600 cb_remove_style (gpointer key, gpointer value, gpointer user_data)
1601 {
1602 	g_free (key);
1603 	gnm_style_unref (value);
1604 	return TRUE;
1605 }
1606 
1607 static GnmExpr const *
applix_func_map_in(GnmConventions const * conv,Workbook * scope,char const * name,GnmExprList * args)1608 applix_func_map_in (GnmConventions const *conv, Workbook *scope,
1609 		    char const *name, GnmExprList *args)
1610 {
1611 	static struct {
1612 		char const *applix_name;
1613 		char const *gnm_name;
1614 	} const sc_func_renames[] = {
1615 		{ "IPAYMT",	"IPMT" },
1616 		{ "PAYMT",	"PMT" },
1617 		{ "PPAYMT",	"PPMT" },
1618 		{ NULL, NULL }
1619 	};
1620 	static GHashTable *namemap = NULL;
1621 
1622 	GnmFunc  *f;
1623 	char const *new_name;
1624 	int i;
1625 
1626 	if (NULL == namemap) {
1627 		namemap = g_hash_table_new (go_ascii_strcase_hash,
1628 					    go_ascii_strcase_equal);
1629 		for (i = 0; sc_func_renames[i].applix_name; i++)
1630 			g_hash_table_insert (namemap,
1631 				(gchar *) sc_func_renames[i].applix_name,
1632 				(gchar *) sc_func_renames[i].gnm_name);
1633 	}
1634 
1635 	if (NULL != namemap &&
1636 	    NULL != (new_name = g_hash_table_lookup (namemap, name)))
1637 		name = new_name;
1638 	if (NULL == (f = gnm_func_lookup (name, scope)))
1639 		f = gnm_func_add_placeholder (scope, name, "");
1640 	return gnm_expr_new_funcall (f, args);
1641 }
1642 
1643 static GnmConventions *
applix_conventions_new(void)1644 applix_conventions_new (void)
1645 {
1646 	GnmConventions *conv = gnm_conventions_new ();
1647 
1648 	conv->intersection_char		= 0;
1649 	conv->accept_hash_logicals	= TRUE;
1650 	conv->allow_absolute_sheet_references = TRUE;
1651 	conv->range_sep_dotdot		= TRUE;
1652 	conv->decimal_sep_dot = '.';
1653 	conv->arg_sep = ',';
1654 	conv->array_col_sep = ',';
1655 	conv->array_row_sep = ';';
1656 	conv->input.range_ref		= applix_rangeref_parse;
1657 	conv->input.func		= applix_func_map_in;
1658 
1659 	return conv;
1660 }
1661 
1662 void
applix_read(GOIOContext * io_context,WorkbookView * wb_view,GsfInput * src)1663 applix_read (GOIOContext *io_context, WorkbookView *wb_view, GsfInput *src)
1664 {
1665 	int i;
1666 	int res;
1667 	ApplixReadState	state;
1668 	GSList *ptr, *renamed_sheets;
1669 
1670 	/* Init the state variable */
1671 	state.input	  = (GsfInputTextline *)gsf_input_textline_new (src);
1672 	state.parse_error = NULL;
1673 	state.wb_view     = wb_view;
1674 	state.wb          = wb_view_get_workbook (wb_view);
1675 	state.exprs       = g_hash_table_new (&g_str_hash, &g_str_equal);
1676 	state.styles      = g_hash_table_new (&g_str_hash, &g_str_equal);
1677 	state.colors      = g_ptr_array_new ();
1678 	state.attrs       = g_ptr_array_new ();
1679 	state.font_names  = g_ptr_array_new ();
1680 	state.buffer      = NULL;
1681 	state.buffer_size = 0;
1682 	state.line_len    = 80;
1683 	state.sheet_order = NULL;
1684 	state.std_names   = NULL;
1685 	state.real_names  = NULL;
1686 	state.convs       = applix_conventions_new ();
1687 	state.converter   = g_iconv_open ("UTF-8", "ISO-8859-1");
1688 
1689 	/* Actually read the workbook */
1690 	res = applix_read_impl (&state);
1691 
1692 	g_object_unref (state.input);
1693 	g_free (state.buffer);
1694 
1695 	state.sheet_order = g_slist_reverse (state.sheet_order);
1696 	workbook_sheet_reorder (state.wb, state.sheet_order);
1697 	g_slist_free (state.sheet_order);
1698 
1699 	renamed_sheets = NULL;
1700 	for (ptr = state.std_names; ptr != NULL ; ptr = ptr->next) {
1701 		const char *name = ptr->data;
1702 		Sheet *sheet = workbook_sheet_by_name (state.wb, name);
1703 		int idx = sheet ? sheet->index_in_wb : -1;
1704 		renamed_sheets = g_slist_prepend (renamed_sheets,
1705 						  GINT_TO_POINTER (idx));
1706 	}
1707 	renamed_sheets = g_slist_reverse (renamed_sheets);
1708 	workbook_sheet_rename (state.wb, renamed_sheets,
1709 			       state.real_names,
1710 			       GO_CMD_CONTEXT (io_context));
1711 	g_slist_free (renamed_sheets);
1712 	g_slist_free_full (state.std_names, g_free);
1713 	g_slist_free_full (state.real_names, g_free);
1714 
1715 	/* Release the shared expressions and styles */
1716 	g_hash_table_foreach_remove (state.exprs, &cb_remove_texpr, NULL);
1717 	g_hash_table_destroy (state.exprs);
1718 	g_hash_table_foreach_remove (state.styles, &cb_remove_style, NULL);
1719 	g_hash_table_destroy (state.styles);
1720 
1721 	for (i = state.colors->len; --i >= 0 ; )
1722 		style_color_unref (g_ptr_array_index (state.colors, i));
1723 	g_ptr_array_free (state.colors, TRUE);
1724 
1725 	for (i = state.attrs->len; --i >= 0 ; )
1726 		gnm_style_unref (g_ptr_array_index(state.attrs, i));
1727 	g_ptr_array_free (state.attrs, TRUE);
1728 
1729 	for (i = state.font_names->len; --i >= 0 ; )
1730 		g_free (g_ptr_array_index(state.font_names, i));
1731 	g_ptr_array_free (state.font_names, TRUE);
1732 
1733 	if (state.parse_error != NULL)
1734 		go_io_error_info_set (io_context, state.parse_error);
1735 
1736 	gnm_conventions_unref (state.convs);
1737 	gsf_iconv_close (state.converter);
1738 }
1739