ms-formula-write.c 27.7 KB
Newer Older
1
/*
2
 * ms-formula-write.c: export GnmExpr to xls
3
 *
4
 * Authors:
Jody Goldberg's avatar
Jody Goldberg committed
5
 *    Jody Goldberg (jody@gnome.org)
6
 *    Michael Meeks (michael@ximian.com)
7
 *
8
 * (C) 1998-2001 Michael Meeks
9
 * (C) 2002-2005 Jody Goldberg
10 11
 */

12 13
#include <gnumeric-config.h>
#include <gnumeric.h>
14
#include "ms-formula-write.h"
15 16
#include "ms-excel-write.h"
#include "formula-types.h"
17 18 19 20
#include "excel.h"
#include "ms-biff.h"
#include "formula-types.h"
#include "boot.h"
21

22
#include <sheet.h>
23
#include <gutils.h>
24
#include <workbook.h>
25 26
#include <func.h>
#include <value.h>
27
#include <cell.h>
28 29 30 31
#include <expr.h>
#include <expr-impl.h>
#include <expr-name.h>
#include <parse-util.h>
32
#include <goffice/goffice.h>
33

34
#include <gsf/gsf-utils.h>
35
#include <glib/gi18n-lib.h>
36
#include <string.h>
37

38
#define FORMULA_DEBUG 0
39

40 41 42
static guint
sheet_pair_hash (ExcelSheetPair const *sp)
{
43
	return ((GPOINTER_TO_UINT(sp->a) >> 2) & 0xffff) | ((GPOINTER_TO_UINT(sp->b) << 14) & 0xffff0000);
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
}

static gint
sheet_pair_cmp (ExcelSheetPair const *a, ExcelSheetPair const *b)
{
	return a->a == b->a && a->b == b->b;
}

static void
sheet_pair_add_if_unknown (GHashTable *hash, ExcelSheetPair const *pair)
{
	if (NULL == g_hash_table_lookup (hash, pair)) {
		ExcelSheetPair *new_pair = g_new (ExcelSheetPair, 1);
		new_pair->a = pair->a;
		new_pair->b = pair->b;
		new_pair->idx_a = new_pair->idx_b = 0;
		g_hash_table_insert (hash, new_pair, new_pair);
61
		/* g_printerr ("Adding %p:%p\n", pair->a, pair->b); */
62 63 64
	}
}

65 66 67 68 69 70 71 72 73
void
excel_write_prep_sheet (ExcelWriteState *ewb, Sheet const *sheet)
{
	ExcelSheetPair pair;
	pair.a = pair.b = sheet;
	if (pair.a != NULL)
		sheet_pair_add_if_unknown (ewb->sheet_pairs, &pair);
}

74 75
static GnmExpr const *
cb_excel_write_prep_expr (GnmExpr const *expr, GnmExprWalk *data)
76
{
77
	ExcelWriteState *ewb = data->user;
78

79
	switch (GNM_EXPR_GET_OPER (expr)) {
80 81
	case GNM_EXPR_OP_FUNCALL: {
		GnmFunc *func = expr->func.func;
Morten Welinder's avatar
Morten Welinder committed
82
		GnmFuncFlags flags = gnm_func_get_flags (func);
83
		ExcelFunc *ef = g_hash_table_lookup (ewb->function_map, func);
84
		if (ef != NULL)
85
			break;
86

87
		ef = g_new (ExcelFunc, 1);
Morten Welinder's avatar
Morten Welinder committed
88 89
		ef->efunc = (flags & (GNM_FUNC_IS_PLACEHOLDER |
				      GNM_FUNC_IS_WORKBOOK_LOCAL))
90 91 92 93
			? NULL
			: g_hash_table_lookup (excel_func_by_name,
					       func->name);

94 95 96 97 98
		if (ef->efunc && ef->efunc->idx == 0xff) {
			/* These functions appear to be saved as macros! */
			ef->macro_name = g_strdup (ef->efunc->name);
			ef->idx = -1;
		} else if (ef->efunc) {
99
			ef->macro_name = NULL;
100
			ef->idx = ef->efunc->idx;
Morten Welinder's avatar
Morten Welinder committed
101
		} else if (flags & GNM_FUNC_IS_WORKBOOK_LOCAL) {
102 103 104
			ef->macro_name = g_strdup (func->name);
			ef->idx = -1;
		} else {
105 106 107
			char *fname =
				g_utf8_strup (gnm_func_get_name (func, FALSE), -1);
			g_ptr_array_add (ewb->externnames, fname);
108 109
			ef->macro_name = NULL;
			ef->idx = ewb->externnames->len;
110 111 112 113 114
		}
		g_hash_table_insert (ewb->function_map, func, ef);
		break;
	}

115 116
	case GNM_EXPR_OP_CELLREF:
		excel_write_prep_sheet (ewb, expr->cellref.ref.sheet);
117 118 119
		break;

	case GNM_EXPR_OP_CONSTANT: {
Jody Goldberg's avatar
Jody Goldberg committed
120
		GnmValue const *v = expr->constant.value;
121
		if (VALUE_IS_CELLRANGE (v)) {
122 123 124 125 126 127 128 129 130
			ExcelSheetPair pair;
			pair.a = v->v_range.cell.a.sheet;
			pair.b = v->v_range.cell.b.sheet;
			if (pair.a != NULL) {
				if (pair.b == NULL)
					pair.b = pair.a;
				sheet_pair_add_if_unknown (ewb->sheet_pairs, &pair);
			}
		}
131
		break;
132
	}
133

134 135 136
	default:
		break;
	}
137 138

	return NULL;
139 140
}

141 142 143 144 145 146 147 148
/**
 * excel_write_prep_expr :
 * @ewb:
 * @texpr:
 *
 * Searches for interesting functions, names, or sheets.
 * and builds a database of things to write out later.
 **/
149 150 151
void
excel_write_prep_expr (ExcelWriteState *ewb, GnmExprTop const *texpr)
{
152
	gnm_expr_walk (texpr->expr, cb_excel_write_prep_expr, ewb);
153 154
}

155 156 157 158
/**
 * excel_write_prep_expressions :
 * @ewb: state
 *
159
 * Initialize the data structures for exporting functions
160 161 162
 **/
