Whitespace.
[gnumeric.git] / src / parse-util.c
blob3c4359c8702e4b0bd9407cc8767cb52b8eec71b9
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * parse-util.c: Various utility routines to parse or produce
4 * string representations of common reference types.
6 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2008-2009 Morten Welinder (terra@gnome.org)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
24 #include <gnumeric-config.h>
25 #include "gnumeric.h"
26 #include "parse-util.h"
28 #include "application.h"
29 #include "workbook.h"
30 #include "sheet.h"
31 #include "value.h"
32 #include "ranges.h"
33 #include "cell.h"
34 #include "expr.h"
35 #include "number-match.h"
36 #include "gnm-format.h"
37 #include "expr-name.h"
38 #include "func.h"
39 #include "mstyle.h"
40 #include "sheet-style.h"
41 /* For std_expr_name_handler: */
42 #include "expr-impl.h"
43 #include "gutils.h"
44 #include <goffice/goffice.h>
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <glib.h>
49 #include <string.h>
51 static GnmLexerItem *
52 gnm_lexer_item_copy (GnmLexerItem *li)
54 return g_memdup (li, sizeof (*li));
57 GType
58 gnm_lexer_item_get_type (void)
60 static GType t = 0;
62 if (t == 0) {
63 t = g_boxed_type_register_static ("GnmLexerItem",
64 (GBoxedCopyFunc)gnm_lexer_item_copy,
65 (GBoxedFreeFunc)g_free);
67 return t;
70 static void
71 col_name_internal (GString *target, int col)
73 static int const steps[] = {
74 26,
75 26 * 26,
76 26 * 26 * 26,
77 26 * 26 * 26 * 26,
78 26 * 26 * 26 * 26 * 26,
79 26 * 26 * 26 * 26 * 26 * 26,
80 INT_MAX
82 int i;
83 char *dst;
85 if (col < 0) {
86 /* Invalid column. */
87 g_string_append_printf (target, "[C%d]", col);
88 return;
91 for (i = 0; col >= steps[i]; i++)
92 col -= steps[i];
94 g_string_set_size (target, target->len + (i + 1));
95 dst = target->str + target->len;
96 while (i-- >= 0) {
97 *--dst = 'A' + col % 26;
98 col /= 26;
103 * col_name: (skip)
104 * @col: column number
106 * Returns: (transfer none): A string representation of @col
108 char const *
109 col_name (int col)
111 static GString *buffer = NULL;
112 if (!buffer)
113 buffer = g_string_new (NULL);
114 g_string_truncate (buffer, 0);
116 col_name_internal (buffer, col);
118 return buffer->str;
122 * cols_name: (skip)
123 * @start_col: column number
124 * @end_col: column number
126 * Returns: (transfer none): A string representation of the columns from
127 * @start_col to @end_col.
129 char const *
130 cols_name (int start_col, int end_col)
132 static GString *buffer = NULL;
133 if (!buffer)
134 buffer = g_string_new (NULL);
135 g_string_truncate (buffer, 0);
137 col_name_internal (buffer, start_col);
138 if (start_col != end_col) {
139 g_string_append_c (buffer, ':');
140 col_name_internal (buffer, end_col);
143 return buffer->str;
147 * col_parse: (skip)
149 * Returns: (transfer none):
151 char const *
152 col_parse (char const *str, GnmSheetSize const *ss,
153 int *res, unsigned char *relative)
155 char const *ptr, *start = str;
156 int col = -1;
157 int max = ss->max_cols;
159 if (!(*relative = (*start != '$')))
160 start++;
162 for (ptr = start; col < max ; ptr++)
163 if (('a' <= *ptr && *ptr <= 'z'))
164 col = 26 * (col + 1) + (*ptr - 'a');
165 else if (('A' <= *ptr && *ptr <= 'Z'))
166 col = 26 * (col + 1) + (*ptr - 'A');
167 else if (ptr != start) {
168 *res = col;
169 return ptr;
170 } else
171 return NULL;
172 return NULL;
175 /***************************************************************************/
177 static void
178 row_name_internal (GString *target, int row)
180 g_string_append_printf (target, "%d", row + 1);
184 * row_name: (skip)
185 * @row: row number
187 * Returns: (transfer none): A string representation of @row
189 char const *
190 row_name (int row)
192 static GString *buffer = NULL;
193 if (!buffer)
194 buffer = g_string_new (NULL);
195 g_string_truncate (buffer, 0);
197 row_name_internal (buffer, row);
199 return buffer->str;
203 * rows_name: (skip)
204 * @start_row: row number
205 * @end_row: row number
207 * Returns: (transfer none): A string representation of the rows from
208 * @start_row to @end_row.
210 char const *
211 rows_name (int start_row, int end_row)
213 static GString *buffer = NULL;
214 if (!buffer)
215 buffer = g_string_new (NULL);
216 g_string_truncate (buffer, 0);
218 row_name_internal (buffer, start_row);
219 if (start_row != end_row) {
220 g_string_append_c (buffer, ':');
221 row_name_internal (buffer, end_row);
224 return buffer->str;
228 * row_parse: (skip)
230 * Returns: (transfer none):
232 char const *
233 row_parse (char const *str, GnmSheetSize const *ss,
234 int *res, unsigned char *relative)
236 char const *end, *ptr = str;
237 long int row;
238 int max = ss->max_rows;
240 if (!(*relative = (*ptr != '$')))
241 ptr++;
243 /* Initial '0' is not allowed. */
244 if (*ptr <= '0' || *ptr > '9')
245 return NULL;
248 * Do not allow letters after the row number. If we did, then
249 * the name "K3P" would lex as the reference K3 followed by the
250 * name "P".
252 row = strtol (ptr, (char **)&end, 10);
253 if (ptr != end &&
254 !g_unichar_isalnum (g_utf8_get_char (end)) && *end != '_' &&
255 0 < row && row <= max) {
256 *res = row - 1;
257 return end;
258 } else
259 return NULL;
262 /***************************************************************************/
264 static void
265 r1c1_add_index (GString *target, char type, int num, unsigned char relative)
267 if (relative) {
268 if (num != 0)
269 g_string_append_printf (target, "%c[%d]", type, num);
270 else
271 g_string_append_c (target, type);
272 } else
273 g_string_append_printf (target, "%c%d", type, num + 1);
276 static char *
277 wb_rel_uri (Workbook *wb, Workbook *ref_wb)
279 char const *uri = go_doc_get_uri ((GODoc *)wb);
280 char const *ref_uri = go_doc_get_uri ((GODoc *)ref_wb);
281 char *rel_uri = go_url_make_relative (uri, ref_uri);
283 if (rel_uri == NULL || rel_uri[0] == '/') {
284 g_free (rel_uri);
285 return g_strdup (uri);
288 return rel_uri;
292 * cellref_as_string: (skip)
293 * @out: #GnmConventionsOut
294 * @cell_ref:
295 * @no_sheetname: If %TRUE, suppress sheet name
297 * Emits a string containing representation of @ref as evaluated at @pp.
298 * @no_sheetname can be used to suppress the addition of the sheetname
299 * for non-local references.
301 void
302 cellref_as_string (GnmConventionsOut *out,
303 GnmCellRef const *cell_ref,
304 gboolean no_sheetname)
306 GString *target = out->accum;
307 Sheet const *sheet = cell_ref->sheet;
309 /* If it is a non-local reference, add the path to the external sheet */
310 if (sheet != NULL && !no_sheetname) {
311 if (out->pp->wb == NULL && out->pp->sheet == NULL)
312 /* For the expression leak printer. */
313 g_string_append (target, "'?'");
314 else if (NULL == out->pp->wb || sheet->workbook == out->pp->wb)
315 g_string_append (target, sheet->name_quoted);
316 else {
317 char *rel_uri = wb_rel_uri (sheet->workbook, out->pp->wb);
318 g_string_append_c (target, '[');
319 g_string_append (target, rel_uri);
320 g_string_append_c (target, ']');
321 g_string_append (target, sheet->name_quoted);
322 g_free (rel_uri);
324 g_string_append_unichar (target, out->convs->sheet_name_sep);
327 if (out->convs->r1c1_addresses) { /* R1C1 handler */
328 r1c1_add_index (target, 'R', cell_ref->row, cell_ref->row_relative);
329 r1c1_add_index (target, 'C', cell_ref->col, cell_ref->col_relative);
330 } else {
331 GnmCellPos pos;
332 Sheet const *size_sheet = eval_sheet (sheet, out->pp->sheet);
333 GnmSheetSize const *ss =
334 gnm_sheet_get_size2 (size_sheet, out->pp->wb);
336 gnm_cellpos_init_cellref_ss (&pos, cell_ref, &out->pp->eval, ss);
338 if (!cell_ref->col_relative)
339 g_string_append_c (target, '$');
340 col_name_internal (target, pos.col);
342 if (!cell_ref->row_relative)
343 g_string_append_c (target, '$');
344 row_name_internal (target, pos.row);
349 * rangeref_as_string: (skip)
350 * @out: #GnmConventionsOut
351 * @ref: #GnmRangeRef
354 void
355 rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
357 GnmRange r;
358 GString *target = out->accum;
359 Sheet *start_sheet, *end_sheet;
360 GnmSheetSize const *end_ss;
362 gnm_rangeref_normalize_pp (ref, out->pp, &start_sheet, &end_sheet, &r);
364 end_ss = gnm_sheet_get_size2 (end_sheet, out->pp->wb);
366 if (ref->a.sheet) {
367 if (NULL != out->pp->wb && ref->a.sheet->workbook != out->pp->wb) {
368 char *rel_uri = wb_rel_uri (ref->a.sheet->workbook, out->pp->wb);
369 g_string_append_c (target, '[');
370 g_string_append (target, rel_uri);
371 g_string_append_c (target, ']');
372 g_free (rel_uri);
374 if (out->pp->wb == NULL && out->pp->sheet == NULL)
375 /* For the expression leak printer. */
376 g_string_append (target, "'?'");
377 else if (ref->b.sheet == NULL || ref->a.sheet == ref->b.sheet)
378 g_string_append (target, ref->a.sheet->name_quoted);
379 else {
380 g_string_append (target, ref->a.sheet->name_quoted);
381 g_string_append_c (target, ':');
382 g_string_append (target, ref->b.sheet->name_quoted);
384 g_string_append_unichar (target, out->convs->sheet_name_sep);
387 if (out->convs->r1c1_addresses) { /* R1C1 handler */
388 /* be sure to use else if so that a1:iv65535 does not vanish */
389 if (r.start.col == 0 && r.end.col == end_ss->max_cols - 1) {
390 r1c1_add_index (target, 'R', ref->a.row, ref->a.row_relative);
391 if (ref->a.row != ref->b.row ||
392 ref->a.row_relative != ref->b.row_relative) {
393 g_string_append_c (target, ':');
394 r1c1_add_index (target, 'R', ref->b.row, ref->b.row_relative);
396 } else if (r.start.row == 0 && r.end.row == end_ss->max_rows - 1) {
397 r1c1_add_index (target, 'C', ref->a.col, ref->a.col_relative);
398 if (ref->a.col != ref->b.col ||
399 ref->a.col_relative != ref->b.col_relative) {
400 g_string_append_c (target, ':');
401 r1c1_add_index (target, 'C', ref->b.col, ref->b.col_relative);
403 } else {
404 r1c1_add_index (target, 'R', ref->a.row, ref->a.row_relative);
405 r1c1_add_index (target, 'C', ref->a.col, ref->a.col_relative);
406 if (r.start.col != r.end.col ||
407 ref->a.col_relative != ref->b.col_relative ||
408 r.start.row != r.end.row ||
409 ref->a.row_relative != ref->b.row_relative) {
410 g_string_append_c (target, ':');
411 r1c1_add_index (target, 'R', ref->b.row, ref->b.row_relative);
412 r1c1_add_index (target, 'C', ref->b.col, ref->b.col_relative);
415 } else {
416 /* be sure to use else if so that a1:iv65535 does not vanish */
417 if (r.start.col == 0 && r.end.col == end_ss->max_cols - 1) {
418 if (!ref->a.row_relative)
419 g_string_append_c (target, '$');
420 row_name_internal (target, r.start.row);
421 g_string_append_c (target, ':');
422 if (!ref->b.row_relative)
423 g_string_append_c (target, '$');
424 row_name_internal (target, r.end.row);
425 } else if (r.start.row == 0 && r.end.row == end_ss->max_rows - 1) {
426 if (!ref->a.col_relative)
427 g_string_append_c (target, '$');
428 col_name_internal (target, r.start.col);
429 g_string_append_c (target, ':');
430 if (!ref->b.col_relative)
431 g_string_append_c (target, '$');
432 col_name_internal (target, r.end.col);
433 } else {
434 if (!ref->a.col_relative)
435 g_string_append_c (target, '$');
436 col_name_internal (target, r.start.col);
437 if (!ref->a.row_relative)
438 g_string_append_c (target, '$');
439 row_name_internal (target, r.start.row);
441 if (r.start.col != r.end.col ||
442 ref->a.col_relative != ref->b.col_relative ||
443 r.start.row != r.end.row ||
444 ref->a.row_relative != ref->b.row_relative) {
445 g_string_append_c (target, ':');
446 if (!ref->b.col_relative)
447 g_string_append_c (target, '$');
448 col_name_internal (target, r.end.col);
449 if (!ref->b.row_relative)
450 g_string_append_c (target, '$');
451 row_name_internal (target, r.end.row);
458 * gnm_1_0_rangeref_as_string: (skip)
459 * @out: #GnmConventionsOut
460 * @ref: #GnmRangeRef
462 * Simplified variant of rangeref_as_string that old versions of gnumeric can
463 * read. It drops support for full col/row references. We can remap them on
464 * import.
466 * This function also ignores R1C1 settings.
468 void
469 gnm_1_0_rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
471 GnmRange r;
472 GString *target = out->accum;
473 Sheet *start_sheet, *end_sheet;
475 gnm_rangeref_normalize_pp (ref, out->pp, &start_sheet, &end_sheet, &r);
477 if (ref->a.sheet) {
478 if (NULL != out->pp->wb && ref->a.sheet->workbook != out->pp->wb) {
479 char *rel_uri = wb_rel_uri (ref->a.sheet->workbook, out->pp->wb);
480 g_string_append_c (target, '[');
481 g_string_append (target, rel_uri);
482 g_string_append_c (target, ']');
483 g_free (rel_uri);
485 if (out->pp->wb == NULL && out->pp->sheet == NULL)
486 /* For the expression leak printer. */
487 g_string_append (target, "'?'");
488 else if (ref->b.sheet == NULL || ref->a.sheet == ref->b.sheet)
489 g_string_append (target, ref->a.sheet->name_quoted);
490 else {
491 g_string_append (target, ref->a.sheet->name_quoted);
492 g_string_append_c (target, ':');
493 g_string_append (target, ref->b.sheet->name_quoted);
495 g_string_append_unichar (target, out->convs->sheet_name_sep);
498 if (!ref->a.col_relative)
499 g_string_append_c (target, '$');
500 col_name_internal (target, r.start.col);
501 if (!ref->a.row_relative)
502 g_string_append_c (target, '$');
503 row_name_internal (target, r.start.row);
505 if (r.start.col != r.end.col ||
506 ref->a.col_relative != ref->b.col_relative ||
507 r.start.row != r.end.row ||
508 ref->a.row_relative != ref->b.row_relative) {
509 g_string_append_c (target, ':');
510 if (!ref->b.col_relative)
511 g_string_append_c (target, '$');
512 col_name_internal (target, r.end.col);
513 if (!ref->b.row_relative)
514 g_string_append_c (target, '$');
515 row_name_internal (target, r.end.row);
519 static char const *
520 cellref_a1_get (GnmCellRef *out, GnmSheetSize const *ss,
521 char const *in, GnmCellPos const *pos)
523 int col;
524 int row;
526 g_return_val_if_fail (in != NULL, NULL);
527 g_return_val_if_fail (out != NULL, NULL);
529 in = col_parse (in, ss, &col, &out->col_relative);
530 if (!in)
531 return NULL;
533 in = row_parse (in, ss, &row, &out->row_relative);
534 if (!in)
535 return NULL;
537 /* Setup the cell reference information */
538 if (out->row_relative)
539 out->row = row - pos->row;
540 else
541 out->row = row;
543 if (out->col_relative)
544 out->col = col - pos->col;
545 else
546 out->col = col;
548 out->sheet = NULL;
550 return in;
553 /* skip first character (which was R or C) */
554 static char const *
555 r1c1_get_index (char const *str, GnmSheetSize const *ss,
556 int *num, unsigned char *relative, gboolean is_col)
558 char *end;
559 long l;
560 int max = is_col ? ss->max_cols : ss->max_rows;
562 if (str[0] == '\0')
563 return NULL;
565 str++;
566 *relative = (*str == '[');
567 if (*relative)
568 str++;
569 else if (*str == '-' || *str == '+') { /* handle RC-10 as RC followed by -10 */
570 *relative = TRUE;
571 *num = 0;
572 return str;
575 errno = 0;
576 *num = l = strtol (str, &end, 10);
577 if (errno == ERANGE || l <= G_MININT || l > G_MAXINT) {
578 /* Note: this includes G_MININT to avoid negation overflow. */
579 return NULL;
581 if (str == end) {
582 if (*relative)
583 return NULL;
584 *relative = TRUE;
585 *num = 0;
586 } else if (*relative) {
587 if (*end != ']')
588 return NULL;
589 *num = (*num > 0
590 ? *num % max
591 : -(-*num % max));
592 return end + 1;
593 } else {
594 if (*num <= 0 || *num > max)
595 return NULL;
596 (*num)--;
598 return end;
601 static char const *
602 cellref_r1c1_get (GnmCellRef *out, GnmSheetSize const *ss,
603 char const *in, GnmCellPos const *pos)
605 out->sheet = NULL;
606 if (*in != 'R' && *in != 'r')
607 return NULL;
608 if (NULL == (in = r1c1_get_index (in, ss,
609 &out->row, &out->row_relative,
610 FALSE)))
611 return NULL;
612 if (*in != 'C' && *in != 'c')
613 return NULL;
614 if (NULL == (in = r1c1_get_index (in, ss,
615 &out->col, &out->col_relative,
616 TRUE)))
617 return NULL;
618 if (g_ascii_isalpha (*in))
619 return NULL;
620 return in;
624 * cellref_parse: (skip)
625 * @out: (out): destination GnmCellRef
626 * @ss: size of the sheet where parsing is being done
627 * @in: reference description text, no leading whitespace allowed.
628 * @pos: position parsing is being done at
630 * Converts the string representation of a #GnmCellRef into
631 * an internal representation.
633 * Returns: (transfer none): a pointer to the character following the
634 * cellref.
636 char const *
637 cellref_parse (GnmCellRef *out, GnmSheetSize const *ss,
638 char const *in, GnmCellPos const *pos)
640 char const *res;
642 g_return_val_if_fail (in != NULL, NULL);
643 g_return_val_if_fail (out != NULL, NULL);
645 res = cellref_a1_get (out, ss, in, pos);
646 if (res != NULL)
647 return res;
648 return cellref_r1c1_get (out, ss, in, pos);
651 /****************************************************************************/
653 static char const *
654 cell_coord_name2 (int col, int row, gboolean r1c1)
656 static GString *buffer = NULL;
657 if (buffer)
658 g_string_truncate (buffer, 0);
659 else
660 buffer = g_string_new (NULL);
662 if (r1c1) {
663 r1c1_add_index (buffer, 'R', row, FALSE);
664 r1c1_add_index (buffer, 'C', col, FALSE);
665 } else {
666 col_name_internal (buffer, col);
667 row_name_internal (buffer, row);
670 return buffer->str;
674 * cell_coord_name: (skip)
675 * @col: column number
676 * @row: row number
678 * Returns: (transfer none): a string representation of the cell at (@col,@row)
680 char const *
681 cell_coord_name (int col, int row)
683 return cell_coord_name2 (col, row, FALSE);
687 * cellpos_as_string: (skip)
688 * @pos: A #GnmCellPos
690 * Returns: (transfer none): a string representation of the cell at @pos
692 char const *
693 cellpos_as_string (GnmCellPos const *pos)
695 g_return_val_if_fail (pos != NULL, "ERROR");
697 return cell_coord_name (pos->col, pos->row);
700 char const *
701 parsepos_as_string (GnmParsePos const *pp)
703 g_return_val_if_fail (pp != NULL, "ERROR");
705 return cell_coord_name2 (pp->eval.col,
706 pp->eval.row,
707 pp->sheet && pp->sheet->convs->r1c1_addresses);
711 * cell_name:
712 * @cell: #GnmCell
714 * Returns: (transfer none): the name of @cell, like "B11"
716 char const *
717 cell_name (GnmCell const *cell)
719 g_return_val_if_fail (cell != NULL, "ERROR");
721 return cell_coord_name2 (cell->pos.col,
722 cell->pos.row,
723 cell->base.sheet->convs->r1c1_addresses);
727 * cellpos_parse: (skip)
728 * @cell_str: a string representation of a cell name.
729 * @ss: #GnmSheetSize
730 * @res: result
731 * @strict: if this is TRUE, then parsing stops at possible errors,
732 * otherwise an attempt is made to return cell names with
733 * trailing garbage.
735 * Returns: (transfer none): pointer to following char on success, %NULL on
736 * failure. (In the strict case, that would be a pointer to the \0 or %NULL.)
738 char const *
739 cellpos_parse (char const *cell_str, GnmSheetSize const *ss,
740 GnmCellPos *res, gboolean strict)
742 unsigned char dummy_relative;
744 cell_str = col_parse (cell_str, ss, &res->col, &dummy_relative);
745 if (!cell_str)
746 return NULL;
748 cell_str = row_parse (cell_str, ss, &res->row, &dummy_relative);
749 if (!cell_str)
750 return NULL;
752 if (*cell_str != 0 && strict)
753 return NULL;
755 return cell_str;
759 * gnm_expr_char_start_p: (skip)
760 * @c: string
762 * Can the supplied string be an expression ? It does not guarantee that it is,
763 * however, it is possible. If it is possible it strips off any header
764 * characters that are not relevant.
766 * NOTE : things like -1,234 will match
768 char const *
769 gnm_expr_char_start_p (char const * c)
771 char c0;
772 int N = 1;
774 if (NULL == c)
775 return NULL;
777 c0 = *c;
779 if (c0 == '=' || c0 == '@' || c0 == '+' || c0 == '-')
780 while (c[N] == ' ')
781 N++;
783 if (c0 == '=' || c0 == '@' || (c0 == '+' && c[1] == 0))
784 return c + N;
786 if ((c0 == '-' || c0 == '+') && c0 != c[1]) {
787 char *end;
790 * Ok, we have a string that
791 * 1. starts with a sign
792 * 2. does not start with the sign repeated (think --------)
793 * 3. is more than one character
795 * Now we check whether we have a number. We don't want
796 * numbers to be treated as formulae. FIXME: this really
797 * just checks for C-syntax numbers.
799 (void) gnm_strto (c, &end);
800 if (errno || *end != 0 || end == c)
801 return (c0 == '+') ? c + N : c;
802 /* Otherwise, it's a number. */
804 return NULL;
808 * parse_text_value_or_expr:
809 * @pos: If the string looks like an expression parse it at this location.
810 * @text: The text to be parsed.
811 * @val: (out): Returns a GnmValue* if the text was a value, otherwise NULL.
812 * @texpr: (out): Returns a GnmExprTop* if the text was an expression, otherwise NULL.
814 * Utility routine to parse a string and convert it into an expression or value.
816 * If there is a parse failure for an expression an error GnmValue with
817 * the syntax error is returned in @val.
819 void
820 parse_text_value_or_expr (GnmParsePos const *pos, char const *text,
821 GnmValue **val, GnmExprTop const **texpr)
823 char const *expr_start;
824 GODateConventions const *date_conv;
825 GOFormat const *cur_fmt;
826 GOFormat const *cell_fmt;
827 GnmStyle const *cell_style;
829 *texpr = NULL;
830 *val = NULL;
832 /* Determine context information. */
833 date_conv =
834 pos->sheet
835 ? workbook_date_conv (pos->sheet->workbook)
836 : (pos->wb
837 ? workbook_date_conv (pos->wb)
838 : NULL);
839 cell_style = pos->sheet
840 ? sheet_style_get (pos->sheet, pos->eval.col, pos->eval.row)
841 : NULL;
842 cur_fmt = cell_fmt = cell_style ? gnm_style_get_format (cell_style) : NULL;
843 if (cell_fmt && go_format_is_general (cell_fmt)) {
844 GnmCell const *cell = pos->sheet
845 ? sheet_cell_get (pos->sheet, pos->eval.col, pos->eval.row)
846 : NULL;
847 if (cell && cell->value && VALUE_FMT (cell->value))
848 cur_fmt = VALUE_FMT (cell->value);
851 /* Does it match any formats? */
852 *val = format_match (text, cur_fmt, date_conv);
853 if (*val != NULL) {
854 GOFormat const *val_fmt = VALUE_FMT (*val);
855 /* Avoid value formats we don't need. */
856 if (val_fmt && go_format_eq (cell_fmt, val_fmt))
857 value_set_fmt (*val, NULL);
858 return;
861 /* If it does not match known formats, see if it is an expression */
862 expr_start = gnm_expr_char_start_p (text);
863 if (NULL != expr_start && *expr_start) {
864 *texpr = gnm_expr_parse_str (expr_start, pos,
865 GNM_EXPR_PARSE_DEFAULT, NULL, NULL);
866 if (*texpr != NULL)
867 return;
870 /* Fall back on string */
871 *val = value_new_string (text);
874 GnmParseError *
875 parse_error_init (GnmParseError *pe)
877 pe->err = NULL;
878 pe->begin_char = 0;
879 pe->end_char = 0;
881 return pe;
884 void
885 parse_error_free (GnmParseError *pe)
887 if (pe->err != NULL) {
888 g_error_free (pe->err);
889 pe->err = NULL;
893 static GnmParseError *
894 gnm_parse_error_copy (GnmParseError *pe)
896 GnmParseError *res = g_new (GnmParseError, 1);
897 res->begin_char = pe->begin_char;
898 res->end_char = pe->end_char;
899 res->err = (pe->err)? g_error_copy (pe->err): NULL;
900 return res;
903 GType
904 gnm_parse_error_get_type (void)
906 static GType t = 0;
908 if (t == 0) {
909 t = g_boxed_type_register_static ("GnmParseError",
910 (GBoxedCopyFunc)gnm_parse_error_copy,
911 (GBoxedFreeFunc)parse_error_free);
913 return t;
916 /***************************************************************************/
918 static char const *
919 check_quoted (char const *start, int *num_escapes)
921 char const *str = start;
922 if (*str == '\'' || *str == '\"') {
923 char const quote = *str++;
924 *num_escapes = 0;
925 for (; *str && *str != quote; str = g_utf8_next_char (str))
926 if (*str == '\\' && str[1]) {
927 str++;
928 (*num_escapes)++;
930 if (*str)
931 return str+1;
932 } else
933 *num_escapes = -1;
934 return start;
937 static void
938 unquote (char *dst, char const *src, int n)
940 while (n-- > 0)
941 if (*src == '\\' && src[1]) {
942 int l = g_utf8_skip [*(guchar *)(++src)];
943 strncpy (dst, src, l);
944 dst += l;
945 src += l;
946 n -= l;
947 } else
948 *dst++ = *src++;
949 *dst = 0;
953 * wbref_parse:
954 * @convs: #GnmConventions const
955 * @start:
956 * @wb:
958 * Returns : NULL if there is a valid workbook name but it is unknown.
959 * If the string is a valid workbook known name it returns a pointer
960 * the end of the name.
961 * Otherwise returns @start and does not modify @wb.
963 static char const *
964 wbref_parse (GnmConventions const *convs,
965 char const *start, Workbook **wb, Workbook *ref_wb)
967 /* Is this an external reference ? */
968 if (*start == '[') {
969 Workbook *tmp_wb;
971 int num_escapes;
972 char const *end = check_quoted (start+1, &num_escapes);
973 char *name;
975 if (end == start+1) {
976 end = strchr (start, ']');
977 if (end == NULL)
978 return start;
980 if (*end != ']')
981 return start;
983 if (num_escapes < 0)
984 name = g_strndup (start + 1, end - start - 1);
985 else {
986 name = g_malloc (1 + end - start - 2);
987 unquote (name, start+2, end-start-2);
990 tmp_wb = (*convs->input.external_wb) (convs, ref_wb, name);
991 g_free (name);
992 if (tmp_wb == NULL)
993 return NULL;
995 *wb = tmp_wb;
996 return end + 1;
999 return start;
1003 * sheetref_parse: (skip)
1004 * @convs: #GnmConventions
1005 * @start:
1006 * @sheet: (out)
1007 * @wb: A #Workbook
1008 * @allow_3d:
1010 * Returns: (transfer none): %NULL if there is a valid sheet name but it
1011 * is unknown. If the string is a valid sheet name it returns a pointer
1012 * the end of the name. Otherwise returns @start and does not
1013 * modify @sheet.
1015 static char const *
1016 sheetref_parse (GnmConventions const *convs,
1017 char const *start, Sheet **sheet, Workbook const *wb,
1018 gboolean allow_3d)
1020 GString *sheet_name;
1021 char const *end;
1023 *sheet = NULL;
1024 if (*start == '\'' || *start == '"') {
1025 sheet_name = g_string_new (NULL);
1026 end = go_strunescape (sheet_name, start);
1027 if (end == NULL) {
1028 g_string_free (sheet_name, TRUE);
1029 return start;
1031 } else {
1032 gboolean only_digits = TRUE;
1033 end = start;
1036 * Valid: Normal!a1
1037 * Valid: x.y!a1
1038 * Invalid: .y!a1
1040 * Some names starting with digits are actually valid, but
1041 * unparse quoted. Things are quite tricky: most sheet names
1042 * starting with a digit are ok, but not those starting with
1043 * "[0-9]*\." or "[0-9]+[eE]".
1045 * Valid: 42!a1
1046 * Valid: 4x!a1
1047 * Invalid: 1.!a1
1048 * Invalid: 1e!a1
1051 while (1) {
1052 gunichar uc = g_utf8_get_char (end);
1053 if (g_unichar_isalpha (uc) || uc == '_') {
1054 if (only_digits && end != start &&
1055 (uc == 'e' || uc == 'E')) {
1056 end = start;
1057 break;
1059 only_digits = FALSE;
1060 end = g_utf8_next_char (end);
1061 } else if (g_unichar_isdigit (uc)) {
1062 end = g_utf8_next_char (end);
1063 } else if (uc == '.') {
1064 /* Valid, except after only digits. */
1065 if (only_digits) {
1066 end = start;
1067 break;
1069 end++;
1070 } else
1071 break;
1074 if (*end != '!' && (!allow_3d || *end != ':'))
1075 return start;
1077 sheet_name = g_string_new_len (start, end - start);
1080 *sheet = workbook_sheet_by_name (wb, sheet_name->str);
1081 if (*sheet == NULL)
1082 end = start;
1084 g_string_free (sheet_name, TRUE);
1085 return end;
1088 static char const *
1089 r1c1_rangeref_parse (GnmRangeRef *res, char const *ptr, GnmParsePos const *pp)
1091 char const *tmp;
1092 GnmSheetSize const *a_ss, *b_ss;
1093 Sheet const *a_sheet, *b_sheet;
1095 a_sheet = eval_sheet (res->a.sheet, pp->sheet);
1096 b_sheet = eval_sheet (res->b.sheet, a_sheet);
1098 a_ss = gnm_sheet_get_size2 (a_sheet, pp->wb);
1099 b_ss = gnm_sheet_get_size2 (b_sheet, pp->wb);
1101 if (*ptr == 'R' || *ptr == 'r') {
1102 ptr = r1c1_get_index (ptr, a_ss,
1103 &res->a.row, &res->a.row_relative,
1104 FALSE);
1105 if (!ptr)
1106 return NULL;
1107 if (*ptr != 'C' && *ptr != 'c') {
1108 if (g_ascii_isalpha (*ptr))
1109 return NULL;
1110 /* full row R# */
1111 res->a.col_relative = FALSE;
1112 res->a.col = 0;
1113 res->b = res->a;
1114 res->b.col = a_ss->max_cols - 1;
1115 if (ptr[0] != ':' || (ptr[1] != 'R' && ptr[1] != 'r'))
1116 return ptr;
1117 tmp = r1c1_get_index (ptr+1, a_ss,
1118 &res->b.row, &res->b.row_relative,
1119 FALSE);
1120 if (!tmp)
1121 return ptr; /* fallback to just the initial R */
1122 return tmp;
1123 } else {
1124 ptr = r1c1_get_index (ptr, a_ss,
1125 &res->a.col, &res->a.col_relative,
1126 TRUE);
1127 if (!ptr)
1128 return NULL;
1131 res->b = res->a;
1132 if (ptr[0] != ':' || (ptr[1] != 'R' && ptr[1] != 'r') ||
1133 NULL == (tmp = r1c1_get_index (ptr+1, b_ss,
1134 &res->b.row, &res->b.row_relative, FALSE)) ||
1135 (*tmp != 'C' && *tmp != 'c') ||
1136 NULL == (tmp = r1c1_get_index (tmp, b_ss,
1137 &res->b.col, &res->b.col_relative, FALSE)))
1138 return ptr;
1139 return tmp;
1140 } else if (*ptr == 'C' || *ptr == 'c') {
1141 if (NULL == (ptr = r1c1_get_index (ptr, a_ss,
1142 &res->a.col, &res->a.col_relative, TRUE)))
1143 return NULL;
1144 if (g_ascii_isalpha (*ptr))
1145 return NULL;
1146 /* full col C[#] */
1147 res->a.row_relative = FALSE;
1148 res->a.row = 0;
1149 res->b = res->a;
1150 res->b.row = b_ss->max_rows - 1;
1151 if (ptr[0] != ':' || (ptr[1] != 'C' && ptr[1] != 'c'))
1152 return ptr;
1153 tmp = r1c1_get_index (ptr, b_ss,
1154 &res->b.col, &res->b.col_relative,
1155 TRUE);
1156 if (!tmp)
1157 return ptr; /* fallback to just the initial C */
1158 return tmp;
1161 return NULL;
1165 * rangeref_parse: (skip)
1166 * @res: (out): #GnmRangeRef
1167 * @start: the start of the string to parse
1168 * @pp: the location to parse relative to
1169 * @convs: #GnmConventions
1171 * Returns: (transfer none): a pointer to the first invalid character.
1172 * If the result != @start then @res is valid.
1174 char const *
1175 rangeref_parse (GnmRangeRef *res, char const *start, GnmParsePos const *pp,
1176 GnmConventions const *convs)
1178 char const *ptr = start, *start_sheet, *start_wb, *tmp1, *tmp2;
1179 Workbook *wb;
1180 Workbook *ref_wb;
1181 Sheet *a_sheet, *b_sheet;
1182 GnmSheetSize const *a_ss, *b_ss;
1184 g_return_val_if_fail (start != NULL, start);
1185 g_return_val_if_fail (pp != NULL, start);
1187 wb = pp->wb;
1188 ref_wb = wb ? wb : pp->sheet->workbook;
1189 start_wb = start;
1190 start_sheet = wbref_parse (convs, start, &wb, ref_wb);
1191 if (start_sheet == NULL)
1192 return start; /* TODO error unknown workbook */
1193 ptr = sheetref_parse (convs, start_sheet, &res->a.sheet, wb, TRUE);
1194 if (ptr == NULL)
1195 return start; /* TODO error unknown sheet */
1196 if (ptr != start_sheet) {
1197 const char *ref;
1199 if (*ptr == ':') { /* 3d ref */
1200 ptr = sheetref_parse (convs, ptr+1, &res->b.sheet, wb, FALSE);
1201 if (ptr == NULL)
1202 return start; /* TODO error unknown sheet */
1203 } else
1204 res->b.sheet = NULL;
1206 if (*ptr++ != '!')
1207 return start; /* TODO syntax error */
1209 ref = value_error_name (GNM_ERROR_REF, FALSE);
1210 if (strncmp (ptr, ref, strlen (ref)) == 0) {
1211 res->a.sheet = invalid_sheet;
1212 res->a.col = res->a.row = 0;
1213 res->a.col_relative = res->a.row_relative = FALSE;
1214 res->b.sheet = res->a.sheet;
1215 ptr += strlen (ref);
1216 return ptr;
1218 } else {
1219 if (start_sheet != start_wb)
1220 return start; /* Workbook, but no sheet. */
1221 res->b.sheet = NULL;
1224 if (convs->r1c1_addresses) { /* R1C1 handler */
1225 const char *tmp1 = r1c1_rangeref_parse (res, ptr, pp);
1226 return (tmp1 != NULL) ? tmp1 : start;
1229 a_sheet = eval_sheet (res->a.sheet, pp->sheet);
1230 b_sheet = eval_sheet (res->b.sheet, a_sheet);
1232 a_ss = gnm_sheet_get_size2 (a_sheet, pp->wb);
1233 b_ss = gnm_sheet_get_size2 (b_sheet, pp->wb);
1235 tmp1 = col_parse (ptr, a_ss, &res->a.col, &res->a.col_relative);
1236 if (tmp1 == NULL) { /* check for row only ref 2:3 */
1237 tmp1 = row_parse (ptr, a_ss,
1238 &res->a.row, &res->a.row_relative);
1239 if (!tmp1 || *tmp1++ != ':') /* row only requires : even for singleton */
1240 return start;
1241 tmp2 = row_parse (tmp1, b_ss,
1242 &res->b.row, &res->b.row_relative);
1243 if (!tmp2)
1244 return start;
1245 res->a.col_relative = res->b.col_relative = FALSE;
1246 res->a.col = 0;
1247 res->b.col = b_ss->max_cols - 1;
1248 if (res->a.row_relative)
1249 res->a.row -= pp->eval.row;
1250 if (res->b.row_relative)
1251 res->b.row -= pp->eval.row;
1252 return tmp2;
1255 tmp2 = row_parse (tmp1, a_ss, &res->a.row, &res->a.row_relative);
1256 if (tmp2 == NULL) { /* check for col only ref B:C or R1C1 style */
1257 if (*tmp1++ != ':') /* col only requires : even for singleton */
1258 return start;
1259 tmp2 = col_parse (tmp1, a_ss,
1260 &res->b.col, &res->b.col_relative);
1261 if (!tmp2)
1262 return start;
1263 res->a.row_relative = res->b.row_relative = FALSE;
1264 res->a.row = 0;
1265 res->b.row = b_ss->max_rows - 1;
1266 if (res->a.col_relative)
1267 res->a.col -= pp->eval.col;
1268 if (res->b.col_relative)
1269 res->b.col -= pp->eval.col;
1270 return tmp2;
1273 if (res->a.col_relative)
1274 res->a.col -= pp->eval.col;
1275 if (res->a.row_relative)
1276 res->a.row -= pp->eval.row;
1278 ptr = tmp2;
1279 if (*ptr != ':')
1280 goto singleton;
1282 tmp1 = col_parse (ptr+1, b_ss, &res->b.col, &res->b.col_relative);
1283 if (!tmp1)
1284 goto singleton; /* strange, but valid singleton */
1285 tmp2 = row_parse (tmp1, b_ss, &res->b.row, &res->b.row_relative);
1286 if (!tmp2)
1287 goto singleton; /* strange, but valid singleton */
1289 if (res->b.col_relative)
1290 res->b.col -= pp->eval.col;
1291 if (res->b.row_relative)
1292 res->b.row -= pp->eval.row;
1293 return tmp2;
1295 singleton:
1296 res->b.col = res->a.col;
1297 res->b.row = res->a.row;
1298 res->b.col_relative = res->a.col_relative;
1299 res->b.row_relative = res->a.row_relative;
1300 return ptr;
1303 /* ------------------------------------------------------------------------- */
1305 static void
1306 std_expr_func_handler (GnmConventionsOut *out, GnmExprFunction const *func)
1308 char const *name = gnm_func_get_name (func->func,
1309 out->convs->localized_function_names);
1310 GString *target = out->accum;
1312 g_string_append (target, name);
1313 /* FIXME: possibly a space here. */
1314 gnm_expr_list_as_string (func->argc, func->argv, out);
1317 static void
1318 std_expr_name_handler (GnmConventionsOut *out, GnmExprName const *name)
1320 GnmNamedExpr const *thename = name->name;
1321 GString *target = out->accum;
1323 if (!expr_name_is_active (thename)) {
1324 g_string_append (target,
1325 value_error_name (GNM_ERROR_REF,
1326 out->convs->output.translated));
1327 return;
1330 if (name->optional_scope != NULL) {
1331 Workbook *out_wb = out->pp->wb
1332 ? out->pp->wb
1333 : out->pp->sheet->workbook;
1334 if (name->optional_scope->workbook != out_wb) {
1335 char *rel_uri = wb_rel_uri (name->optional_scope->workbook, out_wb);
1336 g_string_append_c (target, '[');
1337 g_string_append (target, rel_uri);
1338 g_string_append_c (target, ']');
1339 g_free (rel_uri);
1340 } else {
1341 g_string_append (target, name->optional_scope->name_quoted);
1342 g_string_append_unichar (target, out->convs->sheet_name_sep);
1344 } else if (out->pp->sheet != NULL &&
1345 thename->pos.sheet != NULL &&
1346 thename->pos.sheet != out->pp->sheet) {
1347 g_string_append (target, thename->pos.sheet->name_quoted);
1348 g_string_append_unichar (target, out->convs->sheet_name_sep);
1349 } else if (out->pp->sheet &&
1350 thename->pos.sheet == NULL &&
1351 expr_name_lookup (out->pp, expr_name_name (thename)) != thename) {
1352 /* Special syntax for global names shadowed by sheet names. */
1353 g_string_append (target, "[]");
1356 g_string_append (target, expr_name_name (thename));
1359 static void
1360 std_output_string (GnmConventionsOut *out, GOString const *str)
1362 go_strescape (out->accum, str->str);
1365 /* ------------------------------------------------------------------------- */
1367 static GString *
1368 std_sheet_name_quote (GnmConventions const *convs,
1369 char const *str)
1371 gunichar uc = g_utf8_get_char (str);
1372 GString *res = g_string_sized_new (20);
1373 char const *p;
1374 int nletters;
1375 int ndigits;
1377 if (g_ascii_isalpha (uc)) {
1378 nletters = 1;
1379 ndigits = 0;
1380 p = str + 1;
1381 } else if (g_unichar_isalpha (uc) || uc == '_') {
1382 nletters = -1;
1383 ndigits = -1;
1384 p = g_utf8_next_char (str);
1385 } else
1386 goto quoted;
1388 /* FIXME: What about '?' and '\\'. I cannot enter those. */
1390 for (; *p; p = g_utf8_next_char (p)) {
1391 uc = g_utf8_get_char (p);
1393 if (g_ascii_isalpha (uc)) {
1394 if (ndigits == 0)
1395 nletters++;
1396 } else if (g_ascii_isdigit (uc)) {
1397 if (ndigits >= 0)
1398 ndigits++;
1399 } else if (uc == '.' || uc == '_' || g_unichar_isalpha (uc))
1400 nletters = ndigits = -1;
1401 else
1402 goto quoted;
1405 if (ndigits > 0) {
1406 static const GnmSheetSize max_size = {
1407 GNM_MAX_COLS, GNM_MAX_ROWS
1410 * Excel also quotes things that look like cell references.
1411 * Precisely, check for a match against
1412 * ([A-Za-z]+)0*([1-9][0-9]*)
1413 * where $1 is a valid column name and $2 is a valid row
1414 * number. (The 0* is an Excel bug.)
1417 int col, row;
1418 unsigned char col_relative, row_relative;
1419 if (!col_parse (str, &max_size, &col, &col_relative))
1420 goto unquoted;
1422 p = str + nletters;
1423 while (*p == '0')
1424 p++, ndigits--;
1425 if (!row_parse (p, &max_size, &row, &row_relative))
1426 goto unquoted;
1428 goto quoted;
1431 unquoted:
1432 g_string_append (res, str);
1433 return res;
1435 quoted:
1436 g_string_append_c (res, '\'');
1437 /* This is UTF-8 safe. */
1438 for (; *str; str++) {
1439 gchar c = *str;
1440 if (c == '\'' || c == '\\')
1441 g_string_append_c (res, '\\');
1442 g_string_append_c (res, c);
1444 g_string_append_c (res, '\'');
1446 return res;
1449 static char const *
1450 std_name_parser (char const *str,
1451 G_GNUC_UNUSED GnmConventions const *convs)
1453 gunichar uc = g_utf8_get_char (str);
1455 if (!g_unichar_isalpha (uc) && uc != '_' && uc != '\\')
1456 return NULL;
1458 do {
1459 str = g_utf8_next_char (str);
1460 uc = g_utf8_get_char (str);
1461 } while (g_unichar_isalnum (uc) ||
1462 uc == '_' ||
1463 uc == '?' ||
1464 uc == '\\' ||
1465 uc == '.');
1467 return str;
1470 static GnmExpr const *
1471 std_func_map (GnmConventions const *convs, Workbook *scope,
1472 char const *name, GnmExprList *args)
1474 GnmFunc *f = convs->localized_function_names
1475 ? gnm_func_lookup_localized (name, scope)
1476 : gnm_func_lookup (name, scope);
1478 if (!f) {
1479 f = convs->localized_function_names
1480 ? gnm_func_add_placeholder_localized (NULL, name)
1481 : gnm_func_add_placeholder_localized (name, NULL);
1484 return gnm_expr_new_funcall (f, args);
1487 static Workbook *
1488 std_external_wb (G_GNUC_UNUSED GnmConventions const *convs,
1489 Workbook *ref_wb,
1490 const char *wb_name)
1492 const char *ref_uri = ref_wb ? go_doc_get_uri ((GODoc *)ref_wb) : NULL;
1493 return gnm_app_workbook_get_by_name (wb_name, ref_uri);
1496 static char const *
1497 std_string_parser (char const *in, GString *target,
1498 G_GNUC_UNUSED GnmConventions const *convs)
1500 return go_strunescape (target, in);
1504 * gnm_conventions_new_full:
1505 * @size:
1507 * Construct a GnmConventions of @size.
1509 * Returns: (transfer full): A #GnmConventions with default values.
1511 GnmConventions *
1512 gnm_conventions_new_full (unsigned size)
1514 GnmConventions *convs;
1516 g_return_val_if_fail (size >= sizeof (GnmConventions), NULL);
1518 convs = g_malloc0 (size);
1519 convs->ref_count = 1;
1521 convs->r1c1_addresses = FALSE;
1522 convs->localized_function_names = FALSE;
1524 convs->sheet_name_sep = '!';
1525 convs->intersection_char = ' ';
1526 convs->exp_is_left_associative = FALSE;
1527 convs->input.range_ref = rangeref_parse;
1528 convs->input.string = std_string_parser;
1529 convs->input.name = std_name_parser;
1530 convs->input.name_validate = expr_name_validate;
1531 convs->input.func = std_func_map;
1532 convs->input.external_wb = std_external_wb;
1534 convs->output.decimal_digits = -1;
1535 convs->output.translated = TRUE;
1536 convs->output.string = std_output_string;
1537 convs->output.name = std_expr_name_handler;
1538 convs->output.func = std_expr_func_handler;
1539 convs->output.cell_ref = cellref_as_string;
1540 convs->output.range_ref = rangeref_as_string;
1541 convs->output.boolean = NULL;
1542 convs->output.quote_sheet_name = std_sheet_name_quote;
1544 return convs;
1548 * gnm_conventions_new:
1550 * A convenience wrapper around gnm_conventions_new_full
1551 * that constructs a GnmConventions of std size.
1553 * Returns: (transfer full): A #GnmConventions with default values.
1555 GnmConventions *
1556 gnm_conventions_new (void)
1558 return gnm_conventions_new_full (sizeof (GnmConventions));
1562 * gnm_conventions_unref: (skip)
1563 * @c: (transfer full): #GnmConventions
1565 * Release a reference to a #GnmConvention
1567 void
1568 gnm_conventions_unref (GnmConventions *c)
1570 if (c == NULL)
1571 return;
1573 g_return_if_fail (c->ref_count > 0);
1575 c->ref_count--;
1576 if (c->ref_count > 0)
1577 return;
1579 g_free (c);
1583 * gnm_conventions_ref: (skip)
1584 * @c: (transfer none) (nullable): #GnmConventions
1586 * Returns: (transfer full) (nullable): a new reference to @c
1588 GnmConventions *
1589 gnm_conventions_ref (GnmConventions const *c)
1591 GnmConventions *uc = (GnmConventions *)c;
1592 if (uc)
1593 uc->ref_count++;
1594 return uc;
1597 GType
1598 gnm_conventions_get_type (void)
1600 static GType t = 0;
1602 if (t == 0) {
1603 t = g_boxed_type_register_static ("GnmConventions",
1604 (GBoxedCopyFunc)gnm_conventions_ref,
1605 (GBoxedFreeFunc)gnm_conventions_unref);
1607 return t;
1610 /* ------------------------------------------------------------------------- */
1612 GnmConventions const *gnm_conventions_default;
1613 GnmConventions const *gnm_conventions_xls_r1c1;
1615 void
1616 parse_util_init (void)
1618 GnmConventions *convs;
1620 convs = gnm_conventions_new ();
1621 convs->range_sep_colon = TRUE;
1622 convs->r1c1_addresses = FALSE;
1623 /* Not ready for general use yet. */
1624 convs->localized_function_names = g_getenv ("GNM_LOCAL_FUNCS") != NULL;
1625 gnm_conventions_default = convs;
1627 convs = gnm_conventions_new ();
1628 convs->range_sep_colon = TRUE;
1629 convs->r1c1_addresses = TRUE;
1630 convs->localized_function_names = gnm_conventions_default->localized_function_names;
1631 gnm_conventions_xls_r1c1 = convs;
1634 void
1635 parse_util_shutdown (void)
1637 gnm_conventions_unref ((GnmConventions *)gnm_conventions_default);
1638 gnm_conventions_default = NULL;
1639 gnm_conventions_unref ((GnmConventions *)gnm_conventions_xls_r1c1);
1640 gnm_conventions_xls_r1c1 = NULL;
1643 /* ------------------------------------------------------------------------- */
1645 * gnm_expr_conv_quote:
1646 * @convs: #GnmConventions
1647 * @str: string to quote
1649 * Returns: (transfer full): A quoted string according to @convs. If no
1650 * quoting is necessary, a literal copy of @str will be returned.
1652 GString *
1653 gnm_expr_conv_quote (GnmConventions const *convs,
1654 char const *str)
1656 g_return_val_if_fail (convs != NULL, NULL);
1657 g_return_val_if_fail (convs->output.quote_sheet_name != NULL, NULL);
1658 g_return_val_if_fail (str != NULL, NULL);
1659 g_return_val_if_fail (str[0] != 0, NULL);
1661 return convs->output.quote_sheet_name (convs, str);