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