void
excel_write_prep_expressions (ExcelWriteState *ewb)
163
{
164
	g_return_if_fail (ewb != NULL);
165

166 167 168
	ewb->sheet_pairs = g_hash_table_new_full (
		(GHashFunc) sheet_pair_hash, (GEqualFunc) sheet_pair_cmp,
		NULL, g_free);
169 170
}

171
/****************************************************************************/
172

173 174 175 176 177 178 179 180
typedef enum {
	XL_REF		= 0,	/* R */
	XL_VAL		= 1,	/* V */
	XL_ARRAY	= 2,	/* A */
	XL_ROOT		= 3,	/* v */
	NUM_XL_TYPES	= 3
} XLOpType;
typedef enum {
Morten Welinder's avatar
Morten Welinder committed
181
	CTXT_CELL = 0,
182
	CTXT_ARRAY = 1,
Morten Welinder's avatar
Morten Welinder committed
183
	CTXT_NAME_OBJ = 2,
184 185
	NUM_CTXTS
} XLContextType;
186

187 188
typedef struct {
	ExcelWriteState *ewb;
189 190
	Sheet		*sheet;
	int		 col, row;
191
	gboolean	 use_name_variant;
Morten Welinder's avatar
Morten Welinder committed
192
	gboolean         allow_sheetless_ref;
193 194 195 196
	XLContextType	 context;

	/* Accumulator for storing arrays after the expression */
	GSList		*arrays;
197
	GnmExprTop const *array_texpr;
198 199
} PolishData;

200
static void write_node (PolishData *pd, GnmExpr const *expr,
201 202 203 204 205
			int paren_level, XLOpType target_type);

#define CLASS_REF	0x00
#define CLASS_VAL	0x20
#define CLASS_ARRAY	0x40
206
static const guint8 xl_op_class[NUM_CTXTS][NUM_XL_TYPES][NUM_XL_TYPES+1] = {
207 208 209 210
	{ /* CELL | Wants REF	Wants VAL	Wants ARR	From Root */
/* From REF */	 { CLASS_REF,	CLASS_VAL,	CLASS_ARRAY,	CLASS_VAL },
/* From VAL */	 { CLASS_VAL,	CLASS_VAL,	CLASS_VAL,	CLASS_VAL },
/* From ARRAY */ { CLASS_ARRAY,	CLASS_VAL,	CLASS_ARRAY,	CLASS_VAL },
211
	},
212 213 214 215
	{ /* ARR  | Wants REF	Wants VAL	Wants ARR	From Root */
/* From REF */	 { CLASS_REF,	CLASS_VAL,	CLASS_ARRAY,	CLASS_VAL },
/* From VAL */	 { CLASS_ARRAY,	CLASS_VAL,	CLASS_ARRAY,	CLASS_VAL },
/* From ARRAY */ { CLASS_ARRAY,	CLASS_VAL,	CLASS_ARRAY,	CLASS_ARRAY },
216
	},
217 218 219 220
	{ /* NAME | Wants REF	Wants VAL	Wants ARR	From Root */
/* From REF */	 { CLASS_REF,	CLASS_ARRAY,	CLASS_ARRAY,	CLASS_REF },
/* From VAL */	 { CLASS_ARRAY,	CLASS_ARRAY,	CLASS_ARRAY,	CLASS_ARRAY },
/* From ARRAY */ { CLASS_ARRAY,	CLASS_ARRAY,	CLASS_ARRAY,	CLASS_ARRAY },
221 222 223 224 225 226 227 228 229 230 231 232 233 234
	}
};

static XLOpType
xl_map_char_to_type (char t)
{
	if (t == 'V')
		return XL_VAL;
	if (t == 'R')
		return XL_REF;
	if (t == 'A')
		return XL_ARRAY;
	if (t == 'v')
		return XL_ROOT;
Morten Welinder's avatar
Morten Welinder committed
235
	g_warning ("unknown op class '%c' assuming val", t ? t : '-');
236 237 238 239 240 241 242 243
	return XL_VAL;
}

static guint8
xl_get_op_class (PolishData *pd, XLOpType src_type, XLOpType target_type)
{
	return xl_op_class[pd->context][src_type][target_type];
}
244 245 246 247

static void
push_guint8 (PolishData *pd, guint8 b)
{
248
	ms_biff_put_var_write (pd->ewb->bp, &b, sizeof(guint8));
249 250 251 252 253 254
}

static void
push_guint16 (PolishData *pd, guint16 b)
{
	guint8 data[2];
255
	GSF_LE_SET_GUINT16 (data, b);
256
	ms_biff_put_var_write (pd->ewb->bp, data, sizeof(data));
257 258
}

259 260 261 262 263 264 265 266
static void
push_gint16 (PolishData *pd, gint16 b)
{
	guint8 data[2];
	GSF_LE_SET_GINT16 (data, b);
	ms_biff_put_var_write (pd->ewb->bp, data, sizeof(data));
}

267 268 269 270
static void
push_guint32 (PolishData *pd, guint32 b)
{
	guint8 data[4];
271
	GSF_LE_SET_GUINT32 (data, b);
272
	ms_biff_put_var_write (pd->ewb->bp, data, sizeof(data));
273 274
}

275
static void
Jody Goldberg's avatar
Jody Goldberg committed
276
write_cellref_v7 (PolishData *pd, GnmCellRef const *ref,
277
		  guint8 *out_col, guint8 *out_row)
278 279 280
{
	guint    row, col;

281
	if (pd->use_name_variant)
282 283
		col = ref->col & 0xff;
	else if (ref->col_relative)
284 285 286
		col = ref->col + pd->col;
	else
		col = ref->col;
287

288
	if (ref->row_relative && !pd->use_name_variant)
289 290 291 292 293
		row = ref->row + pd->row;
	else
		row = ref->row;

	if (ref->col_relative)
294
		row |= 0x4000;
295
	if (ref->row_relative)
296
		row |= 0x8000;
297

298 299
	GSF_LE_SET_GUINT16 (out_row, row);
	GSF_LE_SET_GUINT8  (out_col, col);
300 301
}

