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