Code cleanup
[gnumeric.git] / plugins / applix / applix-read.c
blob5564375598c6d051a4b628ab8ebaccf69ff9d5b8
1 /* vim: set sw=8: */
3 /*
4 * applix-read.c : Routines to read applix version 4 & 5 spreadsheets.
6 * Copyright (C) 2000-2002 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
25 * I do not have much in the way of useful docs.
26 * This is a guess based on some sample sheets with a few pointers from
27 * http://www.vistasource.com/products/axware/fileformats/wptchc01.html
30 #include <gnumeric-config.h>
31 #include <glib/gi18n-lib.h>
32 #include <gnumeric.h>
33 #include "applix.h"
35 #include <application.h>
36 #include <expr.h>
37 #include <expr-name.h>
38 #include <func.h>
39 #include <value.h>
40 #include <sheet.h>
41 #include <sheet-view.h>
42 #include <number-match.h>
43 #include <cell.h>
44 #include <parse-util.h>
45 #include <sheet-style.h>
46 #include <style.h>
47 #include <style-border.h>
48 #include <style-color.h>
49 #include <selection.h>
50 #include <position.h>
51 #include <ranges.h>
52 #include <command-context.h>
53 #include <workbook-view.h>
54 #include <workbook.h>
55 #include <parse-util.h>
57 #include <goffice/goffice.h>
59 #include <gsf/gsf-input-textline.h>
61 #include <string.h>
62 #include <stdlib.h>
64 typedef struct {
65 GsfInputTextline *input;
66 GOErrorInfo *parse_error;
67 WorkbookView *wb_view;
68 Workbook *wb;
69 GHashTable *exprs, *styles;
70 GPtrArray *colors;
71 GPtrArray *attrs;
72 GPtrArray *font_names;
74 unsigned char *buffer;
75 size_t buffer_size;
76 size_t line_len;
77 int zoom;
78 GSList *sheet_order;
79 GSList *std_names, *real_names;
81 GnmConventions *convs;
82 } ApplixReadState;
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
92 #define a_strncmp(buf, str) strncmp ((buf), str, sizeof (str) - 1)
94 static long
95 au_strtol (const unsigned char *str, unsigned char **end)
97 char *send;
98 long res = strtol ((const char *)str, &send, 10);
99 if (end) *end = (unsigned char *)send;
100 return res;
103 static long
104 a_strtol (const char *str, char **end)
106 return strtol (str, end, 10);
110 /* The maximum numer of character potentially involved in a new line */
111 #define MAX_END_OF_LINE_SLOP 16
113 static int applix_parse_error (ApplixReadState *, char const *format, ...)
114 G_GNUC_PRINTF (2, 3);
116 static int
117 applix_parse_error (ApplixReadState *state, char const *format, ...)
119 va_list args;
120 char *err;
122 if (state->parse_error == NULL)
123 state->parse_error = go_error_info_new_str (
124 _("Parse error while reading Applix file."));
126 va_start (args, format);
127 err = g_strdup_vprintf (format, args);
128 va_end (args);
130 go_error_info_add_details (state->parse_error, go_error_info_new_str (err));
131 g_free (err);
133 return -1;
137 * applix_parse_value : Parse applix's optionally quoted values.
139 * @follow: A pointer to a char * that is adjusted to point 2 chars AFTER the
140 * end of the string.
142 * returns the strings and null terminates it.
144 static char *
145 applix_parse_value (char *buf, char **follow)
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++;
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;
166 return buf;
169 /* A..Z, AA..ZZ */
170 #define APPLIX_SHEET_MAX_COLS 702
171 #define APPLIX_SHEET_MAX_ROWS 65536
173 static const GnmSheetSize applix_sheet_size = {
174 APPLIX_SHEET_MAX_COLS, APPLIX_SHEET_MAX_ROWS
177 static gboolean
178 valid_col (Sheet const *sheet, int c)
180 return c >= 0 && c < gnm_sheet_get_max_cols (sheet);
183 static gboolean
184 valid_row (Sheet const *sheet, int r)
186 return r >= 0 && r < gnm_sheet_get_max_rows (sheet);
189 static gboolean
190 valid_cellpos (Sheet const *sheet, const GnmCellPos *cpos)
192 return (sheet &&
193 valid_col (sheet, cpos->col) &&
194 valid_row (sheet, cpos->row));
197 static char const *
198 applix_col_parse (char const *str, int *res, unsigned char *relative)
200 return col_parse (str, &applix_sheet_size, res, relative);
203 static char const *
204 applix_row_parse (char const *str, int *res, unsigned char *relative)
206 return row_parse (str, &applix_sheet_size, res, relative);
209 static char const *
210 applix_cellpos_parse (char const *cell_str, Sheet const *sheet,
211 GnmCellPos *res, gboolean strict)
213 unsigned char dummy_relative;
215 cell_str = applix_col_parse (cell_str, &res->col, &dummy_relative);
216 if (!cell_str)
217 return NULL;
219 cell_str = applix_row_parse (cell_str, &res->row, &dummy_relative);
220 if (!cell_str)
221 return NULL;
223 if (*cell_str != 0 && strict)
224 return NULL;
226 return cell_str;
229 static char const *
230 applix_sheetref_parse (char const *start, Sheet **sheet, Workbook const *wb)
232 char const *end, *begin;
233 char *name;
235 begin = end = (*start == '$') ? start + 1 : start;
236 while (*end && g_ascii_isalnum (*end))
237 end++;
239 if (*end != ':') {
240 *sheet = NULL;
241 return start;
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;
250 static char const *
251 applix_rangeref_parse (GnmRangeRef *res, char const *start, GnmParsePos const *pp,
252 GnmConventions const *convention)
254 char const *ptr = start, *tmp1, *tmp2;
255 Workbook *wb = pp->wb;
257 /* TODO : Does not handle external references */
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;
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;
296 static unsigned char *
297 applix_get_line (ApplixReadState *state)
299 unsigned char *ptr, *end, *buf;
300 size_t len, skip = 0, offset = 0;
302 while (NULL != (ptr = gsf_input_textline_ascii_gets (state->input))) {
303 len = strlen (ptr);
305 /* Clip at the state line length */
306 if (len > state->line_len)
307 len = state->line_len;
309 if ((offset + len) > state->buffer_size) {
310 state->buffer_size += state->line_len;
311 state->buffer = g_realloc (state->buffer, state->buffer_size + 1);
314 end = ptr + len;
315 ptr += skip;
316 buf = state->buffer + offset;
317 while (ptr < end) {
318 if (*ptr == '^') {
319 if (ptr [1] != '^') {
320 if (ptr [1] == '\0' || ptr [2] == '\0') {
321 applix_parse_error (state, _("Missing characters for character encoding"));
322 *(buf++) = *(ptr++);
323 } else if (ptr [1] < 'a' || ptr [1] > 'p' ||
324 ptr [2] < 'a' || ptr [2] > 'p') {
325 applix_parse_error (state, _("Invalid characters for encoding '%c%c'"),
326 ptr[1], ptr[2]);
327 *(buf++) = *(ptr++);
328 } else {
329 *(buf++) = ((ptr[1] - 'a') << 8) | (ptr[2] - 'a');
330 ptr += 3;
332 } else /* an encoded carat */
333 *(buf++) = '^', ptr += 2;
334 } else
335 *(buf++) = *(ptr++);
338 offset = buf - state->buffer;
340 if (len >= state->line_len)
341 skip = 1; /* skip the leading space for next line */
342 else
343 break;
346 if (offset == 0 && ptr == NULL)
347 return NULL;
349 if (state->buffer != NULL)
350 state->buffer [offset] = '\0';
351 return state->buffer;
354 static gboolean
355 applix_read_colormap (ApplixReadState *state)
357 unsigned char *buffer, *pos, *iter, *end;
358 int count;
359 long numbers[6];
362 while (NULL != (buffer = applix_get_line (state))) {
364 if (!a_strncmp (buffer, "END COLORMAP"))
365 return FALSE;
367 iter = pos = buffer + strlen (buffer) - 1;
368 for (count = 6; --count >= 0; pos = iter) {
369 while (--iter > buffer && g_ascii_isdigit (*iter))
372 if (iter <= buffer || *iter != ' ')
373 return TRUE;
375 numbers[count] = au_strtol (iter + 1, &end);
376 if (end != pos || numbers[count] < 0 || numbers[count] > 255)
377 return TRUE;
379 if (numbers[0] != 0 || numbers[5] != 0)
380 return TRUE;
382 *pos = '\0';
385 int const c = numbers[1];
386 int const m = numbers[2];
387 int const y = numbers[3];
388 int const k = numbers[4];
389 guint8 r, g, b;
391 /* From Shelf-2.1 /gui/colorcom.c:1330 */
392 /* cmyk to rgb */
393 r = 255 - MIN(255, c+k); /* red */
394 g = 255 - MIN(255, m+k); /* green */
395 b = 255 - MIN(255, y+k); /* blue */
397 /* Store the result */
398 g_ptr_array_add (state->colors,
399 gnm_color_new_rgb8 (r, g, b));
400 #if 0
401 g_printerr ("'%s' %ld %ld %ld %ld\n", buffer, numbers[1],
402 numbers[2], numbers[3], numbers[4]);
403 #endif
407 return TRUE;
410 static gboolean
411 applix_read_typefaces (ApplixReadState *state)
413 unsigned char *ptr;
415 while (NULL != (ptr = applix_get_line (state))) {
416 if (!a_strncmp (ptr, "END TYPEFACE TABLE"))
417 return FALSE;
418 g_ptr_array_add (state->font_names, g_strdup (ptr));
421 return FALSE;
424 static GnmColor *
425 applix_get_color (ApplixReadState *state, char **buf)
427 /* Skip 'FG' or 'BG' */
428 char *start = *buf+2;
429 int num = a_strtol (start, buf);
431 if (start == *buf) {
432 applix_parse_error (state, "Invalid color");
433 return NULL;
436 if (num >= 0 && num < (int)state->colors->len)
437 return style_color_ref (g_ptr_array_index(state->colors, num));
439 return style_color_black ();
442 static int
443 applix_get_precision (char const *val)
445 if ('0' <= *val && *val <= '9')
446 return *val - '0';
447 if (*val != 'f')
448 g_warning ("APPLIX : unknow number format %c", *val);
449 return 2;
452 static GnmStyle *
453 applix_parse_style (ApplixReadState *state, unsigned char **buffer)
455 GnmStyle *style;
456 char *start = *buffer, *tmp = start;
457 gboolean is_protected = FALSE, is_invisible = FALSE;
458 char const *format_prefix = NULL, *format_suffix = NULL;
459 int font_id = 0; /* default */
461 *buffer = NULL;
462 if (*tmp == 'P') {
463 is_protected = TRUE;
464 tmp = ++start;
466 if (*tmp == 'I') {
467 is_invisible = TRUE;
468 tmp = ++start;
470 if ((is_protected || is_invisible)) {
471 if (*tmp != ' ') {
472 applix_parse_error (state, "Invalid format, protection problem");
473 return NULL;
475 tmp = ++start;
478 if (*tmp != '(') {
479 applix_parse_error (state, "Invalid format, missing '('");
480 return NULL;
483 while (*(++tmp) && *tmp != ')')
486 if (tmp[0] != ')' || tmp[1] != ' ') {
487 applix_parse_error (state, "Invalid format missing ')'");
488 return NULL;
491 /* Look the descriptor string up in the hash of parsed styles */
492 tmp[1] = '\0';
493 style = g_hash_table_lookup (state->styles, start);
494 if (style == NULL) {
495 /* Parse the descriptor */
496 char *sep = start;
498 /* Allocate the new style */
499 style = gnm_style_new_default ();
501 gnm_style_set_contents_locked (style, is_protected);
502 gnm_style_set_contents_hidden (style, is_invisible);
504 if (sep[1] == '\'')
505 sep += 2;
506 else
507 ++sep;
509 /* Formatting and alignment */
510 for (; *sep && *sep != '|' && *sep != ')' ; ) {
512 if (*sep == ',') {
513 ++sep;
514 continue;
517 if (g_ascii_isdigit (*sep)) {
518 GnmHAlign a;
519 switch (*sep) {
520 case '1' : a = GNM_HALIGN_LEFT; break;
521 case '2' : a = GNM_HALIGN_RIGHT; break;
522 case '3' : a = GNM_HALIGN_CENTER; break;
523 case '4' : a = GNM_HALIGN_FILL; break;
524 default :
525 applix_parse_error (state, "Unknown horizontal alignment '%c'", *sep);
526 return NULL;
528 gnm_style_set_align_h (style, a);
529 ++sep;
530 } else if (*sep == 'V') {
531 GnmVAlign a;
532 switch (sep[1]) {
533 case 'T' : a = GNM_VALIGN_TOP; break;
534 case 'C' : a = GNM_VALIGN_CENTER; break;
535 case 'B' : a = GNM_VALIGN_BOTTOM; break;
536 default :
537 applix_parse_error (state, "Unknown vertical alignment '%c'", *sep);
538 return NULL;
540 gnm_style_set_align_v (style, a);
541 sep += 2;
542 break;
543 } else {
544 gboolean get_precision = FALSE;
545 char const *format = NULL;
547 switch (*sep) {
548 case 'D' : {
549 int id = 0;
550 char *end;
551 static char const * const date_formats[] = {
552 /* 1 */ "mmmm d, yyyy",
553 /* 2 */ "mmm d, yyyy",
554 /* 3 */ "d mmm yy",
555 /* 4 */ "mm/dd/yy",
556 /* 5 */ "dd.mm.yy",
557 /* 6 */ "yyyy-mm-dd",
558 /* 7 */ "yy-mm-dd",
559 /* 8 */ "yyyy mm dd",
560 /* 9 */ "yy mm dd",
561 /* 10 */ "yyyymmdd",
562 /* 11 */ "yymmdd",
563 /* 12 */ "dd/mm/yy",
564 /* 13 */ "dd.mm.yyyy",
565 /* 14 */ "mmm dd, yyyy",
566 /* 15 */ "mmmm yyyy",
567 /* 16 */ "mmm.yyyy"
570 /* General : do nothing */
571 if (sep[1] == 'N') {
572 sep += 2;
573 break;
576 if (!g_ascii_isdigit (sep[1]) ||
577 (0 == (id = a_strtol (sep+1, &end))) ||
578 sep+1 == end ||
579 id < 1 || id > 16) {
580 applix_parse_error (state, "Unknown format %d", id);
581 return NULL;
583 format = date_formats[id - 1];
584 sep = end;
585 break;
588 case 'T' :
589 switch (sep[1]) {
590 case '0' : format = "hh:mm:ss AM/PM"; break;
591 case '1' : format = "hh:mm AM/PM"; break;
592 case '2' : format = "hh:mm:ss"; break;
593 case '3' : format = "hh:mm"; break;
594 default :
595 applix_parse_error (state, "Unknown time format '%c'", sep[1]);
596 return NULL;
598 sep += 2;
599 break;
601 case 'G' : /* general */
602 gnm_style_set_format (style, go_format_general ());
604 /* What is 'Gf' ? */
605 if (sep[1] == 'f')
606 sep += 2;
607 else while (g_ascii_isdigit (*(++sep)))
609 break;
611 case 'C' : /* currency or comma */
612 /* comma 'CO' */
613 if (sep[1] == 'O') {
614 ++sep;
615 format_prefix = "#,##0";
616 } else
617 /* FIXME : what currency to use for differnt locales */
618 format_prefix = "$ #,##0";
620 format_suffix = "";
621 get_precision = TRUE;
622 break;
624 case 'S' : /* scientific */
625 format_suffix = "E+00";
626 get_precision = TRUE;
627 break;
629 case 'P' : /* percentage */
630 format_suffix = "%";
631 get_precision = TRUE;
632 break;
634 case 'F' : /* fixed */
635 get_precision = TRUE;
636 break;
638 #if 0
639 /* FIXME : Add these to gnumeric ? */
640 case "GR0" : Graph ? Seems like a truncated integer histogram
641 /* looks like crap, no need to support */
643 #endif
644 case 'B' : if (sep[1] == '0') {
645 /* TODO : support this in gnumeric */
646 sep += 2;
647 break;
649 /* Fall through */
650 default :
651 applix_parse_error (state, "Unknown format '%c'", *sep);
652 return NULL;
655 if (get_precision) {
656 static char const *zeros = "000000000";
657 char *tmp_format;
658 char const *prec = "", *decimal = "";
659 int n_prec = applix_get_precision (++sep);
661 sep++;
662 if (n_prec > 0) {
663 prec = zeros + 9 - n_prec;
664 decimal = ".";
667 if (!format_prefix)
668 format_prefix = "0";
669 tmp_format = g_strconcat (format_prefix, decimal, prec,
670 format_suffix, NULL);
672 gnm_style_set_format_text (style, tmp_format);
673 g_free (tmp_format);
674 } else if (NULL != format)
675 gnm_style_set_format_text (style, format);
679 /* Font spec */
680 for (++sep ; *sep && *sep != '|' && *sep != ')' ; ) {
682 /* check for the 1 character modifiers */
683 switch (*sep) {
684 case 'B' :
685 gnm_style_set_font_bold (style, TRUE);
686 ++sep;
687 break;
688 case 'I' :
689 gnm_style_set_font_italic (style, TRUE);
690 ++sep;
691 break;
692 case 'U' :
693 gnm_style_set_font_uline (style, UNDERLINE_SINGLE);
694 ++sep;
695 break;
696 case 'D' :
697 gnm_style_set_font_uline (style, UNDERLINE_DOUBLE);
698 ++sep;
699 break;
700 case 'f' :
701 if (sep[1] == 'g' ) {
702 /* TODO : what is this ?? */
703 sep += 2;
704 break;
706 applix_parse_error (state, "Unknown font modifier 'f%c'", sep[1]);
707 return NULL;
709 case 'F' :
710 if (sep[1] == 'G' ) {
711 GnmColor *color = applix_get_color (state, &sep);
712 if (color == NULL)
713 return NULL;
714 gnm_style_set_font_color (style, color);
715 break;
717 applix_parse_error (state, "Unknown font modifier F%c", sep[1]);
718 return NULL;
720 case 'P' : {
721 char *start = ++sep;
722 double size = go_strtod (start, &sep);
724 if (start != sep && size > 0.) {
725 gnm_style_set_font_size (style, size / gnm_app_dpi_to_pixels ());
726 break;
728 applix_parse_error (state, "Invalid font size '%s", start);
729 return NULL;
732 case 'W' :
733 if (sep[1] == 'T') {
734 /* FIXME : What is WTO ?? */
735 if (sep[2] == 'O') {
736 sep +=3;
737 break;
739 gnm_style_set_wrap_text (style, TRUE);
740 sep +=2;
741 break;
743 applix_parse_error (state, "Unknown font modifier W%c", sep[1]);
744 return NULL;
746 case 'T' :
747 if (sep[1] == 'F') {
748 /* be a font ID numbered from 0 */
749 char *start = (sep += 2);
751 font_id = a_strtol (start, &sep);
752 if (start == sep || font_id < 0 || font_id >= (int)state->font_names->len) {
753 applix_parse_error (state, "Unknown font index %s", start);
754 font_id = 0;
756 break;
760 default :
761 applix_parse_error (state, "Unknown font modifier");
762 return NULL;
765 if (*sep == ',')
766 ++sep;
769 if (*sep != '|' && *sep != ')') {
770 applix_parse_error (state, "Invalid font specification");
771 return NULL;
774 if (font_id < (int)state->font_names->len)
775 gnm_style_set_font_name (style, g_ptr_array_index (state->font_names, font_id));
777 /* Background, pattern, and borders */
778 for (++sep ; *sep && *sep != ')' ; ) {
780 if (sep[0] == 'S' && sep[1] == 'H') {
781 /* A map from applix patten
782 * indicies to gnumeric.
784 static int const map[] = { 0,
785 1, 6, 5, 4, 3, 2, 24,
786 24, 14, 13, 17, 16, 15, 11,
787 19, 20, 21, 22, 23,
789 char *end;
790 int num = a_strtol (sep += 2, &end);
792 if (sep == end || 0 >= num || num >= (int)G_N_ELEMENTS (map)) {
793 applix_parse_error (state, "Unknown pattern %s", sep);
794 goto error;
797 num = map[num];
798 gnm_style_set_pattern (style, num);
799 sep = end;
801 if (sep[0] == 'F' && sep[1] == 'G' ) {
802 GnmColor *color = applix_get_color (state, &sep);
803 if (color == NULL)
804 goto error;
805 gnm_style_set_pattern_color (style, color);
808 if (sep[0] == 'B' && sep[1] == 'G') {
809 GnmColor *color = applix_get_color (state, &sep);
810 if (color == NULL)
811 goto error;
812 gnm_style_set_back_color (style, color);
814 } else if (sep[0] == 'T' || sep[0] == 'B' || sep[0] == 'L' || sep[0] == 'R') {
815 /* A map from applix border indicies to gnumeric. */
816 static GnmStyleBorderType const map[] = {0,
817 GNM_STYLE_BORDER_THIN,
818 GNM_STYLE_BORDER_MEDIUM,
819 GNM_STYLE_BORDER_THICK,
820 GNM_STYLE_BORDER_DASHED,
821 GNM_STYLE_BORDER_DOUBLE
824 GnmColor *color;
825 GnmStyleElement const type =
826 (sep[0] == 'T') ? MSTYLE_BORDER_TOP :
827 (sep[0] == 'B') ? MSTYLE_BORDER_BOTTOM :
828 (sep[0] == 'L') ? MSTYLE_BORDER_LEFT : MSTYLE_BORDER_RIGHT;
829 GnmStyleBorderOrientation const orient = (sep[0] == 'T' || sep[0] == 'B')
830 ? GNM_STYLE_BORDER_HORIZONTAL : GNM_STYLE_BORDER_VERTICAL;
831 char *end;
832 int num = a_strtol (++sep, &end);
834 if (sep == end || 0 >= num || num >= (int)G_N_ELEMENTS (map)) {
835 applix_parse_error (state, "Unknown border style %s", sep);
836 goto error;
838 sep = end;
840 if (sep[0] == 'F' && sep[1] == 'G' ) {
841 color = applix_get_color (state, &sep);
842 if (color == NULL)
843 goto error;
844 } else
845 color = style_color_black ();
847 gnm_style_set_border (style, type,
848 gnm_style_border_fetch (map[num], color, orient));
851 if (*sep == ',')
852 ++sep;
853 else if (*sep != ')') {
854 applix_parse_error (state, "Invalid pattern, background, or border");
855 goto error;
859 if (*sep != ')') {
860 applix_parse_error (state, "Invalid pattern or background");
861 goto error;
864 /* Store the newly parsed style along with its descriptor */
865 g_hash_table_insert (state->styles, g_strdup (start), style);
868 *buffer = tmp + 2;
869 gnm_style_ref (style);
870 return style;
872 error:
873 if (style)
874 gnm_style_unref (style);
875 return NULL;
878 static gboolean
879 applix_read_attributes (ApplixReadState *state)
881 int count = 0;
882 unsigned char *ptr, *tmp;
883 GnmStyle *style;
885 while (NULL != (ptr = applix_get_line (state))) {
886 if (!a_strncmp (ptr, "Attr Table End"))
887 return FALSE;
889 if (ptr [0] != '<')
890 return applix_parse_error (state, "Invalid attribute");
892 /* TODO : The first style seems to be a different format */
893 if (count++) {
894 tmp = ptr + 1;
895 style = applix_parse_style (state, &tmp);
896 if (style == NULL || *tmp != '>')
897 return applix_parse_error (state, "Invalid attribute");
898 g_ptr_array_add (state->attrs, style);
902 /* NOTREACHED */
903 return FALSE;
906 static Sheet *
907 applix_fetch_sheet (ApplixReadState *state, char const *name)
909 Sheet *sheet = workbook_sheet_by_name (state->wb, name);
911 if (sheet == NULL) {
912 int cols = APPLIX_SHEET_MAX_COLS;
913 int rows = APPLIX_SHEET_MAX_ROWS;
914 gnm_sheet_suggest_size (&cols, &rows);
915 sheet = sheet_new (state->wb, name, cols, rows);
916 workbook_sheet_attach (state->wb, sheet);
917 g_object_set (sheet, "zoom-factor", state->zoom / 100.0, NULL);
918 sheet_flag_recompute_spans (sheet);
921 return sheet;
924 static Sheet *
925 applix_parse_sheet (ApplixReadState *state, unsigned char **buffer,
926 char const separator)
928 Sheet *sheet;
930 /* Get sheet name */
931 char *tmp = strchr (*buffer, separator);
933 if (tmp == NULL) {
934 applix_parse_error (state, "Invalid sheet name.");
935 return NULL;
938 *tmp = '\0';
939 sheet = applix_fetch_sheet (state, *buffer);
940 *buffer = tmp+1;
941 return sheet;
944 static char *
945 applix_parse_cellref (ApplixReadState *state, unsigned char *buffer,
946 Sheet **sheet, GnmCellPos *pos,
947 char const separator)
949 *sheet = applix_parse_sheet (state, &buffer, separator);
951 /* Get cell addr */
952 if (*sheet) {
953 buffer = (unsigned char *)applix_cellpos_parse
954 (buffer, *sheet, pos, FALSE);
955 if (buffer)
956 return buffer;
959 *sheet = NULL;
960 pos->col = pos->row = -1;
961 return NULL;
964 static int
965 applix_height_to_pixels (int height)
967 return height+4;
969 static int
970 applix_width_to_pixels (int width)
972 return width*8 + 3;
975 static int
976 applix_read_current_view (ApplixReadState *state, unsigned char *buffer)
978 /* What is this ? */
979 unsigned char *ptr;
980 while (NULL != (ptr = applix_get_line (state)))
981 if (!a_strncmp (ptr, "End View, Name: ~Current~"))
982 return 0;
983 return -1;
986 static int
987 applix_read_view (ApplixReadState *state, unsigned char *buffer)
989 Sheet *sheet = NULL;
990 unsigned char *name = buffer + 19;
991 unsigned char *tmp;
992 gboolean ignore;
994 tmp = strchr (name, ':');
995 if (tmp == NULL)
996 return 0;
997 *tmp = '\0';
999 ignore = tmp[1] != '~';
1000 if (!ignore)
1001 state->sheet_order = g_slist_prepend (state->sheet_order,
1002 applix_fetch_sheet (state, name));
1004 while (NULL != (buffer = applix_get_line (state))) {
1005 if (!a_strncmp (buffer, "View End, Name: ~"))
1006 break;
1007 if (ignore)
1008 continue;
1010 if (!a_strncmp (buffer, "View Top Left: ")) {
1011 GnmCellPos pos;
1012 if (applix_parse_cellref (state, buffer+15, &sheet, &pos, ':') &&
1013 valid_cellpos (sheet, &pos))
1014 gnm_sheet_view_set_initial_top_left (sheet_get_view (sheet, state->wb_view),
1015 pos.col, pos.row);
1016 } else if (!a_strncmp (buffer, "View Open Cell: ")) {
1017 GnmCellPos pos;
1018 if (applix_parse_cellref (state, buffer+16, &sheet, &pos, ':') &&
1019 valid_cellpos (sheet, &pos))
1020 sv_selection_set (sheet_get_view (sheet, state->wb_view),
1021 &pos, pos.col, pos.row, pos.col, pos.row);
1022 } else if (!a_strncmp (buffer, "View Default Column Width ")) {
1023 char *ptr, *tmp = buffer + 26;
1024 int width = a_strtol (tmp, &ptr);
1025 if (tmp == ptr || width <= 0)
1026 return applix_parse_error (state, "Invalid default column width");
1028 sheet_col_set_default_size_pixels (sheet,
1029 applix_width_to_pixels (width));
1030 } else if (!a_strncmp (buffer, "View Default Row Height: ")) {
1031 char *ptr, *tmp = buffer + 25;
1032 int height = a_strtol (tmp, &ptr);
1033 if (tmp == ptr || height <= 0)
1034 return applix_parse_error (state, "Invalid default row height");
1036 /* height + one for the grid line */
1037 sheet_row_set_default_size_pixels (sheet,
1038 applix_height_to_pixels (height));
1039 } else if (!a_strncmp (buffer, "View Row Heights: ")) {
1040 char *ptr = buffer + 17;
1041 do {
1042 int row, height;
1043 char *tmp;
1045 row = a_strtol (tmp = ptr + 1, &ptr) - 1;
1046 if (tmp == ptr || row < 0 || *ptr != ':')
1047 return applix_parse_error (state, "Invalid row size row number");
1048 height = a_strtol (tmp = ptr + 1, &ptr);
1049 if (height >= 32768)
1050 height -= 32768;
1052 if (tmp == ptr || height <= 0)
1053 return applix_parse_error (state, "Invalid row size");
1055 /* These seem to assume
1056 * top margin 2
1057 * bottom margin 1
1058 * size in pixels = val -32768 (sometimes ??)
1060 sheet_row_set_size_pixels (sheet, row,
1061 applix_height_to_pixels (height),
1062 TRUE);
1063 } while (ptr[0] == ' ' && g_ascii_isdigit (ptr[1]));
1064 } else if (!a_strncmp (buffer, "View Column Widths: ")) {
1065 char const *ptr = buffer + 19;
1066 char const *tmp;
1067 int col, width;
1068 unsigned char dummy;
1070 do {
1071 ptr = applix_col_parse (tmp = ptr + 1, &col, &dummy);
1072 if (!ptr || *ptr != ':')
1073 return applix_parse_error (state, "Invalid column");
1074 width = a_strtol (tmp = ptr + 1, (char **)&ptr);
1075 if (tmp == ptr || width <= 0)
1076 return applix_parse_error (state, "Invalid column size");
1078 /* These seem to assume
1079 * pixels = 8*width + 3 for the grid lines and margins
1081 sheet_col_set_size_pixels (sheet, col,
1082 applix_width_to_pixels (width),
1083 TRUE);
1084 } while (ptr[0] == ' ' && g_ascii_isalpha (ptr[1]));
1088 return 0;
1091 static int
1092 applix_read_cells (ApplixReadState *state)
1094 Sheet *sheet;
1095 GnmStyle *style;
1096 GnmCell *cell;
1097 GnmCellPos pos;
1098 GnmParseError perr;
1099 unsigned char content_type, *tmp, *ptr;
1101 while (NULL != (ptr = applix_get_line (state))) {
1102 gboolean const val_is_string = (ptr[0] != '\0' && ptr[1] == '\'');
1104 if (!a_strncmp (ptr, "*END SPREADSHEETS"))
1105 break;
1107 /* Parse formatting */
1108 style = applix_parse_style (state, &ptr);
1109 if (style == NULL)
1110 return -1;
1111 if (ptr == NULL) {
1112 gnm_style_unref (style);
1113 return -1;
1116 /* Get cell */
1117 ptr = applix_parse_cellref (state, ptr, &sheet, &pos, '!');
1118 if (ptr == NULL) {
1119 gnm_style_unref (style);
1120 return applix_parse_error (state, "Expression did not specify target cell");
1123 if (!valid_cellpos (sheet, &pos)) {
1124 gnm_style_unref (style);
1125 g_warning ("Ignoring sheet contents beyond allowed range.");
1126 continue;
1129 cell = sheet_cell_fetch (sheet, pos.col, pos.row);
1131 /* Apply the formating */
1132 sheet_style_set_pos (sheet, pos.col, pos.row, style);
1133 content_type = *ptr;
1134 switch (content_type) {
1135 case ';' : /* First of a shared formula */
1136 case '.' : { /* instance of a shared formula */
1137 GnmParsePos pos;
1138 GnmValue *val = NULL;
1139 GnmRange r;
1140 char *expr_string;
1142 ptr = applix_parse_value (ptr+2, &expr_string);
1144 /* Just in case something failed */
1145 if (ptr == NULL)
1146 return -1;
1148 if (!val_is_string)
1149 /* Does it match any formats (use default date convention) */
1150 val = format_match (ptr, NULL, NULL);
1152 if (val == NULL)
1153 /* TODO : Could this happen ? */
1154 val = value_new_string (ptr);
1156 #if 0
1157 g_printerr ("\'%s\'\n\'%s\'\n", ptr, expr_string);
1158 #endif
1160 if (content_type == ';') {
1161 GnmExprTop const *texpr;
1162 gboolean is_array = FALSE;
1164 if (*expr_string == '~') {
1165 Sheet *start_sheet, *end_sheet;
1166 tmp = applix_parse_cellref (state, expr_string+1, &start_sheet,
1167 &r.start, ':');
1168 if (start_sheet == NULL || tmp == NULL || tmp[0] != '.' || tmp[1] != '.') {
1169 applix_parse_error (state, "Invalid array expression");
1170 continue;
1173 tmp = applix_parse_cellref (state, tmp+2, &end_sheet,
1174 &r.end, ':');
1175 if (end_sheet == NULL || tmp == NULL || tmp[0] != '~') {
1176 applix_parse_error (state, "Invalid array expression");
1177 continue;
1180 if (start_sheet != end_sheet) {
1181 applix_parse_error (state, "3D array functions are not supported.");
1182 continue;
1185 if (!valid_cellpos (start_sheet, &r.start) ||
1186 !valid_cellpos (end_sheet, &r.end)) {
1187 g_warning ("Ignoring sheet contents beyond allowed range.");
1188 continue;
1191 is_array = TRUE;
1192 expr_string = tmp+3; /* ~addr~<space><space>=expr */
1195 /* We need to continue at all costs so that the
1196 * rest of the sheet can be parsed. If we quit, then trailing
1197 * 'Formula ' lines confuse the parser
1199 (void) parse_error_init(&perr);
1200 if (*expr_string != '=' && *expr_string != '+') {
1201 applix_parse_error (state, _("Expression did not start with '=' ? '%s'"),
1202 expr_string);
1203 texpr = gnm_expr_top_new_constant (value_new_string (expr_string));
1204 } else
1205 texpr = gnm_expr_parse_str (expr_string+1,
1206 parse_pos_init_cell (&pos, cell),
1207 GNM_EXPR_PARSE_DEFAULT,
1208 state->convs,
1209 &perr);
1211 if (texpr == NULL) {
1212 applix_parse_error (state, _("%s!%s : unable to parse '%s'\n %s"),
1213 sheet->name_quoted, cell_name (cell),
1214 expr_string, perr.err->message);
1215 texpr = gnm_expr_top_new_constant (value_new_string (expr_string));
1216 } else if (is_array) {
1217 gnm_cell_set_array (sheet,
1219 texpr);
1220 gnm_cell_assign_value (cell, val);
1221 /* Leak? */
1222 } else {
1223 gnm_cell_set_expr_and_value (cell, texpr, val, TRUE);
1224 /* Leak? */
1227 if (!applix_get_line (state) ||
1228 a_strncmp (state->buffer, "Formula: ")) {
1229 applix_parse_error (state, "Missing formula ID");
1230 continue;
1233 ptr = state->buffer + 9;
1235 /* Store the newly parsed expression along with its descriptor */
1236 g_hash_table_insert (state->exprs,
1237 g_strdup (ptr),
1238 (gpointer)texpr);
1240 parse_error_free (&perr);
1241 } else {
1242 GnmExprTop const *texpr;
1243 char const *key = expr_string + strlen (expr_string);
1244 while (key > expr_string && !g_ascii_isspace (key[-1]))
1245 key--;
1246 #if 0
1247 g_printerr ("Shared '%s'\n", expr_string);
1248 #endif
1249 texpr = g_hash_table_lookup (state->exprs, key);
1250 gnm_cell_set_expr_and_value (cell, texpr, val, TRUE);
1252 break;
1255 case ':' : { /* simple value */
1256 GnmValue *val = NULL;
1258 ptr += 2;
1259 #if 0
1260 g_printerr ("\"%s\" %d\n", ptr, val_is_string);
1261 #endif
1262 /* Does it match any formats (use default date convention) */
1263 if (!val_is_string)
1264 val = format_match (ptr, NULL, NULL);
1265 if (val == NULL)
1266 val = value_new_string (ptr);
1268 if (gnm_cell_is_array (cell))
1269 gnm_cell_assign_value (cell, val);
1270 else
1271 gnm_cell_set_value (cell, val);
1272 break;
1275 default :
1276 g_warning ("Unknown cell type '%c'", content_type);
1280 return 0;
1283 static int
1284 applix_read_row_list (ApplixReadState *state, unsigned char *ptr)
1286 unsigned char *tmp;
1287 GnmRange r;
1288 Sheet *sheet = applix_parse_sheet (state, &ptr, ' ');
1290 if (ptr == NULL)
1291 return -1;
1292 if (*ptr != '!')
1293 return applix_parse_error (state, "Invalid row format");
1295 r.start.row = r.end.row = au_strtol (++ptr, &tmp) - 1;
1296 if (tmp == ptr || r.start.row < 0 || tmp[0] != ':' || tmp[1] != ' ')
1297 return applix_parse_error (state, "Invalid row format row number");
1299 ++tmp;
1300 do {
1301 unsigned attr_index;
1303 r.start.col = au_strtol (ptr = tmp+1, &tmp);
1304 if (tmp == ptr || r.start.col < 0 || tmp[0] != '-')
1305 return applix_parse_error (state, "Invalid row format start col");
1306 r.end.col = au_strtol (ptr = tmp+1, &tmp);
1307 if (tmp == ptr || r.end.col < 0 || tmp[0] != ':')
1308 return applix_parse_error (state, "Invalid row format end col");
1309 attr_index = au_strtol (ptr = tmp+1, &tmp);
1310 if (tmp != ptr && attr_index >= 2 && attr_index < state->attrs->len+2) {
1311 GnmStyle *style = g_ptr_array_index(state->attrs, attr_index-2);
1312 gnm_style_ref (style);
1313 sheet_style_set_range (sheet, &r, style);
1314 } else if (attr_index != 1) /* TODO : What the hell is attr 1 ?? */
1315 return applix_parse_error (state, "Invalid row format attr index");
1317 /* Just for kicks they added a trailing space */
1318 } while (tmp[0] && g_ascii_isdigit (tmp[1]));
1320 return 0;
1323 static gboolean
1324 applix_read_sheet_table (ApplixReadState *state)
1326 unsigned char *ptr;
1327 unsigned char *std_name, *real_name;
1328 while (NULL != (ptr = applix_get_line (state))) {
1329 if (!a_strncmp (ptr, "END SHEETS TABLE"))
1330 return FALSE;
1332 /* Sheet A: ~Foo~ */
1333 std_name = ptr + 6;
1334 ptr = strchr (std_name, ':');
1335 if (ptr == NULL)
1336 continue;
1337 *ptr = '\0';
1339 real_name = ptr + 3;
1340 ptr = strchr (real_name, '~');
1341 if (ptr == NULL)
1342 continue;
1343 *ptr = '\0';
1345 state->std_names = g_slist_prepend (state->std_names,
1346 g_strdup (std_name));
1347 state->real_names = g_slist_prepend (state->real_names,
1348 g_strdup (real_name));
1350 return TRUE;
1353 static gboolean
1354 applix_read_header_footer (ApplixReadState *state)
1356 unsigned char *ptr;
1357 while (NULL != (ptr = applix_get_line (state)))
1358 if (!a_strncmp (ptr, "Headers And Footers End"))
1359 return FALSE;
1360 return TRUE;
1363 static gboolean
1364 applix_read_absolute_name (ApplixReadState *state, char *buffer)
1366 char *end;
1367 GnmRangeRef ref;
1368 GnmParsePos pp;
1369 GnmExprTop const *texpr;
1371 /* .ABCDe. Coordinate: A:B2..A:C4 */
1372 /* Spec guarantees that there are no dots in the name */
1373 buffer = strchr (buffer, '.');
1374 if (buffer == NULL)
1375 return TRUE;
1376 end = strchr (++buffer, '.');
1377 if (end == NULL)
1378 return TRUE;
1379 *end = '\0';
1380 end = strchr (end + 1, ':');
1381 if (end == NULL)
1382 return TRUE;
1383 applix_rangeref_parse (&ref, end+2,
1384 parse_pos_init (&pp, state->wb, NULL, 0, 0),
1385 state->convs);
1386 ref.a.col_relative = ref.b.col_relative =
1387 ref.a.row_relative = ref.b.row_relative = FALSE;
1389 texpr = gnm_expr_top_new_constant
1390 (value_new_cellrange_unsafe (&ref.a, &ref.b));
1391 expr_name_add (&pp, buffer, texpr, NULL, TRUE, NULL);
1393 return FALSE;
1396 static gboolean
1397 applix_read_relative_name (ApplixReadState *state, char *buffer)
1399 int dummy;
1400 char *end;
1401 GnmRangeRef ref, flag;
1402 GnmParsePos pp;
1403 GnmExprTop const *texpr;
1405 /* .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 */
1406 /* Spec guarantees that there are no dots in the name */
1407 buffer = strchr (buffer, '.');
1408 if (buffer == NULL)
1409 return TRUE;
1410 end = strchr (++buffer, '.');
1411 if (end == NULL)
1412 return TRUE;
1413 *end = '\0';
1414 if (12 != sscanf (end + 2,
1415 " tCol:%d tRow:%d tSheet:%d bCol:%d bRow:%d bSheet: %d tColAbs:%d tRowAbs:%d tSheetAbs:%d bColAbs:%d bRowAbs:%d bSheetAbs:%d",
1416 &ref.a.col, &ref.a.row, &dummy, &ref.b.col, &ref.b.row, &dummy,
1417 &flag.a.col, &flag.a.row, &dummy, &flag.b.col, &flag.b.row, &dummy))
1418 return TRUE;
1420 ref.a.col_relative = (flag.a.col == 0);
1421 ref.b.col_relative = (flag.b.col == 0);
1422 ref.a.row_relative = (flag.a.row == 0);
1423 ref.b.row_relative = (flag.b.row == 0);
1425 ref.a.sheet = ref.b.sheet = NULL;
1426 texpr = gnm_expr_top_new_constant
1427 (value_new_cellrange_unsafe (&ref.a, &ref.b));
1428 parse_pos_init (&pp, state->wb, NULL,
1429 MAX (-ref.a.col, 0), MAX (-ref.a.row, 0));
1430 expr_name_add (&pp, buffer, texpr, NULL, TRUE, NULL);
1432 return FALSE;
1435 #define ABS_NAMED_RANGE "Named Range, Name:"
1436 #define REL_NAMED_RANGE "Relative Named Range, Name:"
1438 static int
1439 applix_read_impl (ApplixReadState *state)
1441 Sheet *sheet;
1442 GnmCellPos pos;
1443 int ext_links = -1;
1444 unsigned char *real_name = NULL;
1445 char top_cell_addr[30] = "";
1446 char cur_cell_addr[30] = "";
1447 unsigned char *buffer;
1448 char default_text_format[128] = "";
1449 char default_number_format[128] = "";
1450 int def_col_width = -1;
1451 int win_width = -1;
1452 int win_height = -1;
1454 while (NULL != (buffer = applix_get_line (state))) {
1455 if (!a_strncmp (buffer, "*BEGIN SPREADSHEETS VERSION=")) {
1456 char encoding_buffer[32];
1457 int v0, v1;
1458 if (3 != sscanf (buffer, "*BEGIN SPREADSHEETS VERSION=%d/%d ENCODING=%31s",
1459 &v0, &v1, encoding_buffer))
1460 return applix_parse_error (state, "Invalid header ");
1462 /* FIXME : Guess that version 400 is a minimum */
1463 if (v0 < 400)
1464 return applix_parse_error (state, "Versions < 4.0 are not supported");
1466 /* We only have a sample of '7BIT' right now */
1467 if (strcmp (encoding_buffer, "7BIT"))
1468 return applix_parse_error (state, "We only have samples of '7BIT' encoding, please send us this sample.");
1470 } else if (!a_strncmp (buffer, "Num ExtLinks:")) {
1471 if (1 != sscanf (buffer, "Num ExtLinks: %d", &ext_links))
1472 return applix_parse_error (state, "Missing number of external links");
1474 } else if (!a_strncmp (buffer, "Spreadsheet Dump Rev")) {
1475 int major_rev, minor_rev, len;
1476 if (3 != sscanf (buffer, "Spreadsheet Dump Rev %d.%d Line Length %d",
1477 &major_rev, &minor_rev, &len))
1478 return applix_parse_error (state, "Missing dump revision");
1479 if (len < 0 || 65535 < len) /* magic sanity check */
1480 return applix_parse_error (state, "Invalid line length");
1481 state->line_len = len;
1483 d (0, g_printerr ("Applix load : Saved with revision %d.%d",
1484 major_rev, minor_rev););
1485 } else if (!a_strncmp (buffer, "Current Doc Real Name:")) {
1486 g_free (real_name);
1487 real_name = NULL; /* FIXME? g_strdup (buffer + 22); */
1489 } else if (!strcmp (buffer, "COLORMAP")) {
1490 if (applix_read_colormap (state))
1491 return applix_parse_error (state, "invalid colormap");
1493 } else if (!strcmp (buffer, "TYPEFACE TABLE")) {
1494 if (applix_read_typefaces (state))
1495 return applix_parse_error (state, "invalid typefaces");
1497 } else if (!strcmp (buffer, "Attr Table Start")) {
1498 if (applix_read_attributes (state))
1499 return applix_parse_error (state, "Invalid attribute table");
1501 } else if (!a_strncmp (buffer, "View, Name: ~Current~")) {
1502 if (0 != applix_read_current_view (state, buffer))
1503 return applix_parse_error (state, "Invalid view");
1505 } else if (!a_strncmp (buffer, "View Start, Name: ~")) {
1506 if (0 != applix_read_view (state, buffer))
1507 return applix_parse_error (state, "Invalid view");
1509 } else if (!a_strncmp (buffer, "Default Label Style")) {
1510 if (1 != sscanf (buffer, "Default Label Style %127s", default_text_format))
1511 return applix_parse_error (state, "invalid default label style");
1513 } else if (!a_strncmp (buffer, "Default Number Style")) {
1514 if (1 != sscanf (buffer, "Default Number Style %127s", default_number_format))
1515 return applix_parse_error (state, "invalid default number style");
1517 } else if (!a_strncmp (buffer, "Document Column Width:")) {
1518 if (1 != sscanf (buffer, "Document Column Width: %d", &def_col_width))
1519 return applix_parse_error (state, "invalid col width");
1521 } else if (!a_strncmp (buffer, "Percent Zoom Factor:")) {
1522 if (1 != sscanf (buffer, "Percent Zoom Factor: %d", &state->zoom) ||
1523 state->zoom <= 10 || 500 <= state->zoom)
1524 return applix_parse_error (state, "invalid zoom");
1525 } else if (!a_strncmp (buffer, "Window Width:")) {
1526 if (1 != sscanf (buffer, "Window Width: %d", &win_width))
1527 return applix_parse_error (state, "invalid win width");
1528 } else if (!a_strncmp (buffer, "Window Height:")) {
1529 if (1 != sscanf (buffer, "Window Height: %d", &win_height))
1530 return applix_parse_error (state, "invalid win height");
1531 } else if (!a_strncmp (buffer, "Top Left:")) {
1532 if (1 != sscanf (buffer, "Top Left: %25s", top_cell_addr))
1533 return applix_parse_error (state, "invalid top left");
1534 } else if (!a_strncmp (buffer, "Open Cell:")) {
1535 if (1 != sscanf (buffer, "Open Cell: %25s", cur_cell_addr))
1536 return applix_parse_error (state, "invalid cur cell");
1537 } else if (!a_strncmp (buffer, "SHEETS TABLE")) {
1538 if (applix_read_sheet_table (state))
1539 return applix_parse_error (state, "sheet table");
1540 } else if (!a_strncmp (buffer, ABS_NAMED_RANGE)) {
1541 if (applix_read_absolute_name (state, buffer + sizeof (ABS_NAMED_RANGE)))
1542 return applix_parse_error (state, "Absolute named range");
1543 } else if (!a_strncmp (buffer, REL_NAMED_RANGE)) {
1544 if (applix_read_relative_name (state, buffer + sizeof (REL_NAMED_RANGE)))
1545 return applix_parse_error (state, "Relative named range");
1546 } else if (!a_strncmp (buffer, "Row List")) {
1547 if (applix_read_row_list (state, buffer + sizeof ("Row List")))
1548 return applix_parse_error (state, "row list");
1549 } else if (!a_strncmp (buffer, "Headers And Footers")) {
1550 if (applix_read_header_footer (state))
1551 return applix_parse_error (state, "headers and footers");
1553 break; /* BREAK OUT OF THE LOOP HERE */
1557 if (applix_read_cells (state))
1558 return -1;
1560 /* We only need the sheet, the visible cell, and edit pos are already set */
1561 if (applix_parse_cellref (state, cur_cell_addr, &sheet, &pos, ':') &&
1562 valid_cellpos (sheet, &pos))
1563 wb_view_sheet_focus (state->wb_view, sheet);
1565 return 0;
1568 static gboolean
1569 cb_remove_texpr (gpointer key, gpointer value, gpointer user_data)
1571 g_free (key);
1572 gnm_expr_top_unref (value);
1573 return TRUE;
1575 static gboolean
1576 cb_remove_style (gpointer key, gpointer value, gpointer user_data)
1578 g_free (key);
1579 gnm_style_unref (value);
1580 return TRUE;
1583 static GnmExpr const *
1584 applix_func_map_in (GnmConventions const *conv, Workbook *scope,
1585 char const *name, GnmExprList *args)
1587 static struct {
1588 char const *applix_name;
1589 char const *gnm_name;
1590 } const sc_func_renames[] = {
1591 { "IPAYMT", "IPMT" },
1592 { "PAYMT", "PMT" },
1593 { "PPAYMT", "PPMT" },
1594 { NULL, NULL }
1596 static GHashTable *namemap = NULL;
1598 GnmFunc *f;
1599 char const *new_name;
1600 int i;
1602 if (NULL == namemap) {
1603 namemap = g_hash_table_new (go_ascii_strcase_hash,
1604 go_ascii_strcase_equal);
1605 for (i = 0; sc_func_renames[i].applix_name; i++)
1606 g_hash_table_insert (namemap,
1607 (gchar *) sc_func_renames[i].applix_name,
1608 (gchar *) sc_func_renames[i].gnm_name);
1611 if (NULL != namemap &&
1612 NULL != (new_name = g_hash_table_lookup (namemap, name)))
1613 name = new_name;
1614 if (NULL == (f = gnm_func_lookup (name, scope)))
1615 f = gnm_func_add_placeholder (scope, name, "");
1616 return gnm_expr_new_funcall (f, args);
1619 static GnmConventions *
1620 applix_conventions_new (void)
1622 GnmConventions *conv = gnm_conventions_new ();
1624 conv->intersection_char = 0;
1625 conv->accept_hash_logicals = TRUE;
1626 conv->allow_absolute_sheet_references = TRUE;
1627 conv->range_sep_dotdot = TRUE;
1628 conv->input.range_ref = applix_rangeref_parse;
1629 conv->input.func = applix_func_map_in;
1631 return conv;
1634 void
1635 applix_read (GOIOContext *io_context, WorkbookView *wb_view, GsfInput *src)
1637 int i;
1638 int res;
1639 ApplixReadState state;
1640 GSList *ptr, *renamed_sheets;
1642 /* Init the state variable */
1643 state.input = (GsfInputTextline *)gsf_input_textline_new (src);
1644 state.parse_error = NULL;
1645 state.wb_view = wb_view;
1646 state.wb = wb_view_get_workbook (wb_view);
1647 state.exprs = g_hash_table_new (&g_str_hash, &g_str_equal);
1648 state.styles = g_hash_table_new (&g_str_hash, &g_str_equal);
1649 state.colors = g_ptr_array_new ();
1650 state.attrs = g_ptr_array_new ();
1651 state.font_names = g_ptr_array_new ();
1652 state.buffer = NULL;
1653 state.buffer_size = 0;
1654 state.line_len = 80;
1655 state.sheet_order = NULL;
1656 state.std_names = NULL;
1657 state.real_names = NULL;
1658 state.convs = applix_conventions_new ();
1660 /* Actually read the workbook */
1661 res = applix_read_impl (&state);
1663 g_object_unref (state.input);
1664 g_free (state.buffer);
1666 state.sheet_order = g_slist_reverse (state.sheet_order);
1667 workbook_sheet_reorder (state.wb, state.sheet_order);
1668 g_slist_free (state.sheet_order);
1670 renamed_sheets = NULL;
1671 for (ptr = state.std_names; ptr != NULL ; ptr = ptr->next) {
1672 const char *name = ptr->data;
1673 Sheet *sheet = workbook_sheet_by_name (state.wb, name);
1674 int idx = sheet ? sheet->index_in_wb : -1;
1675 renamed_sheets = g_slist_prepend (renamed_sheets,
1676 GINT_TO_POINTER (idx));
1678 renamed_sheets = g_slist_reverse (renamed_sheets);
1679 workbook_sheet_rename (state.wb, renamed_sheets,
1680 state.real_names,
1681 GO_CMD_CONTEXT (io_context));
1682 g_slist_free (renamed_sheets);
1683 g_slist_free_full (state.std_names, g_free);
1684 g_slist_free_full (state.real_names, g_free);
1686 /* Release the shared expressions and styles */
1687 g_hash_table_foreach_remove (state.exprs, &cb_remove_texpr, NULL);
1688 g_hash_table_destroy (state.exprs);
1689 g_hash_table_foreach_remove (state.styles, &cb_remove_style, NULL);
1690 g_hash_table_destroy (state.styles);
1692 for (i = state.colors->len; --i >= 0 ; )
1693 style_color_unref (g_ptr_array_index (state.colors, i));
1694 g_ptr_array_free (state.colors, TRUE);
1696 for (i = state.attrs->len; --i >= 0 ; )
1697 gnm_style_unref (g_ptr_array_index(state.attrs, i));
1698 g_ptr_array_free (state.attrs, TRUE);
1700 for (i = state.font_names->len; --i >= 0 ; )
1701 g_free (g_ptr_array_index(state.font_names, i));
1702 g_ptr_array_free (state.font_names, TRUE);
1704 if (state.parse_error != NULL)
1705 go_io_error_info_set (io_context, state.parse_error);
1707 gnm_conventions_unref (state.convs);