parse-util.c 41.2 KB
Newer Older
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 3 4 5
/*
 * parse-util.c: Various utility routines to parse or produce
 *     string representations of common reference types.
 *
6
 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
Morten Welinder's avatar
Morten Welinder committed
7
 * Copyright (C) 2008-2009 Morten Welinder (terra@gnome.org)
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
J.H.M. Dassen (Ray)'s avatar
J.H.M. Dassen (Ray) committed
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
22 23
 * USA
 */
24 25
#include <gnumeric-config.h>
#include "gnumeric.h"
26
#include "parse-util.h"
27

28
#include "application.h"
29
#include "workbook.h"
30 31 32
#include "sheet.h"
#include "value.h"
#include "ranges.h"
Jody Goldberg's avatar
Jody Goldberg committed
33 34
#include "cell.h"
#include "expr.h"
35
#include "number-match.h"
36
#include "gnm-format.h"
37
#include "expr-name.h"
38
#include "func.h"
39 40
#include "mstyle.h"
#include "sheet-style.h"
Jody Goldberg's avatar
Jody Goldberg committed
41
/* For std_expr_name_handler: */
42
#include "expr-impl.h"
43
#include "gutils.h"
44
#include <goffice/goffice.h>
45

46
#include <errno.h>
47
#include <stdlib.h>
48
#include <glib.h>
Jody Goldberg's avatar
Jody Goldberg committed
49
#include <string.h>
50

51 52 53
static GnmLexerItem *
gnm_lexer_item_copy (GnmLexerItem *li)
{
Morten Welinder's avatar
Morten Welinder committed
54
	return g_memdup (li, sizeof (*li));
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
}

GType
gnm_lexer_item_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmLexerItem",
			 (GBoxedCopyFunc)gnm_lexer_item_copy,
			 (GBoxedFreeFunc)g_free);
	}
	return t;
}

70 71 72
static void
col_name_internal (GString *target, int col)
{
Jody Goldberg's avatar
Jody Goldberg committed
73
	static int const steps[] = {
74 75 76 77 78 79 80 81 82 83 84
		26,
		26 * 26,
		26 * 26 * 26,
		26 * 26 * 26 * 26,
		26 * 26 * 26 * 26 * 26,
		26 * 26 * 26 * 26 * 26 * 26,
		INT_MAX
	};
	int i;
	char *dst;

85 86 87 88 89 90
	if (col < 0) {
		/* Invalid column.  */
		g_string_append_printf (target, "[C%d]", col);
		return;
	}

91 92 93 94 95 96 97 98 99
	for (i = 0; col >= steps[i]; i++)
		col -= steps[i];

	g_string_set_size (target, target->len + (i + 1));
	dst = target->str + target->len;
	while (i-- >= 0) {
		*--dst = 'A' + col % 26;
		col /= 26;
	}
100 101
}

Morten Welinder's avatar
Morten Welinder committed
102 103 104 105 106 107
/**
 * col_name: (skip)
 * @col: column number
 *
 * Returns: (transfer none): A string representation of @col
 */
108 109 110
char const *
col_name (int col)
{
111 112
	static GString *buffer = NULL;
	if (!buffer)
Morten Welinder's avatar
Morten Welinder committed
113
		buffer = g_string_new (NULL);
114 115 116 117 118
	g_string_truncate (buffer, 0);

	col_name_internal (buffer, col);

	return buffer->str;
119 120
}

Morten Welinder's avatar
Morten Welinder committed
121 122 123 124 125 126 127 128
/**
 * cols_name: (skip)
 * @start_col: column number
 * @end_col: column number
 *
 * Returns: (transfer none): A string representation of the columns from
 * @start_col to @end_col.
 */
129 130 131
char const *
cols_name (int start_col, int end_col)
{
132 133
	static GString *buffer = NULL;
	if (!buffer)
Morten Welinder's avatar
Morten Welinder committed
134
		buffer = g_string_new (NULL);
135
	g_string_truncate (buffer, 0);
136

137
	col_name_internal (buffer, start_col);
138
	if (start_col != end_col) {
139 140
		g_string_append_c (buffer, ':');
		col_name_internal (buffer, end_col);
141
	}
142 143

	return buffer->str;
144 145
}

Morten Welinder's avatar
Morten Welinder committed
146 147 148 149 150
/**
 * col_parse: (skip)
 *
 * Returns: (transfer none):
 */
151
char const *
152
col_parse (char const *str, GnmSheetSize const *ss,
153
	   int *res, unsigned char *relative)