302
static void
Jody Goldberg's avatar
Jody Goldberg committed
303
write_cellref_v8 (PolishData *pd, GnmCellRef const *ref,
304
		  guint8 *out_col, guint8 *out_row)
305 306 307
{
	guint    row, col;

308
	if (pd->use_name_variant)
309 310
		col = ref->col & 0xff;
	else if (ref->col_relative)
311 312 313
		col = ref->col + pd->col;
	else
		col = ref->col;
314

315
	if (ref->row_relative && !pd->use_name_variant)
316 317 318 319 320 321 322 323 324
		row = ref->row + pd->row;
	else
		row = ref->row;

	if (ref->col_relative)
		col |= 0x4000;
	if (ref->row_relative)
		col |= 0x8000;

325 326
	GSF_LE_SET_GUINT16 (out_row, row);
	GSF_LE_SET_GUINT16 (out_col, col);
327 328
}

329
static void
330
write_string1 (PolishData *pd, gchar const *txt)
331
{
332
	push_guint8 (pd, FORMULA_PTG_STR);
333
	excel_write_string (pd->ewb->bp, STR_ONE_BYTE_LENGTH, txt);
334 335
}

336 337 338 339 340
static void
write_string (PolishData *pd, gchar const *txt)
{
	size_t i, n = 0, len = g_utf8_strlen (txt, -1);
	const char *p = txt;
Morten Welinder's avatar
Morten Welinder committed
341 342 343 344 345 346 347 348
	const size_t CHUNK_LEN = 255;

	/*
	 * The xls format only handles strings up to 255 characters
	 * in formulae.  To make up for that, we create expressions
	 * like ("s1"&"s2"&...).  Note, that the parentheses are
	 * explicitly present in the file.
	 */
349
	for (i = 0; n == 0 || i < len; ) {
Morten Welinder's avatar
Morten Welinder committed
350
		if (len - i <= CHUNK_LEN) {
351 352 353
			write_string1 (pd, p);
			i = len;
		} else {
Morten Welinder's avatar
Morten Welinder committed
354
			const char *endcut = g_utf8_offset_to_pointer (p, CHUNK_LEN);
355 356 357 358 359 360 361
			size_t cutlen = endcut - p;
			char *cut = g_memdup (p, cutlen + 1);
			cut[cutlen] = 0;
			write_string1 (pd, cut);
			g_free (cut);

			p = endcut;
Morten Welinder's avatar
Morten Welinder committed
362
			i += CHUNK_LEN;
363 364 365 366 367 368 369 370 371 372 373
		}

		if (n > 0)
			push_guint8 (pd, FORMULA_PTG_CONCAT);
		n++;
	}

	if (n > 1)
		push_guint8 (pd, FORMULA_PTG_PAREN);
}

374
static void
Jody Goldberg's avatar
Jody Goldberg committed
375
excel_formula_write_CELLREF (PolishData *pd, GnmCellRef const *ref,
376
			     Sheet *sheet_b, XLOpType target_type)
377 378
{
	guint8 data[24];
379 380
	guint8 ptg;
	guint8 op_class = xl_get_op_class (pd, XL_REF, target_type);
381

Jody Goldberg's avatar
Jody Goldberg committed
382 383 384 385
	g_return_if_fail (pd);
	g_return_if_fail (ref);

	if (ref->sheet == NULL) {
386

Jody Goldberg's avatar
Jody Goldberg committed
387 388
		g_return_if_fail (sheet_b == NULL);

Morten Welinder's avatar
Morten Welinder committed
389
		if (!pd->allow_sheetless_ref) {
390 391 392 393 394 395
			g_warning ("XL does not support unqualified references in global names");
		}

		ptg = (pd->use_name_variant && (ref->col_relative || ref->row_relative))
			? FORMULA_PTG_REFN : FORMULA_PTG_REF;
		push_guint8 (pd, ptg + op_class);
396
		if (pd->ewb->bp->version <= MS_BIFF_V7) {
397
			write_cellref_v7 (pd, ref, data + 2, data);
Jody Goldberg's avatar
Jody Goldberg committed
398
			ms_biff_put_var_write (pd->ewb->bp, data, 3);
399
		} else {
400
			write_cellref_v8 (pd, ref, data + 2, data);
Jody Goldberg's avatar
Jody Goldberg committed
401
			ms_biff_put_var_write (pd->ewb->bp, data, 4);
402 403
		}
	} else {
404
		push_guint8 (pd, FORMULA_PTG_REF_3D + op_class);
405 406 407 408 409
		if (pd->ewb->bp->version <= MS_BIFF_V7) {
			guint16 idx_a, idx_b;
			gint16 ixals;

			/* FIXME no external references for now */
410
			g_return_if_fail (pd->ewb->base.wb == ref->sheet->workbook);
411

Jody Goldberg's avatar
Jody Goldberg committed
412 413 414
			idx_a = ref->sheet->index_in_wb;
			idx_b = (sheet_b != NULL) ? sheet_b->index_in_wb : idx_a;
			ixals = -(idx_a+1);
415
			GSF_LE_SET_GUINT16 (data, ixals);
416 417
			GSF_LE_SET_GUINT32 (data +  2, 0x0);
			GSF_LE_SET_GUINT32 (data +  6, 0x0);
418 419
			GSF_LE_SET_GUINT16 (data + 10, idx_a);
			GSF_LE_SET_GUINT16 (data + 12, idx_b);
420
			write_cellref_v7 (pd, ref, data + 16, data + 14);
Jody Goldberg's avatar
Jody Goldberg committed
421
			ms_biff_put_var_write (pd->ewb->bp, data, 17);
422
		} else {
423
			guint16 extn_idx = excel_write_get_externsheet_idx (
Jody Goldberg's avatar
Jody Goldberg committed
424
						pd->ewb, ref->sheet, sheet_b);
425
			GSF_LE_SET_GUINT16 (data, extn_idx);
426
			write_cellref_v8 (pd, ref, data + 4, data + 2);
Jody Goldberg's avatar
Jody Goldberg committed
427
			ms_biff_put_var_write (pd->ewb->bp, data, 6);
428 429 430 431 432
		}
	}
}

