1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * utils.c: Various utility routines that do not depend on the GUI of Gnumeric
6 * Miguel de Icaza (miguel@gnu.org)
7 * Jukka-Pekka Iivonen (iivonen@iki.fi)
8 * Zbigniew Chyla (cyba@gnome.pl)
10 #include <gnumeric-config.h>
11 #include <glib/gi18n-lib.h>
14 #include "gnumeric-paths.h"
20 #include <goffice/goffice.h>
25 #include <sys/types.h>
30 #include <gsf/gsf-impl-utils.h>
31 #include <gsf/gsf-doc-meta-data.h>
32 #include <gsf/gsf-timestamp.h>
34 static char *gnumeric_lib_dir
;
35 static char *gnumeric_data_dir
;
36 static char *gnumeric_locale_dir
;
37 static char *gnumeric_usr_dir
;
38 static char *gnumeric_usr_dir_unversioned
;
39 static char *gnumeric_extern_plugin_dir
;
40 static GSList
*gutils_xml_in_docs
;
43 running_in_tree (void)
45 const char *argv0
= g_get_prgname ();
50 /* Sometime we see, e.g., "lt-gnumeric" as basename. */
52 char *base
= g_path_get_basename (argv0
);
53 gboolean has_lt_prefix
= (strncmp (base
, "lt-", 3) == 0);
59 /* Look for ".libs" as final path element. */
61 const char *dotlibs
= strstr (argv0
, ".libs/");
63 (dotlibs
== argv0
|| G_IS_DIR_SEPARATOR (dotlibs
[-1])) &&
64 strchr (dotlibs
+ 6, G_DIR_SEPARATOR
) == NULL
)
76 gchar
*dir
= g_win32_get_package_installation_directory_of_module (NULL
);
77 gnumeric_lib_dir
= g_build_filename (dir
, "lib",
78 "gnumeric", GNM_VERSION_FULL
,
80 gnumeric_data_dir
= g_build_filename (dir
, "share",
81 "gnumeric", GNM_VERSION_FULL
,
83 gnumeric_locale_dir
= g_build_filename (dir
, "share", "locale", NULL
);
84 gnumeric_extern_plugin_dir
= g_build_filename
85 (dir
, "lib", "gnumeric", GNM_API_VERSION
, "plugins",
89 if (running_in_tree ()) {
90 const char *argv0
= g_get_prgname ();
91 char *dotlibs
= g_path_get_dirname (argv0
);
92 char *top
= g_build_filename (dotlibs
, "..", "../", NULL
);
93 char *plugins
= g_build_filename (top
, PLUGIN_SUBDIR
, NULL
);
94 if (g_file_test (plugins
, G_FILE_TEST_IS_DIR
))
96 go_filename_simplify (top
, GO_DOTDOT_SYNTACTIC
,
101 if (0) g_printerr ("Running in-tree\n");
104 if (!gnumeric_lib_dir
)
105 gnumeric_lib_dir
= g_strdup (GNUMERIC_LIBDIR
);
106 gnumeric_data_dir
= g_strdup (GNUMERIC_DATADIR
);
107 gnumeric_locale_dir
= g_strdup (GNUMERIC_LOCALEDIR
);
108 gnumeric_extern_plugin_dir
= g_strdup (GNUMERIC_EXTERNPLUGINDIR
);
110 home_dir
= g_get_home_dir ();
111 gnumeric_usr_dir_unversioned
= home_dir
112 ? g_build_filename (home_dir
, ".gnumeric", NULL
)
114 gnumeric_usr_dir
= gnumeric_usr_dir_unversioned
115 ? g_build_filename (gnumeric_usr_dir_unversioned
, GNM_VERSION_FULL
, NULL
)
120 gutils_shutdown (void)
124 g_free (gnumeric_lib_dir
);
125 gnumeric_lib_dir
= NULL
;
126 g_free (gnumeric_data_dir
);
127 gnumeric_data_dir
= NULL
;
128 g_free (gnumeric_locale_dir
);
129 gnumeric_locale_dir
= NULL
;
130 g_free (gnumeric_usr_dir
);
131 gnumeric_usr_dir
= NULL
;
132 g_free (gnumeric_usr_dir_unversioned
);
133 gnumeric_usr_dir_unversioned
= NULL
;
134 g_free (gnumeric_extern_plugin_dir
);
135 gnumeric_extern_plugin_dir
= NULL
;
137 for (l
= gutils_xml_in_docs
; l
; l
= l
->next
) {
138 GsfXMLInDoc
**pdoc
= l
->data
;
139 gsf_xml_in_doc_free (*pdoc
);
142 g_slist_free (gutils_xml_in_docs
);
143 gutils_xml_in_docs
= NULL
;
147 gnm_sys_lib_dir (void)
149 return gnumeric_lib_dir
;
153 gnm_sys_data_dir (void)
155 return gnumeric_data_dir
;
159 gnm_sys_extern_plugin_dir (void)
161 return gnumeric_extern_plugin_dir
;
165 gnm_locale_dir (void)
167 return gnumeric_locale_dir
;
171 gnm_usr_dir (gboolean versioned
)
173 return versioned
? gnumeric_usr_dir
: gnumeric_usr_dir_unversioned
;
178 all_ascii (const char *s
)
180 while ((guchar
)*s
< 0x7f) {
190 * Like strto[ld], but...
191 * 1. handles non-ascii characters
192 * 2. disallows 0x000.0p+00 and 0.0d+00
193 * 3. ensures sane errno on exit
196 gnm_utf8_strto (const char *s
, char **end
)
202 GString
const *decimal
= go_locale_get_decimal ();
203 gboolean seen_decimal
= FALSE
;
204 gboolean seen_digit
= FALSE
;
210 res
= gnm_strto (s
, end
);
211 goto handle_denormal
;
214 ascii
= g_string_sized_new (100);
220 while (g_unichar_isspace (g_utf8_get_char (p
))) {
221 p
= g_utf8_next_char (p
);
225 sign
= go_unichar_issign (g_utf8_get_char (p
));
227 g_string_append_c (ascii
, "-/+"[sign
+ 1]);
228 p
= g_utf8_next_char (p
);
232 if (strncmp (p
, decimal
->str
, decimal
->len
) == 0) {
236 go_string_append_gstring (ascii
, decimal
);
238 } else if (g_unichar_isdigit (g_utf8_get_char (p
))) {
239 g_string_append_c (ascii
, '0' + g_unichar_digit_value (g_utf8_get_char (p
)));
240 p
= g_utf8_next_char (p
);
247 /* No conversion, bail to gnm_strto for nan etc. */
248 g_string_free (ascii
, TRUE
);
249 return gnm_strto (s
, end
);
252 if (*p
== 'e' || *p
== 'E') {
255 g_string_append_c (ascii
, 'e');
256 p
= g_utf8_next_char (p
);
258 sign
= go_unichar_issign (g_utf8_get_char (p
));
260 g_string_append_c (ascii
, "-/+"[sign
+ 1]);
261 p
= g_utf8_next_char (p
);
263 while (g_unichar_isdigit (g_utf8_get_char (p
))) {
264 g_string_append_c (ascii
, '0' + g_unichar_digit_value (g_utf8_get_char (p
)));
265 p
= g_utf8_next_char (p
);
269 res
= gnm_strto (ascii
->str
, end
);
271 *end
= g_utf8_offset_to_pointer
272 (s
, spaces
+ g_utf8_pointer_to_offset (ascii
->str
, *end
));
273 g_string_free (ascii
, TRUE
);
279 if (res
!= 0 && gnm_abs (res
) < GNM_MIN
)
288 * Like strtol, but...
289 * 1. handles non-ascii characters
290 * 2. assumes base==10
291 * 3. ensures sane errno on exit
294 gnm_utf8_strtol (const char *s
, char **end
)
299 unsigned long res
= 0, lim
, limd
;
305 while (g_unichar_isspace (g_utf8_get_char (p
)))
306 p
= g_utf8_next_char (p
);
308 sign
= go_unichar_issign (g_utf8_get_char (p
));
310 p
= g_utf8_next_char (p
);
312 lim
= (-(unsigned long)LONG_MIN
) / 10u;
313 limd
= (-(unsigned long)LONG_MIN
) % 10u;
315 lim
= (unsigned long)LONG_MAX
/ 10u;
316 limd
= (unsigned long)LONG_MAX
% 10u;
319 if (!g_unichar_isdigit (g_utf8_get_char (p
))) {
325 while (g_unichar_isdigit (g_utf8_get_char (p
))) {
326 guint8 dig
= g_unichar_digit_value (g_utf8_get_char (p
));
327 p
= g_utf8_next_char (p
);
329 if (res
> lim
|| (res
== lim
&& dig
> limd
)) {
331 while (g_unichar_isdigit (g_utf8_get_char (p
)))
332 p
= g_utf8_next_char (p
);
335 return sign
< 0 ? LONG_MIN
: LONG_MAX
;
338 res
= res
* 10u + dig
;
342 return sign
< 0 ? (long)-res
: (long)res
;
347 gnm_regcomp_XL (GORegexp
*preg
, char const *pattern
, int cflags
,
348 gboolean anchor_start
, gboolean anchor_end
)
350 GString
*res
= g_string_new (NULL
);
354 g_string_append_c (res
, '^');
359 g_string_append (res
, ".*");
364 g_string_append_c (res
, '.');
369 if (pattern
[1] == '*' ||
375 pattern
= go_regexp_quote1 (res
, pattern
);
380 g_string_append_c (res
, '$');
382 retval
= go_regcomp (preg
, res
->str
, cflags
);
383 g_string_free (res
, TRUE
);
388 * gnm_excel_search_impl:
389 * @needle: the pattern to search for, see gnm_regcomp_XL.
390 * @haystack: the string to search in.
391 * @skip: zero-based search start point in characters.
393 * Returns: -1 for a non-match, or zero-based location in
396 * The is the implementation of Excel's SEARCH function.
397 * However, note that @skip and return value are zero-based.
400 gnm_excel_search_impl (const char *needle
, const char *haystack
,
407 for (i
= skip
, hay2
= haystack
; i
> 0; i
--) {
410 hay2
= g_utf8_next_char (hay2
);
413 if (gnm_regcomp_XL (&r
, needle
, GO_REG_ICASE
, FALSE
, FALSE
) == GO_REG_OK
) {
416 switch (go_regexec (&r
, hay2
, 1, &rm
, 0)) {
422 g_utf8_pointer_to_offset (hay2
, hay2
+ rm
.rm_so
);
424 g_warning ("Unexpected go_regexec result");
428 g_warning ("Unexpected regcomp result");
437 color_to_string (PangoColor color
)
439 static char result
[100];
440 sprintf (result
, "%04x:%04x:%04x", color
.red
, color
.green
, color
.blue
);
445 enum_name (GType typ
, int i
)
447 static char result
[100];
448 GEnumClass
*ec
= g_type_class_ref (typ
);
451 GEnumValue
*ev
= g_enum_get_value (ec
, i
);
452 g_type_class_unref (ec
);
454 if (ev
&& ev
->value_nick
)
455 return ev
->value_nick
;
456 if (ev
&& ev
->value_name
)
457 return ev
->value_name
;
460 sprintf (result
, "%d", i
);
465 cb_gnm_pango_attr_dump (PangoAttribute
*attr
, gpointer user_data
)
467 g_print (" start=%u; end=%u\n", attr
->start_index
, attr
->end_index
);
468 switch (attr
->klass
->type
) {
469 case PANGO_ATTR_FAMILY
:
470 g_print (" family=\"%s\"\n", ((PangoAttrString
*)attr
)->value
);
472 case PANGO_ATTR_LANGUAGE
:
473 g_print (" language=\"%s\"\n", pango_language_to_string (((PangoAttrLanguage
*)attr
)->value
));
475 case PANGO_ATTR_STYLE
:
476 g_print (" style=%s\n",
477 enum_name (PANGO_TYPE_STYLE
, ((PangoAttrInt
*)attr
)->value
));
479 case PANGO_ATTR_WEIGHT
:
480 g_print (" weight=%s\n",
481 enum_name (PANGO_TYPE_WEIGHT
, ((PangoAttrInt
*)attr
)->value
));
483 case PANGO_ATTR_VARIANT
:
484 g_print (" variant=%s\n",
485 enum_name (PANGO_TYPE_VARIANT
, ((PangoAttrInt
*)attr
)->value
));
487 case PANGO_ATTR_STRETCH
:
488 g_print (" stretch=%s\n",
489 enum_name (PANGO_TYPE_STRETCH
, ((PangoAttrInt
*)attr
)->value
));
491 case PANGO_ATTR_UNDERLINE
:
492 g_print (" underline=%s\n",
493 enum_name (PANGO_TYPE_UNDERLINE
, ((PangoAttrInt
*)attr
)->value
));
495 case PANGO_ATTR_STRIKETHROUGH
:
496 g_print (" strikethrough=%d\n", ((PangoAttrInt
*)attr
)->value
);
498 case PANGO_ATTR_RISE
:
499 g_print (" rise=%d\n", ((PangoAttrInt
*)attr
)->value
);
501 case PANGO_ATTR_FALLBACK
:
502 g_print (" fallback=%d\n", ((PangoAttrInt
*)attr
)->value
);
504 case PANGO_ATTR_LETTER_SPACING
:
505 g_print (" letter_spacing=%d\n", ((PangoAttrInt
*)attr
)->value
);
507 case PANGO_ATTR_SIZE
:
508 g_print (" size=%d%s\n",
509 ((PangoAttrSize
*)attr
)->size
,
510 ((PangoAttrSize
*)attr
)->absolute
? " abs" : "");
512 case PANGO_ATTR_SCALE
:
513 g_print (" scale=%g\n", ((PangoAttrFloat
*)attr
)->value
);
515 case PANGO_ATTR_FOREGROUND
:
516 g_print (" foreground=%s\n", color_to_string (((PangoAttrColor
*)attr
)->color
));
518 case PANGO_ATTR_BACKGROUND
:
519 g_print (" background=%s\n", color_to_string (((PangoAttrColor
*)attr
)->color
));
521 case PANGO_ATTR_UNDERLINE_COLOR
:
522 g_print (" underline_color=%s\n", color_to_string (((PangoAttrColor
*)attr
)->color
));
524 case PANGO_ATTR_STRIKETHROUGH_COLOR
:
525 g_print (" strikethrough_color=%s\n", color_to_string (((PangoAttrColor
*)attr
)->color
));
527 case PANGO_ATTR_FONT_DESC
: {
528 char *desc
= pango_font_description_to_string (((PangoAttrFontDesc
*)attr
)->desc
);
529 g_print (" font=\"%s\"\n", desc
);
534 g_print (" type=%s\n", enum_name (PANGO_TYPE_ATTR_TYPE
, attr
->klass
->type
));
541 gnm_pango_attr_dump (PangoAttrList
*list
)
543 g_print ("PangoAttrList at %p\n", list
);
544 pango_attr_list_filter (list
, cb_gnm_pango_attr_dump
, NULL
);
550 cb_gnm_pango_attr_list_equal (PangoAttribute
*a
, gpointer _sl
)
553 *sl
= g_slist_prepend (*sl
, a
);
558 * This is a bit of a hack. It might claim a difference even when things
559 * actually are equal. But not the other way around.
562 gnm_pango_attr_list_equal (PangoAttrList
const *l1
, PangoAttrList
const *l2
)
566 else if (l1
== NULL
|| l2
== NULL
)
570 GSList
*sl1
= NULL
, *sl2
= NULL
;
571 (void)pango_attr_list_filter ((PangoAttrList
*)l1
,
572 cb_gnm_pango_attr_list_equal
,
574 (void)pango_attr_list_filter ((PangoAttrList
*)l2
,
575 cb_gnm_pango_attr_list_equal
,
579 const PangoAttribute
*a1
= sl1
->data
;
580 const PangoAttribute
*a2
= sl2
->data
;
581 if (a1
->start_index
!= a2
->start_index
||
582 a1
->end_index
!= a2
->end_index
||
583 !pango_attribute_equal (a1
, a2
))
585 sl1
= g_slist_delete_link (sl1
, sl1
);
586 sl2
= g_slist_delete_link (sl2
, sl2
);
596 /* ------------------------------------------------------------------------- */
600 char *monetary_locale
;
603 * gnm_push_C_locale: (skip)
605 * Returns the current locale, and sets the locale and the value-format
606 * engine's locale to 'C'. The caller must call gnm_pop_C_locale to free the
607 * result and restore the previous locale.
610 gnm_push_C_locale (void)
612 GnmLocale
*old
= g_new0 (GnmLocale
, 1);
614 old
->num_locale
= g_strdup (go_setlocale (LC_NUMERIC
, NULL
));
615 go_setlocale (LC_NUMERIC
, "C");
616 old
->monetary_locale
= g_strdup (go_setlocale (LC_MONETARY
, NULL
));
617 go_setlocale (LC_MONETARY
, "C");
618 go_locale_untranslated_booleans ();
624 * gnm_pop_C_locale: (skip)
625 * @locale: #GnmLocale
627 * Frees the result of gnm_push_C_locale and restores the original locale.
630 gnm_pop_C_locale (GnmLocale
*locale
)
632 /* go_setlocale restores bools to locale translation */
633 go_setlocale (LC_MONETARY
, locale
->monetary_locale
);
634 g_free (locale
->monetary_locale
);
635 go_setlocale (LC_NUMERIC
, locale
->num_locale
);
636 g_free (locale
->num_locale
);
640 /* ------------------------------------------------------------------------- */
643 gnm_debug_flag (const char *flag
)
646 key
.key
= (char *)flag
;
649 return g_parse_debug_string (g_getenv ("GNM_DEBUG"), &key
, 1) != 0;
652 /* ------------------------------------------------------------------------- */
655 gnm_string_add_number (GString
*buf
, gnm_float d
)
657 size_t old_len
= buf
->len
;
662 gnm_float l10
= gnm_log10 (FLT_RADIX
);
663 digits
= (int)gnm_ceil (GNM_MANT_DIG
* l10
) +
664 (l10
== (int)l10
? 0 : 1);
667 g_string_append_printf (buf
, "%.*" GNM_FORMAT_g
, digits
- 1, d
);
668 d2
= gnm_strto (buf
->str
+ old_len
, NULL
);
671 g_string_truncate (buf
, old_len
);
672 g_string_append_printf (buf
, "%.*" GNM_FORMAT_g
, digits
, d
);
676 /* ------------------------------------------------------------------------- */
679 gnm_insert_meta_date (GODoc
*doc
, char const *name
)
681 GValue
*value
= g_new0 (GValue
, 1);
683 GsfTimestamp
*ts
= gsf_timestamp_new ();
685 g_get_current_time (&tm
);
688 gsf_timestamp_set_time (ts
, tm
.tv_sec
);
689 g_value_init (value
, GSF_TIMESTAMP_TYPE
);
690 gsf_timestamp_to_value (ts
, value
);
691 gsf_timestamp_free (ts
);
693 gsf_doc_meta_data_insert (go_doc_get_meta_data (doc
),
698 /* ------------------------------------------------------------------------- */
701 gnm_object_get_bool (gpointer o
, const char *name
)
704 g_object_get (o
, name
, &b
, NULL
);
709 gnm_object_has_readable_prop (gconstpointer obj
, const char *property
,
710 GType typ
, gpointer pres
)
718 klass
= G_OBJECT_GET_CLASS (G_OBJECT (obj
));
719 spec
= g_object_class_find_property (klass
, property
);
721 !(G_PARAM_READABLE
& spec
->flags
) ||
722 (typ
!= G_TYPE_NONE
&& spec
->value_type
!= typ
))
726 g_object_get (G_OBJECT (obj
), property
, pres
, NULL
);
734 gnm_float_equal (gnm_float
const *a
, const gnm_float
*b
)
739 /* ------------------------------------------------------------------------- */
742 gnm_float_hash (gnm_float
const *d
)
745 gnm_float mant
= gnm_frexp (gnm_abs (*d
), &expt
);
746 guint h
= ((guint
)(0x80000000u
* mant
)) ^ expt
;
752 /* ------------------------------------------------------------------------- */
755 GnmHashTableOrder order
;
760 cb_compare (gconstpointer a_
, gconstpointer b_
, gpointer user_data
)
762 struct cb_compare
*user
= user_data
;
763 gpointer
*a
= (gpointer
)a_
;
764 gpointer
*b
= (gpointer
)b_
;
766 return user
->order (a
[0], a
[1], b
[0], b
[1], user
->user
);
771 * gnm_hash_table_foreach_ordered:
773 * @callback: (scope async): #GHFunc
774 * @order: (scope async): Ordering function
775 * @user: user data for callback and order
777 * Like g_hash_table_foreach, but with an ordering imposed.
780 gnm_hash_table_foreach_ordered (GHashTable
*h
,
782 GnmHashTableOrder order
,
788 GHashTableIter hiter
;
791 /* Gather all key-value pairs */
792 data
= g_ptr_array_new ();
793 g_hash_table_iter_init (&hiter
, h
);
794 while (g_hash_table_iter_next (&hiter
, &key
, &value
)) {
795 g_ptr_array_add (data
, key
);
796 g_ptr_array_add (data
, value
);
799 /* Sort according to given ordering */
802 g_qsort_with_data (data
->pdata
,
803 data
->len
/ 2, 2 * sizeof (gpointer
),
807 /* Call user callback with all pairs */
808 for (ui
= 0; ui
< data
->len
; ui
+= 2)
809 callback (g_ptr_array_index (data
, ui
),
810 g_ptr_array_index (data
, ui
+ 1),
814 g_ptr_array_free (data
, TRUE
);
817 /* ------------------------------------------------------------------------- */
820 gnm_xml_in_doc_dispose_on_exit (GsfXMLInDoc
**pdoc
)
822 gutils_xml_in_docs
= g_slist_prepend (gutils_xml_in_docs
, pdoc
);
825 /* ------------------------------------------------------------------------- */