154
{
155
	char const *ptr, *start = str;
156
	int col = -1;
157
	int max = ss->max_cols;
158

159 160
	if (!(*relative = (*start != '$')))
		start++;
161

162
	for (ptr = start; col < max ; ptr++)
163 164 165 166
		if (('a' <= *ptr && *ptr <= 'z'))
			col = 26 * (col + 1) + (*ptr - 'a');
		else if (('A' <= *ptr && *ptr <= 'Z'))
			col = 26 * (col + 1) + (*ptr - 'A');
167
		else if (ptr != start) {
168 169 170
			*res = col;
			return ptr;
		} else
171 172
			return NULL;
	return NULL;
173 174 175 176
}

/***************************************************************************/

177 178
static void
row_name_internal (GString *target, int row)
179
{
180
	g_string_append_printf (target, "%d", row + 1);
181 182
}

Morten Welinder's avatar
Morten Welinder committed
183 184 185 186 187 188
/**
 * row_name: (skip)
 * @row: row number
 *
 * Returns: (transfer none): A string representation of @row
 */
189 190 191
char const *
row_name (int row)
{
192 193
	static GString *buffer = NULL;
	if (!buffer)
Morten Welinder's avatar
Morten Welinder committed
194
		buffer = g_string_new (NULL);
195 196 197 198 199
	g_string_truncate (buffer, 0);

	row_name_internal (buffer, row);

	return buffer->str;
200 201
}

Morten Welinder's avatar
Morten Welinder committed
202 203 204 205 206 207 208 209
/**
 * rows_name: (skip)
 * @start_row: row number
 * @end_row: row number
 *
 * Returns: (transfer none): A string representation of the rows from
 * @start_row to @end_row.
 */
210 211 212
char const *
rows_name (int start_row, int end_row)
{
213 214
	static GString *buffer = NULL;
	if (!buffer)
Morten Welinder's avatar
Morten Welinder committed
215
		buffer = g_string_new (NULL);
216
	g_string_truncate (buffer, 0);
217

218
	row_name_internal (buffer, start_row);
219
	if (start_row != end_row) {
220 221
		g_string_append_c (buffer, ':');
		row_name_internal (buffer, end_row);
222
	}
223 224

	return buffer->str;
225 226
}

Morten Welinder's avatar
Morten Welinder committed
227 228 229 230 231
/**
 * row_parse: (skip)
 *
 * Returns: (transfer none):
 */
232
char const *
233
row_parse (char const *str, GnmSheetSize const *ss,
234
	   int *res, unsigned char *relative)
235 236
{
	char const *end, *ptr = str;
237
	long int row;
238
	int max = ss->max_rows;
239 240 241 242

	if (!(*relative = (*ptr != '$')))
		ptr++;

243 244 245 246
	/* Initial '0' is not allowed.  */
	if (*ptr <= '0' || *ptr > '9')
		return NULL;

247 248 249 250 251
	/*
	 * Do not allow letters after the row number.  If we did, then
	 * the name "K3P" would lex as the reference K3 followed by the
	 * name "P".
	 */
252
	row = strtol (ptr, (char **)&end, 10);
253 254
	if (ptr != end &&
	    !g_unichar_isalnum (g_utf8_get_char (end)) && *end != '_' &&
255
	    0 < row && row <= max) {
256 257 258
		*res = row - 1;
		return end;
	} else
259
		return NULL;
260 261
}

262
/***************************************************************************/
263

264 265 266 267 268 269 270 271 272 273 274 275
static void
r1c1_add_index (GString *target, char type, int num, unsigned char relative)
{
	if (relative) {
		if (num != 0)
			g_string_append_printf (target, "%c[%d]", type, num);
		else
			g_string_append_c (target, type);
	} else
		g_string_append_printf (target, "%c%d", type, num + 1);
}

276 277 278
static char *
wb_rel_uri (Workbook *wb, Workbook *ref_wb)
{
279 280
	char const *uri = go_doc_get_uri ((GODoc *)wb);
	char const *ref_uri = go_doc_get_uri ((GODoc *)ref_wb);
281 282 283 284 285 286 287 288 289 290
	char *rel_uri = go_url_make_relative (uri, ref_uri);

	if (rel_uri == NULL || rel_uri[0] == '/') {
		g_free (rel_uri);
		return g_strdup (uri);
	}

	return rel_uri;
}

291
/**
Morten Welinder's avatar
Morten Welinder committed
292
 * cellref_as_string: (skip)
293 294
 * @out: #GnmConventionsOut
 * @cell_ref:
Morten Welinder's avatar
Morten Welinder committed
295
 * @no_sheetname: If %TRUE, suppress sheet name
296
 *
Morten Welinder's avatar
Morten Welinder committed
297 298 299
 * Emits a string containing representation of @ref as evaluated at @pp.
 * @no_sheetname can be used to suppress the addition of the sheetname
 * for non-local references.
300
 **/
