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