static void
Jody Goldberg's avatar
Jody Goldberg committed
433
excel_formula_write_AREA (PolishData *pd, GnmCellRef const *a, GnmCellRef const *b,
434
			  XLOpType target_type)
435 436
{
	guint8 data[24];
437 438
	guint8 ptg;
	guint8 op_class = xl_get_op_class (pd, XL_REF, target_type);
439

Jody Goldberg's avatar
Jody Goldberg committed
440
	if (a->sheet == NULL && b->sheet == NULL) {
441

442
		if (!pd->allow_sheetless_ref) {
443 444 445 446 447 448 449 450
			g_warning ("XL does not support unqualified references in global names");
		}

		ptg = (pd->use_name_variant &&
		       (a->col_relative || a->row_relative ||
			b->col_relative || b->row_relative))
			? FORMULA_PTG_AREAN : FORMULA_PTG_AREA;
		push_guint8 (pd, ptg + op_class);
451
		if (pd->ewb->bp->version <= MS_BIFF_V7) {
452 453
			write_cellref_v7 (pd, a, data + 4, data);
			write_cellref_v7 (pd, b, data + 5, data + 2);
Jody Goldberg's avatar
Jody Goldberg committed
454
			ms_biff_put_var_write (pd->ewb->bp, data, 6);
455
		} else {
456 457
			write_cellref_v8 (pd, a, data + 4, data + 0);
			write_cellref_v8 (pd, b, data + 6, data + 2);
Jody Goldberg's avatar
Jody Goldberg committed
458
			ms_biff_put_var_write (pd->ewb->bp, data, 8);
459
		}
Jody Goldberg's avatar
Jody Goldberg committed
460
		return;
Jody Goldberg's avatar
Jody Goldberg committed
461
	}
Jody Goldberg's avatar
Jody Goldberg committed
462 463 464 465 466 467 468 469

	g_return_if_fail (a->sheet != NULL);

	if (a->col != b->col || a->row != b->row ||
	    a->col_relative != b->col_relative ||
	    a->row_relative != b->row_relative) {
		g_return_if_fail (a->sheet != NULL);

470
		push_guint8 (pd, FORMULA_PTG_AREA_3D + op_class);
471
		if (pd->ewb->bp->version <= MS_BIFF_V7) {
Jody Goldberg's avatar
Jody Goldberg committed
472
			guint16 idx_a, idx_b;
473 474 475
			gint16 ixals;

			/* FIXME no external references for now */
476
			g_return_if_fail (pd->ewb->base.wb == a->sheet->workbook);
Jody Goldberg's avatar
Jody Goldberg committed
477 478 479 480 481 482

			idx_a = a->sheet->index_in_wb;
			idx_b = (b->sheet != NULL)
				? b->sheet->index_in_wb : idx_a;

			ixals = -(idx_a+1);
483 484

			GSF_LE_SET_GUINT16 (data, ixals);
485 486
			GSF_LE_SET_GUINT32 (data +  2, 0x0);
			GSF_LE_SET_GUINT32 (data +  6, 0x0);
487
			GSF_LE_SET_GUINT16 (data + 10, idx_a);
Jody Goldberg's avatar
Jody Goldberg committed
488
			GSF_LE_SET_GUINT16 (data + 12, idx_b);
489 490
			write_cellref_v7 (pd, a, data + 18, data + 14);
			write_cellref_v7 (pd, b, data + 19, data + 16);
Jody Goldberg's avatar
Jody Goldberg committed
491
			ms_biff_put_var_write (pd->ewb->bp, data, 20);
492
		} else {
493
			guint16 extn_idx = excel_write_get_externsheet_idx (
Jody Goldberg's avatar
Jody Goldberg committed
494
						pd->ewb, a->sheet, b->sheet);
495
			GSF_LE_SET_GUINT16 (data, extn_idx);
496 497
			write_cellref_v8 (pd, a, data + 6, data + 2);
			write_cellref_v8 (pd, b, data + 8, data + 4);
Jody Goldberg's avatar
Jody Goldberg committed
498
			ms_biff_put_var_write (pd->ewb->bp, data, 10);
499
		}
Jody Goldberg's avatar
Jody Goldberg committed
500
	} else
501
		excel_formula_write_CELLREF (pd, a, b->sheet, target_type);
502 503
}

504
static char *
505
guess_arg_types (GnmFunc *func)
506
{
507 508
	char *res;
	int i, min, max;
509

Morten Welinder's avatar
Morten Welinder committed
510
	if (!gnm_func_is_fixargs (func))
511 512
		return NULL;

513
	gnm_func_count_args (func, &min, &max);
514

515 516 517 518 519 520 521 522 523
	res = g_new (char, max + 1);
	res[max] = 0;

	for (i = 0; i < max; i++) {
		char t = gnm_func_get_arg_type (func, i);
		if (t == 'r' || t == 'A')
			res[i] = 'A';
		else
			res[i] = 'V';
524 525 526 527 528 529 530 531 532 533
	}

#if FORMULA_DEBUG > 1
	g_printerr ("Coming up with arg types for %s: %s\n",
		    func->name, res);
#endif

	return res;
}

534
static void
535 536
write_funcall (PolishData *pd, GnmExpr const *expr,
	       XLOpType target_type)