301
void
302
cellref_as_string (GnmConventionsOut *out,
Jody Goldberg's avatar
Jody Goldberg committed
303
		   GnmCellRef const *cell_ref,
304
		   gboolean no_sheetname)
305
{
306
	GString *target = out->accum;
307
	Sheet const *sheet = cell_ref->sheet;
308

309 310
	/* If it is a non-local reference, add the path to the external sheet */
	if (sheet != NULL && !no_sheetname) {
311
		if (out->pp->wb == NULL && out->pp->sheet == NULL)
312 313
			/* For the expression leak printer.  */
			g_string_append (target, "'?'");
314
		else if (NULL == out->pp->wb || sheet->workbook == out->pp->wb)
315 316
			g_string_append (target, sheet->name_quoted);
		else {
317
			char *rel_uri = wb_rel_uri (sheet->workbook, out->pp->wb);
318
			g_string_append_c (target, '[');
319
			g_string_append (target, rel_uri);
320 321
			g_string_append_c (target, ']');
			g_string_append (target, sheet->name_quoted);
322
			g_free (rel_uri);
323
		}
324
		g_string_append_unichar (target, out->convs->sheet_name_sep);
325 326
	}

327
	if (out->convs->r1c1_addresses) { /* R1C1 handler */
328 329 330
		r1c1_add_index (target, 'R', cell_ref->row, cell_ref->row_relative);
		r1c1_add_index (target, 'C', cell_ref->col, cell_ref->col_relative);
	} else {
331
		GnmCellPos pos;
332
		Sheet const *size_sheet = eval_sheet (sheet, out->pp->sheet);
333 334
		GnmSheetSize const *ss =
			gnm_sheet_get_size2 (size_sheet, out->pp->wb);
335

336
		gnm_cellpos_init_cellref_ss (&pos, cell_ref, &out->pp->eval, ss);
337

338
		if (!cell_ref->col_relative)
339
			g_string_append_c (target, '$');
340
		col_name_internal (target, pos.col);
341

342 343 344
		if (!cell_ref->row_relative)
			g_string_append_c (target, '$');
		row_name_internal (target, pos.row);
345
	}
346 347
}

348
/**
Morten Welinder's avatar
Morten Welinder committed
349
 * rangeref_as_string: (skip)
350 351
 * @out: #GnmConventionsOut
 * @ref: #GnmRangeRef
352 353
 *
 **/
