2 * fn-string.c: Built in string functions.
5 * Miguel de Icaza (miguel@gnu.org)
6 * Sean Atkinson (sca20@cam.ac.uk)
7 * Jukka-Pekka Iivonen (iivonen@iki.fi)
8 * Almer S. Tigelaar (almer@gnome.org)
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
23 #include <gnumeric-config.h>
26 #include <parse-util.h>
28 #include <gnm-format.h>
34 #include <number-match.h>
36 #include <rangefunc-strings.h>
38 #include <goffice/goffice.h>
39 #include <gsf/gsf-utils.h>
40 #include <gsf/gsf-msole-utils.h>
42 #include <gnm-plugin.h>
47 GNM_PLUGIN_MODULE_HEADER
;
49 /***************************************************************************/
51 static GIConv CHAR_iconv
;
53 static GnmFuncHelp
const help_char
[] = {
54 { GNM_FUNC_HELP_NAME
, F_("CHAR:the CP1252 (Windows-1252) character for the code point @{x}")},
55 { GNM_FUNC_HELP_ARG
, F_("x:code point")},
56 { GNM_FUNC_HELP_DESCRIPTION
, F_("CHAR(@{x}) returns the CP1252 (Windows-1252) character with code @{x}.")},
57 { GNM_FUNC_HELP_DESCRIPTION
, F_("@{x} must be in the range 1 to 255.")},
58 { GNM_FUNC_HELP_DESCRIPTION
, F_("CP1252 (Windows-1252) is also known as the \"ANSI code page\", "
59 "but it is not an ANSI standard.")},
60 { GNM_FUNC_HELP_DESCRIPTION
, F_("CP1252 (Windows-1252) is based on an early draft of ISO-8859-1, "
61 "and contains all of its printable characters. It also contains all "
62 "of ISO-8859-15's printable characters (but partially at different "
64 { GNM_FUNC_HELP_NOTE
, F_("In CP1252 (Windows-1252), 129, 141, 143, 144, and 157 do not have matching characters.") },
65 { GNM_FUNC_HELP_NOTE
, F_("For @{x} from 1 to 255 except 129, 141, 143, 144, and 157 we have CODE(CHAR(@{x}))=@{x}.") },
66 { GNM_FUNC_HELP_DESCRIPTION
, F_("This function is Excel compatible.") },
67 { GNM_FUNC_HELP_EXAMPLES
, "=CHAR(65)" },
68 { GNM_FUNC_HELP_SEEALSO
, "CODE"},
73 gnumeric_char (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
75 gnm_float c
= value_get_as_float (argv
[0]);
77 if (c
>= 1 && c
< 128) {
81 return value_new_string (result
);
82 } else if (c
>= 128 && c
< 256) {
84 char *str
= g_convert_with_iconv (&c2
, 1, CHAR_iconv
,
87 int len
= g_utf8_strlen (str
, -1);
89 return value_new_string_nocopy (str
);
90 g_warning ("iconv for CHAR(%d) produced a string of length %d",
94 g_warning ("iconv failed for CHAR(%d)", c2
);
97 return value_new_error_VALUE (ei
->pos
);
100 /***************************************************************************/
102 static GnmFuncHelp
const help_unichar
[] = {
103 { GNM_FUNC_HELP_NAME
, F_("UNICHAR:the Unicode character represented by the Unicode code point @{x}")},
104 { GNM_FUNC_HELP_ARG
, F_("x:Unicode code point")},
105 { GNM_FUNC_HELP_EXAMPLES
, "=UNICHAR(65)"},
106 { GNM_FUNC_HELP_EXAMPLES
, "=UNICHAR(960)"},
107 { GNM_FUNC_HELP_EXAMPLES
, "=UNICHAR(20000)"},
108 { GNM_FUNC_HELP_SEEALSO
, "CHAR,UNICODE,CODE"},
113 gnumeric_unichar (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
115 gnm_float c
= value_get_as_float (argv
[0]);
117 if (c
>= 0 && c
<= INT_MAX
&& g_unichar_validate ((gunichar
)c
)) {
119 int len
= g_unichar_to_utf8 ((gunichar
)c
, utf8
);
121 return value_new_string (utf8
);
123 return value_new_error_VALUE (ei
->pos
);
126 /***************************************************************************/
128 static GIConv CODE_iconv
;
130 static GnmFuncHelp
const help_code
[] = {
131 { GNM_FUNC_HELP_NAME
, F_("CODE:the CP1252 (Windows-1252) code point for the character @{c}")},
132 { GNM_FUNC_HELP_ARG
, F_("c:character")},
133 { GNM_FUNC_HELP_DESCRIPTION
, F_("@{c} must be a valid CP1252 (Windows-1252) character.")},
134 { GNM_FUNC_HELP_DESCRIPTION
, F_("CP1252 (Windows-1252) is also known as the \"ANSI code page\", but it is not an ANSI standard.")},
135 { GNM_FUNC_HELP_DESCRIPTION
, F_("CP1252 (Windows-1252) is based on an early draft of ISO-8859-1, and contains all of its printable characters (but partially at different positions.)")},
136 { GNM_FUNC_HELP_NOTE
, F_("In CP1252 (Windows-1252), 129, 141, 143, 144, and 157 do not have matching characters.") },
137 { GNM_FUNC_HELP_NOTE
, F_("For @{x} from 1 to 255 except 129, 141, 143, 144, and 157 we have CODE(CHAR(@{x}))=@{x}.") },
138 { GNM_FUNC_HELP_DESCRIPTION
, F_("This function is Excel compatible.") },
139 { GNM_FUNC_HELP_EXAMPLES
, "=CODE(\"A\")" },
140 { GNM_FUNC_HELP_SEEALSO
, "CHAR"},
141 { GNM_FUNC_HELP_END
}
145 gnumeric_code (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
147 char const *s
= value_peek_string (argv
[0]);
148 const unsigned char *us
= (const unsigned char *)s
;
154 return value_new_error_VALUE (ei
->pos
);
157 return value_new_int (*us
);
159 clen
= g_utf8_next_char (s
) - s
;
160 str
= g_convert_with_iconv (s
, clen
, CODE_iconv
,
161 NULL
, &written
, NULL
);
163 res
= value_new_int ((unsigned char)*str
);
165 g_warning ("iconv failed for CODE(U%x)", g_utf8_get_char (s
));
166 res
= value_new_error_VALUE (ei
->pos
);
173 /***************************************************************************/
175 static GnmFuncHelp
const help_unicode
[] = {
176 { GNM_FUNC_HELP_NAME
, F_("UNICODE:the Unicode code point for the character @{c}")},
177 { GNM_FUNC_HELP_ARG
, F_("c:character")},
178 { GNM_FUNC_HELP_EXAMPLES
, "=UNICODE(\"A\")" },
179 { GNM_FUNC_HELP_SEEALSO
, "UNICHAR,CODE,CHAR"},
184 gnumeric_unicode (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
186 char const *s
= value_peek_string (argv
[0]);
189 return value_new_error_VALUE (ei
->pos
);
191 return value_new_int (g_utf8_get_char (s
));
194 /***************************************************************************/
197 gnm_compare_strings (const char *cstr1
, const char *cstr2
)
199 const char *a
= cstr1
, *b
= cstr2
;
203 /* Skip leading ASCII prefixes that match. */
204 while (*a
== *b
&& *a
!= 0 && *b
!= 0)
207 * If we've hit the end of one string, we ought to have hit the
208 * end of the other. Otherwise the strings are different.
210 if (*a
== 0 || *b
== 0)
214 * If they differ in two ASCII characters (including terminating
215 * NULs), the strings must be distinct.
217 if ((guchar
)*a
< 128 && (guchar
)*b
< 128)
221 * We are using NFD normalization, ie. Characters are decomposed by
222 * canonical equivalence, and multiple combining characters are
223 * arranged in a specific order. Note that ligatures remain ligatures,
224 * formatting such as subscript-3 versus 3 are retained.
226 * Note that for example, the distinct Unicode strings "U+212B"
227 * (the angstrom sign "Ã…") and "U+00C5" (the Swedish letter "Ã…")
228 * are both expanded by NFD (or NFKD) into the sequence
229 * "U+0041 U+030A" (Latin letter "A" and combining ring above "°")
230 * Of course "U+0041 U+030A" is retained in form, so we need to work
231 * with at least the last ASCII character. Performance should nearly
232 * be identical to using all
234 str1
= g_utf8_normalize (cstr1
, -1, G_NORMALIZE_DEFAULT
);
235 str2
= g_utf8_normalize (cstr2
, -1, G_NORMALIZE_DEFAULT
);
237 eq
= (g_strcmp0 (str1
, str2
) == 0);
245 static GnmFuncHelp
const help_exact
[] = {
246 { GNM_FUNC_HELP_NAME
, F_("EXACT:TRUE if @{string1} is exactly equal to @{string2}")},
247 { GNM_FUNC_HELP_ARG
, F_("string1:first string")},
248 { GNM_FUNC_HELP_ARG
, F_("string2:second string")},
249 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
250 { GNM_FUNC_HELP_EXAMPLES
, "=EXACT(\"Gnumeric\",\"Gnumeric\")" },
251 { GNM_FUNC_HELP_EXAMPLES
, "=EXACT(\"gnumeric\",\"Gnumeric\")" },
252 { GNM_FUNC_HELP_SEEALSO
, "LEN,SEARCH,DELTA"},
257 gnumeric_exact (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
259 return value_new_bool (gnm_compare_strings (value_peek_string (argv
[0]),
260 value_peek_string (argv
[1])));
263 /***************************************************************************/
265 static GnmFuncHelp
const help_len
[] = {
266 { GNM_FUNC_HELP_NAME
, F_("LEN:the number of characters of the string @{s}")},
267 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
268 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
269 { GNM_FUNC_HELP_EXAMPLES
, "=LEN(\"Helsinki\")" },
270 { GNM_FUNC_HELP_EXAMPLES
, "=LEN(\"L\xc3\xa9vy\")" },
271 { GNM_FUNC_HELP_SEEALSO
, "CHAR,CODE,LENB"},
276 gnumeric_len (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
278 return value_new_int (g_utf8_strlen (value_peek_string (argv
[0]), -1));
281 /***************************************************************************/
282 static GnmFuncHelp
const help_lenb
[] = {
283 { GNM_FUNC_HELP_NAME
, F_("LENB:the number of bytes in the string @{s}")},
284 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
285 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
286 { GNM_FUNC_HELP_EXAMPLES
, "=LENB(\"Helsinki\")" },
287 { GNM_FUNC_HELP_EXAMPLES
, "=LENB(\"L\xc3\xa9vy\")" },
288 { GNM_FUNC_HELP_SEEALSO
, "CHAR, CODE, LEN"},
293 gnumeric_lenb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
295 return value_new_int (strlen (value_peek_string (argv
[0])));
298 /***************************************************************************/
300 static GnmFuncHelp
const help_left
[] = {
301 { GNM_FUNC_HELP_NAME
, F_("LEFT:the first @{num_chars} characters of the string @{s}")},
302 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
303 { GNM_FUNC_HELP_ARG
, F_("num_chars:the number of characters to return (defaults to 1)")},
304 { GNM_FUNC_HELP_NOTE
, F_("If the string @{s} is in a right-to-left script, the returned first characters are from the right of the string.")},
305 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.")},
306 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.")},
307 { GNM_FUNC_HELP_EXAMPLES
, "=LEFT(\"L\xc3\xa9vy\",3)" },
308 { GNM_FUNC_HELP_EXAMPLES
, "=LEFT(\"L\xc3\xa9vy\",2)" },
309 { GNM_FUNC_HELP_SEEALSO
, "MID,RIGHT,LEN,MIDB,RIGHTB,LENB"},
310 { GNM_FUNC_HELP_END
}
314 gnumeric_left (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
316 const guchar
*peek
= (const guchar
*)value_peek_string (argv
[0]);
317 gnm_float count
= argv
[1] ? value_get_as_float (argv
[1]) : 1.0;
321 return value_new_error_VALUE (ei
->pos
);
322 icount
= (int)MIN ((gnm_float
)INT_MAX
, count
);
324 for (newlen
= 0; peek
[newlen
] != 0 && icount
> 0; icount
--)
325 newlen
+= g_utf8_skip
[peek
[newlen
]];
327 return value_new_string_nocopy (g_strndup (peek
, newlen
));
330 /***************************************************************************/
332 static GnmFuncHelp
const help_leftb
[] = {
333 { GNM_FUNC_HELP_NAME
, F_("LEFTB:the first characters of the string @{s} comprising at most @{num_bytes} bytes")},
334 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
335 { GNM_FUNC_HELP_ARG
, F_("num_bytes:the maximum number of bytes to return (defaults to 1)")},
336 { GNM_FUNC_HELP_NOTE
, F_("The semantics of this function is subject to change as various applications implement it.")},
337 { GNM_FUNC_HELP_NOTE
, F_("If the string is in a right-to-left script, the returned first characters are from the right of the string.")},
338 { GNM_FUNC_HELP_EXCEL
, F_("While this function is syntactically Excel compatible, the differences in the underlying text encoding will usually yield different results.")},
339 { GNM_FUNC_HELP_ODF
, F_("While this function is OpenFormula compatible, most of its behavior is, at this time, implementation specific.")},
340 { GNM_FUNC_HELP_EXAMPLES
, "=LEFTB(\"L\xc3\xa9vy\",3)" },
341 { GNM_FUNC_HELP_EXAMPLES
, "=LEFTB(\"L\xc3\xa9vy\",2)" },
342 { GNM_FUNC_HELP_SEEALSO
, "MIDB,RIGHTB,LENB,LEFT,MID,RIGHT,LEN" },
343 { GNM_FUNC_HELP_END
}
347 gnumeric_leftb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
349 const guchar
*peek
= (const guchar
*)value_peek_string (argv
[0]);
350 gnm_float count
= argv
[1] ? value_get_as_float (argv
[1]) : 1.0;
351 int len
= strlen (peek
);
355 return value_new_error_VALUE (ei
->pos
);
356 icount
= (int)MIN ((gnm_float
)INT_MAX
, count
);
358 return value_new_string (peek
);
360 newlen
= ((const guchar
*)g_utf8_find_prev_char (peek
, peek
+ icount
+ 1)) - peek
;
362 return value_new_string_nocopy (g_strndup (peek
, newlen
));
364 /***************************************************************************/
366 static GnmFuncHelp
const help_lower
[] = {
367 { GNM_FUNC_HELP_NAME
, F_("LOWER:a lower-case version of the string @{text}")},
368 { GNM_FUNC_HELP_ARG
, F_("text:string")},
369 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.")},
370 { GNM_FUNC_HELP_EXAMPLES
, "=LOWER(\"J. F. Kennedy\")" },
371 { GNM_FUNC_HELP_EXAMPLES
, "=LOWER(\"L\xc3\x89VY\")" },
372 { GNM_FUNC_HELP_SEEALSO
, "UPPER"},
377 gnumeric_lower (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
379 return value_new_string_nocopy (g_utf8_strdown (value_peek_string (argv
[0]), -1));
382 /***************************************************************************/
384 static GnmFuncHelp
const help_mid
[] = {
385 { GNM_FUNC_HELP_NAME
, F_("MID:the substring of the string @{s} starting at position @{position} consisting of @{length} characters")},
386 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
387 { GNM_FUNC_HELP_ARG
, F_("position:the starting position")},
388 { GNM_FUNC_HELP_ARG
, F_("length:the number of characters to return")},
389 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.")},
390 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.")},
391 { GNM_FUNC_HELP_EXAMPLES
, "=MID(\"L\xc3\xa9vy\",2,1)" },
392 { GNM_FUNC_HELP_EXAMPLES
, "=MID(\"L\xc3\xa9vy\",3,2)" },
393 { GNM_FUNC_HELP_SEEALSO
, "LEFT,RIGHT,LEN,LEFTB,MIDB,RIGHTB,LENB"},
394 { GNM_FUNC_HELP_END
}
398 gnumeric_mid (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
400 char const *source
= value_peek_string (argv
[0]);
401 gnm_float pos
= value_get_as_float (argv
[1]);
402 gnm_float len
= value_get_as_float (argv
[2]);
403 size_t slen
= g_utf8_strlen (source
, -1);
405 size_t ilen
, ipos
, ulen
;
407 if (len
< 0 || pos
< 1)
408 return value_new_error_VALUE (ei
->pos
);
410 return value_new_string ("");
412 /* Make ipos zero-based. */
413 ipos
= (size_t)(pos
- 1);
414 ilen
= (size_t)MIN (len
, (gnm_float
)(slen
- ipos
));
416 upos
= g_utf8_offset_to_pointer (source
, ipos
);
417 ulen
= g_utf8_offset_to_pointer (upos
, ilen
) - upos
;
419 return value_new_string_nocopy (g_strndup (upos
, ulen
));
422 /***************************************************************************/
424 static GnmFuncHelp
const help_midb
[] = {
425 { GNM_FUNC_HELP_NAME
, F_("MIDB:the characters following the first @{start_pos} bytes comprising at most @{num_bytes} bytes")},
426 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
427 { GNM_FUNC_HELP_ARG
, F_("start_pos:the number of the byte with which to start (defaults to 1)")},
428 { GNM_FUNC_HELP_ARG
, F_("num_bytes:the maximum number of bytes to return (defaults to 1)")},
429 { GNM_FUNC_HELP_NOTE
, F_("The semantics of this function is subject to change as various applications implement it.")},
430 { GNM_FUNC_HELP_EXCEL
, F_("While this function is syntactically Excel compatible, "
431 "the differences in the underlying text encoding will usually yield different results.")},
432 { GNM_FUNC_HELP_ODF
, F_("While this function is OpenFormula compatible, most of its behavior is, at this time, implementation specific.")},
433 { GNM_FUNC_HELP_EXAMPLES
, "=MIDB(\"L\xc3\xa9vy\",2,1)" },
434 { GNM_FUNC_HELP_EXAMPLES
, "=MIDB(\"L\xc3\xa9vy\",2,2)" },
435 { GNM_FUNC_HELP_EXAMPLES
, "=MIDB(\"L\xc3\xa9vy\",3,2)" },
436 { GNM_FUNC_HELP_SEEALSO
, "LEFTB,RIGHTB,LENB,LEFT,MID,RIGHT,LEN"},
437 { GNM_FUNC_HELP_END
}
441 gnumeric_midb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
443 const guchar
*peek
= (const guchar
*)value_peek_string (argv
[0]);
444 gnm_float pos
= value_get_as_float (argv
[1]);
445 gnm_float len
= value_get_as_float (argv
[2]);
446 int slen
= strlen (peek
);
447 int ipos
, ilen
, newlen
;
449 if ((len
< 0) || (pos
< 1))
450 return value_new_error_VALUE (ei
->pos
);
451 ipos
= (int)MIN ((gnm_float
)INT_MAX
/ 2, pos
) - 1;
452 ilen
= (int)MIN ((gnm_float
)INT_MAX
/ 2, len
);
453 if ((ipos
>= slen
) ||
454 ((gunichar
)-1 == g_utf8_get_char_validated (peek
+ ipos
, -1)))
455 return value_new_error_VALUE (ei
->pos
);
457 if ((ipos
+ ilen
) > slen
)
458 return value_new_string (peek
+ ipos
);
460 newlen
= ((const guchar
*)g_utf8_find_prev_char (peek
+ ipos
, peek
+ ipos
+ ilen
+ 1))
463 return value_new_string_nocopy (g_strndup (peek
+ ipos
, newlen
));
466 /***************************************************************************/
468 static GnmFuncHelp
const help_findb
[] = {
469 { GNM_FUNC_HELP_NAME
, F_("FINDB:first byte position of @{string1} in @{string2} following byte position @{start}")},
470 { GNM_FUNC_HELP_ARG
, F_("string1:search string")},
471 { GNM_FUNC_HELP_ARG
, F_("string2:search field")},
472 { GNM_FUNC_HELP_ARG
, F_("start:starting byte position, defaults to 1")},
473 { GNM_FUNC_HELP_NOTE
, F_("This search is case-sensitive.")},
474 { GNM_FUNC_HELP_EXCEL
, F_("While this function is syntactically Excel compatible, "
475 "the differences in the underlying text encoding will usually yield different results.")},
476 { GNM_FUNC_HELP_ODF
, F_("While this function is OpenFormula compatible, most of its behavior is, at this time, implementation specific.")},
477 { GNM_FUNC_HELP_EXAMPLES
, "=FINDB(\"v\",\"L\xc3\xa9vy\")" },
478 { GNM_FUNC_HELP_EXAMPLES
, "=FINDB(\"v\",\"L\xc3\xa9vy\",3)" },
479 { GNM_FUNC_HELP_EXAMPLES
, "=FINDB(\"v\",\"L\xc3\xa9vy\",5)" },
480 { GNM_FUNC_HELP_SEEALSO
, "FIND,LEFTB,RIGHTB,LENB,LEFT,MID,RIGHT,LEN"},
485 gnumeric_findb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
487 char const *needle
= value_peek_string (argv
[0]);
488 char const *haystack
= value_peek_string (argv
[1]);
489 gnm_float count
= argv
[2] ? value_get_as_float (argv
[2]) : 1.0;
490 size_t haystacksize
= strlen (haystack
);
495 if (count
< 1 || count
>= haystacksize
+ 1)
496 return value_new_error_VALUE (ei
->pos
);
498 icount
= (size_t) count
;
499 p
= (icount
== 1) ? haystack
: g_utf8_find_next_char (haystack
+ (icount
- 2) , NULL
);
501 p
= g_strstr_len (p
, strlen (p
), needle
);
504 ((p
- haystack
) + 1);
506 return value_new_error_VALUE (ei
->pos
);
509 /***************************************************************************/
511 static GnmFuncHelp
const help_right
[] = {
512 { GNM_FUNC_HELP_NAME
, F_("RIGHT:the last @{num_chars} characters of the string @{s}")},
513 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
514 { GNM_FUNC_HELP_ARG
, F_("num_chars:the number of characters to return (defaults to 1)")},
515 { GNM_FUNC_HELP_NOTE
, F_("If the string @{s} is in a right-to-left script, the returned last characters are from the left of the string.")},
516 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.")},
517 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.")},
518 { GNM_FUNC_HELP_EXAMPLES
, "=RIGHT(\"L\xc3\xa9vy\",2)" },
519 { GNM_FUNC_HELP_EXAMPLES
, "=RIGHT(\"L\xc3\xa9vy\",3)" },
520 { GNM_FUNC_HELP_SEEALSO
, "LEFT,MID,LEN,LEFTB,MIDB,RIGHTB,LENB"},
521 { GNM_FUNC_HELP_END
}
525 gnumeric_right (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
527 char const *os
= value_peek_string (argv
[0]);
528 gnm_float count
= argv
[1] ? value_get_as_float (argv
[1]) : 1.0;
532 return value_new_error_VALUE (ei
->pos
);
533 icount
= (int)MIN ((gnm_float
)INT_MAX
, count
);
535 slen
= g_utf8_strlen (os
, -1);
538 return value_new_string (g_utf8_offset_to_pointer (os
, slen
- icount
));
540 /* We could just duplicate the arg, but that would not ensure
541 that the result was a string. */
542 return value_new_string (os
);
545 /***************************************************************************/
547 static GnmFuncHelp
const help_rightb
[] = {
548 { GNM_FUNC_HELP_NAME
, F_("RIGHTB:the last characters of the string @{s} comprising at most @{num_bytes} bytes")},
549 { GNM_FUNC_HELP_ARG
, F_("s:the string")},
550 { GNM_FUNC_HELP_ARG
, F_("num_bytes:the maximum number of bytes to return (defaults to 1)")},
551 { GNM_FUNC_HELP_NOTE
, F_("The semantics of this function is subject to change as various applications implement it.")},
552 { GNM_FUNC_HELP_NOTE
, F_("If the string @{s} is in a right-to-left script, the returned last characters are from the left of the string.")},
553 { GNM_FUNC_HELP_EXCEL
, F_("While this function is syntactically Excel compatible, the differences in the underlying text encoding will usually yield different results.")},
554 { GNM_FUNC_HELP_ODF
, F_("While this function is OpenFormula compatible, most of its behavior is, at this time, implementation specific.")},
555 { GNM_FUNC_HELP_EXAMPLES
, "=RIGHTB(\"L\xc3\xa9vy\",2)" },
556 { GNM_FUNC_HELP_EXAMPLES
, "=RIGHTB(\"L\xc3\xa9vy\",3)" },
557 { GNM_FUNC_HELP_SEEALSO
, "LEFTB,MIDB,LENB,LEFT,MID,RIGHT,LEN"},
558 { GNM_FUNC_HELP_END
}
562 gnumeric_rightb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
564 const guchar
*peek
= (const guchar
*)value_peek_string (argv
[0]);
565 gnm_float count
= argv
[1] ? value_get_as_float (argv
[1]) : 1.0;
566 int len
= strlen (peek
);
571 return value_new_error_VALUE (ei
->pos
);
572 icount
= (int)MIN ((gnm_float
)INT_MAX
, count
);
574 return value_new_string (peek
);
576 res
= g_utf8_find_next_char (peek
+ len
- icount
- 1, peek
+ len
);
577 return value_new_string ((res
== NULL
) ? "" : res
);
580 /***************************************************************************/
582 static GnmFuncHelp
const help_upper
[] = {
583 { GNM_FUNC_HELP_NAME
, F_("UPPER:an upper-case version of the string @{text}")},
584 { GNM_FUNC_HELP_ARG
, F_("text:string")},
585 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
586 { GNM_FUNC_HELP_EXAMPLES
, "=UPPER(\"Gnumeric\")" },
587 { GNM_FUNC_HELP_EXAMPLES
, "=UPPER(\"L\xc3\xa9vy\")" },
588 { GNM_FUNC_HELP_SEEALSO
, "LOWER"},
594 gnumeric_upper (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
596 return value_new_string_nocopy (g_utf8_strup (value_peek_string (argv
[0]), -1));
599 /***************************************************************************/
601 static GnmFuncHelp
const help_concatenate
[] = {
602 { GNM_FUNC_HELP_NAME
, F_("CONCATENATE:the concatenation of the strings @{s1}, @{s2},\xe2\x80\xa6")},
603 { GNM_FUNC_HELP_ARG
, F_("s1:first string")},
604 { GNM_FUNC_HELP_ARG
, F_("s2:second string")},
605 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
606 { GNM_FUNC_HELP_EXAMPLES
, "=CONCATENATE(\"aa\",\"bb\")" },
607 { GNM_FUNC_HELP_SEEALSO
, "LEFT,MID,RIGHT"},
613 gnumeric_concatenate (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
615 return string_range_function (argc
, argv
, ei
,
618 COLLECT_IGNORE_BLANKS
,
622 static GnmFuncHelp
const help_concat
[] = {
623 { GNM_FUNC_HELP_NAME
, F_("CONCAT:the concatenation of the strings @{s1}, @{s2},\xe2\x80\xa6")},
624 { GNM_FUNC_HELP_ARG
, F_("s1:first string")},
625 { GNM_FUNC_HELP_ARG
, F_("s2:second string")},
626 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
627 { GNM_FUNC_HELP_EXAMPLES
, "=CONCAT(\"aa\",\"bb\")" },
628 { GNM_FUNC_HELP_NOTE
, F_("This function is identical to CONCATENATE") },
629 { GNM_FUNC_HELP_SEEALSO
, "LEFT,MID,RIGHT"},
634 gnumeric_concat (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
636 return gnumeric_concatenate (ei
, argc
, argv
);
639 /***************************************************************************/
641 static GnmFuncHelp
const help_textjoin
[] = {
642 { GNM_FUNC_HELP_NAME
, F_("TEXTJOIN:the concatenation of the strings @{s1}, @{s2},\xe2\x80\xa6 delimited by @{del}")},
643 { GNM_FUNC_HELP_ARG
, F_("del:delimiter")},
644 { GNM_FUNC_HELP_ARG
, F_("blank:ignore blanks")},
645 { GNM_FUNC_HELP_ARG
, F_("s1:first string")},
646 { GNM_FUNC_HELP_ARG
, F_("s2:second string")},
647 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
648 { GNM_FUNC_HELP_EXAMPLES
, "=TEXTJOIN(\"::\",FALSE,\"aa\",\"bb\")" },
649 { GNM_FUNC_HELP_SEEALSO
, "CONCATENATE"},
655 gboolean ignore_blanks
;
659 range_textjoin (GPtrArray
*data
, char **pres
, gpointer user_
)
661 struct cb_textjoin
*user
= user_
;
662 GString
*res
= g_string_new (NULL
);
663 gboolean first
= TRUE
;
666 for (ui
= 0; ui
< data
->len
; ui
++) {
667 const char *s
= g_ptr_array_index (data
, ui
);
669 if (s
[0] == 0 && user
->ignore_blanks
)
675 g_string_append (res
, user
->delim
);
677 g_string_append (res
, s
);
680 *pres
= g_string_free (res
, FALSE
);
685 gnumeric_textjoin (GnmFuncEvalInfo
*ei
, int argc
, GnmExprConstPtr
const *argv
)
689 struct cb_textjoin data
;
694 return value_new_error_VALUE (ei
->pos
);
696 v
= gnm_expr_eval (argv
[0], ei
->pos
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
697 if (VALUE_IS_ERROR (v
))
699 data
.delim
= value_get_as_string (v
);
702 v
= gnm_expr_eval (argv
[1], ei
->pos
, GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
703 if (VALUE_IS_ERROR (v
))
705 data
.ignore_blanks
= value_get_as_bool (v
, &err
); // What about err?
708 v
= string_range_function (argc
- 2, argv
+ 2, ei
,
711 data
.ignore_blanks
? COLLECT_IGNORE_BLANKS
: 0,
720 /***************************************************************************/
722 static GnmFuncHelp
const help_rept
[] = {
723 { GNM_FUNC_HELP_NAME
, F_("REPT:@{num} repetitions of string @{text}")},
724 { GNM_FUNC_HELP_ARG
, F_("text:string")},
725 { GNM_FUNC_HELP_ARG
, F_("num:non-negative integer")},
726 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
727 { GNM_FUNC_HELP_EXAMPLES
, "=REPT(\"x\",3)" },
728 { GNM_FUNC_HELP_SEEALSO
, "CONCATENATE"},
733 gnumeric_rept (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
735 char const *source
= value_peek_string (argv
[0]);
736 gnm_float num
= value_get_as_float (argv
[1]);
737 size_t len
= strlen (source
);
742 return value_new_error_VALUE (ei
->pos
);
744 /* Fast special case. =REPT ("",2^30) should not take long. */
745 if (len
== 0 || num
< 1)
746 return value_new_string ("");
748 /* Check if the length would overflow. */
749 if (num
>= INT_MAX
/ len
)
750 return value_new_error_VALUE (ei
->pos
);
753 res
= g_try_malloc (len
* inum
+ 1);
755 return value_new_error_VALUE (ei
->pos
);
757 for (i
= 0; inum
-- > 0; i
+= len
)
758 memcpy (res
+ i
, source
, len
);
761 return value_new_string_nocopy (res
);
764 /***************************************************************************/
766 static GnmFuncHelp
const help_clean
[] = {
767 { GNM_FUNC_HELP_NAME
, F_("CLEAN:@{text} with any non-printable characters removed")},
768 { GNM_FUNC_HELP_ARG
, F_("text:string")},
769 { GNM_FUNC_HELP_DESCRIPTION
, F_("CLEAN removes non-printable characters from its argument leaving only regular characters and white-space.") },
770 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
771 { GNM_FUNC_HELP_EXAMPLES
, "=CLEAN(\"Gnumeric\"&char(7))" },
776 gnumeric_clean (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
778 char const *s
= value_peek_string (argv
[0]);
779 GString
*res
= g_string_sized_new (strlen (s
));
782 gunichar uc
= g_utf8_get_char (s
);
784 if (g_unichar_isprint (uc
))
785 g_string_append_unichar (res
, uc
);
787 s
= g_utf8_next_char (s
);
790 return value_new_string_nocopy (g_string_free (res
, FALSE
));
793 /***************************************************************************/
795 static GnmFuncHelp
const help_find
[] = {
796 { GNM_FUNC_HELP_NAME
, F_("FIND:first position of @{string1} in @{string2} following position @{start}")},
797 { GNM_FUNC_HELP_ARG
, F_("string1:search string")},
798 { GNM_FUNC_HELP_ARG
, F_("string2:search field")},
799 { GNM_FUNC_HELP_ARG
, F_("start:starting position, defaults to 1")},
800 { GNM_FUNC_HELP_NOTE
, F_("This search is case-sensitive.")},
801 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
802 { GNM_FUNC_HELP_EXAMPLES
, "=FIND(\"num\",\"Gnumeric\")" },
803 { GNM_FUNC_HELP_SEEALSO
, "EXACT,LEN,MID,SEARCH"},
808 gnumeric_find (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
810 char const *needle
= value_peek_string (argv
[0]);
811 char const *haystack
= value_peek_string (argv
[1]);
812 gnm_float count
= argv
[2] ? value_get_as_float (argv
[2]) : 1.0;
813 size_t haystacksize
= g_utf8_strlen (haystack
, -1);
817 if (count
< 1 || count
>= haystacksize
+ 1)
818 return value_new_error_VALUE (ei
->pos
);
819 icount
= (size_t)count
;
821 haystack
= g_utf8_offset_to_pointer (haystack
, icount
- 1);
823 p
= g_strstr_len (haystack
, strlen (haystack
), needle
);
826 (g_utf8_pointer_to_offset (haystack
, p
) + icount
);
828 return value_new_error_VALUE (ei
->pos
);
831 /***************************************************************************/
833 static GnmFuncHelp
const help_fixed
[] = {
834 { GNM_FUNC_HELP_NAME
, F_("FIXED:formatted string representation of @{num}")},
835 { GNM_FUNC_HELP_ARG
, F_("num:number")},
836 { GNM_FUNC_HELP_ARG
, F_("decimals:number of decimals")},
837 { GNM_FUNC_HELP_ARG
, F_("no_commas:TRUE if no thousand separators should be used, "
838 "defaults to FALSE")},
839 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
840 { GNM_FUNC_HELP_EXAMPLES
, "=FIXED(1234.567,2)" },
841 { GNM_FUNC_HELP_EXAMPLES
, "=FIXED(1234.567,2,TRUE)" },
842 { GNM_FUNC_HELP_SEEALSO
, "TEXT,VALUE,DOLLAR"},
847 gnumeric_fixed (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
849 gnm_float num
= value_get_as_float (argv
[0]);
850 gnm_float decimals
= argv
[1] ? value_get_as_float (argv
[1]) : 2.0;
851 gboolean no_commas
= argv
[2] ? value_get_as_checked_bool (argv
[2]) : FALSE
;
856 GOFormatDetails
*details
;
858 decimals
= gnm_fake_trunc (decimals
);
860 return value_new_error_VALUE (ei
->pos
);
862 /* no decimal point : just round and pad 0's */
863 gnm_float mult
= gnm_pow10 (decimals
);
865 num
= 0; /* Underflow */
867 num
= gnm_fake_round (num
* mult
) / mult
;
870 v
= value_new_float (num
);
872 details
= go_format_details_new (GO_FORMAT_NUMBER
);
873 details
->num_decimals
= decimals
;
874 details
->thousands_sep
= !no_commas
;
875 format
= g_string_new (NULL
);
876 go_format_generate_str (format
, details
);
877 go_format_details_free (details
);
879 fmt
= go_format_new_from_XL (format
->str
);
880 g_string_free (format
, TRUE
);
882 res
= format_value (fmt
, v
, -1,
883 sheet_date_conv (ei
->pos
->sheet
));
885 go_format_unref (fmt
);
888 return value_new_string_nocopy (res
);
891 /***************************************************************************/
893 static GnmFuncHelp
const help_proper
[] = {
894 { GNM_FUNC_HELP_NAME
, F_("PROPER:@{text} with initial of each word capitalised")},
895 { GNM_FUNC_HELP_ARG
, F_("text:string")},
896 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
897 { GNM_FUNC_HELP_EXAMPLES
, "=PROPER(\"j. f. kennedy\")" },
898 { GNM_FUNC_HELP_SEEALSO
, "LOWER,UPPER"},
903 gnumeric_proper (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
906 GString
*res
= g_string_new (NULL
);
907 gboolean inword
= FALSE
;
909 p
= value_peek_string (argv
[0]);
911 gunichar uc
= g_utf8_get_char (p
);
913 if (g_unichar_isalpha (uc
)) {
915 g_string_append_unichar
916 (res
, g_unichar_tolower (uc
));
918 g_string_append_unichar
919 (res
, g_unichar_toupper (uc
));
923 g_string_append_unichar (res
, uc
);
927 p
= g_utf8_next_char (p
);
930 return value_new_string_nocopy (g_string_free (res
, FALSE
));
933 /***************************************************************************/
935 static GnmFuncHelp
const help_replace
[] = {
936 { GNM_FUNC_HELP_NAME
, F_("REPLACE:string @{old} with @{num} characters "
937 "starting at @{start} replaced by @{new}")},
938 { GNM_FUNC_HELP_ARG
, F_("old:original text")},
939 { GNM_FUNC_HELP_ARG
, F_("start:starting position")},
940 { GNM_FUNC_HELP_ARG
, F_("num:number of characters to be replaced")},
941 { GNM_FUNC_HELP_ARG
, F_("new:replacement string")},
942 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
943 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACE(\"Gnumeric\",2,6,\"*6*\")" },
944 { GNM_FUNC_HELP_SEEALSO
, "MID,SEARCH,SUBSTITUTE,TRIM"},
949 gnumeric_replace (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
951 char const *old
= value_peek_string (argv
[0]);
952 gnm_float start
= value_get_as_float (argv
[1]);
953 gnm_float num
= value_get_as_float (argv
[2]);
954 char const *new = value_peek_string (argv
[3]);
955 size_t istart
, inum
, oldlen
, precutlen
, postcutlen
, newlen
;
959 if (start
< 1 || num
< 0)
960 return value_new_error_VALUE (ei
->pos
);
962 oldlen
= g_utf8_strlen (old
, -1);
963 /* Make istart zero-based. */
964 istart
= (int)MIN ((gnm_float
)oldlen
, start
- 1);
965 inum
= (int)MIN((gnm_float
)(oldlen
- istart
), num
);
967 /* |<----precut----><cut><---postcut--->| */
970 p
= g_utf8_offset_to_pointer (old
, istart
);
971 q
= g_utf8_offset_to_pointer (p
, inum
);
974 postcutlen
= strlen (q
);
975 newlen
= strlen (new);
977 res
= g_malloc (precutlen
+ newlen
+ postcutlen
+ 1);
978 memcpy (res
, old
, precutlen
);
979 memcpy (res
+ precutlen
, new, newlen
);
980 memcpy (res
+ precutlen
+ newlen
, q
, postcutlen
+ 1);
981 return value_new_string_nocopy (res
);
984 /***************************************************************************/
986 static GnmFuncHelp
const help_replaceb
[] = {
987 { GNM_FUNC_HELP_NAME
, F_("REPLACEB:string @{old} with up to @{num} bytes "
988 "starting at @{start} replaced by @{new}")},
989 { GNM_FUNC_HELP_ARG
, F_("old:original text")},
990 { GNM_FUNC_HELP_ARG
, F_("start:starting byte position")},
991 { GNM_FUNC_HELP_ARG
, F_("num:number of bytes to be replaced")},
992 { GNM_FUNC_HELP_ARG
, F_("new:replacement string")},
993 { GNM_FUNC_HELP_DESCRIPTION
, F_("REPLACEB replaces the string of valid unicode characters starting "
994 "at the byte @{start} and ending at @{start}+@{num}-1 with the string @{new}.")},
995 { GNM_FUNC_HELP_NOTE
, F_("The semantics of this function is subject to change as various applications implement it.")},
996 { GNM_FUNC_HELP_EXCEL
, F_("While this function is syntactically Excel compatible, "
997 "the differences in the underlying text encoding will usually yield different results.")},
998 { GNM_FUNC_HELP_ODF
, F_("While this function is OpenFormula compatible, most of its behavior is, at this time, implementation specific.")},
999 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACEB(\"L\xc3\xa9vy\",2,1,\"*\")" },
1000 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACEB(\"L\xc3\xa9vy\",2,2,\"*\")" },
1001 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACEB(\"L\xc3\xa9vy\",2,3,\"*\")" },
1002 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACEB(\"L\xc3\xa9vy\",2,4,\"*\")" },
1003 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACEB(\"L\xc3\xa9vy\",3,2,\"*\")" },
1004 { GNM_FUNC_HELP_EXAMPLES
, "=REPLACEB(\"L\xc3\xa9vy\",3,3,\"*\")" },
1005 { GNM_FUNC_HELP_SEEALSO
, "MID,SEARCH,SUBSTITUTE,TRIM"},
1006 { GNM_FUNC_HELP_END
}
1010 gnumeric_replaceb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1012 char const *old
= value_peek_string (argv
[0]);
1013 gnm_float pos
= value_get_as_float (argv
[1]);
1014 gnm_float len
= value_get_as_float (argv
[2]);
1015 char const *new = value_peek_string (argv
[3]);
1016 int slen
= strlen (old
);
1017 int ipos
, ilen
, newlen
;
1020 if ((len
< 0) || (pos
< 1))
1021 return value_new_error_VALUE (ei
->pos
);
1022 ipos
= (int)MIN ((gnm_float
)INT_MAX
/ 2, pos
) - 1;
1023 ilen
= (int)MIN ((gnm_float
)INT_MAX
/ 2, len
);
1024 if ((ipos
> slen
) ||
1025 (ipos
+ ilen
> slen
) ||
1026 ((gunichar
)-1 == g_utf8_get_char_validated (old
+ ipos
, -1)) ||
1027 ((gunichar
)-1 == g_utf8_get_char_validated (old
+ ipos
+ ilen
, -1)) ||
1028 !g_utf8_validate (old
+ ipos
, ilen
, NULL
))
1029 return value_new_error_VALUE (ei
->pos
);
1030 newlen
= strlen (new);
1031 res
= g_malloc (slen
- ilen
+ newlen
+ 1);
1032 memcpy (res
, old
, ipos
);
1033 memcpy (res
+ ipos
, new, newlen
);
1034 memcpy (res
+ ipos
+ newlen
, old
+ ipos
+ ilen
, slen
- ipos
- ilen
+ 1);
1035 return value_new_string_nocopy (res
);
1038 /***************************************************************************/
1039 /* Note: help_t is a reserved symbol. */
1041 static GnmFuncHelp
const help_t_
[] = {
1042 { GNM_FUNC_HELP_NAME
, F_("T:@{value} if and only if @{value} is text, otherwise empty")},
1043 { GNM_FUNC_HELP_ARG
, F_("value:original value")},
1044 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1045 { GNM_FUNC_HELP_EXAMPLES
, "=T(\"Gnumeric\")" },
1046 { GNM_FUNC_HELP_EXAMPLES
, "=T(64)"},
1047 { GNM_FUNC_HELP_SEEALSO
, "CELL,N,VALUE"},
1048 { GNM_FUNC_HELP_END
}
1051 /* Note: gnumeric_t is a reserved symbol. */
1054 gnumeric_t_ (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1056 if (VALUE_IS_STRING (argv
[0]))
1057 return value_dup (argv
[0]);
1059 return value_new_empty ();
1062 /***************************************************************************/
1064 static GnmFuncHelp
const help_text
[] = {
1065 { GNM_FUNC_HELP_NAME
, F_("TEXT:@{value} as a string formatted as @{format}")},
1066 { GNM_FUNC_HELP_ARG
, F_("value:value to be formatted")},
1067 { GNM_FUNC_HELP_ARG
, F_("format:desired format")},
1068 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1069 { GNM_FUNC_HELP_EXAMPLES
, "=TEXT(3.223,\"$0.00\")" },
1070 { GNM_FUNC_HELP_EXAMPLES
, "=TEXT(date(1999,4,15),\"mmmm, dd, yy\")" },
1071 { GNM_FUNC_HELP_SEEALSO
, "DOLLAR,FIXED,VALUE"},
1072 { GNM_FUNC_HELP_END
}
1076 gnumeric_text (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1078 GnmValue
*res
, *match
= NULL
;
1079 GnmValue
const *v
= argv
[0];
1080 GODateConventions
const *conv
=
1081 sheet_date_conv (ei
->pos
->sheet
);
1084 /* Why do we have to do these here? */
1085 if (VALUE_IS_STRING (v
)) {
1086 match
= format_match (value_peek_string (v
), NULL
, conv
);
1089 } else if (VALUE_IS_EMPTY (v
))
1092 lfmt
= go_format_str_delocalize (value_peek_string (argv
[1]));
1094 GOFormat
*fmt
= go_format_new_from_XL (lfmt
);
1095 GString
*str
= g_string_sized_new (80);
1096 GOFormatNumberError err
;
1099 err
= format_value_gstring (str
, fmt
, v
, -1, conv
);
1101 g_string_free (str
, TRUE
);
1102 res
= value_new_error_VALUE (ei
->pos
);
1104 res
= value_new_string_nocopy (g_string_free (str
, FALSE
));
1106 go_format_unref (fmt
);
1108 res
= value_new_error_VALUE (ei
->pos
);
1111 value_release (match
);
1116 /***************************************************************************/
1118 static GnmFuncHelp
const help_trim
[] = {
1119 { GNM_FUNC_HELP_NAME
, F_("TRIM:@{text} with only single spaces between words")},
1120 { GNM_FUNC_HELP_ARG
, F_("text:string")},
1121 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1122 { GNM_FUNC_HELP_EXAMPLES
, "=TRIM(\" a bbb cc \")" },
1123 { GNM_FUNC_HELP_SEEALSO
, "CLEAN,MID,REPLACE,SUBSTITUTE"},
1124 { GNM_FUNC_HELP_END
}
1128 gnumeric_trim (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1131 GString
*res
= g_string_new (NULL
);
1132 gboolean space
= TRUE
;
1133 size_t last_len
= 0;
1135 s
= value_peek_string (argv
[0]);
1137 gunichar uc
= g_utf8_get_char (s
);
1140 * FIXME: This takes care of tabs and the likes too
1141 * is that the desired behaviour?
1143 if (g_unichar_isspace (uc
)) {
1145 last_len
= res
->len
;
1146 g_string_append_unichar (res
, uc
);
1150 g_string_append_unichar (res
, uc
);
1154 s
= g_utf8_next_char (s
);
1158 g_string_truncate (res
, last_len
);
1160 return value_new_string_nocopy (g_string_free (res
, FALSE
));
1163 /***************************************************************************/
1165 static GnmFuncHelp
const help_value
[] = {
1166 { GNM_FUNC_HELP_NAME
, F_("VALUE:numeric value of @{text}")},
1167 { GNM_FUNC_HELP_ARG
, F_("text:string")},
1168 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1169 { GNM_FUNC_HELP_EXAMPLES
, "=VALUE(\"$1,000\")" },
1170 { GNM_FUNC_HELP_SEEALSO
, "DOLLAR,FIXED,TEXT"},
1171 { GNM_FUNC_HELP_END
}
1175 gnumeric_value (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1177 if (VALUE_IS_EMPTY (argv
[0]) || VALUE_IS_NUMBER (argv
[0]))
1178 return value_dup (argv
[0]);
1181 char const *p
= value_peek_string (argv
[0]);
1183 /* Skip leading spaces */
1184 while (*p
&& g_unichar_isspace (g_utf8_get_char (p
)))
1185 p
= g_utf8_next_char (p
);
1187 v
= format_match_number (p
, NULL
,
1188 sheet_date_conv (ei
->pos
->sheet
));
1192 return value_new_error_VALUE (ei
->pos
);
1196 /***************************************************************************/
1198 static GnmFuncHelp
const help_numbervalue
[] = {
1199 { GNM_FUNC_HELP_NAME
, F_("NUMBERVALUE:numeric value of @{text}")},
1200 { GNM_FUNC_HELP_ARG
, F_("text:string")},
1201 { GNM_FUNC_HELP_ARG
, F_("separator:decimal separator")},
1202 { GNM_FUNC_HELP_NOTE
, F_("If @{text} does not look like a decimal number, "
1203 "NUMBERVALUE returns the value VALUE would "
1204 "return (ignoring the given @{separator}).")},
1205 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.") },
1206 { GNM_FUNC_HELP_EXAMPLES
, "=NUMBERVALUE(\"$1,000\",\",\")" },
1207 { GNM_FUNC_HELP_SEEALSO
, "VALUE"},
1208 { GNM_FUNC_HELP_END
}
1212 gnumeric_numbervalue (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1214 char const *sep
= value_peek_string (argv
[1]);
1215 if (strlen(sep
) != 1 || (*sep
!= '.' && *sep
!= ',')) {
1216 return value_new_error_VALUE (ei
->pos
);
1219 if (VALUE_IS_EMPTY (argv
[0]) || VALUE_IS_NUMBER (argv
[0]))
1220 return value_dup (argv
[0]);
1223 char const *p
= value_peek_string (argv
[0]);
1227 GOFormatFamily family
= GO_FORMAT_GENERAL
;
1229 decimal
= g_string_new (sep
);
1230 thousand
= g_string_new ((*sep
== '.') ? ",":".");
1231 curr
= g_string_new ("$");
1233 /* Skip leading spaces */
1234 while (*p
&& g_unichar_isspace (g_utf8_get_char (p
)))
1235 p
= g_utf8_next_char (p
);
1237 v
= format_match_decimal_number_with_locale
1238 (p
, &family
, curr
, thousand
, decimal
);
1240 g_string_free (decimal
, TRUE
);
1241 g_string_free (thousand
, TRUE
);
1242 g_string_free (curr
, TRUE
);
1245 v
= format_match_number
1247 sheet_date_conv (ei
->pos
->sheet
));
1251 return value_new_error_VALUE (ei
->pos
);
1255 /***************************************************************************/
1257 static GnmFuncHelp
const help_substitute
[] = {
1258 { GNM_FUNC_HELP_NAME
, F_("SUBSTITUTE:@{text} with all occurrences of @{old} replaced by @{new}")},
1259 { GNM_FUNC_HELP_ARG
, F_("text:original text")},
1260 { GNM_FUNC_HELP_ARG
, F_("old:string to be replaced")},
1261 { GNM_FUNC_HELP_ARG
, F_("new:replacement string")},
1262 { GNM_FUNC_HELP_ARG
, F_("num:if @{num} is specified and a number "
1263 "only the @{num}th occurrence of @{old} is replaced")},
1264 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1265 { GNM_FUNC_HELP_EXAMPLES
, "=SUBSTITUTE(\"United Nations Educational, Scientific and Cultural Organization\",\"ation\",\"-5-\")" },
1266 { GNM_FUNC_HELP_EXAMPLES
, "=SUBSTITUTE(\"United Nations Educational, Scientific and Cultural Organization\",\"ation\",\"-5-\",2)" },
1267 { GNM_FUNC_HELP_SEEALSO
, "REPLACE,TRIM"},
1268 { GNM_FUNC_HELP_END
}
1272 gnumeric_substitute (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1275 * Careful: value_peek_string handle only two live
1276 * pointers at a time.
1278 char *textcopy
= VALUE_IS_STRING (argv
[0]) ? NULL
: value_get_as_string (argv
[0]);
1279 char const *text
= textcopy
? textcopy
: value_peek_string (argv
[0]);
1280 char const *old
= value_peek_string (argv
[1]);
1281 char const *new = value_peek_string (argv
[2]);
1283 int oldlen
, newlen
, len
, inst
;
1288 gnm_float fnum
= value_get_as_float (argv
[3]);
1291 return value_new_error_VALUE (ei
->pos
);
1293 num
= (int)MIN((gnm_float
)INT_MAX
, fnum
);
1296 oldlen
= strlen (old
);
1299 ? value_new_string_nocopy (textcopy
)
1300 : value_dup (argv
[0]);
1302 newlen
= strlen (new);
1303 len
= strlen (text
);
1304 s
= g_string_sized_new (len
);
1308 while (p
- text
< len
) {
1309 char const *f
= strstr (p
, old
);
1313 g_string_append_len (s
, p
, f
- p
);
1317 if (num
== 0 || num
== inst
) {
1318 g_string_append_len (s
, new, newlen
);
1322 g_string_append_len (s
, old
, oldlen
);
1324 g_string_append (s
, p
);
1326 return value_new_string_nocopy (g_string_free (s
, FALSE
));
1329 /***************************************************************************/
1331 static GnmFuncHelp
const help_dollar
[] = {
1332 { GNM_FUNC_HELP_NAME
, F_("DOLLAR:@{num} formatted as currency")},
1333 { GNM_FUNC_HELP_ARG
, F_("num:number")},
1334 { GNM_FUNC_HELP_ARG
, F_("decimals:decimals")},
1335 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1336 { GNM_FUNC_HELP_EXAMPLES
, "=DOLLAR(12345)" },
1337 { GNM_FUNC_HELP_SEEALSO
, "FIXED,TEXT,VALUE"},
1338 { GNM_FUNC_HELP_END
}
1342 gnumeric_dollar (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1348 gnm_float number
= value_get_as_float (argv
[0]);
1349 gnm_float decimals
= argv
[1] ? value_get_as_float (argv
[1]) : 2.0;
1350 gboolean precedes
, space_sep
;
1351 const GString
*curr
= go_locale_get_currency (&precedes
, &space_sep
);
1354 /* This is what Excel appears to do. */
1355 if (decimals
>= 128)
1356 return value_new_error_VALUE (ei
->pos
);
1357 decimals
= gnm_fake_trunc (decimals
);
1359 /* Since decimals can be negative, round the number. */
1360 p10
= gnm_pow10 (decimals
);
1362 number
= 0; /* Underflow. */
1364 number
= gnm_fake_round (number
* p10
) / p10
;
1366 fmt_str
= g_string_sized_new (150);
1368 g_string_append_c (fmt_str
, '"');
1369 go_string_append_gstring (fmt_str
, curr
);
1370 g_string_append (fmt_str
, space_sep
? "\" " : "\"");
1372 g_string_append (fmt_str
, "#,##0");
1374 g_string_append_c (fmt_str
, '.');
1375 go_string_append_c_n (fmt_str
, '0', (int)decimals
);
1378 g_string_append (fmt_str
, space_sep
? " \"" : "\"");
1379 go_string_append_gstring (fmt_str
, curr
);
1380 g_string_append_c (fmt_str
, '"');
1383 /* No color and no space-for-parenthesis. */
1384 g_string_append (fmt_str
, ";(");
1385 g_string_append_len (fmt_str
, fmt_str
->str
, fmt_str
->len
- 2);
1386 g_string_append_c (fmt_str
, ')');
1388 sf
= go_format_new_from_XL (fmt_str
->str
);
1390 v
= value_new_float (number
);
1391 s
= format_value (sf
, v
, -1,
1392 sheet_date_conv (ei
->pos
->sheet
));
1394 go_format_unref (sf
);
1396 g_string_free (fmt_str
, TRUE
);
1398 return value_new_string_nocopy (s
);
1401 /***************************************************************************/
1403 static GnmFuncHelp
const help_search
[] = {
1404 { GNM_FUNC_HELP_NAME
, F_("SEARCH:the location of the @{search} string within "
1405 "@{text} after position @{start}")},
1406 { GNM_FUNC_HELP_ARG
, F_("search:search string")},
1407 { GNM_FUNC_HELP_ARG
, F_("text:search field")},
1408 { GNM_FUNC_HELP_ARG
, F_("start:starting position, defaults to 1")},
1409 { GNM_FUNC_HELP_DESCRIPTION
, F_("@{search} may contain wildcard characters (*) and "
1410 "question marks (?). A question mark matches any "
1411 "single character, and a wildcard matches any "
1412 "string including the empty string. To search for "
1413 "* or ?, precede the symbol with ~.")},
1414 { GNM_FUNC_HELP_NOTE
, F_("This search is not case sensitive.") },
1415 { GNM_FUNC_HELP_NOTE
, F_("If @{search} is not found, SEARCH returns #VALUE!") },
1416 { GNM_FUNC_HELP_NOTE
, F_("If @{start} is less than one or it is greater than "
1417 "the length of @{text}, SEARCH returns #VALUE!") },
1418 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
1419 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCH(\"c\",\"Canc\xc3\xban\")" },
1420 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCH(\"c\",\"Canc\xc3\xban\",2)" },
1421 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCH(\"c*c\",\"Canc\xc3\xban\")" },
1422 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCH(\"c*c\",\"Canc\xc3\xban\",2)" },
1423 { GNM_FUNC_HELP_SEEALSO
, "FIND,SEARCHB"},
1424 { GNM_FUNC_HELP_END
}
1428 gnumeric_search (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1430 char const *needle
= value_peek_string (argv
[0]);
1431 char const *haystack
= value_peek_string (argv
[1]);
1432 gnm_float start
= argv
[2] ? value_get_as_float (argv
[2]) : 1.0;
1435 if (start
< 1 || start
>= INT_MAX
)
1436 return value_new_error_VALUE (ei
->pos
);
1438 res
= gnm_excel_search_impl (needle
, haystack
, (int)start
- 1);
1440 ? value_new_error_VALUE (ei
->pos
)
1441 : value_new_int (1 + res
);
1444 /***************************************************************************/
1446 static GnmFuncHelp
const help_searchb
[] = {
1447 { GNM_FUNC_HELP_NAME
, F_("SEARCHB:the location of the @{search} string within "
1448 "@{text} after byte position @{start}")},
1449 { GNM_FUNC_HELP_ARG
, F_("search:search string")},
1450 { GNM_FUNC_HELP_ARG
, F_("text:search field")},
1451 { GNM_FUNC_HELP_ARG
, F_("start:starting byte position, defaults to 1")},
1452 { GNM_FUNC_HELP_DESCRIPTION
, F_("@{search} may contain wildcard characters (*) and "
1453 "question marks (?). A question mark matches any "
1454 "single character, and a wildcard matches any "
1455 "string including the empty string. To search for "
1456 "* or ?, precede the symbol with ~.")},
1457 { GNM_FUNC_HELP_NOTE
, F_("This search is not case sensitive.") },
1458 { GNM_FUNC_HELP_NOTE
, F_("If @{search} is not found, SEARCHB returns #VALUE!") },
1459 { GNM_FUNC_HELP_NOTE
, F_("If @{start} is less than one or it is greater than "
1460 "the byte length of @{text}, SEARCHB returns #VALUE!") },
1461 { GNM_FUNC_HELP_NOTE
, F_("The semantics of this function is subject to change as various applications implement it.")},
1462 { GNM_FUNC_HELP_EXCEL
, F_("While this function is syntactically Excel compatible, "
1463 "the differences in the underlying text encoding will usually yield different results.")},
1464 { GNM_FUNC_HELP_ODF
, F_("While this function is OpenFormula compatible, most of its behavior is, at this time, implementation specific.")},
1465 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCHB(\"n\",\"Canc\xc3\xban\")" },
1466 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCHB(\"n\",\"Canc\xc3\xban\",4)" },
1467 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCHB(\"n\",\"Canc\xc3\xban\",6)" },
1468 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCHB(\"n*n\",\"Canc\xc3\xban\")" },
1469 { GNM_FUNC_HELP_EXAMPLES
, "=SEARCHB(\"n*n\",\"Canc\xc3\xban\",4)" },
1470 { GNM_FUNC_HELP_SEEALSO
, "FINDB,SEARCH"},
1471 { GNM_FUNC_HELP_END
}
1475 gnumeric_searchb (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1477 char const *needle
= value_peek_string (argv
[0]);
1478 char const *haystack
= value_peek_string (argv
[1]);
1479 gnm_float start
= argv
[2] ? value_get_as_float (argv
[2]) : 1.0;
1483 if (start
< 1 || start
>= INT_MAX
|| start
> strlen (haystack
))
1484 return value_new_error_VALUE (ei
->pos
);
1485 /* Make istart zero-based. */
1486 istart
= (int)(start
- 1);
1489 istart
= g_utf8_next_char(haystack
+ istart
- 1) - haystack
;
1491 if (gnm_regcomp_XL (&r
, needle
, GO_REG_ICASE
, FALSE
, FALSE
) == GO_REG_OK
) {
1494 switch (go_regexec (&r
, haystack
+ istart
, 1, &rm
, 0)) {
1495 case GO_REG_NOMATCH
:
1499 return value_new_int
1500 (1 + istart
+ rm
.rm_so
);
1502 g_warning ("Unexpected go_regexec result");
1506 g_warning ("Unexpected regcomp result");
1509 return value_new_error_VALUE (ei
->pos
);
1512 /***************************************************************************/
1514 static GnmFuncHelp
const help_asc
[] = {
1515 { GNM_FUNC_HELP_NAME
, F_("ASC:text with full-width katakana and ASCII characters converted to half-width")},
1516 { GNM_FUNC_HELP_ARG
, F_("text:string")},
1517 { GNM_FUNC_HELP_DESCRIPTION
, F_("ASC converts full-width katakana and ASCII characters to half-width equivalent characters, copying all others.")},
1518 { GNM_FUNC_HELP_DESCRIPTION
, F_("The distinction between half-width and full-width characters is described in http://www.unicode.org/reports/tr11/.")},
1519 { GNM_FUNC_HELP_EXCEL
, F_("For most strings, this function has the same effect as in Excel.")},
1520 { GNM_FUNC_HELP_NOTE
, F_("While in obsolete encodings ASC used to translate between 2-byte and 1-byte characters, this is not the case in UTF-8.")},
1521 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.")},
1522 { GNM_FUNC_HELP_EXAMPLES
, "=ASC(\"\xef\xbc\xa1\xef\xbc\xa2\xef\xbc\xa3\")"},
1523 { GNM_FUNC_HELP_SEEALSO
, "JIS"},
1524 { GNM_FUNC_HELP_END
}
1528 gnm_asc_half (gunichar c
, GString
*str
)
1556 if (0x30a1 <= c
&& c
<= 0x30aa)
1557 return (c
%2 == 0) ? ((c
- 0x30a2)/2 + 0xff71)
1558 : ((c
- 0x30a1)/2 + 0xff67);
1561 return ((c
- 0x30ab)/2 + 0xff76);
1563 g_string_append_unichar
1564 (str
, (c
- 0x30ac)/2 + 0xff76);
1572 return ((c
- 0x30c4)/2 + 0xff82);
1574 g_string_append_unichar
1575 (str
, (c
- 0x30c5)/2 + 0xff82);
1580 return (c
- 0x30ca + 0xff85);
1584 return ((c
- 0x30cf)/3 + 0xff8a);
1586 g_string_append_unichar
1587 (str
, (c
- 0x30d0)/3 + 0xff8a);
1591 g_string_append_unichar
1592 (str
, (c
- 0x30d1)/3 + 0xff8a);
1596 return (c
- 0x30de + 0xff8f);
1599 return ((c
- 0x30e4)/2 + 0xff94);
1601 return ((c
- 0x30e3)/2 + 0xff6c);
1604 return (c
- 0x30e9 + 0xff97);
1618 return (c
- 0xff01 + 0x0021);
1625 gnumeric_asc (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1627 char const *peek
= value_peek_string (argv
[0]);
1628 GString
*str
= g_string_new (NULL
);
1630 while ((*peek
) != '\0') {
1631 gunichar wc
= gnm_asc_half (g_utf8_get_char (peek
), str
);
1632 g_string_append_unichar (str
, wc
);
1633 peek
= g_utf8_next_char(peek
);
1636 return value_new_string_nocopy (g_string_free (str
, FALSE
));
1639 /***************************************************************************/
1641 static GnmFuncHelp
const help_jis
[] = {
1642 { GNM_FUNC_HELP_NAME
, F_("JIS:text with half-width katakana and ASCII characters converted to full-width")},
1643 { GNM_FUNC_HELP_ARG
, F_("text:original text")},
1644 { GNM_FUNC_HELP_DESCRIPTION
, F_("JIS converts half-width katakana and ASCII characters "
1645 "to full-width equivalent characters, copying all others.")},
1646 { GNM_FUNC_HELP_DESCRIPTION
, F_("The distinction between half-width and full-width characters "
1647 "is described in http://www.unicode.org/reports/tr11/.")},
1648 { GNM_FUNC_HELP_EXCEL
, F_("For most strings, this function has the same effect as in Excel.")},
1649 { GNM_FUNC_HELP_NOTE
, F_("While in obsolete encodings JIS used to translate between 1-byte "
1650 "and 2-byte characters, this is not the case in UTF-8.")},
1651 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.")},
1652 { GNM_FUNC_HELP_EXAMPLES
, "=JIS(\"ABC\")"},
1653 { GNM_FUNC_HELP_SEEALSO
, "ASC"},
1654 { GNM_FUNC_HELP_END
}
1658 gnm_asc_full (gunichar c
, gunichar fc
)
1671 return (c
- 0x0021 + 0xff01);
1687 return ((c
- 0xff67)*2 + 0x30a1);
1689 return ((c
- 0xff6c)*2 + 0x30e3);
1695 return ((c
- 0xff71)*2 + 0x30a2);
1698 return ((c
- 0xff76)*2 + 0x30ac);
1700 return ((c
- 0xff76)*2 + 0x30ab);
1704 return ((c
- 0xff82)*2 + 0x30c5);
1706 return ((c
- 0xff82)*2 + 0x30c4);
1709 return ((c
- 0xff85)*2 + 0x30ca);
1712 return ((c
- 0xff8a)*3 + 0x30d0);
1713 else if (fc
== 0xff9f)
1714 return ((c
- 0xff8a)*3 + 0x30d1);
1716 return ((c
- 0xff8a)*3 + 0x30cf);
1719 return (c
- 0xff8f + 0x30de);
1721 return ((c
- 0xff94)*2 + 0x30e4);
1723 return (c
- 0xff97 + 0x30e9);
1736 gnumeric_jis (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1738 char const *peek
= value_peek_string (argv
[0]);
1739 GString
*str
= g_string_new (NULL
);
1740 gunichar tc
= g_utf8_get_char (peek
);
1742 while ((*peek
) != '\0') {
1744 char const *next
= g_utf8_next_char(peek
);
1745 fc
= g_utf8_get_char (next
);
1746 g_string_append_unichar (str
, gnm_asc_full (tc
, fc
));
1751 return value_new_string_nocopy (g_string_free (str
, FALSE
));
1753 /***************************************************************************/
1754 GnmFuncDescriptor
const string_functions
[] = {
1755 { "asc", "s", help_asc
,
1757 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1758 { "char", "f", help_char
,
1759 gnumeric_char
, NULL
,
1760 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1761 { "unichar", "f", help_unichar
,
1762 gnumeric_unichar
, NULL
,
1763 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_BASIC
},
1764 { "clean", "S", help_clean
,
1765 gnumeric_clean
, NULL
,
1766 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1767 { "code", "S", help_code
,
1768 gnumeric_code
, NULL
,
1769 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1770 { "unicode", "S", help_unicode
,
1771 gnumeric_unicode
, NULL
,
1772 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_BASIC
},
1773 { "concat", NULL
, help_concat
,
1774 NULL
, gnumeric_concat
,
1775 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1776 { "concatenate", NULL
, help_concatenate
,
1777 NULL
, gnumeric_concatenate
,
1778 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1779 { "dollar", "f|f", help_dollar
,
1780 gnumeric_dollar
, NULL
,
1781 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1782 { "exact", "SS", help_exact
,
1783 gnumeric_exact
, NULL
,
1784 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1785 { "find", "SS|f", help_find
,
1786 gnumeric_find
, NULL
,
1787 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1788 { "findb", "SS|f", help_findb
,
1789 gnumeric_findb
, NULL
,
1790 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1791 { "fixed", "f|fb", help_fixed
,
1792 gnumeric_fixed
, NULL
,
1793 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1794 { "jis", "s", help_jis
,
1796 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1797 { "left", "S|f", help_left
,
1798 gnumeric_left
, NULL
,
1799 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1800 { "leftb", "S|f", help_leftb
,
1801 gnumeric_leftb
, NULL
,
1802 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1803 { "len", "S", help_len
,
1805 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1806 { "lenb", "S", help_lenb
,
1807 gnumeric_lenb
, NULL
,
1808 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1809 { "lower", "S", help_lower
,
1810 gnumeric_lower
, NULL
,
1811 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1812 { "mid", "Sff", help_mid
,
1814 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1815 { "midb", "Sff", help_midb
,
1816 gnumeric_midb
, NULL
,
1817 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1818 { "numbervalue", "SS", help_numbervalue
,
1819 gnumeric_numbervalue
, NULL
,
1820 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_BASIC
},
1821 { "proper", "S", help_proper
,
1822 gnumeric_proper
, NULL
,
1823 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1824 { "replace", "SffS", help_replace
,
1825 gnumeric_replace
, NULL
,
1826 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1827 { "replaceb", "SffS", help_replaceb
,
1828 gnumeric_replaceb
, NULL
,
1829 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1830 { "rept", "Sf", help_rept
,
1831 gnumeric_rept
, NULL
,
1832 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1833 { "right", "S|f", help_right
,
1834 gnumeric_right
, NULL
,
1835 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1836 { "rightb", "S|f", help_rightb
,
1837 gnumeric_rightb
, NULL
,
1838 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1839 { "search", "SS|f", help_search
,
1840 gnumeric_search
, NULL
,
1841 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1842 { "searchb", "SS|f", help_searchb
,
1843 gnumeric_searchb
, NULL
,
1844 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1845 { "substitute", "SSS|f", help_substitute
,
1846 gnumeric_substitute
, NULL
,
1847 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1848 { "t", "S", help_t_
,
1850 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1851 { "text", "Ss", help_text
,
1852 gnumeric_text
, NULL
,
1853 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1854 { "textjoin", NULL
, help_textjoin
,
1855 NULL
, gnumeric_textjoin
,
1856 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1857 { "trim", "S", help_trim
,
1858 gnumeric_trim
, NULL
,
1859 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1860 { "upper", "S", help_upper
,
1861 gnumeric_upper
, NULL
,
1862 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1863 { "value", "S", help_value
,
1864 gnumeric_value
, NULL
,
1865 GNM_FUNC_SIMPLE
, GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1871 G_MODULE_EXPORT
void
1872 go_plugin_init (GOPlugin
*plugin
, GOCmdContext
*cc
)
1874 int codepage
= gsf_msole_iconv_win_codepage ();
1875 CHAR_iconv
= gsf_msole_iconv_open_for_import (codepage
);
1876 CODE_iconv
= gsf_msole_iconv_open_for_export ();
1879 G_MODULE_EXPORT
void
1880 go_plugin_shutdown (GOPlugin
*plugin
, GOCmdContext
*cc
)
1882 gsf_iconv_close (CHAR_iconv
);
1883 gsf_iconv_close (CODE_iconv
);