537
{
538
	static guint8 const zeros [12];
539

540
	int arg, min_args, max_args, name_arg = 0;
541 542 543
	gboolean prompt   = FALSE;
	gboolean cmdequiv = FALSE;
	char const *arg_types = NULL;
544
	char *arg_types_holder = NULL;
545 546
	GnmFunc *func = expr->func.func;
	ExcelFunc *ef = g_hash_table_lookup (pd->ewb->function_map, func);
547
	XLOpType arg_type = XL_VAL; /* default */
548 549 550
	XLOpType func_type;
	int func_idx;
	guint8 op_class;
551

552 553
	g_return_if_fail (ef != NULL);

554 555 556 557
	if (ef->efunc) {
		min_args = ef->efunc->min_args;
		max_args = ef->efunc->max_args;
		func_idx = ef->efunc->idx;
Morten Welinder's avatar
Morten Welinder committed
558 559 560
		func_type = ef->efunc->type
			? xl_map_char_to_type (ef->efunc->type)
			: XL_VAL; // Assumption
561 562 563 564 565 566 567 568
		arg_types = ef->efunc->known_args;
	} else {
		min_args = max_args = expr->func.argc;
		func_idx = 0xff;
		func_type = XL_VAL;  /*Assumption */
	}

	if (ef->efunc == NULL || ef->efunc->idx == 0xff) {
569 570
		if (ef->macro_name != NULL) {
			push_guint8 (pd, FORMULA_PTG_NAME);
571
			push_guint16 (pd, ef->idx);
572 573
			ms_biff_put_var_write (pd->ewb->bp, zeros,
				(pd->ewb->bp->version <= MS_BIFF_V7) ? 12 : 2);
574
		} else {
575 576 577
			push_guint8 (pd, FORMULA_PTG_NAME_X);
			if (pd->ewb->bp->version <= MS_BIFF_V7) {
				/* The Magic Addin entry is after the real sheets
578 579
				 * for globals (names that call addins) and
				 * for locals (fomulas that call addins) */
580
				push_gint16  (pd, pd->ewb->esheets->len + 1);
581 582 583 584 585 586 587 588 589
				ms_biff_put_var_write (pd->ewb->bp, zeros, 8);
				push_guint16 (pd, ef->idx);
				ms_biff_put_var_write (pd->ewb->bp, zeros, 12);
			} else {
				/* I write the Addin Magic entry 1st */
				push_guint16 (pd, 0);
				push_guint16 (pd, ef->idx);
				push_guint16 (pd, 0); /* reserved */
			}
590
		}
591 592
		name_arg = 1;
	}
593

594 595 596
	if (!arg_types)
		arg_types = arg_types_holder = guess_arg_types (func);
	for (arg = 0; arg < expr->func.argc; arg++) {
597
		if (ef->efunc != NULL && arg >= ef->efunc->max_args) {
598
			go_io_warning (pd->ewb->io_context,
599
				_("Too many arguments for function '%s', MS Excel can only handle %d not %d"),
Morten Welinder's avatar
Morten Welinder committed
600
				ef->efunc->name, ef->efunc->max_args, expr->func.argc);
601
			break;
602 603 604 605 606 607
		} else { /* convert the args */
			if (arg_types != NULL && *arg_types) {
				arg_type = xl_map_char_to_type (*arg_types);
				if (arg_types[1])
					arg_types++;
			}
608
			write_node (pd, expr->func.argv[arg], 0, arg_type);
609
		}
610 611 612 613 614
	}
	/* If XL requires more arguments than we do
	 * pad the remainder with missing args */
	for ( ; arg < min_args ; arg++)
		push_guint8 (pd, FORMULA_PTG_MISSARG);
615
	g_free (arg_types_holder);
Jody Goldberg's avatar
Jody Goldberg committed
616

617 618 619 620 621 622 623 624
	op_class = xl_get_op_class (pd, func_type, target_type);
	if (name_arg || min_args != max_args) {
		push_guint8  (pd, FORMULA_PTG_FUNC_VAR + op_class);
		push_guint8  (pd, (arg + name_arg) | (prompt ? 0x80 : 0));
		push_guint16 (pd, func_idx         | (cmdequiv ? 0x8000 : 0));
	} else {
		push_guint8  (pd, FORMULA_PTG_FUNC + op_class);
		push_guint16 (pd, func_idx);
625 626 627
	}
}

628
static void
629 630
excel_formula_write_NAME_v8 (PolishData *pd, GnmExpr const *expr,
			     XLOpType target_type)
631 632 633 634 635 636 637 638 639 640 641 642 643
{
	guint8 data [7];
	gpointer tmp;
	unsigned name_idx;

	memset (data, 0, sizeof data);

	tmp = g_hash_table_lookup (pd->ewb->names,
				   (gpointer)expr->name.name);
	g_return_if_fail (tmp != NULL);

	name_idx = GPOINTER_TO_UINT (tmp);
	if (expr->name.optional_scope == NULL) {
644 645
		GSF_LE_SET_GUINT8  (data + 0, FORMULA_PTG_NAME +
			xl_get_op_class (pd, XL_REF, target_type));
646 647 648 649 650
		GSF_LE_SET_GUINT16 (data + 1, name_idx);
		ms_biff_put_var_write (pd->ewb->bp, data, 5);
	} else {
		guint16 extn_idx = excel_write_get_externsheet_idx (pd->ewb,
			expr->name.optional_scope, NULL);
651 652
		GSF_LE_SET_GUINT8  (data + 0, FORMULA_PTG_NAME_X +
			xl_get_op_class (pd, XL_REF, target_type));
653 654 655 656 657 658
		GSF_LE_SET_GUINT16 (data + 1, extn_idx);
		GSF_LE_SET_GUINT16 (data + 3, name_idx);
		ms_biff_put_var_write (pd->ewb->bp, data, 7);
	}
}

659
static void
660 661
excel_formula_write_NAME_v7 (PolishData *pd, GnmExpr const *expr,
			     XLOpType target_type)