354
void
355
rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
356
{
Jody Goldberg's avatar
Jody Goldberg committed
357
	GnmRange r;
358
	GString *target = out->accum;
359
	Sheet *start_sheet, *end_sheet;
360 361 362
	GnmSheetSize const *end_ss;

	gnm_rangeref_normalize_pp (ref, out->pp, &start_sheet, &end_sheet, &r);
363

364
	end_ss = gnm_sheet_get_size2 (end_sheet, out->pp->wb);
365

366
	if (ref->a.sheet) {
367 368
		if (NULL != out->pp->wb && ref->a.sheet->workbook != out->pp->wb) {
			char *rel_uri = wb_rel_uri (ref->a.sheet->workbook, out->pp->wb);
369
			g_string_append_c (target, '[');
370
			g_string_append (target, rel_uri);
371
			g_string_append_c (target, ']');
372
			g_free (rel_uri);
373
		}
374 375
		if (out->pp->wb == NULL && out->pp->sheet == NULL)
			/* For the expression leak printer.  */
376 377 378 379 380 381 382 383
			g_string_append (target, "'?'");
		else if (ref->b.sheet == NULL || ref->a.sheet == ref->b.sheet)
			g_string_append (target, ref->a.sheet->name_quoted);
		else {
			g_string_append (target, ref->a.sheet->name_quoted);
			g_string_append_c (target, ':');
			g_string_append (target, ref->b.sheet->name_quoted);
		}
384
		g_string_append_unichar (target, out->convs->sheet_name_sep);
385 386
	}

387
	if (out->convs->r1c1_addresses) { /* R1C1 handler */
388
		/* be sure to use else if so that a1:iv65535 does not vanish */
389
		if (r.start.col == 0 && r.end.col == end_ss->max_cols - 1) {
390 391 392 393 394 395
			r1c1_add_index (target, 'R', ref->a.row, ref->a.row_relative);
			if (ref->a.row != ref->b.row ||
			    ref->a.row_relative != ref->b.row_relative) {
				g_string_append_c (target, ':');
				r1c1_add_index (target, 'R', ref->b.row, ref->b.row_relative);
			}
396
		} else if (r.start.row == 0 && r.end.row == end_ss->max_rows - 1) {
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
			r1c1_add_index (target, 'C', ref->a.col, ref->a.col_relative);
			if (ref->a.col != ref->b.col ||
			    ref->a.col_relative != ref->b.col_relative) {
				g_string_append_c (target, ':');
				r1c1_add_index (target, 'C', ref->b.col, ref->b.col_relative);
			}
		} else {
			r1c1_add_index (target, 'R', ref->a.row, ref->a.row_relative);
			r1c1_add_index (target, 'C', ref->a.col, ref->a.col_relative);
			if (r.start.col != r.end.col ||
			    ref->a.col_relative != ref->b.col_relative ||
			    r.start.row != r.end.row ||
			    ref->a.row_relative != ref->b.row_relative) {
				g_string_append_c (target, ':');
				r1c1_add_index (target, 'R', ref->b.row, ref->b.row_relative);
				r1c1_add_index (target, 'C', ref->b.col, ref->b.col_relative);
			}
		}
415
	} else {
416
		/* be sure to use else if so that a1:iv65535 does not vanish */
417
		if (r.start.col == 0 && r.end.col == end_ss->max_cols - 1) {
418 419 420 421 422 423 424
			if (!ref->a.row_relative)
				g_string_append_c (target, '$');
			row_name_internal (target, r.start.row);
			g_string_append_c (target, ':');
			if (!ref->b.row_relative)
				g_string_append_c (target, '$');
			row_name_internal (target, r.end.row);
425
		} else if (r.start.row == 0 && r.end.row == end_ss->max_rows - 1) {
426 427 428
			if (!ref->a.col_relative)
				g_string_append_c (target, '$');
			col_name_internal (target, r.start.col);
429
			g_string_append_c (target, ':');
430
			if (!ref->b.col_relative)
431 432
				g_string_append_c (target, '$');
			col_name_internal (target, r.end.col);
433 434
		} else {
			if (!ref->a.col_relative)
435
				g_string_append_c (target, '$');
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
			col_name_internal (target, r.start.col);
			if (!ref->a.row_relative)
				g_string_append_c (target, '$');
			row_name_internal (target, r.start.row);

			if (r.start.col != r.end.col ||
			    ref->a.col_relative != ref->b.col_relative ||
			    r.start.row != r.end.row ||
			    ref->a.row_relative != ref->b.row_relative) {
				g_string_append_c (target, ':');
				if (!ref->b.col_relative)
					g_string_append_c (target, '$');
				col_name_internal (target, r.end.col);
				if (!ref->b.row_relative)
					g_string_append_c (target, '$');
				row_name_internal (target, r.end.row);
			}
453 454 455 456
		}
	}
}

457
/**
Morten Welinder's avatar
Morten Welinder committed
458
 * gnm_1_0_rangeref_as_string: (skip)
459 460
 * @out: #GnmConventionsOut
 * @ref: #GnmRangeRef
461
 *
462 463 464
 * Simplified variant of rangeref_as_string that old versions of gnumeric can
 * read.  It drops support for full col/row references.  We can remap them on
 * import.
465 466
 *
 * This function also ignores R1C1 settings.
467 468
 **/
void
469
gnm_1_0_rangeref_as_string (GnmConventionsOut *out, GnmRangeRef const *ref)
470 471
{
	GnmRange r;
472
	GString *target = out->accum;
473
	Sheet *start_sheet, *end_sheet;
474

475
	gnm_rangeref_normalize_pp (ref, out->pp, &start_sheet, &end_sheet, &r);
476 477

	if (ref->a.sheet) {
478 479
		if (NULL != out->pp->wb && ref->a.sheet->workbook != out->pp->wb) {
			char *rel_uri = wb_rel_uri (ref->a.sheet->workbook, out->pp->wb);
480
			g_string_append_c (target, '[');
481
			g_string_append (target, rel_uri);
482
			g_string_append_c (target, ']');
483
			g_free (rel_uri);
484
		}
485
		if (out->pp->wb == NULL && out->pp->sheet == NULL)
486 487 488 489 490 491 492 493 494
			/* For the expression leak printer. */
			g_string_append (target, "'?'");
		else if (ref->b.sheet == NULL || ref->a.sheet == ref->b.sheet)
			g_string_append (target, ref->a.sheet->name_quoted);
		else {
			g_string_append (target, ref->a.sheet->name_quoted);
			g_string_append_c (target, ':');
			g_string_append (target, ref->b.sheet->name_quoted);
		}
495
		g_string_append_unichar (target, out->convs->sheet_name_sep);
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
	}

	if (!ref->a.col_relative)
		g_string_append_c (target, '$');
	col_name_internal (target, r.start.col);
	if (!ref->a.row_relative)
		g_string_append_c (target, '$');
	row_name_internal (target, r.start.row);

	if (r.start.col != r.end.col ||
	    ref->a.col_relative != ref->b.col_relative ||
	    r.start.row != r.end.row ||
	    ref->a.row_relative != ref->b.row_relative) {
		g_string_append_c (target, ':');
		if (!ref->b.col_relative)
			g_string_append_c (target, '$');
		col_name_internal (target, r.end.col);
		if (!ref->b.row_relative)
			g_string_append_c (target, '$');
		row_name_internal (target, r.end.row);
	}
}

