Fixed typos
[gnumeric.git] / plugins / sc / sc.c
blobfb6c81a2fa930276b8824d9c2264326b94dcb4ed
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * sc.c - file import of SC/xspread files
4 * Copyright 1999 Jeff Garzik <jgarzik@mandrakesoft.com>
5 * Copyright (C) 2010 Andreas J. Guelzow <aguelzow@pyrshep.ca> All Rights Reserved
7 * With some code from sylk.c
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) version 3.
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
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
26 #include <gnumeric.h>
27 #include <string.h>
28 #include <goffice/goffice.h>
29 #include <gnm-plugin.h>
30 #include "sheet-style.h"
31 #include "workbook-view.h"
32 #include "workbook.h"
33 #include "parse-util.h"
34 #include "value.h"
35 #include "cell.h"
36 #include "ranges.h"
37 #include "style.h"
38 #include "sheet.h"
39 #include "expr.h"
40 #include "expr-name.h"
41 #include "func.h"
42 #include "sheet-view.h"
43 #include "selection.h"
44 #include "rendered-value.h"
45 #include "gnm-style-impl.h"
47 #include <gsf/gsf-input.h>
48 #include <gsf/gsf-input-textline.h>
49 #include <string.h>
50 #include <math.h>
51 #include <stdlib.h>
53 GNM_PLUGIN_MODULE_HEADER;
55 gboolean sc_file_probe (GOFileOpener const *fo, GsfInput *input,
56 GOFileProbeLevel pl);
57 void sc_file_open (GOFileOpener const *fo, GOIOContext *io_context,
58 WorkbookView *wb_view, GsfInput *input);
60 typedef struct {
61 GsfInputTextline *textline;
62 Sheet *sheet;
63 GIConv converter;
64 GnmConventions *convs;
65 GOIOContext *context; /* The IOcontext managing things */
66 char *last_error;
67 GArray *precision;
68 GPtrArray *formats;
69 } ScParseState;
71 typedef enum {
72 LABEL,
73 LEFTSTRING,
74 RIGHTSTRING
75 } sc_string_cmd_t;
78 static GOErrorInfo *sc_go_error_info_new_vprintf (GOSeverity severity,
79 char const *msg_format, ...)
80 G_GNUC_PRINTF (2, 3);
82 static GOErrorInfo *
83 sc_go_error_info_new_vprintf (GOSeverity severity,
84 char const *msg_format, ...)
86 va_list args;
87 GOErrorInfo *ei;
89 va_start (args, msg_format);
90 ei = go_error_info_new_vprintf (severity, msg_format, args);
91 va_end (args);
93 return ei;
96 static gboolean sc_warning (ScParseState *state, char const *fmt, ...)
97 G_GNUC_PRINTF (2, 3);
99 static gboolean
100 sc_warning (ScParseState *state, char const *fmt, ...)
102 char *msg;
103 char *detail;
104 va_list args;
106 va_start (args, fmt);
107 detail = g_strdup_vprintf (fmt, args);
108 va_end (args);
110 if (IS_SHEET (state->sheet))
111 msg = g_strdup_printf (_("On worksheet %s:"),state->sheet->name_quoted);
112 else
113 msg = g_strdup (_("General SC import error"));
115 if (0 != go_str_compare (msg, state->last_error)) {
116 GOErrorInfo *ei = sc_go_error_info_new_vprintf
117 (GO_WARNING, "%s", msg);
119 go_io_error_info_set (state->context, ei);
120 g_free (state->last_error);
121 state->last_error = msg;
122 } else
123 g_free (msg);
125 go_error_info_add_details
126 (state->context->info->data,
127 sc_go_error_info_new_vprintf (GO_WARNING, "%s", detail));
129 g_free (detail);
131 return FALSE; /* convenience */
134 static gboolean
135 enlarge (ScParseState *state, int col, int row)
137 GnmSheetSize const *size = gnm_sheet_get_size (state->sheet);
138 gboolean err = FALSE;
140 if (col >= size->max_cols
141 || row >= size->max_rows) {
142 GOUndo * goundo;
143 int cols_needed = (col >= size->max_cols) ? col + 1
144 : size->max_cols;
145 int rows_needed = (row >= size->max_rows) ? row + 1
146 : size->max_rows;
147 gnm_sheet_suggest_size (&cols_needed, &rows_needed);
149 goundo = gnm_sheet_resize
150 (state->sheet, cols_needed, rows_needed, NULL, &err);
151 if (goundo) g_object_unref (goundo);
154 return err;
158 static GnmCell *
159 sc_sheet_cell_fetch (ScParseState *state, int col, int row)
161 gboolean err = enlarge (state, col, row);
163 if (err) {
164 sc_warning (state, _("The cell in row %i and column %i is beyond "
165 "Gnumeric's maximum sheet size."),
166 row, col);
168 return NULL;
169 } else
170 return sheet_cell_fetch (state->sheet, col, row);
173 static gint
174 sc_colname_to_coords (char const *colname, gint *m)
176 int mult;
177 int digits = 1;
179 g_return_val_if_fail (colname, 0);
181 if (!colname || !*colname || !g_ascii_isalpha (*colname))
182 return 0;
184 mult = g_ascii_toupper (*colname) - 'A';
185 if (mult < 0 || mult > 25)
186 return 0;
188 colname++;
190 if (g_ascii_isalpha (*colname)) {
191 int ofs = g_ascii_toupper (*colname) - 'A';
192 if (ofs < 0 || ofs > 25)
193 return 0;
194 mult = ((mult + 1) * 26) + ofs;
195 digits++;
198 *m = mult;
200 return digits;
204 /* we can't use cellpos_parse b/c it doesn't support 0 bases (A0, B0, ...) */
205 static gboolean
206 sc_cellname_to_coords (char const *cellname, GnmCellPos *pos)
208 int mult, digits;
210 g_return_val_if_fail (cellname, FALSE);
212 digits = sc_colname_to_coords (cellname, &mult);
213 if (digits == 0)
214 goto err_out;
216 pos->col = mult;
217 cellname += digits;
219 /* XXX need to replace this block with strtol+error checking */
220 if (1) {
221 if (!g_ascii_isdigit (*cellname))
222 goto err_out;
224 pos->row = atoi (cellname);
227 g_return_val_if_fail (pos->col > -1, FALSE);
228 g_return_val_if_fail (pos->row > -1, FALSE);
229 return TRUE;
231 err_out:
232 pos->col = pos->row = -1;
233 return FALSE;
238 static gboolean
239 sc_parse_coord_real (ScParseState *state, char const *strdata, GnmCellPos *pos,
240 size_t tmplen)
242 char *tmpstr;
243 GnmNamedExpr *nexpr;
244 GnmParsePos pp;
245 GnmValue *v;
247 g_return_val_if_fail (strdata, FALSE);
249 tmpstr = g_strndup (strdata, tmplen);
251 /* It ought to be a cellref. */
252 if (sc_cellname_to_coords (tmpstr, pos)) {
253 g_return_val_if_fail (pos->col >= 0, FALSE);
254 g_return_val_if_fail (pos->row >= 0, FALSE);
255 g_free (tmpstr);
256 return TRUE;
259 /* But it could be a named expression of the same kind. */
261 parse_pos_init (&pp, NULL, state->sheet, 0, 0);
262 nexpr = expr_name_lookup (&pp, tmpstr);
263 if (nexpr && (v = gnm_expr_top_get_range (nexpr->texpr))) {
264 if (VALUE_IS_CELLRANGE (v)) {
265 GnmEvalPos ep;
266 const GnmCellRef *cr = &v->v_range.cell.a;
268 eval_pos_init_sheet (&ep, state->sheet);
269 pos->col = gnm_cellref_get_col (cr, &ep);
270 pos->row = gnm_cellref_get_row (cr, &ep);
271 value_release (v);
272 g_free (tmpstr);
273 return TRUE;
276 value_release (v);
278 g_free (tmpstr);
279 return FALSE;
282 static gboolean
283 sc_parse_coord (ScParseState *state, char const **strdata, GnmCellPos *pos)
285 char const *s, *eq;
286 gboolean res;
288 g_return_val_if_fail (strdata, FALSE);
289 g_return_val_if_fail (*strdata, FALSE);
291 s = *strdata;
293 eq = strstr (s, " = ");
294 if (!eq)
295 return FALSE;
297 res = sc_parse_coord_real (state, s, pos, eq - s);
299 if (res) {
300 if ((eq - s + 1 + 3) > (int) strlen (s))
301 res = FALSE;
302 else
303 *strdata = eq + 3;
305 return res;
308 static void
309 set_h_align (Sheet *sheet, GnmCellPos const *pos, GnmHAlign ha)
311 GnmRange r;
312 GnmStyle *style = gnm_style_new ();
313 gnm_style_set_align_h (style, ha);
314 r.start = r.end = *pos;
315 sheet_style_apply_range (sheet, &r, style);
318 static void
319 sc_parse_set_handle_option (ScParseState *state, char const *option)
321 if (g_str_has_prefix (option, "iterations=")) {
322 int it = atoi (option + 11);
323 if (it > 0) {
324 workbook_iteration_enabled (state->sheet->workbook, TRUE);
325 workbook_iteration_max_number (state->sheet->workbook, it);
327 } else if (g_str_has_prefix (option, "autocalc"))
328 workbook_set_recalcmode (state->sheet->workbook, TRUE);
329 else if (g_str_has_prefix (option, "!autocalc"))
330 workbook_set_recalcmode (state->sheet->workbook, FALSE);
333 static gboolean
334 sc_parse_set (ScParseState *state, char const *cmd, char const *str,
335 GnmCellPos const *cpos)
337 gchar** options = g_strsplit (str, " ", -1), **tmp;
339 if (options != NULL)
340 for (tmp = options; *tmp != NULL; tmp++)
341 sc_parse_set_handle_option (state, *tmp);
342 g_strfreev(options);
344 /* Most of these settings are not applicable to Gnumeric */
345 return TRUE;
348 static gboolean
349 sc_parse_goto (ScParseState *state, char const *cmd, char const *str,
350 GnmCellPos const *cpos)
352 GnmCellPos pos = { -1, -1 };
353 gboolean res;
355 res = sc_parse_coord_real (state, str, &pos, strlen (str));
356 if (!res)
357 return FALSE;
359 SHEET_FOREACH_VIEW(state->sheet, sv,
360 sv_selection_set
361 (sv, &pos, pos.col, pos.row, pos.col, pos.row););
363 return TRUE;
366 static gboolean
367 sc_parse_format_definition (ScParseState *state, char const *cmd, char const *str)
369 sc_warning (state, "Ignoring column format definition: %s", str);
370 return TRUE;
373 static void
374 sc_parse_format_set_width (ScParseState *state, int len, int col_from, int col_to)
376 GnmFont *style_font;
377 int width;
378 int col;
379 GnmStyle *mstyle;
380 gboolean err;
382 if (len < 1)
383 return;
385 err = enlarge (state, col_to, 0);
386 if (err) {
387 sc_warning (state, _("The sheet is wider than "
388 "Gnumeric can handle."));
389 return;
392 mstyle = gnm_style_new_default ();
393 style_font = gnm_style_get_font
394 (mstyle, state->sheet->rendered_values->context);
395 width = PANGO_PIXELS (len * style_font->go.metrics->avg_digit_width) + 4;
396 gnm_style_unref (mstyle);
398 for (col = col_from; col <= col_to; col++)
399 sheet_col_set_size_pixels (state->sheet, col, width, TRUE);
402 static void
403 sc_parse_format_free_precision (ScParseState *state)
405 if (state->precision != NULL)
406 g_array_free (state->precision, TRUE);
409 static int
410 sc_parse_format_get_precision (ScParseState *state, int col)
412 if (state->precision != NULL &&
413 col < (int)state->precision->len) {
414 return (g_array_index(state->precision, int, col) - 1 );
415 } else return -1;
418 static void
419 sc_parse_format_save_precision (ScParseState *state, int precision,
420 int col_from, int col_to)
422 int col;
424 if (state->precision == NULL)
425 state->precision = g_array_new (FALSE, TRUE, sizeof (int));
427 if (!(col_to < (int)state->precision->len))
428 state->precision = g_array_set_size (state->precision, col_to + 1);
430 for (col = col_from; col <= col_to; col++)
431 g_array_index(state->precision, int, col) = precision + 1;
434 static char *
435 sc_parse_format_apply_precision (ScParseState *state, char *format, int col)
437 if (strchr (format, '&')) {
438 GString* str = g_string_new (format);
439 char *amp = str->str;
440 int off = 0;
442 g_free (format);
443 while (NULL != (amp = strchr (str->str + off, '&'))) {
444 off = amp - str->str + 1;
445 if (amp == str->str || *(amp - 1) != '\\') {
446 int p = sc_parse_format_get_precision (state, col);
447 int i;
448 if (p == -1) {
449 p = 0;
450 sc_warning (state, _("Encountered precision dependent format without set precision."));
452 off--;
453 g_string_erase (str, off, 1);
454 for (i = 0; i < p; i++)
455 g_string_insert_c (str, off, '0');
459 format = g_string_free (str, FALSE);
461 return format;
464 static void
465 sc_parse_format_set_type (ScParseState *state, int type, int col_from, int col_to)
467 char const *o_format = type >= 0 && (size_t)type < state->formats->len
468 ? g_ptr_array_index(state->formats, type)
469 : NULL;
470 int col;
472 if (o_format == NULL) {
473 sc_warning (state, _("Column format %i is undefined."), type);
474 return;
476 for (col = col_from; col <= col_to; col++) {
477 char *fmt = g_strdup (o_format);
478 GOFormat *gfmt;
479 GnmStyle *style;
480 GnmRange range;
481 range_init_cols (&range, state->sheet, col, col);
482 fmt = sc_parse_format_apply_precision (state, fmt, col);
483 gfmt = go_format_new_from_XL (fmt);
484 style = gnm_style_new_default ();
485 gnm_style_set_format (style, gfmt);
486 sheet_style_apply_range (state->sheet, &range, style);
487 /* gnm_style_unref (style); reference has been absorbed */
488 go_format_unref (gfmt);
489 g_free (fmt);
494 static gboolean
495 sc_parse_format (ScParseState *state, char const *cmd, char const *str,
496 GnmCellPos const *cpos)
498 char const *s = str;
499 int col_from = -1, col_to = -1, d;
500 int len = 0, precision = 0, format_type = 0;
502 if (g_ascii_isdigit ((gchar) *str))
503 return sc_parse_format_definition (state, cmd, str);
505 d = sc_colname_to_coords (s, &col_from);
507 if (d == 0)
508 goto cannotparse;
510 s += d;
511 if (*s == ':') {
512 s++;
513 d = sc_colname_to_coords (s, &col_to);
514 if (d == 0)
515 goto cannotparse;
516 s += d;
517 } else
518 col_to= col_from;
519 while (*s == ' ')
520 s++;
522 d = sscanf(s, "%i %i %i", &len, &precision, &format_type);
524 if (d != 3)
525 goto cannotparse;
527 if (len > 0)
528 sc_parse_format_set_width (state, len, col_from, col_to);
529 sc_parse_format_save_precision (state, precision, col_from, col_to);
530 sc_parse_format_set_type (state, format_type, col_from, col_to);
532 return TRUE;
533 cannotparse:
534 sc_warning (state, "Unable to parse: %s %s", cmd, str);
535 return FALSE;
538 static gboolean
539 sc_parse_fmt (ScParseState *state, char const *cmd, char const *str,
540 GnmCellPos const *cpos)
542 char const *s = str, *space;
543 char *fmt;
544 gboolean res;
545 GOFormat *gfmt;
546 GnmStyle *style;
547 GnmCellPos pos = { -1, -1 };
549 space = strstr (s, "\"");
550 space--;
551 if (!space)
552 return FALSE;
554 res = sc_parse_coord_real (state, s, &pos, space - s);
555 if (!res)
556 return FALSE;
557 s = space + 2;
558 space = strstr (s, "\"");
559 if (!space)
560 return FALSE;
561 fmt = g_strndup (s, space - s);
562 fmt = sc_parse_format_apply_precision (state, fmt, pos.col);
563 gfmt = go_format_new_from_XL (fmt);
564 style = gnm_style_new_default ();
565 gnm_style_set_format (style, gfmt);
567 sheet_style_apply_pos (state->sheet, pos.col, pos.row, style);
568 /* gnm_style_unref (style); reference has been absorbed */
569 go_format_unref (gfmt);
570 g_free (fmt);
572 return TRUE;
575 static gboolean
576 sc_parse_label (ScParseState *state, char const *cmd, char const *str,
577 GnmCellPos const *pos)
579 GnmCell *cell;
580 char *s = NULL, *tmpout;
581 char const *tmpstr;
582 gboolean result = FALSE;
584 g_return_val_if_fail (str, FALSE);
586 if (*str != '"' || str[1] == 0)
587 goto err_out;
589 s = tmpout = g_strdup (str);
590 if (!s)
591 goto err_out;
593 tmpstr = str + 1; /* skip leading " */
594 while (*tmpstr) {
595 if (*tmpstr != '\\') {
596 *tmpout = *tmpstr;
597 tmpout++;
599 tmpstr++;
601 if (*(tmpstr - 1) != '"')
602 goto err_out;
603 tmpout--;
604 *tmpout = 0;
606 cell = sc_sheet_cell_fetch (state, pos->col, pos->row);
607 if (!cell)
608 goto err_out;
610 gnm_cell_set_text (cell, s);
612 if (strcmp (cmd, "leftstring") == 0)
613 set_h_align (state->sheet, pos, GNM_HALIGN_LEFT);
614 else if (strcmp (cmd, "rightstring") == 0)
615 set_h_align (state->sheet, pos, GNM_HALIGN_RIGHT);
616 #if 0
617 else
618 cmdtype = LABEL;
619 #endif
621 result = TRUE;
622 /* fall through */
624 err_out:
625 g_free (s);
626 return result;
630 #if 0
631 static GSList *
632 sc_parse_cell_name_list (ScParseState *state, char const *cell_name_str,
633 int *error_flag)
635 char *buf;
636 GSList *cells = NULL;
637 GnmCell *cell;
638 GnmCellPos pos;
639 int i, n;
641 g_return_val_if_fail (state->sheet != NULL, NULL);
642 g_return_val_if_fail (IS_SHEET (state->sheet), NULL);
643 g_return_val_if_fail (cell_name_str != NULL, NULL);
644 g_return_val_if_fail (error_flag != NULL, NULL);
646 buf = g_malloc (strlen (cell_name_str) + 1);
647 for (i = n = 0; cell_name_str[i]; i++) {
649 if ((cell_name_str [i] == ',') ||
650 (!cell_name_str [i])){
651 buf [n] = '\0';
653 if (!cellpos_parse (buf, &pos)){
654 *error_flag = 1;
655 g_free (buf);
656 g_slist_free (cells);
657 return NULL;
660 cell = sc_sheet_cell_fetch (state, pos.col, pos.row);
661 if (cell != NULL)
662 cells = g_slist_append (cells, (gpointer) cell);
663 n = 0;
664 } else
665 buf [n++] = cell_name_str [i];
668 *error_flag = 0;
669 g_free (buf);
670 return cells;
672 #endif
675 static char const *
676 sc_row_parse (char const *str, Sheet *sheet, int *res, unsigned char *relative)
678 char const *end, *ptr = str;
679 long int row;
681 if (!(*relative = (*ptr != '$')))
682 ptr++;
684 if (*ptr < '0' || *ptr > '9')
685 return NULL;
688 * Do not allow letters after the row number. If we did, then
689 * the name "K3P" would lex as the reference K3 followed by the
690 * name "P".
692 row = strtol (ptr, (char **)&end, 10);
693 if (ptr != end &&
694 !g_unichar_isalnum (g_utf8_get_char (end)) && *end != '_' &&
695 0 <= row && row < gnm_sheet_get_max_rows (sheet)) {
696 *res = row;
697 return end;
698 } else
699 return NULL;
703 static char const *
704 sc_rangeref_parse (GnmRangeRef *res, char const *start, GnmParsePos const *pp,
705 G_GNUC_UNUSED GnmConventions const *convs)
707 char const *ptr = start, *tmp1, *tmp2;
708 GnmSheetSize const *ss;
710 g_return_val_if_fail (start != NULL, start);
711 g_return_val_if_fail (pp != NULL, start);
713 ss = gnm_sheet_get_size (pp->sheet);
715 res->a.sheet = NULL;
716 tmp1 = col_parse (ptr, ss, &res->a.col, &res->a.col_relative);
717 if (!tmp1)
718 return start;
719 tmp2 = sc_row_parse (tmp1, pp->sheet, &res->a.row, &res->a.row_relative);
720 if (!tmp2)
721 return start;
722 if (res->a.col_relative)
723 res->a.col -= pp->eval.col;
724 if (res->a.row_relative)
725 res->a.row -= pp->eval.row;
727 /* prepare as if it's a singleton, in case we want to fall back */
728 res->b = res->a;
729 if (*tmp2 != ':')
730 return tmp2;
732 start = tmp2;
733 tmp1 = col_parse (start+1, ss, &res->b.col, &res->b.col_relative);
734 if (!tmp1)
735 return start;
736 tmp2 = sc_row_parse (tmp1, pp->sheet, &res->b.row, &res->b.row_relative);
737 if (!tmp2)
738 return start;
739 if (res->b.col_relative)
740 res->b.col -= pp->eval.col;
741 if (res->b.row_relative)
742 res->b.row -= pp->eval.row;
743 return tmp2;
746 static GnmExprTop const *
747 sc_parse_expr (ScParseState *state, const char *str, GnmParsePos *pp)
749 GnmExprTop const *texpr;
750 const char *p1;
751 gboolean infunc = FALSE;
752 GString *exprstr;
754 exprstr = g_string_sized_new (500);
755 for (p1 = str; *p1; p1++) {
756 char c = *p1;
757 if (infunc) {
758 infunc = g_ascii_isalpha (c);
759 if (!infunc && *p1 != '(')
760 g_string_append_len (exprstr, "()", 2);
761 g_string_append_c (exprstr, c);
762 } else if (*p1 == '@')
763 infunc = TRUE;
764 else
765 g_string_append_c (exprstr, c);
767 if (infunc)
768 g_string_append_len (exprstr, "()", 2);
770 texpr = gnm_expr_parse_str (exprstr->str, pp,
771 GNM_EXPR_PARSE_DEFAULT,
772 state->convs, NULL);
773 g_string_free (exprstr, TRUE);
775 return texpr;
779 static gboolean
780 sc_parse_let (ScParseState *state, char const *cmd, char const *str,
781 GnmCellPos const *pos)
783 GnmExprTop const *texpr;
784 GnmCell *cell;
785 GnmParsePos pp;
786 GnmValue const *v;
788 g_return_val_if_fail (cmd, FALSE);
789 g_return_val_if_fail (str, FALSE);
791 cell = sc_sheet_cell_fetch (state, pos->col, pos->row);
792 if (!cell)
793 return FALSE;
795 texpr = sc_parse_expr (state, str,
796 parse_pos_init_cell (&pp, cell));
798 if (!texpr) {
799 sc_warning (state, _("Unable to parse cmd='%s', str='%s', col=%d, row=%d."),
800 cmd, str, pos->col, pos->row);
801 return TRUE;
804 v = gnm_expr_top_get_constant (texpr);
805 if (v && VALUE_IS_NUMBER (v)) {
806 gnm_cell_set_value (cell, value_dup (v));
807 } else {
808 gnm_cell_set_expr (cell, texpr);
809 cell_queue_recalc (cell);
812 if (texpr) gnm_expr_top_unref (texpr);
813 return TRUE;
816 static gboolean
817 sc_parse_define (ScParseState *state, char const *cmd, char const *str,
818 GnmCellPos const *dummy_pos)
820 GnmParsePos pp;
821 GString *name = g_string_new (NULL);
822 char *errstr = NULL;
823 GnmNamedExpr *nexpr;
824 gboolean res = FALSE;
825 GnmExprTop const *texpr;
827 str = go_strunescape (name, str);
828 if (!str)
829 goto out;
830 while (g_ascii_isspace (*str))
831 str++;
832 texpr = sc_parse_expr (state, str,
833 parse_pos_init (&pp, NULL, state->sheet, 0, 0));
834 if (!texpr) {
835 sc_warning (state, "Unable to parse cmd='%s', str='%s'.", cmd, str);
836 goto out;
839 nexpr = expr_name_add (&pp, name->str, texpr, &errstr, TRUE, NULL);
840 if (!nexpr)
841 goto out;
843 res = TRUE;
845 out:
846 g_string_free (name, TRUE);
847 g_free (errstr);
848 return res;
851 typedef struct {
852 char const *name;
853 int namelen;
854 gboolean (*handler) (ScParseState *state, char const *name,
855 char const *str, GnmCellPos const *pos);
856 gboolean have_coord;
857 } sc_cmd_t;
859 static sc_cmd_t const sc_cmd_list[] = {
860 { "leftstring", 10, sc_parse_label, TRUE },
861 { "rightstring", 11, sc_parse_label, TRUE },
862 { "label", 5, sc_parse_label, TRUE },
863 { "let", 3, sc_parse_let, TRUE },
864 { "define", 6, sc_parse_define, FALSE },
865 { "fmt", 3, sc_parse_fmt, FALSE },
866 { "format", 6, sc_parse_format, FALSE },
867 { "set", 3, sc_parse_set, FALSE },
868 { "goto", 4, sc_parse_goto, FALSE },
869 { NULL, 0, NULL, 0 },
873 static gboolean
874 sc_parse_line (ScParseState *state, char *buf)
876 char const *space;
877 int i, cmdlen;
878 sc_cmd_t const *cmd;
880 g_return_val_if_fail (state, FALSE);
881 g_return_val_if_fail (state->sheet, FALSE);
882 g_return_val_if_fail (buf, FALSE);
884 for (space = buf; g_ascii_isalnum (*space) || *space == '_'; space++)
885 ; /* Nothing */
886 if (*space == 0)
887 return TRUE;
888 cmdlen = space - buf;
889 while (*space == ' ')
890 space++;
892 for (i = 0 ; sc_cmd_list[i].name != NULL ; ++i) {
893 cmd = &sc_cmd_list [i];
894 if (cmd->namelen == cmdlen &&
895 strncmp (cmd->name, buf, cmdlen) == 0) {
896 GnmCellPos pos = { -1, -1 };
897 char const *strdata = space;
899 if (cmd->have_coord) {
900 if (!sc_parse_coord (state, &strdata, &pos)) {
901 sc_warning (state, "Cannot parse %s\n",
902 buf);
903 return FALSE;
907 cmd->handler (state, cmd->name, strdata, &pos);
908 return TRUE;
912 sc_warning (state, "Unhandled directive: '%-.*s'",
913 cmdlen, buf);
914 return TRUE;
918 static GOErrorInfo *
919 sc_parse_sheet (ScParseState *state)
921 unsigned char *data;
922 GOErrorInfo *res = NULL;
924 while ((data = gsf_input_textline_ascii_gets (state->textline)) != NULL) {
925 char *utf8data;
927 g_strchomp (data);
928 utf8data = g_convert_with_iconv (data, -1, state->converter,
929 NULL, NULL, NULL);
931 if (g_ascii_isalpha (*data) && !sc_parse_line (state, utf8data)) {
932 if (!res)
933 res = go_error_info_new_str
934 (_("Error parsing line"));
937 g_free (utf8data);
940 return res;
943 static GnmExpr const *
944 sc_func_map_in (GnmConventions const *conv, Workbook *scope,
945 char const *name, GnmExprList *args)
947 static struct {
948 char const *sc_name;
949 char const *gnm_name;
950 } const sc_func_renames[] = {
951 { "AVG", "AVERAGE" },
952 { "DTR", "RADIANS" },
953 { "FABS", "ABS" },
954 { "COLS", "COLUMNS" },
955 { "AVG", "AVERAGE" },
956 { "POW", "POWER" },
957 { "PROD", "PRODUCT" },
958 { "RND", "ROUND" },
959 { "RTD", "DEGREES" },
960 { "STDDEV", "STDEV" },
961 { "STON", "INT" },
962 { "SUBSTR", "MID" },
963 { NULL, NULL }
965 static GHashTable *namemap = NULL;
967 GnmFunc *f;
968 char const *new_name;
969 int i;
971 if (NULL == namemap) {
972 namemap = g_hash_table_new (go_ascii_strcase_hash,
973 go_ascii_strcase_equal);
974 for (i = 0; sc_func_renames[i].sc_name; i++)
975 g_hash_table_insert (namemap,
976 (gchar *) sc_func_renames[i].sc_name,
977 (gchar *) sc_func_renames[i].gnm_name);
980 if (NULL != namemap &&
981 NULL != (new_name = g_hash_table_lookup (namemap, name)))
982 name = new_name;
983 if (NULL == (f = gnm_func_lookup (name, scope)))
984 f = gnm_func_add_placeholder (scope, name, "");
985 return gnm_expr_new_funcall (f, args);
988 static GnmConventions *
989 sc_conventions (void)
991 GnmConventions *conv = gnm_conventions_new ();
993 conv->decimal_sep_dot = TRUE;
994 conv->range_sep_colon = TRUE;
995 conv->input.range_ref = sc_rangeref_parse;
996 conv->input.func = sc_func_map_in;
998 return conv;
1001 static void
1002 sc_format_free (gpointer data, gpointer user_data)
1004 g_free (data);
1007 void
1008 sc_file_open (GOFileOpener const *fo, GOIOContext *io_context,
1009 WorkbookView *wb_view, GsfInput *input)
1011 Workbook *wb;
1012 char *name;
1013 GOErrorInfo *error;
1014 ScParseState state;
1016 wb = wb_view_get_workbook (wb_view);
1017 name = workbook_sheet_get_free_name (wb, "SC", FALSE, TRUE);
1018 state.sheet = sheet_new (wb, name, 256, 65536);
1019 g_free (name);
1020 workbook_sheet_attach (wb, state.sheet);
1022 /* This should probably come from import dialog. */
1023 state.converter = g_iconv_open ("UTF-8", "ISO-8859-1");
1025 state.convs = sc_conventions ();
1026 state.context = io_context;
1027 state.last_error = NULL;
1028 state.precision = NULL;
1029 state.formats = g_ptr_array_sized_new (10);
1030 g_ptr_array_add (state.formats, g_strdup ("#.&")); /* 0 */
1031 g_ptr_array_add (state.formats, g_strdup ("0.&E+00")); /* 1 */
1032 g_ptr_array_add (state.formats, g_strdup ("##0.&E+00")); /* 2 */
1033 g_ptr_array_add (state.formats, g_strdup ("[$-f8f2]m/d/yy")); /* 3 */
1034 g_ptr_array_add (state.formats, g_strdup ("[$-f800]dddd, mmmm dd, yyyy")); /* 4 */
1035 g_ptr_array_set_size (state.formats, 10);
1037 state.textline = (GsfInputTextline *) gsf_input_textline_new (input);
1038 error = sc_parse_sheet (&state);
1039 if (error != NULL) {
1040 workbook_sheet_delete (state.sheet);
1041 go_io_error_info_set (io_context, error);
1043 g_object_unref (state.textline);
1044 g_iconv_close (state.converter);
1045 gnm_conventions_unref (state.convs);
1046 g_free (state.last_error);
1047 sc_parse_format_free_precision (&state);
1049 /*In glib 2.22 or later we could use g_ptr_array_set_free_func */
1050 g_ptr_array_foreach (state.formats, (GFunc) sc_format_free, NULL);
1051 g_ptr_array_unref (state.formats);
1055 static guint8 const signature[] =
1056 "# This data file was generated by the Spreadsheet Calculator.";
1058 gboolean
1059 sc_file_probe (GOFileOpener const *fo, GsfInput *input,
1060 GOFileProbeLevel pl)
1062 char const *header = NULL;
1064 if (!gsf_input_seek (input, 0, G_SEEK_SET))
1065 header = gsf_input_read (input, sizeof (signature)-1, NULL);
1066 return header != NULL &&
1067 memcmp (header, signature, sizeof (signature)-1) == 0;
1072 * http://www.thule.no/haynie/cpumods/a2620/docs/commrc.sc.txt:
1073 * format B 20 2
1075 * http://www.mcs.kent.edu/system/documentation/xspread/demo_func
1076 * format A 15 2 0
1077 * goto C7