2 * number-match.c: This file includes the support for matching
3 * entered strings as numbers (by trying to apply one of the existing
6 * The idea is simple: we create a regular expression from the format
7 * string that would match a value entered in that format. Then, on
8 * lookup we try to match the string against every regular expression
9 * we have: if a match is found, then we decode the number using a
10 * precomputed parallel-list of subexpressions.
13 * Morten Welinder (terra@gnome.org)
14 * Miguel de Icaza (miguel@gnu.org)
16 #include <gnumeric-config.h>
17 #include <glib/gi18n-lib.h>
19 #include <number-match.h>
23 #include <gnm-format.h>
27 #include <gnm-datetime.h>
28 #include <goffice/goffice.h>
36 #undef DEBUG_NUMBER_MATCH
39 * value_is_error: Check to see if a string begins with one of the magic
42 * @str: The string to test
44 * Returns: an error if there is one, or %NULL.
47 value_is_error (char const *str
)
54 for (e
= (GnmStdError
)0; e
< GNM_ERROR_UNKNOWN
; e
++)
55 if (0 == strcmp (str
, value_error_name (e
, TRUE
)))
56 return value_new_error_std (NULL
, e
);
62 * format_match_simple:
63 * @text: A String to match against.
65 * Attempt to match the supplied string as a simple value.
67 * WARNING WARNING WARNING : This routine should NEVER be changed to match
68 * VALUE_STRING that will break the parsers
69 * handling of named expressions.
72 format_match_simple (char const *text
)
74 /* Is it a boolean? */
75 if (0 == g_ascii_strcasecmp (text
, go_locale_boolean_name (TRUE
)))
76 return value_new_bool (TRUE
);
77 if (0 == g_ascii_strcasecmp (text
, go_locale_boolean_name (FALSE
)))
78 return value_new_bool (FALSE
);
82 GnmValue
*err
= value_is_error (text
);
87 /* Is it a floating-point number */
92 d
= gnm_utf8_strto (text
, &end
);
93 if (text
!= end
&& errno
!= ERANGE
&& gnm_finite (d
)) {
94 /* Allow and ignore spaces at the end. */
95 while (g_ascii_isspace (*end
))
98 return value_new_float (d
);
107 GORegexp re_MMMMddyyyy
;
108 GORegexp re_ddMMMMyyyy
;
109 GORegexp re_yyyymmdd1
;
110 GORegexp re_yyyymmdd2
;
111 GORegexp re_mmddyyyy
;
115 GORegexp re_hhmmssds
;
116 GORegexp re_hhmmss_ampm
;
121 datetime_locale_clear (void)
123 g_free (datetime_locale
.lc_time
);
124 go_regfree (&datetime_locale
.re_MMMMddyyyy
);
125 go_regfree (&datetime_locale
.re_ddMMMMyyyy
);
126 go_regfree (&datetime_locale
.re_yyyymmdd1
);
127 go_regfree (&datetime_locale
.re_yyyymmdd2
);
128 go_regfree (&datetime_locale
.re_mmddyyyy
);
129 go_regfree (&datetime_locale
.re_mmdd
);
130 go_regfree (&datetime_locale
.re_hhmmss1
);
131 go_regfree (&datetime_locale
.re_hhmmss2
);
132 go_regfree (&datetime_locale
.re_hhmmssds
);
133 go_regfree (&datetime_locale
.re_hhmmss_ampm
);
134 memset (&datetime_locale
, 0, sizeof (datetime_locale
));
138 my_regerror (int err
, GORegexp
const *preg
)
140 static char buffer
[1024];
141 go_regerror (err
, preg
, buffer
, sizeof (buffer
));
146 datetime_locale_setup1 (GORegexp
*rx
, char const *pat
)
148 int ret
= go_regcomp (rx
, pat
, GO_REG_ICASE
);
150 g_warning ("Failed to compile rx \"%s\": %s\n",
152 my_regerror (ret
, rx
));
158 datetime_locale_setup (char const *lc_time
)
160 GString
*p_MMMM
= g_string_sized_new (200);
161 GString
*p_MMM
= g_string_sized_new (200);
162 GString
*p_decimal
= g_string_sized_new (10);
166 datetime_locale
.lc_time
= g_strdup (lc_time
);
168 for (m
= 1; m
<= 12; m
++) {
170 g_string_append_c (p_MMMM
, '|');
171 g_string_append_c (p_MMMM
, '(');
172 s
= go_date_month_name (m
, FALSE
);
173 go_regexp_quote (p_MMMM
, s
);
175 g_string_append_c (p_MMMM
, ')');
178 g_string_append_c (p_MMM
, '|');
179 g_string_append_c (p_MMM
, '(');
180 s
= go_date_month_name (m
, TRUE
);
181 go_regexp_quote (p_MMM
, s
);
182 /* nb_NO actually adds a "." for these abbreviations. */
183 if (g_unichar_ispunct (g_utf8_get_char (g_utf8_prev_char (p_MMM
->str
+ p_MMM
->len
))))
184 g_string_append_c (p_MMM
, '?');
186 g_string_append_c (p_MMM
, ')');
189 go_regexp_quote (p_decimal
, go_locale_get_decimal ()->str
);
204 s
= g_strconcat ("^(",
208 ")(-|/|\\s)(\\d+)((,?\\s+|-|/)(\\d+))?\\b",
210 datetime_locale_setup1 (&datetime_locale
.re_MMMMddyyyy
, s
);
218 * "1. december, 2000"
224 s
= g_strconcat ("^(\\d+)(-|/|\\.?\\s*)(",
228 ")((,?\\s*|-|/)(\\d+))?\\b",
230 datetime_locale_setup1 (&datetime_locale
.re_ddMMMMyyyy
, s
);
235 * (with special support for 20001231:123456)
237 s
= g_strconcat ("^(\\d\\d\\d\\d)(\\d\\d)(\\d\\d)(:\\d\\d\\d\\d\\d\\d(",
241 datetime_locale_setup1 (&datetime_locale
.re_yyyymmdd1
, s
);
248 datetime_locale_setup1 (&datetime_locale
.re_yyyymmdd2
,
249 "^(\\d\\d\\d\\d)[-/.](\\d+)[-/.](\\d+)\\b");
252 * "01/31/2001" [Jan 31] if month_before_day
253 * "1/2/88" [Jan 2] if month_before_day
254 * "1/2/88" [Feb 1] if !month_before_day
255 * "31/1/2001" [Jan 31] if !month_before_day
257 datetime_locale_setup1 (&datetime_locale
.re_mmddyyyy
,
258 "^(\\d+)[-/.](\\d+)[-/.](\\d+)\\b");
263 * "01/31" [Jan 31] if month_before_day
264 * "31/1" [Jan 31] if !month_before_day
266 datetime_locale_setup1 (&datetime_locale
.re_mmdd
,
267 "^(\\d+)([-/.])(\\d+)\\b");
271 * "30:00.3" [A little more than 30min]
274 /* ^(((\d+):)?(\d+):)?(\d+.\d*)\s*$ */
275 s
= g_strconcat ("^(((\\d+):)?(\\d+):)?(\\d+",
279 datetime_locale_setup1 (&datetime_locale
.re_hhmmssds
, s
);
284 * "15:30" [15:30:00] if prefer_hour
285 * "15:30" [00:15:30] if !prefer_hour
287 datetime_locale_setup1 (&datetime_locale
.re_hhmmss1
,
288 "^(\\d+):(\\d+)(:(\\d+))?\\s*$");
294 s
= g_strconcat ("^(\\d\\d)(\\d\\d)(\\d\\d)?(",
298 datetime_locale_setup1 (&datetime_locale
.re_hhmmss2
, s
);
307 s
= g_strconcat ("^(\\d+)(:(\\d+)(:(\\d+(",
309 "\\d*)?))?)?\\s*((am)|(pm))\\s*$",
311 datetime_locale_setup1 (&datetime_locale
.re_hhmmss_ampm
, s
);
314 g_string_free (p_MMMM
, TRUE
);
315 g_string_free (p_MMM
, TRUE
);
316 g_string_free (p_decimal
, TRUE
);
320 find_month (GORegmatch
const *pm
)
324 for (m
= 1; m
<= 12; m
++) {
325 if (pm
->rm_so
!= pm
->rm_eo
)
334 handle_int (char const *text
, GORegmatch
const *pm
, int min
, int max
, int maxlen
)
337 char const *p
= text
+ pm
->rm_so
;
338 char const *end
= text
+ pm
->rm_eo
;
342 gunichar uc
= g_utf8_get_char (p
);
343 p
= g_utf8_next_char (p
);
344 i
= (10 * i
) + g_unichar_digit_value (uc
);
347 if (i
> max
|| len
> maxlen
)
358 handle_day (char const *text
, GORegmatch
const *pm
)
360 return handle_int (text
, pm
, 1, 31, 2);
364 handle_month (char const *text
, GORegmatch
const *pm
)
366 return handle_int (text
, pm
, 1, 12, 2);
372 time_t now
= time (NULL
);
373 struct tm
*tm
= localtime (&now
);
374 return 1900 + tm
->tm_year
;
378 handle_year (char const *text
, GORegmatch
const *pm
)
382 if (pm
->rm_so
== pm
->rm_eo
)
383 return current_year ();
385 y
= handle_int (text
, pm
, 0, 9999, 4);
393 else if (y
< (gnm_datetime_allow_negative () ? 1582 : 1900))
401 handle_float (char const *text
, GORegmatch
const *pm
)
408 /* Empty means zero. */
409 if (pm
->rm_so
== pm
->rm_eo
)
412 p
= text
+ pm
->rm_so
;
413 end
= text
+ pm
->rm_eo
;
415 gunichar uc
= g_utf8_get_char (p
);
416 int d
= g_unichar_digit_value (uc
);
417 p
= g_utf8_next_char (p
);
418 if (d
< 0) break; /* Must be decimal sep. */
419 val
= (10 * val
) + d
;
423 gunichar uc
= g_utf8_get_char (p
);
424 int d
= g_unichar_digit_value (uc
);
425 p
= g_utf8_next_char (p
);
434 fixup_hour_ampm (gnm_float
*hour
, const GORegmatch
*pm
)
436 gboolean is_am
= (pm
->rm_so
!= pm
->rm_eo
);
438 if (*hour
< 1 || *hour
> 12) {
450 valid_hms (gnm_float h
, gnm_float m
, gnm_float s
,
451 gboolean allow_elapsed
, char *elapsed
)
453 gboolean h_ok
= h
>= 0 && h
< 24;
454 gboolean m_ok
= m
>= 0 && m
< 60;
455 gboolean s_ok
= s
>= 0 && s
< 60;
457 /* Boring old clock time. */
458 if (h_ok
&& m_ok
&& s_ok
) {
467 if (*elapsed
== 'h' && m_ok
&& s_ok
)
470 if (*elapsed
== 'm' && h
== 0 && s_ok
)
473 if (*elapsed
== 's' && h
== 0 && m
== 0)
479 #define DO_SIGN(sign,uc,action) \
481 if (uc == '-' || uc == UNICODE_MINUS_SIGN_C) { \
484 } else if (uc == '+') { \
490 #define SKIP_DIGITS(text) while (g_ascii_isdigit (*(text))) (text)++
492 #define SKIP_SPACES(text) \
493 while (*(text) && g_unichar_isspace (g_utf8_get_char (text))) \
494 (text) = g_utf8_next_char (text)
498 format_match_time (char const *text
, gboolean allow_elapsed
,
499 gboolean prefer_hour
, gboolean add_format
)
503 gnm_float hour
, minute
, second
;
505 GORegmatch match
[10];
506 char const *time_format
= NULL
;
511 /* AM/PM means hour is needed. No sign allowed. */
512 /* ^(\d+)(:(\d+)(:(\d+(.\d*)?))?)?\s*((am)|(pm))\s*$ */
513 /* 1 2 3 4 5 6 78 9 */
514 if (go_regexec (&datetime_locale
.re_hhmmss_ampm
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
515 hour
= handle_float (text
, match
+ 1);
516 fixup_hour_ampm (&hour
, match
+ 8);
517 minute
= handle_float (text
, match
+ 3);
518 second
= handle_float (text
, match
+ 5);
519 if (valid_hms (hour
, minute
, second
, FALSE
, NULL
)) {
520 time_format
= "h:mm:ss AM/PM";
525 uc
= g_utf8_get_char (text
);
528 text
= g_utf8_next_char (text
);
532 /* If fractional seconds are present, we know the layout. */
533 /* ^(((\d+):)?(\d+):)?(\d+.\d*)\s*$ */
535 if (go_regexec (&datetime_locale
.re_hhmmssds
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
537 match
[3].rm_so
!= match
[3].rm_eo
539 : (match
[4].rm_so
!= match
[4].rm_eo
543 hour
= handle_float (text
, match
+ 3);
544 minute
= handle_float (text
, match
+ 4);
545 second
= handle_float (text
, match
+ 5);
547 if (valid_hms (hour
, minute
, second
, allow_elapsed
, &elapsed
)) {
548 time_format
= elapsed
? "[h]:mm:ss" : "h:mm:ss";
553 /* ^(\d+):(\d+)(:(\d+))?\s*$ */
555 if (go_regexec (&datetime_locale
.re_hhmmss1
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
556 gboolean has_all
= (match
[4].rm_so
!= match
[4].rm_eo
);
558 const char *time_format_elapsed
;
560 if (prefer_hour
|| has_all
) {
561 hour
= handle_float (text
, match
+ 1);
562 minute
= handle_float (text
, match
+ 2);
563 second
= handle_float (text
, match
+ 4);
564 time_format
= has_all
? "h:mm:ss" : "h:mm";
565 time_format_elapsed
= has_all
? "[h]:mm:ss" : "[h]:mm";
569 minute
= handle_float (text
, match
+ 1);
570 second
= handle_float (text
, match
+ 2);
571 time_format
= "mm:ss";
572 time_format_elapsed
= "[m]:ss";
576 if (valid_hms (hour
, minute
, second
, allow_elapsed
, &elapsed
)) {
578 time_format
= time_format_elapsed
;
583 /* ^(\d\d)(\d\d)(\d\d)?(\.\d*)?\s*$ */
585 if (go_regexec (&datetime_locale
.re_hhmmss2
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
586 gboolean has3
= (match
[3].rm_so
!= match
[3].rm_eo
);
587 gboolean hasfrac
= (match
[4].rm_so
!= match
[4].rm_eo
);
589 const char *time_format_elapsed
;
591 if ((prefer_hour
&& !hasfrac
) || has3
) {
592 hour
= handle_float (text
, match
+ 1);
593 minute
= handle_float (text
, match
+ 2);
594 second
= handle_float (text
, match
+ 3) + handle_float (text
, match
+ 4);
595 time_format
= "h:mm:ss";
596 time_format_elapsed
= "[h]:mm:ss";
600 minute
= handle_float (text
, match
+ 1);
601 second
= handle_float (text
, match
+ 2) + handle_float (text
, match
+ 4);
602 time_format
= "mm:ss";
603 time_format_elapsed
= "[m]:ss";
607 if (valid_hms (hour
, minute
, second
, allow_elapsed
, &elapsed
)) {
609 time_format
= time_format_elapsed
;
617 time_val
= (second
+ 60 * (minute
+ 60 * hour
)) / (24 * 60 * 60);
619 time_val
= 0 - time_val
;
620 v
= value_new_float (time_val
);
623 GOFormat
*fmt
= go_format_new_from_XL (time_format
);
624 value_set_fmt (v
, fmt
);
625 go_format_unref (fmt
);
632 valid_dmy (int d
, int m
, int y
)
634 /* Avoid sign-induced problem. d and m are capped. */
635 return y
>= 0 && g_date_valid_dmy (d
, m
, y
);
640 format_match_datetime (char const *text
,
641 GODateConventions
const *date_conv
,
642 gboolean month_before_day
,
644 gboolean presume_date
)
646 int day
, month
, year
;
648 gnm_float time_val
, date_val
;
649 char const *lc_time
= setlocale (LC_TIME
, NULL
);
650 GORegmatch match
[31];
653 char *date_format
= NULL
;
654 GnmValue
*res
= NULL
;
655 char *time_format
= NULL
;
657 if (lc_time
!= datetime_locale
.lc_time
&&
659 datetime_locale
.lc_time
== NULL
||
660 strcmp (lc_time
, datetime_locale
.lc_time
))) {
661 datetime_locale_clear ();
662 datetime_locale_setup (lc_time
);
666 uc
= g_utf8_get_char (text
);
667 dig1
= g_unichar_digit_value (uc
);
669 /* ^(MMMM)(-|/|\s)(\d+)((,\s+|-|/)(\d+))?\b */
673 go_regexec (&datetime_locale
.re_MMMMddyyyy
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
674 month
= find_month (&match
[2]);
675 if (month
== -1) month
= find_month (&match
[2 + 12]);
676 day
= handle_day (text
, match
+ 27);
678 match
[27].rm_eo
- match
[27].rm_so
>= 4 &&
679 match
[28].rm_so
== match
[28].rm_eo
) {
680 /* Only one number with 4+ digits -- might be a year. */
681 year
= handle_year (text
, match
+ 27);
684 year
= handle_year (text
, match
+ 30);
686 if (valid_dmy (day
, month
, year
)) {
687 date_format
= gnm_format_frob_slashes ("mmm/dd/yyyy");
688 text
+= match
[0].rm_eo
;
693 /* ^(\d+)(-|/|\.?\s*)(MMMM)((,?\s*|-|/)(\d+))?\b */
697 go_regexec (&datetime_locale
.re_ddMMMMyyyy
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
698 day
= handle_day (text
, match
+ 1);
699 month
= find_month (&match
[4]);
700 if (month
== -1) month
= find_month (&match
[4 + 12]);
701 year
= handle_year (text
, match
+ 30);
702 if (valid_dmy (day
, month
, year
)) {
703 date_format
= g_strdup ("d-mmm-yyyy");
704 text
+= match
[0].rm_eo
;
709 /* ^(\d\d\d\d)(\d\d)(\d\d)(:\d\d\d\d\d\d(\.\d*)?)?\s*$ */
711 if (dig1
> 0 && /* Exclude zero. */
712 go_regexec (&datetime_locale
.re_yyyymmdd1
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
713 year
= handle_year (text
, match
+ 1);
714 month
= handle_month (text
, match
+ 2);
715 day
= handle_day (text
, match
+ 3);
716 if (valid_dmy (day
, month
, year
)) {
717 date_format
= g_strdup ("yyyy-mmm-dd");
718 text
+= match
[3].rm_eo
;
725 /* ^(\d\d\d\d)[-/.](\d\d)[-/.](\d\d)\b */
727 if (dig1
> 0 && /* Exclude zero. */
728 go_regexec (&datetime_locale
.re_yyyymmdd2
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
729 year
= handle_year (text
, match
+ 1);
730 month
= handle_month (text
, match
+ 2);
731 day
= handle_day (text
, match
+ 3);
732 if (valid_dmy (day
, month
, year
)) {
733 date_format
= g_strdup ("yyyy-mmm-dd");
734 text
+= match
[0].rm_eo
;
739 /* ^(\d+)[-/.](\d+)[-/.](\d+)\b */
742 go_regexec (&datetime_locale
.re_mmddyyyy
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
743 if (month_before_day
) {
744 month
= handle_month (text
, match
+ 1);
745 day
= handle_day (text
, match
+ 2);
747 month
= handle_month (text
, match
+ 2);
748 day
= handle_day (text
, match
+ 1);
750 year
= handle_year (text
, match
+ 3);
751 if (valid_dmy (day
, month
, year
)) {
752 date_format
= gnm_format_frob_slashes (month_before_day
755 text
+= match
[0].rm_eo
;
760 /* ^(\d+)([-/.])(\d+)\b */
763 go_regexec (&datetime_locale
.re_mmdd
, text
, G_N_ELEMENTS (match
), match
, 0) == 0) {
765 * Unless we already have a date format, do not accept
766 * 1-10, for example. See bug 376090.
768 gboolean good_ddmmsep
=
770 text
[match
[2].rm_so
] == '/';
771 if (match
[1].rm_eo
- match
[1].rm_so
== 4) {
772 year
= handle_year (text
, match
+ 1);
773 month
= handle_month (text
, match
+ 3);
775 date_format
= g_strdup ("yyyy/m");
776 } else if (match
[3].rm_eo
- match
[3].rm_so
== 4) {
777 month
= handle_month (text
, match
+ 1);
778 year
= handle_year (text
, match
+ 3);
780 date_format
= g_strdup ("m/yyyy");
781 } else if (good_ddmmsep
&& month_before_day
) {
782 month
= handle_month (text
, match
+ 1);
783 day
= handle_day (text
, match
+ 3);
784 year
= current_year ();
785 date_format
= gnm_format_frob_slashes ("m/d/yyyy");
786 } else if (good_ddmmsep
) {
787 month
= handle_month (text
, match
+ 3);
788 day
= handle_day (text
, match
+ 1);
789 year
= current_year ();
790 date_format
= gnm_format_frob_slashes ("d/m/yyyy");
792 year
= month
= day
= -1;
793 if (valid_dmy (day
, month
, year
)) {
794 text
+= match
[0].rm_eo
;
799 g_free (date_format
);
803 g_date_clear (&date
, 1);
804 g_date_set_dmy (&date
, day
, month
, year
);
805 if (!g_date_valid (&date
))
807 date_val
= go_date_g_to_serial (&date
, date_conv
);
812 GnmValue
*v
= format_match_time (text
, FALSE
,
817 time_val
= value_get_as_float (v
);
820 time_format
= g_strdup (go_format_as_XL (fmt
));
825 res
= value_new_float (date_val
+ time_val
);
829 char *format
= g_strconcat (date_format
,
833 fmt
= go_format_new_from_XL (format
);
836 fmt
= go_format_new_from_XL (date_format
);
837 value_set_fmt (res
, fmt
);
838 go_format_unref (fmt
);
842 g_free (date_format
);
843 g_free (time_format
);
848 * Match "12/23", "-12/23", "1 2/3", "-1 2/3", and even "-123".
849 * Does not match "1/0".
851 * Spaces are allowed anywhere but between digits and between
854 * The number of digits in the denominator is stored in @denlen.
857 format_match_fraction (char const *text
, int *denlen
, gboolean mixed_only
)
860 gnm_float whole
, num
, den
, f
;
866 uc
= g_utf8_get_char (text
);
867 DO_SIGN (sign
, uc
, { text
= g_utf8_next_char (text
); });
869 if (*text
== 0 || !g_ascii_isdigit (*text
))
881 whole
= gnm_utf8_strto (start
, NULL
);
889 } else if (!g_ascii_isdigit (*text
))
900 num
= gnm_utf8_strto (start
, NULL
);
908 *denlen
= text
- start
;
914 den
= gnm_utf8_strto (start
, NULL
);
921 f
= whole
+ num
/ den
;
925 return value_new_float (f
);
930 format_match_decimal_number_with_locale (char const *text
, GOFormatFamily
*family
,
931 GString
const *curr
, GString
const *thousand
,
932 GString
const *decimal
)
934 gboolean par_open
= FALSE
;
935 gboolean par_close
= FALSE
;
936 gboolean has_curr
= FALSE
;
937 gboolean has_percent
= FALSE
;
939 GString
*numstr
= g_string_sized_new (20);
940 gboolean last_was_digit
= FALSE
;
941 gboolean allow1000
= (thousand
!= NULL
) && (thousand
->len
!= 0);
943 g_return_val_if_fail (curr
!= NULL
, NULL
);
944 g_return_val_if_fail (decimal
!= NULL
, NULL
);
947 gunichar uc
= g_utf8_get_char (text
);
949 if (!has_curr
&& strncmp (curr
->str
, text
, curr
->len
) == 0) {
955 if (g_unichar_isspace (uc
)) {
956 text
= g_utf8_next_char (text
);
962 g_string_append_c (numstr
, sign
);
963 text
= g_utf8_next_char (text
);
968 if (!par_open
&& !sign
&& uc
== '(') {
970 g_string_append_c (numstr
, sign
);
982 if (last_was_digit
&&
984 strncmp (thousand
->str
, text
, thousand
->len
) == 0 &&
985 g_ascii_isdigit (text
[thousand
->len
]) &&
986 g_ascii_isdigit (text
[thousand
->len
+ 1]) &&
987 g_ascii_isdigit (text
[thousand
->len
+ 2])) {
988 text
+= thousand
->len
;
992 if (strncmp (decimal
->str
, text
, decimal
->len
) == 0) {
993 GString
const *local_decimal
= go_locale_get_decimal ();
994 g_string_append_len (numstr
, local_decimal
->str
, local_decimal
->len
);
995 text
+= decimal
->len
;
1000 if (g_ascii_isdigit (c
)) {
1001 g_string_append_c (numstr
, c
);
1003 last_was_digit
= TRUE
;
1006 last_was_digit
= FALSE
;
1008 if (c
== 'e' || c
== 'E') {
1013 * Pretend to have seen a sign so we don't accept
1020 g_string_append_c (numstr
, c
);
1023 uc
= g_utf8_get_char (text
);
1024 DO_SIGN (esign
, uc
, {
1025 text
= g_utf8_next_char (text
);
1026 g_string_append_c (numstr
, esign
);
1036 gunichar uc
= g_utf8_get_char (text
);
1038 if (!has_curr
&& strncmp (curr
->str
, text
, curr
->len
) == 0) {
1044 if (g_unichar_isspace (uc
)) {
1045 text
= g_utf8_next_char (text
);
1050 DO_SIGN (sign
, uc
, {
1051 g_string_prepend_c (numstr
, sign
);
1052 text
= g_utf8_next_char (text
);
1057 if (!par_close
&& par_open
&& uc
== ')') {
1063 if (!has_percent
&& uc
== '%') {
1074 par_open
!= par_close
||
1075 (has_percent
&& (par_open
|| has_curr
))) {
1076 g_string_free (numstr
, TRUE
);
1083 f
= gnm_utf8_strto (numstr
->str
, &end
);
1084 bad
= *end
|| errno
== ERANGE
;
1085 g_string_free (numstr
, TRUE
);
1091 *family
= GO_FORMAT_ACCOUNTING
;
1093 *family
= GO_FORMAT_CURRENCY
;
1094 else if (has_percent
)
1095 *family
= GO_FORMAT_PERCENTAGE
;
1097 *family
= GO_FORMAT_GENERAL
;
1102 return value_new_float (f
);
1111 set_money_format (GnmValue
*v
, const char *fmttxt
)
1113 gnm_float f
= value_get_as_float (v
);
1116 GOFormat
*fmt
= go_format_new_from_XL (fmttxt
);
1117 value_set_fmt (v
, fmt
);
1118 go_format_unref (fmt
);
1120 value_set_fmt (v
, go_format_default_money ());
1122 if (f
!= gnm_floor (f
)) {
1124 for (i
= 0; i
< 2; i
++) {
1126 go_format_inc_precision (VALUE_FMT (v
));
1127 value_set_fmt (v
, fmt
);
1128 go_format_unref (fmt
);
1134 * Major alternate currencies to try after the locale's currency.
1135 * We do not want three-letter currency codes in here.
1137 static const struct {
1140 } alternate_currencies
[] = {
1148 format_match_decimal_number (char const *text
, GOFormatFamily
*family
,
1149 gboolean try_alternates
)
1151 GString
const *curr
= go_locale_get_currency (NULL
, NULL
);
1152 GString
const *thousand
= go_locale_get_thousand ();
1153 GString
const *decimal
= go_locale_get_decimal ();
1157 v
= format_match_decimal_number_with_locale (text
, family
, curr
, thousand
, decimal
);
1159 try_alternates
&& v
== NULL
&& ui
< G_N_ELEMENTS (alternate_currencies
);
1161 const char *sym
= alternate_currencies
[ui
].sym
;
1162 if (strstr (text
, sym
) == 0)
1165 GString
*altcurr
= g_string_new (sym
);
1166 v
= format_match_decimal_number_with_locale
1167 (text
, family
, altcurr
, thousand
, decimal
);
1168 g_string_free (altcurr
, TRUE
);
1170 set_money_format (v
, alternate_currencies
[ui
].fmt
);
1179 * @text: The text to parse
1180 * @cur_fmt: The current format for the value (potentially NULL)
1181 * @date_conv: optional date convention
1183 * Attempts to parse the supplied string to see if it matches a known value
1184 * format. The caller is responsible for releasing the resulting value.
1187 format_match (char const *text
, GOFormat
const *cur_fmt
,
1188 GODateConventions
const *date_conv
)
1194 if (text
[0] == '\0')
1195 return value_new_empty ();
1197 /* If it begins with a '\'' it is a string */
1198 if (text
[0] == '\'')
1199 return value_new_string (text
+ 1);
1201 fam
= cur_fmt
? go_format_get_family (cur_fmt
) : GO_FORMAT_GENERAL
;
1203 case GO_FORMAT_TEXT
:
1204 return value_new_string (text
);
1206 case GO_FORMAT_NUMBER
:
1207 case GO_FORMAT_CURRENCY
:
1208 case GO_FORMAT_ACCOUNTING
:
1209 case GO_FORMAT_PERCENTAGE
:
1210 case GO_FORMAT_SCIENTIFIC
:
1211 v
= format_match_decimal_number (text
, &fam
, FALSE
);
1213 v
= value_is_error (text
);
1215 value_set_fmt (v
, cur_fmt
);
1218 case GO_FORMAT_DATE
: {
1219 gboolean month_before_day
=
1220 gnm_format_month_before_day (cur_fmt
, NULL
) != 0;
1222 v
= format_match_datetime (text
, date_conv
,
1227 v
= format_match_decimal_number (text
, &fam
, FALSE
);
1229 v
= value_is_error (text
);
1231 value_set_fmt (v
, cur_fmt
);
1235 case GO_FORMAT_TIME
: {
1236 gboolean month_before_day
=
1237 gnm_format_month_before_day (cur_fmt
, NULL
) != 0;
1239 gboolean prefer_hour
=
1240 gnm_format_has_hour (cur_fmt
, NULL
);
1242 v
= format_match_datetime (text
, date_conv
,
1247 v
= format_match_time (text
, TRUE
, prefer_hour
, FALSE
);
1249 v
= format_match_decimal_number (text
, &fam
, FALSE
);
1251 v
= value_is_error (text
);
1253 value_set_fmt (v
, cur_fmt
);
1257 case GO_FORMAT_FRACTION
:
1258 v
= format_match_fraction (text
, &denlen
, FALSE
);
1260 v
= format_match_decimal_number (text
, &fam
, FALSE
);
1262 v
= value_is_error (text
);
1264 value_set_fmt (v
, cur_fmt
);
1271 /* Check basic types */
1272 v
= format_match_simple (text
);
1276 v
= format_match_decimal_number (text
, &fam
, TRUE
);
1279 case GO_FORMAT_PERCENTAGE
:
1280 value_set_fmt (v
, go_format_default_percentage ());
1282 case GO_FORMAT_CURRENCY
:
1284 set_money_format (v
, NULL
);
1286 case GO_FORMAT_ACCOUNTING
:
1287 value_set_fmt (v
, go_format_default_accounting ());
1296 v
= format_match_datetime (text
, date_conv
,
1297 go_locale_month_before_day () != 0,
1303 v
= format_match_time (text
, TRUE
, TRUE
, TRUE
);
1307 v
= format_match_fraction (text
, &denlen
, TRUE
);
1310 char const *qqq
= "?????" + 5;
1313 denlen
= MIN (denlen
, 5);
1314 sprintf (fmtstr
, "# %s/%s", qqq
- denlen
, qqq
- denlen
);
1315 fmt
= go_format_new_from_XL (fmtstr
);
1316 value_set_fmt (v
, fmt
);
1317 go_format_unref (fmt
);
1325 * format_match_number:
1326 * @text: The text to parse
1327 * @cur_fmt: The current format for the value (potentially NULL)
1328 * @date_conv: optional date convention
1330 * Attempts to parse the supplied string to see if it matches a known value format.
1331 * Will eventually use the current cell format in preference to canned formats.
1332 * If @format is supplied it will get a copy of the matching format with no
1333 * additional references. The caller is responsible for releasing the
1334 * resulting value. Will ONLY return numbers.
1337 format_match_number (char const *text
, GOFormat
const *cur_fmt
,
1338 GODateConventions
const *date_conv
)
1340 GnmValue
*res
= format_match (text
, cur_fmt
, date_conv
);
1343 if (VALUE_IS_NUMBER (res
))
1345 value_release (res
);