519
static char const *
520
cellref_a1_get (GnmCellRef *out, GnmSheetSize const *ss,
521
		char const *in, GnmCellPos const *pos)
522
{
523 524
	int col;
	int row;
525

526 527
	g_return_val_if_fail (in != NULL, NULL);
	g_return_val_if_fail (out != NULL, NULL);
528

529
	in = col_parse (in, ss, &col, &out->col_relative);
530
	if (!in)
531
		return NULL;
532

533
	in = row_parse (in, ss, &row, &out->row_relative);
534
	if (!in)
535
		return NULL;
536 537 538

	/* Setup the cell reference information */
	if (out->row_relative)
Jody Goldberg's avatar
Jody Goldberg committed
539
		out->row = row - pos->row;
540 541 542 543
	else
		out->row = row;

	if (out->col_relative)
Jody Goldberg's avatar
Jody Goldberg committed
544
		out->col = col - pos->col;
545 546 547 548 549
	else
		out->col = col;

	out->sheet = NULL;

550
	return in;
551 552
}

553 554
/* skip first character (which was R or C) */
static char const *
555
r1c1_get_index (char const *str, GnmSheetSize const *ss,
556
		int *num, unsigned char *relative, gboolean is_col)
557
{
558
	char *end;
Morten Welinder's avatar
Morten Welinder committed
559 560 561
	long l;
	int max = is_col ? ss->max_cols : ss->max_rows;

562 563
	if (str[0] == '\0')
		return NULL;
564

565
	str++;
Morten Welinder's avatar
Morten Welinder committed
566 567
	*relative = (*str == '[');
	if (*relative)
568
		str++;
569 570 571 572 573 574
	else if (*str == '-' || *str == '+') { /* handle RC-10 as RC followed by -10 */
		*relative = TRUE;
		*num = 0;
		return str;
	}

575
	errno = 0;
Morten Welinder's avatar
Morten Welinder committed
576 577 578
	*num = l = strtol (str, &end, 10);
	if (errno == ERANGE || l <= G_MININT || l > G_MAXINT) {
		/* Note: this includes G_MININT to avoid negation overflow.  */
579
		return NULL;
Morten Welinder's avatar
Morten Welinder committed
580
	}
581 582 583 584 585 586 587 588
	if (str == end) {
		if (*relative)
			return NULL;
		*relative = TRUE;
		*num = 0;
	} else if (*relative) {
		if (*end != ']')
			return NULL;
Morten Welinder's avatar
Morten Welinder committed
589 590 591
		*num = (*num > 0
			? *num % max
			: -(-*num % max));
592 593
		return end + 1;
	} else {
594
		if (*num <= 0 || *num > max)
595 596
			return NULL;
		(*num)--;
597
	}
598
	return end;
599 600
}

601
static char const *
602
cellref_r1c1_get (GnmCellRef *out, GnmSheetSize const *ss,
603
		  char const *in, GnmCellPos const *pos)
604 605
{
	out->sheet = NULL;
606
	if (*in != 'R' && *in != 'r')
607
		return NULL;
608
	if (NULL == (in = r1c1_get_index (in, ss,
609 610
					  &out->row, &out->row_relative,
					  FALSE)))
611
		return NULL;
612
	if (*in != 'C' && *in != 'c')
613
		return NULL;
614
	if (NULL == (in = r1c1_get_index (in, ss,
615 616
					  &out->col, &out->col_relative,
					  TRUE)))
617 618 619 620
		return NULL;
	if (g_ascii_isalpha (*in))
		return NULL;
	return in;
621 622 623
}

