1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
24 #include <gnumeric-config.h>
26 #include "parse-util.h"
28 #include "application.h"
35 #include "number-match.h"
36 #include "gnm-format.h"
37 #include "expr-name.h"
40 #include "sheet-style.h"
41 /* For std_expr_name_handler: */
42 #include "expr-impl.h"
44 #include <goffice/goffice.h>
52 gnm_lexer_item_copy (GnmLexerItem
*li
)
54 GnmLexerItem
*res
= g_new (GnmLexerItem
, 1);
60 gnm_lexer_item_get_type (void)
65 t
= g_boxed_type_register_static ("GnmLexerItem",
66 (GBoxedCopyFunc
)gnm_lexer_item_copy
,
67 (GBoxedFreeFunc
)g_free
);
73 col_name_internal (GString
*target
, int col
)
75 static int const steps
[] = {
80 26 * 26 * 26 * 26 * 26,
81 26 * 26 * 26 * 26 * 26 * 26,
89 g_string_append_printf (target
, "[C%d]", col
);
93 for (i
= 0; col
>= steps
[i
]; i
++)
96 g_string_set_size (target
, target
->len
+ (i
+ 1));
97 dst
= target
->str
+ target
->len
;
99 *--dst
= 'A' + col
% 26;
107 static GString
*buffer
= NULL
;
109 buffer
= g_string_new (NULL
);
110 g_string_truncate (buffer
, 0);
112 col_name_internal (buffer
, col
);
118 cols_name (int start_col
, int end_col
)
120 static GString
*buffer
= NULL
;
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
);
135 col_parse (char const *str
, GnmSheetSize
const *ss
,
136 int *res
, unsigned char *relative
)
138 char const *ptr
, *start
= str
;
140 int max
= ss
->max_cols
;
142 if (!(*relative
= (*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
) {
158 /***************************************************************************/
161 row_name_internal (GString
*target
, int row
)
163 g_string_append_printf (target
, "%d", row
+ 1);
169 static GString
*buffer
= NULL
;
171 buffer
= g_string_new (NULL
);
172 g_string_truncate (buffer
, 0);
174 row_name_internal (buffer
, row
);
180 rows_name (int start_row
, int end_row
)
182 static GString
*buffer
= NULL
;
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
);
197 row_parse (char const *str
, GnmSheetSize
const *ss
,
198 int *res
, unsigned char *relative
)
200 char const *end
, *ptr
= str
;
202 int max
= ss
->max_rows
;
204 if (!(*relative
= (*ptr
!= '$')))
207 /* Initial '0' is not allowed. */
208 if (*ptr
<= '0' || *ptr
> '9')
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
216 row
= strtol (ptr
, (char **)&end
, 10);
218 !g_unichar_isalnum (g_utf8_get_char (end
)) && *end
!= '_' &&
219 0 < row
&& row
<= max
) {
226 /***************************************************************************/
229 r1c1_add_index (GString
*target
, char type
, int num
, unsigned char relative
)
233 g_string_append_printf (target
, "%c[%d]", type
, num
);
235 g_string_append_c (target
, type
);
237 g_string_append_printf (target
, "%c%d", type
, num
+ 1);
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] == '/') {
249 return g_strdup (uri
);
256 * cellref_as_string :
257 * @out: #GnmConventionsOut
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.
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
);
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
);
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
);
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
319 rangeref_as_string (GnmConventionsOut
*out
, GnmRangeRef
const *ref
)
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
);
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
, ']');
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
);
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
);
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
);
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
);
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
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
430 * This function also ignores R1C1 settings.
433 gnm_1_0_rangeref_as_string (GnmConventionsOut
*out
, GnmRangeRef
const *ref
)
436 GString
*target
= out
->accum
;
437 Sheet
*start_sheet
, *end_sheet
;
439 gnm_rangeref_normalize_pp (ref
, out
->pp
, &start_sheet
, &end_sheet
, &r
);
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
, ']');
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
);
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
);
484 cellref_a1_get (GnmCellRef
*out
, GnmSheetSize
const *ss
,
485 char const *in
, GnmCellPos
const *pos
)
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
);
497 in
= row_parse (in
, ss
, &row
, &out
->row_relative
);
501 /* Setup the cell reference information */
502 if (out
->row_relative
)
503 out
->row
= row
- pos
->row
;
507 if (out
->col_relative
)
508 out
->col
= col
- pos
->col
;
517 /* skip first character (which was R or C) */
519 r1c1_get_index (char const *str
, GnmSheetSize
const *ss
,
520 int *num
, unsigned char *relative
, gboolean is_col
)
524 int max
= is_col
? ss
->max_cols
: ss
->max_rows
;
530 *relative
= (*str
== '[');
533 else if (*str
== '-' || *str
== '+') { /* handle RC-10 as RC followed by -10 */
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. */
550 } else if (*relative
) {
558 if (*num
<= 0 || *num
> max
)
566 cellref_r1c1_get (GnmCellRef
*out
, GnmSheetSize
const *ss
,
567 char const *in
, GnmCellPos
const *pos
)
570 if (*in
!= 'R' && *in
!= 'r')
572 if (NULL
== (in
= r1c1_get_index (in
, ss
,
573 &out
->row
, &out
->row_relative
,
576 if (*in
!= 'C' && *in
!= 'c')
578 if (NULL
== (in
= r1c1_get_index (in
, ss
,
579 &out
->col
, &out
->col_relative
,
582 if (g_ascii_isalpha (*in
))
589 * @out: destination GnmCellRef
590 * @in: reference description text, no leading
591 * whitespace allowed.
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.
600 cellref_parse (GnmCellRef
*out
, GnmSheetSize
const *ss
,
601 char const *in
, GnmCellPos
const *pos
)
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
);
611 return cellref_r1c1_get (out
, ss
, in
, pos
);
614 /****************************************************************************/
617 cell_coord_name2 (int col
, int row
, gboolean r1c1
)
619 static GString
*buffer
= NULL
;
621 g_string_truncate (buffer
, 0);
623 buffer
= g_string_new (NULL
);
626 r1c1_add_index (buffer
, 'R', row
, FALSE
);
627 r1c1_add_index (buffer
, 'C', col
, FALSE
);
629 col_name_internal (buffer
, col
);
630 row_name_internal (buffer
, row
);
637 cell_coord_name (int col
, int row
)
639 return cell_coord_name2 (col
, row
, FALSE
);
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
);
651 parsepos_as_string (GnmParsePos
const *pp
)
653 g_return_val_if_fail (pp
!= NULL
, "ERROR");
655 return cell_coord_name2 (pp
->eval
.col
,
657 pp
->sheet
&& pp
->sheet
->convs
->r1c1_addresses
);
661 cell_name (GnmCell
const *cell
)
663 g_return_val_if_fail (cell
!= NULL
, "ERROR");
665 return cell_coord_name2 (cell
->pos
.col
,
667 cell
->base
.sheet
->convs
->r1c1_addresses
);
672 * @cell_str: a string representation of a cell name.
675 * @strict: if this is TRUE, then parsing stops at possible errors,
676 * otherwise an attempt is made to return cell names with
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.)
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
);
692 cell_str
= row_parse (cell_str
, ss
, &res
->row
, &dummy_relative
);
696 if (*cell_str
!= 0 && strict
)
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
712 gnm_expr_char_start_p (char const * c
)
722 if (c0
== '=' || c0
== '@' || c0
== '+' || c0
== '-')
726 if (c0
== '=' || c0
== '@' || (c0
== '+' && c
[1] == 0))
729 if ((c0
== '-' || c0
== '+') && c0
!= c
[1]) {
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. */
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
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
;
775 /* Determine context information. */
778 ? workbook_date_conv (pos
->sheet
->workbook
)
780 ? workbook_date_conv (pos
->wb
)
782 cell_style
= pos
->sheet
783 ? sheet_style_get (pos
->sheet
, pos
->eval
.col
, pos
->eval
.row
)
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
)
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
);
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
);
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
);
813 /* Fall back on string */
814 *val
= value_new_string (text
);
818 parse_error_init (GnmParseError
*pe
)
828 parse_error_free (GnmParseError
*pe
)
830 if (pe
->err
!= NULL
) {
831 g_error_free (pe
->err
);
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
;
847 gnm_parse_error_get_type (void)
852 t
= g_boxed_type_register_static ("GnmParseError",
853 (GBoxedCopyFunc
)gnm_parse_error_copy
,
854 (GBoxedFreeFunc
)parse_error_free
);
859 /***************************************************************************/
862 check_quoted (char const *start
, int *num_escapes
)
864 char const *str
= start
;
865 if (*str
== '\'' || *str
== '\"') {
866 char const quote
= *str
++;
868 for (; *str
&& *str
!= quote
; str
= g_utf8_next_char (str
))
869 if (*str
== '\\' && str
[1]) {
881 unquote (char *dst
, char const *src
, int n
)
884 if (*src
== '\\' && src
[1]) {
885 int l
= g_utf8_skip
[*(guchar
*)(++src
)];
886 strncpy (dst
, src
, l
);
897 * @convs: #GnmConventions const
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.
907 wbref_parse (GnmConventions
const *convs
,
908 char const *start
, Workbook
**wb
, Workbook
*ref_wb
)
910 /* Is this an external reference ? */
915 char const *end
= check_quoted (start
+1, &num_escapes
);
918 if (end
== start
+1) {
919 end
= strchr (start
, ']');
927 name
= g_strndup (start
+ 1, end
- start
- 1);
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
);
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.
959 sheetref_parse (GnmConventions
const *convs
,
960 char const *start
, Sheet
**sheet
, Workbook
const *wb
,
967 if (*start
== '\'' || *start
== '"') {
968 sheet_name
= g_string_new (NULL
);
969 end
= go_strunescape (sheet_name
, start
);
971 g_string_free (sheet_name
, TRUE
);
975 gboolean only_digits
= TRUE
;
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]".
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')) {
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. */
1017 if (*end
!= '!' && (!allow_3d
|| *end
!= ':'))
1020 sheet_name
= g_string_new_len (start
, end
- start
);
1023 *sheet
= workbook_sheet_by_name (wb
, sheet_name
->str
);
1027 g_string_free (sheet_name
, TRUE
);
1032 r1c1_rangeref_parse (GnmRangeRef
*res
, char const *ptr
, GnmParsePos
const *pp
)
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
,
1050 if (*ptr
!= 'C' && *ptr
!= 'c') {
1051 if (g_ascii_isalpha (*ptr
))
1054 res
->a
.col_relative
= FALSE
;
1057 res
->b
.col
= a_ss
->max_cols
- 1;
1058 if (ptr
[0] != ':' || (ptr
[1] != 'R' && ptr
[1] != 'r'))
1060 tmp
= r1c1_get_index (ptr
+1, a_ss
,
1061 &res
->b
.row
, &res
->b
.row_relative
,
1064 return ptr
; /* fallback to just the initial R */
1067 ptr
= r1c1_get_index (ptr
, a_ss
,
1068 &res
->a
.col
, &res
->a
.col_relative
,
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
)))
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
)))
1087 if (g_ascii_isalpha (*ptr
))
1090 res
->a
.row_relative
= FALSE
;
1093 res
->b
.row
= b_ss
->max_rows
- 1;
1094 if (ptr
[0] != ':' || (ptr
[1] != 'C' && ptr
[1] != 'c'))
1096 tmp
= r1c1_get_index (ptr
, b_ss
,
1097 &res
->b
.col
, &res
->b
.col_relative
,
1100 return ptr
; /* fallback to just the initial C */
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.
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
;
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
);
1131 ref_wb
= wb
? wb
: pp
->sheet
->workbook
;
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
);
1138 return start
; /* TODO error unknown sheet */
1139 if (ptr
!= start_sheet
) {
1142 if (*ptr
== ':') { /* 3d ref */
1143 ptr
= sheetref_parse (convs
, ptr
+1, &res
->b
.sheet
, wb
, FALSE
);
1145 return start
; /* TODO error unknown sheet */
1147 res
->b
.sheet
= NULL
;
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
);
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 */
1184 tmp2
= row_parse (tmp1
, b_ss
,
1185 &res
->b
.row
, &res
->b
.row_relative
);
1188 res
->a
.col_relative
= res
->b
.col_relative
= FALSE
;
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
;
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 */
1202 tmp2
= col_parse (tmp1
, a_ss
,
1203 &res
->b
.col
, &res
->b
.col_relative
);
1206 res
->a
.row_relative
= res
->b
.row_relative
= FALSE
;
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
;
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
;
1225 tmp1
= col_parse (ptr
+1, b_ss
, &res
->b
.col
, &res
->b
.col_relative
);
1227 goto singleton
; /* strange, but valid singleton */
1228 tmp2
= row_parse (tmp1
, b_ss
, &res
->b
.row
, &res
->b
.row_relative
);
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
;
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
;
1246 /* ------------------------------------------------------------------------- */
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
);
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
));
1273 if (name
->optional_scope
!= NULL
) {
1274 Workbook
*out_wb
= 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
, ']');
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
));
1303 std_output_string (GnmConventionsOut
*out
, GOString
const *str
)
1305 go_strescape (out
->accum
, str
->str
);
1308 /* ------------------------------------------------------------------------- */
1311 std_sheet_name_quote (GnmConventions
const *convs
,
1314 gunichar uc
= g_utf8_get_char (str
);
1315 GString
*res
= g_string_sized_new (20);
1320 if (g_ascii_isalpha (uc
)) {
1324 } else if (g_unichar_isalpha (uc
) || uc
== '_') {
1327 p
= g_utf8_next_char (str
);
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
)) {
1339 } else if (g_ascii_isdigit (uc
)) {
1342 } else if (uc
== '.' || uc
== '_' || g_unichar_isalpha (uc
))
1343 nletters
= ndigits
= -1;
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.)
1361 unsigned char col_relative
, row_relative
;
1362 if (!col_parse (str
, &max_size
, &col
, &col_relative
))
1368 if (!row_parse (p
, &max_size
, &row
, &row_relative
))
1375 g_string_append (res
, str
);
1379 g_string_append_c (res
, '\'');
1380 /* This is UTF-8 safe. */
1381 for (; *str
; str
++) {
1383 if (c
== '\'' || c
== '\\')
1384 g_string_append_c (res
, '\\');
1385 g_string_append_c (res
, c
);
1387 g_string_append_c (res
, '\'');
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
!= '\\')
1402 str
= g_utf8_next_char (str
);
1403 uc
= g_utf8_get_char (str
);
1404 } while (g_unichar_isalnum (uc
) ||
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
);
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
);
1431 std_external_wb (G_GNUC_UNUSED GnmConventions
const *convs
,
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
);
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 :
1450 * Construct a GnmConventions of @size.
1452 * Returns a GnmConventions with default values. Caller is responsible for
1453 * freeing the result.
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
;
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.
1501 gnm_conventions_new (void)
1503 return gnm_conventions_new_full (sizeof (GnmConventions
));
1507 * gnm_conventions_unref :
1508 * @c: #GnmConventions
1510 * Release a convention
1513 gnm_conventions_unref (GnmConventions
*c
)
1518 g_return_if_fail (c
->ref_count
> 0);
1521 if (c
->ref_count
> 0)
1528 gnm_conventions_ref (GnmConventions
*c
)
1536 gnm_conventions_get_type (void)
1541 t
= g_boxed_type_register_static ("GnmConventions",
1542 (GBoxedCopyFunc
)gnm_conventions_ref
,
1543 (GBoxedFreeFunc
)gnm_conventions_unref
);
1548 /* ------------------------------------------------------------------------- */
1550 GnmConventions
const *gnm_conventions_default
;
1551 GnmConventions
const *gnm_conventions_xls_r1c1
;
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
;
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
1593 gnm_expr_conv_quote (GnmConventions
const *convs
,
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
);