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