Introspection fixes.
[gnumeric.git] / src / gutils.c
blobb7cf2bae97f6a4aa8493bbc68b9e1641152a226c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * utils.c: Various utility routines that do not depend on the GUI of Gnumeric
5 * Authors:
6 * Miguel de Icaza (miguel@gnu.org)
7 * Jukka-Pekka Iivonen (iivonen@iki.fi)
8 * Zbigniew Chyla (cyba@gnome.pl)
9 */
10 #include <gnumeric-config.h>
11 #include <glib/gi18n-lib.h>
12 #include "gnumeric.h"
13 #include "gutils.h"
14 #include "gnumeric-paths.h"
16 #include "sheet.h"
17 #include "ranges.h"
18 #include "mathfunc.h"
20 #include <goffice/goffice.h>
22 #include <stdlib.h>
23 #include <math.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <locale.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;
42 static gboolean
43 running_in_tree (void)
45 const char *argv0 = g_get_prgname ();
47 if (!argv0)
48 return FALSE;
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);
54 g_free (base);
55 if (has_lt_prefix)
56 return TRUE;
59 /* Look for ".libs" as final path element. */
61 const char *dotlibs = strstr (argv0, ".libs/");
62 if (dotlibs &&
63 (dotlibs == argv0 || G_IS_DIR_SEPARATOR (dotlibs[-1])) &&
64 strchr (dotlibs + 6, G_DIR_SEPARATOR) == NULL)
65 return TRUE;
68 return FALSE;
71 static gboolean gutils_inited = FALSE;
73 void
74 gutils_init (void)
76 char const *home_dir;
78 // This function will end up being called twice in normal operation:
79 // once from gnm_pre_parse_init and once from gnm_init. Introspection
80 // will not get the first.
81 if (gutils_inited)
82 return;
84 #ifdef G_OS_WIN32
85 gchar *dir = g_win32_get_package_installation_directory_of_module (NULL);
86 gnumeric_lib_dir = g_build_filename (dir, "lib",
87 "gnumeric", GNM_VERSION_FULL,
88 NULL);
89 gnumeric_data_dir = g_build_filename (dir, "share",
90 "gnumeric", GNM_VERSION_FULL,
91 NULL);
92 gnumeric_locale_dir = g_build_filename (dir, "share", "locale", NULL);
93 gnumeric_extern_plugin_dir = g_build_filename
94 (dir, "lib", "gnumeric", GNM_API_VERSION, "plugins",
95 NULL);
96 g_free (dir);
97 #else
98 if (running_in_tree ()) {
99 const char *argv0 = g_get_prgname ();
100 char *dotlibs = g_path_get_dirname (argv0);
101 char *top = g_build_filename (dotlibs, "..", "../", NULL);
102 char *plugins = g_build_filename (top, PLUGIN_SUBDIR, NULL);
103 if (g_file_test (plugins, G_FILE_TEST_IS_DIR))
104 gnumeric_lib_dir =
105 go_filename_simplify (top, GO_DOTDOT_SYNTACTIC,
106 FALSE);
107 g_free (top);
108 g_free (plugins);
109 g_free (dotlibs);
110 if (0) g_printerr ("Running in-tree\n");
113 if (!gnumeric_lib_dir)
114 gnumeric_lib_dir = g_strdup (GNUMERIC_LIBDIR);
115 gnumeric_data_dir = g_strdup (GNUMERIC_DATADIR);
116 gnumeric_locale_dir = g_strdup (GNUMERIC_LOCALEDIR);
117 gnumeric_extern_plugin_dir = g_strdup (GNUMERIC_EXTERNPLUGINDIR);
118 #endif
119 home_dir = g_get_home_dir ();
120 gnumeric_usr_dir_unversioned = home_dir
121 ? g_build_filename (home_dir, ".gnumeric", NULL)
122 : NULL;
123 gnumeric_usr_dir = gnumeric_usr_dir_unversioned
124 ? g_build_filename (gnumeric_usr_dir_unversioned, GNM_VERSION_FULL, NULL)
125 : NULL;
127 gutils_inited = TRUE;
130 void
131 gutils_shutdown (void)
133 GSList *l;
135 g_free (gnumeric_lib_dir);
136 gnumeric_lib_dir = NULL;
137 g_free (gnumeric_data_dir);
138 gnumeric_data_dir = NULL;
139 g_free (gnumeric_locale_dir);
140 gnumeric_locale_dir = NULL;
141 g_free (gnumeric_usr_dir);
142 gnumeric_usr_dir = NULL;
143 g_free (gnumeric_usr_dir_unversioned);
144 gnumeric_usr_dir_unversioned = NULL;
145 g_free (gnumeric_extern_plugin_dir);
146 gnumeric_extern_plugin_dir = NULL;
148 for (l = gutils_xml_in_docs; l; l = l->next) {
149 GsfXMLInDoc **pdoc = l->data;
150 gsf_xml_in_doc_free (*pdoc);
151 *pdoc = NULL;
153 g_slist_free (gutils_xml_in_docs);
154 gutils_xml_in_docs = NULL;
157 char const *
158 gnm_sys_lib_dir (void)
160 return gnumeric_lib_dir;
163 char const *
164 gnm_sys_data_dir (void)
166 return gnumeric_data_dir;
169 char const *
170 gnm_sys_extern_plugin_dir (void)
172 return gnumeric_extern_plugin_dir;
175 char const *
176 gnm_locale_dir (void)
178 return gnumeric_locale_dir;
181 char const *
182 gnm_usr_dir (gboolean versioned)
184 return versioned ? gnumeric_usr_dir : gnumeric_usr_dir_unversioned;
188 static gboolean
189 all_ascii (const char *s)
191 while ((guchar)*s < 0x7f) {
192 if (*s)
193 s++;
194 else
195 return TRUE;
197 return FALSE;
201 * Like strto[ld], but...
202 * 1. handles non-ascii characters
203 * 2. disallows 0x000.0p+00 and 0.0d+00
204 * 3. ensures sane errno on exit
206 gnm_float
207 gnm_utf8_strto (const char *s, char **end)
209 const char *p;
210 int sign;
211 char *dummy_end;
212 GString *ascii;
213 GString const *decimal = go_locale_get_decimal ();
214 gboolean seen_decimal = FALSE;
215 gboolean seen_digit = FALSE;
216 size_t spaces = 0;
217 gnm_float res;
218 int save_errno;
220 if (all_ascii (s)) {
221 res = gnm_strto (s, end);
222 goto handle_denormal;
225 ascii = g_string_sized_new (100);
227 if (!end)
228 end = &dummy_end;
230 p = s;
231 while (g_unichar_isspace (g_utf8_get_char (p))) {
232 p = g_utf8_next_char (p);
233 spaces++;
236 sign = go_unichar_issign (g_utf8_get_char (p));
237 if (sign) {
238 g_string_append_c (ascii, "-/+"[sign + 1]);
239 p = g_utf8_next_char (p);
242 do {
243 if (strncmp (p, decimal->str, decimal->len) == 0) {
244 if (seen_decimal)
245 break;
246 seen_decimal = TRUE;
247 go_string_append_gstring (ascii, decimal);
248 p += decimal->len;
249 } else if (g_unichar_isdigit (g_utf8_get_char (p))) {
250 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
251 p = g_utf8_next_char (p);
252 seen_digit = TRUE;
253 } else
254 break;
255 } while (1);
257 if (!seen_digit) {
258 /* No conversion, bail to gnm_strto for nan etc. */
259 g_string_free (ascii, TRUE);
260 return gnm_strto (s, end);
263 if (*p == 'e' || *p == 'E') {
264 int sign;
266 g_string_append_c (ascii, 'e');
267 p = g_utf8_next_char (p);
269 sign = go_unichar_issign (g_utf8_get_char (p));
270 if (sign) {
271 g_string_append_c (ascii, "-/+"[sign + 1]);
272 p = g_utf8_next_char (p);
274 while (g_unichar_isdigit (g_utf8_get_char (p))) {
275 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
276 p = g_utf8_next_char (p);
280 res = gnm_strto (ascii->str, end);
281 save_errno = errno;
282 *end = g_utf8_offset_to_pointer
283 (s, spaces + g_utf8_pointer_to_offset (ascii->str, *end));
284 g_string_free (ascii, TRUE);
286 errno = save_errno;
288 handle_denormal:
289 save_errno = errno;
290 if (res != 0 && gnm_abs (res) < GNM_MIN)
291 errno = 0;
292 else
293 errno = save_errno;
295 return res;
299 * Like strtol, but...
300 * 1. handles non-ascii characters
301 * 2. assumes base==10
302 * 3. ensures sane errno on exit
304 long
305 gnm_utf8_strtol (const char *s, char **end)
307 const char *p;
308 int sign;
309 char *dummy_end;
310 unsigned long res = 0, lim, limd;
312 if (!end)
313 end = &dummy_end;
315 p = s;
316 while (g_unichar_isspace (g_utf8_get_char (p)))
317 p = g_utf8_next_char (p);
319 sign = go_unichar_issign (g_utf8_get_char (p));
320 if (sign)
321 p = g_utf8_next_char (p);
322 if (sign < 0) {
323 lim = (-(unsigned long)LONG_MIN) / 10u;
324 limd = (-(unsigned long)LONG_MIN) % 10u;
325 } else {
326 lim = (unsigned long)LONG_MAX / 10u;
327 limd = (unsigned long)LONG_MAX % 10u;
330 if (!g_unichar_isdigit (g_utf8_get_char (p))) {
331 errno = 0;
332 *end = (char *)s;
333 return 0;
336 while (g_unichar_isdigit (g_utf8_get_char (p))) {
337 guint8 dig = g_unichar_digit_value (g_utf8_get_char (p));
338 p = g_utf8_next_char (p);
340 if (res > lim || (res == lim && dig > limd)) {
341 /* Overflow */
342 while (g_unichar_isdigit (g_utf8_get_char (p)))
343 p = g_utf8_next_char (p);
344 *end = (char *)p;
345 errno = ERANGE;
346 return sign < 0 ? LONG_MIN : LONG_MAX;
349 res = res * 10u + dig;
351 *end = (char *)p;
352 errno = 0;
353 return sign < 0 ? (long)-res : (long)res;
358 gnm_regcomp_XL (GORegexp *preg, char const *pattern, int cflags,
359 gboolean anchor_start, gboolean anchor_end)
361 GString *res = g_string_new (NULL);
362 int retval;
364 if (anchor_start)
365 g_string_append_c (res, '^');
367 while (*pattern) {
368 switch (*pattern) {
369 case '*':
370 g_string_append (res, ".*");
371 pattern++;
372 break;
374 case '?':
375 g_string_append_c (res, '.');
376 pattern++;
377 break;
379 case '~':
380 if (pattern[1] == '*' ||
381 pattern[1] == '?' ||
382 pattern[1] == '~')
383 pattern++;
384 /* Fall through */
385 default:
386 pattern = go_regexp_quote1 (res, pattern);
390 if (anchor_end)
391 g_string_append_c (res, '$');
393 retval = go_regcomp (preg, res->str, cflags);
394 g_string_free (res, TRUE);
395 return retval;
399 * gnm_excel_search_impl:
400 * @needle: the pattern to search for, see gnm_regcomp_XL.
401 * @haystack: the string to search in.
402 * @skip: zero-based search start point in characters.
404 * Returns: -1 for a non-match, or zero-based location in
405 * characters.
407 * The is the implementation of Excel's SEARCH function.
408 * However, note that @skip and return value are zero-based.
411 gnm_excel_search_impl (const char *needle, const char *haystack,
412 size_t skip)
414 const char *hay2;
415 size_t i;
416 GORegexp r;
418 for (i = skip, hay2 = haystack; i > 0; i--) {
419 if (*hay2 == 0)
420 return -1;
421 hay2 = g_utf8_next_char (hay2);
424 if (gnm_regcomp_XL (&r, needle, GO_REG_ICASE, FALSE, FALSE) == GO_REG_OK) {
425 GORegmatch rm;
427 switch (go_regexec (&r, hay2, 1, &rm, 0)) {
428 case GO_REG_NOMATCH:
429 break;
430 case GO_REG_OK:
431 go_regfree (&r);
432 return skip +
433 g_utf8_pointer_to_offset (hay2, hay2 + rm.rm_so);
434 default:
435 g_warning ("Unexpected go_regexec result");
437 go_regfree (&r);
438 } else {
439 g_warning ("Unexpected regcomp result");
442 return -1;
446 #if 0
447 static char const *
448 color_to_string (PangoColor color)
450 static char result[100];
451 sprintf (result, "%04x:%04x:%04x", color.red, color.green, color.blue);
452 return result;
455 static const char *
456 enum_name (GType typ, int i)
458 static char result[100];
459 GEnumClass *ec = g_type_class_ref (typ);
461 if (ec) {
462 GEnumValue *ev = g_enum_get_value (ec, i);
463 g_type_class_unref (ec);
465 if (ev && ev->value_nick)
466 return ev->value_nick;
467 if (ev && ev->value_name)
468 return ev->value_name;
471 sprintf (result, "%d", i);
472 return result;
475 static gboolean
476 cb_gnm_pango_attr_dump (PangoAttribute *attr, gpointer user_data)
478 g_print (" start=%u; end=%u\n", attr->start_index, attr->end_index);
479 switch (attr->klass->type) {
480 case PANGO_ATTR_FAMILY:
481 g_print (" family=\"%s\"\n", ((PangoAttrString *)attr)->value);
482 break;
483 case PANGO_ATTR_LANGUAGE:
484 g_print (" language=\"%s\"\n", pango_language_to_string (((PangoAttrLanguage *)attr)->value));
485 break;
486 case PANGO_ATTR_STYLE:
487 g_print (" style=%s\n",
488 enum_name (PANGO_TYPE_STYLE, ((PangoAttrInt *)attr)->value));
489 break;
490 case PANGO_ATTR_WEIGHT:
491 g_print (" weight=%s\n",
492 enum_name (PANGO_TYPE_WEIGHT, ((PangoAttrInt *)attr)->value));
493 break;
494 case PANGO_ATTR_VARIANT:
495 g_print (" variant=%s\n",
496 enum_name (PANGO_TYPE_VARIANT, ((PangoAttrInt *)attr)->value));
497 break;
498 case PANGO_ATTR_STRETCH:
499 g_print (" stretch=%s\n",
500 enum_name (PANGO_TYPE_STRETCH, ((PangoAttrInt *)attr)->value));
501 break;
502 case PANGO_ATTR_UNDERLINE:
503 g_print (" underline=%s\n",
504 enum_name (PANGO_TYPE_UNDERLINE, ((PangoAttrInt *)attr)->value));
505 break;
506 case PANGO_ATTR_STRIKETHROUGH:
507 g_print (" strikethrough=%d\n", ((PangoAttrInt *)attr)->value);
508 break;
509 case PANGO_ATTR_RISE:
510 g_print (" rise=%d\n", ((PangoAttrInt *)attr)->value);
511 break;
512 case PANGO_ATTR_FALLBACK:
513 g_print (" fallback=%d\n", ((PangoAttrInt *)attr)->value);
514 break;
515 case PANGO_ATTR_LETTER_SPACING:
516 g_print (" letter_spacing=%d\n", ((PangoAttrInt *)attr)->value);
517 break;
518 case PANGO_ATTR_SIZE:
519 g_print (" size=%d%s\n",
520 ((PangoAttrSize *)attr)->size,
521 ((PangoAttrSize *)attr)->absolute ? " abs" : "");
522 break;
523 case PANGO_ATTR_SCALE:
524 g_print (" scale=%g\n", ((PangoAttrFloat *)attr)->value);
525 break;
526 case PANGO_ATTR_FOREGROUND:
527 g_print (" foreground=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
528 break;
529 case PANGO_ATTR_BACKGROUND:
530 g_print (" background=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
531 break;
532 case PANGO_ATTR_UNDERLINE_COLOR:
533 g_print (" underline_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
534 break;
535 case PANGO_ATTR_STRIKETHROUGH_COLOR:
536 g_print (" strikethrough_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
537 break;
538 case PANGO_ATTR_FONT_DESC: {
539 char *desc = pango_font_description_to_string (((PangoAttrFontDesc*)attr)->desc);
540 g_print (" font=\"%s\"\n", desc);
541 g_free (desc);
542 break;
544 default:
545 g_print (" type=%s\n", enum_name (PANGO_TYPE_ATTR_TYPE, attr->klass->type));
548 return FALSE;
551 void
552 gnm_pango_attr_dump (PangoAttrList *list)
554 g_print ("PangoAttrList at %p\n", list);
555 pango_attr_list_filter (list, cb_gnm_pango_attr_dump, NULL);
557 #endif
560 static gboolean
561 cb_gnm_pango_attr_list_equal (PangoAttribute *a, gpointer _sl)
563 GSList **sl = _sl;
564 *sl = g_slist_prepend (*sl, a);
565 return FALSE;
569 * This is a bit of a hack. It might claim a difference even when things
570 * actually are equal. But not the other way around.
572 gboolean
573 gnm_pango_attr_list_equal (PangoAttrList const *l1, PangoAttrList const *l2)
575 if (l1 == l2)
576 return TRUE;
577 else if (l1 == NULL || l2 == NULL)
578 return FALSE;
579 else {
580 gboolean res;
581 GSList *sl1 = NULL, *sl2 = NULL;
582 (void)pango_attr_list_filter ((PangoAttrList *)l1,
583 cb_gnm_pango_attr_list_equal,
584 &sl1);
585 (void)pango_attr_list_filter ((PangoAttrList *)l2,
586 cb_gnm_pango_attr_list_equal,
587 &sl2);
589 while (sl1 && sl2) {
590 const PangoAttribute *a1 = sl1->data;
591 const PangoAttribute *a2 = sl2->data;
592 if (a1->start_index != a2->start_index ||
593 a1->end_index != a2->end_index ||
594 !pango_attribute_equal (a1, a2))
595 break;
596 sl1 = g_slist_delete_link (sl1, sl1);
597 sl2 = g_slist_delete_link (sl2, sl2);
600 res = (sl1 == sl2);
601 g_slist_free (sl1);
602 g_slist_free (sl2);
603 return res;
607 /* ------------------------------------------------------------------------- */
609 struct _GnmLocale {
610 char *num_locale;
611 char *monetary_locale;
614 * gnm_push_C_locale: (skip)
616 * Returns the current locale, and sets the locale and the value-format
617 * engine's locale to 'C'. The caller must call gnm_pop_C_locale to free the
618 * result and restore the previous locale.
620 GnmLocale *
621 gnm_push_C_locale (void)
623 GnmLocale *old = g_new0 (GnmLocale, 1);
625 old->num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
626 go_setlocale (LC_NUMERIC, "C");
627 old->monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
628 go_setlocale (LC_MONETARY, "C");
629 go_locale_untranslated_booleans ();
631 return old;
635 * gnm_pop_C_locale: (skip)
636 * @locale: #GnmLocale
638 * Frees the result of gnm_push_C_locale and restores the original locale.
640 void
641 gnm_pop_C_locale (GnmLocale *locale)
643 /* go_setlocale restores bools to locale translation */
644 go_setlocale (LC_MONETARY, locale->monetary_locale);
645 g_free (locale->monetary_locale);
646 go_setlocale (LC_NUMERIC, locale->num_locale);
647 g_free (locale->num_locale);
648 g_free (locale);
651 /* ------------------------------------------------------------------------- */
653 gboolean
654 gnm_debug_flag (const char *flag)
656 GDebugKey key;
657 key.key = (char *)flag;
658 key.value = 1;
660 return g_parse_debug_string (g_getenv ("GNM_DEBUG"), &key, 1) != 0;
663 /* ------------------------------------------------------------------------- */
665 void
666 gnm_string_add_number (GString *buf, gnm_float d)
668 size_t old_len = buf->len;
669 double d2;
670 static int digits;
672 if (digits == 0) {
673 gnm_float l10 = gnm_log10 (FLT_RADIX);
674 digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
675 (l10 == (int)l10 ? 0 : 1);
678 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits - 1, d);
679 d2 = gnm_strto (buf->str + old_len, NULL);
681 if (d != d2) {
682 g_string_truncate (buf, old_len);
683 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits, d);
687 /* ------------------------------------------------------------------------- */
689 void
690 gnm_insert_meta_date (GODoc *doc, char const *name)
692 GValue *value = g_new0 (GValue, 1);
693 GTimeVal tm;
694 GsfTimestamp *ts = gsf_timestamp_new ();
696 g_get_current_time (&tm);
697 tm.tv_usec = 0L;
699 gsf_timestamp_set_time (ts, tm.tv_sec);
700 g_value_init (value, GSF_TIMESTAMP_TYPE);
701 gsf_timestamp_to_value (ts, value);
702 gsf_timestamp_free (ts);
704 gsf_doc_meta_data_insert (go_doc_get_meta_data (doc),
705 g_strdup (name),
706 value);
709 /* ------------------------------------------------------------------------- */
711 gboolean
712 gnm_object_get_bool (gpointer o, const char *name)
714 gboolean b;
715 g_object_get (o, name, &b, NULL);
716 return b;
719 gboolean
720 gnm_object_has_readable_prop (gconstpointer obj, const char *property,
721 GType typ, gpointer pres)
723 GObjectClass *klass;
724 GParamSpec *spec;
726 if (!obj)
727 return FALSE;
729 klass = G_OBJECT_GET_CLASS (G_OBJECT (obj));
730 spec = g_object_class_find_property (klass, property);
731 if (!spec ||
732 !(G_PARAM_READABLE & spec->flags) ||
733 (typ != G_TYPE_NONE && spec->value_type != typ))
734 return FALSE;
736 if (pres)
737 g_object_get (G_OBJECT (obj), property, pres, NULL);
738 return TRUE;
744 gint
745 gnm_float_equal (gnm_float const *a, const gnm_float *b)
747 return (*a == *b);
750 /* ------------------------------------------------------------------------- */
752 guint
753 gnm_float_hash (gnm_float const *d)
755 int expt;
756 gnm_float mant = gnm_frexp (gnm_abs (*d), &expt);
757 guint h = ((guint)(0x80000000u * mant)) ^ expt;
758 if (*d >= 0)
759 h ^= 0x55555555;
760 return h;
763 /* ------------------------------------------------------------------------- */
765 struct cb_compare {
766 GnmHashTableOrder order;
767 gpointer user;
770 static gint
771 cb_compare (gconstpointer a_, gconstpointer b_, gpointer user_data)
773 struct cb_compare *user = user_data;
774 gpointer *a = (gpointer )a_;
775 gpointer *b = (gpointer )b_;
777 return user->order (a[0], a[1], b[0], b[1], user->user);
782 * gnm_hash_table_foreach_ordered:
783 * @h: Hash table
784 * @callback: (scope async): #GHFunc
785 * @order: (scope async): Ordering function
786 * @user: user data for callback and order
788 * Like g_hash_table_foreach, but with an ordering imposed.
790 void
791 gnm_hash_table_foreach_ordered (GHashTable *h,
792 GHFunc callback,
793 GnmHashTableOrder order,
794 gpointer user)
796 unsigned ui;
797 GPtrArray *data;
798 struct cb_compare u;
799 GHashTableIter hiter;
800 gpointer key, value;
802 /* Gather all key-value pairs */
803 data = g_ptr_array_new ();
804 g_hash_table_iter_init (&hiter, h);
805 while (g_hash_table_iter_next (&hiter, &key, &value)) {
806 g_ptr_array_add (data, key);
807 g_ptr_array_add (data, value);
810 /* Sort according to given ordering */
811 u.order = order;
812 u.user = user;
813 g_qsort_with_data (data->pdata,
814 data->len / 2, 2 * sizeof (gpointer),
815 cb_compare,
816 &u);
818 /* Call user callback with all pairs */
819 for (ui = 0; ui < data->len; ui += 2)
820 callback (g_ptr_array_index (data, ui),
821 g_ptr_array_index (data, ui + 1),
822 user);
824 /* Clean up */
825 g_ptr_array_free (data, TRUE);
828 /* ------------------------------------------------------------------------- */
830 void
831 gnm_xml_in_doc_dispose_on_exit (GsfXMLInDoc **pdoc)
833 gutils_xml_in_docs = g_slist_prepend (gutils_xml_in_docs, pdoc);
836 /* ------------------------------------------------------------------------- */