1 /*
2 * sc.c - file import of SC/xspread files
3 * Copyright 1999 Jeff Garzik <jgarzik@mandrakesoft.com>
4 * Copyright (C) 2010 Andreas J. Guelzow <aguelzow@pyrshep.ca> All Rights Reserved
5 *
6 * With some code from sylk.c
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include <gnumeric.h>
26 #include <string.h>
27 #include <goffice/goffice.h>
28 #include <gnm-plugin.h>
29 #include <sheet-style.h>
30 #include <workbook-view.h>
31 #include <workbook.h>
32 #include <parse-util.h>
33 #include <value.h>
34 #include <cell.h>
35 #include <ranges.h>
36 #include <style.h>
37 #include <sheet.h>
38 #include <expr.h>
39 #include <expr-name.h>
40 #include <func.h>
41 #include <sheet-view.h>
42 #include <selection.h>
43 #include <rendered-value.h>
44 #include <style-font.h>
45
46 #include <gsf/gsf-input.h>
47 #include <gsf/gsf-input-textline.h>
48 #include <string.h>
49 #include <math.h>
50 #include <stdlib.h>
51
52 GNM_PLUGIN_MODULE_HEADER;
53
54 gboolean sc_file_probe (GOFileOpener const *fo, GsfInput *input,
55 GOFileProbeLevel pl);
56 void sc_file_open (GOFileOpener const *fo, GOIOContext *io_context,
57 WorkbookView *wb_view, GsfInput *input);
58
59 typedef struct {
60 GsfInputTextline *textline;
61 Sheet *sheet;
62 GIConv converter;
63 GnmConventions *convs;
64 GOIOContext *context; /* The IOcontext managing things */
65 char *last_error;
66 GArray *precision;
67 GPtrArray *formats;
68 } ScParseState;
69
70 typedef enum {
71 LABEL,
72 LEFTSTRING,
73 RIGHTSTRING
74 } sc_string_cmd_t;
75
76
77 static GOErrorInfo *sc_go_error_info_new_vprintf (GOSeverity severity,
78 char const *msg_format, ...)
79 G_GNUC_PRINTF (2, 3);
80
81 static GOErrorInfo *
sc_go_error_info_new_vprintf(GOSeverity severity,char const * msg_format,...)82 sc_go_error_info_new_vprintf (GOSeverity severity,
83 char const *msg_format, ...)
84 {
85 va_list args;
86 GOErrorInfo *ei;
87
88 va_start (args, msg_format);
89 ei = go_error_info_new_vprintf (severity, msg_format, args);
90 va_end (args);
91
92 return ei;
93 }
94
95 static gboolean sc_warning (ScParseState *state, char const *fmt, ...)
96 G_GNUC_PRINTF (2, 3);
97
98 static gboolean
sc_warning(ScParseState * state,char const * fmt,...)99 sc_warning (ScParseState *state, char const *fmt, ...)
100 {
101 char *msg;
102 char *detail;
103 va_list args;
104
105 va_start (args, fmt);
106 detail = g_strdup_vprintf (fmt, args);
107 va_end (args);
108
109 if (IS_SHEET (state->sheet))
110 msg = g_strdup_printf (_("On worksheet %s:"),state->sheet->name_quoted);
111 else
112 msg = g_strdup (_("General SC import error"));
113
114 if (0 != go_str_compare (msg, state->last_error)) {
115 GOErrorInfo *ei = sc_go_error_info_new_vprintf
116 (GO_WARNING, "%s", msg);
117
118 go_io_error_info_set (state->context, ei);
119 g_free (state->last_error);
120 state->last_error = msg;
121 } else
122 g_free (msg);
123
124 go_error_info_add_details
125 (state->context->info->data,
126 sc_go_error_info_new_vprintf (GO_WARNING, "%s", detail));
127
128 g_free (detail);
129
130 return FALSE; /* convenience */
131 }
132
133 static gboolean
enlarge(ScParseState * state,int col,int row)134 enlarge (ScParseState *state, int col, int row)
135 {
136 GnmSheetSize const *size = gnm_sheet_get_size (state->sheet);
137 gboolean err = FALSE;
138
139 if (col >= size->max_cols
140 || row >= size->max_rows) {
141 GOUndo * goundo;
142 int cols_needed = (col >= size->max_cols) ? col + 1
143 : size->max_cols;
144 int rows_needed = (row >= size->max_rows) ? row + 1
145 : size->max_rows;
146 gnm_sheet_suggest_size (&cols_needed, &rows_needed);
147
148 goundo = gnm_sheet_resize
149 (state->sheet, cols_needed, rows_needed, NULL, &err);
150 if (goundo) g_object_unref (goundo);
151 }
152
153 return err;
154 }
155
156
157 static GnmCell *
sc_sheet_cell_fetch(ScParseState * state,int col,int row)158 sc_sheet_cell_fetch (ScParseState *state, int col, int row)
159 {
160 gboolean err = enlarge (state, col, row);
161
162 if (err) {
163 sc_warning (state, _("The cell in row %i and column %i is beyond "
164 "Gnumeric's maximum sheet size."),
165 row, col);
166
167 return NULL;
168 } else
169 return sheet_cell_fetch (state->sheet, col, row);
170 }
171
172 static gint
sc_colname_to_coords(char const * colname,gint * m)173 sc_colname_to_coords (char const *colname, gint *m)
174 {
175 int mult;
176 int digits = 1;
177
178 g_return_val_if_fail (colname, 0);
179
180 if (!colname || !*colname || !g_ascii_isalpha (*colname))
181 return 0;
182
183 mult = g_ascii_toupper (*colname) - 'A';
184 if (mult < 0 || mult > 25)
185 return 0;
186
187 colname++;
188
189 if (g_ascii_isalpha (*colname)) {
190 int ofs = g_ascii_toupper (*colname) - 'A';
191 if (ofs < 0 || ofs > 25)
192 return 0;
193 mult = ((mult + 1) * 26) + ofs;
194 digits++;
195 }
196
197 *m = mult;
198
199 return digits;
200 }
201
202
203 /* we can't use cellpos_parse b/c it doesn't support 0 bases (A0, B0, ...) */
204 static gboolean
sc_cellname_to_coords(char const * cellname,GnmCellPos * pos)205 sc_cellname_to_coords (char const *cellname, GnmCellPos *pos)
206 {
207 int mult, digits;
208
209 g_return_val_if_fail (cellname, FALSE);
210
211 digits = sc_colname_to_coords (cellname, &mult);
212 if (digits == 0)
213 goto err_out;
214
215 pos->col = mult;
216 cellname += digits;
217
218 /* XXX need to replace this block with strtol+error checking */
219 if (1) {
220 if (!g_ascii_isdigit (*cellname))
221 goto err_out;
222
223 pos->row = atoi (cellname);
224 }
225
226 g_return_val_if_fail (pos->col > -1, FALSE);
227 g_return_val_if_fail (pos->row > -1, FALSE);
228 return TRUE;
229
230 err_out:
231 pos->col = pos->row = -1;
232 return FALSE;
233 }
234
235
236
237 static gboolean
sc_parse_coord_real(ScParseState * state,char const * strdata,GnmCellPos * pos,size_t tmplen)238 sc_parse_coord_real (ScParseState *state, char const *strdata, GnmCellPos *pos,
239 size_t tmplen)
240 {
241 char *tmpstr;
242 GnmNamedExpr *nexpr;
243 GnmParsePos pp;
244 GnmValue *v;
245
246 g_return_val_if_fail (strdata, FALSE);
247
248 tmpstr = g_strndup (strdata, tmplen);
249
250 /* It ought to be a cellref. */
251 if (sc_cellname_to_coords (tmpstr, pos)) {
252 g_return_val_if_fail (pos->col >= 0, FALSE);
253 g_return_val_if_fail (pos->row >= 0, FALSE);
254 g_free (tmpstr);
255 return TRUE;
256 }
257
258 /* But it could be a named expression of the same kind. */
259
260 parse_pos_init (&pp, NULL, state->sheet, 0, 0);
261 nexpr = expr_name_lookup (&pp, tmpstr);
262 if (nexpr && (v = gnm_expr_top_get_range (nexpr->texpr))) {
263 if (VALUE_IS_CELLRANGE (v)) {
264 GnmEvalPos ep;
265 const GnmCellRef *cr = &v->v_range.cell.a;
266
267 eval_pos_init_sheet (&ep, state->sheet);
268 pos->col = gnm_cellref_get_col (cr, &ep);
269 pos->row = gnm_cellref_get_row (cr, &ep);
270 value_release (v);
271 g_free (tmpstr);
272 return TRUE;
273 }
274
275 value_release (v);
276 }
277 g_free (tmpstr);
278 return FALSE;
279 }
280
281 static gboolean
sc_parse_coord(ScParseState * state,char const ** strdata,GnmCellPos * pos)282 sc_parse_coord (ScParseState *state, char const **strdata, GnmCellPos *pos)
283 {
284 char const *s, *eq;
285 gboolean res;
286
287 g_return_val_if_fail (strdata, FALSE);
288 g_return_val_if_fail (*strdata, FALSE);
289
290 s = *strdata;
291
292 eq = strstr (s, " = ");
293 if (!eq)
294 return FALSE;
295
296 res = sc_parse_coord_real (state, s, pos, eq - s);
297
298 if (res) {
299 if ((eq - s + 1 + 3) > (int) strlen (s))
300 res = FALSE;
301 else
302 *strdata = eq + 3;
303 }
304 return res;
305 }
306
307 static void
set_h_align(Sheet * sheet,GnmCellPos const * pos,GnmHAlign ha)308 set_h_align (Sheet *sheet, GnmCellPos const *pos, GnmHAlign ha)
309 {
310 GnmRange r;
311 GnmStyle *style = gnm_style_new ();
312 gnm_style_set_align_h (style, ha);
313 r.start = r.end = *pos;
314 sheet_style_apply_range (sheet, &r, style);
315 }
316
317 static void
sc_parse_set_handle_option(ScParseState * state,char const * option)318 sc_parse_set_handle_option (ScParseState *state, char const *option)
319 {
320 if (g_str_has_prefix (option, "iterations=")) {
321 int it = atoi (option + 11);
322 if (it > 0) {
323 workbook_iteration_enabled (state->sheet->workbook, TRUE);
324 workbook_iteration_max_number (state->sheet->workbook, it);
325 }
326 } else if (g_str_has_prefix (option, "autocalc"))
327 workbook_set_recalcmode (state->sheet->workbook, TRUE);
328 else if (g_str_has_prefix (option, "!autocalc"))
329 workbook_set_recalcmode (state->sheet->workbook, FALSE);
330 }
331
332 static gboolean
sc_parse_set(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * cpos)333 sc_parse_set (ScParseState *state, char const *cmd, char const *str,
334 GnmCellPos const *cpos)
335 {
336 gchar** options = g_strsplit (str, " ", -1), **tmp;
337
338 if (options != NULL)
339 for (tmp = options; *tmp != NULL; tmp++)
340 sc_parse_set_handle_option (state, *tmp);
341 g_strfreev(options);
342
343 /* Most of these settings are not applicable to Gnumeric */
344 return TRUE;
345 }
346
347 static gboolean
sc_parse_goto(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * cpos)348 sc_parse_goto (ScParseState *state, char const *cmd, char const *str,
349 GnmCellPos const *cpos)
350 {
351 GnmCellPos pos = { -1, -1 };
352 gboolean res;
353
354 res = sc_parse_coord_real (state, str, &pos, strlen (str));
355 if (!res)
356 return FALSE;
357
358 SHEET_FOREACH_VIEW(state->sheet, sv,
359 sv_selection_set
360 (sv, &pos, pos.col, pos.row, pos.col, pos.row););
361
362 return TRUE;
363 }
364
365 static gboolean
sc_parse_format_definition(ScParseState * state,char const * cmd,char const * str)366 sc_parse_format_definition (ScParseState *state, char const *cmd, char const *str)
367 {
368 sc_warning (state, "Ignoring column format definition: %s", str);
369 return TRUE;
370 }
371
372 static void
sc_parse_format_set_width(ScParseState * state,int len,int col_from,int col_to)373 sc_parse_format_set_width (ScParseState *state, int len, int col_from, int col_to)
374 {
375 GnmFont *style_font;
376 int width;
377 int col;
378 GnmStyle *mstyle;
379 gboolean err;
380
381 if (len < 1)
382 return;
383
384 err = enlarge (state, col_to, 0);
385 if (err) {
386 sc_warning (state, _("The sheet is wider than "
387 "Gnumeric can handle."));
388 return;
389 }
390
391 mstyle = gnm_style_new_default ();
392 style_font = gnm_style_get_font
393 (mstyle, state->sheet->rendered_values->context);
394 width = PANGO_PIXELS (len * style_font->go.metrics->avg_digit_width) + 4;
395 gnm_style_unref (mstyle);
396
397 for (col = col_from; col <= col_to; col++)
398 sheet_col_set_size_pixels (state->sheet, col, width, TRUE);
399 }
400
401 static void
sc_parse_format_free_precision(ScParseState * state)402 sc_parse_format_free_precision (ScParseState *state)
403 {
404 if (state->precision != NULL)
405 g_array_free (state->precision, TRUE);
406 }
407
408 static int
sc_parse_format_get_precision(ScParseState * state,int col)409 sc_parse_format_get_precision (ScParseState *state, int col)
410 {
411 if (state->precision != NULL &&
412 col < (int)state->precision->len) {
413 return (g_array_index(state->precision, int, col) - 1 );
414 } else return -1;
415 }
416
417 static void
sc_parse_format_save_precision(ScParseState * state,int precision,int col_from,int col_to)418 sc_parse_format_save_precision (ScParseState *state, int precision,
419 int col_from, int col_to)
420 {
421 int col;
422
423 if (state->precision == NULL)
424 state->precision = g_array_new (FALSE, TRUE, sizeof (int));
425
426 if (!(col_to < (int)state->precision->len))
427 state->precision = g_array_set_size (state->precision, col_to + 1);
428
429 for (col = col_from; col <= col_to; col++)
430 g_array_index(state->precision, int, col) = precision + 1;
431 }
432
433 static char *
sc_parse_format_apply_precision(ScParseState * state,char * format,int col)434 sc_parse_format_apply_precision (ScParseState *state, char *format, int col)
435 {
436 if (strchr (format, '&')) {
437 GString* str = g_string_new (format);
438 char *amp = str->str;
439 int off = 0;
440
441 g_free (format);
442 while (NULL != (amp = strchr (str->str + off, '&'))) {
443 off = amp - str->str + 1;
444 if (amp == str->str || *(amp - 1) != '\\') {
445 int p = sc_parse_format_get_precision (state, col);
446 int i;
447 if (p == -1) {
448 p = 0;
449 sc_warning (state, _("Encountered precision dependent format without set precision."));
450 }
451 off--;
452 g_string_erase (str, off, 1);
453 for (i = 0; i < p; i++)
454 g_string_insert_c (str, off, '0');
455 }
456
457 }
458 format = g_string_free (str, FALSE);
459 }
460 return format;
461 }
462
463 static void
sc_parse_format_set_type(ScParseState * state,int type,int col_from,int col_to)464 sc_parse_format_set_type (ScParseState *state, int type, int col_from, int col_to)
465 {
466 char const *o_format = type >= 0 && (size_t)type < state->formats->len
467 ? g_ptr_array_index(state->formats, type)
468 : NULL;
469 int col;
470
471 if (o_format == NULL) {
472 sc_warning (state, _("Column format %i is undefined."), type);
473 return;
474 }
475 for (col = col_from; col <= col_to; col++) {
476 char *fmt = g_strdup (o_format);
477 GOFormat *gfmt;
478 GnmStyle *style;
479 GnmRange range;
480 range_init_cols (&range, state->sheet, col, col);
481 fmt = sc_parse_format_apply_precision (state, fmt, col);
482 gfmt = go_format_new_from_XL (fmt);
483 style = gnm_style_new_default ();
484 gnm_style_set_format (style, gfmt);
485 sheet_style_apply_range (state->sheet, &range, style);
486 /* gnm_style_unref (style); reference has been absorbed */
487 go_format_unref (gfmt);
488 g_free (fmt);
489 }
490
491 }
492
493 static gboolean
sc_parse_format(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * cpos)494 sc_parse_format (ScParseState *state, char const *cmd, char const *str,
495 GnmCellPos const *cpos)
496 {
497 char const *s = str;
498 int col_from = -1, col_to = -1, d;
499 int len = 0, precision = 0, format_type = 0;
500
501 if (g_ascii_isdigit ((gchar) *str))
502 return sc_parse_format_definition (state, cmd, str);
503
504 d = sc_colname_to_coords (s, &col_from);
505
506 if (d == 0)
507 goto cannotparse;
508
509 s += d;
510 if (*s == ':') {
511 s++;
512 d = sc_colname_to_coords (s, &col_to);
513 if (d == 0)
514 goto cannotparse;
515 s += d;
516 } else
517 col_to= col_from;
518 while (*s == ' ')
519 s++;
520
521 d = sscanf(s, "%i %i %i", &len, &precision, &format_type);
522
523 if (d != 3)
524 goto cannotparse;
525
526 if (len > 0)
527 sc_parse_format_set_width (state, len, col_from, col_to);
528 sc_parse_format_save_precision (state, precision, col_from, col_to);
529 sc_parse_format_set_type (state, format_type, col_from, col_to);
530
531 return TRUE;
532 cannotparse:
533 sc_warning (state, "Unable to parse: %s %s", cmd, str);
534 return FALSE;
535 }
536
537 static gboolean
sc_parse_fmt(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * cpos)538 sc_parse_fmt (ScParseState *state, char const *cmd, char const *str,
539 GnmCellPos const *cpos)
540 {
541 char const *s = str, *space;
542 char *fmt;
543 gboolean res;
544 GOFormat *gfmt;
545 GnmStyle *style;
546 GnmCellPos pos = { -1, -1 };
547
548 space = strstr (s, "\"");
549 space--;
550 if (!space)
551 return FALSE;
552
553 res = sc_parse_coord_real (state, s, &pos, space - s);
554 if (!res)
555 return FALSE;
556 s = space + 2;
557 space = strstr (s, "\"");
558 if (!space)
559 return FALSE;
560 fmt = g_strndup (s, space - s);
561 fmt = sc_parse_format_apply_precision (state, fmt, pos.col);
562 gfmt = go_format_new_from_XL (fmt);
563 style = gnm_style_new_default ();
564 gnm_style_set_format (style, gfmt);
565
566 sheet_style_apply_pos (state->sheet, pos.col, pos.row, style);
567 /* gnm_style_unref (style); reference has been absorbed */
568 go_format_unref (gfmt);
569 g_free (fmt);
570
571 return TRUE;
572 }
573
574 static gboolean
sc_parse_label(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * pos)575 sc_parse_label (ScParseState *state, char const *cmd, char const *str,
576 GnmCellPos const *pos)
577 {
578 GnmCell *cell;
579 char *s = NULL, *tmpout;
580 char const *tmpstr;
581 gboolean result = FALSE;
582
583 g_return_val_if_fail (str, FALSE);
584
585 if (*str != '"' || str[1] == 0)
586 goto err_out;
587
588 s = tmpout = g_strdup (str);
589 if (!s)
590 goto err_out;
591
592 tmpstr = str + 1; /* skip leading " */
593 while (*tmpstr) {
594 if (*tmpstr != '\\') {
595 *tmpout = *tmpstr;
596 tmpout++;
597 }
598 tmpstr++;
599 }
600 if (*(tmpstr - 1) != '"')
601 goto err_out;
602 tmpout--;
603 *tmpout = 0;
604
605 cell = sc_sheet_cell_fetch (state, pos->col, pos->row);
606 if (!cell)
607 goto err_out;
608
609 gnm_cell_set_text (cell, s);
610
611 if (strcmp (cmd, "leftstring") == 0)
612 set_h_align (state->sheet, pos, GNM_HALIGN_LEFT);
613 else if (strcmp (cmd, "rightstring") == 0)
614 set_h_align (state->sheet, pos, GNM_HALIGN_RIGHT);
615 #if 0
616 else
617 cmdtype = LABEL;
618 #endif
619
620 result = TRUE;
621 /* fall through */
622
623 err_out:
624 g_free (s);
625 return result;
626 }
627
628
629 #if 0
630 static GSList *
631 sc_parse_cell_name_list (ScParseState *state, char const *cell_name_str,
632 int *error_flag)
633 {
634 char *buf;
635 GSList *cells = NULL;
636 GnmCell *cell;
637 GnmCellPos pos;
638 int i, n;
639
640 g_return_val_if_fail (state->sheet != NULL, NULL);
641 g_return_val_if_fail (IS_SHEET (state->sheet), NULL);
642 g_return_val_if_fail (cell_name_str != NULL, NULL);
643 g_return_val_if_fail (error_flag != NULL, NULL);
644
645 buf = g_malloc (strlen (cell_name_str) + 1);
646 for (i = n = 0; cell_name_str[i]; i++) {
647
648 if ((cell_name_str [i] == ',') ||
649 (!cell_name_str [i])){
650 buf [n] = '\0';
651
652 if (!cellpos_parse (buf, &pos)){
653 *error_flag = 1;
654 g_free (buf);
655 g_slist_free (cells);
656 return NULL;
657 }
658
659 cell = sc_sheet_cell_fetch (state, pos.col, pos.row);
660 if (cell != NULL)
661 cells = g_slist_append (cells, (gpointer) cell);
662 n = 0;
663 } else
664 buf [n++] = cell_name_str [i];
665 }
666
667 *error_flag = 0;
668 g_free (buf);
669 return cells;
670 }
671 #endif
672
673
674 static char const *
sc_row_parse(char const * str,Sheet * sheet,int * res,unsigned char * relative)675 sc_row_parse (char const *str, Sheet *sheet, int *res, unsigned char *relative)
676 {
677 char const *end, *ptr = str;
678 long int row;
679
680 if (!(*relative = (*ptr != '$')))
681 ptr++;
682
683 if (*ptr < '0' || *ptr > '9')
684 return NULL;
685
686 /*
687 * Do not allow letters after the row number. If we did, then
688 * the name "K3P" would lex as the reference K3 followed by the
689 * name "P".
690 */
691 row = strtol (ptr, (char **)&end, 10);
692 if (ptr != end &&
693 !g_unichar_isalnum (g_utf8_get_char (end)) && *end != '_' &&
694 0 <= row && row < gnm_sheet_get_max_rows (sheet)) {
695 *res = row;
696 return end;
697 } else
698 return NULL;
699 }
700
701
702 static char const *
sc_rangeref_parse(GnmRangeRef * res,char const * start,GnmParsePos const * pp,G_GNUC_UNUSED GnmConventions const * convs)703 sc_rangeref_parse (GnmRangeRef *res, char const *start, GnmParsePos const *pp,
704 G_GNUC_UNUSED GnmConventions const *convs)
705 {
706 char const *ptr = start, *tmp1, *tmp2;
707 GnmSheetSize const *ss;
708
709 g_return_val_if_fail (start != NULL, start);
710 g_return_val_if_fail (pp != NULL, start);
711
712 ss = gnm_sheet_get_size (pp->sheet);
713
714 res->a.sheet = NULL;
715 tmp1 = col_parse (ptr, ss, &res->a.col, &res->a.col_relative);
716 if (!tmp1)
717 return start;
718 tmp2 = sc_row_parse (tmp1, pp->sheet, &res->a.row, &res->a.row_relative);
719 if (!tmp2)
720 return start;
721 if (res->a.col_relative)
722 res->a.col -= pp->eval.col;
723 if (res->a.row_relative)
724 res->a.row -= pp->eval.row;
725
726 /* prepare as if it's a singleton, in case we want to fall back */
727 res->b = res->a;
728 if (*tmp2 != ':')
729 return tmp2;
730
731 start = tmp2;
732 tmp1 = col_parse (start+1, ss, &res->b.col, &res->b.col_relative);
733 if (!tmp1)
734 return start;
735 tmp2 = sc_row_parse (tmp1, pp->sheet, &res->b.row, &res->b.row_relative);
736 if (!tmp2)
737 return start;
738 if (res->b.col_relative)
739 res->b.col -= pp->eval.col;
740 if (res->b.row_relative)
741 res->b.row -= pp->eval.row;
742 return tmp2;
743 }
744
745 static GnmExprTop const *
sc_parse_expr(ScParseState * state,const char * str,GnmParsePos * pp)746 sc_parse_expr (ScParseState *state, const char *str, GnmParsePos *pp)
747 {
748 GnmExprTop const *texpr;
749 const char *p1;
750 gboolean infunc = FALSE;
751 GString *exprstr;
752
753 exprstr = g_string_sized_new (500);
754 for (p1 = str; *p1; p1++) {
755 char c = *p1;
756 if (infunc) {
757 infunc = g_ascii_isalpha (c);
758 if (!infunc && *p1 != '(')
759 g_string_append_len (exprstr, "()", 2);
760 g_string_append_c (exprstr, c);
761 } else if (*p1 == '@')
762 infunc = TRUE;
763 else
764 g_string_append_c (exprstr, c);
765 }
766 if (infunc)
767 g_string_append_len (exprstr, "()", 2);
768
769 texpr = gnm_expr_parse_str (exprstr->str, pp,
770 GNM_EXPR_PARSE_DEFAULT,
771 state->convs, NULL);
772 g_string_free (exprstr, TRUE);
773
774 return texpr;
775 }
776
777
778 static gboolean
sc_parse_let(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * pos)779 sc_parse_let (ScParseState *state, char const *cmd, char const *str,
780 GnmCellPos const *pos)
781 {
782 GnmExprTop const *texpr;
783 GnmCell *cell;
784 GnmParsePos pp;
785 GnmValue const *v;
786
787 g_return_val_if_fail (cmd, FALSE);
788 g_return_val_if_fail (str, FALSE);
789
790 cell = sc_sheet_cell_fetch (state, pos->col, pos->row);
791 if (!cell)
792 return FALSE;
793
794 texpr = sc_parse_expr (state, str,
795 parse_pos_init_cell (&pp, cell));
796
797 if (!texpr) {
798 sc_warning (state, _("Unable to parse cmd='%s', str='%s', col=%d, row=%d."),
799 cmd, str, pos->col, pos->row);
800 return TRUE;
801 }
802
803 v = gnm_expr_top_get_constant (texpr);
804 if (v && VALUE_IS_NUMBER (v)) {
805 gnm_cell_set_value (cell, value_dup (v));
806 } else {
807 gnm_cell_set_expr (cell, texpr);
808 cell_queue_recalc (cell);
809 }
810
811 if (texpr) gnm_expr_top_unref (texpr);
812 return TRUE;
813 }
814
815 static gboolean
sc_parse_define(ScParseState * state,char const * cmd,char const * str,GnmCellPos const * dummy_pos)816 sc_parse_define (ScParseState *state, char const *cmd, char const *str,
817 GnmCellPos const *dummy_pos)
818 {
819 GnmParsePos pp;
820 GString *name = g_string_new (NULL);
821 char *errstr = NULL;
822 GnmNamedExpr *nexpr;
823 gboolean res = FALSE;
824 GnmExprTop const *texpr;
825
826 str = go_strunescape (name, str);
827 if (!str)
828 goto out;
829 while (g_ascii_isspace (*str))
830 str++;
831 texpr = sc_parse_expr (state, str,
832 parse_pos_init (&pp, NULL, state->sheet, 0, 0));
833 if (!texpr) {
834 sc_warning (state, "Unable to parse cmd='%s', str='%s'.", cmd, str);
835 goto out;
836 }
837
838 nexpr = expr_name_add (&pp, name->str, texpr, &errstr, TRUE, NULL);
839 if (!nexpr)
840 goto out;
841
842 res = TRUE;
843
844 out:
845 g_string_free (name, TRUE);
846 g_free (errstr);
847 return res;
848 }
849
850 typedef struct {
851 char const *name;
852 int namelen;
853 gboolean (*handler) (ScParseState *state, char const *name,
854 char const *str, GnmCellPos const *pos);
855 gboolean have_coord;
856 } sc_cmd_t;
857
858 static sc_cmd_t const sc_cmd_list[] = {
859 { "leftstring", 10, sc_parse_label, TRUE },
860 { "rightstring", 11, sc_parse_label, TRUE },
861 { "label", 5, sc_parse_label, TRUE },
862 { "let", 3, sc_parse_let, TRUE },
863 { "define", 6, sc_parse_define, FALSE },
864 { "fmt", 3, sc_parse_fmt, FALSE },
865 { "format", 6, sc_parse_format, FALSE },
866 { "set", 3, sc_parse_set, FALSE },
867 { "goto", 4, sc_parse_goto, FALSE },
868 { NULL, 0, NULL, 0 },
869 };
870
871
872 static gboolean
sc_parse_line(ScParseState * state,char * buf)873 sc_parse_line (ScParseState *state, char *buf)
874 {
875 char const *space;
876 int i, cmdlen;
877 sc_cmd_t const *cmd;
878
879 g_return_val_if_fail (state, FALSE);
880 g_return_val_if_fail (state->sheet, FALSE);
881 g_return_val_if_fail (buf, FALSE);
882
883 for (space = buf; g_ascii_isalnum (*space) || *space == '_'; space++)
884 ; /* Nothing */
885 if (*space == 0)
886 return TRUE;
887 cmdlen = space - buf;
888 while (*space == ' ')
889 space++;
890
891 for (i = 0 ; sc_cmd_list[i].name != NULL ; ++i) {
892 cmd = &sc_cmd_list [i];
893 if (cmd->namelen == cmdlen &&
894 strncmp (cmd->name, buf, cmdlen) == 0) {
895 GnmCellPos pos = { -1, -1 };
896 char const *strdata = space;
897
898 if (cmd->have_coord) {
899 if (!sc_parse_coord (state, &strdata, &pos)) {
900 sc_warning (state, "Cannot parse %s\n",
901 buf);
902 return FALSE;
903 }
904 }
905
906 cmd->handler (state, cmd->name, strdata, &pos);
907 return TRUE;
908 }
909 }
910
911 sc_warning (state, "Unhandled directive: '%-.*s'",
912 cmdlen, buf);
913 return TRUE;
914 }
915
916
917 static GOErrorInfo *
sc_parse_sheet(ScParseState * state)918 sc_parse_sheet (ScParseState *state)
919 {
920 unsigned char *data;
921 GOErrorInfo *res = NULL;
922
923 while ((data = gsf_input_textline_ascii_gets (state->textline)) != NULL) {
924 char *utf8data;
925
926 g_strchomp (data);
927 utf8data = g_convert_with_iconv (data, -1, state->converter,
928 NULL, NULL, NULL);
929
930 if (g_ascii_isalpha (*data) && !sc_parse_line (state, utf8data)) {
931 if (!res)
932 res = go_error_info_new_str
933 (_("Error parsing line"));
934 }
935
936 g_free (utf8data);
937 }
938
939 return res;
940 }
941
942 static GnmExpr const *
sc_func_map_in(GnmConventions const * conv,Workbook * scope,char const * name,GnmExprList * args)943 sc_func_map_in (GnmConventions const *conv, Workbook *scope,
944 char const *name, GnmExprList *args)
945 {
946 static struct {
947 char const *sc_name;
948 char const *gnm_name;
949 } const sc_func_renames[] = {
950 { "AVG", "AVERAGE" },
951 { "DTR", "RADIANS" },
952 { "FABS", "ABS" },
953 { "COLS", "COLUMNS" },
954 { "AVG", "AVERAGE" },
955 { "POW", "POWER" },
956 { "PROD", "PRODUCT" },
957 { "RND", "ROUND" },
958 { "RTD", "DEGREES" },
959 { "STDDEV", "STDEV" },
960 { "STON", "INT" },
961 { "SUBSTR", "MID" },
962 { NULL, NULL }
963 };
964 static GHashTable *namemap = NULL;
965
966 GnmFunc *f;
967 char const *new_name;
968 int i;
969
970 if (NULL == namemap) {
971 namemap = g_hash_table_new (go_ascii_strcase_hash,
972 go_ascii_strcase_equal);
973 for (i = 0; sc_func_renames[i].sc_name; i++)
974 g_hash_table_insert (namemap,
975 (gchar *) sc_func_renames[i].sc_name,
976 (gchar *) sc_func_renames[i].gnm_name);
977 }
978
979 if (NULL != namemap &&
980 NULL != (new_name = g_hash_table_lookup (namemap, name)))
981 name = new_name;
982 if (NULL == (f = gnm_func_lookup (name, scope)))
983 f = gnm_func_add_placeholder (scope, name, "");
984 return gnm_expr_new_funcall (f, args);
985 }
986
987 static GnmConventions *
sc_conventions(void)988 sc_conventions (void)
989 {
990 GnmConventions *conv = gnm_conventions_new ();
991
992 conv->decimal_sep_dot = TRUE;
993 conv->range_sep_colon = TRUE;
994 conv->input.range_ref = sc_rangeref_parse;
995 conv->input.func = sc_func_map_in;
996
997 return conv;
998 }
999
1000 static void
sc_format_free(gpointer data,gpointer user_data)1001 sc_format_free (gpointer data, gpointer user_data)
1002 {
1003 g_free (data);
1004 }
1005
1006 void
sc_file_open(GOFileOpener const * fo,GOIOContext * io_context,WorkbookView * wb_view,GsfInput * input)1007 sc_file_open (GOFileOpener const *fo, GOIOContext *io_context,
1008 WorkbookView *wb_view, GsfInput *input)
1009 {
1010 Workbook *wb;
1011 char *name;
1012 GOErrorInfo *error;
1013 ScParseState state;
1014
1015 wb = wb_view_get_workbook (wb_view);
1016 name = workbook_sheet_get_free_name (wb, "SC", FALSE, TRUE);
1017 state.sheet = sheet_new (wb, name, 256, 65536);
1018 g_free (name);
1019 workbook_sheet_attach (wb, state.sheet);
1020
1021 /* This should probably come from import dialog. */
1022 state.converter = g_iconv_open ("UTF-8", "ISO-8859-1");
1023
1024 state.convs = sc_conventions ();
1025 state.context = io_context;
1026 state.last_error = NULL;
1027 state.precision = NULL;
1028 state.formats = g_ptr_array_sized_new (10);
1029 g_ptr_array_add (state.formats, g_strdup ("#.&")); /* 0 */
1030 g_ptr_array_add (state.formats, g_strdup ("0.&E+00")); /* 1 */
1031 g_ptr_array_add (state.formats, g_strdup ("##0.&E+00")); /* 2 */
1032 g_ptr_array_add (state.formats, g_strdup ("[$-f8f2]m/d/yy")); /* 3 */
1033 g_ptr_array_add (state.formats, g_strdup ("[$-f800]dddd, mmmm dd, yyyy")); /* 4 */
1034 g_ptr_array_set_size (state.formats, 10);
1035
1036 state.textline = (GsfInputTextline *) gsf_input_textline_new (input);
1037 error = sc_parse_sheet (&state);
1038 if (error != NULL) {
1039 workbook_sheet_delete (state.sheet);
1040 go_io_error_info_set (io_context, error);
1041 }
1042 g_object_unref (state.textline);
1043 g_iconv_close (state.converter);
1044 gnm_conventions_unref (state.convs);
1045 g_free (state.last_error);
1046 sc_parse_format_free_precision (&state);
1047
1048 /*In glib 2.22 or later we could use g_ptr_array_set_free_func */
1049 g_ptr_array_foreach (state.formats, (GFunc) sc_format_free, NULL);
1050 g_ptr_array_unref (state.formats);
1051 }
1052
1053
1054 static guint8 const signature[] =
1055 "# This data file was generated by the Spreadsheet Calculator.";
1056
1057 gboolean
sc_file_probe(GOFileOpener const * fo,GsfInput * input,GOFileProbeLevel pl)1058 sc_file_probe (GOFileOpener const *fo, GsfInput *input,
1059 GOFileProbeLevel pl)
1060 {
1061 char const *header = NULL;
1062
1063 if (!gsf_input_seek (input, 0, G_SEEK_SET))
1064 header = gsf_input_read (input, sizeof (signature)-1, NULL);
1065 return header != NULL &&
1066 memcmp (header, signature, sizeof (signature)-1) == 0;
1067 }
1068
1069
1070 /*
1071 * http://www.thule.no/haynie/cpumods/a2620/docs/commrc.sc.txt:
1072 * format B 20 2
1073 *
1074 * http://www.mcs.kent.edu/system/documentation/xspread/demo_func
1075 * format A 15 2 0
1076 * goto C7
1077 *
1078 */
1079