1.12.39
[gnumeric.git] / src / parse-util.c
blobf27555b766baeb135a6197011a23545d757647af
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 GnmLexerItem *res = g_new (GnmLexerItem, 1);
55 *res = *li;
56 return res;
59 GType
60 gnm_lexer_item_get_type (void)
62 static GType t = 0;
64 if (t == 0) {
65 t = g_boxed_type_register_static ("GnmLexerItem",
66 (GBoxedCopyFunc)gnm_lexer_item_copy,
67 (GBoxedFreeFunc)g_free);
69 return t;
72 static void
73 col_name_internal (GString *target, int col)
75 static int const steps[] = {
76 26,
77 26 * 26,
78 26 * 26 * 26,
79 26 * 26 * 26 * 26,
80 26 * 26 * 26 * 26 * 26,
81 26 * 26 * 26 * 26 * 26 * 26,
82 INT_MAX
84 int i;
85 char *dst;
87 if (col < 0) {
88 /* Invalid column. */
89 g_string_append_printf (target, "[C%d]", col);
90 return;
93 for (i = 0; col >= steps[i]; i++)
94 col -= steps[i];
96 g_string_set_size (target, target->len + (i + 1));
97 dst = target->str + target->len;
98 while (i-- >= 0) {
99 *--dst = 'A' + col % 26;
100 col /= 26;
104 char const *
105 col_name (int col)
107 static GString *buffer = NULL;
108 if (!buffer)
109 buffer = g_string_new (NULL);
110 g_string_truncate (buffer, 0);
112 col_name_internal (buffer, col);
114 return buffer->str;
117 char const *
118 cols_name (int start_col, int end_col)
120 static GString *buffer = NULL;
121 if (!buffer)
122 buffer = g_string_new (NULL);
123 g_string_truncate (buffer, 0);
125 col_name_internal (buffer, start_col);
126 if (start_col != end_col) {
127 g_string_append_c (buffer, ':');
128 col_name_internal (buffer, end_col);
131 return buffer->str;
134 char const *
135 col_parse (char const *str, GnmSheetSize const *ss,
136 int *res, unsigned char *relative)
138 char const *ptr, *start = str;
139 int col = -1;
140 int max = ss->max_cols;
142 if (!(*relative = (*start != '$')))
143 start++;
145 for (ptr = start; col < max ; ptr++)
146 if (('a' <= *ptr && *ptr <= 'z'))
147 col = 26 * (col + 1) + (*ptr - 'a');
148 else if (('A' <= *ptr && *ptr <= 'Z'))
149 col = 26 * (col + 1) + (*ptr - 'A');
150 else if (ptr != start) {
151 *res = col;
152 return ptr;
153 } else
154 return NULL;
155 return NULL;
158 /***************************************************************************/
160 static void
161 row_name_internal (GString *target, int row)
163 g_string_append_printf (target, "%d", row + 1);
166 char const *
167 row_name (int row)
169 static GString *buffer = NULL;
170 if (!buffer)
171 buffer = g_string_new (NULL);
172 g_string_truncate (buffer, 0);
174 row_name_internal (buffer, row);
176 return buffer->str;
179 char const *
180 rows_name (int start_row, int end_row)
182 static GString *buffer = NULL;
183 if (!buffer)
184 buffer = g_string_new (NULL);
185 g_string_truncate (buffer, 0);
187 row_name_internal (buffer, start_row);
188 if (start_row != end_row) {
189 g_string_append_c (buffer, ':');
190 row_name_internal (buffer, end_row);
193 return buffer->str;
196 char const *
197 row_parse (char const *str, GnmSheetSize const *ss,
198 int *res, unsigned char *relative)
200 char const *end, *ptr = str;
201 long int row;
202 int max = ss->max_rows;
204 if (!(*relative = (*ptr != '$')))
205 ptr++;
207 /* Initial '0' is not allowed. */
208 if (*ptr <= '0' || *ptr > '9')
209 return NULL;
212 * Do not allow letters after the row number. If we did, then
213 * the name "K3P" would lex as the reference K3 followed by the
214 * name "P".
216 row = strtol (ptr, (char **)&end, 10);
217 if (ptr != end &&
218 !g_unichar_isalnum (g_utf8_get_char (end)) && *end != '_' &&
219 0 < row && row <= max) {
220 *res = row - 1;
221 return end;
222 } else
223 return NULL;
226 /***************************************************************************/
228 static void
229 r1c1_add_index (GString *target, char type, int num, unsigned char relative)
231 if (relative) {
232 if (num != 0)
233 g_string_append_printf (target, "%c[%d]", type, num);
234 else
235 g_string_append_c (target, type);
236 } else
237 g_string_append_printf (target, "%c%d", type, num + 1);
240 static char *
241 wb_rel_uri (Workbook *wb, Workbook *ref_wb)
243 char const *uri = go_doc_get_uri ((GODoc *)wb);
244 char const *ref_uri = go_doc_get_uri ((GODoc *)ref_wb);
245 char *rel_uri = go_url_make_relative (uri, ref_uri);
247 if (rel_uri == NULL || rel_uri[0] == '/') {
248 g_free (rel_uri);
249 return g_strdup (uri);
252 return rel_uri;
256 * cellref_as_string :
257 * @out: #GnmConventionsOut
258 * @cell_ref:
259 * @no_sheetname:
261 * Returns a string that the caller needs to free containing the A1 format
262 * representation of @ref as evaluated at @pp. @no_sheetname can be used to
263 * suppress the addition of the sheetname for non-local references.
265 void
266 cellref_as_string (GnmConventionsOut *out,
267 GnmCellRef const *cell_ref,
268 gboolean no_sheetname)
270 GString *target = out->accum;
271 Sheet const *sheet = cell_ref->sheet;
273 /* If it is a non-local reference, add the path to the external sheet */
274 if (sheet != NULL && !no_sheetname) {
275 if (out->pp->wb == NULL && out->pp->sheet == NULL)
276 /* For the expression leak printer. */
277 g_string_append (target, "'?'");
278 else if (NULL == out->pp->wb || sheet->workbook == out->pp->wb)
279 g_string_append (target, sheet->name_quoted);
280 else {
281 char *rel_uri = wb_rel_uri (sheet->workbook, out->pp->wb);
282 g_string_append_c (target, '[');
283 g_string_append (target, rel_uri);
284 g_string_append_c (target, ']');
285 g_string_append (target, sheet->name_quoted);
286 g_free (rel_uri);
288 g_string_append_unichar (target, out->convs->sheet_name_sep);
291 if (out->convs->r1c1_addresses) { /* R1C1 handler */
292 r1c1_add_index (target, 'R', cell_ref->row, cell_ref->row_relative);
293 r1c1_add_index (target, 'C', cell_ref->col, cell_ref->col_relative);
294 } else {
295 GnmCellPos pos;
296 Sheet const *size_sheet = eval_sheet (sheet, out->pp->sheet);
297 GnmSheetSize const *ss =
298 gnm_sheet_get_size2 (size_sheet, out->pp->wb);
300 gnm_cellpos_init_cellref_ss (&pos, cell_ref, &out->pp->eval, ss);
302 if (!cell_ref->col_relative)
303 g_string_append_c (target, '$');
304 col_name_internal (target, pos.col);
306 if (!cell_ref->row_relative)
307 g_string_append_c (target, '$');
308 row_name_internal (target, pos.row);
313 * rangeref_as_string :
314 * @out: #GnmConventionsOut
315 * @ref: #GnmRangeRef
318 void
319 rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
321 GnmRange r;
322 GString *target = out->accum;
323 Sheet *start_sheet, *end_sheet;
324 GnmSheetSize const *end_ss;
326 gnm_rangeref_normalize_pp (ref, out->pp, &start_sheet, &end_sheet, &r);
328 end_ss = gnm_sheet_get_size2 (end_sheet, out->pp->wb);
330 if (ref->a.sheet) {
331 if (NULL != out->pp->wb && ref->a.sheet->workbook != out->pp->wb) {
332 char *rel_uri = wb_rel_uri (ref->a.sheet->workbook, out->pp->wb);
333 g_string_append_c (target, '[');
334 g_string_append (target, rel_uri);
335 g_string_append_c (target, ']');
336 g_free (rel_uri);
338 if (out->pp->wb == NULL && out->pp->sheet == NULL)
339 /* For the expression leak printer. */
340 g_string_append (target, "'?'");
341 else if (ref->b.sheet == NULL || ref->a.sheet == ref->b.sheet)
342 g_string_append (target, ref->a.sheet->name_quoted);
343 else {
344 g_string_append (target, ref->a.sheet->name_quoted);
345 g_string_append_c (target, ':');
346 g_string_append (target, ref->b.sheet->name_quoted);
348 g_string_append_unichar (target, out->convs->sheet_name_sep);
351 if (out->convs->r1c1_addresses) { /* R1C1 handler */
352 /* be sure to use else if so that a1:iv65535 does not vanish */
353 if (r.start.col == 0 && r.end.col == end_ss->max_cols - 1) {
354 r1c1_add_index (target, 'R', ref->a.row, ref->a.row_relative);
355 if (ref->a.row != ref->b.row ||
356 ref->a.row_relative != ref->b.row_relative) {
357 g_string_append_c (target, ':');
358 r1c1_add_index (target, 'R', ref->b.row, ref->b.row_relative);
360 } else if (r.start.row == 0 && r.end.row == end_ss->max_rows - 1) {
361 r1c1_add_index (target, 'C', ref->a.col, ref->a.col_relative);
362 if (ref->a.col != ref->b.col ||
363 ref->a.col_relative != ref->b.col_relative) {
364 g_string_append_c (target, ':');
365 r1c1_add_index (target, 'C', ref->b.col, ref->b.col_relative);
367 } else {
368 r1c1_add_index (target, 'R', ref->a.row, ref->a.row_relative);
369 r1c1_add_index (target, 'C', ref->a.col, ref->a.col_relative);
370 if (r.start.col != r.end.col ||
371 ref->a.col_relative != ref->b.col_relative ||
372 r.start.row != r.end.row ||
373 ref->a.row_relative != ref->b.row_relative) {
374 g_string_append_c (target, ':');
375 r1c1_add_index (target, 'R', ref->b.row, ref->b.row_relative);
376 r1c1_add_index (target, 'C', ref->b.col, ref->b.col_relative);
379 } else {
380 /* be sure to use else if so that a1:iv65535 does not vanish */
381 if (r.start.col == 0 && r.end.col == end_ss->max_cols - 1) {
382 if (!ref->a.row_relative)
383 g_string_append_c (target, '$');
384 row_name_internal (target, r.start.row);
385 g_string_append_c (target, ':');
386 if (!ref->b.row_relative)
387 g_string_append_c (target, '$');
388 row_name_internal (target, r.end.row);
389 } else if (r.start.row == 0 && r.end.row == end_ss->max_rows - 1) {
390 if (!ref->a.col_relative)
391 g_string_append_c (target, '$');
392 col_name_internal (target, r.start.col);
393 g_string_append_c (target, ':');
394 if (!ref->b.col_relative)
395 g_string_append_c (target, '$');
396 col_name_internal (target, r.end.col);
397 } else {
398 if (!ref->a.col_relative)
399 g_string_append_c (target, '$');
400 col_name_internal (target, r.start.col);
401 if (!ref->a.row_relative)
402 g_string_append_c (target, '$');
403 row_name_internal (target, r.start.row);
405 if (r.start.col != r.end.col ||
406 ref->a.col_relative != ref->b.col_relative ||
407 r.start.row != r.end.row ||
408 ref->a.row_relative != ref->b.row_relative) {
409 g_string_append_c (target, ':');
410 if (!ref->b.col_relative)
411 g_string_append_c (target, '$');
412 col_name_internal (target, r.end.col);
413 if (!ref->b.row_relative)
414 g_string_append_c (target, '$');
415 row_name_internal (target, r.end.row);
422 * gnm_1_0_rangeref_as_string :
423 * @out: #GnmConventionsOut
424 * @ref: #GnmRangeRef
426 * Simplified variant of rangeref_as_string that old versions of gnumeric can
427 * read. It drops support for full col/row references. We can remap them on
428 * import.
430 * This function also ignores R1C1 settings.
432 void
433 gnm_1_0_rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
435 GnmRange r;
436 GString *target = out->accum;
437 Sheet *start_sheet, *end_sheet;
439 gnm_rangeref_normalize_pp (ref, out->pp, &start_sheet, &end_sheet, &r);
441 if (ref->a.sheet) {
442 if (NULL != out->pp->wb && ref->a.sheet->workbook != out->pp->wb) {
443 char *rel_uri = wb_rel_uri (ref->a.sheet->workbook, out->pp->wb);
444 g_string_append_c (target, '[');
445 g_string_append (target, rel_uri);
446 g_string_append_c (target, ']');
447 g_free (rel_uri);
449 if (out->pp->wb == NULL && out->pp->sheet == NULL)
450 /* For the expression leak printer. */
451 g_string_append (target, "'?'");
452 else if (ref->b.sheet == NULL || ref->a.sheet == ref->b.sheet)
453 g_string_append (target, ref->a.sheet->name_quoted);
454 else {
455 g_string_append (target, ref->a.sheet->name_quoted);
456 g_string_append_c (target, ':');
457 g_string_append (target, ref->b.sheet->name_quoted);
459 g_string_append_unichar (target, out->convs->sheet_name_sep);
462 if (!ref->a.col_relative)
463 g_string_append_c (target, '$');
464 col_name_internal (target, r.start.col);
465 if (!ref->a.row_relative)
466 g_string_append_c (target, '$');
467 row_name_internal (target, r.start.row);
469 if (r.start.col != r.end.col ||
470 ref->a.col_relative != ref->b.col_relative ||
471 r.start.row != r.end.row ||
472 ref->a.row_relative != ref->b.row_relative) {
473 g_string_append_c (target, ':');
474 if (!ref->b.col_relative)
475 g_string_append_c (target, '$');
476 col_name_internal (target, r.end.col);
477 if (!ref->b.row_relative)
478 g_string_append_c (target, '$');
479 row_name_internal (target, r.end.row);
483 static char const *
484 cellref_a1_get (GnmCellRef *out, GnmSheetSize const *ss,
485 char const *in, GnmCellPos const *pos)
487 int col;
488 int row;
490 g_return_val_if_fail (in != NULL, NULL);
491 g_return_val_if_fail (out != NULL, NULL);
493 in = col_parse (in, ss, &col, &out->col_relative);
494 if (!in)
495 return NULL;
497 in = row_parse (in, ss, &row, &out->row_relative);
498 if (!in)
499 return NULL;
501 /* Setup the cell reference information */
502 if (out->row_relative)
503 out->row = row - pos->row;
504 else
505 out->row = row;
507 if (out->col_relative)
508 out->col = col - pos->col;
509 else
510 out->col = col;
512 out->sheet = NULL;
514 return in;
517 /* skip first character (which was R or C) */
518 static char const *
519 r1c1_get_index (char const *str, GnmSheetSize const *ss,
520 int *num, unsigned char *relative, gboolean is_col)
522 char *end;
523 long l;
524 int max = is_col ? ss->max_cols : ss->max_rows;
526 if (str[0] == '\0')
527 return NULL;
529 str++;
530 *relative = (*str == '[');
531 if (*relative)
532 str++;
533 else if (*str == '-' || *str == '+') { /* handle RC-10 as RC followed by -10 */
534 *relative = TRUE;
535 *num = 0;
536 return str;
539 errno = 0;
540 *num = l = strtol (str, &end, 10);
541 if (errno == ERANGE || l <= G_MININT || l > G_MAXINT) {
542 /* Note: this includes G_MININT to avoid negation overflow. */
543 return NULL;
545 if (str == end) {
546 if (*relative)
547 return NULL;
548 *relative = TRUE;
549 *num = 0;
550 } else if (*relative) {
551 if (*end != ']')
552 return NULL;
553 *num = (*num > 0
554 ? *num % max
555 : -(-*num % max));
556 return end + 1;
557 } else {
558 if (*num <= 0 || *num > max)
559 return NULL;
560 (*num)--;
562 return end;
565 static char const *
566 cellref_r1c1_get (GnmCellRef *out, GnmSheetSize const *ss,
567 char const *in, GnmCellPos const *pos)
569 out->sheet = NULL;
570 if (*in != 'R' && *in != 'r')
571 return NULL;
572 if (NULL == (in = r1c1_get_index (in, ss,
573 &out->row, &out->row_relative,
574 FALSE)))
575 return NULL;
576 if (*in != 'C' && *in != 'c')
577 return NULL;
578 if (NULL == (in = r1c1_get_index (in, ss,
579 &out->col, &out->col_relative,
580 TRUE)))
581 return NULL;
582 if (g_ascii_isalpha (*in))
583 return NULL;
584 return in;
588 * cellref_parse:
589 * @out: destination GnmCellRef
590 * @in: reference description text, no leading
591 * whitespace allowed.
592 * @pos:
594 * Converts the char * representation of a Cell reference into
595 * an internal representation.
597 * Return value: a pointer to the character following the cellref.
599 char const *
600 cellref_parse (GnmCellRef *out, GnmSheetSize const *ss,
601 char const *in, GnmCellPos const *pos)
603 char const *res;
605 g_return_val_if_fail (in != NULL, NULL);
606 g_return_val_if_fail (out != NULL, NULL);
608 res = cellref_a1_get (out, ss, in, pos);
609 if (res != NULL)
610 return res;
611 return cellref_r1c1_get (out, ss, in, pos);
614 /****************************************************************************/
616 static char const *
617 cell_coord_name2 (int col, int row, gboolean r1c1)
619 static GString *buffer = NULL;
620 if (buffer)
621 g_string_truncate (buffer, 0);
622 else
623 buffer = g_string_new (NULL);
625 if (r1c1) {
626 r1c1_add_index (buffer, 'R', row, FALSE);
627 r1c1_add_index (buffer, 'C', col, FALSE);
628 } else {
629 col_name_internal (buffer, col);
630 row_name_internal (buffer, row);
633 return buffer->str;
636 char const *
637 cell_coord_name (int col, int row)
639 return cell_coord_name2 (col, row, FALSE);
642 char const *
643 cellpos_as_string (GnmCellPos const *pos)
645 g_return_val_if_fail (pos != NULL, "ERROR");
647 return cell_coord_name (pos->col, pos->row);
650 char const *
651 parsepos_as_string (GnmParsePos const *pp)
653 g_return_val_if_fail (pp != NULL, "ERROR");
655 return cell_coord_name2 (pp->eval.col,
656 pp->eval.row,
657 pp->sheet && pp->sheet->convs->r1c1_addresses);
660 char const *
661 cell_name (GnmCell const *cell)
663 g_return_val_if_fail (cell != NULL, "ERROR");
665 return cell_coord_name2 (cell->pos.col,
666 cell->pos.row,
667 cell->base.sheet->convs->r1c1_addresses);
671 * cellpos_parse:
672 * @cell_str: a string representation of a cell name.
673 * @ss: #GnmSheetSize
674 * @res: result
675 * @strict: if this is TRUE, then parsing stops at possible errors,
676 * otherwise an attempt is made to return cell names with
677 * trailing garbage.
679 * Return value: pointer to following char on success, NULL on failure.
680 * (In the strict case, that would be a pointer to the \0 or NULL.)
682 char const *
683 cellpos_parse (char const *cell_str, GnmSheetSize const *ss,
684 GnmCellPos *res, gboolean strict)
686 unsigned char dummy_relative;
688 cell_str = col_parse (cell_str, ss, &res->col, &dummy_relative);
689 if (!cell_str)
690 return NULL;
692 cell_str = row_parse (cell_str, ss, &res->row, &dummy_relative);
693 if (!cell_str)
694 return NULL;
696 if (*cell_str != 0 && strict)
697 return NULL;
699 return cell_str;
703 * gnm_expr_char_start_p:
705 * Can the supplied string be an expression ? It does not guarantee that it is,
706 * however, it is possible. If it is possible it strips off any header
707 * characters that are not relevant.
709 * NOTE : things like -1,234 will match
711 char const *
712 gnm_expr_char_start_p (char const * c)
714 char c0;
715 int N = 1;
717 if (NULL == c)
718 return NULL;
720 c0 = *c;
722 if (c0 == '=' || c0 == '@' || c0 == '+' || c0 == '-')
723 while (c[N] == ' ')
724 N++;
726 if (c0 == '=' || c0 == '@' || (c0 == '+' && c[1] == 0))
727 return c + N;
729 if ((c0 == '-' || c0 == '+') && c0 != c[1]) {
730 char *end;
733 * Ok, we have a string that
734 * 1. starts with a sign
735 * 2. does not start with the sign repeated (think --------)
736 * 3. is more than one character
738 * Now we check whether we have a number. We don't want
739 * numbers to be treated as formulae. FIXME: this really
740 * just checks for C-syntax numbers.
742 (void) gnm_strto (c, &end);
743 if (errno || *end != 0 || end == c)
744 return (c0 == '+') ? c + N : c;
745 /* Otherwise, it's a number. */
747 return NULL;
751 * parse_text_value_or_expr:
752 * @pos: If the string looks like an expression parse it at this location.
753 * @text: The text to be parsed.
754 * @val: Returns a GnmValue* if the text was a value, otherwise NULL.
755 * @texpr: Returns a GnmExprTop* if the text was an expression, otherwise NULL.
757 * Utility routine to parse a string and convert it into an expression or value.
759 * If there is a parse failure for an expression an error GnmValue with the syntax
760 * error is returned.
762 void
763 parse_text_value_or_expr (GnmParsePos const *pos, char const *text,
764 GnmValue **val, GnmExprTop const **texpr)
766 char const *expr_start;
767 GODateConventions const *date_conv;
768 GOFormat const *cur_fmt;
769 GOFormat const *cell_fmt;
770 GnmStyle const *cell_style;
772 *texpr = NULL;
773 *val = NULL;
775 /* Determine context information. */
776 date_conv =
777 pos->sheet
778 ? workbook_date_conv (pos->sheet->workbook)
779 : (pos->wb
780 ? workbook_date_conv (pos->wb)
781 : NULL);
782 cell_style = pos->sheet
783 ? sheet_style_get (pos->sheet, pos->eval.col, pos->eval.row)
784 : NULL;
785 cur_fmt = cell_fmt = cell_style ? gnm_style_get_format (cell_style) : NULL;
786 if (cell_fmt && go_format_is_general (cell_fmt)) {
787 GnmCell const *cell = pos->sheet
788 ? sheet_cell_get (pos->sheet, pos->eval.col, pos->eval.row)
789 : NULL;
790 if (cell && cell->value && VALUE_FMT (cell->value))
791 cur_fmt = VALUE_FMT (cell->value);
794 /* Does it match any formats? */
795 *val = format_match (text, cur_fmt, date_conv);
796 if (*val != NULL) {
797 GOFormat const *val_fmt = VALUE_FMT (*val);
798 /* Avoid value formats we don't need. */
799 if (val_fmt && go_format_eq (cell_fmt, val_fmt))
800 value_set_fmt (*val, NULL);
801 return;
804 /* If it does not match known formats, see if it is an expression */
805 expr_start = gnm_expr_char_start_p (text);
806 if (NULL != expr_start && *expr_start) {
807 *texpr = gnm_expr_parse_str (expr_start, pos,
808 GNM_EXPR_PARSE_DEFAULT, NULL, NULL);
809 if (*texpr != NULL)
810 return;
813 /* Fall back on string */
814 *val = value_new_string (text);
817 GnmParseError *
818 parse_error_init (GnmParseError *pe)
820 pe->err = NULL;
821 pe->begin_char = 0;
822 pe->end_char = 0;
824 return pe;
827 void
828 parse_error_free (GnmParseError *pe)
830 if (pe->err != NULL) {
831 g_error_free (pe->err);
832 pe->err = NULL;
836 static GnmParseError *
837 gnm_parse_error_copy (GnmParseError *pe)
839 GnmParseError *res = g_new (GnmParseError, 1);
840 res->begin_char = pe->begin_char;
841 res->end_char = pe->end_char;
842 res->err = (pe->err)? g_error_copy (pe->err): NULL;
843 return res;
846 GType
847 gnm_parse_error_get_type (void)
849 static GType t = 0;
851 if (t == 0) {
852 t = g_boxed_type_register_static ("GnmParseError",
853 (GBoxedCopyFunc)gnm_parse_error_copy,
854 (GBoxedFreeFunc)parse_error_free);
856 return t;
859 /***************************************************************************/
861 static char const *
862 check_quoted (char const *start, int *num_escapes)
864 char const *str = start;
865 if (*str == '\'' || *str == '\"') {
866 char const quote = *str++;
867 *num_escapes = 0;
868 for (; *str && *str != quote; str = g_utf8_next_char (str))
869 if (*str == '\\' && str[1]) {
870 str++;
871 (*num_escapes)++;
873 if (*str)
874 return str+1;
875 } else
876 *num_escapes = -1;
877 return start;
880 static void
881 unquote (char *dst, char const *src, int n)
883 while (n-- > 0)
884 if (*src == '\\' && src[1]) {
885 int l = g_utf8_skip [*(guchar *)(++src)];
886 strncpy (dst, src, l);
887 dst += l;
888 src += l;
889 n -= l;
890 } else
891 *dst++ = *src++;
892 *dst = 0;
896 * wbref_parse :
897 * @convs: #GnmConventions const
898 * @start:
899 * @wb:
901 * Returns : NULL if there is a valid workbook name but it is unknown.
902 * If the string is a valid workbook known name it returns a pointer
903 * the end of the name.
904 * Otherwise returns @start and does not modify @wb.
906 static char const *
907 wbref_parse (GnmConventions const *convs,
908 char const *start, Workbook **wb, Workbook *ref_wb)
910 /* Is this an external reference ? */
911 if (*start == '[') {
912 Workbook *tmp_wb;
914 int num_escapes;
915 char const *end = check_quoted (start+1, &num_escapes);
916 char *name;
918 if (end == start+1) {
919 end = strchr (start, ']');
920 if (end == NULL)
921 return start;
923 if (*end != ']')
924 return start;
926 if (num_escapes < 0)
927 name = g_strndup (start + 1, end - start - 1);
928 else {
929 name = g_malloc (1 + end - start - 2);
930 unquote (name, start+2, end-start-2);
933 tmp_wb = (*convs->input.external_wb) (convs, ref_wb, name);
934 g_free (name);
935 if (tmp_wb == NULL)
936 return NULL;
938 *wb = tmp_wb;
939 return end + 1;
942 return start;
946 * sheetref_parse :
947 * @convs:
948 * @start:
949 * @sheet:
950 * @wb:
951 * @allow_3d:
953 * Returns : NULL if there is a valid sheet name but it is unknown.
954 * If the string is a valid sheet name it returns a pointer
955 * the end of the name.
956 * Otherwise returns @start and does not modify @sheet.
958 static char const *
959 sheetref_parse (GnmConventions const *convs,
960 char const *start, Sheet **sheet, Workbook const *wb,
961 gboolean allow_3d)
963 GString *sheet_name;
964 char const *end;
966 *sheet = NULL;
967 if (*start == '\'' || *start == '"') {
968 sheet_name = g_string_new (NULL);
969 end = go_strunescape (sheet_name, start);
970 if (end == NULL) {
971 g_string_free (sheet_name, TRUE);
972 return start;
974 } else {
975 gboolean only_digits = TRUE;
976 end = start;
979 * Valid: Normal!a1
980 * Valid: x.y!a1
981 * Invalid: .y!a1
983 * Some names starting with digits are actually valid, but
984 * unparse quoted. Things are quite tricky: most sheet names
985 * starting with a digit are ok, but not those starting with
986 * "[0-9]*\." or "[0-9]+[eE]".
988 * Valid: 42!a1
989 * Valid: 4x!a1
990 * Invalid: 1.!a1
991 * Invalid: 1e!a1
994 while (1) {
995 gunichar uc = g_utf8_get_char (end);
996 if (g_unichar_isalpha (uc) || uc == '_') {
997 if (only_digits && end != start &&
998 (uc == 'e' || uc == 'E')) {
999 end = start;
1000 break;
1002 only_digits = FALSE;
1003 end = g_utf8_next_char (end);
1004 } else if (g_unichar_isdigit (uc)) {
1005 end = g_utf8_next_char (end);
1006 } else if (uc == '.') {
1007 /* Valid, except after only digits. */
1008 if (only_digits) {
1009 end = start;
1010 break;
1012 end++;
1013 } else
1014 break;
1017 if (*end != '!' && (!allow_3d || *end != ':'))
1018 return start;
1020 sheet_name = g_string_new_len (start, end - start);
1023 *sheet = workbook_sheet_by_name (wb, sheet_name->str);
1024 if (*sheet == NULL)
1025 end = start;
1027 g_string_free (sheet_name, TRUE);
1028 return end;
1031 static char const *
1032 r1c1_rangeref_parse (GnmRangeRef *res, char const *ptr, GnmParsePos const *pp)
1034 char const *tmp;
1035 GnmSheetSize const *a_ss, *b_ss;
1036 Sheet const *a_sheet, *b_sheet;
1038 a_sheet = eval_sheet (res->a.sheet, pp->sheet);
1039 b_sheet = eval_sheet (res->b.sheet, a_sheet);
1041 a_ss = gnm_sheet_get_size2 (a_sheet, pp->wb);
1042 b_ss = gnm_sheet_get_size2 (b_sheet, pp->wb);
1044 if (*ptr == 'R' || *ptr == 'r') {
1045 ptr = r1c1_get_index (ptr, a_ss,
1046 &res->a.row, &res->a.row_relative,
1047 FALSE);
1048 if (!ptr)
1049 return NULL;
1050 if (*ptr != 'C' && *ptr != 'c') {
1051 if (g_ascii_isalpha (*ptr))
1052 return NULL;
1053 /* full row R# */
1054 res->a.col_relative = FALSE;
1055 res->a.col = 0;
1056 res->b = res->a;
1057 res->b.col = a_ss->max_cols - 1;
1058 if (ptr[0] != ':' || (ptr[1] != 'R' && ptr[1] != 'r'))
1059 return ptr;
1060 tmp = r1c1_get_index (ptr+1, a_ss,
1061 &res->b.row, &res->b.row_relative,
1062 FALSE);
1063 if (!tmp)
1064 return ptr; /* fallback to just the initial R */
1065 return tmp;
1066 } else {
1067 ptr = r1c1_get_index (ptr, a_ss,
1068 &res->a.col, &res->a.col_relative,
1069 TRUE);
1070 if (!ptr)
1071 return NULL;
1074 res->b = res->a;
1075 if (ptr[0] != ':' || (ptr[1] != 'R' && ptr[1] != 'r') ||
1076 NULL == (tmp = r1c1_get_index (ptr+1, b_ss,
1077 &res->b.row, &res->b.row_relative, FALSE)) ||
1078 (*tmp != 'C' && *tmp != 'c') ||
1079 NULL == (tmp = r1c1_get_index (tmp, b_ss,
1080 &res->b.col, &res->b.col_relative, FALSE)))
1081 return ptr;
1082 return tmp;
1083 } else if (*ptr == 'C' || *ptr == 'c') {
1084 if (NULL == (ptr = r1c1_get_index (ptr, a_ss,
1085 &res->a.col, &res->a.col_relative, TRUE)))
1086 return NULL;
1087 if (g_ascii_isalpha (*ptr))
1088 return NULL;
1089 /* full col C[#] */
1090 res->a.row_relative = FALSE;
1091 res->a.row = 0;
1092 res->b = res->a;
1093 res->b.row = b_ss->max_rows - 1;
1094 if (ptr[0] != ':' || (ptr[1] != 'C' && ptr[1] != 'c'))
1095 return ptr;
1096 tmp = r1c1_get_index (ptr, b_ss,
1097 &res->b.col, &res->b.col_relative,
1098 TRUE);
1099 if (!tmp)
1100 return ptr; /* fallback to just the initial C */
1101 return tmp;
1104 return NULL;
1108 * rangeref_parse :
1109 * @res: where to store the result
1110 * @start: the start of the string to parse
1111 * @pp: the location to parse relative to
1112 * @convs: #GnmConventions
1114 * Returns a pointer to the first invalid character.
1115 * If the result != @start then @res is valid.
1117 char const *
1118 rangeref_parse (GnmRangeRef *res, char const *start, GnmParsePos const *pp,
1119 GnmConventions const *convs)
1121 char const *ptr = start, *start_sheet, *start_wb, *tmp1, *tmp2;
1122 Workbook *wb;
1123 Workbook *ref_wb;
1124 Sheet *a_sheet, *b_sheet;
1125 GnmSheetSize const *a_ss, *b_ss;
1127 g_return_val_if_fail (start != NULL, start);
1128 g_return_val_if_fail (pp != NULL, start);
1130 wb = pp->wb;
1131 ref_wb = wb ? wb : pp->sheet->workbook;
1132 start_wb = start;
1133 start_sheet = wbref_parse (convs, start, &wb, ref_wb);
1134 if (start_sheet == NULL)
1135 return start; /* TODO error unknown workbook */
1136 ptr = sheetref_parse (convs, start_sheet, &res->a.sheet, wb, TRUE);
1137 if (ptr == NULL)
1138 return start; /* TODO error unknown sheet */
1139 if (ptr != start_sheet) {
1140 const char *ref;
1142 if (*ptr == ':') { /* 3d ref */
1143 ptr = sheetref_parse (convs, ptr+1, &res->b.sheet, wb, FALSE);
1144 if (ptr == NULL)
1145 return start; /* TODO error unknown sheet */
1146 } else
1147 res->b.sheet = NULL;
1149 if (*ptr++ != '!')
1150 return start; /* TODO syntax error */
1152 ref = value_error_name (GNM_ERROR_REF, FALSE);
1153 if (strncmp (ptr, ref, strlen (ref)) == 0) {
1154 res->a.sheet = invalid_sheet;
1155 res->a.col = res->a.row = 0;
1156 res->a.col_relative = res->a.row_relative = FALSE;
1157 res->b.sheet = res->a.sheet;
1158 ptr += strlen (ref);
1159 return ptr;
1161 } else {
1162 if (start_sheet != start_wb)
1163 return start; /* Workbook, but no sheet. */
1164 res->b.sheet = NULL;
1167 if (convs->r1c1_addresses) { /* R1C1 handler */
1168 const char *tmp1 = r1c1_rangeref_parse (res, ptr, pp);
1169 return (tmp1 != NULL) ? tmp1 : start;
1172 a_sheet = eval_sheet (res->a.sheet, pp->sheet);
1173 b_sheet = eval_sheet (res->b.sheet, a_sheet);
1175 a_ss = gnm_sheet_get_size2 (a_sheet, pp->wb);
1176 b_ss = gnm_sheet_get_size2 (b_sheet, pp->wb);
1178 tmp1 = col_parse (ptr, a_ss, &res->a.col, &res->a.col_relative);
1179 if (tmp1 == NULL) { /* check for row only ref 2:3 */
1180 tmp1 = row_parse (ptr, a_ss,
1181 &res->a.row, &res->a.row_relative);
1182 if (!tmp1 || *tmp1++ != ':') /* row only requires : even for singleton */
1183 return start;
1184 tmp2 = row_parse (tmp1, b_ss,
1185 &res->b.row, &res->b.row_relative);
1186 if (!tmp2)
1187 return start;
1188 res->a.col_relative = res->b.col_relative = FALSE;
1189 res->a.col = 0;
1190 res->b.col = b_ss->max_cols - 1;
1191 if (res->a.row_relative)
1192 res->a.row -= pp->eval.row;
1193 if (res->b.row_relative)
1194 res->b.row -= pp->eval.row;
1195 return tmp2;
1198 tmp2 = row_parse (tmp1, a_ss, &res->a.row, &res->a.row_relative);
1199 if (tmp2 == NULL) { /* check for col only ref B:C or R1C1 style */
1200 if (*tmp1++ != ':') /* col only requires : even for singleton */
1201 return start;
1202 tmp2 = col_parse (tmp1, a_ss,
1203 &res->b.col, &res->b.col_relative);
1204 if (!tmp2)
1205 return start;
1206 res->a.row_relative = res->b.row_relative = FALSE;
1207 res->a.row = 0;
1208 res->b.row = b_ss->max_rows - 1;
1209 if (res->a.col_relative)
1210 res->a.col -= pp->eval.col;
1211 if (res->b.col_relative)
1212 res->b.col -= pp->eval.col;
1213 return tmp2;
1216 if (res->a.col_relative)
1217 res->a.col -= pp->eval.col;
1218 if (res->a.row_relative)
1219 res->a.row -= pp->eval.row;
1221 ptr = tmp2;
1222 if (*ptr != ':')
1223 goto singleton;
1225 tmp1 = col_parse (ptr+1, b_ss, &res->b.col, &res->b.col_relative);
1226 if (!tmp1)
1227 goto singleton; /* strange, but valid singleton */
1228 tmp2 = row_parse (tmp1, b_ss, &res->b.row, &res->b.row_relative);
1229 if (!tmp2)
1230 goto singleton; /* strange, but valid singleton */
1232 if (res->b.col_relative)
1233 res->b.col -= pp->eval.col;
1234 if (res->b.row_relative)
1235 res->b.row -= pp->eval.row;
1236 return tmp2;
1238 singleton:
1239 res->b.col = res->a.col;
1240 res->b.row = res->a.row;
1241 res->b.col_relative = res->a.col_relative;
1242 res->b.row_relative = res->a.row_relative;
1243 return ptr;
1246 /* ------------------------------------------------------------------------- */
1248 static void
1249 std_expr_func_handler (GnmConventionsOut *out, GnmExprFunction const *func)
1251 char const *name = gnm_func_get_name (func->func,
1252 out->convs->localized_function_names);
1253 GString *target = out->accum;
1255 g_string_append (target, name);
1256 /* FIXME: possibly a space here. */
1257 gnm_expr_list_as_string (func->argc, func->argv, out);
1260 static void
1261 std_expr_name_handler (GnmConventionsOut *out, GnmExprName const *name)
1263 GnmNamedExpr const *thename = name->name;
1264 GString *target = out->accum;
1266 if (!expr_name_is_active (thename)) {
1267 g_string_append (target,
1268 value_error_name (GNM_ERROR_REF,
1269 out->convs->output.translated));
1270 return;
1273 if (name->optional_scope != NULL) {
1274 Workbook *out_wb = out->pp->wb
1275 ? out->pp->wb
1276 : out->pp->sheet->workbook;
1277 if (name->optional_scope->workbook != out_wb) {
1278 char *rel_uri = wb_rel_uri (name->optional_scope->workbook, out_wb);
1279 g_string_append_c (target, '[');
1280 g_string_append (target, rel_uri);
1281 g_string_append_c (target, ']');
1282 g_free (rel_uri);
1283 } else {
1284 g_string_append (target, name->optional_scope->name_quoted);
1285 g_string_append_unichar (target, out->convs->sheet_name_sep);
1287 } else if (out->pp->sheet != NULL &&
1288 thename->pos.sheet != NULL &&
1289 thename->pos.sheet != out->pp->sheet) {
1290 g_string_append (target, thename->pos.sheet->name_quoted);
1291 g_string_append_unichar (target, out->convs->sheet_name_sep);
1292 } else if (out->pp->sheet &&
1293 thename->pos.sheet == NULL &&
1294 expr_name_lookup (out->pp, expr_name_name (thename)) != thename) {
1295 /* Special syntax for global names shadowed by sheet names. */
1296 g_string_append (target, "[]");
1299 g_string_append (target, expr_name_name (thename));
1302 static void
1303 std_output_string (GnmConventionsOut *out, GOString const *str)
1305 go_strescape (out->accum, str->str);
1308 /* ------------------------------------------------------------------------- */
1310 static GString *
1311 std_sheet_name_quote (GnmConventions const *convs,
1312 char const *str)
1314 gunichar uc = g_utf8_get_char (str);
1315 GString *res = g_string_sized_new (20);
1316 char const *p;
1317 int nletters;
1318 int ndigits;
1320 if (g_ascii_isalpha (uc)) {
1321 nletters = 1;
1322 ndigits = 0;
1323 p = str + 1;
1324 } else if (g_unichar_isalpha (uc) || uc == '_') {
1325 nletters = -1;
1326 ndigits = -1;
1327 p = g_utf8_next_char (str);
1328 } else
1329 goto quoted;
1331 /* FIXME: What about '?' and '\\'. I cannot enter those. */
1333 for (; *p; p = g_utf8_next_char (p)) {
1334 uc = g_utf8_get_char (p);
1336 if (g_ascii_isalpha (uc)) {
1337 if (ndigits == 0)
1338 nletters++;
1339 } else if (g_ascii_isdigit (uc)) {
1340 if (ndigits >= 0)
1341 ndigits++;
1342 } else if (uc == '.' || uc == '_' || g_unichar_isalpha (uc))
1343 nletters = ndigits = -1;
1344 else
1345 goto quoted;
1348 if (ndigits > 0) {
1349 static const GnmSheetSize max_size = {
1350 GNM_MAX_COLS, GNM_MAX_ROWS
1353 * Excel also quotes things that look like cell references.
1354 * Precisely, check for a match against
1355 * ([A-Za-z]+)0*([1-9][0-9]*)
1356 * where $1 is a valid column name and $2 is a valid row
1357 * number. (The 0* is an Excel bug.)
1360 int col, row;
1361 unsigned char col_relative, row_relative;
1362 if (!col_parse (str, &max_size, &col, &col_relative))
1363 goto unquoted;
1365 p = str + nletters;
1366 while (*p == '0')
1367 p++, ndigits--;
1368 if (!row_parse (p, &max_size, &row, &row_relative))
1369 goto unquoted;
1371 goto quoted;
1374 unquoted:
1375 g_string_append (res, str);
1376 return res;
1378 quoted:
1379 g_string_append_c (res, '\'');
1380 /* This is UTF-8 safe. */
1381 for (; *str; str++) {
1382 gchar c = *str;
1383 if (c == '\'' || c == '\\')
1384 g_string_append_c (res, '\\');
1385 g_string_append_c (res, c);
1387 g_string_append_c (res, '\'');
1389 return res;
1392 static char const *
1393 std_name_parser (char const *str,
1394 G_GNUC_UNUSED GnmConventions const *convs)
1396 gunichar uc = g_utf8_get_char (str);
1398 if (!g_unichar_isalpha (uc) && uc != '_' && uc != '\\')
1399 return NULL;
1401 do {
1402 str = g_utf8_next_char (str);
1403 uc = g_utf8_get_char (str);
1404 } while (g_unichar_isalnum (uc) ||
1405 uc == '_' ||
1406 uc == '?' ||
1407 uc == '\\' ||
1408 uc == '.');
1410 return str;
1413 static GnmExpr const *
1414 std_func_map (GnmConventions const *convs, Workbook *scope,
1415 char const *name, GnmExprList *args)
1417 GnmFunc *f = convs->localized_function_names
1418 ? gnm_func_lookup_localized (name, scope)
1419 : gnm_func_lookup (name, scope);
1421 if (!f) {
1422 f = convs->localized_function_names
1423 ? gnm_func_add_placeholder_localized (NULL, name)
1424 : gnm_func_add_placeholder_localized (name, NULL);
1427 return gnm_expr_new_funcall (f, args);
1430 static Workbook *
1431 std_external_wb (G_GNUC_UNUSED GnmConventions const *convs,
1432 Workbook *ref_wb,
1433 const char *wb_name)
1435 const char *ref_uri = ref_wb ? go_doc_get_uri ((GODoc *)ref_wb) : NULL;
1436 return gnm_app_workbook_get_by_name (wb_name, ref_uri);
1439 static char const *
1440 std_string_parser (char const *in, GString *target,
1441 G_GNUC_UNUSED GnmConventions const *convs)
1443 return go_strunescape (target, in);
1447 * gnm_conventions_new_full :
1448 * @size:
1450 * Construct a GnmConventions of @size.
1452 * Returns a GnmConventions with default values. Caller is responsible for
1453 * freeing the result.
1455 GnmConventions *
1456 gnm_conventions_new_full (unsigned size)
1458 GnmConventions *convs;
1460 g_return_val_if_fail (size >= sizeof (GnmConventions), NULL);
1462 convs = g_malloc0 (size);
1463 convs->ref_count = 1;
1465 convs->r1c1_addresses = FALSE;
1466 convs->localized_function_names = FALSE;
1468 convs->sheet_name_sep = '!';
1469 convs->intersection_char = ' ';
1470 convs->exp_is_left_associative = FALSE;
1471 convs->input.range_ref = rangeref_parse;
1472 convs->input.string = std_string_parser;
1473 convs->input.name = std_name_parser;
1474 convs->input.name_validate = expr_name_validate;
1475 convs->input.func = std_func_map;
1476 convs->input.external_wb = std_external_wb;
1478 convs->output.decimal_digits = -1;
1479 convs->output.translated = TRUE;
1480 convs->output.string = std_output_string;
1481 convs->output.name = std_expr_name_handler;
1482 convs->output.func = std_expr_func_handler;
1483 convs->output.cell_ref = cellref_as_string;
1484 convs->output.range_ref = rangeref_as_string;
1485 convs->output.boolean = NULL;
1486 convs->output.quote_sheet_name = std_sheet_name_quote;
1488 return convs;
1492 * gnm_conventions_new :
1494 * A convenience wrapper around gnm_conventions_new_full
1495 * that constructs a GnmConventions of std size.
1497 * Returns a GnmConventions with default values. Caller is responsible for
1498 * freeing the result.
1500 GnmConventions *
1501 gnm_conventions_new (void)
1503 return gnm_conventions_new_full (sizeof (GnmConventions));
1507 * gnm_conventions_unref :
1508 * @c: #GnmConventions
1510 * Release a convention
1512 void
1513 gnm_conventions_unref (GnmConventions *c)
1515 if (c == NULL)
1516 return;
1518 g_return_if_fail (c->ref_count > 0);
1520 c->ref_count--;
1521 if (c->ref_count > 0)
1522 return;
1524 g_free (c);
1527 GnmConventions *
1528 gnm_conventions_ref (GnmConventions *c)
1530 if (c)
1531 c->ref_count++;
1532 return c;
1535 GType
1536 gnm_conventions_get_type (void)
1538 static GType t = 0;
1540 if (t == 0) {
1541 t = g_boxed_type_register_static ("GnmConventions",
1542 (GBoxedCopyFunc)gnm_conventions_ref,
1543 (GBoxedFreeFunc)gnm_conventions_unref);
1545 return t;
1548 /* ------------------------------------------------------------------------- */
1550 GnmConventions const *gnm_conventions_default;
1551 GnmConventions const *gnm_conventions_xls_r1c1;
1553 void
1554 parse_util_init (void)
1556 GnmConventions *convs;
1558 convs = gnm_conventions_new ();
1559 convs->range_sep_colon = TRUE;
1560 convs->r1c1_addresses = FALSE;
1561 /* Not ready for general use yet. */
1562 convs->localized_function_names = g_getenv ("GNM_LOCAL_FUNCS") != NULL;
1563 gnm_conventions_default = convs;
1565 convs = gnm_conventions_new ();
1566 convs->range_sep_colon = TRUE;
1567 convs->r1c1_addresses = TRUE;
1568 convs->localized_function_names = gnm_conventions_default->localized_function_names;
1569 gnm_conventions_xls_r1c1 = convs;
1572 void
1573 parse_util_shutdown (void)
1575 gnm_conventions_unref ((GnmConventions *)gnm_conventions_default);
1576 gnm_conventions_default = NULL;
1577 gnm_conventions_unref ((GnmConventions *)gnm_conventions_xls_r1c1);
1578 gnm_conventions_xls_r1c1 = NULL;
1581 /* ------------------------------------------------------------------------- */
1583 * gnm_expr_conv_quote:
1584 * @convs: #GnmConventions
1585 * @str: string to quote
1587 * Quotes @str according to the convention @convs if necessary.
1588 * or returns a literal copy of @str if no quoting was needed.
1590 * Return value: caller is responsible for the resulting GString
1592 GString *
1593 gnm_expr_conv_quote (GnmConventions const *convs,
1594 char const *str)
1596 g_return_val_if_fail (convs != NULL, NULL);
1597 g_return_val_if_fail (convs->output.quote_sheet_name != NULL, NULL);
1598 g_return_val_if_fail (str != NULL, NULL);
1599 g_return_val_if_fail (str[0] != 0, NULL);
1601 return convs->output.quote_sheet_name (convs, str);