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