662
{
Jody Goldberg's avatar
Jody Goldberg committed
663
	guint8 data [25];
Jody Goldberg's avatar
Jody Goldberg committed
664 665
	gpointer tmp;
	unsigned name_idx;
666

Jody Goldberg's avatar
Jody Goldberg committed
667
	memset (data, 0, sizeof data);
668

Jody Goldberg's avatar
Jody Goldberg committed
669 670 671
	tmp = g_hash_table_lookup (pd->ewb->names,
				   (gpointer)expr->name.name);
	g_return_if_fail (tmp != NULL);
672

Jody Goldberg's avatar
Jody Goldberg committed
673
	name_idx = GPOINTER_TO_UINT (tmp);
Jody Goldberg's avatar
Jody Goldberg committed
674
	if (expr->name.optional_scope == NULL) {
675 676
		GSF_LE_SET_GUINT8  (data + 0, FORMULA_PTG_NAME +
			xl_get_op_class (pd, XL_REF, target_type));
Jody Goldberg's avatar
Jody Goldberg committed
677 678 679
		GSF_LE_SET_GUINT16 (data + 1, name_idx);
		ms_biff_put_var_write (pd->ewb->bp, data, 15);
	} else {
680
		int externsheet = (pd->sheet == expr->name.optional_scope)
681
			? (int) (pd->ewb->esheets->len + 1)
682
			: expr->name.optional_scope->index_in_wb;
Morten Welinder's avatar
Morten Welinder committed
683
		guint32 id = ++(pd->ewb->unique_name_id);
684

685 686
		GSF_LE_SET_GUINT8  (data +  0, FORMULA_PTG_NAME_X +
			xl_get_op_class (pd, XL_REF, target_type));
687
		GSF_LE_SET_GUINT16 (data +  1, -(externsheet+1));
Jody Goldberg's avatar
Jody Goldberg committed
688 689 690
		GSF_LE_SET_GUINT16 (data + 11, name_idx);
		GSF_LE_SET_GUINT16 (data +  9,   1); /* undocumented marked 'reserved' */
		GSF_LE_SET_GUINT16 (data + 19, 0xf); /* undocumented marked 'reserved' */
Morten Welinder's avatar
Morten Welinder committed
691
		GSF_LE_SET_GUINT32 (data + 21, id); /* undocumented marked 'reserved' */
Jody Goldberg's avatar
Jody Goldberg committed
692 693
		ms_biff_put_var_write (pd->ewb->bp, data, 25);
	}
694 695
}

696
static void
697 698
write_node (PolishData *pd, GnmExpr const *expr, int paren_level,
	    XLOpType target_type)
699
{
700
	static struct {
701
		guint8 xl_op;
702
		int prec;		      /* Precedences -- should match parser.y  */
703
		int assoc_left, assoc_right;  /* 0: no, 1: yes.  */
704
	} const operations [] = {
705
		{ FORMULA_PTG_PAREN,	 0, 0, 0 }, /* Parentheses for clarity  */
706 707 708 709 710 711 712 713 714 715
		{ FORMULA_PTG_EQUAL,	 1, 1, 0 },
		{ FORMULA_PTG_GT,	 1, 1, 0 },
		{ FORMULA_PTG_LT,	 1, 1, 0 },
		{ FORMULA_PTG_GTE,	 1, 1, 0 },
		{ FORMULA_PTG_LTE,	 1, 1, 0 },
		{ FORMULA_PTG_NOT_EQUAL, 1, 1, 0 },
		{ FORMULA_PTG_ADD,	 3, 1, 0 },
		{ FORMULA_PTG_SUB,	 3, 1, 0 },
		{ FORMULA_PTG_MULT,	 4, 1, 0 },
		{ FORMULA_PTG_DIV,	 4, 1, 0 },
716
		{ FORMULA_PTG_EXP,	 5, 0, 1 },
717 718 719 720 721
		{ FORMULA_PTG_CONCAT,	 2, 1, 0 },
		{ 0, 0, 0, 0 }, /* Funcall  */
		{ 0, 0, 0, 0 }, /* Name     */
		{ 0, 0, 0, 0 }, /* Constant */
		{ 0, 0, 0, 0 }, /* Var      */
722 723 724
		{ FORMULA_PTG_U_MINUS,	 7, 0, 0 }, /* Unary - */
		{ FORMULA_PTG_U_PLUS,	 7, 0, 0 }, /* Unary + */
		{ FORMULA_PTG_PERCENT,	 6, 0, 0 }, /* Percentage (NOT MODULO) */
725 726
		{ 0, 0, 0, 0 },	/* Array Corner   */
		{ 0, 0, 0, 0 },	/* Array Element  */
Morten Welinder's avatar
Morten Welinder committed
727 728 729
		{ 0, 8, 1, 0 }, /* Set      */
		{ FORMULA_PTG_RANGE,	 10, 1, 0 },
		{ FORMULA_PTG_INTERSECT, 9, 1, 0 }
730
	};
731
	const GnmExprOp op = GNM_EXPR_GET_OPER (expr);
732

733
	switch (op) {
734 735 736
	case GNM_EXPR_OP_ANY_BINARY :
		if (target_type != XL_ARRAY)
			target_type = XL_VAL;
737
		/* Fall through.  */
738
	case GNM_EXPR_OP_RANGE_CTOR:
739
	case GNM_EXPR_OP_INTERSECT: {
740 741
		int const prec = operations[op].prec;

742 743 744 745 746 747
		write_node (pd, expr->binary.value_a,
			    prec - operations[op].assoc_left,
			    target_type);
		write_node (pd, expr->binary.value_b,
			    prec - operations[op].assoc_right,
			    target_type);
748 749 750
		push_guint8 (pd, operations[op].xl_op);
		if (prec <= paren_level)
			push_guint8 (pd, FORMULA_PTG_PAREN);
751
		break;
752 753
	}

754
	case GNM_EXPR_OP_FUNCALL :
755
		write_funcall (pd, expr, target_type);
756
		break;
757

758
        case GNM_EXPR_OP_CONSTANT : {
Jody Goldberg's avatar
Jody Goldberg committed
759
		GnmValue const *v = expr->constant.value;
760
		switch (v->v_any.type) {
761

762
		case VALUE_FLOAT: {
763
			guint8 data[10];
764 765 766 767
			gnm_float f = value_get_as_float (v);
			int i;

			if (f >= 0 && f <= 0xffff && f == (i = (int)f)) {
768 769
				GSF_LE_SET_GUINT8  (data, FORMULA_PTG_INT);
				GSF_LE_SET_GUINT16 (data + 1, i);
770
				ms_biff_put_var_write (pd->ewb->bp, data, 3);
771
			} else {
772
				GSF_LE_SET_GUINT8 (data, FORMULA_PTG_NUM);
773
				gsf_le_set_double (data + 1, f);
774
				ms_biff_put_var_write (pd->ewb->bp, data, 9);
775 776 777
			}
			break;
		}
778
		case VALUE_BOOLEAN : {
779
			guint8 data[2];
780
			GSF_LE_SET_GUINT8 (data, FORMULA_PTG_BOOL);
Morten Welinder's avatar
Morten Welinder committed
781
			GSF_LE_SET_GUINT8 (data+1, value_get_as_int (v));
782
			ms_biff_put_var_write (pd->ewb->bp, data, 2);
783 784 785
			break;
		}

786
		case VALUE_ERROR : {
787
			guint8 data[2];
788
			GSF_LE_SET_GUINT8 (data, FORMULA_PTG_ERR);
789 790
			GSF_LE_SET_GUINT8 (data+1, excel_write_map_errcode (v));
			ms_biff_put_var_write (pd->ewb->bp, data, 2);
791 792 793
			break;
		}

794
		case VALUE_EMPTY : {
795
			guint8 data = FORMULA_PTG_MISSARG;
796
			ms_biff_put_var_write (pd->ewb->bp, &data, 1);
797 798 799
			break;
		}

800
		case VALUE_STRING:
801
			write_string (pd, value_peek_string (v));
802
			break;
803

Jody Goldberg's avatar
Jody Goldberg committed
804
		case VALUE_CELLRANGE:
805 806 807
			excel_formula_write_AREA (pd,
				&v->v_range.cell.a, &v->v_range.cell.b,
				target_type);
808
			break;
809

810
		case VALUE_ARRAY : {
811
			guint8 data[8];
812

813
			if (v->v_array.x > 256 || v->v_array.y > 65536)
814 815
				g_warning ("Array far too big");

816 817 818 819 820 821
			/* Class is A or V
			 * name == A
			 * target_type == 'V' ? 'V' : 'A'
			 **/
			GSF_LE_SET_GUINT8  (data + 0, FORMULA_PTG_ARRAY +
				xl_get_op_class (pd, XL_ARRAY, target_type));
822 823 824 825
			GSF_LE_SET_GUINT8  (data + 1, v->v_array.x - 1);
			GSF_LE_SET_GUINT16 (data + 2, v->v_array.y - 1);
			GSF_LE_SET_GUINT16 (data + 4, 0x0); /* ? */
			GSF_LE_SET_GUINT16 (data + 6, 0x0); /* ? */
826
			ms_biff_put_var_write (pd->ewb->bp, data, 8);
827

828
			pd->arrays = g_slist_prepend (pd->arrays, (gpointer)v);
829 830
			break;
		}
831

832
		default : {
833 834
			write_string (pd, "(Unknown value)");
			g_warning ("Unhandled value type %d", v->v_any.type);
835 836
			break;
		}
837
		}
838 839
		break;
	}
840
	case GNM_EXPR_OP_ANY_UNARY : {
841
		int const prec = operations[op].prec;
842

843
		write_node (pd, expr->unary.value, operations[op].prec,
844
			(target_type != XL_ARRAY) ? XL_VAL : XL_ARRAY);
845
		push_guint8 (pd, operations[op].xl_op);
846
		if (op != GNM_EXPR_OP_PAREN && prec <= paren_level)
847
			push_guint8 (pd, FORMULA_PTG_PAREN);
Jody Goldberg's avatar
Jody Goldberg committed
848
		break;
849
	}
850

851
	case GNM_EXPR_OP_CELLREF :
852 853
		excel_formula_write_CELLREF (pd,
			&expr->cellref.ref, NULL, target_type);
854
		break;
855

856
	case GNM_EXPR_OP_NAME :
857
		if (pd->ewb->bp->version >= MS_BIFF_V8)
858
			excel_formula_write_NAME_v8 (pd, expr, target_type);
859
		else
860
			excel_formula_write_NAME_v7 (pd, expr, target_type);
861
		break;
862

863 864 865 866 867
	case GNM_EXPR_OP_ARRAY_CORNER :
	case GNM_EXPR_OP_ARRAY_ELEM : {
		guint8 data[5];
		int x, y, ptg;

868
		if (!gnm_expr_top_is_array_elem (pd->array_texpr, &x, &y))
869
			x = y = 0;
870

871
		ptg = FORMULA_PTG_EXPR;
872
		if (pd->sheet != NULL) {
Morten Welinder's avatar
Morten Welinder committed
873 874 875 876 877
			GnmCell const *ccell =
				sheet_cell_get (pd->sheet, pd->col - x, pd->row - y);
			if (ccell &&
			    gnm_expr_top_is_array_corner (ccell->base.texpr) &&
			    gnm_expr_is_data_table (gnm_expr_top_get_array_expr (ccell->base.texpr), NULL, NULL))
878 879 880
				ptg = FORMULA_PTG_TBL;
		}

881
		GSF_LE_SET_GUINT8 (data, ptg);
882 883
		GSF_LE_SET_GUINT16 (data+1, pd->row - y);
		GSF_LE_SET_GUINT16 (data+3, pd->col - x);
884
		ms_biff_put_var_write (pd->ewb->bp, data, 5);
885 886 887 888 889 890

		/* Be anal */
		g_return_if_fail (paren_level == 0);
		break;
	}

Morten Welinder's avatar
Morten Welinder committed
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
	case GNM_EXPR_OP_SET:
		if (expr->set.argc > 0) {
			int const prec = operations[op].prec;
			int i;

			write_node (pd, expr->set.argv[0],
				    operations[op].assoc_left,
				    target_type);

			for (i = 1; i < expr->set.argc; i++) {
				write_node (pd, expr->set.argv[i],
					    operations[op].assoc_right,
					    target_type);
				push_guint8 (pd, FORMULA_PTG_UNION);
			}
			if (prec <= paren_level)
				push_guint8 (pd, FORMULA_PTG_PAREN);
			break;
		}
		/* Fall through for now.  */

912
	default : {
913
		gchar *err = g_strdup_printf ("Unknown Operator %d",
914
					      GNM_EXPR_GET_OPER (expr));
915 916
		write_string (pd, err);
		g_free (err);
917
		g_warning ("Unhandled expr type %d", GNM_EXPR_GET_OPER (expr));
918 919
		break;
	}
920
	}
921 922
}

