1 /*
2  * fn-information.c:  Information built-in functions
3  *
4  * Authors:
5  *   Jukka-Pekka Iivonen (iivonen@iki.fi)
6  *   Jody Goldberg (jody@gnome.org)
7  *   Morten Welinder (terra@gnome.org)
8  *   Almer S. Tigelaar (almer@gnome.org)
9  *   Harlan Grove
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, see <https://www.gnu.org/licenses/>.
23  *
24  * Many thanks to Harlan Grove for his excellent characterization and writeup
25  * of the multitude of different potential arguments across the various
26  * different spreadsheets.  Although neither the code is not his, the set of
27  * attributes, and the comments on their behviour are.  Hence he holds partial
28  * copyright on the CELL implementation.
29  */
30 
31 #include <gnumeric-config.h>
32 #include <gnumeric.h>
33 #include <func.h>
34 #include <parse-util.h>
35 #include <cell.h>
36 #include <ranges.h>
37 #include <sheet.h>
38 #include <workbook.h>
39 #include <gnm-format.h>
40 #include <style.h>
41 #include <style-font.h>
42 #include <value.h>
43 #include <expr.h>
44 #include <workbook.h>
45 #include <sheet-style.h>
46 #include <number-match.h>
47 #include <gnm-i18n.h>
48 #include <hlink.h>
49 
50 #include <goffice/goffice.h>
51 #include <gnm-plugin.h>
52 
53 #ifdef HAVE_UNAME
54 #include <sys/utsname.h>
55 #endif
56 #include <math.h>
57 #include <stdlib.h>
58 #include <string.h>
59 
60 GNM_PLUGIN_MODULE_HEADER;
61 
62 /***************************************************************************/
63 
64 static GnmFuncHelp const help_cell[] = {
65         { GNM_FUNC_HELP_NAME, F_("CELL:information of @{type} about @{cell}")},
66         { GNM_FUNC_HELP_ARG, F_("type:string specifying the type of information requested")},
67         { GNM_FUNC_HELP_ARG, F_("cell:cell reference")},
68         { GNM_FUNC_HELP_DESCRIPTION, F_("@{type} specifies the type of information you want to obtain:\n"
69 					"  address        \t\tReturns the given cell reference as text.\n"
70 					"  col            \t\tReturns the number of the column in @{cell}.\n"
71 					"  color          \t\tReturns 0.\n"
72 					"  contents       \t\tReturns the contents of the cell in @{cell}.\n"
73 					"  column         \t\tReturns the number of the column in @{cell}.\n"
74 					"  columnwidth    \tReturns the column width.\n"
75 					"  coord          \t\tReturns the absolute address of @{cell}.\n"
76 					"  datatype       \tsame as type\n"
77 					"  filename       \t\tReturns the name of the file of @{cell}.\n"
78 					"  format         \t\tReturns the code of the format of the cell.\n"
79 					"  formulatype    \tsame as type\n"
80 					"  locked         \t\tReturns 1 if @{cell} is locked.\n"
81 					"  parentheses    \tReturns 1 if @{cell} contains a negative value\n"
82 					"                 \t\tand its format displays it with parentheses.\n"
83 					"  prefix         \t\tReturns a character indicating the horizontal\n"
84 					"                 \t\talignment of @{cell}.\n"
85 					"  prefixcharacter  \tsame as prefix\n"
86 					"  protect        \t\tReturns 1 if @{cell} is locked.\n"
87 					"  row            \t\tReturns the number of the row in @{cell}.\n"
88 					"  sheetname      \tReturns the name of the sheet of @{cell}.\n"
89 					"  type           \t\tReturns \"l\" if @{cell} contains a string, \n"
90 					"                 \t\t\"v\" if it contains some other value, and \n"
91 					"                 \t\t\"b\" if @{cell} is blank.\n"
92 					"  value          \t\tReturns the contents of the cell in @{cell}.\n"
93 					"  width          \t\tReturns the column width.")},
94 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
95         { GNM_FUNC_HELP_EXAMPLES, "=CELL(\"col\",A1)" },
96         { GNM_FUNC_HELP_EXAMPLES, "=CELL(\"width\",A1)" },
97         { GNM_FUNC_HELP_SEEALSO, "INDIRECT"},
98         { GNM_FUNC_HELP_END}
99 };
100 
101 typedef struct {
102 	char const *format;
103 	char const *output;
104 } translate_t;
105 static const translate_t translate_table[] = {
106 #if 0
107 	{ "General", "G" },
108 	{ "0", "F0" },
109 	{ "#,##0", ",0" },
110 	{ "0.00", "F2" },
111 	{ "#,##0.00", ",2" },
112 	{ "\"$\"#,##0_);\\(\"$\"#,##0\\)", "C0" },
113 	{ "\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)", "C0-" },
114 	{ "\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)", "C2" },
115 	{ "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)", "C2-" },
116 	{ "0%", "P0" },
117 	{ "0.00%", "P2" },
118 	{ "0.00e+00", "S2" },
119 	{ "# ?/?", "G" },
120 	{ "# ?" "?/?" "?", "G" },   /* Don't accidentally use trigraphs here. */
121 #endif
122 	{ "m/d/yy", "D4" },
123 	{ "m/d/yy h:mm", "D4" },
124 	{ "mm/dd/yy", "D4" },
125 	{ "d-mmm-yy", "D1" },
126 	{ "dd-mmm-yy", "D1" },
127 	{ "d-mmm", "D2" },
128 	{ "dd-mmm", "D2" },
129 	{ "mmm-yy", "D3" },
130 	{ "mm/dd", "D5" },
131 	{ "h:mm am/pm", "D7" },
132 	{ "h:mm:ss am/pm", "D6" },
133 	{ "h:mm", "D9" },
134 	{ "h:mm:ss", "D8" }
135 };
136 
137 static GnmValue *
translate_cell_format(GOFormat const * format)138 translate_cell_format (GOFormat const *format)
139 {
140 	int i;
141 	const char *fmt;
142 	const int translate_table_count = G_N_ELEMENTS (translate_table);
143 	gboolean exact;
144 	GOFormatDetails details;
145 
146 	if (format == NULL)
147 		goto fallback;
148 
149 	fmt = go_format_as_XL (format);
150 
151 	/*
152 	 * TODO : What does this do in different locales ??
153 	 */
154 	for (i = 0; i < translate_table_count; i++) {
155 		const translate_t *t = &translate_table[i];
156 
157 		if (!g_ascii_strcasecmp (fmt, t->format)) {
158 			return value_new_string (t->output);
159 		}
160 	}
161 
162 	go_format_get_details (format, &details, &exact);
163 	if (0 && !exact) {
164 		g_printerr ("Inexact for %s\n", fmt);
165 		goto fallback;
166 	}
167 
168 	switch (details.family) {
169 	case GO_FORMAT_NUMBER:
170 		return value_new_string_nocopy
171 			(g_strdup_printf
172 			 ("%c%d",
173 			  details.thousands_sep ? ',' : 'F',
174 			  details.num_decimals));
175 	case GO_FORMAT_CURRENCY:
176 	case GO_FORMAT_ACCOUNTING:
177 		return value_new_string_nocopy
178 			(g_strdup_printf
179 			 ("C%d%s",
180 			  details.num_decimals,
181 			  details.negative_red ? "-" : ""));
182 	case GO_FORMAT_PERCENTAGE:
183 		return value_new_string_nocopy
184 			(g_strdup_printf
185 			 ("P%d",
186 			  details.num_decimals));
187 	case GO_FORMAT_SCIENTIFIC:
188 		return value_new_string_nocopy
189 			(g_strdup_printf
190 			 ("S%d",
191 			  details.num_decimals));
192 	default:
193 		goto fallback;
194 	}
195 
196 fallback:
197 	return value_new_string ("G");
198 }
199 
200 /* TODO : turn this into a range based routine */
201 static GnmValue *
gnumeric_cell(GnmFuncEvalInfo * ei,GnmValue const * const * argv)202 gnumeric_cell (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
203 {
204 	char const *info_type = value_peek_string (argv[0]);
205 	GnmCellRef const *ref;
206 	const Sheet *sheet;
207 
208 	if (!VALUE_IS_CELLRANGE (argv[1]))
209 		return value_new_error_VALUE (ei->pos);
210 
211 	ref = &argv[1]->v_range.cell.a;
212 	sheet = eval_sheet (ref->sheet, ei->pos->sheet);
213 
214 	/*
215 	 * CELL translates its keywords (ick)
216 	  adresse	- address
217 	  colonne	- col
218 	  contenu	- contents
219 	  couleur	- color
220 	  format	- format
221 	  largeur	- width
222 	  ligne		- row
223 	  nomfichier	- filename
224 	  parentheses	- parentheses
225 	  prefixe	- prefix
226 	  protege	- protect
227 	  type		- type
228 	  */
229 
230 	/* from CELL - limited usefulness! */
231 	if (!g_ascii_strcasecmp(info_type, "address")) {
232 		GnmParsePos pp;
233 		GnmConventionsOut out;
234 		out.accum = g_string_new (NULL);
235 		out.pp    = parse_pos_init_evalpos (&pp, ei->pos);
236 		out.convs = gnm_conventions_default;
237 		cellref_as_string (&out, ref, TRUE);
238 		return value_new_string_nocopy (g_string_free (out.accum, FALSE));
239 
240 	} else if (!g_ascii_strcasecmp(info_type, "sheetname")) {
241 		return value_new_string (sheet->name_unquoted);
242 
243 	/* from later 123 versions - USEFUL! */
244 	} else if (!g_ascii_strcasecmp(info_type, "coord")) {
245 		GnmParsePos pp;
246 		GnmConventionsOut out;
247 		out.accum = g_string_new (NULL);
248 		out.pp    = parse_pos_init_evalpos (&pp, ei->pos);
249 		out.convs = gnm_conventions_default;
250 		cellref_as_string (&out, ref, TRUE);
251 		return value_new_string_nocopy (g_string_free (out.accum, FALSE));
252 
253 	/* from CELL - pointless - use COLUMN instead! */
254 	} else if (!g_ascii_strcasecmp (info_type, "col") ||
255 		   !g_ascii_strcasecmp (info_type, "column")) {
256 		return value_new_int (ref->col + 1);
257 
258 	/* from CELL - pointless - use ROW instead! */
259 	} else if (!g_ascii_strcasecmp (info_type, "row")) {
260 		return value_new_int (ref->row + 1);
261 
262 	/* from CELL - limited usefulness
263 	 * NOTE: differences between Excel & 123 - Excel's returns 1 whenever
264 	 * there's a color specified for EITHER positive OR negative values
265 	 * in the number format, e.g., 1 for format "[Black]0;-0;0" but not
266 	 * for format "0;-0;[Green]0"
267 	 * Another place where Excel doesn't conform to its documentation!
268 	 *
269 	 * 20180503: and even the above isn't right.  What appears to be test
270 	 * is this:
271 	 * (a) The format must be conditional; "[Red]0" won't do
272 	 * (b) One of the first two conditional formats must have a color
273 	 *     specified.
274 	 */
275 	} else if (!g_ascii_strcasecmp (info_type, "color")) {
276 		/* See 1.7.6 for old version.  */
277 		return value_new_int (0);
278 
279 	/* absolutely pointless - compatibility only */
280 	} else if (!g_ascii_strcasecmp (info_type, "contents") ||
281 		   !g_ascii_strcasecmp (info_type, "value")) {
282 		GnmCell const *cell =
283 			sheet_cell_get (sheet, ref->col, ref->row);
284 		if (cell && cell->value)
285 			return value_dup (cell->value);
286 		return value_new_empty ();
287 
288 	/* from CELL - limited usefulness!
289 	 * A testament to Microsoft's hypocracy! They could include this from
290 	 * 123R2.2 (it wasn't in 123R2.0x), modify it in Excel 4.0 to include
291 	 * the worksheet name, but they can't make any other changes to CELL?!
292 	 */
293 	} else if (!g_ascii_strcasecmp (info_type, "filename")) {
294 		char const *name = go_doc_get_uri (GO_DOC (sheet->workbook));
295 
296 		if (name == NULL)
297 			return value_new_string ("");
298 		else
299 			return value_new_string (name);
300 
301 	/* from CELL */
302 	/* Backwards compatibility w/123 - unnecessary */
303 	} else if (!g_ascii_strcasecmp (info_type, "format")) {
304 		GnmStyle const *mstyle =
305 			sheet_style_get (sheet, ref->col, ref->row);
306 
307 		return translate_cell_format (gnm_style_get_format (mstyle));
308 
309 	/* from CELL */
310 	/* Backwards compatibility w/123 - unnecessary */
311 	} else if (!g_ascii_strcasecmp (info_type, "parentheses")) {
312 		/* See 1.7.6 for old version.  */
313 		return value_new_int (0);
314 
315 	/* from CELL */
316 	/* Backwards compatibility w/123 - unnecessary */
317 	} else if (!g_ascii_strcasecmp (info_type, "prefix") ||
318 		   !g_ascii_strcasecmp (info_type, "prefixcharacter")) {
319 		GnmStyle const *mstyle =
320 			sheet_style_get (sheet, ref->col, ref->row);
321 		GnmCell const *cell =
322 			sheet_cell_get (sheet, ref->col, ref->row);
323 
324 		if (cell && cell->value && VALUE_IS_STRING (cell->value)) {
325 			switch (gnm_style_get_align_h (mstyle)) {
326 			case GNM_HALIGN_GENERAL:
327 			case GNM_HALIGN_LEFT:
328 			case GNM_HALIGN_JUSTIFY:
329 			case GNM_HALIGN_DISTRIBUTED:
330 						return value_new_string ("'");
331 			case GNM_HALIGN_RIGHT:	return value_new_string ("\"");
332 			case GNM_HALIGN_CENTER_ACROSS_SELECTION:
333 			case GNM_HALIGN_CENTER:	return value_new_string ("^");
334 			case GNM_HALIGN_FILL:	return value_new_string ("\\");
335 			default :		return value_new_string ("");
336 			}
337 		}
338 		return value_new_string ("");
339 
340 	/* from CELL */
341 	} else if (!g_ascii_strcasecmp (info_type, "locked") ||
342 		   !g_ascii_strcasecmp (info_type, "protect")) {
343 		GnmStyle const *mstyle =
344 			sheet_style_get (sheet, ref->col, ref->row);
345 		return value_new_int (gnm_style_get_contents_locked (mstyle) ? 1 : 0);
346 
347     /* different characteristics grouped for efficiency
348      * TYPE needed for backward compatibility w/123 but otherwise useless
349      * DATATYPE and FORMULATYPE are options in later 123 versions' @CELL
350      * no need for them but included to make 123 conversion easier
351     Case "datatype", "formulatype", "type"
352         t = Left(prop, 1)
353 
354             rv = IIf( t = "f" And rng.HasFormula, "f", "" )
355 
356             If rng.formula = "" Then
357                 rv = rv & "b"
358             ElseIf IsNumeric("0" & CStr(rng.Value)) _
359               Or (t = "t" And IsError(rng.Value)) Then
360                 rv = rv & "v"
361             ElseIf rng.Value = CVErr(xlErrNA) Then
362                 rv = rv & "n"
363             ElseIf IsError(rng.Value) Then
364                 rv = rv & "e"
365             Else
366                 rv = rv & "l"
367             End If
368         End If
369 	*/
370 
371 	} else if (!g_ascii_strcasecmp (info_type, "type") ||
372 		   !g_ascii_strcasecmp (info_type, "datatype") ||
373 		   !g_ascii_strcasecmp (info_type, "formulatype")) {
374 		GnmCell const *cell =
375 			sheet_cell_get (sheet, ref->col, ref->row);
376 		if (cell && cell->value) {
377 			if (VALUE_IS_STRING (cell->value))
378 				return value_new_string ("l");
379 			else
380 				return value_new_string ("v");
381 		}
382 		return value_new_string ("b");
383 
384 	/* from CELL */
385 	} else if (!g_ascii_strcasecmp (info_type, "width") ||
386 		   !g_ascii_strcasecmp (info_type, "columnwidth")) {
387 		ColRowInfo const *info =
388 			sheet_col_get_info (sheet, ref->col);
389 		double charwidth;
390 		int    cellwidth;
391 
392 		charwidth = gnm_font_default_width;
393 		cellwidth = info->size_pts;
394 
395 		return value_new_int (rint (cellwidth / charwidth));
396 	}
397 
398 	return value_new_error_VALUE (ei->pos);
399 }
400 
401 #if 0
402 /*
403 *extension to CELL providing 123 @CELL/@CELLPOINTER functionality as
404 *well as access to most Range properties
405 *1st arg determines the property of characteristic being sought
406 *2nd arg [OPTIONAL] specifies cell reference - AcitveCell if missing
407 *3rd arg [OPTIONAL] specifies whether to return an array or not
408 *    True = return array result for .Areas(1)
409 *    False/missing = return scalar result for .Areas(1).Cells(1, 1)
410 */
411 Function ExtCell( _
412   prop As String, _
413   Optional rng As Variant, _
414   Optional rar As Boolean = False _
415 ) As Variant
416     Dim ws As Worksheet, wb As Workbook, rv As Variant
417     Dim i As Long, j As Long, m As Long, n As Long, t As String
418 
419     Application.Volatile True
420 
421     If TypeOf rng Is Range Then
422         If rar Then
423             Set rng = rng.Areas(1)
424         Else
425             Set rng = rng.Areas(1).Cells(1, 1)
426         End If
427     ElseIf IsMissing(rng) Then
428         Set rng = ActiveCell
429     Else
430         ExtCell = CVErr(xlErrRef)
431         Exit Function
432     End If
433 
434     prop = LCase(prop)
435 
436     m = rng.rows.Count
437     n = rng.Columns.Count
438     rv = rng.Value
439 
440     Set ws = rng.Worksheet
441     Set wb = ws.Parent
442 
443     Select Case prop
444 
445     Case "across"  /* from later 123 versions - limited usefulness! */
446         If rar Then
447             For i = 1 To m
448                 For j = 1 To n
449                     rv(i, j) = CLng( _
450                       rng.Cells(i, j).HorizontalAlignment = _
451                       xlHAlignCenterAcrossSelection _
452                     )
453                 Next j
454             Next i
455         Else
456             rv = CLng( _
457               rng.HorizontalAlignment = _
458               xlHAlignCenterAcrossSelection _
459             )
460         End If
461 
462     Case "backgroundcolor"  /* from later 123 versions - USEFUL! */
463         If rar Then
464             For i = 1 To m
465                 For j = 1 To n
466                     rv(i, j) = rng.Cells(i, j).Interior.ColorIndex
467                 Next j
468             Next i
469         Else
470             rv = rng.Interior.ColorIndex
471         End If
472 
473     Case "bold"  /* from later 123 versions - USEFUL! */
474         If rar Then
475             For i = 1 To m
476                 For j = 1 To n
477                     rv(i, j) = CLng(rng.Cells(i, j).Font.Bold)
478                 Next j
479             Next i
480         Else
481             rv = CLng(rng.Font.Bold)
482         End If
483 
484 
485     Case "bottomborder"  /* from later 123 versions - USEFUL! */
486     /* Note: many possible return values! wrap inside SIGN to test T/F */
487         If rar Then
488             For i = 1 To m
489                 For j = 1 To n
490                     rv(i, j) = _
491                       rng.Cells(i, j).Borders(xlEdgeBottom).LineStyle - _
492                       xlLineStyleNone
493                 Next j
494             Next i
495         Else
496             rv = rng.Borders(xlEdgeBottom).LineStyle - xlLineStyleNone
497         End If
498 
499     Case "bottombordercolor"  /* from later 123 versions - USEFUL! */
500         If rar Then
501             For i = 1 To m
502                 For j = 1 To n
503                     rv(i, j) = _
504                       rng.Cells(i, j).Borders(xlEdgeBottom).ColorIndex
505                 Next j
506             Next i
507         Else
508             rv = rng.Borders(xlEdgeBottom).ColorIndex
509         End If
510 
511     Case "columnhidden"
512         If rar Then
513             For i = 1 To m
514                 For j = 1 To n
515                     rv(i, j) = rng.Cells(i, j).EntireColumn.Hidden
516                 Next j
517             Next i
518         Else
519             rv = rng.EntireColumn.Hidden
520         End If
521 
522     Case "comment"
523         If rar Then
524             For i = 1 To m
525                 For j = 1 To n
526                     If Not rng.Cells(i, j).Comment Is Nothing Then
527                         rv(i, j) = rng.Cells(i, j).Comment.text
528                     Else
529                         rv(i, j) = ""
530                     End If
531                 Next j
532             Next i
533         Else
534             If Not rng.Comment Is Nothing Then
535                 rv = rng.Comment.text
536             Else
537                 rv = ""
538             End If
539         End If
540 
541     Case "currentarray"  /* NOTE: returns Range addresses! */
542         If rar Then
543             For i = 1 To m
544                 For j = 1 To n
545                     rv(i, j) = rng.Cells(i, j).CurrentArray.Address
546                 Next j
547             Next i
548         Else
549             rv = rng.CurrentArray.Address
550         End If
551 
552     Case "currentregion"  /* NOTE: returns Range addresses! */
553         If rar Then
554             For i = 1 To m
555                 For j = 1 To n
556                     rv(i, j) = rng.Cells(i, j).CurrentRegion.Address
557                 Next j
558             Next i
559         Else
560             rv = rng.CurrentRegion.Address
561         End If
562 
563     Case "filedate"  /* from later 123 versions - limited usefulness! */
564         t = wb.BuiltinDocumentProperties("Last Save Time")  /* invariant! */
565 
566         If rar Then
567             For i = 1 To m
568                 For j = 1 To n
569                     rv(i, j) = t
570                 Next j
571             Next i
572         Else
573             rv = t
574         End If
575 
576     Case "fontface", "fontname", "typeface"  /* from later 123 versions */
577         If rar Then
578             For i = 1 To m
579                 For j = 1 To n
580                     rv(i, j) = rng.Cells(i, j).Font.Name
581                 Next j
582             Next i
583         Else
584             rv = rng.Font.Name
585         End If
586 
587     Case "fontsize", "pitch", "typesize"  /* from later 123 versions */
588         If rar Then
589             For i = 1 To m
590                 For j = 1 To n
591                     rv(i, j) = rng.Cells(i, j).Font.Size
592                 Next j
593             Next i
594         Else
595             rv = rng.Font.Size
596         End If
597 
598     Case "formula"
599         If rar Then
600             For i = 1 To m
601                 For j = 1 To n
602                     rv(i, j) = rng.Cells(i, j).formula
603                 Next j
604             Next i
605         Else
606             rv = rng.formula
607         End If
608 
609     Case "formulaarray"  /* questionable usefulness */
610         If rar Then
611             For i = 1 To m
612                 For j = 1 To n
613                     rv(i, j) = rng.Cells(i, j).FormulaArray
614                 Next j
615             Next i
616         Else
617             rv = rng.FormulaArray
618         End If
619 
620     Case "formulahidden"
621         If rar Then
622             For i = 1 To m
623                 For j = 1 To n
624                     rv(i, j) = CLng(rng.Cells(i, j).FormulaHidden)
625                 Next j
626             Next i
627         Else
628             rv = CLng(rng.FormulaHidden)
629         End If
630 
631     Case "formulalocal"
632         If rar Then
633             For i = 1 To m
634                 For j = 1 To n
635                     rv(i, j) = rng.Cells(i, j).FormulaLocal
636                 Next j
637             Next i
638         Else
639             rv = rng.FormulaLocal
640         End If
641 
642     Case "formular1c1"
643         If rar Then
644             For i = 1 To m
645                 For j = 1 To n
646                     rv(i, j) = rng.Cells(i, j).FormulaR1C1
647                 Next j
648             Next i
649         Else
650             rv = rng.FormulaR1C1
651         End If
652 
653     Case "formular1c1local"
654         If rar Then
655             For i = 1 To m
656                 For j = 1 To n
657                     rv(i, j) = rng.Cells(i, j).FormulaR1C1Local
658                 Next j
659             Next i
660         Else
661             rv = rng.FormulaR1C1Local
662         End If
663 
664     Case "halign", "horizontalalignment"  /* from later 123 versions */
665     /* Note: different return values than 123. 0 = general alignment */
666         If rar Then
667             For i = 1 To m
668                 For j = 1 To n
669                     rv(i, j) = _
670                       rng.Cells(i, j).HorizontalAlignment - _
671                       xlHAlignGeneral
672                 Next j
673             Next i
674         Else
675             rv = rng.HorizontalAlignment - xlHAlignGeneral
676         End If
677 
678     Case "hasarray"
679         If rar Then
680             For i = 1 To m
681                 For j = 1 To n
682                     rv(i, j) = CLng(rng.Cells(i, j).HasArray)
683                 Next j
684             Next i
685         Else
686             rv = CLng(rng.HasArray)
687         End If
688 
689     Case "hasformula"
690         If rar Then
691             For i = 1 To m
692                 For j = 1 To n
693                     rv(i, j) = CLng(rng.Cells(i, j).HasFormula)
694                 Next j
695             Next i
696         Else
697             rv = CLng(rng.HasFormula)
698         End If
699 
700     Case "hashyperlink", "hashyperlinks"
701         If rar Then
702             For i = 1 To m
703                 For j = 1 To n
704                     rv(i, j) = CLng(rng.Cells(i, j).Hyperlinks.Count > 0)
705                 Next j
706             Next i
707         Else
708             rv = CLng(rng.Hyperlinks.Count > 0)
709         End If
710 
711     Case "height", "rowheight"  /* from later 123 versions - USEFUL! */
712         If rar Then
713             For i = 1 To m
714                 For j = 1 To n
715                     rv(i, j) = rng.Cells(i, j).Height
716                 Next j
717             Next i
718         Else
719             rv = rng.Height
720         End If
721 
722     Case "hidden"  /* see ColumnHidden and RowHidden - this is less useful */
723         If rar Then
724             For i = 1 To m
725                 For j = 1 To n
726                     rv(i, j) = CLng(rng.Cells(i, j).Hidden)
727                 Next j
728             Next i
729         Else
730             rv = CLng(rng.Hidden)
731         End If
732 
733     Case "hyperlinkaddress"
734         If rar Then
735             For i = 1 To m
736                 For j = 1 To n
737                     rv(i, j) = rng.Cells(i, j).Hyperlinks(1).Address
738                 Next j
739             Next i
740         Else
741             rv = rng.Hyperlinks(1).Address
742         End If
743 
744     Case "indentlevel"
745         If rar Then
746             For i = 1 To m
747                 For j = 1 To n
748                     rv(i, j) = rng.Cells(i, j).rng.IndentLevel
749                 Next j
750             Next i
751         Else
752             rv = rng.rng.IndentLevel
753         End If
754 
755     Case "italic"  /* from later 123 versions - USEFUL! */
756         If rar Then
757             For i = 1 To m
758                 For j = 1 To n
759                     rv(i, j) = CLng(rng.Cells(i, j).Font.Italic)
760                 Next j
761             Next i
762         Else
763             rv = CLng(rng.Font.Italic)
764         End If
765 
766     Case "left"
767         If rar Then
768             For i = 1 To m
769                 For j = 1 To n
770                     rv(i, j) = rng.Cells(i, j).Left
771                 Next j
772             Next i
773         Else
774             rv = rng.Left
775         End If
776 
777     Case "leftborder"  /* from later 123 versions */
778     /* Note: many possible return values! wrap inside SIGN to test T/F */
779         If rar Then
780             For i = 1 To m
781                 For j = 1 To n
782                     rv(i, j) = _
783                       rng.Cells(i, j).Borders(xlEdgeLeft).LineStyle - _
784                       xlLineStyleNone
785                 Next j
786             Next i
787         Else
788             rv = rng.Borders(xlEdgeLeft).LineStyle - xlLineStyleNone
789         End If
790 
791     Case "leftbordercolor"  /* from later 123 versions */
792         If rar Then
793             For i = 1 To m
794                 For j = 1 To n
795                     rv(i, j) = _
796                       rng.Cells(i, j).Borders(xlEdgeLeft).ColorIndex
797                 Next j
798             Next i
799         Else
800             rv = rng.Borders(xlEdgeLeft).ColorIndex
801         End If
802 
803     Case "mergearea"  /* NOTE: returns Range addresses! */
804         If rar Then
805             For i = 1 To m
806                 For j = 1 To n
807                     rv(i, j) = rng.Cells(i, j).MergeArea.Address
808                 Next j
809             Next i
810         Else
811             rv = rng.MergeArea.Address
812         End If
813 
814     Case "mergecells"
815         If rar Then
816             For i = 1 To m
817                 For j = 1 To n
818                     rv(i, j) = CLng(rng.Cells(i, j).MergeCells)
819                 Next j
820             Next i
821         Else
822             rv = CLng(rng.MergeCells)
823         End If
824 
825     Case "name"
826         If rar Then
827             For i = 1 To m
828                 For j = 1 To n
829                     rv(i, j) = rng.Cells(i, j).Name
830                 Next j
831             Next i
832         Else
833             rv = rng.Name
834         End If
835 
836     Case "numberformat"
837         If rar Then
838             For i = 1 To m
839                 For j = 1 To n
840                     rv(i, j) = rng.Cells(i, j).NumberFormat
841                 Next j
842             Next i
843         Else
844             rv = rng.NumberFormat
845         End If
846 
847     Case "numberformatlocal"
848         If rar Then
849             For i = 1 To m
850                 For j = 1 To n
851                     rv(i, j) = rng.Cells(i, j).NumberFormatLocal
852                 Next j
853             Next i
854         Else
855             rv = rng.NumberFormatLocal
856         End If
857 
858     Case "orientation", "rotation"  /* from later 123 versions */
859         If rar Then
860             For i = 1 To m
861                 For j = 1 To n
862                     rv(i, j) = rng.Cells(i, j).Orientation
863                 Next j
864             Next i
865         Else
866             rv = rng.Orientation
867         End If
868 
869     Case "pattern"  /* from later 123 versions */
870         If rar Then
871             For i = 1 To m
872                 For j = 1 To n
873                     rv(i, j) = _
874                       rng.Cells(i, j).Interior.Pattern - _
875                       xlPatternNone
876                 Next j
877             Next i
878         Else
879             rv = rng.Interior.Pattern - xlPatternNone
880         End If
881 
882     Case "patterncolor"  /* from later 123 versions */
883         If rar Then
884             For i = 1 To m
885                 For j = 1 To n
886                     rv(i, j) = _
887                       rng.Cells(i, j).Interior.PatternColorIndex
888                 Next j
889             Next i
890         Else
891             rv = rng.Interior.PatternColorIndex
892         End If
893 
894     Case "rightborder"  /* from later 123 versions */
895     /* Note: many possible return values! wrap inside SIGN to test T/F */
896         If rar Then
897             For i = 1 To m
898                 For j = 1 To n
899                     rv(i, j) = _
900                       rng.Cells(i, j).Borders(xlEdgeRight).LineStyle - _
901                       xlLineStyleNone
902                 Next j
903             Next i
904         Else
905             rv = rng.Borders(xlEdgeRight).LineStyle - xlLineStyleNone
906         End If
907 
908     Case "rightbordercolor"  /* from later 123 versions */
909         If rar Then
910             For i = 1 To m
911                 For j = 1 To n
912                     rv(i, j) = _
913                       rng.Cells(i, j).Borders(xlEdgeRight).ColorIndex
914                 Next j
915             Next i
916         Else
917             rv = rng.Borders(xlEdgeRight).ColorIndex
918         End If
919 
920     Case "rowhidden"
921         If rar Then
922             For i = 1 To m
923                 For j = 1 To n
924                     rv(i, j) = CLng(rng.Cells(i, j).EntireRow.Hidden)
925                 Next j
926             Next i
927         Else
928             rv = CLng(rng.EntireRow.Hidden)
929         End If
930 
931     Case "scrollarea"
932     /* Who needs consistency?! Why doesn't this return a Range object? */
933         t = ws.ScrollArea  /* invariant! */
934 
935         If rar Then
936             For i = 1 To m
937                 For j = 1 To n
938                     rv(i, j) = t
939                 Next j
940             Next i
941         Else
942             rv = t
943         End If
944 
945     Case "sheet", "worksheet"  /* from later 123 versions - USEFUL! */
946         t = ws.Index  /* invariant! */
947 
948         If rar Then
949             For i = 1 To m
950                 For j = 1 To n
951                     rv(i, j) = t
952                 Next j
953             Next i
954         Else
955             rv = t
956         End If
957 
958     Case "sheetname", "worksheetname"  /* from later 123 versions - USEFUL! */
959         t = ws.Name  /* invariant */
960 
961         If rar Then
962             For i = 1 To m
963                 For j = 1 To n
964                     rv(i, j) = t
965                 Next j
966             Next i
967         Else
968             rv = t
969         End If
970 
971     Case "sheetcount", "sheetscount", "worksheetcount", "worksheetscount"
972         t = wb.Worksheets.Count  /* invariant */
973 
974         If rar Then
975             For i = 1 To m
976                 For j = 1 To n
977                     rv(i, j) = t
978                 Next j
979             Next i
980         Else
981             rv = t
982         End If
983 
984     Case "shrinktofit"
985         If rar Then
986             For i = 1 To m
987                 For j = 1 To n
988                     rv(i, j) = CLng(rng.Cells(i, j).ShrinkToFit)
989                 Next j
990             Next i
991         Else
992             rv = CLng(rng.ShrinkToFit)
993         End If
994 
995     Case "stylename"
996         If rar Then
997             For i = 1 To m
998                 For j = 1 To n
999                     rv(i, j) = rng.Cells(i, j).Style.Name
1000                 Next j
1001             Next i
1002         Else
1003             rv = rng.Style.Name
1004         End If
1005 
1006     Case "text"  /* USEFUL! */
1007         If rar Then
1008             For i = 1 To m
1009                 For j = 1 To n
1010                     rv(i, j) = rng.Cells(i, j).text
1011                 Next j
1012             Next i
1013         Else
1014             rv = rng.text
1015         End If
1016 
1017     Case "textcolor"  /* from later 123 versions - USEFUL! */
1018         If rar Then
1019             For i = 1 To m
1020                 For j = 1 To n
1021                     rv(i, j) = rng.Cells(i, j).Font.ColorIndex
1022                 Next j
1023             Next i
1024         Else
1025             rv = rng.Font.ColorIndex
1026         End If
1027 
1028     Case "top"
1029         If rar Then
1030             For i = 1 To m
1031                 For j = 1 To n
1032                     rv(i, j) = rng.Cells(i, j).Top
1033                 Next j
1034             Next i
1035         Else
1036             rv = rng.Top
1037         End If
1038 
1039     Case "topborder"  /* from later 123 versions */
1040     /* Note: many possible return values! wrap inside SIGN to test T/F */
1041         If rar Then
1042             For i = 1 To m
1043                 For j = 1 To n
1044                     rv(i, j) = _
1045                       rng.Cells(i, j).Borders(xlEdgeTop).LineStyle - _
1046                       xlLineStyleNone
1047                 Next j
1048             Next i
1049         Else
1050             rv = rng.Borders(xlEdgeTop).LineStyle - xlLineStyleNone
1051         End If
1052 
1053     Case "topbordercolor"  /* from later 123 versions */
1054         If rar Then
1055             For i = 1 To m
1056                 For j = 1 To n
1057                     rv(i, j) = _
1058                       rng.Cells(i, j).Borders(xlEdgeTop).ColorIndex
1059                 Next j
1060             Next i
1061         Else
1062             rv = rng.Borders(xlEdgeTop).ColorIndex
1063         End If
1064 
1065     Case "underline"  /* from later 123 versions - USEFUL! */
1066     /* Note: many possible return values! wrap inside SIGN to test T/F */
1067         If rar Then
1068             For i = 1 To m
1069                 For j = 1 To n
1070                     rv(i, j) = _
1071                       rng.Cells(i, j).Font.Underline - _
1072                       xlUnderlineStyleNone
1073                 Next j
1074             Next i
1075         Else
1076             rv = rng.Font.Underline - xlUnderlineStyleNone
1077         End If
1078 
1079     Case "usedrange"  /* NOTE: returns Range addresses! */
1080         t = ws.UsedRange.Address  /* invariant */
1081 
1082         If rar Then
1083             For i = 1 To m
1084                 For j = 1 To n
1085                     rv(i, j) = t
1086                 Next j
1087             Next i
1088         Else
1089             rv = t
1090         End If
1091 
1092     Case "usestandardheight"
1093         If rar Then
1094             For i = 1 To m
1095                 For j = 1 To n
1096                     rv(i, j) = CLng(rng.Cells(i, j).UseStandardHeight)
1097                 Next j
1098             Next i
1099         Else
1100             rv = CLng(rng.UseStandardHeight)
1101         End If
1102 
1103     Case "usestandardwidth"
1104         If rar Then
1105             For i = 1 To m
1106                 For j = 1 To n
1107                     rv(i, j) = CLng(rng.Cells(i, j).UseStandardWidth)
1108                 Next j
1109             Next i
1110         Else
1111             rv = CLng(rng.UseStandardWidth)
1112         End If
1113 
1114     Case "valign", "verticalalignment"  /* from later 123 versions */
1115     /* Note: different return values than 123. 0 = Bottom-aligned */
1116         If rar Then
1117             For i = 1 To m
1118                 For j = 1 To n
1119                     rv(i, j) = _
1120                       rng.Cells(i, j).VerticalAlignment - _
1121                       xlVAlignBottom
1122                 Next j
1123             Next i
1124         Else
1125             rv = rng.VerticalAlignment - xlVAlignBottom
1126         End If
1127 
1128     Case "visible", "sheetvisible", "worksheetvisible"
1129         t = CLng(ws.Visible)  /* invariant */
1130 
1131         If rar Then
1132             For i = 1 To m
1133                 For j = 1 To n
1134                     rv(i, j) = t
1135                 Next j
1136             Next i
1137         Else
1138             rv = t
1139         End If
1140 
1141     Case "workbookfullname"  /* same as FileName in later 123 versions */
1142         t = wb.FullName  /* invariant */
1143 
1144         If rar Then
1145             For i = 1 To m
1146                 For j = 1 To n
1147                     rv(i, j) = t
1148                 Next j
1149             Next i
1150         Else
1151             rv = t
1152         End If
1153 
1154     Case "workbookname"
1155         t = wb.Name  /* invariant */
1156 
1157         If rar Then
1158             For i = 1 To m
1159                 For j = 1 To n
1160                     rv(i, j) = t
1161                 Next j
1162             Next i
1163         Else
1164             rv = t
1165         End If
1166 
1167     Case "workbookpath"
1168         t = wb.path  /* invariant */
1169 
1170         If rar Then
1171             For i = 1 To m
1172                 For j = 1 To n
1173                     rv(i, j) = t
1174                 Next j
1175             Next i
1176         Else
1177             rv = t
1178         End If
1179 
1180     Case "wrap", "wraptext"  /* from later 123 versions */
1181         If rar Then
1182             For i = 1 To m
1183                 For j = 1 To n
1184                     rv(i, j) = CLng(rng.Cells(i, j).WrapText)
1185                 Next j
1186             Next i
1187         Else
1188             rv = CLng(rng.WrapText)
1189         End If
1190 
1191     Case Else  /* invalid property/characteristic */
1192         t = CVErr(xlErrValue)  /* invariant */
1193 
1194         If rar Then
1195             For i = 1 To m
1196                 For j = 1 To n
1197                     rv(i, j) = t
1198                 Next j
1199             Next i
1200         Else
1201             rv = t
1202         End If
1203 
1204     End Select
1205 
1206     ExtCell = rv
1207 End Function
1208 #endif
1209 
1210 /***************************************************************************/
1211 
1212 static GnmFuncHelp const help_expression[] = {
1213         { GNM_FUNC_HELP_NAME, F_("EXPRESSION:expression in @{cell} as a string")},
1214         { GNM_FUNC_HELP_ARG, F_("cell:a cell reference")},
1215         { GNM_FUNC_HELP_NOTE, F_("If @{cell} contains no expression, EXPRESSION returns empty.")},
1216         { GNM_FUNC_HELP_SEEALSO, "TEXT"},
1217         { GNM_FUNC_HELP_END}
1218 };
1219 
1220 static GnmValue *
gnumeric_expression(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1221 gnumeric_expression (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1222 {
1223 	GnmValue const * const v = argv[0];
1224 	if (VALUE_IS_CELLRANGE (v)) {
1225 		GnmCell *cell;
1226 		GnmCellRef const * a = &v->v_range.cell.a;
1227 		GnmCellRef const * b = &v->v_range.cell.b;
1228 
1229 		if (a->col != b->col || a->row != b->row || a->sheet !=b->sheet)
1230 			return value_new_error_REF (ei->pos);
1231 
1232 		cell = sheet_cell_get (eval_sheet (a->sheet, ei->pos->sheet),
1233 				       a->col, a->row);
1234 
1235 		if (cell && gnm_cell_has_expr (cell)) {
1236 			GnmParsePos pos;
1237 			char *expr_string = gnm_expr_top_as_string
1238 				(cell->base.texpr,
1239 				 parse_pos_init_cell (&pos, cell),
1240 				 gnm_conventions_default);
1241 			return value_new_string_nocopy (expr_string);
1242 		}
1243 	}
1244 
1245 	return value_new_empty ();
1246 }
1247 /***************************************************************************/
1248 
1249 static GnmFuncHelp const help_get_formula[] = {
1250 	{ GNM_FUNC_HELP_NAME, F_("GET.FORMULA:the formula in @{cell} as a string")},
1251 	{ GNM_FUNC_HELP_ARG, F_("cell:the referenced cell")},
1252 	{ GNM_FUNC_HELP_ODF, F_("GET.FORMULA is the OpenFormula function FORMULA.") },
1253 	{ GNM_FUNC_HELP_EXAMPLES, F_("If A1 is empty and A2 contains =B1+B2, then\n"
1254 				     "GET.FORMULA(A2) yields '=B1+B2' and\n"
1255 				     "GET.FORMULA(A1) yields ''.") },
1256 	{ GNM_FUNC_HELP_SEEALSO, "EXPRESSION,ISFORMULA"},
1257 	{ GNM_FUNC_HELP_END }
1258 };
1259 
1260 static GnmValue *
gnumeric_get_formula(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1261 gnumeric_get_formula (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1262 {
1263 	GnmValue const * const v = argv[0];
1264 	if (VALUE_IS_CELLRANGE (v)) {
1265 		GnmCell *cell;
1266 		GnmCellRef const * a = &v->v_range.cell.a;
1267 		GnmCellRef const * b = &v->v_range.cell.b;
1268 
1269 		if (a->col != b->col || a->row != b->row || a->sheet !=b->sheet)
1270 			return value_new_error_REF (ei->pos);
1271 
1272 		cell = sheet_cell_get (eval_sheet (a->sheet, ei->pos->sheet),
1273 				       a->col, a->row);
1274 
1275 		if (cell && gnm_cell_has_expr (cell)) {
1276 			GnmConventionsOut out;
1277 			GnmParsePos	  pp;
1278 			out.accum = g_string_new ("=");
1279 			out.pp    = parse_pos_init_cell (&pp, cell);
1280 			out.convs = gnm_conventions_default;
1281 			gnm_expr_top_as_gstring (cell->base.texpr, &out);
1282 			return value_new_string_nocopy (g_string_free (out.accum, FALSE));
1283 		}
1284 	}
1285 
1286 	return value_new_empty ();
1287 }
1288 
1289 /***************************************************************************/
1290 
1291 static GnmFuncHelp const help_isformula[] = {
1292 	{ GNM_FUNC_HELP_NAME, F_("ISFORMULA:TRUE if @{cell} contains a formula")},
1293 	{ GNM_FUNC_HELP_ARG, F_("cell:the referenced cell")},
1294 	{ GNM_FUNC_HELP_ODF, F_("ISFORMULA is OpenFormula compatible.") },
1295 	{ GNM_FUNC_HELP_SEEALSO, "GET.FORMULA"},
1296 	{ GNM_FUNC_HELP_END }
1297 };
1298 
1299 static GnmValue *
gnumeric_isformula(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1300 gnumeric_isformula (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1301 {
1302 	GnmValue const * const v = argv[0];
1303 	if (VALUE_IS_CELLRANGE (v)) {
1304 		GnmCell *cell;
1305 		GnmCellRef const * a = &v->v_range.cell.a;
1306 		GnmCellRef const * b = &v->v_range.cell.b;
1307 
1308 		if (a->col != b->col || a->row != b->row || a->sheet !=b->sheet)
1309 			return value_new_error_REF (ei->pos);
1310 
1311 		cell = sheet_cell_get (eval_sheet (a->sheet, ei->pos->sheet),
1312 				       a->col, a->row);
1313 		return value_new_bool (cell && gnm_cell_has_expr (cell));
1314 	}
1315 
1316 	return value_new_error_REF (ei->pos);
1317 }
1318 
1319 
1320 /***************************************************************************/
1321 
1322 static GnmFuncHelp const help_countblank[] = {
1323         { GNM_FUNC_HELP_NAME, F_("COUNTBLANK:the number of blank cells in @{range}")},
1324         { GNM_FUNC_HELP_ARG, F_("range:a cell range")},
1325 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1326         { GNM_FUNC_HELP_EXAMPLES, F_("COUNTBLANK(A1:A20) returns the number of blank cell in A1:A20.") },
1327         { GNM_FUNC_HELP_SEEALSO, "COUNT"},
1328         { GNM_FUNC_HELP_END}
1329 };
1330 
1331 static GnmValue *
cb_countblank(GnmValueIter const * iter,gpointer user)1332 cb_countblank (GnmValueIter const *iter, gpointer user)
1333 {
1334 	GnmValue const *v = iter->v;
1335 
1336 	if (VALUE_IS_STRING (v) && value_peek_string (v)[0] == 0)
1337 		; /* Nothing -- the empty string is blank.  */
1338 	else if (VALUE_IS_EMPTY (v))
1339 		; /* Nothing  */
1340 	else
1341 		*((int *)user) -= 1;
1342 
1343 	return NULL;
1344 }
1345 
1346 static GnmValue *
gnumeric_countblank(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1347 gnumeric_countblank (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1348 {
1349 	GnmValue const *v = argv[0];
1350 	int count =
1351 		value_area_get_width (v, ei->pos) *
1352 		value_area_get_height (v, ei->pos);
1353 	int nsheets = 1;
1354 
1355 	if (VALUE_IS_CELLRANGE (v)) {
1356 		GnmRange r;
1357 		Sheet *start_sheet, *end_sheet;
1358 
1359 		gnm_rangeref_normalize (&v->v_range.cell, ei->pos,
1360 					&start_sheet, &end_sheet, &r);
1361 
1362 		if (start_sheet != end_sheet && end_sheet != NULL)
1363 			nsheets = 1 + abs (end_sheet->index_in_wb -
1364 					   start_sheet->index_in_wb);
1365 	}
1366 
1367 	count *= nsheets;
1368 
1369 	value_area_foreach (v, ei->pos, CELL_ITER_IGNORE_BLANK,
1370 			    &cb_countblank, &count);
1371 
1372 	return value_new_int (count);
1373 }
1374 
1375 /***************************************************************************/
1376 
1377 static GnmFuncHelp const help_info[] = {
1378         { GNM_FUNC_HELP_NAME, F_("INFO:information about the current operating environment "
1379 				 "according to @{type}")},
1380         { GNM_FUNC_HELP_ARG, F_("type:string giving the type of information requested")},
1381 	{ GNM_FUNC_HELP_DESCRIPTION, F_("INFO returns information about the current operating "
1382 					"environment according to @{type}:\n"
1383 					"  memavail     \t\tReturns the amount of memory available, bytes.\n"
1384 					"  memused      \tReturns the amount of memory used (bytes).\n"
1385 					"  numfile      \t\tReturns the number of active worksheets.\n"
1386 					"  osversion    \t\tReturns the operating system version.\n"
1387 					"  recalc       \t\tReturns the recalculation mode (automatic).\n"
1388 					"  release      \t\tReturns the version of Gnumeric as text.\n"
1389 					"  system       \t\tReturns the name of the environment.\n"
1390 					"  totmem       \t\tReturns the amount of total memory available.")},
1391 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1392         { GNM_FUNC_HELP_EXAMPLES, "=INFO(\"system\")" },
1393         { GNM_FUNC_HELP_EXAMPLES, "=INFO(\"release\")" },
1394         { GNM_FUNC_HELP_EXAMPLES, "=INFO(\"numfile\")" },
1395         { GNM_FUNC_HELP_SEEALSO, "CELL"},
1396         { GNM_FUNC_HELP_END}
1397 };
1398 
1399 static GnmValue *
gnumeric_info(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1400 gnumeric_info (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1401 {
1402 	char const * const info_type = value_peek_string (argv[0]);
1403 	if (!g_ascii_strcasecmp (info_type, "directory")) {
1404 		/* Path of the current directory or folder.  */
1405 		return value_new_error (ei->pos, _("Unimplemented"));
1406 	} else if (!g_ascii_strcasecmp (info_type, "memavail")) {
1407 		/* Amount of memory available, in bytes.  */
1408 		return value_new_int (15 << 20);  /* Good enough... */
1409 	} else if (!g_ascii_strcasecmp (info_type, "memused")) {
1410 		/* Amount of memory being used for data.  */
1411 		return value_new_int (1 << 20);  /* Good enough... */
1412 	} else if (!g_ascii_strcasecmp (info_type, "numfile")) {
1413 		/* Number of active worksheets.  */
1414 		return value_new_int (1);  /* Good enough... */
1415 	} else if (!g_ascii_strcasecmp (info_type, "origin")) {
1416 		/* Absolute A1-style reference, as text, prepended with "$A:"
1417 		 * for Lotus 1-2-3 release 3.x compatibility. Returns the cell
1418 		 * reference of the top and leftmost cell visible in the
1419 		 * window, based on the current scrolling position.
1420 		 */
1421 		return value_new_error (ei->pos, _("Unimplemented"));
1422 	} else if (!g_ascii_strcasecmp (info_type, "osversion")) {
1423 #ifdef HAVE_UNAME
1424 		/* Current operating system version, as text.  */
1425 		struct utsname unamedata;
1426 
1427 		if (uname (&unamedata) == -1)
1428 			return value_new_error (ei->pos,
1429 						_("Unknown version"));
1430 		else {
1431 			char *tmp = g_strdup_printf (_("%s version %s"),
1432 						     unamedata.sysname,
1433 						     unamedata.release);
1434 			return value_new_string_nocopy (tmp);
1435 		}
1436 #elif defined(G_OS_WIN32)
1437 		/* fake XP */
1438 		return value_new_string ("Windows (32-bit) NT 5.01");
1439 #else
1440 		// Nothing -- go to catch-all
1441 #endif
1442 	} else if (!g_ascii_strcasecmp (info_type, "recalc")) {
1443 		/* Current recalculation mode; returns "Automatic" or "Manual".  */
1444 		Workbook const *wb = ei->pos->sheet->workbook;
1445 		return value_new_string (
1446 			workbook_get_recalcmode (wb) ? _("Automatic") : _("Manual"));
1447 	} else if (!g_ascii_strcasecmp (info_type, "release")) {
1448 		/* Version of Gnumeric (Well, Microsoft Excel), as text.  */
1449 		return value_new_string (GNM_VERSION_FULL);
1450 	} else if (!g_ascii_strcasecmp (info_type, "system")) {
1451 #ifdef HAVE_UNAME
1452 		/* Name of the operating environment.  */
1453 		struct utsname unamedata;
1454 
1455 		if (uname (&unamedata) == -1)
1456 			return value_new_error (ei->pos, _("Unknown system"));
1457 		else
1458 			return value_new_string (unamedata.sysname);
1459 #elif defined(G_OS_WIN32)
1460 		return value_new_string ("pcdos");	/* seems constant */
1461 #else
1462 		// Nothing -- go to catch-all
1463 #endif
1464 	} else if (!g_ascii_strcasecmp (info_type, "totmem")) {
1465 		/* Total memory available, including memory already in use, in
1466 		 * bytes.
1467 		 */
1468 		return value_new_int (16 << 20);  /* Good enough... */
1469 	}
1470 
1471 	return value_new_error (ei->pos, _("Unknown info_type"));
1472 }
1473 
1474 /***************************************************************************/
1475 
1476 static GnmFuncHelp const help_iserror[] = {
1477         { GNM_FUNC_HELP_NAME, F_("ISERROR:TRUE if @{value} is any error value")},
1478         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1479 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1480         { GNM_FUNC_HELP_EXAMPLES, "=ISERROR(NA())" },
1481         { GNM_FUNC_HELP_EXAMPLES, "=ISERROR(5/0)" },
1482         { GNM_FUNC_HELP_SEEALSO, "ISERR,ISNA"},
1483         { GNM_FUNC_HELP_END}
1484 };
1485 
1486 static GnmValue *
gnumeric_iserror(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1487 gnumeric_iserror (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1488 {
1489 	return value_new_bool (VALUE_IS_ERROR (argv[0]));
1490 }
1491 
1492 /***************************************************************************/
1493 
1494 static GnmFuncHelp const help_isna[] = {
1495         { GNM_FUNC_HELP_NAME, F_("ISNA:TRUE if @{value} is the #N/A error value")},
1496         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1497 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1498         { GNM_FUNC_HELP_EXAMPLES, "=ISNA(NA())" },
1499         { GNM_FUNC_HELP_EXAMPLES, "=ISNA(5/0)" },
1500         { GNM_FUNC_HELP_SEEALSO, "NA"},
1501         { GNM_FUNC_HELP_END}
1502 };
1503 
1504 /*
1505  * We need to operator directly in the input expression in order to bypass
1506  * the error handling mechanism
1507  */
1508 static GnmValue *
gnumeric_isna(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1509 gnumeric_isna (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1510 {
1511 	return value_new_bool (value_error_classify (argv[0]) == GNM_ERROR_NA);
1512 }
1513 
1514 /***************************************************************************/
1515 
1516 static GnmFuncHelp const help_iserr[] = {
1517         { GNM_FUNC_HELP_NAME, F_("ISERR:TRUE if @{value} is any error value except #N/A")},
1518         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1519 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1520         { GNM_FUNC_HELP_EXAMPLES, "=ISERR(NA())" },
1521         { GNM_FUNC_HELP_EXAMPLES, "=ISERR(5/0)" },
1522         { GNM_FUNC_HELP_SEEALSO, "ISERROR"},
1523         { GNM_FUNC_HELP_END}
1524 };
1525 
1526 static GnmValue *
gnumeric_iserr(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1527 gnumeric_iserr (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1528 {
1529 	return value_new_bool (VALUE_IS_ERROR (argv[0]) &&
1530 			       value_error_classify (argv[0]) != GNM_ERROR_NA);
1531 }
1532 
1533 /***************************************************************************/
1534 
1535 static GnmFuncHelp const help_error_type[] = {
1536         { GNM_FUNC_HELP_NAME, F_("ERROR.TYPE:the type of @{error}")},
1537         { GNM_FUNC_HELP_ARG, F_("error:an error")},
1538         { GNM_FUNC_HELP_DESCRIPTION, F_("ERROR.TYPE returns an error number corresponding to the given "
1539 					"error value.  The error numbers for error values are:\n\n"
1540 					"\t#DIV/0!  \t\t2\n"
1541 					"\t#VALUE!  \t3\n"
1542 					"\t#REF!    \t\t4\n"
1543 					"\t#NAME?   \t5\n"
1544 					"\t#NUM!    \t6\n"
1545 					"\t#N/A     \t\t7")},
1546 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1547         { GNM_FUNC_HELP_EXAMPLES, "=ERROR.TYPE(NA())" },
1548         { GNM_FUNC_HELP_EXAMPLES, "=ERROR.TYPE(ERROR(\"#X\"))" },
1549         { GNM_FUNC_HELP_SEEALSO, "ISERROR"},
1550         { GNM_FUNC_HELP_END}
1551 };
1552 
1553 static GnmValue *
gnumeric_error_type(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1554 gnumeric_error_type (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1555 {
1556 	switch (value_error_classify (argv[0])) {
1557 	case GNM_ERROR_NULL: return value_new_int (1);
1558 	case GNM_ERROR_DIV0: return value_new_int (2);
1559 	case GNM_ERROR_VALUE: return value_new_int (3);
1560 	case GNM_ERROR_REF: return value_new_int (4);
1561 	case GNM_ERROR_NAME: return value_new_int (5);
1562 	case GNM_ERROR_NUM: return value_new_int (6);
1563 	case GNM_ERROR_NA: return value_new_int (7);
1564 	default:
1565 		return value_new_error_NA (ei->pos);
1566 	}
1567 }
1568 
1569 /***************************************************************************/
1570 
1571 static GnmFuncHelp const help_na[] = {
1572         { GNM_FUNC_HELP_NAME, F_("NA:the error value #N/A")},
1573 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1574         { GNM_FUNC_HELP_EXAMPLES, "=NA()" },
1575         { GNM_FUNC_HELP_EXAMPLES, "=ISNA(NA())" },
1576         { GNM_FUNC_HELP_SEEALSO, "ISNA"},
1577         { GNM_FUNC_HELP_END}
1578 };
1579 
1580 
1581 static GnmValue *
gnumeric_na(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1582 gnumeric_na (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1583 {
1584 	return value_new_error_NA (ei->pos);
1585 }
1586 
1587 /***************************************************************************/
1588 
1589 static GnmFuncHelp const help_error[] = {
1590         { GNM_FUNC_HELP_NAME, F_("ERROR:the error with the given @{name}")},
1591         { GNM_FUNC_HELP_ARG, F_("name:string")},
1592         { GNM_FUNC_HELP_EXAMPLES, "=ERROR(\"#N/A\")" },
1593         { GNM_FUNC_HELP_EXAMPLES, "=ISNA(ERROR(\"#N/A\"))" },
1594         { GNM_FUNC_HELP_SEEALSO, "ISERROR"},
1595         { GNM_FUNC_HELP_END}
1596 };
1597 
1598 static GnmValue *
gnumeric_error(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1599 gnumeric_error (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1600 {
1601 	return value_new_error (ei->pos, value_peek_string (argv[0]));
1602 }
1603 
1604 /***************************************************************************/
1605 
1606 static GnmFuncHelp const help_isblank[] = {
1607         { GNM_FUNC_HELP_NAME, F_("ISBLANK:TRUE if @{value} is blank")},
1608         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1609         { GNM_FUNC_HELP_DESCRIPTION, F_("This function checks if a value is blank.  Empty cells are blank, but empty strings are not.")},
1610 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1611         { GNM_FUNC_HELP_EXAMPLES, "=ISBLANK(\"\")" },
1612         { GNM_FUNC_HELP_END}
1613 };
1614 
1615 static GnmValue *
gnumeric_isblank(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1616 gnumeric_isblank (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1617 {
1618 	return value_new_bool (VALUE_IS_EMPTY (argv[0]));
1619 }
1620 
1621 /***************************************************************************/
1622 
1623 static GnmFuncHelp const help_iseven[] = {
1624         { GNM_FUNC_HELP_NAME, F_("ISEVEN:TRUE if @{n} is even")},
1625         { GNM_FUNC_HELP_ARG, F_("n:number")},
1626 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1627         { GNM_FUNC_HELP_EXAMPLES, "=ISEVEN(4)" },
1628         { GNM_FUNC_HELP_SEEALSO, "ISODD"},
1629         { GNM_FUNC_HELP_END}
1630 };
1631 
1632 static GnmValue *
gnumeric_iseven(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1633 gnumeric_iseven (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1634 {
1635 	gnm_float x = value_get_as_float (argv[0]);
1636 	gnm_float r = gnm_fmod (gnm_abs (x), 2);
1637 
1638 	/* If x is too big, this will always be true.  */
1639 	return value_new_bool (r < 1);
1640 }
1641 
1642 /***************************************************************************/
1643 
1644 static GnmFuncHelp const help_islogical[] = {
1645         { GNM_FUNC_HELP_NAME, F_("ISLOGICAL:TRUE if @{value} is a logical value")},
1646         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1647         { GNM_FUNC_HELP_DESCRIPTION, F_("This function checks if a value is either TRUE or FALSE.") },
1648 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1649         { GNM_FUNC_HELP_EXAMPLES, "=ISLOGICAL(1)" },
1650         { GNM_FUNC_HELP_EXAMPLES, "=ISLOGICAL(\"Gnumeric\")" },
1651         { GNM_FUNC_HELP_END}
1652 };
1653 
1654 static GnmValue *
gnumeric_islogical(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1655 gnumeric_islogical (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1656 {
1657 	return value_new_bool (VALUE_IS_BOOLEAN (argv[0]));
1658 }
1659 
1660 /***************************************************************************/
1661 
1662 static GnmFuncHelp const help_isnontext[] = {
1663         { GNM_FUNC_HELP_NAME, F_("ISNONTEXT:TRUE if @{value} is not text")},
1664         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1665 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1666         { GNM_FUNC_HELP_EXAMPLES, "=ISNONTEXT(\"Gnumeric\")" },
1667         { GNM_FUNC_HELP_SEEALSO, "ISTEXT"},
1668         { GNM_FUNC_HELP_END}
1669 };
1670 
1671 static GnmValue *
gnumeric_isnontext(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1672 gnumeric_isnontext (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1673 {
1674 	return value_new_bool (!VALUE_IS_STRING (argv[0]));
1675 }
1676 
1677 /***************************************************************************/
1678 
1679 static GnmFuncHelp const help_isnumber[] = {
1680         { GNM_FUNC_HELP_NAME, F_("ISNUMBER:TRUE if @{value} is a number")},
1681         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1682         { GNM_FUNC_HELP_DESCRIPTION, F_("This function checks if a value is a number.  Neither TRUE nor FALSE are numbers for this purpose.") },
1683 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1684         { GNM_FUNC_HELP_EXAMPLES, "=ISNUMBER(\"Gnumeric\")" },
1685         { GNM_FUNC_HELP_EXAMPLES, "=ISNUMBER(PI())" },
1686         { GNM_FUNC_HELP_END}
1687 };
1688 
1689 static GnmValue *
gnumeric_isnumber(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1690 gnumeric_isnumber (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1691 {
1692 	return value_new_bool (argv[0] && VALUE_IS_FLOAT (argv[0]));
1693 }
1694 
1695 /***************************************************************************/
1696 
1697 static GnmFuncHelp const help_isodd[] = {
1698         { GNM_FUNC_HELP_NAME, F_("ISODD:TRUE if @{n} is odd")},
1699         { GNM_FUNC_HELP_ARG, F_("n:number")},
1700 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1701         { GNM_FUNC_HELP_EXAMPLES, "=ISODD(3)" },
1702         { GNM_FUNC_HELP_SEEALSO, "ISEVEN"},
1703         { GNM_FUNC_HELP_END}
1704 };
1705 
1706 static GnmValue *
gnumeric_isodd(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1707 gnumeric_isodd (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1708 {
1709 	gnm_float x = value_get_as_float (argv[0]);
1710 	gnm_float r = gnm_fmod (gnm_abs (x), 2);
1711 
1712 	/* If x is too big, this will always be false.  */
1713 	return value_new_bool (r >= 1);
1714 }
1715 
1716 /***************************************************************************/
1717 
1718 static GnmFuncHelp const help_isref[] = {
1719         { GNM_FUNC_HELP_NAME, F_("ISREF:TRUE if @{value} is a reference")},
1720         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1721         { GNM_FUNC_HELP_DESCRIPTION, F_("This function checks if a value is a cell reference.") },
1722 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1723         { GNM_FUNC_HELP_EXAMPLES, "=ISREF(A1)" },
1724         { GNM_FUNC_HELP_END}
1725 };
1726 
1727 static GnmValue *
gnumeric_isref(GnmFuncEvalInfo * ei,int argc,GnmExprConstPtr const * argv)1728 gnumeric_isref (GnmFuncEvalInfo *ei, int argc, GnmExprConstPtr const *argv)
1729 {
1730 	GnmValue *v;
1731 	gboolean res;
1732 
1733 	if (argc != 1)
1734 		return value_new_error (ei->pos,
1735 					_("Invalid number of arguments"));
1736 
1737 	v = gnm_expr_eval (argv[0], ei->pos,
1738 			   GNM_EXPR_EVAL_PERMIT_NON_SCALAR |
1739 			   GNM_EXPR_EVAL_WANT_REF);
1740 	res = VALUE_IS_CELLRANGE (v);
1741 	value_release (v);
1742 
1743 	return value_new_bool (res);
1744 }
1745 
1746 /***************************************************************************/
1747 
1748 static GnmFuncHelp const help_istext[] = {
1749         { GNM_FUNC_HELP_NAME, F_("ISTEXT:TRUE if @{value} is text")},
1750         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1751 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1752         { GNM_FUNC_HELP_EXAMPLES, "=ISTEXT(\"Gnumeric\")" },
1753         { GNM_FUNC_HELP_EXAMPLES, "=ISTEXT(34)" },
1754         { GNM_FUNC_HELP_SEEALSO, "ISNONTEXT"},
1755         { GNM_FUNC_HELP_END}
1756 };
1757 
1758 static GnmValue *
gnumeric_istext(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1759 gnumeric_istext (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1760 {
1761 	return value_new_bool (VALUE_IS_STRING (argv[0]));
1762 }
1763 
1764 /***************************************************************************/
1765 
1766 static GnmFuncHelp const help_n[] = {
1767         { GNM_FUNC_HELP_NAME, F_("N:@{text} converted to a number")},
1768         { GNM_FUNC_HELP_ARG, F_("text:string")},
1769 	{ GNM_FUNC_HELP_NOTE, F_("If @{text} contains non-numerical text, 0 is returned.") },
1770 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1771         { GNM_FUNC_HELP_EXAMPLES, "=N(\"42\")" },
1772         { GNM_FUNC_HELP_EXAMPLES, F_("=N(\"eleven\")") },
1773         { GNM_FUNC_HELP_END}
1774 };
1775 
1776 static GnmValue *
gnumeric_n(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1777 gnumeric_n (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1778 {
1779 	GnmValue *v;
1780 
1781 	if (VALUE_IS_NUMBER (argv[0]))
1782 		return value_new_float (value_get_as_float (argv[0]));
1783 
1784 	if (!VALUE_IS_STRING (argv[0]))
1785 		return value_new_error_NUM (ei->pos);
1786 
1787 	v = format_match_number (value_peek_string (argv[0]),
1788 				 NULL,
1789 				 sheet_date_conv (ei->pos->sheet));
1790 	if (v != NULL)
1791 		return v;
1792 
1793 	return value_new_float (0);
1794 }
1795 
1796 /***************************************************************************/
1797 
1798 static GnmFuncHelp const help_type[] = {
1799         { GNM_FUNC_HELP_NAME, F_("TYPE:a number indicating the data type of @{value}")},
1800         { GNM_FUNC_HELP_ARG, F_("value:a value")},
1801 	{ GNM_FUNC_HELP_DESCRIPTION, F_("TYPE returns a number indicating the data type of @{value}:\n"
1802 					"1  \t= number\n"
1803 					"2  \t= text\n"
1804 					"4  \t= boolean\n"
1805 					"16 \t= error\n"
1806 					"64 \t= array")},
1807 	{ GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
1808         { GNM_FUNC_HELP_EXAMPLES, "=TYPE(3)" },
1809         { GNM_FUNC_HELP_EXAMPLES, "=TYPE(\"Gnumeric\")" },
1810         { GNM_FUNC_HELP_END}
1811 };
1812 
1813 static GnmValue *
gnumeric_type(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1814 gnumeric_type (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1815 {
1816 	GnmValue const *v = argv[0];
1817 	switch (v ? v->v_any.type : VALUE_EMPTY) {
1818 	case VALUE_BOOLEAN:
1819 		return value_new_int (4);
1820 	case VALUE_EMPTY:
1821 	case VALUE_FLOAT:
1822 		return value_new_int (1);
1823 	case VALUE_CELLRANGE:
1824 	case VALUE_ERROR:
1825 		return value_new_int (16);
1826 	case VALUE_STRING:
1827 		return value_new_int (2);
1828 	case VALUE_ARRAY:
1829 		return value_new_int (64);
1830 	default:
1831 		break;
1832 	}
1833 	/* not reached */
1834 	return value_new_error_VALUE (ei->pos);
1835 }
1836 
1837 /***************************************************************************/
1838 
1839 static GnmFuncHelp const help_getenv[] = {
1840         { GNM_FUNC_HELP_NAME, F_("GETENV:the value of execution environment variable @{name}")},
1841         { GNM_FUNC_HELP_ARG, F_("name:the name of the environment variable")},
1842 	{ GNM_FUNC_HELP_NOTE, F_("If a variable called @{name} does not exist, #N/A will be returned.") },
1843 	{ GNM_FUNC_HELP_NOTE, F_("Variable names are case sensitive.") },
1844 	{ GNM_FUNC_HELP_EXAMPLES, "=GETENV(\"HOME\")" },
1845         { GNM_FUNC_HELP_END}
1846 };
1847 
1848 static GnmValue *
gnumeric_getenv(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1849 gnumeric_getenv (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1850 {
1851 	char const *var = value_peek_string (argv[0]);
1852 	char const *val = g_getenv (var);
1853 
1854 	if (val && g_utf8_validate (val, -1, NULL))
1855 		return value_new_string (val);
1856 	else
1857 		return value_new_error_NA (ei->pos);
1858 }
1859 
1860 /***************************************************************************/
1861 
1862 static GnmFuncHelp const help_get_link[] = {
1863 	{ GNM_FUNC_HELP_NAME, F_("GET.LINK:the target of the hyperlink attached to @{cell} as a string")},
1864 	{ GNM_FUNC_HELP_ARG,  F_("cell:the referenced cell")},
1865 	{ GNM_FUNC_HELP_NOTE, F_("The value return is not updated automatically when "
1866 				 "the link attached to @{cell} changes but requires a"
1867 				 " recalculation.")},
1868 	{ GNM_FUNC_HELP_SEEALSO, "HYPERLINK"},
1869 	{ GNM_FUNC_HELP_END }
1870 };
1871 
1872 static GnmValue *
gnumeric_get_link(GnmFuncEvalInfo * ei,GnmValue const * const * argv)1873 gnumeric_get_link (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
1874 {
1875 	GnmValue const * const v = argv[0];
1876 
1877 	if (VALUE_IS_CELLRANGE (v)) {
1878 		GnmCellRef const * a = &v->v_range.cell.a;
1879 		GnmCellRef const * b = &v->v_range.cell.b;
1880 		Sheet *sheet;
1881 		GnmHLink *lnk;
1882 		GnmCellPos pos;
1883 
1884 		if (a->col != b->col || a->row != b->row || a->sheet !=b->sheet)
1885 			return value_new_error_REF (ei->pos);
1886 
1887 		sheet = (a->sheet == NULL) ? ei->pos->sheet : a->sheet;
1888 		gnm_cellpos_init_cellref (&pos, a, &(ei->pos->eval), sheet);
1889 		lnk = gnm_sheet_hlink_find (sheet, &pos);
1890 
1891 		if (lnk)
1892 			return value_new_string (gnm_hlink_get_target (lnk));
1893 	}
1894 
1895 	return value_new_empty ();
1896 }
1897 
1898 /***************************************************************************/
1899 
1900 GnmFuncDescriptor const info_functions[] = {
1901 	{ "cell",	"sr",  help_cell,
1902 	  gnumeric_cell, NULL,
1903 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_SUBSET_WITH_EXTENSIONS, GNM_FUNC_TEST_STATUS_BASIC },
1904 	{ "error.type",	"E",  help_error_type,
1905 	  gnumeric_error_type, NULL,
1906 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1907 	{ "info",	"s",  help_info,
1908 	  gnumeric_info, NULL,
1909 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1910 	{ "isblank",	"E",  help_isblank,
1911 	  gnumeric_isblank, NULL,
1912 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1913 	{ "iserr",	"E",    help_iserr,
1914 	  gnumeric_iserr, NULL,
1915 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1916 	{ "iserror",	"E",    help_iserror,
1917 	  gnumeric_iserror, NULL,
1918 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1919 	{ "iseven",	"f",  help_iseven,
1920 	  gnumeric_iseven, NULL,
1921 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1922 	{ "islogical",	"E",  help_islogical,
1923 	  gnumeric_islogical, NULL,
1924 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1925 	{ "isna",	"E",    help_isna,
1926 	  gnumeric_isna, NULL,
1927 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1928 	{ "isnontext",	"E",  help_isnontext,
1929 	  gnumeric_isnontext, NULL,
1930 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1931 	{ "isnumber",	"E",  help_isnumber,
1932 	  gnumeric_isnumber, NULL,
1933 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1934 	{ "isodd",	"S",  help_isodd,
1935 	  gnumeric_isodd, NULL,
1936 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1937 	{ "isref",	NULL,  help_isref,
1938 	  NULL, gnumeric_isref,
1939 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1940 	{ "istext",	"E",  help_istext,
1941 	  gnumeric_istext, NULL,
1942 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1943 	{ "n",		"S",  help_n,
1944 	  gnumeric_n, NULL,
1945 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1946 	{ "na",		"", help_na,
1947 	  gnumeric_na, NULL,
1948 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1949 	{ "type",	"?",  help_type,
1950 	  gnumeric_type, NULL,
1951 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1952 
1953 /* XL stores this in statistical ? */
1954         { "countblank",	"r",   help_countblank,
1955 	  gnumeric_countblank, NULL,
1956 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
1957 
1958 	{ "error",	"s",   help_error,
1959 	  gnumeric_error, NULL,
1960 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
1961 
1962 	{ "expression",	"r",    help_expression,
1963 	  gnumeric_expression, NULL,
1964 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
1965 /* XLM : looks common in charts */
1966 	{ "get.formula", "r",    help_get_formula,
1967 	  gnumeric_get_formula, NULL,
1968 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
1969 	{ "get.link", "r",    help_get_link,
1970 	  gnumeric_get_link, NULL,
1971 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
1972 	{ "isformula", "r",    help_isformula,
1973 	  gnumeric_isformula, NULL,
1974 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE},
1975 	{ "getenv",	"s",  help_getenv,
1976 	  gnumeric_getenv, NULL,
1977 	  GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE },
1978 
1979         {NULL}
1980 };
1981