/**
Morten Welinder's avatar
Morten Welinder committed
624 625 626 627 628
 * cellref_parse: (skip)
 * @out: (out): destination GnmCellRef
 * @ss: size of the sheet where parsing is being done
 * @in: reference description text, no leading whitespace allowed.
 * @pos: position parsing is being done at
629
 *
Morten Welinder's avatar
Morten Welinder committed
630
 * Converts the string representation of a #GnmCellRef into
631
 * an internal representation.
632
 *
Morten Welinder's avatar
Morten Welinder committed
633 634
 * Returns: (transfer none): a pointer to the character following the
 * cellref.
635
 **/
636
char const *
637
cellref_parse (GnmCellRef *out, GnmSheetSize const *ss,
638
	       char const *in, GnmCellPos const *pos)
639
{
640
	char const *res;
Morten Welinder's avatar
Morten Welinder committed
641

642 643 644
	g_return_val_if_fail (in != NULL, NULL);
	g_return_val_if_fail (out != NULL, NULL);

645
	res = cellref_a1_get (out, ss, in, pos);
646 647
	if (res != NULL)
		return res;
648
	return cellref_r1c1_get (out, ss, in, pos);
649 650 651 652
}

/****************************************************************************/

653 654
static char const *
cell_coord_name2 (int col, int row, gboolean r1c1)
655
{
656
	static GString *buffer = NULL;
657 658 659
	if (buffer)
		g_string_truncate (buffer, 0);
	else
Morten Welinder's avatar
Morten Welinder committed
660
		buffer = g_string_new (NULL);
661

662 663 664 665 666 667 668
	if (r1c1) {
		r1c1_add_index (buffer, 'R', row, FALSE);
		r1c1_add_index (buffer, 'C', col, FALSE);
	} else {
		col_name_internal (buffer, col);
		row_name_internal (buffer, row);
	}
669 670

	return buffer->str;
671
}
672

Morten Welinder's avatar
Morten Welinder committed
673 674 675 676 677 678 679
/**
 * cell_coord_name: (skip)
 * @col: column number
 * @row: row number
 *
 * Returns: (transfer none): a string representation of the cell at (@col,@row)
 */
680 681 682 683 684 685
char const *
cell_coord_name (int col, int row)
{
	return cell_coord_name2 (col, row, FALSE);
}

Morten Welinder's avatar
Morten Welinder committed
686 687 688 689 690 691
/**
 * cellpos_as_string: (skip)
 * @pos: A #GnmCellPos
 *
 * Returns: (transfer none): a string representation of the cell at @pos
 */
692
char const *
Jody Goldberg's avatar
Jody Goldberg committed
693
cellpos_as_string (GnmCellPos const *pos)
694 695 696 697 698 699
{
	g_return_val_if_fail (pos != NULL, "ERROR");

	return cell_coord_name (pos->col, pos->row);
}

700 701 702 703 704 705 706
char const *
parsepos_as_string (GnmParsePos const *pp)
{
	g_return_val_if_fail (pp != NULL, "ERROR");

	return cell_coord_name2 (pp->eval.col,
				 pp->eval.row,
707
				 pp->sheet && pp->sheet->convs->r1c1_addresses);
708 709
}

Morten Welinder's avatar
Morten Welinder committed
710 711 712 713 714 715
/**
 * cell_name:
 * @cell: #GnmCell
 *
 * Returns: (transfer none): the name of @cell, like "B11"
 */
716
char const *
717
cell_name (GnmCell const *cell)
718 719 720
{
	g_return_val_if_fail (cell != NULL, "ERROR");

721 722
	return cell_coord_name2 (cell->pos.col,
				 cell->pos.row,
723
				 cell->base.sheet->convs->r1c1_addresses);
724 725 726
}

/**
Morten Welinder's avatar
Morten Welinder committed
727
 * cellpos_parse: (skip)
728 729 730
 * @cell_str:   a string representation of a cell name.
 * @ss:          #GnmSheetSize
 * @res:         result
731
 * @strict:      if this is TRUE, then parsing stops at possible errors,
Morten Welinder's avatar
Morten Welinder committed
732 733
 *               otherwise an attempt is made to return cell names with
 *               trailing garbage.
734
 *
Morten Welinder's avatar
Morten Welinder committed
735 736
 * Returns: (transfer none): pointer to following char on success, %NULL on
 * failure.  (In the strict case, that would be a pointer to the \0 or %NULL.)
737
 */
738
char const *
739
cellpos_parse (char const *cell_str, GnmSheetSize const *ss,
740
	       GnmCellPos *res, gboolean strict)
