summaryrefslogtreecommitdiff
path: root/eel/eel-glib-extensions.c
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-12-01 22:24:23 -0300
committerPerberos <[email protected]>2011-12-01 22:24:23 -0300
commit0e004c696b0e68b2cff37a4c3315b022a35eaf43 (patch)
tree43261e815529cb9518ed7be37af13b846af8b26b /eel/eel-glib-extensions.c
downloadcaja-0e004c696b0e68b2cff37a4c3315b022a35eaf43.tar.bz2
caja-0e004c696b0e68b2cff37a4c3315b022a35eaf43.tar.xz
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'eel/eel-glib-extensions.c')
-rw-r--r--eel/eel-glib-extensions.c1231
1 files changed, 1231 insertions, 0 deletions
diff --git a/eel/eel-glib-extensions.c b/eel/eel-glib-extensions.c
new file mode 100644
index 00000000..b72029fd
--- /dev/null
+++ b/eel/eel-glib-extensions.c
@@ -0,0 +1,1231 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-glib-extensions.c - implementation of new functions that conceptually
+ belong in glib. Perhaps some of these will be
+ actually rolled into glib someday.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: John Sullivan <[email protected]>
+*/
+
+#include <config.h>
+#include "eel-glib-extensions.h"
+
+#include "eel-debug.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-string.h"
+#include "eel-i18n.h"
+#include <glib-object.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <locale.h>
+
+/* Legal conversion specifiers, as specified in the C standard. */
+#define C_STANDARD_STRFTIME_CHARACTERS "aAbBcdHIjmMpSUwWxXyYZ"
+#define C_STANDARD_NUMERIC_STRFTIME_CHARACTERS "dHIjmMSUwWyY"
+#define SUS_EXTENDED_STRFTIME_MODIFIERS "EO"
+
+#define SAFE_SHELL_CHARACTERS "-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+typedef struct
+{
+ GHashTable *hash_table;
+ char *display_name;
+ gboolean keys_known_to_be_strings;
+} HashTableToFree;
+
+static GList *hash_tables_to_free_at_exit;
+
+/**
+ * eel_g_date_new_tm:
+ *
+ * Get a new GDate * for the date represented by a tm struct.
+ * The caller is responsible for g_free-ing the result.
+ * @time_pieces: Pointer to a tm struct representing the date to be converted.
+ *
+ * Returns: Newly allocated date.
+ *
+ **/
+GDate *
+eel_g_date_new_tm (struct tm *time_pieces)
+{
+ /* tm uses 0-based months; GDate uses 1-based months.
+ * tm_year needs 1900 added to get the full year.
+ */
+ return g_date_new_dmy (time_pieces->tm_mday,
+ time_pieces->tm_mon + 1,
+ time_pieces->tm_year + 1900);
+}
+
+/**
+ * eel_strdup_strftime:
+ *
+ * Cover for standard date-and-time-formatting routine strftime that returns
+ * a newly-allocated string of the correct size. The caller is responsible
+ * for g_free-ing the returned string.
+ *
+ * Besides the buffer management, there are two differences between this
+ * and the library strftime:
+ *
+ * 1) The modifiers "-" and "_" between a "%" and a numeric directive
+ * are defined as for the GNU version of strftime. "-" means "do not
+ * pad the field" and "_" means "pad with spaces instead of zeroes".
+ * 2) Non-ANSI extensions to strftime are flagged at runtime with a
+ * warning, so it's easy to notice use of the extensions without
+ * testing with multiple versions of the library.
+ *
+ * @format: format string to pass to strftime. See strftime documentation
+ * for details.
+ * @time_pieces: date/time, in struct format.
+ *
+ * Return value: Newly allocated string containing the formatted time.
+ **/
+char *
+eel_strdup_strftime (const char *format, struct tm *time_pieces)
+{
+ GString *string;
+ const char *remainder, *percent;
+ char code[4], buffer[512];
+ char *piece, *result, *converted;
+ size_t string_length;
+ gboolean strip_leading_zeros, turn_leading_zeros_to_spaces;
+ char modifier;
+ int i;
+
+ /* Format could be translated, and contain UTF-8 chars,
+ * so convert to locale encoding which strftime uses */
+ converted = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
+ g_return_val_if_fail (converted != NULL, NULL);
+
+ string = g_string_new ("");
+ remainder = converted;
+
+ /* Walk from % character to % character. */
+ for (;;)
+ {
+ percent = strchr (remainder, '%');
+ if (percent == NULL)
+ {
+ g_string_append (string, remainder);
+ break;
+ }
+ g_string_append_len (string, remainder,
+ percent - remainder);
+
+ /* Handle the "%" character. */
+ remainder = percent + 1;
+ switch (*remainder)
+ {
+ case '-':
+ strip_leading_zeros = TRUE;
+ turn_leading_zeros_to_spaces = FALSE;
+ remainder++;
+ break;
+ case '_':
+ strip_leading_zeros = FALSE;
+ turn_leading_zeros_to_spaces = TRUE;
+ remainder++;
+ break;
+ case '%':
+ g_string_append_c (string, '%');
+ remainder++;
+ continue;
+ case '\0':
+ g_warning ("Trailing %% passed to eel_strdup_strftime");
+ g_string_append_c (string, '%');
+ continue;
+ default:
+ strip_leading_zeros = FALSE;
+ turn_leading_zeros_to_spaces = FALSE;
+ break;
+ }
+
+ modifier = 0;
+ if (strchr (SUS_EXTENDED_STRFTIME_MODIFIERS, *remainder) != NULL)
+ {
+ modifier = *remainder;
+ remainder++;
+
+ if (*remainder == 0)
+ {
+ g_warning ("Unfinished %%%c modifier passed to eel_strdup_strftime", modifier);
+ break;
+ }
+ }
+
+ if (strchr (C_STANDARD_STRFTIME_CHARACTERS, *remainder) == NULL)
+ {
+ g_warning ("eel_strdup_strftime does not support "
+ "non-standard escape code %%%c",
+ *remainder);
+ }
+
+ /* Convert code to strftime format. We have a fixed
+ * limit here that each code can expand to a maximum
+ * of 512 bytes, which is probably OK. There's no
+ * limit on the total size of the result string.
+ */
+ i = 0;
+ code[i++] = '%';
+ if (modifier != 0)
+ {
+#ifdef HAVE_STRFTIME_EXTENSION
+ code[i++] = modifier;
+#endif
+ }
+ code[i++] = *remainder;
+ code[i++] = '\0';
+ string_length = strftime (buffer, sizeof (buffer),
+ code, time_pieces);
+ if (string_length == 0)
+ {
+ /* We could put a warning here, but there's no
+ * way to tell a successful conversion to
+ * empty string from a failure.
+ */
+ buffer[0] = '\0';
+ }
+
+ /* Strip leading zeros if requested. */
+ piece = buffer;
+ if (strip_leading_zeros || turn_leading_zeros_to_spaces)
+ {
+ if (strchr (C_STANDARD_NUMERIC_STRFTIME_CHARACTERS, *remainder) == NULL)
+ {
+ g_warning ("eel_strdup_strftime does not support "
+ "modifier for non-numeric escape code %%%c%c",
+ remainder[-1],
+ *remainder);
+ }
+ if (*piece == '0')
+ {
+ do
+ {
+ piece++;
+ }
+ while (*piece == '0');
+ if (!g_ascii_isdigit (*piece))
+ {
+ piece--;
+ }
+ }
+ if (turn_leading_zeros_to_spaces)
+ {
+ memset (buffer, ' ', piece - buffer);
+ piece = buffer;
+ }
+ }
+ remainder++;
+
+ /* Add this piece. */
+ g_string_append (string, piece);
+ }
+
+ /* Convert the string back into utf-8. */
+ result = g_locale_to_utf8 (string->str, -1, NULL, NULL, NULL);
+
+ g_string_free (string, TRUE);
+ g_free (converted);
+
+ return result;
+}
+
+/**
+ * eel_g_list_exactly_one_item
+ *
+ * Like g_list_length (list) == 1, only O(1) instead of O(n).
+ * @list: List.
+ *
+ * Return value: TRUE if the list has exactly one item.
+ **/
+gboolean
+eel_g_list_exactly_one_item (GList *list)
+{
+ return list != NULL && list->next == NULL;
+}
+
+/**
+ * eel_g_list_more_than_one_item
+ *
+ * Like g_list_length (list) > 1, only O(1) instead of O(n).
+ * @list: List.
+ *
+ * Return value: TRUE if the list has more than one item.
+ **/
+gboolean
+eel_g_list_more_than_one_item (GList *list)
+{
+ return list != NULL && list->next != NULL;
+}
+
+/**
+ * eel_g_list_equal
+ *
+ * Compares two lists to see if they are equal.
+ * @list_a: First list.
+ * @list_b: Second list.
+ *
+ * Return value: TRUE if the lists are the same length with the same elements.
+ **/
+gboolean
+eel_g_list_equal (GList *list_a, GList *list_b)
+{
+ GList *p, *q;
+
+ for (p = list_a, q = list_b; p != NULL && q != NULL; p = p->next, q = q->next)
+ {
+ if (p->data != q->data)
+ {
+ return FALSE;
+ }
+ }
+ return p == NULL && q == NULL;
+}
+
+/**
+ * eel_g_str_list_equal
+ *
+ * Compares two lists of C strings to see if they are equal.
+ * @list_a: First list.
+ * @list_b: Second list.
+ *
+ * Return value: TRUE if the lists contain the same strings.
+ **/
+gboolean
+eel_g_str_list_equal (GList *list_a, GList *list_b)
+{
+ GList *p, *q;
+
+ for (p = list_a, q = list_b; p != NULL && q != NULL; p = p->next, q = q->next)
+ {
+ if (eel_strcmp (p->data, q->data) != 0)
+ {
+ return FALSE;
+ }
+ }
+ return p == NULL && q == NULL;
+}
+
+/**
+ * eel_g_str_list_copy
+ *
+ * @list: List of strings and/or NULLs to copy.
+ * Return value: Deep copy of @list.
+ **/
+GList *
+eel_g_str_list_copy (GList *list)
+{
+ GList *node, *result;
+
+ result = NULL;
+
+ for (node = g_list_last (list); node != NULL; node = node->prev)
+ {
+ result = g_list_prepend (result, g_strdup (node->data));
+ }
+ return result;
+}
+
+/**
+ * eel_g_str_list_alphabetize
+ *
+ * Sort a list of strings using locale-sensitive rules.
+ *
+ * @list: List of strings and/or NULLs.
+ *
+ * Return value: @list, sorted.
+ **/
+GList *
+eel_g_str_list_alphabetize (GList *list)
+{
+ return g_list_sort (list, (GCompareFunc) g_utf8_collate);
+}
+
+int
+eel_g_str_list_index (GList *str_list,
+ const char *str)
+{
+ int i;
+ GList *l;
+ for (i = 0, l = str_list; l != NULL; l = l->next, i++)
+ {
+ if (!strcmp (str, (const char*)l->data))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * eel_g_list_free_deep_custom
+ *
+ * Frees the elements of a list and then the list, using a custom free function.
+ *
+ * @list: List of elements that can be freed with the provided free function.
+ * @element_free_func: function to call with the data pointer and user_data to free it.
+ * @user_data: User data to pass to element_free_func
+ **/
+void
+eel_g_list_free_deep_custom (GList *list, GFunc element_free_func, gpointer user_data)
+{
+ g_list_foreach (list, element_free_func, user_data);
+ g_list_free (list);
+}
+
+/**
+ * eel_g_list_free_deep
+ *
+ * Frees the elements of a list and then the list.
+ * @list: List of elements that can be freed with g_free.
+ **/
+void
+eel_g_list_free_deep (GList *list)
+{
+ eel_g_list_free_deep_custom (list, (GFunc) g_free, NULL);
+}
+
+/**
+ * eel_g_list_free_deep_custom
+ *
+ * Frees the elements of a list and then the list, using a custom free function.
+ *
+ * @list: List of elements that can be freed with the provided free function.
+ * @element_free_func: function to call with the data pointer and user_data to free it.
+ * @user_data: User data to pass to element_free_func
+ **/
+void
+eel_g_slist_free_deep_custom (GSList *list, GFunc element_free_func, gpointer user_data)
+{
+ g_slist_foreach (list, element_free_func, user_data);
+ g_slist_free (list);
+}
+
+/**
+ * eel_g_slist_free_deep
+ *
+ * Frees the elements of a list and then the list.
+ * @list: List of elements that can be freed with g_free.
+ **/
+void
+eel_g_slist_free_deep (GSList *list)
+{
+ eel_g_slist_free_deep_custom (list, (GFunc) g_free, NULL);
+}
+
+
+/**
+ * eel_g_strv_find
+ *
+ * Get index of string in array of strings.
+ *
+ * @strv: NULL-terminated array of strings.
+ * @find_me: string to search for.
+ *
+ * Return value: index of array entry in @strv that
+ * matches @find_me, or -1 if no matching entry.
+ */
+int
+eel_g_strv_find (char **strv, const char *find_me)
+{
+ int index;
+
+ g_return_val_if_fail (find_me != NULL, -1);
+
+ for (index = 0; strv[index] != NULL; ++index)
+ {
+ if (strcmp (strv[index], find_me) == 0)
+ {
+ return index;
+ }
+ }
+
+ return -1;
+}
+
+gboolean
+eel_g_strv_equal (char **a, char **b)
+{
+ int i;
+
+ if (g_strv_length (a) != g_strv_length (b))
+ {
+ return FALSE;
+ }
+
+ for (i = 0; a[i] != NULL; i++)
+ {
+ if (strcmp (a[i], b[i]) != 0)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static int
+compare_pointers (gconstpointer pointer_1, gconstpointer pointer_2)
+{
+ if ((const char *) pointer_1 < (const char *) pointer_2)
+ {
+ return -1;
+ }
+ if ((const char *) pointer_1 > (const char *) pointer_2)
+ {
+ return +1;
+ }
+ return 0;
+}
+
+gboolean
+eel_g_lists_sort_and_check_for_intersection (GList **list_1,
+ GList **list_2)
+
+{
+ GList *node_1, *node_2;
+ int compare_result;
+
+ *list_1 = g_list_sort (*list_1, compare_pointers);
+ *list_2 = g_list_sort (*list_2, compare_pointers);
+
+ node_1 = *list_1;
+ node_2 = *list_2;
+
+ while (node_1 != NULL && node_2 != NULL)
+ {
+ compare_result = compare_pointers (node_1->data, node_2->data);
+ if (compare_result == 0)
+ {
+ return TRUE;
+ }
+ if (compare_result <= 0)
+ {
+ node_1 = node_1->next;
+ }
+ if (compare_result >= 0)
+ {
+ node_2 = node_2->next;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * eel_g_list_partition
+ *
+ * Parition a list into two parts depending on whether the data
+ * elements satisfy a provided predicate. Order is preserved in both
+ * of the resulting lists, and the original list is consumed. A list
+ * of the items that satisfy the predicate is returned, and the list
+ * of items not satisfying the predicate is returned via the failed
+ * out argument.
+ *
+ * @list: List to partition.
+ * @predicate: Function to call on each element.
+ * @user_data: Data to pass to function.
+ * @failed: The GList * variable pointed to by this argument will be
+ * set to the list of elements for which the predicate returned
+ * false. */
+
+GList *
+eel_g_list_partition (GList *list,
+ EelPredicateFunction predicate,
+ gpointer user_data,
+ GList **failed)
+{
+ GList *predicate_true;
+ GList *predicate_false;
+ GList *reverse;
+ GList *p;
+ GList *next;
+
+ predicate_true = NULL;
+ predicate_false = NULL;
+
+ reverse = g_list_reverse (list);
+
+ for (p = reverse; p != NULL; p = next)
+ {
+ next = p->next;
+
+ if (next != NULL)
+ {
+ next->prev = NULL;
+ }
+
+ if (predicate (p->data, user_data))
+ {
+ p->next = predicate_true;
+ if (predicate_true != NULL)
+ {
+ predicate_true->prev = p;
+ }
+ predicate_true = p;
+ }
+ else
+ {
+ p->next = predicate_false;
+ if (predicate_false != NULL)
+ {
+ predicate_false->prev = p;
+ }
+ predicate_false = p;
+ }
+ }
+
+ *failed = predicate_false;
+ return predicate_true;
+}
+
+/**
+ * eel_get_system_time
+ *
+ * Return value: number of microseconds since the machine was turned on
+ */
+gint64
+eel_get_system_time (void)
+{
+ struct timeval tmp;
+
+ gettimeofday (&tmp, NULL);
+ return (gint64)tmp.tv_usec + (gint64)tmp.tv_sec * G_GINT64_CONSTANT (1000000);
+}
+
+static void
+print_key_string (gpointer key, gpointer value, gpointer callback_data)
+{
+ g_assert (callback_data == NULL);
+
+ g_print ("--> %s\n", (char *) key);
+}
+
+static void
+free_hash_tables_at_exit (void)
+{
+ GList *p;
+ HashTableToFree *hash_table_to_free;
+ guint size;
+
+ for (p = hash_tables_to_free_at_exit; p != NULL; p = p->next)
+ {
+ hash_table_to_free = p->data;
+
+ size = g_hash_table_size (hash_table_to_free->hash_table);
+ if (size != 0)
+ {
+ if (hash_table_to_free->keys_known_to_be_strings)
+ {
+ g_print ("\n--- Hash table keys for warning below:\n");
+ g_hash_table_foreach (hash_table_to_free->hash_table,
+ print_key_string,
+ NULL);
+ }
+ g_warning ("\"%s\" hash table still has %u element%s at quit time%s",
+ hash_table_to_free->display_name, size,
+ size == 1 ? "" : "s",
+ hash_table_to_free->keys_known_to_be_strings
+ ? " (keys above)" : "");
+ }
+
+ g_hash_table_destroy (hash_table_to_free->hash_table);
+ g_free (hash_table_to_free->display_name);
+ g_free (hash_table_to_free);
+ }
+ g_list_free (hash_tables_to_free_at_exit);
+ hash_tables_to_free_at_exit = NULL;
+}
+
+GHashTable *
+eel_g_hash_table_new_free_at_exit (GHashFunc hash_func,
+ GCompareFunc key_compare_func,
+ const char *display_name)
+{
+ GHashTable *hash_table;
+ HashTableToFree *hash_table_to_free;
+
+ /* FIXME: We can take out the CAJA_DEBUG check once we
+ * have fixed more of the leaks. For now, it's a bit too noisy
+ * for the general public.
+ */
+ if (hash_tables_to_free_at_exit == NULL)
+ {
+ eel_debug_call_at_shutdown (free_hash_tables_at_exit);
+ }
+
+ hash_table = g_hash_table_new (hash_func, key_compare_func);
+
+ hash_table_to_free = g_new (HashTableToFree, 1);
+ hash_table_to_free->hash_table = hash_table;
+ hash_table_to_free->display_name = g_strdup (display_name);
+ hash_table_to_free->keys_known_to_be_strings =
+ hash_func == g_str_hash;
+
+ hash_tables_to_free_at_exit = g_list_prepend
+ (hash_tables_to_free_at_exit, hash_table_to_free);
+
+ return hash_table;
+}
+
+typedef struct
+{
+ GList *keys;
+ GList *values;
+} FlattenedHashTable;
+
+static void
+flatten_hash_table_element (gpointer key, gpointer value, gpointer callback_data)
+{
+ FlattenedHashTable *flattened_table;
+
+ flattened_table = callback_data;
+ flattened_table->keys = g_list_prepend
+ (flattened_table->keys, key);
+ flattened_table->values = g_list_prepend
+ (flattened_table->values, value);
+}
+
+void
+eel_g_hash_table_safe_for_each (GHashTable *hash_table,
+ GHFunc callback,
+ gpointer callback_data)
+{
+ FlattenedHashTable flattened;
+ GList *p, *q;
+
+ flattened.keys = NULL;
+ flattened.values = NULL;
+
+ g_hash_table_foreach (hash_table,
+ flatten_hash_table_element,
+ &flattened);
+
+ for (p = flattened.keys, q = flattened.values;
+ p != NULL;
+ p = p->next, q = q->next)
+ {
+ (* callback) (p->data, q->data, callback_data);
+ }
+
+ g_list_free (flattened.keys);
+ g_list_free (flattened.values);
+}
+
+int
+eel_round (double d)
+{
+ double val;
+
+ val = floor (d + .5);
+
+ /* The tests are needed because the result of floating-point to integral
+ * conversion is undefined if the floating point value is not representable
+ * in the new type. E.g. the magnititude is too large or a negative
+ * floating-point value being converted to an unsigned.
+ */
+ g_return_val_if_fail (val <= INT_MAX, INT_MAX);
+ g_return_val_if_fail (val >= INT_MIN, INT_MIN);
+
+ return val;
+}
+
+GList *
+eel_g_list_from_g_slist (GSList *slist)
+{
+ GList *list;
+ GSList *node;
+
+ list = NULL;
+ for (node = slist; node != NULL; node = node->next)
+ {
+ list = g_list_prepend (list, node->data);
+ }
+ return g_list_reverse (list);
+}
+
+GSList *
+eel_g_slist_from_g_list (GList *list)
+{
+ GSList *slist;
+ GList *node;
+
+ slist = NULL;
+ for (node = list; node != NULL; node = node->next)
+ {
+ slist = g_slist_prepend (slist, node->data);
+ }
+ return g_slist_reverse (slist);
+}
+
+/* Return the operating system name: Linux, Solaris, etc. */
+char *
+eel_get_operating_system_name (void)
+{
+ struct utsname buffer;
+
+ if (uname (&buffer) != -1)
+ {
+ /* Check for special sysnames for which there is
+ * more accepted names.
+ */
+ if (eel_str_is_equal (buffer.sysname, "SunOS"))
+ {
+ return g_strdup ("Solaris");
+ }
+
+ return g_strdup (buffer.sysname);
+ }
+
+ return g_strdup ("Unix");
+}
+
+int
+eel_compare_integer (gconstpointer a,
+ gconstpointer b)
+{
+ int int_a;
+ int int_b;
+
+ int_a = GPOINTER_TO_INT (a);
+ int_b = GPOINTER_TO_INT (b);
+
+ if (int_a == int_b)
+ {
+ return 0;
+ }
+
+ return int_a < int_b ? -1 : 1;
+}
+
+/**
+ * eel_g_object_list_ref
+ *
+ * Ref all the objects in a list.
+ * @list: GList of objects.
+ **/
+GList *
+eel_g_object_list_ref (GList *list)
+{
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ return list;
+}
+
+/**
+ * eel_g_object_list_unref
+ *
+ * Unref all the objects in a list.
+ * @list: GList of objects.
+ **/
+void
+eel_g_object_list_unref (GList *list)
+{
+ g_list_foreach (list, (GFunc) g_object_unref, NULL);
+}
+
+/**
+ * eel_g_object_list_free
+ *
+ * Free a list of objects after unrefing them.
+ * @list: GList of objects.
+ **/
+void
+eel_g_object_list_free (GList *list)
+{
+ eel_g_object_list_unref (list);
+ g_list_free (list);
+}
+
+/**
+ * eel_g_object_list_copy
+ *
+ * Copy the list of objects, ref'ing each one.
+ * @list: GList of objects.
+ **/
+GList *
+eel_g_object_list_copy (GList *list)
+{
+ return g_list_copy (eel_g_object_list_ref (list));
+}
+
+/**
+ * eel_add_weak_pointer
+ *
+ * Nulls out a saved reference to an object when the object gets destroyed.
+ *
+ * @pointer_location: Address of the saved pointer.
+ **/
+void
+eel_add_weak_pointer (gpointer pointer_location)
+{
+ gpointer *object_location;
+
+ g_return_if_fail (pointer_location != NULL);
+
+ object_location = (gpointer *) pointer_location;
+ if (*object_location == NULL)
+ {
+ /* The reference is NULL, nothing to do. */
+ return;
+ }
+
+ g_return_if_fail (G_IS_OBJECT (*object_location));
+
+ g_object_add_weak_pointer (G_OBJECT (*object_location),
+ object_location);
+}
+
+/**
+ * eel_remove_weak_pointer
+ *
+ * Removes the weak pointer that was added by eel_add_weak_pointer.
+ * Also nulls out the pointer.
+ *
+ * @pointer_location: Pointer that was passed to eel_add_weak_pointer.
+ **/
+void
+eel_remove_weak_pointer (gpointer pointer_location)
+{
+ gpointer *object_location;
+
+ g_return_if_fail (pointer_location != NULL);
+
+ object_location = (gpointer *) pointer_location;
+ if (*object_location == NULL)
+ {
+ /* The object was already destroyed and the reference
+ * nulled out, nothing to do.
+ */
+ return;
+ }
+
+ g_return_if_fail (G_IS_OBJECT (*object_location));
+
+ g_object_remove_weak_pointer (G_OBJECT (*object_location),
+ object_location);
+
+ *object_location = NULL;
+}
+
+/* Get the filename encoding, returns TRUE if utf8 */
+
+typedef struct _EelFilenameCharsetCache EelFilenameCharsetCache;
+
+struct _EelFilenameCharsetCache
+{
+ gboolean is_utf8;
+ gchar *charset;
+ gchar *filename_charset;
+};
+
+static void
+filename_charset_cache_free (gpointer data)
+{
+ EelFilenameCharsetCache *cache = data;
+ g_free (cache->charset);
+ g_free (cache->filename_charset);
+ g_free (cache);
+}
+
+/*
+ * eel_get_filename_charset:
+ * @charset: return location for the name of the filename encoding
+ *
+ * Determines the character set used for filenames by consulting the
+ * environment variables G_FILENAME_ENCODING and G_BROKEN_FILENAMES.
+ *
+ * G_FILENAME_ENCODING may be set to a comma-separated list of character
+ * set names. The special token "@locale" is taken to mean the character set
+ * for the current locale. The first character set from the list is taken
+ * as the filename encoding.
+ * If G_FILENAME_ENCODING is not set, but G_BROKEN_FILENAMES is, the
+ * character set of the current locale is taken as the filename encoding.
+ *
+ * The returned @charset belongs to Eel and must not be freed.
+ *
+ * Return value: %TRUE if the charset used for filename is UTF-8.
+ */
+gboolean
+eel_get_filename_charset (const gchar **filename_charset)
+{
+ static GStaticPrivate cache_private = G_STATIC_PRIVATE_INIT;
+ EelFilenameCharsetCache *cache = g_static_private_get (&cache_private);
+ const gchar *charset;
+
+ if (!cache)
+ {
+ cache = g_new0 (EelFilenameCharsetCache, 1);
+ g_static_private_set (&cache_private, cache, filename_charset_cache_free);
+ }
+
+ g_get_charset (&charset);
+
+ if (!(cache->charset && strcmp (cache->charset, charset) == 0))
+ {
+ const gchar *new_charset;
+ gchar *p, *q;
+
+ g_free (cache->charset);
+ g_free (cache->filename_charset);
+ cache->charset = g_strdup (charset);
+
+ p = getenv ("G_FILENAME_ENCODING");
+ if (p != NULL)
+ {
+ q = strchr (p, ',');
+ if (!q)
+ q = p + strlen (p);
+
+ if (strncmp ("@locale", p, q - p) == 0)
+ {
+ cache->is_utf8 = g_get_charset (&new_charset);
+ cache->filename_charset = g_strdup (new_charset);
+ }
+ else
+ {
+ cache->filename_charset = g_strndup (p, q - p);
+ cache->is_utf8 = (strcmp (cache->filename_charset, "UTF-8") == 0);
+ }
+ }
+ else if (getenv ("G_BROKEN_FILENAMES") != NULL)
+ {
+ cache->is_utf8 = g_get_charset (&new_charset);
+ cache->filename_charset = g_strdup (new_charset);
+ }
+ else
+ {
+ cache->filename_charset = g_strdup ("UTF-8");
+ cache->is_utf8 = TRUE;
+ }
+ }
+
+ if (filename_charset)
+ *filename_charset = cache->filename_charset;
+
+ return cache->is_utf8;
+}
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+static void
+check_tm_to_g_date (time_t time)
+{
+ struct tm *before_conversion;
+ struct tm after_conversion;
+ GDate *date;
+
+ before_conversion = localtime (&time);
+ date = eel_g_date_new_tm (before_conversion);
+
+ g_date_to_struct_tm (date, &after_conversion);
+
+ g_date_free (date);
+
+ EEL_CHECK_INTEGER_RESULT (after_conversion.tm_mday,
+ before_conversion->tm_mday);
+ EEL_CHECK_INTEGER_RESULT (after_conversion.tm_mon,
+ before_conversion->tm_mon);
+ EEL_CHECK_INTEGER_RESULT (after_conversion.tm_year,
+ before_conversion->tm_year);
+}
+
+static gboolean
+eel_test_predicate (gpointer data,
+ gpointer callback_data)
+{
+ return g_ascii_strcasecmp (data, callback_data) <= 0;
+}
+
+static char *
+test_strftime (const char *format,
+ int year,
+ int month,
+ int day,
+ int hour,
+ int minute,
+ int second)
+{
+ struct tm time_pieces;
+
+ time_pieces.tm_sec = second;
+ time_pieces.tm_min = minute;
+ time_pieces.tm_hour = hour;
+ time_pieces.tm_mday = day;
+ time_pieces.tm_mon = month - 1;
+ time_pieces.tm_year = year - 1900;
+ time_pieces.tm_isdst = -1;
+ mktime (&time_pieces);
+
+ return eel_strdup_strftime (format, &time_pieces);
+}
+
+void
+eel_self_check_glib_extensions (void)
+{
+ char **strv;
+ GList *compare_list_1;
+ GList *compare_list_2;
+ GList *compare_list_3;
+ GList *compare_list_4;
+ GList *compare_list_5;
+ gint64 time1, time2;
+ GList *list_to_partition;
+ GList *expected_passed;
+ GList *expected_failed;
+ GList *actual_passed;
+ GList *actual_failed;
+ char *huge_string;
+
+ check_tm_to_g_date (0); /* lower limit */
+ check_tm_to_g_date ((time_t) -1); /* upper limit */
+ check_tm_to_g_date (time (NULL)); /* current time */
+
+ strv = g_strsplit ("zero|one|two|three|four", "|", 0);
+ EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "zero"), 0);
+ EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "one"), 1);
+ EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "four"), 4);
+ EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "five"), -1);
+ EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, ""), -1);
+ EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "o"), -1);
+ g_strfreev (strv);
+
+ /* eel_get_system_time */
+ time1 = eel_get_system_time ();
+ time2 = eel_get_system_time ();
+ EEL_CHECK_BOOLEAN_RESULT (time1 - time2 > -1000, TRUE);
+ EEL_CHECK_BOOLEAN_RESULT (time1 - time2 <= 0, TRUE);
+
+ /* eel_g_str_list_equal */
+
+ /* We g_strdup because identical string constants can be shared. */
+
+ compare_list_1 = NULL;
+ compare_list_1 = g_list_append (compare_list_1, g_strdup ("Apple"));
+ compare_list_1 = g_list_append (compare_list_1, g_strdup ("zebra"));
+ compare_list_1 = g_list_append (compare_list_1, g_strdup ("!@#!@$#@$!"));
+
+ compare_list_2 = NULL;
+ compare_list_2 = g_list_append (compare_list_2, g_strdup ("Apple"));
+ compare_list_2 = g_list_append (compare_list_2, g_strdup ("zebra"));
+ compare_list_2 = g_list_append (compare_list_2, g_strdup ("!@#!@$#@$!"));
+
+ compare_list_3 = NULL;
+ compare_list_3 = g_list_append (compare_list_3, g_strdup ("Apple"));
+ compare_list_3 = g_list_append (compare_list_3, g_strdup ("zebra"));
+
+ compare_list_4 = NULL;
+ compare_list_4 = g_list_append (compare_list_4, g_strdup ("Apple"));
+ compare_list_4 = g_list_append (compare_list_4, g_strdup ("zebra"));
+ compare_list_4 = g_list_append (compare_list_4, g_strdup ("!@#!@$#@$!"));
+ compare_list_4 = g_list_append (compare_list_4, g_strdup ("foobar"));
+
+ compare_list_5 = NULL;
+ compare_list_5 = g_list_append (compare_list_5, g_strdup ("Apple"));
+ compare_list_5 = g_list_append (compare_list_5, g_strdup ("zzzzzebraaaaaa"));
+ compare_list_5 = g_list_append (compare_list_5, g_strdup ("!@#!@$#@$!"));
+
+ EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_2), TRUE);
+ EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_3), FALSE);
+ EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_4), FALSE);
+ EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_5), FALSE);
+
+ eel_g_list_free_deep (compare_list_1);
+ eel_g_list_free_deep (compare_list_2);
+ eel_g_list_free_deep (compare_list_3);
+ eel_g_list_free_deep (compare_list_4);
+ eel_g_list_free_deep (compare_list_5);
+
+ /* eel_g_list_partition */
+
+ list_to_partition = NULL;
+ list_to_partition = g_list_append (list_to_partition, "Cadillac");
+ list_to_partition = g_list_append (list_to_partition, "Pontiac");
+ list_to_partition = g_list_append (list_to_partition, "Ford");
+ list_to_partition = g_list_append (list_to_partition, "Range Rover");
+
+ expected_passed = NULL;
+ expected_passed = g_list_append (expected_passed, "Cadillac");
+ expected_passed = g_list_append (expected_passed, "Ford");
+
+ expected_failed = NULL;
+ expected_failed = g_list_append (expected_failed, "Pontiac");
+ expected_failed = g_list_append (expected_failed, "Range Rover");
+
+ actual_passed = eel_g_list_partition (list_to_partition,
+ eel_test_predicate,
+ "m",
+ &actual_failed);
+
+ EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (expected_passed, actual_passed), TRUE);
+ EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (expected_failed, actual_failed), TRUE);
+
+ /* Don't free "list_to_partition", since it is consumed
+ * by eel_g_list_partition.
+ */
+
+ g_list_free (expected_passed);
+ g_list_free (actual_passed);
+ g_list_free (expected_failed);
+ g_list_free (actual_failed);
+
+ /* eel_strdup_strftime */
+ huge_string = g_new (char, 10000+1);
+ memset (huge_string, 'a', 10000);
+ huge_string[10000] = '\0';
+
+ setlocale (LC_TIME, "C");
+
+ EEL_CHECK_STRING_RESULT (test_strftime ("", 2000, 1, 1, 0, 0, 0), "");
+ EEL_CHECK_STRING_RESULT (test_strftime (huge_string, 2000, 1, 1, 0, 0, 0), huge_string);
+ EEL_CHECK_STRING_RESULT (test_strftime ("%%", 2000, 1, 1, 1, 0, 0), "%");
+ EEL_CHECK_STRING_RESULT (test_strftime ("%%%%", 2000, 1, 1, 1, 0, 0), "%%");
+ EEL_CHECK_STRING_RESULT (test_strftime ("%m/%d/%y, %I:%M %p", 2000, 1, 1, 1, 0, 0), "01/01/00, 01:00 AM");
+ EEL_CHECK_STRING_RESULT (test_strftime ("%-m/%-d/%y, %-I:%M %p", 2000, 1, 1, 1, 0, 0), "1/1/00, 1:00 AM");
+ EEL_CHECK_STRING_RESULT (test_strftime ("%_m/%_d/%y, %_I:%M %p", 2000, 1, 1, 1, 0, 0), " 1/ 1/00, 1:00 AM");
+
+ setlocale (LC_TIME, "");
+
+ g_free (huge_string);
+
+ /* eel_shell_quote */
+ EEL_CHECK_STRING_RESULT (g_shell_quote (""), "''");
+ EEL_CHECK_STRING_RESULT (g_shell_quote ("a"), "'a'");
+ EEL_CHECK_STRING_RESULT (g_shell_quote ("("), "'('");
+ EEL_CHECK_STRING_RESULT (g_shell_quote ("'"), "''\\'''");
+ EEL_CHECK_STRING_RESULT (g_shell_quote ("'a"), "''\\''a'");
+ EEL_CHECK_STRING_RESULT (g_shell_quote ("a'"), "'a'\\'''");
+ EEL_CHECK_STRING_RESULT (g_shell_quote ("a'a"), "'a'\\''a'");
+
+ /* eel_compare_integer */
+ EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (0), GINT_TO_POINTER (0)), 0);
+ EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (0), GINT_TO_POINTER (1)), -1);
+ EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (1), GINT_TO_POINTER (0)), 1);
+ EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (-1), GINT_TO_POINTER (0)), -1);
+ EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (0), GINT_TO_POINTER (-1)), 1);
+ EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (-1), GINT_TO_POINTER (-1)), 0);
+
+#ifdef __linux__
+ EEL_CHECK_STRING_RESULT (eel_get_operating_system_name (), "Linux");
+#endif
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */