diff --git a/glib/deprecated/gallocator.c b/glib/deprecated/gallocator.c
new file mode 100644
index 0000000000000000000000000000000000000000..66483b6323a495cbcadf846859b6a8e286042049
--- /dev/null
+++ b/glib/deprecated/gallocator.c
@@ -0,0 +1,104 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include "config.h"
+
+/* we know we are deprecated here, no need for warnings */
+#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+#endif
+
+#include "gallocator.h"
+
+#include
+#include
+
+struct _GMemChunk {
+ guint alloc_size; /* the size of an atom */
+};
+
+GMemChunk*
+g_mem_chunk_new (const gchar *name,
+ gint atom_size,
+ gsize area_size,
+ gint type)
+{
+ GMemChunk *mem_chunk;
+
+ g_return_val_if_fail (atom_size > 0, NULL);
+
+ mem_chunk = g_slice_new (GMemChunk);
+ mem_chunk->alloc_size = atom_size;
+
+ return mem_chunk;
+}
+
+void
+g_mem_chunk_destroy (GMemChunk *mem_chunk)
+{
+ g_return_if_fail (mem_chunk != NULL);
+
+ g_slice_free (GMemChunk, mem_chunk);
+}
+
+gpointer
+g_mem_chunk_alloc (GMemChunk *mem_chunk)
+{
+ g_return_val_if_fail (mem_chunk != NULL, NULL);
+
+ return g_slice_alloc (mem_chunk->alloc_size);
+}
+
+gpointer
+g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
+{
+ g_return_val_if_fail (mem_chunk != NULL, NULL);
+
+ return g_slice_alloc0 (mem_chunk->alloc_size);
+}
+
+void
+g_mem_chunk_free (GMemChunk *mem_chunk,
+ gpointer mem)
+{
+ g_return_if_fail (mem_chunk != NULL);
+
+ g_slice_free1 (mem_chunk->alloc_size, mem);
+}
+
+GAllocator*
+g_allocator_new (const gchar *name,
+ guint n_preallocs)
+{
+ /* some (broken) GAllocator uses depend on non-NULL allocators */
+ return (void *) 1;
+}
+
+void g_allocator_free (GAllocator *allocator) { }
+
+void g_mem_chunk_clean (GMemChunk *mem_chunk) { }
+void g_mem_chunk_reset (GMemChunk *mem_chunk) { }
+void g_mem_chunk_print (GMemChunk *mem_chunk) { }
+void g_mem_chunk_info (void) { }
+void g_blow_chunks (void) { }
+
+void g_list_push_allocator (GAllocator *allocator) { }
+void g_list_pop_allocator (void) { }
+
+void g_slist_push_allocator (GAllocator *allocator) { }
+void g_slist_pop_allocator (void) { }
+
+void g_node_push_allocator (GAllocator *allocator) { }
+void g_node_pop_allocator (void) { }
diff --git a/glib/deprecated/gallocator.h b/glib/deprecated/gallocator.h
new file mode 100644
index 0000000000000000000000000000000000000000..005e92b6eb1e991cf40194049020e77c4de0cd1d
--- /dev/null
+++ b/glib/deprecated/gallocator.h
@@ -0,0 +1,88 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#ifndef __G_ALLOCATOR_H__
+#define __G_ALLOCATOR_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GAllocator GAllocator;
+typedef struct _GMemChunk GMemChunk;
+
+#define G_ALLOC_ONLY 1
+#define G_ALLOC_AND_FREE 2
+#define G_ALLOCATOR_LIST 1
+#define G_ALLOCATOR_SLIST 2
+#define G_ALLOCATOR_NODE 3
+
+#define g_chunk_new(type, chunk) ((type *) g_mem_chunk_alloc (chunk))
+#define g_chunk_new0(type, chunk) ((type *) g_mem_chunk_alloc0 (chunk))
+#define g_chunk_free(mem, mem_chunk) (g_mem_chunk_free (mem_chunk, mem))
+#define g_mem_chunk_create(type, x, y) (g_mem_chunk_new (NULL, sizeof (type), 0, 0))
+
+
+GLIB_DEPRECATED
+GMemChunk * g_mem_chunk_new (const gchar *name,
+ gint atom_size,
+ gsize area_size,
+ gint type);
+GLIB_DEPRECATED
+void g_mem_chunk_destroy (GMemChunk *mem_chunk);
+GLIB_DEPRECATED
+gpointer g_mem_chunk_alloc (GMemChunk *mem_chunk);
+GLIB_DEPRECATED
+gpointer g_mem_chunk_alloc0 (GMemChunk *mem_chunk);
+GLIB_DEPRECATED
+void g_mem_chunk_free (GMemChunk *mem_chunk,
+ gpointer mem);
+GLIB_DEPRECATED
+void g_mem_chunk_clean (GMemChunk *mem_chunk);
+GLIB_DEPRECATED
+void g_mem_chunk_reset (GMemChunk *mem_chunk);
+GLIB_DEPRECATED
+void g_mem_chunk_print (GMemChunk *mem_chunk);
+GLIB_DEPRECATED
+void g_mem_chunk_info (void);
+GLIB_DEPRECATED
+void g_blow_chunks (void);
+
+
+GLIB_DEPRECATED
+GAllocator * g_allocator_new (const gchar *name,
+ guint n_preallocs);
+GLIB_DEPRECATED
+void g_allocator_free (GAllocator *allocator);
+GLIB_DEPRECATED
+void g_list_push_allocator (GAllocator *allocator);
+GLIB_DEPRECATED
+void g_list_pop_allocator (void);
+GLIB_DEPRECATED
+void g_slist_push_allocator (GAllocator *allocator);
+GLIB_DEPRECATED
+void g_slist_pop_allocator (void);
+GLIB_DEPRECATED
+void g_node_push_allocator (GAllocator *allocator);
+GLIB_DEPRECATED
+void g_node_pop_allocator (void);
+
+G_END_DECLS
+
+#endif /* __G_ALLOCATOR_H__ */
diff --git a/glib/deprecated/gcache.c b/glib/deprecated/gcache.c
new file mode 100644
index 0000000000000000000000000000000000000000..9e04145be7fad9039c490b3cbff9171eb79679c1
--- /dev/null
+++ b/glib/deprecated/gcache.c
@@ -0,0 +1,350 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+/* we know we are deprecated here, no need for warnings */
+#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+#endif
+
+#include "gcache.h"
+
+#include "gslice.h"
+#include "ghash.h"
+#include "gtestutils.h"
+
+/**
+ * SECTION:caches
+ * @title: Caches
+ * @short_description: caches allow sharing of complex data structures
+ * to save resources
+ *
+ * A #GCache allows sharing of complex data structures, in order to
+ * save system resources.
+ *
+ * GCache uses keys and values. A GCache key describes the properties
+ * of a particular resource. A GCache value is the actual resource.
+ *
+ * GCache has been marked as deprecated, since this API is rarely
+ * used and not very actively maintained.
+ */
+
+typedef struct _GCacheNode GCacheNode;
+
+struct _GCacheNode
+{
+ /* A reference counted node */
+ gpointer value;
+ gint ref_count;
+};
+
+/**
+ * GCache:
+ *
+ * The #GCache struct is an opaque data structure containing
+ * information about a #GCache. It should only be accessed via the
+ * following functions.
+ *
+ * Deprecated:2.32: Use a #GHashTable instead
+ */
+struct _GCache
+{
+ /* Called to create a value from a key */
+ GCacheNewFunc value_new_func;
+
+ /* Called to destroy a value */
+ GCacheDestroyFunc value_destroy_func;
+
+ /* Called to duplicate a key */
+ GCacheDupFunc key_dup_func;
+
+ /* Called to destroy a key */
+ GCacheDestroyFunc key_destroy_func;
+
+ /* Associates keys with nodes */
+ GHashTable *key_table;
+
+ /* Associates nodes with keys */
+ GHashTable *value_table;
+};
+
+static inline GCacheNode*
+g_cache_node_new (gpointer value)
+{
+ GCacheNode *node = g_slice_new (GCacheNode);
+ node->value = value;
+ node->ref_count = 1;
+ return node;
+}
+
+static inline void
+g_cache_node_destroy (GCacheNode *node)
+{
+ g_slice_free (GCacheNode, node);
+}
+
+/**
+ * g_cache_new:
+ * @value_new_func: a function to create a new object given a key.
+ * This is called by g_cache_insert() if an object
+ * with the given key does not already exist
+ * @value_destroy_func: a function to destroy an object. It is called
+ * by g_cache_remove() when the object is no
+ * longer needed (i.e. its reference count drops
+ * to 0)
+ * @key_dup_func: a function to copy a key. It is called by
+ * g_cache_insert() if the key does not already exist in
+ * the #GCache
+ * @key_destroy_func: a function to destroy a key. It is called by
+ * g_cache_remove() when the object is no longer
+ * needed (i.e. its reference count drops to 0)
+ * @hash_key_func: a function to create a hash value from a key
+ * @hash_value_func: a function to create a hash value from a value
+ * @key_equal_func: a function to compare two keys. It should return
+ * %TRUE if the two keys are equivalent
+ *
+ * Creates a new #GCache.
+ *
+ * Returns: a new #GCache
+ *
+ * Deprecated:2.32: Use a #GHashTable instead
+ */
+
+/**
+ * GCacheNewFunc:
+ * @key: a #GCache key
+ *
+ * Specifies the type of the @value_new_func function passed to
+ * g_cache_new(). It is passed a #GCache key and should create the
+ * value corresponding to the key.
+ *
+ * Returns: a new #GCache value corresponding to the key.
+ */
+
+/**
+ * GCacheDestroyFunc:
+ * @value: the #GCache value to destroy
+ *
+ * Specifies the type of the @value_destroy_func and @key_destroy_func
+ * functions passed to g_cache_new(). The functions are passed a
+ * pointer to the #GCache key or #GCache value and should free any
+ * memory and other resources associated with it.
+ */
+
+/**
+ * GCacheDupFunc:
+ * @value: the #GCache key to destroy (__not__ a
+ * #GCache value as it seems)
+ *
+ * Specifies the type of the @key_dup_func function passed to
+ * g_cache_new(). The function is passed a key
+ * (__not__ a value as the prototype implies) and
+ * should return a duplicate of the key.
+ *
+ * Returns: a copy of the #GCache key
+ */
+GCache*
+g_cache_new (GCacheNewFunc value_new_func,
+ GCacheDestroyFunc value_destroy_func,
+ GCacheDupFunc key_dup_func,
+ GCacheDestroyFunc key_destroy_func,
+ GHashFunc hash_key_func,
+ GHashFunc hash_value_func,
+ GEqualFunc key_equal_func)
+{
+ GCache *cache;
+
+ g_return_val_if_fail (value_new_func != NULL, NULL);
+ g_return_val_if_fail (value_destroy_func != NULL, NULL);
+ g_return_val_if_fail (key_dup_func != NULL, NULL);
+ g_return_val_if_fail (key_destroy_func != NULL, NULL);
+ g_return_val_if_fail (hash_key_func != NULL, NULL);
+ g_return_val_if_fail (hash_value_func != NULL, NULL);
+ g_return_val_if_fail (key_equal_func != NULL, NULL);
+
+ cache = g_slice_new (GCache);
+ cache->value_new_func = value_new_func;
+ cache->value_destroy_func = value_destroy_func;
+ cache->key_dup_func = key_dup_func;
+ cache->key_destroy_func = key_destroy_func;
+ cache->key_table = g_hash_table_new (hash_key_func, key_equal_func);
+ cache->value_table = g_hash_table_new (hash_value_func, NULL);
+
+ return cache;
+}
+
+/**
+ * g_cache_destroy:
+ * @cache: a #GCache
+ *
+ * Frees the memory allocated for the #GCache.
+ *
+ * Note that it does not destroy the keys and values which were
+ * contained in the #GCache.
+ *
+ * Deprecated:2.32: Use a #GHashTable instead
+ */
+void
+g_cache_destroy (GCache *cache)
+{
+ g_return_if_fail (cache != NULL);
+
+ g_hash_table_destroy (cache->key_table);
+ g_hash_table_destroy (cache->value_table);
+ g_slice_free (GCache, cache);
+}
+
+/**
+ * g_cache_insert:
+ * @cache: a #GCache
+ * @key: a key describing a #GCache object
+ *
+ * Gets the value corresponding to the given key, creating it if
+ * necessary. It first checks if the value already exists in the
+ * #GCache, by using the @key_equal_func function passed to
+ * g_cache_new(). If it does already exist it is returned, and its
+ * reference count is increased by one. If the value does not currently
+ * exist, if is created by calling the @value_new_func. The key is
+ * duplicated by calling @key_dup_func and the duplicated key and value
+ * are inserted into the #GCache.
+ *
+ * Returns: a pointer to a #GCache value
+ *
+ * Deprecated:2.32: Use a #GHashTable instead
+ */
+gpointer
+g_cache_insert (GCache *cache,
+ gpointer key)
+{
+ GCacheNode *node;
+ gpointer value;
+
+ g_return_val_if_fail (cache != NULL, NULL);
+
+ node = g_hash_table_lookup (cache->key_table, key);
+ if (node)
+ {
+ node->ref_count += 1;
+ return node->value;
+ }
+
+ key = (* cache->key_dup_func) (key);
+ value = (* cache->value_new_func) (key);
+ node = g_cache_node_new (value);
+
+ g_hash_table_insert (cache->key_table, key, node);
+ g_hash_table_insert (cache->value_table, value, key);
+
+ return node->value;
+}
+
+/**
+ * g_cache_remove:
+ * @cache: a #GCache
+ * @value: the value to remove
+ *
+ * Decreases the reference count of the given value. If it drops to 0
+ * then the value and its corresponding key are destroyed, using the
+ * @value_destroy_func and @key_destroy_func passed to g_cache_new().
+ *
+ * Deprecated:2.32: Use a #GHashTable instead
+ */
+void
+g_cache_remove (GCache *cache,
+ gconstpointer value)
+{
+ GCacheNode *node;
+ gpointer key;
+
+ g_return_if_fail (cache != NULL);
+
+ key = g_hash_table_lookup (cache->value_table, value);
+ node = g_hash_table_lookup (cache->key_table, key);
+
+ g_return_if_fail (node != NULL);
+
+ node->ref_count -= 1;
+ if (node->ref_count == 0)
+ {
+ g_hash_table_remove (cache->value_table, value);
+ g_hash_table_remove (cache->key_table, key);
+
+ (* cache->key_destroy_func) (key);
+ (* cache->value_destroy_func) (node->value);
+ g_cache_node_destroy (node);
+ }
+}
+
+/**
+ * g_cache_key_foreach:
+ * @cache: a #GCache
+ * @func: the function to call with each #GCache key
+ * @user_data: user data to pass to the function
+ *
+ * Calls the given function for each of the keys in the #GCache.
+ *
+ * NOTE @func is passed three parameters, the value and key of a cache
+ * entry and the @user_data. The order of value and key is different
+ * from the order in which g_hash_table_foreach() passes key-value
+ * pairs to its callback function !
+ *
+ * Deprecated:2.32: Use a #GHashTable instead
+ */
+void
+g_cache_key_foreach (GCache *cache,
+ GHFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (cache != NULL);
+ g_return_if_fail (func != NULL);
+
+ g_hash_table_foreach (cache->value_table, func, user_data);
+}
+
+/**
+ * g_cache_value_foreach:
+ * @cache: a #GCache
+ * @func: the function to call with each #GCache value
+ * @user_data: user data to pass to the function
+ *
+ * Calls the given function for each of the values in the #GCache.
+ *
+ * Deprecated:2.10: The reason is that it passes pointers to internal
+ * data structures to @func; use g_cache_key_foreach() instead
+ */
+void
+g_cache_value_foreach (GCache *cache,
+ GHFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (cache != NULL);
+ g_return_if_fail (func != NULL);
+
+ g_hash_table_foreach (cache->key_table, func, user_data);
+}
diff --git a/glib/deprecated/gcache.h b/glib/deprecated/gcache.h
new file mode 100644
index 0000000000000000000000000000000000000000..e1c1f2cde67bcaaa6e9209d707fb4fb9be9a1d9a
--- /dev/null
+++ b/glib/deprecated/gcache.h
@@ -0,0 +1,75 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_CACHE_H__
+#define __G_CACHE_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GCache GCache GLIB_DEPRECATED_TYPE_IN_2_26_FOR(GHashTable);
+
+typedef gpointer (*GCacheNewFunc) (gpointer key) GLIB_DEPRECATED_TYPE_IN_2_26;
+typedef gpointer (*GCacheDupFunc) (gpointer value) GLIB_DEPRECATED_TYPE_IN_2_26;
+typedef void (*GCacheDestroyFunc) (gpointer value) GLIB_DEPRECATED_TYPE_IN_2_26;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+
+/* Caches
+ */
+GLIB_DEPRECATED
+GCache* g_cache_new (GCacheNewFunc value_new_func,
+ GCacheDestroyFunc value_destroy_func,
+ GCacheDupFunc key_dup_func,
+ GCacheDestroyFunc key_destroy_func,
+ GHashFunc hash_key_func,
+ GHashFunc hash_value_func,
+ GEqualFunc key_equal_func);
+GLIB_DEPRECATED
+void g_cache_destroy (GCache *cache);
+GLIB_DEPRECATED
+gpointer g_cache_insert (GCache *cache,
+ gpointer key);
+GLIB_DEPRECATED
+void g_cache_remove (GCache *cache,
+ gconstpointer value);
+GLIB_DEPRECATED
+void g_cache_key_foreach (GCache *cache,
+ GHFunc func,
+ gpointer user_data);
+GLIB_DEPRECATED
+void g_cache_value_foreach (GCache *cache,
+ GHFunc func,
+ gpointer user_data);
+
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+G_END_DECLS
+
+#endif /* __G_CACHE_H__ */
diff --git a/glib/deprecated/gcompletion.c b/glib/deprecated/gcompletion.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f0979bb0041c3445162ff207d890d448bccb174
--- /dev/null
+++ b/glib/deprecated/gcompletion.c
@@ -0,0 +1,503 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+/* we know we are deprecated here, no need for warnings */
+#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+#endif
+
+#include "gcompletion.h"
+
+#include
+#include
+#include
+
+#include
+
+/**
+ * SECTION:completion
+ * @title: Automatic String Completion
+ * @short_description: support for automatic completion using a group
+ * of target strings
+ *
+ * #GCompletion provides support for automatic completion of a string
+ * using any group of target strings. It is typically used for file
+ * name completion as is common in many UNIX shells.
+ *
+ * A #GCompletion is created using g_completion_new(). Target items are
+ * added and removed with g_completion_add_items(),
+ * g_completion_remove_items() and g_completion_clear_items(). A
+ * completion attempt is requested with g_completion_complete() or
+ * g_completion_complete_utf8(). When no longer needed, the
+ * #GCompletion is freed with g_completion_free().
+ *
+ * Items in the completion can be simple strings (e.g. filenames), or
+ * pointers to arbitrary data structures. If data structures are used
+ * you must provide a #GCompletionFunc in g_completion_new(), which
+ * retrieves the item's string from the data structure. You can change
+ * the way in which strings are compared by setting a different
+ * #GCompletionStrncmpFunc in g_completion_set_compare().
+ *
+ * GCompletion has been marked as deprecated, since this API is rarely
+ * used and not very actively maintained.
+ **/
+
+/**
+ * GCompletion:
+ * @items: list of target items (strings or data structures).
+ * @func: function which is called to get the string associated with a
+ * target item. It is %NULL if the target items are strings.
+ * @prefix: the last prefix passed to g_completion_complete() or
+ * g_completion_complete_utf8().
+ * @cache: the list of items which begin with @prefix.
+ * @strncmp_func: The function to use when comparing strings. Use
+ * g_completion_set_compare() to modify this function.
+ *
+ * The data structure used for automatic completion.
+ **/
+
+/**
+ * GCompletionFunc:
+ * @Param1: the completion item.
+ *
+ * Specifies the type of the function passed to g_completion_new(). It
+ * should return the string corresponding to the given target item.
+ * This is used when you use data structures as #GCompletion items.
+ *
+ * Returns: the string corresponding to the item.
+ **/
+
+/**
+ * GCompletionStrncmpFunc:
+ * @s1: string to compare with @s2.
+ * @s2: string to compare with @s1.
+ * @n: maximal number of bytes to compare.
+ *
+ * Specifies the type of the function passed to
+ * g_completion_set_compare(). This is used when you use strings as
+ * #GCompletion items.
+ *
+ * Returns: an integer less than, equal to, or greater than zero if
+ * the first @n bytes of @s1 is found, respectively, to be
+ * less than, to match, or to be greater than the first @n
+ * bytes of @s2.
+ **/
+
+static void completion_check_cache (GCompletion* cmp,
+ gchar** new_prefix);
+
+/**
+ * g_completion_new:
+ * @func: the function to be called to return the string representing
+ * an item in the #GCompletion, or %NULL if strings are going to
+ * be used as the #GCompletion items.
+ *
+ * Creates a new #GCompletion.
+ *
+ * Returns: the new #GCompletion.
+ **/
+GCompletion*
+g_completion_new (GCompletionFunc func)
+{
+ GCompletion* gcomp;
+
+ gcomp = g_new (GCompletion, 1);
+ gcomp->items = NULL;
+ gcomp->cache = NULL;
+ gcomp->prefix = NULL;
+ gcomp->func = func;
+ gcomp->strncmp_func = strncmp;
+
+ return gcomp;
+}
+
+/**
+ * g_completion_add_items:
+ * @cmp: the #GCompletion.
+ * @items: (transfer none): the list of items to add.
+ *
+ * Adds items to the #GCompletion.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_completion_add_items (GCompletion* cmp,
+ GList* items)
+{
+ GList* it;
+
+ g_return_if_fail (cmp != NULL);
+
+ /* optimize adding to cache? */
+ if (cmp->cache)
+ {
+ g_list_free (cmp->cache);
+ cmp->cache = NULL;
+ }
+
+ if (cmp->prefix)
+ {
+ g_free (cmp->prefix);
+ cmp->prefix = NULL;
+ }
+
+ it = items;
+ while (it)
+ {
+ cmp->items = g_list_prepend (cmp->items, it->data);
+ it = it->next;
+ }
+}
+
+/**
+ * g_completion_remove_items:
+ * @cmp: the #GCompletion.
+ * @items: (transfer none): the items to remove.
+ *
+ * Removes items from a #GCompletion. The items are not freed, so if the memory
+ * was dynamically allocated, free @items with g_list_free_full() after calling
+ * this function.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_completion_remove_items (GCompletion* cmp,
+ GList* items)
+{
+ GList* it;
+
+ g_return_if_fail (cmp != NULL);
+
+ it = items;
+ while (cmp->items && it)
+ {
+ cmp->items = g_list_remove (cmp->items, it->data);
+ it = it->next;
+ }
+
+ it = items;
+ while (cmp->cache && it)
+ {
+ cmp->cache = g_list_remove(cmp->cache, it->data);
+ it = it->next;
+ }
+}
+
+/**
+ * g_completion_clear_items:
+ * @cmp: the #GCompletion.
+ *
+ * Removes all items from the #GCompletion. The items are not freed, so if the
+ * memory was dynamically allocated, it should be freed after calling this
+ * function.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_completion_clear_items (GCompletion* cmp)
+{
+ g_return_if_fail (cmp != NULL);
+
+ g_list_free (cmp->items);
+ cmp->items = NULL;
+ g_list_free (cmp->cache);
+ cmp->cache = NULL;
+ g_free (cmp->prefix);
+ cmp->prefix = NULL;
+}
+
+static void
+completion_check_cache (GCompletion* cmp,
+ gchar** new_prefix)
+{
+ GList* list;
+ gsize len;
+ gsize i;
+ gsize plen;
+ gchar* postfix;
+ gchar* s;
+
+ if (!new_prefix)
+ return;
+ if (!cmp->cache)
+ {
+ *new_prefix = NULL;
+ return;
+ }
+
+ len = strlen(cmp->prefix);
+ list = cmp->cache;
+ s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
+ postfix = s + len;
+ plen = strlen (postfix);
+ list = list->next;
+
+ while (list && plen)
+ {
+ s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
+ s += len;
+ for (i = 0; i < plen; ++i)
+ {
+ if (postfix[i] != s[i])
+ break;
+ }
+ plen = i;
+ list = list->next;
+ }
+
+ *new_prefix = g_new0 (gchar, len + plen + 1);
+ strncpy (*new_prefix, cmp->prefix, len);
+ strncpy (*new_prefix + len, postfix, plen);
+}
+
+/**
+ * g_completion_complete_utf8:
+ * @cmp: the #GCompletion
+ * @prefix: the prefix string, typically used by the user, which is compared
+ * with each of the items
+ * @new_prefix: if non-%NULL, returns the longest prefix which is common to all
+ * items that matched @prefix, or %NULL if no items matched @prefix.
+ * This string should be freed when no longer needed.
+ *
+ * Attempts to complete the string @prefix using the #GCompletion target items.
+ * In contrast to g_completion_complete(), this function returns the largest common
+ * prefix that is a valid UTF-8 string, omitting a possible common partial
+ * character.
+ *
+ * You should use this function instead of g_completion_complete() if your
+ * items are UTF-8 strings.
+ *
+ * Returns: (element-type utf8) (transfer none): the list of items whose strings begin with @prefix. This should
+ * not be changed.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+GList*
+g_completion_complete_utf8 (GCompletion *cmp,
+ const gchar *prefix,
+ gchar **new_prefix)
+{
+ GList *list;
+ gchar *p, *q;
+
+ list = g_completion_complete (cmp, prefix, new_prefix);
+
+ if (new_prefix && *new_prefix)
+ {
+ p = *new_prefix + strlen (*new_prefix);
+ q = g_utf8_find_prev_char (*new_prefix, p);
+
+ switch (g_utf8_get_char_validated (q, p - q))
+ {
+ case (gunichar)-2:
+ case (gunichar)-1:
+ *q = 0;
+ break;
+ default: ;
+ }
+
+ }
+
+ return list;
+}
+
+/**
+ * g_completion_complete:
+ * @cmp: the #GCompletion.
+ * @prefix: the prefix string, typically typed by the user, which is
+ * compared with each of the items.
+ * @new_prefix: if non-%NULL, returns the longest prefix which is
+ * common to all items that matched @prefix, or %NULL if
+ * no items matched @prefix. This string should be freed
+ * when no longer needed.
+ *
+ * Attempts to complete the string @prefix using the #GCompletion
+ * target items.
+ *
+ * Returns: (transfer none): the list of items whose strings begin with
+ * @prefix. This should not be changed.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+GList*
+g_completion_complete (GCompletion* cmp,
+ const gchar* prefix,
+ gchar** new_prefix)
+{
+ gsize plen, len;
+ gboolean done = FALSE;
+ GList* list;
+
+ g_return_val_if_fail (cmp != NULL, NULL);
+ g_return_val_if_fail (prefix != NULL, NULL);
+
+ len = strlen (prefix);
+ if (cmp->prefix && cmp->cache)
+ {
+ plen = strlen (cmp->prefix);
+ if (plen <= len && ! cmp->strncmp_func (prefix, cmp->prefix, plen))
+ {
+ /* use the cache */
+ list = cmp->cache;
+ while (list)
+ {
+ GList *next = list->next;
+
+ if (cmp->strncmp_func (prefix,
+ cmp->func ? cmp->func (list->data) : (gchar*) list->data,
+ len))
+ cmp->cache = g_list_delete_link (cmp->cache, list);
+
+ list = next;
+ }
+ done = TRUE;
+ }
+ }
+
+ if (!done)
+ {
+ /* normal code */
+ g_list_free (cmp->cache);
+ cmp->cache = NULL;
+ list = cmp->items;
+ while (*prefix && list)
+ {
+ if (!cmp->strncmp_func (prefix,
+ cmp->func ? cmp->func (list->data) : (gchar*) list->data,
+ len))
+ cmp->cache = g_list_prepend (cmp->cache, list->data);
+ list = list->next;
+ }
+ }
+ if (cmp->prefix)
+ {
+ g_free (cmp->prefix);
+ cmp->prefix = NULL;
+ }
+ if (cmp->cache)
+ cmp->prefix = g_strdup (prefix);
+ completion_check_cache (cmp, new_prefix);
+
+ return *prefix ? cmp->cache : cmp->items;
+}
+
+/**
+ * g_completion_free:
+ * @cmp: the #GCompletion.
+ *
+ * Frees all memory used by the #GCompletion. The items are not freed, so if
+ * the memory was dynamically allocated, it should be freed after calling this
+ * function.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_completion_free (GCompletion* cmp)
+{
+ g_return_if_fail (cmp != NULL);
+
+ g_completion_clear_items (cmp);
+ g_free (cmp);
+}
+
+/**
+ * g_completion_set_compare:
+ * @cmp: a #GCompletion.
+ * @strncmp_func: the string comparison function.
+ *
+ * Sets the function to use for string comparisons. The default string
+ * comparison function is strncmp().
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_completion_set_compare(GCompletion *cmp,
+ GCompletionStrncmpFunc strncmp_func)
+{
+ cmp->strncmp_func = strncmp_func;
+}
+
+#ifdef TEST_COMPLETION
+#include
+int
+main (int argc,
+ char* argv[])
+{
+ FILE *file;
+ gchar buf[1024];
+ GList *list;
+ GList *result;
+ GList *tmp;
+ GCompletion *cmp;
+ gint i;
+ gchar *longp = NULL;
+
+ if (argc < 3)
+ {
+ g_warning ("Usage: %s filename prefix1 [prefix2 ...]",
+ (argc > 0) ? argv[0] : "gcompletion");
+ return 1;
+ }
+
+ file = fopen (argv[1], "r");
+ if (!file)
+ {
+ g_warning ("Cannot open %s", argv[1]);
+ return 1;
+ }
+
+ cmp = g_completion_new (NULL);
+ list = g_list_alloc ();
+ while (fgets (buf, 1024, file))
+ {
+ list->data = g_strdup (buf);
+ g_completion_add_items (cmp, list);
+ }
+ fclose (file);
+
+ for (i = 2; i < argc; ++i)
+ {
+ printf ("COMPLETING: %s\n", argv[i]);
+ result = g_completion_complete (cmp, argv[i], &longp);
+ g_list_foreach (result, (GFunc) printf, NULL);
+ printf ("LONG MATCH: %s\n", longp);
+ g_free (longp);
+ longp = NULL;
+ }
+
+ g_list_foreach (cmp->items, (GFunc) g_free, NULL);
+ g_completion_free (cmp);
+ g_list_free (list);
+
+ return 0;
+}
+#endif
diff --git a/glib/deprecated/gcompletion.h b/glib/deprecated/gcompletion.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fd1f0393ffceaf9687b4a08a8ff8a6073b09b4b
--- /dev/null
+++ b/glib/deprecated/gcompletion.h
@@ -0,0 +1,83 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_COMPLETION_H__
+#define __G_COMPLETION_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GCompletion GCompletion;
+
+typedef gchar* (*GCompletionFunc) (gpointer);
+
+/* GCompletion
+ */
+
+typedef gint (*GCompletionStrncmpFunc) (const gchar *s1,
+ const gchar *s2,
+ gsize n);
+
+struct _GCompletion
+{
+ GList* items;
+ GCompletionFunc func;
+
+ gchar* prefix;
+ GList* cache;
+ GCompletionStrncmpFunc strncmp_func;
+};
+
+GLIB_DEPRECATED_IN_2_26
+GCompletion* g_completion_new (GCompletionFunc func);
+GLIB_DEPRECATED_IN_2_26
+void g_completion_add_items (GCompletion* cmp,
+ GList* items);
+GLIB_DEPRECATED_IN_2_26
+void g_completion_remove_items (GCompletion* cmp,
+ GList* items);
+GLIB_DEPRECATED_IN_2_26
+void g_completion_clear_items (GCompletion* cmp);
+GLIB_DEPRECATED_IN_2_26
+GList* g_completion_complete (GCompletion* cmp,
+ const gchar* prefix,
+ gchar** new_prefix);
+GLIB_DEPRECATED_IN_2_26
+GList* g_completion_complete_utf8 (GCompletion *cmp,
+ const gchar* prefix,
+ gchar** new_prefix);
+GLIB_DEPRECATED_IN_2_26
+void g_completion_set_compare (GCompletion *cmp,
+ GCompletionStrncmpFunc strncmp_func);
+GLIB_DEPRECATED_IN_2_26
+void g_completion_free (GCompletion* cmp);
+
+G_END_DECLS
+
+#endif /* __G_COMPLETION_H__ */
diff --git a/glib/deprecated/gmain.h b/glib/deprecated/gmain.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d08eb6b416c92b493f6eb20e5cfbe7db1875c84
--- /dev/null
+++ b/glib/deprecated/gmain.h
@@ -0,0 +1,135 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_DEPRECATED_MAIN_H__
+#define __G_DEPRECATED_MAIN_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+/* ============== Compat main loop stuff ================== */
+
+/**
+ * g_main_new:
+ * @is_running: set to %TRUE to indicate that the loop is running. This
+ * is not very important since calling g_main_run() will set this
+ * to %TRUE anyway.
+ *
+ * Creates a new #GMainLoop for th default main context.
+ *
+ * Returns: a new #GMainLoop
+ *
+ * Deprecated: 2.2: Use g_main_loop_new() instead
+ */
+#define g_main_new(is_running) g_main_loop_new (NULL, is_running) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_loop_new)
+
+/**
+ * g_main_run:
+ * @loop: a #GMainLoop
+ *
+ * Runs a main loop until it stops running.
+ *
+ * Deprecated: 2.2: Use g_main_loop_run() instead
+ */
+#define g_main_run(loop) g_main_loop_run(loop) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_loop_run)
+
+/**
+ * g_main_quit:
+ * @loop: a #GMainLoop
+ *
+ * Stops the #GMainLoop.
+ * If g_main_run() was called to run the #GMainLoop, it will now return.
+ *
+ * Deprecated: 2.2: Use g_main_loop_quit() instead
+ */
+#define g_main_quit(loop) g_main_loop_quit(loop) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_loop_quit)
+
+/**
+ * g_main_destroy:
+ * @loop: a #GMainLoop
+ *
+ * Frees the memory allocated for the #GMainLoop.
+ *
+ * Deprecated: 2.2: Use g_main_loop_unref() instead
+ */
+#define g_main_destroy(loop) g_main_loop_unref(loop) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_loop_unref)
+
+/**
+ * g_main_is_running:
+ * @loop: a #GMainLoop
+ *
+ * Checks if the main loop is running.
+ *
+ * Returns: %TRUE if the main loop is running
+ *
+ * Deprecated: 2.2: Use g_main_loop_is_running() instead
+ */
+#define g_main_is_running(loop) g_main_loop_is_running(loop) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_loop_is_running)
+
+/**
+ * g_main_iteration:
+ * @may_block: set to %TRUE if it should block (i.e. wait) until an event
+ * source becomes ready. It will return after an event source has been
+ * processed. If set to %FALSE it will return immediately if no event
+ * source is ready to be processed.
+ *
+ * Runs a single iteration for the default #GMainContext.
+ *
+ * Returns: %TRUE if more events are pending.
+ *
+ * Deprecated: 2.2: Use g_main_context_iteration() instead.
+ */
+#define g_main_iteration(may_block) g_main_context_iteration (NULL, may_block) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_context_iteration)
+
+/**
+ * g_main_pending:
+ *
+ * Checks if any events are pending for the default #GMainContext
+ * (i.e. ready to be processed).
+ *
+ * Returns: %TRUE if any events are pending.
+ *
+ * Deprecated: 2.2: Use g_main_context_pending() instead.
+ */
+#define g_main_pending() g_main_context_pending (NULL) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_context_pending)
+
+/**
+ * g_main_set_poll_func:
+ * @func: the function to call to poll all file descriptors
+ *
+ * Sets the function to use for the handle polling of file descriptors
+ * for the default main context.
+ *
+ * Deprecated: 2.2: Use g_main_context_set_poll_func() again
+ */
+#define g_main_set_poll_func(func) g_main_context_set_poll_func (NULL, func) GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_main_context_set_poll_func)
+
+G_END_DECLS
+
+#endif /* __G_DEPRECATED_MAIN_H__ */
diff --git a/glib/deprecated/grel.c b/glib/deprecated/grel.c
new file mode 100644
index 0000000000000000000000000000000000000000..b48ec11733417433537660a431192458192399e2
--- /dev/null
+++ b/glib/deprecated/grel.c
@@ -0,0 +1,685 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+/* we know we are deprecated here, no need for warnings */
+#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+#endif
+
+#include "grel.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+/**
+ * SECTION:relations
+ * @title: Relations and Tuples
+ * @short_description: tables of data which can be indexed on any
+ * number of fields
+ *
+ * A #GRelation is a table of data which can be indexed on any number
+ * of fields, rather like simple database tables. A #GRelation contains
+ * a number of records, called tuples. Each record contains a number of
+ * fields. Records are not ordered, so it is not possible to find the
+ * record at a particular index.
+ *
+ * Note that #GRelation tables are currently limited to 2 fields.
+ *
+ * To create a GRelation, use g_relation_new().
+ *
+ * To specify which fields should be indexed, use g_relation_index().
+ * Note that this must be called before any tuples are added to the
+ * #GRelation.
+ *
+ * To add records to a #GRelation use g_relation_insert().
+ *
+ * To determine if a given record appears in a #GRelation, use
+ * g_relation_exists(). Note that fields are compared directly, so
+ * pointers must point to the exact same position (i.e. different
+ * copies of the same string will not match.)
+ *
+ * To count the number of records which have a particular value in a
+ * given field, use g_relation_count().
+ *
+ * To get all the records which have a particular value in a given
+ * field, use g_relation_select(). To access fields of the resulting
+ * records, use g_tuples_index(). To free the resulting records use
+ * g_tuples_destroy().
+ *
+ * To delete all records which have a particular value in a given
+ * field, use g_relation_delete().
+ *
+ * To destroy the #GRelation, use g_relation_destroy().
+ *
+ * To help debug #GRelation objects, use g_relation_print().
+ *
+ * GRelation has been marked as deprecated, since this API has never
+ * been fully implemented, is not very actively maintained and rarely
+ * used.
+ **/
+
+typedef struct _GRealTuples GRealTuples;
+
+/**
+ * GRelation:
+ *
+ * The #GRelation struct is an opaque data structure to represent a
+ * [Relation][glib-Relations-and-Tuples]. It should
+ * only be accessed via the following functions.
+ **/
+struct _GRelation
+{
+ gint fields;
+ gint current_field;
+
+ GHashTable *all_tuples;
+ GHashTable **hashed_tuple_tables;
+
+ gint count;
+};
+
+/**
+ * GTuples:
+ * @len: the number of records that matched.
+ *
+ * The #GTuples struct is used to return records (or tuples) from the
+ * #GRelation by g_relation_select(). It only contains one public
+ * member - the number of records that matched. To access the matched
+ * records, you must use g_tuples_index().
+ **/
+struct _GRealTuples
+{
+ gint len;
+ gint width;
+ gpointer *data;
+};
+
+static gboolean
+tuple_equal_2 (gconstpointer v_a,
+ gconstpointer v_b)
+{
+ gpointer* a = (gpointer*) v_a;
+ gpointer* b = (gpointer*) v_b;
+
+ return a[0] == b[0] && a[1] == b[1];
+}
+
+static guint
+tuple_hash_2 (gconstpointer v_a)
+{
+#if GLIB_SIZEOF_VOID_P > GLIB_SIZEOF_LONG
+ /* In practise this snippet has been written for 64-bit Windows
+ * where ints are 32 bits, pointers 64 bits. More exotic platforms
+ * need more tweaks.
+ */
+ guint* a = (guint*) v_a;
+
+ return (a[0] ^ a[1] ^ a[2] ^ a[3]);
+#else
+ gpointer* a = (gpointer*) v_a;
+
+ return (gulong)a[0] ^ (gulong)a[1];
+#endif
+}
+
+static GHashFunc
+tuple_hash (gint fields)
+{
+ switch (fields)
+ {
+ case 2:
+ return tuple_hash_2;
+ default:
+ g_error ("no tuple hash for %d", fields);
+ }
+
+ return NULL;
+}
+
+static GEqualFunc
+tuple_equal (gint fields)
+{
+ switch (fields)
+ {
+ case 2:
+ return tuple_equal_2;
+ default:
+ g_error ("no tuple equal for %d", fields);
+ }
+
+ return NULL;
+}
+
+/**
+ * g_relation_new:
+ * @fields: the number of fields.
+ *
+ * Creates a new #GRelation with the given number of fields. Note that
+ * currently the number of fields must be 2.
+ *
+ * Returns: a new #GRelation.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+GRelation*
+g_relation_new (gint fields)
+{
+ GRelation* rel = g_new0 (GRelation, 1);
+
+ rel->fields = fields;
+ rel->all_tuples = g_hash_table_new (tuple_hash (fields), tuple_equal (fields));
+ rel->hashed_tuple_tables = g_new0 (GHashTable*, fields);
+
+ return rel;
+}
+
+static void
+relation_delete_value_tuple (gpointer tuple_key,
+ gpointer tuple_value,
+ gpointer user_data)
+{
+ GRelation *relation = user_data;
+ gpointer *tuple = tuple_value;
+ g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
+}
+
+static void
+g_relation_free_array (gpointer key, gpointer value, gpointer user_data)
+{
+ g_hash_table_destroy ((GHashTable*) value);
+}
+
+/**
+ * g_relation_destroy:
+ * @relation: a #GRelation.
+ *
+ * Destroys the #GRelation, freeing all memory allocated. However, it
+ * does not free memory allocated for the tuple data, so you should
+ * free that first if appropriate.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_relation_destroy (GRelation *relation)
+{
+ gint i;
+
+ if (relation)
+ {
+ for (i = 0; i < relation->fields; i += 1)
+ {
+ if (relation->hashed_tuple_tables[i])
+ {
+ g_hash_table_foreach (relation->hashed_tuple_tables[i], g_relation_free_array, NULL);
+ g_hash_table_destroy (relation->hashed_tuple_tables[i]);
+ }
+ }
+
+ g_hash_table_foreach (relation->all_tuples, relation_delete_value_tuple, relation);
+ g_hash_table_destroy (relation->all_tuples);
+
+ g_free (relation->hashed_tuple_tables);
+ g_free (relation);
+ }
+}
+
+/**
+ * g_relation_index:
+ * @relation: a #GRelation.
+ * @field: the field to index, counting from 0.
+ * @hash_func: a function to produce a hash value from the field data.
+ * @key_equal_func: a function to compare two values of the given field.
+ *
+ * Creates an index on the given field. Note that this must be called
+ * before any records are added to the #GRelation.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_relation_index (GRelation *relation,
+ gint field,
+ GHashFunc hash_func,
+ GEqualFunc key_equal_func)
+{
+ g_return_if_fail (relation != NULL);
+
+ g_return_if_fail (relation->count == 0 && relation->hashed_tuple_tables[field] == NULL);
+
+ relation->hashed_tuple_tables[field] = g_hash_table_new (hash_func, key_equal_func);
+}
+
+/**
+ * g_relation_insert:
+ * @relation: a #GRelation.
+ * @...: the fields of the record to add. These must match the
+ * number of fields in the #GRelation, and of type #gpointer
+ * or #gconstpointer.
+ *
+ * Inserts a record into a #GRelation.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_relation_insert (GRelation *relation,
+ ...)
+{
+ gpointer* tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
+ va_list args;
+ gint i;
+
+ va_start (args, relation);
+
+ for (i = 0; i < relation->fields; i += 1)
+ tuple[i] = va_arg (args, gpointer);
+
+ va_end (args);
+
+ g_hash_table_insert (relation->all_tuples, tuple, tuple);
+
+ relation->count += 1;
+
+ for (i = 0; i < relation->fields; i += 1)
+ {
+ GHashTable *table;
+ gpointer key;
+ GHashTable *per_key_table;
+
+ table = relation->hashed_tuple_tables[i];
+
+ if (table == NULL)
+ continue;
+
+ key = tuple[i];
+ per_key_table = g_hash_table_lookup (table, key);
+
+ if (per_key_table == NULL)
+ {
+ per_key_table = g_hash_table_new (tuple_hash (relation->fields), tuple_equal (relation->fields));
+ g_hash_table_insert (table, key, per_key_table);
+ }
+
+ g_hash_table_insert (per_key_table, tuple, tuple);
+ }
+}
+
+static void
+g_relation_delete_tuple (gpointer tuple_key,
+ gpointer tuple_value,
+ gpointer user_data)
+{
+ gpointer *tuple = (gpointer*) tuple_value;
+ GRelation *relation = (GRelation *) user_data;
+ gint j;
+
+ g_assert (tuple_key == tuple_value);
+
+ for (j = 0; j < relation->fields; j += 1)
+ {
+ GHashTable *one_table = relation->hashed_tuple_tables[j];
+ gpointer one_key;
+ GHashTable *per_key_table;
+
+ if (one_table == NULL)
+ continue;
+
+ if (j == relation->current_field)
+ /* can't delete from the table we're foreaching in */
+ continue;
+
+ one_key = tuple[j];
+
+ per_key_table = g_hash_table_lookup (one_table, one_key);
+
+ g_hash_table_remove (per_key_table, tuple);
+ }
+
+ if (g_hash_table_remove (relation->all_tuples, tuple))
+ g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
+
+ relation->count -= 1;
+}
+
+/**
+ * g_relation_delete:
+ * @relation: a #GRelation.
+ * @key: the value to compare with.
+ * @field: the field of each record to match.
+ *
+ * Deletes any records from a #GRelation that have the given key value
+ * in the given field.
+ *
+ * Returns: the number of records deleted.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+gint
+g_relation_delete (GRelation *relation,
+ gconstpointer key,
+ gint field)
+{
+ GHashTable *table;
+ GHashTable *key_table;
+ gint count;
+
+ g_return_val_if_fail (relation != NULL, 0);
+
+ table = relation->hashed_tuple_tables[field];
+ count = relation->count;
+
+ g_return_val_if_fail (table != NULL, 0);
+
+ key_table = g_hash_table_lookup (table, key);
+
+ if (!key_table)
+ return 0;
+
+ relation->current_field = field;
+
+ g_hash_table_foreach (key_table, g_relation_delete_tuple, relation);
+
+ g_hash_table_remove (table, key);
+
+ g_hash_table_destroy (key_table);
+
+ /* @@@ FIXME: Remove empty hash tables. */
+
+ return count - relation->count;
+}
+
+static void
+g_relation_select_tuple (gpointer tuple_key,
+ gpointer tuple_value,
+ gpointer user_data)
+{
+ gpointer *tuple = (gpointer*) tuple_value;
+ GRealTuples *tuples = (GRealTuples*) user_data;
+ gint stride = sizeof (gpointer) * tuples->width;
+
+ g_assert (tuple_key == tuple_value);
+
+ memcpy (tuples->data + (tuples->len * tuples->width),
+ tuple,
+ stride);
+
+ tuples->len += 1;
+}
+
+/**
+ * g_relation_select:
+ * @relation: a #GRelation.
+ * @key: the value to compare with.
+ * @field: the field of each record to match.
+ *
+ * Returns all of the tuples which have the given key in the given
+ * field. Use g_tuples_index() to access the returned records. The
+ * returned records should be freed with g_tuples_destroy().
+ *
+ * Returns: the records (tuples) that matched.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+GTuples*
+g_relation_select (GRelation *relation,
+ gconstpointer key,
+ gint field)
+{
+ GHashTable *table;
+ GHashTable *key_table;
+ GRealTuples *tuples;
+ gint count;
+
+ g_return_val_if_fail (relation != NULL, NULL);
+
+ table = relation->hashed_tuple_tables[field];
+
+ g_return_val_if_fail (table != NULL, NULL);
+
+ tuples = g_new0 (GRealTuples, 1);
+ key_table = g_hash_table_lookup (table, key);
+
+ if (!key_table)
+ return (GTuples*)tuples;
+
+ count = g_relation_count (relation, key, field);
+
+ tuples->data = g_malloc (sizeof (gpointer) * relation->fields * count);
+ tuples->width = relation->fields;
+
+ g_hash_table_foreach (key_table, g_relation_select_tuple, tuples);
+
+ g_assert (count == tuples->len);
+
+ return (GTuples*)tuples;
+}
+
+/**
+ * g_relation_count:
+ * @relation: a #GRelation.
+ * @key: the value to compare with.
+ * @field: the field of each record to match.
+ *
+ * Returns the number of tuples in a #GRelation that have the given
+ * value in the given field.
+ *
+ * Returns: the number of matches.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+gint
+g_relation_count (GRelation *relation,
+ gconstpointer key,
+ gint field)
+{
+ GHashTable *table;
+ GHashTable *key_table;
+
+ g_return_val_if_fail (relation != NULL, 0);
+
+ table = relation->hashed_tuple_tables[field];
+
+ g_return_val_if_fail (table != NULL, 0);
+
+ key_table = g_hash_table_lookup (table, key);
+
+ if (!key_table)
+ return 0;
+
+ return g_hash_table_size (key_table);
+}
+
+/**
+ * g_relation_exists:
+ * @relation: a #GRelation.
+ * @...: the fields of the record to compare. The number must match
+ * the number of fields in the #GRelation.
+ *
+ * Returns %TRUE if a record with the given values exists in a
+ * #GRelation. Note that the values are compared directly, so that, for
+ * example, two copies of the same string will not match.
+ *
+ * Returns: %TRUE if a record matches.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+gboolean
+g_relation_exists (GRelation *relation, ...)
+{
+ gpointer *tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
+ va_list args;
+ gint i;
+ gboolean result;
+
+ va_start(args, relation);
+
+ for (i = 0; i < relation->fields; i += 1)
+ tuple[i] = va_arg(args, gpointer);
+
+ va_end(args);
+
+ result = g_hash_table_lookup (relation->all_tuples, tuple) != NULL;
+
+ g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
+
+ return result;
+}
+
+/**
+ * g_tuples_destroy:
+ * @tuples: the tuple data to free.
+ *
+ * Frees the records which were returned by g_relation_select(). This
+ * should always be called after g_relation_select() when you are
+ * finished with the records. The records are not removed from the
+ * #GRelation.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_tuples_destroy (GTuples *tuples0)
+{
+ GRealTuples *tuples = (GRealTuples*) tuples0;
+
+ if (tuples)
+ {
+ g_free (tuples->data);
+ g_free (tuples);
+ }
+}
+
+/**
+ * g_tuples_index:
+ * @tuples: the tuple data, returned by g_relation_select().
+ * @index_: the index of the record.
+ * @field: the field to return.
+ *
+ * Gets a field from the records returned by g_relation_select(). It
+ * returns the given field of the record at the given index. The
+ * returned value should not be changed.
+ *
+ * Returns: the field of the record.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+gpointer
+g_tuples_index (GTuples *tuples0,
+ gint index,
+ gint field)
+{
+ GRealTuples *tuples = (GRealTuples*) tuples0;
+
+ g_return_val_if_fail (tuples0 != NULL, NULL);
+ g_return_val_if_fail (field < tuples->width, NULL);
+
+ return tuples->data[index * tuples->width + field];
+}
+
+/* Print
+ */
+
+static void
+g_relation_print_one (gpointer tuple_key,
+ gpointer tuple_value,
+ gpointer user_data)
+{
+ gint i;
+ GString *gstring;
+ GRelation* rel = (GRelation*) user_data;
+ gpointer* tuples = (gpointer*) tuple_value;
+
+ gstring = g_string_new ("[");
+
+ for (i = 0; i < rel->fields; i += 1)
+ {
+ g_string_append_printf (gstring, "%p", tuples[i]);
+
+ if (i < (rel->fields - 1))
+ g_string_append (gstring, ",");
+ }
+
+ g_string_append (gstring, "]");
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%s", gstring->str);
+ g_string_free (gstring, TRUE);
+}
+
+static void
+g_relation_print_index (gpointer tuple_key,
+ gpointer tuple_value,
+ gpointer user_data)
+{
+ GRelation* rel = (GRelation*) user_data;
+ GHashTable* table = (GHashTable*) tuple_value;
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** key %p", tuple_key);
+
+ g_hash_table_foreach (table,
+ g_relation_print_one,
+ rel);
+}
+
+/**
+ * g_relation_print:
+ * @relation: a #GRelation.
+ *
+ * Outputs information about all records in a #GRelation, as well as
+ * the indexes. It is for debugging.
+ *
+ * Deprecated: 2.26: Rarely used API
+ **/
+void
+g_relation_print (GRelation *relation)
+{
+ gint i;
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** all tuples (%d)", relation->count);
+
+ g_hash_table_foreach (relation->all_tuples,
+ g_relation_print_one,
+ relation);
+
+ for (i = 0; i < relation->fields; i += 1)
+ {
+ if (relation->hashed_tuple_tables[i] == NULL)
+ continue;
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** index %d", i);
+
+ g_hash_table_foreach (relation->hashed_tuple_tables[i],
+ g_relation_print_index,
+ relation);
+ }
+
+}
diff --git a/glib/deprecated/grel.h b/glib/deprecated/grel.h
new file mode 100644
index 0000000000000000000000000000000000000000..3a65240c1fe8e46ed7609c87efdcbfe785b50e5c
--- /dev/null
+++ b/glib/deprecated/grel.h
@@ -0,0 +1,105 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_REL_H__
+#define __G_REL_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GRelation GRelation;
+typedef struct _GTuples GTuples;
+
+struct _GTuples
+{
+ guint len;
+};
+
+/* GRelation
+ *
+ * Indexed Relations. Imagine a really simple table in a
+ * database. Relations are not ordered. This data type is meant for
+ * maintaining a N-way mapping.
+ *
+ * g_relation_new() creates a relation with FIELDS fields
+ *
+ * g_relation_destroy() frees all resources
+ * g_tuples_destroy() frees the result of g_relation_select()
+ *
+ * g_relation_index() indexes relation FIELD with the provided
+ * equality and hash functions. this must be done before any
+ * calls to insert are made.
+ *
+ * g_relation_insert() inserts a new tuple. you are expected to
+ * provide the right number of fields.
+ *
+ * g_relation_delete() deletes all relations with KEY in FIELD
+ * g_relation_select() returns ...
+ * g_relation_count() counts ...
+ */
+
+GLIB_DEPRECATED_IN_2_26
+GRelation* g_relation_new (gint fields);
+GLIB_DEPRECATED_IN_2_26
+void g_relation_destroy (GRelation *relation);
+GLIB_DEPRECATED_IN_2_26
+void g_relation_index (GRelation *relation,
+ gint field,
+ GHashFunc hash_func,
+ GEqualFunc key_equal_func);
+GLIB_DEPRECATED_IN_2_26
+void g_relation_insert (GRelation *relation,
+ ...);
+GLIB_DEPRECATED_IN_2_26
+gint g_relation_delete (GRelation *relation,
+ gconstpointer key,
+ gint field);
+GLIB_DEPRECATED_IN_2_26
+GTuples* g_relation_select (GRelation *relation,
+ gconstpointer key,
+ gint field);
+GLIB_DEPRECATED_IN_2_26
+gint g_relation_count (GRelation *relation,
+ gconstpointer key,
+ gint field);
+GLIB_DEPRECATED_IN_2_26
+gboolean g_relation_exists (GRelation *relation,
+ ...);
+GLIB_DEPRECATED_IN_2_26
+void g_relation_print (GRelation *relation);
+GLIB_DEPRECATED_IN_2_26
+void g_tuples_destroy (GTuples *tuples);
+GLIB_DEPRECATED_IN_2_26
+gpointer g_tuples_index (GTuples *tuples,
+ gint index_,
+ gint field);
+
+G_END_DECLS
+
+#endif /* __G_REL_H__ */
diff --git a/glib/deprecated/gthread-deprecated.c b/glib/deprecated/gthread-deprecated.c
new file mode 100644
index 0000000000000000000000000000000000000000..be98a74b63defd785edcaaeb2938684d09ae5d18
--- /dev/null
+++ b/glib/deprecated/gthread-deprecated.c
@@ -0,0 +1,1578 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * gthread.c: MT safety related functions
+ * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
+ * Owen Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include "config.h"
+
+/* we know we are deprecated here, no need for warnings */
+#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+#endif
+
+#include "gmessages.h"
+#include "gslice.h"
+#include "gmain.h"
+#include "gthread.h"
+#include "gthreadprivate.h"
+#include "deprecated/gthread.h"
+#include "garray.h"
+
+#include "gutils.h"
+
+/* {{{1 Documentation */
+
+/**
+ * SECTION:threads-deprecated
+ * @title: Deprecated thread API
+ * @short_description: old thread APIs (for reference only)
+ * @see_also: #GThread
+ *
+ * These APIs are deprecated. You should not use them in new code.
+ * This section remains only to assist with understanding code that was
+ * written to use these APIs at some point in the past.
+ **/
+
+/**
+ * GThreadPriority:
+ * @G_THREAD_PRIORITY_LOW: a priority lower than normal
+ * @G_THREAD_PRIORITY_NORMAL: the default priority
+ * @G_THREAD_PRIORITY_HIGH: a priority higher than normal
+ * @G_THREAD_PRIORITY_URGENT: the highest priority
+ *
+ * Thread priorities.
+ *
+ * Deprecated:2.32: Thread priorities no longer have any effect.
+ */
+
+/**
+ * GThreadFunctions:
+ * @mutex_new: virtual function pointer for g_mutex_new()
+ * @mutex_lock: virtual function pointer for g_mutex_lock()
+ * @mutex_trylock: virtual function pointer for g_mutex_trylock()
+ * @mutex_unlock: virtual function pointer for g_mutex_unlock()
+ * @mutex_free: virtual function pointer for g_mutex_free()
+ * @cond_new: virtual function pointer for g_cond_new()
+ * @cond_signal: virtual function pointer for g_cond_signal()
+ * @cond_broadcast: virtual function pointer for g_cond_broadcast()
+ * @cond_wait: virtual function pointer for g_cond_wait()
+ * @cond_timed_wait: virtual function pointer for g_cond_timed_wait()
+ * @cond_free: virtual function pointer for g_cond_free()
+ * @private_new: virtual function pointer for g_private_new()
+ * @private_get: virtual function pointer for g_private_get()
+ * @private_set: virtual function pointer for g_private_set()
+ * @thread_create: virtual function pointer for g_thread_create()
+ * @thread_yield: virtual function pointer for g_thread_yield()
+ * @thread_join: virtual function pointer for g_thread_join()
+ * @thread_exit: virtual function pointer for g_thread_exit()
+ * @thread_set_priority: virtual function pointer for
+ * g_thread_set_priority()
+ * @thread_self: virtual function pointer for g_thread_self()
+ * @thread_equal: used internally by recursive mutex locks and by some
+ * assertion checks
+ *
+ * This function table is no longer used by g_thread_init()
+ * to initialize the thread system.
+ */
+
+/**
+ * G_THREADS_IMPL_POSIX:
+ *
+ * This macro is defined if POSIX style threads are used.
+ *
+ * Deprecated:2.32:POSIX threads are in use on all non-Windows systems.
+ * Use %G_OS_WIN32 to detect Windows.
+ */
+
+/**
+ * G_THREADS_IMPL_WIN32:
+ *
+ * This macro is defined if Windows style threads are used.
+ *
+ * Deprecated:2.32:Use %G_OS_WIN32 to detect Windows.
+ */
+
+
+/* {{{1 Exported Variables */
+
+/* Set this FALSE to have previously-compiled GStaticMutex code use the
+ * slow path (ie: call into us) to avoid compatibility problems.
+ */
+gboolean g_thread_use_default_impl = FALSE;
+
+GThreadFunctions g_thread_functions_for_glib_use =
+{
+ g_mutex_new,
+ g_mutex_lock,
+ g_mutex_trylock,
+ g_mutex_unlock,
+ g_mutex_free,
+ g_cond_new,
+ g_cond_signal,
+ g_cond_broadcast,
+ g_cond_wait,
+ g_cond_timed_wait,
+ g_cond_free,
+ g_private_new,
+ g_private_get,
+ g_private_set,
+ NULL,
+ g_thread_yield,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static guint64
+gettime (void)
+{
+ return g_get_monotonic_time () * 1000;
+}
+
+guint64 (*g_thread_gettime) (void) = gettime;
+
+/* Initialisation {{{1 ---------------------------------------------------- */
+gboolean g_threads_got_initialized = TRUE;
+
+/**
+ * g_thread_init:
+ * @vtable: a function table of type #GThreadFunctions, that provides
+ * the entry points to the thread system to be used. Since 2.32,
+ * this parameter is ignored and should always be %NULL
+ *
+ * If you use GLib from more than one thread, you must initialize the
+ * thread system by calling g_thread_init().
+ *
+ * Since version 2.24, calling g_thread_init() multiple times is allowed,
+ * but nothing happens except for the first call.
+ *
+ * Since version 2.32, GLib does not support custom thread implementations
+ * anymore and the @vtable parameter is ignored and you should pass %NULL.
+ *
+ * g_thread_init() must not be called directly or indirectly
+ * in a callback from GLib. Also no mutexes may be currently locked while
+ * calling g_thread_init().
+ *
+ * To use g_thread_init() in your program, you have to link
+ * with the libraries that the command pkg-config --libs
+ * gthread-2.0 outputs. This is not the case for all the
+ * other thread-related functions of GLib. Those can be used without
+ * having to link with the thread libraries.
+ *
+ * Deprecated:2.32: This function is no longer necessary. The GLib
+ * threading system is automatically initialized at the start
+ * of your program.
+ */
+
+/**
+ * g_thread_get_initialized:
+ *
+ * Indicates if g_thread_init() has been called.
+ *
+ * Returns: %TRUE if threads have been initialized.
+ *
+ * Since: 2.20
+ */
+gboolean
+g_thread_get_initialized (void)
+{
+ return g_thread_supported ();
+}
+
+/* We need this for ABI compatibility */
+GLIB_AVAILABLE_IN_ALL
+void g_thread_init_glib (void);
+void g_thread_init_glib (void) { }
+
+/* Internal variables {{{1 */
+
+static GSList *g_thread_all_threads = NULL;
+static GSList *g_thread_free_indices = NULL;
+
+/* Protects g_thread_all_threads and g_thread_free_indices */
+G_LOCK_DEFINE_STATIC (g_static_mutex);
+G_LOCK_DEFINE_STATIC (g_thread);
+
+/* Misc. GThread functions {{{1 */
+
+/**
+ * g_thread_set_priority:
+ * @thread: a #GThread.
+ * @priority: ignored
+ *
+ * This function does nothing.
+ *
+ * Deprecated:2.32: Thread priorities no longer have any effect.
+ */
+void
+g_thread_set_priority (GThread *thread,
+ GThreadPriority priority)
+{
+}
+
+/**
+ * g_thread_foreach:
+ * @thread_func: function to call for all #GThread structures
+ * @user_data: second argument to @thread_func
+ *
+ * Call @thread_func on all #GThreads that have been
+ * created with g_thread_create().
+ *
+ * Note that threads may decide to exit while @thread_func is
+ * running, so without intimate knowledge about the lifetime of
+ * foreign threads, @thread_func shouldn't access the GThread*
+ * pointer passed in as first argument. However, @thread_func will
+ * not be called for threads which are known to have exited already.
+ *
+ * Due to thread lifetime checks, this function has an execution complexity
+ * which is quadratic in the number of existing threads.
+ *
+ * Since: 2.10
+ *
+ * Deprecated:2.32: There aren't many things you can do with a #GThread,
+ * except comparing it with one that was returned from g_thread_create().
+ * There are better ways to find out if your thread is still alive.
+ */
+void
+g_thread_foreach (GFunc thread_func,
+ gpointer user_data)
+{
+ GSList *slist = NULL;
+ GRealThread *thread;
+ g_return_if_fail (thread_func != NULL);
+ /* snapshot the list of threads for iteration */
+ G_LOCK (g_thread);
+ slist = g_slist_copy (g_thread_all_threads);
+ G_UNLOCK (g_thread);
+ /* walk the list, skipping non-existent threads */
+ while (slist)
+ {
+ GSList *node = slist;
+ slist = node->next;
+ /* check whether the current thread still exists */
+ G_LOCK (g_thread);
+ if (g_slist_find (g_thread_all_threads, node->data))
+ thread = node->data;
+ else
+ thread = NULL;
+ G_UNLOCK (g_thread);
+ if (thread)
+ thread_func (thread, user_data);
+ g_slist_free_1 (node);
+ }
+}
+
+static void
+g_enumerable_thread_remove (gpointer data)
+{
+ GRealThread *thread = data;
+
+ G_LOCK (g_thread);
+ g_thread_all_threads = g_slist_remove (g_thread_all_threads, thread);
+ G_UNLOCK (g_thread);
+}
+
+GPrivate enumerable_thread_private = G_PRIVATE_INIT (g_enumerable_thread_remove);
+
+static void
+g_enumerable_thread_add (GRealThread *thread)
+{
+ G_LOCK (g_thread);
+ g_thread_all_threads = g_slist_prepend (g_thread_all_threads, thread);
+ G_UNLOCK (g_thread);
+
+ g_private_set (&enumerable_thread_private, thread);
+}
+
+static gpointer
+g_deprecated_thread_proxy (gpointer data)
+{
+ GRealThread *real = data;
+
+ g_enumerable_thread_add (real);
+
+ return g_thread_proxy (data);
+}
+
+/**
+ * g_thread_create:
+ * @func: a function to execute in the new thread
+ * @data: an argument to supply to the new thread
+ * @joinable: should this thread be joinable?
+ * @error: return location for error, or %NULL
+ *
+ * This function creates a new thread.
+ *
+ * The new thread executes the function @func with the argument @data.
+ * If the thread was created successfully, it is returned.
+ *
+ * @error can be %NULL to ignore errors, or non-%NULL to report errors.
+ * The error is set, if and only if the function returns %NULL.
+ *
+ * This function returns a reference to the created thread only if
+ * @joinable is %TRUE. In that case, you must free this reference by
+ * calling g_thread_unref() or g_thread_join(). If @joinable is %FALSE
+ * then you should probably not touch the return value.
+ *
+ * Returns: the new #GThread on success
+ *
+ * Deprecated:2.32: Use g_thread_new() instead
+ */
+GThread *
+g_thread_create (GThreadFunc func,
+ gpointer data,
+ gboolean joinable,
+ GError **error)
+{
+ return g_thread_create_full (func, data, 0, joinable, 0, 0, error);
+}
+
+/**
+ * g_thread_create_full:
+ * @func: a function to execute in the new thread.
+ * @data: an argument to supply to the new thread.
+ * @stack_size: a stack size for the new thread.
+ * @joinable: should this thread be joinable?
+ * @bound: ignored
+ * @priority: ignored
+ * @error: return location for error.
+ *
+ * This function creates a new thread.
+ *
+ * Returns: the new #GThread on success.
+ *
+ * Deprecated:2.32: The @bound and @priority arguments are now ignored.
+ * Use g_thread_new().
+ */
+GThread *
+g_thread_create_full (GThreadFunc func,
+ gpointer data,
+ gulong stack_size,
+ gboolean joinable,
+ gboolean bound,
+ GThreadPriority priority,
+ GError **error)
+{
+ GThread *thread;
+
+ thread = g_thread_new_internal (NULL, g_deprecated_thread_proxy,
+ func, data, stack_size, NULL, error);
+
+ if (thread && !joinable)
+ {
+ thread->joinable = FALSE;
+ g_thread_unref (thread);
+ }
+
+ return thread;
+}
+
+/* GOnce {{{1 ------------------------------------------------------------- */
+gboolean
+g_once_init_enter_impl (volatile gsize *location)
+{
+ return (g_once_init_enter) (location);
+}
+
+/* GStaticMutex {{{1 ------------------------------------------------------ */
+
+/**
+ * GStaticMutex:
+ *
+ * A #GStaticMutex works like a #GMutex.
+ *
+ * Prior to GLib 2.32, GStaticMutex had the significant advantage
+ * that it doesn't need to be created at run-time, but can be defined
+ * at compile-time. Since 2.32, #GMutex can be statically allocated
+ * as well, and GStaticMutex has been deprecated.
+ *
+ * Here is a version of our give_me_next_number() example using
+ * a GStaticMutex:
+ * |[
+ * int
+ * give_me_next_number (void)
+ * {
+ * static int current_number = 0;
+ * int ret_val;
+ * static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+ *
+ * g_static_mutex_lock (&mutex);
+ * ret_val = current_number = calc_next_number (current_number);
+ * g_static_mutex_unlock (&mutex);
+ *
+ * return ret_val;
+ * }
+ * ]|
+ *
+ * Sometimes you would like to dynamically create a mutex. If you don't
+ * want to require prior calling to g_thread_init(), because your code
+ * should also be usable in non-threaded programs, you are not able to
+ * use g_mutex_new() and thus #GMutex, as that requires a prior call to
+ * g_thread_init(). In these cases you can also use a #GStaticMutex.
+ * It must be initialized with g_static_mutex_init() before using it
+ * and freed with with g_static_mutex_free() when not needed anymore to
+ * free up any allocated resources.
+ *
+ * Even though #GStaticMutex is not opaque, it should only be used with
+ * the following functions, as it is defined differently on different
+ * platforms.
+ *
+ * All of the g_static_mutex_* functions apart from
+ * g_static_mutex_get_mutex() can also be used even if g_thread_init()
+ * has not yet been called. Then they do nothing, apart from
+ * g_static_mutex_trylock() which does nothing but returning %TRUE.
+ *
+ * All of the g_static_mutex_* functions are actually macros. Apart from
+ * taking their addresses, you can however use them as if they were
+ * functions.
+ */
+
+/**
+ * G_STATIC_MUTEX_INIT:
+ *
+ * A #GStaticMutex must be initialized with this macro, before it can
+ * be used. This macro can used be to initialize a variable, but it
+ * cannot be assigned to a variable. In that case you have to use
+ * g_static_mutex_init().
+ *
+ * |[
+ * GStaticMutex my_mutex = G_STATIC_MUTEX_INIT;
+ * ]|
+ **/
+
+/**
+ * g_static_mutex_init:
+ * @mutex: a #GStaticMutex to be initialized.
+ *
+ * Initializes @mutex.
+ * Alternatively you can initialize it with %G_STATIC_MUTEX_INIT.
+ *
+ * Deprecated: 2.32: Use g_mutex_init()
+ */
+void
+g_static_mutex_init (GStaticMutex *mutex)
+{
+ static const GStaticMutex init_mutex = G_STATIC_MUTEX_INIT;
+
+ g_return_if_fail (mutex);
+
+ *mutex = init_mutex;
+}
+
+/* IMPLEMENTATION NOTE:
+ *
+ * On some platforms a GStaticMutex is actually a normal GMutex stored
+ * inside of a structure instead of being allocated dynamically. We can
+ * only do this for platforms on which we know, in advance, how to
+ * allocate (size) and initialise (value) that memory.
+ *
+ * On other platforms, a GStaticMutex is nothing more than a pointer to
+ * a GMutex. In that case, the first access we make to the static mutex
+ * must first allocate the normal GMutex and store it into the pointer.
+ *
+ * configure.ac writes macros into glibconfig.h to determine if
+ * g_static_mutex_get_mutex() accesses the structure in memory directly
+ * (on platforms where we are able to do that) or if it ends up here,
+ * where we may have to allocate the GMutex before returning it.
+ */
+
+/**
+ * g_static_mutex_get_mutex:
+ * @mutex: a #GStaticMutex.
+ *
+ * For some operations (like g_cond_wait()) you must have a #GMutex
+ * instead of a #GStaticMutex. This function will return the
+ * corresponding #GMutex for @mutex.
+ *
+ * Returns: the #GMutex corresponding to @mutex.
+ *
+ * Deprecated: 2.32: Just use a #GMutex
+ */
+GMutex *
+g_static_mutex_get_mutex_impl (GStaticMutex* mutex)
+{
+ GMutex *result;
+
+ if (!g_thread_supported ())
+ return NULL;
+
+ result = g_atomic_pointer_get (&mutex->mutex);
+
+ if (!result)
+ {
+ G_LOCK (g_static_mutex);
+
+ result = mutex->mutex;
+ if (!result)
+ {
+ result = g_mutex_new ();
+ g_atomic_pointer_set (&mutex->mutex, result);
+ }
+
+ G_UNLOCK (g_static_mutex);
+ }
+
+ return result;
+}
+
+/* IMPLEMENTATION NOTE:
+ *
+ * g_static_mutex_lock(), g_static_mutex_trylock() and
+ * g_static_mutex_unlock() are all preprocessor macros that wrap the
+ * corresponding g_mutex_*() function around a call to
+ * g_static_mutex_get_mutex().
+ */
+
+/**
+ * g_static_mutex_lock:
+ * @mutex: a #GStaticMutex.
+ *
+ * Works like g_mutex_lock(), but for a #GStaticMutex.
+ *
+ * Deprecated: 2.32: Use g_mutex_lock()
+ */
+
+/**
+ * g_static_mutex_trylock:
+ * @mutex: a #GStaticMutex.
+ *
+ * Works like g_mutex_trylock(), but for a #GStaticMutex.
+ *
+ * Returns: %TRUE, if the #GStaticMutex could be locked.
+ *
+ * Deprecated: 2.32: Use g_mutex_trylock()
+ */
+
+/**
+ * g_static_mutex_unlock:
+ * @mutex: a #GStaticMutex.
+ *
+ * Works like g_mutex_unlock(), but for a #GStaticMutex.
+ *
+ * Deprecated: 2.32: Use g_mutex_unlock()
+ */
+
+/**
+ * g_static_mutex_free:
+ * @mutex: a #GStaticMutex to be freed.
+ *
+ * Releases all resources allocated to @mutex.
+ *
+ * You don't have to call this functions for a #GStaticMutex with an
+ * unbounded lifetime, i.e. objects declared 'static', but if you have
+ * a #GStaticMutex as a member of a structure and the structure is
+ * freed, you should also free the #GStaticMutex.
+ *
+ * Calling g_static_mutex_free() on a locked mutex may result in
+ * undefined behaviour.
+ *
+ * Deprecated: 2.32: Use g_mutex_clear()
+ */
+void
+g_static_mutex_free (GStaticMutex* mutex)
+{
+ GMutex **runtime_mutex;
+
+ g_return_if_fail (mutex);
+
+ /* The runtime_mutex is the first (or only) member of GStaticMutex,
+ * see both versions (of glibconfig.h) in configure.ac. Note, that
+ * this variable is NULL, if g_thread_init() hasn't been called or
+ * if we're using the default thread implementation and it provides
+ * static mutexes. */
+ runtime_mutex = ((GMutex**)mutex);
+
+ if (*runtime_mutex)
+ g_mutex_free (*runtime_mutex);
+
+ *runtime_mutex = NULL;
+}
+
+/* {{{1 GStaticRecMutex */
+
+/**
+ * GStaticRecMutex:
+ *
+ * A #GStaticRecMutex works like a #GStaticMutex, but it can be locked
+ * multiple times by one thread. If you enter it n times, you have to
+ * unlock it n times again to let other threads lock it. An exception
+ * is the function g_static_rec_mutex_unlock_full(): that allows you to
+ * unlock a #GStaticRecMutex completely returning the depth, (i.e. the
+ * number of times this mutex was locked). The depth can later be used
+ * to restore the state of the #GStaticRecMutex by calling
+ * g_static_rec_mutex_lock_full(). In GLib 2.32, #GStaticRecMutex has
+ * been deprecated in favor of #GRecMutex.
+ *
+ * Even though #GStaticRecMutex is not opaque, it should only be used
+ * with the following functions.
+ *
+ * All of the g_static_rec_mutex_* functions can be used even if
+ * g_thread_init() has not been called. Then they do nothing, apart
+ * from g_static_rec_mutex_trylock(), which does nothing but returning
+ * %TRUE.
+ */
+
+/**
+ * G_STATIC_REC_MUTEX_INIT:
+ *
+ * A #GStaticRecMutex must be initialized with this macro before it can
+ * be used. This macro can used be to initialize a variable, but it
+ * cannot be assigned to a variable. In that case you have to use
+ * g_static_rec_mutex_init().
+ *
+ * |[
+ * GStaticRecMutex my_mutex = G_STATIC_REC_MUTEX_INIT;
+ * ]|
+ */
+
+/**
+ * g_static_rec_mutex_init:
+ * @mutex: a #GStaticRecMutex to be initialized.
+ *
+ * A #GStaticRecMutex must be initialized with this function before it
+ * can be used. Alternatively you can initialize it with
+ * %G_STATIC_REC_MUTEX_INIT.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_init()
+ */
+void
+g_static_rec_mutex_init (GStaticRecMutex *mutex)
+{
+ static const GStaticRecMutex init_mutex = G_STATIC_REC_MUTEX_INIT;
+
+ g_return_if_fail (mutex);
+
+ *mutex = init_mutex;
+}
+
+static GRecMutex *
+g_static_rec_mutex_get_rec_mutex_impl (GStaticRecMutex* mutex)
+{
+ GRecMutex *result;
+
+ if (!g_thread_supported ())
+ return NULL;
+
+ result = (GRecMutex *) g_atomic_pointer_get (&mutex->mutex.mutex);
+
+ if (!result)
+ {
+ G_LOCK (g_static_mutex);
+
+ result = (GRecMutex *) mutex->mutex.mutex;
+ if (!result)
+ {
+ result = g_slice_new (GRecMutex);
+ g_rec_mutex_init (result);
+ g_atomic_pointer_set (&mutex->mutex.mutex, (GMutex *) result);
+ }
+
+ G_UNLOCK (g_static_mutex);
+ }
+
+ return result;
+}
+
+/**
+ * g_static_rec_mutex_lock:
+ * @mutex: a #GStaticRecMutex to lock.
+ *
+ * Locks @mutex. If @mutex is already locked by another thread, the
+ * current thread will block until @mutex is unlocked by the other
+ * thread. If @mutex is already locked by the calling thread, this
+ * functions increases the depth of @mutex and returns immediately.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_lock()
+ */
+void
+g_static_rec_mutex_lock (GStaticRecMutex* mutex)
+{
+ GRecMutex *rm;
+ rm = g_static_rec_mutex_get_rec_mutex_impl (mutex);
+ g_rec_mutex_lock (rm);
+ mutex->depth++;
+}
+
+/**
+ * g_static_rec_mutex_trylock:
+ * @mutex: a #GStaticRecMutex to lock.
+ *
+ * Tries to lock @mutex. If @mutex is already locked by another thread,
+ * it immediately returns %FALSE. Otherwise it locks @mutex and returns
+ * %TRUE. If @mutex is already locked by the calling thread, this
+ * functions increases the depth of @mutex and immediately returns
+ * %TRUE.
+ *
+ * Returns: %TRUE, if @mutex could be locked.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_trylock()
+ */
+gboolean
+g_static_rec_mutex_trylock (GStaticRecMutex* mutex)
+{
+ GRecMutex *rm;
+ rm = g_static_rec_mutex_get_rec_mutex_impl (mutex);
+
+ if (g_rec_mutex_trylock (rm))
+ {
+ mutex->depth++;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * g_static_rec_mutex_unlock:
+ * @mutex: a #GStaticRecMutex to unlock.
+ *
+ * Unlocks @mutex. Another thread will be allowed to lock @mutex only
+ * when it has been unlocked as many times as it had been locked
+ * before. If @mutex is completely unlocked and another thread is
+ * blocked in a g_static_rec_mutex_lock() call for @mutex, it will be
+ * woken and can lock @mutex itself.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_unlock()
+ */
+void
+g_static_rec_mutex_unlock (GStaticRecMutex* mutex)
+{
+ GRecMutex *rm;
+ rm = g_static_rec_mutex_get_rec_mutex_impl (mutex);
+ mutex->depth--;
+ g_rec_mutex_unlock (rm);
+}
+
+/**
+ * g_static_rec_mutex_lock_full:
+ * @mutex: a #GStaticRecMutex to lock.
+ * @depth: number of times this mutex has to be unlocked to be
+ * completely unlocked.
+ *
+ * Works like calling g_static_rec_mutex_lock() for @mutex @depth times.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_lock()
+ */
+void
+g_static_rec_mutex_lock_full (GStaticRecMutex *mutex,
+ guint depth)
+{
+ GRecMutex *rm;
+
+ rm = g_static_rec_mutex_get_rec_mutex_impl (mutex);
+ while (depth--)
+ {
+ g_rec_mutex_lock (rm);
+ mutex->depth++;
+ }
+}
+
+/**
+ * g_static_rec_mutex_unlock_full:
+ * @mutex: a #GStaticRecMutex to completely unlock.
+ *
+ * Completely unlocks @mutex. If another thread is blocked in a
+ * g_static_rec_mutex_lock() call for @mutex, it will be woken and can
+ * lock @mutex itself. This function returns the number of times that
+ * @mutex has been locked by the current thread. To restore the state
+ * before the call to g_static_rec_mutex_unlock_full() you can call
+ * g_static_rec_mutex_lock_full() with the depth returned by this
+ * function.
+ *
+ * Returns: number of times @mutex has been locked by the current
+ * thread.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_unlock()
+ */
+guint
+g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex)
+{
+ GRecMutex *rm;
+ gint depth;
+ gint i;
+
+ rm = g_static_rec_mutex_get_rec_mutex_impl (mutex);
+
+ /* all access to mutex->depth done while still holding the lock */
+ depth = mutex->depth;
+ i = mutex->depth;
+ mutex->depth = 0;
+
+ while (i--)
+ g_rec_mutex_unlock (rm);
+
+ return depth;
+}
+
+/**
+ * g_static_rec_mutex_free:
+ * @mutex: a #GStaticRecMutex to be freed.
+ *
+ * Releases all resources allocated to a #GStaticRecMutex.
+ *
+ * You don't have to call this functions for a #GStaticRecMutex with an
+ * unbounded lifetime, i.e. objects declared 'static', but if you have
+ * a #GStaticRecMutex as a member of a structure and the structure is
+ * freed, you should also free the #GStaticRecMutex.
+ *
+ * Deprecated: 2.32: Use g_rec_mutex_clear()
+ */
+void
+g_static_rec_mutex_free (GStaticRecMutex *mutex)
+{
+ g_return_if_fail (mutex);
+
+ if (mutex->mutex.mutex)
+ {
+ GRecMutex *rm = (GRecMutex *) mutex->mutex.mutex;
+
+ g_rec_mutex_clear (rm);
+ g_slice_free (GRecMutex, rm);
+ }
+}
+
+/* GStaticRWLock {{{1 ----------------------------------------------------- */
+
+/**
+ * GStaticRWLock:
+ *
+ * The #GStaticRWLock struct represents a read-write lock. A read-write
+ * lock can be used for protecting data that some portions of code only
+ * read from, while others also write. In such situations it is
+ * desirable that several readers can read at once, whereas of course
+ * only one writer may write at a time.
+ *
+ * Take a look at the following example:
+ * |[
+ * GStaticRWLock rwlock = G_STATIC_RW_LOCK_INIT;
+ * GPtrArray *array;
+ *
+ * gpointer
+ * my_array_get (guint index)
+ * {
+ * gpointer retval = NULL;
+ *
+ * if (!array)
+ * return NULL;
+ *
+ * g_static_rw_lock_reader_lock (&rwlock);
+ * if (index < array->len)
+ * retval = g_ptr_array_index (array, index);
+ * g_static_rw_lock_reader_unlock (&rwlock);
+ *
+ * return retval;
+ * }
+ *
+ * void
+ * my_array_set (guint index, gpointer data)
+ * {
+ * g_static_rw_lock_writer_lock (&rwlock);
+ *
+ * if (!array)
+ * array = g_ptr_array_new ();
+ *
+ * if (index >= array->len)
+ * g_ptr_array_set_size (array, index + 1);
+ * g_ptr_array_index (array, index) = data;
+ *
+ * g_static_rw_lock_writer_unlock (&rwlock);
+ * }
+ * ]|
+ *
+ * This example shows an array which can be accessed by many readers
+ * (the my_array_get() function) simultaneously, whereas the writers
+ * (the my_array_set() function) will only be allowed once at a time
+ * and only if no readers currently access the array. This is because
+ * of the potentially dangerous resizing of the array. Using these
+ * functions is fully multi-thread safe now.
+ *
+ * Most of the time, writers should have precedence over readers. That
+ * means, for this implementation, that as soon as a writer wants to
+ * lock the data, no other reader is allowed to lock the data, whereas,
+ * of course, the readers that already have locked the data are allowed
+ * to finish their operation. As soon as the last reader unlocks the
+ * data, the writer will lock it.
+ *
+ * Even though #GStaticRWLock is not opaque, it should only be used
+ * with the following functions.
+ *
+ * All of the g_static_rw_lock_* functions can be used even if
+ * g_thread_init() has not been called. Then they do nothing, apart
+ * from g_static_rw_lock_*_trylock, which does nothing but returning %TRUE.
+ *
+ * A read-write lock has a higher overhead than a mutex. For example, both
+ * g_static_rw_lock_reader_lock() and g_static_rw_lock_reader_unlock() have
+ * to lock and unlock a #GStaticMutex, so it takes at least twice the time
+ * to lock and unlock a #GStaticRWLock that it does to lock and unlock a
+ * #GStaticMutex. So only data structures that are accessed by multiple
+ * readers, and which keep the lock for a considerable time justify a
+ * #GStaticRWLock. The above example most probably would fare better with a
+ * #GStaticMutex.
+ *
+ * Deprecated: 2.32: Use a #GRWLock instead
+ **/
+
+/**
+ * G_STATIC_RW_LOCK_INIT:
+ *
+ * A #GStaticRWLock must be initialized with this macro before it can
+ * be used. This macro can used be to initialize a variable, but it
+ * cannot be assigned to a variable. In that case you have to use
+ * g_static_rw_lock_init().
+ *
+ * |[
+ * GStaticRWLock my_lock = G_STATIC_RW_LOCK_INIT;
+ * ]|
+ */
+
+/**
+ * g_static_rw_lock_init:
+ * @lock: a #GStaticRWLock to be initialized.
+ *
+ * A #GStaticRWLock must be initialized with this function before it
+ * can be used. Alternatively you can initialize it with
+ * %G_STATIC_RW_LOCK_INIT.
+ *
+ * Deprecated: 2.32: Use g_rw_lock_init() instead
+ */
+void
+g_static_rw_lock_init (GStaticRWLock* lock)
+{
+ static const GStaticRWLock init_lock = G_STATIC_RW_LOCK_INIT;
+
+ g_return_if_fail (lock);
+
+ *lock = init_lock;
+}
+
+inline static void
+g_static_rw_lock_wait (GCond** cond, GStaticMutex* mutex)
+{
+ if (!*cond)
+ *cond = g_cond_new ();
+ g_cond_wait (*cond, g_static_mutex_get_mutex (mutex));
+}
+
+inline static void
+g_static_rw_lock_signal (GStaticRWLock* lock)
+{
+ if (lock->want_to_write && lock->write_cond)
+ g_cond_signal (lock->write_cond);
+ else if (lock->want_to_read && lock->read_cond)
+ g_cond_broadcast (lock->read_cond);
+}
+
+/**
+ * g_static_rw_lock_reader_lock:
+ * @lock: a #GStaticRWLock to lock for reading.
+ *
+ * Locks @lock for reading. There may be unlimited concurrent locks for
+ * reading of a #GStaticRWLock at the same time. If @lock is already
+ * locked for writing by another thread or if another thread is already
+ * waiting to lock @lock for writing, this function will block until
+ * @lock is unlocked by the other writing thread and no other writing
+ * threads want to lock @lock. This lock has to be unlocked by
+ * g_static_rw_lock_reader_unlock().
+ *
+ * #GStaticRWLock is not recursive. It might seem to be possible to
+ * recursively lock for reading, but that can result in a deadlock, due
+ * to writer preference.
+ *
+ * Deprecated: 2.32: Use g_rw_lock_reader_lock() instead
+ */
+void
+g_static_rw_lock_reader_lock (GStaticRWLock* lock)
+{
+ g_return_if_fail (lock);
+
+ if (!g_threads_got_initialized)
+ return;
+
+ g_static_mutex_lock (&lock->mutex);
+ lock->want_to_read++;
+ while (lock->have_writer || lock->want_to_write)
+ g_static_rw_lock_wait (&lock->read_cond, &lock->mutex);
+ lock->want_to_read--;
+ lock->read_counter++;
+ g_static_mutex_unlock (&lock->mutex);
+}
+
+/**
+ * g_static_rw_lock_reader_trylock:
+ * @lock: a #GStaticRWLock to lock for reading
+ *
+ * Tries to lock @lock for reading. If @lock is already locked for
+ * writing by another thread or if another thread is already waiting to
+ * lock @lock for writing, immediately returns %FALSE. Otherwise locks
+ * @lock for reading and returns %TRUE. This lock has to be unlocked by
+ * g_static_rw_lock_reader_unlock().
+ *
+ * Returns: %TRUE, if @lock could be locked for reading
+ *
+ * Deprecated: 2.32: Use g_rw_lock_reader_trylock() instead
+ */
+gboolean
+g_static_rw_lock_reader_trylock (GStaticRWLock* lock)
+{
+ gboolean ret_val = FALSE;
+
+ g_return_val_if_fail (lock, FALSE);
+
+ if (!g_threads_got_initialized)
+ return TRUE;
+
+ g_static_mutex_lock (&lock->mutex);
+ if (!lock->have_writer && !lock->want_to_write)
+ {
+ lock->read_counter++;
+ ret_val = TRUE;
+ }
+ g_static_mutex_unlock (&lock->mutex);
+ return ret_val;
+}
+
+/**
+ * g_static_rw_lock_reader_unlock:
+ * @lock: a #GStaticRWLock to unlock after reading
+ *
+ * Unlocks @lock. If a thread waits to lock @lock for writing and all
+ * locks for reading have been unlocked, the waiting thread is woken up
+ * and can lock @lock for writing.
+ *
+ * Deprecated: 2.32: Use g_rw_lock_reader_unlock() instead
+ */
+void
+g_static_rw_lock_reader_unlock (GStaticRWLock* lock)
+{
+ g_return_if_fail (lock);
+
+ if (!g_threads_got_initialized)
+ return;
+
+ g_static_mutex_lock (&lock->mutex);
+ lock->read_counter--;
+ if (lock->read_counter == 0)
+ g_static_rw_lock_signal (lock);
+ g_static_mutex_unlock (&lock->mutex);
+}
+
+/**
+ * g_static_rw_lock_writer_lock:
+ * @lock: a #GStaticRWLock to lock for writing
+ *
+ * Locks @lock for writing. If @lock is already locked for writing or
+ * reading by other threads, this function will block until @lock is
+ * completely unlocked and then lock @lock for writing. While this
+ * functions waits to lock @lock, no other thread can lock @lock for
+ * reading. When @lock is locked for writing, no other thread can lock
+ * @lock (neither for reading nor writing). This lock has to be
+ * unlocked by g_static_rw_lock_writer_unlock().
+ *
+ * Deprecated: 2.32: Use g_rw_lock_writer_lock() instead
+ */
+void
+g_static_rw_lock_writer_lock (GStaticRWLock* lock)
+{
+ g_return_if_fail (lock);
+
+ if (!g_threads_got_initialized)
+ return;
+
+ g_static_mutex_lock (&lock->mutex);
+ lock->want_to_write++;
+ while (lock->have_writer || lock->read_counter)
+ g_static_rw_lock_wait (&lock->write_cond, &lock->mutex);
+ lock->want_to_write--;
+ lock->have_writer = TRUE;
+ g_static_mutex_unlock (&lock->mutex);
+}
+
+/**
+ * g_static_rw_lock_writer_trylock:
+ * @lock: a #GStaticRWLock to lock for writing
+ *
+ * Tries to lock @lock for writing. If @lock is already locked (for
+ * either reading or writing) by another thread, it immediately returns
+ * %FALSE. Otherwise it locks @lock for writing and returns %TRUE. This
+ * lock has to be unlocked by g_static_rw_lock_writer_unlock().
+ *
+ * Returns: %TRUE, if @lock could be locked for writing
+ *
+ * Deprecated: 2.32: Use g_rw_lock_writer_trylock() instead
+ */
+gboolean
+g_static_rw_lock_writer_trylock (GStaticRWLock* lock)
+{
+ gboolean ret_val = FALSE;
+
+ g_return_val_if_fail (lock, FALSE);
+
+ if (!g_threads_got_initialized)
+ return TRUE;
+
+ g_static_mutex_lock (&lock->mutex);
+ if (!lock->have_writer && !lock->read_counter)
+ {
+ lock->have_writer = TRUE;
+ ret_val = TRUE;
+ }
+ g_static_mutex_unlock (&lock->mutex);
+ return ret_val;
+}
+
+/**
+ * g_static_rw_lock_writer_unlock:
+ * @lock: a #GStaticRWLock to unlock after writing.
+ *
+ * Unlocks @lock. If a thread is waiting to lock @lock for writing and
+ * all locks for reading have been unlocked, the waiting thread is
+ * woken up and can lock @lock for writing. If no thread is waiting to
+ * lock @lock for writing, and some thread or threads are waiting to
+ * lock @lock for reading, the waiting threads are woken up and can
+ * lock @lock for reading.
+ *
+ * Deprecated: 2.32: Use g_rw_lock_writer_unlock() instead
+ */
+void
+g_static_rw_lock_writer_unlock (GStaticRWLock* lock)
+{
+ g_return_if_fail (lock);
+
+ if (!g_threads_got_initialized)
+ return;
+
+ g_static_mutex_lock (&lock->mutex);
+ lock->have_writer = FALSE;
+ g_static_rw_lock_signal (lock);
+ g_static_mutex_unlock (&lock->mutex);
+}
+
+/**
+ * g_static_rw_lock_free:
+ * @lock: a #GStaticRWLock to be freed.
+ *
+ * Releases all resources allocated to @lock.
+ *
+ * You don't have to call this functions for a #GStaticRWLock with an
+ * unbounded lifetime, i.e. objects declared 'static', but if you have
+ * a #GStaticRWLock as a member of a structure, and the structure is
+ * freed, you should also free the #GStaticRWLock.
+ *
+ * Deprecated: 2.32: Use a #GRWLock instead
+ */
+void
+g_static_rw_lock_free (GStaticRWLock* lock)
+{
+ g_return_if_fail (lock);
+
+ if (lock->read_cond)
+ {
+ g_cond_free (lock->read_cond);
+ lock->read_cond = NULL;
+ }
+ if (lock->write_cond)
+ {
+ g_cond_free (lock->write_cond);
+ lock->write_cond = NULL;
+ }
+ g_static_mutex_free (&lock->mutex);
+}
+
+/* GPrivate {{{1 ------------------------------------------------------ */
+
+/**
+ * g_private_new:
+ * @notify: a #GDestroyNotify
+ *
+ * Creates a new #GPrivate.
+ *
+ * Deprecated:2.32: dynamic allocation of #GPrivate is a bad idea. Use
+ * static storage and G_PRIVATE_INIT() instead.
+ *
+ * Returns: a newly allocated #GPrivate (which can never be destroyed)
+ */
+GPrivate *
+g_private_new (GDestroyNotify notify)
+{
+ GPrivate tmp = G_PRIVATE_INIT (notify);
+ GPrivate *key;
+
+ key = g_slice_new (GPrivate);
+ *key = tmp;
+
+ return key;
+}
+
+/* {{{1 GStaticPrivate */
+
+typedef struct _GStaticPrivateNode GStaticPrivateNode;
+struct _GStaticPrivateNode
+{
+ gpointer data;
+ GDestroyNotify destroy;
+ GStaticPrivate *owner;
+};
+
+static void
+g_static_private_cleanup (gpointer data)
+{
+ GArray *array = data;
+ guint i;
+
+ for (i = 0; i < array->len; i++ )
+ {
+ GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i);
+ if (node->destroy)
+ node->destroy (node->data);
+ }
+
+ g_array_free (array, TRUE);
+}
+
+GPrivate static_private_private = G_PRIVATE_INIT (g_static_private_cleanup);
+
+/**
+ * GStaticPrivate:
+ *
+ * A #GStaticPrivate works almost like a #GPrivate, but it has one
+ * significant advantage. It doesn't need to be created at run-time
+ * like a #GPrivate, but can be defined at compile-time. This is
+ * similar to the difference between #GMutex and #GStaticMutex.
+ *
+ * Now look at our give_me_next_number() example with #GStaticPrivate:
+ * |[
+ * int
+ * give_me_next_number ()
+ * {
+ * static GStaticPrivate current_number_key = G_STATIC_PRIVATE_INIT;
+ * int *current_number = g_static_private_get (¤t_number_key);
+ *
+ * if (!current_number)
+ * {
+ * current_number = g_new (int, 1);
+ * *current_number = 0;
+ * g_static_private_set (¤t_number_key, current_number, g_free);
+ * }
+ *
+ * *current_number = calc_next_number (*current_number);
+ *
+ * return *current_number;
+ * }
+ * ]|
+ */
+
+/**
+ * G_STATIC_PRIVATE_INIT:
+ *
+ * Every #GStaticPrivate must be initialized with this macro, before it
+ * can be used.
+ *
+ * |[
+ * GStaticPrivate my_private = G_STATIC_PRIVATE_INIT;
+ * ]|
+ */
+
+/**
+ * g_static_private_init:
+ * @private_key: a #GStaticPrivate to be initialized
+ *
+ * Initializes @private_key. Alternatively you can initialize it with
+ * %G_STATIC_PRIVATE_INIT.
+ */
+void
+g_static_private_init (GStaticPrivate *private_key)
+{
+ private_key->index = 0;
+}
+
+/**
+ * g_static_private_get:
+ * @private_key: a #GStaticPrivate
+ *
+ * Works like g_private_get() only for a #GStaticPrivate.
+ *
+ * This function works even if g_thread_init() has not yet been called.
+ *
+ * Returns: the corresponding pointer
+ */
+gpointer
+g_static_private_get (GStaticPrivate *private_key)
+{
+ GArray *array;
+ gpointer ret = NULL;
+
+ array = g_private_get (&static_private_private);
+
+ if (array && private_key->index != 0 && private_key->index <= array->len)
+ {
+ GStaticPrivateNode *node;
+
+ node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
+
+ /* Deal with the possibility that the GStaticPrivate which used
+ * to have this index got freed and the index got allocated to
+ * a new one. In this case, the data in the node is stale, so
+ * free it and return NULL.
+ */
+ if (G_UNLIKELY (node->owner != private_key))
+ {
+ if (node->destroy)
+ node->destroy (node->data);
+ node->destroy = NULL;
+ node->data = NULL;
+ node->owner = NULL;
+ }
+ ret = node->data;
+ }
+
+ return ret;
+}
+
+/**
+ * g_static_private_set:
+ * @private_key: a #GStaticPrivate
+ * @data: the new pointer
+ * @notify: a function to be called with the pointer whenever the
+ * current thread ends or sets this pointer again
+ *
+ * Sets the pointer keyed to @private_key for the current thread and
+ * the function @notify to be called with that pointer (%NULL or
+ * non-%NULL), whenever the pointer is set again or whenever the
+ * current thread ends.
+ *
+ * This function works even if g_thread_init() has not yet been called.
+ * If g_thread_init() is called later, the @data keyed to @private_key
+ * will be inherited only by the main thread, i.e. the one that called
+ * g_thread_init().
+ *
+ * @notify is used quite differently from @destructor in g_private_new().
+ */
+void
+g_static_private_set (GStaticPrivate *private_key,
+ gpointer data,
+ GDestroyNotify notify)
+{
+ GArray *array;
+ static guint next_index = 0;
+ GStaticPrivateNode *node;
+
+ if (!private_key->index)
+ {
+ G_LOCK (g_thread);
+
+ if (!private_key->index)
+ {
+ if (g_thread_free_indices)
+ {
+ private_key->index = GPOINTER_TO_UINT (g_thread_free_indices->data);
+ g_thread_free_indices = g_slist_delete_link (g_thread_free_indices,
+ g_thread_free_indices);
+ }
+ else
+ private_key->index = ++next_index;
+ }
+
+ G_UNLOCK (g_thread);
+ }
+
+ array = g_private_get (&static_private_private);
+ if (!array)
+ {
+ array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode));
+ g_private_set (&static_private_private, array);
+ }
+ if (private_key->index > array->len)
+ g_array_set_size (array, private_key->index);
+
+ node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
+
+ if (node->destroy)
+ node->destroy (node->data);
+
+ node->data = data;
+ node->destroy = notify;
+ node->owner = private_key;
+}
+
+/**
+ * g_static_private_free:
+ * @private_key: a #GStaticPrivate to be freed
+ *
+ * Releases all resources allocated to @private_key.
+ *
+ * You don't have to call this functions for a #GStaticPrivate with an
+ * unbounded lifetime, i.e. objects declared 'static', but if you have
+ * a #GStaticPrivate as a member of a structure and the structure is
+ * freed, you should also free the #GStaticPrivate.
+ */
+void
+g_static_private_free (GStaticPrivate *private_key)
+{
+ guint idx = private_key->index;
+
+ if (!idx)
+ return;
+
+ private_key->index = 0;
+
+ /* Freeing the per-thread data is deferred to either the
+ * thread end or the next g_static_private_get() call for
+ * the same index.
+ */
+ G_LOCK (g_thread);
+ g_thread_free_indices = g_slist_prepend (g_thread_free_indices,
+ GUINT_TO_POINTER (idx));
+ G_UNLOCK (g_thread);
+}
+
+/* GMutex {{{1 ------------------------------------------------------ */
+
+/**
+ * g_mutex_new:
+ *
+ * Allocates and initializes a new #GMutex.
+ *
+ * Returns: a newly allocated #GMutex. Use g_mutex_free() to free
+ *
+ * Deprecated: 2.32: GMutex can now be statically allocated, or embedded
+ * in structures and initialised with g_mutex_init().
+ */
+GMutex *
+g_mutex_new (void)
+{
+ GMutex *mutex;
+
+ mutex = g_slice_new (GMutex);
+ g_mutex_init (mutex);
+
+ return mutex;
+}
+
+/**
+ * g_mutex_free:
+ * @mutex: a #GMutex
+ *
+ * Destroys a @mutex that has been created with g_mutex_new().
+ *
+ * Calling g_mutex_free() on a locked mutex may result
+ * in undefined behaviour.
+ *
+ * Deprecated: 2.32: GMutex can now be statically allocated, or embedded
+ * in structures and initialised with g_mutex_init().
+ */
+void
+g_mutex_free (GMutex *mutex)
+{
+ g_mutex_clear (mutex);
+ g_slice_free (GMutex, mutex);
+}
+
+/* GCond {{{1 ------------------------------------------------------ */
+
+/**
+ * g_cond_new:
+ *
+ * Allocates and initializes a new #GCond.
+ *
+ * Returns: a newly allocated #GCond. Free with g_cond_free()
+ *
+ * Deprecated: 2.32: GCond can now be statically allocated, or embedded
+ * in structures and initialised with g_cond_init().
+ */
+GCond *
+g_cond_new (void)
+{
+ GCond *cond;
+
+ cond = g_slice_new (GCond);
+ g_cond_init (cond);
+
+ return cond;
+}
+
+/**
+ * g_cond_free:
+ * @cond: a #GCond
+ *
+ * Destroys a #GCond that has been created with g_cond_new().
+ *
+ * Calling g_cond_free() for a #GCond on which threads are
+ * blocking leads to undefined behaviour.
+ *
+ * Deprecated: 2.32: GCond can now be statically allocated, or embedded
+ * in structures and initialised with g_cond_init().
+ */
+void
+g_cond_free (GCond *cond)
+{
+ g_cond_clear (cond);
+ g_slice_free (GCond, cond);
+}
+
+/**
+ * g_cond_timed_wait:
+ * @cond: a #GCond
+ * @mutex: a #GMutex that is currently locked
+ * @abs_time: a #GTimeVal, determining the final time
+ *
+ * Waits until this thread is woken up on @cond, but not longer than
+ * until the time specified by @abs_time. The @mutex is unlocked before
+ * falling asleep and locked again before resuming.
+ *
+ * If @abs_time is %NULL, g_cond_timed_wait() acts like g_cond_wait().
+ *
+ * This function can be used even if g_thread_init() has not yet been
+ * called, and, in that case, will immediately return %TRUE.
+ *
+ * To easily calculate @abs_time a combination of g_get_real_time()
+ * and g_time_val_add() can be used.
+ *
+ * Returns: %TRUE if @cond was signalled, or %FALSE on timeout
+ *
+ * Deprecated:2.32: Use g_cond_wait_until() instead.
+ */
+gboolean
+g_cond_timed_wait (GCond *cond,
+ GMutex *mutex,
+ GTimeVal *abs_time)
+{
+ gint64 end_time;
+
+ if (abs_time == NULL)
+ {
+ g_cond_wait (cond, mutex);
+ return TRUE;
+ }
+
+ end_time = abs_time->tv_sec;
+ end_time *= 1000000;
+ end_time += abs_time->tv_usec;
+
+ /* would be nice if we had clock_rtoffset, but that didn't seem to
+ * make it into the kernel yet...
+ */
+ end_time += g_get_monotonic_time () - g_get_real_time ();
+
+ return g_cond_wait_until (cond, mutex, end_time);
+}
+
+/* {{{1 Epilogue */
+/* vim: set foldmethod=marker: */
diff --git a/glib/deprecated/gthread.h b/glib/deprecated/gthread.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d490a1107b7abb30dd24cd6f88f0a943bdd8f7f
--- /dev/null
+++ b/glib/deprecated/gthread.h
@@ -0,0 +1,293 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_DEPRECATED_THREAD_H__
+#define __G_DEPRECATED_THREAD_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+
+typedef enum
+{
+ G_THREAD_PRIORITY_LOW,
+ G_THREAD_PRIORITY_NORMAL,
+ G_THREAD_PRIORITY_HIGH,
+ G_THREAD_PRIORITY_URGENT
+} GThreadPriority GLIB_DEPRECATED_TYPE_IN_2_32;
+
+struct _GThread
+{
+ /*< private >*/
+ GThreadFunc func;
+ gpointer data;
+ gboolean joinable;
+ GThreadPriority priority;
+};
+
+typedef struct _GThreadFunctions GThreadFunctions GLIB_DEPRECATED_TYPE_IN_2_32;
+struct _GThreadFunctions
+{
+ GMutex* (*mutex_new) (void);
+ void (*mutex_lock) (GMutex *mutex);
+ gboolean (*mutex_trylock) (GMutex *mutex);
+ void (*mutex_unlock) (GMutex *mutex);
+ void (*mutex_free) (GMutex *mutex);
+ GCond* (*cond_new) (void);
+ void (*cond_signal) (GCond *cond);
+ void (*cond_broadcast) (GCond *cond);
+ void (*cond_wait) (GCond *cond,
+ GMutex *mutex);
+ gboolean (*cond_timed_wait) (GCond *cond,
+ GMutex *mutex,
+ GTimeVal *end_time);
+ void (*cond_free) (GCond *cond);
+ GPrivate* (*private_new) (GDestroyNotify destructor);
+ gpointer (*private_get) (GPrivate *private_key);
+ void (*private_set) (GPrivate *private_key,
+ gpointer data);
+ void (*thread_create) (GThreadFunc func,
+ gpointer data,
+ gulong stack_size,
+ gboolean joinable,
+ gboolean bound,
+ GThreadPriority priority,
+ gpointer thread,
+ GError **error);
+ void (*thread_yield) (void);
+ void (*thread_join) (gpointer thread);
+ void (*thread_exit) (void);
+ void (*thread_set_priority)(gpointer thread,
+ GThreadPriority priority);
+ void (*thread_self) (gpointer thread);
+ gboolean (*thread_equal) (gpointer thread1,
+ gpointer thread2);
+} GLIB_DEPRECATED_TYPE_IN_2_32;
+
+GLIB_VAR GThreadFunctions g_thread_functions_for_glib_use;
+GLIB_VAR gboolean g_thread_use_default_impl;
+
+GLIB_VAR guint64 (*g_thread_gettime) (void);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_thread_new)
+GThread *g_thread_create (GThreadFunc func,
+ gpointer data,
+ gboolean joinable,
+ GError **error);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_thread_new)
+GThread *g_thread_create_full (GThreadFunc func,
+ gpointer data,
+ gulong stack_size,
+ gboolean joinable,
+ gboolean bound,
+ GThreadPriority priority,
+ GError **error);
+
+GLIB_DEPRECATED_IN_2_32
+void g_thread_set_priority (GThread *thread,
+ GThreadPriority priority);
+
+GLIB_DEPRECATED_IN_2_32
+void g_thread_foreach (GFunc thread_func,
+ gpointer user_data);
+
+#ifndef G_OS_WIN32
+#include
+#include
+#endif
+
+#define g_static_mutex_get_mutex g_static_mutex_get_mutex_impl GLIB_DEPRECATED_MACRO_IN_2_32
+#ifndef G_OS_WIN32
+#define G_STATIC_MUTEX_INIT { NULL, PTHREAD_MUTEX_INITIALIZER } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_init)
+#else
+#define G_STATIC_MUTEX_INIT { NULL } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_init)
+#endif
+typedef struct
+{
+ GMutex *mutex;
+#ifndef G_OS_WIN32
+ /* only for ABI compatibility reasons */
+ pthread_mutex_t unused;
+#endif
+} GStaticMutex GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GMutex);
+
+#define g_static_mutex_lock(mutex) \
+ g_mutex_lock (g_static_mutex_get_mutex (mutex)) GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_lock)
+#define g_static_mutex_trylock(mutex) \
+ g_mutex_trylock (g_static_mutex_get_mutex (mutex)) GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_trylock)
+#define g_static_mutex_unlock(mutex) \
+ g_mutex_unlock (g_static_mutex_get_mutex (mutex)) GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_unlock)
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_mutex_init)
+void g_static_mutex_init (GStaticMutex *mutex);
+GLIB_DEPRECATED_IN_2_32_FOR(g_mutex_clear)
+void g_static_mutex_free (GStaticMutex *mutex);
+GLIB_DEPRECATED_IN_2_32_FOR(GMutex)
+GMutex *g_static_mutex_get_mutex_impl (GStaticMutex *mutex);
+
+typedef struct _GStaticRecMutex GStaticRecMutex GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GRecMutex);
+struct _GStaticRecMutex
+{
+ /*< private >*/
+ GStaticMutex mutex;
+ guint depth;
+
+ /* ABI compat only */
+ union {
+#ifdef G_OS_WIN32
+ void *owner;
+#else
+ pthread_t owner;
+#endif
+ gdouble dummy;
+ } unused;
+} GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GRecMutex);
+
+#define G_STATIC_REC_MUTEX_INIT { G_STATIC_MUTEX_INIT, 0, { 0 } } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_rec_mutex_init)
+GLIB_DEPRECATED_IN_2_32_FOR(g_rec_mutex_init)
+void g_static_rec_mutex_init (GStaticRecMutex *mutex);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rec_mutex_lock)
+void g_static_rec_mutex_lock (GStaticRecMutex *mutex);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rec_mutex_try_lock)
+gboolean g_static_rec_mutex_trylock (GStaticRecMutex *mutex);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rec_mutex_unlock)
+void g_static_rec_mutex_unlock (GStaticRecMutex *mutex);
+
+GLIB_DEPRECATED_IN_2_32
+void g_static_rec_mutex_lock_full (GStaticRecMutex *mutex,
+ guint depth);
+
+GLIB_DEPRECATED_IN_2_32
+guint g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rec_mutex_free)
+void g_static_rec_mutex_free (GStaticRecMutex *mutex);
+
+typedef struct _GStaticRWLock GStaticRWLock GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GRWLock);
+struct _GStaticRWLock
+{
+ /*< private >*/
+ GStaticMutex mutex;
+ GCond *read_cond;
+ GCond *write_cond;
+ guint read_counter;
+ gboolean have_writer;
+ guint want_to_read;
+ guint want_to_write;
+} GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GRWLock);
+
+#define G_STATIC_RW_LOCK_INIT { G_STATIC_MUTEX_INIT, NULL, NULL, 0, FALSE, 0, 0 } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_rw_lock_init)
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_init)
+void g_static_rw_lock_init (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_reader_lock)
+void g_static_rw_lock_reader_lock (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_reader_trylock)
+gboolean g_static_rw_lock_reader_trylock (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_reader_unlock)
+void g_static_rw_lock_reader_unlock (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_writer_lock)
+void g_static_rw_lock_writer_lock (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_writer_trylock)
+gboolean g_static_rw_lock_writer_trylock (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_writer_unlock)
+void g_static_rw_lock_writer_unlock (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_rw_lock_free)
+void g_static_rw_lock_free (GStaticRWLock *lock);
+
+GLIB_DEPRECATED_IN_2_32
+GPrivate * g_private_new (GDestroyNotify notify);
+
+typedef struct _GStaticPrivate GStaticPrivate GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GPrivate);
+struct _GStaticPrivate
+{
+ /*< private >*/
+ guint index;
+} GLIB_DEPRECATED_TYPE_IN_2_32_FOR(GPrivate);
+
+#define G_STATIC_PRIVATE_INIT { 0 } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(G_PRIVATE_INIT)
+GLIB_DEPRECATED_IN_2_32
+void g_static_private_init (GStaticPrivate *private_key);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_private_get)
+gpointer g_static_private_get (GStaticPrivate *private_key);
+
+GLIB_DEPRECATED_IN_2_32_FOR(g_private_set)
+void g_static_private_set (GStaticPrivate *private_key,
+ gpointer data,
+ GDestroyNotify notify);
+
+GLIB_DEPRECATED_IN_2_32
+void g_static_private_free (GStaticPrivate *private_key);
+
+GLIB_DEPRECATED_IN_2_32
+gboolean g_once_init_enter_impl (volatile gsize *location);
+
+GLIB_DEPRECATED_IN_2_32
+void g_thread_init (gpointer vtable);
+GLIB_DEPRECATED_IN_2_32
+void g_thread_init_with_errorcheck_mutexes (gpointer vtable);
+
+GLIB_DEPRECATED_IN_2_32
+gboolean g_thread_get_initialized (void);
+
+GLIB_VAR gboolean g_threads_got_initialized;
+
+#define g_thread_supported() (1) GLIB_DEPRECATED_MACRO_IN_2_32
+
+GLIB_DEPRECATED_IN_2_32
+GMutex * g_mutex_new (void);
+GLIB_DEPRECATED_IN_2_32
+void g_mutex_free (GMutex *mutex);
+GLIB_DEPRECATED_IN_2_32
+GCond * g_cond_new (void);
+GLIB_DEPRECATED_IN_2_32
+void g_cond_free (GCond *cond);
+GLIB_DEPRECATED_IN_2_32
+gboolean g_cond_timed_wait (GCond *cond,
+ GMutex *mutex,
+ GTimeVal *timeval);
+
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+G_END_DECLS
+
+#endif /* __G_DEPRECATED_THREAD_H__ */
diff --git a/glib/dirent/dirent-zip b/glib/dirent/dirent-zip
new file mode 100644
index 0000000000000000000000000000000000000000..7301987eb0f55241aac5d6ff959310898958cb82
--- /dev/null
+++ b/glib/dirent/dirent-zip
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Build developer package for the dirent library.
+
+ZIP=/tmp/dirent.zip
+
+mkdir -p dist/include dist/lib
+cp dirent.h dist/include
+cp dirent.lib dist/lib
+
+(cd dist
+rm $ZIP
+zip $ZIP -@ <
+ * Significantly revised and rewinddir, seekdir and telldir added by Colin
+ * Peters
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "dirent.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include /* for GetFileAttributes */
+
+#include
+
+#ifdef _UNICODE
+#define _tdirent _wdirent
+#define _TDIR _WDIR
+#define _topendir _wopendir
+#define _tclosedir _wclosedir
+#define _treaddir _wreaddir
+#define _trewinddir _wrewinddir
+#define _ttelldir _wtelldir
+#define _tseekdir _wseekdir
+#else
+#define _tdirent dirent
+#define _TDIR DIR
+#define _topendir opendir
+#define _tclosedir closedir
+#define _treaddir readdir
+#define _trewinddir rewinddir
+#define _ttelldir telldir
+#define _tseekdir seekdir
+#endif
+
+#define SUFFIX _T("*")
+#define SLASH _T("\\")
+
+
+/*
+ * opendir
+ *
+ * Returns a pointer to a DIR structure appropriately filled in to begin
+ * searching a directory.
+ */
+_TDIR *
+_topendir (const _TCHAR *szPath)
+{
+ _TDIR *nd;
+ unsigned int rc;
+ _TCHAR szFullPath[MAX_PATH];
+
+ errno = 0;
+
+ if (!szPath)
+ {
+ errno = EFAULT;
+ return (_TDIR *) 0;
+ }
+
+ if (szPath[0] == _T('\0'))
+ {
+ errno = ENOTDIR;
+ return (_TDIR *) 0;
+ }
+
+ /* Attempt to determine if the given path really is a directory. */
+ rc = GetFileAttributes (szPath);
+ if (rc == (unsigned int)-1)
+ {
+ /* call GetLastError for more error info */
+ errno = ENOENT;
+ return (_TDIR *) 0;
+ }
+ if (!(rc & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /* Error, entry exists but not a directory. */
+ errno = ENOTDIR;
+ return (_TDIR *) 0;
+ }
+
+ /* Make an absolute pathname. */
+ _tfullpath (szFullPath, szPath, MAX_PATH);
+
+ /* Allocate enough space to store DIR structure and the complete
+ * directory path given. */
+ nd = (_TDIR *) malloc (sizeof (_TDIR) + (_tcslen(szFullPath) + _tcslen (SLASH) +
+ _tcslen(SUFFIX) + 1) * sizeof(_TCHAR));
+
+ if (!nd)
+ {
+ /* Error, out of memory. */
+ errno = ENOMEM;
+ return (_TDIR *) 0;
+ }
+
+ /* Create the search expression. */
+ _tcscpy (nd->dd_name, szFullPath);
+
+ /* Add on a slash if the path does not end with one. */
+ if (nd->dd_name[0] != _T('\0') &&
+ nd->dd_name[_tcslen (nd->dd_name) - 1] != _T('/') &&
+ nd->dd_name[_tcslen (nd->dd_name) - 1] != _T('\\'))
+ {
+ _tcscat (nd->dd_name, SLASH);
+ }
+
+ /* Add on the search pattern */
+ _tcscat (nd->dd_name, SUFFIX);
+
+ /* Initialize handle to -1 so that a premature closedir doesn't try
+ * to call _findclose on it. */
+ nd->dd_handle = -1;
+
+ /* Initialize the status. */
+ nd->dd_stat = 0;
+
+ /* Initialize the dirent structure. ino and reclen are invalid under
+ * Win32, and name simply points at the appropriate part of the
+ * findfirst_t structure. */
+ nd->dd_dir.d_ino = 0;
+ nd->dd_dir.d_reclen = 0;
+ nd->dd_dir.d_namlen = 0;
+ memset (nd->dd_dir.d_name, 0, sizeof (nd->dd_dir.d_name));
+
+ return nd;
+}
+
+
+/*
+ * readdir
+ *
+ * Return a pointer to a dirent structure filled with the information on the
+ * next entry in the directory.
+ */
+struct _tdirent *
+_treaddir (_TDIR * dirp)
+{
+ errno = 0;
+
+ /* Check for valid DIR struct. */
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return (struct _tdirent *) 0;
+ }
+
+ if (dirp->dd_stat < 0)
+ {
+ /* We have already returned all files in the directory
+ * (or the structure has an invalid dd_stat). */
+ return (struct _tdirent *) 0;
+ }
+ else if (dirp->dd_stat == 0)
+ {
+ /* We haven't started the search yet. */
+ /* Start the search */
+ dirp->dd_handle = _tfindfirst (dirp->dd_name, &(dirp->dd_dta));
+
+ if (dirp->dd_handle == -1)
+ {
+ /* Whoops! Seems there are no files in that
+ * directory. */
+ dirp->dd_stat = -1;
+ }
+ else
+ {
+ dirp->dd_stat = 1;
+ }
+ }
+ else
+ {
+ /* Get the next search entry. */
+ if (_tfindnext (dirp->dd_handle, &(dirp->dd_dta)))
+ {
+ /* We are off the end or otherwise error.
+ _findnext sets errno to ENOENT if no more file
+ Undo this. */
+ DWORD winerr = GetLastError();
+ if (winerr == ERROR_NO_MORE_FILES)
+ errno = 0;
+ _findclose (dirp->dd_handle);
+ dirp->dd_handle = -1;
+ dirp->dd_stat = -1;
+ }
+ else
+ {
+ /* Update the status to indicate the correct
+ * number. */
+ dirp->dd_stat++;
+ }
+ }
+
+ if (dirp->dd_stat > 0)
+ {
+ /* Successfully got an entry. Everything about the file is
+ * already appropriately filled in except the length of the
+ * file name. */
+ dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dta.name);
+ _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);
+ return &dirp->dd_dir;
+ }
+
+ return (struct _tdirent *) 0;
+}
+
+
+/*
+ * closedir
+ *
+ * Frees up resources allocated by opendir.
+ */
+int
+_tclosedir (_TDIR * dirp)
+{
+ int rc;
+
+ errno = 0;
+ rc = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (dirp->dd_handle != -1)
+ {
+ rc = _findclose (dirp->dd_handle);
+ }
+
+ /* Delete the dir structure. */
+ free (dirp);
+
+ return rc;
+}
+
+/*
+ * rewinddir
+ *
+ * Return to the beginning of the directory "stream". We simply call findclose
+ * and then reset things like an opendir.
+ */
+void
+_trewinddir (_TDIR * dirp)
+{
+ errno = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return;
+ }
+
+ if (dirp->dd_handle != -1)
+ {
+ _findclose (dirp->dd_handle);
+ }
+
+ dirp->dd_handle = -1;
+ dirp->dd_stat = 0;
+}
+
+/*
+ * telldir
+ *
+ * Returns the "position" in the "directory stream" which can be used with
+ * seekdir to go back to an old entry. We simply return the value in stat.
+ */
+long
+_ttelldir (_TDIR * dirp)
+{
+ errno = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return -1;
+ }
+ return dirp->dd_stat;
+}
+
+/*
+ * seekdir
+ *
+ * Seek to an entry previously returned by telldir. We rewind the directory
+ * and call readdir repeatedly until either dd_stat is the position number
+ * or -1 (off the end). This is not perfect, in that the directory may
+ * have changed while we weren't looking. But that is probably the case with
+ * any such system.
+ */
+void
+_tseekdir (_TDIR * dirp, long lPos)
+{
+ errno = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return;
+ }
+
+ if (lPos < -1)
+ {
+ /* Seeking to an invalid position. */
+ errno = EINVAL;
+ return;
+ }
+ else if (lPos == -1)
+ {
+ /* Seek past end. */
+ if (dirp->dd_handle != -1)
+ {
+ _findclose (dirp->dd_handle);
+ }
+ dirp->dd_handle = -1;
+ dirp->dd_stat = -1;
+ }
+ else
+ {
+ /* Rewind and read forward to the appropriate index. */
+ _trewinddir (dirp);
+
+ while ((dirp->dd_stat < lPos) && _treaddir (dirp))
+ ;
+ }
+}
diff --git a/glib/dirent/dirent.h b/glib/dirent/dirent.h
new file mode 100644
index 0000000000000000000000000000000000000000..857710f6a1eaad32656fbaf5b9a9ee71a7b4b681
--- /dev/null
+++ b/glib/dirent/dirent.h
@@ -0,0 +1,127 @@
+/*
+ * DIRENT.H (formerly DIRLIB.H)
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is a part of the mingw-runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within the package.
+ *
+ */
+#ifndef _DIRENT_H_
+#define _DIRENT_H_
+
+#include
+#include
+
+#ifndef RC_INVOKED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dirent
+{
+ long d_ino; /* Always zero. */
+ unsigned short d_reclen; /* Always zero. */
+ unsigned short d_namlen; /* Length of name in d_name. */
+ char d_name[FILENAME_MAX+1]; /* File name plus nul delimiter. */
+};
+
+#ifdef _WIN64
+#define INTPTR __int64
+#else
+#define INTPTR long
+#endif
+
+/*
+ * This is an internal data structure. Good programmers will not use it
+ * except as an argument to one of the functions below.
+ * dd_stat field is now int (was short in older versions).
+ */
+typedef struct
+{
+ /* disk transfer area for this dir */
+ struct _finddata_t dd_dta;
+
+ /* dirent struct to return from dir (NOTE: this makes this thread
+ * safe as long as only one thread uses a particular DIR struct at
+ * a time) */
+ struct dirent dd_dir;
+
+ /* _findnext handle */
+ INTPTR dd_handle;
+
+ /*
+ * Status of search:
+ * 0 = not started yet (next entry to read is first entry)
+ * -1 = off the end
+ * positive = 0 based index of next entry
+ */
+ int dd_stat;
+
+ /* given path for dir with search pattern (struct is extended) */
+ char dd_name[1];
+} DIR;
+
+DIR* __cdecl opendir (const char*);
+struct dirent* __cdecl readdir (DIR*);
+int __cdecl closedir (DIR*);
+void __cdecl rewinddir (DIR*);
+long __cdecl telldir (DIR*);
+void __cdecl seekdir (DIR*, long);
+
+
+/* wide char versions */
+
+struct _wdirent
+{
+ long d_ino; /* Always zero. */
+ unsigned short d_reclen; /* Always zero. */
+ unsigned short d_namlen; /* Length of name in d_name. */
+ wchar_t d_name[FILENAME_MAX+1]; /* File name plus nul delimiter. */
+};
+
+/*
+ * This is an internal data structure. Good programmers will not use it
+ * except as an argument to one of the functions below.
+ */
+typedef struct
+{
+ /* disk transfer area for this dir */
+ struct _wfinddata_t dd_dta;
+
+ /* dirent struct to return from dir (NOTE: this makes this thread
+ * safe as long as only one thread uses a particular DIR struct at
+ * a time) */
+ struct _wdirent dd_dir;
+
+ /* _findnext handle */
+ INTPTR dd_handle;
+
+ /*
+ * Status of search:
+ * 0 = not started yet (next entry to read is first entry)
+ * -1 = off the end
+ * positive = 0 based index of next entry
+ */
+ int dd_stat;
+
+ /* given path for dir with search pattern (struct is extended) */
+ wchar_t dd_name[1];
+} _WDIR;
+
+
+
+_WDIR* __cdecl _wopendir (const wchar_t*);
+struct _wdirent* __cdecl _wreaddir (_WDIR*);
+int __cdecl _wclosedir (_WDIR*);
+void __cdecl _wrewinddir (_WDIR*);
+long __cdecl _wtelldir (_WDIR*);
+void __cdecl _wseekdir (_WDIR*, long);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Not RC_INVOKED */
+
+#endif /* Not _DIRENT_H_ */
diff --git a/glib/dirent/wdirent.c b/glib/dirent/wdirent.c
new file mode 100644
index 0000000000000000000000000000000000000000..098d8542e347ae7e7f9c9c410988c77bcc68208c
--- /dev/null
+++ b/glib/dirent/wdirent.c
@@ -0,0 +1,3 @@
+#define _UNICODE 1
+#define UNICODE 1
+#include "dirent.c"
diff --git a/glib/docs.c b/glib/docs.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d6321b8a4a03a2deef6a39d067f42f3064934dd
--- /dev/null
+++ b/glib/docs.c
@@ -0,0 +1,2636 @@
+/*
+ * Copyright © 2011 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Matthias Clasen
+ */
+
+
+/* This file collects documentation for macros, typedefs and
+ * the like, which have no good home in any of the 'real' source
+ * files.
+ */
+
+/* Basic types {{{1 */
+
+/**
+ * SECTION:types
+ * @title: Basic Types
+ * @short_description: standard GLib types, defined for ease-of-use
+ * and portability
+ *
+ * GLib defines a number of commonly used types, which can be divided
+ * into several groups:
+ * - New types which are not part of standard C (but are defined in
+ * various C standard library header files) — #gboolean, #gssize.
+ * - Integer types which are guaranteed to be the same size across
+ * all platforms — #gint8, #guint8, #gint16, #guint16, #gint32,
+ * #guint32, #gint64, #guint64.
+ * - Types which are easier to use than their standard C counterparts -
+ * #gpointer, #gconstpointer, #guchar, #guint, #gushort, #gulong.
+ * - Types which correspond exactly to standard C types, but are
+ * included for completeness — #gchar, #gint, #gshort, #glong,
+ * #gfloat, #gdouble.
+ * - Types which correspond exactly to standard C99 types, but are available
+ * to use even if your compiler does not support C99 — #gsize, #goffset,
+ * #gintptr, #guintptr.
+ *
+ * GLib also defines macros for the limits of some of the standard
+ * integer and floating point types, as well as macros for suitable
+ * printf() formats for these types.
+ *
+ * Note that depending on the platform and build configuration, the format
+ * macros might not be compatible with the system provided printf() function,
+ * because GLib might use a different printf() implementation internally.
+ * The format macros will always work with GLib API (like g_print()), and with
+ * any C99 compatible printf() implementation.
+ */
+
+/**
+ * gboolean:
+ *
+ * A standard boolean type.
+ * Variables of this type should only contain the value
+ * %TRUE or %FALSE.
+ *
+ * Never directly compare the contents of a #gboolean variable with the values
+ * %TRUE or %FALSE. Use `if (condition)` to check a #gboolean is "true", instead
+ * of `if (condition == TRUE)`. Likewise use `if (!condition)` to check a
+ * #gboolean is "false".
+ *
+ * There is no validation when assigning to a #gboolean variable and so it could
+ * contain any value represented by a #gint. This is why the use of `if
+ * (condition)` is recommended. All non-zero values in C evaluate to "true".
+ */
+
+/**
+ * gpointer:
+ *
+ * An untyped pointer.
+ * #gpointer looks better and is easier to use than void*.
+ */
+
+/**
+ * gconstpointer:
+ *
+ * An untyped pointer to constant data.
+ * The data pointed to should not be changed.
+ *
+ * This is typically used in function prototypes to indicate
+ * that the data pointed to will not be altered by the function.
+ */
+
+/**
+ * gchar:
+ *
+ * Corresponds to the standard C char type.
+ */
+
+/**
+ * guchar:
+ *
+ * Corresponds to the standard C unsigned char type.
+ */
+
+/**
+ * gint:
+ *
+ * Corresponds to the standard C int type.
+ * Values of this type can range from %G_MININT to %G_MAXINT.
+ */
+
+/**
+ * G_MININT:
+ *
+ * The minimum value which can be held in a #gint.
+ */
+
+/**
+ * G_MAXINT:
+ *
+ * The maximum value which can be held in a #gint.
+ */
+
+/**
+ * guint:
+ *
+ * Corresponds to the standard C unsigned int type.
+ * Values of this type can range from 0 to %G_MAXUINT.
+ */
+
+/**
+ * G_MAXUINT:
+ *
+ * The maximum value which can be held in a #guint.
+ */
+
+/**
+ * gshort:
+ *
+ * Corresponds to the standard C short type.
+ * Values of this type can range from %G_MINSHORT to %G_MAXSHORT.
+ */
+
+/**
+ * G_MINSHORT:
+ *
+ * The minimum value which can be held in a #gshort.
+ */
+
+/**
+ * G_MAXSHORT:
+ *
+ * The maximum value which can be held in a #gshort.
+ */
+
+/**
+ * gushort:
+ *
+ * Corresponds to the standard C unsigned short type.
+ * Values of this type can range from 0 to %G_MAXUSHORT.
+ */
+
+/**
+ * G_MAXUSHORT:
+ *
+ * The maximum value which can be held in a #gushort.
+ */
+
+/**
+ * glong:
+ *
+ * Corresponds to the standard C long type.
+ * Values of this type can range from %G_MINLONG to %G_MAXLONG.
+ */
+
+/**
+ * G_MINLONG:
+ *
+ * The minimum value which can be held in a #glong.
+ */
+
+/**
+ * G_MAXLONG:
+ *
+ * The maximum value which can be held in a #glong.
+ */
+
+/**
+ * gulong:
+ *
+ * Corresponds to the standard C unsigned long type.
+ * Values of this type can range from 0 to %G_MAXULONG.
+ */
+
+/**
+ * G_MAXULONG:
+ *
+ * The maximum value which can be held in a #gulong.
+ */
+
+/**
+ * gint8:
+ *
+ * A signed integer guaranteed to be 8 bits on all platforms.
+ * Values of this type can range from %G_MININT8 (= -128) to
+ * %G_MAXINT8 (= 127).
+ */
+
+/**
+ * G_MAXINT8:
+ *
+ * The maximum value which can be held in a #gint8.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * guint8:
+ *
+ * An unsigned integer guaranteed to be 8 bits on all platforms.
+ * Values of this type can range from 0 to %G_MAXUINT8 (= 255).
+ */
+
+/**
+ * G_MAXUINT8:
+ *
+ * The maximum value which can be held in a #guint8.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * gint16:
+ *
+ * A signed integer guaranteed to be 16 bits on all platforms.
+ * Values of this type can range from %G_MININT16 (= -32,768) to
+ * %G_MAXINT16 (= 32,767).
+ *
+ * To print or scan values of this type, use
+ * %G_GINT16_MODIFIER and/or %G_GINT16_FORMAT.
+ */
+
+/**
+ * G_MAXINT16:
+ *
+ * The maximum value which can be held in a #gint16.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GINT16_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #gint16 or #guint16. It
+ * is a string literal, but doesn't include the percent-sign, such
+ * that you can add precision and length modifiers between percent-sign
+ * and conversion specifier and append a conversion specifier.
+ *
+ * The following example prints "0x7b";
+ * |[
+ * gint16 value = 123;
+ * g_print ("%#" G_GINT16_MODIFIER "x", value);
+ * ]|
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GINT16_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning and
+ * printing values of type #gint16. It is a string literal, but doesn't
+ * include the percent-sign, such that you can add precision and length
+ * modifiers between percent-sign and conversion specifier.
+ *
+ * |[
+ * gint16 in;
+ * gint32 out;
+ * sscanf ("42", "%" G_GINT16_FORMAT, &in)
+ * out = in * 1000;
+ * g_print ("%" G_GINT32_FORMAT, out);
+ * ]|
+ */
+
+/**
+ * guint16:
+ *
+ * An unsigned integer guaranteed to be 16 bits on all platforms.
+ * Values of this type can range from 0 to %G_MAXUINT16 (= 65,535).
+ *
+ * To print or scan values of this type, use
+ * %G_GINT16_MODIFIER and/or %G_GUINT16_FORMAT.
+ */
+
+/**
+ * G_MAXUINT16:
+ *
+ * The maximum value which can be held in a #guint16.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GUINT16_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #guint16. See also %G_GINT16_FORMAT
+ */
+
+/**
+ * gint32:
+ *
+ * A signed integer guaranteed to be 32 bits on all platforms.
+ * Values of this type can range from %G_MININT32 (= -2,147,483,648)
+ * to %G_MAXINT32 (= 2,147,483,647).
+ *
+ * To print or scan values of this type, use
+ * %G_GINT32_MODIFIER and/or %G_GINT32_FORMAT.
+ */
+
+/**
+ * G_MAXINT32:
+ *
+ * The maximum value which can be held in a #gint32.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GINT32_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #gint32 or #guint32. It
+ * is a string literal. See also %G_GINT16_MODIFIER.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GINT32_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #gint32. See also %G_GINT16_FORMAT.
+ */
+
+/**
+ * guint32:
+ *
+ * An unsigned integer guaranteed to be 32 bits on all platforms.
+ * Values of this type can range from 0 to %G_MAXUINT32 (= 4,294,967,295).
+ *
+ * To print or scan values of this type, use
+ * %G_GINT32_MODIFIER and/or %G_GUINT32_FORMAT.
+ */
+
+/**
+ * G_MAXUINT32:
+ *
+ * The maximum value which can be held in a #guint32.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GUINT32_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #guint32. See also %G_GINT16_FORMAT.
+ */
+
+/**
+ * gint64:
+ *
+ * A signed integer guaranteed to be 64 bits on all platforms.
+ * Values of this type can range from %G_MININT64
+ * (= -9,223,372,036,854,775,808) to %G_MAXINT64
+ * (= 9,223,372,036,854,775,807).
+ *
+ * To print or scan values of this type, use
+ * %G_GINT64_MODIFIER and/or %G_GINT64_FORMAT.
+ */
+
+/**
+ * G_MAXINT64:
+ *
+ * The maximum value which can be held in a #gint64.
+ */
+
+/**
+ * G_GINT64_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #gint64 or #guint64.
+ * It is a string literal.
+ *
+ * Some platforms do not support printing 64-bit integers, even
+ * though the types are supported. On such platforms %G_GINT64_MODIFIER
+ * is not defined.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GINT64_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #gint64. See also %G_GINT16_FORMAT.
+ *
+ * Some platforms do not support scanning and printing 64-bit integers,
+ * even though the types are supported. On such platforms %G_GINT64_FORMAT
+ * is not defined. Note that scanf() may not support 64-bit integers, even
+ * if %G_GINT64_FORMAT is defined. Due to its weak error handling, scanf()
+ * is not recommended for parsing anyway; consider using g_ascii_strtoull()
+ * instead.
+ */
+
+/**
+ * guint64:
+ *
+ * An unsigned integer guaranteed to be 64-bits on all platforms.
+ * Values of this type can range from 0 to %G_MAXUINT64
+ * (= 18,446,744,073,709,551,615).
+ *
+ * To print or scan values of this type, use
+ * %G_GINT64_MODIFIER and/or %G_GUINT64_FORMAT.
+ */
+
+/**
+ * G_MAXUINT64:
+ *
+ * The maximum value which can be held in a #guint64.
+ */
+
+/**
+ * G_GUINT64_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #guint64. See also %G_GINT16_FORMAT.
+ *
+ * Some platforms do not support scanning and printing 64-bit integers,
+ * even though the types are supported. On such platforms %G_GUINT64_FORMAT
+ * is not defined. Note that scanf() may not support 64-bit integers, even
+ * if %G_GINT64_FORMAT is defined. Due to its weak error handling, scanf()
+ * is not recommended for parsing anyway; consider using g_ascii_strtoull()
+ * instead.
+ */
+
+/**
+ * G_GINT64_CONSTANT:
+ * @val: a literal integer value, e.g. 0x1d636b02300a7aa7
+ *
+ * This macro is used to insert 64-bit integer literals
+ * into the source code.
+ */
+
+/**
+ * G_GUINT64_CONSTANT:
+ * @val: a literal integer value, e.g. 0x1d636b02300a7aa7U
+ *
+ * This macro is used to insert 64-bit unsigned integer
+ * literals into the source code.
+ *
+ * Since: 2.10
+ */
+
+/**
+ * gfloat:
+ *
+ * Corresponds to the standard C float type.
+ * Values of this type can range from -%G_MAXFLOAT to %G_MAXFLOAT.
+ */
+
+/**
+ * G_MINFLOAT:
+ *
+ * The minimum positive value which can be held in a #gfloat.
+ *
+ * If you are interested in the smallest value which can be held
+ * in a #gfloat, use -%G_MAXFLOAT.
+ */
+
+/**
+ * G_MAXFLOAT:
+ *
+ * The maximum value which can be held in a #gfloat.
+ */
+
+/**
+ * gdouble:
+ *
+ * Corresponds to the standard C double type.
+ * Values of this type can range from -%G_MAXDOUBLE to %G_MAXDOUBLE.
+ */
+
+/**
+ * G_MINDOUBLE:
+ *
+ * The minimum positive value which can be held in a #gdouble.
+ *
+ * If you are interested in the smallest value which can be held
+ * in a #gdouble, use -%G_MAXDOUBLE.
+ */
+
+/**
+ * G_MAXDOUBLE:
+ *
+ * The maximum value which can be held in a #gdouble.
+ */
+
+/**
+ * gsize:
+ *
+ * An unsigned integer type of the result of the sizeof operator,
+ * corresponding to the size_t type defined in C99.
+ * This type is wide enough to hold the numeric value of a pointer,
+ * so it is usually 32 bit wide on a 32-bit platform and 64 bit wide
+ * on a 64-bit platform. Values of this type can range from 0 to
+ * %G_MAXSIZE.
+ *
+ * To print or scan values of this type, use
+ * %G_GSIZE_MODIFIER and/or %G_GSIZE_FORMAT.
+ */
+
+/**
+ * G_MAXSIZE:
+ *
+ * The maximum value which can be held in a #gsize.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_GSIZE_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #gsize. It
+ * is a string literal.
+ *
+ * Since: 2.6
+ */
+
+/**
+ * G_GSIZE_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #gsize. See also %G_GINT16_FORMAT.
+ *
+ * Since: 2.6
+ */
+
+/**
+ * gssize:
+ *
+ * A signed variant of #gsize, corresponding to the
+ * ssize_t defined on most platforms.
+ * Values of this type can range from %G_MINSSIZE
+ * to %G_MAXSSIZE.
+ *
+ * To print or scan values of this type, use
+ * %G_GSSIZE_MODIFIER and/or %G_GSSIZE_FORMAT.
+ */
+
+/**
+ * G_MINSSIZE:
+ *
+ * The minimum value which can be held in a #gssize.
+ *
+ * Since: 2.14
+ */
+
+/**
+ * G_MAXSSIZE:
+ *
+ * The maximum value which can be held in a #gssize.
+ *
+ * Since: 2.14
+ */
+
+/**
+ * G_GSSIZE_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #gssize. See also %G_GINT16_FORMAT.
+ *
+ * Since: 2.6
+ */
+
+/**
+ * G_GSSIZE_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #gssize. It
+ * is a string literal.
+ *
+ * Since: 2.6
+ */
+
+/**
+ * goffset:
+ *
+ * A signed integer type that is used for file offsets,
+ * corresponding to the POSIX type `off_t` as if compiling with
+ * `_FILE_OFFSET_BITS` set to 64. #goffset is always 64 bits wide, even on
+ * 32-bit architectures.
+ * Values of this type can range from %G_MINOFFSET to
+ * %G_MAXOFFSET.
+ *
+ * To print or scan values of this type, use
+ * %G_GOFFSET_MODIFIER and/or %G_GOFFSET_FORMAT.
+ *
+ * Since: 2.14
+ */
+
+/**
+ * G_MINOFFSET:
+ *
+ * The minimum value which can be held in a #goffset.
+ */
+
+/**
+ * G_MAXOFFSET:
+ *
+ * The maximum value which can be held in a #goffset.
+ */
+
+/**
+ * G_GOFFSET_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #goffset. It is a string
+ * literal. See also %G_GINT64_MODIFIER.
+ *
+ * Since: 2.20
+ */
+
+/**
+ * G_GOFFSET_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #goffset. See also %G_GINT64_FORMAT.
+ *
+ * Since: 2.20
+ */
+
+/**
+ * G_GOFFSET_CONSTANT:
+ * @val: a literal integer value, e.g. 0x1d636b02300a7aa7
+ *
+ * This macro is used to insert #goffset 64-bit integer literals
+ * into the source code.
+ *
+ * See also G_GINT64_CONSTANT().
+ *
+ * Since: 2.20
+ */
+
+/**
+ * gintptr:
+ *
+ * Corresponds to the C99 type intptr_t,
+ * a signed integer type that can hold any pointer.
+ *
+ * To print or scan values of this type, use
+ * %G_GINTPTR_MODIFIER and/or %G_GINTPTR_FORMAT.
+ *
+ * Since: 2.18
+ */
+
+/**
+ * G_GINTPTR_MODIFIER:
+ *
+ * The platform dependent length modifier for conversion specifiers
+ * for scanning and printing values of type #gintptr or #guintptr.
+ * It is a string literal.
+ *
+ * Since: 2.22
+ */
+
+/**
+ * G_GINTPTR_FORMAT:
+ *
+ * This is the platform dependent conversion specifier for scanning
+ * and printing values of type #gintptr.
+ *
+ * Since: 2.22
+ */
+
+/**
+ * guintptr:
+ *
+ * Corresponds to the C99 type uintptr_t,
+ * an unsigned integer type that can hold any pointer.
+ *
+ * To print or scan values of this type, use
+ * %G_GINTPTR_MODIFIER and/or %G_GUINTPTR_FORMAT.
+ *
+ * Since: 2.18
+ */
+
+/**
+ * G_GUINTPTR_FORMAT:
+ *
+ * This is the platform dependent conversion specifier
+ * for scanning and printing values of type #guintptr.
+ *
+ * Since: 2.22
+ */
+
+/* Type conversion {{{1 */
+
+/**
+ * SECTION:type_conversion
+ * @title: Type Conversion Macros
+ * @short_description: portably storing integers in pointer variables
+ *
+ * Many times GLib, GTK+, and other libraries allow you to pass "user
+ * data" to a callback, in the form of a void pointer. From time to time
+ * you want to pass an integer instead of a pointer. You could allocate
+ * an integer, with something like:
+ * |[
+ * int *ip = g_new (int, 1);
+ * *ip = 42;
+ * ]|
+ * But this is inconvenient, and it's annoying to have to free the
+ * memory at some later time.
+ *
+ * Pointers are always at least 32 bits in size (on all platforms GLib
+ * intends to support). Thus you can store at least 32-bit integer values
+ * in a pointer value. Naively, you might try this, but it's incorrect:
+ * |[
+ * gpointer p;
+ * int i;
+ * p = (void*) 42;
+ * i = (int) p;
+ * ]|
+ * Again, that example was not correct, don't copy it.
+ * The problem is that on some systems you need to do this:
+ * |[
+ * gpointer p;
+ * int i;
+ * p = (void*) (long) 42;
+ * i = (int) (long) p;
+ * ]|
+ * The GLib macros GPOINTER_TO_INT(), GINT_TO_POINTER(), etc. take care
+ * to do the right thing on every platform.
+ *
+ * Warning: You may not store pointers in integers. This is not
+ * portable in any way, shape or form. These macros only allow storing
+ * integers in pointers, and only preserve 32 bits of the integer; values
+ * outside the range of a 32-bit integer will be mangled.
+ */
+
+/**
+ * GINT_TO_POINTER:
+ * @i: integer to stuff into a pointer
+ *
+ * Stuffs an integer into a pointer type.
+ *
+ * Remember, you may not store pointers in integers. This is not portable
+ * in any way, shape or form. These macros only allow storing integers in
+ * pointers, and only preserve 32 bits of the integer; values outside the
+ * range of a 32-bit integer will be mangled.
+ */
+
+/**
+ * GPOINTER_TO_INT:
+ * @p: pointer containing an integer
+ *
+ * Extracts an integer from a pointer. The integer must have
+ * been stored in the pointer with GINT_TO_POINTER().
+ *
+ * Remember, you may not store pointers in integers. This is not portable
+ * in any way, shape or form. These macros only allow storing integers in
+ * pointers, and only preserve 32 bits of the integer; values outside the
+ * range of a 32-bit integer will be mangled.
+ */
+
+/**
+ * GUINT_TO_POINTER:
+ * @u: unsigned integer to stuff into the pointer
+ *
+ * Stuffs an unsigned integer into a pointer type.
+ */
+
+/**
+ * GPOINTER_TO_UINT:
+ * @p: pointer to extract an unsigned integer from
+ *
+ * Extracts an unsigned integer from a pointer. The integer must have
+ * been stored in the pointer with GUINT_TO_POINTER().
+ */
+
+/**
+ * GSIZE_TO_POINTER:
+ * @s: #gsize to stuff into the pointer
+ *
+ * Stuffs a #gsize into a pointer type.
+ */
+
+/**
+ * GPOINTER_TO_SIZE:
+ * @p: pointer to extract a #gsize from
+ *
+ * Extracts a #gsize from a pointer. The #gsize must have
+ * been stored in the pointer with GSIZE_TO_POINTER().
+ */
+
+/* Byte order {{{1 */
+
+/**
+ * SECTION:byte_order
+ * @title: Byte Order Macros
+ * @short_description: a portable way to convert between different byte orders
+ *
+ * These macros provide a portable way to determine the host byte order
+ * and to convert values between different byte orders.
+ *
+ * The byte order is the order in which bytes are stored to create larger
+ * data types such as the #gint and #glong values.
+ * The host byte order is the byte order used on the current machine.
+ *
+ * Some processors store the most significant bytes (i.e. the bytes that
+ * hold the largest part of the value) first. These are known as big-endian
+ * processors. Other processors (notably the x86 family) store the most
+ * significant byte last. These are known as little-endian processors.
+ *
+ * Finally, to complicate matters, some other processors store the bytes in
+ * a rather curious order known as PDP-endian. For a 4-byte word, the 3rd
+ * most significant byte is stored first, then the 4th, then the 1st and
+ * finally the 2nd.
+ *
+ * Obviously there is a problem when these different processors communicate
+ * with each other, for example over networks or by using binary file formats.
+ * This is where these macros come in. They are typically used to convert
+ * values into a byte order which has been agreed on for use when
+ * communicating between different processors. The Internet uses what is
+ * known as 'network byte order' as the standard byte order (which is in
+ * fact the big-endian byte order).
+ *
+ * Note that the byte order conversion macros may evaluate their arguments
+ * multiple times, thus you should not use them with arguments which have
+ * side-effects.
+ */
+
+/**
+ * G_BYTE_ORDER:
+ *
+ * The host byte order.
+ * This can be either %G_LITTLE_ENDIAN or %G_BIG_ENDIAN (support for
+ * %G_PDP_ENDIAN may be added in future.)
+ */
+
+/**
+ * G_LITTLE_ENDIAN:
+ *
+ * Specifies one of the possible types of byte order.
+ * See %G_BYTE_ORDER.
+ */
+
+/**
+ * G_BIG_ENDIAN:
+ *
+ * Specifies one of the possible types of byte order.
+ * See %G_BYTE_ORDER.
+ */
+
+/**
+ * G_PDP_ENDIAN:
+ *
+ * Specifies one of the possible types of byte order
+ * (currently unused). See %G_BYTE_ORDER.
+ */
+
+/**
+ * g_htonl:
+ * @val: a 32-bit integer value in host byte order
+ *
+ * Converts a 32-bit integer value from host to network byte order.
+ *
+ * Returns: @val converted to network byte order
+ */
+
+/**
+ * g_htons:
+ * @val: a 16-bit integer value in host byte order
+ *
+ * Converts a 16-bit integer value from host to network byte order.
+ *
+ * Returns: @val converted to network byte order
+ */
+
+/**
+ * g_ntohl:
+ * @val: a 32-bit integer value in network byte order
+ *
+ * Converts a 32-bit integer value from network to host byte order.
+ *
+ * Returns: @val converted to host byte order.
+ */
+
+/**
+ * g_ntohs:
+ * @val: a 16-bit integer value in network byte order
+ *
+ * Converts a 16-bit integer value from network to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT_FROM_BE:
+ * @val: a #gint value in big-endian byte order
+ *
+ * Converts a #gint value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT_FROM_LE:
+ * @val: a #gint value in little-endian byte order
+ *
+ * Converts a #gint value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT_TO_BE:
+ * @val: a #gint value in host byte order
+ *
+ * Converts a #gint value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian byte order
+ */
+
+/**
+ * GINT_TO_LE:
+ * @val: a #gint value in host byte order
+ *
+ * Converts a #gint value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian byte order
+ */
+
+/**
+ * GUINT_FROM_BE:
+ * @val: a #guint value in big-endian byte order
+ *
+ * Converts a #guint value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT_FROM_LE:
+ * @val: a #guint value in little-endian byte order
+ *
+ * Converts a #guint value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT_TO_BE:
+ * @val: a #guint value in host byte order
+ *
+ * Converts a #guint value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian byte order
+ */
+
+/**
+ * GUINT_TO_LE:
+ * @val: a #guint value in host byte order
+ *
+ * Converts a #guint value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian byte order.
+ */
+
+/**
+ * GLONG_FROM_BE:
+ * @val: a #glong value in big-endian byte order
+ *
+ * Converts a #glong value from big-endian to the host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GLONG_FROM_LE:
+ * @val: a #glong value in little-endian byte order
+ *
+ * Converts a #glong value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GLONG_TO_BE:
+ * @val: a #glong value in host byte order
+ *
+ * Converts a #glong value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian byte order
+ */
+
+/**
+ * GLONG_TO_LE:
+ * @val: a #glong value in host byte order
+ *
+ * Converts a #glong value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GULONG_FROM_BE:
+ * @val: a #gulong value in big-endian byte order
+ *
+ * Converts a #gulong value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GULONG_FROM_LE:
+ * @val: a #gulong value in little-endian byte order
+ *
+ * Converts a #gulong value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GULONG_TO_BE:
+ * @val: a #gulong value in host byte order
+ *
+ * Converts a #gulong value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GULONG_TO_LE:
+ * @val: a #gulong value in host byte order
+ *
+ * Converts a #gulong value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GSIZE_FROM_BE:
+ * @val: a #gsize value in big-endian byte order
+ *
+ * Converts a #gsize value from big-endian to the host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GSIZE_FROM_LE:
+ * @val: a #gsize value in little-endian byte order
+ *
+ * Converts a #gsize value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GSIZE_TO_BE:
+ * @val: a #gsize value in host byte order
+ *
+ * Converts a #gsize value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian byte order
+ */
+
+/**
+ * GSIZE_TO_LE:
+ * @val: a #gsize value in host byte order
+ *
+ * Converts a #gsize value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GSSIZE_FROM_BE:
+ * @val: a #gssize value in big-endian byte order
+ *
+ * Converts a #gssize value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GSSIZE_FROM_LE:
+ * @val: a #gssize value in little-endian byte order
+ *
+ * Converts a #gssize value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GSSIZE_TO_BE:
+ * @val: a #gssize value in host byte order
+ *
+ * Converts a #gssize value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GSSIZE_TO_LE:
+ * @val: a #gssize value in host byte order
+ *
+ * Converts a #gssize value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GINT16_FROM_BE:
+ * @val: a #gint16 value in big-endian byte order
+ *
+ * Converts a #gint16 value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT16_FROM_LE:
+ * @val: a #gint16 value in little-endian byte order
+ *
+ * Converts a #gint16 value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT16_TO_BE:
+ * @val: a #gint16 value in host byte order
+ *
+ * Converts a #gint16 value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GINT16_TO_LE:
+ * @val: a #gint16 value in host byte order
+ *
+ * Converts a #gint16 value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GUINT16_FROM_BE:
+ * @val: a #guint16 value in big-endian byte order
+ *
+ * Converts a #guint16 value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT16_FROM_LE:
+ * @val: a #guint16 value in little-endian byte order
+ *
+ * Converts a #guint16 value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT16_TO_BE:
+ * @val: a #guint16 value in host byte order
+ *
+ * Converts a #guint16 value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GUINT16_TO_LE:
+ * @val: a #guint16 value in host byte order
+ *
+ * Converts a #guint16 value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GINT32_FROM_BE:
+ * @val: a #gint32 value in big-endian byte order
+ *
+ * Converts a #gint32 value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT32_FROM_LE:
+ * @val: a #gint32 value in little-endian byte order
+ *
+ * Converts a #gint32 value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT32_TO_BE:
+ * @val: a #gint32 value in host byte order
+ *
+ * Converts a #gint32 value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GINT32_TO_LE:
+ * @val: a #gint32 value in host byte order
+ *
+ * Converts a #gint32 value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GUINT32_FROM_BE:
+ * @val: a #guint32 value in big-endian byte order
+ *
+ * Converts a #guint32 value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT32_FROM_LE:
+ * @val: a #guint32 value in little-endian byte order
+ *
+ * Converts a #guint32 value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT32_TO_BE:
+ * @val: a #guint32 value in host byte order
+ *
+ * Converts a #guint32 value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GUINT32_TO_LE:
+ * @val: a #guint32 value in host byte order
+ *
+ * Converts a #guint32 value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GINT64_FROM_BE:
+ * @val: a #gint64 value in big-endian byte order
+ *
+ * Converts a #gint64 value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT64_FROM_LE:
+ * @val: a #gint64 value in little-endian byte order
+ *
+ * Converts a #gint64 value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GINT64_TO_BE:
+ * @val: a #gint64 value in host byte order
+ *
+ * Converts a #gint64 value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GINT64_TO_LE:
+ * @val: a #gint64 value in host byte order
+ *
+ * Converts a #gint64 value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GUINT64_FROM_BE:
+ * @val: a #guint64 value in big-endian byte order
+ *
+ * Converts a #guint64 value from big-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT64_FROM_LE:
+ * @val: a #guint64 value in little-endian byte order
+ *
+ * Converts a #guint64 value from little-endian to host byte order.
+ *
+ * Returns: @val converted to host byte order
+ */
+
+/**
+ * GUINT64_TO_BE:
+ * @val: a #guint64 value in host byte order
+ *
+ * Converts a #guint64 value from host byte order to big-endian.
+ *
+ * Returns: @val converted to big-endian
+ */
+
+/**
+ * GUINT64_TO_LE:
+ * @val: a #guint64 value in host byte order
+ *
+ * Converts a #guint64 value from host byte order to little-endian.
+ *
+ * Returns: @val converted to little-endian
+ */
+
+/**
+ * GUINT16_SWAP_BE_PDP:
+ * @val: a #guint16 value in big-endian or pdp-endian byte order
+ *
+ * Converts a #guint16 value between big-endian and pdp-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/**
+ * GUINT16_SWAP_LE_BE:
+ * @val: a #guint16 value in little-endian or big-endian byte order
+ *
+ * Converts a #guint16 value between little-endian and big-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/**
+ * GUINT16_SWAP_LE_PDP:
+ * @val: a #guint16 value in little-endian or pdp-endian byte order
+ *
+ * Converts a #guint16 value between little-endian and pdp-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/**
+ * GUINT32_SWAP_BE_PDP:
+ * @val: a #guint32 value in big-endian or pdp-endian byte order
+ *
+ * Converts a #guint32 value between big-endian and pdp-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/**
+ * GUINT32_SWAP_LE_BE:
+ * @val: a #guint32 value in little-endian or big-endian byte order
+ *
+ * Converts a #guint32 value between little-endian and big-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/**
+ * GUINT32_SWAP_LE_PDP:
+ * @val: a #guint32 value in little-endian or pdp-endian byte order
+ *
+ * Converts a #guint32 value between little-endian and pdp-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/**
+ * GUINT64_SWAP_LE_BE:
+ * @val: a #guint64 value in little-endian or big-endian byte order
+ *
+ * Converts a #guint64 value between little-endian and big-endian byte order.
+ * The conversion is symmetric so it can be used both ways.
+ *
+ * Returns: @val converted to the opposite byte order
+ */
+
+/* Bounds-checked integer arithmetic {{{1 */
+/**
+ * SECTION:checkedmath
+ * @title: Bounds-checking integer arithmetic
+ * @short_description: a set of helpers for performing checked integer arithmetic
+ *
+ * GLib offers a set of macros for doing additions and multiplications
+ * of unsigned integers, with checks for overflows.
+ *
+ * The helpers all have three arguments. A pointer to the destination
+ * is always the first argument and the operands to the operation are
+ * the other two.
+ *
+ * Following standard GLib convention, the helpers return %TRUE in case
+ * of success (ie: no overflow).
+ *
+ * The helpers may be macros, normal functions or inlines. They may be
+ * implemented with inline assembly or compiler intrinsics where
+ * available.
+ *
+ * Since: 2.48
+ */
+
+/**
+ * g_uint_checked_add
+ * @dest: a pointer to the #guint destination
+ * @a: the #guint left operand
+ * @b: the #guint right operand
+ *
+ * Performs a checked addition of @a and @b, storing the result in
+ * @dest.
+ *
+ * If the operation is successful, %TRUE is returned. If the operation
+ * overflows then the state of @dest is undefined and %FALSE is
+ * returned.
+ *
+ * Returns: %TRUE if there was no overflow
+ * Since: 2.48
+ */
+
+/**
+ * g_uint_checked_mul
+ * @dest: a pointer to the #guint destination
+ * @a: the #guint left operand
+ * @b: the #guint right operand
+ *
+ * Performs a checked multiplication of @a and @b, storing the result in
+ * @dest.
+ *
+ * If the operation is successful, %TRUE is returned. If the operation
+ * overflows then the state of @dest is undefined and %FALSE is
+ * returned.
+ *
+ * Returns: %TRUE if there was no overflow
+ * Since: 2.48
+ */
+
+/**
+ * g_uint64_checked_add
+ * @dest: a pointer to the #guint64 destination
+ * @a: the #guint64 left operand
+ * @b: the #guint64 right operand
+ *
+ * Performs a checked addition of @a and @b, storing the result in
+ * @dest.
+ *
+ * If the operation is successful, %TRUE is returned. If the operation
+ * overflows then the state of @dest is undefined and %FALSE is
+ * returned.
+ *
+ * Returns: %TRUE if there was no overflow
+ * Since: 2.48
+ */
+
+/**
+ * g_uint64_checked_mul
+ * @dest: a pointer to the #guint64 destination
+ * @a: the #guint64 left operand
+ * @b: the #guint64 right operand
+ *
+ * Performs a checked multiplication of @a and @b, storing the result in
+ * @dest.
+ *
+ * If the operation is successful, %TRUE is returned. If the operation
+ * overflows then the state of @dest is undefined and %FALSE is
+ * returned.
+ *
+ * Returns: %TRUE if there was no overflow
+ * Since: 2.48
+ */
+
+/**
+ * g_size_checked_add
+ * @dest: a pointer to the #gsize destination
+ * @a: the #gsize left operand
+ * @b: the #gsize right operand
+ *
+ * Performs a checked addition of @a and @b, storing the result in
+ * @dest.
+ *
+ * If the operation is successful, %TRUE is returned. If the operation
+ * overflows then the state of @dest is undefined and %FALSE is
+ * returned.
+ *
+ * Returns: %TRUE if there was no overflow
+ * Since: 2.48
+ */
+
+/**
+ * g_size_checked_mul
+ * @dest: a pointer to the #gsize destination
+ * @a: the #gsize left operand
+ * @b: the #gsize right operand
+ *
+ * Performs a checked multiplication of @a and @b, storing the result in
+ * @dest.
+ *
+ * If the operation is successful, %TRUE is returned. If the operation
+ * overflows then the state of @dest is undefined and %FALSE is
+ * returned.
+ *
+ * Returns: %TRUE if there was no overflow
+ * Since: 2.48
+ */
+/* Numerical Definitions {{{1 */
+
+/**
+ * SECTION:numerical
+ * @title: Numerical Definitions
+ * @short_description: mathematical constants, and floating point decomposition
+ *
+ * GLib offers mathematical constants such as %G_PI for the value of pi;
+ * many platforms have these in the C library, but some don't, the GLib
+ * versions always exist.
+ *
+ * The #GFloatIEEE754 and #GDoubleIEEE754 unions are used to access the
+ * sign, mantissa and exponent of IEEE floats and doubles. These unions are
+ * defined as appropriate for a given platform. IEEE floats and doubles are
+ * supported (used for storage) by at least Intel, PPC and Sparc. See
+ * [IEEE 754-2008](http://en.wikipedia.org/wiki/IEEE_float)
+ * for more information about IEEE number formats.
+ */
+
+/**
+ * G_IEEE754_FLOAT_BIAS:
+ *
+ * The bias by which exponents in single-precision floats are offset.
+ */
+
+/**
+ * G_IEEE754_DOUBLE_BIAS:
+ *
+ * The bias by which exponents in double-precision floats are offset.
+ */
+
+/**
+ * GFloatIEEE754:
+ * @v_float: the double value
+ *
+ * The #GFloatIEEE754 and #GDoubleIEEE754 unions are used to access the sign,
+ * mantissa and exponent of IEEE floats and doubles. These unions are defined
+ * as appropriate for a given platform. IEEE floats and doubles are supported
+ * (used for storage) by at least Intel, PPC and Sparc.
+ */
+
+/**
+ * GDoubleIEEE754:
+ * @v_double: the double value
+ *
+ * The #GFloatIEEE754 and #GDoubleIEEE754 unions are used to access the sign,
+ * mantissa and exponent of IEEE floats and doubles. These unions are defined
+ * as appropriate for a given platform. IEEE floats and doubles are supported
+ * (used for storage) by at least Intel, PPC and Sparc.
+ */
+
+/**
+ * G_E:
+ *
+ * The base of natural logarithms.
+ */
+
+/**
+ * G_LN2:
+ *
+ * The natural logarithm of 2.
+ */
+
+/**
+ * G_LN10:
+ *
+ * The natural logarithm of 10.
+ */
+
+/**
+ * G_PI:
+ *
+ * The value of pi (ratio of circle's circumference to its diameter).
+ */
+
+/**
+ * G_PI_2:
+ *
+ * Pi divided by 2.
+ */
+
+/**
+ * G_PI_4:
+ *
+ * Pi divided by 4.
+ */
+
+/**
+ * G_SQRT2:
+ *
+ * The square root of two.
+ */
+
+/**
+ * G_LOG_2_BASE_10:
+ *
+ * Multiplying the base 2 exponent by this number yields the base 10 exponent.
+ */
+
+/* Macros {{{1 */
+
+/**
+ * SECTION:macros
+ * @title: Standard Macros
+ * @short_description: commonly-used macros
+ *
+ * These macros provide a few commonly-used features.
+ */
+
+/**
+ * G_OS_WIN32:
+ *
+ * This macro is defined only on Windows. So you can bracket
+ * Windows-specific code in "\#ifdef G_OS_WIN32".
+ */
+
+/**
+ * G_OS_UNIX:
+ *
+ * This macro is defined only on UNIX. So you can bracket
+ * UNIX-specific code in "\#ifdef G_OS_UNIX".
+ */
+
+/**
+ * G_DIR_SEPARATOR:
+ *
+ * The directory separator character.
+ * This is '/' on UNIX machines and '\' under Windows.
+ */
+
+/**
+ * G_DIR_SEPARATOR_S:
+ *
+ * The directory separator as a string.
+ * This is "/" on UNIX machines and "\" under Windows.
+ */
+
+/**
+ * G_IS_DIR_SEPARATOR:
+ * @c: a character
+ *
+ * Checks whether a character is a directory
+ * separator. It returns %TRUE for '/' on UNIX
+ * machines and for '\' or '/' under Windows.
+ *
+ * Since: 2.6
+ */
+
+/**
+ * G_SEARCHPATH_SEPARATOR:
+ *
+ * The search path separator character.
+ * This is ':' on UNIX machines and ';' under Windows.
+ */
+
+/**
+ * G_SEARCHPATH_SEPARATOR_S:
+ *
+ * The search path separator as a string.
+ * This is ":" on UNIX machines and ";" under Windows.
+ */
+
+/**
+ * TRUE:
+ *
+ * Defines the %TRUE value for the #gboolean type.
+ */
+
+/**
+ * FALSE:
+ *
+ * Defines the %FALSE value for the #gboolean type.
+ */
+
+/**
+ * NULL:
+ *
+ * Defines the standard %NULL pointer.
+ */
+
+/**
+ * MIN:
+ * @a: a numeric value
+ * @b: a numeric value
+ *
+ * Calculates the minimum of @a and @b.
+ *
+ * Returns: the minimum of @a and @b.
+ */
+
+/**
+ * MAX:
+ * @a: a numeric value
+ * @b: a numeric value
+ *
+ * Calculates the maximum of @a and @b.
+ *
+ * Returns: the maximum of @a and @b.
+ */
+
+/**
+ * ABS:
+ * @a: a numeric value
+ *
+ * Calculates the absolute value of @a.
+ * The absolute value is simply the number with any negative sign taken away.
+ *
+ * For example,
+ * - ABS(-10) is 10.
+ * - ABS(10) is also 10.
+ *
+ * Returns: the absolute value of @a.
+ */
+
+/**
+ * CLAMP:
+ * @x: the value to clamp
+ * @low: the minimum value allowed
+ * @high: the maximum value allowed
+ *
+ * Ensures that @x is between the limits set by @low and @high. If @low is
+ * greater than @high the result is undefined.
+ *
+ * For example,
+ * - CLAMP(5, 10, 15) is 10.
+ * - CLAMP(15, 5, 10) is 10.
+ * - CLAMP(20, 15, 25) is 20.
+ *
+ * Returns: the value of @x clamped to the range between @low and @high
+ */
+
+/**
+ * G_APPROX_VALUE:
+ * @a: a numeric value
+ * @b: a numeric value
+ * @epsilon: a numeric value that expresses the tolerance between @a and @b
+ *
+ * Evaluates to a truth value if the absolute difference between @a and @b is
+ * smaller than @epsilon, and to a false value otherwise.
+ *
+ * For example,
+ * - `G_APPROX_VALUE (5, 6, 2)` evaluates to true
+ * - `G_APPROX_VALUE (3.14, 3.15, 0.001)` evaluates to false
+ * - `G_APPROX_VALUE (n, 0.f, FLT_EPSILON)` evaluates to true if `n` is within
+ * the single precision floating point epsilon from zero
+ *
+ * Returns: %TRUE if the two values are within the desired range
+ *
+ * Since: 2.58
+ */
+
+/**
+ * G_STRUCT_MEMBER:
+ * @member_type: the type of the struct field
+ * @struct_p: a pointer to a struct
+ * @struct_offset: the offset of the field from the start of the struct,
+ * in bytes
+ *
+ * Returns a member of a structure at a given offset, using the given type.
+ *
+ * Returns: the struct member
+ */
+
+/**
+ * G_STRUCT_MEMBER_P:
+ * @struct_p: a pointer to a struct
+ * @struct_offset: the offset from the start of the struct, in bytes
+ *
+ * Returns an untyped pointer to a given offset of a struct.
+ *
+ * Returns: an untyped pointer to @struct_p plus @struct_offset bytes
+ */
+
+/**
+ * G_STRUCT_OFFSET:
+ * @struct_type: a structure type, e.g. #GtkWidget
+ * @member: a field in the structure, e.g. @window
+ *
+ * Returns the offset, in bytes, of a member of a struct.
+ *
+ * Returns: the offset of @member from the start of @struct_type
+ */
+
+/**
+ * G_N_ELEMENTS:
+ * @arr: the array
+ *
+ * Determines the number of elements in an array. The array must be
+ * declared so the compiler knows its size at compile-time; this
+ * macro will not work on an array allocated on the heap, only static
+ * arrays or arrays on the stack.
+ */
+
+/* Miscellaneous Macros {{{1 */
+
+/**
+ * SECTION:macros_misc
+ * @title: Miscellaneous Macros
+ * @short_description: specialized macros which are not used often
+ *
+ * These macros provide more specialized features which are not
+ * needed so often by application programmers.
+ */
+
+/**
+ * G_STMT_START:
+ *
+ * Used within multi-statement macros so that they can be used in places
+ * where only one statement is expected by the compiler.
+ */
+
+/**
+ * G_STMT_END:
+ *
+ * Used within multi-statement macros so that they can be used in places
+ * where only one statement is expected by the compiler.
+ */
+
+/**
+ * G_BEGIN_DECLS:
+ *
+ * Used (along with %G_END_DECLS) to bracket header files. If the
+ * compiler in use is a C++ compiler, adds extern "C"
+ * around the header.
+ */
+
+/**
+ * G_END_DECLS:
+ *
+ * Used (along with %G_BEGIN_DECLS) to bracket header files. If the
+ * compiler in use is a C++ compiler, adds extern "C"
+ * around the header.
+ */
+
+/**
+ * G_VA_COPY:
+ * @ap1: the va_list variable to place a copy of @ap2 in
+ * @ap2: a va_list
+ *
+ * Portable way to copy va_list variables.
+ *
+ * In order to use this function, you must include string.h yourself,
+ * because this macro may use memmove() and GLib does not include
+ * string.h for you.
+ *
+ * Each invocation of `G_VA_COPY (ap1, ap2)` must be matched with a
+ * corresponding `va_end (ap1)` call in the same function.
+ */
+
+/**
+ * G_STRINGIFY:
+ * @macro_or_string: a macro or a string
+ *
+ * Accepts a macro or a string and converts it into a string after
+ * preprocessor argument expansion. For example, the following code:
+ *
+ * |[
+ * #define AGE 27
+ * const gchar *greeting = G_STRINGIFY (AGE) " today!";
+ * ]|
+ *
+ * is transformed by the preprocessor into (code equivalent to):
+ *
+ * |[
+ * const gchar *greeting = "27 today!";
+ * ]|
+ */
+
+/**
+ * G_PASTE:
+ * @identifier1: an identifier
+ * @identifier2: an identifier
+ *
+ * Yields a new preprocessor pasted identifier
+ * @identifier1identifier2 from its expanded
+ * arguments @identifier1 and @identifier2. For example,
+ * the following code:
+ * |[
+ * #define GET(traveller,method) G_PASTE(traveller_get_, method) (traveller)
+ * const gchar *name = GET (traveller, name);
+ * const gchar *quest = GET (traveller, quest);
+ * GdkColor *favourite = GET (traveller, favourite_colour);
+ * ]|
+ *
+ * is transformed by the preprocessor into:
+ * |[
+ * const gchar *name = traveller_get_name (traveller);
+ * const gchar *quest = traveller_get_quest (traveller);
+ * GdkColor *favourite = traveller_get_favourite_colour (traveller);
+ * ]|
+ *
+ * Since: 2.20
+ */
+
+/**
+ * G_STATIC_ASSERT:
+ * @expr: a constant expression
+ *
+ * The G_STATIC_ASSERT() macro lets the programmer check
+ * a condition at compile time, the condition needs to
+ * be compile time computable. The macro can be used in
+ * any place where a typedef is valid.
+ *
+ * A typedef is generally allowed in exactly the same places that
+ * a variable declaration is allowed. For this reason, you should
+ * not use G_STATIC_ASSERT() in the middle of blocks of code.
+ *
+ * The macro should only be used once per source code line.
+ *
+ * Since: 2.20
+ */
+
+/**
+ * G_STATIC_ASSERT_EXPR:
+ * @expr: a constant expression
+ *
+ * The G_STATIC_ASSERT_EXPR() macro lets the programmer check
+ * a condition at compile time. The condition needs to be
+ * compile time computable.
+ *
+ * Unlike G_STATIC_ASSERT(), this macro evaluates to an expression
+ * and, as such, can be used in the middle of other expressions.
+ * Its value should be ignored. This can be accomplished by placing
+ * it as the first argument of a comma expression.
+ *
+ * |[
+ * #define ADD_ONE_TO_INT(x) \
+ * (G_STATIC_ASSERT_EXPR(sizeof (x) == sizeof (int)), ((x) + 1))
+ * ]|
+ *
+ * Since: 2.30
+ */
+
+/**
+ * G_GNUC_EXTENSION:
+ *
+ * Expands to __extension__ when gcc is used as the compiler. This simply
+ * tells gcc not to warn about the following non-standard code when compiling
+ * with the `-pedantic` option.
+ */
+
+/**
+ * G_GNUC_CHECK_VERSION:
+ * @major: major version to check against
+ * @minor: minor version to check against
+ *
+ * Expands to a check for a compiler with __GNUC__ defined and a version
+ * greater than or equal to the major and minor numbers provided. For example,
+ * the following would only match on compilers such as GCC 4.8 or newer.
+ *
+ * |[
+ * #if G_GNUC_CHECK_VERSION(4, 8)
+ * #endif
+ * ]|
+ *
+ * Since: 2.42
+ */
+
+/**
+ * G_GNUC_BEGIN_IGNORE_DEPRECATIONS:
+ *
+ * Tells gcc (if it is a new enough version) to temporarily stop emitting
+ * warnings when functions marked with %G_GNUC_DEPRECATED or
+ * %G_GNUC_DEPRECATED_FOR are called. This is useful for when you have
+ * one deprecated function calling another one, or when you still have
+ * regression tests for deprecated functions.
+ *
+ * Use %G_GNUC_END_IGNORE_DEPRECATIONS to begin warning again. (If you
+ * are not compiling with `-Wdeprecated-declarations` then neither macro
+ * has any effect.)
+ *
+ * This macro can be used either inside or outside of a function body,
+ * but must appear on a line by itself. Both this macro and the corresponding
+ * %G_GNUC_END_IGNORE_DEPRECATIONS are considered statements, so they
+ * should not be used around branching or loop conditions; for instance,
+ * this use is invalid:
+ *
+ * |[
+ * G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ * if (check == some_deprecated_function ())
+ * G_GNUC_END_IGNORE_DEPRECATIONS
+ * {
+ * do_something ();
+ * }
+ * ]|
+ *
+ * and you should move the deprecated section outside the condition
+ *
+ * |[
+ *
+ * // Solution A
+ * some_data_t *res;
+ *
+ * G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ * res = some_deprecated_function ();
+ * G_GNUC_END_IGNORE_DEPRECATIONS
+ *
+ * if (check == res)
+ * {
+ * do_something ();
+ * }
+ *
+ * // Solution B
+ * G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ * if (check == some_deprecated_function ())
+ * {
+ * do_something ();
+ * }
+ * G_GNUC_END_IGNORE_DEPRECATIONS
+ * ]|
+ *
+ * |[
+ * G_GNUC_INTERNAL
+ * void _g_log_fallback_handler (const gchar *log_domain,
+ * GLogLevelFlags log_level,
+ * const gchar *message,
+ * gpointer unused_data);
+ * ]|
+ *
+ * Since: 2.6
+ */
+
+/**
+ * G_LIKELY:
+ * @expr: the expression
+ *
+ * Hints the compiler that the expression is likely to evaluate to
+ * a true value. The compiler may use this information for optimizations.
+ *
+ * |[
+ * if (G_LIKELY (random () != 1))
+ * g_print ("not one");
+ * ]|
+ *
+ * Returns: the value of @expr
+ *
+ * Since: 2.2
+ */
+
+/**
+ * G_UNLIKELY:
+ * @expr: the expression
+ *
+ * Hints the compiler that the expression is unlikely to evaluate to
+ * a true value. The compiler may use this information for optimizations.
+ *
+ * |[
+ * if (G_UNLIKELY (random () == 1))
+ * g_print ("a random one");
+ * ]|
+ *
+ * Returns: the value of @expr
+ *
+ * Since: 2.2
+ */
+
+/**
+ * G_STRLOC:
+ *
+ * Expands to a string identifying the current code position.
+ */
+
+/**
+ * G_STRFUNC:
+ *
+ * Expands to a string identifying the current function.
+ *
+ * Since: 2.4
+ */
+
+/**
+ * G_HAVE_GNUC_VISIBILITY:
+ *
+ * Defined to 1 if gcc-style visibility handling is supported.
+ */
+
+/* g_auto(), g_autoptr() and helpers {{{1 */
+
+/**
+ * g_auto:
+ * @TypeName: a supported variable type
+ *
+ * Helper to declare a variable with automatic cleanup.
+ *
+ * The variable is cleaned up in a way appropriate to its type when the
+ * variable goes out of scope. The type must support this.
+ * The way to clean up the type must have been defined using one of the macros
+ * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC() or G_DEFINE_AUTO_CLEANUP_FREE_FUNC().
+ *
+ * This feature is only supported on GCC and clang. This macro is not
+ * defined on other compilers and should not be used in programs that
+ * are intended to be portable to those compilers.
+ *
+ * This is meant to be used with stack-allocated structures and
+ * non-pointer types. For the (more commonly used) pointer version, see
+ * g_autoptr().
+ *
+ * This macro can be used to avoid having to do explicit cleanups of
+ * local variables when exiting functions. It often vastly simplifies
+ * handling of error conditions, removing the need for various tricks
+ * such as `goto out` or repeating of cleanup code. It is also helpful
+ * for non-error cases.
+ *
+ * Consider the following example:
+ *
+ * |[
+ * GVariant *
+ * my_func(void)
+ * {
+ * g_auto(GQueue) queue = G_QUEUE_INIT;
+ * g_auto(GVariantBuilder) builder;
+ * g_auto(GStrv) strv;
+ *
+ * g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+ * strv = g_strsplit("a:b:c", ":", -1);
+ *
+ * ...
+ *
+ * if (error_condition)
+ * return NULL;
+ *
+ * ...
+ *
+ * return g_variant_builder_end (&builder);
+ * }
+ * ]|
+ *
+ * You must initialize the variable in some way — either by use of an
+ * initialiser or by ensuring that an `_init` function will be called on
+ * it unconditionally before it goes out of scope.
+ *
+ * Since: 2.44
+ */
+
+/**
+ * g_autoptr:
+ * @TypeName: a supported variable type
+ *
+ * Helper to declare a pointer variable with automatic cleanup.
+ *
+ * The variable is cleaned up in a way appropriate to its type when the
+ * variable goes out of scope. The type must support this.
+ * The way to clean up the type must have been defined using the macro
+ * G_DEFINE_AUTOPTR_CLEANUP_FUNC().
+ *
+ * This feature is only supported on GCC and clang. This macro is not
+ * defined on other compilers and should not be used in programs that
+ * are intended to be portable to those compilers.
+ *
+ * This is meant to be used to declare pointers to types with cleanup
+ * functions. The type of the variable is a pointer to @TypeName. You
+ * must not add your own `*`.
+ *
+ * This macro can be used to avoid having to do explicit cleanups of
+ * local variables when exiting functions. It often vastly simplifies
+ * handling of error conditions, removing the need for various tricks
+ * such as `goto out` or repeating of cleanup code. It is also helpful
+ * for non-error cases.
+ *
+ * Consider the following example:
+ *
+ * |[
+ * gboolean
+ * check_exists(GVariant *dict)
+ * {
+ * g_autoptr(GVariant) dirname, basename = NULL;
+ * g_autofree gchar *path = NULL;
+ *
+ * dirname = g_variant_lookup_value (dict, "dirname", G_VARIANT_TYPE_STRING);
+ *
+ * if (dirname == NULL)
+ * return FALSE;
+ *
+ * basename = g_variant_lookup_value (dict, "basename", G_VARIANT_TYPE_STRING);
+ *
+ * if (basename == NULL)
+ * return FALSE;
+ *
+ * path = g_build_filename (g_variant_get_string (dirname, NULL),
+ * g_variant_get_string (basename, NULL),
+ * NULL);
+ *
+ * return g_access (path, R_OK) == 0;
+ * }
+ * ]|
+ *
+ * You must initialise the variable in some way — either by use of an
+ * initialiser or by ensuring that it is assigned to unconditionally
+ * before it goes out of scope.
+ *
+ * See also g_auto(), g_autofree() and g_steal_pointer().
+ *
+ * Since: 2.44
+ */
+
+/**
+ * g_autofree:
+ *
+ * Macro to add an attribute to pointer variable to ensure automatic
+ * cleanup using g_free().
+ *
+ * This macro differs from g_autoptr() in that it is an attribute supplied
+ * before the type name, rather than wrapping the type definition. Instead
+ * of using a type-specific lookup, this macro always calls g_free() directly.
+ *
+ * This means it's useful for any type that is returned from
+ * g_malloc().
+ *
+ * Otherwise, this macro has similar constraints as g_autoptr(): only
+ * supported on GCC and clang, the variable must be initialized, etc.
+ *
+ * |[
+ * gboolean
+ * operate_on_malloc_buf (void)
+ * {
+ * g_autofree guint8* membuf = NULL;
+ *
+ * membuf = g_malloc (8192);
+ *
+ * // Some computation on membuf
+ *
+ * // membuf will be automatically freed here
+ * return TRUE;
+ * }
+ * ]|
+ *
+ * Since: 2.44
+ */
+
+/**
+ * g_autolist:
+ * @TypeName: a supported variable type
+ *
+ * Helper to declare a list variable with automatic deep cleanup.
+ *
+ * The list is deeply freed, in a way appropriate to the specified type, when the
+ * variable goes out of scope. The type must support this.
+ *
+ * This feature is only supported on GCC and clang. This macro is not
+ * defined on other compilers and should not be used in programs that
+ * are intended to be portable to those compilers.
+ *
+ * This is meant to be used to declare lists of a type with a cleanup
+ * function. The type of the variable is a `GList *`. You
+ * must not add your own `*`.
+ *
+ * This macro can be used to avoid having to do explicit cleanups of
+ * local variables when exiting functions. It often vastly simplifies
+ * handling of error conditions, removing the need for various tricks
+ * such as `goto out` or repeating of cleanup code. It is also helpful
+ * for non-error cases.
+ *
+ * See also g_autoslist(), g_autoptr() and g_steal_pointer().
+ *
+ * Since: 2.56
+ */
+
+/**
+ * g_autoslist:
+ * @TypeName: a supported variable type
+ *
+ * Helper to declare a singly linked list variable with automatic deep cleanup.
+ *
+ * The list is deeply freed, in a way appropriate to the specified type, when the
+ * variable goes out of scope. The type must support this.
+ *
+ * This feature is only supported on GCC and clang. This macro is not
+ * defined on other compilers and should not be used in programs that
+ * are intended to be portable to those compilers.
+ *
+ * This is meant to be used to declare lists of a type with a cleanup
+ * function. The type of the variable is a `GSList *`. You
+ * must not add your own `*`.
+ *
+ * This macro can be used to avoid having to do explicit cleanups of
+ * local variables when exiting functions. It often vastly simplifies
+ * handling of error conditions, removing the need for various tricks
+ * such as `goto out` or repeating of cleanup code. It is also helpful
+ * for non-error cases.
+ *
+ * See also g_autolist(), g_autoptr() and g_steal_pointer().
+ *
+ * Since: 2.56
+ */
+
+/**
+ * g_autoqueue:
+ * @TypeName: a supported variable type
+ *
+ * Helper to declare a double-ended queue variable with automatic deep cleanup.
+ *
+ * The queue is deeply freed, in a way appropriate to the specified type, when the
+ * variable goes out of scope. The type must support this.
+ *
+ * This feature is only supported on GCC and clang. This macro is not
+ * defined on other compilers and should not be used in programs that
+ * are intended to be portable to those compilers.
+ *
+ * This is meant to be used to declare queues of a type with a cleanup
+ * function. The type of the variable is a `GQueue *`. You
+ * must not add your own `*`.
+ *
+ * This macro can be used to avoid having to do explicit cleanups of
+ * local variables when exiting functions. It often vastly simplifies
+ * handling of error conditions, removing the need for various tricks
+ * such as `goto out` or repeating of cleanup code. It is also helpful
+ * for non-error cases.
+ *
+ * See also g_autolist(), g_autoptr() and g_steal_pointer().
+ *
+ * Since: 2.62
+ */
+
+
+/**
+ * G_DEFINE_AUTOPTR_CLEANUP_FUNC:
+ * @TypeName: a type name to define a g_autoptr() cleanup function for
+ * @func: the cleanup function
+ *
+ * Defines the appropriate cleanup function for a pointer type.
+ *
+ * The function will not be called if the variable to be cleaned up
+ * contains %NULL.
+ *
+ * This will typically be the `_free()` or `_unref()` function for the given
+ * type.
+ *
+ * With this definition, it will be possible to use g_autoptr() with
+ * @TypeName.
+ *
+ * |[
+ * G_DEFINE_AUTOPTR_CLEANUP_FUNC(GObject, g_object_unref)
+ * ]|
+ *
+ * This macro should be used unconditionally; it is a no-op on compilers
+ * where cleanup is not supported.
+ *
+ * Since: 2.44
+ */
+
+/**
+ * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC:
+ * @TypeName: a type name to define a g_auto() cleanup function for
+ * @func: the clear function
+ *
+ * Defines the appropriate cleanup function for a type.
+ *
+ * This will typically be the `_clear()` function for the given type.
+ *
+ * With this definition, it will be possible to use g_auto() with
+ * @TypeName.
+ *
+ * |[
+ * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GQueue, g_queue_clear)
+ * ]|
+ *
+ * This macro should be used unconditionally; it is a no-op on compilers
+ * where cleanup is not supported.
+ *
+ * Since: 2.44
+ */
+
+/**
+ * G_DEFINE_AUTO_CLEANUP_FREE_FUNC:
+ * @TypeName: a type name to define a g_auto() cleanup function for
+ * @func: the free function
+ * @none: the "none" value for the type
+ *
+ * Defines the appropriate cleanup function for a type.
+ *
+ * With this definition, it will be possible to use g_auto() with
+ * @TypeName.
+ *
+ * This function will be rarely used. It is used with pointer-based
+ * typedefs and non-pointer types where the value of the variable
+ * represents a resource that must be freed. Two examples are #GStrv
+ * and file descriptors.
+ *
+ * @none specifies the "none" value for the type in question. It is
+ * probably something like %NULL or `-1`. If the variable is found to
+ * contain this value then the free function will not be called.
+ *
+ * |[
+ * G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
+ * ]|
+ *
+ * This macro should be used unconditionally; it is a no-op on compilers
+ * where cleanup is not supported.
+ *
+ * Since: 2.44
+ */
+
+/* Warnings and Assertions {{{1 */
+
+/**
+ * SECTION:warnings
+ * @title: Warnings and Assertions
+ * @short_description: warnings and assertions to use in runtime code
+ *
+ * GLib defines several warning functions and assertions which can be used to
+ * warn of programmer errors when calling functions, and print error messages
+ * from command line programs.
+ *
+ * The g_return_if_fail(), g_return_val_if_fail(), g_return_if_reached() and
+ * g_return_val_if_reached() macros are intended as pre-condition assertions, to
+ * be used at the top of a public function to check that the function’s
+ * arguments are acceptable. Any failure of such a pre-condition assertion is
+ * considered a programming error on the part of the caller of the public API,
+ * and the program is considered to be in an undefined state afterwards. They
+ * are similar to the libc assert() function, but provide more context on
+ * failures.
+ *
+ * For example:
+ * |[
+ * gboolean
+ * g_dtls_connection_shutdown (GDtlsConnection *conn,
+ * gboolean shutdown_read,
+ * gboolean shutdown_write,
+ * GCancellable *cancellable,
+ * GError **error)
+ * {
+ * // local variable declarations
+ *
+ * g_return_val_if_fail (G_IS_DTLS_CONNECTION (conn), FALSE);
+ * g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+ * g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ *
+ * // function body
+ *
+ * return return_val;
+ * }
+ * ]|
+ *
+ * g_print(), g_printerr() and g_set_print_handler() are intended to be used for
+ * output from command line applications, since they output to standard output
+ * and standard error by default — whereas functions like g_message() and
+ * g_log() may be redirected to special purpose message windows, files, or the
+ * system journal.
+ */
+
+/* Windows Compatibility Functions {{{1 */
+
+/**
+ * SECTION:windows
+ * @title: Windows Compatibility Functions
+ * @short_description: UNIX emulation on Windows
+ *
+ * These functions provide some level of UNIX emulation on the
+ * Windows platform. If your application really needs the POSIX
+ * APIs, we suggest you try the Cygwin project.
+ */
+
+/**
+ * MAXPATHLEN:
+ *
+ * Provided for UNIX emulation on Windows; equivalent to UNIX
+ * macro %MAXPATHLEN, which is the maximum length of a filename
+ * (including full path).
+ */
+
+/**
+ * G_WIN32_DLLMAIN_FOR_DLL_NAME:
+ * @static: empty or "static"
+ * @dll_name: the name of the (pointer to the) char array where
+ * the DLL name will be stored. If this is used, you must also
+ * include `windows.h`. If you need a more complex DLL entry
+ * point function, you cannot use this
+ *
+ * On Windows, this macro defines a DllMain() function that stores
+ * the actual DLL name that the code being compiled will be included in.
+ *
+ * On non-Windows platforms, expands to nothing.
+ */
+
+/**
+ * G_WIN32_HAVE_WIDECHAR_API:
+ *
+ * On Windows, this macro defines an expression which evaluates to
+ * %TRUE if the code is running on a version of Windows where the wide
+ * character versions of the Win32 API functions, and the wide character
+ * versions of the C library functions work. (They are always present in
+ * the DLLs, but don't work on Windows 9x and Me.)
+ *
+ * On non-Windows platforms, it is not defined.
+ *
+ * Since: 2.6
+ */
+
+
+/**
+ * G_WIN32_IS_NT_BASED:
+ *
+ * On Windows, this macro defines an expression which evaluates to
+ * %TRUE if the code is running on an NT-based Windows operating system.
+ *
+ * On non-Windows platforms, it is not defined.
+ *
+ * Since: 2.6
+ */
+
+ /* Epilogue {{{1 */
+/* vim: set foldmethod=marker: */
diff --git a/glib/galloca.h b/glib/galloca.h
new file mode 100644
index 0000000000000000000000000000000000000000..86f0d7665aa02a1a5884f08ba5398b59420e21ad
--- /dev/null
+++ b/glib/galloca.h
@@ -0,0 +1,145 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_ALLOCA_H__
+#define __G_ALLOCA_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+#include
+
+#if defined(__BIONIC__) && defined (GLIB_HAVE_ALLOCA_H)
+# include
+#elif defined(__GNUC__)
+/* GCC does the right thing */
+# undef alloca
+# define alloca(size) __builtin_alloca (size)
+#elif defined (GLIB_HAVE_ALLOCA_H)
+/* a native and working alloca.h is there */
+# include
+#else /* !__GNUC__ && !GLIB_HAVE_ALLOCA_H */
+# if defined(_MSC_VER) || defined(__DMC__)
+# include
+# define alloca _alloca
+# else /* !_MSC_VER && !__DMC__ */
+# ifdef _AIX
+# pragma alloca
+# else /* !_AIX */
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+G_BEGIN_DECLS
+char *alloca ();
+G_END_DECLS
+# endif /* !alloca */
+# endif /* !_AIX */
+# endif /* !_MSC_VER && !__DMC__ */
+#endif /* !__GNUC__ && !GLIB_HAVE_ALLOCA_H */
+
+/**
+ * g_alloca:
+ * @size: number of bytes to allocate.
+ *
+ * Allocates @size bytes on the stack; these bytes will be freed when the current
+ * stack frame is cleaned up. This macro essentially just wraps the alloca()
+ * function present on most UNIX variants.
+ * Thus it provides the same advantages and pitfalls as alloca():
+ *
+ * - alloca() is very fast, as on most systems it's implemented by just adjusting
+ * the stack pointer register.
+ *
+ * - It doesn't cause any memory fragmentation, within its scope, separate alloca()
+ * blocks just build up and are released together at function end.
+ *
+ * - Allocation sizes have to fit into the current stack frame. For instance in a
+ * threaded environment on Linux, the per-thread stack size is limited to 2 Megabytes,
+ * so be sparse with alloca() uses.
+ *
+ * - Allocation failure due to insufficient stack space is not indicated with a %NULL
+ * return like e.g. with malloc(). Instead, most systems probably handle it the same
+ * way as out of stack space situations from infinite function recursion, i.e.
+ * with a segmentation fault.
+ *
+ * - Allowing @size to be specified by an untrusted party would allow for them
+ * to trigger a segmentation fault by specifying a large size, leading to a
+ * denial of service vulnerability. @size must always be entirely under the
+ * control of the program.
+ *
+ * - Special care has to be taken when mixing alloca() with GNU C variable sized arrays.
+ * Stack space allocated with alloca() in the same scope as a variable sized array
+ * will be freed together with the variable sized array upon exit of that scope, and
+ * not upon exit of the enclosing function scope.
+ *
+ * Returns: space for @size bytes, allocated on the stack
+ */
+#define g_alloca(size) alloca (size)
+
+/**
+ * g_alloca0:
+ * @size: number of bytes to allocate.
+ *
+ * Wraps g_alloca() and initializes allocated memory to zeroes.
+ * If @size is `0` it returns %NULL.
+ *
+ * Note that the @size argument will be evaluated multiple times.
+ *
+ * Returns: (nullable) (transfer full): space for @size bytes, allocated on the stack
+ *
+ * Since: 2.72
+ */
+#define g_alloca0(size) ((size) == 0 ? NULL : memset (g_alloca (size), 0, (size)))
+
+/**
+ * g_newa:
+ * @struct_type: Type of memory chunks to be allocated
+ * @n_structs: Number of chunks to be allocated
+ *
+ * Wraps g_alloca() in a more typesafe manner.
+ *
+ * As mentioned in the documentation for g_alloca(), @n_structs must always be
+ * entirely under the control of the program, or you may introduce a denial of
+ * service vulnerability. In addition, the multiplication of @struct_type by
+ * @n_structs is not checked, so an overflow may lead to a remote code execution
+ * vulnerability.
+ *
+ * Returns: Pointer to stack space for @n_structs chunks of type @struct_type
+ */
+#define g_newa(struct_type, n_structs) ((struct_type*) g_alloca (sizeof (struct_type) * (gsize) (n_structs)))
+
+/**
+ * g_newa0:
+ * @struct_type: the type of the elements to allocate.
+ * @n_structs: the number of elements to allocate.
+ *
+ * Wraps g_alloca0() in a more typesafe manner.
+ *
+ * Returns: (nullable) (transfer full): Pointer to stack space for @n_structs
+ * chunks of type @struct_type
+ *
+ * Since: 2.72
+ */
+#define g_newa0(struct_type, n_structs) ((struct_type*) g_alloca0 (sizeof (struct_type) * (gsize) (n_structs)))
+
+#endif /* __G_ALLOCA_H__ */
diff --git a/glib/garcbox.c b/glib/garcbox.c
new file mode 100644
index 0000000000000000000000000000000000000000..70aebb6eddce209395fcd00ac78f5dd537af7a5f
--- /dev/null
+++ b/glib/garcbox.c
@@ -0,0 +1,381 @@
+/* garcbox.c: Atomically reference counted data
+ *
+ * Copyright 2018 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include "config.h"
+
+#include "grcboxprivate.h"
+
+#include "gmessages.h"
+#include "grefcount.h"
+
+#ifdef ENABLE_VALGRIND
+#include "valgrind.h"
+#endif
+
+#include "glib_trace.h"
+
+#include
+
+#define G_ARC_BOX(p) (GArcBox *) (((char *) (p)) - G_ARC_BOX_SIZE)
+
+/**
+ * SECTION:arcbox
+ * @Title: Atomically reference counted data
+ * @Short_description: Allocated memory with atomic reference counting semantics
+ *
+ * An "atomically reference counted box", or "ArcBox", is an opaque wrapper
+ * data type that is guaranteed to be as big as the size of a given data type,
+ * and which augments the given data type with thread safe reference counting
+ * semantics for its memory management.
+ *
+ * ArcBox is useful if you have a plain old data type, like a structure
+ * typically placed on the stack, and you wish to provide additional API
+ * to use it on the heap; or if you want to implement a new type to be
+ * passed around by reference without necessarily implementing copy/free
+ * semantics or your own reference counting.
+ *
+ * The typical use is:
+ *
+ * |[
+ * typedef struct {
+ * char *name;
+ * char *address;
+ * char *city;
+ * char *state;
+ * int age;
+ * } Person;
+ *
+ * Person *
+ * person_new (void)
+ * {
+ * return g_atomic_rc_box_new0 (Person);
+ * }
+ * ]|
+ *
+ * Every time you wish to acquire a reference on the memory, you should
+ * call g_atomic_rc_box_acquire(); similarly, when you wish to release a reference
+ * you should call g_atomic_rc_box_release():
+ *
+ * |[
+ * // Add a Person to the Database; the Database acquires ownership
+ * // of the Person instance
+ * void
+ * add_person_to_database (Database *db, Person *p)
+ * {
+ * db->persons = g_list_prepend (db->persons, g_atomic_rc_box_acquire (p));
+ * }
+ *
+ * // Removes a Person from the Database; the reference acquired by
+ * // add_person_to_database() is released here
+ * void
+ * remove_person_from_database (Database *db, Person *p)
+ * {
+ * db->persons = g_list_remove (db->persons, p);
+ * g_atomic_rc_box_release (p);
+ * }
+ * ]|
+ *
+ * If you have additional memory allocated inside the structure, you can
+ * use g_atomic_rc_box_release_full(), which takes a function pointer, which
+ * will be called if the reference released was the last:
+ *
+ * |[
+ * void
+ * person_clear (Person *p)
+ * {
+ * g_free (p->name);
+ * g_free (p->address);
+ * g_free (p->city);
+ * g_free (p->state);
+ * }
+ *
+ * void
+ * remove_person_from_database (Database *db, Person *p)
+ * {
+ * db->persons = g_list_remove (db->persons, p);
+ * g_atomic_rc_box_release_full (p, (GDestroyNotify) person_clear);
+ * }
+ * ]|
+ *
+ * If you wish to transfer the ownership of a reference counted data
+ * type without increasing the reference count, you can use g_steal_pointer():
+ *
+ * |[
+ * Person *p = g_atomic_rc_box_new (Person);
+ *
+ * fill_person_details (p);
+ *
+ * add_person_to_database (db, g_steal_pointer (&p));
+ * ]|
+ *
+ * ## Thread safety
+ *
+ * The reference counting operations on data allocated using g_atomic_rc_box_alloc(),
+ * g_atomic_rc_box_new(), and g_atomic_rc_box_dup() are guaranteed to be atomic, and thus
+ * can be safely be performed by different threads. It is important to note that
+ * only the reference acquisition and release are atomic; changes to the content
+ * of the data are your responsibility.
+ *
+ * ## Automatic pointer clean up
+ *
+ * If you want to add g_autoptr() support to your plain old data type through
+ * reference counting, you can use the G_DEFINE_AUTOPTR_CLEANUP_FUNC() and
+ * g_atomic_rc_box_release():
+ *
+ * |[
+ * G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, g_atomic_rc_box_release)
+ * ]|
+ *
+ * If you need to clear the contents of the data, you will need to use an
+ * ancillary function that calls g_rc_box_release_full():
+ *
+ * |[
+ * static void
+ * my_data_struct_release (MyDataStruct *data)
+ * {
+ * // my_data_struct_clear() is defined elsewhere
+ * g_atomic_rc_box_release_full (data, (GDestroyNotify) my_data_struct_clear);
+ * }
+ *
+ * G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, my_data_struct_release)
+ * ]|
+ *
+ * Since: 2.58
+ */
+
+/**
+ * g_atomic_rc_box_alloc:
+ * @block_size: the size of the allocation, must be greater than 0
+ *
+ * Allocates @block_size bytes of memory, and adds atomic
+ * reference counting semantics to it.
+ *
+ * The data will be freed when its reference count drops to
+ * zero.
+ *
+ * The allocated data is guaranteed to be suitably aligned for any
+ * built-in type.
+ *
+ * Returns: (transfer full) (not nullable): a pointer to the allocated memory
+ *
+ * Since: 2.58
+ */
+gpointer
+g_atomic_rc_box_alloc (gsize block_size)
+{
+ g_return_val_if_fail (block_size > 0, NULL);
+
+ return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, FALSE);
+}
+
+/**
+ * g_atomic_rc_box_alloc0:
+ * @block_size: the size of the allocation, must be greater than 0
+ *
+ * Allocates @block_size bytes of memory, and adds atomic
+ * reference counting semantics to it.
+ *
+ * The contents of the returned data is set to zero.
+ *
+ * The data will be freed when its reference count drops to
+ * zero.
+ *
+ * The allocated data is guaranteed to be suitably aligned for any
+ * built-in type.
+ *
+ * Returns: (transfer full) (not nullable): a pointer to the allocated memory
+ *
+ * Since: 2.58
+ */
+gpointer
+g_atomic_rc_box_alloc0 (gsize block_size)
+{
+ g_return_val_if_fail (block_size > 0, NULL);
+
+ return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, TRUE);
+}
+
+/**
+ * g_atomic_rc_box_new:
+ * @type: the type to allocate, typically a structure name
+ *
+ * A convenience macro to allocate atomically reference counted
+ * data with the size of the given @type.
+ *
+ * This macro calls g_atomic_rc_box_alloc() with `sizeof (@type)` and
+ * casts the returned pointer to a pointer of the given @type,
+ * avoiding a type cast in the source code.
+ *
+ * Returns: (transfer full) (not nullable): a pointer to the allocated
+ * memory, cast to a pointer for the given @type
+ *
+ * Since: 2.58
+ */
+
+/**
+ * g_atomic_rc_box_new0:
+ * @type: the type to allocate, typically a structure name
+ *
+ * A convenience macro to allocate atomically reference counted
+ * data with the size of the given @type, and set its contents
+ * to zero.
+ *
+ * This macro calls g_atomic_rc_box_alloc0() with `sizeof (@type)` and
+ * casts the returned pointer to a pointer of the given @type,
+ * avoiding a type cast in the source code.
+ *
+ * Returns: (transfer full) (not nullable): a pointer to the allocated
+ * memory, cast to a pointer for the given @type
+ *
+ * Since: 2.58
+ */
+
+/**
+ * g_atomic_rc_box_dup:
+ * @block_size: the number of bytes to copy, must be greater than 0
+ * @mem_block: (not nullable): the memory to copy
+ *
+ * Allocates a new block of data with atomic reference counting
+ * semantics, and copies @block_size bytes of @mem_block
+ * into it.
+ *
+ * Returns: (transfer full) (not nullable): a pointer to the allocated
+ * memory
+ *
+ * Since: 2.58
+ */
+gpointer
+(g_atomic_rc_box_dup) (gsize block_size,
+ gconstpointer mem_block)
+{
+ gpointer res;
+
+ g_return_val_if_fail (block_size > 0, NULL);
+ g_return_val_if_fail (mem_block != NULL, NULL);
+
+ res = g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, FALSE);
+ memcpy (res, mem_block, block_size);
+
+ return res;
+}
+
+/**
+ * g_atomic_rc_box_acquire:
+ * @mem_block: (not nullable): a pointer to reference counted data
+ *
+ * Atomically acquires a reference on the data pointed by @mem_block.
+ *
+ * Returns: (transfer full) (not nullable): a pointer to the data,
+ * with its reference count increased
+ *
+ * Since: 2.58
+ */
+gpointer
+(g_atomic_rc_box_acquire) (gpointer mem_block)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+
+ g_return_val_if_fail (mem_block != NULL, NULL);
+#ifndef G_DISABLE_ASSERT
+ g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL);
+#endif
+
+ g_atomic_ref_count_inc (&real_box->ref_count);
+
+ TRACE (GLIB_RCBOX_ACQUIRE (mem_block, 1));
+
+ return mem_block;
+}
+
+/**
+ * g_atomic_rc_box_release:
+ * @mem_block: (transfer full) (not nullable): a pointer to reference counted data
+ *
+ * Atomically releases a reference on the data pointed by @mem_block.
+ *
+ * If the reference was the last one, it will free the
+ * resources allocated for @mem_block.
+ *
+ * Since: 2.58
+ */
+void
+g_atomic_rc_box_release (gpointer mem_block)
+{
+ g_atomic_rc_box_release_full (mem_block, NULL);
+}
+
+/**
+ * g_atomic_rc_box_release_full:
+ * @mem_block: (transfer full) (not nullable): a pointer to reference counted data
+ * @clear_func: (not nullable): a function to call when clearing the data
+ *
+ * Atomically releases a reference on the data pointed by @mem_block.
+ *
+ * If the reference was the last one, it will call @clear_func
+ * to clear the contents of @mem_block, and then will free the
+ * resources allocated for @mem_block.
+ *
+ * Since: 2.58
+ */
+void
+g_atomic_rc_box_release_full (gpointer mem_block,
+ GDestroyNotify clear_func)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+
+ g_return_if_fail (mem_block != NULL);
+#ifndef G_DISABLE_ASSERT
+ g_return_if_fail (real_box->magic == G_BOX_MAGIC);
+#endif
+
+ if (g_atomic_ref_count_dec (&real_box->ref_count))
+ {
+ char *real_mem = (char *) real_box - real_box->private_offset;
+
+ TRACE (GLIB_RCBOX_RELEASE (mem_block, 1));
+
+ if (clear_func != NULL)
+ clear_func (mem_block);
+
+ TRACE (GLIB_RCBOX_FREE (mem_block));
+ g_free (real_mem);
+ }
+}
+
+/**
+ * g_atomic_rc_box_get_size:
+ * @mem_block: (not nullable): a pointer to reference counted data
+ *
+ * Retrieves the size of the reference counted data pointed by @mem_block.
+ *
+ * Returns: the size of the data, in bytes
+ *
+ * Since: 2.58
+ */
+gsize
+g_atomic_rc_box_get_size (gpointer mem_block)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+
+ g_return_val_if_fail (mem_block != NULL, 0);
+#ifndef G_DISABLE_ASSERT
+ g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, 0);
+#endif
+
+ return real_box->mem_size;
+}
diff --git a/glib/garray.c b/glib/garray.c
new file mode 100644
index 0000000000000000000000000000000000000000..1ab3beeda308354123ca7c66f9c56adfd0690b74
--- /dev/null
+++ b/glib/garray.c
@@ -0,0 +1,2575 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "garray.h"
+
+#include "gbytes.h"
+#include "ghash.h"
+#include "gslice.h"
+#include "gmem.h"
+#include "gtestutils.h"
+#include "gthread.h"
+#include "gmessages.h"
+#include "gqsort.h"
+#include "grefcount.h"
+#include "gutilsprivate.h"
+
+/**
+ * SECTION:arrays
+ * @title: Arrays
+ * @short_description: arrays of arbitrary elements which grow
+ * automatically as elements are added
+ *
+ * Arrays are similar to standard C arrays, except that they grow
+ * automatically as elements are added.
+ *
+ * Array elements can be of any size (though all elements of one array
+ * are the same size), and the array can be automatically cleared to
+ * '0's and zero-terminated.
+ *
+ * To create a new array use g_array_new().
+ *
+ * To add elements to an array with a cost of O(n) at worst, use
+ * g_array_append_val(), g_array_append_vals(), g_array_prepend_val(),
+ * g_array_prepend_vals(), g_array_insert_val() and g_array_insert_vals().
+ *
+ * To access an element of an array in O(1) (to read it or to write it),
+ * use g_array_index().
+ *
+ * To set the size of an array, use g_array_set_size().
+ *
+ * To free an array, use g_array_unref() or g_array_free().
+ *
+ * All the sort functions are internally calling a quick-sort (or similar)
+ * function with an average cost of O(n log(n)) and a worst case
+ * cost of O(n^2).
+ *
+ * Here is an example that stores integers in a #GArray:
+ * |[
+ * GArray *garray;
+ * gint i;
+ * // We create a new array to store gint values.
+ * // We don't want it zero-terminated or cleared to 0's.
+ * garray = g_array_new (FALSE, FALSE, sizeof (gint));
+ * for (i = 0; i < 10000; i++)
+ * g_array_append_val (garray, i);
+ * for (i = 0; i < 10000; i++)
+ * if (g_array_index (garray, gint, i) != i)
+ * g_print ("ERROR: got %d instead of %d\n",
+ * g_array_index (garray, gint, i), i);
+ * g_array_free (garray, TRUE);
+ * ]|
+ */
+
+#define MIN_ARRAY_SIZE 16
+
+typedef struct _GRealArray GRealArray;
+
+/**
+ * GArray:
+ * @data: a pointer to the element data. The data may be moved as
+ * elements are added to the #GArray.
+ * @len: the number of elements in the #GArray not including the
+ * possible terminating zero element.
+ *
+ * Contains the public fields of a GArray.
+ */
+struct _GRealArray
+{
+ guint8 *data;
+ guint len;
+ guint elt_capacity;
+ guint elt_size;
+ guint zero_terminated : 1;
+ guint clear : 1;
+ gatomicrefcount ref_count;
+ GDestroyNotify clear_func;
+};
+
+/**
+ * g_array_index:
+ * @a: a #GArray
+ * @t: the type of the elements
+ * @i: the index of the element to return
+ *
+ * Returns the element of a #GArray at the given index. The return
+ * value is cast to the given type. This is the main way to read or write an
+ * element in a #GArray.
+ *
+ * Writing an element is typically done by reference, as in the following
+ * example. This example gets a pointer to an element in a #GArray, and then
+ * writes to a field in it:
+ * |[
+ * EDayViewEvent *event;
+ * // This gets a pointer to the 4th element in the array of
+ * // EDayViewEvent structs.
+ * event = &g_array_index (events, EDayViewEvent, 3);
+ * event->start_time = g_get_current_time ();
+ * ]|
+ *
+ * This example reads from and writes to an array of integers:
+ * |[
+ * g_autoptr(GArray) int_array = g_array_new (FALSE, FALSE, sizeof (guint));
+ * for (guint i = 0; i < 10; i++)
+ * g_array_append_val (int_array, i);
+ *
+ * guint *my_int = &g_array_index (int_array, guint, 1);
+ * g_print ("Int at index 1 is %u; decrementing it\n", *my_int);
+ * *my_int = *my_int - 1;
+ * ]|
+ *
+ * Returns: the element of the #GArray at the index given by @i
+ */
+
+#define g_array_elt_len(array,i) ((gsize)(array)->elt_size * (i))
+#define g_array_elt_pos(array,i) ((array)->data + g_array_elt_len((array),(i)))
+#define g_array_elt_zero(array, pos, len) \
+ (memset (g_array_elt_pos ((array), pos), 0, g_array_elt_len ((array), len)))
+#define g_array_zero_terminate(array) G_STMT_START{ \
+ if ((array)->zero_terminated) \
+ g_array_elt_zero ((array), (array)->len, 1); \
+}G_STMT_END
+
+static void g_array_maybe_expand (GRealArray *array,
+ guint len);
+
+/**
+ * g_array_new:
+ * @zero_terminated: %TRUE if the array should have an extra element at
+ * the end which is set to 0
+ * @clear_: %TRUE if #GArray elements should be automatically cleared
+ * to 0 when they are allocated
+ * @element_size: the size of each element in bytes
+ *
+ * Creates a new #GArray with a reference count of 1.
+ *
+ * Returns: the new #GArray
+ */
+GArray*
+g_array_new (gboolean zero_terminated,
+ gboolean clear,
+ guint elt_size)
+{
+ g_return_val_if_fail (elt_size > 0, NULL);
+#if (UINT_WIDTH / 8) >= GLIB_SIZEOF_SIZE_T
+ g_return_val_if_fail (elt_size <= G_MAXSIZE / 2 - 1, NULL);
+#endif
+
+ return g_array_sized_new (zero_terminated, clear, elt_size, 0);
+}
+
+/**
+ * g_array_steal:
+ * @array: a #GArray.
+ * @len: (optional) (out): pointer to retrieve the number of
+ * elements of the original array
+ *
+ * Frees the data in the array and resets the size to zero, while
+ * the underlying array is preserved for use elsewhere and returned
+ * to the caller.
+ *
+ * If the array was created with the @zero_terminate property
+ * set to %TRUE, the returned data is zero terminated too.
+ *
+ * If array elements contain dynamically-allocated memory,
+ * the array elements should also be freed by the caller.
+ *
+ * A short example of use:
+ * |[
+ * ...
+ * gpointer data;
+ * gsize data_len;
+ * data = g_array_steal (some_array, &data_len);
+ * ...
+ * ]|
+
+ * Returns: (transfer full): the element data, which should be
+ * freed using g_free().
+ *
+ * Since: 2.64
+ */
+gpointer
+g_array_steal (GArray *array,
+ gsize *len)
+{
+ GRealArray *rarray;
+ gpointer segment;
+
+ g_return_val_if_fail (array != NULL, NULL);
+
+ rarray = (GRealArray *) array;
+ segment = (gpointer) rarray->data;
+
+ if (len != NULL)
+ *len = rarray->len;
+
+ rarray->data = NULL;
+ rarray->len = 0;
+ rarray->elt_capacity = 0;
+ return segment;
+}
+
+/**
+ * g_array_sized_new:
+ * @zero_terminated: %TRUE if the array should have an extra element at
+ * the end with all bits cleared
+ * @clear_: %TRUE if all bits in the array should be cleared to 0 on
+ * allocation
+ * @element_size: size of each element in the array
+ * @reserved_size: number of elements preallocated
+ *
+ * Creates a new #GArray with @reserved_size elements preallocated and
+ * a reference count of 1. This avoids frequent reallocation, if you
+ * are going to add many elements to the array. Note however that the
+ * size of the array is still 0.
+ *
+ * Returns: the new #GArray
+ */
+GArray*
+g_array_sized_new (gboolean zero_terminated,
+ gboolean clear,
+ guint elt_size,
+ guint reserved_size)
+{
+ GRealArray *array;
+
+ g_return_val_if_fail (elt_size > 0, NULL);
+#if (UINT_WIDTH / 8) >= GLIB_SIZEOF_SIZE_T
+ g_return_val_if_fail (elt_size <= G_MAXSIZE / 2 - 1, NULL);
+#endif
+
+ array = g_slice_new (GRealArray);
+
+ array->data = NULL;
+ array->len = 0;
+ array->elt_capacity = 0;
+ array->zero_terminated = (zero_terminated ? 1 : 0);
+ array->clear = (clear ? 1 : 0);
+ array->elt_size = elt_size;
+ array->clear_func = NULL;
+
+ g_atomic_ref_count_init (&array->ref_count);
+
+ if (array->zero_terminated || reserved_size != 0)
+ {
+ g_array_maybe_expand (array, reserved_size);
+ g_array_zero_terminate(array);
+ }
+
+ return (GArray*) array;
+}
+
+/**
+ * g_array_set_clear_func:
+ * @array: A #GArray
+ * @clear_func: a function to clear an element of @array
+ *
+ * Sets a function to clear an element of @array.
+ *
+ * The @clear_func will be called when an element in the array
+ * data segment is removed and when the array is freed and data
+ * segment is deallocated as well. @clear_func will be passed a
+ * pointer to the element to clear, rather than the element itself.
+ *
+ * Note that in contrast with other uses of #GDestroyNotify
+ * functions, @clear_func is expected to clear the contents of
+ * the array element it is given, but not free the element itself.
+ *
+ * |[
+ * typedef struct
+ * {
+ * gchar *str;
+ * GObject *obj;
+ * } ArrayElement;
+ *
+ * static void
+ * array_element_clear (ArrayElement *element)
+ * {
+ * g_clear_pointer (&element->str, g_free);
+ * g_clear_object (&element->obj);
+ * }
+ *
+ * // main code
+ * GArray *garray = g_array_new (FALSE, FALSE, sizeof (ArrayElement));
+ * g_array_set_clear_func (garray, (GDestroyNotify) array_element_clear);
+ * // assign data to the structure
+ * g_array_free (garray, TRUE);
+ * ]|
+ *
+ * Since: 2.32
+ */
+void
+g_array_set_clear_func (GArray *array,
+ GDestroyNotify clear_func)
+{
+ GRealArray *rarray = (GRealArray *) array;
+
+ g_return_if_fail (array != NULL);
+
+ rarray->clear_func = clear_func;
+}
+
+/**
+ * g_array_ref:
+ * @array: A #GArray
+ *
+ * Atomically increments the reference count of @array by one.
+ * This function is thread-safe and may be called from any thread.
+ *
+ * Returns: The passed in #GArray
+ *
+ * Since: 2.22
+ */
+GArray *
+g_array_ref (GArray *array)
+{
+ GRealArray *rarray = (GRealArray*) array;
+ g_return_val_if_fail (array, NULL);
+
+ g_atomic_ref_count_inc (&rarray->ref_count);
+
+ return array;
+}
+
+typedef enum
+{
+ FREE_SEGMENT = 1 << 0,
+ PRESERVE_WRAPPER = 1 << 1
+} ArrayFreeFlags;
+
+static gchar *array_free (GRealArray *, ArrayFreeFlags);
+
+/**
+ * g_array_unref:
+ * @array: A #GArray
+ *
+ * Atomically decrements the reference count of @array by one. If the
+ * reference count drops to 0, all memory allocated by the array is
+ * released. This function is thread-safe and may be called from any
+ * thread.
+ *
+ * Since: 2.22
+ */
+void
+g_array_unref (GArray *array)
+{
+ GRealArray *rarray = (GRealArray*) array;
+ g_return_if_fail (array);
+
+ if (g_atomic_ref_count_dec (&rarray->ref_count))
+ array_free (rarray, FREE_SEGMENT);
+}
+
+/**
+ * g_array_get_element_size:
+ * @array: A #GArray
+ *
+ * Gets the size of the elements in @array.
+ *
+ * Returns: Size of each element, in bytes
+ *
+ * Since: 2.22
+ */
+guint
+g_array_get_element_size (GArray *array)
+{
+ GRealArray *rarray = (GRealArray*) array;
+
+ g_return_val_if_fail (array, 0);
+
+ return rarray->elt_size;
+}
+
+/**
+ * g_array_free:
+ * @array: a #GArray
+ * @free_segment: if %TRUE the actual element data is freed as well
+ *
+ * Frees the memory allocated for the #GArray. If @free_segment is
+ * %TRUE it frees the memory block holding the elements as well. Pass
+ * %FALSE if you want to free the #GArray wrapper but preserve the
+ * underlying array for use elsewhere. If the reference count of
+ * @array is greater than one, the #GArray wrapper is preserved but
+ * the size of @array will be set to zero.
+ *
+ * If array contents point to dynamically-allocated memory, they should
+ * be freed separately if @free_seg is %TRUE and no @clear_func
+ * function has been set for @array.
+ *
+ * This function is not thread-safe. If using a #GArray from multiple
+ * threads, use only the atomic g_array_ref() and g_array_unref()
+ * functions.
+ *
+ * Returns: the element data if @free_segment is %FALSE, otherwise
+ * %NULL. The element data should be freed using g_free().
+ */
+gchar*
+g_array_free (GArray *farray,
+ gboolean free_segment)
+{
+ GRealArray *array = (GRealArray*) farray;
+ ArrayFreeFlags flags;
+
+ g_return_val_if_fail (array, NULL);
+
+ flags = (free_segment ? FREE_SEGMENT : 0);
+
+ /* if others are holding a reference, preserve the wrapper but do free/return the data */
+ if (!g_atomic_ref_count_dec (&array->ref_count))
+ flags |= PRESERVE_WRAPPER;
+
+ return array_free (array, flags);
+}
+
+static gchar *
+array_free (GRealArray *array,
+ ArrayFreeFlags flags)
+{
+ gchar *segment;
+
+ if (flags & FREE_SEGMENT)
+ {
+ if (array->clear_func != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < array->len; i++)
+ array->clear_func (g_array_elt_pos (array, i));
+ }
+
+ g_free (array->data);
+ segment = NULL;
+ }
+ else
+ segment = (gchar*) array->data;
+
+ if (flags & PRESERVE_WRAPPER)
+ {
+ array->data = NULL;
+ array->len = 0;
+ array->elt_capacity = 0;
+ }
+ else
+ {
+ g_slice_free1 (sizeof (GRealArray), array);
+ }
+
+ return segment;
+}
+
+/**
+ * g_array_append_vals:
+ * @array: a #GArray
+ * @data: (not nullable): a pointer to the elements to append to the end of the array
+ * @len: the number of elements to append
+ *
+ * Adds @len elements onto the end of the array.
+ *
+ * Returns: the #GArray
+ */
+/**
+ * g_array_append_val:
+ * @a: a #GArray
+ * @v: the value to append to the #GArray
+ *
+ * Adds the value on to the end of the array. The array will grow in
+ * size automatically if necessary.
+ *
+ * g_array_append_val() is a macro which uses a reference to the value
+ * parameter @v. This means that you cannot use it with literal values
+ * such as "27". You must use variables.
+ *
+ * Returns: the #GArray
+ */
+GArray*
+g_array_append_vals (GArray *farray,
+ gconstpointer data,
+ guint len)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+
+ if (len == 0)
+ return farray;
+
+ g_array_maybe_expand (array, len);
+
+ memcpy (g_array_elt_pos (array, array->len), data,
+ g_array_elt_len (array, len));
+
+ array->len += len;
+
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_prepend_vals:
+ * @array: a #GArray
+ * @data: (nullable): a pointer to the elements to prepend to the start of the array
+ * @len: the number of elements to prepend, which may be zero
+ *
+ * Adds @len elements onto the start of the array.
+ *
+ * @data may be %NULL if (and only if) @len is zero. If @len is zero, this
+ * function is a no-op.
+ *
+ * This operation is slower than g_array_append_vals() since the
+ * existing elements in the array have to be moved to make space for
+ * the new elements.
+ *
+ * Returns: the #GArray
+ */
+/**
+ * g_array_prepend_val:
+ * @a: a #GArray
+ * @v: the value to prepend to the #GArray
+ *
+ * Adds the value on to the start of the array. The array will grow in
+ * size automatically if necessary.
+ *
+ * This operation is slower than g_array_append_val() since the
+ * existing elements in the array have to be moved to make space for
+ * the new element.
+ *
+ * g_array_prepend_val() is a macro which uses a reference to the value
+ * parameter @v. This means that you cannot use it with literal values
+ * such as "27". You must use variables.
+ *
+ * Returns: the #GArray
+ */
+GArray*
+g_array_prepend_vals (GArray *farray,
+ gconstpointer data,
+ guint len)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+
+ if (len == 0)
+ return farray;
+
+ g_array_maybe_expand (array, len);
+
+ memmove (g_array_elt_pos (array, len), g_array_elt_pos (array, 0),
+ g_array_elt_len (array, array->len));
+
+ memcpy (g_array_elt_pos (array, 0), data, g_array_elt_len (array, len));
+
+ array->len += len;
+
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_insert_vals:
+ * @array: a #GArray
+ * @index_: the index to place the elements at
+ * @data: (nullable): a pointer to the elements to insert
+ * @len: the number of elements to insert
+ *
+ * Inserts @len elements into a #GArray at the given index.
+ *
+ * If @index_ is greater than the array’s current length, the array is expanded.
+ * The elements between the old end of the array and the newly inserted elements
+ * will be initialised to zero if the array was configured to clear elements;
+ * otherwise their values will be undefined.
+ *
+ * If @index_ is less than the array’s current length, new entries will be
+ * inserted into the array, and the existing entries above @index_ will be moved
+ * upwards.
+ *
+ * @data may be %NULL if (and only if) @len is zero. If @len is zero, this
+ * function is a no-op.
+ *
+ * Returns: the #GArray
+ */
+/**
+ * g_array_insert_val:
+ * @a: a #GArray
+ * @i: the index to place the element at
+ * @v: the value to insert into the array
+ *
+ * Inserts an element into an array at the given index.
+ *
+ * g_array_insert_val() is a macro which uses a reference to the value
+ * parameter @v. This means that you cannot use it with literal values
+ * such as "27". You must use variables.
+ *
+ * Returns: the #GArray
+ */
+GArray*
+g_array_insert_vals (GArray *farray,
+ guint index_,
+ gconstpointer data,
+ guint len)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+
+ if (len == 0)
+ return farray;
+
+ /* Is the index off the end of the array, and hence do we need to over-allocate
+ * and clear some elements? */
+ if (index_ >= array->len)
+ {
+ g_array_maybe_expand (array, index_ - array->len + len);
+ return g_array_append_vals (g_array_set_size (farray, index_), data, len);
+ }
+
+ g_array_maybe_expand (array, len);
+
+ memmove (g_array_elt_pos (array, len + index_),
+ g_array_elt_pos (array, index_),
+ g_array_elt_len (array, array->len - index_));
+
+ memcpy (g_array_elt_pos (array, index_), data, g_array_elt_len (array, len));
+
+ array->len += len;
+
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_set_size:
+ * @array: a #GArray
+ * @length: the new size of the #GArray
+ *
+ * Sets the size of the array, expanding it if necessary. If the array
+ * was created with @clear_ set to %TRUE, the new elements are set to 0.
+ *
+ * Returns: the #GArray
+ */
+GArray*
+g_array_set_size (GArray *farray,
+ guint length)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+
+ if (length > array->len)
+ {
+ g_array_maybe_expand (array, length - array->len);
+
+ if (array->clear)
+ g_array_elt_zero (array, array->len, length - array->len);
+ }
+ else if (length < array->len)
+ g_array_remove_range (farray, length, array->len - length);
+
+ array->len = length;
+
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_remove_index:
+ * @array: a #GArray
+ * @index_: the index of the element to remove
+ *
+ * Removes the element at the given index from a #GArray. The following
+ * elements are moved down one place.
+ *
+ * Returns: the #GArray
+ */
+GArray*
+g_array_remove_index (GArray *farray,
+ guint index_)
+{
+ GRealArray* array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+
+ g_return_val_if_fail (index_ < array->len, NULL);
+
+ if (array->clear_func != NULL)
+ array->clear_func (g_array_elt_pos (array, index_));
+
+ if (index_ != array->len - 1)
+ memmove (g_array_elt_pos (array, index_),
+ g_array_elt_pos (array, index_ + 1),
+ g_array_elt_len (array, array->len - index_ - 1));
+
+ array->len -= 1;
+
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ g_array_elt_zero (array, array->len, 1);
+ else
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_remove_index_fast:
+ * @array: a @GArray
+ * @index_: the index of the element to remove
+ *
+ * Removes the element at the given index from a #GArray. The last
+ * element in the array is used to fill in the space, so this function
+ * does not preserve the order of the #GArray. But it is faster than
+ * g_array_remove_index().
+ *
+ * Returns: the #GArray
+ */
+GArray*
+g_array_remove_index_fast (GArray *farray,
+ guint index_)
+{
+ GRealArray* array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+
+ g_return_val_if_fail (index_ < array->len, NULL);
+
+ if (array->clear_func != NULL)
+ array->clear_func (g_array_elt_pos (array, index_));
+
+ if (index_ != array->len - 1)
+ memcpy (g_array_elt_pos (array, index_),
+ g_array_elt_pos (array, array->len - 1),
+ g_array_elt_len (array, 1));
+
+ array->len -= 1;
+
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ g_array_elt_zero (array, array->len, 1);
+ else
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_remove_range:
+ * @array: a @GArray
+ * @index_: the index of the first element to remove
+ * @length: the number of elements to remove
+ *
+ * Removes the given number of elements starting at the given index
+ * from a #GArray. The following elements are moved to close the gap.
+ *
+ * Returns: the #GArray
+ *
+ * Since: 2.4
+ */
+GArray*
+g_array_remove_range (GArray *farray,
+ guint index_,
+ guint length)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_val_if_fail (array, NULL);
+ g_return_val_if_fail (index_ <= array->len, NULL);
+ g_return_val_if_fail (index_ + length <= array->len, NULL);
+
+ if (array->clear_func != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < length; i++)
+ array->clear_func (g_array_elt_pos (array, index_ + i));
+ }
+
+ if (index_ + length != array->len)
+ memmove (g_array_elt_pos (array, index_),
+ g_array_elt_pos (array, index_ + length),
+ (array->len - (index_ + length)) * array->elt_size);
+
+ array->len -= length;
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ g_array_elt_zero (array, array->len, length);
+ else
+ g_array_zero_terminate (array);
+
+ return farray;
+}
+
+/**
+ * g_array_sort:
+ * @array: a #GArray
+ * @compare_func: comparison function
+ *
+ * Sorts a #GArray using @compare_func which should be a qsort()-style
+ * comparison function (returns less than zero for first arg is less
+ * than second arg, zero for equal, greater zero if first arg is
+ * greater than second arg).
+ *
+ * This is guaranteed to be a stable sort since version 2.32.
+ */
+void
+g_array_sort (GArray *farray,
+ GCompareFunc compare_func)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_if_fail (array != NULL);
+
+ /* Don't use qsort as we want a guaranteed stable sort */
+ if (array->len > 0)
+ g_qsort_with_data (array->data,
+ array->len,
+ array->elt_size,
+ (GCompareDataFunc)compare_func,
+ NULL);
+}
+
+/**
+ * g_array_sort_with_data:
+ * @array: a #GArray
+ * @compare_func: comparison function
+ * @user_data: data to pass to @compare_func
+ *
+ * Like g_array_sort(), but the comparison function receives an extra
+ * user data argument.
+ *
+ * This is guaranteed to be a stable sort since version 2.32.
+ *
+ * There used to be a comment here about making the sort stable by
+ * using the addresses of the elements in the comparison function.
+ * This did not actually work, so any such code should be removed.
+ */
+void
+g_array_sort_with_data (GArray *farray,
+ GCompareDataFunc compare_func,
+ gpointer user_data)
+{
+ GRealArray *array = (GRealArray*) farray;
+
+ g_return_if_fail (array != NULL);
+
+ if (array->len > 0)
+ g_qsort_with_data (array->data,
+ array->len,
+ array->elt_size,
+ compare_func,
+ user_data);
+}
+
+/**
+ * g_array_binary_search:
+ * @array: a #GArray.
+ * @target: a pointer to the item to look up.
+ * @compare_func: A #GCompareFunc used to locate @target.
+ * @out_match_index: (optional) (out): return location
+ * for the index of the element, if found.
+ *
+ * Checks whether @target exists in @array by performing a binary
+ * search based on the given comparison function @compare_func which
+ * get pointers to items as arguments. If the element is found, %TRUE
+ * is returned and the element’s index is returned in @out_match_index
+ * (if non-%NULL). Otherwise, %FALSE is returned and @out_match_index
+ * is undefined. If @target exists multiple times in @array, the index
+ * of the first instance is returned. This search is using a binary
+ * search, so the @array must absolutely be sorted to return a correct
+ * result (if not, the function may produce false-negative).
+ *
+ * This example defines a comparison function and search an element in a #GArray:
+ * |[
+ * static gint*
+ * cmpint (gconstpointer a, gconstpointer b)
+ * {
+ * const gint *_a = a;
+ * const gint *_b = b;
+ *
+ * return *_a - *_b;
+ * }
+ * ...
+ * gint i = 424242;
+ * guint matched_index;
+ * gboolean result = g_array_binary_search (garray, &i, cmpint, &matched_index);
+ * ...
+ * ]|
+ *
+ * Returns: %TRUE if @target is one of the elements of @array, %FALSE otherwise.
+ *
+ * Since: 2.62
+ */
+gboolean
+g_array_binary_search (GArray *array,
+ gconstpointer target,
+ GCompareFunc compare_func,
+ guint *out_match_index)
+{
+ gboolean result = FALSE;
+ GRealArray *_array = (GRealArray *) array;
+ guint left, middle = 0, right;
+ gint val;
+
+ g_return_val_if_fail (_array != NULL, FALSE);
+ g_return_val_if_fail (compare_func != NULL, FALSE);
+
+ if (G_LIKELY(_array->len))
+ {
+ left = 0;
+ right = _array->len - 1;
+
+ while (left <= right)
+ {
+ middle = left + (right - left) / 2;
+
+ val = compare_func (_array->data + (_array->elt_size * middle), target);
+ if (val == 0)
+ {
+ result = TRUE;
+ break;
+ }
+ else if (val < 0)
+ left = middle + 1;
+ else if (/* val > 0 && */ middle > 0)
+ right = middle - 1;
+ else
+ break; /* element not found */
+ }
+ }
+
+ if (result && out_match_index != NULL)
+ *out_match_index = middle;
+
+ return result;
+}
+
+static void
+g_array_maybe_expand (GRealArray *array,
+ guint len)
+{
+ guint max_len, want_len;
+
+ /* The maximum array length is derived from following constraints:
+ * - The number of bytes must fit into a gsize / 2.
+ * - The number of elements must fit into guint.
+ * - zero terminated arrays must leave space for the terminating element
+ */
+ max_len = MIN (G_MAXSIZE / 2 / array->elt_size, G_MAXUINT) - array->zero_terminated;
+
+ /* Detect potential overflow */
+ if G_UNLIKELY ((max_len - array->len) < len)
+ g_error ("adding %u to array would overflow", len);
+
+ want_len = array->len + len + array->zero_terminated;
+ if (want_len > array->elt_capacity)
+ {
+ gsize want_alloc = g_nearest_pow (g_array_elt_len (array, want_len));
+ want_alloc = MAX (want_alloc, MIN_ARRAY_SIZE);
+
+ array->data = g_realloc (array->data, want_alloc);
+
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ memset (g_array_elt_pos (array, array->elt_capacity), 0,
+ g_array_elt_len (array, want_len - array->elt_capacity));
+
+ array->elt_capacity = MIN (want_alloc / array->elt_size, G_MAXUINT);
+ }
+}
+
+/**
+ * SECTION:arrays_pointer
+ * @title: Pointer Arrays
+ * @short_description: arrays of pointers to any type of data, which
+ * grow automatically as new elements are added
+ *
+ * Pointer Arrays are similar to Arrays but are used only for storing
+ * pointers.
+ *
+ * If you remove elements from the array, elements at the end of the
+ * array are moved into the space previously occupied by the removed
+ * element. This means that you should not rely on the index of particular
+ * elements remaining the same. You should also be careful when deleting
+ * elements while iterating over the array.
+ *
+ * To create a pointer array, use g_ptr_array_new().
+ *
+ * To add elements to a pointer array, use g_ptr_array_add().
+ *
+ * To remove elements from a pointer array, use g_ptr_array_remove(),
+ * g_ptr_array_remove_index() or g_ptr_array_remove_index_fast().
+ *
+ * To access an element of a pointer array, use g_ptr_array_index().
+ *
+ * To set the size of a pointer array, use g_ptr_array_set_size().
+ *
+ * To free a pointer array, use g_ptr_array_free().
+ *
+ * An example using a #GPtrArray:
+ * |[
+ * GPtrArray *array;
+ * gchar *string1 = "one";
+ * gchar *string2 = "two";
+ * gchar *string3 = "three";
+ *
+ * array = g_ptr_array_new ();
+ * g_ptr_array_add (array, (gpointer) string1);
+ * g_ptr_array_add (array, (gpointer) string2);
+ * g_ptr_array_add (array, (gpointer) string3);
+ *
+ * if (g_ptr_array_index (array, 0) != (gpointer) string1)
+ * g_print ("ERROR: got %p instead of %p\n",
+ * g_ptr_array_index (array, 0), string1);
+ *
+ * g_ptr_array_free (array, TRUE);
+ * ]|
+ */
+
+typedef struct _GRealPtrArray GRealPtrArray;
+
+/**
+ * GPtrArray:
+ * @pdata: points to the array of pointers, which may be moved when the
+ * array grows
+ * @len: number of pointers in the array
+ *
+ * Contains the public fields of a pointer array.
+ */
+struct _GRealPtrArray
+{
+ gpointer *pdata;
+ guint len;
+ guint alloc;
+ gatomicrefcount ref_count;
+ GDestroyNotify element_free_func;
+};
+
+/**
+ * g_ptr_array_index:
+ * @array: a #GPtrArray
+ * @index_: the index of the pointer to return
+ *
+ * Returns the pointer at the given index of the pointer array.
+ *
+ * This does not perform bounds checking on the given @index_,
+ * so you are responsible for checking it against the array length.
+ *
+ * Returns: the pointer at the given index
+ */
+
+static void g_ptr_array_maybe_expand (GRealPtrArray *array,
+ guint len);
+
+static GPtrArray *
+ptr_array_new (guint reserved_size,
+ GDestroyNotify element_free_func)
+{
+ GRealPtrArray *array;
+
+ array = g_slice_new (GRealPtrArray);
+
+ array->pdata = NULL;
+ array->len = 0;
+ array->alloc = 0;
+ array->element_free_func = element_free_func;
+
+ g_atomic_ref_count_init (&array->ref_count);
+
+ if (reserved_size != 0)
+ g_ptr_array_maybe_expand (array, reserved_size);
+
+ return (GPtrArray *) array;
+}
+
+/**
+ * g_ptr_array_new:
+ *
+ * Creates a new #GPtrArray with a reference count of 1.
+ *
+ * Returns: the new #GPtrArray
+ */
+GPtrArray*
+g_ptr_array_new (void)
+{
+ return ptr_array_new (0, NULL);
+}
+
+/**
+ * g_ptr_array_steal:
+ * @array: a #GPtrArray.
+ * @len: (optional) (out): pointer to retrieve the number of
+ * elements of the original array
+ *
+ * Frees the data in the array and resets the size to zero, while
+ * the underlying array is preserved for use elsewhere and returned
+ * to the caller.
+ *
+ * Even if set, the #GDestroyNotify function will never be called
+ * on the current contents of the array and the caller is
+ * responsible for freeing the array elements.
+ *
+ * An example of use:
+ * |[
+ * g_autoptr(GPtrArray) chunk_buffer = g_ptr_array_new_with_free_func (g_bytes_unref);
+ *
+ * // Some part of your application appends a number of chunks to the pointer array.
+ * g_ptr_array_add (chunk_buffer, g_bytes_new_static ("hello", 5));
+ * g_ptr_array_add (chunk_buffer, g_bytes_new_static ("world", 5));
+ *
+ * …
+ *
+ * // Periodically, the chunks need to be sent as an array-and-length to some
+ * // other part of the program.
+ * GBytes **chunks;
+ * gsize n_chunks;
+ *
+ * chunks = g_ptr_array_steal (chunk_buffer, &n_chunks);
+ * for (gsize i = 0; i < n_chunks; i++)
+ * {
+ * // Do something with each chunk here, and then free them, since
+ * // g_ptr_array_steal() transfers ownership of all the elements and the
+ * // array to the caller.
+ * …
+ *
+ * g_bytes_unref (chunks[i]);
+ * }
+ *
+ * g_free (chunks);
+ *
+ * // After calling g_ptr_array_steal(), the pointer array can be reused for the
+ * // next set of chunks.
+ * g_assert (chunk_buffer->len == 0);
+ * ]|
+ *
+ * Returns: (transfer full): the element data, which should be
+ * freed using g_free().
+ *
+ * Since: 2.64
+ */
+gpointer *
+g_ptr_array_steal (GPtrArray *array,
+ gsize *len)
+{
+ GRealPtrArray *rarray;
+ gpointer *segment;
+
+ g_return_val_if_fail (array != NULL, NULL);
+
+ rarray = (GRealPtrArray *) array;
+ segment = (gpointer *) rarray->pdata;
+
+ if (len != NULL)
+ *len = rarray->len;
+
+ rarray->pdata = NULL;
+ rarray->len = 0;
+ rarray->alloc = 0;
+ return segment;
+}
+
+/**
+ * g_ptr_array_copy:
+ * @array: #GPtrArray to duplicate
+ * @func: (nullable): a copy function used to copy every element in the array
+ * @user_data: user data passed to the copy function @func, or %NULL
+ *
+ * Makes a full (deep) copy of a #GPtrArray.
+ *
+ * @func, as a #GCopyFunc, takes two arguments, the data to be copied
+ * and a @user_data pointer. On common processor architectures, it's safe to
+ * pass %NULL as @user_data if the copy function takes only one argument. You
+ * may get compiler warnings from this though if compiling with GCC’s
+ * `-Wcast-function-type` warning.
+ *
+ * If @func is %NULL, then only the pointers (and not what they are
+ * pointing to) are copied to the new #GPtrArray.
+ *
+ * The copy of @array will have the same #GDestroyNotify for its elements as
+ * @array.
+ *
+ * Returns: (transfer full): a deep copy of the initial #GPtrArray.
+ *
+ * Since: 2.62
+ **/
+GPtrArray *
+g_ptr_array_copy (GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data)
+{
+ GPtrArray *new_array;
+
+ g_return_val_if_fail (array != NULL, NULL);
+
+ new_array = ptr_array_new (array->len,
+ ((GRealPtrArray *) array)->element_free_func);
+
+ if (func != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < array->len; i++)
+ new_array->pdata[i] = func (array->pdata[i], user_data);
+ }
+ else if (array->len > 0)
+ {
+ memcpy (new_array->pdata, array->pdata,
+ array->len * sizeof (*array->pdata));
+ }
+
+ new_array->len = array->len;
+
+ return new_array;
+}
+
+/**
+ * g_ptr_array_sized_new:
+ * @reserved_size: number of pointers preallocated
+ *
+ * Creates a new #GPtrArray with @reserved_size pointers preallocated
+ * and a reference count of 1. This avoids frequent reallocation, if
+ * you are going to add many pointers to the array. Note however that
+ * the size of the array is still 0.
+ *
+ * Returns: the new #GPtrArray
+ */
+GPtrArray*
+g_ptr_array_sized_new (guint reserved_size)
+{
+ return ptr_array_new (reserved_size, NULL);
+}
+
+/**
+ * g_array_copy:
+ * @array: A #GArray.
+ *
+ * Create a shallow copy of a #GArray. If the array elements consist of
+ * pointers to data, the pointers are copied but the actual data is not.
+ *
+ * Returns: (transfer container): A copy of @array.
+ *
+ * Since: 2.62
+ **/
+GArray *
+g_array_copy (GArray *array)
+{
+ GRealArray *rarray = (GRealArray *) array;
+ GRealArray *new_rarray;
+
+ g_return_val_if_fail (rarray != NULL, NULL);
+
+ new_rarray =
+ (GRealArray *) g_array_sized_new (rarray->zero_terminated, rarray->clear,
+ rarray->elt_size, rarray->elt_capacity);
+ new_rarray->len = rarray->len;
+ if (rarray->len > 0)
+ memcpy (new_rarray->data, rarray->data, rarray->len * rarray->elt_size);
+
+ g_array_zero_terminate (new_rarray);
+
+ return (GArray *) new_rarray;
+}
+
+/**
+ * g_ptr_array_new_with_free_func:
+ * @element_free_func: (nullable): A function to free elements with
+ * destroy @array or %NULL
+ *
+ * Creates a new #GPtrArray with a reference count of 1 and use
+ * @element_free_func for freeing each element when the array is destroyed
+ * either via g_ptr_array_unref(), when g_ptr_array_free() is called with
+ * @free_segment set to %TRUE or when removing elements.
+ *
+ * Returns: A new #GPtrArray
+ *
+ * Since: 2.22
+ */
+GPtrArray*
+g_ptr_array_new_with_free_func (GDestroyNotify element_free_func)
+{
+ return ptr_array_new (0, element_free_func);
+}
+
+/**
+ * g_ptr_array_new_full:
+ * @reserved_size: number of pointers preallocated
+ * @element_free_func: (nullable): A function to free elements with
+ * destroy @array or %NULL
+ *
+ * Creates a new #GPtrArray with @reserved_size pointers preallocated
+ * and a reference count of 1. This avoids frequent reallocation, if
+ * you are going to add many pointers to the array. Note however that
+ * the size of the array is still 0. It also set @element_free_func
+ * for freeing each element when the array is destroyed either via
+ * g_ptr_array_unref(), when g_ptr_array_free() is called with
+ * @free_segment set to %TRUE or when removing elements.
+ *
+ * Returns: A new #GPtrArray
+ *
+ * Since: 2.30
+ */
+GPtrArray*
+g_ptr_array_new_full (guint reserved_size,
+ GDestroyNotify element_free_func)
+{
+ return ptr_array_new (reserved_size, element_free_func);
+}
+
+/**
+ * g_ptr_array_set_free_func:
+ * @array: A #GPtrArray
+ * @element_free_func: (nullable): A function to free elements with
+ * destroy @array or %NULL
+ *
+ * Sets a function for freeing each element when @array is destroyed
+ * either via g_ptr_array_unref(), when g_ptr_array_free() is called
+ * with @free_segment set to %TRUE or when removing elements.
+ *
+ * Since: 2.22
+ */
+void
+g_ptr_array_set_free_func (GPtrArray *array,
+ GDestroyNotify element_free_func)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+
+ g_return_if_fail (array);
+
+ rarray->element_free_func = element_free_func;
+}
+
+/**
+ * g_ptr_array_ref:
+ * @array: a #GPtrArray
+ *
+ * Atomically increments the reference count of @array by one.
+ * This function is thread-safe and may be called from any thread.
+ *
+ * Returns: The passed in #GPtrArray
+ *
+ * Since: 2.22
+ */
+GPtrArray*
+g_ptr_array_ref (GPtrArray *array)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+
+ g_return_val_if_fail (array, NULL);
+
+ g_atomic_ref_count_inc (&rarray->ref_count);
+
+ return array;
+}
+
+static gpointer *ptr_array_free (GPtrArray *, ArrayFreeFlags);
+
+/**
+ * g_ptr_array_unref:
+ * @array: A #GPtrArray
+ *
+ * Atomically decrements the reference count of @array by one. If the
+ * reference count drops to 0, the effect is the same as calling
+ * g_ptr_array_free() with @free_segment set to %TRUE. This function
+ * is thread-safe and may be called from any thread.
+ *
+ * Since: 2.22
+ */
+void
+g_ptr_array_unref (GPtrArray *array)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+
+ g_return_if_fail (array);
+
+ if (g_atomic_ref_count_dec (&rarray->ref_count))
+ ptr_array_free (array, FREE_SEGMENT);
+}
+
+/**
+ * g_ptr_array_free:
+ * @array: a #GPtrArray
+ * @free_seg: if %TRUE the actual pointer array is freed as well
+ *
+ * Frees the memory allocated for the #GPtrArray. If @free_seg is %TRUE
+ * it frees the memory block holding the elements as well. Pass %FALSE
+ * if you want to free the #GPtrArray wrapper but preserve the
+ * underlying array for use elsewhere. If the reference count of @array
+ * is greater than one, the #GPtrArray wrapper is preserved but the
+ * size of @array will be set to zero.
+ *
+ * If array contents point to dynamically-allocated memory, they should
+ * be freed separately if @free_seg is %TRUE and no #GDestroyNotify
+ * function has been set for @array.
+ *
+ * This function is not thread-safe. If using a #GPtrArray from multiple
+ * threads, use only the atomic g_ptr_array_ref() and g_ptr_array_unref()
+ * functions.
+ *
+ * Returns: (transfer full) (nullable): the pointer array if @free_seg is
+ * %FALSE, otherwise %NULL. The pointer array should be freed using g_free().
+ */
+gpointer*
+g_ptr_array_free (GPtrArray *array,
+ gboolean free_segment)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+ ArrayFreeFlags flags;
+
+ g_return_val_if_fail (rarray, NULL);
+
+ flags = (free_segment ? FREE_SEGMENT : 0);
+
+ /* if others are holding a reference, preserve the wrapper but
+ * do free/return the data
+ */
+ if (!g_atomic_ref_count_dec (&rarray->ref_count))
+ flags |= PRESERVE_WRAPPER;
+
+ return ptr_array_free (array, flags);
+}
+
+static gpointer *
+ptr_array_free (GPtrArray *array,
+ ArrayFreeFlags flags)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+ gpointer *segment;
+
+ if (flags & FREE_SEGMENT)
+ {
+ /* Data here is stolen and freed manually. It is an
+ * error to attempt to access the array data (including
+ * mutating the array bounds) during destruction).
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=769064
+ */
+ gpointer *stolen_pdata = g_steal_pointer (&rarray->pdata);
+ if (rarray->element_free_func != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < rarray->len; ++i)
+ rarray->element_free_func (stolen_pdata[i]);
+ }
+
+ g_free (stolen_pdata);
+ segment = NULL;
+ }
+ else
+ segment = rarray->pdata;
+
+ if (flags & PRESERVE_WRAPPER)
+ {
+ rarray->pdata = NULL;
+ rarray->len = 0;
+ rarray->alloc = 0;
+ }
+ else
+ {
+ g_slice_free1 (sizeof (GRealPtrArray), rarray);
+ }
+
+ return segment;
+}
+
+static void
+g_ptr_array_maybe_expand (GRealPtrArray *array,
+ guint len)
+{
+ guint max_len;
+
+ /* The maximum array length is derived from following constraints:
+ * - The number of bytes must fit into a gsize / 2.
+ * - The number of elements must fit into guint.
+ */
+ max_len = MIN (G_MAXSIZE / 2 / sizeof (gpointer), G_MAXUINT);
+
+ /* Detect potential overflow */
+ if G_UNLIKELY ((max_len - array->len) < len)
+ g_error ("adding %u to array would overflow", len);
+
+ if ((array->len + len) > array->alloc)
+ {
+ guint old_alloc = array->alloc;
+ gsize want_alloc = g_nearest_pow (sizeof (gpointer) * (array->len + len));
+ want_alloc = MAX (want_alloc, MIN_ARRAY_SIZE);
+ array->alloc = MIN (want_alloc / sizeof (gpointer), G_MAXUINT);
+ array->pdata = g_realloc (array->pdata, want_alloc);
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ for ( ; old_alloc < array->alloc; old_alloc++)
+ array->pdata [old_alloc] = NULL;
+ }
+}
+
+/**
+ * g_ptr_array_set_size:
+ * @array: a #GPtrArray
+ * @length: the new length of the pointer array
+ *
+ * Sets the size of the array. When making the array larger,
+ * newly-added elements will be set to %NULL. When making it smaller,
+ * if @array has a non-%NULL #GDestroyNotify function then it will be
+ * called for the removed elements.
+ */
+void
+g_ptr_array_set_size (GPtrArray *array,
+ gint length)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+ guint length_unsigned;
+
+ g_return_if_fail (rarray);
+ g_return_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL));
+ g_return_if_fail (length >= 0);
+
+ length_unsigned = (guint) length;
+
+ if (length_unsigned > rarray->len)
+ {
+ guint i;
+ g_ptr_array_maybe_expand (rarray, (length_unsigned - rarray->len));
+ /* This is not
+ * memset (array->pdata + array->len, 0,
+ * sizeof (gpointer) * (length_unsigned - array->len));
+ * to make it really portable. Remember (void*)NULL needn't be
+ * bitwise zero. It of course is silly not to use memset (..,0,..).
+ */
+ for (i = rarray->len; i < length_unsigned; i++)
+ rarray->pdata[i] = NULL;
+ }
+ else if (length_unsigned < rarray->len)
+ g_ptr_array_remove_range (array, length_unsigned, rarray->len - length_unsigned);
+
+ rarray->len = length_unsigned;
+}
+
+static gpointer
+ptr_array_remove_index (GPtrArray *array,
+ guint index_,
+ gboolean fast,
+ gboolean free_element)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *) array;
+ gpointer result;
+
+ g_return_val_if_fail (rarray, NULL);
+ g_return_val_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL), NULL);
+
+ g_return_val_if_fail (index_ < rarray->len, NULL);
+
+ result = rarray->pdata[index_];
+
+ if (rarray->element_free_func != NULL && free_element)
+ rarray->element_free_func (rarray->pdata[index_]);
+
+ if (index_ != rarray->len - 1 && !fast)
+ memmove (rarray->pdata + index_, rarray->pdata + index_ + 1,
+ sizeof (gpointer) * (rarray->len - index_ - 1));
+ else if (index_ != rarray->len - 1)
+ rarray->pdata[index_] = rarray->pdata[rarray->len - 1];
+
+ rarray->len -= 1;
+
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ rarray->pdata[rarray->len] = NULL;
+
+ return result;
+}
+
+/**
+ * g_ptr_array_remove_index:
+ * @array: a #GPtrArray
+ * @index_: the index of the pointer to remove
+ *
+ * Removes the pointer at the given index from the pointer array.
+ * The following elements are moved down one place. If @array has
+ * a non-%NULL #GDestroyNotify function it is called for the removed
+ * element. If so, the return value from this function will potentially point
+ * to freed memory (depending on the #GDestroyNotify implementation).
+ *
+ * Returns: (nullable): the pointer which was removed
+ */
+gpointer
+g_ptr_array_remove_index (GPtrArray *array,
+ guint index_)
+{
+ return ptr_array_remove_index (array, index_, FALSE, TRUE);
+}
+
+/**
+ * g_ptr_array_remove_index_fast:
+ * @array: a #GPtrArray
+ * @index_: the index of the pointer to remove
+ *
+ * Removes the pointer at the given index from the pointer array.
+ * The last element in the array is used to fill in the space, so
+ * this function does not preserve the order of the array. But it
+ * is faster than g_ptr_array_remove_index(). If @array has a non-%NULL
+ * #GDestroyNotify function it is called for the removed element. If so, the
+ * return value from this function will potentially point to freed memory
+ * (depending on the #GDestroyNotify implementation).
+ *
+ * Returns: (nullable): the pointer which was removed
+ */
+gpointer
+g_ptr_array_remove_index_fast (GPtrArray *array,
+ guint index_)
+{
+ return ptr_array_remove_index (array, index_, TRUE, TRUE);
+}
+
+/**
+ * g_ptr_array_steal_index:
+ * @array: a #GPtrArray
+ * @index_: the index of the pointer to steal
+ *
+ * Removes the pointer at the given index from the pointer array.
+ * The following elements are moved down one place. The #GDestroyNotify for
+ * @array is *not* called on the removed element; ownership is transferred to
+ * the caller of this function.
+ *
+ * Returns: (transfer full) (nullable): the pointer which was removed
+ * Since: 2.58
+ */
+gpointer
+g_ptr_array_steal_index (GPtrArray *array,
+ guint index_)
+{
+ return ptr_array_remove_index (array, index_, FALSE, FALSE);
+}
+
+/**
+ * g_ptr_array_steal_index_fast:
+ * @array: a #GPtrArray
+ * @index_: the index of the pointer to steal
+ *
+ * Removes the pointer at the given index from the pointer array.
+ * The last element in the array is used to fill in the space, so
+ * this function does not preserve the order of the array. But it
+ * is faster than g_ptr_array_steal_index(). The #GDestroyNotify for @array is
+ * *not* called on the removed element; ownership is transferred to the caller
+ * of this function.
+ *
+ * Returns: (transfer full) (nullable): the pointer which was removed
+ * Since: 2.58
+ */
+gpointer
+g_ptr_array_steal_index_fast (GPtrArray *array,
+ guint index_)
+{
+ return ptr_array_remove_index (array, index_, TRUE, FALSE);
+}
+
+/**
+ * g_ptr_array_remove_range:
+ * @array: a @GPtrArray
+ * @index_: the index of the first pointer to remove
+ * @length: the number of pointers to remove
+ *
+ * Removes the given number of pointers starting at the given index
+ * from a #GPtrArray. The following elements are moved to close the
+ * gap. If @array has a non-%NULL #GDestroyNotify function it is
+ * called for the removed elements.
+ *
+ * Returns: the @array
+ *
+ * Since: 2.4
+ */
+GPtrArray*
+g_ptr_array_remove_range (GPtrArray *array,
+ guint index_,
+ guint length)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+ guint i;
+
+ g_return_val_if_fail (rarray != NULL, NULL);
+ g_return_val_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL), NULL);
+ g_return_val_if_fail (index_ <= rarray->len, NULL);
+ g_return_val_if_fail (index_ + length <= rarray->len, NULL);
+
+ if (rarray->element_free_func != NULL)
+ {
+ for (i = index_; i < index_ + length; i++)
+ rarray->element_free_func (rarray->pdata[i]);
+ }
+
+ if (index_ + length != rarray->len)
+ {
+ memmove (&rarray->pdata[index_],
+ &rarray->pdata[index_ + length],
+ (rarray->len - (index_ + length)) * sizeof (gpointer));
+ }
+
+ rarray->len -= length;
+ if (G_UNLIKELY (g_mem_gc_friendly))
+ {
+ for (i = 0; i < length; i++)
+ rarray->pdata[rarray->len + i] = NULL;
+ }
+
+ return array;
+}
+
+/**
+ * g_ptr_array_remove:
+ * @array: a #GPtrArray
+ * @data: the pointer to remove
+ *
+ * Removes the first occurrence of the given pointer from the pointer
+ * array. The following elements are moved down one place. If @array
+ * has a non-%NULL #GDestroyNotify function it is called for the
+ * removed element.
+ *
+ * It returns %TRUE if the pointer was removed, or %FALSE if the
+ * pointer was not found.
+ *
+ * Returns: %TRUE if the pointer is removed, %FALSE if the pointer
+ * is not found in the array
+ */
+gboolean
+g_ptr_array_remove (GPtrArray *array,
+ gpointer data)
+{
+ guint i;
+
+ g_return_val_if_fail (array, FALSE);
+ g_return_val_if_fail (array->len == 0 || (array->len != 0 && array->pdata != NULL), FALSE);
+
+ for (i = 0; i < array->len; i += 1)
+ {
+ if (array->pdata[i] == data)
+ {
+ g_ptr_array_remove_index (array, i);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * g_ptr_array_remove_fast:
+ * @array: a #GPtrArray
+ * @data: the pointer to remove
+ *
+ * Removes the first occurrence of the given pointer from the pointer
+ * array. The last element in the array is used to fill in the space,
+ * so this function does not preserve the order of the array. But it
+ * is faster than g_ptr_array_remove(). If @array has a non-%NULL
+ * #GDestroyNotify function it is called for the removed element.
+ *
+ * It returns %TRUE if the pointer was removed, or %FALSE if the
+ * pointer was not found.
+ *
+ * Returns: %TRUE if the pointer was found in the array
+ */
+gboolean
+g_ptr_array_remove_fast (GPtrArray *array,
+ gpointer data)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+ guint i;
+
+ g_return_val_if_fail (rarray, FALSE);
+ g_return_val_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL), FALSE);
+
+ for (i = 0; i < rarray->len; i += 1)
+ {
+ if (rarray->pdata[i] == data)
+ {
+ g_ptr_array_remove_index_fast (array, i);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * g_ptr_array_add:
+ * @array: a #GPtrArray
+ * @data: the pointer to add
+ *
+ * Adds a pointer to the end of the pointer array. The array will grow
+ * in size automatically if necessary.
+ */
+void
+g_ptr_array_add (GPtrArray *array,
+ gpointer data)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+
+ g_return_if_fail (rarray);
+ g_return_if_fail (rarray->len == 0 || (rarray->len != 0 && rarray->pdata != NULL));
+
+ g_ptr_array_maybe_expand (rarray, 1);
+
+ rarray->pdata[rarray->len++] = data;
+}
+
+/**
+ * g_ptr_array_extend:
+ * @array_to_extend: a #GPtrArray.
+ * @array: (transfer none): a #GPtrArray to add to the end of @array_to_extend.
+ * @func: (nullable): a copy function used to copy every element in the array
+ * @user_data: user data passed to the copy function @func, or %NULL
+ *
+ * Adds all pointers of @array to the end of the array @array_to_extend.
+ * The array will grow in size automatically if needed. @array_to_extend is
+ * modified in-place.
+ *
+ * @func, as a #GCopyFunc, takes two arguments, the data to be copied
+ * and a @user_data pointer. On common processor architectures, it's safe to
+ * pass %NULL as @user_data if the copy function takes only one argument. You
+ * may get compiler warnings from this though if compiling with GCC’s
+ * `-Wcast-function-type` warning.
+ *
+ * If @func is %NULL, then only the pointers (and not what they are
+ * pointing to) are copied to the new #GPtrArray.
+ *
+ * Since: 2.62
+ **/
+void
+g_ptr_array_extend (GPtrArray *array_to_extend,
+ GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data)
+{
+ GRealPtrArray *rarray_to_extend = (GRealPtrArray *) array_to_extend;
+
+ g_return_if_fail (array_to_extend != NULL);
+ g_return_if_fail (array != NULL);
+
+ g_ptr_array_maybe_expand (rarray_to_extend, array->len);
+
+ if (func != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < array->len; i++)
+ rarray_to_extend->pdata[i + rarray_to_extend->len] =
+ func (array->pdata[i], user_data);
+ }
+ else if (array->len > 0)
+ {
+ memcpy (rarray_to_extend->pdata + rarray_to_extend->len, array->pdata,
+ array->len * sizeof (*array->pdata));
+ }
+
+ rarray_to_extend->len += array->len;
+}
+
+/**
+ * g_ptr_array_extend_and_steal:
+ * @array_to_extend: (transfer none): a #GPtrArray.
+ * @array: (transfer container): a #GPtrArray to add to the end of
+ * @array_to_extend.
+ *
+ * Adds all the pointers in @array to the end of @array_to_extend, transferring
+ * ownership of each element from @array to @array_to_extend and modifying
+ * @array_to_extend in-place. @array is then freed.
+ *
+ * As with g_ptr_array_free(), @array will be destroyed if its reference count
+ * is 1. If its reference count is higher, it will be decremented and the
+ * length of @array set to zero.
+ *
+ * Since: 2.62
+ **/
+void
+g_ptr_array_extend_and_steal (GPtrArray *array_to_extend,
+ GPtrArray *array)
+{
+ gpointer *pdata;
+
+ g_ptr_array_extend (array_to_extend, array, NULL, NULL);
+
+ /* Get rid of @array without triggering the GDestroyNotify attached
+ * to the elements moved from @array to @array_to_extend. */
+ pdata = g_steal_pointer (&array->pdata);
+ array->len = 0;
+ ((GRealPtrArray *) array)->alloc = 0;
+ g_ptr_array_unref (array);
+ g_free (pdata);
+}
+
+/**
+ * g_ptr_array_insert:
+ * @array: a #GPtrArray
+ * @index_: the index to place the new element at, or -1 to append
+ * @data: the pointer to add.
+ *
+ * Inserts an element into the pointer array at the given index. The
+ * array will grow in size automatically if necessary.
+ *
+ * Since: 2.40
+ */
+void
+g_ptr_array_insert (GPtrArray *array,
+ gint index_,
+ gpointer data)
+{
+ GRealPtrArray *rarray = (GRealPtrArray *)array;
+
+ g_return_if_fail (rarray);
+ g_return_if_fail (index_ >= -1);
+ g_return_if_fail (index_ <= (gint)rarray->len);
+
+ g_ptr_array_maybe_expand (rarray, 1);
+
+ if (index_ < 0)
+ index_ = rarray->len;
+
+ if ((guint) index_ < rarray->len)
+ memmove (&(rarray->pdata[index_ + 1]),
+ &(rarray->pdata[index_]),
+ (rarray->len - index_) * sizeof (gpointer));
+
+ rarray->len++;
+ rarray->pdata[index_] = data;
+}
+
+/* Please keep this doc-comment in sync with pointer_array_sort_example()
+ * in glib/tests/array-test.c */
+/**
+ * g_ptr_array_sort:
+ * @array: a #GPtrArray
+ * @compare_func: comparison function
+ *
+ * Sorts the array, using @compare_func which should be a qsort()-style
+ * comparison function (returns less than zero for first arg is less
+ * than second arg, zero for equal, greater than zero if irst arg is
+ * greater than second arg).
+ *
+ * Note that the comparison function for g_ptr_array_sort() doesn't
+ * take the pointers from the array as arguments, it takes pointers to
+ * the pointers in the array. Here is a full example of usage:
+ *
+ * |[
+ * typedef struct
+ * {
+ * gchar *name;
+ * gint size;
+ * } FileListEntry;
+ *
+ * static gint
+ * sort_filelist (gconstpointer a, gconstpointer b)
+ * {
+ * const FileListEntry *entry1 = *((FileListEntry **) a);
+ * const FileListEntry *entry2 = *((FileListEntry **) b);
+ *
+ * return g_ascii_strcasecmp (entry1->name, entry2->name);
+ * }
+ *
+ * …
+ * g_autoptr (GPtrArray) file_list = NULL;
+ *
+ * // initialize file_list array and load with many FileListEntry entries
+ * ...
+ * // now sort it with
+ * g_ptr_array_sort (file_list, sort_filelist);
+ * ]|
+ *
+ * This is guaranteed to be a stable sort since version 2.32.
+ */
+void
+g_ptr_array_sort (GPtrArray *array,
+ GCompareFunc compare_func)
+{
+ g_return_if_fail (array != NULL);
+
+ /* Don't use qsort as we want a guaranteed stable sort */
+ if (array->len > 0)
+ g_qsort_with_data (array->pdata,
+ array->len,
+ sizeof (gpointer),
+ (GCompareDataFunc)compare_func,
+ NULL);
+}
+
+/* Please keep this doc-comment in sync with
+ * pointer_array_sort_with_data_example() in glib/tests/array-test.c */
+/**
+ * g_ptr_array_sort_with_data:
+ * @array: a #GPtrArray
+ * @compare_func: comparison function
+ * @user_data: data to pass to @compare_func
+ *
+ * Like g_ptr_array_sort(), but the comparison function has an extra
+ * user data argument.
+ *
+ * Note that the comparison function for g_ptr_array_sort_with_data()
+ * doesn't take the pointers from the array as arguments, it takes
+ * pointers to the pointers in the array. Here is a full example of use:
+ *
+ * |[
+ * typedef enum { SORT_NAME, SORT_SIZE } SortMode;
+ *
+ * typedef struct
+ * {
+ * gchar *name;
+ * gint size;
+ * } FileListEntry;
+ *
+ * static gint
+ * sort_filelist (gconstpointer a, gconstpointer b, gpointer user_data)
+ * {
+ * gint order;
+ * const SortMode sort_mode = GPOINTER_TO_INT (user_data);
+ * const FileListEntry *entry1 = *((FileListEntry **) a);
+ * const FileListEntry *entry2 = *((FileListEntry **) b);
+ *
+ * switch (sort_mode)
+ * {
+ * case SORT_NAME:
+ * order = g_ascii_strcasecmp (entry1->name, entry2->name);
+ * break;
+ * case SORT_SIZE:
+ * order = entry1->size - entry2->size;
+ * break;
+ * default:
+ * order = 0;
+ * break;
+ * }
+ * return order;
+ * }
+ *
+ * ...
+ * g_autoptr (GPtrArray) file_list = NULL;
+ * SortMode sort_mode;
+ *
+ * // initialize file_list array and load with many FileListEntry entries
+ * ...
+ * // now sort it with
+ * sort_mode = SORT_NAME;
+ * g_ptr_array_sort_with_data (file_list,
+ * sort_filelist,
+ * GINT_TO_POINTER (sort_mode));
+ * ]|
+ *
+ * This is guaranteed to be a stable sort since version 2.32.
+ */
+void
+g_ptr_array_sort_with_data (GPtrArray *array,
+ GCompareDataFunc compare_func,
+ gpointer user_data)
+{
+ g_return_if_fail (array != NULL);
+
+ if (array->len > 0)
+ g_qsort_with_data (array->pdata,
+ array->len,
+ sizeof (gpointer),
+ compare_func,
+ user_data);
+}
+
+/**
+ * g_ptr_array_foreach:
+ * @array: a #GPtrArray
+ * @func: the function to call for each array element
+ * @user_data: user data to pass to the function
+ *
+ * Calls a function for each element of a #GPtrArray. @func must not
+ * add elements to or remove elements from the array.
+ *
+ * Since: 2.4
+ */
+void
+g_ptr_array_foreach (GPtrArray *array,
+ GFunc func,
+ gpointer user_data)
+{
+ guint i;
+
+ g_return_if_fail (array);
+
+ for (i = 0; i < array->len; i++)
+ (*func) (array->pdata[i], user_data);
+}
+
+/**
+ * g_ptr_array_find: (skip)
+ * @haystack: pointer array to be searched
+ * @needle: pointer to look for
+ * @index_: (optional) (out): return location for the index of
+ * the element, if found
+ *
+ * Checks whether @needle exists in @haystack. If the element is found, %TRUE is
+ * returned and the element’s index is returned in @index_ (if non-%NULL).
+ * Otherwise, %FALSE is returned and @index_ is undefined. If @needle exists
+ * multiple times in @haystack, the index of the first instance is returned.
+ *
+ * This does pointer comparisons only. If you want to use more complex equality
+ * checks, such as string comparisons, use g_ptr_array_find_with_equal_func().
+ *
+ * Returns: %TRUE if @needle is one of the elements of @haystack
+ * Since: 2.54
+ */
+gboolean
+g_ptr_array_find (GPtrArray *haystack,
+ gconstpointer needle,
+ guint *index_)
+{
+ return g_ptr_array_find_with_equal_func (haystack, needle, NULL, index_);
+}
+
+/**
+ * g_ptr_array_find_with_equal_func: (skip)
+ * @haystack: pointer array to be searched
+ * @needle: pointer to look for
+ * @equal_func: (nullable): the function to call for each element, which should
+ * return %TRUE when the desired element is found; or %NULL to use pointer
+ * equality
+ * @index_: (optional) (out): return location for the index of
+ * the element, if found
+ *
+ * Checks whether @needle exists in @haystack, using the given @equal_func.
+ * If the element is found, %TRUE is returned and the element’s index is
+ * returned in @index_ (if non-%NULL). Otherwise, %FALSE is returned and @index_
+ * is undefined. If @needle exists multiple times in @haystack, the index of
+ * the first instance is returned.
+ *
+ * @equal_func is called with the element from the array as its first parameter,
+ * and @needle as its second parameter. If @equal_func is %NULL, pointer
+ * equality is used.
+ *
+ * Returns: %TRUE if @needle is one of the elements of @haystack
+ * Since: 2.54
+ */
+gboolean
+g_ptr_array_find_with_equal_func (GPtrArray *haystack,
+ gconstpointer needle,
+ GEqualFunc equal_func,
+ guint *index_)
+{
+ guint i;
+
+ g_return_val_if_fail (haystack != NULL, FALSE);
+
+ if (equal_func == NULL)
+ equal_func = g_direct_equal;
+
+ for (i = 0; i < haystack->len; i++)
+ {
+ if (equal_func (g_ptr_array_index (haystack, i), needle))
+ {
+ if (index_ != NULL)
+ *index_ = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * SECTION:arrays_byte
+ * @title: Byte Arrays
+ * @short_description: arrays of bytes
+ *
+ * #GByteArray is a mutable array of bytes based on #GArray, to provide arrays
+ * of bytes which grow automatically as elements are added.
+ *
+ * To create a new #GByteArray use g_byte_array_new(). To add elements to a
+ * #GByteArray, use g_byte_array_append(), and g_byte_array_prepend().
+ *
+ * To set the size of a #GByteArray, use g_byte_array_set_size().
+ *
+ * To free a #GByteArray, use g_byte_array_free().
+ *
+ * An example for using a #GByteArray:
+ * |[
+ * GByteArray *gbarray;
+ * gint i;
+ *
+ * gbarray = g_byte_array_new ();
+ * for (i = 0; i < 10000; i++)
+ * g_byte_array_append (gbarray, (guint8*) "abcd", 4);
+ *
+ * for (i = 0; i < 10000; i++)
+ * {
+ * g_assert (gbarray->data[4*i] == 'a');
+ * g_assert (gbarray->data[4*i+1] == 'b');
+ * g_assert (gbarray->data[4*i+2] == 'c');
+ * g_assert (gbarray->data[4*i+3] == 'd');
+ * }
+ *
+ * g_byte_array_free (gbarray, TRUE);
+ * ]|
+ *
+ * See #GBytes if you are interested in an immutable object representing a
+ * sequence of bytes.
+ */
+
+/**
+ * GByteArray:
+ * @data: a pointer to the element data. The data may be moved as
+ * elements are added to the #GByteArray
+ * @len: the number of elements in the #GByteArray
+ *
+ * Contains the public fields of a GByteArray.
+ */
+
+/**
+ * g_byte_array_new:
+ *
+ * Creates a new #GByteArray with a reference count of 1.
+ *
+ * Returns: (transfer full): the new #GByteArray
+ */
+GByteArray*
+g_byte_array_new (void)
+{
+ return (GByteArray *)g_array_sized_new (FALSE, FALSE, 1, 0);
+}
+
+/**
+ * g_byte_array_steal:
+ * @array: a #GByteArray.
+ * @len: (optional) (out): pointer to retrieve the number of
+ * elements of the original array
+ *
+ * Frees the data in the array and resets the size to zero, while
+ * the underlying array is preserved for use elsewhere and returned
+ * to the caller.
+ *
+ * Returns: (transfer full): the element data, which should be
+ * freed using g_free().
+ *
+ * Since: 2.64
+ */
+guint8 *
+g_byte_array_steal (GByteArray *array,
+ gsize *len)
+{
+ return (guint8 *) g_array_steal ((GArray *) array, len);
+}
+
+/**
+ * g_byte_array_new_take:
+ * @data: (transfer full) (array length=len): byte data for the array
+ * @len: length of @data
+ *
+ * Create byte array containing the data. The data will be owned by the array
+ * and will be freed with g_free(), i.e. it could be allocated using g_strdup().
+ *
+ * Do not use it if @len is greater than %G_MAXUINT. #GByteArray
+ * stores the length of its data in #guint, which may be shorter than
+ * #gsize.
+ *
+ * Since: 2.32
+ *
+ * Returns: (transfer full): a new #GByteArray
+ */
+GByteArray*
+g_byte_array_new_take (guint8 *data,
+ gsize len)
+{
+ GByteArray *array;
+ GRealArray *real;
+
+ g_return_val_if_fail (len <= G_MAXUINT, NULL);
+ array = g_byte_array_new ();
+ real = (GRealArray *)array;
+ g_assert (real->data == NULL);
+ g_assert (real->len == 0);
+
+ real->data = data;
+ real->len = len;
+ real->elt_capacity = len;
+
+ return array;
+}
+
+/**
+ * g_byte_array_sized_new:
+ * @reserved_size: number of bytes preallocated
+ *
+ * Creates a new #GByteArray with @reserved_size bytes preallocated.
+ * This avoids frequent reallocation, if you are going to add many
+ * bytes to the array. Note however that the size of the array is still
+ * 0.
+ *
+ * Returns: the new #GByteArray
+ */
+GByteArray*
+g_byte_array_sized_new (guint reserved_size)
+{
+ return (GByteArray *)g_array_sized_new (FALSE, FALSE, 1, reserved_size);
+}
+
+/**
+ * g_byte_array_free:
+ * @array: a #GByteArray
+ * @free_segment: if %TRUE the actual byte data is freed as well
+ *
+ * Frees the memory allocated by the #GByteArray. If @free_segment is
+ * %TRUE it frees the actual byte data. If the reference count of
+ * @array is greater than one, the #GByteArray wrapper is preserved but
+ * the size of @array will be set to zero.
+ *
+ * Returns: the element data if @free_segment is %FALSE, otherwise
+ * %NULL. The element data should be freed using g_free().
+ */
+guint8*
+g_byte_array_free (GByteArray *array,
+ gboolean free_segment)
+{
+ return (guint8 *)g_array_free ((GArray *)array, free_segment);
+}
+
+/**
+ * g_byte_array_free_to_bytes:
+ * @array: (transfer full): a #GByteArray
+ *
+ * Transfers the data from the #GByteArray into a new immutable #GBytes.
+ *
+ * The #GByteArray is freed unless the reference count of @array is greater
+ * than one, the #GByteArray wrapper is preserved but the size of @array
+ * will be set to zero.
+ *
+ * This is identical to using g_bytes_new_take() and g_byte_array_free()
+ * together.
+ *
+ * Since: 2.32
+ *
+ * Returns: (transfer full): a new immutable #GBytes representing same
+ * byte data that was in the array
+ */
+GBytes*
+g_byte_array_free_to_bytes (GByteArray *array)
+{
+ gsize length;
+
+ g_return_val_if_fail (array != NULL, NULL);
+
+ length = array->len;
+ return g_bytes_new_take (g_byte_array_free (array, FALSE), length);
+}
+
+/**
+ * g_byte_array_ref:
+ * @array: A #GByteArray
+ *
+ * Atomically increments the reference count of @array by one.
+ * This function is thread-safe and may be called from any thread.
+ *
+ * Returns: The passed in #GByteArray
+ *
+ * Since: 2.22
+ */
+GByteArray*
+g_byte_array_ref (GByteArray *array)
+{
+ return (GByteArray *)g_array_ref ((GArray *)array);
+}
+
+/**
+ * g_byte_array_unref:
+ * @array: A #GByteArray
+ *
+ * Atomically decrements the reference count of @array by one. If the
+ * reference count drops to 0, all memory allocated by the array is
+ * released. This function is thread-safe and may be called from any
+ * thread.
+ *
+ * Since: 2.22
+ */
+void
+g_byte_array_unref (GByteArray *array)
+{
+ g_array_unref ((GArray *)array);
+}
+
+/**
+ * g_byte_array_append:
+ * @array: a #GByteArray
+ * @data: the byte data to be added
+ * @len: the number of bytes to add
+ *
+ * Adds the given bytes to the end of the #GByteArray.
+ * The array will grow in size automatically if necessary.
+ *
+ * Returns: the #GByteArray
+ */
+GByteArray*
+g_byte_array_append (GByteArray *array,
+ const guint8 *data,
+ guint len)
+{
+ g_array_append_vals ((GArray *)array, (guint8 *)data, len);
+
+ return array;
+}
+
+/**
+ * g_byte_array_prepend:
+ * @array: a #GByteArray
+ * @data: the byte data to be added
+ * @len: the number of bytes to add
+ *
+ * Adds the given data to the start of the #GByteArray.
+ * The array will grow in size automatically if necessary.
+ *
+ * Returns: the #GByteArray
+ */
+GByteArray*
+g_byte_array_prepend (GByteArray *array,
+ const guint8 *data,
+ guint len)
+{
+ g_array_prepend_vals ((GArray *)array, (guint8 *)data, len);
+
+ return array;
+}
+
+/**
+ * g_byte_array_set_size:
+ * @array: a #GByteArray
+ * @length: the new size of the #GByteArray
+ *
+ * Sets the size of the #GByteArray, expanding it if necessary.
+ *
+ * Returns: the #GByteArray
+ */
+GByteArray*
+g_byte_array_set_size (GByteArray *array,
+ guint length)
+{
+ g_array_set_size ((GArray *)array, length);
+
+ return array;
+}
+
+/**
+ * g_byte_array_remove_index:
+ * @array: a #GByteArray
+ * @index_: the index of the byte to remove
+ *
+ * Removes the byte at the given index from a #GByteArray.
+ * The following bytes are moved down one place.
+ *
+ * Returns: the #GByteArray
+ **/
+GByteArray*
+g_byte_array_remove_index (GByteArray *array,
+ guint index_)
+{
+ g_array_remove_index ((GArray *)array, index_);
+
+ return array;
+}
+
+/**
+ * g_byte_array_remove_index_fast:
+ * @array: a #GByteArray
+ * @index_: the index of the byte to remove
+ *
+ * Removes the byte at the given index from a #GByteArray. The last
+ * element in the array is used to fill in the space, so this function
+ * does not preserve the order of the #GByteArray. But it is faster
+ * than g_byte_array_remove_index().
+ *
+ * Returns: the #GByteArray
+ */
+GByteArray*
+g_byte_array_remove_index_fast (GByteArray *array,
+ guint index_)
+{
+ g_array_remove_index_fast ((GArray *)array, index_);
+
+ return array;
+}
+
+/**
+ * g_byte_array_remove_range:
+ * @array: a @GByteArray
+ * @index_: the index of the first byte to remove
+ * @length: the number of bytes to remove
+ *
+ * Removes the given number of bytes starting at the given index from a
+ * #GByteArray. The following elements are moved to close the gap.
+ *
+ * Returns: the #GByteArray
+ *
+ * Since: 2.4
+ */
+GByteArray*
+g_byte_array_remove_range (GByteArray *array,
+ guint index_,
+ guint length)
+{
+ g_return_val_if_fail (array, NULL);
+ g_return_val_if_fail (index_ <= array->len, NULL);
+ g_return_val_if_fail (index_ + length <= array->len, NULL);
+
+ return (GByteArray *)g_array_remove_range ((GArray *)array, index_, length);
+}
+
+/**
+ * g_byte_array_sort:
+ * @array: a #GByteArray
+ * @compare_func: comparison function
+ *
+ * Sorts a byte array, using @compare_func which should be a
+ * qsort()-style comparison function (returns less than zero for first
+ * arg is less than second arg, zero for equal, greater than zero if
+ * first arg is greater than second arg).
+ *
+ * If two array elements compare equal, their order in the sorted array
+ * is undefined. If you want equal elements to keep their order (i.e.
+ * you want a stable sort) you can write a comparison function that,
+ * if two elements would otherwise compare equal, compares them by
+ * their addresses.
+ */
+void
+g_byte_array_sort (GByteArray *array,
+ GCompareFunc compare_func)
+{
+ g_array_sort ((GArray *)array, compare_func);
+}
+
+/**
+ * g_byte_array_sort_with_data:
+ * @array: a #GByteArray
+ * @compare_func: comparison function
+ * @user_data: data to pass to @compare_func
+ *
+ * Like g_byte_array_sort(), but the comparison function takes an extra
+ * user data argument.
+ */
+void
+g_byte_array_sort_with_data (GByteArray *array,
+ GCompareDataFunc compare_func,
+ gpointer user_data)
+{
+ g_array_sort_with_data ((GArray *)array, compare_func, user_data);
+}
diff --git a/glib/garray.h b/glib/garray.h
new file mode 100644
index 0000000000000000000000000000000000000000..67131b5b3b459f3d940d816bc4edaf44c5afda77
--- /dev/null
+++ b/glib/garray.h
@@ -0,0 +1,281 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_ARRAY_H__
+#define __G_ARRAY_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GBytes GBytes;
+typedef struct _GArray GArray;
+typedef struct _GByteArray GByteArray;
+typedef struct _GPtrArray GPtrArray;
+
+struct _GArray
+{
+ gchar *data;
+ guint len;
+};
+
+struct _GByteArray
+{
+ guint8 *data;
+ guint len;
+};
+
+struct _GPtrArray
+{
+ gpointer *pdata;
+ guint len;
+};
+
+/* Resizable arrays. remove fills any cleared spot and shortens the
+ * array, while preserving the order. remove_fast will distort the
+ * order by moving the last element to the position of the removed.
+ */
+
+#define g_array_append_val(a,v) g_array_append_vals (a, &(v), 1)
+#define g_array_prepend_val(a,v) g_array_prepend_vals (a, &(v), 1)
+#define g_array_insert_val(a,i,v) g_array_insert_vals (a, i, &(v), 1)
+#define g_array_index(a,t,i) (((t*) (void *) (a)->data) [(i)])
+
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_new (gboolean zero_terminated,
+ gboolean clear_,
+ guint element_size);
+GLIB_AVAILABLE_IN_2_64
+gpointer g_array_steal (GArray *array,
+ gsize *len);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_sized_new (gboolean zero_terminated,
+ gboolean clear_,
+ guint element_size,
+ guint reserved_size);
+GLIB_AVAILABLE_IN_2_62
+GArray* g_array_copy (GArray *array);
+GLIB_AVAILABLE_IN_ALL
+gchar* g_array_free (GArray *array,
+ gboolean free_segment);
+GLIB_AVAILABLE_IN_ALL
+GArray *g_array_ref (GArray *array);
+GLIB_AVAILABLE_IN_ALL
+void g_array_unref (GArray *array);
+GLIB_AVAILABLE_IN_ALL
+guint g_array_get_element_size (GArray *array);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_append_vals (GArray *array,
+ gconstpointer data,
+ guint len);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_prepend_vals (GArray *array,
+ gconstpointer data,
+ guint len);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_insert_vals (GArray *array,
+ guint index_,
+ gconstpointer data,
+ guint len);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_set_size (GArray *array,
+ guint length);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_remove_index (GArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_remove_index_fast (GArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_ALL
+GArray* g_array_remove_range (GArray *array,
+ guint index_,
+ guint length);
+GLIB_AVAILABLE_IN_ALL
+void g_array_sort (GArray *array,
+ GCompareFunc compare_func);
+GLIB_AVAILABLE_IN_ALL
+void g_array_sort_with_data (GArray *array,
+ GCompareDataFunc compare_func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_62
+gboolean g_array_binary_search (GArray *array,
+ gconstpointer target,
+ GCompareFunc compare_func,
+ guint *out_match_index);
+GLIB_AVAILABLE_IN_ALL
+void g_array_set_clear_func (GArray *array,
+ GDestroyNotify clear_func);
+
+/* Resizable pointer array. This interface is much less complicated
+ * than the above. Add appends a pointer. Remove fills any cleared
+ * spot and shortens the array. remove_fast will again distort order.
+ */
+#define g_ptr_array_index(array,index_) ((array)->pdata)[index_]
+GLIB_AVAILABLE_IN_ALL
+GPtrArray* g_ptr_array_new (void);
+GLIB_AVAILABLE_IN_ALL
+GPtrArray* g_ptr_array_new_with_free_func (GDestroyNotify element_free_func);
+GLIB_AVAILABLE_IN_2_64
+gpointer* g_ptr_array_steal (GPtrArray *array,
+ gsize *len);
+GLIB_AVAILABLE_IN_2_62
+GPtrArray *g_ptr_array_copy (GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_ALL
+GPtrArray* g_ptr_array_sized_new (guint reserved_size);
+GLIB_AVAILABLE_IN_ALL
+GPtrArray* g_ptr_array_new_full (guint reserved_size,
+ GDestroyNotify element_free_func);
+GLIB_AVAILABLE_IN_ALL
+gpointer* g_ptr_array_free (GPtrArray *array,
+ gboolean free_seg);
+GLIB_AVAILABLE_IN_ALL
+GPtrArray* g_ptr_array_ref (GPtrArray *array);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_unref (GPtrArray *array);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_set_free_func (GPtrArray *array,
+ GDestroyNotify element_free_func);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_set_size (GPtrArray *array,
+ gint length);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_ptr_array_remove_index (GPtrArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_ptr_array_remove_index_fast (GPtrArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_2_58
+gpointer g_ptr_array_steal_index (GPtrArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_2_58
+gpointer g_ptr_array_steal_index_fast (GPtrArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_ptr_array_remove (GPtrArray *array,
+ gpointer data);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_ptr_array_remove_fast (GPtrArray *array,
+ gpointer data);
+GLIB_AVAILABLE_IN_ALL
+GPtrArray *g_ptr_array_remove_range (GPtrArray *array,
+ guint index_,
+ guint length);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_add (GPtrArray *array,
+ gpointer data);
+GLIB_AVAILABLE_IN_2_62
+void g_ptr_array_extend (GPtrArray *array_to_extend,
+ GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_62
+void g_ptr_array_extend_and_steal (GPtrArray *array_to_extend,
+ GPtrArray *array);
+GLIB_AVAILABLE_IN_2_40
+void g_ptr_array_insert (GPtrArray *array,
+ gint index_,
+ gpointer data);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_sort (GPtrArray *array,
+ GCompareFunc compare_func);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_sort_with_data (GPtrArray *array,
+ GCompareDataFunc compare_func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_ALL
+void g_ptr_array_foreach (GPtrArray *array,
+ GFunc func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_54
+gboolean g_ptr_array_find (GPtrArray *haystack,
+ gconstpointer needle,
+ guint *index_);
+GLIB_AVAILABLE_IN_2_54
+gboolean g_ptr_array_find_with_equal_func (GPtrArray *haystack,
+ gconstpointer needle,
+ GEqualFunc equal_func,
+ guint *index_);
+
+
+/* Byte arrays, an array of guint8. Implemented as a GArray,
+ * but type-safe.
+ */
+
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_new (void);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_new_take (guint8 *data,
+ gsize len);
+GLIB_AVAILABLE_IN_2_64
+guint8* g_byte_array_steal (GByteArray *array,
+ gsize *len);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_sized_new (guint reserved_size);
+GLIB_AVAILABLE_IN_ALL
+guint8* g_byte_array_free (GByteArray *array,
+ gboolean free_segment);
+GLIB_AVAILABLE_IN_ALL
+GBytes* g_byte_array_free_to_bytes (GByteArray *array);
+GLIB_AVAILABLE_IN_ALL
+GByteArray *g_byte_array_ref (GByteArray *array);
+GLIB_AVAILABLE_IN_ALL
+void g_byte_array_unref (GByteArray *array);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_append (GByteArray *array,
+ const guint8 *data,
+ guint len);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_prepend (GByteArray *array,
+ const guint8 *data,
+ guint len);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_set_size (GByteArray *array,
+ guint length);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_remove_index (GByteArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_remove_index_fast (GByteArray *array,
+ guint index_);
+GLIB_AVAILABLE_IN_ALL
+GByteArray* g_byte_array_remove_range (GByteArray *array,
+ guint index_,
+ guint length);
+GLIB_AVAILABLE_IN_ALL
+void g_byte_array_sort (GByteArray *array,
+ GCompareFunc compare_func);
+GLIB_AVAILABLE_IN_ALL
+void g_byte_array_sort_with_data (GByteArray *array,
+ GCompareDataFunc compare_func,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __G_ARRAY_H__ */
diff --git a/glib/gasyncqueue.c b/glib/gasyncqueue.c
new file mode 100644
index 0000000000000000000000000000000000000000..98c7d8a603816e6424a5cca12ff03bf20a37afa7
--- /dev/null
+++ b/glib/gasyncqueue.c
@@ -0,0 +1,906 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GAsyncQueue: asynchronous queue implementation, based on GQueue.
+ * Copyright (C) 2000 Sebastian Wilhelmi; University of Karlsruhe
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+
+#include "gasyncqueue.h"
+#include "gasyncqueueprivate.h"
+
+#include "gmain.h"
+#include "gmem.h"
+#include "gqueue.h"
+#include "gtestutils.h"
+#include "gtimer.h"
+#include "gthread.h"
+#include "deprecated/gthread.h"
+
+
+/**
+ * SECTION:async_queues
+ * @title: Asynchronous Queues
+ * @short_description: asynchronous communication between threads
+ * @see_also: #GThreadPool
+ *
+ * Often you need to communicate between different threads. In general
+ * it's safer not to do this by shared memory, but by explicit message
+ * passing. These messages only make sense asynchronously for
+ * multi-threaded applications though, as a synchronous operation could
+ * as well be done in the same thread.
+ *
+ * Asynchronous queues are an exception from most other GLib data
+ * structures, as they can be used simultaneously from multiple threads
+ * without explicit locking and they bring their own builtin reference
+ * counting. This is because the nature of an asynchronous queue is that
+ * it will always be used by at least 2 concurrent threads.
+ *
+ * For using an asynchronous queue you first have to create one with
+ * g_async_queue_new(). #GAsyncQueue structs are reference counted,
+ * use g_async_queue_ref() and g_async_queue_unref() to manage your
+ * references.
+ *
+ * A thread which wants to send a message to that queue simply calls
+ * g_async_queue_push() to push the message to the queue.
+ *
+ * A thread which is expecting messages from an asynchronous queue
+ * simply calls g_async_queue_pop() for that queue. If no message is
+ * available in the queue at that point, the thread is now put to sleep
+ * until a message arrives. The message will be removed from the queue
+ * and returned. The functions g_async_queue_try_pop() and
+ * g_async_queue_timeout_pop() can be used to only check for the presence
+ * of messages or to only wait a certain time for messages respectively.
+ *
+ * For almost every function there exist two variants, one that locks
+ * the queue and one that doesn't. That way you can hold the queue lock
+ * (acquire it with g_async_queue_lock() and release it with
+ * g_async_queue_unlock()) over multiple queue accessing instructions.
+ * This can be necessary to ensure the integrity of the queue, but should
+ * only be used when really necessary, as it can make your life harder
+ * if used unwisely. Normally you should only use the locking function
+ * variants (those without the _unlocked suffix).
+ *
+ * In many cases, it may be more convenient to use #GThreadPool when
+ * you need to distribute work to a set of worker threads instead of
+ * using #GAsyncQueue manually. #GThreadPool uses a GAsyncQueue
+ * internally.
+ */
+
+/**
+ * GAsyncQueue:
+ *
+ * An opaque data structure which represents an asynchronous queue.
+ *
+ * It should only be accessed through the `g_async_queue_*` functions.
+ */
+struct _GAsyncQueue
+{
+ GMutex mutex;
+ GCond cond;
+ GQueue queue;
+ GDestroyNotify item_free_func;
+ guint waiting_threads;
+ gint ref_count;
+};
+
+typedef struct
+{
+ GCompareDataFunc func;
+ gpointer user_data;
+} SortData;
+
+/**
+ * g_async_queue_new:
+ *
+ * Creates a new asynchronous queue.
+ *
+ * Returns: a new #GAsyncQueue. Free with g_async_queue_unref()
+ */
+GAsyncQueue *
+g_async_queue_new (void)
+{
+ return g_async_queue_new_full (NULL);
+}
+
+/**
+ * g_async_queue_new_full:
+ * @item_free_func: (nullable): function to free queue elements
+ *
+ * Creates a new asynchronous queue and sets up a destroy notify
+ * function that is used to free any remaining queue items when
+ * the queue is destroyed after the final unref.
+ *
+ * Returns: a new #GAsyncQueue. Free with g_async_queue_unref()
+ *
+ * Since: 2.16
+ */
+GAsyncQueue *
+g_async_queue_new_full (GDestroyNotify item_free_func)
+{
+ GAsyncQueue *queue;
+
+ queue = g_new (GAsyncQueue, 1);
+ g_mutex_init (&queue->mutex);
+ g_cond_init (&queue->cond);
+ g_queue_init (&queue->queue);
+ queue->waiting_threads = 0;
+ queue->ref_count = 1;
+ queue->item_free_func = item_free_func;
+
+ return queue;
+}
+
+/**
+ * g_async_queue_ref:
+ * @queue: a #GAsyncQueue
+ *
+ * Increases the reference count of the asynchronous @queue by 1.
+ * You do not need to hold the lock to call this function.
+ *
+ * Returns: the @queue that was passed in (since 2.6)
+ */
+GAsyncQueue *
+g_async_queue_ref (GAsyncQueue *queue)
+{
+ g_return_val_if_fail (queue, NULL);
+
+ g_atomic_int_inc (&queue->ref_count);
+
+ return queue;
+}
+
+/**
+ * g_async_queue_ref_unlocked:
+ * @queue: a #GAsyncQueue
+ *
+ * Increases the reference count of the asynchronous @queue by 1.
+ *
+ * Deprecated: 2.8: Reference counting is done atomically.
+ * so g_async_queue_ref() can be used regardless of the @queue's
+ * lock.
+ */
+void
+g_async_queue_ref_unlocked (GAsyncQueue *queue)
+{
+ g_return_if_fail (queue);
+
+ g_atomic_int_inc (&queue->ref_count);
+}
+
+/**
+ * g_async_queue_unref_and_unlock:
+ * @queue: a #GAsyncQueue
+ *
+ * Decreases the reference count of the asynchronous @queue by 1
+ * and releases the lock. This function must be called while holding
+ * the @queue's lock. If the reference count went to 0, the @queue
+ * will be destroyed and the memory allocated will be freed.
+ *
+ * Deprecated: 2.8: Reference counting is done atomically.
+ * so g_async_queue_unref() can be used regardless of the @queue's
+ * lock.
+ */
+void
+g_async_queue_unref_and_unlock (GAsyncQueue *queue)
+{
+ g_return_if_fail (queue);
+
+ g_mutex_unlock (&queue->mutex);
+ g_async_queue_unref (queue);
+}
+
+/**
+ * g_async_queue_unref:
+ * @queue: a #GAsyncQueue.
+ *
+ * Decreases the reference count of the asynchronous @queue by 1.
+ *
+ * If the reference count went to 0, the @queue will be destroyed
+ * and the memory allocated will be freed. So you are not allowed
+ * to use the @queue afterwards, as it might have disappeared.
+ * You do not need to hold the lock to call this function.
+ */
+void
+g_async_queue_unref (GAsyncQueue *queue)
+{
+ g_return_if_fail (queue);
+
+ if (g_atomic_int_dec_and_test (&queue->ref_count))
+ {
+ g_return_if_fail (queue->waiting_threads == 0);
+ g_mutex_clear (&queue->mutex);
+ g_cond_clear (&queue->cond);
+ if (queue->item_free_func)
+ g_queue_foreach (&queue->queue, (GFunc) queue->item_free_func, NULL);
+ g_queue_clear (&queue->queue);
+ g_free (queue);
+ }
+}
+
+/**
+ * g_async_queue_lock:
+ * @queue: a #GAsyncQueue
+ *
+ * Acquires the @queue's lock. If another thread is already
+ * holding the lock, this call will block until the lock
+ * becomes available.
+ *
+ * Call g_async_queue_unlock() to drop the lock again.
+ *
+ * While holding the lock, you can only call the
+ * g_async_queue_*_unlocked() functions on @queue. Otherwise,
+ * deadlock may occur.
+ */
+void
+g_async_queue_lock (GAsyncQueue *queue)
+{
+ g_return_if_fail (queue);
+
+ g_mutex_lock (&queue->mutex);
+}
+
+/**
+ * g_async_queue_unlock:
+ * @queue: a #GAsyncQueue
+ *
+ * Releases the queue's lock.
+ *
+ * Calling this function when you have not acquired
+ * the with g_async_queue_lock() leads to undefined
+ * behaviour.
+ */
+void
+g_async_queue_unlock (GAsyncQueue *queue)
+{
+ g_return_if_fail (queue);
+
+ g_mutex_unlock (&queue->mutex);
+}
+
+/**
+ * g_async_queue_push:
+ * @queue: a #GAsyncQueue
+ * @data: @data to push into the @queue
+ *
+ * Pushes the @data into the @queue. @data must not be %NULL.
+ */
+void
+g_async_queue_push (GAsyncQueue *queue,
+ gpointer data)
+{
+ g_return_if_fail (queue);
+ g_return_if_fail (data);
+
+ g_mutex_lock (&queue->mutex);
+ g_async_queue_push_unlocked (queue, data);
+ g_mutex_unlock (&queue->mutex);
+}
+
+/**
+ * g_async_queue_push_unlocked:
+ * @queue: a #GAsyncQueue
+ * @data: @data to push into the @queue
+ *
+ * Pushes the @data into the @queue. @data must not be %NULL.
+ *
+ * This function must be called while holding the @queue's lock.
+ */
+void
+g_async_queue_push_unlocked (GAsyncQueue *queue,
+ gpointer data)
+{
+ g_return_if_fail (queue);
+ g_return_if_fail (data);
+
+ g_queue_push_head (&queue->queue, data);
+ if (queue->waiting_threads > 0)
+ g_cond_signal (&queue->cond);
+}
+
+/**
+ * g_async_queue_push_sorted:
+ * @queue: a #GAsyncQueue
+ * @data: the @data to push into the @queue
+ * @func: the #GCompareDataFunc is used to sort @queue
+ * @user_data: user data passed to @func.
+ *
+ * Inserts @data into @queue using @func to determine the new
+ * position.
+ *
+ * This function requires that the @queue is sorted before pushing on
+ * new elements, see g_async_queue_sort().
+ *
+ * This function will lock @queue before it sorts the queue and unlock
+ * it when it is finished.
+ *
+ * For an example of @func see g_async_queue_sort().
+ *
+ * Since: 2.10
+ */
+void
+g_async_queue_push_sorted (GAsyncQueue *queue,
+ gpointer data,
+ GCompareDataFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (queue != NULL);
+
+ g_mutex_lock (&queue->mutex);
+ g_async_queue_push_sorted_unlocked (queue, data, func, user_data);
+ g_mutex_unlock (&queue->mutex);
+}
+
+static gint
+g_async_queue_invert_compare (gpointer v1,
+ gpointer v2,
+ SortData *sd)
+{
+ return -sd->func (v1, v2, sd->user_data);
+}
+
+/**
+ * g_async_queue_push_sorted_unlocked:
+ * @queue: a #GAsyncQueue
+ * @data: the @data to push into the @queue
+ * @func: the #GCompareDataFunc is used to sort @queue
+ * @user_data: user data passed to @func.
+ *
+ * Inserts @data into @queue using @func to determine the new
+ * position.
+ *
+ * The sort function @func is passed two elements of the @queue.
+ * It should return 0 if they are equal, a negative value if the
+ * first element should be higher in the @queue or a positive value
+ * if the first element should be lower in the @queue than the second
+ * element.
+ *
+ * This function requires that the @queue is sorted before pushing on
+ * new elements, see g_async_queue_sort().
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * For an example of @func see g_async_queue_sort().
+ *
+ * Since: 2.10
+ */
+void
+g_async_queue_push_sorted_unlocked (GAsyncQueue *queue,
+ gpointer data,
+ GCompareDataFunc func,
+ gpointer user_data)
+{
+ SortData sd;
+
+ g_return_if_fail (queue != NULL);
+
+ sd.func = func;
+ sd.user_data = user_data;
+
+ g_queue_insert_sorted (&queue->queue,
+ data,
+ (GCompareDataFunc)g_async_queue_invert_compare,
+ &sd);
+ if (queue->waiting_threads > 0)
+ g_cond_signal (&queue->cond);
+}
+
+static gpointer
+g_async_queue_pop_intern_unlocked (GAsyncQueue *queue,
+ gboolean wait,
+ gint64 end_time)
+{
+ gpointer retval;
+
+ if (!g_queue_peek_tail_link (&queue->queue) && wait)
+ {
+ queue->waiting_threads++;
+ while (!g_queue_peek_tail_link (&queue->queue))
+ {
+ if (end_time == -1)
+ g_cond_wait (&queue->cond, &queue->mutex);
+ else
+ {
+ if (!g_cond_wait_until (&queue->cond, &queue->mutex, end_time))
+ break;
+ }
+ }
+ queue->waiting_threads--;
+ }
+
+ retval = g_queue_pop_tail (&queue->queue);
+
+ g_assert (retval || !wait || end_time > 0);
+
+ return retval;
+}
+
+/**
+ * g_async_queue_pop:
+ * @queue: a #GAsyncQueue
+ *
+ * Pops data from the @queue. If @queue is empty, this function
+ * blocks until data becomes available.
+ *
+ * Returns: data from the queue
+ */
+gpointer
+g_async_queue_pop (GAsyncQueue *queue)
+{
+ gpointer retval;
+
+ g_return_val_if_fail (queue, NULL);
+
+ g_mutex_lock (&queue->mutex);
+ retval = g_async_queue_pop_intern_unlocked (queue, TRUE, -1);
+ g_mutex_unlock (&queue->mutex);
+
+ return retval;
+}
+
+/**
+ * g_async_queue_pop_unlocked:
+ * @queue: a #GAsyncQueue
+ *
+ * Pops data from the @queue. If @queue is empty, this function
+ * blocks until data becomes available.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Returns: data from the queue.
+ */
+gpointer
+g_async_queue_pop_unlocked (GAsyncQueue *queue)
+{
+ g_return_val_if_fail (queue, NULL);
+
+ return g_async_queue_pop_intern_unlocked (queue, TRUE, -1);
+}
+
+/**
+ * g_async_queue_try_pop:
+ * @queue: a #GAsyncQueue
+ *
+ * Tries to pop data from the @queue. If no data is available,
+ * %NULL is returned.
+ *
+ * Returns: (nullable): data from the queue or %NULL, when no data is
+ * available immediately.
+ */
+gpointer
+g_async_queue_try_pop (GAsyncQueue *queue)
+{
+ gpointer retval;
+
+ g_return_val_if_fail (queue, NULL);
+
+ g_mutex_lock (&queue->mutex);
+ retval = g_async_queue_pop_intern_unlocked (queue, FALSE, -1);
+ g_mutex_unlock (&queue->mutex);
+
+ return retval;
+}
+
+/**
+ * g_async_queue_try_pop_unlocked:
+ * @queue: a #GAsyncQueue
+ *
+ * Tries to pop data from the @queue. If no data is available,
+ * %NULL is returned.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Returns: (nullable): data from the queue or %NULL, when no data is
+ * available immediately.
+ */
+gpointer
+g_async_queue_try_pop_unlocked (GAsyncQueue *queue)
+{
+ g_return_val_if_fail (queue, NULL);
+
+ return g_async_queue_pop_intern_unlocked (queue, FALSE, -1);
+}
+
+/**
+ * g_async_queue_timeout_pop:
+ * @queue: a #GAsyncQueue
+ * @timeout: the number of microseconds to wait
+ *
+ * Pops data from the @queue. If the queue is empty, blocks for
+ * @timeout microseconds, or until data becomes available.
+ *
+ * If no data is received before the timeout, %NULL is returned.
+ *
+ * Returns: (nullable): data from the queue or %NULL, when no data is
+ * received before the timeout.
+ */
+gpointer
+g_async_queue_timeout_pop (GAsyncQueue *queue,
+ guint64 timeout)
+{
+ gint64 end_time = g_get_monotonic_time () + timeout;
+ gpointer retval;
+
+ g_return_val_if_fail (queue != NULL, NULL);
+
+ g_mutex_lock (&queue->mutex);
+ retval = g_async_queue_pop_intern_unlocked (queue, TRUE, end_time);
+ g_mutex_unlock (&queue->mutex);
+
+ return retval;
+}
+
+/**
+ * g_async_queue_timeout_pop_unlocked:
+ * @queue: a #GAsyncQueue
+ * @timeout: the number of microseconds to wait
+ *
+ * Pops data from the @queue. If the queue is empty, blocks for
+ * @timeout microseconds, or until data becomes available.
+ *
+ * If no data is received before the timeout, %NULL is returned.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Returns: (nullable): data from the queue or %NULL, when no data is
+ * received before the timeout.
+ */
+gpointer
+g_async_queue_timeout_pop_unlocked (GAsyncQueue *queue,
+ guint64 timeout)
+{
+ gint64 end_time = g_get_monotonic_time () + timeout;
+
+ g_return_val_if_fail (queue != NULL, NULL);
+
+ return g_async_queue_pop_intern_unlocked (queue, TRUE, end_time);
+}
+
+/**
+ * g_async_queue_timed_pop:
+ * @queue: a #GAsyncQueue
+ * @end_time: a #GTimeVal, determining the final time
+ *
+ * Pops data from the @queue. If the queue is empty, blocks until
+ * @end_time or until data becomes available.
+ *
+ * If no data is received before @end_time, %NULL is returned.
+ *
+ * To easily calculate @end_time, a combination of g_get_real_time()
+ * and g_time_val_add() can be used.
+ *
+ * Returns: (nullable): data from the queue or %NULL, when no data is
+ * received before @end_time.
+ *
+ * Deprecated: use g_async_queue_timeout_pop().
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+gpointer
+g_async_queue_timed_pop (GAsyncQueue *queue,
+ GTimeVal *end_time)
+{
+ gint64 m_end_time;
+ gpointer retval;
+
+ g_return_val_if_fail (queue, NULL);
+
+ if (end_time != NULL)
+ {
+ m_end_time = g_get_monotonic_time () +
+ ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ());
+ }
+ else
+ m_end_time = -1;
+
+ g_mutex_lock (&queue->mutex);
+ retval = g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time);
+ g_mutex_unlock (&queue->mutex);
+
+ return retval;
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/**
+ * g_async_queue_timed_pop_unlocked:
+ * @queue: a #GAsyncQueue
+ * @end_time: a #GTimeVal, determining the final time
+ *
+ * Pops data from the @queue. If the queue is empty, blocks until
+ * @end_time or until data becomes available.
+ *
+ * If no data is received before @end_time, %NULL is returned.
+ *
+ * To easily calculate @end_time, a combination of g_get_real_time()
+ * and g_time_val_add() can be used.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Returns: (nullable): data from the queue or %NULL, when no data is
+ * received before @end_time.
+ *
+ * Deprecated: use g_async_queue_timeout_pop_unlocked().
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+gpointer
+g_async_queue_timed_pop_unlocked (GAsyncQueue *queue,
+ GTimeVal *end_time)
+{
+ gint64 m_end_time;
+
+ g_return_val_if_fail (queue, NULL);
+
+ if (end_time != NULL)
+ {
+ m_end_time = g_get_monotonic_time () +
+ ((gint64) end_time->tv_sec * G_USEC_PER_SEC + end_time->tv_usec - g_get_real_time ());
+ }
+ else
+ m_end_time = -1;
+
+ return g_async_queue_pop_intern_unlocked (queue, TRUE, m_end_time);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/**
+ * g_async_queue_length:
+ * @queue: a #GAsyncQueue.
+ *
+ * Returns the length of the queue.
+ *
+ * Actually this function returns the number of data items in
+ * the queue minus the number of waiting threads, so a negative
+ * value means waiting threads, and a positive value means available
+ * entries in the @queue. A return value of 0 could mean n entries
+ * in the queue and n threads waiting. This can happen due to locking
+ * of the queue or due to scheduling.
+ *
+ * Returns: the length of the @queue
+ */
+gint
+g_async_queue_length (GAsyncQueue *queue)
+{
+ gint retval;
+
+ g_return_val_if_fail (queue, 0);
+
+ g_mutex_lock (&queue->mutex);
+ retval = queue->queue.length - queue->waiting_threads;
+ g_mutex_unlock (&queue->mutex);
+
+ return retval;
+}
+
+/**
+ * g_async_queue_length_unlocked:
+ * @queue: a #GAsyncQueue
+ *
+ * Returns the length of the queue.
+ *
+ * Actually this function returns the number of data items in
+ * the queue minus the number of waiting threads, so a negative
+ * value means waiting threads, and a positive value means available
+ * entries in the @queue. A return value of 0 could mean n entries
+ * in the queue and n threads waiting. This can happen due to locking
+ * of the queue or due to scheduling.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Returns: the length of the @queue.
+ */
+gint
+g_async_queue_length_unlocked (GAsyncQueue *queue)
+{
+ g_return_val_if_fail (queue, 0);
+
+ return queue->queue.length - queue->waiting_threads;
+}
+
+/**
+ * g_async_queue_sort:
+ * @queue: a #GAsyncQueue
+ * @func: the #GCompareDataFunc is used to sort @queue
+ * @user_data: user data passed to @func
+ *
+ * Sorts @queue using @func.
+ *
+ * The sort function @func is passed two elements of the @queue.
+ * It should return 0 if they are equal, a negative value if the
+ * first element should be higher in the @queue or a positive value
+ * if the first element should be lower in the @queue than the second
+ * element.
+ *
+ * This function will lock @queue before it sorts the queue and unlock
+ * it when it is finished.
+ *
+ * If you were sorting a list of priority numbers to make sure the
+ * lowest priority would be at the top of the queue, you could use:
+ * |[
+ * gint32 id1;
+ * gint32 id2;
+ *
+ * id1 = GPOINTER_TO_INT (element1);
+ * id2 = GPOINTER_TO_INT (element2);
+ *
+ * return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1);
+ * ]|
+ *
+ * Since: 2.10
+ */
+void
+g_async_queue_sort (GAsyncQueue *queue,
+ GCompareDataFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (queue != NULL);
+ g_return_if_fail (func != NULL);
+
+ g_mutex_lock (&queue->mutex);
+ g_async_queue_sort_unlocked (queue, func, user_data);
+ g_mutex_unlock (&queue->mutex);
+}
+
+/**
+ * g_async_queue_sort_unlocked:
+ * @queue: a #GAsyncQueue
+ * @func: the #GCompareDataFunc is used to sort @queue
+ * @user_data: user data passed to @func
+ *
+ * Sorts @queue using @func.
+ *
+ * The sort function @func is passed two elements of the @queue.
+ * It should return 0 if they are equal, a negative value if the
+ * first element should be higher in the @queue or a positive value
+ * if the first element should be lower in the @queue than the second
+ * element.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Since: 2.10
+ */
+void
+g_async_queue_sort_unlocked (GAsyncQueue *queue,
+ GCompareDataFunc func,
+ gpointer user_data)
+{
+ SortData sd;
+
+ g_return_if_fail (queue != NULL);
+ g_return_if_fail (func != NULL);
+
+ sd.func = func;
+ sd.user_data = user_data;
+
+ g_queue_sort (&queue->queue,
+ (GCompareDataFunc)g_async_queue_invert_compare,
+ &sd);
+}
+
+/**
+ * g_async_queue_remove:
+ * @queue: a #GAsyncQueue
+ * @item: the data to remove from the @queue
+ *
+ * Remove an item from the queue.
+ *
+ * Returns: %TRUE if the item was removed
+ *
+ * Since: 2.46
+ */
+gboolean
+g_async_queue_remove (GAsyncQueue *queue,
+ gpointer item)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (queue != NULL, FALSE);
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ g_mutex_lock (&queue->mutex);
+ ret = g_async_queue_remove_unlocked (queue, item);
+ g_mutex_unlock (&queue->mutex);
+
+ return ret;
+}
+
+/**
+ * g_async_queue_remove_unlocked:
+ * @queue: a #GAsyncQueue
+ * @item: the data to remove from the @queue
+ *
+ * Remove an item from the queue.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Returns: %TRUE if the item was removed
+ *
+ * Since: 2.46
+ */
+gboolean
+g_async_queue_remove_unlocked (GAsyncQueue *queue,
+ gpointer item)
+{
+ g_return_val_if_fail (queue != NULL, FALSE);
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ return g_queue_remove (&queue->queue, item);
+}
+
+/**
+ * g_async_queue_push_front:
+ * @queue: a #GAsyncQueue
+ * @item: data to push into the @queue
+ *
+ * Pushes the @item into the @queue. @item must not be %NULL.
+ * In contrast to g_async_queue_push(), this function
+ * pushes the new item ahead of the items already in the queue,
+ * so that it will be the next one to be popped off the queue.
+ *
+ * Since: 2.46
+ */
+void
+g_async_queue_push_front (GAsyncQueue *queue,
+ gpointer item)
+{
+ g_return_if_fail (queue != NULL);
+ g_return_if_fail (item != NULL);
+
+ g_mutex_lock (&queue->mutex);
+ g_async_queue_push_front_unlocked (queue, item);
+ g_mutex_unlock (&queue->mutex);
+}
+
+/**
+ * g_async_queue_push_front_unlocked:
+ * @queue: a #GAsyncQueue
+ * @item: data to push into the @queue
+ *
+ * Pushes the @item into the @queue. @item must not be %NULL.
+ * In contrast to g_async_queue_push_unlocked(), this function
+ * pushes the new item ahead of the items already in the queue,
+ * so that it will be the next one to be popped off the queue.
+ *
+ * This function must be called while holding the @queue's lock.
+ *
+ * Since: 2.46
+ */
+void
+g_async_queue_push_front_unlocked (GAsyncQueue *queue,
+ gpointer item)
+{
+ g_return_if_fail (queue != NULL);
+ g_return_if_fail (item != NULL);
+
+ g_queue_push_tail (&queue->queue, item);
+ if (queue->waiting_threads > 0)
+ g_cond_signal (&queue->cond);
+}
+
+/*
+ * Private API
+ */
+
+GMutex *
+_g_async_queue_get_mutex (GAsyncQueue *queue)
+{
+ g_return_val_if_fail (queue, NULL);
+
+ return &queue->mutex;
+}
diff --git a/glib/gasyncqueue.h b/glib/gasyncqueue.h
new file mode 100644
index 0000000000000000000000000000000000000000..73e537b0790e087d465078198b4229fefb1ca4c2
--- /dev/null
+++ b/glib/gasyncqueue.h
@@ -0,0 +1,124 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_ASYNCQUEUE_H__
+#define __G_ASYNCQUEUE_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GAsyncQueue GAsyncQueue;
+
+GLIB_AVAILABLE_IN_ALL
+GAsyncQueue *g_async_queue_new (void);
+GLIB_AVAILABLE_IN_ALL
+GAsyncQueue *g_async_queue_new_full (GDestroyNotify item_free_func);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_lock (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_unlock (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+GAsyncQueue *g_async_queue_ref (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_unref (GAsyncQueue *queue);
+
+GLIB_DEPRECATED_FOR(g_async_queue_ref)
+void g_async_queue_ref_unlocked (GAsyncQueue *queue);
+
+GLIB_DEPRECATED_FOR(g_async_queue_unref)
+void g_async_queue_unref_and_unlock (GAsyncQueue *queue);
+
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_push (GAsyncQueue *queue,
+ gpointer data);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_push_unlocked (GAsyncQueue *queue,
+ gpointer data);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_push_sorted (GAsyncQueue *queue,
+ gpointer data,
+ GCompareDataFunc func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_push_sorted_unlocked (GAsyncQueue *queue,
+ gpointer data,
+ GCompareDataFunc func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_async_queue_pop (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_async_queue_pop_unlocked (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_async_queue_try_pop (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_async_queue_try_pop_unlocked (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_async_queue_timeout_pop (GAsyncQueue *queue,
+ guint64 timeout);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_async_queue_timeout_pop_unlocked (GAsyncQueue *queue,
+ guint64 timeout);
+GLIB_AVAILABLE_IN_ALL
+gint g_async_queue_length (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+gint g_async_queue_length_unlocked (GAsyncQueue *queue);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_sort (GAsyncQueue *queue,
+ GCompareDataFunc func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_ALL
+void g_async_queue_sort_unlocked (GAsyncQueue *queue,
+ GCompareDataFunc func,
+ gpointer user_data);
+
+GLIB_AVAILABLE_IN_2_46
+gboolean g_async_queue_remove (GAsyncQueue *queue,
+ gpointer item);
+GLIB_AVAILABLE_IN_2_46
+gboolean g_async_queue_remove_unlocked (GAsyncQueue *queue,
+ gpointer item);
+GLIB_AVAILABLE_IN_2_46
+void g_async_queue_push_front (GAsyncQueue *queue,
+ gpointer item);
+GLIB_AVAILABLE_IN_2_46
+void g_async_queue_push_front_unlocked (GAsyncQueue *queue,
+ gpointer item);
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GLIB_DEPRECATED_FOR(g_async_queue_timeout_pop)
+gpointer g_async_queue_timed_pop (GAsyncQueue *queue,
+ GTimeVal *end_time);
+GLIB_DEPRECATED_FOR(g_async_queue_timeout_pop_unlocked)
+gpointer g_async_queue_timed_pop_unlocked (GAsyncQueue *queue,
+ GTimeVal *end_time);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+G_END_DECLS
+
+#endif /* __G_ASYNCQUEUE_H__ */
diff --git a/glib/gasyncqueueprivate.h b/glib/gasyncqueueprivate.h
new file mode 100644
index 0000000000000000000000000000000000000000..b1622c54c06c2d37972118cc5f0e8a1994087143
--- /dev/null
+++ b/glib/gasyncqueueprivate.h
@@ -0,0 +1,29 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#ifndef __G_ASYNCQUEUEPRIVATE_H__
+#define __G_ASYNCQUEUEPRIVATE_H__
+
+#include "gasyncqueue.h"
+
+G_BEGIN_DECLS
+
+GMutex *_g_async_queue_get_mutex (GAsyncQueue *queue);
+
+G_END_DECLS
+
+#endif /* __G_ASYNCQUEUEPRIVATE_H__ */
diff --git a/glib/gatomic.c b/glib/gatomic.c
new file mode 100644
index 0000000000000000000000000000000000000000..0bc67aa35e563cd891d6762b92ce3a9817030d9b
--- /dev/null
+++ b/glib/gatomic.c
@@ -0,0 +1,967 @@
+/*
+ * Copyright © 2011 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Ryan Lortie
+ */
+
+#include "config.h"
+
+#include "gatomic.h"
+
+/**
+ * SECTION:atomic_operations
+ * @title: Atomic Operations
+ * @short_description: basic atomic integer and pointer operations
+ * @see_also: #GMutex
+ *
+ * The following is a collection of compiler macros to provide atomic
+ * access to integer and pointer-sized values.
+ *
+ * The macros that have 'int' in the name will operate on pointers to
+ * #gint and #guint. The macros with 'pointer' in the name will operate
+ * on pointers to any pointer-sized value, including #gsize. There is
+ * no support for 64bit operations on platforms with 32bit pointers
+ * because it is not generally possible to perform these operations
+ * atomically.
+ *
+ * The get, set and exchange operations for integers and pointers
+ * nominally operate on #gint and #gpointer, respectively. Of the
+ * arithmetic operations, the 'add' operation operates on (and returns)
+ * signed integer values (#gint and #gssize) and the 'and', 'or', and
+ * 'xor' operations operate on (and return) unsigned integer values
+ * (#guint and #gsize).
+ *
+ * All of the operations act as a full compiler and (where appropriate)
+ * hardware memory barrier. Acquire and release or producer and
+ * consumer barrier semantics are not available through this API.
+ *
+ * It is very important that all accesses to a particular integer or
+ * pointer be performed using only this API and that different sizes of
+ * operation are not mixed or used on overlapping memory regions. Never
+ * read or assign directly from or to a value -- always use this API.
+ *
+ * For simple reference counting purposes you should use
+ * g_atomic_int_inc() and g_atomic_int_dec_and_test(). Other uses that
+ * fall outside of simple reference counting patterns are prone to
+ * subtle bugs and occasionally undefined behaviour. It is also worth
+ * noting that since all of these operations require global
+ * synchronisation of the entire machine, they can be quite slow. In
+ * the case of performing multiple atomic operations it can often be
+ * faster to simply acquire a mutex lock around the critical area,
+ * perform the operations normally and then release the lock.
+ **/
+
+/**
+ * G_ATOMIC_LOCK_FREE:
+ *
+ * This macro is defined if the atomic operations of GLib are
+ * implemented using real hardware atomic operations. This means that
+ * the GLib atomic API can be used between processes and safely mixed
+ * with other (hardware) atomic APIs.
+ *
+ * If this macro is not defined, the atomic operations may be
+ * emulated using a mutex. In that case, the GLib atomic operations are
+ * only atomic relative to themselves and within a single process.
+ **/
+
+/* NOTE CAREFULLY:
+ *
+ * This file is the lowest-level part of GLib.
+ *
+ * Other lowlevel parts of GLib (threads, slice allocator, g_malloc,
+ * messages, etc) call into these functions and macros to get work done.
+ *
+ * As such, these functions can not call back into any part of GLib
+ * without risking recursion.
+ */
+
+#ifdef G_ATOMIC_LOCK_FREE
+
+/* if G_ATOMIC_LOCK_FREE was defined by `meson configure` then we MUST
+ * implement the atomic operations in a lock-free manner.
+ */
+
+#if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+
+/**
+ * g_atomic_int_get:
+ * @atomic: a pointer to a #gint or #guint
+ *
+ * Gets the current value of @atomic.
+ *
+ * This call acts as a full compiler and hardware
+ * memory barrier (before the get).
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of the integer
+ *
+ * Since: 2.4
+ **/
+gint
+(g_atomic_int_get) (const volatile gint *atomic)
+{
+ return g_atomic_int_get (atomic);
+}
+
+/**
+ * g_atomic_int_set:
+ * @atomic: a pointer to a #gint or #guint
+ * @newval: a new value to store
+ *
+ * Sets the value of @atomic to @newval.
+ *
+ * This call acts as a full compiler and hardware
+ * memory barrier (after the set).
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Since: 2.4
+ */
+void
+(g_atomic_int_set) (volatile gint *atomic,
+ gint newval)
+{
+ g_atomic_int_set (atomic, newval);
+}
+
+/**
+ * g_atomic_int_inc:
+ * @atomic: a pointer to a #gint or #guint
+ *
+ * Increments the value of @atomic by 1.
+ *
+ * Think of this operation as an atomic version of `{ *atomic += 1; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Since: 2.4
+ **/
+void
+(g_atomic_int_inc) (volatile gint *atomic)
+{
+ g_atomic_int_inc (atomic);
+}
+
+/**
+ * g_atomic_int_dec_and_test:
+ * @atomic: a pointer to a #gint or #guint
+ *
+ * Decrements the value of @atomic by 1.
+ *
+ * Think of this operation as an atomic version of
+ * `{ *atomic -= 1; return (*atomic == 0); }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: %TRUE if the resultant value is zero
+ *
+ * Since: 2.4
+ **/
+gboolean
+(g_atomic_int_dec_and_test) (volatile gint *atomic)
+{
+ return g_atomic_int_dec_and_test (atomic);
+}
+
+/**
+ * g_atomic_int_compare_and_exchange:
+ * @atomic: a pointer to a #gint or #guint
+ * @oldval: the value to compare with
+ * @newval: the value to conditionally replace with
+ *
+ * Compares @atomic to @oldval and, if equal, sets it to @newval.
+ * If @atomic was not equal to @oldval then no change occurs.
+ *
+ * This compare and exchange is done atomically.
+ *
+ * Think of this operation as an atomic version of
+ * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: %TRUE if the exchange took place
+ *
+ * Since: 2.4
+ **/
+gboolean
+(g_atomic_int_compare_and_exchange) (volatile gint *atomic,
+ gint oldval,
+ gint newval)
+{
+ return g_atomic_int_compare_and_exchange (atomic, oldval, newval);
+}
+
+/**
+ * g_atomic_int_add:
+ * @atomic: a pointer to a #gint or #guint
+ * @val: the value to add
+ *
+ * Atomically adds @val to the value of @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic += val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * Before version 2.30, this function did not return a value
+ * (but g_atomic_int_exchange_and_add() did, and had the same meaning).
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the add, signed
+ *
+ * Since: 2.4
+ **/
+gint
+(g_atomic_int_add) (volatile gint *atomic,
+ gint val)
+{
+ return g_atomic_int_add (atomic, val);
+}
+
+/**
+ * g_atomic_int_and:
+ * @atomic: a pointer to a #gint or #guint
+ * @val: the value to 'and'
+ *
+ * Performs an atomic bitwise 'and' of the value of @atomic and @val,
+ * storing the result back in @atomic.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic &= val; return tmp; }`.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the operation, unsigned
+ *
+ * Since: 2.30
+ **/
+guint
+(g_atomic_int_and) (volatile guint *atomic,
+ guint val)
+{
+ return g_atomic_int_and (atomic, val);
+}
+
+/**
+ * g_atomic_int_or:
+ * @atomic: a pointer to a #gint or #guint
+ * @val: the value to 'or'
+ *
+ * Performs an atomic bitwise 'or' of the value of @atomic and @val,
+ * storing the result back in @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic |= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the operation, unsigned
+ *
+ * Since: 2.30
+ **/
+guint
+(g_atomic_int_or) (volatile guint *atomic,
+ guint val)
+{
+ return g_atomic_int_or (atomic, val);
+}
+
+/**
+ * g_atomic_int_xor:
+ * @atomic: a pointer to a #gint or #guint
+ * @val: the value to 'xor'
+ *
+ * Performs an atomic bitwise 'xor' of the value of @atomic and @val,
+ * storing the result back in @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the operation, unsigned
+ *
+ * Since: 2.30
+ **/
+guint
+(g_atomic_int_xor) (volatile guint *atomic,
+ guint val)
+{
+ return g_atomic_int_xor (atomic, val);
+}
+
+
+/**
+ * g_atomic_pointer_get:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ *
+ * Gets the current value of @atomic.
+ *
+ * This call acts as a full compiler and hardware
+ * memory barrier (before the get).
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of the pointer
+ *
+ * Since: 2.4
+ **/
+gpointer
+(g_atomic_pointer_get) (const volatile void *atomic)
+{
+ return g_atomic_pointer_get ((gpointer *) atomic);
+}
+
+/**
+ * g_atomic_pointer_set:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @newval: a new value to store
+ *
+ * Sets the value of @atomic to @newval.
+ *
+ * This call acts as a full compiler and hardware
+ * memory barrier (after the set).
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Since: 2.4
+ **/
+void
+(g_atomic_pointer_set) (volatile void *atomic,
+ gpointer newval)
+{
+ g_atomic_pointer_set ((gpointer *) atomic, newval);
+}
+
+/**
+ * g_atomic_pointer_compare_and_exchange:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @oldval: the value to compare with
+ * @newval: the value to conditionally replace with
+ *
+ * Compares @atomic to @oldval and, if equal, sets it to @newval.
+ * If @atomic was not equal to @oldval then no change occurs.
+ *
+ * This compare and exchange is done atomically.
+ *
+ * Think of this operation as an atomic version of
+ * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: %TRUE if the exchange took place
+ *
+ * Since: 2.4
+ **/
+gboolean
+(g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
+ gpointer oldval,
+ gpointer newval)
+{
+ return g_atomic_pointer_compare_and_exchange ((gpointer *) atomic,
+ oldval, newval);
+}
+
+/**
+ * g_atomic_pointer_add:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @val: the value to add
+ *
+ * Atomically adds @val to the value of @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic += val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the add, signed
+ *
+ * Since: 2.30
+ **/
+gssize
+(g_atomic_pointer_add) (volatile void *atomic,
+ gssize val)
+{
+ return g_atomic_pointer_add ((gpointer *) atomic, val);
+}
+
+/**
+ * g_atomic_pointer_and:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @val: the value to 'and'
+ *
+ * Performs an atomic bitwise 'and' of the value of @atomic and @val,
+ * storing the result back in @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic &= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the operation, unsigned
+ *
+ * Since: 2.30
+ **/
+gsize
+(g_atomic_pointer_and) (volatile void *atomic,
+ gsize val)
+{
+ return g_atomic_pointer_and ((gpointer *) atomic, val);
+}
+
+/**
+ * g_atomic_pointer_or:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @val: the value to 'or'
+ *
+ * Performs an atomic bitwise 'or' of the value of @atomic and @val,
+ * storing the result back in @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic |= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the operation, unsigned
+ *
+ * Since: 2.30
+ **/
+gsize
+(g_atomic_pointer_or) (volatile void *atomic,
+ gsize val)
+{
+ return g_atomic_pointer_or ((gpointer *) atomic, val);
+}
+
+/**
+ * g_atomic_pointer_xor:
+ * @atomic: (not nullable): a pointer to a #gpointer-sized value
+ * @val: the value to 'xor'
+ *
+ * Performs an atomic bitwise 'xor' of the value of @atomic and @val,
+ * storing the result back in @atomic.
+ *
+ * Think of this operation as an atomic version of
+ * `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
+ *
+ * This call acts as a full compiler and hardware memory barrier.
+ *
+ * While @atomic has a `volatile` qualifier, this is a historical artifact and
+ * the pointer passed to it should not be `volatile`.
+ *
+ * Returns: the value of @atomic before the operation, unsigned
+ *
+ * Since: 2.30
+ **/
+gsize
+(g_atomic_pointer_xor) (volatile void *atomic,
+ gsize val)
+{
+ return g_atomic_pointer_xor ((gpointer *) atomic, val);
+}
+
+#elif defined (G_PLATFORM_WIN32)
+
+#include
+#if !defined(_M_AMD64) && !defined (_M_IA64) && !defined(_M_X64) && !(defined _MSC_VER && _MSC_VER <= 1200)
+#define InterlockedAnd _InterlockedAnd
+#define InterlockedOr _InterlockedOr
+#define InterlockedXor _InterlockedXor
+#endif
+
+#if !defined (_MSC_VER) || _MSC_VER <= 1200
+#include "gmessages.h"
+/* Inlined versions for older compiler */
+static LONG
+_gInterlockedAnd (volatile guint *atomic,
+ guint val)
+{
+ LONG i, j;
+
+ j = *atomic;
+ do {
+ i = j;
+ j = InterlockedCompareExchange(atomic, i & val, i);
+ } while (i != j);
+
+ return j;
+}
+#define InterlockedAnd(a,b) _gInterlockedAnd(a,b)
+static LONG
+_gInterlockedOr (volatile guint *atomic,
+ guint val)
+{
+ LONG i, j;
+
+ j = *atomic;
+ do {
+ i = j;
+ j = InterlockedCompareExchange(atomic, i | val, i);
+ } while (i != j);
+
+ return j;
+}
+#define InterlockedOr(a,b) _gInterlockedOr(a,b)
+static LONG
+_gInterlockedXor (volatile guint *atomic,
+ guint val)
+{
+ LONG i, j;
+
+ j = *atomic;
+ do {
+ i = j;
+ j = InterlockedCompareExchange(atomic, i ^ val, i);
+ } while (i != j);
+
+ return j;
+}
+#define InterlockedXor(a,b) _gInterlockedXor(a,b)
+#endif
+
+/*
+ * http://msdn.microsoft.com/en-us/library/ms684122(v=vs.85).aspx
+ */
+gint
+(g_atomic_int_get) (const volatile gint *atomic)
+{
+ MemoryBarrier ();
+ return *atomic;
+}
+
+void
+(g_atomic_int_set) (volatile gint *atomic,
+ gint newval)
+{
+ *atomic = newval;
+ MemoryBarrier ();
+}
+
+void
+(g_atomic_int_inc) (volatile gint *atomic)
+{
+ InterlockedIncrement (atomic);
+}
+
+gboolean
+(g_atomic_int_dec_and_test) (volatile gint *atomic)
+{
+ return InterlockedDecrement (atomic) == 0;
+}
+
+gboolean
+(g_atomic_int_compare_and_exchange) (volatile gint *atomic,
+ gint oldval,
+ gint newval)
+{
+ return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
+}
+
+gint
+(g_atomic_int_add) (volatile gint *atomic,
+ gint val)
+{
+ return InterlockedExchangeAdd (atomic, val);
+}
+
+guint
+(g_atomic_int_and) (volatile guint *atomic,
+ guint val)
+{
+ return InterlockedAnd (atomic, val);
+}
+
+guint
+(g_atomic_int_or) (volatile guint *atomic,
+ guint val)
+{
+ return InterlockedOr (atomic, val);
+}
+
+guint
+(g_atomic_int_xor) (volatile guint *atomic,
+ guint val)
+{
+ return InterlockedXor (atomic, val);
+}
+
+
+gpointer
+(g_atomic_pointer_get) (const volatile void *atomic)
+{
+ const gpointer *ptr = atomic;
+
+ MemoryBarrier ();
+ return *ptr;
+}
+
+void
+(g_atomic_pointer_set) (volatile void *atomic,
+ gpointer newval)
+{
+ gpointer *ptr = atomic;
+
+ *ptr = newval;
+ MemoryBarrier ();
+}
+
+gboolean
+(g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
+ gpointer oldval,
+ gpointer newval)
+{
+ return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval;
+}
+
+gssize
+(g_atomic_pointer_add) (volatile void *atomic,
+ gssize val)
+{
+#if GLIB_SIZEOF_VOID_P == 8
+ return InterlockedExchangeAdd64 (atomic, val);
+#else
+ return InterlockedExchangeAdd (atomic, val);
+#endif
+}
+
+gsize
+(g_atomic_pointer_and) (volatile void *atomic,
+ gsize val)
+{
+#if GLIB_SIZEOF_VOID_P == 8
+ return InterlockedAnd64 (atomic, val);
+#else
+ return InterlockedAnd (atomic, val);
+#endif
+}
+
+gsize
+(g_atomic_pointer_or) (volatile void *atomic,
+ gsize val)
+{
+#if GLIB_SIZEOF_VOID_P == 8
+ return InterlockedOr64 (atomic, val);
+#else
+ return InterlockedOr (atomic, val);
+#endif
+}
+
+gsize
+(g_atomic_pointer_xor) (volatile void *atomic,
+ gsize val)
+{
+#if GLIB_SIZEOF_VOID_P == 8
+ return InterlockedXor64 (atomic, val);
+#else
+ return InterlockedXor (atomic, val);
+#endif
+}
+#else
+
+/* This error occurs when `meson configure` decided that we should be capable
+ * of lock-free atomics but we find at compile-time that we are not.
+ */
+#error G_ATOMIC_LOCK_FREE defined, but incapable of lock-free atomics.
+
+#endif /* defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */
+
+#else /* G_ATOMIC_LOCK_FREE */
+
+/* We are not permitted to call into any GLib functions from here, so we
+ * can not use GMutex.
+ *
+ * Fortunately, we already take care of the Windows case above, and all
+ * non-Windows platforms on which glib runs have pthreads. Use those.
+ */
+#include
+
+static pthread_mutex_t g_atomic_lock = PTHREAD_MUTEX_INITIALIZER;
+
+gint
+(g_atomic_int_get) (const volatile gint *atomic)
+{
+ gint value;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ value = *atomic;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return value;
+}
+
+void
+(g_atomic_int_set) (volatile gint *atomic,
+ gint value)
+{
+ pthread_mutex_lock (&g_atomic_lock);
+ *atomic = value;
+ pthread_mutex_unlock (&g_atomic_lock);
+}
+
+void
+(g_atomic_int_inc) (volatile gint *atomic)
+{
+ pthread_mutex_lock (&g_atomic_lock);
+ (*atomic)++;
+ pthread_mutex_unlock (&g_atomic_lock);
+}
+
+gboolean
+(g_atomic_int_dec_and_test) (volatile gint *atomic)
+{
+ gboolean is_zero;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ is_zero = --(*atomic) == 0;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return is_zero;
+}
+
+gboolean
+(g_atomic_int_compare_and_exchange) (volatile gint *atomic,
+ gint oldval,
+ gint newval)
+{
+ gboolean success;
+
+ pthread_mutex_lock (&g_atomic_lock);
+
+ if ((success = (*atomic == oldval)))
+ *atomic = newval;
+
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return success;
+}
+
+gint
+(g_atomic_int_add) (volatile gint *atomic,
+ gint val)
+{
+ gint oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *atomic;
+ *atomic = oldval + val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+guint
+(g_atomic_int_and) (volatile guint *atomic,
+ guint val)
+{
+ guint oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *atomic;
+ *atomic = oldval & val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+guint
+(g_atomic_int_or) (volatile guint *atomic,
+ guint val)
+{
+ guint oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *atomic;
+ *atomic = oldval | val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+guint
+(g_atomic_int_xor) (volatile guint *atomic,
+ guint val)
+{
+ guint oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *atomic;
+ *atomic = oldval ^ val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+
+gpointer
+(g_atomic_pointer_get) (const volatile void *atomic)
+{
+ const gpointer *ptr = atomic;
+ gpointer value;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ value = *ptr;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return value;
+}
+
+void
+(g_atomic_pointer_set) (volatile void *atomic,
+ gpointer newval)
+{
+ gpointer *ptr = atomic;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ *ptr = newval;
+ pthread_mutex_unlock (&g_atomic_lock);
+}
+
+gboolean
+(g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
+ gpointer oldval,
+ gpointer newval)
+{
+ gpointer *ptr = atomic;
+ gboolean success;
+
+ pthread_mutex_lock (&g_atomic_lock);
+
+ if ((success = (*ptr == oldval)))
+ *ptr = newval;
+
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return success;
+}
+
+gssize
+(g_atomic_pointer_add) (volatile void *atomic,
+ gssize val)
+{
+ gssize *ptr = atomic;
+ gssize oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *ptr;
+ *ptr = oldval + val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+gsize
+(g_atomic_pointer_and) (volatile void *atomic,
+ gsize val)
+{
+ gsize *ptr = atomic;
+ gsize oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *ptr;
+ *ptr = oldval & val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+gsize
+(g_atomic_pointer_or) (volatile void *atomic,
+ gsize val)
+{
+ gsize *ptr = atomic;
+ gsize oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *ptr;
+ *ptr = oldval | val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+gsize
+(g_atomic_pointer_xor) (volatile void *atomic,
+ gsize val)
+{
+ gsize *ptr = atomic;
+ gsize oldval;
+
+ pthread_mutex_lock (&g_atomic_lock);
+ oldval = *ptr;
+ *ptr = oldval ^ val;
+ pthread_mutex_unlock (&g_atomic_lock);
+
+ return oldval;
+}
+
+#endif
+
+/**
+ * g_atomic_int_exchange_and_add:
+ * @atomic: a pointer to a #gint
+ * @val: the value to add
+ *
+ * This function existed before g_atomic_int_add() returned the prior
+ * value of the integer (which it now does). It is retained only for
+ * compatibility reasons. Don't use this function in new code.
+ *
+ * Returns: the value of @atomic before the add, signed
+ * Since: 2.4
+ * Deprecated: 2.30: Use g_atomic_int_add() instead.
+ **/
+gint
+g_atomic_int_exchange_and_add (volatile gint *atomic,
+ gint val)
+{
+ return (g_atomic_int_add) ((gint *) atomic, val);
+}
diff --git a/glib/gatomic.h b/glib/gatomic.h
new file mode 100644
index 0000000000000000000000000000000000000000..7bb44430a94bab085e5d414a67738734cc016760
--- /dev/null
+++ b/glib/gatomic.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright © 2011 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Ryan Lortie
+ */
+
+#ifndef __G_ATOMIC_H__
+#define __G_ATOMIC_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_ALL
+gint g_atomic_int_get (const volatile gint *atomic);
+GLIB_AVAILABLE_IN_ALL
+void g_atomic_int_set (volatile gint *atomic,
+ gint newval);
+GLIB_AVAILABLE_IN_ALL
+void g_atomic_int_inc (volatile gint *atomic);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_atomic_int_dec_and_test (volatile gint *atomic);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_atomic_int_compare_and_exchange (volatile gint *atomic,
+ gint oldval,
+ gint newval);
+GLIB_AVAILABLE_IN_ALL
+gint g_atomic_int_add (volatile gint *atomic,
+ gint val);
+GLIB_AVAILABLE_IN_2_30
+guint g_atomic_int_and (volatile guint *atomic,
+ guint val);
+GLIB_AVAILABLE_IN_2_30
+guint g_atomic_int_or (volatile guint *atomic,
+ guint val);
+GLIB_AVAILABLE_IN_ALL
+guint g_atomic_int_xor (volatile guint *atomic,
+ guint val);
+
+GLIB_AVAILABLE_IN_ALL
+gpointer g_atomic_pointer_get (const volatile void *atomic);
+GLIB_AVAILABLE_IN_ALL
+void g_atomic_pointer_set (volatile void *atomic,
+ gpointer newval);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_atomic_pointer_compare_and_exchange (volatile void *atomic,
+ gpointer oldval,
+ gpointer newval);
+GLIB_AVAILABLE_IN_ALL
+gssize g_atomic_pointer_add (volatile void *atomic,
+ gssize val);
+GLIB_AVAILABLE_IN_2_30
+gsize g_atomic_pointer_and (volatile void *atomic,
+ gsize val);
+GLIB_AVAILABLE_IN_2_30
+gsize g_atomic_pointer_or (volatile void *atomic,
+ gsize val);
+GLIB_AVAILABLE_IN_ALL
+gsize g_atomic_pointer_xor (volatile void *atomic,
+ gsize val);
+
+GLIB_DEPRECATED_IN_2_30_FOR(g_atomic_int_add)
+gint g_atomic_int_exchange_and_add (volatile gint *atomic,
+ gint val);
+
+G_END_DECLS
+
+#if defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+
+/* We prefer the new C11-style atomic extension of GCC if available */
+/* OHOS_GLIB_COMPATIBLE
+ * ohos.glib.compatible.001: glib 2.62.5 update 2.68.1 Incompatible with gstreamer 1.16.2
+ * static volatile gsize _init_once = 0; // Conflicts with volatile,
+ * if (g_once_init_enter (&_init_once))
+ * add "&& !defined(__clang__)"
+ */
+#if defined(__ATOMIC_SEQ_CST) && !defined(__clang__)
+
+#define g_atomic_int_get(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ gint gaig_temp; \
+ (void) (0 ? *(atomic) ^ *(atomic) : 1); \
+ __atomic_load ((gint *)(atomic), &gaig_temp, __ATOMIC_SEQ_CST); \
+ (gint) gaig_temp; \
+ }))
+#define g_atomic_int_set(atomic, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ gint gais_temp = (gint) (newval); \
+ (void) (0 ? *(atomic) ^ (newval) : 1); \
+ __atomic_store ((gint *)(atomic), &gais_temp, __ATOMIC_SEQ_CST); \
+ }))
+
+#if defined(glib_typeof)
+#define g_atomic_pointer_get(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ glib_typeof (*(atomic)) gapg_temp_newval; \
+ glib_typeof ((atomic)) gapg_temp_atomic = (atomic); \
+ __atomic_load (gapg_temp_atomic, &gapg_temp_newval, __ATOMIC_SEQ_CST); \
+ gapg_temp_newval; \
+ }))
+#define g_atomic_pointer_set(atomic, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ glib_typeof ((atomic)) gaps_temp_atomic = (atomic); \
+ glib_typeof (*(atomic)) gaps_temp_newval = (newval); \
+ (void) (0 ? (gpointer) * (atomic) : NULL); \
+ __atomic_store (gaps_temp_atomic, &gaps_temp_newval, __ATOMIC_SEQ_CST); \
+ }))
+#else /* if !(defined(glib_typeof) */
+#define g_atomic_pointer_get(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ gpointer gapg_temp_newval; \
+ gpointer *gapg_temp_atomic = (gpointer *)(atomic); \
+ __atomic_load (gapg_temp_atomic, &gapg_temp_newval, __ATOMIC_SEQ_CST); \
+ gapg_temp_newval; \
+ }))
+#define g_atomic_pointer_set(atomic, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ gpointer *gaps_temp_atomic = (gpointer *)(atomic); \
+ gpointer gaps_temp_newval = (gpointer)(newval); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ __atomic_store (gaps_temp_atomic, &gaps_temp_newval, __ATOMIC_SEQ_CST); \
+ }))
+#endif /* if defined(glib_typeof) */
+
+#define g_atomic_int_inc(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ *(atomic) : 1); \
+ (void) __atomic_fetch_add ((atomic), 1, __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_int_dec_and_test(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ *(atomic) : 1); \
+ __atomic_fetch_sub ((atomic), 1, __ATOMIC_SEQ_CST) == 1; \
+ }))
+#if defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L
+/* See comments below about equivalent g_atomic_pointer_compare_and_exchange()
+ * shenanigans for type-safety when compiling in C++ mode. */
+#define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
+ (G_GNUC_EXTENSION ({ \
+ glib_typeof (*(atomic)) gaicae_oldval = (oldval); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (newval) ^ (oldval) : 1); \
+ __atomic_compare_exchange_n ((atomic), &gaicae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
+ }))
+#else /* if !(defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L) */
+#define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
+ (G_GNUC_EXTENSION ({ \
+ gint gaicae_oldval = (oldval); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (newval) ^ (oldval) : 1); \
+ __atomic_compare_exchange_n ((atomic), (void *) (&(gaicae_oldval)), (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
+ }))
+#endif /* defined(glib_typeof) */
+#define g_atomic_int_add(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (gint) __atomic_fetch_add ((atomic), (val), __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_int_and(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (guint) __atomic_fetch_and ((atomic), (val), __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_int_or(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (guint) __atomic_fetch_or ((atomic), (val), __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_int_xor(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (guint) __atomic_fetch_xor ((atomic), (val), __ATOMIC_SEQ_CST); \
+ }))
+
+#if defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L
+/* This is typesafe because we check we can assign oldval to the type of
+ * (*atomic). Unfortunately it can only be done in C++ because gcc/clang warn
+ * when atomic is volatile and not oldval, or when atomic is gsize* and oldval
+ * is NULL. Note that clang++ force us to be typesafe because it is an error if the 2nd
+ * argument of __atomic_compare_exchange_n() has a different type than the
+ * first.
+ * https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1919
+ * https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1715#note_1024120. */
+#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof (oldval) == sizeof (gpointer)); \
+ glib_typeof (*(atomic)) gapcae_oldval = (oldval); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ __atomic_compare_exchange_n ((atomic), &gapcae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
+ }))
+#else /* if !(defined(glib_typeof) && defined(__cplusplus) && __cplusplus >= 201103L) */
+#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof (oldval) == sizeof (gpointer)); \
+ gpointer gapcae_oldval = (gpointer)(oldval); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ __atomic_compare_exchange_n ((atomic), (void *) (&(gapcae_oldval)), (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
+ }))
+#endif /* defined(glib_typeof) */
+#define g_atomic_pointer_add(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gssize) __atomic_fetch_add ((atomic), (val), __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_pointer_and(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ gsize *gapa_atomic = (gsize *) (atomic); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gsize)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gsize) __atomic_fetch_and (gapa_atomic, (val), __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_pointer_or(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ gsize *gapo_atomic = (gsize *) (atomic); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gsize)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gsize) __atomic_fetch_or (gapo_atomic, (val), __ATOMIC_SEQ_CST); \
+ }))
+#define g_atomic_pointer_xor(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ gsize *gapx_atomic = (gsize *) (atomic); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gsize)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gsize) __atomic_fetch_xor (gapx_atomic, (val), __ATOMIC_SEQ_CST); \
+ }))
+
+#else /* defined(__ATOMIC_SEQ_CST) */
+
+/* We want to achieve __ATOMIC_SEQ_CST semantics here. See
+ * https://en.cppreference.com/w/c/atomic/memory_order#Constants. For load
+ * operations, that means performing an *acquire*:
+ * > A load operation with this memory order performs the acquire operation on
+ * > the affected memory location: no reads or writes in the current thread can
+ * > be reordered before this load. All writes in other threads that release
+ * > the same atomic variable are visible in the current thread.
+ *
+ * “no reads or writes in the current thread can be reordered before this load”
+ * is implemented using a compiler barrier (a no-op `__asm__` section) to
+ * prevent instruction reordering. Writes in other threads are synchronised
+ * using `__sync_synchronize()`. It’s unclear from the GCC documentation whether
+ * `__sync_synchronize()` acts as a compiler barrier, hence our explicit use of
+ * one.
+ *
+ * For store operations, `__ATOMIC_SEQ_CST` means performing a *release*:
+ * > A store operation with this memory order performs the release operation:
+ * > no reads or writes in the current thread can be reordered after this store.
+ * > All writes in the current thread are visible in other threads that acquire
+ * > the same atomic variable (see Release-Acquire ordering below) and writes
+ * > that carry a dependency into the atomic variable become visible in other
+ * > threads that consume the same atomic (see Release-Consume ordering below).
+ *
+ * “no reads or writes in the current thread can be reordered after this store”
+ * is implemented using a compiler barrier to prevent instruction reordering.
+ * “All writes in the current thread are visible in other threads” is implemented
+ * using `__sync_synchronize()`; similarly for “writes that carry a dependency”.
+ */
+#define g_atomic_int_get(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ gint gaig_result; \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ *(atomic) : 1); \
+ gaig_result = (gint) *(atomic); \
+ __sync_synchronize (); \
+ __asm__ __volatile__ ("" : : : "memory"); \
+ gaig_result; \
+ }))
+#define g_atomic_int_set(atomic, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (newval) : 1); \
+ __sync_synchronize (); \
+ __asm__ __volatile__ ("" : : : "memory"); \
+ *(atomic) = (newval); \
+ }))
+#define g_atomic_pointer_get(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ gpointer gapg_result; \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ gapg_result = (gpointer) *(atomic); \
+ __sync_synchronize (); \
+ __asm__ __volatile__ ("" : : : "memory"); \
+ gapg_result; \
+ }))
+#if defined(glib_typeof)
+#define g_atomic_pointer_set(atomic, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ __sync_synchronize (); \
+ __asm__ __volatile__ ("" : : : "memory"); \
+ *(atomic) = (glib_typeof (*(atomic))) (gsize) (newval); \
+ }))
+#else /* if !(defined(glib_typeof) */
+#define g_atomic_pointer_set(atomic, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ __sync_synchronize (); \
+ __asm__ __volatile__ ("" : : : "memory"); \
+ *(atomic) = (gpointer) (gsize) (newval); \
+ }))
+#endif /* if defined(glib_typeof) */
+
+#define g_atomic_int_inc(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ *(atomic) : 1); \
+ (void) __sync_fetch_and_add ((atomic), 1); \
+ }))
+#define g_atomic_int_dec_and_test(atomic) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ *(atomic) : 1); \
+ __sync_fetch_and_sub ((atomic), 1) == 1; \
+ }))
+#define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (newval) ^ (oldval) : 1); \
+ __sync_bool_compare_and_swap ((atomic), (oldval), (newval)) ? TRUE : FALSE; \
+ }))
+#define g_atomic_int_add(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (gint) __sync_fetch_and_add ((atomic), (val)); \
+ }))
+#define g_atomic_int_and(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (guint) __sync_fetch_and_and ((atomic), (val)); \
+ }))
+#define g_atomic_int_or(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (guint) __sync_fetch_and_or ((atomic), (val)); \
+ }))
+#define g_atomic_int_xor(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
+ (void) (0 ? *(atomic) ^ (val) : 1); \
+ (guint) __sync_fetch_and_xor ((atomic), (val)); \
+ }))
+
+#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ __sync_bool_compare_and_swap ((atomic), (oldval), (newval)) ? TRUE : FALSE; \
+ }))
+#define g_atomic_pointer_add(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gssize) __sync_fetch_and_add ((atomic), (val)); \
+ }))
+#define g_atomic_pointer_and(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gsize) __sync_fetch_and_and ((atomic), (val)); \
+ }))
+#define g_atomic_pointer_or(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gsize) __sync_fetch_and_or ((atomic), (val)); \
+ }))
+#define g_atomic_pointer_xor(atomic, val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gpointer)); \
+ (void) (0 ? (gpointer) *(atomic) : NULL); \
+ (void) (0 ? (val) ^ (val) : 1); \
+ (gsize) __sync_fetch_and_xor ((atomic), (val)); \
+ }))
+
+#endif /* !defined(__ATOMIC_SEQ_CST) */
+
+#else /* defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */
+
+#define g_atomic_int_get(atomic) \
+ (g_atomic_int_get ((gint *) (atomic)))
+#define g_atomic_int_set(atomic, newval) \
+ (g_atomic_int_set ((gint *) (atomic), (gint) (newval)))
+#define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \
+ (g_atomic_int_compare_and_exchange ((gint *) (atomic), (oldval), (newval)))
+#define g_atomic_int_add(atomic, val) \
+ (g_atomic_int_add ((gint *) (atomic), (val)))
+#define g_atomic_int_and(atomic, val) \
+ (g_atomic_int_and ((guint *) (atomic), (val)))
+#define g_atomic_int_or(atomic, val) \
+ (g_atomic_int_or ((guint *) (atomic), (val)))
+#define g_atomic_int_xor(atomic, val) \
+ (g_atomic_int_xor ((guint *) (atomic), (val)))
+#define g_atomic_int_inc(atomic) \
+ (g_atomic_int_inc ((gint *) (atomic)))
+#define g_atomic_int_dec_and_test(atomic) \
+ (g_atomic_int_dec_and_test ((gint *) (atomic)))
+
+#if defined(glib_typeof)
+ /* The (void *) cast in the middle *looks* redundant, because
+ * g_atomic_pointer_get returns void * already, but it's to silence
+ * -Werror=bad-function-cast when we're doing something like:
+ * guintptr a, b; ...; a = g_atomic_pointer_get (&b);
+ * which would otherwise be assigning the void * result of
+ * g_atomic_pointer_get directly to the pointer-sized but
+ * non-pointer-typed result. */
+#define g_atomic_pointer_get(atomic) \
+ (glib_typeof (*(atomic))) (void *) ((g_atomic_pointer_get) ((void *) atomic))
+#else /* !(defined(glib_typeof) */
+#define g_atomic_pointer_get(atomic) \
+ (g_atomic_pointer_get (atomic))
+#endif
+
+#define g_atomic_pointer_set(atomic, newval) \
+ (g_atomic_pointer_set ((atomic), (gpointer) (newval)))
+
+#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
+ (g_atomic_pointer_compare_and_exchange ((atomic), (gpointer) (oldval), (gpointer) (newval)))
+#define g_atomic_pointer_add(atomic, val) \
+ (g_atomic_pointer_add ((atomic), (gssize) (val)))
+#define g_atomic_pointer_and(atomic, val) \
+ (g_atomic_pointer_and ((atomic), (gsize) (val)))
+#define g_atomic_pointer_or(atomic, val) \
+ (g_atomic_pointer_or ((atomic), (gsize) (val)))
+#define g_atomic_pointer_xor(atomic, val) \
+ (g_atomic_pointer_xor ((atomic), (gsize) (val)))
+
+#endif /* defined(G_ATOMIC_LOCK_FREE) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */
+
+#endif /* __G_ATOMIC_H__ */
diff --git a/glib/gbacktrace.c b/glib/gbacktrace.c
new file mode 100644
index 0000000000000000000000000000000000000000..77cef100a5aafea0682c0052dc63033a7b7899cd
--- /dev/null
+++ b/glib/gbacktrace.c
@@ -0,0 +1,455 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
+ * then
+ */
+
+#include "config.h"
+#include "glibconfig.h"
+
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_SYS_TIME_H
+#include
+#endif
+#include
+
+#include
+
+#ifdef G_OS_UNIX
+#include
+#include
+#ifdef HAVE_SYS_SELECT_H
+#include
+#endif /* HAVE_SYS_SELECT_H */
+#endif
+
+#include
+
+#ifdef G_OS_WIN32
+# define STRICT /* Strict typing, please */
+# define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
+# include
+# undef STRICT
+#else
+# include
+#endif
+
+#include "gbacktrace.h"
+
+#include "gtypes.h"
+#include "gmain.h"
+#include "gprintfint.h"
+#include "gunicode.h"
+#include "gutils.h"
+
+#ifndef G_OS_WIN32
+static void stack_trace (const char * const *args);
+#endif
+
+/* Default to using LLDB for backtraces on macOS. */
+#ifdef __APPLE__
+#define USE_LLDB
+#endif
+
+#ifdef USE_LLDB
+#define DEBUGGER "lldb"
+#else
+#define DEBUGGER "gdb"
+#endif
+
+/* People want to hit this from their debugger... */
+GLIB_AVAILABLE_IN_ALL volatile gboolean glib_on_error_halt;
+volatile gboolean glib_on_error_halt = TRUE;
+
+/**
+ * g_on_error_query:
+ * @prg_name: the program name, needed by gdb for the "[S]tack trace"
+ * option. If @prg_name is %NULL, g_get_prgname() is called to get
+ * the program name (which will work correctly if gdk_init() or
+ * gtk_init() has been called)
+ *
+ * Prompts the user with
+ * `[E]xit, [H]alt, show [S]tack trace or [P]roceed`.
+ * This function is intended to be used for debugging use only.
+ * The following example shows how it can be used together with
+ * the g_log() functions.
+ *
+ * |[
+ * #include
+ *
+ * static void
+ * log_handler (const gchar *log_domain,
+ * GLogLevelFlags log_level,
+ * const gchar *message,
+ * gpointer user_data)
+ * {
+ * g_log_default_handler (log_domain, log_level, message, user_data);
+ *
+ * g_on_error_query (MY_PROGRAM_NAME);
+ * }
+ *
+ * int
+ * main (int argc, char *argv[])
+ * {
+ * g_log_set_handler (MY_LOG_DOMAIN,
+ * G_LOG_LEVEL_WARNING |
+ * G_LOG_LEVEL_ERROR |
+ * G_LOG_LEVEL_CRITICAL,
+ * log_handler,
+ * NULL);
+ * ...
+ * ]|
+ *
+ * If "[E]xit" is selected, the application terminates with a call
+ * to _exit(0).
+ *
+ * If "[S]tack" trace is selected, g_on_error_stack_trace() is called.
+ * This invokes gdb, which attaches to the current process and shows
+ * a stack trace. The prompt is then shown again.
+ *
+ * If "[P]roceed" is selected, the function returns.
+ *
+ * This function may cause different actions on non-UNIX platforms.
+ *
+ * On Windows consider using the `G_DEBUGGER` environment
+ * variable (see [Running GLib Applications](glib-running.html)) and
+ * calling g_on_error_stack_trace() instead.
+ */
+void
+g_on_error_query (const gchar *prg_name)
+{
+#ifndef G_OS_WIN32
+ static const gchar * const query1 = "[E]xit, [H]alt";
+ static const gchar * const query2 = ", show [S]tack trace";
+ static const gchar * const query3 = " or [P]roceed";
+ gchar buf[16];
+
+ if (!prg_name)
+ prg_name = g_get_prgname ();
+
+ retry:
+
+ if (prg_name)
+ _g_fprintf (stdout,
+ "%s (pid:%u): %s%s%s: ",
+ prg_name,
+ (guint) getpid (),
+ query1,
+ query2,
+ query3);
+ else
+ _g_fprintf (stdout,
+ "(process:%u): %s%s: ",
+ (guint) getpid (),
+ query1,
+ query3);
+ fflush (stdout);
+
+ if (isatty(0) && isatty(1))
+ fgets (buf, 8, stdin);
+ else
+ strcpy (buf, "E\n");
+
+ if ((buf[0] == 'E' || buf[0] == 'e')
+ && buf[1] == '\n')
+ _exit (0);
+ else if ((buf[0] == 'P' || buf[0] == 'p')
+ && buf[1] == '\n')
+ return;
+ else if (prg_name
+ && (buf[0] == 'S' || buf[0] == 's')
+ && buf[1] == '\n')
+ {
+ g_on_error_stack_trace (prg_name);
+ goto retry;
+ }
+ else if ((buf[0] == 'H' || buf[0] == 'h')
+ && buf[1] == '\n')
+ {
+ while (glib_on_error_halt)
+ ;
+ glib_on_error_halt = TRUE;
+ return;
+ }
+ else
+ goto retry;
+#else
+ if (!prg_name)
+ prg_name = g_get_prgname ();
+
+ /* MessageBox is allowed on UWP apps only when building against
+ * the debug CRT, which will set -D_DEBUG */
+#if defined(_DEBUG) || !defined(G_WINAPI_ONLY_APP)
+ {
+ WCHAR *caption = NULL;
+
+ if (prg_name && *prg_name)
+ {
+ caption = g_utf8_to_utf16 (prg_name, -1, NULL, NULL, NULL);
+ }
+
+ MessageBoxW (NULL, L"g_on_error_query called, program terminating",
+ caption,
+ MB_OK|MB_ICONERROR);
+
+ g_free (caption);
+ }
+#else
+ printf ("g_on_error_query called, program '%s' terminating\n",
+ (prg_name && *prg_name) ? prg_name : "(null)");
+#endif
+ _exit(0);
+#endif
+}
+
+/**
+ * g_on_error_stack_trace:
+ * @prg_name: the program name, needed by gdb for the "[S]tack trace"
+ * option
+ *
+ * Invokes gdb, which attaches to the current process and shows a
+ * stack trace. Called by g_on_error_query() when the "[S]tack trace"
+ * option is selected. You can get the current process's program name
+ * with g_get_prgname(), assuming that you have called gtk_init() or
+ * gdk_init().
+ *
+ * This function may cause different actions on non-UNIX platforms.
+ *
+ * When running on Windows, this function is *not* called by
+ * g_on_error_query(). If called directly, it will raise an
+ * exception, which will crash the program. If the `G_DEBUGGER` environment
+ * variable is set, a debugger will be invoked to attach and
+ * handle that exception (see [Running GLib Applications](glib-running.html)).
+ */
+void
+g_on_error_stack_trace (const gchar *prg_name)
+{
+#if defined(G_OS_UNIX)
+ pid_t pid;
+ gchar buf[16];
+ const gchar *args[5] = { DEBUGGER, NULL, NULL, NULL, NULL };
+ int status;
+
+ if (!prg_name)
+ return;
+
+ _g_sprintf (buf, "%u", (guint) getpid ());
+
+#ifdef USE_LLDB
+ args[1] = prg_name;
+ args[2] = "-p";
+ args[3] = buf;
+#else
+ args[1] = prg_name;
+ args[2] = buf;
+#endif
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ stack_trace (args);
+ _exit (0);
+ }
+ else if (pid == (pid_t) -1)
+ {
+ perror ("unable to fork " DEBUGGER);
+ return;
+ }
+
+ /* Wait until the child really terminates. On Mac OS X waitpid ()
+ * will also return when the child is being stopped due to tracing.
+ */
+ while (1)
+ {
+ pid_t retval = waitpid (pid, &status, 0);
+ if (WIFEXITED (retval) || WIFSIGNALED (retval))
+ break;
+ }
+#else
+ if (IsDebuggerPresent ())
+ G_BREAKPOINT ();
+ else
+ g_abort ();
+#endif
+}
+
+#ifndef G_OS_WIN32
+
+static gboolean stack_trace_done = FALSE;
+
+static void
+stack_trace_sigchld (int signum)
+{
+ stack_trace_done = TRUE;
+}
+
+#define BUFSIZE 1024
+
+static void
+stack_trace (const char * const *args)
+{
+ pid_t pid;
+ int in_fd[2];
+ int out_fd[2];
+ fd_set fdset;
+ fd_set readset;
+ struct timeval tv;
+ int sel, idx, state;
+#ifdef USE_LLDB
+ int line_idx;
+#endif
+ char buffer[BUFSIZE];
+ char c;
+
+ stack_trace_done = FALSE;
+ signal (SIGCHLD, stack_trace_sigchld);
+
+ if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
+ {
+ perror ("unable to open pipe");
+ _exit (0);
+ }
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Save stderr for printing failure below */
+ int old_err = dup (2);
+ if (old_err != -1)
+ {
+ int getfd = fcntl (old_err, F_GETFD);
+ if (getfd != -1)
+ (void) fcntl (old_err, F_SETFD, getfd | FD_CLOEXEC);
+ }
+
+ close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
+ close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
+ close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
+
+ execvp (args[0], (char **) args); /* exec gdb */
+
+ /* Print failure to original stderr */
+ if (old_err != -1)
+ {
+ close (2);
+ dup (old_err);
+ }
+ perror ("exec " DEBUGGER " failed");
+ _exit (0);
+ }
+ else if (pid == (pid_t) -1)
+ {
+ perror ("unable to fork");
+ _exit (0);
+ }
+
+ FD_ZERO (&fdset);
+ FD_SET (out_fd[0], &fdset);
+
+#ifdef USE_LLDB
+ write (in_fd[1], "bt\n", 3);
+ write (in_fd[1], "p x = 0\n", 8);
+ write (in_fd[1], "process detach\n", 15);
+ write (in_fd[1], "quit\n", 5);
+#else
+ write (in_fd[1], "backtrace\n", 10);
+ write (in_fd[1], "p x = 0\n", 8);
+ write (in_fd[1], "quit\n", 5);
+#endif
+
+ idx = 0;
+#ifdef USE_LLDB
+ line_idx = 0;
+#endif
+ state = 0;
+
+ while (1)
+ {
+ readset = fdset;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
+ if (sel == -1)
+ break;
+
+ if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
+ {
+ if (read (out_fd[0], &c, 1))
+ {
+#ifdef USE_LLDB
+ line_idx += 1;
+#endif
+
+ switch (state)
+ {
+ case 0:
+#ifdef USE_LLDB
+ if (c == '*' || (c == ' ' && line_idx == 1))
+#else
+ if (c == '#')
+#endif
+ {
+ state = 1;
+ idx = 0;
+ buffer[idx++] = c;
+ }
+ break;
+ case 1:
+ if (idx < BUFSIZE)
+ buffer[idx++] = c;
+ if ((c == '\n') || (c == '\r'))
+ {
+ buffer[idx] = 0;
+ _g_fprintf (stdout, "%s", buffer);
+ state = 0;
+ idx = 0;
+#ifdef USE_LLDB
+ line_idx = 0;
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if (stack_trace_done)
+ break;
+ }
+
+ close (in_fd[0]);
+ close (in_fd[1]);
+ close (out_fd[0]);
+ close (out_fd[1]);
+ _exit (0);
+}
+
+#endif /* !G_OS_WIN32 */
diff --git a/glib/gbacktrace.h b/glib/gbacktrace.h
new file mode 100644
index 0000000000000000000000000000000000000000..09b8ccbd3d87e3064f4952a86af89e3933f4b57e
--- /dev/null
+++ b/glib/gbacktrace.h
@@ -0,0 +1,72 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_BACKTRACE_H__
+#define __G_BACKTRACE_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+#ifdef __sun__
+#include
+#endif
+#include
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_ALL
+void g_on_error_query (const gchar *prg_name);
+GLIB_AVAILABLE_IN_ALL
+void g_on_error_stack_trace (const gchar *prg_name);
+
+/**
+ * G_BREAKPOINT:
+ *
+ * Inserts a breakpoint instruction into the code.
+ *
+ * On architectures which support it, this is implemented as a soft interrupt
+ * and on other architectures it raises a `SIGTRAP` signal.
+ *
+ * `SIGTRAP` is used rather than abort() to allow breakpoints to be skipped past
+ * in a debugger if they are not the desired target of debugging.
+ */
+#if (defined (__i386__) || defined (__x86_64__)) && defined (__GNUC__) && __GNUC__ >= 2
+# define G_BREAKPOINT() G_STMT_START{ __asm__ __volatile__ ("int $03"); }G_STMT_END
+#elif (defined (_MSC_VER) || defined (__DMC__)) && defined (_M_IX86)
+# define G_BREAKPOINT() G_STMT_START{ __asm int 3h }G_STMT_END
+#elif defined (_MSC_VER)
+# define G_BREAKPOINT() G_STMT_START{ __debugbreak(); }G_STMT_END
+#elif defined (__alpha__) && !defined(__osf__) && defined (__GNUC__) && __GNUC__ >= 2
+# define G_BREAKPOINT() G_STMT_START{ __asm__ __volatile__ ("bpt"); }G_STMT_END
+#elif defined (__APPLE__) || (defined(_WIN32) && (defined(__clang__) || defined(__GNUC__)))
+# define G_BREAKPOINT() G_STMT_START{ __builtin_trap(); }G_STMT_END
+#else /* !__i386__ && !__alpha__ */
+# define G_BREAKPOINT() G_STMT_START{ raise (SIGTRAP); }G_STMT_END
+#endif /* __i386__ */
+
+G_END_DECLS
+
+#endif /* __G_BACKTRACE_H__ */
diff --git a/glib/gbase64.c b/glib/gbase64.c
new file mode 100644
index 0000000000000000000000000000000000000000..f2d110e2e9a1c0e14d9b0552e073b1dd4ae6c26c
--- /dev/null
+++ b/glib/gbase64.c
@@ -0,0 +1,462 @@
+/* gbase64.c - Base64 encoding/decoding
+ *
+ * Copyright (C) 2006 Alexander Larsson
+ * Copyright (C) 2000-2003 Ximian Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ *
+ * This is based on code in camel, written by:
+ * Michael Zucchi
+ * Jeffrey Stedfast
+ */
+
+#include "config.h"
+
+#include
+
+#include "gbase64.h"
+#include "gtestutils.h"
+#include "glibintl.h"
+
+
+/**
+ * SECTION:base64
+ * @title: Base64 Encoding
+ * @short_description: encodes and decodes data in Base64 format
+ *
+ * Base64 is an encoding that allows a sequence of arbitrary bytes to be
+ * encoded as a sequence of printable ASCII characters. For the definition
+ * of Base64, see
+ * [RFC 1421](http://www.ietf.org/rfc/rfc1421.txt)
+ * or
+ * [RFC 2045](http://www.ietf.org/rfc/rfc2045.txt).
+ * Base64 is most commonly used as a MIME transfer encoding
+ * for email.
+ *
+ * GLib supports incremental encoding using g_base64_encode_step() and
+ * g_base64_encode_close(). Incremental decoding can be done with
+ * g_base64_decode_step(). To encode or decode data in one go, use
+ * g_base64_encode() or g_base64_decode(). To avoid memory allocation when
+ * decoding, you can use g_base64_decode_inplace().
+ *
+ * Support for Base64 encoding has been added in GLib 2.12.
+ */
+
+static const char base64_alphabet[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * g_base64_encode_step:
+ * @in: (array length=len) (element-type guint8): the binary data to encode
+ * @len: the length of @in
+ * @break_lines: whether to break long lines
+ * @out: (out) (array) (element-type guint8): pointer to destination buffer
+ * @state: (inout): Saved state between steps, initialize to 0
+ * @save: (inout): Saved state between steps, initialize to 0
+ *
+ * Incrementally encode a sequence of binary data into its Base-64 stringified
+ * representation. By calling this function multiple times you can convert
+ * data in chunks to avoid having to have the full encoded data in memory.
+ *
+ * When all of the data has been converted you must call
+ * g_base64_encode_close() to flush the saved state.
+ *
+ * The output buffer must be large enough to fit all the data that will
+ * be written to it. Due to the way base64 encodes you will need
+ * at least: (@len / 3 + 1) * 4 + 4 bytes (+ 4 may be needed in case of
+ * non-zero state). If you enable line-breaking you will need at least:
+ * ((@len / 3 + 1) * 4 + 4) / 76 + 1 bytes of extra space.
+ *
+ * @break_lines is typically used when putting base64-encoded data in emails.
+ * It breaks the lines at 76 columns instead of putting all of the text on
+ * the same line. This avoids problems with long lines in the email system.
+ * Note however that it breaks the lines with `LF` characters, not
+ * `CR LF` sequences, so the result cannot be passed directly to SMTP
+ * or certain other protocols.
+ *
+ * Returns: The number of bytes of output that was written
+ *
+ * Since: 2.12
+ */
+gsize
+g_base64_encode_step (const guchar *in,
+ gsize len,
+ gboolean break_lines,
+ gchar *out,
+ gint *state,
+ gint *save)
+{
+ char *outptr;
+ const guchar *inptr;
+
+ g_return_val_if_fail (in != NULL || len == 0, 0);
+ g_return_val_if_fail (out != NULL, 0);
+ g_return_val_if_fail (state != NULL, 0);
+ g_return_val_if_fail (save != NULL, 0);
+
+ if (len == 0)
+ return 0;
+
+ inptr = in;
+ outptr = out;
+
+ if (len + ((char *) save) [0] > 2)
+ {
+ const guchar *inend = in+len-2;
+ int c1, c2, c3;
+ int already;
+
+ already = *state;
+
+ switch (((char *) save) [0])
+ {
+ case 1:
+ c1 = ((unsigned char *) save) [1];
+ goto skip1;
+ case 2:
+ c1 = ((unsigned char *) save) [1];
+ c2 = ((unsigned char *) save) [2];
+ goto skip2;
+ }
+
+ /*
+ * yes, we jump into the loop, no i'm not going to change it,
+ * it's beautiful!
+ */
+ while (inptr < inend)
+ {
+ c1 = *inptr++;
+ skip1:
+ c2 = *inptr++;
+ skip2:
+ c3 = *inptr++;
+ *outptr++ = base64_alphabet [ c1 >> 2 ];
+ *outptr++ = base64_alphabet [ c2 >> 4 |
+ ((c1&0x3) << 4) ];
+ *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
+ (c3 >> 6) ];
+ *outptr++ = base64_alphabet [ c3 & 0x3f ];
+ /* this is a bit ugly ... */
+ if (break_lines && (++already) >= 19)
+ {
+ *outptr++ = '\n';
+ already = 0;
+ }
+ }
+
+ ((char *)save)[0] = 0;
+ len = 2 - (inptr - inend);
+ *state = already;
+ }
+
+ g_assert (len == 0 || len == 1 || len == 2);
+
+ {
+ char *saveout;
+
+ /* points to the slot for the next char to save */
+ saveout = & (((char *)save)[1]) + ((char *)save)[0];
+
+ /* len can only be 0 1 or 2 */
+ switch(len)
+ {
+ case 2:
+ *saveout++ = *inptr++;
+ G_GNUC_FALLTHROUGH;
+ case 1:
+ *saveout++ = *inptr++;
+ }
+ ((char *)save)[0] += len;
+ }
+
+ return outptr - out;
+}
+
+/**
+ * g_base64_encode_close:
+ * @break_lines: whether to break long lines
+ * @out: (out) (array) (element-type guint8): pointer to destination buffer
+ * @state: (inout): Saved state from g_base64_encode_step()
+ * @save: (inout): Saved state from g_base64_encode_step()
+ *
+ * Flush the status from a sequence of calls to g_base64_encode_step().
+ *
+ * The output buffer must be large enough to fit all the data that will
+ * be written to it. It will need up to 4 bytes, or up to 5 bytes if
+ * line-breaking is enabled.
+ *
+ * The @out array will not be automatically nul-terminated.
+ *
+ * Returns: The number of bytes of output that was written
+ *
+ * Since: 2.12
+ */
+gsize
+g_base64_encode_close (gboolean break_lines,
+ gchar *out,
+ gint *state,
+ gint *save)
+{
+ int c1, c2;
+ char *outptr = out;
+
+ g_return_val_if_fail (out != NULL, 0);
+ g_return_val_if_fail (state != NULL, 0);
+ g_return_val_if_fail (save != NULL, 0);
+
+ c1 = ((unsigned char *) save) [1];
+ c2 = ((unsigned char *) save) [2];
+
+ switch (((char *) save) [0])
+ {
+ case 2:
+ outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
+ g_assert (outptr [2] != 0);
+ goto skip;
+ case 1:
+ outptr[2] = '=';
+ c2 = 0; /* saved state here is not relevant */
+ skip:
+ outptr [0] = base64_alphabet [ c1 >> 2 ];
+ outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
+ outptr [3] = '=';
+ outptr += 4;
+ break;
+ }
+ if (break_lines)
+ *outptr++ = '\n';
+
+ *save = 0;
+ *state = 0;
+
+ return outptr - out;
+}
+
+/**
+ * g_base64_encode:
+ * @data: (array length=len) (element-type guint8) (nullable): the binary data to encode
+ * @len: the length of @data
+ *
+ * Encode a sequence of binary data into its Base-64 stringified
+ * representation.
+ *
+ * Returns: (transfer full): a newly allocated, zero-terminated Base-64
+ * encoded string representing @data. The returned string must
+ * be freed with g_free().
+ *
+ * Since: 2.12
+ */
+gchar *
+g_base64_encode (const guchar *data,
+ gsize len)
+{
+ gchar *out;
+ gint state = 0, outlen;
+ gint save = 0;
+
+ g_return_val_if_fail (data != NULL || len == 0, NULL);
+
+ /* We can use a smaller limit here, since we know the saved state is 0,
+ +1 is needed for trailing \0, also check for unlikely integer overflow */
+ g_return_val_if_fail (len < ((G_MAXSIZE - 1) / 4 - 1) * 3, NULL);
+
+ out = g_malloc ((len / 3 + 1) * 4 + 1);
+
+ outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
+ outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save);
+ out[outlen] = '\0';
+
+ return (gchar *) out;
+}
+
+static const unsigned char mime_base64_rank[256] = {
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+};
+
+/**
+ * g_base64_decode_step: (skip)
+ * @in: (array length=len) (element-type guint8): binary input data
+ * @len: max length of @in data to decode
+ * @out: (out caller-allocates) (array) (element-type guint8): output buffer
+ * @state: (inout): Saved state between steps, initialize to 0
+ * @save: (inout): Saved state between steps, initialize to 0
+ *
+ * Incrementally decode a sequence of binary data from its Base-64 stringified
+ * representation. By calling this function multiple times you can convert
+ * data in chunks to avoid having to have the full encoded data in memory.
+ *
+ * The output buffer must be large enough to fit all the data that will
+ * be written to it. Since base64 encodes 3 bytes in 4 chars you need
+ * at least: (@len / 4) * 3 + 3 bytes (+ 3 may be needed in case of non-zero
+ * state).
+ *
+ * Returns: The number of bytes of output that was written
+ *
+ * Since: 2.12
+ **/
+gsize
+g_base64_decode_step (const gchar *in,
+ gsize len,
+ guchar *out,
+ gint *state,
+ guint *save)
+{
+ const guchar *inptr;
+ guchar *outptr;
+ const guchar *inend;
+ guchar c, rank;
+ guchar last[2];
+ unsigned int v;
+ int i;
+
+ g_return_val_if_fail (in != NULL || len == 0, 0);
+ g_return_val_if_fail (out != NULL, 0);
+ g_return_val_if_fail (state != NULL, 0);
+ g_return_val_if_fail (save != NULL, 0);
+
+ if (len == 0)
+ return 0;
+
+ inend = (const guchar *)in+len;
+ outptr = out;
+
+ /* convert 4 base64 bytes to 3 normal bytes */
+ v=*save;
+ i=*state;
+
+ last[0] = last[1] = 0;
+
+ /* we use the sign in the state to determine if we got a padding character
+ in the previous sequence */
+ if (i < 0)
+ {
+ i = -i;
+ last[0] = '=';
+ }
+
+ inptr = (const guchar *)in;
+ while (inptr < inend)
+ {
+ c = *inptr++;
+ rank = mime_base64_rank [c];
+ if (rank != 0xff)
+ {
+ last[1] = last[0];
+ last[0] = c;
+ v = (v<<6) | rank;
+ i++;
+ if (i==4)
+ {
+ *outptr++ = v>>16;
+ if (last[1] != '=')
+ *outptr++ = v>>8;
+ if (last[0] != '=')
+ *outptr++ = v;
+ i=0;
+ }
+ }
+ }
+
+ *save = v;
+ *state = last[0] == '=' ? -i : i;
+
+ return outptr - out;
+}
+
+/**
+ * g_base64_decode:
+ * @text: (not nullable): zero-terminated string with base64 text to decode
+ * @out_len: (out): The length of the decoded data is written here
+ *
+ * Decode a sequence of Base-64 encoded text into binary data. Note
+ * that the returned binary data is not necessarily zero-terminated,
+ * so it should not be used as a character string.
+ *
+ * Returns: (transfer full) (array length=out_len) (element-type guint8):
+ * newly allocated buffer containing the binary data
+ * that @text represents. The returned buffer must
+ * be freed with g_free().
+ *
+ * Since: 2.12
+ */
+guchar *
+g_base64_decode (const gchar *text,
+ gsize *out_len)
+{
+ guchar *ret;
+ gsize input_length;
+ gint state = 0;
+ guint save = 0;
+
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (out_len != NULL, NULL);
+
+ input_length = strlen (text);
+
+ /* We can use a smaller limit here, since we know the saved state is 0,
+ +1 used to avoid calling g_malloc0(0), and hence returning NULL */
+ ret = g_malloc0 ((input_length / 4) * 3 + 1);
+
+ *out_len = g_base64_decode_step (text, input_length, ret, &state, &save);
+
+ return ret;
+}
+
+/**
+ * g_base64_decode_inplace:
+ * @text: (inout) (array length=out_len) (element-type guint8): zero-terminated
+ * string with base64 text to decode
+ * @out_len: (inout): The length of the decoded data is written here
+ *
+ * Decode a sequence of Base-64 encoded text into binary data
+ * by overwriting the input data.
+ *
+ * Returns: (transfer none): The binary data that @text responds. This pointer
+ * is the same as the input @text.
+ *
+ * Since: 2.20
+ */
+guchar *
+g_base64_decode_inplace (gchar *text,
+ gsize *out_len)
+{
+ gint input_length, state = 0;
+ guint save = 0;
+
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (out_len != NULL, NULL);
+
+ input_length = strlen (text);
+
+ g_return_val_if_fail (input_length > 1, NULL);
+
+ *out_len = g_base64_decode_step (text, input_length, (guchar *) text, &state, &save);
+
+ return (guchar *) text;
+}
diff --git a/glib/gbase64.h b/glib/gbase64.h
new file mode 100644
index 0000000000000000000000000000000000000000..662c597ff9c2fb457a8e5abe9f15e702aac12aa4
--- /dev/null
+++ b/glib/gbase64.h
@@ -0,0 +1,61 @@
+/* gbase64.h - Base64 coding functions
+ *
+ * Copyright (C) 2005 Alexander Larsson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#ifndef __G_BASE64_H__
+#define __G_BASE64_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_ALL
+gsize g_base64_encode_step (const guchar *in,
+ gsize len,
+ gboolean break_lines,
+ gchar *out,
+ gint *state,
+ gint *save);
+GLIB_AVAILABLE_IN_ALL
+gsize g_base64_encode_close (gboolean break_lines,
+ gchar *out,
+ gint *state,
+ gint *save);
+GLIB_AVAILABLE_IN_ALL
+gchar* g_base64_encode (const guchar *data,
+ gsize len) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gsize g_base64_decode_step (const gchar *in,
+ gsize len,
+ guchar *out,
+ gint *state,
+ guint *save);
+GLIB_AVAILABLE_IN_ALL
+guchar *g_base64_decode (const gchar *text,
+ gsize *out_len) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+guchar *g_base64_decode_inplace (gchar *text,
+ gsize *out_len);
+
+
+G_END_DECLS
+
+#endif /* __G_BASE64_H__ */
diff --git a/glib/gbitlock.c b/glib/gbitlock.c
new file mode 100644
index 0000000000000000000000000000000000000000..9384d1a4410f90e1876d7d59a88ed230d802b069
--- /dev/null
+++ b/glib/gbitlock.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright © 2008 Ryan Lortie
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Ryan Lortie
+ */
+
+#include "config.h"
+
+#include "gbitlock.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "gthreadprivate.h"
+
+#ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
+#undef HAVE_FUTEX
+#endif
+
+#ifndef HAVE_FUTEX
+static GMutex g_futex_mutex;
+static GSList *g_futex_address_list = NULL;
+#endif
+
+#ifdef HAVE_FUTEX
+/*
+ * We have headers for futex(2) on the build machine. This does not
+ * imply that every system that ever runs the resulting glib will have
+ * kernel support for futex, but you'd have to have a pretty old
+ * kernel in order for that not to be the case.
+ *
+ * If anyone actually gets bit by this, please file a bug. :)
+ */
+#include
+#include
+#include
+
+#ifndef FUTEX_WAIT_PRIVATE
+#define FUTEX_WAIT_PRIVATE FUTEX_WAIT
+#define FUTEX_WAKE_PRIVATE FUTEX_WAKE
+#endif
+
+/* < private >
+ * g_futex_wait:
+ * @address: a pointer to an integer
+ * @value: the value that should be at @address
+ *
+ * Atomically checks that the value stored at @address is equal to
+ * @value and then blocks. If the value stored at @address is not
+ * equal to @value then this function returns immediately.
+ *
+ * To unblock, call g_futex_wake() on @address.
+ *
+ * This call may spuriously unblock (for example, in response to the
+ * process receiving a signal) but this is not guaranteed. Unlike the
+ * Linux system call of a similar name, there is no guarantee that a
+ * waiting process will unblock due to a g_futex_wake() call in a
+ * separate process.
+ */
+static void
+g_futex_wait (const gint *address,
+ gint value)
+{
+ syscall (__NR_futex, address, (gsize) FUTEX_WAIT_PRIVATE, (gsize) value, NULL);
+}
+
+/* < private >
+ * g_futex_wake:
+ * @address: a pointer to an integer
+ *
+ * Nominally, wakes one thread that is blocked in g_futex_wait() on
+ * @address (if any thread is currently waiting).
+ *
+ * As mentioned in the documentation for g_futex_wait(), spurious
+ * wakeups may occur. As such, this call may result in more than one
+ * thread being woken up.
+ */
+static void
+g_futex_wake (const gint *address)
+{
+ syscall (__NR_futex, address, (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
+}
+
+#else
+
+/* emulate futex(2) */
+typedef struct
+{
+ const gint *address;
+ gint ref_count;
+ GCond wait_queue;
+} WaitAddress;
+
+static WaitAddress *
+g_futex_find_address (const gint *address)
+{
+ GSList *node;
+
+ for (node = g_futex_address_list; node; node = node->next)
+ {
+ WaitAddress *waiter = node->data;
+
+ if (waiter->address == address)
+ return waiter;
+ }
+
+ return NULL;
+}
+
+static void
+g_futex_wait (const gint *address,
+ gint value)
+{
+ g_mutex_lock (&g_futex_mutex);
+ if G_LIKELY (g_atomic_int_get (address) == value)
+ {
+ WaitAddress *waiter;
+
+ if ((waiter = g_futex_find_address (address)) == NULL)
+ {
+ waiter = g_slice_new (WaitAddress);
+ waiter->address = address;
+ g_cond_init (&waiter->wait_queue);
+ waiter->ref_count = 0;
+ g_futex_address_list =
+ g_slist_prepend (g_futex_address_list, waiter);
+ }
+
+ waiter->ref_count++;
+ g_cond_wait (&waiter->wait_queue, &g_futex_mutex);
+
+ if (!--waiter->ref_count)
+ {
+ g_futex_address_list =
+ g_slist_remove (g_futex_address_list, waiter);
+ g_cond_clear (&waiter->wait_queue);
+ g_slice_free (WaitAddress, waiter);
+ }
+ }
+ g_mutex_unlock (&g_futex_mutex);
+}
+
+static void
+g_futex_wake (const gint *address)
+{
+ WaitAddress *waiter;
+
+ /* need to lock here for two reasons:
+ * 1) need to acquire/release lock to ensure waiter is not in
+ * the process of registering a wait
+ * 2) need to -stay- locked until the end to ensure a wake()
+ * in another thread doesn't cause 'waiter' to stop existing
+ */
+ g_mutex_lock (&g_futex_mutex);
+ if ((waiter = g_futex_find_address (address)))
+ g_cond_signal (&waiter->wait_queue);
+ g_mutex_unlock (&g_futex_mutex);
+}
+#endif
+
+#define CONTENTION_CLASSES 11
+static gint g_bit_lock_contended[CONTENTION_CLASSES]; /* (atomic) */
+
+#if (defined (i386) || defined (__amd64__))
+ #if G_GNUC_CHECK_VERSION(4, 5)
+ #define USE_ASM_GOTO 1
+ #endif
+#endif
+
+/**
+ * g_bit_lock:
+ * @address: a pointer to an integer
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * Sets the indicated @lock_bit in @address. If the bit is already
+ * set, this call will block until g_bit_unlock() unsets the
+ * corresponding bit.
+ *
+ * Attempting to lock on two different bits within the same integer is
+ * not supported and will very probably cause deadlocks.
+ *
+ * The value of the bit that is set is (1u << @bit). If @bit is not
+ * between 0 and 31 then the result is undefined.
+ *
+ * This function accesses @address atomically. All other accesses to
+ * @address must be atomic in order for this function to work
+ * reliably. While @address has a `volatile` qualifier, this is a historical
+ * artifact and the argument passed to it should not be `volatile`.
+ *
+ * Since: 2.24
+ **/
+void
+g_bit_lock (volatile gint *address,
+ gint lock_bit)
+{
+ gint *address_nonvolatile = (gint *) address;
+
+#ifdef USE_ASM_GOTO
+ retry:
+ __asm__ volatile goto ("lock bts %1, (%0)\n"
+ "jc %l[contended]"
+ : /* no output */
+ : "r" (address), "r" (lock_bit)
+ : "cc", "memory"
+ : contended);
+ return;
+
+ contended:
+ {
+ guint mask = 1u << lock_bit;
+ guint v;
+
+ v = (guint) g_atomic_int_get (address_nonvolatile);
+ if (v & mask)
+ {
+ guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended);
+
+ g_atomic_int_add (&g_bit_lock_contended[class], +1);
+ g_futex_wait (address_nonvolatile, v);
+ g_atomic_int_add (&g_bit_lock_contended[class], -1);
+ }
+ }
+ goto retry;
+#else
+ guint mask = 1u << lock_bit;
+ guint v;
+
+ retry:
+ v = g_atomic_int_or (address_nonvolatile, mask);
+ if (v & mask)
+ /* already locked */
+ {
+ guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended);
+
+ g_atomic_int_add (&g_bit_lock_contended[class], +1);
+ g_futex_wait (address_nonvolatile, v);
+ g_atomic_int_add (&g_bit_lock_contended[class], -1);
+
+ goto retry;
+ }
+#endif
+}
+
+/**
+ * g_bit_trylock:
+ * @address: a pointer to an integer
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * Sets the indicated @lock_bit in @address, returning %TRUE if
+ * successful. If the bit is already set, returns %FALSE immediately.
+ *
+ * Attempting to lock on two different bits within the same integer is
+ * not supported.
+ *
+ * The value of the bit that is set is (1u << @bit). If @bit is not
+ * between 0 and 31 then the result is undefined.
+ *
+ * This function accesses @address atomically. All other accesses to
+ * @address must be atomic in order for this function to work
+ * reliably. While @address has a `volatile` qualifier, this is a historical
+ * artifact and the argument passed to it should not be `volatile`.
+ *
+ * Returns: %TRUE if the lock was acquired
+ *
+ * Since: 2.24
+ **/
+gboolean
+g_bit_trylock (volatile gint *address,
+ gint lock_bit)
+{
+#ifdef USE_ASM_GOTO
+ gboolean result;
+
+ __asm__ volatile ("lock bts %2, (%1)\n"
+ "setnc %%al\n"
+ "movzx %%al, %0"
+ : "=r" (result)
+ : "r" (address), "r" (lock_bit)
+ : "cc", "memory");
+
+ return result;
+#else
+ gint *address_nonvolatile = (gint *) address;
+ guint mask = 1u << lock_bit;
+ guint v;
+
+ v = g_atomic_int_or (address_nonvolatile, mask);
+
+ return ~v & mask;
+#endif
+}
+
+/**
+ * g_bit_unlock:
+ * @address: a pointer to an integer
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * Clears the indicated @lock_bit in @address. If another thread is
+ * currently blocked in g_bit_lock() on this same bit then it will be
+ * woken up.
+ *
+ * This function accesses @address atomically. All other accesses to
+ * @address must be atomic in order for this function to work
+ * reliably. While @address has a `volatile` qualifier, this is a historical
+ * artifact and the argument passed to it should not be `volatile`.
+ *
+ * Since: 2.24
+ **/
+void
+g_bit_unlock (volatile gint *address,
+ gint lock_bit)
+{
+ gint *address_nonvolatile = (gint *) address;
+
+#ifdef USE_ASM_GOTO
+ __asm__ volatile ("lock btr %1, (%0)"
+ : /* no output */
+ : "r" (address), "r" (lock_bit)
+ : "cc", "memory");
+#else
+ guint mask = 1u << lock_bit;
+
+ g_atomic_int_and (address_nonvolatile, ~mask);
+#endif
+
+ {
+ guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended);
+
+ if (g_atomic_int_get (&g_bit_lock_contended[class]))
+ g_futex_wake (address_nonvolatile);
+ }
+}
+
+
+/* We emulate pointer-sized futex(2) because the kernel API only
+ * supports integers.
+ *
+ * We assume that the 'interesting' part is always the lower order bits.
+ * This assumption holds because pointer bitlocks are restricted to
+ * using the low order bits of the pointer as the lock.
+ *
+ * On 32 bits, there is nothing to do since the pointer size is equal to
+ * the integer size. On little endian the lower-order bits don't move,
+ * so do nothing. Only on 64bit big endian do we need to do a bit of
+ * pointer arithmetic: the low order bits are shifted by 4 bytes. We
+ * have a helper function that always does the right thing here.
+ *
+ * Since we always consider the low-order bits of the integer value, a
+ * simple cast from (gsize) to (guint) always takes care of that.
+ *
+ * After that, pointer-sized futex becomes as simple as:
+ *
+ * g_futex_wait (g_futex_int_address (address), (guint) value);
+ *
+ * and
+ *
+ * g_futex_wake (g_futex_int_address (int_address));
+ */
+static const gint *
+g_futex_int_address (const void *address)
+{
+ const gint *int_address = address;
+
+ /* this implementation makes these (reasonable) assumptions: */
+ G_STATIC_ASSERT (G_BYTE_ORDER == G_LITTLE_ENDIAN ||
+ (G_BYTE_ORDER == G_BIG_ENDIAN &&
+ sizeof (int) == 4 &&
+ (sizeof (gpointer) == 4 || sizeof (gpointer) == 8)));
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
+ int_address++;
+#endif
+
+ return int_address;
+}
+
+/**
+ * g_pointer_bit_lock:
+ * @address: (not nullable): a pointer to a #gpointer-sized value
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * This is equivalent to g_bit_lock, but working on pointers (or other
+ * pointer-sized values).
+ *
+ * For portability reasons, you may only lock on the bottom 32 bits of
+ * the pointer.
+ *
+ * While @address has a `volatile` qualifier, this is a historical
+ * artifact and the argument passed to it should not be `volatile`.
+ *
+ * Since: 2.30
+ **/
+void
+(g_pointer_bit_lock) (volatile void *address,
+ gint lock_bit)
+{
+ void *address_nonvolatile = (void *) address;
+
+ g_return_if_fail (lock_bit < 32);
+
+ {
+#ifdef USE_ASM_GOTO
+ retry:
+ __asm__ volatile goto ("lock bts %1, (%0)\n"
+ "jc %l[contended]"
+ : /* no output */
+ : "r" (address), "r" ((gsize) lock_bit)
+ : "cc", "memory"
+ : contended);
+ return;
+
+ contended:
+ {
+ gsize *pointer_address = address_nonvolatile;
+ gsize mask = 1u << lock_bit;
+ gsize v;
+
+ v = (gsize) g_atomic_pointer_get (pointer_address);
+ if (v & mask)
+ {
+ guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended);
+
+ g_atomic_int_add (&g_bit_lock_contended[class], +1);
+ g_futex_wait (g_futex_int_address (address_nonvolatile), v);
+ g_atomic_int_add (&g_bit_lock_contended[class], -1);
+ }
+ }
+ goto retry;
+#else
+ gsize *pointer_address = address_nonvolatile;
+ gsize mask = 1u << lock_bit;
+ gsize v;
+
+ retry:
+ v = g_atomic_pointer_or (pointer_address, mask);
+ if (v & mask)
+ /* already locked */
+ {
+ guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended);
+
+ g_atomic_int_add (&g_bit_lock_contended[class], +1);
+ g_futex_wait (g_futex_int_address (address_nonvolatile), (guint) v);
+ g_atomic_int_add (&g_bit_lock_contended[class], -1);
+
+ goto retry;
+ }
+#endif
+ }
+}
+
+/**
+ * g_pointer_bit_trylock:
+ * @address: (not nullable): a pointer to a #gpointer-sized value
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * This is equivalent to g_bit_trylock(), but working on pointers (or
+ * other pointer-sized values).
+ *
+ * For portability reasons, you may only lock on the bottom 32 bits of
+ * the pointer.
+ *
+ * While @address has a `volatile` qualifier, this is a historical
+ * artifact and the argument passed to it should not be `volatile`.
+ *
+ * Returns: %TRUE if the lock was acquired
+ *
+ * Since: 2.30
+ **/
+gboolean
+(g_pointer_bit_trylock) (volatile void *address,
+ gint lock_bit)
+{
+ g_return_val_if_fail (lock_bit < 32, FALSE);
+
+ {
+#ifdef USE_ASM_GOTO
+ gboolean result;
+
+ __asm__ volatile ("lock bts %2, (%1)\n"
+ "setnc %%al\n"
+ "movzx %%al, %0"
+ : "=r" (result)
+ : "r" (address), "r" ((gsize) lock_bit)
+ : "cc", "memory");
+
+ return result;
+#else
+ void *address_nonvolatile = (void *) address;
+ gsize *pointer_address = address_nonvolatile;
+ gsize mask = 1u << lock_bit;
+ gsize v;
+
+ g_return_val_if_fail (lock_bit < 32, FALSE);
+
+ v = g_atomic_pointer_or (pointer_address, mask);
+
+ return ~v & mask;
+#endif
+ }
+}
+
+/**
+ * g_pointer_bit_unlock:
+ * @address: (not nullable): a pointer to a #gpointer-sized value
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * This is equivalent to g_bit_unlock, but working on pointers (or other
+ * pointer-sized values).
+ *
+ * For portability reasons, you may only lock on the bottom 32 bits of
+ * the pointer.
+ *
+ * While @address has a `volatile` qualifier, this is a historical
+ * artifact and the argument passed to it should not be `volatile`.
+ *
+ * Since: 2.30
+ **/
+void
+(g_pointer_bit_unlock) (volatile void *address,
+ gint lock_bit)
+{
+ void *address_nonvolatile = (void *) address;
+
+ g_return_if_fail (lock_bit < 32);
+
+ {
+#ifdef USE_ASM_GOTO
+ __asm__ volatile ("lock btr %1, (%0)"
+ : /* no output */
+ : "r" (address), "r" ((gsize) lock_bit)
+ : "cc", "memory");
+#else
+ gsize *pointer_address = address_nonvolatile;
+ gsize mask = 1u << lock_bit;
+
+ g_atomic_pointer_and (pointer_address, ~mask);
+#endif
+
+ {
+ guint class = ((gsize) address_nonvolatile) % G_N_ELEMENTS (g_bit_lock_contended);
+ if (g_atomic_int_get (&g_bit_lock_contended[class]))
+ g_futex_wake (g_futex_int_address (address_nonvolatile));
+ }
+ }
+}
diff --git a/glib/gbitlock.h b/glib/gbitlock.h
new file mode 100644
index 0000000000000000000000000000000000000000..8054bc8ae57b729bd9b52fb690be9d1ecd8c5d38
--- /dev/null
+++ b/glib/gbitlock.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2008 Ryan Lortie
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Ryan Lortie
+ */
+
+#ifndef __G_BITLOCK_H__
+#define __G_BITLOCK_H__
+
+#include
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_ALL
+void g_bit_lock (volatile gint *address,
+ gint lock_bit);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bit_trylock (volatile gint *address,
+ gint lock_bit);
+GLIB_AVAILABLE_IN_ALL
+void g_bit_unlock (volatile gint *address,
+ gint lock_bit);
+
+GLIB_AVAILABLE_IN_ALL
+void g_pointer_bit_lock (volatile void *address,
+ gint lock_bit);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_pointer_bit_trylock (volatile void *address,
+ gint lock_bit);
+GLIB_AVAILABLE_IN_ALL
+void g_pointer_bit_unlock (volatile void *address,
+ gint lock_bit);
+
+#ifdef __GNUC__
+
+#define g_pointer_bit_lock(address, lock_bit) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \
+ g_pointer_bit_lock ((address), (lock_bit)); \
+ }))
+
+#define g_pointer_bit_trylock(address, lock_bit) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \
+ g_pointer_bit_trylock ((address), (lock_bit)); \
+ }))
+
+#define g_pointer_bit_unlock(address, lock_bit) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(address) == sizeof (gpointer)); \
+ g_pointer_bit_unlock ((address), (lock_bit)); \
+ }))
+
+#endif
+
+G_END_DECLS
+
+#endif /* __G_BITLOCK_H_ */
diff --git a/glib/gbookmarkfile.c b/glib/gbookmarkfile.c
new file mode 100644
index 0000000000000000000000000000000000000000..f98b71efa3f3300f6caa2cb06cb5a5976d9467f9
--- /dev/null
+++ b/glib/gbookmarkfile.c
@@ -0,0 +1,4022 @@
+/* gbookmarkfile.c: parsing and building desktop bookmarks
+ *
+ * Copyright (C) 2005-2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#include "config.h"
+
+#include "gbookmarkfile.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "gconvert.h"
+#include "gdataset.h"
+#include "gdatetime.h"
+#include "gerror.h"
+#include "gfileutils.h"
+#include "ghash.h"
+#include "glibintl.h"
+#include "glist.h"
+#include "gmain.h"
+#include "gmarkup.h"
+#include "gmem.h"
+#include "gmessages.h"
+#include "gshell.h"
+#include "gslice.h"
+#include "gstdio.h"
+#include "gstring.h"
+#include "gstrfuncs.h"
+#include "gtimer.h"
+#include "gutils.h"
+
+
+/**
+ * SECTION:bookmarkfile
+ * @title: Bookmark file parser
+ * @short_description: parses files containing bookmarks
+ *
+ * GBookmarkFile lets you parse, edit or create files containing bookmarks
+ * to URI, along with some meta-data about the resource pointed by the URI
+ * like its MIME type, the application that is registering the bookmark and
+ * the icon that should be used to represent the bookmark. The data is stored
+ * using the
+ * [Desktop Bookmark Specification](http://www.gnome.org/~ebassi/bookmark-spec).
+ *
+ * The syntax of the bookmark files is described in detail inside the
+ * Desktop Bookmark Specification, here is a quick summary: bookmark
+ * files use a sub-class of the XML Bookmark Exchange Language
+ * specification, consisting of valid UTF-8 encoded XML, under the
+ * root element; each bookmark is stored inside a
+ * element, using its URI: no relative paths can
+ * be used inside a bookmark file. The bookmark may have a user defined
+ * title and description, to be used instead of the URI. Under the
+ * element, with its owner attribute set to
+ * `http://freedesktop.org`, is stored the meta-data about a resource
+ * pointed by its URI. The meta-data consists of the resource's MIME
+ * type; the applications that have registered a bookmark; the groups
+ * to which a bookmark belongs to; a visibility flag, used to set the
+ * bookmark as "private" to the applications and groups that has it
+ * registered; the URI and MIME type of an icon, to be used when
+ * displaying the bookmark inside a GUI.
+ *
+ * Here is an example of a bookmark file:
+ * [bookmarks.xbel](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/glib/tests/bookmarks.xbel)
+ *
+ * A bookmark file might contain more than one bookmark; each bookmark
+ * is accessed through its URI.
+ *
+ * The important caveat of bookmark files is that when you add a new
+ * bookmark you must also add the application that is registering it, using
+ * g_bookmark_file_add_application() or g_bookmark_file_set_application_info().
+ * If a bookmark has no applications then it won't be dumped when creating
+ * the on disk representation, using g_bookmark_file_to_data() or
+ * g_bookmark_file_to_file().
+ *
+ * The #GBookmarkFile parser was added in GLib 2.12.
+ */
+
+/* XBEL 1.0 standard entities */
+#define XBEL_VERSION "1.0"
+#define XBEL_DTD_NICK "xbel"
+#define XBEL_DTD_SYSTEM "+//IDN python.org//DTD XML Bookmark " \
+ "Exchange Language 1.0//EN//XML"
+
+#define XBEL_DTD_URI "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd"
+
+#define XBEL_ROOT_ELEMENT "xbel"
+#define XBEL_FOLDER_ELEMENT "folder" /* unused */
+#define XBEL_BOOKMARK_ELEMENT "bookmark"
+#define XBEL_ALIAS_ELEMENT "alias" /* unused */
+#define XBEL_SEPARATOR_ELEMENT "separator" /* unused */
+#define XBEL_TITLE_ELEMENT "title"
+#define XBEL_DESC_ELEMENT "desc"
+#define XBEL_INFO_ELEMENT "info"
+#define XBEL_METADATA_ELEMENT "metadata"
+
+#define XBEL_VERSION_ATTRIBUTE "version"
+#define XBEL_FOLDED_ATTRIBUTE "folded" /* unused */
+#define XBEL_OWNER_ATTRIBUTE "owner"
+#define XBEL_ADDED_ATTRIBUTE "added"
+#define XBEL_VISITED_ATTRIBUTE "visited"
+#define XBEL_MODIFIED_ATTRIBUTE "modified"
+#define XBEL_ID_ATTRIBUTE "id"
+#define XBEL_HREF_ATTRIBUTE "href"
+#define XBEL_REF_ATTRIBUTE "ref" /* unused */
+
+#define XBEL_YES_VALUE "yes"
+#define XBEL_NO_VALUE "no"
+
+/* Desktop bookmark spec entities */
+#define BOOKMARK_METADATA_OWNER "http://freedesktop.org"
+
+#define BOOKMARK_NAMESPACE_NAME "bookmark"
+#define BOOKMARK_NAMESPACE_URI "http://www.freedesktop.org/standards/desktop-bookmarks"
+
+#define BOOKMARK_GROUPS_ELEMENT "groups"
+#define BOOKMARK_GROUP_ELEMENT "group"
+#define BOOKMARK_APPLICATIONS_ELEMENT "applications"
+#define BOOKMARK_APPLICATION_ELEMENT "application"
+#define BOOKMARK_ICON_ELEMENT "icon"
+#define BOOKMARK_PRIVATE_ELEMENT "private"
+
+#define BOOKMARK_NAME_ATTRIBUTE "name"
+#define BOOKMARK_EXEC_ATTRIBUTE "exec"
+#define BOOKMARK_COUNT_ATTRIBUTE "count"
+#define BOOKMARK_TIMESTAMP_ATTRIBUTE "timestamp" /* deprecated by "modified" */
+#define BOOKMARK_MODIFIED_ATTRIBUTE "modified"
+#define BOOKMARK_HREF_ATTRIBUTE "href"
+#define BOOKMARK_TYPE_ATTRIBUTE "type"
+
+/* Shared MIME Info entities */
+#define MIME_NAMESPACE_NAME "mime"
+#define MIME_NAMESPACE_URI "http://www.freedesktop.org/standards/shared-mime-info"
+#define MIME_TYPE_ELEMENT "mime-type"
+#define MIME_TYPE_ATTRIBUTE "type"
+
+
+typedef struct _BookmarkAppInfo BookmarkAppInfo;
+typedef struct _BookmarkMetadata BookmarkMetadata;
+typedef struct _BookmarkItem BookmarkItem;
+typedef struct _ParseData ParseData;
+
+struct _BookmarkAppInfo
+{
+ gchar *name;
+ gchar *exec;
+
+ guint count;
+
+ GDateTime *stamp; /* (owned) */
+};
+
+struct _BookmarkMetadata
+{
+ gchar *mime_type;
+
+ GList *groups;
+
+ GList *applications;
+ GHashTable *apps_by_name;
+
+ gchar *icon_href;
+ gchar *icon_mime;
+
+ guint is_private : 1;
+};
+
+struct _BookmarkItem
+{
+ gchar *uri;
+
+ gchar *title;
+ gchar *description;
+
+ GDateTime *added; /* (owned) */
+ GDateTime *modified; /* (owned) */
+ GDateTime *visited; /* (owned) */
+
+ BookmarkMetadata *metadata;
+};
+
+struct _GBookmarkFile
+{
+ gchar *title;
+ gchar *description;
+
+ /* we store our items in a list and keep a copy inside
+ * a hash table for faster lookup performances
+ */
+ GList *items;
+ GHashTable *items_by_uri;
+};
+
+/* parser state machine */
+typedef enum
+{
+ STATE_STARTED = 0,
+
+ STATE_ROOT,
+ STATE_BOOKMARK,
+ STATE_TITLE,
+ STATE_DESC,
+ STATE_INFO,
+ STATE_METADATA,
+ STATE_APPLICATIONS,
+ STATE_APPLICATION,
+ STATE_GROUPS,
+ STATE_GROUP,
+ STATE_MIME,
+ STATE_ICON,
+
+ STATE_FINISHED
+} ParserState;
+
+static void g_bookmark_file_init (GBookmarkFile *bookmark);
+static void g_bookmark_file_clear (GBookmarkFile *bookmark);
+static gboolean g_bookmark_file_parse (GBookmarkFile *bookmark,
+ const gchar *buffer,
+ gsize length,
+ GError **error);
+static gchar * g_bookmark_file_dump (GBookmarkFile *bookmark,
+ gsize *length,
+ GError **error);
+static BookmarkItem *g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
+ const gchar *uri);
+static void g_bookmark_file_add_item (GBookmarkFile *bookmark,
+ BookmarkItem *item,
+ GError **error);
+
+static gboolean timestamp_from_iso8601 (const gchar *iso_date,
+ GDateTime **out_date_time,
+ GError **error);
+
+/********************************
+ * BookmarkAppInfo *
+ * *
+ * Application metadata storage *
+ ********************************/
+static BookmarkAppInfo *
+bookmark_app_info_new (const gchar *name)
+{
+ BookmarkAppInfo *retval;
+
+ g_warn_if_fail (name != NULL);
+
+ retval = g_slice_new (BookmarkAppInfo);
+
+ retval->name = g_strdup (name);
+ retval->exec = NULL;
+ retval->count = 0;
+ retval->stamp = NULL;
+
+ return retval;
+}
+
+static void
+bookmark_app_info_free (BookmarkAppInfo *app_info)
+{
+ if (!app_info)
+ return;
+
+ g_free (app_info->name);
+ g_free (app_info->exec);
+ g_clear_pointer (&app_info->stamp, g_date_time_unref);
+
+ g_slice_free (BookmarkAppInfo, app_info);
+}
+
+static gchar *
+bookmark_app_info_dump (BookmarkAppInfo *app_info)
+{
+ gchar *retval;
+ gchar *name, *exec, *modified, *count;
+
+ g_warn_if_fail (app_info != NULL);
+
+ if (app_info->count == 0)
+ return NULL;
+
+ name = g_markup_escape_text (app_info->name, -1);
+ exec = g_markup_escape_text (app_info->exec, -1);
+ modified = g_date_time_format_iso8601 (app_info->stamp);
+ count = g_strdup_printf ("%u", app_info->count);
+
+ retval = g_strconcat (" "
+ "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT
+ " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\""
+ " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\""
+ " " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", modified, "\""
+ " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n",
+ NULL);
+
+ g_free (name);
+ g_free (exec);
+ g_free (modified);
+ g_free (count);
+
+ return retval;
+}
+
+
+/***********************
+ * BookmarkMetadata *
+ * *
+ * Metadata storage *
+ ***********************/
+static BookmarkMetadata *
+bookmark_metadata_new (void)
+{
+ BookmarkMetadata *retval;
+
+ retval = g_slice_new (BookmarkMetadata);
+
+ retval->mime_type = NULL;
+
+ retval->groups = NULL;
+
+ retval->applications = NULL;
+ retval->apps_by_name = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ NULL);
+
+ retval->is_private = FALSE;
+
+ retval->icon_href = NULL;
+ retval->icon_mime = NULL;
+
+ return retval;
+}
+
+static void
+bookmark_metadata_free (BookmarkMetadata *metadata)
+{
+ if (!metadata)
+ return;
+
+ g_free (metadata->mime_type);
+
+ g_list_free_full (metadata->groups, g_free);
+ g_list_free_full (metadata->applications, (GDestroyNotify) bookmark_app_info_free);
+
+ g_hash_table_destroy (metadata->apps_by_name);
+
+ g_free (metadata->icon_href);
+ g_free (metadata->icon_mime);
+
+ g_slice_free (BookmarkMetadata, metadata);
+}
+
+static gchar *
+bookmark_metadata_dump (BookmarkMetadata *metadata)
+{
+ GString *retval;
+ gchar *buffer;
+
+ if (!metadata->applications)
+ return NULL;
+
+ retval = g_string_sized_new (1024);
+
+ /* metadata container */
+ g_string_append (retval,
+ " "
+ "<" XBEL_METADATA_ELEMENT
+ " " XBEL_OWNER_ATTRIBUTE "=\"" BOOKMARK_METADATA_OWNER
+ "\">\n");
+
+ /* mime type */
+ if (metadata->mime_type) {
+ buffer = g_strconcat (" "
+ "<" MIME_NAMESPACE_NAME ":" MIME_TYPE_ELEMENT " "
+ MIME_TYPE_ATTRIBUTE "=\"", metadata->mime_type, "\"/>\n",
+ NULL);
+ g_string_append (retval, buffer);
+ g_free (buffer);
+ }
+
+ if (metadata->groups)
+ {
+ GList *l;
+
+ /* open groups container */
+ g_string_append (retval,
+ " "
+ "<" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_GROUPS_ELEMENT ">\n");
+
+ for (l = g_list_last (metadata->groups); l != NULL; l = l->prev)
+ {
+ gchar *group_name;
+
+ group_name = g_markup_escape_text ((gchar *) l->data, -1);
+ buffer = g_strconcat (" "
+ "<" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_GROUP_ELEMENT ">",
+ group_name,
+ "" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_GROUP_ELEMENT ">\n", NULL);
+ g_string_append (retval, buffer);
+
+ g_free (buffer);
+ g_free (group_name);
+ }
+
+ /* close groups container */
+ g_string_append (retval,
+ " "
+ "" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_GROUPS_ELEMENT ">\n");
+ }
+
+ if (metadata->applications)
+ {
+ GList *l;
+
+ /* open applications container */
+ g_string_append (retval,
+ " "
+ "<" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
+
+ for (l = g_list_last (metadata->applications); l != NULL; l = l->prev)
+ {
+ BookmarkAppInfo *app_info = (BookmarkAppInfo *) l->data;
+ gchar *app_data;
+
+ g_warn_if_fail (app_info != NULL);
+
+ app_data = bookmark_app_info_dump (app_info);
+
+ if (app_data)
+ {
+ retval = g_string_append (retval, app_data);
+
+ g_free (app_data);
+ }
+ }
+
+ /* close applications container */
+ g_string_append (retval,
+ " "
+ "" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
+ }
+
+ /* icon */
+ if (metadata->icon_href)
+ {
+ if (!metadata->icon_mime)
+ metadata->icon_mime = g_strdup ("application/octet-stream");
+
+ buffer = g_strconcat (" "
+ "<" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_ICON_ELEMENT
+ " " BOOKMARK_HREF_ATTRIBUTE "=\"", metadata->icon_href,
+ "\" " BOOKMARK_TYPE_ATTRIBUTE "=\"", metadata->icon_mime, "\"/>\n", NULL);
+ g_string_append (retval, buffer);
+
+ g_free (buffer);
+ }
+
+ /* private hint */
+ if (metadata->is_private)
+ g_string_append (retval,
+ " "
+ "<" BOOKMARK_NAMESPACE_NAME
+ ":" BOOKMARK_PRIVATE_ELEMENT "/>\n");
+
+ /* close metadata container */
+ g_string_append (retval,
+ " "
+ "" XBEL_METADATA_ELEMENT ">\n");
+
+ return g_string_free (retval, FALSE);
+}
+
+/******************************************************
+ * BookmarkItem *
+ * *
+ * Storage for a single bookmark item inside the list *
+ ******************************************************/
+static BookmarkItem *
+bookmark_item_new (const gchar *uri)
+{
+ BookmarkItem *item;
+
+ g_warn_if_fail (uri != NULL);
+
+ item = g_slice_new (BookmarkItem);
+ item->uri = g_strdup (uri);
+
+ item->title = NULL;
+ item->description = NULL;
+
+ item->added = NULL;
+ item->modified = NULL;
+ item->visited = NULL;
+
+ item->metadata = NULL;
+
+ return item;
+}
+
+static void
+bookmark_item_free (BookmarkItem *item)
+{
+ if (!item)
+ return;
+
+ g_free (item->uri);
+ g_free (item->title);
+ g_free (item->description);
+
+ if (item->metadata)
+ bookmark_metadata_free (item->metadata);
+
+ g_clear_pointer (&item->added, g_date_time_unref);
+ g_clear_pointer (&item->modified, g_date_time_unref);
+ g_clear_pointer (&item->visited, g_date_time_unref);
+
+ g_slice_free (BookmarkItem, item);
+}
+
+static void
+bookmark_item_touch_modified (BookmarkItem *item)
+{
+ g_clear_pointer (&item->modified, g_date_time_unref);
+ item->modified = g_date_time_new_now_utc ();
+}
+
+static gchar *
+bookmark_item_dump (BookmarkItem *item)
+{
+ GString *retval;
+ gchar *escaped_uri;
+
+ /* at this point, we must have at least a registered application; if we don't
+ * we don't screw up the bookmark file, and just skip this item
+ */
+ if (!item->metadata || !item->metadata->applications)
+ {
+ g_warning ("Item for URI '%s' has no registered applications: skipping.", item->uri);
+ return NULL;
+ }
+
+ retval = g_string_sized_new (4096);
+
+ g_string_append (retval, " <" XBEL_BOOKMARK_ELEMENT " ");
+
+ escaped_uri = g_markup_escape_text (item->uri, -1);
+
+ g_string_append (retval, XBEL_HREF_ATTRIBUTE "=\"");
+ g_string_append (retval, escaped_uri);
+ g_string_append (retval , "\" ");
+
+ g_free (escaped_uri);
+
+ if (item->added)
+ {
+ char *added;
+
+ added = g_date_time_format_iso8601 (item->added);
+ g_string_append (retval, XBEL_ADDED_ATTRIBUTE "=\"");
+ g_string_append (retval, added);
+ g_string_append (retval, "\" ");
+ g_free (added);
+ }
+
+ if (item->modified)
+ {
+ char *modified;
+
+ modified = g_date_time_format_iso8601 (item->modified);
+ g_string_append (retval, XBEL_MODIFIED_ATTRIBUTE "=\"");
+ g_string_append (retval, modified);
+ g_string_append (retval, "\" ");
+ g_free (modified);
+ }
+
+ if (item->visited)
+ {
+ char *visited;
+
+ visited = g_date_time_format_iso8601 (item->visited);
+ g_string_append (retval, XBEL_VISITED_ATTRIBUTE "=\"");
+ g_string_append (retval, visited);
+ g_string_append (retval, "\" ");
+ g_free (visited);
+ }
+
+ if (retval->str[retval->len - 1] == ' ')
+ g_string_truncate (retval, retval->len - 1);
+ g_string_append (retval, ">\n");
+
+ if (item->title)
+ {
+ gchar *escaped_title;
+
+ escaped_title = g_markup_escape_text (item->title, -1);
+ g_string_append (retval, " " "<" XBEL_TITLE_ELEMENT ">");
+ g_string_append (retval, escaped_title);
+ g_string_append (retval, "" XBEL_TITLE_ELEMENT ">\n");
+
+ g_free (escaped_title);
+ }
+
+ if (item->description)
+ {
+ gchar *escaped_desc;
+
+ escaped_desc = g_markup_escape_text (item->description, -1);
+ g_string_append (retval, " " "<" XBEL_DESC_ELEMENT ">");
+ g_string_append (retval, escaped_desc);
+ g_string_append (retval, "" XBEL_DESC_ELEMENT ">\n");
+
+ g_free (escaped_desc);
+ }
+
+ if (item->metadata)
+ {
+ gchar *metadata;
+
+ metadata = bookmark_metadata_dump (item->metadata);
+ if (metadata)
+ {
+ g_string_append (retval, " " "<" XBEL_INFO_ELEMENT ">\n");
+ g_string_append (retval, metadata);
+ g_string_append (retval, " " "" XBEL_INFO_ELEMENT ">\n");
+
+ g_free (metadata);
+ }
+ }
+
+ g_string_append (retval, " " XBEL_BOOKMARK_ELEMENT ">\n");
+
+ return g_string_free (retval, FALSE);
+}
+
+static BookmarkAppInfo *
+bookmark_item_lookup_app_info (BookmarkItem *item,
+ const gchar *app_name)
+{
+ g_warn_if_fail (item != NULL && app_name != NULL);
+
+ if (!item->metadata)
+ return NULL;
+
+ return g_hash_table_lookup (item->metadata->apps_by_name, app_name);
+}
+
+/*************************
+ * GBookmarkFile *
+ *************************/
+
+static void
+g_bookmark_file_init (GBookmarkFile *bookmark)
+{
+ bookmark->title = NULL;
+ bookmark->description = NULL;
+
+ bookmark->items = NULL;
+ bookmark->items_by_uri = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ NULL);
+}
+
+static void
+g_bookmark_file_clear (GBookmarkFile *bookmark)
+{
+ g_free (bookmark->title);
+ g_free (bookmark->description);
+
+ g_list_free_full (bookmark->items, (GDestroyNotify) bookmark_item_free);
+ bookmark->items = NULL;
+
+ if (bookmark->items_by_uri)
+ {
+ g_hash_table_destroy (bookmark->items_by_uri);
+
+ bookmark->items_by_uri = NULL;
+ }
+}
+
+struct _ParseData
+{
+ ParserState state;
+
+ GHashTable *namespaces;
+
+ GBookmarkFile *bookmark_file;
+ BookmarkItem *current_item;
+};
+
+static ParseData *
+parse_data_new (void)
+{
+ ParseData *retval;
+
+ retval = g_new (ParseData, 1);
+
+ retval->state = STATE_STARTED;
+ retval->namespaces = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+ retval->bookmark_file = NULL;
+ retval->current_item = NULL;
+
+ return retval;
+}
+
+static void
+parse_data_free (ParseData *parse_data)
+{
+ g_hash_table_destroy (parse_data->namespaces);
+
+ g_free (parse_data);
+}
+
+#define IS_ATTRIBUTE(s,a) ((0 == strcmp ((s), (a))))
+
+static void
+parse_bookmark_element (GMarkupParseContext *context,
+ ParseData *parse_data,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *uri, *added, *modified, *visited;
+ const gchar *attr;
+ gint i;
+ BookmarkItem *item;
+ GError *add_error;
+
+ g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_BOOKMARK));
+
+ i = 0;
+ uri = added = modified = visited = NULL;
+ for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
+ {
+ if (IS_ATTRIBUTE (attr, XBEL_HREF_ATTRIBUTE))
+ uri = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, XBEL_ADDED_ATTRIBUTE))
+ added = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, XBEL_MODIFIED_ATTRIBUTE))
+ modified = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, XBEL_VISITED_ATTRIBUTE))
+ visited = attribute_values[i];
+ else
+ {
+ /* bookmark is defined by the XBEL spec, so we need
+ * to error out if the element has different or
+ * missing attributes
+ */
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+ _("Unexpected attribute “%s” for element “%s”"),
+ attr,
+ XBEL_BOOKMARK_ELEMENT);
+ return;
+ }
+ }
+
+ if (!uri)
+ {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Attribute “%s” of element “%s” not found"),
+ XBEL_HREF_ATTRIBUTE,
+ XBEL_BOOKMARK_ELEMENT);
+ return;
+ }
+
+ g_warn_if_fail (parse_data->current_item == NULL);
+
+ item = bookmark_item_new (uri);
+
+ if (added != NULL && !timestamp_from_iso8601 (added, &item->added, error))
+ {
+ bookmark_item_free (item);
+ return;
+ }
+
+ if (modified != NULL && !timestamp_from_iso8601 (modified, &item->modified, error))
+ {
+ bookmark_item_free (item);
+ return;
+ }
+
+ if (visited != NULL && !timestamp_from_iso8601 (visited, &item->visited, error))
+ {
+ bookmark_item_free (item);
+ return;
+ }
+
+ add_error = NULL;
+ g_bookmark_file_add_item (parse_data->bookmark_file,
+ item,
+ &add_error);
+ if (add_error)
+ {
+ bookmark_item_free (item);
+
+ g_propagate_error (error, add_error);
+
+ return;
+ }
+
+ parse_data->current_item = item;
+}
+
+static void
+parse_application_element (GMarkupParseContext *context,
+ ParseData *parse_data,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *name, *exec, *count, *stamp, *modified;
+ const gchar *attr;
+ gint i;
+ BookmarkItem *item;
+ BookmarkAppInfo *ai;
+
+ g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_APPLICATION));
+
+ i = 0;
+ name = exec = count = stamp = modified = NULL;
+ for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
+ {
+ if (IS_ATTRIBUTE (attr, BOOKMARK_NAME_ATTRIBUTE))
+ name = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, BOOKMARK_EXEC_ATTRIBUTE))
+ exec = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, BOOKMARK_COUNT_ATTRIBUTE))
+ count = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, BOOKMARK_TIMESTAMP_ATTRIBUTE))
+ stamp = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, BOOKMARK_MODIFIED_ATTRIBUTE))
+ modified = attribute_values[i];
+ }
+
+ /* the "name" and "exec" attributes are mandatory */
+ if (!name)
+ {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Attribute “%s” of element “%s” not found"),
+ BOOKMARK_NAME_ATTRIBUTE,
+ BOOKMARK_APPLICATION_ELEMENT);
+ return;
+ }
+
+ if (!exec)
+ {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Attribute “%s” of element “%s” not found"),
+ BOOKMARK_EXEC_ATTRIBUTE,
+ BOOKMARK_APPLICATION_ELEMENT);
+ return;
+ }
+
+ g_warn_if_fail (parse_data->current_item != NULL);
+ item = parse_data->current_item;
+
+ ai = bookmark_item_lookup_app_info (item, name);
+ if (!ai)
+ {
+ ai = bookmark_app_info_new (name);
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
+ g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
+ }
+
+ g_free (ai->exec);
+ ai->exec = g_strdup (exec);
+
+ if (count)
+ ai->count = atoi (count);
+ else
+ ai->count = 1;
+
+ g_clear_pointer (&ai->stamp, g_date_time_unref);
+ if (modified != NULL)
+ {
+ if (!timestamp_from_iso8601 (modified, &ai->stamp, error))
+ return;
+ }
+ else
+ {
+ /* the timestamp attribute has been deprecated but we still parse
+ * it for backward compatibility
+ */
+ if (stamp)
+ ai->stamp = g_date_time_new_from_unix_utc (atol (stamp));
+ else
+ ai->stamp = g_date_time_new_now_utc ();
+ }
+}
+
+static void
+parse_mime_type_element (GMarkupParseContext *context,
+ ParseData *parse_data,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *type;
+ const gchar *attr;
+ gint i;
+ BookmarkItem *item;
+
+ g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_MIME));
+
+ i = 0;
+ type = NULL;
+ for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
+ {
+ if (IS_ATTRIBUTE (attr, MIME_TYPE_ATTRIBUTE))
+ type = attribute_values[i];
+ }
+
+ if (!type)
+ type = "application/octet-stream";
+
+ g_warn_if_fail (parse_data->current_item != NULL);
+ item = parse_data->current_item;
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ g_free (item->metadata->mime_type);
+ item->metadata->mime_type = g_strdup (type);
+}
+
+static void
+parse_icon_element (GMarkupParseContext *context,
+ ParseData *parse_data,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error)
+{
+ const gchar *href;
+ const gchar *type;
+ const gchar *attr;
+ gint i;
+ BookmarkItem *item;
+
+ g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_ICON));
+
+ i = 0;
+ href = NULL;
+ type = NULL;
+ for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
+ {
+ if (IS_ATTRIBUTE (attr, BOOKMARK_HREF_ATTRIBUTE))
+ href = attribute_values[i];
+ else if (IS_ATTRIBUTE (attr, BOOKMARK_TYPE_ATTRIBUTE))
+ type = attribute_values[i];
+ }
+
+ /* the "href" attribute is mandatory */
+ if (!href)
+ {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Attribute “%s” of element “%s” not found"),
+ BOOKMARK_HREF_ATTRIBUTE,
+ BOOKMARK_ICON_ELEMENT);
+ return;
+ }
+
+ if (!type)
+ type = "application/octet-stream";
+
+ g_warn_if_fail (parse_data->current_item != NULL);
+ item = parse_data->current_item;
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ g_free (item->metadata->icon_href);
+ g_free (item->metadata->icon_mime);
+ item->metadata->icon_href = g_strdup (href);
+ item->metadata->icon_mime = g_strdup (type);
+}
+
+/* scans through the attributes of an element for the "xmlns" pragma, and
+ * adds any resulting namespace declaration to a per-parser hashtable, using
+ * the namespace name as a key for the namespace URI; if no key was found,
+ * the namespace is considered as default, and stored under the "default" key.
+ *
+ * FIXME: this works on the assumption that the generator of the XBEL file
+ * is either this code or is smart enough to place the namespace declarations
+ * inside the main root node or inside the metadata node and does not redefine
+ * a namespace inside an inner node; this does *not* conform to the
+ * XML-NS standard, although is a close approximation. In order to make this
+ * conformant to the XML-NS specification we should use a per-element
+ * namespace table inside GMarkup and ask it to resolve the namespaces for us.
+ */
+static void
+map_namespace_to_name (ParseData *parse_data,
+ const gchar **attribute_names,
+ const gchar **attribute_values)
+{
+ const gchar *attr;
+ gint i;
+
+ g_warn_if_fail (parse_data != NULL);
+
+ if (!attribute_names || !attribute_names[0])
+ return;
+
+ i = 0;
+ for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
+ {
+ if (g_str_has_prefix (attr, "xmlns"))
+ {
+ gchar *namespace_name, *namespace_uri;
+ gchar *p;
+
+ p = g_utf8_strchr (attr, -1, ':');
+ if (p)
+ p = g_utf8_next_char (p);
+ else
+ p = "default";
+
+ namespace_name = g_strdup (p);
+ namespace_uri = g_strdup (attribute_values[i]);
+
+ g_hash_table_replace (parse_data->namespaces,
+ namespace_name,
+ namespace_uri);
+ }
+ }
+}
+
+/* checks whether @element_full is equal to @element.
+ *
+ * if @namespace is set, it tries to resolve the namespace to a known URI,
+ * and if found is prepended to the element name, from which is separated
+ * using the character specified in the @sep parameter.
+ */
+static gboolean
+is_element_full (ParseData *parse_data,
+ const gchar *element_full,
+ const gchar *namespace,
+ const gchar *element,
+ const gchar sep)
+{
+ gchar *ns_uri, *ns_name;
+ const gchar *p, *element_name;
+ gboolean retval;
+
+ g_warn_if_fail (parse_data != NULL);
+ g_warn_if_fail (element_full != NULL);
+
+ if (!element)
+ return FALSE;
+
+ /* no namespace requested: dumb element compare */
+ if (!namespace)
+ return (0 == strcmp (element_full, element));
+
+ /* search for namespace separator; if none found, assume we are under the
+ * default namespace, and set ns_name to our "default" marker; if no default
+ * namespace has been set, just do a plain comparison between @full_element
+ * and @element.
+ */
+ p = g_utf8_strchr (element_full, -1, ':');
+ if (p)
+ {
+ ns_name = g_strndup (element_full, p - element_full);
+ element_name = g_utf8_next_char (p);
+ }
+ else
+ {
+ ns_name = g_strdup ("default");
+ element_name = element_full;
+ }
+
+ ns_uri = g_hash_table_lookup (parse_data->namespaces, ns_name);
+ if (!ns_uri)
+ {
+ /* no default namespace found */
+ g_free (ns_name);
+
+ return (0 == strcmp (element_full, element));
+ }
+
+ retval = (0 == strcmp (ns_uri, namespace) &&
+ 0 == strcmp (element_name, element));
+
+ g_free (ns_name);
+
+ return retval;
+}
+
+#define IS_ELEMENT(p,s,e) (is_element_full ((p), (s), NULL, (e), '\0'))
+#define IS_ELEMENT_NS(p,s,n,e) (is_element_full ((p), (s), (n), (e), '|'))
+
+static const gchar *
+parser_state_to_element_name (ParserState state)
+{
+ switch (state)
+ {
+ case STATE_STARTED:
+ case STATE_FINISHED:
+ return "(top-level)";
+ case STATE_ROOT:
+ return XBEL_ROOT_ELEMENT;
+ case STATE_BOOKMARK:
+ return XBEL_BOOKMARK_ELEMENT;
+ case STATE_TITLE:
+ return XBEL_TITLE_ELEMENT;
+ case STATE_DESC:
+ return XBEL_DESC_ELEMENT;
+ case STATE_INFO:
+ return XBEL_INFO_ELEMENT;
+ case STATE_METADATA:
+ return XBEL_METADATA_ELEMENT;
+ case STATE_APPLICATIONS:
+ return BOOKMARK_APPLICATIONS_ELEMENT;
+ case STATE_APPLICATION:
+ return BOOKMARK_APPLICATION_ELEMENT;
+ case STATE_GROUPS:
+ return BOOKMARK_GROUPS_ELEMENT;
+ case STATE_GROUP:
+ return BOOKMARK_GROUP_ELEMENT;
+ case STATE_MIME:
+ return MIME_TYPE_ELEMENT;
+ case STATE_ICON:
+ return BOOKMARK_ICON_ELEMENT;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+start_element_raw_cb (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = (ParseData *) user_data;
+
+ /* we must check for namespace declarations first
+ *
+ * XXX - we could speed up things by checking for namespace declarations
+ * only on the root node, where they usually are; this would probably break
+ * on streams not produced by us or by "smart" generators
+ */
+ map_namespace_to_name (parse_data, attribute_names, attribute_values);
+
+ switch (parse_data->state)
+ {
+ case STATE_STARTED:
+ if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
+ {
+ const gchar *attr;
+ gint i;
+
+ i = 0;
+ for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
+ {
+ if ((IS_ATTRIBUTE (attr, XBEL_VERSION_ATTRIBUTE)) &&
+ (0 == strcmp (attribute_values[i], XBEL_VERSION)))
+ parse_data->state = STATE_ROOT;
+ }
+ }
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s”, tag “%s” expected"),
+ element_name, XBEL_ROOT_ELEMENT);
+ break;
+ case STATE_ROOT:
+ if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
+ parse_data->state = STATE_TITLE;
+ else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
+ parse_data->state = STATE_DESC;
+ else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
+ {
+ GError *inner_error = NULL;
+
+ parse_data->state = STATE_BOOKMARK;
+
+ parse_bookmark_element (context,
+ parse_data,
+ attribute_names,
+ attribute_values,
+ &inner_error);
+ if (inner_error)
+ g_propagate_error (error, inner_error);
+ }
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s” inside “%s”"),
+ element_name,
+ XBEL_ROOT_ELEMENT);
+ break;
+ case STATE_BOOKMARK:
+ if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
+ parse_data->state = STATE_TITLE;
+ else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
+ parse_data->state = STATE_DESC;
+ else if (IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT))
+ parse_data->state = STATE_INFO;
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s” inside “%s”"),
+ element_name,
+ XBEL_BOOKMARK_ELEMENT);
+ break;
+ case STATE_INFO:
+ if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
+ {
+ const gchar *attr;
+ gint i;
+
+ i = 0;
+ for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
+ {
+ if ((IS_ATTRIBUTE (attr, XBEL_OWNER_ATTRIBUTE)) &&
+ (0 == strcmp (attribute_values[i], BOOKMARK_METADATA_OWNER)))
+ {
+ parse_data->state = STATE_METADATA;
+
+ if (!parse_data->current_item->metadata)
+ parse_data->current_item->metadata = bookmark_metadata_new ();
+ }
+ }
+ }
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s”, tag “%s” expected"),
+ element_name,
+ XBEL_METADATA_ELEMENT);
+ break;
+ case STATE_METADATA:
+ if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT))
+ parse_data->state = STATE_APPLICATIONS;
+ else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT))
+ parse_data->state = STATE_GROUPS;
+ else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT))
+ parse_data->current_item->metadata->is_private = TRUE;
+ else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
+ {
+ GError *inner_error = NULL;
+
+ parse_data->state = STATE_ICON;
+
+ parse_icon_element (context,
+ parse_data,
+ attribute_names,
+ attribute_values,
+ &inner_error);
+ if (inner_error)
+ g_propagate_error (error, inner_error);
+ }
+ else if (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT))
+ {
+ GError *inner_error = NULL;
+
+ parse_data->state = STATE_MIME;
+
+ parse_mime_type_element (context,
+ parse_data,
+ attribute_names,
+ attribute_values,
+ &inner_error);
+ if (inner_error)
+ g_propagate_error (error, inner_error);
+ }
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ _("Unexpected tag “%s” inside “%s”"),
+ element_name,
+ XBEL_METADATA_ELEMENT);
+ break;
+ case STATE_APPLICATIONS:
+ if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATION_ELEMENT))
+ {
+ GError *inner_error = NULL;
+
+ parse_data->state = STATE_APPLICATION;
+
+ parse_application_element (context,
+ parse_data,
+ attribute_names,
+ attribute_values,
+ &inner_error);
+ if (inner_error)
+ g_propagate_error (error, inner_error);
+ }
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s”, tag “%s” expected"),
+ element_name,
+ BOOKMARK_APPLICATION_ELEMENT);
+ break;
+ case STATE_GROUPS:
+ if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUP_ELEMENT))
+ parse_data->state = STATE_GROUP;
+ else
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s”, tag “%s” expected"),
+ element_name,
+ BOOKMARK_GROUP_ELEMENT);
+ break;
+
+ case STATE_TITLE:
+ case STATE_DESC:
+ case STATE_APPLICATION:
+ case STATE_GROUP:
+ case STATE_MIME:
+ case STATE_ICON:
+ case STATE_FINISHED:
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Unexpected tag “%s” inside “%s”"),
+ element_name,
+ parser_state_to_element_name (parse_data->state));
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+end_element_raw_cb (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = (ParseData *) user_data;
+
+ if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
+ parse_data->state = STATE_FINISHED;
+ else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
+ {
+ parse_data->current_item = NULL;
+
+ parse_data->state = STATE_ROOT;
+ }
+ else if ((IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT)) ||
+ (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT)) ||
+ (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT)))
+ {
+ if (parse_data->current_item)
+ parse_data->state = STATE_BOOKMARK;
+ else
+ parse_data->state = STATE_ROOT;
+ }
+ else if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
+ parse_data->state = STATE_INFO;
+ else if (IS_ELEMENT_NS (parse_data, element_name,
+ BOOKMARK_NAMESPACE_URI,
+ BOOKMARK_APPLICATION_ELEMENT))
+ parse_data->state = STATE_APPLICATIONS;
+ else if (IS_ELEMENT_NS (parse_data, element_name,
+ BOOKMARK_NAMESPACE_URI,
+ BOOKMARK_GROUP_ELEMENT))
+ parse_data->state = STATE_GROUPS;
+ else if ((IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT)) ||
+ (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT)) ||
+ (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT)) ||
+ (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT)) ||
+ (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT)))
+ parse_data->state = STATE_METADATA;
+}
+
+static void
+text_raw_cb (GMarkupParseContext *context,
+ const gchar *text,
+ gsize length,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = (ParseData *) user_data;
+ gchar *payload;
+
+ payload = g_strndup (text, length);
+
+ switch (parse_data->state)
+ {
+ case STATE_TITLE:
+ if (parse_data->current_item)
+ {
+ g_free (parse_data->current_item->title);
+ parse_data->current_item->title = g_strdup (payload);
+ }
+ else
+ {
+ g_free (parse_data->bookmark_file->title);
+ parse_data->bookmark_file->title = g_strdup (payload);
+ }
+ break;
+ case STATE_DESC:
+ if (parse_data->current_item)
+ {
+ g_free (parse_data->current_item->description);
+ parse_data->current_item->description = g_strdup (payload);
+ }
+ else
+ {
+ g_free (parse_data->bookmark_file->description);
+ parse_data->bookmark_file->description = g_strdup (payload);
+ }
+ break;
+ case STATE_GROUP:
+ {
+ GList *groups;
+
+ g_warn_if_fail (parse_data->current_item != NULL);
+
+ if (!parse_data->current_item->metadata)
+ parse_data->current_item->metadata = bookmark_metadata_new ();
+
+ groups = parse_data->current_item->metadata->groups;
+ parse_data->current_item->metadata->groups = g_list_prepend (groups, g_strdup (payload));
+ }
+ break;
+ case STATE_ROOT:
+ case STATE_BOOKMARK:
+ case STATE_INFO:
+ case STATE_METADATA:
+ case STATE_APPLICATIONS:
+ case STATE_APPLICATION:
+ case STATE_GROUPS:
+ case STATE_MIME:
+ case STATE_ICON:
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+
+ g_free (payload);
+}
+
+static const GMarkupParser markup_parser =
+{
+ start_element_raw_cb, /* start_element */
+ end_element_raw_cb, /* end_element */
+ text_raw_cb, /* text */
+ NULL, /* passthrough */
+ NULL
+};
+
+static gboolean
+g_bookmark_file_parse (GBookmarkFile *bookmark,
+ const gchar *buffer,
+ gsize length,
+ GError **error)
+{
+ GMarkupParseContext *context;
+ ParseData *parse_data;
+ GError *parse_error, *end_error;
+ gboolean retval;
+
+ g_warn_if_fail (bookmark != NULL);
+
+ if (!buffer)
+ return FALSE;
+
+ parse_error = NULL;
+ end_error = NULL;
+
+ if (length == (gsize) -1)
+ length = strlen (buffer);
+
+ parse_data = parse_data_new ();
+ parse_data->bookmark_file = bookmark;
+
+ context = g_markup_parse_context_new (&markup_parser,
+ G_MARKUP_PARSE_FLAGS_NONE,
+ parse_data,
+ (GDestroyNotify) parse_data_free);
+
+ retval = g_markup_parse_context_parse (context,
+ buffer,
+ length,
+ &parse_error);
+ if (!retval)
+ g_propagate_error (error, parse_error);
+ else
+ {
+ retval = g_markup_parse_context_end_parse (context, &end_error);
+ if (!retval)
+ g_propagate_error (error, end_error);
+ }
+
+ g_markup_parse_context_free (context);
+
+ return retval;
+}
+
+static gchar *
+g_bookmark_file_dump (GBookmarkFile *bookmark,
+ gsize *length,
+ GError **error)
+{
+ GString *retval;
+ gchar *buffer;
+ GList *l;
+
+ retval = g_string_sized_new (4096);
+
+ g_string_append (retval,
+ "\n"
+#if 0
+ /* XXX - do we really need the doctype? */
+ "\n"
+#endif
+ "<" XBEL_ROOT_ELEMENT " " XBEL_VERSION_ATTRIBUTE "=\"" XBEL_VERSION "\"\n"
+ " xmlns:" BOOKMARK_NAMESPACE_NAME "=\"" BOOKMARK_NAMESPACE_URI "\"\n"
+ " xmlns:" MIME_NAMESPACE_NAME "=\"" MIME_NAMESPACE_URI "\"\n>");
+
+ if (bookmark->title)
+ {
+ gchar *escaped_title;
+
+ escaped_title = g_markup_escape_text (bookmark->title, -1);
+
+ buffer = g_strconcat (" "
+ "<" XBEL_TITLE_ELEMENT ">",
+ escaped_title,
+ "" XBEL_TITLE_ELEMENT ">\n", NULL);
+
+ g_string_append (retval, buffer);
+
+ g_free (buffer);
+ g_free (escaped_title);
+ }
+
+ if (bookmark->description)
+ {
+ gchar *escaped_desc;
+
+ escaped_desc = g_markup_escape_text (bookmark->description, -1);
+
+ buffer = g_strconcat (" "
+ "<" XBEL_DESC_ELEMENT ">",
+ escaped_desc,
+ "" XBEL_DESC_ELEMENT ">\n", NULL);
+ g_string_append (retval, buffer);
+
+ g_free (buffer);
+ g_free (escaped_desc);
+ }
+
+ if (!bookmark->items)
+ goto out;
+ else
+ retval = g_string_append (retval, "\n");
+
+ /* the items are stored in reverse order */
+ for (l = g_list_last (bookmark->items);
+ l != NULL;
+ l = l->prev)
+ {
+ BookmarkItem *item = (BookmarkItem *) l->data;
+ gchar *item_dump;
+
+ item_dump = bookmark_item_dump (item);
+ if (!item_dump)
+ continue;
+
+ retval = g_string_append (retval, item_dump);
+
+ g_free (item_dump);
+ }
+
+out:
+ g_string_append (retval, "" XBEL_ROOT_ELEMENT ">");
+
+ if (length)
+ *length = retval->len;
+
+ return g_string_free (retval, FALSE);
+}
+
+/**************
+ * Misc *
+ **************/
+
+static gboolean
+timestamp_from_iso8601 (const gchar *iso_date,
+ GDateTime **out_date_time,
+ GError **error)
+{
+ GDateTime *dt = g_date_time_new_from_iso8601 (iso_date, NULL);
+ if (dt == NULL)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR, G_BOOKMARK_FILE_ERROR_READ,
+ _("Invalid date/time ‘%s’ in bookmark file"), iso_date);
+ return FALSE;
+ }
+
+ *out_date_time = g_steal_pointer (&dt);
+ return TRUE;
+}
+
+G_DEFINE_QUARK (g-bookmark-file-error-quark, g_bookmark_file_error)
+
+/********************
+ * Public API *
+ ********************/
+
+/**
+ * g_bookmark_file_new: (constructor)
+ *
+ * Creates a new empty #GBookmarkFile object.
+ *
+ * Use g_bookmark_file_load_from_file(), g_bookmark_file_load_from_data()
+ * or g_bookmark_file_load_from_data_dirs() to read an existing bookmark
+ * file.
+ *
+ * Returns: an empty #GBookmarkFile
+ *
+ * Since: 2.12
+ */
+GBookmarkFile *
+g_bookmark_file_new (void)
+{
+ GBookmarkFile *bookmark;
+
+ bookmark = g_new (GBookmarkFile, 1);
+
+ g_bookmark_file_init (bookmark);
+
+ return bookmark;
+}
+
+/**
+ * g_bookmark_file_free:
+ * @bookmark: a #GBookmarkFile
+ *
+ * Frees a #GBookmarkFile.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_free (GBookmarkFile *bookmark)
+{
+ if (!bookmark)
+ return;
+
+ g_bookmark_file_clear (bookmark);
+
+ g_free (bookmark);
+}
+
+/**
+ * g_bookmark_file_load_from_data:
+ * @bookmark: an empty #GBookmarkFile struct
+ * @data: (array length=length) (element-type guint8): desktop bookmarks
+ * loaded in memory
+ * @length: the length of @data in bytes
+ * @error: return location for a #GError, or %NULL
+ *
+ * Loads a bookmark file from memory into an empty #GBookmarkFile
+ * structure. If the object cannot be created then @error is set to a
+ * #GBookmarkFileError.
+ *
+ * Returns: %TRUE if a desktop bookmark could be loaded.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_load_from_data (GBookmarkFile *bookmark,
+ const gchar *data,
+ gsize length,
+ GError **error)
+{
+ GError *parse_error;
+ gboolean retval;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+
+ if (length == (gsize) -1)
+ length = strlen (data);
+
+ if (bookmark->items)
+ {
+ g_bookmark_file_clear (bookmark);
+ g_bookmark_file_init (bookmark);
+ }
+
+ parse_error = NULL;
+ retval = g_bookmark_file_parse (bookmark, data, length, &parse_error);
+
+ if (!retval)
+ g_propagate_error (error, parse_error);
+
+ return retval;
+}
+
+/**
+ * g_bookmark_file_load_from_file:
+ * @bookmark: an empty #GBookmarkFile struct
+ * @filename: (type filename): the path of a filename to load, in the
+ * GLib file name encoding
+ * @error: return location for a #GError, or %NULL
+ *
+ * Loads a desktop bookmark file into an empty #GBookmarkFile structure.
+ * If the file could not be loaded then @error is set to either a #GFileError
+ * or #GBookmarkFileError.
+ *
+ * Returns: %TRUE if a desktop bookmark file could be loaded
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_load_from_file (GBookmarkFile *bookmark,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gchar *buffer = NULL;
+ gsize len;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ if (!g_file_get_contents (filename, &buffer, &len, error))
+ goto out;
+
+ if (!g_bookmark_file_load_from_data (bookmark, buffer, len, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (buffer);
+ return ret;
+}
+
+
+/* Iterates through all the directories in *dirs trying to
+ * find file. When it successfully locates file, returns a
+ * string its absolute path. It also leaves the unchecked
+ * directories in *dirs. You should free the returned string
+ *
+ * Adapted from gkeyfile.c
+ */
+static gchar *
+find_file_in_data_dirs (const gchar *file,
+ gchar ***dirs,
+ GError **error)
+{
+ gchar **data_dirs, *data_dir, *path;
+
+ path = NULL;
+
+ if (dirs == NULL)
+ return NULL;
+
+ data_dirs = *dirs;
+ path = NULL;
+ while (data_dirs && (data_dir = *data_dirs) && !path)
+ {
+ gchar *candidate_file, *sub_dir;
+
+ candidate_file = (gchar *) file;
+ sub_dir = g_strdup ("");
+ while (candidate_file != NULL && !path)
+ {
+ gchar *p;
+
+ path = g_build_filename (data_dir, sub_dir,
+ candidate_file, NULL);
+
+ candidate_file = strchr (candidate_file, '-');
+
+ if (candidate_file == NULL)
+ break;
+
+ candidate_file++;
+
+ g_free (sub_dir);
+ sub_dir = g_strndup (file, candidate_file - file - 1);
+
+ for (p = sub_dir; *p != '\0'; p++)
+ {
+ if (*p == '-')
+ *p = G_DIR_SEPARATOR;
+ }
+ }
+ g_free (sub_dir);
+ data_dirs++;
+ }
+
+ *dirs = data_dirs;
+
+ if (!path)
+ {
+ g_set_error_literal (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND,
+ _("No valid bookmark file found in data dirs"));
+
+ return NULL;
+ }
+
+ return path;
+}
+
+
+/**
+ * g_bookmark_file_load_from_data_dirs:
+ * @bookmark: a #GBookmarkFile
+ * @file: (type filename): a relative path to a filename to open and parse
+ * @full_path: (out) (optional) (type filename): return location for a string
+ * containing the full path of the file, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * This function looks for a desktop bookmark file named @file in the
+ * paths returned from g_get_user_data_dir() and g_get_system_data_dirs(),
+ * loads the file into @bookmark and returns the file's full path in
+ * @full_path. If the file could not be loaded then @error is
+ * set to either a #GFileError or #GBookmarkFileError.
+ *
+ * Returns: %TRUE if a key file could be loaded, %FALSE otherwise
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_load_from_data_dirs (GBookmarkFile *bookmark,
+ const gchar *file,
+ gchar **full_path,
+ GError **error)
+{
+ GError *file_error = NULL;
+ gchar **all_data_dirs, **data_dirs;
+ const gchar *user_data_dir;
+ const gchar * const * system_data_dirs;
+ gsize i, j;
+ gchar *output_path;
+ gboolean found_file;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
+
+ user_data_dir = g_get_user_data_dir ();
+ system_data_dirs = g_get_system_data_dirs ();
+ all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
+
+ i = 0;
+ all_data_dirs[i++] = g_strdup (user_data_dir);
+
+ j = 0;
+ while (system_data_dirs[j] != NULL)
+ all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
+
+ found_file = FALSE;
+ data_dirs = all_data_dirs;
+ output_path = NULL;
+ while (*data_dirs != NULL && !found_file)
+ {
+ g_free (output_path);
+
+ output_path = find_file_in_data_dirs (file, &data_dirs, &file_error);
+
+ if (file_error)
+ {
+ g_propagate_error (error, file_error);
+ break;
+ }
+
+ found_file = g_bookmark_file_load_from_file (bookmark,
+ output_path,
+ &file_error);
+ if (file_error)
+ {
+ g_propagate_error (error, file_error);
+ break;
+ }
+ }
+
+ if (found_file && full_path)
+ *full_path = output_path;
+ else
+ g_free (output_path);
+
+ g_strfreev (all_data_dirs);
+
+ return found_file;
+}
+
+
+/**
+ * g_bookmark_file_to_data:
+ * @bookmark: a #GBookmarkFile
+ * @length: (out) (optional): return location for the length of the returned string, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * This function outputs @bookmark as a string.
+ *
+ * Returns: (transfer full) (array length=length) (element-type guint8):
+ * a newly allocated string holding the contents of the #GBookmarkFile
+ *
+ * Since: 2.12
+ */
+gchar *
+g_bookmark_file_to_data (GBookmarkFile *bookmark,
+ gsize *length,
+ GError **error)
+{
+ GError *write_error = NULL;
+ gchar *retval;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+
+ retval = g_bookmark_file_dump (bookmark, length, &write_error);
+ if (write_error)
+ {
+ g_propagate_error (error, write_error);
+
+ return NULL;
+ }
+
+ return retval;
+}
+
+/**
+ * g_bookmark_file_to_file:
+ * @bookmark: a #GBookmarkFile
+ * @filename: (type filename): path of the output file
+ * @error: return location for a #GError, or %NULL
+ *
+ * This function outputs @bookmark into a file. The write process is
+ * guaranteed to be atomic by using g_file_set_contents() internally.
+ *
+ * Returns: %TRUE if the file was successfully written.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_to_file (GBookmarkFile *bookmark,
+ const gchar *filename,
+ GError **error)
+{
+ gchar *data;
+ GError *data_error, *write_error;
+ gsize len;
+ gboolean retval;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ data_error = NULL;
+ data = g_bookmark_file_to_data (bookmark, &len, &data_error);
+ if (data_error)
+ {
+ g_propagate_error (error, data_error);
+
+ return FALSE;
+ }
+
+ write_error = NULL;
+ g_file_set_contents (filename, data, len, &write_error);
+ if (write_error)
+ {
+ g_propagate_error (error, write_error);
+
+ retval = FALSE;
+ }
+ else
+ retval = TRUE;
+
+ g_free (data);
+
+ return retval;
+}
+
+static BookmarkItem *
+g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
+ const gchar *uri)
+{
+ g_warn_if_fail (bookmark != NULL && uri != NULL);
+
+ return g_hash_table_lookup (bookmark->items_by_uri, uri);
+}
+
+/* this function adds a new item to the list */
+static void
+g_bookmark_file_add_item (GBookmarkFile *bookmark,
+ BookmarkItem *item,
+ GError **error)
+{
+ g_warn_if_fail (bookmark != NULL);
+ g_warn_if_fail (item != NULL);
+
+ /* this should never happen; and if it does, then we are
+ * screwing up something big time.
+ */
+ if (G_UNLIKELY (g_bookmark_file_has_item (bookmark, item->uri)))
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_INVALID_URI,
+ _("A bookmark for URI “%s” already exists"),
+ item->uri);
+ return;
+ }
+
+ bookmark->items = g_list_prepend (bookmark->items, item);
+
+ g_hash_table_replace (bookmark->items_by_uri,
+ item->uri,
+ item);
+
+ if (item->added == NULL)
+ item->added = g_date_time_new_now_utc ();
+
+ if (item->modified == NULL)
+ item->modified = g_date_time_new_now_utc ();
+
+ if (item->visited == NULL)
+ item->visited = g_date_time_new_now_utc ();
+}
+
+/**
+ * g_bookmark_file_remove_item:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Removes the bookmark for @uri from the bookmark file @bookmark.
+ *
+ * Returns: %TRUE if the bookmark was removed successfully.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_remove_item (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ bookmark->items = g_list_remove (bookmark->items, item);
+ g_hash_table_remove (bookmark->items_by_uri, item->uri);
+
+ bookmark_item_free (item);
+
+ return TRUE;
+}
+
+/**
+ * g_bookmark_file_has_item:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ *
+ * Looks whether the desktop bookmark has an item with its URI set to @uri.
+ *
+ * Returns: %TRUE if @uri is inside @bookmark, %FALSE otherwise
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_has_item (GBookmarkFile *bookmark,
+ const gchar *uri)
+{
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ return (NULL != g_hash_table_lookup (bookmark->items_by_uri, uri));
+}
+
+/**
+ * g_bookmark_file_get_uris:
+ * @bookmark: a #GBookmarkFile
+ * @length: (out) (optional): return location for the number of returned URIs, or %NULL
+ *
+ * Returns all URIs of the bookmarks in the bookmark file @bookmark.
+ * The array of returned URIs will be %NULL-terminated, so @length may
+ * optionally be %NULL.
+ *
+ * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
+ * Use g_strfreev() to free it.
+ *
+ * Since: 2.12
+ */
+gchar **
+g_bookmark_file_get_uris (GBookmarkFile *bookmark,
+ gsize *length)
+{
+ GList *l;
+ gchar **uris;
+ gsize i, n_items;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+
+ n_items = g_list_length (bookmark->items);
+ uris = g_new0 (gchar *, n_items + 1);
+
+ /* the items are stored in reverse order, so we walk the list backward */
+ for (l = g_list_last (bookmark->items), i = 0; l != NULL; l = l->prev)
+ {
+ BookmarkItem *item = (BookmarkItem *) l->data;
+
+ g_warn_if_fail (item != NULL);
+
+ uris[i++] = g_strdup (item->uri);
+ }
+ uris[i] = NULL;
+
+ if (length)
+ *length = i;
+
+ return uris;
+}
+
+/**
+ * g_bookmark_file_set_title:
+ * @bookmark: a #GBookmarkFile
+ * @uri: (nullable): a valid URI or %NULL
+ * @title: a UTF-8 encoded string
+ *
+ * Sets @title as the title of the bookmark for @uri inside the
+ * bookmark file @bookmark.
+ *
+ * If @uri is %NULL, the title of @bookmark is set.
+ *
+ * If a bookmark for @uri cannot be found then it is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_set_title (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *title)
+{
+ g_return_if_fail (bookmark != NULL);
+
+ if (!uri)
+ {
+ g_free (bookmark->title);
+ bookmark->title = g_strdup (title);
+ }
+ else
+ {
+ BookmarkItem *item;
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ g_free (item->title);
+ item->title = g_strdup (title);
+
+ bookmark_item_touch_modified (item);
+ }
+}
+
+/**
+ * g_bookmark_file_get_title:
+ * @bookmark: a #GBookmarkFile
+ * @uri: (nullable): a valid URI or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Returns the title of the bookmark for @uri.
+ *
+ * If @uri is %NULL, the title of @bookmark is returned.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: (transfer full): a newly allocated string or %NULL if the specified
+ * URI cannot be found.
+ *
+ * Since: 2.12
+ */
+gchar *
+g_bookmark_file_get_title (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+
+ if (!uri)
+ return g_strdup (bookmark->title);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ return g_strdup (item->title);
+}
+
+/**
+ * g_bookmark_file_set_description:
+ * @bookmark: a #GBookmarkFile
+ * @uri: (nullable): a valid URI or %NULL
+ * @description: a string
+ *
+ * Sets @description as the description of the bookmark for @uri.
+ *
+ * If @uri is %NULL, the description of @bookmark is set.
+ *
+ * If a bookmark for @uri cannot be found then it is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_set_description (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *description)
+{
+ g_return_if_fail (bookmark != NULL);
+
+ if (!uri)
+ {
+ g_free (bookmark->description);
+ bookmark->description = g_strdup (description);
+ }
+ else
+ {
+ BookmarkItem *item;
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ g_free (item->description);
+ item->description = g_strdup (description);
+
+ bookmark_item_touch_modified (item);
+ }
+}
+
+/**
+ * g_bookmark_file_get_description:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Retrieves the description of the bookmark for @uri.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: (transfer full): a newly allocated string or %NULL if the specified
+ * URI cannot be found.
+ *
+ * Since: 2.12
+ */
+gchar *
+g_bookmark_file_get_description (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+
+ if (!uri)
+ return g_strdup (bookmark->description);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ return g_strdup (item->description);
+}
+
+/**
+ * g_bookmark_file_set_mime_type:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @mime_type: a MIME type
+ *
+ * Sets @mime_type as the MIME type of the bookmark for @uri.
+ *
+ * If a bookmark for @uri cannot be found then it is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *mime_type)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (mime_type != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ g_free (item->metadata->mime_type);
+
+ item->metadata->mime_type = g_strdup (mime_type);
+ bookmark_item_touch_modified (item);
+}
+
+/**
+ * g_bookmark_file_get_mime_type:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Retrieves the MIME type of the resource pointed by @uri.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
+ * event that the MIME type cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
+ *
+ * Returns: (transfer full): a newly allocated string or %NULL if the specified
+ * URI cannot be found.
+ *
+ * Since: 2.12
+ */
+gchar *
+g_bookmark_file_get_mime_type (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ if (!item->metadata)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
+ _("No MIME type defined in the bookmark for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ return g_strdup (item->metadata->mime_type);
+}
+
+/**
+ * g_bookmark_file_set_is_private:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @is_private: %TRUE if the bookmark should be marked as private
+ *
+ * Sets the private flag of the bookmark for @uri.
+ *
+ * If a bookmark for @uri cannot be found then it is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gboolean is_private)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ item->metadata->is_private = (is_private == TRUE);
+ bookmark_item_touch_modified (item);
+}
+
+/**
+ * g_bookmark_file_get_is_private:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets whether the private flag of the bookmark for @uri is set.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
+ * event that the private flag cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
+ *
+ * Returns: %TRUE if the private flag is set, %FALSE otherwise.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_get_is_private (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ if (!item->metadata)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
+ _("No private flag has been defined in bookmark for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ return item->metadata->is_private;
+}
+
+/**
+ * g_bookmark_file_set_added:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @added: a timestamp or -1 to use the current time
+ *
+ * Sets the time the bookmark for @uri was added into @bookmark.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_set_added_date_time() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+void
+g_bookmark_file_set_added (GBookmarkFile *bookmark,
+ const gchar *uri,
+ time_t added)
+{
+ GDateTime *added_dt = (added != (time_t) -1) ? g_date_time_new_from_unix_utc (added) : g_date_time_new_now_utc ();
+ g_bookmark_file_set_added_date_time (bookmark, uri, added_dt);
+ g_date_time_unref (added_dt);
+}
+
+/**
+ * g_bookmark_file_set_added_date_time:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @added: a #GDateTime
+ *
+ * Sets the time the bookmark for @uri was added into @bookmark.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * Since: 2.66
+ */
+void
+g_bookmark_file_set_added_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GDateTime *added)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (added != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ g_clear_pointer (&item->added, g_date_time_unref);
+ item->added = g_date_time_ref (added);
+ g_clear_pointer (&item->modified, g_date_time_unref);
+ item->modified = g_date_time_ref (added);
+}
+
+/**
+ * g_bookmark_file_get_added:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the time the bookmark for @uri was added to @bookmark
+ *
+ * In the event the URI cannot be found, -1 is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: a timestamp
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_get_added_date_time() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+time_t
+g_bookmark_file_get_added (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ GDateTime *added = g_bookmark_file_get_added_date_time (bookmark, uri, error);
+ return (added != NULL) ? g_date_time_to_unix (added) : (time_t) -1;
+}
+
+/**
+ * g_bookmark_file_get_added_date_time:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the time the bookmark for @uri was added to @bookmark
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: (transfer none): a #GDateTime
+ *
+ * Since: 2.66
+ */
+GDateTime *
+g_bookmark_file_get_added_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ return item->added;
+}
+
+/**
+ * g_bookmark_file_set_modified:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @modified: a timestamp or -1 to use the current time
+ *
+ * Sets the last time the bookmark for @uri was last modified.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * The "modified" time should only be set when the bookmark's meta-data
+ * was actually changed. Every function of #GBookmarkFile that
+ * modifies a bookmark also changes the modification time, except for
+ * g_bookmark_file_set_visited_date_time().
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_set_modified_date_time() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+void
+g_bookmark_file_set_modified (GBookmarkFile *bookmark,
+ const gchar *uri,
+ time_t modified)
+{
+ GDateTime *modified_dt = (modified != (time_t) -1) ? g_date_time_new_from_unix_utc (modified) : g_date_time_new_now_utc ();
+ g_bookmark_file_set_modified_date_time (bookmark, uri, modified_dt);
+ g_date_time_unref (modified_dt);
+}
+
+/**
+ * g_bookmark_file_set_modified_date_time:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @modified: a #GDateTime
+ *
+ * Sets the last time the bookmark for @uri was last modified.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * The "modified" time should only be set when the bookmark's meta-data
+ * was actually changed. Every function of #GBookmarkFile that
+ * modifies a bookmark also changes the modification time, except for
+ * g_bookmark_file_set_visited_date_time().
+ *
+ * Since: 2.66
+ */
+void
+g_bookmark_file_set_modified_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GDateTime *modified)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (modified != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ g_clear_pointer (&item->modified, g_date_time_unref);
+ item->modified = g_date_time_ref (modified);
+}
+
+/**
+ * g_bookmark_file_get_modified:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the time when the bookmark for @uri was last modified.
+ *
+ * In the event the URI cannot be found, -1 is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: a timestamp
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_get_modified_date_time() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+time_t
+g_bookmark_file_get_modified (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ GDateTime *modified = g_bookmark_file_get_modified_date_time (bookmark, uri, error);
+ return (modified != NULL) ? g_date_time_to_unix (modified) : (time_t) -1;
+}
+
+/**
+ * g_bookmark_file_get_modified_date_time:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the time when the bookmark for @uri was last modified.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: (transfer none): a #GDateTime
+ *
+ * Since: 2.66
+ */
+GDateTime *
+g_bookmark_file_get_modified_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ return item->modified;
+}
+
+/**
+ * g_bookmark_file_set_visited:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @visited: a timestamp or -1 to use the current time
+ *
+ * Sets the time the bookmark for @uri was last visited.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * The "visited" time should only be set if the bookmark was launched,
+ * either using the command line retrieved by g_bookmark_file_get_application_info()
+ * or by the default application for the bookmark's MIME type, retrieved
+ * using g_bookmark_file_get_mime_type(). Changing the "visited" time
+ * does not affect the "modified" time.
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_set_visited_date_time() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+void
+g_bookmark_file_set_visited (GBookmarkFile *bookmark,
+ const gchar *uri,
+ time_t visited)
+{
+ GDateTime *visited_dt = (visited != (time_t) -1) ? g_date_time_new_from_unix_utc (visited) : g_date_time_new_now_utc ();
+ g_bookmark_file_set_visited_date_time (bookmark, uri, visited_dt);
+ g_date_time_unref (visited_dt);
+}
+
+/**
+ * g_bookmark_file_set_visited_date_time:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @visited: a #GDateTime
+ *
+ * Sets the time the bookmark for @uri was last visited.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * The "visited" time should only be set if the bookmark was launched,
+ * either using the command line retrieved by g_bookmark_file_get_application_info()
+ * or by the default application for the bookmark's MIME type, retrieved
+ * using g_bookmark_file_get_mime_type(). Changing the "visited" time
+ * does not affect the "modified" time.
+ *
+ * Since: 2.66
+ */
+void
+g_bookmark_file_set_visited_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GDateTime *visited)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (visited != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ g_clear_pointer (&item->visited, g_date_time_unref);
+ item->visited = g_date_time_ref (visited);
+}
+
+/**
+ * g_bookmark_file_get_visited:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the time the bookmark for @uri was last visited.
+ *
+ * In the event the URI cannot be found, -1 is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: a timestamp.
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_get_visited_date_time() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+time_t
+g_bookmark_file_get_visited (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error)
+{
+ GDateTime *visited = g_bookmark_file_get_visited_date_time (bookmark, uri, error);
+ return (visited != NULL) ? g_date_time_to_unix (visited) : (time_t) -1;
+}
+
+/**
+ * g_bookmark_file_get_visited_date_time:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the time the bookmark for @uri was last visited.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: (transfer none): a #GDateTime
+ *
+ * Since: 2.66
+ */
+GDateTime *
+g_bookmark_file_get_visited_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ return item->visited;
+}
+
+/**
+ * g_bookmark_file_has_group:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @group: the group name to be searched
+ * @error: return location for a #GError, or %NULL
+ *
+ * Checks whether @group appears in the list of groups to which
+ * the bookmark for @uri belongs to.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: %TRUE if @group was found.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_has_group (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *group,
+ GError **error)
+{
+ BookmarkItem *item;
+ GList *l;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ if (!item->metadata)
+ return FALSE;
+
+ for (l = item->metadata->groups; l != NULL; l = l->next)
+ {
+ if (strcmp (l->data, group) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+/**
+ * g_bookmark_file_add_group:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @group: the group name to be added
+ *
+ * Adds @group to the list of groups to which the bookmark for @uri
+ * belongs to.
+ *
+ * If no bookmark for @uri is found then it is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_add_group (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *group)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (group != NULL && group[0] != '\0');
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ if (!g_bookmark_file_has_group (bookmark, uri, group, NULL))
+ {
+ item->metadata->groups = g_list_prepend (item->metadata->groups,
+ g_strdup (group));
+
+ bookmark_item_touch_modified (item);
+ }
+}
+
+/**
+ * g_bookmark_file_remove_group:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @group: the group name to be removed
+ * @error: return location for a #GError, or %NULL
+ *
+ * Removes @group from the list of groups to which the bookmark
+ * for @uri belongs to.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ * In the event no group was defined, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
+ *
+ * Returns: %TRUE if @group was successfully removed.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_remove_group (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *group,
+ GError **error)
+{
+ BookmarkItem *item;
+ GList *l;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ if (!item->metadata)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
+ _("No groups set in bookmark for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ for (l = item->metadata->groups; l != NULL; l = l->next)
+ {
+ if (strcmp (l->data, group) == 0)
+ {
+ item->metadata->groups = g_list_remove_link (item->metadata->groups, l);
+ g_free (l->data);
+ g_list_free_1 (l);
+
+ bookmark_item_touch_modified (item);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * g_bookmark_file_set_groups:
+ * @bookmark: a #GBookmarkFile
+ * @uri: an item's URI
+ * @groups: (nullable) (array length=length) (element-type utf8): an array of
+ * group names, or %NULL to remove all groups
+ * @length: number of group name values in @groups
+ *
+ * Sets a list of group names for the item with URI @uri. Each previously
+ * set group name list is removed.
+ *
+ * If @uri cannot be found then an item for it is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_set_groups (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar **groups,
+ gsize length)
+{
+ BookmarkItem *item;
+ gsize i;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+ g_return_if_fail (groups != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ g_list_free_full (item->metadata->groups, g_free);
+ item->metadata->groups = NULL;
+
+ if (groups)
+ {
+ for (i = 0; i < length && groups[i] != NULL; i++)
+ item->metadata->groups = g_list_append (item->metadata->groups,
+ g_strdup (groups[i]));
+ }
+
+ bookmark_item_touch_modified (item);
+}
+
+/**
+ * g_bookmark_file_get_groups:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @length: (out) (optional): return location for the length of the returned string, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Retrieves the list of group names of the bookmark for @uri.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * The returned array is %NULL terminated, so @length may optionally
+ * be %NULL.
+ *
+ * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of group names.
+ * Use g_strfreev() to free it.
+ *
+ * Since: 2.12
+ */
+gchar **
+g_bookmark_file_get_groups (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gsize *length,
+ GError **error)
+{
+ BookmarkItem *item;
+ GList *l;
+ gsize len, i;
+ gchar **retval;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ if (!item->metadata)
+ {
+ if (length)
+ *length = 0;
+
+ return NULL;
+ }
+
+ len = g_list_length (item->metadata->groups);
+ retval = g_new0 (gchar *, len + 1);
+ for (l = g_list_last (item->metadata->groups), i = 0;
+ l != NULL;
+ l = l->prev)
+ {
+ gchar *group_name = (gchar *) l->data;
+
+ g_warn_if_fail (group_name != NULL);
+
+ retval[i++] = g_strdup (group_name);
+ }
+ retval[i] = NULL;
+
+ if (length)
+ *length = len;
+
+ return retval;
+}
+
+/**
+ * g_bookmark_file_add_application:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: (nullable): the name of the application registering the bookmark
+ * or %NULL
+ * @exec: (nullable): command line to be used to launch the bookmark or %NULL
+ *
+ * Adds the application with @name and @exec to the list of
+ * applications that have registered a bookmark for @uri into
+ * @bookmark.
+ *
+ * Every bookmark inside a #GBookmarkFile must have at least an
+ * application registered. Each application must provide a name, a
+ * command line useful for launching the bookmark, the number of times
+ * the bookmark has been registered by the application and the last
+ * time the application registered this bookmark.
+ *
+ * If @name is %NULL, the name of the application will be the
+ * same returned by g_get_application_name(); if @exec is %NULL, the
+ * command line will be a composition of the program name as
+ * returned by g_get_prgname() and the "\%u" modifier, which will be
+ * expanded to the bookmark's URI.
+ *
+ * This function will automatically take care of updating the
+ * registrations count and timestamping in case an application
+ * with the same @name had already registered a bookmark for
+ * @uri inside @bookmark.
+ *
+ * If no bookmark for @uri is found, one is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_add_application (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ const gchar *exec)
+{
+ BookmarkItem *item;
+ gchar *app_name, *app_exec;
+ GDateTime *stamp;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ if (name && name[0] != '\0')
+ app_name = g_strdup (name);
+ else
+ app_name = g_strdup (g_get_application_name ());
+
+ if (exec && exec[0] != '\0')
+ app_exec = g_strdup (exec);
+ else
+ app_exec = g_strjoin (" ", g_get_prgname(), "%u", NULL);
+
+ stamp = g_date_time_new_now_utc ();
+
+ g_bookmark_file_set_application_info (bookmark, uri,
+ app_name,
+ app_exec,
+ -1,
+ stamp,
+ NULL);
+
+ g_date_time_unref (stamp);
+ g_free (app_exec);
+ g_free (app_name);
+}
+
+/**
+ * g_bookmark_file_remove_application:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: the name of the application
+ * @error: return location for a #GError or %NULL
+ *
+ * Removes application registered with @name from the list of applications
+ * that have registered a bookmark for @uri inside @bookmark.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ * In the event that no application with name @app_name has registered
+ * a bookmark for @uri, %FALSE is returned and error is set to
+ * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.
+ *
+ * Returns: %TRUE if the application was successfully removed.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_remove_application (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ GError **error)
+{
+ GError *set_error;
+ gboolean retval;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ set_error = NULL;
+ retval = g_bookmark_file_set_application_info (bookmark, uri,
+ name,
+ "",
+ 0,
+ NULL,
+ &set_error);
+ if (set_error)
+ {
+ g_propagate_error (error, set_error);
+
+ return FALSE;
+ }
+
+ return retval;
+}
+
+/**
+ * g_bookmark_file_has_application:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: the name of the application
+ * @error: return location for a #GError or %NULL
+ *
+ * Checks whether the bookmark for @uri inside @bookmark has been
+ * registered by application @name.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: %TRUE if the application @name was found
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_has_application (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ return (NULL != bookmark_item_lookup_app_info (item, name));
+}
+
+/**
+ * g_bookmark_file_set_app_info:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: an application's name
+ * @exec: an application's command line
+ * @count: the number of registrations done for this application
+ * @stamp: the time of the last registration for this application
+ * @error: return location for a #GError or %NULL
+ *
+ * Sets the meta-data of application @name inside the list of
+ * applications that have registered a bookmark for @uri inside
+ * @bookmark.
+ *
+ * You should rarely use this function; use g_bookmark_file_add_application()
+ * and g_bookmark_file_remove_application() instead.
+ *
+ * @name can be any UTF-8 encoded string used to identify an
+ * application.
+ * @exec can have one of these two modifiers: "\%f", which will
+ * be expanded as the local file name retrieved from the bookmark's
+ * URI; "\%u", which will be expanded as the bookmark's URI.
+ * The expansion is done automatically when retrieving the stored
+ * command line using the g_bookmark_file_get_application_info() function.
+ * @count is the number of times the application has registered the
+ * bookmark; if is < 0, the current registration count will be increased
+ * by one, if is 0, the application with @name will be removed from
+ * the list of registered applications.
+ * @stamp is the Unix time of the last registration; if it is -1, the
+ * current time will be used.
+ *
+ * If you try to remove an application by setting its registration count to
+ * zero, and no bookmark for @uri is found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
+ * in the event that no application @name has registered a bookmark
+ * for @uri, %FALSE is returned and error is set to
+ * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. Otherwise, if no bookmark
+ * for @uri is found, one is created.
+ *
+ * Returns: %TRUE if the application's meta-data was successfully
+ * changed.
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_set_application_info() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+gboolean
+g_bookmark_file_set_app_info (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ const gchar *exec,
+ gint count,
+ time_t stamp,
+ GError **error)
+{
+ GDateTime *stamp_dt = (stamp != (time_t) -1) ? g_date_time_new_from_unix_utc (stamp) : g_date_time_new_now_utc ();
+ gboolean retval;
+ retval = g_bookmark_file_set_application_info (bookmark, uri, name, exec, count,
+ stamp_dt, error);
+ g_date_time_unref (stamp_dt);
+ return retval;
+}
+
+/**
+ * g_bookmark_file_set_application_info:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: an application's name
+ * @exec: an application's command line
+ * @count: the number of registrations done for this application
+ * @stamp: (nullable): the time of the last registration for this application,
+ * which may be %NULL if @count is 0
+ * @error: return location for a #GError or %NULL
+ *
+ * Sets the meta-data of application @name inside the list of
+ * applications that have registered a bookmark for @uri inside
+ * @bookmark.
+ *
+ * You should rarely use this function; use g_bookmark_file_add_application()
+ * and g_bookmark_file_remove_application() instead.
+ *
+ * @name can be any UTF-8 encoded string used to identify an
+ * application.
+ * @exec can have one of these two modifiers: "\%f", which will
+ * be expanded as the local file name retrieved from the bookmark's
+ * URI; "\%u", which will be expanded as the bookmark's URI.
+ * The expansion is done automatically when retrieving the stored
+ * command line using the g_bookmark_file_get_application_info() function.
+ * @count is the number of times the application has registered the
+ * bookmark; if is < 0, the current registration count will be increased
+ * by one, if is 0, the application with @name will be removed from
+ * the list of registered applications.
+ * @stamp is the Unix time of the last registration.
+ *
+ * If you try to remove an application by setting its registration count to
+ * zero, and no bookmark for @uri is found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
+ * in the event that no application @name has registered a bookmark
+ * for @uri, %FALSE is returned and error is set to
+ * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. Otherwise, if no bookmark
+ * for @uri is found, one is created.
+ *
+ * Returns: %TRUE if the application's meta-data was successfully
+ * changed.
+ *
+ * Since: 2.66
+ */
+gboolean
+g_bookmark_file_set_application_info (GBookmarkFile *bookmark,
+ const char *uri,
+ const char *name,
+ const char *exec,
+ int count,
+ GDateTime *stamp,
+ GError **error)
+{
+ BookmarkItem *item;
+ BookmarkAppInfo *ai;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (exec != NULL, FALSE);
+ g_return_val_if_fail (count == 0 || stamp != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ if (count == 0)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+ else
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+ }
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ ai = bookmark_item_lookup_app_info (item, name);
+ if (!ai)
+ {
+ if (count == 0)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
+ _("No application with name “%s” registered a bookmark for “%s”"),
+ name,
+ uri);
+ return FALSE;
+ }
+ else
+ {
+ ai = bookmark_app_info_new (name);
+
+ item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
+ g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
+ }
+ }
+
+ if (count == 0)
+ {
+ item->metadata->applications = g_list_remove (item->metadata->applications, ai);
+ g_hash_table_remove (item->metadata->apps_by_name, ai->name);
+ bookmark_app_info_free (ai);
+
+ bookmark_item_touch_modified (item);
+
+ return TRUE;
+ }
+ else if (count > 0)
+ ai->count = count;
+ else
+ ai->count += 1;
+
+ g_clear_pointer (&ai->stamp, g_date_time_unref);
+ ai->stamp = g_date_time_ref (stamp);
+
+ if (exec && exec[0] != '\0')
+ {
+ g_free (ai->exec);
+ ai->exec = g_shell_quote (exec);
+ }
+
+ bookmark_item_touch_modified (item);
+
+ return TRUE;
+}
+
+/* expands the application's command line */
+static gchar *
+expand_exec_line (const gchar *exec_fmt,
+ const gchar *uri)
+{
+ GString *exec;
+ gchar ch;
+
+ exec = g_string_sized_new (512);
+ while ((ch = *exec_fmt++) != '\0')
+ {
+ if (ch != '%')
+ {
+ exec = g_string_append_c (exec, ch);
+ continue;
+ }
+
+ ch = *exec_fmt++;
+ switch (ch)
+ {
+ case '\0':
+ goto out;
+ case 'U':
+ case 'u':
+ g_string_append (exec, uri);
+ break;
+ case 'F':
+ case 'f':
+ {
+ gchar *file = g_filename_from_uri (uri, NULL, NULL);
+ if (file)
+ {
+ g_string_append (exec, file);
+ g_free (file);
+ }
+ else
+ {
+ g_string_free (exec, TRUE);
+ return NULL;
+ }
+ }
+ break;
+ case '%':
+ default:
+ exec = g_string_append_c (exec, ch);
+ break;
+ }
+ }
+
+ out:
+ return g_string_free (exec, FALSE);
+}
+
+/**
+ * g_bookmark_file_get_app_info:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: an application's name
+ * @exec: (out) (optional): return location for the command line of the application, or %NULL
+ * @count: (out) (optional): return location for the registration count, or %NULL
+ * @stamp: (out) (optional): return location for the last registration time, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the registration information of @app_name for the bookmark for
+ * @uri. See g_bookmark_file_set_application_info() for more information about
+ * the returned data.
+ *
+ * The string returned in @app_exec must be freed.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
+ * event that no application with name @app_name has registered a bookmark
+ * for @uri, %FALSE is returned and error is set to
+ * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
+ * the command line fails, an error of the %G_SHELL_ERROR domain is
+ * set and %FALSE is returned.
+ *
+ * Returns: %TRUE on success.
+ *
+ * Since: 2.12
+ * Deprecated: 2.66: Use g_bookmark_file_get_application_info() instead, as
+ * `time_t` is deprecated due to the year 2038 problem.
+ */
+gboolean
+g_bookmark_file_get_app_info (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ gchar **exec,
+ guint *count,
+ time_t *stamp,
+ GError **error)
+{
+ GDateTime *stamp_dt = NULL;
+ gboolean retval;
+
+ retval = g_bookmark_file_get_application_info (bookmark, uri, name, exec, count, &stamp_dt, error);
+ if (!retval)
+ return FALSE;
+
+ if (stamp != NULL)
+ *stamp = g_date_time_to_unix (stamp_dt);
+
+ return TRUE;
+}
+
+/**
+ * g_bookmark_file_get_application_info:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @name: an application's name
+ * @exec: (out) (optional): return location for the command line of the application, or %NULL
+ * @count: (out) (optional): return location for the registration count, or %NULL
+ * @stamp: (out) (optional) (transfer none): return location for the last registration time, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the registration information of @app_name for the bookmark for
+ * @uri. See g_bookmark_file_set_application_info() for more information about
+ * the returned data.
+ *
+ * The string returned in @app_exec must be freed.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
+ * event that no application with name @app_name has registered a bookmark
+ * for @uri, %FALSE is returned and error is set to
+ * %G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
+ * the command line fails, an error of the %G_SHELL_ERROR domain is
+ * set and %FALSE is returned.
+ *
+ * Returns: %TRUE on success.
+ *
+ * Since: 2.66
+ */
+gboolean
+g_bookmark_file_get_application_info (GBookmarkFile *bookmark,
+ const char *uri,
+ const char *name,
+ char **exec,
+ unsigned int *count,
+ GDateTime **stamp,
+ GError **error)
+{
+ BookmarkItem *item;
+ BookmarkAppInfo *ai;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ ai = bookmark_item_lookup_app_info (item, name);
+ if (!ai)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
+ _("No application with name “%s” registered a bookmark for “%s”"),
+ name,
+ uri);
+ return FALSE;
+ }
+
+ if (exec)
+ {
+ GError *unquote_error = NULL;
+ gchar *command_line;
+
+ command_line = g_shell_unquote (ai->exec, &unquote_error);
+ if (unquote_error)
+ {
+ g_propagate_error (error, unquote_error);
+ return FALSE;
+ }
+
+ *exec = expand_exec_line (command_line, uri);
+ if (!*exec)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_INVALID_URI,
+ _("Failed to expand exec line “%s” with URI “%s”"),
+ ai->exec, uri);
+ g_free (command_line);
+
+ return FALSE;
+ }
+ else
+ g_free (command_line);
+ }
+
+ if (count)
+ *count = ai->count;
+
+ if (stamp)
+ *stamp = ai->stamp;
+
+ return TRUE;
+}
+
+/**
+ * g_bookmark_file_get_applications:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @length: (out) (optional): return location of the length of the returned list, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Retrieves the names of the applications that have registered the
+ * bookmark for @uri.
+ *
+ * In the event the URI cannot be found, %NULL is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: (array length=length) (transfer full): a newly allocated %NULL-terminated array of strings.
+ * Use g_strfreev() to free it.
+ *
+ * Since: 2.12
+ */
+gchar **
+g_bookmark_file_get_applications (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gsize *length,
+ GError **error)
+{
+ BookmarkItem *item;
+ GList *l;
+ gchar **apps;
+ gsize i, n_apps;
+
+ g_return_val_if_fail (bookmark != NULL, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return NULL;
+ }
+
+ if (!item->metadata)
+ {
+ if (length)
+ *length = 0;
+
+ return NULL;
+ }
+
+ n_apps = g_list_length (item->metadata->applications);
+ apps = g_new0 (gchar *, n_apps + 1);
+
+ for (l = g_list_last (item->metadata->applications), i = 0;
+ l != NULL;
+ l = l->prev)
+ {
+ BookmarkAppInfo *ai;
+
+ ai = (BookmarkAppInfo *) l->data;
+
+ g_warn_if_fail (ai != NULL);
+ g_warn_if_fail (ai->name != NULL);
+
+ apps[i++] = g_strdup (ai->name);
+ }
+ apps[i] = NULL;
+
+ if (length)
+ *length = i;
+
+ return apps;
+}
+
+/**
+ * g_bookmark_file_get_size:
+ * @bookmark: a #GBookmarkFile
+ *
+ * Gets the number of bookmarks inside @bookmark.
+ *
+ * Returns: the number of bookmarks
+ *
+ * Since: 2.12
+ */
+gint
+g_bookmark_file_get_size (GBookmarkFile *bookmark)
+{
+ g_return_val_if_fail (bookmark != NULL, 0);
+
+ return g_list_length (bookmark->items);
+}
+
+/**
+ * g_bookmark_file_move_item:
+ * @bookmark: a #GBookmarkFile
+ * @old_uri: a valid URI
+ * @new_uri: (nullable): a valid URI, or %NULL
+ * @error: return location for a #GError or %NULL
+ *
+ * Changes the URI of a bookmark item from @old_uri to @new_uri. Any
+ * existing bookmark for @new_uri will be overwritten. If @new_uri is
+ * %NULL, then the bookmark is removed.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: %TRUE if the URI was successfully changed
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_move_item (GBookmarkFile *bookmark,
+ const gchar *old_uri,
+ const gchar *new_uri,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (old_uri != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, old_uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ old_uri);
+ return FALSE;
+ }
+
+ if (new_uri && new_uri[0] != '\0')
+ {
+ if (g_strcmp0 (old_uri, new_uri) == 0)
+ return TRUE;
+
+ if (g_bookmark_file_has_item (bookmark, new_uri))
+ {
+ if (!g_bookmark_file_remove_item (bookmark, new_uri, error))
+ return FALSE;
+ }
+
+ g_hash_table_steal (bookmark->items_by_uri, item->uri);
+
+ g_free (item->uri);
+ item->uri = g_strdup (new_uri);
+ bookmark_item_touch_modified (item);
+
+ g_hash_table_replace (bookmark->items_by_uri, item->uri, item);
+
+ return TRUE;
+ }
+ else
+ {
+ if (!g_bookmark_file_remove_item (bookmark, old_uri, error))
+ return FALSE;
+
+ return TRUE;
+ }
+}
+
+/**
+ * g_bookmark_file_set_icon:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @href: (nullable): the URI of the icon for the bookmark, or %NULL
+ * @mime_type: the MIME type of the icon for the bookmark
+ *
+ * Sets the icon for the bookmark for @uri. If @href is %NULL, unsets
+ * the currently set icon. @href can either be a full URL for the icon
+ * file or the icon name following the Icon Naming specification.
+ *
+ * If no bookmark for @uri is found one is created.
+ *
+ * Since: 2.12
+ */
+void
+g_bookmark_file_set_icon (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *href,
+ const gchar *mime_type)
+{
+ BookmarkItem *item;
+
+ g_return_if_fail (bookmark != NULL);
+ g_return_if_fail (uri != NULL);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ item = bookmark_item_new (uri);
+ g_bookmark_file_add_item (bookmark, item, NULL);
+ }
+
+ if (!item->metadata)
+ item->metadata = bookmark_metadata_new ();
+
+ g_free (item->metadata->icon_href);
+ g_free (item->metadata->icon_mime);
+
+ item->metadata->icon_href = g_strdup (href);
+
+ if (mime_type && mime_type[0] != '\0')
+ item->metadata->icon_mime = g_strdup (mime_type);
+ else
+ item->metadata->icon_mime = g_strdup ("application/octet-stream");
+
+ bookmark_item_touch_modified (item);
+}
+
+/**
+ * g_bookmark_file_get_icon:
+ * @bookmark: a #GBookmarkFile
+ * @uri: a valid URI
+ * @href: (out) (optional): return location for the icon's location or %NULL
+ * @mime_type: (out) (optional): return location for the icon's MIME type or %NULL
+ * @error: return location for a #GError or %NULL
+ *
+ * Gets the icon of the bookmark for @uri.
+ *
+ * In the event the URI cannot be found, %FALSE is returned and
+ * @error is set to %G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
+ *
+ * Returns: %TRUE if the icon for the bookmark for the URI was found.
+ * You should free the returned strings.
+ *
+ * Since: 2.12
+ */
+gboolean
+g_bookmark_file_get_icon (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gchar **href,
+ gchar **mime_type,
+ GError **error)
+{
+ BookmarkItem *item;
+
+ g_return_val_if_fail (bookmark != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = g_bookmark_file_lookup_item (bookmark, uri);
+ if (!item)
+ {
+ g_set_error (error, G_BOOKMARK_FILE_ERROR,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ _("No bookmark found for URI “%s”"),
+ uri);
+ return FALSE;
+ }
+
+ if ((!item->metadata) || (!item->metadata->icon_href))
+ return FALSE;
+
+ if (href)
+ *href = g_strdup (item->metadata->icon_href);
+
+ if (mime_type)
+ *mime_type = g_strdup (item->metadata->icon_mime);
+
+ return TRUE;
+}
diff --git a/glib/gbookmarkfile.h b/glib/gbookmarkfile.h
new file mode 100644
index 0000000000000000000000000000000000000000..82ea98d5d03ef1fa4a154c76b5c692fdf9489ebf
--- /dev/null
+++ b/glib/gbookmarkfile.h
@@ -0,0 +1,295 @@
+/* gbookmarkfile.h: parsing and building desktop bookmarks
+ *
+ * Copyright (C) 2005-2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#ifndef __G_BOOKMARK_FILE_H__
+#define __G_BOOKMARK_FILE_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+
+/**
+ * G_BOOKMARK_FILE_ERROR:
+ *
+ * Error domain for bookmark file parsing.
+ *
+ * Errors in this domain will be from the #GBookmarkFileError
+ * enumeration. See #GError for information on error domains.
+ */
+#define G_BOOKMARK_FILE_ERROR (g_bookmark_file_error_quark ())
+
+
+/**
+ * GBookmarkFileError:
+ * @G_BOOKMARK_FILE_ERROR_INVALID_URI: URI was ill-formed
+ * @G_BOOKMARK_FILE_ERROR_INVALID_VALUE: a requested field was not found
+ * @G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED: a requested application did
+ * not register a bookmark
+ * @G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND: a requested URI was not found
+ * @G_BOOKMARK_FILE_ERROR_READ: document was ill formed
+ * @G_BOOKMARK_FILE_ERROR_UNKNOWN_ENCODING: the text being parsed was
+ * in an unknown encoding
+ * @G_BOOKMARK_FILE_ERROR_WRITE: an error occurred while writing
+ * @G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND: requested file was not found
+ *
+ * Error codes returned by bookmark file parsing.
+ */
+typedef enum
+{
+ G_BOOKMARK_FILE_ERROR_INVALID_URI,
+ G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
+ G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
+ G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
+ G_BOOKMARK_FILE_ERROR_READ,
+ G_BOOKMARK_FILE_ERROR_UNKNOWN_ENCODING,
+ G_BOOKMARK_FILE_ERROR_WRITE,
+ G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND
+} GBookmarkFileError;
+
+GLIB_AVAILABLE_IN_ALL
+GQuark g_bookmark_file_error_quark (void);
+
+/**
+ * GBookmarkFile:
+ *
+ * An opaque data structure representing a set of bookmarks.
+ */
+typedef struct _GBookmarkFile GBookmarkFile;
+
+GLIB_AVAILABLE_IN_ALL
+GBookmarkFile *g_bookmark_file_new (void);
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_free (GBookmarkFile *bookmark);
+
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_load_from_file (GBookmarkFile *bookmark,
+ const gchar *filename,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_load_from_data (GBookmarkFile *bookmark,
+ const gchar *data,
+ gsize length,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_load_from_data_dirs (GBookmarkFile *bookmark,
+ const gchar *file,
+ gchar **full_path,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gchar * g_bookmark_file_to_data (GBookmarkFile *bookmark,
+ gsize *length,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_to_file (GBookmarkFile *bookmark,
+ const gchar *filename,
+ GError **error);
+
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_set_title (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *title);
+GLIB_AVAILABLE_IN_ALL
+gchar * g_bookmark_file_get_title (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_set_description (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *description);
+GLIB_AVAILABLE_IN_ALL
+gchar * g_bookmark_file_get_description (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *mime_type);
+GLIB_AVAILABLE_IN_ALL
+gchar * g_bookmark_file_get_mime_type (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_set_groups (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar **groups,
+ gsize length);
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_add_group (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *group);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_has_group (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *group,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gchar ** g_bookmark_file_get_groups (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gsize *length,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_add_application (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ const gchar *exec);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_has_application (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gchar ** g_bookmark_file_get_applications (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gsize *length,
+ GError **error);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_set_application_info)
+gboolean g_bookmark_file_set_app_info (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ const gchar *exec,
+ gint count,
+ time_t stamp,
+ GError **error);
+GLIB_AVAILABLE_IN_2_66
+gboolean g_bookmark_file_set_application_info (GBookmarkFile *bookmark,
+ const char *uri,
+ const char *name,
+ const char *exec,
+ int count,
+ GDateTime *stamp,
+ GError **error);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_get_application_info)
+gboolean g_bookmark_file_get_app_info (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ gchar **exec,
+ guint *count,
+ time_t *stamp,
+ GError **error);
+GLIB_AVAILABLE_IN_2_66
+gboolean g_bookmark_file_get_application_info (GBookmarkFile *bookmark,
+ const char *uri,
+ const char *name,
+ char **exec,
+ unsigned int *count,
+ GDateTime **stamp,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gboolean is_private);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_get_is_private (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+void g_bookmark_file_set_icon (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *href,
+ const gchar *mime_type);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_get_icon (GBookmarkFile *bookmark,
+ const gchar *uri,
+ gchar **href,
+ gchar **mime_type,
+ GError **error);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_set_added_date_time)
+void g_bookmark_file_set_added (GBookmarkFile *bookmark,
+ const gchar *uri,
+ time_t added);
+GLIB_AVAILABLE_IN_2_66
+void g_bookmark_file_set_added_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GDateTime *added);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_get_added_date_time)
+time_t g_bookmark_file_get_added (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error);
+GLIB_AVAILABLE_IN_2_66
+GDateTime *g_bookmark_file_get_added_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GError **error);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_set_modified_date_time)
+void g_bookmark_file_set_modified (GBookmarkFile *bookmark,
+ const gchar *uri,
+ time_t modified);
+GLIB_AVAILABLE_IN_2_66
+void g_bookmark_file_set_modified_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GDateTime *modified);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_get_modified_date_time)
+time_t g_bookmark_file_get_modified (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error);
+GLIB_AVAILABLE_IN_2_66
+GDateTime *g_bookmark_file_get_modified_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GError **error);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_set_visited_date_time)
+void g_bookmark_file_set_visited (GBookmarkFile *bookmark,
+ const gchar *uri,
+ time_t visited);
+GLIB_AVAILABLE_IN_2_66
+void g_bookmark_file_set_visited_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GDateTime *visited);
+GLIB_DEPRECATED_IN_2_66_FOR(g_bookmark_file_get_visited_date_time)
+time_t g_bookmark_file_get_visited (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error);
+GLIB_AVAILABLE_IN_2_66
+GDateTime *g_bookmark_file_get_visited_date_time (GBookmarkFile *bookmark,
+ const char *uri,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_has_item (GBookmarkFile *bookmark,
+ const gchar *uri);
+GLIB_AVAILABLE_IN_ALL
+gint g_bookmark_file_get_size (GBookmarkFile *bookmark);
+GLIB_AVAILABLE_IN_ALL
+gchar ** g_bookmark_file_get_uris (GBookmarkFile *bookmark,
+ gsize *length);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_remove_group (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *group,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_remove_application (GBookmarkFile *bookmark,
+ const gchar *uri,
+ const gchar *name,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_remove_item (GBookmarkFile *bookmark,
+ const gchar *uri,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bookmark_file_move_item (GBookmarkFile *bookmark,
+ const gchar *old_uri,
+ const gchar *new_uri,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __G_BOOKMARK_FILE_H__ */
diff --git a/glib/gbsearcharray.h b/glib/gbsearcharray.h
new file mode 100644
index 0000000000000000000000000000000000000000..39afa3f79424098bff98a37a1af2b80c47436df9
--- /dev/null
+++ b/glib/gbsearcharray.h
@@ -0,0 +1,299 @@
+/* GBSearchArray - Binary Searchable Array implementation
+ * Copyright (C) 2000-2003 Tim Janik
+ *
+ * This software is provided "as is"; redistribution and modification
+ * is permitted, provided that the following disclaimer is retained.
+ *
+ * This software 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.
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+#ifndef __G_BSEARCH_ARRAY_H__
+#define __G_BSEARCH_ARRAY_H__
+
+#include
+#include
+
+
+G_BEGIN_DECLS /* c++ guards */
+
+/* this implementation is intended to be usable in third-party code
+ * simply by pasting the contents of this file. as such, the
+ * implementation needs to be self-contained within this file.
+ */
+
+/* convenience macro to avoid signed overflow for value comparisons */
+#define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) > (v2) ? +1 : (v1) == (v2) ? 0 : -1)
+
+
+/* --- typedefs --- */
+typedef gint (*GBSearchCompareFunc) (gconstpointer bsearch_node1, /* key */
+ gconstpointer bsearch_node2);
+typedef enum
+{
+ G_BSEARCH_ARRAY_ALIGN_POWER2 = 1 << 0, /* align memory to power2 sizes */
+ G_BSEARCH_ARRAY_AUTO_SHRINK = 1 << 1 /* shrink array upon removal */
+} GBSearchArrayFlags;
+
+
+/* --- structures --- */
+typedef struct
+{
+ guint sizeof_node;
+ GBSearchCompareFunc cmp_nodes;
+ guint flags;
+} GBSearchConfig;
+typedef union
+{
+ guint n_nodes;
+ /*< private >*/
+ gpointer alignment_dummy1;
+ glong alignment_dummy2;
+ gdouble alignment_dummy3;
+} GBSearchArray;
+
+
+/* --- public API --- */
+static inline GBSearchArray* g_bsearch_array_create (const GBSearchConfig *bconfig);
+static inline gpointer g_bsearch_array_get_nth (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ guint nth);
+static inline guint g_bsearch_array_get_index (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer node_in_array);
+static inline GBSearchArray* g_bsearch_array_remove (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ guint index_);
+/* provide uninitialized space at index for node insertion */
+static inline GBSearchArray* g_bsearch_array_grow (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ guint index);
+/* insert key_node into array if it does not exist, otherwise do nothing */
+static inline GBSearchArray* g_bsearch_array_insert (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer key_node);
+/* insert key_node into array if it does not exist,
+ * otherwise replace the existing node's contents with key_node
+ */
+static inline GBSearchArray* g_bsearch_array_replace (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer key_node);
+static inline void g_bsearch_array_free (GBSearchArray *barray,
+ const GBSearchConfig *bconfig);
+#define g_bsearch_array_get_n_nodes(barray) (((GBSearchArray*) (barray))->n_nodes)
+
+/* g_bsearch_array_lookup():
+ * return NULL or exact match node
+ */
+#define g_bsearch_array_lookup(barray, bconfig, key_node) \
+ g_bsearch_array_lookup_fuzzy ((barray), (bconfig), (key_node), 0)
+
+/* g_bsearch_array_lookup_sibling():
+ * return NULL for barray->n_nodes==0, otherwise return the
+ * exact match node, or, if there's no such node, return the
+ * node last visited, which is pretty close to an exact match
+ * (will be one off into either direction).
+ */
+#define g_bsearch_array_lookup_sibling(barray, bconfig, key_node) \
+ g_bsearch_array_lookup_fuzzy ((barray), (bconfig), (key_node), 1)
+
+/* g_bsearch_array_lookup_insertion():
+ * return NULL for barray->n_nodes==0 or exact match, otherwise
+ * return the node where key_node should be inserted (may be one
+ * after end, i.e. g_bsearch_array_get_index(result) <= barray->n_nodes).
+ */
+#define g_bsearch_array_lookup_insertion(barray, bconfig, key_node) \
+ g_bsearch_array_lookup_fuzzy ((barray), (bconfig), (key_node), 2)
+
+
+/* --- implementation --- */
+/* helper macro to cut down realloc()s */
+#define G_BSEARCH_UPPER_POWER2(n) ((n) ? 1 << g_bit_storage ((n) - 1) : 0)
+#define G_BSEARCH_ARRAY_NODES(barray) (((guint8*) (barray)) + sizeof (GBSearchArray))
+static inline GBSearchArray*
+g_bsearch_array_create (const GBSearchConfig *bconfig)
+{
+ GBSearchArray *barray;
+ guint size;
+
+ g_return_val_if_fail (bconfig != NULL, NULL);
+
+ size = sizeof (GBSearchArray) + bconfig->sizeof_node;
+ if (bconfig->flags & G_BSEARCH_ARRAY_ALIGN_POWER2)
+ size = G_BSEARCH_UPPER_POWER2 (size);
+ barray = (GBSearchArray *) g_malloc (size);
+ memset (barray, 0, sizeof (GBSearchArray));
+
+ return barray;
+}
+static inline gpointer
+g_bsearch_array_lookup_fuzzy (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer key_node,
+ const guint sibling_or_after);
+static inline gpointer
+g_bsearch_array_lookup_fuzzy (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer key_node,
+ const guint sibling_or_after)
+{
+ GBSearchCompareFunc cmp_nodes = bconfig->cmp_nodes;
+ guint8 *check = NULL, *nodes = G_BSEARCH_ARRAY_NODES (barray);
+ guint n_nodes = barray->n_nodes, offs = 0;
+ guint sizeof_node = bconfig->sizeof_node;
+ gint cmp = 0;
+
+ while (offs < n_nodes)
+ {
+ guint i = (offs + n_nodes) >> 1;
+
+ check = nodes + i * sizeof_node;
+ cmp = cmp_nodes (key_node, check);
+ if (cmp == 0)
+ return sibling_or_after > 1 ? NULL : check;
+ else if (cmp < 0)
+ n_nodes = i;
+ else /* (cmp > 0) */
+ offs = i + 1;
+ }
+
+ /* check is last mismatch, cmp > 0 indicates greater key */
+ return G_LIKELY (!sibling_or_after) ? NULL : (sibling_or_after > 1 && cmp > 0) ? check + sizeof_node : check;
+}
+static inline gpointer
+g_bsearch_array_get_nth (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ guint nth)
+{
+ return (G_LIKELY (nth < barray->n_nodes) ?
+ G_BSEARCH_ARRAY_NODES (barray) + nth * bconfig->sizeof_node :
+ NULL);
+}
+static inline guint
+g_bsearch_array_get_index (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer node_in_array)
+{
+ guint distance = ((guint8*) node_in_array) - G_BSEARCH_ARRAY_NODES (barray);
+
+ g_return_val_if_fail (node_in_array != NULL, barray->n_nodes);
+
+ distance /= bconfig->sizeof_node;
+
+ return MIN (distance, barray->n_nodes + 1); /* may return one after end */
+}
+static inline GBSearchArray*
+g_bsearch_array_grow (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ guint index_)
+{
+ guint old_size = barray->n_nodes * bconfig->sizeof_node;
+ guint new_size = old_size + bconfig->sizeof_node;
+ guint8 *node;
+
+ g_return_val_if_fail (index_ <= barray->n_nodes, NULL);
+
+ if (G_UNLIKELY (bconfig->flags & G_BSEARCH_ARRAY_ALIGN_POWER2))
+ {
+ new_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + new_size);
+ old_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + old_size);
+ if (old_size != new_size)
+ barray = (GBSearchArray *) g_realloc (barray, new_size);
+ }
+ else
+ barray = (GBSearchArray *) g_realloc (barray, sizeof (GBSearchArray) + new_size);
+ node = G_BSEARCH_ARRAY_NODES (barray) + index_ * bconfig->sizeof_node;
+ memmove (node + bconfig->sizeof_node, node, (barray->n_nodes - index_) * bconfig->sizeof_node);
+ barray->n_nodes += 1;
+ return barray;
+}
+static inline GBSearchArray*
+g_bsearch_array_insert (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer key_node)
+{
+ guint8 *node;
+
+ if (G_UNLIKELY (!barray->n_nodes))
+ {
+ barray = g_bsearch_array_grow (barray, bconfig, 0);
+ node = G_BSEARCH_ARRAY_NODES (barray);
+ }
+ else
+ {
+ node = (guint8 *) g_bsearch_array_lookup_insertion (barray, bconfig, key_node);
+ if (G_LIKELY (node))
+ {
+ guint index_ = g_bsearch_array_get_index (barray, bconfig, node);
+
+ /* grow and insert */
+ barray = g_bsearch_array_grow (barray, bconfig, index_);
+ node = G_BSEARCH_ARRAY_NODES (barray) + index_ * bconfig->sizeof_node;
+ }
+ else /* no insertion needed, node already there */
+ return barray;
+ }
+ memcpy (node, key_node, bconfig->sizeof_node);
+ return barray;
+}
+static inline GBSearchArray*
+g_bsearch_array_replace (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ gconstpointer key_node)
+{
+ guint8 *node = (guint8 *) g_bsearch_array_lookup (barray, bconfig, key_node);
+ if (G_LIKELY (node)) /* expected path */
+ memcpy (node, key_node, bconfig->sizeof_node);
+ else /* revert to insertion */
+ barray = g_bsearch_array_insert (barray, bconfig, key_node);
+ return barray;
+}
+static inline GBSearchArray*
+g_bsearch_array_remove (GBSearchArray *barray,
+ const GBSearchConfig *bconfig,
+ guint index_)
+{
+ guint8 *node;
+
+ g_return_val_if_fail (index_ < barray->n_nodes, NULL);
+
+ barray->n_nodes -= 1;
+ node = G_BSEARCH_ARRAY_NODES (barray) + index_ * bconfig->sizeof_node;
+ memmove (node, node + bconfig->sizeof_node, (barray->n_nodes - index_) * bconfig->sizeof_node);
+ if (G_UNLIKELY (bconfig->flags & G_BSEARCH_ARRAY_AUTO_SHRINK))
+ {
+ guint new_size = barray->n_nodes * bconfig->sizeof_node;
+ guint old_size = new_size + bconfig->sizeof_node;
+
+ if (G_UNLIKELY (bconfig->flags & G_BSEARCH_ARRAY_ALIGN_POWER2))
+ {
+ new_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + new_size);
+ old_size = G_BSEARCH_UPPER_POWER2 (sizeof (GBSearchArray) + old_size);
+ if (old_size != new_size)
+ barray = (GBSearchArray *) g_realloc (barray, new_size);
+ }
+ else
+ barray = (GBSearchArray *) g_realloc (barray, sizeof (GBSearchArray) + new_size);
+ }
+ return barray;
+}
+static inline void
+g_bsearch_array_free (GBSearchArray *barray,
+ const GBSearchConfig *bconfig)
+{
+ g_return_if_fail (barray != NULL);
+
+ g_free (barray);
+}
+
+G_END_DECLS /* c++ guards */
+
+#endif /* !__G_BSEARCH_ARRAY_H__ */
diff --git a/glib/gbytes.c b/glib/gbytes.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6ca0e300eed4dfd3cd50d591dc50e3ef5f014d2
--- /dev/null
+++ b/glib/gbytes.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright © 2009, 2010 Codethink Limited
+ * Copyright © 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Ryan Lortie
+ * Stef Walter
+ */
+
+#include "config.h"
+
+#include "gbytes.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+/**
+ * GBytes:
+ *
+ * A simple refcounted data type representing an immutable sequence of zero or
+ * more bytes from an unspecified origin.
+ *
+ * The purpose of a #GBytes is to keep the memory region that it holds
+ * alive for as long as anyone holds a reference to the bytes. When
+ * the last reference count is dropped, the memory is released. Multiple
+ * unrelated callers can use byte data in the #GBytes without coordinating
+ * their activities, resting assured that the byte data will not change or
+ * move while they hold a reference.
+ *
+ * A #GBytes can come from many different origins that may have
+ * different procedures for freeing the memory region. Examples are
+ * memory from g_malloc(), from memory slices, from a #GMappedFile or
+ * memory from other allocators.
+ *
+ * #GBytes work well as keys in #GHashTable. Use g_bytes_equal() and
+ * g_bytes_hash() as parameters to g_hash_table_new() or g_hash_table_new_full().
+ * #GBytes can also be used as keys in a #GTree by passing the g_bytes_compare()
+ * function to g_tree_new().
+ *
+ * The data pointed to by this bytes must not be modified. For a mutable
+ * array of bytes see #GByteArray. Use g_bytes_unref_to_array() to create a
+ * mutable array for a #GBytes sequence. To create an immutable #GBytes from
+ * a mutable #GByteArray, use the g_byte_array_free_to_bytes() function.
+ *
+ * Since: 2.32
+ **/
+
+/* Keep in sync with glib/tests/bytes.c */
+struct _GBytes
+{
+ gconstpointer data; /* may be NULL iff (size == 0) */
+ gsize size; /* may be 0 */
+ gatomicrefcount ref_count;
+ GDestroyNotify free_func;
+ gpointer user_data;
+};
+
+/**
+ * g_bytes_new:
+ * @data: (transfer none) (array length=size) (element-type guint8) (nullable):
+ * the data to be used for the bytes
+ * @size: the size of @data
+ *
+ * Creates a new #GBytes from @data.
+ *
+ * @data is copied. If @size is 0, @data may be %NULL.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new (gconstpointer data,
+ gsize size)
+{
+ g_return_val_if_fail (data != NULL || size == 0, NULL);
+
+ return g_bytes_new_take (g_memdup2 (data, size), size);
+}
+
+/**
+ * g_bytes_new_take:
+ * @data: (transfer full) (array length=size) (element-type guint8) (nullable):
+ * the data to be used for the bytes
+ * @size: the size of @data
+ *
+ * Creates a new #GBytes from @data.
+ *
+ * After this call, @data belongs to the bytes and may no longer be
+ * modified by the caller. g_free() will be called on @data when the
+ * bytes is no longer in use. Because of this @data must have been created by
+ * a call to g_malloc(), g_malloc0() or g_realloc() or by one of the many
+ * functions that wrap these calls (such as g_new(), g_strdup(), etc).
+ *
+ * For creating #GBytes with memory from other allocators, see
+ * g_bytes_new_with_free_func().
+ *
+ * @data may be %NULL if @size is 0.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_take (gpointer data,
+ gsize size)
+{
+ return g_bytes_new_with_free_func (data, size, g_free, data);
+}
+
+
+/**
+ * g_bytes_new_static: (skip)
+ * @data: (transfer full) (array length=size) (element-type guint8) (nullable):
+ * the data to be used for the bytes
+ * @size: the size of @data
+ *
+ * Creates a new #GBytes from static data.
+ *
+ * @data must be static (ie: never modified or freed). It may be %NULL if @size
+ * is 0.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_static (gconstpointer data,
+ gsize size)
+{
+ return g_bytes_new_with_free_func (data, size, NULL, NULL);
+}
+
+/**
+ * g_bytes_new_with_free_func: (skip)
+ * @data: (array length=size) (element-type guint8) (nullable):
+ * the data to be used for the bytes
+ * @size: the size of @data
+ * @free_func: the function to call to release the data
+ * @user_data: data to pass to @free_func
+ *
+ * Creates a #GBytes from @data.
+ *
+ * When the last reference is dropped, @free_func will be called with the
+ * @user_data argument.
+ *
+ * @data must not be modified after this call is made until @free_func has
+ * been called to indicate that the bytes is no longer in use.
+ *
+ * @data may be %NULL if @size is 0.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_with_free_func (gconstpointer data,
+ gsize size,
+ GDestroyNotify free_func,
+ gpointer user_data)
+{
+ GBytes *bytes;
+
+ g_return_val_if_fail (data != NULL || size == 0, NULL);
+
+ bytes = g_slice_new (GBytes);
+ bytes->data = data;
+ bytes->size = size;
+ bytes->free_func = free_func;
+ bytes->user_data = user_data;
+ g_atomic_ref_count_init (&bytes->ref_count);
+
+ return (GBytes *)bytes;
+}
+
+/**
+ * g_bytes_new_from_bytes:
+ * @bytes: a #GBytes
+ * @offset: offset which subsection starts at
+ * @length: length of subsection
+ *
+ * Creates a #GBytes which is a subsection of another #GBytes. The @offset +
+ * @length may not be longer than the size of @bytes.
+ *
+ * A reference to @bytes will be held by the newly created #GBytes until
+ * the byte data is no longer needed.
+ *
+ * Since 2.56, if @offset is 0 and @length matches the size of @bytes, then
+ * @bytes will be returned with the reference count incremented by 1. If @bytes
+ * is a slice of another #GBytes, then the resulting #GBytes will reference
+ * the same #GBytes instead of @bytes. This allows consumers to simplify the
+ * usage of #GBytes when asynchronously writing to streams.
+ *
+ * Returns: (transfer full): a new #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_new_from_bytes (GBytes *bytes,
+ gsize offset,
+ gsize length)
+{
+ gchar *base;
+
+ /* Note that length may be 0. */
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (offset <= bytes->size, NULL);
+ g_return_val_if_fail (offset + length <= bytes->size, NULL);
+
+ /* Avoid an extra GBytes if all bytes were requested */
+ if (offset == 0 && length == bytes->size)
+ return g_bytes_ref (bytes);
+
+ base = (gchar *)bytes->data + offset;
+
+ /* Avoid referencing intermediate GBytes. In practice, this should
+ * only loop once.
+ */
+ while (bytes->free_func == (gpointer)g_bytes_unref)
+ bytes = bytes->user_data;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (base >= (gchar *)bytes->data, NULL);
+ g_return_val_if_fail (base <= (gchar *)bytes->data + bytes->size, NULL);
+ g_return_val_if_fail (base + length <= (gchar *)bytes->data + bytes->size, NULL);
+
+ return g_bytes_new_with_free_func (base, length,
+ (GDestroyNotify)g_bytes_unref, g_bytes_ref (bytes));
+}
+
+/**
+ * g_bytes_get_data:
+ * @bytes: a #GBytes
+ * @size: (out) (optional): location to return size of byte data
+ *
+ * Get the byte data in the #GBytes. This data should not be modified.
+ *
+ * This function will always return the same pointer for a given #GBytes.
+ *
+ * %NULL may be returned if @size is 0. This is not guaranteed, as the #GBytes
+ * may represent an empty string with @data non-%NULL and @size as 0. %NULL will
+ * not be returned if @size is non-zero.
+ *
+ * Returns: (transfer none) (array length=size) (element-type guint8) (nullable):
+ * a pointer to the byte data, or %NULL
+ *
+ * Since: 2.32
+ */
+gconstpointer
+g_bytes_get_data (GBytes *bytes,
+ gsize *size)
+{
+ g_return_val_if_fail (bytes != NULL, NULL);
+ if (size)
+ *size = bytes->size;
+ return bytes->data;
+}
+
+/**
+ * g_bytes_get_size:
+ * @bytes: a #GBytes
+ *
+ * Get the size of the byte data in the #GBytes.
+ *
+ * This function will always return the same value for a given #GBytes.
+ *
+ * Returns: the size
+ *
+ * Since: 2.32
+ */
+gsize
+g_bytes_get_size (GBytes *bytes)
+{
+ g_return_val_if_fail (bytes != NULL, 0);
+ return bytes->size;
+}
+
+
+/**
+ * g_bytes_ref:
+ * @bytes: a #GBytes
+ *
+ * Increase the reference count on @bytes.
+ *
+ * Returns: the #GBytes
+ *
+ * Since: 2.32
+ */
+GBytes *
+g_bytes_ref (GBytes *bytes)
+{
+ g_return_val_if_fail (bytes != NULL, NULL);
+
+ g_atomic_ref_count_inc (&bytes->ref_count);
+
+ return bytes;
+}
+
+/**
+ * g_bytes_unref:
+ * @bytes: (nullable): a #GBytes
+ *
+ * Releases a reference on @bytes. This may result in the bytes being
+ * freed. If @bytes is %NULL, it will return immediately.
+ *
+ * Since: 2.32
+ */
+void
+g_bytes_unref (GBytes *bytes)
+{
+ if (bytes == NULL)
+ return;
+
+ if (g_atomic_ref_count_dec (&bytes->ref_count))
+ {
+ if (bytes->free_func != NULL)
+ bytes->free_func (bytes->user_data);
+ g_slice_free (GBytes, bytes);
+ }
+}
+
+/**
+ * g_bytes_equal:
+ * @bytes1: (type GLib.Bytes): a pointer to a #GBytes
+ * @bytes2: (type GLib.Bytes): a pointer to a #GBytes to compare with @bytes1
+ *
+ * Compares the two #GBytes values being pointed to and returns
+ * %TRUE if they are equal.
+ *
+ * This function can be passed to g_hash_table_new() as the @key_equal_func
+ * parameter, when using non-%NULL #GBytes pointers as keys in a #GHashTable.
+ *
+ * Returns: %TRUE if the two keys match.
+ *
+ * Since: 2.32
+ */
+gboolean
+g_bytes_equal (gconstpointer bytes1,
+ gconstpointer bytes2)
+{
+ const GBytes *b1 = bytes1;
+ const GBytes *b2 = bytes2;
+
+ g_return_val_if_fail (bytes1 != NULL, FALSE);
+ g_return_val_if_fail (bytes2 != NULL, FALSE);
+
+ return b1->size == b2->size &&
+ (b1->size == 0 || memcmp (b1->data, b2->data, b1->size) == 0);
+}
+
+/**
+ * g_bytes_hash:
+ * @bytes: (type GLib.Bytes): a pointer to a #GBytes key
+ *
+ * Creates an integer hash code for the byte data in the #GBytes.
+ *
+ * This function can be passed to g_hash_table_new() as the @key_hash_func
+ * parameter, when using non-%NULL #GBytes pointers as keys in a #GHashTable.
+ *
+ * Returns: a hash value corresponding to the key.
+ *
+ * Since: 2.32
+ */
+guint
+g_bytes_hash (gconstpointer bytes)
+{
+ const GBytes *a = bytes;
+ const signed char *p, *e;
+ guint32 h = 5381;
+
+ g_return_val_if_fail (bytes != NULL, 0);
+
+ for (p = (signed char *)a->data, e = (signed char *)a->data + a->size; p != e; p++)
+ h = (h << 5) + h + *p;
+
+ return h;
+}
+
+/**
+ * g_bytes_compare:
+ * @bytes1: (type GLib.Bytes): a pointer to a #GBytes
+ * @bytes2: (type GLib.Bytes): a pointer to a #GBytes to compare with @bytes1
+ *
+ * Compares the two #GBytes values.
+ *
+ * This function can be used to sort GBytes instances in lexicographical order.
+ *
+ * If @bytes1 and @bytes2 have different length but the shorter one is a
+ * prefix of the longer one then the shorter one is considered to be less than
+ * the longer one. Otherwise the first byte where both differ is used for
+ * comparison. If @bytes1 has a smaller value at that position it is
+ * considered less, otherwise greater than @bytes2.
+ *
+ * Returns: a negative value if @bytes1 is less than @bytes2, a positive value
+ * if @bytes1 is greater than @bytes2, and zero if @bytes1 is equal to
+ * @bytes2
+ *
+ *
+ * Since: 2.32
+ */
+gint
+g_bytes_compare (gconstpointer bytes1,
+ gconstpointer bytes2)
+{
+ const GBytes *b1 = bytes1;
+ const GBytes *b2 = bytes2;
+ gint ret;
+
+ g_return_val_if_fail (bytes1 != NULL, 0);
+ g_return_val_if_fail (bytes2 != NULL, 0);
+
+ ret = memcmp (b1->data, b2->data, MIN (b1->size, b2->size));
+ if (ret == 0 && b1->size != b2->size)
+ ret = b1->size < b2->size ? -1 : 1;
+ return ret;
+}
+
+static gpointer
+try_steal_and_unref (GBytes *bytes,
+ GDestroyNotify free_func,
+ gsize *size)
+{
+ gpointer result;
+
+ if (bytes->free_func != free_func || bytes->data == NULL ||
+ bytes->user_data != bytes->data)
+ return NULL;
+
+ /* Are we the only reference? */
+ if (g_atomic_ref_count_compare (&bytes->ref_count, 1))
+ {
+ *size = bytes->size;
+ result = (gpointer)bytes->data;
+ g_slice_free (GBytes, bytes);
+ return result;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * g_bytes_unref_to_data:
+ * @bytes: (transfer full): a #GBytes
+ * @size: (out): location to place the length of the returned data
+ *
+ * Unreferences the bytes, and returns a pointer the same byte data
+ * contents.
+ *
+ * As an optimization, the byte data is returned without copying if this was
+ * the last reference to bytes and bytes was created with g_bytes_new(),
+ * g_bytes_new_take() or g_byte_array_free_to_bytes(). In all other cases the
+ * data is copied.
+ *
+ * Returns: (transfer full) (array length=size) (element-type guint8)
+ * (not nullable): a pointer to the same byte data, which should be
+ * freed with g_free()
+ *
+ * Since: 2.32
+ */
+gpointer
+g_bytes_unref_to_data (GBytes *bytes,
+ gsize *size)
+{
+ gpointer result;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (size != NULL, NULL);
+
+ /*
+ * Optimal path: if this is was the last reference, then we can return
+ * the data from this GBytes without copying.
+ */
+
+ result = try_steal_and_unref (bytes, g_free, size);
+ if (result == NULL)
+ {
+ /*
+ * Copy: Non g_malloc (or compatible) allocator, or static memory,
+ * so we have to copy, and then unref.
+ */
+ result = g_memdup2 (bytes->data, bytes->size);
+ *size = bytes->size;
+ g_bytes_unref (bytes);
+ }
+
+ return result;
+}
+
+/**
+ * g_bytes_unref_to_array:
+ * @bytes: (transfer full): a #GBytes
+ *
+ * Unreferences the bytes, and returns a new mutable #GByteArray containing
+ * the same byte data.
+ *
+ * As an optimization, the byte data is transferred to the array without copying
+ * if this was the last reference to bytes and bytes was created with
+ * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all
+ * other cases the data is copied.
+ *
+ * Do not use it if @bytes contains more than %G_MAXUINT
+ * bytes. #GByteArray stores the length of its data in #guint, which
+ * may be shorter than #gsize, that @bytes is using.
+ *
+ * Returns: (transfer full): a new mutable #GByteArray containing the same byte data
+ *
+ * Since: 2.32
+ */
+GByteArray *
+g_bytes_unref_to_array (GBytes *bytes)
+{
+ gpointer data;
+ gsize size;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+
+ data = g_bytes_unref_to_data (bytes, &size);
+ return g_byte_array_new_take (data, size);
+}
+
+/**
+ * g_bytes_get_region:
+ * @bytes: a #GBytes
+ * @element_size: a non-zero element size
+ * @offset: an offset to the start of the region within the @bytes
+ * @n_elements: the number of elements in the region
+ *
+ * Gets a pointer to a region in @bytes.
+ *
+ * The region starts at @offset many bytes from the start of the data
+ * and contains @n_elements many elements of @element_size size.
+ *
+ * @n_elements may be zero, but @element_size must always be non-zero.
+ * Ideally, @element_size is a static constant (eg: sizeof a struct).
+ *
+ * This function does careful bounds checking (including checking for
+ * arithmetic overflows) and returns a non-%NULL pointer if the
+ * specified region lies entirely within the @bytes. If the region is
+ * in some way out of range, or if an overflow has occurred, then %NULL
+ * is returned.
+ *
+ * Note: it is possible to have a valid zero-size region. In this case,
+ * the returned pointer will be equal to the base pointer of the data of
+ * @bytes, plus @offset. This will be non-%NULL except for the case
+ * where @bytes itself was a zero-sized region. Since it is unlikely
+ * that you will be using this function to check for a zero-sized region
+ * in a zero-sized @bytes, %NULL effectively always means "error".
+ *
+ * Returns: (nullable): the requested region, or %NULL in case of an error
+ *
+ * Since: 2.70
+ */
+gconstpointer
+g_bytes_get_region (GBytes *bytes,
+ gsize element_size,
+ gsize offset,
+ gsize n_elements)
+{
+ gsize total_size;
+ gsize end_offset;
+
+ g_return_val_if_fail (element_size > 0, NULL);
+
+ /* No other assertion checks here. If something is wrong then we will
+ * simply crash (via NULL dereference or divide-by-zero).
+ */
+
+ if (!g_size_checked_mul (&total_size, element_size, n_elements))
+ return NULL;
+
+ if (!g_size_checked_add (&end_offset, offset, total_size))
+ return NULL;
+
+ /* We now have:
+ *
+ * 0 <= offset <= end_offset
+ *
+ * So we need only check that end_offset is within the range of the
+ * size of @bytes and we're good to go.
+ */
+
+ if (end_offset > bytes->size)
+ return NULL;
+
+ /* We now have:
+ *
+ * 0 <= offset <= end_offset <= bytes->size
+ */
+
+ return ((guchar *) bytes->data) + offset;
+}
\ No newline at end of file
diff --git a/glib/gbytes.h b/glib/gbytes.h
new file mode 100644
index 0000000000000000000000000000000000000000..37cad861d46b3ed64f44c87e8b8a4bca4943a3cd
--- /dev/null
+++ b/glib/gbytes.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2009, 2010 Codethink Limited
+ * Copyright © 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ *
+ * Author: Ryan Lortie
+ * Stef Walter
+ */
+
+#ifndef __G_BYTES_H__
+#define __G_BYTES_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_ALL
+GBytes * g_bytes_new (gconstpointer data,
+ gsize size);
+
+GLIB_AVAILABLE_IN_ALL
+GBytes * g_bytes_new_take (gpointer data,
+ gsize size);
+
+GLIB_AVAILABLE_IN_ALL
+GBytes * g_bytes_new_static (gconstpointer data,
+ gsize size);
+
+GLIB_AVAILABLE_IN_ALL
+GBytes * g_bytes_new_with_free_func (gconstpointer data,
+ gsize size,
+ GDestroyNotify free_func,
+ gpointer user_data);
+
+GLIB_AVAILABLE_IN_ALL
+GBytes * g_bytes_new_from_bytes (GBytes *bytes,
+ gsize offset,
+ gsize length);
+
+GLIB_AVAILABLE_IN_ALL
+gconstpointer g_bytes_get_data (GBytes *bytes,
+ gsize *size);
+
+GLIB_AVAILABLE_IN_ALL
+gsize g_bytes_get_size (GBytes *bytes);
+
+GLIB_AVAILABLE_IN_ALL
+GBytes * g_bytes_ref (GBytes *bytes);
+
+GLIB_AVAILABLE_IN_ALL
+void g_bytes_unref (GBytes *bytes);
+
+GLIB_AVAILABLE_IN_ALL
+gpointer g_bytes_unref_to_data (GBytes *bytes,
+ gsize *size);
+
+GLIB_AVAILABLE_IN_ALL
+GByteArray * g_bytes_unref_to_array (GBytes *bytes);
+
+GLIB_AVAILABLE_IN_ALL
+guint g_bytes_hash (gconstpointer bytes);
+
+GLIB_AVAILABLE_IN_ALL
+gboolean g_bytes_equal (gconstpointer bytes1,
+ gconstpointer bytes2);
+
+GLIB_AVAILABLE_IN_ALL
+gint g_bytes_compare (gconstpointer bytes1,
+ gconstpointer bytes2);
+
+GLIB_AVAILABLE_IN_2_70
+gconstpointer g_bytes_get_region (GBytes *bytes,
+ gsize element_size,
+ gsize offset,
+ gsize n_elements);
+
+
+G_END_DECLS
+
+#endif /* __G_BYTES_H__ */
diff --git a/glib/gcharset.c b/glib/gcharset.c
new file mode 100644
index 0000000000000000000000000000000000000000..09d3fa4cca10399c834d72bd0385a4b585759527
--- /dev/null
+++ b/glib/gcharset.c
@@ -0,0 +1,838 @@
+/* gcharset.c - Charset information
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include "config.h"
+
+#include "gcharset.h"
+#include "gcharsetprivate.h"
+
+#include "garray.h"
+#include "genviron.h"
+#include "ghash.h"
+#include "gmessages.h"
+#include "gstrfuncs.h"
+#include "gthread.h"
+#include "gthreadprivate.h"
+#ifdef G_OS_WIN32
+#include "gwin32.h"
+#endif
+
+#include "libcharset/libcharset.h"
+
+#include
+#include
+
+#if (HAVE_LANGINFO_TIME_CODESET || HAVE_LANGINFO_CODESET)
+#include
+#endif
+
+#include
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include
+#endif
+
+G_LOCK_DEFINE_STATIC (aliases);
+
+static GHashTable *
+get_alias_hash (void)
+{
+ static GHashTable *alias_hash = NULL;
+ const char *aliases;
+
+ G_LOCK (aliases);
+
+ if (!alias_hash)
+ {
+ alias_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ aliases = _g_locale_get_charset_aliases ();
+ while (*aliases != '\0')
+ {
+ const char *canonical;
+ const char *alias;
+ const char **alias_array;
+ int count = 0;
+
+ alias = aliases;
+ aliases += strlen (aliases) + 1;
+ canonical = aliases;
+ aliases += strlen (aliases) + 1;
+
+ alias_array = g_hash_table_lookup (alias_hash, canonical);
+ if (alias_array)
+ {
+ while (alias_array[count])
+ count++;
+ }
+
+ alias_array = g_renew (const char *, alias_array, count + 2);
+ alias_array[count] = alias;
+ alias_array[count + 1] = NULL;
+
+ g_hash_table_insert (alias_hash, (char *)canonical, alias_array);
+ }
+ }
+
+ G_UNLOCK (aliases);
+
+ return alias_hash;
+}
+
+/* As an abuse of the alias table, the following routines gets
+ * the charsets that are aliases for the canonical name.
+ */
+const char **
+_g_charset_get_aliases (const char *canonical_name)
+{
+ GHashTable *alias_hash = get_alias_hash ();
+
+ return g_hash_table_lookup (alias_hash, canonical_name);
+}
+
+static gboolean
+g_utf8_get_charset_internal (const char *raw_data,
+ const char **a)
+{
+ /* Allow CHARSET to override the charset of any locale category. Users should
+ * probably never be setting this — instead, just add the charset after a `.`
+ * in `LANGUAGE`/`LC_ALL`/`LC_*`/`LANG`. I can’t find any reference (in
+ * `git log`, code comments, or man pages) to this environment variable being
+ * standardised or documented or even used anywhere outside GLib. Perhaps it
+ * should eventually be removed. */
+ const char *charset = g_getenv ("CHARSET");
+
+ if (charset && *charset)
+ {
+ *a = charset;
+
+ if (charset && strstr (charset, "UTF-8"))
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ /* The libcharset code tries to be thread-safe without
+ * a lock, but has a memory leak and a missing memory
+ * barrier, so we lock for it
+ */
+ G_LOCK (aliases);
+ charset = _g_locale_charset_unalias (raw_data);
+ G_UNLOCK (aliases);
+
+ if (charset && *charset)
+ {
+ *a = charset;
+
+ if (charset && strstr (charset, "UTF-8"))
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ /* Assume this for compatibility at present. */
+ *a = "US-ASCII";
+
+ return FALSE;
+}
+
+typedef struct _GCharsetCache GCharsetCache;
+
+struct _GCharsetCache {
+ gboolean is_utf8;
+ gchar *raw;
+ gchar *charset;
+};
+
+static void
+charset_cache_free (gpointer data)
+{
+ GCharsetCache *cache = data;
+ g_free (cache->raw);
+ g_free (cache->charset);
+ g_free (cache);
+}
+
+/**
+ * g_get_charset:
+ * @charset: (out) (optional) (transfer none): return location for character set
+ * name, or %NULL.
+ *
+ * Obtains the character set for the [current locale][setlocale]; you
+ * might use this character set as an argument to g_convert(), to convert
+ * from the current locale's encoding to some other encoding. (Frequently
+ * g_locale_to_utf8() and g_locale_from_utf8() are nice shortcuts, though.)
+ *
+ * On Windows the character set returned by this function is the
+ * so-called system default ANSI code-page. That is the character set
+ * used by the "narrow" versions of C library and Win32 functions that
+ * handle file names. It might be different from the character set
+ * used by the C library's current locale.
+ *
+ * On Linux, the character set is found by consulting nl_langinfo() if
+ * available. If not, the environment variables `LC_ALL`, `LC_CTYPE`, `LANG`
+ * and `CHARSET` are queried in order.
+ *
+ * The return value is %TRUE if the locale's encoding is UTF-8, in that
+ * case you can perhaps avoid calling g_convert().
+ *
+ * The string returned in @charset is not allocated, and should not be
+ * freed.
+ *
+ * Returns: %TRUE if the returned charset is UTF-8
+ */
+gboolean
+g_get_charset (const char **charset)
+{
+ static GPrivate cache_private = G_PRIVATE_INIT (charset_cache_free);
+ GCharsetCache *cache = g_private_get (&cache_private);
+ const gchar *raw;
+
+ if (!cache)
+ cache = g_private_set_alloc0 (&cache_private, sizeof (GCharsetCache));
+
+ G_LOCK (aliases);
+ raw = _g_locale_charset_raw ();
+ G_UNLOCK (aliases);
+
+ if (cache->raw == NULL || strcmp (cache->raw, raw) != 0)
+ {
+ const gchar *new_charset;
+
+ g_free (cache->raw);
+ g_free (cache->charset);
+ cache->raw = g_strdup (raw);
+ cache->is_utf8 = g_utf8_get_charset_internal (raw, &new_charset);
+ cache->charset = g_strdup (new_charset);
+ }
+
+ if (charset)
+ *charset = cache->charset;
+
+ return cache->is_utf8;
+}
+
+/*
+ * Do the same as g_get_charset() but it temporarily set locale (LC_ALL to
+ * LC_TIME) to correctly check for charset about time conversion relatives.
+ *
+ * Returns: %TRUE if the returned charset is UTF-8
+ */
+gboolean
+_g_get_time_charset (const char **charset)
+{
+ static GPrivate cache_private = G_PRIVATE_INIT (charset_cache_free);
+ GCharsetCache *cache = g_private_get (&cache_private);
+ const gchar *raw;
+
+ if (!cache)
+ cache = g_private_set_alloc0 (&cache_private, sizeof (GCharsetCache));
+
+#ifdef HAVE_LANGINFO_TIME_CODESET
+ raw = nl_langinfo (_NL_TIME_CODESET);
+#else
+ G_LOCK (aliases);
+ raw = _g_locale_charset_raw ();
+ G_UNLOCK (aliases);
+#endif
+
+ if (cache->raw == NULL || strcmp (cache->raw, raw) != 0)
+ {
+ const gchar *new_charset;
+
+ g_free (cache->raw);
+ g_free (cache->charset);
+ cache->raw = g_strdup (raw);
+ cache->is_utf8 = g_utf8_get_charset_internal (raw, &new_charset);
+ cache->charset = g_strdup (new_charset);
+ }
+
+ if (charset)
+ *charset = cache->charset;
+
+ return cache->is_utf8;
+}
+/*
+ * Do the same as g_get_charset() but it temporarily set locale (LC_ALL to
+ * LC_CTYPE) to correctly check for charset about CTYPE conversion relatives.
+ *
+ * Returns: %TRUE if the returned charset is UTF-8
+ */
+gboolean
+_g_get_ctype_charset (const char **charset)
+{
+ static GPrivate cache_private = G_PRIVATE_INIT (charset_cache_free);
+ GCharsetCache *cache = g_private_get (&cache_private);
+ const gchar *raw;
+
+ if (!cache)
+ cache = g_private_set_alloc0 (&cache_private, sizeof (GCharsetCache));
+
+#ifdef HAVE_LANGINFO_CODESET
+ raw = nl_langinfo (CODESET);
+#else
+ G_LOCK (aliases);
+ raw = _g_locale_charset_raw ();
+ G_UNLOCK (aliases);
+#endif
+
+ if (cache->raw == NULL || strcmp (cache->raw, raw) != 0)
+ {
+ const gchar *new_charset;
+
+ g_free (cache->raw);
+ g_free (cache->charset);
+ cache->raw = g_strdup (raw);
+ cache->is_utf8 = g_utf8_get_charset_internal (raw, &new_charset);
+ cache->charset = g_strdup (new_charset);
+ }
+
+ if (charset)
+ *charset = cache->charset;
+
+ return cache->is_utf8;
+}
+
+/**
+ * g_get_codeset:
+ *
+ * Gets the character set for the current locale.
+ *
+ * Returns: a newly allocated string containing the name
+ * of the character set. This string must be freed with g_free().
+ */
+gchar *
+g_get_codeset (void)
+{
+ const gchar *charset;
+
+ g_get_charset (&charset);
+
+ return g_strdup (charset);
+}
+
+/**
+ * g_get_console_charset:
+ * @charset: (out) (optional) (transfer none): return location for character set
+ * name, or %NULL.
+ *
+ * Obtains the character set used by the console attached to the process,
+ * which is suitable for printing output to the terminal.
+ *
+ * Usually this matches the result returned by g_get_charset(), but in
+ * environments where the locale's character set does not match the encoding
+ * of the console this function tries to guess a more suitable value instead.
+ *
+ * On Windows the character set returned by this function is the
+ * output code page used by the console associated with the calling process.
+ * If the codepage can't be determined (for example because there is no
+ * console attached) UTF-8 is assumed.
+ *
+ * The return value is %TRUE if the locale's encoding is UTF-8, in that
+ * case you can perhaps avoid calling g_convert().
+ *
+ * The string returned in @charset is not allocated, and should not be
+ * freed.
+ *
+ * Returns: %TRUE if the returned charset is UTF-8
+ *
+ * Since: 2.62
+ */
+gboolean
+g_get_console_charset (const char **charset)
+{
+#ifdef G_OS_WIN32
+ static GPrivate cache_private = G_PRIVATE_INIT (charset_cache_free);
+ GCharsetCache *cache = g_private_get (&cache_private);
+ const gchar *locale;
+ unsigned int cp;
+ char buf[2 + 20 + 1]; /* "CP" + G_MAXUINT64 (to be safe) in decimal form (20 bytes) + "\0" */
+ const gchar *raw = NULL;
+
+ if (!cache)
+ cache = g_private_set_alloc0 (&cache_private, sizeof (GCharsetCache));
+
+ /* first try to query $LANG (works for Cygwin/MSYS/MSYS2 and others using mintty) */
+ locale = g_getenv ("LANG");
+ if (locale != NULL && locale[0] != '\0')
+ {
+ /* If the locale name contains an encoding after the dot, return it. */
+ const char *dot = strchr (locale, '.');
+
+ if (dot != NULL)
+ {
+ const char *modifier;
+
+ dot++;
+ /* Look for the possible @... trailer and remove it, if any. */
+ modifier = strchr (dot, '@');
+ if (modifier == NULL)
+ raw = dot;
+ else if ((gsize) (modifier - dot) < sizeof (buf))
+ {
+ memcpy (buf, dot, modifier - dot);
+ buf[modifier - dot] = '\0';
+ raw = buf;
+ }
+ }
+ }
+ /* next try querying console codepage using native win32 API */
+ if (raw == NULL)
+ {
+ cp = GetConsoleOutputCP ();
+ if (cp)
+ {
+ sprintf (buf, "CP%u", cp);
+ raw = buf;
+ }
+ else if (GetLastError () != ERROR_INVALID_HANDLE)
+ {
+ gchar *emsg = g_win32_error_message (GetLastError ());
+ g_warning ("Failed to determine console output code page: %s. "
+ "Falling back to UTF-8", emsg);
+ g_free (emsg);
+ }
+ }
+ /* fall-back to UTF-8 if the rest failed (it's a universal default) */
+ if (raw == NULL)
+ raw = "UTF-8";
+
+ if (cache->raw == NULL || strcmp (cache->raw, raw) != 0)
+ {
+ const gchar *new_charset;
+
+ g_free (cache->raw);
+ g_free (cache->charset);
+ cache->raw = g_strdup (raw);
+ cache->is_utf8 = g_utf8_get_charset_internal (raw, &new_charset);
+ cache->charset = g_strdup (new_charset);
+ }
+
+ if (charset)
+ *charset = cache->charset;
+
+ return cache->is_utf8;
+#else
+ /* assume the locale settings match the console encoding on non-Windows OSs */
+ return g_get_charset (charset);
+#endif
+}
+
+#ifndef G_OS_WIN32
+
+/* read an alias file for the locales */
+static void
+read_aliases (const gchar *file,
+ GHashTable *alias_table)
+{
+ FILE *fp;
+ char buf[256];
+
+ fp = fopen (file,"r");
+ if (!fp)
+ return;
+ while (fgets (buf, 256, fp))
+ {
+ char *p, *q;
+
+ g_strstrip (buf);
+
+ /* Line is a comment */
+ if ((buf[0] == '#') || (buf[0] == '\0'))
+ continue;
+
+ /* Reads first column */
+ for (p = buf, q = NULL; *p; p++) {
+ if ((*p == '\t') || (*p == ' ') || (*p == ':')) {
+ *p = '\0';
+ q = p+1;
+ while ((*q == '\t') || (*q == ' ')) {
+ q++;
+ }
+ break;
+ }
+ }
+ /* The line only had one column */
+ if (!q || *q == '\0')
+ continue;
+
+ /* Read second column */
+ for (p = q; *p; p++) {
+ if ((*p == '\t') || (*p == ' ')) {
+ *p = '\0';
+ break;
+ }
+ }
+
+ /* Add to alias table if necessary */
+ if (!g_hash_table_lookup (alias_table, buf)) {
+ g_hash_table_insert (alias_table, g_strdup (buf), g_strdup (q));
+ }
+ }
+ fclose (fp);
+}
+
+#endif
+
+static char *
+unalias_lang (char *lang)
+{
+#ifndef G_OS_WIN32
+ static GHashTable *alias_table = NULL;
+ char *p;
+ int i;
+
+ if (g_once_init_enter (&alias_table))
+ {
+ GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
+ read_aliases ("/usr/share/locale/locale.alias", table);
+ g_once_init_leave (&alias_table, table);
+ }
+
+ i = 0;
+ while ((p = g_hash_table_lookup (alias_table, lang)) && (strcmp (p, lang) != 0))
+ {
+ lang = p;
+ if (i++ == 30)
+ {
+ static gboolean said_before = FALSE;
+ if (!said_before)
+ g_warning ("Too many alias levels for a locale, "
+ "may indicate a loop");
+ said_before = TRUE;
+ return lang;
+ }
+ }
+#endif
+ return lang;
+}
+
+/* Mask for components of locale spec. The ordering here is from
+ * least significant to most significant
+ */
+enum
+{
+ COMPONENT_CODESET = 1 << 0,
+ COMPONENT_TERRITORY = 1 << 1,
+ COMPONENT_MODIFIER = 1 << 2
+};
+
+/* Break an X/Open style locale specification into components
+ */
+static guint
+explode_locale (const gchar *locale,
+ gchar **language,
+ gchar **territory,
+ gchar **codeset,
+ gchar **modifier)
+{
+ const gchar *uscore_pos;
+ const gchar *at_pos;
+ const gchar *dot_pos;
+
+ guint mask = 0;
+
+ uscore_pos = strchr (locale, '_');
+ dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
+ at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
+
+ if (at_pos)
+ {
+ mask |= COMPONENT_MODIFIER;
+ *modifier = g_strdup (at_pos);
+ }
+ else
+ at_pos = locale + strlen (locale);
+
+ if (dot_pos)
+ {
+ mask |= COMPONENT_CODESET;
+ *codeset = g_strndup (dot_pos, at_pos - dot_pos);
+ }
+ else
+ dot_pos = at_pos;
+
+ if (uscore_pos)
+ {
+ mask |= COMPONENT_TERRITORY;
+ *territory = g_strndup (uscore_pos, dot_pos - uscore_pos);
+ }
+ else
+ uscore_pos = dot_pos;
+
+ *language = g_strndup (locale, uscore_pos - locale);
+
+ return mask;
+}
+
+/*
+ * Compute all interesting variants for a given locale name -
+ * by stripping off different components of the value.
+ *
+ * For simplicity, we assume that the locale is in
+ * X/Open format: language[_territory][.codeset][@modifier]
+ *
+ * TODO: Extend this to handle the CEN format (see the GNUlibc docs)
+ * as well. We could just copy the code from glibc wholesale
+ * but it is big, ugly, and complicated, so I'm reluctant
+ * to do so when this should handle 99% of the time...
+ */
+static void
+append_locale_variants (GPtrArray *array,
+ const gchar *locale)
+{
+ gchar *language = NULL;
+ gchar *territory = NULL;
+ gchar *codeset = NULL;
+ gchar *modifier = NULL;
+
+ guint mask;
+ guint i, j;
+
+ g_return_if_fail (locale != NULL);
+
+ mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
+
+ /* Iterate through all possible combinations, from least attractive
+ * to most attractive.
+ */
+ for (j = 0; j <= mask; ++j)
+ {
+ i = mask - j;
+
+ if ((i & ~mask) == 0)
+ {
+ gchar *val = g_strconcat (language,
+ (i & COMPONENT_TERRITORY) ? territory : "",
+ (i & COMPONENT_CODESET) ? codeset : "",
+ (i & COMPONENT_MODIFIER) ? modifier : "",
+ NULL);
+ g_ptr_array_add (array, val);
+ }
+ }
+
+ g_free (language);
+ if (mask & COMPONENT_CODESET)
+ g_free (codeset);
+ if (mask & COMPONENT_TERRITORY)
+ g_free (territory);
+ if (mask & COMPONENT_MODIFIER)
+ g_free (modifier);
+}
+
+/**
+ * g_get_locale_variants:
+ * @locale: a locale identifier
+ *
+ * Returns a list of derived variants of @locale, which can be used to
+ * e.g. construct locale-dependent filenames or search paths. The returned
+ * list is sorted from most desirable to least desirable.
+ * This function handles territory, charset and extra locale modifiers. See
+ * [`setlocale(3)`](man:setlocale) for information about locales and their format.
+ *
+ * @locale itself is guaranteed to be returned in the output.
+ *
+ * For example, if @locale is `fr_BE`, then the returned list
+ * is `fr_BE`, `fr`. If @locale is `en_GB.UTF-8@euro`, then the returned list
+ * is `en_GB.UTF-8@euro`, `en_GB.UTF-8`, `en_GB@euro`, `en_GB`, `en.UTF-8@euro`,
+ * `en.UTF-8`, `en@euro`, `en`.
+ *
+ * If you need the list of variants for the current locale,
+ * use g_get_language_names().
+ *
+ * Returns: (transfer full) (array zero-terminated=1) (element-type utf8): a newly
+ * allocated array of newly allocated strings with the locale variants. Free with
+ * g_strfreev().
+ *
+ * Since: 2.28
+ */
+gchar **
+g_get_locale_variants (const gchar *locale)
+{
+ GPtrArray *array;
+
+ g_return_val_if_fail (locale != NULL, NULL);
+
+ array = g_ptr_array_sized_new (8);
+ append_locale_variants (array, locale);
+ g_ptr_array_add (array, NULL);
+
+ return (gchar **) g_ptr_array_free (array, FALSE);
+}
+
+/* The following is (partly) taken from the gettext package.
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */
+
+static const gchar *
+guess_category_value (const gchar *category_name)
+{
+ const gchar *retval;
+
+ /* The highest priority value is the 'LANGUAGE' environment
+ variable. This is a GNU extension. */
+ retval = g_getenv ("LANGUAGE");
+ if ((retval != NULL) && (retval[0] != '\0'))
+ return retval;
+
+ /* 'LANGUAGE' is not set. So we have to proceed with the POSIX
+ methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'. On some
+ systems this can be done by the 'setlocale' function itself. */
+
+ /* Setting of LC_ALL overwrites all other. */
+ retval = g_getenv ("LC_ALL");
+ if ((retval != NULL) && (retval[0] != '\0'))
+ return retval;
+
+ /* Next comes the name of the desired category. */
+ retval = g_getenv (category_name);
+ if ((retval != NULL) && (retval[0] != '\0'))
+ return retval;
+
+ /* Last possibility is the LANG environment variable. */
+ retval = g_getenv ("LANG");
+ if ((retval != NULL) && (retval[0] != '\0'))
+ return retval;
+
+#ifdef G_PLATFORM_WIN32
+ /* g_win32_getlocale() first checks for LC_ALL, LC_MESSAGES and
+ * LANG, which we already did above. Oh well. The main point of
+ * calling g_win32_getlocale() is to get the thread's locale as used
+ * by Windows and the Microsoft C runtime (in the "English_United
+ * States" format) translated into the Unixish format.
+ */
+ {
+ char *locale = g_win32_getlocale ();
+ retval = g_intern_string (locale);
+ g_free (locale);
+ return retval;
+ }
+#endif
+
+ return NULL;
+}
+
+typedef struct _GLanguageNamesCache GLanguageNamesCache;
+
+struct _GLanguageNamesCache {
+ gchar *languages;
+ gchar **language_names;
+};
+
+static void
+language_names_cache_free (gpointer data)
+{
+ GLanguageNamesCache *cache = data;
+ g_free (cache->languages);
+ g_strfreev (cache->language_names);
+ g_free (cache);
+}
+
+/**
+ * g_get_language_names:
+ *
+ * Computes a list of applicable locale names, which can be used to
+ * e.g. construct locale-dependent filenames or search paths. The returned
+ * list is sorted from most desirable to least desirable and always contains
+ * the default locale "C".
+ *
+ * For example, if LANGUAGE=de:en_US, then the returned list is
+ * "de", "en_US", "en", "C".
+ *
+ * This function consults the environment variables `LANGUAGE`, `LC_ALL`,
+ * `LC_MESSAGES` and `LANG` to find the list of locales specified by the
+ * user.
+ *
+ * Returns: (array zero-terminated=1) (transfer none): a %NULL-terminated array of strings owned by GLib
+ * that must not be modified or freed.
+ *
+ * Since: 2.6
+ */
+const gchar * const *
+g_get_language_names (void)
+{
+ return g_get_language_names_with_category ("LC_MESSAGES");
+}
+
+/**
+ * g_get_language_names_with_category:
+ * @category_name: a locale category name
+ *
+ * Computes a list of applicable locale names with a locale category name,
+ * which can be used to construct the fallback locale-dependent filenames
+ * or search paths. The returned list is sorted from most desirable to
+ * least desirable and always contains the default locale "C".
+ *
+ * This function consults the environment variables `LANGUAGE`, `LC_ALL`,
+ * @category_name, and `LANG` to find the list of locales specified by the
+ * user.
+ *
+ * g_get_language_names() returns g_get_language_names_with_category("LC_MESSAGES").
+ *
+ * Returns: (array zero-terminated=1) (transfer none): a %NULL-terminated array of strings owned by
+ * the thread g_get_language_names_with_category was called from.
+ * It must not be modified or freed. It must be copied if planned to be used in another thread.
+ *
+ * Since: 2.58
+ */
+const gchar * const *
+g_get_language_names_with_category (const gchar *category_name)
+{
+ static GPrivate cache_private = G_PRIVATE_INIT ((void (*)(gpointer)) g_hash_table_unref);
+ GHashTable *cache = g_private_get (&cache_private);
+ const gchar *languages;
+ GLanguageNamesCache *name_cache;
+
+ g_return_val_if_fail (category_name != NULL, NULL);
+
+ if (!cache)
+ {
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, language_names_cache_free);
+ g_private_set (&cache_private, cache);
+ }
+
+ languages = guess_category_value (category_name);
+ if (!languages)
+ languages = "C";
+
+ name_cache = (GLanguageNamesCache *) g_hash_table_lookup (cache, category_name);
+ if (!(name_cache && name_cache->languages &&
+ strcmp (name_cache->languages, languages) == 0))
+ {
+ GPtrArray *array;
+ gchar **alist, **a;
+
+ g_hash_table_remove (cache, category_name);
+
+ array = g_ptr_array_sized_new (8);
+
+ alist = g_strsplit (languages, ":", 0);
+ for (a = alist; *a; a++)
+ append_locale_variants (array, unalias_lang (*a));
+ g_strfreev (alist);
+ g_ptr_array_add (array, g_strdup ("C"));
+ g_ptr_array_add (array, NULL);
+
+ name_cache = g_new0 (GLanguageNamesCache, 1);
+ name_cache->languages = g_strdup (languages);
+ name_cache->language_names = (gchar **) g_ptr_array_free (array, FALSE);
+ g_hash_table_insert (cache, g_strdup (category_name), name_cache);
+ }
+
+ return (const gchar * const *) name_cache->language_names;
+}
diff --git a/glib/gcharset.h b/glib/gcharset.h
new file mode 100644
index 0000000000000000000000000000000000000000..82020f6047b890dc5d3a3ef84b4d11184e90cb90
--- /dev/null
+++ b/glib/gcharset.h
@@ -0,0 +1,47 @@
+/* gcharset.h - Charset functions
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#ifndef __G_CHARSET_H__
+#define __G_CHARSET_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_ALL
+gboolean g_get_charset (const char **charset);
+GLIB_AVAILABLE_IN_ALL
+gchar * g_get_codeset (void);
+GLIB_AVAILABLE_IN_2_62
+gboolean g_get_console_charset (const char **charset);
+
+GLIB_AVAILABLE_IN_ALL
+const gchar * const * g_get_language_names (void);
+GLIB_AVAILABLE_IN_2_58
+const gchar * const * g_get_language_names_with_category
+ (const gchar *category_name);
+GLIB_AVAILABLE_IN_ALL
+gchar ** g_get_locale_variants (const gchar *locale);
+
+G_END_DECLS
+
+#endif /* __G_CHARSET_H__ */
diff --git a/glib/gcharsetprivate.h b/glib/gcharsetprivate.h
new file mode 100644
index 0000000000000000000000000000000000000000..9b1def278e9254416cce54bddb5d481c5e197982
--- /dev/null
+++ b/glib/gcharsetprivate.h
@@ -0,0 +1,34 @@
+/* gunicodeprivate.h
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#ifndef __G_CHARSET_PRIVATE_H__
+#define __G_CHARSET_PRIVATE_H__
+
+#include "gcharset.h"
+
+G_BEGIN_DECLS
+
+const char ** _g_charset_get_aliases (const char *canonical_name);
+
+gboolean _g_get_time_charset (const char **charset);
+
+gboolean _g_get_ctype_charset (const char **charset);
+
+G_END_DECLS
+
+#endif
diff --git a/glib/gchecksum.c b/glib/gchecksum.c
new file mode 100644
index 0000000000000000000000000000000000000000..29b479bc66ba90414fc3df82ab43f4b841d3bba0
--- /dev/null
+++ b/glib/gchecksum.c
@@ -0,0 +1,1864 @@
+/* gchecksum.h - data hashing functions
+ *
+ * Copyright (C) 2007 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#include "config.h"
+
+#include
+
+#include "gchecksum.h"
+
+#include "gslice.h"
+#include "gmem.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gtypes.h"
+#include "glibintl.h"
+
+
+/**
+ * SECTION:checksum
+ * @title: Data Checksums
+ * @short_description: computes the checksum for data
+ *
+ * GLib provides a generic API for computing checksums (or "digests")
+ * for a sequence of arbitrary bytes, using various hashing algorithms
+ * like MD5, SHA-1 and SHA-256. Checksums are commonly used in various
+ * environments and specifications.
+ *
+ * GLib supports incremental checksums using the GChecksum data
+ * structure, by calling g_checksum_update() as long as there's data
+ * available and then using g_checksum_get_string() or
+ * g_checksum_get_digest() to compute the checksum and return it either
+ * as a string in hexadecimal form, or as a raw sequence of bytes. To
+ * compute the checksum for binary blobs and NUL-terminated strings in
+ * one go, use the convenience functions g_compute_checksum_for_data()
+ * and g_compute_checksum_for_string(), respectively.
+ *
+ * Support for checksums has been added in GLib 2.16
+ **/
+
+#define IS_VALID_TYPE(type) ((type) >= G_CHECKSUM_MD5 && (type) <= G_CHECKSUM_SHA384)
+
+/* The fact that these are lower case characters is part of the ABI */
+static const gchar hex_digits[] = "0123456789abcdef";
+
+#define MD5_DATASIZE 64
+#define MD5_DIGEST_LEN 16
+
+typedef struct
+{
+ guint32 buf[4];
+ guint32 bits[2];
+
+ union {
+ guchar data[MD5_DATASIZE];
+ guint32 data32[MD5_DATASIZE / 4];
+ } u;
+
+ guchar digest[MD5_DIGEST_LEN];
+} Md5sum;
+
+#define SHA1_DATASIZE 64
+#define SHA1_DIGEST_LEN 20
+
+typedef struct
+{
+ guint32 buf[5];
+ guint32 bits[2];
+
+ /* we pack 64 unsigned chars into 16 32-bit unsigned integers */
+ guint32 data[16];
+
+ guchar digest[SHA1_DIGEST_LEN];
+} Sha1sum;
+
+#define SHA256_DATASIZE 64
+#define SHA256_DIGEST_LEN 32
+
+typedef struct
+{
+ guint32 buf[8];
+ guint32 bits[2];
+
+ guint8 data[SHA256_DATASIZE];
+
+ guchar digest[SHA256_DIGEST_LEN];
+} Sha256sum;
+
+/* SHA2 is common thing for SHA-384, SHA-512, SHA-512/224 and SHA-512/256 */
+#define SHA2_BLOCK_LEN 128 /* 1024 bits message block */
+#define SHA384_DIGEST_LEN 48
+#define SHA512_DIGEST_LEN 64
+
+typedef struct
+{
+ guint64 H[8];
+
+ guint8 block[SHA2_BLOCK_LEN];
+ guint8 block_len;
+
+ guint64 data_len[2];
+
+ guchar digest[SHA512_DIGEST_LEN];
+} Sha512sum;
+
+struct _GChecksum
+{
+ GChecksumType type;
+
+ gchar *digest_str;
+
+ union {
+ Md5sum md5;
+ Sha1sum sha1;
+ Sha256sum sha256;
+ Sha512sum sha512;
+ } sum;
+};
+
+/* we need different byte swapping functions because MD5 expects buffers
+ * to be little-endian, while SHA1 and SHA256 expect them in big-endian
+ * form.
+ */
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define md5_byte_reverse(buffer,length)
+#else
+/* assume that the passed buffer is integer aligned */
+static inline void
+md5_byte_reverse (guchar *buffer,
+ gulong length)
+{
+ guint32 bit;
+
+ do
+ {
+ bit = (guint32) ((unsigned) buffer[3] << 8 | buffer[2]) << 16 |
+ ((unsigned) buffer[1] << 8 | buffer[0]);
+ * (guint32 *) buffer = bit;
+ buffer += 4;
+ }
+ while (--length);
+}
+#endif /* G_BYTE_ORDER == G_LITTLE_ENDIAN */
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define sha_byte_reverse(buffer,length)
+#else
+static inline void
+sha_byte_reverse (guint32 *buffer,
+ gint length)
+{
+ length /= sizeof (guint32);
+ while (length--)
+ {
+ *buffer = GUINT32_SWAP_LE_BE (*buffer);
+ ++buffer;
+ }
+}
+#endif /* G_BYTE_ORDER == G_BIG_ENDIAN */
+
+static gchar *
+digest_to_string (guint8 *digest,
+ gsize digest_len)
+{
+ gsize i, len = digest_len * 2;
+ gchar *retval;
+
+ retval = g_new (gchar, len + 1);
+
+ for (i = 0; i < digest_len; i++)
+ {
+ guint8 byte = digest[i];
+
+ retval[2 * i] = hex_digits[byte >> 4];
+ retval[2 * i + 1] = hex_digits[byte & 0xf];
+ }
+
+ retval[len] = 0;
+
+ return retval;
+}
+
+/*
+ * MD5 Checksum
+ */
+
+/* This MD5 digest computation is based on the equivalent code
+ * written by Colin Plumb. It came with this notice:
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ */
+
+static void
+md5_sum_init (Md5sum *md5)
+{
+ /* arbitrary constants */
+ md5->buf[0] = 0x67452301;
+ md5->buf[1] = 0xefcdab89;
+ md5->buf[2] = 0x98badcfe;
+ md5->buf[3] = 0x10325476;
+
+ md5->bits[0] = md5->bits[1] = 0;
+}
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. md5_sum_update()
+ * blocks the data and converts bytes into longwords for this routine.
+ */
+static void
+md5_transform (guint32 buf[4],
+ guint32 const in[16])
+{
+ guint32 a, b, c, d;
+
+/* The four core functions - F1 is optimized somewhat */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1 (z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define md5_step(f, w, x, y, z, data, s) \
+ ( w += f (x, y, z) + data, w = w << s | w >> (32 - s), w += x )
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ md5_step (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ md5_step (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ md5_step (F1, c, d, a, b, in[2] + 0x242070db, 17);
+ md5_step (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ md5_step (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ md5_step (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ md5_step (F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ md5_step (F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ md5_step (F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ md5_step (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ md5_step (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ md5_step (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ md5_step (F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ md5_step (F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ md5_step (F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ md5_step (F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ md5_step (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ md5_step (F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ md5_step (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ md5_step (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ md5_step (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ md5_step (F2, d, a, b, c, in[10] + 0x02441453, 9);
+ md5_step (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ md5_step (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ md5_step (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ md5_step (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ md5_step (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ md5_step (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ md5_step (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ md5_step (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ md5_step (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ md5_step (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ md5_step (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ md5_step (F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ md5_step (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ md5_step (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ md5_step (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ md5_step (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ md5_step (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ md5_step (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ md5_step (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ md5_step (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ md5_step (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ md5_step (F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ md5_step (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ md5_step (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ md5_step (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ md5_step (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ md5_step (F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ md5_step (F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ md5_step (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ md5_step (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ md5_step (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ md5_step (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ md5_step (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ md5_step (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ md5_step (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ md5_step (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ md5_step (F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ md5_step (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ md5_step (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ md5_step (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ md5_step (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ md5_step (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef md5_step
+}
+
+static void
+md5_sum_update (Md5sum *md5,
+ const guchar *data,
+ gsize length)
+{
+ guint32 bit;
+
+ bit = md5->bits[0];
+ md5->bits[0] = bit + ((guint32) length << 3);
+
+ /* carry from low to high */
+ if (md5->bits[0] < bit)
+ md5->bits[1] += 1;
+
+ md5->bits[1] += length >> 29;
+
+ /* bytes already in Md5sum->u.data */
+ bit = (bit >> 3) & 0x3f;
+
+ /* handle any leading odd-sized chunks */
+ if (bit)
+ {
+ guchar *p = md5->u.data + bit;
+
+ bit = MD5_DATASIZE - bit;
+ if (length < bit)
+ {
+ memcpy (p, data, length);
+ return;
+ }
+
+ memcpy (p, data, bit);
+
+ md5_byte_reverse (md5->u.data, 16);
+ md5_transform (md5->buf, md5->u.data32);
+
+ data += bit;
+ length -= bit;
+ }
+
+ /* process data in 64-byte chunks */
+ while (length >= MD5_DATASIZE)
+ {
+ memcpy (md5->u.data, data, MD5_DATASIZE);
+
+ md5_byte_reverse (md5->u.data, 16);
+ md5_transform (md5->buf, md5->u.data32);
+
+ data += MD5_DATASIZE;
+ length -= MD5_DATASIZE;
+ }
+
+ /* handle any remaining bytes of data */
+ memcpy (md5->u.data, data, length);
+}
+
+/* closes a checksum */
+static void
+md5_sum_close (Md5sum *md5)
+{
+ guint count;
+ guchar *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (md5->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80.
+ * This is safe since there is always at least one byte free
+ */
+ p = md5->u.data + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = MD5_DATASIZE - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8)
+ {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (p, 0, count);
+
+ md5_byte_reverse (md5->u.data, 16);
+ md5_transform (md5->buf, md5->u.data32);
+
+ /* Now fill the next block with 56 bytes */
+ memset (md5->u.data, 0, MD5_DATASIZE - 8);
+ }
+ else
+ {
+ /* Pad block to 56 bytes */
+ memset (p, 0, count - 8);
+ }
+
+ md5_byte_reverse (md5->u.data, 14);
+
+ /* Append length in bits and transform */
+ md5->u.data32[14] = md5->bits[0];
+ md5->u.data32[15] = md5->bits[1];
+
+ md5_transform (md5->buf, md5->u.data32);
+ md5_byte_reverse ((guchar *) md5->buf, 4);
+
+ memcpy (md5->digest, md5->buf, 16);
+
+ /* Reset buffers in case they contain sensitive data */
+ memset (md5->buf, 0, sizeof (md5->buf));
+ memset (md5->u.data, 0, sizeof (md5->u.data));
+}
+
+static gchar *
+md5_sum_to_string (Md5sum *md5)
+{
+ return digest_to_string (md5->digest, MD5_DIGEST_LEN);
+}
+
+static void
+md5_sum_digest (Md5sum *md5,
+ guint8 *digest)
+{
+ gint i;
+
+ for (i = 0; i < MD5_DIGEST_LEN; i++)
+ digest[i] = md5->digest[i];
+}
+
+/*
+ * SHA-1 Checksum
+ */
+
+/* The following implementation comes from D-Bus dbus-sha.c. I've changed
+ * it to use GLib types and to work more like the MD5 implementation above.
+ * I left the comments to have a history of this code.
+ * -- Emmanuele Bassi, ebassi@gnome.org
+ */
+
+/* The following comments have the history of where this code
+ * comes from. I actually copied it from GNet in GNOME CVS.
+ * - hp@redhat.com
+ */
+
+/*
+ * sha.h : Implementation of the Secure Hash Algorithm
+ *
+ * Part of the Python Cryptography Toolkit, version 1.0.0
+ *
+ * Copyright (C) 1995, A.M. Kuchling
+ *
+ * Distribute and use freely; there are no restrictions on further
+ * dissemination and usage except those imposed by the laws of your
+ * country of residence.
+ *
+ */
+
+/* SHA: NIST's Secure Hash Algorithm */
+
+/* Based on SHA code originally posted to sci.crypt by Peter Gutmann
+ in message <30ajo5$oe8@ccu2.auckland.ac.nz>.
+ Modified to test for endianness on creation of SHA objects by AMK.
+ Also, the original specification of SHA was found to have a weakness
+ by NSA/NIST. This code implements the fixed version of SHA.
+*/
+
+/* Here's the first paragraph of Peter Gutmann's posting:
+
+The following is my SHA (FIPS 180) code updated to allow use of the "fixed"
+SHA, thanks to Jim Gillogly and an anonymous contributor for the information on
+what's changed in the new version. The fix is a simple change which involves
+adding a single rotate in the initial expansion function. It is unknown
+whether this is an optimal solution to the problem which was discovered in the
+SHA or whether it's simply a bandaid which fixes the problem with a minimum of
+effort (for example the reengineering of a great many Capstone chips).
+*/
+
+static void
+sha1_sum_init (Sha1sum *sha1)
+{
+ /* initialize constants */
+ sha1->buf[0] = 0x67452301L;
+ sha1->buf[1] = 0xEFCDAB89L;
+ sha1->buf[2] = 0x98BADCFEL;
+ sha1->buf[3] = 0x10325476L;
+ sha1->buf[4] = 0xC3D2E1F0L;
+
+ /* initialize bits */
+ sha1->bits[0] = sha1->bits[1] = 0;
+}
+
+/* The SHA f()-functions. */
+
+#define f1(x,y,z) (z ^ (x & (y ^ z))) /* Rounds 0-19 */
+#define f2(x,y,z) (x ^ y ^ z) /* Rounds 20-39 */
+#define f3(x,y,z) (( x & y) | (z & (x | y))) /* Rounds 40-59 */
+#define f4(x,y,z) (x ^ y ^ z) /* Rounds 60-79 */
+
+/* The SHA Mysterious Constants */
+#define K1 0x5A827999L /* Rounds 0-19 */
+#define K2 0x6ED9EBA1L /* Rounds 20-39 */
+#define K3 0x8F1BBCDCL /* Rounds 40-59 */
+#define K4 0xCA62C1D6L /* Rounds 60-79 */
+
+/* 32-bit rotate left - kludged with shifts */
+#define ROTL(n,X) (((X) << n ) | ((X) >> (32 - n)))
+
+/* The initial expanding function. The hash function is defined over an
+ 80-word expanded input array W, where the first 16 are copies of the input
+ data, and the remaining 64 are defined by
+
+ W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]
+
+ This implementation generates these values on the fly in a circular
+ buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
+ optimization.
+
+ The updated SHA changes the expanding function by adding a rotate of 1
+ bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
+ for this information */
+
+#define expand(W,i) (W[ i & 15 ] = ROTL (1, (W[ i & 15] ^ \
+ W[(i - 14) & 15] ^ \
+ W[(i - 8) & 15] ^ \
+ W[(i - 3) & 15])))
+
+
+/* The prototype SHA sub-round. The fundamental sub-round is:
+
+ a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
+ b' = a;
+ c' = ROTL( 30, b );
+ d' = c;
+ e' = d;
+
+ but this is implemented by unrolling the loop 5 times and renaming the
+ variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
+ This code is then replicated 20 times for each of the 4 functions, using
+ the next 20 values from the W[] array each time */
+
+#define subRound(a, b, c, d, e, f, k, data) \
+ (e += ROTL (5, a) + f(b, c, d) + k + data, b = ROTL (30, b))
+
+static void
+sha1_transform (guint32 buf[5],
+ guint32 in[16])
+{
+ guint32 A, B, C, D, E;
+
+ A = buf[0];
+ B = buf[1];
+ C = buf[2];
+ D = buf[3];
+ E = buf[4];
+
+ /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */
+ subRound (A, B, C, D, E, f1, K1, in[0]);
+ subRound (E, A, B, C, D, f1, K1, in[1]);
+ subRound (D, E, A, B, C, f1, K1, in[2]);
+ subRound (C, D, E, A, B, f1, K1, in[3]);
+ subRound (B, C, D, E, A, f1, K1, in[4]);
+ subRound (A, B, C, D, E, f1, K1, in[5]);
+ subRound (E, A, B, C, D, f1, K1, in[6]);
+ subRound (D, E, A, B, C, f1, K1, in[7]);
+ subRound (C, D, E, A, B, f1, K1, in[8]);
+ subRound (B, C, D, E, A, f1, K1, in[9]);
+ subRound (A, B, C, D, E, f1, K1, in[10]);
+ subRound (E, A, B, C, D, f1, K1, in[11]);
+ subRound (D, E, A, B, C, f1, K1, in[12]);
+ subRound (C, D, E, A, B, f1, K1, in[13]);
+ subRound (B, C, D, E, A, f1, K1, in[14]);
+ subRound (A, B, C, D, E, f1, K1, in[15]);
+ subRound (E, A, B, C, D, f1, K1, expand (in, 16));
+ subRound (D, E, A, B, C, f1, K1, expand (in, 17));
+ subRound (C, D, E, A, B, f1, K1, expand (in, 18));
+ subRound (B, C, D, E, A, f1, K1, expand (in, 19));
+
+ subRound (A, B, C, D, E, f2, K2, expand (in, 20));
+ subRound (E, A, B, C, D, f2, K2, expand (in, 21));
+ subRound (D, E, A, B, C, f2, K2, expand (in, 22));
+ subRound (C, D, E, A, B, f2, K2, expand (in, 23));
+ subRound (B, C, D, E, A, f2, K2, expand (in, 24));
+ subRound (A, B, C, D, E, f2, K2, expand (in, 25));
+ subRound (E, A, B, C, D, f2, K2, expand (in, 26));
+ subRound (D, E, A, B, C, f2, K2, expand (in, 27));
+ subRound (C, D, E, A, B, f2, K2, expand (in, 28));
+ subRound (B, C, D, E, A, f2, K2, expand (in, 29));
+ subRound (A, B, C, D, E, f2, K2, expand (in, 30));
+ subRound (E, A, B, C, D, f2, K2, expand (in, 31));
+ subRound (D, E, A, B, C, f2, K2, expand (in, 32));
+ subRound (C, D, E, A, B, f2, K2, expand (in, 33));
+ subRound (B, C, D, E, A, f2, K2, expand (in, 34));
+ subRound (A, B, C, D, E, f2, K2, expand (in, 35));
+ subRound (E, A, B, C, D, f2, K2, expand (in, 36));
+ subRound (D, E, A, B, C, f2, K2, expand (in, 37));
+ subRound (C, D, E, A, B, f2, K2, expand (in, 38));
+ subRound (B, C, D, E, A, f2, K2, expand (in, 39));
+
+ subRound (A, B, C, D, E, f3, K3, expand (in, 40));
+ subRound (E, A, B, C, D, f3, K3, expand (in, 41));
+ subRound (D, E, A, B, C, f3, K3, expand (in, 42));
+ subRound (C, D, E, A, B, f3, K3, expand (in, 43));
+ subRound (B, C, D, E, A, f3, K3, expand (in, 44));
+ subRound (A, B, C, D, E, f3, K3, expand (in, 45));
+ subRound (E, A, B, C, D, f3, K3, expand (in, 46));
+ subRound (D, E, A, B, C, f3, K3, expand (in, 47));
+ subRound (C, D, E, A, B, f3, K3, expand (in, 48));
+ subRound (B, C, D, E, A, f3, K3, expand (in, 49));
+ subRound (A, B, C, D, E, f3, K3, expand (in, 50));
+ subRound (E, A, B, C, D, f3, K3, expand (in, 51));
+ subRound (D, E, A, B, C, f3, K3, expand (in, 52));
+ subRound (C, D, E, A, B, f3, K3, expand (in, 53));
+ subRound (B, C, D, E, A, f3, K3, expand (in, 54));
+ subRound (A, B, C, D, E, f3, K3, expand (in, 55));
+ subRound (E, A, B, C, D, f3, K3, expand (in, 56));
+ subRound (D, E, A, B, C, f3, K3, expand (in, 57));
+ subRound (C, D, E, A, B, f3, K3, expand (in, 58));
+ subRound (B, C, D, E, A, f3, K3, expand (in, 59));
+
+ subRound (A, B, C, D, E, f4, K4, expand (in, 60));
+ subRound (E, A, B, C, D, f4, K4, expand (in, 61));
+ subRound (D, E, A, B, C, f4, K4, expand (in, 62));
+ subRound (C, D, E, A, B, f4, K4, expand (in, 63));
+ subRound (B, C, D, E, A, f4, K4, expand (in, 64));
+ subRound (A, B, C, D, E, f4, K4, expand (in, 65));
+ subRound (E, A, B, C, D, f4, K4, expand (in, 66));
+ subRound (D, E, A, B, C, f4, K4, expand (in, 67));
+ subRound (C, D, E, A, B, f4, K4, expand (in, 68));
+ subRound (B, C, D, E, A, f4, K4, expand (in, 69));
+ subRound (A, B, C, D, E, f4, K4, expand (in, 70));
+ subRound (E, A, B, C, D, f4, K4, expand (in, 71));
+ subRound (D, E, A, B, C, f4, K4, expand (in, 72));
+ subRound (C, D, E, A, B, f4, K4, expand (in, 73));
+ subRound (B, C, D, E, A, f4, K4, expand (in, 74));
+ subRound (A, B, C, D, E, f4, K4, expand (in, 75));
+ subRound (E, A, B, C, D, f4, K4, expand (in, 76));
+ subRound (D, E, A, B, C, f4, K4, expand (in, 77));
+ subRound (C, D, E, A, B, f4, K4, expand (in, 78));
+ subRound (B, C, D, E, A, f4, K4, expand (in, 79));
+
+ /* Build message digest */
+ buf[0] += A;
+ buf[1] += B;
+ buf[2] += C;
+ buf[3] += D;
+ buf[4] += E;
+}
+
+#undef K1
+#undef K2
+#undef K3
+#undef K4
+#undef f1
+#undef f2
+#undef f3
+#undef f4
+#undef ROTL
+#undef expand
+#undef subRound
+
+static void
+sha1_sum_update (Sha1sum *sha1,
+ const guchar *buffer,
+ gsize count)
+{
+ guint32 tmp;
+ guint dataCount;
+
+ /* Update bitcount */
+ tmp = sha1->bits[0];
+ if ((sha1->bits[0] = tmp + ((guint32) count << 3) ) < tmp)
+ sha1->bits[1] += 1; /* Carry from low to high */
+ sha1->bits[1] += count >> 29;
+
+ /* Get count of bytes already in data */
+ dataCount = (guint) (tmp >> 3) & 0x3F;
+
+ /* Handle any leading odd-sized chunks */
+ if (dataCount)
+ {
+ guchar *p = (guchar *) sha1->data + dataCount;
+
+ dataCount = SHA1_DATASIZE - dataCount;
+ if (count < dataCount)
+ {
+ memcpy (p, buffer, count);
+ return;
+ }
+
+ memcpy (p, buffer, dataCount);
+
+ sha_byte_reverse (sha1->data, SHA1_DATASIZE);
+ sha1_transform (sha1->buf, sha1->data);
+
+ buffer += dataCount;
+ count -= dataCount;
+ }
+
+ /* Process data in SHA1_DATASIZE chunks */
+ while (count >= SHA1_DATASIZE)
+ {
+ memcpy (sha1->data, buffer, SHA1_DATASIZE);
+
+ sha_byte_reverse (sha1->data, SHA1_DATASIZE);
+ sha1_transform (sha1->buf, sha1->data);
+
+ buffer += SHA1_DATASIZE;
+ count -= SHA1_DATASIZE;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy (sha1->data, buffer, count);
+}
+
+/* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern
+ 1 0* (64-bit count of bits processed, MSB-first) */
+static void
+sha1_sum_close (Sha1sum *sha1)
+{
+ gint count;
+ guchar *data_p;
+
+ /* Compute number of bytes mod 64 */
+ count = (gint) ((sha1->bits[0] >> 3) & 0x3f);
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ data_p = (guchar *) sha1->data + count;
+ *data_p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = SHA1_DATASIZE - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8)
+ {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (data_p, 0, count);
+
+ sha_byte_reverse (sha1->data, SHA1_DATASIZE);
+ sha1_transform (sha1->buf, sha1->data);
+
+ /* Now fill the next block with 56 bytes */
+ memset (sha1->data, 0, SHA1_DATASIZE - 8);
+ }
+ else
+ {
+ /* Pad block to 56 bytes */
+ memset (data_p, 0, count - 8);
+ }
+
+ /* Append length in bits and transform */
+ sha1->data[14] = sha1->bits[1];
+ sha1->data[15] = sha1->bits[0];
+
+ sha_byte_reverse (sha1->data, SHA1_DATASIZE - 8);
+ sha1_transform (sha1->buf, sha1->data);
+ sha_byte_reverse (sha1->buf, SHA1_DIGEST_LEN);
+
+ memcpy (sha1->digest, sha1->buf, SHA1_DIGEST_LEN);
+
+ /* Reset buffers in case they contain sensitive data */
+ memset (sha1->buf, 0, sizeof (sha1->buf));
+ memset (sha1->data, 0, sizeof (sha1->data));
+}
+
+static gchar *
+sha1_sum_to_string (Sha1sum *sha1)
+{
+ return digest_to_string (sha1->digest, SHA1_DIGEST_LEN);
+}
+
+static void
+sha1_sum_digest (Sha1sum *sha1,
+ guint8 *digest)
+{
+ gint i;
+
+ for (i = 0; i < SHA1_DIGEST_LEN; i++)
+ digest[i] = sha1->digest[i];
+}
+
+/*
+ * SHA-256 Checksum
+ */
+
+/* adapted from the SHA256 implementation in gsk/src/hash/gskhash.c.
+ *
+ * Copyright (C) 2006 Dave Benson
+ * Released under the terms of the GNU Lesser General Public License
+ */
+
+static void
+sha256_sum_init (Sha256sum *sha256)
+{
+ sha256->buf[0] = 0x6a09e667;
+ sha256->buf[1] = 0xbb67ae85;
+ sha256->buf[2] = 0x3c6ef372;
+ sha256->buf[3] = 0xa54ff53a;
+ sha256->buf[4] = 0x510e527f;
+ sha256->buf[5] = 0x9b05688c;
+ sha256->buf[6] = 0x1f83d9ab;
+ sha256->buf[7] = 0x5be0cd19;
+
+ sha256->bits[0] = sha256->bits[1] = 0;
+}
+
+#define GET_UINT32(n,b,i) G_STMT_START{ \
+ (n) = ((guint32) (b)[(i) ] << 24) \
+ | ((guint32) (b)[(i) + 1] << 16) \
+ | ((guint32) (b)[(i) + 2] << 8) \
+ | ((guint32) (b)[(i) + 3] ); } G_STMT_END
+
+#define PUT_UINT32(n,b,i) G_STMT_START{ \
+ (b)[(i) ] = (guint8) ((n) >> 24); \
+ (b)[(i) + 1] = (guint8) ((n) >> 16); \
+ (b)[(i) + 2] = (guint8) ((n) >> 8); \
+ (b)[(i) + 3] = (guint8) ((n) ); } G_STMT_END
+
+static void
+sha256_transform (guint32 buf[8],
+ guint8 const data[64])
+{
+ guint32 temp1, temp2, W[64];
+ guint32 A, B, C, D, E, F, G, H;
+
+ GET_UINT32 (W[0], data, 0);
+ GET_UINT32 (W[1], data, 4);
+ GET_UINT32 (W[2], data, 8);
+ GET_UINT32 (W[3], data, 12);
+ GET_UINT32 (W[4], data, 16);
+ GET_UINT32 (W[5], data, 20);
+ GET_UINT32 (W[6], data, 24);
+ GET_UINT32 (W[7], data, 28);
+ GET_UINT32 (W[8], data, 32);
+ GET_UINT32 (W[9], data, 36);
+ GET_UINT32 (W[10], data, 40);
+ GET_UINT32 (W[11], data, 44);
+ GET_UINT32 (W[12], data, 48);
+ GET_UINT32 (W[13], data, 52);
+ GET_UINT32 (W[14], data, 56);
+ GET_UINT32 (W[15], data, 60);
+
+#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)
+#define ROTR(x,n) (SHR (x,n) | (x << (32 - n)))
+
+#define S0(x) (ROTR (x, 7) ^ ROTR (x,18) ^ SHR (x, 3))
+#define S1(x) (ROTR (x,17) ^ ROTR (x,19) ^ SHR (x,10))
+#define S2(x) (ROTR (x, 2) ^ ROTR (x,13) ^ ROTR (x,22))
+#define S3(x) (ROTR (x, 6) ^ ROTR (x,11) ^ ROTR (x,25))
+
+#define F0(x,y,z) ((x & y) | (z & (x | y)))
+#define F1(x,y,z) (z ^ (x & (y ^ z)))
+
+#define R(t) (W[t] = S1(W[t - 2]) + W[t - 7] + \
+ S0(W[t - 15]) + W[t - 16])
+
+#define P(a,b,c,d,e,f,g,h,x,K) G_STMT_START { \
+ temp1 = h + S3(e) + F1(e,f,g) + K + x; \
+ temp2 = S2(a) + F0(a,b,c); \
+ d += temp1; h = temp1 + temp2; } G_STMT_END
+
+ A = buf[0];
+ B = buf[1];
+ C = buf[2];
+ D = buf[3];
+ E = buf[4];
+ F = buf[5];
+ G = buf[6];
+ H = buf[7];
+
+ P (A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98);
+ P (H, A, B, C, D, E, F, G, W[ 1], 0x71374491);
+ P (G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF);
+ P (F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5);
+ P (E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B);
+ P (D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1);
+ P (C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4);
+ P (B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5);
+ P (A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98);
+ P (H, A, B, C, D, E, F, G, W[ 9], 0x12835B01);
+ P (G, H, A, B, C, D, E, F, W[10], 0x243185BE);
+ P (F, G, H, A, B, C, D, E, W[11], 0x550C7DC3);
+ P (E, F, G, H, A, B, C, D, W[12], 0x72BE5D74);
+ P (D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE);
+ P (C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7);
+ P (B, C, D, E, F, G, H, A, W[15], 0xC19BF174);
+ P (A, B, C, D, E, F, G, H, R(16), 0xE49B69C1);
+ P (H, A, B, C, D, E, F, G, R(17), 0xEFBE4786);
+ P (G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6);
+ P (F, G, H, A, B, C, D, E, R(19), 0x240CA1CC);
+ P (E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F);
+ P (D, E, F, G, H, A, B, C, R(21), 0x4A7484AA);
+ P (C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC);
+ P (B, C, D, E, F, G, H, A, R(23), 0x76F988DA);
+ P (A, B, C, D, E, F, G, H, R(24), 0x983E5152);
+ P (H, A, B, C, D, E, F, G, R(25), 0xA831C66D);
+ P (G, H, A, B, C, D, E, F, R(26), 0xB00327C8);
+ P (F, G, H, A, B, C, D, E, R(27), 0xBF597FC7);
+ P (E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3);
+ P (D, E, F, G, H, A, B, C, R(29), 0xD5A79147);
+ P (C, D, E, F, G, H, A, B, R(30), 0x06CA6351);
+ P (B, C, D, E, F, G, H, A, R(31), 0x14292967);
+ P (A, B, C, D, E, F, G, H, R(32), 0x27B70A85);
+ P (H, A, B, C, D, E, F, G, R(33), 0x2E1B2138);
+ P (G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC);
+ P (F, G, H, A, B, C, D, E, R(35), 0x53380D13);
+ P (E, F, G, H, A, B, C, D, R(36), 0x650A7354);
+ P (D, E, F, G, H, A, B, C, R(37), 0x766A0ABB);
+ P (C, D, E, F, G, H, A, B, R(38), 0x81C2C92E);
+ P (B, C, D, E, F, G, H, A, R(39), 0x92722C85);
+ P (A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1);
+ P (H, A, B, C, D, E, F, G, R(41), 0xA81A664B);
+ P (G, H, A, B, C, D, E, F, R(42), 0xC24B8B70);
+ P (F, G, H, A, B, C, D, E, R(43), 0xC76C51A3);
+ P (E, F, G, H, A, B, C, D, R(44), 0xD192E819);
+ P (D, E, F, G, H, A, B, C, R(45), 0xD6990624);
+ P (C, D, E, F, G, H, A, B, R(46), 0xF40E3585);
+ P (B, C, D, E, F, G, H, A, R(47), 0x106AA070);
+ P (A, B, C, D, E, F, G, H, R(48), 0x19A4C116);
+ P (H, A, B, C, D, E, F, G, R(49), 0x1E376C08);
+ P (G, H, A, B, C, D, E, F, R(50), 0x2748774C);
+ P (F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5);
+ P (E, F, G, H, A, B, C, D, R(52), 0x391C0CB3);
+ P (D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A);
+ P (C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F);
+ P (B, C, D, E, F, G, H, A, R(55), 0x682E6FF3);
+ P (A, B, C, D, E, F, G, H, R(56), 0x748F82EE);
+ P (H, A, B, C, D, E, F, G, R(57), 0x78A5636F);
+ P (G, H, A, B, C, D, E, F, R(58), 0x84C87814);
+ P (F, G, H, A, B, C, D, E, R(59), 0x8CC70208);
+ P (E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA);
+ P (D, E, F, G, H, A, B, C, R(61), 0xA4506CEB);
+ P (C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7);
+ P (B, C, D, E, F, G, H, A, R(63), 0xC67178F2);
+
+#undef SHR
+#undef ROTR
+#undef S0
+#undef S1
+#undef S2
+#undef S3
+#undef F0
+#undef F1
+#undef R
+#undef P
+
+ buf[0] += A;
+ buf[1] += B;
+ buf[2] += C;
+ buf[3] += D;
+ buf[4] += E;
+ buf[5] += F;
+ buf[6] += G;
+ buf[7] += H;
+}
+
+static void
+sha256_sum_update (Sha256sum *sha256,
+ const guchar *buffer,
+ gsize length)
+{
+ guint32 left, fill;
+ const guint8 *input = buffer;
+
+ if (length == 0)
+ return;
+
+ left = sha256->bits[0] & 0x3F;
+ fill = 64 - left;
+
+ sha256->bits[0] += length;
+ sha256->bits[0] &= 0xFFFFFFFF;
+
+ if (sha256->bits[0] < length)
+ sha256->bits[1]++;
+
+ if (left > 0 && length >= fill)
+ {
+ memcpy ((sha256->data + left), input, fill);
+
+ sha256_transform (sha256->buf, sha256->data);
+ length -= fill;
+ input += fill;
+
+ left = 0;
+ }
+
+ while (length >= SHA256_DATASIZE)
+ {
+ sha256_transform (sha256->buf, input);
+
+ length -= 64;
+ input += 64;
+ }
+
+ if (length)
+ memcpy (sha256->data + left, input, length);
+}
+
+static guint8 sha256_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void
+sha256_sum_close (Sha256sum *sha256)
+{
+ guint32 last, padn;
+ guint32 high, low;
+ guint8 msglen[8];
+
+ high = (sha256->bits[0] >> 29)
+ | (sha256->bits[1] << 3);
+ low = (sha256->bits[0] << 3);
+
+ PUT_UINT32 (high, msglen, 0);
+ PUT_UINT32 (low, msglen, 4);
+
+ last = sha256->bits[0] & 0x3F;
+ padn = (last < 56) ? (56 - last) : (120 - last);
+
+ sha256_sum_update (sha256, sha256_padding, padn);
+ sha256_sum_update (sha256, msglen, 8);
+
+ PUT_UINT32 (sha256->buf[0], sha256->digest, 0);
+ PUT_UINT32 (sha256->buf[1], sha256->digest, 4);
+ PUT_UINT32 (sha256->buf[2], sha256->digest, 8);
+ PUT_UINT32 (sha256->buf[3], sha256->digest, 12);
+ PUT_UINT32 (sha256->buf[4], sha256->digest, 16);
+ PUT_UINT32 (sha256->buf[5], sha256->digest, 20);
+ PUT_UINT32 (sha256->buf[6], sha256->digest, 24);
+ PUT_UINT32 (sha256->buf[7], sha256->digest, 28);
+}
+
+#undef PUT_UINT32
+#undef GET_UINT32
+
+static gchar *
+sha256_sum_to_string (Sha256sum *sha256)
+{
+ return digest_to_string (sha256->digest, SHA256_DIGEST_LEN);
+}
+
+static void
+sha256_sum_digest (Sha256sum *sha256,
+ guint8 *digest)
+{
+ gint i;
+
+ for (i = 0; i < SHA256_DIGEST_LEN; i++)
+ digest[i] = sha256->digest[i];
+}
+
+/*
+ * SHA-384, SHA-512, SHA-512/224 and SHA-512/256 Checksums
+ *
+ * Implemented following FIPS-180-4 standard at
+ * http://csrc.nist.gov/publications/fips/fips180-4/fips180-4.pdf.
+ * References in the form [§x.y.z] map to sections in that document.
+ *
+ * Author(s): Eduardo Lima Mitev
+ * Igor Gnatenko
+ */
+
+/* SHA-384, SHA-512, SHA-512/224 and SHA-512/256 functions [§4.1.3] */
+#define Ch(x,y,z) ((x & y) ^ (~x & z))
+#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
+#define SHR(n,x) (x >> n)
+#define ROTR(n,x) (SHR (n, x) | (x << (64 - n)))
+#define SIGMA0(x) (ROTR (28, x) ^ ROTR (34, x) ^ ROTR (39, x))
+#define SIGMA1(x) (ROTR (14, x) ^ ROTR (18, x) ^ ROTR (41, x))
+#define sigma0(x) (ROTR ( 1, x) ^ ROTR ( 8, x) ^ SHR ( 7, x))
+#define sigma1(x) (ROTR (19, x) ^ ROTR (61, x) ^ SHR ( 6, x))
+
+#define PUT_UINT64(n,b,i) G_STMT_START{ \
+ (b)[(i) ] = (guint8) (n >> 56); \
+ (b)[(i) + 1] = (guint8) (n >> 48); \
+ (b)[(i) + 2] = (guint8) (n >> 40); \
+ (b)[(i) + 3] = (guint8) (n >> 32); \
+ (b)[(i) + 4] = (guint8) (n >> 24); \
+ (b)[(i) + 5] = (guint8) (n >> 16); \
+ (b)[(i) + 6] = (guint8) (n >> 8); \
+ (b)[(i) + 7] = (guint8) (n ); } G_STMT_END
+
+/* SHA-384 and SHA-512 constants [§4.2.3] */
+static const guint64 SHA2_K[80] = {
+ G_GUINT64_CONSTANT (0x428a2f98d728ae22), G_GUINT64_CONSTANT (0x7137449123ef65cd),
+ G_GUINT64_CONSTANT (0xb5c0fbcfec4d3b2f), G_GUINT64_CONSTANT (0xe9b5dba58189dbbc),
+ G_GUINT64_CONSTANT (0x3956c25bf348b538), G_GUINT64_CONSTANT (0x59f111f1b605d019),
+ G_GUINT64_CONSTANT (0x923f82a4af194f9b), G_GUINT64_CONSTANT (0xab1c5ed5da6d8118),
+ G_GUINT64_CONSTANT (0xd807aa98a3030242), G_GUINT64_CONSTANT (0x12835b0145706fbe),
+ G_GUINT64_CONSTANT (0x243185be4ee4b28c), G_GUINT64_CONSTANT (0x550c7dc3d5ffb4e2),
+ G_GUINT64_CONSTANT (0x72be5d74f27b896f), G_GUINT64_CONSTANT (0x80deb1fe3b1696b1),
+ G_GUINT64_CONSTANT (0x9bdc06a725c71235), G_GUINT64_CONSTANT (0xc19bf174cf692694),
+ G_GUINT64_CONSTANT (0xe49b69c19ef14ad2), G_GUINT64_CONSTANT (0xefbe4786384f25e3),
+ G_GUINT64_CONSTANT (0x0fc19dc68b8cd5b5), G_GUINT64_CONSTANT (0x240ca1cc77ac9c65),
+ G_GUINT64_CONSTANT (0x2de92c6f592b0275), G_GUINT64_CONSTANT (0x4a7484aa6ea6e483),
+ G_GUINT64_CONSTANT (0x5cb0a9dcbd41fbd4), G_GUINT64_CONSTANT (0x76f988da831153b5),
+ G_GUINT64_CONSTANT (0x983e5152ee66dfab), G_GUINT64_CONSTANT (0xa831c66d2db43210),
+ G_GUINT64_CONSTANT (0xb00327c898fb213f), G_GUINT64_CONSTANT (0xbf597fc7beef0ee4),
+ G_GUINT64_CONSTANT (0xc6e00bf33da88fc2), G_GUINT64_CONSTANT (0xd5a79147930aa725),
+ G_GUINT64_CONSTANT (0x06ca6351e003826f), G_GUINT64_CONSTANT (0x142929670a0e6e70),
+ G_GUINT64_CONSTANT (0x27b70a8546d22ffc), G_GUINT64_CONSTANT (0x2e1b21385c26c926),
+ G_GUINT64_CONSTANT (0x4d2c6dfc5ac42aed), G_GUINT64_CONSTANT (0x53380d139d95b3df),
+ G_GUINT64_CONSTANT (0x650a73548baf63de), G_GUINT64_CONSTANT (0x766a0abb3c77b2a8),
+ G_GUINT64_CONSTANT (0x81c2c92e47edaee6), G_GUINT64_CONSTANT (0x92722c851482353b),
+ G_GUINT64_CONSTANT (0xa2bfe8a14cf10364), G_GUINT64_CONSTANT (0xa81a664bbc423001),
+ G_GUINT64_CONSTANT (0xc24b8b70d0f89791), G_GUINT64_CONSTANT (0xc76c51a30654be30),
+ G_GUINT64_CONSTANT (0xd192e819d6ef5218), G_GUINT64_CONSTANT (0xd69906245565a910),
+ G_GUINT64_CONSTANT (0xf40e35855771202a), G_GUINT64_CONSTANT (0x106aa07032bbd1b8),
+ G_GUINT64_CONSTANT (0x19a4c116b8d2d0c8), G_GUINT64_CONSTANT (0x1e376c085141ab53),
+ G_GUINT64_CONSTANT (0x2748774cdf8eeb99), G_GUINT64_CONSTANT (0x34b0bcb5e19b48a8),
+ G_GUINT64_CONSTANT (0x391c0cb3c5c95a63), G_GUINT64_CONSTANT (0x4ed8aa4ae3418acb),
+ G_GUINT64_CONSTANT (0x5b9cca4f7763e373), G_GUINT64_CONSTANT (0x682e6ff3d6b2b8a3),
+ G_GUINT64_CONSTANT (0x748f82ee5defb2fc), G_GUINT64_CONSTANT (0x78a5636f43172f60),
+ G_GUINT64_CONSTANT (0x84c87814a1f0ab72), G_GUINT64_CONSTANT (0x8cc702081a6439ec),
+ G_GUINT64_CONSTANT (0x90befffa23631e28), G_GUINT64_CONSTANT (0xa4506cebde82bde9),
+ G_GUINT64_CONSTANT (0xbef9a3f7b2c67915), G_GUINT64_CONSTANT (0xc67178f2e372532b),
+ G_GUINT64_CONSTANT (0xca273eceea26619c), G_GUINT64_CONSTANT (0xd186b8c721c0c207),
+ G_GUINT64_CONSTANT (0xeada7dd6cde0eb1e), G_GUINT64_CONSTANT (0xf57d4f7fee6ed178),
+ G_GUINT64_CONSTANT (0x06f067aa72176fba), G_GUINT64_CONSTANT (0x0a637dc5a2c898a6),
+ G_GUINT64_CONSTANT (0x113f9804bef90dae), G_GUINT64_CONSTANT (0x1b710b35131c471b),
+ G_GUINT64_CONSTANT (0x28db77f523047d84), G_GUINT64_CONSTANT (0x32caab7b40c72493),
+ G_GUINT64_CONSTANT (0x3c9ebe0a15c9bebc), G_GUINT64_CONSTANT (0x431d67c49c100d4c),
+ G_GUINT64_CONSTANT (0x4cc5d4becb3e42b6), G_GUINT64_CONSTANT (0x597f299cfc657e2a),
+ G_GUINT64_CONSTANT (0x5fcb6fab3ad6faec), G_GUINT64_CONSTANT (0x6c44198c4a475817)
+};
+
+
+static void
+sha384_sum_init (Sha512sum *sha512)
+{
+ /* Initial Hash Value [§5.3.4] */
+ sha512->H[0] = G_GUINT64_CONSTANT (0xcbbb9d5dc1059ed8);
+ sha512->H[1] = G_GUINT64_CONSTANT (0x629a292a367cd507);
+ sha512->H[2] = G_GUINT64_CONSTANT (0x9159015a3070dd17);
+ sha512->H[3] = G_GUINT64_CONSTANT (0x152fecd8f70e5939);
+ sha512->H[4] = G_GUINT64_CONSTANT (0x67332667ffc00b31);
+ sha512->H[5] = G_GUINT64_CONSTANT (0x8eb44a8768581511);
+ sha512->H[6] = G_GUINT64_CONSTANT (0xdb0c2e0d64f98fa7);
+ sha512->H[7] = G_GUINT64_CONSTANT (0x47b5481dbefa4fa4);
+
+ sha512->block_len = 0;
+
+ sha512->data_len[0] = 0;
+ sha512->data_len[1] = 0;
+}
+
+static void
+sha512_sum_init (Sha512sum *sha512)
+{
+ /* Initial Hash Value [§5.3.5] */
+ sha512->H[0] = G_GUINT64_CONSTANT (0x6a09e667f3bcc908);
+ sha512->H[1] = G_GUINT64_CONSTANT (0xbb67ae8584caa73b);
+ sha512->H[2] = G_GUINT64_CONSTANT (0x3c6ef372fe94f82b);
+ sha512->H[3] = G_GUINT64_CONSTANT (0xa54ff53a5f1d36f1);
+ sha512->H[4] = G_GUINT64_CONSTANT (0x510e527fade682d1);
+ sha512->H[5] = G_GUINT64_CONSTANT (0x9b05688c2b3e6c1f);
+ sha512->H[6] = G_GUINT64_CONSTANT (0x1f83d9abfb41bd6b);
+ sha512->H[7] = G_GUINT64_CONSTANT (0x5be0cd19137e2179);
+
+ sha512->block_len = 0;
+
+ sha512->data_len[0] = 0;
+ sha512->data_len[1] = 0;
+}
+
+static void
+sha512_transform (guint64 H[8],
+ guint8 const data[SHA2_BLOCK_LEN])
+{
+ gint i;
+ gint t;
+ guint64 a, b, c, d, e, f, g, h;
+ guint64 M[16];
+ guint64 W[80];
+
+ /* SHA-512 hash computation [§6.4.2] */
+
+ /* prepare the message schedule */
+ for (i = 0; i < 16; i++)
+ {
+ gint p = i * 8;
+
+ M[i] =
+ ((guint64) data[p + 0] << 56) |
+ ((guint64) data[p + 1] << 48) |
+ ((guint64) data[p + 2] << 40) |
+ ((guint64) data[p + 3] << 32) |
+ ((guint64) data[p + 4] << 24) |
+ ((guint64) data[p + 5] << 16) |
+ ((guint64) data[p + 6] << 8) |
+ ((guint64) data[p + 7] );
+ }
+
+ for (t = 0; t < 80; t++)
+ if (t < 16)
+ W[t] = M[t];
+ else
+ W[t] = sigma1 (W[t - 2]) + W[t - 7] + sigma0 (W[t - 15]) + W[t - 16];
+
+ /* initialize the eight working variables */
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+ for (t = 0; t < 80; t++)
+ {
+ guint64 T1, T2;
+
+ T1 = h + SIGMA1 (e) + Ch (e, f, g) + SHA2_K[t] + W[t];
+ T2 = SIGMA0 (a) + Maj (a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ /* Compute the intermediate hash value H */
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+}
+
+static void
+sha512_sum_update (Sha512sum *sha512,
+ const guchar *buffer,
+ gsize length)
+{
+ gsize block_left, offset = 0;
+
+ if (length == 0)
+ return;
+
+ sha512->data_len[0] += length * 8;
+ if (sha512->data_len[0] < length)
+ sha512->data_len[1]++;
+
+ /* try to fill current block */
+ block_left = SHA2_BLOCK_LEN - sha512->block_len;
+ if (block_left > 0)
+ {
+ gsize fill_len;
+
+ fill_len = MIN (block_left, length);
+ memcpy (sha512->block + sha512->block_len, buffer, fill_len);
+ sha512->block_len += fill_len;
+ length -= fill_len;
+ offset += fill_len;
+
+ if (sha512->block_len == SHA2_BLOCK_LEN)
+ {
+ sha512_transform (sha512->H, sha512->block);
+ sha512->block_len = 0;
+ }
+ }
+
+ /* process complete blocks */
+ while (length >= SHA2_BLOCK_LEN)
+ {
+ memcpy (sha512->block, buffer + offset, SHA2_BLOCK_LEN);
+
+ sha512_transform (sha512->H, sha512->block);
+
+ length -= SHA2_BLOCK_LEN;
+ offset += SHA2_BLOCK_LEN;
+ }
+
+ /* keep remaining data for next block */
+ if (length > 0)
+ {
+ memcpy (sha512->block, buffer + offset, length);
+ sha512->block_len = length;
+ }
+}
+
+static void
+sha512_sum_close (Sha512sum *sha512)
+{
+ guint l;
+ gint zeros;
+ guint8 pad[SHA2_BLOCK_LEN * 2] = { 0, };
+ guint pad_len = 0;
+ gint i;
+
+ /* apply padding [§5.1.2] */
+ l = sha512->block_len * 8;
+ zeros = 896 - (l + 1);
+
+ if (zeros < 0)
+ zeros += 128 * 8;
+
+ pad[0] = 0x80; /* 1000 0000 */
+ zeros -= 7;
+ pad_len++;
+
+ memset (pad + pad_len, 0x00, zeros / 8);
+ pad_len += zeros / 8;
+ zeros = zeros % 8;
+ (void) zeros; /* don’t care about the dead store */
+
+ /* put message bit length at the end of padding */
+ PUT_UINT64 (sha512->data_len[1], pad, pad_len);
+ pad_len += 8;
+
+ PUT_UINT64 (sha512->data_len[0], pad, pad_len);
+ pad_len += 8;
+
+ /* update checksum with the padded block */
+ sha512_sum_update (sha512, pad, pad_len);
+
+ /* copy resulting 64-bit words into digest */
+ for (i = 0; i < 8; i++)
+ PUT_UINT64 (sha512->H[i], sha512->digest, i * 8);
+}
+
+static gchar *
+sha384_sum_to_string (Sha512sum *sha512)
+{
+ return digest_to_string (sha512->digest, SHA384_DIGEST_LEN);
+}
+
+static gchar *
+sha512_sum_to_string (Sha512sum *sha512)
+{
+ return digest_to_string (sha512->digest, SHA512_DIGEST_LEN);
+}
+
+static void
+sha384_sum_digest (Sha512sum *sha512,
+ guint8 *digest)
+{
+ memcpy (digest, sha512->digest, SHA384_DIGEST_LEN);
+}
+
+static void
+sha512_sum_digest (Sha512sum *sha512,
+ guint8 *digest)
+{
+ memcpy (digest, sha512->digest, SHA512_DIGEST_LEN);
+}
+
+#undef Ch
+#undef Maj
+#undef SHR
+#undef ROTR
+#undef SIGMA0
+#undef SIGMA1
+#undef sigma0
+#undef sigma1
+
+#undef PUT_UINT64
+
+/*
+ * Public API
+ */
+
+/**
+ * g_checksum_type_get_length:
+ * @checksum_type: a #GChecksumType
+ *
+ * Gets the length in bytes of digests of type @checksum_type
+ *
+ * Returns: the checksum length, or -1 if @checksum_type is
+ * not supported.
+ *
+ * Since: 2.16
+ */
+gssize
+g_checksum_type_get_length (GChecksumType checksum_type)
+{
+ gssize len = -1;
+
+ switch (checksum_type)
+ {
+ case G_CHECKSUM_MD5:
+ len = MD5_DIGEST_LEN;
+ break;
+ case G_CHECKSUM_SHA1:
+ len = SHA1_DIGEST_LEN;
+ break;
+ case G_CHECKSUM_SHA256:
+ len = SHA256_DIGEST_LEN;
+ break;
+ case G_CHECKSUM_SHA384:
+ len = SHA384_DIGEST_LEN;
+ break;
+ case G_CHECKSUM_SHA512:
+ len = SHA512_DIGEST_LEN;
+ break;
+ default:
+ len = -1;
+ break;
+ }
+
+ return len;
+}
+
+/**
+ * g_checksum_new:
+ * @checksum_type: the desired type of checksum
+ *
+ * Creates a new #GChecksum, using the checksum algorithm @checksum_type.
+ * If the @checksum_type is not known, %NULL is returned.
+ * A #GChecksum can be used to compute the checksum, or digest, of an
+ * arbitrary binary blob, using different hashing algorithms.
+ *
+ * A #GChecksum works by feeding a binary blob through g_checksum_update()
+ * until there is data to be checked; the digest can then be extracted
+ * using g_checksum_get_string(), which will return the checksum as a
+ * hexadecimal string; or g_checksum_get_digest(), which will return a
+ * vector of raw bytes. Once either g_checksum_get_string() or
+ * g_checksum_get_digest() have been called on a #GChecksum, the checksum
+ * will be closed and it won't be possible to call g_checksum_update()
+ * on it anymore.
+ *
+ * Returns: (transfer full) (nullable): the newly created #GChecksum, or %NULL.
+ * Use g_checksum_free() to free the memory allocated by it.
+ *
+ * Since: 2.16
+ */
+GChecksum *
+g_checksum_new (GChecksumType checksum_type)
+{
+ GChecksum *checksum;
+
+ if (! IS_VALID_TYPE (checksum_type))
+ return NULL;
+
+ checksum = g_slice_new0 (GChecksum);
+ checksum->type = checksum_type;
+
+ g_checksum_reset (checksum);
+
+ return checksum;
+}
+
+/**
+ * g_checksum_reset:
+ * @checksum: the #GChecksum to reset
+ *
+ * Resets the state of the @checksum back to its initial state.
+ *
+ * Since: 2.18
+ **/
+void
+g_checksum_reset (GChecksum *checksum)
+{
+ g_return_if_fail (checksum != NULL);
+
+ g_free (checksum->digest_str);
+ checksum->digest_str = NULL;
+
+ switch (checksum->type)
+ {
+ case G_CHECKSUM_MD5:
+ md5_sum_init (&(checksum->sum.md5));
+ break;
+ case G_CHECKSUM_SHA1:
+ sha1_sum_init (&(checksum->sum.sha1));
+ break;
+ case G_CHECKSUM_SHA256:
+ sha256_sum_init (&(checksum->sum.sha256));
+ break;
+ case G_CHECKSUM_SHA384:
+ sha384_sum_init (&(checksum->sum.sha512));
+ break;
+ case G_CHECKSUM_SHA512:
+ sha512_sum_init (&(checksum->sum.sha512));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+/**
+ * g_checksum_copy:
+ * @checksum: the #GChecksum to copy
+ *
+ * Copies a #GChecksum. If @checksum has been closed, by calling
+ * g_checksum_get_string() or g_checksum_get_digest(), the copied
+ * checksum will be closed as well.
+ *
+ * Returns: (transfer full): the copy of the passed #GChecksum. Use
+ * g_checksum_free() when finished using it.
+ *
+ * Since: 2.16
+ */
+GChecksum *
+g_checksum_copy (const GChecksum *checksum)
+{
+ GChecksum *copy;
+
+ g_return_val_if_fail (checksum != NULL, NULL);
+
+ copy = g_slice_new (GChecksum);
+ *copy = *checksum;
+
+ copy->digest_str = g_strdup (checksum->digest_str);
+
+ return copy;
+}
+
+/**
+ * g_checksum_free:
+ * @checksum: a #GChecksum
+ *
+ * Frees the memory allocated for @checksum.
+ *
+ * Since: 2.16
+ */
+void
+g_checksum_free (GChecksum *checksum)
+{
+ if (G_LIKELY (checksum))
+ {
+ g_free (checksum->digest_str);
+
+ g_slice_free (GChecksum, checksum);
+ }
+}
+
+/**
+ * g_checksum_update:
+ * @checksum: a #GChecksum
+ * @data: (array length=length) (element-type guint8): buffer used to compute the checksum
+ * @length: size of the buffer, or -1 if it is a null-terminated string.
+ *
+ * Feeds @data into an existing #GChecksum. The checksum must still be
+ * open, that is g_checksum_get_string() or g_checksum_get_digest() must
+ * not have been called on @checksum.
+ *
+ * Since: 2.16
+ */
+void
+g_checksum_update (GChecksum *checksum,
+ const guchar *data,
+ gssize length)
+{
+ g_return_if_fail (checksum != NULL);
+ g_return_if_fail (length == 0 || data != NULL);
+
+ if (length < 0)
+ length = strlen ((const gchar *) data);
+
+ if (checksum->digest_str)
+ {
+ g_warning ("The checksum '%s' has been closed and cannot be updated "
+ "anymore.",
+ checksum->digest_str);
+ return;
+ }
+
+ switch (checksum->type)
+ {
+ case G_CHECKSUM_MD5:
+ md5_sum_update (&(checksum->sum.md5), data, length);
+ break;
+ case G_CHECKSUM_SHA1:
+ sha1_sum_update (&(checksum->sum.sha1), data, length);
+ break;
+ case G_CHECKSUM_SHA256:
+ sha256_sum_update (&(checksum->sum.sha256), data, length);
+ break;
+ case G_CHECKSUM_SHA384:
+ case G_CHECKSUM_SHA512:
+ sha512_sum_update (&(checksum->sum.sha512), data, length);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+/**
+ * g_checksum_get_string:
+ * @checksum: a #GChecksum
+ *
+ * Gets the digest as a hexadecimal string.
+ *
+ * Once this function has been called the #GChecksum can no longer be
+ * updated with g_checksum_update().
+ *
+ * The hexadecimal characters will be lower case.
+ *
+ * Returns: the hexadecimal representation of the checksum. The
+ * returned string is owned by the checksum and should not be modified
+ * or freed.
+ *
+ * Since: 2.16
+ */
+const gchar *
+g_checksum_get_string (GChecksum *checksum)
+{
+ gchar *str = NULL;
+
+ g_return_val_if_fail (checksum != NULL, NULL);
+
+ if (checksum->digest_str)
+ return checksum->digest_str;
+
+ switch (checksum->type)
+ {
+ case G_CHECKSUM_MD5:
+ md5_sum_close (&(checksum->sum.md5));
+ str = md5_sum_to_string (&(checksum->sum.md5));
+ break;
+ case G_CHECKSUM_SHA1:
+ sha1_sum_close (&(checksum->sum.sha1));
+ str = sha1_sum_to_string (&(checksum->sum.sha1));
+ break;
+ case G_CHECKSUM_SHA256:
+ sha256_sum_close (&(checksum->sum.sha256));
+ str = sha256_sum_to_string (&(checksum->sum.sha256));
+ break;
+ case G_CHECKSUM_SHA384:
+ sha512_sum_close (&(checksum->sum.sha512));
+ str = sha384_sum_to_string (&(checksum->sum.sha512));
+ break;
+ case G_CHECKSUM_SHA512:
+ sha512_sum_close (&(checksum->sum.sha512));
+ str = sha512_sum_to_string (&(checksum->sum.sha512));
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ checksum->digest_str = str;
+
+ return checksum->digest_str;
+}
+
+/**
+ * g_checksum_get_digest: (skip)
+ * @checksum: a #GChecksum
+ * @buffer: (array length=digest_len): output buffer
+ * @digest_len: (inout): an inout parameter. The caller initializes it to the size of @buffer.
+ * After the call it contains the length of the digest.
+ *
+ * Gets the digest from @checksum as a raw binary vector and places it
+ * into @buffer. The size of the digest depends on the type of checksum.
+ *
+ * Once this function has been called, the #GChecksum is closed and can
+ * no longer be updated with g_checksum_update().
+ *
+ * Since: 2.16
+ */
+void
+g_checksum_get_digest (GChecksum *checksum,
+ guint8 *buffer,
+ gsize *digest_len)
+{
+ gboolean checksum_open = FALSE;
+ gchar *str = NULL;
+ gsize len;
+
+ g_return_if_fail (checksum != NULL);
+
+ len = g_checksum_type_get_length (checksum->type);
+ g_return_if_fail (*digest_len >= len);
+
+ checksum_open = !!(checksum->digest_str == NULL);
+
+ switch (checksum->type)
+ {
+ case G_CHECKSUM_MD5:
+ if (checksum_open)
+ {
+ md5_sum_close (&(checksum->sum.md5));
+ str = md5_sum_to_string (&(checksum->sum.md5));
+ }
+ md5_sum_digest (&(checksum->sum.md5), buffer);
+ break;
+ case G_CHECKSUM_SHA1:
+ if (checksum_open)
+ {
+ sha1_sum_close (&(checksum->sum.sha1));
+ str = sha1_sum_to_string (&(checksum->sum.sha1));
+ }
+ sha1_sum_digest (&(checksum->sum.sha1), buffer);
+ break;
+ case G_CHECKSUM_SHA256:
+ if (checksum_open)
+ {
+ sha256_sum_close (&(checksum->sum.sha256));
+ str = sha256_sum_to_string (&(checksum->sum.sha256));
+ }
+ sha256_sum_digest (&(checksum->sum.sha256), buffer);
+ break;
+ case G_CHECKSUM_SHA384:
+ if (checksum_open)
+ {
+ sha512_sum_close (&(checksum->sum.sha512));
+ str = sha384_sum_to_string (&(checksum->sum.sha512));
+ }
+ sha384_sum_digest (&(checksum->sum.sha512), buffer);
+ break;
+ case G_CHECKSUM_SHA512:
+ if (checksum_open)
+ {
+ sha512_sum_close (&(checksum->sum.sha512));
+ str = sha512_sum_to_string (&(checksum->sum.sha512));
+ }
+ sha512_sum_digest (&(checksum->sum.sha512), buffer);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (str)
+ checksum->digest_str = str;
+
+ *digest_len = len;
+}
+
+/**
+ * g_compute_checksum_for_data:
+ * @checksum_type: a #GChecksumType
+ * @data: (array length=length) (element-type guint8): binary blob to compute the digest of
+ * @length: length of @data
+ *
+ * Computes the checksum for a binary @data of @length. This is a
+ * convenience wrapper for g_checksum_new(), g_checksum_get_string()
+ * and g_checksum_free().
+ *
+ * The hexadecimal string returned will be in lower case.
+ *
+ * Returns: (transfer full) (nullable): the digest of the binary data as a
+ * string in hexadecimal, or %NULL if g_checksum_new() fails for
+ * @checksum_type. The returned string should be freed with g_free() when
+ * done using it.
+ *
+ * Since: 2.16
+ */
+gchar *
+g_compute_checksum_for_data (GChecksumType checksum_type,
+ const guchar *data,
+ gsize length)
+{
+ GChecksum *checksum;
+ gchar *retval;
+
+ g_return_val_if_fail (length == 0 || data != NULL, NULL);
+
+ checksum = g_checksum_new (checksum_type);
+ if (!checksum)
+ return NULL;
+
+ g_checksum_update (checksum, data, length);
+ retval = g_strdup (g_checksum_get_string (checksum));
+ g_checksum_free (checksum);
+
+ return retval;
+}
+
+/**
+ * g_compute_checksum_for_string:
+ * @checksum_type: a #GChecksumType
+ * @str: the string to compute the checksum of
+ * @length: the length of the string, or -1 if the string is null-terminated.
+ *
+ * Computes the checksum of a string.
+ *
+ * The hexadecimal string returned will be in lower case.
+ *
+ * Returns: (transfer full) (nullable): the checksum as a hexadecimal string,
+ * or %NULL if g_checksum_new() fails for @checksum_type. The returned string
+ * should be freed with g_free() when done using it.
+ *
+ * Since: 2.16
+ */
+gchar *
+g_compute_checksum_for_string (GChecksumType checksum_type,
+ const gchar *str,
+ gssize length)
+{
+ g_return_val_if_fail (length == 0 || str != NULL, NULL);
+
+ if (length < 0)
+ length = strlen (str);
+
+ return g_compute_checksum_for_data (checksum_type, (const guchar *) str, length);
+}
+
+/**
+ * g_compute_checksum_for_bytes:
+ * @checksum_type: a #GChecksumType
+ * @data: binary blob to compute the digest of
+ *
+ * Computes the checksum for a binary @data. This is a
+ * convenience wrapper for g_checksum_new(), g_checksum_get_string()
+ * and g_checksum_free().
+ *
+ * The hexadecimal string returned will be in lower case.
+ *
+ * Returns: (transfer full) (nullable): the digest of the binary data as a
+ * string in hexadecimal, or %NULL if g_checksum_new() fails for
+ * @checksum_type. The returned string should be freed with g_free() when
+ * done using it.
+ *
+ * Since: 2.34
+ */
+gchar *
+g_compute_checksum_for_bytes (GChecksumType checksum_type,
+ GBytes *data)
+{
+ gconstpointer byte_data;
+ gsize length;
+
+ g_return_val_if_fail (data != NULL, NULL);
+
+ byte_data = g_bytes_get_data (data, &length);
+ return g_compute_checksum_for_data (checksum_type, byte_data, length);
+}
diff --git a/glib/gchecksum.h b/glib/gchecksum.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bb52d8242085c3843bfd471db24f350f74286e8
--- /dev/null
+++ b/glib/gchecksum.h
@@ -0,0 +1,104 @@
+/* gchecksum.h - data hashing functions
+ *
+ * Copyright (C) 2007 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#ifndef __G_CHECKSUM_H__
+#define __G_CHECKSUM_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+/**
+ * GChecksumType:
+ * @G_CHECKSUM_MD5: Use the MD5 hashing algorithm
+ * @G_CHECKSUM_SHA1: Use the SHA-1 hashing algorithm
+ * @G_CHECKSUM_SHA256: Use the SHA-256 hashing algorithm
+ * @G_CHECKSUM_SHA384: Use the SHA-384 hashing algorithm (Since: 2.51)
+ * @G_CHECKSUM_SHA512: Use the SHA-512 hashing algorithm (Since: 2.36)
+ *
+ * The hashing algorithm to be used by #GChecksum when performing the
+ * digest of some data.
+ *
+ * Note that the #GChecksumType enumeration may be extended at a later
+ * date to include new hashing algorithm types.
+ *
+ * Since: 2.16
+ */
+typedef enum {
+ G_CHECKSUM_MD5,
+ G_CHECKSUM_SHA1,
+ G_CHECKSUM_SHA256,
+ G_CHECKSUM_SHA512,
+ G_CHECKSUM_SHA384
+} GChecksumType;
+
+/**
+ * GChecksum:
+ *
+ * An opaque structure representing a checksumming operation.
+ *
+ * To create a new GChecksum, use g_checksum_new(). To free
+ * a GChecksum, use g_checksum_free().
+ *
+ * Since: 2.16
+ */
+typedef struct _GChecksum GChecksum;
+
+GLIB_AVAILABLE_IN_ALL
+gssize g_checksum_type_get_length (GChecksumType checksum_type);
+
+GLIB_AVAILABLE_IN_ALL
+GChecksum * g_checksum_new (GChecksumType checksum_type);
+GLIB_AVAILABLE_IN_ALL
+void g_checksum_reset (GChecksum *checksum);
+GLIB_AVAILABLE_IN_ALL
+GChecksum * g_checksum_copy (const GChecksum *checksum);
+GLIB_AVAILABLE_IN_ALL
+void g_checksum_free (GChecksum *checksum);
+GLIB_AVAILABLE_IN_ALL
+void g_checksum_update (GChecksum *checksum,
+ const guchar *data,
+ gssize length);
+GLIB_AVAILABLE_IN_ALL
+const gchar * g_checksum_get_string (GChecksum *checksum);
+GLIB_AVAILABLE_IN_ALL
+void g_checksum_get_digest (GChecksum *checksum,
+ guint8 *buffer,
+ gsize *digest_len);
+
+GLIB_AVAILABLE_IN_ALL
+gchar *g_compute_checksum_for_data (GChecksumType checksum_type,
+ const guchar *data,
+ gsize length);
+GLIB_AVAILABLE_IN_ALL
+gchar *g_compute_checksum_for_string (GChecksumType checksum_type,
+ const gchar *str,
+ gssize length);
+
+GLIB_AVAILABLE_IN_2_34
+gchar *g_compute_checksum_for_bytes (GChecksumType checksum_type,
+ GBytes *data);
+
+G_END_DECLS
+
+#endif /* __G_CHECKSUM_H__ */
diff --git a/glib/gconstructor.h b/glib/gconstructor.h
new file mode 100644
index 0000000000000000000000000000000000000000..c5e0dfaa6e737e7f013f122b8580ac0bdbb805e8
--- /dev/null
+++ b/glib/gconstructor.h
@@ -0,0 +1,159 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_CONSTRUCTOR_H__
+#define __G_CONSTRUCTOR_H__
+
+/*
+ If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and
+ destructors, in a usable way, including e.g. on library unload. If not you're on
+ your own.
+
+ Some compilers need #pragma to handle this, which does not work with macros,
+ so the way you need to use this is (for constructors):
+
+ #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+ #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor)
+ #endif
+ G_DEFINE_CONSTRUCTOR(my_constructor)
+ static void my_constructor(void) {
+ ...
+ }
+
+*/
+
+#ifndef __GTK_DOC_IGNORE__
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void);
+#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void);
+
+#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
+/* Visual studio 2008 and later has _Pragma */
+
+/*
+ * Only try to include gslist.h if not already included via glib.h,
+ * so that items using gconstructor.h outside of GLib (such as
+ * GResources) continue to build properly.
+ */
+#ifndef __G_LIB_H__
+#include "gslist.h"
+#endif
+
+#include
+
+#define G_HAS_CONSTRUCTORS 1
+
+/* We do some weird things to avoid the constructors being optimized
+ * away on VS2015 if WholeProgramOptimization is enabled. First we
+ * make a reference to the array from the wrapper to make sure its
+ * references. Then we use a pragma to make sure the wrapper function
+ * symbol is always included at the link stage. Also, the symbols
+ * need to be extern (but not dllexport), even though they are not
+ * really used from another object file.
+ */
+
+/* We need to account for differences between the mangling of symbols
+ * for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed
+ * with an underscore but symbols on x64/ARM/ARM64 are not.
+ */
+#ifdef _M_IX86
+#define G_MSVC_SYMBOL_PREFIX "_"
+#else
+#define G_MSVC_SYMBOL_PREFIX ""
+#endif
+
+#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX)
+#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX)
+
+#define G_MSVC_CTOR(_func,_sym_prefix) \
+ static void _func(void); \
+ extern int (* _array ## _func)(void); \
+ int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \
+ __pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \
+ __pragma(section(".CRT$XCU",read)) \
+ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper;
+
+#define G_MSVC_DTOR(_func,_sym_prefix) \
+ static void _func(void); \
+ extern int (* _array ## _func)(void); \
+ int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \
+ __pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \
+ __pragma(section(".CRT$XCU",read)) \
+ __declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor;
+
+#elif defined (_MSC_VER)
+
+#define G_HAS_CONSTRUCTORS 1
+
+/* Pre Visual studio 2008 must use #pragma section */
+#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
+#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
+
+#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
+ section(".CRT$XCU",read)
+#define G_DEFINE_CONSTRUCTOR(_func) \
+ static void _func(void); \
+ static int _func ## _wrapper(void) { _func(); return 0; } \
+ __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper;
+
+#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
+ section(".CRT$XCU",read)
+#define G_DEFINE_DESTRUCTOR(_func) \
+ static void _func(void); \
+ static int _func ## _constructor(void) { atexit (_func); return 0; } \
+ __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
+
+#elif defined(__SUNPRO_C)
+
+/* This is not tested, but i believe it should work, based on:
+ * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c
+ */
+
+#define G_HAS_CONSTRUCTORS 1
+
+#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
+#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
+
+#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
+ init(_func)
+#define G_DEFINE_CONSTRUCTOR(_func) \
+ static void _func(void);
+
+#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
+ fini(_func)
+#define G_DEFINE_DESTRUCTOR(_func) \
+ static void _func(void);
+
+#else
+
+/* constructors not supported for this compiler */
+
+#endif
+
+#endif /* __GTK_DOC_IGNORE__ */
+#endif /* __G_CONSTRUCTOR_H__ */
diff --git a/glib/gconvert.c b/glib/gconvert.c
new file mode 100644
index 0000000000000000000000000000000000000000..052f019e26e22f1f06cad4078fa6d10f43c0f41f
--- /dev/null
+++ b/glib/gconvert.c
@@ -0,0 +1,2066 @@
+/* GLIB - Library of useful routines for C programming
+ *
+ * gconvert.c: Convert between character sets using iconv
+ * Copyright Red Hat Inc., 2000
+ * Authors: Havoc Pennington , Owen Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include "config.h"
+#include "glibconfig.h"
+
+#ifndef G_OS_WIN32
+#include
+#endif
+#include
+#include
+#include
+#include
+
+#ifdef G_OS_WIN32
+#include "win_iconv.c"
+#endif
+
+#ifdef G_PLATFORM_WIN32
+#define STRICT
+#include
+#undef STRICT
+#endif
+
+#include "gconvert.h"
+#include "gconvertprivate.h"
+
+#include "gcharsetprivate.h"
+#include "gslist.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gthread.h"
+#include "gthreadprivate.h"
+#include "gunicode.h"
+#include "gfileutils.h"
+#include "genviron.h"
+
+#include "glibintl.h"
+
+
+/**
+ * SECTION:conversions
+ * @title: Character Set Conversion
+ * @short_description: convert strings between different character sets
+ *
+ * The g_convert() family of function wraps the functionality of iconv().
+ * In addition to pure character set conversions, GLib has functions to
+ * deal with the extra complications of encodings for file names.
+ *
+ * ## File Name Encodings
+ *
+ * Historically, UNIX has not had a defined encoding for file names:
+ * a file name is valid as long as it does not have path separators
+ * in it ("/"). However, displaying file names may require conversion:
+ * from the character set in which they were created, to the character
+ * set in which the application operates. Consider the Spanish file name
+ * "Presentación.sxi". If the application which created it uses
+ * ISO-8859-1 for its encoding,
+ * |[
+ * Character: P r e s e n t a c i ó n . s x i
+ * Hex code: 50 72 65 73 65 6e 74 61 63 69 f3 6e 2e 73 78 69
+ * ]|
+ * However, if the application use UTF-8, the actual file name on
+ * disk would look like this:
+ * |[
+ * Character: P r e s e n t a c i ó n . s x i
+ * Hex code: 50 72 65 73 65 6e 74 61 63 69 c3 b3 6e 2e 73 78 69
+ * ]|
+ * Glib uses UTF-8 for its strings, and GUI toolkits like GTK+ that use
+ * GLib do the same thing. If you get a file name from the file system,
+ * for example, from readdir() or from g_dir_read_name(), and you wish
+ * to display the file name to the user, you will need to convert it
+ * into UTF-8. The opposite case is when the user types the name of a
+ * file they wish to save: the toolkit will give you that string in
+ * UTF-8 encoding, and you will need to convert it to the character
+ * set used for file names before you can create the file with open()
+ * or fopen().
+ *
+ * By default, GLib assumes that file names on disk are in UTF-8
+ * encoding. This is a valid assumption for file systems which
+ * were created relatively recently: most applications use UTF-8
+ * encoding for their strings, and that is also what they use for
+ * the file names they create. However, older file systems may
+ * still contain file names created in "older" encodings, such as
+ * ISO-8859-1. In this case, for compatibility reasons, you may want
+ * to instruct GLib to use that particular encoding for file names
+ * rather than UTF-8. You can do this by specifying the encoding for
+ * file names in the [`G_FILENAME_ENCODING`][G_FILENAME_ENCODING]
+ * environment variable. For example, if your installation uses
+ * ISO-8859-1 for file names, you can put this in your `~/.profile`:
+ * |[
+ * export G_FILENAME_ENCODING=ISO-8859-1
+ * ]|
+ * GLib provides the functions g_filename_to_utf8() and
+ * g_filename_from_utf8() to perform the necessary conversions.
+ * These functions convert file names from the encoding specified
+ * in `G_FILENAME_ENCODING` to UTF-8 and vice-versa. This
+ * [diagram][file-name-encodings-diagram] illustrates how
+ * these functions are used to convert between UTF-8 and the
+ * encoding for file names in the file system.
+ *
+ * ## Conversion between file name encodings # {#file-name-encodings-diagram)
+ *
+ * 
+ *
+ * ## Checklist for Application Writers
+ *
+ * This section is a practical summary of the detailed
+ * things to do to make sure your applications process file
+ * name encodings correctly.
+ *
+ * 1. If you get a file name from the file system from a function
+ * such as readdir() or gtk_file_chooser_get_filename(), you do
+ * not need to do any conversion to pass that file name to
+ * functions like open(), rename(), or fopen() -- those are "raw"
+ * file names which the file system understands.
+ *
+ * 2. If you need to display a file name, convert it to UTF-8 first
+ * by using g_filename_to_utf8(). If conversion fails, display a
+ * string like "Unknown file name". Do not convert this string back
+ * into the encoding used for file names if you wish to pass it to
+ * the file system; use the original file name instead.
+ *
+ * For example, the document window of a word processor could display
+ * "Unknown file name" in its title bar but still let the user save
+ * the file, as it would keep the raw file name internally. This
+ * can happen if the user has not set the `G_FILENAME_ENCODING`
+ * environment variable even though they have files whose names are
+ * not encoded in UTF-8.
+ *
+ * 3. If your user interface lets the user type a file name for saving
+ * or renaming, convert it to the encoding used for file names in
+ * the file system by using g_filename_from_utf8(). Pass the converted
+ * file name to functions like fopen(). If conversion fails, ask the
+ * user to enter a different file name. This can happen if the user
+ * types Japanese characters when `G_FILENAME_ENCODING` is set to
+ * `ISO-8859-1`, for example.
+ */
+
+/* We try to terminate strings in unknown charsets with this many zero bytes
+ * to ensure that multibyte strings really are nul-terminated when we return
+ * them from g_convert() and friends.
+ */
+#define NUL_TERMINATOR_LENGTH 4
+
+G_DEFINE_QUARK (g_convert_error, g_convert_error)
+
+static gboolean
+try_conversion (const char *to_codeset,
+ const char *from_codeset,
+ iconv_t *cd)
+{
+ *cd = iconv_open (to_codeset, from_codeset);
+
+ if (*cd == (iconv_t)-1 && errno == EINVAL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static gboolean
+try_to_aliases (const char **to_aliases,
+ const char *from_codeset,
+ iconv_t *cd)
+{
+ if (to_aliases)
+ {
+ const char **p = to_aliases;
+ while (*p)
+ {
+ if (try_conversion (*p, from_codeset, cd))
+ return TRUE;
+
+ p++;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * g_iconv_open: (skip)
+ * @to_codeset: destination codeset
+ * @from_codeset: source codeset
+ *
+ * Same as the standard UNIX routine iconv_open(), but
+ * may be implemented via libiconv on UNIX flavors that lack
+ * a native implementation.
+ *
+ * GLib provides g_convert() and g_locale_to_utf8() which are likely
+ * more convenient than the raw iconv wrappers.
+ *
+ * Returns: a "conversion descriptor", or (GIConv)-1 if
+ * opening the converter failed.
+ **/
+GIConv
+g_iconv_open (const gchar *to_codeset,
+ const gchar *from_codeset)
+{
+ iconv_t cd;
+
+ if (!try_conversion (to_codeset, from_codeset, &cd))
+ {
+ const char **to_aliases = _g_charset_get_aliases (to_codeset);
+ const char **from_aliases = _g_charset_get_aliases (from_codeset);
+
+ if (from_aliases)
+ {
+ const char **p = from_aliases;
+ while (*p)
+ {
+ if (try_conversion (to_codeset, *p, &cd))
+ goto out;
+
+ if (try_to_aliases (to_aliases, *p, &cd))
+ goto out;
+
+ p++;
+ }
+ }
+
+ if (try_to_aliases (to_aliases, from_codeset, &cd))
+ goto out;
+ }
+
+ out:
+ return (cd == (iconv_t)-1) ? (GIConv)-1 : (GIConv)cd;
+}
+
+/**
+ * g_iconv: (skip)
+ * @converter: conversion descriptor from g_iconv_open()
+ * @inbuf: bytes to convert
+ * @inbytes_left: inout parameter, bytes remaining to convert in @inbuf
+ * @outbuf: converted output bytes
+ * @outbytes_left: inout parameter, bytes available to fill in @outbuf
+ *
+ * Same as the standard UNIX routine iconv(), but
+ * may be implemented via libiconv on UNIX flavors that lack
+ * a native implementation.
+ *
+ * GLib provides g_convert() and g_locale_to_utf8() which are likely
+ * more convenient than the raw iconv wrappers.
+ *
+ * Note that the behaviour of iconv() for characters which are valid in the
+ * input character set, but which have no representation in the output character
+ * set, is implementation defined. This function may return success (with a
+ * positive number of non-reversible conversions as replacement characters were
+ * used), or it may return -1 and set an error such as %EILSEQ, in such a
+ * situation.
+ *
+ * Returns: count of non-reversible conversions, or -1 on error
+ **/
+gsize
+g_iconv (GIConv converter,
+ gchar **inbuf,
+ gsize *inbytes_left,
+ gchar **outbuf,
+ gsize *outbytes_left)
+{
+ iconv_t cd = (iconv_t)converter;
+
+ return iconv (cd, inbuf, inbytes_left, outbuf, outbytes_left);
+}
+
+/**
+ * g_iconv_close: (skip)
+ * @converter: a conversion descriptor from g_iconv_open()
+ *
+ * Same as the standard UNIX routine iconv_close(), but
+ * may be implemented via libiconv on UNIX flavors that lack
+ * a native implementation. Should be called to clean up
+ * the conversion descriptor from g_iconv_open() when
+ * you are done converting things.
+ *
+ * GLib provides g_convert() and g_locale_to_utf8() which are likely
+ * more convenient than the raw iconv wrappers.
+ *
+ * Returns: -1 on error, 0 on success
+ **/
+gint
+g_iconv_close (GIConv converter)
+{
+ iconv_t cd = (iconv_t)converter;
+
+ return iconv_close (cd);
+}
+
+static GIConv
+open_converter (const gchar *to_codeset,
+ const gchar *from_codeset,
+ GError **error)
+{
+ GIConv cd;
+
+ cd = g_iconv_open (to_codeset, from_codeset);
+
+ if (cd == (GIConv) -1)
+ {
+ /* Something went wrong. */
+ if (error)
+ {
+ if (errno == EINVAL)
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
+ _("Conversion from character set “%s” to “%s” is not supported"),
+ from_codeset, to_codeset);
+ else
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Could not open converter from “%s” to “%s”"),
+ from_codeset, to_codeset);
+ }
+ }
+
+ return cd;
+}
+
+static int
+close_converter (GIConv cd)
+{
+ if (cd == (GIConv) -1)
+ return 0;
+
+ return g_iconv_close (cd);
+}
+
+/**
+ * g_convert_with_iconv: (skip)
+ * @str: (array length=len) (element-type guint8):
+ * the string to convert.
+ * @len: the length of the string in bytes, or -1 if the string is
+ * nul-terminated (Note that some encodings may allow nul
+ * bytes to occur inside strings. In that case, using -1
+ * for the @len parameter is unsafe)
+ * @converter: conversion descriptor from g_iconv_open()
+ * @bytes_read: (out) (optional): location to store the number of bytes in
+ * the input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input. If the error
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ * stored will be the byte offset after the last valid
+ * input sequence.
+ * @bytes_written: (out) (optional): the number of bytes stored in
+ * the output buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from one character set to another.
+ *
+ * Note that you should use g_iconv() for streaming conversions.
+ * Despite the fact that @bytes_read can return information about partial
+ * characters, the g_convert_... functions are not generally suitable
+ * for streaming. If the underlying converter maintains internal state,
+ * then this won't be preserved across successive calls to g_convert(),
+ * g_convert_with_iconv() or g_convert_with_fallback(). (An example of
+ * this is the GNU C converter for CP1255 which does not emit a base
+ * character until it knows that the next character is not a mark that
+ * could combine with the base character.)
+ *
+ * Characters which are valid in the input character set, but which have no
+ * representation in the output character set will result in a
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE error. This is in contrast to the iconv()
+ * specification, which leaves this behaviour implementation defined. Note that
+ * this is the same error code as is returned for an invalid byte sequence in
+ * the input character set. To get defined behaviour for conversion of
+ * unrepresentable characters, use g_convert_with_fallback().
+ *
+ * Returns: (array length=bytes_written) (element-type guint8) (transfer full):
+ * If the conversion was successful, a newly allocated buffer
+ * containing the converted string, which must be freed with
+ * g_free(). Otherwise %NULL and @error will be set.
+ **/
+gchar*
+g_convert_with_iconv (const gchar *str,
+ gssize len,
+ GIConv converter,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ gchar *dest;
+ gchar *outp;
+ const gchar *p;
+ gsize inbytes_remaining;
+ gsize outbytes_remaining;
+ gsize err;
+ gsize outbuf_size;
+ gboolean have_error = FALSE;
+ gboolean done = FALSE;
+ gboolean reset = FALSE;
+
+ g_return_val_if_fail (converter != (GIConv) -1, NULL);
+
+ if (len < 0)
+ len = strlen (str);
+
+ p = str;
+ inbytes_remaining = len;
+ outbuf_size = len + NUL_TERMINATOR_LENGTH;
+
+ outbytes_remaining = outbuf_size - NUL_TERMINATOR_LENGTH;
+ outp = dest = g_malloc (outbuf_size);
+
+ while (!done && !have_error)
+ {
+ if (reset)
+ err = g_iconv (converter, NULL, &inbytes_remaining, &outp, &outbytes_remaining);
+ else
+ err = g_iconv (converter, (char **)&p, &inbytes_remaining, &outp, &outbytes_remaining);
+
+ if (err == (gsize) -1)
+ {
+ switch (errno)
+ {
+ case EINVAL:
+ /* Incomplete text, do not report an error */
+ done = TRUE;
+ break;
+ case E2BIG:
+ {
+ gsize used = outp - dest;
+
+ outbuf_size *= 2;
+ dest = g_realloc (dest, outbuf_size);
+
+ outp = dest + used;
+ outbytes_remaining = outbuf_size - used - NUL_TERMINATOR_LENGTH;
+ }
+ break;
+ case EILSEQ:
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+ have_error = TRUE;
+ break;
+ default:
+ {
+ int errsv = errno;
+
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Error during conversion: %s"),
+ g_strerror (errsv));
+ }
+ have_error = TRUE;
+ break;
+ }
+ }
+ else if (err > 0)
+ {
+ /* @err gives the number of replacement characters used. */
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Unrepresentable character in conversion input"));
+ have_error = TRUE;
+ }
+ else
+ {
+ if (!reset)
+ {
+ /* call g_iconv with NULL inbuf to cleanup shift state */
+ reset = TRUE;
+ inbytes_remaining = 0;
+ }
+ else
+ done = TRUE;
+ }
+ }
+
+ memset (outp, 0, NUL_TERMINATOR_LENGTH);
+
+ if (bytes_read)
+ *bytes_read = p - str;
+ else
+ {
+ if ((p - str) != len)
+ {
+ if (!have_error)
+ {
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
+ _("Partial character sequence at end of input"));
+ have_error = TRUE;
+ }
+ }
+ }
+
+ if (bytes_written)
+ *bytes_written = outp - dest; /* Doesn't include '\0' */
+
+ if (have_error)
+ {
+ g_free (dest);
+ return NULL;
+ }
+ else
+ return dest;
+}
+
+/**
+ * g_convert:
+ * @str: (array length=len) (element-type guint8):
+ * the string to convert.
+ * @len: the length of the string in bytes, or -1 if the string is
+ * nul-terminated (Note that some encodings may allow nul
+ * bytes to occur inside strings. In that case, using -1
+ * for the @len parameter is unsafe)
+ * @to_codeset: name of character set into which to convert @str
+ * @from_codeset: character set of @str.
+ * @bytes_read: (out) (optional): location to store the number of bytes in
+ * the input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input. If the error
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ * stored will be the byte offset after the last valid
+ * input sequence.
+ * @bytes_written: (out) (optional): the number of bytes stored in
+ * the output buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from one character set to another.
+ *
+ * Note that you should use g_iconv() for streaming conversions.
+ * Despite the fact that @bytes_read can return information about partial
+ * characters, the g_convert_... functions are not generally suitable
+ * for streaming. If the underlying converter maintains internal state,
+ * then this won't be preserved across successive calls to g_convert(),
+ * g_convert_with_iconv() or g_convert_with_fallback(). (An example of
+ * this is the GNU C converter for CP1255 which does not emit a base
+ * character until it knows that the next character is not a mark that
+ * could combine with the base character.)
+ *
+ * Using extensions such as "//TRANSLIT" may not work (or may not work
+ * well) on many platforms. Consider using g_str_to_ascii() instead.
+ *
+ * Returns: (array length=bytes_written) (element-type guint8) (transfer full):
+ * If the conversion was successful, a newly allocated buffer
+ * containing the converted string, which must be freed with g_free().
+ * Otherwise %NULL and @error will be set.
+ **/
+gchar*
+g_convert (const gchar *str,
+ gssize len,
+ const gchar *to_codeset,
+ const gchar *from_codeset,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ gchar *res;
+ GIConv cd;
+
+ g_return_val_if_fail (str != NULL, NULL);
+ g_return_val_if_fail (to_codeset != NULL, NULL);
+ g_return_val_if_fail (from_codeset != NULL, NULL);
+
+ cd = open_converter (to_codeset, from_codeset, error);
+
+ if (cd == (GIConv) -1)
+ {
+ if (bytes_read)
+ *bytes_read = 0;
+
+ if (bytes_written)
+ *bytes_written = 0;
+
+ return NULL;
+ }
+
+ res = g_convert_with_iconv (str, len, cd,
+ bytes_read, bytes_written,
+ error);
+
+ close_converter (cd);
+
+ return res;
+}
+
+/**
+ * g_convert_with_fallback:
+ * @str: (array length=len) (element-type guint8):
+ * the string to convert.
+ * @len: the length of the string in bytes, or -1 if the string is
+ * nul-terminated (Note that some encodings may allow nul
+ * bytes to occur inside strings. In that case, using -1
+ * for the @len parameter is unsafe)
+ * @to_codeset: name of character set into which to convert @str
+ * @from_codeset: character set of @str.
+ * @fallback: UTF-8 string to use in place of characters not
+ * present in the target encoding. (The string must be
+ * representable in the target encoding).
+ * If %NULL, characters not in the target encoding will
+ * be represented as Unicode escapes \uxxxx or \Uxxxxyyyy.
+ * @bytes_read: (out) (optional): location to store the number of bytes in
+ * the input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input.
+ * @bytes_written: (out) (optional): the number of bytes stored in
+ * the output buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from one character set to another, possibly
+ * including fallback sequences for characters not representable
+ * in the output. Note that it is not guaranteed that the specification
+ * for the fallback sequences in @fallback will be honored. Some
+ * systems may do an approximate conversion from @from_codeset
+ * to @to_codeset in their iconv() functions,
+ * in which case GLib will simply return that approximate conversion.
+ *
+ * Note that you should use g_iconv() for streaming conversions.
+ * Despite the fact that @bytes_read can return information about partial
+ * characters, the g_convert_... functions are not generally suitable
+ * for streaming. If the underlying converter maintains internal state,
+ * then this won't be preserved across successive calls to g_convert(),
+ * g_convert_with_iconv() or g_convert_with_fallback(). (An example of
+ * this is the GNU C converter for CP1255 which does not emit a base
+ * character until it knows that the next character is not a mark that
+ * could combine with the base character.)
+ *
+ * Returns: (array length=bytes_written) (element-type guint8) (transfer full):
+ * If the conversion was successful, a newly allocated buffer
+ * containing the converted string, which must be freed with g_free().
+ * Otherwise %NULL and @error will be set.
+ **/
+gchar*
+g_convert_with_fallback (const gchar *str,
+ gssize len,
+ const gchar *to_codeset,
+ const gchar *from_codeset,
+ const gchar *fallback,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ gchar *utf8;
+ gchar *dest;
+ gchar *outp;
+ const gchar *insert_str = NULL;
+ const gchar *p;
+ gsize inbytes_remaining;
+ const gchar *save_p = NULL;
+ gsize save_inbytes = 0;
+ gsize outbytes_remaining;
+ gsize err;
+ GIConv cd;
+ gsize outbuf_size;
+ gboolean have_error = FALSE;
+ gboolean done = FALSE;
+
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (str != NULL, NULL);
+ g_return_val_if_fail (to_codeset != NULL, NULL);
+ g_return_val_if_fail (from_codeset != NULL, NULL);
+
+ if (len < 0)
+ len = strlen (str);
+
+ /* Try an exact conversion; we only proceed if this fails
+ * due to an illegal sequence in the input string.
+ */
+ dest = g_convert (str, len, to_codeset, from_codeset,
+ bytes_read, bytes_written, &local_error);
+ if (!local_error)
+ return dest;
+
+ if (!g_error_matches (local_error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+ {
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+ else
+ g_error_free (local_error);
+
+ local_error = NULL;
+
+ /* No go; to proceed, we need a converter from "UTF-8" to
+ * to_codeset, and the string as UTF-8.
+ */
+ cd = open_converter (to_codeset, "UTF-8", error);
+ if (cd == (GIConv) -1)
+ {
+ if (bytes_read)
+ *bytes_read = 0;
+
+ if (bytes_written)
+ *bytes_written = 0;
+
+ return NULL;
+ }
+
+ utf8 = g_convert (str, len, "UTF-8", from_codeset,
+ bytes_read, &inbytes_remaining, error);
+ if (!utf8)
+ {
+ close_converter (cd);
+ if (bytes_written)
+ *bytes_written = 0;
+ return NULL;
+ }
+
+ /* Now the heart of the code. We loop through the UTF-8 string, and
+ * whenever we hit an offending character, we form fallback, convert
+ * the fallback to the target codeset, and then go back to
+ * converting the original string after finishing with the fallback.
+ *
+ * The variables save_p and save_inbytes store the input state
+ * for the original string while we are converting the fallback
+ */
+ p = utf8;
+
+ outbuf_size = len + NUL_TERMINATOR_LENGTH;
+ outbytes_remaining = outbuf_size - NUL_TERMINATOR_LENGTH;
+ outp = dest = g_malloc (outbuf_size);
+
+ while (!done && !have_error)
+ {
+ gsize inbytes_tmp = inbytes_remaining;
+ err = g_iconv (cd, (char **)&p, &inbytes_tmp, &outp, &outbytes_remaining);
+ inbytes_remaining = inbytes_tmp;
+
+ if (err == (gsize) -1)
+ {
+ switch (errno)
+ {
+ case EINVAL:
+ g_assert_not_reached();
+ break;
+ case E2BIG:
+ {
+ gsize used = outp - dest;
+
+ outbuf_size *= 2;
+ dest = g_realloc (dest, outbuf_size);
+
+ outp = dest + used;
+ outbytes_remaining = outbuf_size - used - NUL_TERMINATOR_LENGTH;
+
+ break;
+ }
+ case EILSEQ:
+ if (save_p)
+ {
+ /* Error converting fallback string - fatal
+ */
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Cannot convert fallback “%s” to codeset “%s”"),
+ insert_str, to_codeset);
+ have_error = TRUE;
+ break;
+ }
+ else if (p)
+ {
+ if (!fallback)
+ {
+ gunichar ch = g_utf8_get_char (p);
+ insert_str = g_strdup_printf (ch < 0x10000 ? "\\u%04x" : "\\U%08x",
+ ch);
+ }
+ else
+ insert_str = fallback;
+
+ save_p = g_utf8_next_char (p);
+ save_inbytes = inbytes_remaining - (save_p - p);
+ p = insert_str;
+ inbytes_remaining = strlen (p);
+ break;
+ }
+ /* if p is null */
+ G_GNUC_FALLTHROUGH;
+ default:
+ {
+ int errsv = errno;
+
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
+ _("Error during conversion: %s"),
+ g_strerror (errsv));
+ }
+
+ have_error = TRUE;
+ break;
+ }
+ }
+ else
+ {
+ if (save_p)
+ {
+ if (!fallback)
+ g_free ((gchar *)insert_str);
+ p = save_p;
+ inbytes_remaining = save_inbytes;
+ save_p = NULL;
+ }
+ else if (p)
+ {
+ /* call g_iconv with NULL inbuf to cleanup shift state */
+ p = NULL;
+ inbytes_remaining = 0;
+ }
+ else
+ done = TRUE;
+ }
+ }
+
+ /* Cleanup
+ */
+ memset (outp, 0, NUL_TERMINATOR_LENGTH);
+
+ close_converter (cd);
+
+ if (bytes_written)
+ *bytes_written = outp - dest; /* Doesn't include '\0' */
+
+ g_free (utf8);
+
+ if (have_error)
+ {
+ if (save_p && !fallback)
+ g_free ((gchar *)insert_str);
+ g_free (dest);
+ return NULL;
+ }
+ else
+ return dest;
+}
+
+/*
+ * g_locale_to_utf8
+ *
+ *
+ */
+
+/*
+ * Validate @string as UTF-8. @len can be negative if @string is
+ * nul-terminated, or a non-negative value in bytes. If @string ends in an
+ * incomplete sequence, or contains any illegal sequences or nul codepoints,
+ * %NULL will be returned and the error set to
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE.
+ * On success, @bytes_read and @bytes_written, if provided, will be set to
+ * the number of bytes in @string up to @len or the terminating nul byte.
+ * On error, @bytes_read will be set to the byte offset after the last valid
+ * and non-nul UTF-8 sequence in @string, and @bytes_written will be set to 0.
+ */
+static gchar *
+strdup_len (const gchar *string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ gsize real_len;
+ const gchar *end_valid;
+
+ if (!g_utf8_validate (string, len, &end_valid))
+ {
+ if (bytes_read)
+ *bytes_read = end_valid - string;
+ if (bytes_written)
+ *bytes_written = 0;
+
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid byte sequence in conversion input"));
+ return NULL;
+ }
+
+ real_len = end_valid - string;
+
+ if (bytes_read)
+ *bytes_read = real_len;
+ if (bytes_written)
+ *bytes_written = real_len;
+
+ return g_strndup (string, real_len);
+}
+
+typedef enum
+{
+ CONVERT_CHECK_NO_NULS_IN_INPUT = 1 << 0,
+ CONVERT_CHECK_NO_NULS_IN_OUTPUT = 1 << 1
+} ConvertCheckFlags;
+
+/*
+ * Convert from @string in the encoding identified by @from_codeset,
+ * returning a string in the encoding identifed by @to_codeset.
+ * @len can be negative if @string is nul-terminated, or a non-negative
+ * value in bytes. Flags defined in #ConvertCheckFlags can be set in @flags
+ * to check the input, the output, or both, for embedded nul bytes.
+ * On success, @bytes_read, if provided, will be set to the number of bytes
+ * in @string up to @len or the terminating nul byte, and @bytes_written, if
+ * provided, will be set to the number of output bytes written into the
+ * returned buffer, excluding the terminating nul sequence.
+ * On error, @bytes_read will be set to the byte offset after the last valid
+ * sequence in @string, and @bytes_written will be set to 0.
+ */
+static gchar *
+convert_checked (const gchar *string,
+ gssize len,
+ const gchar *to_codeset,
+ const gchar *from_codeset,
+ ConvertCheckFlags flags,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ gchar *out;
+ gsize outbytes;
+
+ if ((flags & CONVERT_CHECK_NO_NULS_IN_INPUT) && len > 0)
+ {
+ const gchar *early_nul = memchr (string, '\0', len);
+ if (early_nul != NULL)
+ {
+ if (bytes_read)
+ *bytes_read = early_nul - string;
+ if (bytes_written)
+ *bytes_written = 0;
+
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Embedded NUL byte in conversion input"));
+ return NULL;
+ }
+ }
+
+ out = g_convert (string, len, to_codeset, from_codeset,
+ bytes_read, &outbytes, error);
+ if (out == NULL)
+ {
+ if (bytes_written)
+ *bytes_written = 0;
+ return NULL;
+ }
+
+ if ((flags & CONVERT_CHECK_NO_NULS_IN_OUTPUT)
+ && memchr (out, '\0', outbytes) != NULL)
+ {
+ g_free (out);
+ if (bytes_written)
+ *bytes_written = 0;
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_EMBEDDED_NUL,
+ _("Embedded NUL byte in conversion output"));
+ return NULL;
+ }
+
+ if (bytes_written)
+ *bytes_written = outbytes;
+ return out;
+}
+
+/**
+ * g_locale_to_utf8:
+ * @opsysstring: (array length=len) (element-type guint8): a string in the
+ * encoding of the current locale. On Windows
+ * this means the system codepage.
+ * @len: the length of the string, or -1 if the string is
+ * nul-terminated (Note that some encodings may allow nul
+ * bytes to occur inside strings. In that case, using -1
+ * for the @len parameter is unsafe)
+ * @bytes_read: (out) (optional): location to store the number of bytes in the
+ * input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input. If the error
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ * stored will be the byte offset after the last valid
+ * input sequence.
+ * @bytes_written: (out) (optional): the number of bytes stored in the output
+ * buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string which is in the encoding used for strings by
+ * the C runtime (usually the same as that used by the operating
+ * system) in the [current locale][setlocale] into a UTF-8 string.
+ *
+ * If the source encoding is not UTF-8 and the conversion output contains a
+ * nul character, the error %G_CONVERT_ERROR_EMBEDDED_NUL is set and the
+ * function returns %NULL.
+ * If the source encoding is UTF-8, an embedded nul character is treated with
+ * the %G_CONVERT_ERROR_ILLEGAL_SEQUENCE error for backward compatibility with
+ * earlier versions of this library. Use g_convert() to produce output that
+ * may contain embedded nul characters.
+ *
+ * Returns: (type utf8): The converted string, or %NULL on an error.
+ **/
+gchar *
+g_locale_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ const char *charset;
+
+ if (g_get_charset (&charset))
+ return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
+ else
+ return convert_checked (opsysstring, len, "UTF-8", charset,
+ CONVERT_CHECK_NO_NULS_IN_OUTPUT,
+ bytes_read, bytes_written, error);
+}
+
+/*
+ * Do the exact same as g_locale_to_utf8 except that the charset would
+ * be retrieved from _g_get_time_charset (which uses LC_TIME)
+ *
+ * Returns: The converted string, or %NULL on an error.
+ */
+gchar *
+_g_time_locale_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ const char *charset;
+
+ if (_g_get_time_charset (&charset))
+ return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
+ else
+ return convert_checked (opsysstring, len, "UTF-8", charset,
+ CONVERT_CHECK_NO_NULS_IN_OUTPUT,
+ bytes_read, bytes_written, error);
+}
+
+/*
+ * Do the exact same as g_locale_to_utf8 except that the charset would
+ * be retrieved from _g_get_ctype_charset (which uses LC_CTYPE)
+ *
+ * Returns: The converted string, or %NULL on an error.
+ */
+gchar *
+_g_ctype_locale_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ const char *charset;
+
+ if (_g_get_ctype_charset (&charset))
+ return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
+ else
+ return convert_checked (opsysstring, len, "UTF-8", charset,
+ CONVERT_CHECK_NO_NULS_IN_OUTPUT,
+ bytes_read, bytes_written, error);
+}
+
+/**
+ * g_locale_from_utf8:
+ * @utf8string: a UTF-8 encoded string
+ * @len: the length of the string, or -1 if the string is
+ * nul-terminated.
+ * @bytes_read: (out) (optional): location to store the number of bytes in the
+ * input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input. If the error
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ * stored will be the byte offset after the last valid
+ * input sequence.
+ * @bytes_written: (out) (optional): the number of bytes stored in the output
+ * buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from UTF-8 to the encoding used for strings by
+ * the C runtime (usually the same as that used by the operating
+ * system) in the [current locale][setlocale]. On Windows this means
+ * the system codepage.
+ *
+ * The input string shall not contain nul characters even if the @len
+ * argument is positive. A nul character found inside the string will result
+ * in error %G_CONVERT_ERROR_ILLEGAL_SEQUENCE. Use g_convert() to convert
+ * input that may contain embedded nul characters.
+ *
+ * Returns: (array length=bytes_written) (element-type guint8) (transfer full):
+ * A newly-allocated buffer containing the converted string,
+ * or %NULL on an error, and error will be set.
+ **/
+gchar *
+g_locale_from_utf8 (const gchar *utf8string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ const gchar *charset;
+
+ if (g_get_charset (&charset))
+ return strdup_len (utf8string, len, bytes_read, bytes_written, error);
+ else
+ return convert_checked (utf8string, len, charset, "UTF-8",
+ CONVERT_CHECK_NO_NULS_IN_INPUT,
+ bytes_read, bytes_written, error);
+}
+
+#ifndef G_PLATFORM_WIN32
+
+typedef struct _GFilenameCharsetCache GFilenameCharsetCache;
+
+struct _GFilenameCharsetCache {
+ gboolean is_utf8;
+ gchar *charset;
+ gchar **filename_charsets;
+};
+
+static void
+filename_charset_cache_free (gpointer data)
+{
+ GFilenameCharsetCache *cache = data;
+ g_free (cache->charset);
+ g_strfreev (cache->filename_charsets);
+ g_free (cache);
+}
+
+/**
+ * g_get_filename_charsets:
+ * @filename_charsets: (out) (transfer none) (array zero-terminated=1):
+ * return location for the %NULL-terminated list of encoding names
+ *
+ * Determines the preferred character sets used for filenames.
+ * The first character set from the @charsets is the filename encoding, the
+ * subsequent character sets are used when trying to generate a displayable
+ * representation of a filename, see g_filename_display_name().
+ *
+ * On Unix, the character sets are determined by consulting the
+ * environment variables `G_FILENAME_ENCODING` and `G_BROKEN_FILENAMES`.
+ * On Windows, the character set used in the GLib API is always UTF-8
+ * and said environment variables have no effect.
+ *
+ * `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][setlocale].
+ * 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. If neither environment variable is set, UTF-8 is taken
+ * as the filename encoding, but the character set of the current locale
+ * is also put in the list of encodings.
+ *
+ * The returned @charsets belong to GLib and must not be freed.
+ *
+ * Note that on Unix, regardless of the locale character set or
+ * `G_FILENAME_ENCODING` value, the actual file names present
+ * on a system might be in any random encoding or just gibberish.
+ *
+ * Returns: %TRUE if the filename encoding is UTF-8.
+ *
+ * Since: 2.6
+ */
+gboolean
+g_get_filename_charsets (const gchar ***filename_charsets)
+{
+ static GPrivate cache_private = G_PRIVATE_INIT (filename_charset_cache_free);
+ GFilenameCharsetCache *cache = g_private_get (&cache_private);
+ const gchar *charset;
+
+ if (!cache)
+ cache = g_private_set_alloc0 (&cache_private, sizeof (GFilenameCharsetCache));
+
+ g_get_charset (&charset);
+
+ if (!(cache->charset && strcmp (cache->charset, charset) == 0))
+ {
+ const gchar *new_charset;
+ const gchar *p;
+ gint i;
+
+ g_free (cache->charset);
+ g_strfreev (cache->filename_charsets);
+ cache->charset = g_strdup (charset);
+
+ p = g_getenv ("G_FILENAME_ENCODING");
+ if (p != NULL && p[0] != '\0')
+ {
+ cache->filename_charsets = g_strsplit (p, ",", 0);
+ cache->is_utf8 = (strcmp (cache->filename_charsets[0], "UTF-8") == 0);
+
+ for (i = 0; cache->filename_charsets[i]; i++)
+ {
+ if (strcmp ("@locale", cache->filename_charsets[i]) == 0)
+ {
+ g_get_charset (&new_charset);
+ g_free (cache->filename_charsets[i]);
+ cache->filename_charsets[i] = g_strdup (new_charset);
+ }
+ }
+ }
+ else if (g_getenv ("G_BROKEN_FILENAMES") != NULL)
+ {
+ cache->filename_charsets = g_new0 (gchar *, 2);
+ cache->is_utf8 = g_get_charset (&new_charset);
+ cache->filename_charsets[0] = g_strdup (new_charset);
+ }
+ else
+ {
+ cache->filename_charsets = g_new0 (gchar *, 3);
+ cache->is_utf8 = TRUE;
+ cache->filename_charsets[0] = g_strdup ("UTF-8");
+ if (!g_get_charset (&new_charset))
+ cache->filename_charsets[1] = g_strdup (new_charset);
+ }
+ }
+
+ if (filename_charsets)
+ *filename_charsets = (const gchar **)cache->filename_charsets;
+
+ return cache->is_utf8;
+}
+
+#else /* G_PLATFORM_WIN32 */
+
+gboolean
+g_get_filename_charsets (const gchar ***filename_charsets)
+{
+ static const gchar *charsets[] = {
+ "UTF-8",
+ NULL
+ };
+
+#ifdef G_OS_WIN32
+ /* On Windows GLib pretends that the filename charset is UTF-8 */
+ if (filename_charsets)
+ *filename_charsets = charsets;
+
+ return TRUE;
+#else
+ gboolean result;
+
+ /* Cygwin works like before */
+ result = g_get_charset (&(charsets[0]));
+
+ if (filename_charsets)
+ *filename_charsets = charsets;
+
+ return result;
+#endif
+}
+
+#endif /* G_PLATFORM_WIN32 */
+
+static gboolean
+get_filename_charset (const gchar **filename_charset)
+{
+ const gchar **charsets;
+ gboolean is_utf8;
+
+ is_utf8 = g_get_filename_charsets (&charsets);
+
+ if (filename_charset)
+ *filename_charset = charsets[0];
+
+ return is_utf8;
+}
+
+/**
+ * g_filename_to_utf8:
+ * @opsysstring: (type filename): a string in the encoding for filenames
+ * @len: the length of the string, or -1 if the string is
+ * nul-terminated (Note that some encodings may allow nul
+ * bytes to occur inside strings. In that case, using -1
+ * for the @len parameter is unsafe)
+ * @bytes_read: (out) (optional): location to store the number of bytes in the
+ * input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input. If the error
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ * stored will be the byte offset after the last valid
+ * input sequence.
+ * @bytes_written: (out) (optional): the number of bytes stored in the output
+ * buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string which is in the encoding used by GLib for
+ * filenames into a UTF-8 string. Note that on Windows GLib uses UTF-8
+ * for filenames; on other platforms, this function indirectly depends on
+ * the [current locale][setlocale].
+ *
+ * The input string shall not contain nul characters even if the @len
+ * argument is positive. A nul character found inside the string will result
+ * in error %G_CONVERT_ERROR_ILLEGAL_SEQUENCE.
+ * If the source encoding is not UTF-8 and the conversion output contains a
+ * nul character, the error %G_CONVERT_ERROR_EMBEDDED_NUL is set and the
+ * function returns %NULL. Use g_convert() to produce output that
+ * may contain embedded nul characters.
+ *
+ * Returns: (type utf8): The converted string, or %NULL on an error.
+ **/
+gchar*
+g_filename_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ const gchar *charset;
+
+ g_return_val_if_fail (opsysstring != NULL, NULL);
+
+ if (get_filename_charset (&charset))
+ return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
+ else
+ return convert_checked (opsysstring, len, "UTF-8", charset,
+ CONVERT_CHECK_NO_NULS_IN_INPUT |
+ CONVERT_CHECK_NO_NULS_IN_OUTPUT,
+ bytes_read, bytes_written, error);
+}
+
+/**
+ * g_filename_from_utf8:
+ * @utf8string: (type utf8): a UTF-8 encoded string.
+ * @len: the length of the string, or -1 if the string is
+ * nul-terminated.
+ * @bytes_read: (out) (optional): location to store the number of bytes in
+ * the input string that were successfully converted, or %NULL.
+ * Even if the conversion was successful, this may be
+ * less than @len if there were partial characters
+ * at the end of the input. If the error
+ * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ * stored will be the byte offset after the last valid
+ * input sequence.
+ * @bytes_written: (out) (optional): the number of bytes stored in
+ * the output buffer (not including the terminating nul).
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from UTF-8 to the encoding GLib uses for
+ * filenames. Note that on Windows GLib uses UTF-8 for filenames;
+ * on other platforms, this function indirectly depends on the
+ * [current locale][setlocale].
+ *
+ * The input string shall not contain nul characters even if the @len
+ * argument is positive. A nul character found inside the string will result
+ * in error %G_CONVERT_ERROR_ILLEGAL_SEQUENCE. If the filename encoding is
+ * not UTF-8 and the conversion output contains a nul character, the error
+ * %G_CONVERT_ERROR_EMBEDDED_NUL is set and the function returns %NULL.
+ *
+ * Returns: (type filename):
+ * The converted string, or %NULL on an error.
+ **/
+gchar*
+g_filename_from_utf8 (const gchar *utf8string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ const gchar *charset;
+
+ if (get_filename_charset (&charset))
+ return strdup_len (utf8string, len, bytes_read, bytes_written, error);
+ else
+ return convert_checked (utf8string, len, charset, "UTF-8",
+ CONVERT_CHECK_NO_NULS_IN_INPUT |
+ CONVERT_CHECK_NO_NULS_IN_OUTPUT,
+ bytes_read, bytes_written, error);
+}
+
+/* Test of haystack has the needle prefix, comparing case
+ * insensitive. haystack may be UTF-8, but needle must
+ * contain only ascii. */
+static gboolean
+has_case_prefix (const gchar *haystack, const gchar *needle)
+{
+ const gchar *h, *n;
+
+ /* Eat one character at a time. */
+ h = haystack;
+ n = needle;
+
+ while (*n && *h &&
+ g_ascii_tolower (*n) == g_ascii_tolower (*h))
+ {
+ n++;
+ h++;
+ }
+
+ return *n == '\0';
+}
+
+typedef enum {
+ UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
+ UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
+ UNSAFE_PATH = 0x8, /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
+ UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
+ UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
+} UnsafeCharacterSet;
+
+static const guchar acceptable[96] = {
+ /* A table of the ASCII chars from space (32) to DEL (127) */
+ /* ! " # $ % & ' ( ) * + , - . / */
+ 0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
+ /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
+ /* @ A B C D E F G H I J K L M N O */
+ 0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
+ /* P Q R S T U V W X Y Z [ \ ] ^ _ */
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
+ /* ` a b c d e f g h i j k l m n o */
+ 0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
+ /* p q r s t u v w x y z { | } ~ DEL */
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
+};
+
+static const gchar hex[] = "0123456789ABCDEF";
+
+/* Note: This escape function works on file: URIs, but if you want to
+ * escape something else, please read RFC-2396 */
+static gchar *
+g_escape_uri_string (const gchar *string,
+ UnsafeCharacterSet mask)
+{
+#define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
+
+ const gchar *p;
+ gchar *q;
+ gchar *result;
+ int c;
+ gint unacceptable;
+ UnsafeCharacterSet use_mask;
+
+ g_return_val_if_fail (mask == UNSAFE_ALL
+ || mask == UNSAFE_ALLOW_PLUS
+ || mask == UNSAFE_PATH
+ || mask == UNSAFE_HOST
+ || mask == UNSAFE_SLASHES, NULL);
+
+ unacceptable = 0;
+ use_mask = mask;
+ for (p = string; *p != '\0'; p++)
+ {
+ c = (guchar) *p;
+ if (!ACCEPTABLE (c))
+ unacceptable++;
+ }
+
+ result = g_malloc (p - string + unacceptable * 2 + 1);
+
+ use_mask = mask;
+ for (q = result, p = string; *p != '\0'; p++)
+ {
+ c = (guchar) *p;
+
+ if (!ACCEPTABLE (c))
+ {
+ *q++ = '%'; /* means hex coming */
+ *q++ = hex[c >> 4];
+ *q++ = hex[c & 15];
+ }
+ else
+ *q++ = *p;
+ }
+
+ *q = '\0';
+
+ return result;
+}
+
+
+static gchar *
+g_escape_file_uri (const gchar *hostname,
+ const gchar *pathname)
+{
+ char *escaped_hostname = NULL;
+ char *escaped_path;
+ char *res;
+
+#ifdef G_OS_WIN32
+ char *p, *backslash;
+
+ /* Turn backslashes into forward slashes. That's what Netscape
+ * does, and they are actually more or less equivalent in Windows.
+ */
+
+ pathname = g_strdup (pathname);
+ p = (char *) pathname;
+
+ while ((backslash = strchr (p, '\\')) != NULL)
+ {
+ *backslash = '/';
+ p = backslash + 1;
+ }
+#endif
+
+ if (hostname && *hostname != '\0')
+ {
+ escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST);
+ }
+
+ escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH);
+
+ res = g_strconcat ("file://",
+ (escaped_hostname) ? escaped_hostname : "",
+ (*escaped_path != '/') ? "/" : "",
+ escaped_path,
+ NULL);
+
+#ifdef G_OS_WIN32
+ g_free ((char *) pathname);
+#endif
+
+ g_free (escaped_hostname);
+ g_free (escaped_path);
+
+ return res;
+}
+
+static int
+unescape_character (const char *scanner)
+{
+ int first_digit;
+ int second_digit;
+
+ first_digit = g_ascii_xdigit_value (scanner[0]);
+ if (first_digit < 0)
+ return -1;
+
+ second_digit = g_ascii_xdigit_value (scanner[1]);
+ if (second_digit < 0)
+ return -1;
+
+ return (first_digit << 4) | second_digit;
+}
+
+static gchar *
+g_unescape_uri_string (const char *escaped,
+ int len,
+ const char *illegal_escaped_characters,
+ gboolean ascii_must_not_be_escaped)
+{
+ const gchar *in, *in_end;
+ gchar *out, *result;
+ int c;
+
+ if (escaped == NULL)
+ return NULL;
+
+ if (len < 0)
+ len = strlen (escaped);
+
+ result = g_malloc (len + 1);
+
+ out = result;
+ for (in = escaped, in_end = escaped + len; in < in_end; in++)
+ {
+ c = *in;
+
+ if (c == '%')
+ {
+ /* catch partial escape sequences past the end of the substring */
+ if (in + 3 > in_end)
+ break;
+
+ c = unescape_character (in + 1);
+
+ /* catch bad escape sequences and NUL characters */
+ if (c <= 0)
+ break;
+
+ /* catch escaped ASCII */
+ if (ascii_must_not_be_escaped && c <= 0x7F)
+ break;
+
+ /* catch other illegal escaped characters */
+ if (strchr (illegal_escaped_characters, c) != NULL)
+ break;
+
+ in += 2;
+ }
+
+ *out++ = c;
+ }
+
+ g_assert (out - result <= len);
+ *out = '\0';
+
+ if (in != in_end)
+ {
+ g_free (result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static gboolean
+is_asciialphanum (gunichar c)
+{
+ return c <= 0x7F && g_ascii_isalnum (c);
+}
+
+static gboolean
+is_asciialpha (gunichar c)
+{
+ return c <= 0x7F && g_ascii_isalpha (c);
+}
+
+/* allows an empty string */
+static gboolean
+hostname_validate (const char *hostname)
+{
+ const char *p;
+ gunichar c, first_char, last_char;
+
+ p = hostname;
+ if (*p == '\0')
+ return TRUE;
+ do
+ {
+ /* read in a label */
+ c = g_utf8_get_char (p);
+ p = g_utf8_next_char (p);
+ if (!is_asciialphanum (c))
+ return FALSE;
+ first_char = c;
+ do
+ {
+ last_char = c;
+ c = g_utf8_get_char (p);
+ p = g_utf8_next_char (p);
+ }
+ while (is_asciialphanum (c) || c == '-');
+ if (last_char == '-')
+ return FALSE;
+
+ /* if that was the last label, check that it was a toplabel */
+ if (c == '\0' || (c == '.' && *p == '\0'))
+ return is_asciialpha (first_char);
+ }
+ while (c == '.');
+ return FALSE;
+}
+
+/**
+ * g_filename_from_uri:
+ * @uri: a uri describing a filename (escaped, encoded in ASCII).
+ * @hostname: (out) (optional) (nullable): Location to store hostname for the URI.
+ * If there is no hostname in the URI, %NULL will be
+ * stored in this location.
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts an escaped ASCII-encoded URI to a local filename in the
+ * encoding used for filenames.
+ *
+ * Returns: (type filename): a newly-allocated string holding
+ * the resulting filename, or %NULL on an error.
+ **/
+gchar *
+g_filename_from_uri (const gchar *uri,
+ gchar **hostname,
+ GError **error)
+{
+ const char *path_part;
+ const char *host_part;
+ char *unescaped_hostname;
+ char *result;
+ char *filename;
+ int offs;
+#ifdef G_OS_WIN32
+ char *p, *slash;
+#endif
+
+ if (hostname)
+ *hostname = NULL;
+
+ if (!has_case_prefix (uri, "file:/"))
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
+ _("The URI “%s” is not an absolute URI using the “file” scheme"),
+ uri);
+ return NULL;
+ }
+
+ path_part = uri + strlen ("file:");
+
+ if (strchr (path_part, '#') != NULL)
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
+ _("The local file URI “%s” may not include a “#”"),
+ uri);
+ return NULL;
+ }
+
+ if (has_case_prefix (path_part, "///"))
+ path_part += 2;
+ else if (has_case_prefix (path_part, "//"))
+ {
+ path_part += 2;
+ host_part = path_part;
+
+ path_part = strchr (path_part, '/');
+
+ if (path_part == NULL)
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
+ _("The URI “%s” is invalid"),
+ uri);
+ return NULL;
+ }
+
+ unescaped_hostname = g_unescape_uri_string (host_part, path_part - host_part, "", TRUE);
+
+ if (unescaped_hostname == NULL ||
+ !hostname_validate (unescaped_hostname))
+ {
+ g_free (unescaped_hostname);
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
+ _("The hostname of the URI “%s” is invalid"),
+ uri);
+ return NULL;
+ }
+
+ if (hostname)
+ *hostname = unescaped_hostname;
+ else
+ g_free (unescaped_hostname);
+ }
+
+ filename = g_unescape_uri_string (path_part, -1, "/", FALSE);
+
+ if (filename == NULL)
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
+ _("The URI “%s” contains invalidly escaped characters"),
+ uri);
+ return NULL;
+ }
+
+ offs = 0;
+#ifdef G_OS_WIN32
+ /* Drop localhost */
+ if (hostname && *hostname != NULL &&
+ g_ascii_strcasecmp (*hostname, "localhost") == 0)
+ {
+ g_free (*hostname);
+ *hostname = NULL;
+ }
+
+ /* Turn slashes into backslashes, because that's the canonical spelling */
+ p = filename;
+ while ((slash = strchr (p, '/')) != NULL)
+ {
+ *slash = '\\';
+ p = slash + 1;
+ }
+
+ /* Windows URIs with a drive letter can be like "file://host/c:/foo"
+ * or "file://host/c|/foo" (some Netscape versions). In those cases, start
+ * the filename from the drive letter.
+ */
+ if (g_ascii_isalpha (filename[1]))
+ {
+ if (filename[2] == ':')
+ offs = 1;
+ else if (filename[2] == '|')
+ {
+ filename[2] = ':';
+ offs = 1;
+ }
+ }
+#endif
+
+ result = g_strdup (filename + offs);
+ g_free (filename);
+
+ return result;
+}
+
+/**
+ * g_filename_to_uri:
+ * @filename: (type filename): an absolute filename specified in the GLib file
+ * name encoding, which is the on-disk file name bytes on Unix, and UTF-8
+ * on Windows
+ * @hostname: (nullable): A UTF-8 encoded hostname, or %NULL for none.
+ * @error: location to store the error occurring, or %NULL to ignore
+ * errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts an absolute filename to an escaped ASCII-encoded URI, with the path
+ * component following Section 3.3. of RFC 2396.
+ *
+ * Returns: a newly-allocated string holding the resulting
+ * URI, or %NULL on an error.
+ **/
+gchar *
+g_filename_to_uri (const gchar *filename,
+ const gchar *hostname,
+ GError **error)
+{
+ char *escaped_uri;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ if (!g_path_is_absolute (filename))
+ {
+ g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH,
+ _("The pathname “%s” is not an absolute path"),
+ filename);
+ return NULL;
+ }
+
+ if (hostname &&
+ !(g_utf8_validate (hostname, -1, NULL)
+ && hostname_validate (hostname)))
+ {
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ _("Invalid hostname"));
+ return NULL;
+ }
+
+#ifdef G_OS_WIN32
+ /* Don't use localhost unnecessarily */
+ if (hostname && g_ascii_strcasecmp (hostname, "localhost") == 0)
+ hostname = NULL;
+#endif
+
+ escaped_uri = g_escape_file_uri (hostname, filename);
+
+ return escaped_uri;
+}
+
+/**
+ * g_uri_list_extract_uris:
+ * @uri_list: an URI list
+ *
+ * Splits an URI list conforming to the text/uri-list
+ * mime type defined in RFC 2483 into individual URIs,
+ * discarding any comments. The URIs are not validated.
+ *
+ * Returns: (transfer full): a newly allocated %NULL-terminated list
+ * of strings holding the individual URIs. The array should be freed
+ * with g_strfreev().
+ *
+ * Since: 2.6
+ */
+gchar **
+g_uri_list_extract_uris (const gchar *uri_list)
+{
+ GPtrArray *uris;
+ const gchar *p, *q;
+
+ uris = g_ptr_array_new ();
+
+ p = uri_list;
+
+ /* We don't actually try to validate the URI according to RFC
+ * 2396, or even check for allowed characters - we just ignore
+ * comments and trim whitespace off the ends. We also
+ * allow LF delimination as well as the specified CRLF.
+ *
+ * We do allow comments like specified in RFC 2483.
+ */
+ while (p)
+ {
+ if (*p != '#')
+ {
+ while (g_ascii_isspace (*p))
+ p++;
+
+ q = p;
+ while (*q && (*q != '\n') && (*q != '\r'))
+ q++;
+
+ if (q > p)
+ {
+ q--;
+ while (q > p && g_ascii_isspace (*q))
+ q--;
+
+ if (q > p)
+ g_ptr_array_add (uris, g_strndup (p, q - p + 1));
+ }
+ }
+ p = strchr (p, '\n');
+ if (p)
+ p++;
+ }
+
+ g_ptr_array_add (uris, NULL);
+
+ return (gchar **) g_ptr_array_free (uris, FALSE);
+}
+
+/**
+ * g_filename_display_basename:
+ * @filename: (type filename): an absolute pathname in the
+ * GLib file name encoding
+ *
+ * Returns the display basename for the particular filename, guaranteed
+ * to be valid UTF-8. The display name might not be identical to the filename,
+ * for instance there might be problems converting it to UTF-8, and some files
+ * can be translated in the display.
+ *
+ * If GLib cannot make sense of the encoding of @filename, as a last resort it
+ * replaces unknown characters with U+FFFD, the Unicode replacement character.
+ * You can search the result for the UTF-8 encoding of this character (which is
+ * "\357\277\275" in octal notation) to find out if @filename was in an invalid
+ * encoding.
+ *
+ * You must pass the whole absolute pathname to this functions so that
+ * translation of well known locations can be done.
+ *
+ * This function is preferred over g_filename_display_name() if you know the
+ * whole path, as it allows translation.
+ *
+ * Returns: a newly allocated string containing
+ * a rendition of the basename of the filename in valid UTF-8
+ *
+ * Since: 2.6
+ **/
+gchar *
+g_filename_display_basename (const gchar *filename)
+{
+ char *basename;
+ char *display_name;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ basename = g_path_get_basename (filename);
+ display_name = g_filename_display_name (basename);
+ g_free (basename);
+ return display_name;
+}
+
+/**
+ * g_filename_display_name:
+ * @filename: (type filename): a pathname hopefully in the
+ * GLib file name encoding
+ *
+ * Converts a filename into a valid UTF-8 string. The conversion is
+ * not necessarily reversible, so you should keep the original around
+ * and use the return value of this function only for display purposes.
+ * Unlike g_filename_to_utf8(), the result is guaranteed to be non-%NULL
+ * even if the filename actually isn't in the GLib file name encoding.
+ *
+ * If GLib cannot make sense of the encoding of @filename, as a last resort it
+ * replaces unknown characters with U+FFFD, the Unicode replacement character.
+ * You can search the result for the UTF-8 encoding of this character (which is
+ * "\357\277\275" in octal notation) to find out if @filename was in an invalid
+ * encoding.
+ *
+ * If you know the whole pathname of the file you should use
+ * g_filename_display_basename(), since that allows location-based
+ * translation of filenames.
+ *
+ * Returns: a newly allocated string containing
+ * a rendition of the filename in valid UTF-8
+ *
+ * Since: 2.6
+ **/
+gchar *
+g_filename_display_name (const gchar *filename)
+{
+ gint i;
+ const gchar **charsets;
+ gchar *display_name = NULL;
+ gboolean is_utf8;
+
+ is_utf8 = g_get_filename_charsets (&charsets);
+
+ if (is_utf8)
+ {
+ if (g_utf8_validate (filename, -1, NULL))
+ display_name = g_strdup (filename);
+ }
+
+ if (!display_name)
+ {
+ /* Try to convert from the filename charsets to UTF-8.
+ * Skip the first charset if it is UTF-8.
+ */
+ for (i = is_utf8 ? 1 : 0; charsets[i]; i++)
+ {
+ display_name = g_convert (filename, -1, "UTF-8", charsets[i],
+ NULL, NULL, NULL);
+
+ if (display_name)
+ break;
+ }
+ }
+
+ /* if all conversions failed, we replace invalid UTF-8
+ * by a question mark
+ */
+ if (!display_name)
+ display_name = g_utf8_make_valid (filename, -1);
+
+ return display_name;
+}
+
+#ifdef G_OS_WIN32
+
+/* Binary compatibility versions. Not for newly compiled code. */
+
+_GLIB_EXTERN gchar *g_filename_to_utf8_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+_GLIB_EXTERN gchar *g_filename_from_utf8_utf8 (const gchar *utf8string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+_GLIB_EXTERN gchar *g_filename_from_uri_utf8 (const gchar *uri,
+ gchar **hostname,
+ GError **error) G_GNUC_MALLOC;
+_GLIB_EXTERN gchar *g_filename_to_uri_utf8 (const gchar *filename,
+ const gchar *hostname,
+ GError **error) G_GNUC_MALLOC;
+
+gchar *
+g_filename_to_utf8_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ return g_filename_to_utf8 (opsysstring, len, bytes_read, bytes_written, error);
+}
+
+gchar *
+g_filename_from_utf8_utf8 (const gchar *utf8string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ return g_filename_from_utf8 (utf8string, len, bytes_read, bytes_written, error);
+}
+
+gchar *
+g_filename_from_uri_utf8 (const gchar *uri,
+ gchar **hostname,
+ GError **error)
+{
+ return g_filename_from_uri (uri, hostname, error);
+}
+
+gchar *
+g_filename_to_uri_utf8 (const gchar *filename,
+ const gchar *hostname,
+ GError **error)
+{
+ return g_filename_to_uri (filename, hostname, error);
+}
+
+#endif
diff --git a/glib/gconvert.h b/glib/gconvert.h
new file mode 100644
index 0000000000000000000000000000000000000000..be58ecfbe1810eca59d841845e6bd55250da68ce
--- /dev/null
+++ b/glib/gconvert.h
@@ -0,0 +1,177 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_CONVERT_H__
+#define __G_CONVERT_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+/**
+ * GConvertError:
+ * @G_CONVERT_ERROR_NO_CONVERSION: Conversion between the requested character
+ * sets is not supported.
+ * @G_CONVERT_ERROR_ILLEGAL_SEQUENCE: Invalid byte sequence in conversion input;
+ * or the character sequence could not be represented in the target
+ * character set.
+ * @G_CONVERT_ERROR_FAILED: Conversion failed for some reason.
+ * @G_CONVERT_ERROR_PARTIAL_INPUT: Partial character sequence at end of input.
+ * @G_CONVERT_ERROR_BAD_URI: URI is invalid.
+ * @G_CONVERT_ERROR_NOT_ABSOLUTE_PATH: Pathname is not an absolute path.
+ * @G_CONVERT_ERROR_NO_MEMORY: No memory available. Since: 2.40
+ * @G_CONVERT_ERROR_EMBEDDED_NUL: An embedded NUL character is present in
+ * conversion output where a NUL-terminated string is expected.
+ * Since: 2.56
+ *
+ * Error codes returned by character set conversion routines.
+ */
+typedef enum
+{
+ G_CONVERT_ERROR_NO_CONVERSION,
+ G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+ G_CONVERT_ERROR_FAILED,
+ G_CONVERT_ERROR_PARTIAL_INPUT,
+ G_CONVERT_ERROR_BAD_URI,
+ G_CONVERT_ERROR_NOT_ABSOLUTE_PATH,
+ G_CONVERT_ERROR_NO_MEMORY,
+ G_CONVERT_ERROR_EMBEDDED_NUL
+} GConvertError;
+
+/**
+ * G_CONVERT_ERROR:
+ *
+ * Error domain for character set conversions. Errors in this domain will
+ * be from the #GConvertError enumeration. See #GError for information on
+ * error domains.
+ */
+#define G_CONVERT_ERROR g_convert_error_quark()
+GLIB_AVAILABLE_IN_ALL
+GQuark g_convert_error_quark (void);
+
+/**
+ * GIConv: (skip)
+ *
+ * The GIConv struct wraps an iconv() conversion descriptor. It contains
+ * private data and should only be accessed using the following functions.
+ */
+typedef struct _GIConv *GIConv;
+
+GLIB_AVAILABLE_IN_ALL
+GIConv g_iconv_open (const gchar *to_codeset,
+ const gchar *from_codeset);
+GLIB_AVAILABLE_IN_ALL
+gsize g_iconv (GIConv converter,
+ gchar **inbuf,
+ gsize *inbytes_left,
+ gchar **outbuf,
+ gsize *outbytes_left);
+GLIB_AVAILABLE_IN_ALL
+gint g_iconv_close (GIConv converter);
+
+
+GLIB_AVAILABLE_IN_ALL
+gchar* g_convert (const gchar *str,
+ gssize len,
+ const gchar *to_codeset,
+ const gchar *from_codeset,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gchar* g_convert_with_iconv (const gchar *str,
+ gssize len,
+ GIConv converter,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gchar* g_convert_with_fallback (const gchar *str,
+ gssize len,
+ const gchar *to_codeset,
+ const gchar *from_codeset,
+ const gchar *fallback,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+
+
+/* Convert between libc's idea of strings and UTF-8.
+ */
+GLIB_AVAILABLE_IN_ALL
+gchar* g_locale_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gchar* g_locale_from_utf8 (const gchar *utf8string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+
+/* Convert between the operating system (or C runtime)
+ * representation of file names and UTF-8.
+ */
+GLIB_AVAILABLE_IN_ALL
+gchar* g_filename_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gchar* g_filename_from_utf8 (const gchar *utf8string,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+
+GLIB_AVAILABLE_IN_ALL
+gchar *g_filename_from_uri (const gchar *uri,
+ gchar **hostname,
+ GError **error) G_GNUC_MALLOC;
+
+GLIB_AVAILABLE_IN_ALL
+gchar *g_filename_to_uri (const gchar *filename,
+ const gchar *hostname,
+ GError **error) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gchar *g_filename_display_name (const gchar *filename) G_GNUC_MALLOC;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_get_filename_charsets (const gchar ***filename_charsets);
+
+GLIB_AVAILABLE_IN_ALL
+gchar *g_filename_display_basename (const gchar *filename) G_GNUC_MALLOC;
+
+GLIB_AVAILABLE_IN_ALL
+gchar **g_uri_list_extract_uris (const gchar *uri_list);
+
+G_END_DECLS
+
+#endif /* __G_CONVERT_H__ */
diff --git a/glib/gconvertprivate.h b/glib/gconvertprivate.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bdc87ff6899a62585d2e849da9cca57868c8695
--- /dev/null
+++ b/glib/gconvertprivate.h
@@ -0,0 +1,40 @@
+/* gconvertprivate.h - Private GLib gconvert functions
+ *
+ * Copyright 2020 Frederic Martinsons
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ */
+
+#ifndef __G_CONVERTPRIVATE_H__
+#define __G_CONVERTPRIVATE_H__
+
+G_BEGIN_DECLS
+
+#include "glib.h"
+
+gchar *_g_time_locale_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+
+gchar *_g_ctype_locale_to_utf8 (const gchar *opsysstring,
+ gssize len,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error) G_GNUC_MALLOC;
+
+G_END_DECLS
+
+#endif /* __G_CONVERTPRIVATE_H__ */
diff --git a/glib/gdataset.c b/glib/gdataset.c
new file mode 100644
index 0000000000000000000000000000000000000000..796d203d8f322ff5c6a9510778ffc75b99fd5647
--- /dev/null
+++ b/glib/gdataset.c
@@ -0,0 +1,1251 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
+ * Copyright (C) 1998 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe ; except for g_data*_foreach()
+ */
+
+#include "config.h"
+
+#include
+
+#include "gdataset.h"
+#include "gbitlock.h"
+
+#include "gslice.h"
+#include "gdatasetprivate.h"
+#include "ghash.h"
+#include "gquark.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gthread.h"
+#include "glib_trace.h"
+
+/**
+ * SECTION:datasets
+ * @title: Datasets
+ * @short_description: associate groups of data elements with
+ * particular memory locations
+ *
+ * Datasets associate groups of data elements with particular memory
+ * locations. These are useful if you need to associate data with a
+ * structure returned from an external library. Since you cannot modify
+ * the structure, you use its location in memory as the key into a
+ * dataset, where you can associate any number of data elements with it.
+ *
+ * There are two forms of most of the dataset functions. The first form
+ * uses strings to identify the data elements associated with a
+ * location. The second form uses #GQuark identifiers, which are
+ * created with a call to g_quark_from_string() or
+ * g_quark_from_static_string(). The second form is quicker, since it
+ * does not require looking up the string in the hash table of #GQuark
+ * identifiers.
+ *
+ * There is no function to create a dataset. It is automatically
+ * created as soon as you add elements to it.
+ *
+ * To add data elements to a dataset use g_dataset_id_set_data(),
+ * g_dataset_id_set_data_full(), g_dataset_set_data() and
+ * g_dataset_set_data_full().
+ *
+ * To get data elements from a dataset use g_dataset_id_get_data() and
+ * g_dataset_get_data().
+ *
+ * To iterate over all data elements in a dataset use
+ * g_dataset_foreach() (not thread-safe).
+ *
+ * To remove data elements from a dataset use
+ * g_dataset_id_remove_data() and g_dataset_remove_data().
+ *
+ * To destroy a dataset, use g_dataset_destroy().
+ **/
+
+/**
+ * SECTION:datalist
+ * @title: Keyed Data Lists
+ * @short_description: lists of data elements which are accessible by a
+ * string or GQuark identifier
+ *
+ * Keyed data lists provide lists of arbitrary data elements which can
+ * be accessed either with a string or with a #GQuark corresponding to
+ * the string.
+ *
+ * The #GQuark methods are quicker, since the strings have to be
+ * converted to #GQuarks anyway.
+ *
+ * Data lists are used for associating arbitrary data with #GObjects,
+ * using g_object_set_data() and related functions.
+ *
+ * To create a datalist, use g_datalist_init().
+ *
+ * To add data elements to a datalist use g_datalist_id_set_data(),
+ * g_datalist_id_set_data_full(), g_datalist_set_data() and
+ * g_datalist_set_data_full().
+ *
+ * To get data elements from a datalist use g_datalist_id_get_data()
+ * and g_datalist_get_data().
+ *
+ * To iterate over all data elements in a datalist use
+ * g_datalist_foreach() (not thread-safe).
+ *
+ * To remove data elements from a datalist use
+ * g_datalist_id_remove_data() and g_datalist_remove_data().
+ *
+ * To remove all data elements from a datalist, use g_datalist_clear().
+ **/
+
+/**
+ * GData:
+ *
+ * An opaque data structure that represents a keyed data list.
+ *
+ * See also: [Keyed data lists][glib-Keyed-Data-Lists].
+ **/
+
+/**
+ * GDestroyNotify:
+ * @data: the data element.
+ *
+ * Specifies the type of function which is called when a data element
+ * is destroyed. It is passed the pointer to the data element and
+ * should free any memory and resources allocated for it.
+ **/
+
+#define G_DATALIST_FLAGS_MASK_INTERNAL 0x7
+
+/* datalist pointer accesses have to be carried out atomically */
+#define G_DATALIST_GET_POINTER(datalist) \
+ ((GData*) ((gsize) g_atomic_pointer_get (datalist) & ~(gsize) G_DATALIST_FLAGS_MASK_INTERNAL))
+
+#define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START { \
+ gpointer _oldv, _newv; \
+ do { \
+ _oldv = g_atomic_pointer_get (datalist); \
+ _newv = (gpointer) (((gsize) _oldv & G_DATALIST_FLAGS_MASK_INTERNAL) | (gsize) pointer); \
+ } while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, _oldv, _newv)); \
+} G_STMT_END
+
+/* --- structures --- */
+typedef struct {
+ GQuark key;
+ gpointer data;
+ GDestroyNotify destroy;
+} GDataElt;
+
+typedef struct _GDataset GDataset;
+struct _GData
+{
+ guint32 len; /* Number of elements */
+ guint32 alloc; /* Number of allocated elements */
+ GDataElt data[1]; /* Flexible array */
+};
+
+struct _GDataset
+{
+ gconstpointer location;
+ GData *datalist;
+};
+
+
+/* --- prototypes --- */
+static inline GDataset* g_dataset_lookup (gconstpointer dataset_location);
+static inline void g_datalist_clear_i (GData **datalist);
+static void g_dataset_destroy_internal (GDataset *dataset);
+static inline gpointer g_data_set_internal (GData **datalist,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func,
+ GDataset *dataset);
+static void g_data_initialize (void);
+
+/* Locking model:
+ * Each standalone GDataList is protected by a bitlock in the datalist pointer,
+ * which protects that modification of the non-flags part of the datalist pointer
+ * and the contents of the datalist.
+ *
+ * For GDataSet we have a global lock g_dataset_global that protects
+ * the global dataset hash and cache, and additionally it protects the
+ * datalist such that we can avoid to use the bit lock in a few places
+ * where it is easy.
+ */
+
+/* --- variables --- */
+G_LOCK_DEFINE_STATIC (g_dataset_global);
+static GHashTable *g_dataset_location_ht = NULL;
+static GDataset *g_dataset_cached = NULL; /* should this be
+ thread specific? */
+
+/* --- functions --- */
+
+#define DATALIST_LOCK_BIT 2
+
+static void
+g_datalist_lock (GData **datalist)
+{
+ g_pointer_bit_lock ((void **)datalist, DATALIST_LOCK_BIT);
+}
+
+static void
+g_datalist_unlock (GData **datalist)
+{
+ g_pointer_bit_unlock ((void **)datalist, DATALIST_LOCK_BIT);
+}
+
+/* Called with the datalist lock held, or the dataset global
+ * lock for dataset lists
+ */
+static void
+g_datalist_clear_i (GData **datalist)
+{
+ GData *data;
+ guint i;
+
+ data = G_DATALIST_GET_POINTER (datalist);
+ G_DATALIST_SET_POINTER (datalist, NULL);
+
+ if (data)
+ {
+ G_UNLOCK (g_dataset_global);
+ for (i = 0; i < data->len; i++)
+ {
+ if (data->data[i].data && data->data[i].destroy)
+ data->data[i].destroy (data->data[i].data);
+ }
+ G_LOCK (g_dataset_global);
+
+ g_free (data);
+ }
+
+}
+
+/**
+ * g_datalist_clear: (skip)
+ * @datalist: a datalist.
+ *
+ * Frees all the data elements of the datalist.
+ * The data elements' destroy functions are called
+ * if they have been set.
+ **/
+void
+g_datalist_clear (GData **datalist)
+{
+ GData *data;
+ guint i;
+
+ g_return_if_fail (datalist != NULL);
+
+ g_datalist_lock (datalist);
+
+ data = G_DATALIST_GET_POINTER (datalist);
+ G_DATALIST_SET_POINTER (datalist, NULL);
+
+ g_datalist_unlock (datalist);
+
+ if (data)
+ {
+ for (i = 0; i < data->len; i++)
+ {
+ if (data->data[i].data && data->data[i].destroy)
+ data->data[i].destroy (data->data[i].data);
+ }
+
+ g_free (data);
+ }
+}
+
+/* HOLDS: g_dataset_global_lock */
+static inline GDataset*
+g_dataset_lookup (gconstpointer dataset_location)
+{
+ GDataset *dataset;
+
+ if (g_dataset_cached && g_dataset_cached->location == dataset_location)
+ return g_dataset_cached;
+
+ dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
+ if (dataset)
+ g_dataset_cached = dataset;
+
+ return dataset;
+}
+
+/* HOLDS: g_dataset_global_lock */
+static void
+g_dataset_destroy_internal (GDataset *dataset)
+{
+ gconstpointer dataset_location;
+
+ dataset_location = dataset->location;
+ while (dataset)
+ {
+ if (G_DATALIST_GET_POINTER(&dataset->datalist) == NULL)
+ {
+ if (dataset == g_dataset_cached)
+ g_dataset_cached = NULL;
+ g_hash_table_remove (g_dataset_location_ht, dataset_location);
+ g_slice_free (GDataset, dataset);
+ break;
+ }
+
+ g_datalist_clear_i (&dataset->datalist);
+ dataset = g_dataset_lookup (dataset_location);
+ }
+}
+
+/**
+ * g_dataset_destroy:
+ * @dataset_location: (not nullable): the location identifying the dataset.
+ *
+ * Destroys the dataset, freeing all memory allocated, and calling any
+ * destroy functions set for data elements.
+ */
+void
+g_dataset_destroy (gconstpointer dataset_location)
+{
+ g_return_if_fail (dataset_location != NULL);
+
+ G_LOCK (g_dataset_global);
+ if (g_dataset_location_ht)
+ {
+ GDataset *dataset;
+
+ dataset = g_dataset_lookup (dataset_location);
+ if (dataset)
+ g_dataset_destroy_internal (dataset);
+ }
+ G_UNLOCK (g_dataset_global);
+}
+
+/* HOLDS: g_dataset_global_lock if dataset != null */
+static inline gpointer
+g_data_set_internal (GData **datalist,
+ GQuark key_id,
+ gpointer new_data,
+ GDestroyNotify new_destroy_func,
+ GDataset *dataset)
+{
+ GData *d, *old_d;
+ GDataElt old, *data, *data_last, *data_end;
+
+ g_datalist_lock (datalist);
+
+ d = G_DATALIST_GET_POINTER (datalist);
+
+ if (new_data == NULL) /* remove */
+ {
+ if (d)
+ {
+ data = d->data;
+ data_last = data + d->len - 1;
+ while (data <= data_last)
+ {
+ if (data->key == key_id)
+ {
+ old = *data;
+ if (data != data_last)
+ *data = *data_last;
+ d->len--;
+
+ /* We don't bother to shrink, but if all data are now gone
+ * we at least free the memory
+ */
+ if (d->len == 0)
+ {
+ G_DATALIST_SET_POINTER (datalist, NULL);
+ g_free (d);
+ /* datalist may be situated in dataset, so must not be
+ * unlocked after we free it
+ */
+ g_datalist_unlock (datalist);
+
+ /* the dataset destruction *must* be done
+ * prior to invocation of the data destroy function
+ */
+ if (dataset)
+ g_dataset_destroy_internal (dataset);
+ }
+ else
+ {
+ g_datalist_unlock (datalist);
+ }
+
+ /* We found and removed an old value
+ * the GData struct *must* already be unlinked
+ * when invoking the destroy function.
+ * we use (new_data==NULL && new_destroy_func!=NULL) as
+ * a special hint combination to "steal"
+ * data without destroy notification
+ */
+ if (old.destroy && !new_destroy_func)
+ {
+ if (dataset)
+ G_UNLOCK (g_dataset_global);
+ old.destroy (old.data);
+ if (dataset)
+ G_LOCK (g_dataset_global);
+ old.data = NULL;
+ }
+
+ return old.data;
+ }
+ data++;
+ }
+ }
+ }
+ else
+ {
+ old.data = NULL;
+ if (d)
+ {
+ data = d->data;
+ data_end = data + d->len;
+ while (data < data_end)
+ {
+ if (data->key == key_id)
+ {
+ if (!data->destroy)
+ {
+ data->data = new_data;
+ data->destroy = new_destroy_func;
+ g_datalist_unlock (datalist);
+ }
+ else
+ {
+ old = *data;
+ data->data = new_data;
+ data->destroy = new_destroy_func;
+
+ g_datalist_unlock (datalist);
+
+ /* We found and replaced an old value
+ * the GData struct *must* already be unlinked
+ * when invoking the destroy function.
+ */
+ if (dataset)
+ G_UNLOCK (g_dataset_global);
+ old.destroy (old.data);
+ if (dataset)
+ G_LOCK (g_dataset_global);
+ }
+ return NULL;
+ }
+ data++;
+ }
+ }
+
+ /* The key was not found, insert it */
+ old_d = d;
+ if (d == NULL)
+ {
+ d = g_malloc (sizeof (GData));
+ d->len = 0;
+ d->alloc = 1;
+ }
+ else if (d->len == d->alloc)
+ {
+ d->alloc = d->alloc * 2;
+ d = g_realloc (d, sizeof (GData) + (d->alloc - 1) * sizeof (GDataElt));
+ }
+ if (old_d != d)
+ G_DATALIST_SET_POINTER (datalist, d);
+
+ d->data[d->len].key = key_id;
+ d->data[d->len].data = new_data;
+ d->data[d->len].destroy = new_destroy_func;
+ d->len++;
+ }
+
+ g_datalist_unlock (datalist);
+
+ return NULL;
+
+}
+
+/**
+ * g_dataset_id_set_data_full: (skip)
+ * @dataset_location: (not nullable): the location identifying the dataset.
+ * @key_id: the #GQuark id to identify the data element.
+ * @data: the data element.
+ * @destroy_func: the function to call when the data element is
+ * removed. This function will be called with the data
+ * element and can be used to free any memory allocated
+ * for it.
+ *
+ * Sets the data element associated with the given #GQuark id, and also
+ * the function to call when the data element is destroyed. Any
+ * previous data with the same key is removed, and its destroy function
+ * is called.
+ **/
+/**
+ * g_dataset_set_data_full: (skip)
+ * @l: the location identifying the dataset.
+ * @k: the string to identify the data element.
+ * @d: the data element.
+ * @f: the function to call when the data element is removed. This
+ * function will be called with the data element and can be used to
+ * free any memory allocated for it.
+ *
+ * Sets the data corresponding to the given string identifier, and the
+ * function to call when the data element is destroyed.
+ **/
+/**
+ * g_dataset_id_set_data:
+ * @l: the location identifying the dataset.
+ * @k: the #GQuark id to identify the data element.
+ * @d: the data element.
+ *
+ * Sets the data element associated with the given #GQuark id. Any
+ * previous data with the same key is removed, and its destroy function
+ * is called.
+ **/
+/**
+ * g_dataset_set_data:
+ * @l: the location identifying the dataset.
+ * @k: the string to identify the data element.
+ * @d: the data element.
+ *
+ * Sets the data corresponding to the given string identifier.
+ **/
+/**
+ * g_dataset_id_remove_data:
+ * @l: the location identifying the dataset.
+ * @k: the #GQuark id identifying the data element.
+ *
+ * Removes a data element from a dataset. The data element's destroy
+ * function is called if it has been set.
+ **/
+/**
+ * g_dataset_remove_data:
+ * @l: the location identifying the dataset.
+ * @k: the string identifying the data element.
+ *
+ * Removes a data element corresponding to a string. Its destroy
+ * function is called if it has been set.
+ **/
+void
+g_dataset_id_set_data_full (gconstpointer dataset_location,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func)
+{
+ GDataset *dataset;
+
+ g_return_if_fail (dataset_location != NULL);
+ if (!data)
+ g_return_if_fail (destroy_func == NULL);
+ if (!key_id)
+ {
+ if (data)
+ g_return_if_fail (key_id > 0);
+ else
+ return;
+ }
+
+ G_LOCK (g_dataset_global);
+ if (!g_dataset_location_ht)
+ g_data_initialize ();
+
+ dataset = g_dataset_lookup (dataset_location);
+ if (!dataset)
+ {
+ dataset = g_slice_new (GDataset);
+ dataset->location = dataset_location;
+ g_datalist_init (&dataset->datalist);
+ g_hash_table_insert (g_dataset_location_ht,
+ (gpointer) dataset->location,
+ dataset);
+ }
+
+ g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
+ G_UNLOCK (g_dataset_global);
+}
+
+/**
+ * g_datalist_id_set_data_full: (skip)
+ * @datalist: a datalist.
+ * @key_id: the #GQuark to identify the data element.
+ * @data: (nullable): the data element or %NULL to remove any previous element
+ * corresponding to @key_id.
+ * @destroy_func: (nullable): the function to call when the data element is
+ * removed. This function will be called with the data
+ * element and can be used to free any memory allocated
+ * for it. If @data is %NULL, then @destroy_func must
+ * also be %NULL.
+ *
+ * Sets the data corresponding to the given #GQuark id, and the
+ * function to be called when the element is removed from the datalist.
+ * Any previous data with the same key is removed, and its destroy
+ * function is called.
+ **/
+/**
+ * g_datalist_set_data_full: (skip)
+ * @dl: a datalist.
+ * @k: the string to identify the data element.
+ * @d: (nullable): the data element, or %NULL to remove any previous element
+ * corresponding to @k.
+ * @f: (nullable): the function to call when the data element is removed.
+ * This function will be called with the data element and can be used to
+ * free any memory allocated for it. If @d is %NULL, then @f must
+ * also be %NULL.
+ *
+ * Sets the data element corresponding to the given string identifier,
+ * and the function to be called when the data element is removed.
+ **/
+/**
+ * g_datalist_id_set_data:
+ * @dl: a datalist.
+ * @q: the #GQuark to identify the data element.
+ * @d: (nullable): the data element, or %NULL to remove any previous element
+ * corresponding to @q.
+ *
+ * Sets the data corresponding to the given #GQuark id. Any previous
+ * data with the same key is removed, and its destroy function is
+ * called.
+ **/
+/**
+ * g_datalist_set_data:
+ * @dl: a datalist.
+ * @k: the string to identify the data element.
+ * @d: (nullable): the data element, or %NULL to remove any previous element
+ * corresponding to @k.
+ *
+ * Sets the data element corresponding to the given string identifier.
+ **/
+/**
+ * g_datalist_id_remove_data:
+ * @dl: a datalist.
+ * @q: the #GQuark identifying the data element.
+ *
+ * Removes an element, using its #GQuark identifier.
+ **/
+/**
+ * g_datalist_remove_data:
+ * @dl: a datalist.
+ * @k: the string identifying the data element.
+ *
+ * Removes an element using its string identifier. The data element's
+ * destroy function is called if it has been set.
+ **/
+void
+g_datalist_id_set_data_full (GData **datalist,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func)
+{
+ g_return_if_fail (datalist != NULL);
+ if (!data)
+ g_return_if_fail (destroy_func == NULL);
+ if (!key_id)
+ {
+ if (data)
+ g_return_if_fail (key_id > 0);
+ else
+ return;
+ }
+
+ g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
+}
+
+/**
+ * g_dataset_id_remove_no_notify: (skip)
+ * @dataset_location: (not nullable): the location identifying the dataset.
+ * @key_id: the #GQuark ID identifying the data element.
+ *
+ * Removes an element, without calling its destroy notification
+ * function.
+ *
+ * Returns: (nullable): the data previously stored at @key_id,
+ * or %NULL if none.
+ **/
+/**
+ * g_dataset_remove_no_notify: (skip)
+ * @l: the location identifying the dataset.
+ * @k: the string identifying the data element.
+ *
+ * Removes an element, without calling its destroy notifier.
+ **/
+gpointer
+g_dataset_id_remove_no_notify (gconstpointer dataset_location,
+ GQuark key_id)
+{
+ gpointer ret_data = NULL;
+
+ g_return_val_if_fail (dataset_location != NULL, NULL);
+
+ G_LOCK (g_dataset_global);
+ if (key_id && g_dataset_location_ht)
+ {
+ GDataset *dataset;
+
+ dataset = g_dataset_lookup (dataset_location);
+ if (dataset)
+ ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
+ }
+ G_UNLOCK (g_dataset_global);
+
+ return ret_data;
+}
+
+/**
+ * g_datalist_id_remove_no_notify: (skip)
+ * @datalist: a datalist.
+ * @key_id: the #GQuark identifying a data element.
+ *
+ * Removes an element, without calling its destroy notification
+ * function.
+ *
+ * Returns: (nullable): the data previously stored at @key_id,
+ * or %NULL if none.
+ **/
+/**
+ * g_datalist_remove_no_notify: (skip)
+ * @dl: a datalist.
+ * @k: the string identifying the data element.
+ *
+ * Removes an element, without calling its destroy notifier.
+ **/
+gpointer
+g_datalist_id_remove_no_notify (GData **datalist,
+ GQuark key_id)
+{
+ gpointer ret_data = NULL;
+
+ g_return_val_if_fail (datalist != NULL, NULL);
+
+ if (key_id)
+ ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
+
+ return ret_data;
+}
+
+/**
+ * g_dataset_id_get_data:
+ * @dataset_location: (not nullable): the location identifying the dataset.
+ * @key_id: the #GQuark id to identify the data element.
+ *
+ * Gets the data element corresponding to a #GQuark.
+ *
+ * Returns: (transfer none) (nullable): the data element corresponding to
+ * the #GQuark, or %NULL if it is not found.
+ **/
+/**
+ * g_dataset_get_data:
+ * @l: the location identifying the dataset.
+ * @k: the string identifying the data element.
+ *
+ * Gets the data element corresponding to a string.
+ *
+ * Returns: (transfer none) (nullable): the data element corresponding to
+ * the string, or %NULL if it is not found.
+ **/
+gpointer
+g_dataset_id_get_data (gconstpointer dataset_location,
+ GQuark key_id)
+{
+ gpointer retval = NULL;
+
+ g_return_val_if_fail (dataset_location != NULL, NULL);
+
+ G_LOCK (g_dataset_global);
+ if (key_id && g_dataset_location_ht)
+ {
+ GDataset *dataset;
+
+ dataset = g_dataset_lookup (dataset_location);
+ if (dataset)
+ retval = g_datalist_id_get_data (&dataset->datalist, key_id);
+ }
+ G_UNLOCK (g_dataset_global);
+
+ return retval;
+}
+
+/**
+ * g_datalist_id_get_data:
+ * @datalist: a datalist.
+ * @key_id: the #GQuark identifying a data element.
+ *
+ * Retrieves the data element corresponding to @key_id.
+ *
+ * Returns: (transfer none) (nullable): the data element, or %NULL if
+ * it is not found.
+ */
+gpointer
+g_datalist_id_get_data (GData **datalist,
+ GQuark key_id)
+{
+ return g_datalist_id_dup_data (datalist, key_id, NULL, NULL);
+}
+
+/**
+ * GDuplicateFunc:
+ * @data: the data to duplicate
+ * @user_data: (closure): user data that was specified in
+ * g_datalist_id_dup_data()
+ *
+ * The type of functions that are used to 'duplicate' an object.
+ * What this means depends on the context, it could just be
+ * incrementing the reference count, if @data is a ref-counted
+ * object.
+ *
+ * Returns: a duplicate of data
+ */
+
+/**
+ * g_datalist_id_dup_data: (skip)
+ * @datalist: location of a datalist
+ * @key_id: the #GQuark identifying a data element
+ * @dup_func: (nullable) (scope call): function to duplicate the old value
+ * @user_data: (closure): passed as user_data to @dup_func
+ *
+ * This is a variant of g_datalist_id_get_data() which
+ * returns a 'duplicate' of the value. @dup_func defines the
+ * meaning of 'duplicate' in this context, it could e.g.
+ * take a reference on a ref-counted object.
+ *
+ * If the @key_id is not set in the datalist then @dup_func
+ * will be called with a %NULL argument.
+ *
+ * Note that @dup_func is called while the datalist is locked, so it
+ * is not allowed to read or modify the datalist.
+ *
+ * This function can be useful to avoid races when multiple
+ * threads are using the same datalist and the same key.
+ *
+ * Returns: (nullable): the result of calling @dup_func on the value
+ * associated with @key_id in @datalist, or %NULL if not set.
+ * If @dup_func is %NULL, the value is returned unmodified.
+ *
+ * Since: 2.34
+ */
+gpointer
+g_datalist_id_dup_data (GData **datalist,
+ GQuark key_id,
+ GDuplicateFunc dup_func,
+ gpointer user_data)
+{
+ gpointer val = NULL;
+ gpointer retval = NULL;
+ GData *d;
+ GDataElt *data, *data_end;
+
+ g_datalist_lock (datalist);
+
+ d = G_DATALIST_GET_POINTER (datalist);
+ if (d)
+ {
+ data = d->data;
+ data_end = data + d->len;
+ do
+ {
+ if (data->key == key_id)
+ {
+ val = data->data;
+ break;
+ }
+ data++;
+ }
+ while (data < data_end);
+ }
+
+ if (dup_func)
+ retval = dup_func (val, user_data);
+ else
+ retval = val;
+
+ g_datalist_unlock (datalist);
+
+ return retval;
+}
+
+/**
+ * g_datalist_id_replace_data: (skip)
+ * @datalist: location of a datalist
+ * @key_id: the #GQuark identifying a data element
+ * @oldval: (nullable): the old value to compare against
+ * @newval: (nullable): the new value to replace it with
+ * @destroy: (nullable): destroy notify for the new value
+ * @old_destroy: (out) (optional): destroy notify for the existing value
+ *
+ * Compares the member that is associated with @key_id in
+ * @datalist to @oldval, and if they are the same, replace
+ * @oldval with @newval.
+ *
+ * This is like a typical atomic compare-and-exchange
+ * operation, for a member of @datalist.
+ *
+ * If the previous value was replaced then ownership of the
+ * old value (@oldval) is passed to the caller, including
+ * the registered destroy notify for it (passed out in @old_destroy).
+ * Its up to the caller to free this as they wish, which may
+ * or may not include using @old_destroy as sometimes replacement
+ * should not destroy the object in the normal way.
+ *
+ * Returns: %TRUE if the existing value for @key_id was replaced
+ * by @newval, %FALSE otherwise.
+ *
+ * Since: 2.34
+ */
+gboolean
+g_datalist_id_replace_data (GData **datalist,
+ GQuark key_id,
+ gpointer oldval,
+ gpointer newval,
+ GDestroyNotify destroy,
+ GDestroyNotify *old_destroy)
+{
+ gpointer val = NULL;
+ GData *d;
+ GDataElt *data, *data_end;
+
+ g_return_val_if_fail (datalist != NULL, FALSE);
+ g_return_val_if_fail (key_id != 0, FALSE);
+
+ if (old_destroy)
+ *old_destroy = NULL;
+
+ g_datalist_lock (datalist);
+
+ d = G_DATALIST_GET_POINTER (datalist);
+ if (d)
+ {
+ data = d->data;
+ data_end = data + d->len - 1;
+ while (data <= data_end)
+ {
+ if (data->key == key_id)
+ {
+ val = data->data;
+ if (val == oldval)
+ {
+ if (old_destroy)
+ *old_destroy = data->destroy;
+ if (newval != NULL)
+ {
+ data->data = newval;
+ data->destroy = destroy;
+ }
+ else
+ {
+ if (data != data_end)
+ *data = *data_end;
+ d->len--;
+
+ /* We don't bother to shrink, but if all data are now gone
+ * we at least free the memory
+ */
+ if (d->len == 0)
+ {
+ G_DATALIST_SET_POINTER (datalist, NULL);
+ g_free (d);
+ }
+ }
+ }
+ break;
+ }
+ data++;
+ }
+ }
+
+ if (val == NULL && oldval == NULL && newval != NULL)
+ {
+ GData *old_d;
+
+ /* insert newval */
+ old_d = d;
+ if (d == NULL)
+ {
+ d = g_malloc (sizeof (GData));
+ d->len = 0;
+ d->alloc = 1;
+ }
+ else if (d->len == d->alloc)
+ {
+ d->alloc = d->alloc * 2;
+ d = g_realloc (d, sizeof (GData) + (d->alloc - 1) * sizeof (GDataElt));
+ }
+ if (old_d != d)
+ G_DATALIST_SET_POINTER (datalist, d);
+
+ d->data[d->len].key = key_id;
+ d->data[d->len].data = newval;
+ d->data[d->len].destroy = destroy;
+ d->len++;
+ }
+
+ g_datalist_unlock (datalist);
+
+ return val == oldval;
+}
+
+/**
+ * g_datalist_get_data:
+ * @datalist: a datalist.
+ * @key: the string identifying a data element.
+ *
+ * Gets a data element, using its string identifier. This is slower than
+ * g_datalist_id_get_data() because it compares strings.
+ *
+ * Returns: (transfer none) (nullable): the data element, or %NULL if it
+ * is not found.
+ **/
+gpointer
+g_datalist_get_data (GData **datalist,
+ const gchar *key)
+{
+ gpointer res = NULL;
+ GData *d;
+ GDataElt *data, *data_end;
+
+ g_return_val_if_fail (datalist != NULL, NULL);
+
+ g_datalist_lock (datalist);
+
+ d = G_DATALIST_GET_POINTER (datalist);
+ if (d)
+ {
+ data = d->data;
+ data_end = data + d->len;
+ while (data < data_end)
+ {
+ if (g_strcmp0 (g_quark_to_string (data->key), key) == 0)
+ {
+ res = data->data;
+ break;
+ }
+ data++;
+ }
+ }
+
+ g_datalist_unlock (datalist);
+
+ return res;
+}
+
+/**
+ * GDataForeachFunc:
+ * @key_id: the #GQuark id to identifying the data element.
+ * @data: the data element.
+ * @user_data: (closure): user data passed to g_dataset_foreach().
+ *
+ * Specifies the type of function passed to g_dataset_foreach(). It is
+ * called with each #GQuark id and associated data element, together
+ * with the @user_data parameter supplied to g_dataset_foreach().
+ **/
+
+/**
+ * g_dataset_foreach:
+ * @dataset_location: (not nullable): the location identifying the dataset.
+ * @func: (scope call): the function to call for each data element.
+ * @user_data: (closure): user data to pass to the function.
+ *
+ * Calls the given function for each data element which is associated
+ * with the given location. Note that this function is NOT thread-safe.
+ * So unless @dataset_location can be protected from any modifications
+ * during invocation of this function, it should not be called.
+ *
+ * @func can make changes to the dataset, but the iteration will not
+ * reflect changes made during the g_dataset_foreach() call, other
+ * than skipping over elements that are removed.
+ **/
+void
+g_dataset_foreach (gconstpointer dataset_location,
+ GDataForeachFunc func,
+ gpointer user_data)
+{
+ GDataset *dataset;
+
+ g_return_if_fail (dataset_location != NULL);
+ g_return_if_fail (func != NULL);
+
+ G_LOCK (g_dataset_global);
+ if (g_dataset_location_ht)
+ {
+ dataset = g_dataset_lookup (dataset_location);
+ G_UNLOCK (g_dataset_global);
+ if (dataset)
+ g_datalist_foreach (&dataset->datalist, func, user_data);
+ }
+ else
+ {
+ G_UNLOCK (g_dataset_global);
+ }
+}
+
+/**
+ * g_datalist_foreach:
+ * @datalist: a datalist.
+ * @func: (scope call): the function to call for each data element.
+ * @user_data: (closure): user data to pass to the function.
+ *
+ * Calls the given function for each data element of the datalist. The
+ * function is called with each data element's #GQuark id and data,
+ * together with the given @user_data parameter. Note that this
+ * function is NOT thread-safe. So unless @datalist can be protected
+ * from any modifications during invocation of this function, it should
+ * not be called.
+ *
+ * @func can make changes to @datalist, but the iteration will not
+ * reflect changes made during the g_datalist_foreach() call, other
+ * than skipping over elements that are removed.
+ **/
+void
+g_datalist_foreach (GData **datalist,
+ GDataForeachFunc func,
+ gpointer user_data)
+{
+ GData *d;
+ guint i, j, len;
+ GQuark *keys;
+
+ g_return_if_fail (datalist != NULL);
+ g_return_if_fail (func != NULL);
+
+ d = G_DATALIST_GET_POINTER (datalist);
+ if (d == NULL)
+ return;
+
+ /* We make a copy of the keys so that we can handle it changing
+ in the callback */
+ len = d->len;
+ keys = g_new (GQuark, len);
+ for (i = 0; i < len; i++)
+ keys[i] = d->data[i].key;
+
+ for (i = 0; i < len; i++)
+ {
+ /* A previous callback might have removed a later item, so always check that
+ it still exists before calling */
+ d = G_DATALIST_GET_POINTER (datalist);
+
+ if (d == NULL)
+ break;
+ for (j = 0; j < d->len; j++)
+ {
+ if (d->data[j].key == keys[i]) {
+ func (d->data[i].key, d->data[i].data, user_data);
+ break;
+ }
+ }
+ }
+ g_free (keys);
+}
+
+/**
+ * g_datalist_init: (skip)
+ * @datalist: a pointer to a pointer to a datalist.
+ *
+ * Resets the datalist to %NULL. It does not free any memory or call
+ * any destroy functions.
+ **/
+void
+g_datalist_init (GData **datalist)
+{
+ g_return_if_fail (datalist != NULL);
+
+ g_atomic_pointer_set (datalist, NULL);
+}
+
+/**
+ * g_datalist_set_flags:
+ * @datalist: pointer to the location that holds a list
+ * @flags: the flags to turn on. The values of the flags are
+ * restricted by %G_DATALIST_FLAGS_MASK (currently
+ * 3; giving two possible boolean flags).
+ * A value for @flags that doesn't fit within the mask is
+ * an error.
+ *
+ * Turns on flag values for a data list. This function is used
+ * to keep a small number of boolean flags in an object with
+ * a data list without using any additional space. It is
+ * not generally useful except in circumstances where space
+ * is very tight. (It is used in the base #GObject type, for
+ * example.)
+ *
+ * Since: 2.8
+ **/
+void
+g_datalist_set_flags (GData **datalist,
+ guint flags)
+{
+ g_return_if_fail (datalist != NULL);
+ g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
+
+ g_atomic_pointer_or (datalist, (gsize)flags);
+}
+
+/**
+ * g_datalist_unset_flags:
+ * @datalist: pointer to the location that holds a list
+ * @flags: the flags to turn off. The values of the flags are
+ * restricted by %G_DATALIST_FLAGS_MASK (currently
+ * 3: giving two possible boolean flags).
+ * A value for @flags that doesn't fit within the mask is
+ * an error.
+ *
+ * Turns off flag values for a data list. See g_datalist_unset_flags()
+ *
+ * Since: 2.8
+ **/
+void
+g_datalist_unset_flags (GData **datalist,
+ guint flags)
+{
+ g_return_if_fail (datalist != NULL);
+ g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
+
+ g_atomic_pointer_and (datalist, ~(gsize)flags);
+}
+
+/**
+ * g_datalist_get_flags:
+ * @datalist: pointer to the location that holds a list
+ *
+ * Gets flags values packed in together with the datalist.
+ * See g_datalist_set_flags().
+ *
+ * Returns: the flags of the datalist
+ *
+ * Since: 2.8
+ **/
+guint
+g_datalist_get_flags (GData **datalist)
+{
+ g_return_val_if_fail (datalist != NULL, 0);
+
+ return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
+}
+
+/* HOLDS: g_dataset_global_lock */
+static void
+g_data_initialize (void)
+{
+ g_return_if_fail (g_dataset_location_ht == NULL);
+
+ g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
+ g_dataset_cached = NULL;
+}
diff --git a/glib/gdataset.h b/glib/gdataset.h
new file mode 100644
index 0000000000000000000000000000000000000000..89a34c71afe6fb1dce2a88de623e94113789a5e6
--- /dev/null
+++ b/glib/gdataset.h
@@ -0,0 +1,150 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_DATASET_H__
+#define __G_DATASET_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+typedef struct _GData GData;
+
+typedef void (*GDataForeachFunc) (GQuark key_id,
+ gpointer data,
+ gpointer user_data);
+
+/* Keyed Data List
+ */
+GLIB_AVAILABLE_IN_ALL
+void g_datalist_init (GData **datalist);
+GLIB_AVAILABLE_IN_ALL
+void g_datalist_clear (GData **datalist);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_datalist_id_get_data (GData **datalist,
+ GQuark key_id);
+GLIB_AVAILABLE_IN_ALL
+void g_datalist_id_set_data_full (GData **datalist,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func);
+
+typedef gpointer (*GDuplicateFunc) (gpointer data, gpointer user_data);
+
+GLIB_AVAILABLE_IN_2_34
+gpointer g_datalist_id_dup_data (GData **datalist,
+ GQuark key_id,
+ GDuplicateFunc dup_func,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_34
+gboolean g_datalist_id_replace_data (GData **datalist,
+ GQuark key_id,
+ gpointer oldval,
+ gpointer newval,
+ GDestroyNotify destroy,
+ GDestroyNotify *old_destroy);
+
+GLIB_AVAILABLE_IN_ALL
+gpointer g_datalist_id_remove_no_notify (GData **datalist,
+ GQuark key_id);
+GLIB_AVAILABLE_IN_ALL
+void g_datalist_foreach (GData **datalist,
+ GDataForeachFunc func,
+ gpointer user_data);
+
+/**
+ * G_DATALIST_FLAGS_MASK:
+ *
+ * A bitmask that restricts the possible flags passed to
+ * g_datalist_set_flags(). Passing a flags value where
+ * flags & ~G_DATALIST_FLAGS_MASK != 0 is an error.
+ */
+#define G_DATALIST_FLAGS_MASK 0x3
+
+GLIB_AVAILABLE_IN_ALL
+void g_datalist_set_flags (GData **datalist,
+ guint flags);
+GLIB_AVAILABLE_IN_ALL
+void g_datalist_unset_flags (GData **datalist,
+ guint flags);
+GLIB_AVAILABLE_IN_ALL
+guint g_datalist_get_flags (GData **datalist);
+
+#define g_datalist_id_set_data(dl, q, d) \
+ g_datalist_id_set_data_full ((dl), (q), (d), NULL)
+#define g_datalist_id_remove_data(dl, q) \
+ g_datalist_id_set_data ((dl), (q), NULL)
+#define g_datalist_set_data_full(dl, k, d, f) \
+ g_datalist_id_set_data_full ((dl), g_quark_from_string (k), (d), (f))
+#define g_datalist_remove_no_notify(dl, k) \
+ g_datalist_id_remove_no_notify ((dl), g_quark_try_string (k))
+#define g_datalist_set_data(dl, k, d) \
+ g_datalist_set_data_full ((dl), (k), (d), NULL)
+#define g_datalist_remove_data(dl, k) \
+ g_datalist_id_set_data ((dl), g_quark_try_string (k), NULL)
+
+/* Location Associated Keyed Data
+ */
+GLIB_AVAILABLE_IN_ALL
+void g_dataset_destroy (gconstpointer dataset_location);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_dataset_id_get_data (gconstpointer dataset_location,
+ GQuark key_id);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_datalist_get_data (GData **datalist,
+ const gchar *key);
+GLIB_AVAILABLE_IN_ALL
+void g_dataset_id_set_data_full (gconstpointer dataset_location,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func);
+GLIB_AVAILABLE_IN_ALL
+gpointer g_dataset_id_remove_no_notify (gconstpointer dataset_location,
+ GQuark key_id);
+GLIB_AVAILABLE_IN_ALL
+void g_dataset_foreach (gconstpointer dataset_location,
+ GDataForeachFunc func,
+ gpointer user_data);
+#define g_dataset_id_set_data(l, k, d) \
+ g_dataset_id_set_data_full ((l), (k), (d), NULL)
+#define g_dataset_id_remove_data(l, k) \
+ g_dataset_id_set_data ((l), (k), NULL)
+#define g_dataset_get_data(l, k) \
+ (g_dataset_id_get_data ((l), g_quark_try_string (k)))
+#define g_dataset_set_data_full(l, k, d, f) \
+ g_dataset_id_set_data_full ((l), g_quark_from_string (k), (d), (f))
+#define g_dataset_remove_no_notify(l, k) \
+ g_dataset_id_remove_no_notify ((l), g_quark_try_string (k))
+#define g_dataset_set_data(l, k, d) \
+ g_dataset_set_data_full ((l), (k), (d), NULL)
+#define g_dataset_remove_data(l, k) \
+ g_dataset_id_set_data ((l), g_quark_try_string (k), NULL)
+
+G_END_DECLS
+
+#endif /* __G_DATASET_H__ */
diff --git a/glib/gdatasetprivate.h b/glib/gdatasetprivate.h
new file mode 100644
index 0000000000000000000000000000000000000000..eb95278f15dcb8fc14b17f8aae3629b4a28e3b67
--- /dev/null
+++ b/glib/gdatasetprivate.h
@@ -0,0 +1,42 @@
+/* GLIB - Library of useful routines for C programming
+ * gdataset-private.h: Internal macros for accessing dataset values
+ * Copyright (C) 2005 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_DATASETPRIVATE_H__
+#define __G_DATASETPRIVATE_H__
+
+#include
+
+G_BEGIN_DECLS
+
+/* GET_FLAGS is implemented via atomic pointer access, to allow memory
+ * barriers to take effect without acquiring the global dataset mutex.
+ */
+#define G_DATALIST_GET_FLAGS(datalist) \
+ ((gsize) g_atomic_pointer_get (datalist) & G_DATALIST_FLAGS_MASK)
+
+
+G_END_DECLS
+
+#endif /* __G_DATASETPRIVATE_H__ */
diff --git a/glib/gdate.c b/glib/gdate.c
new file mode 100644
index 0000000000000000000000000000000000000000..68c8689ef4392e547b2ca31e77733377784154ae
--- /dev/null
+++ b/glib/gdate.c
@@ -0,0 +1,2755 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * MT safe
+ */
+
+#include "config.h"
+#include "glibconfig.h"
+
+#define DEBUG_MSG(x) /* */
+#ifdef G_ENABLE_DEBUG
+/* #define DEBUG_MSG(args) g_message args ; */
+#endif
+
+#include
+#include
+#include
+#include
+
+#ifdef G_OS_WIN32
+#include
+#endif
+
+#include "gdate.h"
+
+#include "gconvert.h"
+#include "gmem.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gthread.h"
+#include "gunicode.h"
+
+#ifdef G_OS_WIN32
+#include "garray.h"
+#endif
+
+/**
+ * SECTION:date
+ * @title: Date and Time Functions
+ * @short_description: calendrical calculations and miscellaneous time stuff
+ *
+ * The #GDate data structure represents a day between January 1, Year 1,
+ * and sometime a few thousand years in the future (right now it will go
+ * to the year 65535 or so, but g_date_set_parse() only parses up to the
+ * year 8000 or so - just count on "a few thousand"). #GDate is meant to
+ * represent everyday dates, not astronomical dates or historical dates
+ * or ISO timestamps or the like. It extrapolates the current Gregorian
+ * calendar forward and backward in time; there is no attempt to change
+ * the calendar to match time periods or locations. #GDate does not store
+ * time information; it represents a day.
+ *
+ * The #GDate implementation has several nice features; it is only a
+ * 64-bit struct, so storing large numbers of dates is very efficient. It
+ * can keep both a Julian and day-month-year representation of the date,
+ * since some calculations are much easier with one representation or the
+ * other. A Julian representation is simply a count of days since some
+ * fixed day in the past; for #GDate the fixed day is January 1, 1 AD.
+ * ("Julian" dates in the #GDate API aren't really Julian dates in the
+ * technical sense; technically, Julian dates count from the start of the
+ * Julian period, Jan 1, 4713 BC).
+ *
+ * #GDate is simple to use. First you need a "blank" date; you can get a
+ * dynamically allocated date from g_date_new(), or you can declare an
+ * automatic variable or array and initialize it by
+ * calling g_date_clear(). A cleared date is safe; it's safe to call
+ * g_date_set_dmy() and the other mutator functions to initialize the
+ * value of a cleared date. However, a cleared date is initially
+ * invalid, meaning that it doesn't represent a day that exists.
+ * It is undefined to call any of the date calculation routines on an
+ * invalid date. If you obtain a date from a user or other
+ * unpredictable source, you should check its validity with the
+ * g_date_valid() predicate. g_date_valid() is also used to check for
+ * errors with g_date_set_parse() and other functions that can
+ * fail. Dates can be invalidated by calling g_date_clear() again.
+ *
+ * It is very important to use the API to access the #GDate
+ * struct. Often only the day-month-year or only the Julian
+ * representation is valid. Sometimes neither is valid. Use the API.
+ *
+ * GLib also features #GDateTime which represents a precise time.
+ */
+
+/**
+ * G_USEC_PER_SEC:
+ *
+ * Number of microseconds in one second (1 million).
+ * This macro is provided for code readability.
+ */
+
+/**
+ * GTimeVal:
+ * @tv_sec: seconds
+ * @tv_usec: microseconds
+ *
+ * Represents a precise time, with seconds and microseconds.
+ *
+ * Similar to the struct timeval returned by the `gettimeofday()`
+ * UNIX system call.
+ *
+ * GLib is attempting to unify around the use of 64-bit integers to
+ * represent microsecond-precision time. As such, this type will be
+ * removed from a future version of GLib. A consequence of using `glong` for
+ * `tv_sec` is that on 32-bit systems `GTimeVal` is subject to the year 2038
+ * problem.
+ *
+ * Deprecated: 2.62: Use #GDateTime or #guint64 instead.
+ */
+
+/**
+ * GDate:
+ * @julian_days: the Julian representation of the date
+ * @julian: this bit is set if @julian_days is valid
+ * @dmy: this is set if @day, @month and @year are valid
+ * @day: the day of the day-month-year representation of the date,
+ * as a number between 1 and 31
+ * @month: the day of the day-month-year representation of the date,
+ * as a number between 1 and 12
+ * @year: the day of the day-month-year representation of the date
+ *
+ * Represents a day between January 1, Year 1 and a few thousand years in
+ * the future. None of its members should be accessed directly.
+ *
+ * If the `GDate` is obtained from g_date_new(), it will be safe
+ * to mutate but invalid and thus not safe for calendrical computations.
+ *
+ * If it's declared on the stack, it will contain garbage so must be
+ * initialized with g_date_clear(). g_date_clear() makes the date invalid
+ * but safe. An invalid date doesn't represent a day, it's "empty." A date
+ * becomes valid after you set it to a Julian day or you set a day, month,
+ * and year.
+ */
+
+/**
+ * GTime:
+ *
+ * Simply a replacement for `time_t`. It has been deprecated
+ * since it is not equivalent to `time_t` on 64-bit platforms
+ * with a 64-bit `time_t`.
+ *
+ * Unrelated to #GTimer.
+ *
+ * Note that #GTime is defined to always be a 32-bit integer,
+ * unlike `time_t` which may be 64-bit on some systems. Therefore,
+ * #GTime will overflow in the year 2038, and you cannot use the
+ * address of a #GTime variable as argument to the UNIX time()
+ * function.
+ *
+ * Instead, do the following:
+ *
+ * |[
+ * time_t ttime;
+ * GTime gtime;
+ *
+ * time (&ttime);
+ * gtime = (GTime)ttime;
+ * ]|
+ *
+ * Deprecated: 2.62: This is not [Y2038-safe](https://en.wikipedia.org/wiki/Year_2038_problem).
+ * Use #GDateTime or #time_t instead.
+ */
+
+/**
+ * GDateDMY:
+ * @G_DATE_DAY: a day
+ * @G_DATE_MONTH: a month
+ * @G_DATE_YEAR: a year
+ *
+ * This enumeration isn't used in the API, but may be useful if you need
+ * to mark a number as a day, month, or year.
+ */
+
+/**
+ * GDateDay:
+ *
+ * Integer representing a day of the month; between 1 and 31.
+ *
+ * The %G_DATE_BAD_DAY value represents an invalid day of the month.
+ */
+
+/**
+ * GDateMonth:
+ * @G_DATE_BAD_MONTH: invalid value
+ * @G_DATE_JANUARY: January
+ * @G_DATE_FEBRUARY: February
+ * @G_DATE_MARCH: March
+ * @G_DATE_APRIL: April
+ * @G_DATE_MAY: May
+ * @G_DATE_JUNE: June
+ * @G_DATE_JULY: July
+ * @G_DATE_AUGUST: August
+ * @G_DATE_SEPTEMBER: September
+ * @G_DATE_OCTOBER: October
+ * @G_DATE_NOVEMBER: November
+ * @G_DATE_DECEMBER: December
+ *
+ * Enumeration representing a month; values are %G_DATE_JANUARY,
+ * %G_DATE_FEBRUARY, etc. %G_DATE_BAD_MONTH is the invalid value.
+ */
+
+/**
+ * GDateYear:
+ *
+ * Integer type representing a year.
+ *
+ * The %G_DATE_BAD_YEAR value is the invalid value. The year
+ * must be 1 or higher; negative ([BCE](https://en.wikipedia.org/wiki/Common_Era))
+ * years are not allowed.
+ *
+ * The year is represented with four digits.
+ */
+
+/**
+ * GDateWeekday:
+ * @G_DATE_BAD_WEEKDAY: invalid value
+ * @G_DATE_MONDAY: Monday
+ * @G_DATE_TUESDAY: Tuesday
+ * @G_DATE_WEDNESDAY: Wednesday
+ * @G_DATE_THURSDAY: Thursday
+ * @G_DATE_FRIDAY: Friday
+ * @G_DATE_SATURDAY: Saturday
+ * @G_DATE_SUNDAY: Sunday
+ *
+ * Enumeration representing a day of the week; %G_DATE_MONDAY,
+ * %G_DATE_TUESDAY, etc. %G_DATE_BAD_WEEKDAY is an invalid weekday.
+ */
+
+/**
+ * G_DATE_BAD_DAY:
+ *
+ * Represents an invalid #GDateDay.
+ */
+
+/**
+ * G_DATE_BAD_JULIAN:
+ *
+ * Represents an invalid Julian day number.
+ */
+
+/**
+ * G_DATE_BAD_YEAR:
+ *
+ * Represents an invalid year.
+ */
+
+/**
+ * g_date_new:
+ *
+ * Allocates a #GDate and initializes
+ * it to a safe state. The new date will
+ * be cleared (as if you'd called g_date_clear()) but invalid (it won't
+ * represent an existing day). Free the return value with g_date_free().
+ *
+ * Returns: a newly-allocated #GDate
+ */
+GDate*
+g_date_new (void)
+{
+ GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
+
+ return d;
+}
+
+/**
+ * g_date_new_dmy:
+ * @day: day of the month
+ * @month: month of the year
+ * @year: year
+ *
+ * Create a new #GDate representing the given day-month-year triplet.
+ *
+ * The triplet you pass in must represent a valid date. Use g_date_valid_dmy()
+ * if needed to validate it. The returned #GDate is guaranteed to be non-%NULL
+ * and valid.
+ *
+ * Returns: (transfer full) (not nullable): a newly-allocated #GDate
+ * initialized with @day, @month, and @year
+ */
+GDate*
+g_date_new_dmy (GDateDay day,
+ GDateMonth m,
+ GDateYear y)
+{
+ GDate *d;
+ g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
+
+ d = g_new (GDate, 1);
+
+ d->julian = FALSE;
+ d->dmy = TRUE;
+
+ d->month = m;
+ d->day = day;
+ d->year = y;
+
+ g_assert (g_date_valid (d));
+
+ return d;
+}
+
+/**
+ * g_date_new_julian:
+ * @julian_day: days since January 1, Year 1
+ *
+ * Create a new #GDate representing the given Julian date.
+ *
+ * The @julian_day you pass in must be valid. Use g_date_valid_julian() if
+ * needed to validate it. The returned #GDate is guaranteed to be non-%NULL and
+ * valid.
+ *
+ * Returns: (transfer full) (not nullable): a newly-allocated #GDate initialized
+ * with @julian_day
+ */
+GDate*
+g_date_new_julian (guint32 julian_day)
+{
+ GDate *d;
+ g_return_val_if_fail (g_date_valid_julian (julian_day), NULL);
+
+ d = g_new (GDate, 1);
+
+ d->julian = TRUE;
+ d->dmy = FALSE;
+
+ d->julian_days = julian_day;
+
+ g_assert (g_date_valid (d));
+
+ return d;
+}
+
+/**
+ * g_date_free:
+ * @date: a #GDate to free
+ *
+ * Frees a #GDate returned from g_date_new().
+ */
+void
+g_date_free (GDate *date)
+{
+ g_return_if_fail (date != NULL);
+
+ g_free (date);
+}
+
+/**
+ * g_date_copy:
+ * @date: a #GDate to copy
+ *
+ * Copies a GDate to a newly-allocated GDate. If the input was invalid
+ * (as determined by g_date_valid()), the invalid state will be copied
+ * as is into the new object.
+ *
+ * Returns: (transfer full): a newly-allocated #GDate initialized from @date
+ *
+ * Since: 2.56
+ */
+GDate *
+g_date_copy (const GDate *date)
+{
+ GDate *res;
+ g_return_val_if_fail (date != NULL, NULL);
+
+ if (g_date_valid (date))
+ res = g_date_new_julian (g_date_get_julian (date));
+ else
+ {
+ res = g_date_new ();
+ *res = *date;
+ }
+
+ return res;
+}
+
+/**
+ * g_date_valid:
+ * @date: a #GDate to check
+ *
+ * Returns %TRUE if the #GDate represents an existing day. The date must not
+ * contain garbage; it should have been initialized with g_date_clear()
+ * if it wasn't allocated by one of the g_date_new() variants.
+ *
+ * Returns: Whether the date is valid
+ */
+gboolean
+g_date_valid (const GDate *d)
+{
+ g_return_val_if_fail (d != NULL, FALSE);
+
+ return (d->julian || d->dmy);
+}
+
+static const guint8 days_in_months[2][13] =
+{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */
+ { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
+};
+
+static const guint16 days_in_year[2][14] =
+{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */
+ { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+/**
+ * g_date_valid_month:
+ * @month: month
+ *
+ * Returns %TRUE if the month value is valid. The 12 #GDateMonth
+ * enumeration values are the only valid months.
+ *
+ * Returns: %TRUE if the month is valid
+ */
+gboolean
+g_date_valid_month (GDateMonth m)
+{
+ return (((gint) m > G_DATE_BAD_MONTH) && ((gint) m < 13));
+}
+
+/**
+ * g_date_valid_year:
+ * @year: year
+ *
+ * Returns %TRUE if the year is valid. Any year greater than 0 is valid,
+ * though there is a 16-bit limit to what #GDate will understand.
+ *
+ * Returns: %TRUE if the year is valid
+ */
+gboolean
+g_date_valid_year (GDateYear y)
+{
+ return ( y > G_DATE_BAD_YEAR );
+}
+
+/**
+ * g_date_valid_day:
+ * @day: day to check
+ *
+ * Returns %TRUE if the day of the month is valid (a day is valid if it's
+ * between 1 and 31 inclusive).
+ *
+ * Returns: %TRUE if the day is valid
+ */
+
+gboolean
+g_date_valid_day (GDateDay d)
+{
+ return ( (d > G_DATE_BAD_DAY) && (d < 32) );
+}
+
+/**
+ * g_date_valid_weekday:
+ * @weekday: weekday
+ *
+ * Returns %TRUE if the weekday is valid. The seven #GDateWeekday enumeration
+ * values are the only valid weekdays.
+ *
+ * Returns: %TRUE if the weekday is valid
+ */
+gboolean
+g_date_valid_weekday (GDateWeekday w)
+{
+ return (((gint) w > G_DATE_BAD_WEEKDAY) && ((gint) w < 8));
+}
+
+/**
+ * g_date_valid_julian:
+ * @julian_date: Julian day to check
+ *
+ * Returns %TRUE if the Julian day is valid. Anything greater than zero
+ * is basically a valid Julian, though there is a 32-bit limit.
+ *
+ * Returns: %TRUE if the Julian day is valid
+ */
+gboolean
+g_date_valid_julian (guint32 j)
+{
+ return (j > G_DATE_BAD_JULIAN);
+}
+
+/**
+ * g_date_valid_dmy:
+ * @day: day
+ * @month: month
+ * @year: year
+ *
+ * Returns %TRUE if the day-month-year triplet forms a valid, existing day
+ * in the range of days #GDate understands (Year 1 or later, no more than
+ * a few thousand years in the future).
+ *
+ * Returns: %TRUE if the date is a valid one
+ */
+gboolean
+g_date_valid_dmy (GDateDay d,
+ GDateMonth m,
+ GDateYear y)
+{
+ /* No need to check the upper bound of @y, because #GDateYear is 16 bits wide,
+ * just like #GDate.year. */
+ return ( (m > G_DATE_BAD_MONTH) &&
+ (m < 13) &&
+ (d > G_DATE_BAD_DAY) &&
+ (y > G_DATE_BAD_YEAR) && /* must check before using g_date_is_leap_year */
+ (d <= (g_date_is_leap_year (y) ?
+ days_in_months[1][m] : days_in_months[0][m])) );
+}
+
+
+/* "Julian days" just means an absolute number of days, where Day 1 ==
+ * Jan 1, Year 1
+ */
+static void
+g_date_update_julian (const GDate *const_d)
+{
+ GDate *d = (GDate *) const_d;
+ GDateYear year;
+ gint idx;
+
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (d->dmy != 0);
+ g_return_if_fail (!d->julian);
+ g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
+
+ /* What we actually do is: multiply years * 365 days in the year,
+ * add the number of years divided by 4, subtract the number of
+ * years divided by 100 and add the number of years divided by 400,
+ * which accounts for leap year stuff. Code from Steffen Beyer's
+ * DateCalc.
+ */
+
+ year = d->year - 1; /* we know d->year > 0 since it's valid */
+
+ d->julian_days = year * 365U;
+ d->julian_days += (year >>= 2); /* divide by 4 and add */
+ d->julian_days -= (year /= 25); /* divides original # years by 100 */
+ d->julian_days += year >> 2; /* divides by 4, which divides original by 400 */
+
+ idx = g_date_is_leap_year (d->year) ? 1 : 0;
+
+ d->julian_days += days_in_year[idx][d->month] + d->day;
+
+ g_return_if_fail (g_date_valid_julian (d->julian_days));
+
+ d->julian = TRUE;
+}
+
+static void
+g_date_update_dmy (const GDate *const_d)
+{
+ GDate *d = (GDate *) const_d;
+ GDateYear y;
+ GDateMonth m;
+ GDateDay day;
+
+ guint32 A, B, C, D, E, M;
+
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (d->julian);
+ g_return_if_fail (!d->dmy);
+ g_return_if_fail (g_date_valid_julian (d->julian_days));
+
+ /* Formula taken from the Calendar FAQ; the formula was for the
+ * Julian Period which starts on 1 January 4713 BC, so we add
+ * 1,721,425 to the number of days before doing the formula.
+ *
+ * I'm sure this can be simplified for our 1 January 1 AD period
+ * start, but I can't figure out how to unpack the formula.
+ */
+
+ A = d->julian_days + 1721425 + 32045;
+ B = ( 4 *(A + 36524) )/ 146097 - 1;
+ C = A - (146097 * B)/4;
+ D = ( 4 * (C + 365) ) / 1461 - 1;
+ E = C - ((1461*D) / 4);
+ M = (5 * (E - 1) + 2)/153;
+
+ m = M + 3 - (12*(M/10));
+ day = E - (153*M + 2)/5;
+ y = 100 * B + D - 4800 + (M/10);
+
+#ifdef G_ENABLE_DEBUG
+ if (!g_date_valid_dmy (day, m, y))
+ g_warning ("OOPS julian: %u computed dmy: %u %u %u",
+ d->julian_days, day, m, y);
+#endif
+
+ d->month = m;
+ d->day = day;
+ d->year = y;
+
+ d->dmy = TRUE;
+}
+
+/**
+ * g_date_get_weekday:
+ * @date: a #GDate
+ *
+ * Returns the day of the week for a #GDate. The date must be valid.
+ *
+ * Returns: day of the week as a #GDateWeekday.
+ */
+GDateWeekday
+g_date_get_weekday (const GDate *d)
+{
+ g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
+
+ if (!d->julian)
+ g_date_update_julian (d);
+
+ g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
+
+ return ((d->julian_days - 1) % 7) + 1;
+}
+
+/**
+ * g_date_get_month:
+ * @date: a #GDate to get the month from
+ *
+ * Returns the month of the year. The date must be valid.
+ *
+ * Returns: month of the year as a #GDateMonth
+ */
+GDateMonth
+g_date_get_month (const GDate *d)
+{
+ g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
+
+ return d->month;
+}
+
+/**
+ * g_date_get_year:
+ * @date: a #GDate
+ *
+ * Returns the year of a #GDate. The date must be valid.
+ *
+ * Returns: year in which the date falls
+ */
+GDateYear
+g_date_get_year (const GDate *d)
+{
+ g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);
+
+ return d->year;
+}
+
+/**
+ * g_date_get_day:
+ * @date: a #GDate to extract the day of the month from
+ *
+ * Returns the day of the month. The date must be valid.
+ *
+ * Returns: day of the month
+ */
+GDateDay
+g_date_get_day (const GDate *d)
+{
+ g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);
+
+ return d->day;
+}
+
+/**
+ * g_date_get_julian:
+ * @date: a #GDate to extract the Julian day from
+ *
+ * Returns the Julian day or "serial number" of the #GDate. The
+ * Julian day is simply the number of days since January 1, Year 1; i.e.,
+ * January 1, Year 1 is Julian day 1; January 2, Year 1 is Julian day 2,
+ * etc. The date must be valid.
+ *
+ * Returns: Julian day
+ */
+guint32
+g_date_get_julian (const GDate *d)
+{
+ g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
+
+ if (!d->julian)
+ g_date_update_julian (d);
+
+ g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN);
+
+ return d->julian_days;
+}
+
+/**
+ * g_date_get_day_of_year:
+ * @date: a #GDate to extract day of year from
+ *
+ * Returns the day of the year, where Jan 1 is the first day of the
+ * year. The date must be valid.
+ *
+ * Returns: day of the year
+ */
+guint
+g_date_get_day_of_year (const GDate *d)
+{
+ gint idx;
+
+ g_return_val_if_fail (g_date_valid (d), 0);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, 0);
+
+ idx = g_date_is_leap_year (d->year) ? 1 : 0;
+
+ return (days_in_year[idx][d->month] + d->day);
+}
+
+/**
+ * g_date_get_monday_week_of_year:
+ * @date: a #GDate
+ *
+ * Returns the week of the year, where weeks are understood to start on
+ * Monday. If the date is before the first Monday of the year, return 0.
+ * The date must be valid.
+ *
+ * Returns: week of the year
+ */
+guint
+g_date_get_monday_week_of_year (const GDate *d)
+{
+ GDateWeekday wd;
+ guint day;
+ GDate first;
+
+ g_return_val_if_fail (g_date_valid (d), 0);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, 0);
+
+ g_date_clear (&first, 1);
+
+ g_date_set_dmy (&first, 1, 1, d->year);
+
+ wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
+ day = g_date_get_day_of_year (d) - 1;
+
+ return ((day + wd)/7U + (wd == 0 ? 1 : 0));
+}
+
+/**
+ * g_date_get_sunday_week_of_year:
+ * @date: a #GDate
+ *
+ * Returns the week of the year during which this date falls, if
+ * weeks are understood to begin on Sunday. The date must be valid.
+ * Can return 0 if the day is before the first Sunday of the year.
+ *
+ * Returns: week number
+ */
+guint
+g_date_get_sunday_week_of_year (const GDate *d)
+{
+ GDateWeekday wd;
+ guint day;
+ GDate first;
+
+ g_return_val_if_fail (g_date_valid (d), 0);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, 0);
+
+ g_date_clear (&first, 1);
+
+ g_date_set_dmy (&first, 1, 1, d->year);
+
+ wd = g_date_get_weekday (&first);
+ if (wd == 7) wd = 0; /* make Sunday day 0 */
+ day = g_date_get_day_of_year (d) - 1;
+
+ return ((day + wd)/7U + (wd == 0 ? 1 : 0));
+}
+
+/**
+ * g_date_get_iso8601_week_of_year:
+ * @date: a valid #GDate
+ *
+ * Returns the week of the year, where weeks are interpreted according
+ * to ISO 8601.
+ *
+ * Returns: ISO 8601 week number of the year.
+ *
+ * Since: 2.6
+ **/
+guint
+g_date_get_iso8601_week_of_year (const GDate *d)
+{
+ guint j, d4, L, d1, w;
+
+ g_return_val_if_fail (g_date_valid (d), 0);
+
+ if (!d->julian)
+ g_date_update_julian (d);
+
+ g_return_val_if_fail (d->julian, 0);
+
+ /* Formula taken from the Calendar FAQ; the formula was for the
+ * Julian Period which starts on 1 January 4713 BC, so we add
+ * 1,721,425 to the number of days before doing the formula.
+ */
+ j = d->julian_days + 1721425;
+ d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461;
+ L = d4 / 1460;
+ d1 = ((d4 - L) % 365) + L;
+ w = d1 / 7 + 1;
+
+ return w;
+}
+
+/**
+ * g_date_days_between:
+ * @date1: the first date
+ * @date2: the second date
+ *
+ * Computes the number of days between two dates.
+ * If @date2 is prior to @date1, the returned value is negative.
+ * Both dates must be valid.
+ *
+ * Returns: the number of days between @date1 and @date2
+ */
+gint
+g_date_days_between (const GDate *d1,
+ const GDate *d2)
+{
+ g_return_val_if_fail (g_date_valid (d1), 0);
+ g_return_val_if_fail (g_date_valid (d2), 0);
+
+ return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
+}
+
+/**
+ * g_date_clear:
+ * @date: pointer to one or more dates to clear
+ * @n_dates: number of dates to clear
+ *
+ * Initializes one or more #GDate structs to a safe but invalid
+ * state. The cleared dates will not represent an existing date, but will
+ * not contain garbage. Useful to init a date declared on the stack.
+ * Validity can be tested with g_date_valid().
+ */
+void
+g_date_clear (GDate *d, guint ndates)
+{
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (ndates != 0);
+
+ memset (d, 0x0, ndates*sizeof (GDate));
+}
+
+G_LOCK_DEFINE_STATIC (g_date_global);
+
+/* These are for the parser, output to the user should use *
+ * g_date_strftime () - this creates more never-freed memory to annoy
+ * all those memory debugger users. :-)
+ */
+
+static gchar *long_month_names[13] =
+{
+ NULL,
+};
+
+static gchar *long_month_names_alternative[13] =
+{
+ NULL,
+};
+
+static gchar *short_month_names[13] =
+{
+ NULL,
+};
+
+static gchar *short_month_names_alternative[13] =
+{
+ NULL,
+};
+
+/* This tells us if we need to update the parse info */
+static gchar *current_locale = NULL;
+
+/* order of these in the current locale */
+static GDateDMY dmy_order[3] =
+{
+ G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
+};
+
+/* Where to chop two-digit years: i.e., for the 1930 default, numbers
+ * 29 and below are counted as in the year 2000, numbers 30 and above
+ * are counted as in the year 1900.
+ */
+
+static const GDateYear twodigit_start_year = 1930;
+
+/* It is impossible to enter a year between 1 AD and 99 AD with this
+ * in effect.
+ */
+static gboolean using_twodigit_years = FALSE;
+
+/* Adjustment of locale era to AD, non-zero means using locale era
+ */
+static gint locale_era_adjust = 0;
+
+struct _GDateParseTokens {
+ gint num_ints;
+ gint n[3];
+ guint month;
+};
+
+typedef struct _GDateParseTokens GDateParseTokens;
+
+static inline gboolean
+update_month_match (gsize *longest,
+ const gchar *haystack,
+ const gchar *needle)
+{
+ gsize length;
+
+ if (needle == NULL)
+ return FALSE;
+
+ length = strlen (needle);
+ if (*longest >= length)
+ return FALSE;
+
+ if (strstr (haystack, needle) == NULL)
+ return FALSE;
+
+ *longest = length;
+ return TRUE;
+}
+
+#define NUM_LEN 10
+
+/* HOLDS: g_date_global_lock */
+static void
+g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
+{
+ gchar num[4][NUM_LEN+1];
+ gint i;
+ const guchar *s;
+
+ /* We count 4, but store 3; so we can give an error
+ * if there are 4.
+ */
+ num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
+
+ s = (const guchar *) str;
+ pt->num_ints = 0;
+ while (*s && pt->num_ints < 4)
+ {
+
+ i = 0;
+ while (*s && g_ascii_isdigit (*s) && i < NUM_LEN)
+ {
+ num[pt->num_ints][i] = *s;
+ ++s;
+ ++i;
+ }
+
+ if (i > 0)
+ {
+ num[pt->num_ints][i] = '\0';
+ ++(pt->num_ints);
+ }
+
+ if (*s == '\0') break;
+
+ ++s;
+ }
+
+ pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
+ pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
+ pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
+
+ pt->month = G_DATE_BAD_MONTH;
+
+ if (pt->num_ints < 3)
+ {
+ gsize longest = 0;
+ gchar *casefold;
+ gchar *normalized;
+
+ casefold = g_utf8_casefold (str, -1);
+ normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+ g_free (casefold);
+
+ for (i = 1; i < 13; ++i)
+ {
+ /* Here month names may be in a genitive case if the language
+ * grammatical rules require it.
+ * Examples of how January may look in some languages:
+ * Catalan: "de gener", Croatian: "siječnja", Polish: "stycznia",
+ * Upper Sorbian: "januara".
+ * Note that most of the languages can't or don't use the the
+ * genitive case here so they use nominative everywhere.
+ * For example, English always uses "January".
+ */
+ if (update_month_match (&longest, normalized, long_month_names[i]))
+ pt->month = i;
+
+ /* Here month names will be in a nominative case.
+ * Examples of how January may look in some languages:
+ * Catalan: "gener", Croatian: "Siječanj", Polish: "styczeń",
+ * Upper Sorbian: "Januar".
+ */
+ if (update_month_match (&longest, normalized, long_month_names_alternative[i]))
+ pt->month = i;
+
+ /* Differences between abbreviated nominative and abbreviated
+ * genitive month names are visible in very few languages but
+ * let's handle them.
+ */
+ if (update_month_match (&longest, normalized, short_month_names[i]))
+ pt->month = i;
+
+ if (update_month_match (&longest, normalized, short_month_names_alternative[i]))
+ pt->month = i;
+ }
+
+ g_free (normalized);
+ }
+}
+
+/* HOLDS: g_date_global_lock */
+static void
+g_date_prepare_to_parse (const gchar *str,
+ GDateParseTokens *pt)
+{
+ const gchar *locale = setlocale (LC_TIME, NULL);
+ gboolean recompute_localeinfo = FALSE;
+ GDate d;
+
+ g_return_if_fail (locale != NULL); /* should not happen */
+
+ g_date_clear (&d, 1); /* clear for scratch use */
+
+ if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) )
+ recompute_localeinfo = TRUE; /* Uh, there used to be a reason for the temporary */
+
+ if (recompute_localeinfo)
+ {
+ int i = 1;
+ GDateParseTokens testpt;
+ gchar buf[128];
+
+ g_free (current_locale); /* still works if current_locale == NULL */
+
+ current_locale = g_strdup (locale);
+
+ short_month_names[0] = "Error";
+ long_month_names[0] = "Error";
+
+ while (i < 13)
+ {
+ gchar *casefold;
+
+ g_date_set_dmy (&d, 1, i, 1976);
+
+ g_return_if_fail (g_date_valid (&d));
+
+ g_date_strftime (buf, 127, "%b", &d);
+
+ casefold = g_utf8_casefold (buf, -1);
+ g_free (short_month_names[i]);
+ short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+ g_free (casefold);
+
+ g_date_strftime (buf, 127, "%B", &d);
+ casefold = g_utf8_casefold (buf, -1);
+ g_free (long_month_names[i]);
+ long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+ g_free (casefold);
+
+ g_date_strftime (buf, 127, "%Ob", &d);
+ casefold = g_utf8_casefold (buf, -1);
+ g_free (short_month_names_alternative[i]);
+ short_month_names_alternative[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+ g_free (casefold);
+
+ g_date_strftime (buf, 127, "%OB", &d);
+ casefold = g_utf8_casefold (buf, -1);
+ g_free (long_month_names_alternative[i]);
+ long_month_names_alternative[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
+ g_free (casefold);
+
+ ++i;
+ }
+
+ /* Determine DMY order */
+
+ /* had to pick a random day - don't change this, some strftimes
+ * are broken on some days, and this one is good so far. */
+ g_date_set_dmy (&d, 4, 7, 1976);
+
+ g_date_strftime (buf, 127, "%x", &d);
+
+ g_date_fill_parse_tokens (buf, &testpt);
+
+ using_twodigit_years = FALSE;
+ locale_era_adjust = 0;
+ dmy_order[0] = G_DATE_DAY;
+ dmy_order[1] = G_DATE_MONTH;
+ dmy_order[2] = G_DATE_YEAR;
+
+ i = 0;
+ while (i < testpt.num_ints)
+ {
+ switch (testpt.n[i])
+ {
+ case 7:
+ dmy_order[i] = G_DATE_MONTH;
+ break;
+ case 4:
+ dmy_order[i] = G_DATE_DAY;
+ break;
+ case 76:
+ using_twodigit_years = TRUE;
+ G_GNUC_FALLTHROUGH;
+ case 1976:
+ dmy_order[i] = G_DATE_YEAR;
+ break;
+ default:
+ /* assume locale era */
+ locale_era_adjust = 1976 - testpt.n[i];
+ dmy_order[i] = G_DATE_YEAR;
+ break;
+ }
+ ++i;
+ }
+
+#if defined(G_ENABLE_DEBUG) && 0
+ DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
+ i = 1;
+ while (i < 13)
+ {
+ DEBUG_MSG ((" %s %s", long_month_names[i], short_month_names[i]));
+ ++i;
+ }
+ DEBUG_MSG (("Alternative month names:"));
+ i = 1;
+ while (i < 13)
+ {
+ DEBUG_MSG ((" %s %s", long_month_names_alternative[i], short_month_names_alternative[i]));
+ ++i;
+ }
+ if (using_twodigit_years)
+ {
+ DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
+ }
+ {
+ gchar *strings[3];
+ i = 0;
+ while (i < 3)
+ {
+ switch (dmy_order[i])
+ {
+ case G_DATE_MONTH:
+ strings[i] = "Month";
+ break;
+ case G_DATE_YEAR:
+ strings[i] = "Year";
+ break;
+ case G_DATE_DAY:
+ strings[i] = "Day";
+ break;
+ default:
+ strings[i] = NULL;
+ break;
+ }
+ ++i;
+ }
+ DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
+ DEBUG_MSG (("**Sample date in this locale: '%s'", buf));
+ }
+#endif
+ }
+
+ g_date_fill_parse_tokens (str, pt);
+}
+
+static guint
+convert_twodigit_year (guint y)
+{
+ if (using_twodigit_years && y < 100)
+ {
+ guint two = twodigit_start_year % 100;
+ guint century = (twodigit_start_year / 100) * 100;
+
+ if (y < two)
+ century += 100;
+
+ y += century;
+ }
+ return y;
+}
+
+/**
+ * g_date_set_parse:
+ * @date: a #GDate to fill in
+ * @str: string to parse
+ *
+ * Parses a user-inputted string @str, and try to figure out what date it
+ * represents, taking the [current locale][setlocale] into account. If the
+ * string is successfully parsed, the date will be valid after the call.
+ * Otherwise, it will be invalid. You should check using g_date_valid()
+ * to see whether the parsing succeeded.
+ *
+ * This function is not appropriate for file formats and the like; it
+ * isn't very precise, and its exact behavior varies with the locale.
+ * It's intended to be a heuristic routine that guesses what the user
+ * means by a given string (and it does work pretty well in that
+ * capacity).
+ */
+void
+g_date_set_parse (GDate *d,
+ const gchar *str)
+{
+ GDateParseTokens pt;
+ guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
+ gsize str_len;
+
+ g_return_if_fail (d != NULL);
+
+ /* set invalid */
+ g_date_clear (d, 1);
+
+ /* Anything longer than this is ridiculous and could take a while to normalize.
+ * This limit is chosen arbitrarily. */
+ str_len = strlen (str);
+ if (str_len > 200)
+ return;
+
+ /* The input has to be valid UTF-8. */
+ if (!g_utf8_validate_len (str, str_len, NULL))
+ return;
+
+ G_LOCK (g_date_global);
+
+ g_date_prepare_to_parse (str, &pt);
+
+ DEBUG_MSG (("Found %d ints, '%d' '%d' '%d' and written out month %d",
+ pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
+
+
+ if (pt.num_ints == 4)
+ {
+ G_UNLOCK (g_date_global);
+ return; /* presumably a typo; bail out. */
+ }
+
+ if (pt.num_ints > 1)
+ {
+ int i = 0;
+ int j = 0;
+
+ g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
+
+ while (i < pt.num_ints && j < 3)
+ {
+ switch (dmy_order[j])
+ {
+ case G_DATE_MONTH:
+ {
+ if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
+ {
+ m = pt.month;
+ ++j; /* skip months, but don't skip this number */
+ continue;
+ }
+ else
+ m = pt.n[i];
+ }
+ break;
+ case G_DATE_DAY:
+ {
+ if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
+ {
+ day = 1;
+ ++j; /* skip days, since we may have month/year */
+ continue;
+ }
+ day = pt.n[i];
+ }
+ break;
+ case G_DATE_YEAR:
+ {
+ y = pt.n[i];
+
+ if (locale_era_adjust != 0)
+ {
+ y += locale_era_adjust;
+ }
+
+ y = convert_twodigit_year (y);
+ }
+ break;
+ default:
+ break;
+ }
+
+ ++i;
+ ++j;
+ }
+
+
+ if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
+ {
+ /* Try YYYY MM DD */
+ y = pt.n[0];
+ m = pt.n[1];
+ day = pt.n[2];
+
+ if (using_twodigit_years && y < 100)
+ y = G_DATE_BAD_YEAR; /* avoids ambiguity */
+ }
+ else if (pt.num_ints == 2)
+ {
+ if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
+ m = pt.month;
+ }
+ }
+ else if (pt.num_ints == 1)
+ {
+ if (pt.month != G_DATE_BAD_MONTH)
+ {
+ /* Month name and year? */
+ m = pt.month;
+ day = 1;
+ y = pt.n[0];
+ }
+ else
+ {
+ /* Try yyyymmdd and yymmdd */
+
+ m = (pt.n[0]/100) % 100;
+ day = pt.n[0] % 100;
+ y = pt.n[0]/10000;
+
+ y = convert_twodigit_year (y);
+ }
+ }
+
+ /* See if we got anything valid out of all this. */
+ /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
+ if (y < 8000 && g_date_valid_dmy (day, m, y))
+ {
+ d->month = m;
+ d->day = day;
+ d->year = y;
+ d->dmy = TRUE;
+ }
+#ifdef G_ENABLE_DEBUG
+ else
+ {
+ DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
+ }
+#endif
+ G_UNLOCK (g_date_global);
+}
+
+/**
+ * g_date_set_time_t:
+ * @date: a #GDate
+ * @timet: time_t value to set
+ *
+ * Sets the value of a date to the date corresponding to a time
+ * specified as a time_t. The time to date conversion is done using
+ * the user's current timezone.
+ *
+ * To set the value of a date to the current day, you could write:
+ * |[
+ * time_t now = time (NULL);
+ * if (now == (time_t) -1)
+ * // handle the error
+ * g_date_set_time_t (date, now);
+ * ]|
+ *
+ * Since: 2.10
+ */
+void
+g_date_set_time_t (GDate *date,
+ time_t timet)
+{
+ struct tm tm;
+
+ g_return_if_fail (date != NULL);
+
+#ifdef HAVE_LOCALTIME_R
+ localtime_r (&timet, &tm);
+#else
+ {
+ struct tm *ptm = localtime (&timet);
+
+ if (ptm == NULL)
+ {
+ /* Happens at least in Microsoft's C library if you pass a
+ * negative time_t. Use 2000-01-01 as default date.
+ */
+#ifndef G_DISABLE_CHECKS
+ g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
+#endif
+
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_year = 100;
+ }
+ else
+ memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
+ }
+#endif
+
+ date->julian = FALSE;
+
+ date->month = tm.tm_mon + 1;
+ date->day = tm.tm_mday;
+ date->year = tm.tm_year + 1900;
+
+ g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year));
+
+ date->dmy = TRUE;
+}
+
+
+/**
+ * g_date_set_time:
+ * @date: a #GDate.
+ * @time_: #GTime value to set.
+ *
+ * Sets the value of a date from a #GTime value.
+ * The time to date conversion is done using the user's current timezone.
+ *
+ * Deprecated: 2.10: Use g_date_set_time_t() instead.
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+void
+g_date_set_time (GDate *date,
+ GTime time_)
+{
+ g_date_set_time_t (date, (time_t) time_);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/**
+ * g_date_set_time_val:
+ * @date: a #GDate
+ * @timeval: #GTimeVal value to set
+ *
+ * Sets the value of a date from a #GTimeVal value. Note that the
+ * @tv_usec member is ignored, because #GDate can't make use of the
+ * additional precision.
+ *
+ * The time to date conversion is done using the user's current timezone.
+ *
+ * Since: 2.10
+ * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use g_date_set_time_t()
+ * instead.
+ */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+void
+g_date_set_time_val (GDate *date,
+ GTimeVal *timeval)
+{
+ g_date_set_time_t (date, (time_t) timeval->tv_sec);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/**
+ * g_date_set_month:
+ * @date: a #GDate
+ * @month: month to set
+ *
+ * Sets the month of the year for a #GDate. If the resulting
+ * day-month-year triplet is invalid, the date will be invalid.
+ */
+void
+g_date_set_month (GDate *d,
+ GDateMonth m)
+{
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (g_date_valid_month (m));
+
+ if (d->julian && !d->dmy) g_date_update_dmy(d);
+ d->julian = FALSE;
+
+ d->month = m;
+
+ if (g_date_valid_dmy (d->day, d->month, d->year))
+ d->dmy = TRUE;
+ else
+ d->dmy = FALSE;
+}
+
+/**
+ * g_date_set_day:
+ * @date: a #GDate
+ * @day: day to set
+ *
+ * Sets the day of the month for a #GDate. If the resulting
+ * day-month-year triplet is invalid, the date will be invalid.
+ */
+void
+g_date_set_day (GDate *d,
+ GDateDay day)
+{
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (g_date_valid_day (day));
+
+ if (d->julian && !d->dmy) g_date_update_dmy(d);
+ d->julian = FALSE;
+
+ d->day = day;
+
+ if (g_date_valid_dmy (d->day, d->month, d->year))
+ d->dmy = TRUE;
+ else
+ d->dmy = FALSE;
+}
+
+/**
+ * g_date_set_year:
+ * @date: a #GDate
+ * @year: year to set
+ *
+ * Sets the year for a #GDate. If the resulting day-month-year
+ * triplet is invalid, the date will be invalid.
+ */
+void
+g_date_set_year (GDate *d,
+ GDateYear y)
+{
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (g_date_valid_year (y));
+
+ if (d->julian && !d->dmy) g_date_update_dmy(d);
+ d->julian = FALSE;
+
+ d->year = y;
+
+ if (g_date_valid_dmy (d->day, d->month, d->year))
+ d->dmy = TRUE;
+ else
+ d->dmy = FALSE;
+}
+
+/**
+ * g_date_set_dmy:
+ * @date: a #GDate
+ * @day: day
+ * @month: month
+ * @y: year
+ *
+ * Sets the value of a #GDate from a day, month, and year.
+ * The day-month-year triplet must be valid; if you aren't
+ * sure it is, call g_date_valid_dmy() to check before you
+ * set it.
+ */
+void
+g_date_set_dmy (GDate *d,
+ GDateDay day,
+ GDateMonth m,
+ GDateYear y)
+{
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (g_date_valid_dmy (day, m, y));
+
+ d->julian = FALSE;
+
+ d->month = m;
+ d->day = day;
+ d->year = y;
+
+ d->dmy = TRUE;
+}
+
+/**
+ * g_date_set_julian:
+ * @date: a #GDate
+ * @julian_date: Julian day number (days since January 1, Year 1)
+ *
+ * Sets the value of a #GDate from a Julian day number.
+ */
+void
+g_date_set_julian (GDate *d,
+ guint32 j)
+{
+ g_return_if_fail (d != NULL);
+ g_return_if_fail (g_date_valid_julian (j));
+
+ d->julian_days = j;
+ d->julian = TRUE;
+ d->dmy = FALSE;
+}
+
+/**
+ * g_date_is_first_of_month:
+ * @date: a #GDate to check
+ *
+ * Returns %TRUE if the date is on the first of a month.
+ * The date must be valid.
+ *
+ * Returns: %TRUE if the date is the first of the month
+ */
+gboolean
+g_date_is_first_of_month (const GDate *d)
+{
+ g_return_val_if_fail (g_date_valid (d), FALSE);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, FALSE);
+
+ if (d->day == 1) return TRUE;
+ else return FALSE;
+}
+
+/**
+ * g_date_is_last_of_month:
+ * @date: a #GDate to check
+ *
+ * Returns %TRUE if the date is the last day of the month.
+ * The date must be valid.
+ *
+ * Returns: %TRUE if the date is the last day of the month
+ */
+gboolean
+g_date_is_last_of_month (const GDate *d)
+{
+ gint idx;
+
+ g_return_val_if_fail (g_date_valid (d), FALSE);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_val_if_fail (d->dmy, FALSE);
+
+ idx = g_date_is_leap_year (d->year) ? 1 : 0;
+
+ if (d->day == days_in_months[idx][d->month]) return TRUE;
+ else return FALSE;
+}
+
+/**
+ * g_date_add_days:
+ * @date: a #GDate to increment
+ * @n_days: number of days to move the date forward
+ *
+ * Increments a date some number of days.
+ * To move forward by weeks, add weeks*7 days.
+ * The date must be valid.
+ */
+void
+g_date_add_days (GDate *d,
+ guint ndays)
+{
+ g_return_if_fail (g_date_valid (d));
+
+ if (!d->julian)
+ g_date_update_julian (d);
+
+ g_return_if_fail (d->julian);
+ g_return_if_fail (ndays <= G_MAXUINT32 - d->julian_days);
+
+ d->julian_days += ndays;
+ d->dmy = FALSE;
+}
+
+/**
+ * g_date_subtract_days:
+ * @date: a #GDate to decrement
+ * @n_days: number of days to move
+ *
+ * Moves a date some number of days into the past.
+ * To move by weeks, just move by weeks*7 days.
+ * The date must be valid.
+ */
+void
+g_date_subtract_days (GDate *d,
+ guint ndays)
+{
+ g_return_if_fail (g_date_valid (d));
+
+ if (!d->julian)
+ g_date_update_julian (d);
+
+ g_return_if_fail (d->julian);
+ g_return_if_fail (d->julian_days > ndays);
+
+ d->julian_days -= ndays;
+ d->dmy = FALSE;
+}
+
+/**
+ * g_date_add_months:
+ * @date: a #GDate to increment
+ * @n_months: number of months to move forward
+ *
+ * Increments a date by some number of months.
+ * If the day of the month is greater than 28,
+ * this routine may change the day of the month
+ * (because the destination month may not have
+ * the current day in it). The date must be valid.
+ */
+void
+g_date_add_months (GDate *d,
+ guint nmonths)
+{
+ guint years, months;
+ gint idx;
+
+ g_return_if_fail (g_date_valid (d));
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_if_fail (d->dmy != 0);
+ g_return_if_fail (nmonths <= G_MAXUINT - (d->month - 1));
+
+ nmonths += d->month - 1;
+
+ years = nmonths/12;
+ months = nmonths%12;
+
+ g_return_if_fail (years <= (guint) (G_MAXUINT16 - d->year));
+
+ d->month = months + 1;
+ d->year += years;
+
+ idx = g_date_is_leap_year (d->year) ? 1 : 0;
+
+ if (d->day > days_in_months[idx][d->month])
+ d->day = days_in_months[idx][d->month];
+
+ d->julian = FALSE;
+
+ g_return_if_fail (g_date_valid (d));
+}
+
+/**
+ * g_date_subtract_months:
+ * @date: a #GDate to decrement
+ * @n_months: number of months to move
+ *
+ * Moves a date some number of months into the past.
+ * If the current day of the month doesn't exist in
+ * the destination month, the day of the month
+ * may change. The date must be valid.
+ */
+void
+g_date_subtract_months (GDate *d,
+ guint nmonths)
+{
+ guint years, months;
+ gint idx;
+
+ g_return_if_fail (g_date_valid (d));
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_if_fail (d->dmy != 0);
+
+ years = nmonths/12;
+ months = nmonths%12;
+
+ g_return_if_fail (d->year > years);
+
+ d->year -= years;
+
+ if (d->month > months) d->month -= months;
+ else
+ {
+ months -= d->month;
+ d->month = 12 - months;
+ d->year -= 1;
+ }
+
+ idx = g_date_is_leap_year (d->year) ? 1 : 0;
+
+ if (d->day > days_in_months[idx][d->month])
+ d->day = days_in_months[idx][d->month];
+
+ d->julian = FALSE;
+
+ g_return_if_fail (g_date_valid (d));
+}
+
+/**
+ * g_date_add_years:
+ * @date: a #GDate to increment
+ * @n_years: number of years to move forward
+ *
+ * Increments a date by some number of years.
+ * If the date is February 29, and the destination
+ * year is not a leap year, the date will be changed
+ * to February 28. The date must be valid.
+ */
+void
+g_date_add_years (GDate *d,
+ guint nyears)
+{
+ g_return_if_fail (g_date_valid (d));
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_if_fail (d->dmy != 0);
+ g_return_if_fail (nyears <= (guint) (G_MAXUINT16 - d->year));
+
+ d->year += nyears;
+
+ if (d->month == 2 && d->day == 29)
+ {
+ if (!g_date_is_leap_year (d->year))
+ d->day = 28;
+ }
+
+ d->julian = FALSE;
+}
+
+/**
+ * g_date_subtract_years:
+ * @date: a #GDate to decrement
+ * @n_years: number of years to move
+ *
+ * Moves a date some number of years into the past.
+ * If the current day doesn't exist in the destination
+ * year (i.e. it's February 29 and you move to a non-leap-year)
+ * then the day is changed to February 29. The date
+ * must be valid.
+ */
+void
+g_date_subtract_years (GDate *d,
+ guint nyears)
+{
+ g_return_if_fail (g_date_valid (d));
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_if_fail (d->dmy != 0);
+ g_return_if_fail (d->year > nyears);
+
+ d->year -= nyears;
+
+ if (d->month == 2 && d->day == 29)
+ {
+ if (!g_date_is_leap_year (d->year))
+ d->day = 28;
+ }
+
+ d->julian = FALSE;
+}
+
+/**
+ * g_date_is_leap_year:
+ * @year: year to check
+ *
+ * Returns %TRUE if the year is a leap year.
+ *
+ * For the purposes of this function, leap year is every year
+ * divisible by 4 unless that year is divisible by 100. If it
+ * is divisible by 100 it would be a leap year only if that year
+ * is also divisible by 400.
+ *
+ * Returns: %TRUE if the year is a leap year
+ */
+gboolean
+g_date_is_leap_year (GDateYear year)
+{
+ g_return_val_if_fail (g_date_valid_year (year), FALSE);
+
+ return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
+ (year % 400) == 0 );
+}
+
+/**
+ * g_date_get_days_in_month:
+ * @month: month
+ * @year: year
+ *
+ * Returns the number of days in a month, taking leap
+ * years into account.
+ *
+ * Returns: number of days in @month during the @year
+ */
+guint8
+g_date_get_days_in_month (GDateMonth month,
+ GDateYear year)
+{
+ gint idx;
+
+ g_return_val_if_fail (g_date_valid_year (year), 0);
+ g_return_val_if_fail (g_date_valid_month (month), 0);
+
+ idx = g_date_is_leap_year (year) ? 1 : 0;
+
+ return days_in_months[idx][month];
+}
+
+/**
+ * g_date_get_monday_weeks_in_year:
+ * @year: a year
+ *
+ * Returns the number of weeks in the year, where weeks
+ * are taken to start on Monday. Will be 52 or 53. The
+ * date must be valid. (Years always have 52 7-day periods,
+ * plus 1 or 2 extra days depending on whether it's a leap
+ * year. This function is basically telling you how many
+ * Mondays are in the year, i.e. there are 53 Mondays if
+ * one of the extra days happens to be a Monday.)
+ *
+ * Returns: number of Mondays in the year
+ */
+guint8
+g_date_get_monday_weeks_in_year (GDateYear year)
+{
+ GDate d;
+
+ g_return_val_if_fail (g_date_valid_year (year), 0);
+
+ g_date_clear (&d, 1);
+ g_date_set_dmy (&d, 1, 1, year);
+ if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
+ g_date_set_dmy (&d, 31, 12, year);
+ if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
+ if (g_date_is_leap_year (year))
+ {
+ g_date_set_dmy (&d, 2, 1, year);
+ if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
+ g_date_set_dmy (&d, 30, 12, year);
+ if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
+ }
+ return 52;
+}
+
+/**
+ * g_date_get_sunday_weeks_in_year:
+ * @year: year to count weeks in
+ *
+ * Returns the number of weeks in the year, where weeks
+ * are taken to start on Sunday. Will be 52 or 53. The
+ * date must be valid. (Years always have 52 7-day periods,
+ * plus 1 or 2 extra days depending on whether it's a leap
+ * year. This function is basically telling you how many
+ * Sundays are in the year, i.e. there are 53 Sundays if
+ * one of the extra days happens to be a Sunday.)
+ *
+ * Returns: the number of weeks in @year
+ */
+guint8
+g_date_get_sunday_weeks_in_year (GDateYear year)
+{
+ GDate d;
+
+ g_return_val_if_fail (g_date_valid_year (year), 0);
+
+ g_date_clear (&d, 1);
+ g_date_set_dmy (&d, 1, 1, year);
+ if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
+ g_date_set_dmy (&d, 31, 12, year);
+ if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
+ if (g_date_is_leap_year (year))
+ {
+ g_date_set_dmy (&d, 2, 1, year);
+ if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
+ g_date_set_dmy (&d, 30, 12, year);
+ if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
+ }
+ return 52;
+}
+
+/**
+ * g_date_compare:
+ * @lhs: first date to compare
+ * @rhs: second date to compare
+ *
+ * qsort()-style comparison function for dates.
+ * Both dates must be valid.
+ *
+ * Returns: 0 for equal, less than zero if @lhs is less than @rhs,
+ * greater than zero if @lhs is greater than @rhs
+ */
+gint
+g_date_compare (const GDate *lhs,
+ const GDate *rhs)
+{
+ g_return_val_if_fail (lhs != NULL, 0);
+ g_return_val_if_fail (rhs != NULL, 0);
+ g_return_val_if_fail (g_date_valid (lhs), 0);
+ g_return_val_if_fail (g_date_valid (rhs), 0);
+
+ /* Remember the self-comparison case! I think it works right now. */
+
+ while (TRUE)
+ {
+ if (lhs->julian && rhs->julian)
+ {
+ if (lhs->julian_days < rhs->julian_days) return -1;
+ else if (lhs->julian_days > rhs->julian_days) return 1;
+ else return 0;
+ }
+ else if (lhs->dmy && rhs->dmy)
+ {
+ if (lhs->year < rhs->year) return -1;
+ else if (lhs->year > rhs->year) return 1;
+ else
+ {
+ if (lhs->month < rhs->month) return -1;
+ else if (lhs->month > rhs->month) return 1;
+ else
+ {
+ if (lhs->day < rhs->day) return -1;
+ else if (lhs->day > rhs->day) return 1;
+ else return 0;
+ }
+
+ }
+
+ }
+ else
+ {
+ if (!lhs->julian) g_date_update_julian (lhs);
+ if (!rhs->julian) g_date_update_julian (rhs);
+ g_return_val_if_fail (lhs->julian, 0);
+ g_return_val_if_fail (rhs->julian, 0);
+ }
+
+ }
+ return 0; /* warnings */
+}
+
+/**
+ * g_date_to_struct_tm:
+ * @date: a #GDate to set the struct tm from
+ * @tm: (not nullable): struct tm to fill
+ *
+ * Fills in the date-related bits of a struct tm using the @date value.
+ * Initializes the non-date parts with something safe but meaningless.
+ */
+void
+g_date_to_struct_tm (const GDate *d,
+ struct tm *tm)
+{
+ GDateWeekday day;
+
+ g_return_if_fail (g_date_valid (d));
+ g_return_if_fail (tm != NULL);
+
+ if (!d->dmy)
+ g_date_update_dmy (d);
+
+ g_return_if_fail (d->dmy != 0);
+
+ /* zero all the irrelevant fields to be sure they're valid */
+
+ /* On Linux and maybe other systems, there are weird non-POSIX
+ * fields on the end of struct tm that choke strftime if they
+ * contain garbage. So we need to 0 the entire struct, not just the
+ * fields we know to exist.
+ */
+
+ memset (tm, 0x0, sizeof (struct tm));
+
+ tm->tm_mday = d->day;
+ tm->tm_mon = d->month - 1; /* 0-11 goes in tm */
+ tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
+
+ day = g_date_get_weekday (d);
+ if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
+
+ tm->tm_wday = (int)day;
+
+ tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
+ tm->tm_isdst = -1; /* -1 means "information not available" */
+}
+
+/**
+ * g_date_clamp:
+ * @date: a #GDate to clamp
+ * @min_date: minimum accepted value for @date
+ * @max_date: maximum accepted value for @date
+ *
+ * If @date is prior to @min_date, sets @date equal to @min_date.
+ * If @date falls after @max_date, sets @date equal to @max_date.
+ * Otherwise, @date is unchanged.
+ * Either of @min_date and @max_date may be %NULL.
+ * All non-%NULL dates must be valid.
+ */
+void
+g_date_clamp (GDate *date,
+ const GDate *min_date,
+ const GDate *max_date)
+{
+ g_return_if_fail (g_date_valid (date));
+
+ if (min_date != NULL)
+ g_return_if_fail (g_date_valid (min_date));
+
+ if (max_date != NULL)
+ g_return_if_fail (g_date_valid (max_date));
+
+ if (min_date != NULL && max_date != NULL)
+ g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
+
+ if (min_date && g_date_compare (date, min_date) < 0)
+ *date = *min_date;
+
+ if (max_date && g_date_compare (max_date, date) < 0)
+ *date = *max_date;
+}
+
+/**
+ * g_date_order:
+ * @date1: the first date
+ * @date2: the second date
+ *
+ * Checks if @date1 is less than or equal to @date2,
+ * and swap the values if this is not the case.
+ */
+void
+g_date_order (GDate *date1,
+ GDate *date2)
+{
+ g_return_if_fail (g_date_valid (date1));
+ g_return_if_fail (g_date_valid (date2));
+
+ if (g_date_compare (date1, date2) > 0)
+ {
+ GDate tmp = *date1;
+ *date1 = *date2;
+ *date2 = tmp;
+ }
+}
+
+#ifdef G_OS_WIN32
+static gboolean
+append_month_name (GArray *result,
+ LCID lcid,
+ SYSTEMTIME *systemtime,
+ gboolean abbreviated,
+ gboolean alternative)
+{
+ int n;
+ WORD base;
+ LPCWSTR lpFormat;
+
+ if (alternative)
+ {
+ base = abbreviated ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1;
+ n = GetLocaleInfoW (lcid, base + systemtime->wMonth - 1, NULL, 0);
+ if (n == 0)
+ return FALSE;
+
+ g_array_set_size (result, result->len + n);
+ if (GetLocaleInfoW (lcid, base + systemtime->wMonth - 1,
+ ((wchar_t *) result->data) + result->len - n, n) != n)
+ return FALSE;
+
+ g_array_set_size (result, result->len - 1);
+ }
+ else
+ {
+ /* According to MSDN, this is the correct method to obtain
+ * the form of the month name used when formatting a full
+ * date; it must be a genitive case in some languages.
+ *
+ * (n == 0) indicates an error, whereas (n < 2) is something we’d never
+ * expect from the given format string, and would break the subsequent code.
+ */
+ lpFormat = abbreviated ? L"ddMMM" : L"ddMMMM";
+ n = GetDateFormatW (lcid, 0, systemtime, lpFormat, NULL, 0);
+ if (n < 2)
+ return FALSE;
+
+ g_array_set_size (result, result->len + n);
+ if (GetDateFormatW (lcid, 0, systemtime, lpFormat,
+ ((wchar_t *) result->data) + result->len - n, n) != n)
+ return FALSE;
+
+ /* We have obtained a day number as two digits and the month name.
+ * Now let's get rid of those two digits: overwrite them with the
+ * month name.
+ */
+ memmove (((wchar_t *) result->data) + result->len - n,
+ ((wchar_t *) result->data) + result->len - n + 2,
+ (n - 2) * sizeof (wchar_t));
+ g_array_set_size (result, result->len - 3);
+ }
+
+ return TRUE;
+}
+
+static gsize
+win32_strftime_helper (const GDate *d,
+ const gchar *format,
+ const struct tm *tm,
+ gchar *s,
+ gsize slen)
+{
+ SYSTEMTIME systemtime;
+ TIME_ZONE_INFORMATION tzinfo;
+ LCID lcid;
+ int n, k;
+ GArray *result;
+ const gchar *p;
+ gunichar c, modifier;
+ const wchar_t digits[] = L"0123456789";
+ gchar *convbuf;
+ glong convlen = 0;
+ gsize retval;
+
+ systemtime.wYear = tm->tm_year + 1900;
+ systemtime.wMonth = tm->tm_mon + 1;
+ systemtime.wDayOfWeek = tm->tm_wday;
+ systemtime.wDay = tm->tm_mday;
+ systemtime.wHour = tm->tm_hour;
+ systemtime.wMinute = tm->tm_min;
+ systemtime.wSecond = tm->tm_sec;
+ systemtime.wMilliseconds = 0;
+
+ lcid = GetThreadLocale ();
+ result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2));
+
+ p = format;
+ while (*p)
+ {
+ c = g_utf8_get_char (p);
+ if (c == '%')
+ {
+ p = g_utf8_next_char (p);
+ if (!*p)
+ {
+ s[0] = '\0';
+ g_array_free (result, TRUE);
+
+ return 0;
+ }
+
+ modifier = '\0';
+ c = g_utf8_get_char (p);
+ if (c == 'E' || c == 'O')
+ {
+ /* "%OB", "%Ob", and "%Oh" are supported, ignore other modified
+ * conversion specifiers for now.
+ */
+ modifier = c;
+ p = g_utf8_next_char (p);
+ if (!*p)
+ {
+ s[0] = '\0';
+ g_array_free (result, TRUE);
+
+ return 0;
+ }
+
+ c = g_utf8_get_char (p);
+ }
+
+ switch (c)
+ {
+ case 'a':
+ if (systemtime.wDayOfWeek == 0)
+ k = 6;
+ else
+ k = systemtime.wDayOfWeek - 1;
+ n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0);
+ g_array_set_size (result, result->len + n);
+ GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ break;
+ case 'A':
+ if (systemtime.wDayOfWeek == 0)
+ k = 6;
+ else
+ k = systemtime.wDayOfWeek - 1;
+ n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0);
+ g_array_set_size (result, result->len + n);
+ GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ break;
+ case 'b':
+ case 'h':
+ if (!append_month_name (result, lcid, &systemtime, TRUE, modifier == 'O'))
+ {
+ /* Ignore the error; this placeholder will be replaced with nothing */
+ }
+ break;
+ case 'B':
+ if (!append_month_name (result, lcid, &systemtime, FALSE, modifier == 'O'))
+ {
+ /* Ignore the error; this placeholder will be replaced with nothing */
+ }
+ break;
+ case 'c':
+ n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
+ if (n > 0)
+ {
+ g_array_set_size (result, result->len + n);
+ GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ }
+ g_array_append_vals (result, L" ", 1);
+ n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
+ if (n > 0)
+ {
+ g_array_set_size (result, result->len + n);
+ GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ }
+ break;
+ case 'C':
+ g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
+ g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1);
+ break;
+ case 'd':
+ g_array_append_vals (result, digits + systemtime.wDay/10, 1);
+ g_array_append_vals (result, digits + systemtime.wDay%10, 1);
+ break;
+ case 'D':
+ g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
+ g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
+ g_array_append_vals (result, L"/", 1);
+ g_array_append_vals (result, digits + systemtime.wDay/10, 1);
+ g_array_append_vals (result, digits + systemtime.wDay%10, 1);
+ g_array_append_vals (result, L"/", 1);
+ g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
+ g_array_append_vals (result, digits + systemtime.wYear%10, 1);
+ break;
+ case 'e':
+ if (systemtime.wDay >= 10)
+ g_array_append_vals (result, digits + systemtime.wDay/10, 1);
+ else
+ g_array_append_vals (result, L" ", 1);
+ g_array_append_vals (result, digits + systemtime.wDay%10, 1);
+ break;
+
+ /* A GDate has no time fields, so for now we can
+ * hardcode all time conversions into zeros (or 12 for
+ * %I). The alternative code snippets in the #else
+ * branches are here ready to be taken into use when
+ * needed by a g_strftime() or g_date_and_time_format()
+ * or whatever.
+ */
+ case 'H':
+#if 1
+ g_array_append_vals (result, L"00", 2);
+#else
+ g_array_append_vals (result, digits + systemtime.wHour/10, 1);
+ g_array_append_vals (result, digits + systemtime.wHour%10, 1);
+#endif
+ break;
+ case 'I':
+#if 1
+ g_array_append_vals (result, L"12", 2);
+#else
+ if (systemtime.wHour == 0)
+ g_array_append_vals (result, L"12", 2);
+ else
+ {
+ g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
+ g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
+ }
+#endif
+ break;
+ case 'j':
+ g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1);
+ g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1);
+ g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1);
+ break;
+ case 'm':
+ g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
+ g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
+ break;
+ case 'M':
+#if 1
+ g_array_append_vals (result, L"00", 2);
+#else
+ g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
+ g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
+#endif
+ break;
+ case 'n':
+ g_array_append_vals (result, L"\n", 1);
+ break;
+ case 'p':
+ n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
+ if (n > 0)
+ {
+ g_array_set_size (result, result->len + n);
+ GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ }
+ break;
+ case 'r':
+ /* This is a rather odd format. Hard to say what to do.
+ * Let's always use the POSIX %I:%M:%S %p
+ */
+#if 1
+ g_array_append_vals (result, L"12:00:00", 8);
+#else
+ if (systemtime.wHour == 0)
+ g_array_append_vals (result, L"12", 2);
+ else
+ {
+ g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
+ g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
+ }
+ g_array_append_vals (result, L":", 1);
+ g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
+ g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
+ g_array_append_vals (result, L":", 1);
+ g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
+ g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
+ g_array_append_vals (result, L" ", 1);
+#endif
+ n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
+ if (n > 0)
+ {
+ g_array_set_size (result, result->len + n);
+ GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ }
+ break;
+ case 'R':
+#if 1
+ g_array_append_vals (result, L"00:00", 5);
+#else
+ g_array_append_vals (result, digits + systemtime.wHour/10, 1);
+ g_array_append_vals (result, digits + systemtime.wHour%10, 1);
+ g_array_append_vals (result, L":", 1);
+ g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
+ g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
+#endif
+ break;
+ case 'S':
+#if 1
+ g_array_append_vals (result, L"00", 2);
+#else
+ g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
+ g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
+#endif
+ break;
+ case 't':
+ g_array_append_vals (result, L"\t", 1);
+ break;
+ case 'T':
+#if 1
+ g_array_append_vals (result, L"00:00:00", 8);
+#else
+ g_array_append_vals (result, digits + systemtime.wHour/10, 1);
+ g_array_append_vals (result, digits + systemtime.wHour%10, 1);
+ g_array_append_vals (result, L":", 1);
+ g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
+ g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
+ g_array_append_vals (result, L":", 1);
+ g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
+ g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
+#endif
+ break;
+ case 'u':
+ if (systemtime.wDayOfWeek == 0)
+ g_array_append_vals (result, L"7", 1);
+ else
+ g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
+ break;
+ case 'U':
+ n = g_date_get_sunday_week_of_year (d);
+ g_array_append_vals (result, digits + n/10, 1);
+ g_array_append_vals (result, digits + n%10, 1);
+ break;
+ case 'V':
+ n = g_date_get_iso8601_week_of_year (d);
+ g_array_append_vals (result, digits + n/10, 1);
+ g_array_append_vals (result, digits + n%10, 1);
+ break;
+ case 'w':
+ g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
+ break;
+ case 'W':
+ n = g_date_get_monday_week_of_year (d);
+ g_array_append_vals (result, digits + n/10, 1);
+ g_array_append_vals (result, digits + n%10, 1);
+ break;
+ case 'x':
+ n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
+ if (n > 0)
+ {
+ g_array_set_size (result, result->len + n);
+ GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ }
+ break;
+ case 'X':
+ n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
+ if (n > 0)
+ {
+ g_array_set_size (result, result->len + n);
+ GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
+ g_array_set_size (result, result->len - 1);
+ }
+ break;
+ case 'y':
+ g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
+ g_array_append_vals (result, digits + systemtime.wYear%10, 1);
+ break;
+ case 'Y':
+ g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
+ g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1);
+ g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
+ g_array_append_vals (result, digits + systemtime.wYear%10, 1);
+ break;
+ case 'Z':
+ n = GetTimeZoneInformation (&tzinfo);
+ if (n == TIME_ZONE_ID_UNKNOWN || n == TIME_ZONE_ID_STANDARD)
+ g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName));
+ else if (n == TIME_ZONE_ID_DAYLIGHT)
+ g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName));
+ break;
+ case '%':
+ g_array_append_vals (result, L"%", 1);
+ break;
+ }
+ }
+ else if (c <= 0xFFFF)
+ {
+ wchar_t wc = c;
+ g_array_append_vals (result, &wc, 1);
+ }
+ else
+ {
+ glong nwc;
+ wchar_t *ws;
+
+ ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL);
+ g_array_append_vals (result, ws, nwc);
+ g_free (ws);
+ }
+ p = g_utf8_next_char (p);
+ }
+
+ convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL);
+ g_array_free (result, TRUE);
+
+ if (!convbuf)
+ {
+ s[0] = '\0';
+ return 0;
+ }
+
+ g_assert (convlen >= 0);
+ if ((gsize) convlen >= slen)
+ {
+ /* Ensure only whole characters are copied into the buffer. */
+ gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
+ g_assert (end != NULL);
+ convlen = end - convbuf;
+
+ /* Return 0 because the buffer isn't large enough. */
+ retval = 0;
+ }
+ else
+ retval = convlen;
+
+ memcpy (s, convbuf, convlen);
+ s[convlen] = '\0';
+ g_free (convbuf);
+
+ return retval;
+}
+
+#endif
+
+/**
+ * g_date_strftime:
+ * @s: destination buffer
+ * @slen: buffer size
+ * @format: format string
+ * @date: valid #GDate
+ *
+ * Generates a printed representation of the date, in a
+ * [locale][setlocale]-specific way.
+ * Works just like the platform's C library strftime() function,
+ * but only accepts date-related formats; time-related formats
+ * give undefined results. Date must be valid. Unlike strftime()
+ * (which uses the locale encoding), works on a UTF-8 format
+ * string and stores a UTF-8 result.
+ *
+ * This function does not provide any conversion specifiers in
+ * addition to those implemented by the platform's C library.
+ * For example, don't expect that using g_date_strftime() would
+ * make the \%F provided by the C99 strftime() work on Windows
+ * where the C library only complies to C89.
+ *
+ * Returns: number of characters written to the buffer, or 0 the buffer was too small
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+gsize
+g_date_strftime (gchar *s,
+ gsize slen,
+ const gchar *format,
+ const GDate *d)
+{
+ struct tm tm;
+#ifndef G_OS_WIN32
+ gsize locale_format_len = 0;
+ gchar *locale_format;
+ gsize tmplen;
+ gchar *tmpbuf;
+ gsize tmpbufsize;
+ gsize convlen = 0;
+ gchar *convbuf;
+ GError *error = NULL;
+ gsize retval;
+#endif
+
+ g_return_val_if_fail (g_date_valid (d), 0);
+ g_return_val_if_fail (slen > 0, 0);
+ g_return_val_if_fail (format != NULL, 0);
+ g_return_val_if_fail (s != NULL, 0);
+
+ g_date_to_struct_tm (d, &tm);
+
+#ifdef G_OS_WIN32
+ if (!g_utf8_validate (format, -1, NULL))
+ {
+ s[0] = '\0';
+ return 0;
+ }
+ return win32_strftime_helper (d, format, &tm, s, slen);
+#else
+
+ locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
+
+ if (error)
+ {
+ g_warning (G_STRLOC "Error converting format to locale encoding: %s", error->message);
+ g_error_free (error);
+
+ s[0] = '\0';
+ return 0;
+ }
+
+ tmpbufsize = MAX (128, locale_format_len * 2);
+ while (TRUE)
+ {
+ tmpbuf = g_malloc (tmpbufsize);
+
+ /* Set the first byte to something other than '\0', to be able to
+ * recognize whether strftime actually failed or just returned "".
+ */
+ tmpbuf[0] = '\1';
+ tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
+
+ if (tmplen == 0 && tmpbuf[0] != '\0')
+ {
+ g_free (tmpbuf);
+ tmpbufsize *= 2;
+
+ if (tmpbufsize > 65536)
+ {
+ g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up");
+ g_free (locale_format);
+
+ s[0] = '\0';
+ return 0;
+ }
+ }
+ else
+ break;
+ }
+ g_free (locale_format);
+
+ convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
+ g_free (tmpbuf);
+
+ if (error)
+ {
+ g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s", error->message);
+ g_error_free (error);
+
+ s[0] = '\0';
+ return 0;
+ }
+
+ if (slen <= convlen)
+ {
+ /* Ensure only whole characters are copied into the buffer.
+ */
+ gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
+ g_assert (end != NULL);
+ convlen = end - convbuf;
+
+ /* Return 0 because the buffer isn't large enough.
+ */
+ retval = 0;
+ }
+ else
+ retval = convlen;
+
+ memcpy (s, convbuf, convlen);
+ s[convlen] = '\0';
+ g_free (convbuf);
+
+ return retval;
+#endif
+}
+
+#pragma GCC diagnostic pop
diff --git a/glib/gdate.h b/glib/gdate.h
new file mode 100644
index 0000000000000000000000000000000000000000..65fe811fa9289ed1158934c7b02b65f5a47cdb90
--- /dev/null
+++ b/glib/gdate.h
@@ -0,0 +1,307 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __G_DATE_H__
+#define __G_DATE_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+/* GDate
+ *
+ * Date calculations (not time for now, to be resolved). These are a
+ * mutant combination of Steffen Beyer's DateCalc routines
+ * (http://www.perl.com/CPAN/authors/id/STBEY/) and Jon Trowbridge's
+ * date routines (written for in-house software). Written by Havoc
+ * Pennington
+ */
+
+typedef gint32 GTime GLIB_DEPRECATED_TYPE_IN_2_62_FOR(GDateTime);
+typedef guint16 GDateYear;
+typedef guint8 GDateDay; /* day of the month */
+typedef struct _GDate GDate;
+
+/* enum used to specify order of appearance in parsed date strings */
+typedef enum
+{
+ G_DATE_DAY = 0,
+ G_DATE_MONTH = 1,
+ G_DATE_YEAR = 2
+} GDateDMY;
+
+/* actual week and month values */
+typedef enum
+{
+ G_DATE_BAD_WEEKDAY = 0,
+ G_DATE_MONDAY = 1,
+ G_DATE_TUESDAY = 2,
+ G_DATE_WEDNESDAY = 3,
+ G_DATE_THURSDAY = 4,
+ G_DATE_FRIDAY = 5,
+ G_DATE_SATURDAY = 6,
+ G_DATE_SUNDAY = 7
+} GDateWeekday;
+typedef enum
+{
+ G_DATE_BAD_MONTH = 0,
+ G_DATE_JANUARY = 1,
+ G_DATE_FEBRUARY = 2,
+ G_DATE_MARCH = 3,
+ G_DATE_APRIL = 4,
+ G_DATE_MAY = 5,
+ G_DATE_JUNE = 6,
+ G_DATE_JULY = 7,
+ G_DATE_AUGUST = 8,
+ G_DATE_SEPTEMBER = 9,
+ G_DATE_OCTOBER = 10,
+ G_DATE_NOVEMBER = 11,
+ G_DATE_DECEMBER = 12
+} GDateMonth;
+
+#define G_DATE_BAD_JULIAN 0U
+#define G_DATE_BAD_DAY 0U
+#define G_DATE_BAD_YEAR 0U
+
+/* Note: directly manipulating structs is generally a bad idea, but
+ * in this case it's an *incredibly* bad idea, because all or part
+ * of this struct can be invalid at any given time. Use the functions,
+ * or you will get hosed, I promise.
+ */
+struct _GDate
+{
+ guint julian_days : 32; /* julian days representation - we use a
+ * bitfield hoping that 64 bit platforms
+ * will pack this whole struct in one big
+ * int
+ */
+
+ guint julian : 1; /* julian is valid */
+ guint dmy : 1; /* dmy is valid */
+
+ /* DMY representation */
+ guint day : 6;
+ guint month : 4;
+ guint year : 16;
+};
+
+/* g_date_new() returns an invalid date, you then have to _set() stuff
+ * to get a usable object. You can also allocate a GDate statically,
+ * then call g_date_clear() to initialize.
+ */
+GLIB_AVAILABLE_IN_ALL
+GDate* g_date_new (void);
+GLIB_AVAILABLE_IN_ALL
+GDate* g_date_new_dmy (GDateDay day,
+ GDateMonth month,
+ GDateYear year);
+GLIB_AVAILABLE_IN_ALL
+GDate* g_date_new_julian (guint32 julian_day);
+GLIB_AVAILABLE_IN_ALL
+void g_date_free (GDate *date);
+GLIB_AVAILABLE_IN_2_56
+GDate* g_date_copy (const GDate *date);
+
+/* check g_date_valid() after doing an operation that might fail, like
+ * _parse. Almost all g_date operations are undefined on invalid
+ * dates (the exceptions are the mutators, since you need those to
+ * return to validity).
+ */
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid_day (GDateDay day) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid_month (GDateMonth month) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid_year (GDateYear year) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid_weekday (GDateWeekday weekday) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid_julian (guint32 julian_date) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_valid_dmy (GDateDay day,
+ GDateMonth month,
+ GDateYear year) G_GNUC_CONST;
+
+GLIB_AVAILABLE_IN_ALL
+GDateWeekday g_date_get_weekday (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+GDateMonth g_date_get_month (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+GDateYear g_date_get_year (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+GDateDay g_date_get_day (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+guint32 g_date_get_julian (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+guint g_date_get_day_of_year (const GDate *date);
+/* First monday/sunday is the start of week 1; if we haven't reached
+ * that day, return 0. These are not ISO weeks of the year; that
+ * routine needs to be added.
+ * these functions return the number of weeks, starting on the
+ * corrsponding day
+ */
+GLIB_AVAILABLE_IN_ALL
+guint g_date_get_monday_week_of_year (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+guint g_date_get_sunday_week_of_year (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+guint g_date_get_iso8601_week_of_year (const GDate *date);
+
+/* If you create a static date struct you need to clear it to get it
+ * in a safe state before use. You can clear a whole array at
+ * once with the ndates argument.
+ */
+GLIB_AVAILABLE_IN_ALL
+void g_date_clear (GDate *date,
+ guint n_dates);
+
+/* The parse routine is meant for dates typed in by a user, so it
+ * permits many formats but tries to catch common typos. If your data
+ * needs to be strictly validated, it is not an appropriate function.
+ */
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_parse (GDate *date,
+ const gchar *str);
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_time_t (GDate *date,
+ time_t timet);
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GLIB_DEPRECATED_IN_2_62_FOR(g_date_set_time_t)
+void g_date_set_time_val (GDate *date,
+ GTimeVal *timeval);
+GLIB_DEPRECATED_FOR(g_date_set_time_t)
+void g_date_set_time (GDate *date,
+ GTime time_);
+G_GNUC_END_IGNORE_DEPRECATIONS
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_month (GDate *date,
+ GDateMonth month);
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_day (GDate *date,
+ GDateDay day);
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_year (GDate *date,
+ GDateYear year);
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_dmy (GDate *date,
+ GDateDay day,
+ GDateMonth month,
+ GDateYear y);
+GLIB_AVAILABLE_IN_ALL
+void g_date_set_julian (GDate *date,
+ guint32 julian_date);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_is_first_of_month (const GDate *date);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_is_last_of_month (const GDate *date);
+
+/* To go forward by some number of weeks just go forward weeks*7 days */
+GLIB_AVAILABLE_IN_ALL
+void g_date_add_days (GDate *date,
+ guint n_days);
+GLIB_AVAILABLE_IN_ALL
+void g_date_subtract_days (GDate *date,
+ guint n_days);
+
+/* If you add/sub months while day > 28, the day might change */
+GLIB_AVAILABLE_IN_ALL
+void g_date_add_months (GDate *date,
+ guint n_months);
+GLIB_AVAILABLE_IN_ALL
+void g_date_subtract_months (GDate *date,
+ guint n_months);
+
+/* If it's feb 29, changing years can move you to the 28th */
+GLIB_AVAILABLE_IN_ALL
+void g_date_add_years (GDate *date,
+ guint n_years);
+GLIB_AVAILABLE_IN_ALL
+void g_date_subtract_years (GDate *date,
+ guint n_years);
+GLIB_AVAILABLE_IN_ALL
+gboolean g_date_is_leap_year (GDateYear year) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+guint8 g_date_get_days_in_month (GDateMonth month,
+ GDateYear year) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+guint8 g_date_get_monday_weeks_in_year (GDateYear year) G_GNUC_CONST;
+GLIB_AVAILABLE_IN_ALL
+guint8 g_date_get_sunday_weeks_in_year (GDateYear year) G_GNUC_CONST;
+
+/* Returns the number of days between the two dates. If date2 comes
+ before date1, a negative value is return. */
+GLIB_AVAILABLE_IN_ALL
+gint g_date_days_between (const GDate *date1,
+ const GDate *date2);
+
+/* qsort-friendly (with a cast...) */
+GLIB_AVAILABLE_IN_ALL
+gint g_date_compare (const GDate *lhs,
+ const GDate *rhs);
+GLIB_AVAILABLE_IN_ALL
+void g_date_to_struct_tm (const GDate *date,
+ struct tm *tm);
+
+GLIB_AVAILABLE_IN_ALL
+void g_date_clamp (GDate *date,
+ const GDate *min_date,
+ const GDate *max_date);
+
+/* Swap date1 and date2's values if date1 > date2. */
+GLIB_AVAILABLE_IN_ALL
+void g_date_order (GDate *date1, GDate *date2);
+
+/* Just like strftime() except you can only use date-related formats.
+ * Using a time format is undefined.
+ */
+GLIB_AVAILABLE_IN_ALL
+gsize g_date_strftime (gchar *s,
+ gsize slen,
+ const gchar *format,
+ const GDate *date);
+
+#define g_date_weekday g_date_get_weekday GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_weekday)
+#define g_date_month g_date_get_month GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_month)
+#define g_date_year g_date_get_year GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_year)
+#define g_date_day g_date_get_day GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_day)
+#define g_date_julian g_date_get_julian GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_julian)
+#define g_date_day_of_year g_date_get_day_of_year GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_day_of_year)
+#define g_date_monday_week_of_year g_date_get_monday_week_of_year GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_monday_week_of_year)
+#define g_date_sunday_week_of_year g_date_get_sunday_week_of_year GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_sunday_week_of_year)
+#define g_date_days_in_month g_date_get_days_in_month GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_days_in_month)
+#define g_date_monday_weeks_in_year g_date_get_monday_weeks_in_year GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_monday_weeks_in_year)
+#define g_date_sunday_weeks_in_year g_date_get_sunday_weeks_in_year GLIB_DEPRECATED_MACRO_IN_2_26_FOR(g_date_get_sunday_weeks_in_year)
+
+G_END_DECLS
+
+#endif /* __G_DATE_H__ */
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
new file mode 100644
index 0000000000000000000000000000000000000000..0ec390c94a05f9e78ea8b91f372b14949ca523fd
--- /dev/null
+++ b/glib/gdatetime.c
@@ -0,0 +1,3527 @@
+/* gdatetime.c
+ *
+ * Copyright (C) 2009-2010 Christian Hergert
+ * Copyright (C) 2010 Thiago Santos
+ * Copyright (C) 2010 Emmanuele Bassi
+ * Copyright © 2010 Codethink Limited
+ * Copyright © 2018 Tomasz Miąsko
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * licence, or (at your option) any later version.
+ *
+ * This 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see .
+ *
+ * Authors: Christian Hergert
+ * Thiago Santos
+ * Emmanuele Bassi
+ * Ryan Lortie
+ * Robert Ancell
+ */
+
+/* Algorithms within this file are based on the Calendar FAQ by
+ * Claus Tondering. It can be found at
+ * http://www.tondering.dk/claus/cal/calendar29.txt
+ *
+ * Copyright and disclaimer
+ * ------------------------
+ * This document is Copyright (C) 2008 by Claus Tondering.
+ * E-mail: claus@tondering.dk. (Please include the word
+ * "calendar" in the subject line.)
+ * The document may be freely distributed, provided this
+ * copyright notice is included and no money is charged for
+ * the document.
+ *
+ * This document is provided "as is". No warranties are made as
+ * to its correctness.
+ */
+
+/* Prologue {{{1 */
+
+#include "config.h"
+
+/* langinfo.h in glibc 2.27 defines ALTMON_* only if _GNU_SOURCE is defined. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include
+#include
+#include
+
+#ifdef HAVE_LANGINFO_TIME
+#include
+#endif
+
+#include "gatomic.h"
+#include "gcharset.h"
+#include "gcharsetprivate.h"
+#include "gconvert.h"
+#include "gconvertprivate.h"
+#include "gdatetime.h"
+#include "gfileutils.h"
+#include "ghash.h"
+#include "glibintl.h"
+#include "gmain.h"
+#include "gmappedfile.h"
+#include "gslice.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gthread.h"
+#include "gtimezone.h"
+
+#ifndef G_OS_WIN32
+#include
+#include
+#else
+#if defined (_MSC_VER) && (_MSC_VER < 1800)
+/* fallback implementation for isnan() on VS2012 and earlier */
+#define isnan _isnan
+#endif
+#endif /* !G_OS_WIN32 */
+
+/**
+ * SECTION:date-time
+ * @title: GDateTime
+ * @short_description: a structure representing Date and Time
+ * @see_also: #GTimeZone
+ *
+ * #GDateTime is a structure that combines a Gregorian date and time
+ * into a single structure. It provides many conversion and methods to
+ * manipulate dates and times. Time precision is provided down to
+ * microseconds and the time can range (proleptically) from 0001-01-01
+ * 00:00:00 to 9999-12-31 23:59:59.999999. #GDateTime follows POSIX
+ * time in the sense that it is oblivious to leap seconds.
+ *
+ * #GDateTime is an immutable object; once it has been created it cannot
+ * be modified further. All modifiers will create a new #GDateTime.
+ * Nearly all such functions can fail due to the date or time going out
+ * of range, in which case %NULL will be returned.
+ *
+ * #GDateTime is reference counted: the reference count is increased by calling
+ * g_date_time_ref() and decreased by calling g_date_time_unref(). When the
+ * reference count drops to 0, the resources allocated by the #GDateTime
+ * structure are released.
+ *
+ * Many parts of the API may produce non-obvious results. As an
+ * example, adding two months to January 31st will yield March 31st
+ * whereas adding one month and then one month again will yield either
+ * March 28th or March 29th. Also note that adding 24 hours is not
+ * always the same as adding one day (since days containing daylight
+ * savings time transitions are either 23 or 25 hours in length).
+ *
+ * #GDateTime is available since GLib 2.26.
+ */
+
+struct _GDateTime
+{
+ /* Microsecond timekeeping within Day */
+ guint64 usec;
+
+ /* TimeZone information */
+ GTimeZone *tz;
+ gint interval;
+
+ /* 1 is 0001-01-01 in Proleptic Gregorian */
+ gint32 days;
+
+ gint ref_count; /* (atomic) */
+};
+
+/* Time conversion {{{1 */
+
+#define UNIX_EPOCH_START 719163
+#define INSTANT_TO_UNIX(instant) \
+ ((instant)/USEC_PER_SECOND - UNIX_EPOCH_START * SEC_PER_DAY)
+#define INSTANT_TO_UNIX_USECS(instant) \
+ ((instant) - UNIX_EPOCH_START * SEC_PER_DAY * USEC_PER_SECOND)
+#define UNIX_TO_INSTANT(unix) \
+ (((gint64) (unix) + UNIX_EPOCH_START * SEC_PER_DAY) * USEC_PER_SECOND)
+#define UNIX_USECS_TO_INSTANT(unix_usecs) \
+ ((gint64) (unix_usecs) + UNIX_EPOCH_START * SEC_PER_DAY * USEC_PER_SECOND)
+#define UNIX_TO_INSTANT_IS_VALID(unix) \
+ ((gint64) (unix) <= INSTANT_TO_UNIX (G_MAXINT64))
+#define UNIX_USECS_TO_INSTANT_IS_VALID(unix_usecs) \
+ ((gint64) (unix_usecs) <= INSTANT_TO_UNIX_USECS (G_MAXINT64))
+
+#define DAYS_IN_4YEARS 1461 /* days in 4 years */
+#define DAYS_IN_100YEARS 36524 /* days in 100 years */
+#define DAYS_IN_400YEARS 146097 /* days in 400 years */
+
+#define USEC_PER_SECOND (G_GINT64_CONSTANT (1000000))
+#define USEC_PER_MINUTE (G_GINT64_CONSTANT (60000000))
+#define USEC_PER_HOUR (G_GINT64_CONSTANT (3600000000))
+#define USEC_PER_MILLISECOND (G_GINT64_CONSTANT (1000))
+#define USEC_PER_DAY (G_GINT64_CONSTANT (86400000000))
+#define SEC_PER_DAY (G_GINT64_CONSTANT (86400))
+
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR (60 * SECS_PER_MINUTE)
+#define SECS_PER_DAY (24 * SECS_PER_HOUR)
+#define SECS_PER_YEAR (365 * SECS_PER_DAY)
+#define SECS_PER_JULIAN (DAYS_PER_PERIOD * SECS_PER_DAY)
+
+#define GREGORIAN_LEAP(y) ((((y) % 4) == 0) && (!((((y) % 100) == 0) && (((y) % 400) != 0))))
+#define JULIAN_YEAR(d) ((d)->julian / 365.25)
+#define DAYS_PER_PERIOD (G_GINT64_CONSTANT (2914695))
+
+static const guint16 days_in_months[2][13] =
+{
+ { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const guint16 days_in_year[2][13] =
+{
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+#ifdef HAVE_LANGINFO_TIME
+
+#define GET_AMPM(d) ((g_date_time_get_hour (d) < 12) ? \
+ nl_langinfo (AM_STR) : \
+ nl_langinfo (PM_STR))
+#define GET_AMPM_IS_LOCALE TRUE
+
+#define PREFERRED_DATE_TIME_FMT nl_langinfo (D_T_FMT)
+#define PREFERRED_DATE_FMT nl_langinfo (D_FMT)
+#define PREFERRED_TIME_FMT nl_langinfo (T_FMT)
+#define PREFERRED_12HR_TIME_FMT nl_langinfo (T_FMT_AMPM)
+
+static const gint weekday_item[2][7] =
+{
+ { ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABDAY_1 },
+ { DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7, DAY_1 }
+};
+
+static const gint month_item[2][12] =
+{
+ { ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12 },
+ { MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12 },
+};
+
+#define WEEKDAY_ABBR(d) nl_langinfo (weekday_item[0][g_date_time_get_day_of_week (d) - 1])
+#define WEEKDAY_ABBR_IS_LOCALE TRUE
+#define WEEKDAY_FULL(d) nl_langinfo (weekday_item[1][g_date_time_get_day_of_week (d) - 1])
+#define WEEKDAY_FULL_IS_LOCALE TRUE
+#define MONTH_ABBR(d) nl_langinfo (month_item[0][g_date_time_get_month (d) - 1])
+#define MONTH_ABBR_IS_LOCALE TRUE
+#define MONTH_FULL(d) nl_langinfo (month_item[1][g_date_time_get_month (d) - 1])
+#define MONTH_FULL_IS_LOCALE TRUE
+
+#else
+
+#define GET_AMPM(d) (get_fallback_ampm (g_date_time_get_hour (d)))
+#define GET_AMPM_IS_LOCALE FALSE
+
+/* Translators: this is the preferred format for expressing the date and the time */
+#define PREFERRED_DATE_TIME_FMT C_("GDateTime", "%a %b %e %H:%M:%S %Y")
+
+/* Translators: this is the preferred format for expressing the date */
+#define PREFERRED_DATE_FMT C_("GDateTime", "%m/%d/%y")
+
+/* Translators: this is the preferred format for expressing the time */
+#define PREFERRED_TIME_FMT C_("GDateTime", "%H:%M:%S")
+
+/* Translators: this is the preferred format for expressing 12 hour time */
+#define PREFERRED_12HR_TIME_FMT C_("GDateTime", "%I:%M:%S %p")
+
+#define WEEKDAY_ABBR(d) (get_weekday_name_abbr (g_date_time_get_day_of_week (d)))
+#define WEEKDAY_ABBR_IS_LOCALE FALSE
+#define WEEKDAY_FULL(d) (get_weekday_name (g_date_time_get_day_of_week (d)))
+#define WEEKDAY_FULL_IS_LOCALE FALSE
+/* We don't yet know if nl_langinfo (MON_n) returns standalone or complete-date
+ * format forms but if nl_langinfo (ALTMON_n) is not supported then we will
+ * have to use MONTH_FULL as standalone. The same if nl_langinfo () does not
+ * exist at all. MONTH_ABBR is similar: if nl_langinfo (_NL_ABALTMON_n) is not
+ * supported then we will use MONTH_ABBR as standalone.
+ */
+#define MONTH_ABBR(d) (get_month_name_abbr_standalone (g_date_time_get_month (d)))
+#define MONTH_ABBR_IS_LOCALE FALSE
+#define MONTH_FULL(d) (get_month_name_standalone (g_date_time_get_month (d)))
+#define MONTH_FULL_IS_LOCALE FALSE
+
+static const gchar *
+get_month_name_standalone (gint month)
+{
+ switch (month)
+ {
+ case 1:
+ /* Translators: Some languages (Baltic, Slavic, Greek, and some more)
+ * need different grammatical forms of month names depending on whether
+ * they are standalone or in a complete date context, with the day
+ * number. Some other languages may prefer starting with uppercase when
+ * they are standalone and with lowercase when they are in a complete
+ * date context. Here are full month names in a form appropriate when
+ * they are used standalone. If your system is Linux with the glibc
+ * version 2.27 (released Feb 1, 2018) or newer or if it is from the BSD
+ * family (which includes OS X) then you can refer to the date command
+ * line utility and see what the command `date +%OB' produces. Also in
+ * the latest Linux the command `locale alt_mon' in your native locale
+ * produces a complete list of month names almost ready to copy and
+ * paste here. Note that in most of the languages (western European,
+ * non-European) there is no difference between the standalone and
+ * complete date form.
+ */
+ return C_("full month name", "January");
+ case 2:
+ return C_("full month name", "February");
+ case 3:
+ return C_("full month name", "March");
+ case 4:
+ return C_("full month name", "April");
+ case 5:
+ return C_("full month name", "May");
+ case 6:
+ return C_("full month name", "June");
+ case 7:
+ return C_("full month name", "July");
+ case 8:
+ return C_("full month name", "August");
+ case 9:
+ return C_("full month name", "September");
+ case 10:
+ return C_("full month name", "October");
+ case 11:
+ return C_("full month name", "November");
+ case 12:
+ return C_("full month name", "December");
+
+ default:
+ g_warning ("Invalid month number %d", month);
+ }
+
+ return NULL;
+}
+
+static const gchar *
+get_month_name_abbr_standalone (gint month)
+{
+ switch (month)
+ {
+ case 1:
+ /* Translators: Some languages need different grammatical forms of
+ * month names depending on whether they are standalone or in a complete
+ * date context, with the day number. Some may prefer starting with
+ * uppercase when they are standalone and with lowercase when they are
+ * in a full date context. However, as these names are abbreviated
+ * the grammatical difference is visible probably only in Belarusian
+ * and Russian. In other languages there is no difference between
+ * the standalone and complete date form when they are abbreviated.
+ * If your system is Linux with the glibc version 2.27 (released
+ * Feb 1, 2018) or newer then you can refer to the date command line
+ * utility and see what the command `date +%Ob' produces. Also in
+ * the latest Linux the command `locale ab_alt_mon' in your native
+ * locale produces a complete list of month names almost ready to copy
+ * and paste here. Note that this feature is not yet supported by any
+ * other platform. Here are abbreviated month names in a form
+ * appropriate when they are used standalone.
+ */
+ return C_("abbreviated month name", "Jan");
+ case 2:
+ return C_("abbreviated month name", "Feb");
+ case 3:
+ return C_("abbreviated month name", "Mar");
+ case 4:
+ return C_("abbreviated month name", "Apr");
+ case 5:
+ return C_("abbreviated month name", "May");
+ case 6:
+ return C_("abbreviated month name", "Jun");
+ case 7:
+ return C_("abbreviated month name", "Jul");
+ case 8:
+ return C_("abbreviated month name", "Aug");
+ case 9:
+ return C_("abbreviated month name", "Sep");
+ case 10:
+ return C_("abbreviated month name", "Oct");
+ case 11:
+ return C_("abbreviated month name", "Nov");
+ case 12:
+ return C_("abbreviated month name", "Dec");
+
+ default:
+ g_warning ("Invalid month number %d", month);
+ }
+
+ return NULL;
+}
+
+static const gchar *
+get_weekday_name (gint day)
+{
+ switch (day)
+ {
+ case 1:
+ return C_("full weekday name", "Monday");
+ case 2:
+ return C_("full weekday name", "Tuesday");
+ case 3:
+ return C_("full weekday name", "Wednesday");
+ case 4:
+ return C_("full weekday name", "Thursday");
+ case 5:
+ return C_("full weekday name", "Friday");
+ case 6:
+ return C_("full weekday name", "Saturday");
+ case 7:
+ return C_("full weekday name", "Sunday");
+
+ default:
+ g_warning ("Invalid week day number %d", day);
+ }
+
+ return NULL;
+}
+
+static const gchar *
+get_weekday_name_abbr (gint day)
+{
+ switch (day)
+ {
+ case 1:
+ return C_("abbreviated weekday name", "Mon");
+ case 2:
+ return C_("abbreviated weekday name", "Tue");
+ case 3:
+ return C_("abbreviated weekday name", "Wed");
+ case 4:
+ return C_("abbreviated weekday name", "Thu");
+ case 5:
+ return C_("abbreviated weekday name", "Fri");
+ case 6:
+ return C_("abbreviated weekday name", "Sat");
+ case 7:
+ return C_("abbreviated weekday name", "Sun");
+
+ default:
+ g_warning ("Invalid week day number %d", day);
+ }
+
+ return NULL;
+}
+
+#endif /* HAVE_LANGINFO_TIME */
+
+#ifdef HAVE_LANGINFO_ALTMON
+
+/* If nl_langinfo () supports ALTMON_n then MON_n returns full date format
+ * forms and ALTMON_n returns standalone forms.
+ */
+
+#define MONTH_FULL_WITH_DAY(d) MONTH_FULL(d)
+#define MONTH_FULL_WITH_DAY_IS_LOCALE MONTH_FULL_IS_LOCALE
+
+static const gint alt_month_item[12] =
+{
+ ALTMON_1, ALTMON_2, ALTMON_3, ALTMON_4, ALTMON_5, ALTMON_6,
+ ALTMON_7, ALTMON_8, ALTMON_9, ALTMON_10, ALTMON_11, ALTMON_12
+};
+
+#define MONTH_FULL_STANDALONE(d) nl_langinfo (alt_month_item[g_date_time_get_month (d) - 1])
+#define MONTH_FULL_STANDALONE_IS_LOCALE TRUE
+
+#else
+
+/* If nl_langinfo () does not support ALTMON_n then either MON_n returns
+ * standalone forms or nl_langinfo (MON_n) does not work so we have defined
+ * it as standalone form.
+ */
+
+#define MONTH_FULL_STANDALONE(d) MONTH_FULL(d)
+#define MONTH_FULL_STANDALONE_IS_LOCALE MONTH_FULL_IS_LOCALE
+#define MONTH_FULL_WITH_DAY(d) (get_month_name_with_day (g_date_time_get_month (d)))
+#define MONTH_FULL_WITH_DAY_IS_LOCALE FALSE
+
+static const gchar *
+get_month_name_with_day (gint month)
+{
+ switch (month)
+ {
+ case 1:
+ /* Translators: Some languages need different grammatical forms of
+ * month names depending on whether they are standalone or in a full
+ * date context, with the day number. Some may prefer starting with
+ * uppercase when they are standalone and with lowercase when they are
+ * in a full date context. Here are full month names in a form
+ * appropriate when they are used in a full date context, with the
+ * day number. If your system is Linux with the glibc version 2.27
+ * (released Feb 1, 2018) or newer or if it is from the BSD family
+ * (which includes OS X) then you can refer to the date command line
+ * utility and see what the command `date +%B' produces. Also in
+ * the latest Linux the command `locale mon' in your native locale
+ * produces a complete list of month names almost ready to copy and
+ * paste here. In older Linux systems due to a bug the result is
+ * incorrect in some languages. Note that in most of the languages
+ * (western European, non-European) there is no difference between the
+ * standalone and complete date form.
+ */
+ return C_("full month name with day", "January");
+ case 2:
+ return C_("full month name with day", "February");
+ case 3:
+ return C_("full month name with day", "March");
+ case 4:
+ return C_("full month name with day", "April");
+ case 5:
+ return C_("full month name with day", "May");
+ case 6:
+ return C_("full month name with day", "June");
+ case 7:
+ return C_("full month name with day", "July");
+ case 8:
+ return C_("full month name with day", "August");
+ case 9:
+ return C_("full month name with day", "September");
+ case 10:
+ return C_("full month name with day", "October");
+ case 11:
+ return C_("full month name with day", "November");
+ case 12:
+ return C_("full month name with day", "December");
+
+ default:
+ g_warning ("Invalid month number %d", month);
+ }
+
+ return NULL;
+}
+
+#endif /* HAVE_LANGINFO_ALTMON */
+
+#ifdef HAVE_LANGINFO_ABALTMON
+
+/* If nl_langinfo () supports _NL_ABALTMON_n then ABMON_n returns full
+ * date format forms and _NL_ABALTMON_n returns standalone forms.
+ */
+
+#define MONTH_ABBR_WITH_DAY(d) MONTH_ABBR(d)
+#define MONTH_ABBR_WITH_DAY_IS_LOCALE MONTH_ABBR_IS_LOCALE
+
+static const gint ab_alt_month_item[12] =
+{
+ _NL_ABALTMON_1, _NL_ABALTMON_2, _NL_ABALTMON_3, _NL_ABALTMON_4,
+ _NL_ABALTMON_5, _NL_ABALTMON_6, _NL_ABALTMON_7, _NL_ABALTMON_8,
+ _NL_ABALTMON_9, _NL_ABALTMON_10, _NL_ABALTMON_11, _NL_ABALTMON_12
+};
+
+#define MONTH_ABBR_STANDALONE(d) nl_langinfo (ab_alt_month_item[g_date_time_get_month (d) - 1])
+#define MONTH_ABBR_STANDALONE_IS_LOCALE TRUE
+
+#else
+
+/* If nl_langinfo () does not support _NL_ABALTMON_n then either ABMON_n
+ * returns standalone forms or nl_langinfo (ABMON_n) does not work so we
+ * have defined it as standalone form. Now it's time to swap.
+ */
+
+#define MONTH_ABBR_STANDALONE(d) MONTH_ABBR(d)
+#define MONTH_ABBR_STANDALONE_IS_LOCALE MONTH_ABBR_IS_LOCALE
+#define MONTH_ABBR_WITH_DAY(d) (get_month_name_abbr_with_day (g_date_time_get_month (d)))
+#define MONTH_ABBR_WITH_DAY_IS_LOCALE FALSE
+
+static const gchar *
+get_month_name_abbr_with_day (gint month)
+{
+ switch (month)
+ {
+ case 1:
+ /* Translators: Some languages need different grammatical forms of
+ * month names depending on whether they are standalone or in a full
+ * date context, with the day number. Some may prefer starting with
+ * uppercase when they are standalone and with lowercase when they are
+ * in a full date context. Here are abbreviated month names in a form
+ * appropriate when they are used in a full date context, with the
+ * day number. However, as these names are abbreviated the grammatical
+ * difference is visible probably only in Belarusian and Russian.
+ * In other languages there is no difference between the standalone
+ * and complete date form when they are abbreviated. If your system
+ * is Linux with the glibc version 2.27 (released Feb 1, 2018) or newer
+ * then you can refer to the date command line utility and see what the
+ * command `date +%b' produces. Also in the latest Linux the command
+ * `locale abmon' in your native locale produces a complete list of
+ * month names almost ready to copy and paste here. In other systems
+ * due to a bug the result is incorrect in some languages.
+ */
+ return C_("abbreviated month name with day", "Jan");
+ case 2:
+ return C_("abbreviated month name with day", "Feb");
+ case 3:
+ return C_("abbreviated month name with day", "Mar");
+ case 4:
+ return C_("abbreviated month name with day", "Apr");
+ case 5:
+ return C_("abbreviated month name with day", "May");
+ case 6:
+ return C_("abbreviated month name with day", "Jun");
+ case 7:
+ return C_("abbreviated month name with day", "Jul");
+ case 8:
+ return C_("abbreviated month name with day", "Aug");
+ case 9:
+ return C_("abbreviated month name with day", "Sep");
+ case 10:
+ return C_("abbreviated month name with day", "Oct");
+ case 11:
+ return C_("abbreviated month name with day", "Nov");
+ case 12:
+ return C_("abbreviated month name with day", "Dec");
+
+ default:
+ g_warning ("Invalid month number %d", month);
+ }
+
+ return NULL;
+}
+
+#endif /* HAVE_LANGINFO_ABALTMON */
+
+/* Format AM/PM indicator if the locale does not have a localized version. */
+static const gchar *
+get_fallback_ampm (gint hour)
+{
+ if (hour < 12)
+ /* Translators: 'before midday' indicator */
+ return C_("GDateTime", "AM");
+ else
+ /* Translators: 'after midday' indicator */
+ return C_("GDateTime", "PM");
+}
+
+static inline gint
+ymd_to_days (gint year,
+ gint month,
+ gint day)
+{
+ gint64 days;
+
+ days = ((gint64) year - 1) * 365 + ((year - 1) / 4) - ((year - 1) / 100)
+ + ((year - 1) / 400);
+
+ days += days_in_year[0][month - 1];
+ if (GREGORIAN_LEAP (year) && month > 2)
+ day++;
+
+ days += day;
+
+ return days;
+}
+
+static void
+g_date_time_get_week_number (GDateTime *datetime,
+ gint *week_number,
+ gint *day_of_week,
+ gint *day_of_year)
+{
+ gint a, b, c, d, e, f, g, n, s, month = -1, day = -1, year = -1;
+
+ g_date_time_get_ymd (datetime, &year, &month, &day);
+
+ if (month <= 2)
+ {
+ a = g_date_time_get_year (datetime) - 1;
+ b = (a / 4) - (a / 100) + (a / 400);
+ c = ((a - 1) / 4) - ((a - 1) / 100) + ((a - 1) / 400);
+ s = b - c;
+ e = 0;
+ f = day - 1 + (31 * (month - 1));
+ }
+ else
+ {
+ a = year;
+ b = (a / 4) - (a / 100) + (a / 400);
+ c = ((a - 1) / 4) - ((a - 1) / 100) + ((a - 1) / 400);
+ s = b - c;
+ e = s + 1;
+ f = day + (((153 * (month - 3)) + 2) / 5) + 58 + s;
+ }
+
+ g = (a + b) % 7;
+ d = (f + g - e) % 7;
+ n = f + 3 - d;
+
+ if (week_number)
+ {
+ if (n < 0)
+ *week_number = 53 - ((g - s) / 5);
+ else if (n > 364 + s)
+ *week_number = 1;
+ else
+ *week_number = (n / 7) + 1;
+ }
+
+ if (day_of_week)
+ *day_of_week = d + 1;
+
+ if (day_of_year)
+ *day_of_year = f + 1;
+}
+
+/* Lifecycle {{{1 */
+
+static GDateTime *
+g_date_time_alloc (GTimeZone *tz)
+{
+ GDateTime *datetime;
+
+ datetime = g_slice_new0 (GDateTime);
+ datetime->tz = g_time_zone_ref (tz);
+ datetime->ref_count = 1;
+
+ return datetime;
+}
+
+/**
+ * g_date_time_ref:
+ * @datetime: a #GDateTime
+ *
+ * Atomically increments the reference count of @datetime by one.
+ *
+ * Returns: the #GDateTime with the reference count increased
+ *
+ * Since: 2.26
+ */
+GDateTime *
+g_date_time_ref (GDateTime *datetime)
+{
+ g_return_val_if_fail (datetime != NULL, NULL);
+ g_return_val_if_fail (datetime->ref_count > 0, NULL);
+
+ g_atomic_int_inc (&datetime->ref_count);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_unref:
+ * @datetime: a #GDateTime
+ *
+ * Atomically decrements the reference count of @datetime by one.
+ *
+ * When the reference count reaches zero, the resources allocated by
+ * @datetime are freed
+ *
+ * Since: 2.26
+ */
+void
+g_date_time_unref (GDateTime *datetime)
+{
+ g_return_if_fail (datetime != NULL);
+ g_return_if_fail (datetime->ref_count > 0);
+
+ if (g_atomic_int_dec_and_test (&datetime->ref_count))
+ {
+ g_time_zone_unref (datetime->tz);
+ g_slice_free (GDateTime, datetime);
+ }
+}
+
+/* Internal state transformers {{{1 */
+/*< internal >
+ * g_date_time_to_instant:
+ * @datetime: a #GDateTime
+ *
+ * Convert a @datetime into an instant.
+ *
+ * An instant is a number that uniquely describes a particular
+ * microsecond in time, taking time zone considerations into account.
+ * (ie: "03:00 -0400" is the same instant as "02:00 -0500").
+ *
+ * An instant is always positive but we use a signed return value to
+ * avoid troubles with C.
+ */
+static gint64
+g_date_time_to_instant (GDateTime *datetime)
+{
+ gint64 offset;
+
+ offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
+ offset *= USEC_PER_SECOND;
+
+ return datetime->days * USEC_PER_DAY + datetime->usec - offset;
+}
+
+/*< internal >
+ * g_date_time_from_instant:
+ * @tz: a #GTimeZone
+ * @instant: an instant in time
+ *
+ * Creates a #GDateTime from a time zone and an instant.
+ *
+ * This might fail if the time ends up being out of range.
+ */
+static GDateTime *
+g_date_time_from_instant (GTimeZone *tz,
+ gint64 instant)
+{
+ GDateTime *datetime;
+ gint64 offset;
+
+ if (instant < 0 || instant > G_GINT64_CONSTANT (1000000000000000000))
+ return NULL;
+
+ datetime = g_date_time_alloc (tz);
+ datetime->interval = g_time_zone_find_interval (tz,
+ G_TIME_TYPE_UNIVERSAL,
+ INSTANT_TO_UNIX (instant));
+ offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
+ offset *= USEC_PER_SECOND;
+
+ instant += offset;
+
+ datetime->days = instant / USEC_PER_DAY;
+ datetime->usec = instant % USEC_PER_DAY;
+
+ if (datetime->days < 1 || 3652059 < datetime->days)
+ {
+ g_date_time_unref (datetime);
+ datetime = NULL;
+ }
+
+ return datetime;
+}
+
+
+/*< internal >
+ * g_date_time_deal_with_date_change:
+ * @datetime: a #GDateTime
+ *
+ * This function should be called whenever the date changes by adding
+ * days, months or years. It does three things.
+ *
+ * First, we ensure that the date falls between 0001-01-01 and
+ * 9999-12-31 and return %FALSE if it does not.
+ *
+ * Next we update the ->interval field.
+ *
+ * Finally, we ensure that the resulting date and time pair exists (by
+ * ensuring that our time zone has an interval containing it) and
+ * adjusting as required. For example, if we have the time 02:30:00 on
+ * March 13 2010 in Toronto and we add 1 day to it, we would end up with
+ * 2:30am on March 14th, which doesn't exist. In that case, we bump the
+ * time up to 3:00am.
+ */
+static gboolean
+g_date_time_deal_with_date_change (GDateTime *datetime)
+{
+ GTimeType was_dst;
+ gint64 full_time;
+ gint64 usec;
+
+ if (datetime->days < 1 || datetime->days > 3652059)
+ return FALSE;
+
+ was_dst = g_time_zone_is_dst (datetime->tz, datetime->interval);
+
+ full_time = datetime->days * USEC_PER_DAY + datetime->usec;
+
+
+ usec = full_time % USEC_PER_SECOND;
+ full_time /= USEC_PER_SECOND;
+ full_time -= UNIX_EPOCH_START * SEC_PER_DAY;
+
+ datetime->interval = g_time_zone_adjust_time (datetime->tz,
+ was_dst,
+ &full_time);
+ full_time += UNIX_EPOCH_START * SEC_PER_DAY;
+ full_time *= USEC_PER_SECOND;
+ full_time += usec;
+
+ datetime->days = full_time / USEC_PER_DAY;
+ datetime->usec = full_time % USEC_PER_DAY;
+
+ /* maybe daylight time caused us to shift to a different day,
+ * but it definitely didn't push us into a different year */
+ return TRUE;
+}
+
+static GDateTime *
+g_date_time_replace_days (GDateTime *datetime,
+ gint days)
+{
+ GDateTime *new;
+
+ new = g_date_time_alloc (datetime->tz);
+ new->interval = datetime->interval;
+ new->usec = datetime->usec;
+ new->days = days;
+
+ if (!g_date_time_deal_with_date_change (new))
+ {
+ g_date_time_unref (new);
+ new = NULL;
+ }
+
+ return new;
+}
+
+/* now/unix/timeval Constructors {{{1 */
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+/*< internal >
+ * g_date_time_new_from_timeval:
+ * @tz: a #GTimeZone
+ * @tv: a #GTimeVal
+ *
+ * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
+ * given time zone @tz.
+ *
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
+ * given time zone.
+ *
+ * This call can fail (returning %NULL) if @tv represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+static GDateTime *
+g_date_time_new_from_timeval (GTimeZone *tz,
+ const GTimeVal *tv)
+{
+ gint64 tv_sec = tv->tv_sec;
+
+ if (tv_sec > G_MAXINT64 - 1 || !UNIX_TO_INSTANT_IS_VALID (tv_sec + 1))
+ return NULL;
+
+ return g_date_time_from_instant (tz, tv->tv_usec +
+ UNIX_TO_INSTANT (tv->tv_sec));
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/*< internal >
+ * g_date_time_new_from_unix:
+ * @tz: a #GTimeZone
+ * @usecs: the Unix time, in microseconds since the epoch
+ *
+ * Creates a #GDateTime corresponding to the given Unix time @t_us in the
+ * given time zone @tz.
+ *
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC, regardless of the time zone given.
+ *
+ * This call can fail (returning %NULL) if @t represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+static GDateTime *
+g_date_time_new_from_unix (GTimeZone *tz,
+ gint64 usecs)
+{
+ if (!UNIX_USECS_TO_INSTANT_IS_VALID (usecs))
+ return NULL;
+
+ return g_date_time_from_instant (tz, UNIX_USECS_TO_INSTANT (usecs));
+}
+
+/**
+ * g_date_time_new_now: (constructor)
+ * @tz: a #GTimeZone
+ *
+ * Creates a #GDateTime corresponding to this exact instant in the given
+ * time zone @tz. The time is as accurate as the system allows, to a
+ * maximum accuracy of 1 microsecond.
+ *
+ * This function will always succeed unless GLib is still being used after the
+ * year 9999.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_now (GTimeZone *tz)
+{
+ gint64 now_us;
+
+ now_us = g_get_real_time ();
+
+ return g_date_time_new_from_unix (tz, now_us);
+}
+
+/**
+ * g_date_time_new_now_local: (constructor)
+ *
+ * Creates a #GDateTime corresponding to this exact instant in the local
+ * time zone.
+ *
+ * This is equivalent to calling g_date_time_new_now() with the time
+ * zone returned by g_time_zone_new_local().
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_now_local (void)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new_now (local);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_now_utc: (constructor)
+ *
+ * Creates a #GDateTime corresponding to this exact instant in UTC.
+ *
+ * This is equivalent to calling g_date_time_new_now() with the time
+ * zone returned by g_time_zone_new_utc().
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_now_utc (void)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new_now (utc);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_unix_local: (constructor)
+ * @t: the Unix time
+ *
+ * Creates a #GDateTime corresponding to the given Unix time @t in the
+ * local time zone.
+ *
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC, regardless of the local time offset.
+ *
+ * This call can fail (returning %NULL) if @t represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_from_unix_local (gint64 t)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ if (t > G_MAXINT64 / USEC_PER_SECOND ||
+ t < G_MININT64 / USEC_PER_SECOND)
+ return NULL;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new_from_unix (local, t * USEC_PER_SECOND);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_unix_utc: (constructor)
+ * @t: the Unix time
+ *
+ * Creates a #GDateTime corresponding to the given Unix time @t in UTC.
+ *
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC.
+ *
+ * This call can fail (returning %NULL) if @t represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_from_unix_utc (gint64 t)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ if (t > G_MAXINT64 / USEC_PER_SECOND ||
+ t < G_MININT64 / USEC_PER_SECOND)
+ return NULL;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new_from_unix (utc, t * USEC_PER_SECOND);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_timeval_local: (constructor)
+ * @tv: a #GTimeVal
+ *
+ * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
+ * local time zone.
+ *
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
+ * local time offset.
+ *
+ * This call can fail (returning %NULL) if @tv represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
+ * g_date_time_new_from_unix_local() instead.
+ **/
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GDateTime *
+g_date_time_new_from_timeval_local (const GTimeVal *tv)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new_from_timeval (local, tv);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/**
+ * g_date_time_new_from_timeval_utc: (constructor)
+ * @tv: a #GTimeVal
+ *
+ * Creates a #GDateTime corresponding to the given #GTimeVal @tv in UTC.
+ *
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC.
+ *
+ * This call can fail (returning %NULL) if @tv represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: (transfer full) (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use
+ * g_date_time_new_from_unix_utc() instead.
+ **/
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GDateTime *
+g_date_time_new_from_timeval_utc (const GTimeVal *tv)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new_from_timeval (utc, tv);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+/* Parse integers in the form d (week days), dd (hours etc), ddd (ordinal days) or dddd (years) */
+static gboolean
+get_iso8601_int (const gchar *text, gsize length, gint *value)
+{
+ gsize i;
+ guint v = 0;
+
+ if (length < 1 || length > 4)
+ return FALSE;
+
+ for (i = 0; i < length; i++)
+ {
+ const gchar c = text[i];
+ if (c < '0' || c > '9')
+ return FALSE;
+ v = v * 10 + (c - '0');
+ }
+
+ *value = v;
+ return TRUE;
+}
+
+/* Parse seconds in the form ss or ss.sss (variable length decimal) */
+static gboolean
+get_iso8601_seconds (const gchar *text, gsize length, gdouble *value)
+{
+ gsize i;
+ guint64 divisor = 1, v = 0;
+
+ if (length < 2)
+ return FALSE;
+
+ for (i = 0; i < 2; i++)
+ {
+ const gchar c = text[i];
+ if (c < '0' || c > '9')
+ return FALSE;
+ v = v * 10 + (c - '0');
+ }
+
+ if (length > 2 && !(text[i] == '.' || text[i] == ','))
+ return FALSE;
+
+ /* Ignore leap seconds, see g_date_time_new_from_iso8601() */
+ if (v >= 60.0 && v <= 61.0)
+ v = 59.0;
+
+ i++;
+ if (i == length)
+ return FALSE;
+
+ for (; i < length; i++)
+ {
+ const gchar c = text[i];
+ if (c < '0' || c > '9' ||
+ v > (G_MAXUINT64 - (c - '0')) / 10 ||
+ divisor > G_MAXUINT64 / 10)
+ return FALSE;
+ v = v * 10 + (c - '0');
+ divisor *= 10;
+ }
+
+ *value = (gdouble) v / divisor;
+ return TRUE;
+}
+
+static GDateTime *
+g_date_time_new_ordinal (GTimeZone *tz, gint year, gint ordinal_day, gint hour, gint minute, gdouble seconds)
+{
+ GDateTime *dt;
+
+ if (ordinal_day < 1 || ordinal_day > (GREGORIAN_LEAP (year) ? 366 : 365))
+ return NULL;
+
+ dt = g_date_time_new (tz, year, 1, 1, hour, minute, seconds);
+ if (dt == NULL)
+ return NULL;
+ dt->days += ordinal_day - 1;
+
+ return dt;
+}
+
+static GDateTime *
+g_date_time_new_week (GTimeZone *tz, gint year, gint week, gint week_day, gint hour, gint minute, gdouble seconds)
+{
+ gint64 p;
+ gint max_week, jan4_week_day, ordinal_day;
+ GDateTime *dt;
+
+ p = (year * 365 + (year / 4) - (year / 100) + (year / 400)) % 7;
+ max_week = p == 4 ? 53 : 52;
+
+ if (week < 1 || week > max_week || week_day < 1 || week_day > 7)
+ return NULL;
+
+ dt = g_date_time_new (tz, year, 1, 4, 0, 0, 0);
+ if (dt == NULL)
+ return NULL;
+ g_date_time_get_week_number (dt, NULL, &jan4_week_day, NULL);
+ g_date_time_unref (dt);
+
+ ordinal_day = (week * 7) + week_day - (jan4_week_day + 3);
+ if (ordinal_day < 0)
+ {
+ year--;
+ ordinal_day += GREGORIAN_LEAP (year) ? 366 : 365;
+ }
+ else if (ordinal_day > (GREGORIAN_LEAP (year) ? 366 : 365))
+ {
+ ordinal_day -= (GREGORIAN_LEAP (year) ? 366 : 365);
+ year++;
+ }
+
+ return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
+}
+
+static GDateTime *
+parse_iso8601_date (const gchar *text, gsize length,
+ gint hour, gint minute, gdouble seconds, GTimeZone *tz)
+{
+ /* YYYY-MM-DD */
+ if (length == 10 && text[4] == '-' && text[7] == '-')
+ {
+ int year, month, day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 5, 2, &month) ||
+ !get_iso8601_int (text + 8, 2, &day))
+ return NULL;
+ return g_date_time_new (tz, year, month, day, hour, minute, seconds);
+ }
+ /* YYYY-DDD */
+ else if (length == 8 && text[4] == '-')
+ {
+ gint year, ordinal_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 5, 3, &ordinal_day))
+ return NULL;
+ return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
+ }
+ /* YYYY-Www-D */
+ else if (length == 10 && text[4] == '-' && text[5] == 'W' && text[8] == '-')
+ {
+ gint year, week, week_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 6, 2, &week) ||
+ !get_iso8601_int (text + 9, 1, &week_day))
+ return NULL;
+ return g_date_time_new_week (tz, year, week, week_day, hour, minute, seconds);
+ }
+ /* YYYYWwwD */
+ else if (length == 8 && text[4] == 'W')
+ {
+ gint year, week, week_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 5, 2, &week) ||
+ !get_iso8601_int (text + 7, 1, &week_day))
+ return NULL;
+ return g_date_time_new_week (tz, year, week, week_day, hour, minute, seconds);
+ }
+ /* YYYYMMDD */
+ else if (length == 8)
+ {
+ int year, month, day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 4, 2, &month) ||
+ !get_iso8601_int (text + 6, 2, &day))
+ return NULL;
+ return g_date_time_new (tz, year, month, day, hour, minute, seconds);
+ }
+ /* YYYYDDD */
+ else if (length == 7)
+ {
+ gint year, ordinal_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 4, 3, &ordinal_day))
+ return NULL;
+ return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
+ }
+ else
+ return FALSE;
+}
+
+static GTimeZone *
+parse_iso8601_timezone (const gchar *text, gsize length, gssize *tz_offset)
+{
+ gint i, tz_length, offset_hours, offset_minutes;
+ gint offset_sign = 1;
+ GTimeZone *tz;
+
+ /* UTC uses Z suffix */
+ if (length > 0 && text[length - 1] == 'Z')
+ {
+ *tz_offset = length - 1;
+ return g_time_zone_new_utc ();
+ }
+
+ /* Look for '+' or '-' of offset */
+ for (i = length - 1; i >= 0; i--)
+ if (text[i] == '+' || text[i] == '-')
+ {
+ offset_sign = text[i] == '-' ? -1 : 1;
+ break;
+ }
+ if (i < 0)
+ return NULL;
+ tz_length = length - i;
+
+ /* +hh:mm or -hh:mm */
+ if (tz_length == 6 && text[i+3] == ':')
+ {
+ if (!get_iso8601_int (text + i + 1, 2, &offset_hours) ||
+ !get_iso8601_int (text + i + 4, 2, &offset_minutes))
+ return NULL;
+ }
+ /* +hhmm or -hhmm */
+ else if (tz_length == 5)
+ {
+ if (!get_iso8601_int (text + i + 1, 2, &offset_hours) ||
+ !get_iso8601_int (text + i + 3, 2, &offset_minutes))
+ return NULL;
+ }
+ /* +hh or -hh */
+ else if (tz_length == 3)
+ {
+ if (!get_iso8601_int (text + i + 1, 2, &offset_hours))
+ return NULL;
+ offset_minutes = 0;
+ }
+ else
+ return NULL;
+
+ *tz_offset = i;
+ tz = g_time_zone_new_identifier (text + i);
+
+ /* Double-check that the GTimeZone matches our interpretation of the timezone.
+ * This can fail because our interpretation is less strict than (for example)
+ * parse_time() in gtimezone.c, which restricts the range of the parsed
+ * integers. */
+ if (tz == NULL || g_time_zone_get_offset (tz, 0) != offset_sign * (offset_hours * 3600 + offset_minutes * 60))
+ {
+ g_clear_pointer (&tz, g_time_zone_unref);
+ return NULL;
+ }
+
+ return tz;
+}
+
+static gboolean
+parse_iso8601_time (const gchar *text, gsize length,
+ gint *hour, gint *minute, gdouble *seconds, GTimeZone **tz)
+{
+ gssize tz_offset = -1;
+
+ /* Check for timezone suffix */
+ *tz = parse_iso8601_timezone (text, length, &tz_offset);
+ if (tz_offset >= 0)
+ length = tz_offset;
+
+ /* hh:mm:ss(.sss) */
+ if (length >= 8 && text[2] == ':' && text[5] == ':')
+ {
+ return get_iso8601_int (text, 2, hour) &&
+ get_iso8601_int (text + 3, 2, minute) &&
+ get_iso8601_seconds (text + 6, length - 6, seconds);
+ }
+ /* hhmmss(.sss) */
+ else if (length >= 6)
+ {
+ return get_iso8601_int (text, 2, hour) &&
+ get_iso8601_int (text + 2, 2, minute) &&
+ get_iso8601_seconds (text + 4, length - 4, seconds);
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * g_date_time_new_from_iso8601: (constructor)
+ * @text: an ISO 8601 formatted time string.
+ * @default_tz: (nullable): a #GTimeZone to use if the text doesn't contain a
+ * timezone, or %NULL.
+ *
+ * Creates a #GDateTime corresponding to the given
+ * [ISO 8601 formatted string](https://en.wikipedia.org/wiki/ISO_8601)
+ * @text. ISO 8601 strings of the form