741
{
742
	unsigned char dummy_relative;
743

744
	cell_str = col_parse (cell_str, ss, &res->col, &dummy_relative);
745
	if (!cell_str)
746
		return NULL;
747

748
	cell_str = row_parse (cell_str, ss, &res->row, &dummy_relative);
749
	if (!cell_str)
750
		return NULL;
751

Jody Goldberg's avatar
fix.  
Jody Goldberg committed
752
	if (*cell_str != 0 && strict)
753
		return NULL;
754

755
	return cell_str;
756 757
}

758
/**
Morten Welinder's avatar
Morten Welinder committed
759 760
 * gnm_expr_char_start_p: (skip)
 * @c: string
761 762 763 764 765 766 767 768 769 770 771
 *
 * Can the supplied string be an expression ?  It does not guarantee that it is,
 * however, it is possible.  If it is possible it strips off any header
 * characters that are not relevant.
 *
 * NOTE : things like -1,234 will match
 */
char const *
gnm_expr_char_start_p (char const * c)
{
	char c0;
772
	int N = 1;
773 774 775 776 777 778

	if (NULL == c)
		return NULL;

	c0 = *c;

779 780 781 782
	if (c0 == '=' || c0 == '@' || c0 == '+' || c0 == '-')
		while (c[N] == ' ')
			N++;

783
	if (c0 == '=' || c0 == '@' || (c0 == '+' && c[1] == 0))
784
		return c + N;
785 786 787 788 789 790 791 792 793 794 795 796 797 798

	if ((c0 == '-' || c0 == '+') && c0 != c[1]) {
		char *end;

		/*
		 * Ok, we have a string that
		 * 1. starts with a sign
		 * 2. does not start with the sign repeated (think --------)
		 * 3. is more than one character
		 *
		 * Now we check whether we have a number.  We don't want
		 * numbers to be treated as formulae.  FIXME: this really
		 * just checks for C-syntax numbers.
		 */
799
		(void) gnm_strto (c, &end);
800
		if (errno || *end != 0 || end == c)
801
			return (c0 == '+') ? c + N : c;
802 803 804 805 806
		/* Otherwise, it's a number.  */
	}
	return NULL;
}

807
/**
808
 * parse_text_value_or_expr:
809
 * @pos: If the string looks like an expression parse it at this location.
810
 * @text: The text to be parsed.
Morten Welinder's avatar
Morten Welinder committed
811 812
 * @val: (out): Returns a GnmValue* if the text was a value, otherwise NULL.
 * @texpr: (out): Returns a GnmExprTop* if the text was an expression, otherwise NULL.
813
 *
814 815
 * Utility routine to parse a string and convert it into an expression or value.
 *
Morten Welinder's avatar
Morten Welinder committed
816 817
 * If there is a parse failure for an expression an error GnmValue with
 * the syntax error is returned in @val.
818
 */
819
void
Morten Welinder's avatar
Morten Welinder committed
820
parse_text_value_or_expr (GnmParsePos const *pos, char const *text,
821
			  GnmValue **val, GnmExprTop const **texpr)
822
{
823
	char const *expr_start;
824 825 826 827
	GODateConventions const *date_conv;
	GOFormat const *cur_fmt;
	GOFormat const *cell_fmt;
	GnmStyle const *cell_style;
828

829
	*texpr = NULL;
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
	*val = NULL;

	/* Determine context information.  */
	date_conv =
		pos->sheet
		? workbook_date_conv (pos->sheet->workbook)
		: (pos->wb
		   ? workbook_date_conv (pos->wb)
		   : NULL);
	cell_style = pos->sheet
		? sheet_style_get (pos->sheet, pos->eval.col, pos->eval.row)
		: NULL;
	cur_fmt = cell_fmt = cell_style ? gnm_style_get_format (cell_style) : NULL;
	if (cell_fmt && go_format_is_general (cell_fmt)) {
		GnmCell const *cell = pos->sheet
			? sheet_cell_get (pos->sheet, pos->eval.col, pos->eval.row)
			: NULL;
		if (cell && cell->value && VALUE_FMT (cell->value))
			cur_fmt = VALUE_FMT (cell->value);
	}
850

851
	/* Does it match any formats?  */
852
	*val = format_match (text, cur_fmt, date_conv);
853 854 855 856 857
	if (*val != NULL) {
		GOFormat const *val_fmt = VALUE_FMT (*val);
		/* Avoid value formats we don't need.  */
		if (val_fmt && go_format_eq (cell_fmt, val_fmt))
			value_set_fmt (*val, NULL);
858
		return;
859
	}
860

861
	/* If it does not match known formats, see if it is an expression */
862
	expr_start = gnm_expr_char_start_p (text);
863
	if (NULL != expr_start && *expr_start) {
864
		*texpr = gnm_expr_parse_str (expr_start, pos,
865
			GNM_EXPR_PARSE_DEFAULT, NULL, NULL);
866
		if (*texpr != NULL)
867
			return;
868 869 870 871
	}

	/* Fall back on string */
	*val = value_new_string (text);
872
}
873