923 924 925 926 927
/* Writes the array and moves to next one */
/* See S59E2B.HTM for some really duff docs */
static void
write_arrays (PolishData *pd)
{
Jody Goldberg's avatar
Jody Goldberg committed
928
	GnmValue const *array;
929
	GSList  *ptr;
930
	int	 x, y;
931
	guint8   data[8];
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
	WriteStringFlags string_flags;

	string_flags = (pd->ewb->bp->version >= MS_BIFF_V8)
		? STR_TWO_BYTE_LENGTH : STR_ONE_BYTE_LENGTH;
	pd->arrays = g_slist_reverse (pd->arrays);
	for (ptr = pd->arrays ; ptr != NULL ; ptr = ptr->next) {
		array = ptr->data;
		if (pd->ewb->bp->version >= MS_BIFF_V8) {
			push_guint8  (pd, array->v_array.x - 1);
			push_guint16 (pd, array->v_array.y - 1);
		} else {
			push_guint8  (pd, (array->v_array.x == 256)
				      ? 0 : array->v_array.x);
			push_guint16 (pd, array->v_array.y);
		}
947

948 949
		for (y = 0; y < array->v_array.y; y++) {
			for (x = 0; x < array->v_array.x; x++) {
Jody Goldberg's avatar
Jody Goldberg committed
950
				GnmValue const *v = array->v_array.vals[x][y];
951

952 953 954 955 956
				if (VALUE_IS_EMPTY (v)) {
					push_guint8 (pd, 0);
					push_guint32 (pd, 0);
					push_guint32 (pd, 0);
				} else if (VALUE_IS_BOOLEAN (v)) {
957
					push_guint8 (pd, 4);
Morten Welinder's avatar
Morten Welinder committed
958
					push_guint32 (pd, value_get_as_int (v));
959
					push_guint32 (pd, 0);
960
				} else if (VALUE_IS_ERROR (v)) {
961 962 963
					push_guint8 (pd, 16);
					push_guint32 (pd, excel_write_map_errcode (v));
					push_guint32 (pd, 0);
964
				} else if (VALUE_IS_FLOAT (v)) {
965 966 967 968 969
					push_guint8 (pd, 1);
					gsf_le_set_double (data, value_get_as_float (v));
					ms_biff_put_var_write (pd->ewb->bp, data, 8);
				} else { /* Can only be a string */
					push_guint8 (pd, 2);
970 971
					excel_write_string (pd->ewb->bp, string_flags,
						value_peek_string (v));
972
				}
973 974 975 976
			}
		}
	}

977 978
	g_slist_free (pd->arrays);
	pd->arrays = NULL;
979 980 981
}

guint32
982
excel_write_array_formula (ExcelWriteState *ewb,
983
			   GnmExprTop const *texpr,
984 985 986 987 988 989 990
			   Sheet *sheet, int fn_col, int fn_row)
{
	PolishData pd;
	unsigned start;
	guint32 len;

	g_return_val_if_fail (ewb, 0);
991
	g_return_val_if_fail (texpr, 0);
992 993 994 995 996 997

	pd.col     = fn_col;
	pd.row     = fn_row;
	pd.sheet   = sheet;
	pd.ewb     = ewb;
	pd.arrays  = NULL;
998
	pd.array_texpr = texpr;
999 1000
	pd.context = CTXT_ARRAY;
	pd.use_name_variant = FALSE;
1001
	pd.allow_sheetless_ref = TRUE;
1002

1003
	start = ewb->bp->curpos;
1004
	write_node (&pd, gnm_expr_top_get_array_expr (texpr), 0, XL_ROOT);
1005
	len = ewb->bp->curpos - start;
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017

	write_arrays (&pd);

	return len;
}

/*
 * This is called for all expressions, including array expressions.
 * (But write_node will not write the inner expression for arrays.)
 */
guint32
excel_write_formula (ExcelWriteState *ewb, GnmExprTop const *texpr,
1018 1019
		     Sheet *sheet, int fn_col, int fn_row,
		     ExcelFuncContext context)
1020
{
1021
	PolishData pd;
1022
	unsigned start;
1023
	guint32 len;
1024
	XLOpType target = XL_ROOT;
1025

1026
	g_return_val_if_fail (ewb, 0);
1027
	g_return_val_if_fail (texpr, 0);
1028

1029 1030 1031 1032 1033
	pd.col     = fn_col;
	pd.row     = fn_row;
	pd.sheet   = sheet;
	pd.ewb     = ewb;
	pd.arrays  = NULL;
Morten Welinder's avatar
Morten Welinder committed
1034 1035
	pd.allow_sheetless_ref = TRUE;

1036 1037 1038 1039 1040 1041
	if (gnm_expr_top_is_array_corner (texpr) ||
	    gnm_expr_top_is_array_elem (texpr, NULL, NULL))
		pd.array_texpr = texpr;
	else
		pd.array_texpr = NULL;

1042 1043 1044 1045 1046 1047
	switch (context) {
	case EXCEL_CALLED_FROM_CELL:
		pd.context = CTXT_CELL;
		pd.use_name_variant = FALSE;
		break;
	case EXCEL_CALLED_FROM_SHARED:
1048
		pd.context = CTXT_CELL;
1049 1050 1051
		pd.use_name_variant = TRUE;
		break;
	case EXCEL_CALLED_FROM_NAME:
Morten Welinder's avatar
Morten Welinder committed
1052 1053 1054 1055 1056 1057
		pd.context = CTXT_NAME_OBJ;
		pd.use_name_variant = TRUE;
		pd.allow_sheetless_ref = FALSE;
		break;
	case EXCEL_CALLED_FROM_OBJ:
		pd.context = CTXT_NAME_OBJ;
1058 1059
		pd.use_name_variant = TRUE;
		break;
1060 1061 1062 1063 1064
	case EXCEL_CALLED_FROM_VALIDATION_LIST:
		pd.context = CTXT_ARRAY;
		pd.use_name_variant = TRUE;
		target = XL_REF;
		break;
1065 1066 1067 1068 1069 1070
	case EXCEL_CALLED_FROM_CONDITION:
	case EXCEL_CALLED_FROM_VALIDATION:
	default:
		pd.context = CTXT_ARRAY; /* What???  */
		pd.use_name_variant = TRUE;
	}
1071

1072
	start = ewb->bp->curpos;
1073
	write_node (&pd, texpr->expr, 0, target);
1074
	len = ewb->bp->curpos - start;
1075

1076
	write_arrays (&pd);
1077

1078
	return len;
1079
}