Morten Welinder's avatar
Morten Welinder committed
874 875
GnmParseError *
parse_error_init (GnmParseError *pe)
876
{
877 878 879
	pe->err		= NULL;
	pe->begin_char	= 0;
	pe->end_char	= 0;
Jody Goldberg's avatar
Jody Goldberg committed
880

881 882 883 884
	return pe;
}

void
Morten Welinder's avatar
Morten Welinder committed
885
parse_error_free (GnmParseError *pe)
886
{
887 888 889
	if (pe->err != NULL) {
		g_error_free (pe->err);
		pe->err = NULL;
890 891
	}
}
892

893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
static GnmParseError *
gnm_parse_error_copy (GnmParseError *pe)
{
	GnmParseError *res = g_new (GnmParseError, 1);
	res->begin_char = pe->begin_char;
	res->end_char = pe->end_char;
	res->err = (pe->err)? g_error_copy (pe->err): NULL;
	return res;
}

GType
gnm_parse_error_get_type (void)
{
	static GType t = 0;

	if (t == 0) {
		t = g_boxed_type_register_static ("GnmParseError",
			 (GBoxedCopyFunc)gnm_parse_error_copy,
			 (GBoxedFreeFunc)parse_error_free);
	}
	return t;
}

916
/***************************************************************************/
917

918
static char const *
919
check_quoted (char const *start, int *num_escapes)
920
{
921
	char const *str = start;
922
	if (*str == '\'' || *str == '\"') {
923
		char const quote = *str++;
924 925 926
		*num_escapes = 0;
		for (; *str && *str != quote; str = g_utf8_next_char (str))
			if (*str == '\\' && str[1]) {
927
				str++;
928 929
				(*num_escapes)++;
			}
930 931
		if (*str)
			return str+1;
932 933
	} else
		*num_escapes = -1;
934
	return start;
935
}
936

937 938
static void
unquote (char *dst, char const *src, int n)
939
{
940
	while (n-- > 0)
941
		if (*src == '\\' && src[1]) {
942 943 944 945 946
			int l = g_utf8_skip [*(guchar *)(++src)];
			strncpy (dst, src, l);
			dst += l;
			src += l;
			n -= l;
947 948 949 950
		} else
			*dst++ = *src++;
	*dst = 0;
}
951

952
/**
Morten Welinder's avatar
Morten Welinder committed
953
 * wbref_parse:
954 955 956
 * @convs: #GnmConventions const
 * @start:
 * @wb:
957 958 959 960 961
 *
 * Returns : NULL if there is a valid workbook name but it is unknown.
 *           If the string is a valid workbook known name it returns a pointer
 *           the end of the name.
 *           Otherwise returns @start and does not modify @wb.
962
 **/
963
static char const *
964 965
wbref_parse (GnmConventions const *convs,
	     char const *start, Workbook **wb, Workbook *ref_wb)
966 967 968
{
	/* Is this an external reference ? */
	if (*start == '[') {
969 970
		Workbook *tmp_wb;

971
		int num_escapes;
972
		char const *end = check_quoted (start+1, &num_escapes);
973 974
		char *name;

975
		if (end == start+1) {
976 977 978
			end = strchr (start, ']');
			if (end == NULL)
				return start;
979
		}
980
		if (*end != ']')
981
			return start;
982

983 984 985 986
		if (num_escapes < 0)
			name = g_strndup (start + 1, end - start - 1);
		else {
			name = g_malloc (1 + end - start - 2);
987
			unquote (name, start+2, end-start-2);
988
		}
989

990
		tmp_wb = (*convs->input.external_wb) (convs, ref_wb, name);
991
		g_free (name);
992 993
		if (tmp_wb == NULL)
			return NULL;
994

995
		*wb = tmp_wb;
996 997 998 999 1000 1001
		return end + 1;
	}

	return start;
}

1002
/**
Morten Welinder's avatar
Morten Welinder committed
1003 1004
 * sheetref_parse: (skip)
 * @convs: #GnmConventions
1005
 * @start:
Morten Welinder's avatar
Morten Welinder committed
1006 1007
 * @sheet: (out)
 * @wb: A #Workbook
1008
 * @allow_3d: