[NetworkManager-libreswan/fg/GUI_IKEv2_advanced_options: 1/4] shared: update shared files
- From: Francesco Giudici <fgiudici src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [NetworkManager-libreswan/fg/GUI_IKEv2_advanced_options: 1/4] shared: update shared files
- Date: Wed, 12 Sep 2018 11:55:53 +0000 (UTC)
commit cdbc66a4985c9a17a8b2e08d88a95b0a0276e722
Author: Francesco Giudici <fgiudici redhat com>
Date: Mon Sep 10 17:51:06 2018 +0200
shared: update shared files
shared/nm-default.h | 2 -
shared/nm-utils/gsystem-local-alloc.h | 208 ----
shared/nm-utils/nm-glib.h | 112 ++-
shared/nm-utils/nm-macros-internal.h | 880 ++++++++++++++---
shared/nm-utils/nm-shared-utils.c | 1717 ++++++++++++++++++++++++++++++++-
shared/nm-utils/nm-shared-utils.h | 747 +++++++++++++-
6 files changed, 3319 insertions(+), 347 deletions(-)
---
diff --git a/shared/nm-default.h b/shared/nm-default.h
index 8dfb6af..58807cc 100644
--- a/shared/nm-default.h
+++ b/shared/nm-default.h
@@ -42,8 +42,6 @@
/* always include these headers for our internal source files. */
-#include "nm-utils/nm-glib.h"
-#include "nm-utils/gsystem-local-alloc.h"
#include "nm-utils/nm-macros-internal.h"
#include "nm-version.h"
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index dd18756..770cf0f 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -14,17 +14,19 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 - 2018 Red Hat, Inc.
*/
#ifndef __NM_GLIB_H__
#define __NM_GLIB_H__
+/*****************************************************************************/
-#include <gio/gio.h>
-#include <string.h>
+#ifndef __NM_MACROS_INTERNAL_H__
+#error "nm-glib.h requires nm-macros-internal.h. Do not include this directly"
+#endif
-#include "gsystem-local-alloc.h"
+/*****************************************************************************/
#ifdef __clang__
@@ -40,6 +42,8 @@
#endif
+/*****************************************************************************/
+
static inline void
__g_type_ensure (GType type)
{
@@ -54,6 +58,8 @@ __g_type_ensure (GType type)
}
#define g_type_ensure __g_type_ensure
+/*****************************************************************************/
+
#if !GLIB_CHECK_VERSION(2,34,0)
#define g_clear_pointer(pp, destroy) \
@@ -73,6 +79,12 @@ __g_type_ensure (GType type)
} \
} G_STMT_END
+#endif
+
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION(2,34,0)
+
/* These are used to clean up the output of test programs; we can just let
* them no-op in older glib.
*/
@@ -102,6 +114,7 @@ __g_type_ensure (GType type)
#endif
+/*****************************************************************************/
#if GLIB_CHECK_VERSION (2, 35, 0)
/* For glib >= 2.36, g_type_init() is deprecated.
@@ -112,12 +125,15 @@ __g_type_ensure (GType type)
#define nm_g_type_init() G_STMT_START { g_type_init (); } G_STMT_END
#endif
+/*****************************************************************************/
/* g_test_initialized() is only available since glib 2.36. */
#if !GLIB_CHECK_VERSION (2, 36, 0)
#define g_test_initialized() (g_test_config_vars->test_initialized)
#endif
+/*****************************************************************************/
+
/* g_assert_cmpmem() is only available since glib 2.46. */
#if !GLIB_CHECK_VERSION (2, 45, 7)
#define g_assert_cmpmem(m1, l1, m2, l2) G_STMT_START {\
@@ -132,6 +148,8 @@ __g_type_ensure (GType type)
} G_STMT_END
#endif
+/*****************************************************************************/
+
/* Rumtime check for glib version. First do a compile time check which
* (if satisfied) shortcuts the runtime check. */
static inline gboolean
@@ -146,9 +164,11 @@ nm_glib_check_version (guint major, guint minor, guint micro)
&& glib_micro_version < micro));
}
+/*****************************************************************************/
+
/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */
static inline void
-__nmtst_g_test_skip (const gchar *msg)
+__nmtst_g_test_skip (const char *msg)
{
#if GLIB_CHECK_VERSION (2, 38, 0)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
@@ -160,6 +180,7 @@ __nmtst_g_test_skip (const gchar *msg)
}
#define g_test_skip __nmtst_g_test_skip
+/*****************************************************************************/
/* g_test_add_data_func_full() is only available since glib 2.34. Add a compatibility wrapper. */
static inline void
@@ -184,6 +205,7 @@ __g_test_add_data_func_full (const char *testpath,
}
#define g_test_add_data_func_full __g_test_add_data_func_full
+/*****************************************************************************/
#if !GLIB_CHECK_VERSION (2, 34, 0)
#define G_DEFINE_QUARK(QN, q_n) \
@@ -199,6 +221,7 @@ q_n##_quark (void) \
}
#endif
+/*****************************************************************************/
static inline gboolean
nm_g_hash_table_replace (GHashTable *hash, gpointer key, gpointer value)
@@ -245,19 +268,21 @@ nm_g_hash_table_add (GHashTable *hash, gpointer key)
#endif
}
+/*****************************************************************************/
+
#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
static inline void
_nm_g_ptr_array_insert (GPtrArray *array,
- gint index_,
+ int index_,
gpointer data)
{
g_return_if_fail (array);
g_return_if_fail (index_ >= -1);
- g_return_if_fail (index_ <= (gint) array->len);
+ g_return_if_fail (index_ <= (int) array->len);
g_ptr_array_add (array, data);
- if (index_ != -1 && index_ != (gint) (array->len - 1)) {
+ if (index_ != -1 && index_ != (int) (array->len - 1)) {
memmove (&(array->pdata[index_ + 1]),
&(array->pdata[index_]),
(array->len - index_ - 1) * sizeof (gpointer));
@@ -265,6 +290,7 @@ _nm_g_ptr_array_insert (GPtrArray *array,
}
}
#endif
+
#if !GLIB_CHECK_VERSION(2, 40, 0)
#define g_ptr_array_insert(array, index, data) G_STMT_START { _nm_g_ptr_array_insert (array, index, data); }
G_STMT_END
#else
@@ -276,14 +302,15 @@ _nm_g_ptr_array_insert (GPtrArray *array,
} G_STMT_END
#endif
+/*****************************************************************************/
#if !GLIB_CHECK_VERSION (2, 40, 0)
static inline gboolean
_g_key_file_save_to_file (GKeyFile *key_file,
- const gchar *filename,
+ const char *filename,
GError **error)
{
- gchar *contents;
+ char *contents;
gboolean success;
gsize length;
@@ -313,6 +340,7 @@ _g_key_file_save_to_file (GKeyFile *key_file,
})
#endif
+/*****************************************************************************/
#if GLIB_CHECK_VERSION (2, 36, 0)
#define g_credentials_get_unix_pid(creds, error) \
@@ -332,6 +360,7 @@ _g_key_file_save_to_file (GKeyFile *key_file,
})
#endif
+/*****************************************************************************/
#if !GLIB_CHECK_VERSION(2, 40, 0) || defined (NM_GLIB_COMPAT_H_TEST)
static inline gpointer *
@@ -372,6 +401,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table,
})
#endif
+/*****************************************************************************/
+
#ifndef g_info
/* g_info was only added with 2.39.2 */
#define g_info(...) g_log (G_LOG_DOMAIN, \
@@ -379,6 +410,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table,
__VA_ARGS__)
#endif
+/*****************************************************************************/
+
#if !GLIB_CHECK_VERSION(2, 44, 0)
static inline gpointer
g_steal_pointer (gpointer pp)
@@ -397,10 +430,11 @@ g_steal_pointer (gpointer pp)
(0 ? (*(pp)) : (g_steal_pointer) (pp))
#endif
+/*****************************************************************************/
static inline gboolean
-_nm_g_strv_contains (const gchar * const *strv,
- const gchar *str)
+_nm_g_strv_contains (const char * const *strv,
+ const char *str)
{
#if !GLIB_CHECK_VERSION(2, 44, 0)
g_return_val_if_fail (strv != NULL, FALSE);
@@ -420,8 +454,10 @@ _nm_g_strv_contains (const gchar * const *strv,
}
#define g_strv_contains _nm_g_strv_contains
+/*****************************************************************************/
+
static inline GVariant *
-_nm_g_variant_new_take_string (gchar *string)
+_nm_g_variant_new_take_string (char *string)
{
#if !GLIB_CHECK_VERSION(2, 36, 0)
GVariant *value;
@@ -452,4 +488,54 @@ _nm_g_variant_new_take_string (gchar *string)
}
#define g_variant_new_take_string _nm_g_variant_new_take_string
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION(2, 38, 0)
+_nm_printf (1, 2)
+static inline GVariant *
+_nm_g_variant_new_printf (const char *format_string, ...)
+{
+ char *string;
+ va_list ap;
+
+ g_return_val_if_fail (format_string, NULL);
+
+ va_start (ap, format_string);
+ string = g_strdup_vprintf (format_string, ap);
+ va_end (ap);
+
+ return g_variant_new_take_string (string);
+}
+#define g_variant_new_printf(...) _nm_g_variant_new_printf(__VA_ARGS__)
+#else
+#define g_variant_new_printf(...) \
+ ({ \
+ GVariant *_v; \
+ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ _v = g_variant_new_printf (__VA_ARGS__); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ _v; \
+ })
+#endif
+
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION (2, 56, 0)
+#define g_object_ref(Obj) ((typeof(Obj)) g_object_ref (Obj))
+#define g_object_ref_sink(Obj) ((typeof(Obj)) g_object_ref_sink (Obj))
+#endif
+
+/*****************************************************************************/
+
+#ifndef g_autofree
+/* we still don't rely on recent glib to provide g_autofree. Hence, we continue
+ * to use our gs_* free macros that we took from libgsystem.
+ *
+ * To ease migration towards g_auto*, add a compat define for g_autofree. */
+#define g_autofree gs_free
+#endif
+
+/*****************************************************************************/
+
#endif /* __NM_GLIB_H__ */
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 2548665..084219b 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -16,6 +16,7 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
+ * (C) Copyright 2012 Colin Walters <walters verbum org>.
* (C) Copyright 2014 Red Hat, Inc.
*/
@@ -25,58 +26,247 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
+#include <string.h>
-#include "nm-glib.h"
+#include <gio/gio.h>
+
+/*****************************************************************************/
+
+#define _nm_packed __attribute__ ((packed))
+#define _nm_unused __attribute__ ((unused))
+#define _nm_pure __attribute__ ((pure))
+#define _nm_const __attribute__ ((const))
+#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#define _nm_align(s) __attribute__ ((aligned (s)))
+#define _nm_alignof(type) __alignof (type)
+#define _nm_alignas(type) _nm_align (_nm_alignof (type))
+#define nm_auto(fcn) __attribute__ ((cleanup(fcn)))
+
+
+#if __GNUC__ >= 7
+#define _nm_fallthrough __attribute__ ((fallthrough))
+#else
+#define _nm_fallthrough
+#endif
+
+/*****************************************************************************/
+
+#ifdef thread_local
+#define _nm_thread_local thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#elif __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) &&
__GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
+#define _nm_thread_local _Thread_local
+#else
+#define _nm_thread_local __thread
+#endif
+
+/*****************************************************************************/
+
+#define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \
+static inline void name (void *v) \
+{ \
+ func (*((CastType *) v)); \
+}
+
+#define NM_AUTO_DEFINE_FCN_VOID0(CastType, name, func) \
+static inline void name (void *v) \
+{ \
+ if (*((CastType *) v)) \
+ func (*((CastType *) v)); \
+}
+
+#define NM_AUTO_DEFINE_FCN(Type, name, func) \
+static inline void name (Type *v) \
+{ \
+ func (*v); \
+}
+
+#define NM_AUTO_DEFINE_FCN0(Type, name, func) \
+static inline void name (Type *v) \
+{ \
+ if (*v) \
+ func (*v); \
+}
+
+/*****************************************************************************/
+
+/**
+ * gs_free:
+ *
+ * Call g_free() on a variable location when it goes out of scope.
+ */
+#define gs_free nm_auto(gs_local_free)
+NM_AUTO_DEFINE_FCN_VOID (void *, gs_local_free, g_free)
+
+/**
+ * gs_unref_object:
+ *
+ * Call g_object_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_object_unref(), the variable may be
+ * %NULL.
+ */
+#define gs_unref_object nm_auto(gs_local_obj_unref)
+NM_AUTO_DEFINE_FCN_VOID0 (GObject *, gs_local_obj_unref, g_object_unref)
+
+/**
+ * gs_unref_variant:
+ *
+ * Call g_variant_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_variant_unref(), the variable may be
+ * %NULL.
+ */
+#define gs_unref_variant nm_auto(gs_local_variant_unref)
+NM_AUTO_DEFINE_FCN0 (GVariant *, gs_local_variant_unref, g_variant_unref)
+
+/**
+ * gs_unref_array:
+ *
+ * Call g_array_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define gs_unref_array nm_auto(gs_local_array_unref)
+NM_AUTO_DEFINE_FCN0 (GArray *, gs_local_array_unref, g_array_unref)
+
+/**
+ * gs_unref_ptrarray:
+ *
+ * Call g_ptr_array_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_ptr_array_unref(), the variable may be
+ * %NULL.
+
+ */
+#define gs_unref_ptrarray nm_auto(gs_local_ptrarray_unref)
+NM_AUTO_DEFINE_FCN0 (GPtrArray *, gs_local_ptrarray_unref, g_ptr_array_unref)
+
+/**
+ * gs_unref_hashtable:
+ *
+ * Call g_hash_table_unref() on a variable location when it goes out
+ * of scope. Note that unlike g_hash_table_unref(), the variable may
+ * be %NULL.
+ */
+#define gs_unref_hashtable nm_auto(gs_local_hashtable_unref)
+NM_AUTO_DEFINE_FCN0 (GHashTable *, gs_local_hashtable_unref, g_hash_table_unref)
+
+/**
+ * gs_free_slist:
+ *
+ * Call g_slist_free() on a variable location when it goes out
+ * of scope.
+ */
+#define gs_free_slist nm_auto(gs_local_free_slist)
+NM_AUTO_DEFINE_FCN (GSList *, gs_local_free_slist, g_slist_free)
+
+/**
+ * gs_unref_bytes:
+ *
+ * Call g_bytes_unref() on a variable location when it goes out
+ * of scope. Note that unlike g_bytes_unref(), the variable may
+ * be %NULL.
+ */
+#define gs_unref_bytes nm_auto(gs_local_bytes_unref)
+NM_AUTO_DEFINE_FCN0 (GBytes *, gs_local_bytes_unref, g_bytes_unref)
+
+/**
+ * gs_strfreev:
+ *
+ * Call g_strfreev() on a variable location when it goes out of scope.
+ */
+#define gs_strfreev nm_auto(gs_local_strfreev)
+NM_AUTO_DEFINE_FCN (char **, gs_local_strfreev, g_strfreev)
+
+/**
+ * gs_free_error:
+ *
+ * Call g_error_free() on a variable location when it goes out of scope.
+ */
+#define gs_free_error nm_auto(gs_local_free_error)
+NM_AUTO_DEFINE_FCN0 (GError *, gs_local_free_error, g_error_free)
+
+/**
+ * gs_unref_keyfile:
+ *
+ * Call g_key_file_unref() on a variable location when it goes out of scope.
+ */
+#define gs_unref_keyfile nm_auto(gs_local_keyfile_unref)
+NM_AUTO_DEFINE_FCN0 (GKeyFile *, gs_local_keyfile_unref, g_key_file_unref)
/*****************************************************************************/
-#define _nm_packed __attribute__ ((packed))
-#define _nm_unused __attribute__ ((unused))
-#define _nm_pure __attribute__ ((pure))
-#define _nm_const __attribute__ ((const))
-#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#include "nm-glib.h"
+
+/*****************************************************************************/
#define nm_offsetofend(t,m) (G_STRUCT_OFFSET (t,m) + sizeof (((t *) NULL)->m))
-#define nm_auto(fcn) __attribute__ ((cleanup(fcn)))
+/*****************************************************************************/
+
+static inline int nm_close (int fd);
/**
* nm_auto_free:
*
* Call free() on a variable location when it goes out of scope.
+ * This is for pointers that are allocated with malloc() instead of
+ * g_malloc().
+ *
+ * In practice, since glib 2.45, g_malloc()/g_free() always wraps malloc()/free().
+ * See bgo#751592. In that case, it would be safe to free pointers allocated with
+ * malloc() with gs_free or g_free().
+ *
+ * However, let's never mix them. To free malloc'ed memory, always use
+ * free() or nm_auto_free.
*/
+NM_AUTO_DEFINE_FCN_VOID (void *, _nm_auto_free_impl, free)
#define nm_auto_free nm_auto(_nm_auto_free_impl)
-GS_DEFINE_CLEANUP_FUNCTION(void*, _nm_auto_free_impl, free)
-static inline void
-_nm_auto_unset_gvalue_impl (GValue *v)
-{
- g_value_unset (v);
-}
-#define nm_auto_unset_gvalue nm_auto(_nm_auto_unset_gvalue_impl)
+NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_free)
+#define nm_auto_free_variant_iter nm_auto(_nm_auto_free_variant_iter)
+
+NM_AUTO_DEFINE_FCN0 (GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref)
+#define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder)
+
+NM_AUTO_DEFINE_FCN (GList *, _nm_auto_free_list, g_list_free)
+#define nm_auto_free_list nm_auto(_nm_auto_free_list)
+
+NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free)
+#define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free)
+
+#define nm_auto_unset_gvalue nm_auto(g_value_unset)
+
+NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref)
+#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass)
+
+NM_AUTO_DEFINE_FCN0 (GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref)
+#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray)
static inline void
-_nm_auto_free_gstring_impl (GString **str)
+_nm_auto_free_gstring (GString **str)
{
if (*str)
g_string_free (*str, TRUE);
}
-#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring_impl)
+#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring)
static inline void
-_nm_auto_close_impl (int *pfd)
+_nm_auto_close (int *pfd)
{
if (*pfd >= 0) {
int errsv = errno;
- (void) close (*pfd);
+ (void) nm_close (*pfd);
errno = errsv;
}
}
-#define nm_auto_close nm_auto(_nm_auto_close_impl)
+#define nm_auto_close nm_auto(_nm_auto_close)
static inline void
-_nm_auto_fclose_impl (FILE **pfd)
+_nm_auto_fclose (FILE **pfd)
{
if (*pfd) {
int errsv = errno;
@@ -85,7 +275,7 @@ _nm_auto_fclose_impl (FILE **pfd)
errno = errsv;
}
}
-#define nm_auto_fclose nm_auto(_nm_auto_fclose_impl)
+#define nm_auto_fclose nm_auto(_nm_auto_fclose)
static inline void
_nm_auto_protect_errno (int *p_saved_errno)
@@ -94,6 +284,26 @@ _nm_auto_protect_errno (int *p_saved_errno)
}
#define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved
= (errno)
+NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref);
+#define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
+
+static inline void
+_nm_auto_freev (gpointer ptr)
+{
+ gpointer **p = ptr;
+ gpointer *_ptr;
+
+ if (*p) {
+ for (_ptr = *p; *_ptr; _ptr++)
+ g_free (*_ptr);
+ g_free (*p);
+ }
+}
+/* g_free a NULL terminated array of pointers, with also freeing each
+ * pointer with g_free(). It essentially does the same as
+ * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */
+#define nm_auto_freev nm_auto(_nm_auto_freev)
+
/*****************************************************************************/
/* http://stackoverflow.com/a/11172679 */
@@ -106,22 +316,27 @@ _nm_auto_protect_errno (int *p_saved_errno)
#define __NM_UTILS_MACRO_REST_HELPER_ONE(first)
#define __NM_UTILS_MACRO_REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__
#define __NM_UTILS_MACRO_REST_NUM(...) \
- __NM_UTILS_MACRO_REST_SELECT_20TH(__VA_ARGS__, \
+ __NM_UTILS_MACRO_REST_SELECT_30TH(__VA_ARGS__, \
+ TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
+ TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway)
-#define __NM_UTILS_MACRO_REST_SELECT_20TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, ...) a20
+#define __NM_UTILS_MACRO_REST_SELECT_30TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, ...) a30
/*****************************************************************************/
-/* http://stackoverflow.com/a/2124385/354393 */
+/* http://stackoverflow.com/a/2124385/354393
+ * https://stackoverflow.com/questions/11317474/macro-to-count-number-of-arguments
+ */
#define NM_NARG(...) \
- _NM_NARG(__VA_ARGS__,_NM_NARG_RSEQ_N())
+ _NM_NARG(, ##__VA_ARGS__, _NM_NARG_RSEQ_N())
#define _NM_NARG(...) \
_NM_NARG_ARG_N(__VA_ARGS__)
#define _NM_NARG_ARG_N( \
+ _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,_26,_27,_28,_29,_30, \
@@ -159,6 +374,7 @@ _nm_auto_protect_errno (int *p_saved_errno)
#elif defined (__clang__)
#define NM_PRAGMA_WARNING_DISABLE(warning) \
_Pragma("clang diagnostic push") \
+ _Pragma(_NM_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \
_Pragma(_NM_PRAGMA_WARNING_DO(warning))
#else
#define NM_PRAGMA_WARNING_DISABLE(warning)
@@ -178,7 +394,7 @@ _nm_auto_protect_errno (int *p_saved_errno)
/**
* NM_G_ERROR_MSG:
- * @error: (allow none): the #GError instance
+ * @error: (allow-none): the #GError instance
*
* All functions must follow the convention that when they
* return a failure, they must also set the GError to a valid
@@ -191,7 +407,7 @@ _nm_auto_protect_errno (int *p_saved_errno)
static inline const char *
NM_G_ERROR_MSG (GError *error)
{
- return error ? (error->message ? : "(null)") : "(no-error)"; \
+ return error ? (error->message ?: "(null)") : "(no-error)"; \
}
/*****************************************************************************/
@@ -199,6 +415,24 @@ NM_G_ERROR_MSG (GError *error)
/* macro to return strlen() of a compile time string. */
#define NM_STRLEN(str) ( sizeof ("" str) - 1 )
+/* returns the length of a NULL terminated array of pointers,
+ * like g_strv_length() does. The difference is:
+ * - it operats on arrays of pointers (of any kind, requiring no cast).
+ * - it accepts NULL to return zero. */
+#define NM_PTRARRAY_LEN(array) \
+ ({ \
+ typeof (*(array)) *const _array = (array); \
+ gsize _n = 0; \
+ \
+ if (_array) { \
+ _nm_unused gconstpointer _type_check_is_pointer = _array[0]; \
+ \
+ while (_array[_n]) \
+ _n++; \
+ } \
+ _n; \
+ })
+
/* Note: @value is only evaluated when *out_val is present.
* Thus,
* NM_SET_OUT (out_str, g_strdup ("hallo"));
@@ -215,6 +449,198 @@ NM_G_ERROR_MSG (GError *error)
/*****************************************************************************/
+#ifndef _NM_CC_SUPPORT_AUTO_TYPE
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 )))
+#define _NM_CC_SUPPORT_AUTO_TYPE 1
+#else
+#define _NM_CC_SUPPORT_AUTO_TYPE 0
+#endif
+#endif
+
+#ifndef _NM_CC_SUPPORT_GENERIC
+#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined
(__clang__))
+#define _NM_CC_SUPPORT_GENERIC 1
+#else
+#define _NM_CC_SUPPORT_GENERIC 0
+#endif
+#endif
+
+#if _NM_CC_SUPPORT_AUTO_TYPE
+#define _nm_auto_type __auto_type
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const alias_type2 *const: ((const type *) (obj)), \
+ const alias_type2 * : ((const type *) (obj)), \
+ alias_type2 *const: (( type *) (obj)), \
+ alias_type2 * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const alias_type2 *const: ((const type *) (obj)), \
+ const alias_type2 * : ((const type *) (obj)), \
+ alias_type2 *const: (( type *) (obj)), \
+ alias_type2 * : (( type *) (obj)), \
+ const alias_type3 *const: ((const type *) (obj)), \
+ const alias_type3 * : ((const type *) (obj)), \
+ alias_type3 *const: (( type *) (obj)), \
+ alias_type3 * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \
+ (_Generic ((obj_expr), \
+ const void *const: ((const type *) (obj)), \
+ const void * : ((const type *) (obj)), \
+ void *const: (( type *) (obj)), \
+ void * : (( type *) (obj)), \
+ const alias_type2 *const: ((const type *) (obj)), \
+ const alias_type2 * : ((const type *) (obj)), \
+ alias_type2 *const: (( type *) (obj)), \
+ alias_type2 * : (( type *) (obj)), \
+ const alias_type3 *const: ((const type *) (obj)), \
+ const alias_type3 * : ((const type *) (obj)), \
+ alias_type3 *const: (( type *) (obj)), \
+ alias_type3 * : (( type *) (obj)), \
+ const alias_type4 *const: ((const type *) (obj)), \
+ const alias_type4 * : ((const type *) (obj)), \
+ alias_type4 *const: (( type *) (obj)), \
+ alias_type4 * : (( type *) (obj)), \
+ const type *const: ((const type *) (obj)), \
+ const type * : ((const type *) (obj)), \
+ type *const: (( type *) (obj)), \
+ type * : (( type *) (obj))))
+#define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_##n (type, obj_expr, obj,
##__VA_ARGS__))
+#define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_x (type, obj_expr, obj, n,
##__VA_ARGS__))
+#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) (_NM_CONSTCAST_FULL_y (type, obj_expr, obj,
NM_NARG (dummy, ##__VA_ARGS__), ##__VA_ARGS__))
+#else
+#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) ((type *) (obj))
+#endif
+
+#define NM_CONSTCAST(type, obj, ...) \
+ NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__)
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_UNCONST_PTR(type, arg) \
+ _Generic ((arg), \
+ const type *: ((type *) (arg)), \
+ type *: ((type *) (arg)))
+#else
+#define NM_UNCONST_PTR(type, arg) \
+ ((type *) (arg))
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_UNCONST_PPTR(type, arg) \
+ _Generic ((arg), \
+ const type * *: ((type **) (arg)), \
+ type * *: ((type **) (arg)), \
+ const type *const*: ((type **) (arg)), \
+ type *const*: ((type **) (arg)))
+#else
+#define NM_UNCONST_PPTR(type, arg) \
+ ((type **) (arg))
+#endif
+
+#define NM_GOBJECT_CAST(type, obj, is_check, ...) \
+ ({ \
+ const void *_obj = (obj); \
+ \
+ nm_assert (_obj || (is_check (_obj))); \
+ NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \
+ })
+
+#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \
+ ({ \
+ const void *_obj = (obj); \
+ \
+ nm_assert (is_check (_obj)); \
+ NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \
+ })
+
+#if _NM_CC_SUPPORT_GENERIC
+/* returns @value, if the type of @value matches @type.
+ * This requires support for C11 _Generic(). If no support is
+ * present, this returns @value directly.
+ *
+ * It's useful to check the let the compiler ensure that @value is
+ * of a certain type. */
+#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value)))
+#else
+#define _NM_ENSURE_TYPE(type, value) (value)
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+/* these macros cast (value) to
+ * - "const char **" (for "MC", mutable-const)
+ * - "const char *const*" (for "CC", const-const)
+ * The point is to do this cast, but only accepting pointers
+ * that are compatible already.
+ *
+ * The problem is, if you add a function like g_strdupv(), the input
+ * argument is not modified (CC), but you want to make it work also
+ * for "char **". C doesn't allow this form of casting (for good reasons),
+ * so the function makes a choice like g_strdupv(char**). That means,
+ * every time you want to call ith with a const argument, you need to
+ * explicitly cast it.
+ *
+ * These macros do the cast, but they only accept a compatible input
+ * type, otherwise they will fail compilation.
+ */
+#define NM_CAST_STRV_MC(value) \
+ (_Generic ((value), \
+ const char * *: (const char * *) (value), \
+ char * *: (const char * *) (value), \
+ void *: (const char * *) (value)))
+#define NM_CAST_STRV_CC(value) \
+ (_Generic ((value), \
+ const char *const*: (const char *const*) (value), \
+ const char * *: (const char *const*) (value), \
+ char *const*: (const char *const*) (value), \
+ char * *: (const char *const*) (value), \
+ const void *: (const char *const*) (value), \
+ void *: (const char *const*) (value)))
+#else
+#define NM_CAST_STRV_MC(value) ((const char * *) (value))
+#define NM_CAST_STRV_CC(value) ((const char *const*) (value))
+#endif
+
+#if _NM_CC_SUPPORT_GENERIC
+#define NM_PROPAGATE_CONST(test_expr, ptr) \
+ (_Generic ((test_expr), \
+ const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \
+ default: (_Generic ((test_expr), \
+ typeof (*(test_expr)) *: (ptr)))))
+#else
+#define NM_PROPAGATE_CONST(test_expr, ptr) (ptr)
+#endif
+
+/*****************************************************************************/
+
#define _NM_IN_SET_EVAL_1( op, _x, y) (_x == (y))
#define _NM_IN_SET_EVAL_2( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_1 (op, _x, __VA_ARGS__)
#define _NM_IN_SET_EVAL_3( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_2 (op, _x, __VA_ARGS__)
@@ -233,21 +659,34 @@ NM_G_ERROR_MSG (GError *error)
#define _NM_IN_SET_EVAL_16(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_15 (op, _x, __VA_ARGS__)
#define _NM_IN_SET_EVAL_N2(op, _x, n, ...) (_NM_IN_SET_EVAL_##n(op, _x, __VA_ARGS__))
-#define _NM_IN_SET_EVAL_N(op, x, n, ...) \
+#define _NM_IN_SET_EVAL_N(op, type, x, n, ...) \
({ \
- typeof(x) _x = (x); \
+ type _x = (x); \
+ \
+ /* trigger a -Wenum-compare warning */ \
+ nm_assert (TRUE || _x == (x)); \
+ \
!!_NM_IN_SET_EVAL_N2(op, _x, n, __VA_ARGS__); \
})
+#define _NM_IN_SET(op, type, x, ...) _NM_IN_SET_EVAL_N(op, type, x, NM_NARG (__VA_ARGS__),
__VA_ARGS__)
+
/* Beware that this does short-circuit evaluation (use "||" instead of "|")
* which has a possibly unexpected non-function-like behavior.
* Use NM_IN_SET_SE if you need all arguments to be evaluted. */
-#define NM_IN_SET(x, ...) _NM_IN_SET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+#define NM_IN_SET(x, ...) _NM_IN_SET(||, typeof (x), x, __VA_ARGS__)
/* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do
* short-circuit evaluation, which can make a difference if the arguments have
* side-effects. */
-#define NM_IN_SET_SE(x, ...) _NM_IN_SET_EVAL_N(|, x, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+#define NM_IN_SET_SE(x, ...) _NM_IN_SET(|, typeof (x), x, __VA_ARGS__)
+
+/* the *_TYPED forms allow to explicitly select the type of "x". This is useful
+ * if "x" doesn't support typeof (bitfields) or you want to gracefully convert
+ * a type using automatic type conversion rules (but not forcing the conversion
+ * with a cast). */
+#define NM_IN_SET_TYPED(type, x, ...) _NM_IN_SET(||, type, x, __VA_ARGS__)
+#define NM_IN_SET_SE_TYPED(type, x, ...) _NM_IN_SET(|, type, x, __VA_ARGS__)
/*****************************************************************************/
@@ -278,8 +717,8 @@ _NM_IN_STRSET_streq (const char *x, const char *s)
#define _NM_IN_STRSET_EVAL_N(op, x, n, ...) \
({ \
const char *_x = (x); \
- ( ((_x == NULL) && _NM_IN_SET_EVAL_N2 (op, (const char *) NULL, n, __VA_ARGS__)) \
- || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x, n, __VA_ARGS__)) \
+ ( ((_x == NULL) && _NM_IN_SET_EVAL_N2 (op, ((const char *) NULL), n, __VA_ARGS__)) \
+ || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x, n, __VA_ARGS__)) \
); \
})
@@ -357,7 +796,7 @@ _NM_IN_STRSET_streq (const char *x, const char *s)
/* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK
* with two differences:
- * - @string must be a quited string-literal
+ * - @string must be a quoted string-literal
* - @fcn must be the full function name, while G_DEFINE_QUARK() appends
* "_quark" to the function name.
* Both properties of G_DEFINE_QUARK() are non favorable, because you can no
@@ -380,6 +819,16 @@ fcn (void) \
/*****************************************************************************/
+static inline GString *
+nm_gstring_prepare (GString **l)
+{
+ if (*l)
+ g_string_set_size (*l, 0);
+ else
+ *l = g_string_sized_new (30);
+ return *l;
+}
+
static inline const char *
nm_str_not_empty (const char *str)
{
@@ -451,7 +900,7 @@ nm_str_realloc (char *str)
#define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \
typedef enum { \
- _PROPERTY_ENUMS_0, \
+ PROP_0, \
__VA_ARGS__ \
_PROPERTY_ENUMS_LAST, \
} _PropertyEnums; \
@@ -460,30 +909,53 @@ static GParamSpec *obj_properties[_PROPERTY_ENUMS_LAST] = { NULL, }
#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \
NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \
static inline void \
-_notify (obj_type *obj, _PropertyEnums prop) \
+_nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums *props) \
{ \
+ const gboolean freeze_thaw = (n > 1); \
+ \
nm_assert (G_IS_OBJECT (obj)); \
- nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \
- g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \
-}
+ nm_assert (n > 0); \
+ \
+ if (freeze_thaw) \
+ g_object_freeze_notify ((GObject *) obj); \
+ while (n-- > 0) { \
+ const _PropertyEnums prop = *props++; \
+ \
+ if (prop != PROP_0) { \
+ nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \
+ nm_assert (obj_properties[prop]); \
+ g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \
+ } \
+ } \
+ if (freeze_thaw) \
+ g_object_thaw_notify ((GObject *) obj); \
+} \
+\
+static inline void \
+_notify (obj_type *obj, _PropertyEnums prop) \
+{ \
+ _nm_gobject_notify_together_impl (obj, 1, &prop); \
+} \
+
+/* invokes _notify() for all arguments (of type _PropertyEnums). Note, that if
+ * there are more than one prop arguments, this will involve a freeze/thaw
+ * of GObject property notifications. */
+#define nm_gobject_notify_together(obj, ...) \
+ _nm_gobject_notify_together_impl (obj, NM_NARG (__VA_ARGS__), (const _PropertyEnums[]) { __VA_ARGS__
})
/*****************************************************************************/
-#define __NM_GET_PRIVATE(self, type, is_check, result_cmd) \
+#define _NM_GET_PRIVATE(self, type, is_check, ...) (&(NM_GOBJECT_CAST_NON_NULL (type, (self), is_check,
##__VA_ARGS__)->_priv))
+#if _NM_CC_SUPPORT_AUTO_TYPE
+#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \
({ \
- /* preserve the const-ness of self. Unfortunately, that
- * way, @self cannot be a void pointer */ \
- typeof (self) _self = (self); \
+ _nm_auto_type _self = NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__); \
\
- /* Get compiler error if variable is of wrong type */ \
- _nm_unused const type *_self2 = (_self); \
- \
- nm_assert (is_check (_self)); \
- ( result_cmd ); \
+ NM_PROPAGATE_CONST (_self, _self->_priv); \
})
-
-#define _NM_GET_PRIVATE(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, &_self->_priv)
-#define _NM_GET_PRIVATE_PTR(self, type, is_check) __NM_GET_PRIVATE(self, type, is_check, _self->_priv)
+#else
+#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) (NM_GOBJECT_CAST_NON_NULL (type, (self), is_check,
##__VA_ARGS__)->_priv)
+#endif
/*****************************************************************************/
@@ -495,6 +967,7 @@ nm_g_object_ref (gpointer obj)
g_object_ref (obj);
return obj;
}
+#define nm_g_object_ref(obj) ((typeof (obj)) nm_g_object_ref (obj))
static inline void
nm_g_object_unref (gpointer obj)
@@ -507,6 +980,58 @@ nm_g_object_unref (gpointer obj)
g_object_unref (obj);
}
+/* Assigns GObject @obj to destination @pp, and takes an additional ref.
+ * The previous value of @pp is unrefed.
+ *
+ * It makes sure to first increase the ref-count of @obj, and handles %NULL
+ * @obj correctly.
+ * */
+#define nm_g_object_ref_set(pp, obj) \
+ ({ \
+ typeof (*(pp)) *const _pp = (pp); \
+ typeof (*_pp) const _obj = (obj); \
+ typeof (*_pp) _p; \
+ gboolean _changed = FALSE; \
+ \
+ nm_assert (!_pp || !*_pp || G_IS_OBJECT (*_pp)); \
+ nm_assert (!_obj || G_IS_OBJECT (_obj)); \
+ \
+ if ( _pp \
+ && ((_p = *_pp) != _obj)) { \
+ nm_g_object_ref (_obj); \
+ *_pp = _obj; \
+ nm_g_object_unref (_p); \
+ _changed = TRUE; \
+ } \
+ _changed; \
+ })
+
+#define nm_clear_pointer(pp, destroy) \
+ ({ \
+ typeof (*(pp)) *_pp = (pp); \
+ typeof (*_pp) _p; \
+ gboolean _changed = FALSE; \
+ \
+ if ( _pp \
+ && (_p = *_pp)) { \
+ _nm_unused gconstpointer _p_check_is_pointer = _p; \
+ \
+ *_pp = NULL; \
+ /* g_clear_pointer() assigns @destroy first to a local variable, so that
+ * you can call "g_clear_pointer (pp, (GDestroyNotify) destroy);" without
+ * gcc emitting a warning. We don't do that, hence, you cannot cast
+ * "destroy" first.
+ *
+ * On the upside: you are not supposed to cast fcn, because the pointer
+ * types are preserved. If you really need a cast, you should cast @pp.
+ * But that is hardly ever necessary. */ \
+ (destroy) (_p); \
+ \
+ _changed = TRUE; \
+ } \
+ _changed; \
+ })
+
/* basically, replaces
* g_clear_pointer (&location, g_free)
* with
@@ -517,23 +1042,20 @@ nm_g_object_unref (gpointer obj)
* pointer or points to a const-pointer.
*/
#define nm_clear_g_free(pp) \
- ({ \
- typeof (*(pp)) *_pp = (pp); \
- typeof (**_pp) *_p = *_pp; \
- \
- if (_p) { \
- *_pp = NULL; \
- g_free (_p); \
- } \
- !!_p; \
- })
+ nm_clear_pointer (pp, g_free)
+
+#define nm_clear_g_object(pp) \
+ nm_clear_pointer (pp, g_object_unref)
static inline gboolean
nm_clear_g_source (guint *id)
{
- if (id && *id) {
- g_source_remove (*id);
+ guint v;
+
+ if ( id
+ && (v = *id)) {
*id = 0;
+ g_source_remove (v);
return TRUE;
}
return FALSE;
@@ -542,9 +1064,12 @@ nm_clear_g_source (guint *id)
static inline gboolean
nm_clear_g_signal_handler (gpointer self, gulong *id)
{
- if (id && *id) {
- g_signal_handler_disconnect (self, *id);
+ gulong v;
+
+ if ( id
+ && (v = *id)) {
*id = 0;
+ g_signal_handler_disconnect (self, v);
return TRUE;
}
return FALSE;
@@ -553,9 +1078,12 @@ nm_clear_g_signal_handler (gpointer self, gulong *id)
static inline gboolean
nm_clear_g_variant (GVariant **variant)
{
- if (variant && *variant) {
- g_variant_unref (*variant);
+ GVariant *v;
+
+ if ( variant
+ && (v = *variant)) {
*variant = NULL;
+ g_variant_unref (v);
return TRUE;
}
return FALSE;
@@ -564,10 +1092,13 @@ nm_clear_g_variant (GVariant **variant)
static inline gboolean
nm_clear_g_cancellable (GCancellable **cancellable)
{
- if (cancellable && *cancellable) {
- g_cancellable_cancel (*cancellable);
- g_object_unref (*cancellable);
+ GCancellable *v;
+
+ if ( cancellable
+ && (v = *cancellable)) {
*cancellable = NULL;
+ g_cancellable_cancel (v);
+ g_object_unref (v);
return TRUE;
}
return FALSE;
@@ -576,18 +1107,65 @@ nm_clear_g_cancellable (GCancellable **cancellable)
/*****************************************************************************/
/* Determine whether @x is a power of two (@x being an integer type).
- * For the special cases @x equals zero or one, it also returns true.
- * For negative @x, always returns FALSE. That only applies, if the data
- * type of @x is signed. */
+ * Basically, this returns TRUE, if @x has exactly one bit set.
+ * For negative values and zero, this always returns FALSE. */
#define nm_utils_is_power_of_two(x) ({ \
typeof(x) __x = (x); \
\
- /* Check if the value is negative. In that case, return FALSE.
- * The first expression is a compile time constant, depending on whether
- * the type is signed. The second expression is a clumsy way for (__x >= 0),
- * which causes a compiler warning for unsigned types. */ \
- ( ( ((typeof(__x)) -1) > ((typeof(__x)) 0) ) || (__x > 0) || (__x == 0) ) \
- && ((__x & (__x - 1)) == 0); \
+ ( (__x > ((typeof(__x)) 0)) \
+ && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \
+ })
+
+#define NM_DIV_ROUND_UP(x, y) \
+ ({ \
+ const typeof(x) _x = (x); \
+ const typeof(y) _y = (y); \
+ \
+ (_x / _y + !!(_x % _y)); \
+ })
+
+/*****************************************************************************/
+
+#define NM_UTILS_LOOKUP_DEFAULT(v) return (v)
+#define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached (v)
+#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) { nm_assert_not_reached (); return (v); }
+#define NM_UTILS_LOOKUP_ITEM(v, n) (void) 0; case v: return (n); (void) 0
+#define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, ""n"")
+#define NM_UTILS_LOOKUP_ITEM_IGNORE(v) (void) 0; case v: break; (void) 0
+#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() (void) 0; default: break; (void) 0
+
+#define _NM_UTILS_LOOKUP_DEFINE(scope, fcn_name, lookup_type, result_type, unknown_val, ...) \
+scope result_type \
+fcn_name (lookup_type val) \
+{ \
+ switch (val) { \
+ (void) 0, \
+ __VA_ARGS__ \
+ (void) 0; \
+ }; \
+ { unknown_val; } \
+}
+
+#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \
+ _NM_UTILS_LOOKUP_DEFINE (, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__)
+#define NM_UTILS_LOOKUP_STR_DEFINE_STATIC(fcn_name, lookup_type, unknown_val, ...) \
+ _NM_UTILS_LOOKUP_DEFINE (static, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__)
+
+/* Call the string-lookup-table function @fcn_name. If the function returns
+ * %NULL, the numeric index is converted to string using a alloca() buffer.
+ * Beware: this macro uses alloca(). */
+#define NM_UTILS_LOOKUP_STR(fcn_name, idx) \
+ ({ \
+ typeof (idx) _idx = (idx); \
+ const char *_s; \
+ \
+ _s = fcn_name (_idx); \
+ if (!_s) { \
+ _s = g_alloca (30); \
+ \
+ g_snprintf ((char *) _s, 30, "(%lld)", (long long) _idx); \
+ } \
+ _s; \
})
/*****************************************************************************/
@@ -595,7 +1173,7 @@ nm_clear_g_cancellable (GCancellable **cancellable)
/* check if @flags has exactly one flag (@check) set. You should call this
* only with @check being a compile time constant and a power of two. */
#define NM_FLAGS_HAS(flags, check) \
- ( (G_STATIC_ASSERT_EXPR ( ((check) != 0) && ((check) & ((check)-1)) == 0 )), (NM_FLAGS_ANY ((flags),
(check))) )
+ ( G_STATIC_ASSERT_EXPR ((check) > 0 && ((check) & ((check) - 1)) == 0), NM_FLAGS_ANY ((flags), (check)) )
#define NM_FLAGS_ANY(flags, check) ( ( ((flags) & (check)) != 0 ) ? TRUE : FALSE )
#define NM_FLAGS_ALL(flags, check) ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE )
@@ -658,6 +1236,33 @@ nm_strstrip (char *str)
return str ? g_strstrip (str) : NULL;
}
+static inline const char *
+nm_strstrip_avoid_copy (const char *str, char **str_free)
+{
+ gsize l;
+ char *s;
+
+ nm_assert (str_free && !*str_free);
+
+ if (!str)
+ return NULL;
+
+ str = nm_str_skip_leading_spaces (str);
+ l = strlen (str);
+ if ( l == 0
+ || !g_ascii_isspace (str[l - 1]))
+ return str;
+ while ( l > 0
+ && g_ascii_isspace (str[l - 1]))
+ l--;
+
+ s = g_new (char, l + 1);
+ memcpy (s, str, l);
+ s[l] = '\0';
+ *str_free = s;
+ return s;
+}
+
/* g_ptr_array_sort()'s compare function takes pointers to the
* value. Thus, you cannot use strcmp directly. You can use
* nm_strcmp_p().
@@ -672,35 +1277,6 @@ nm_strcmp_p (gconstpointer a, gconstpointer b)
return strcmp (s1, s2);
}
-/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
- * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
- * signature. I guess, in glib there are knowledgeable people that ensure
- * that this additional argument doesn't cause problems due to different ABI
- * for every architecture that glib supports.
- * For NetworkManager, we'd rather avoid such stunts.
- **/
-static inline int
-nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
-{
- const char *s1 = *((const char **) a);
- const char *s2 = *((const char **) b);
-
- return strcmp (s1, s2);
-}
-
-static inline int
-nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
-{
- const guint32 a = *((const guint32 *) p_a);
- const guint32 b = *((const guint32 *) p_b);
-
- if (a < b)
- return -1;
- if (a > b)
- return 1;
- return 0;
-}
-
/*****************************************************************************/
/* Taken from systemd's UNIQ_T and UNIQ macros. */
@@ -748,13 +1324,15 @@ nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_d
/*****************************************************************************/
static inline guint
-nm_encode_version (guint major, guint minor, guint micro) {
+nm_encode_version (guint major, guint minor, guint micro)
+{
/* analog to the preprocessor macro NM_ENCODE_VERSION(). */
return (major << 16) | (minor << 8) | micro;
}
static inline void
-nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
+nm_decode_version (guint version, guint *major, guint *minor, guint *micro)
+{
*major = (version & 0xFFFF0000u) >> 16;
*minor = (version & 0x0000FF00u) >> 8;
*micro = (version & 0x000000FFu);
@@ -802,7 +1380,8 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
: "(null)"); \
})
-#define nm_sprintf_buf(buf, format, ...) ({ \
+#define nm_sprintf_buf(buf, format, ...) \
+ ({ \
char * _buf = (buf); \
int _buf_len; \
\
@@ -828,6 +1407,28 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
_buf; \
})
+/* aims to alloca() a buffer and fill it with printf(format, name).
+ * Note that format must not contain any format specifier except
+ * "%s".
+ * If the resulting string would be too large for stack allocation,
+ * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */
+#define nm_construct_name_a(format, name, p_val_to_free) \
+ ({ \
+ const char *const _name = (name); \
+ char **const _p_val_to_free = (p_val_to_free); \
+ const gsize _name_len = strlen (_name); \
+ char *_buf2; \
+ \
+ nm_assert (_p_val_to_free && !*_p_val_to_free); \
+ if (NM_STRLEN (format) + _name_len < 200) \
+ _buf2 = nm_sprintf_bufa (NM_STRLEN (format) + _name_len, format, _name); \
+ else { \
+ _buf2 = g_strdup_printf (format, _name); \
+ *_p_val_to_free = _buf2; \
+ } \
+ (const char *) _buf2; \
+ })
+
/*****************************************************************************/
/**
@@ -841,7 +1442,7 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
* Using _Bool has advantages over gboolean:
*
* - commonly _Bool is one byte large, instead of gboolean's 4 bytes (because gboolean
- * is a typedef for gint). Especially when having boolean fields in a struct, we can
+ * is a typedef for int). Especially when having boolean fields in a struct, we can
* thereby easily save some space.
*
* - _Bool type guarantees that two "true" expressions compare equal. E.g. the follwing
@@ -875,7 +1476,6 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
#define false 0
#endif
-
#ifdef _G_BOOLEAN_EXPR
/* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR().
* As glib's implementation uses a local variable _g_boolean_var_,
@@ -901,7 +1501,57 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) {
#define _G_BOOLEAN_EXPR(expr) __NM_G_BOOLEAN_EXPR_IMPL (NM_UNIQ, expr)
#endif
-
/*****************************************************************************/
+/**
+ * nm_steal_int:
+ * @p_val: pointer to an int type.
+ *
+ * Returns: *p_val and sets *p_val to zero the same time.
+ * Accepts %NULL, in which case also numeric 0 will be returned.
+ */
+#define nm_steal_int(p_val) \
+ ({ \
+ typeof (p_val) const _p_val = (p_val); \
+ typeof (*_p_val) _val = 0; \
+ \
+ if ( _p_val \
+ && (_val = *_p_val)) { \
+ *_p_val = 0; \
+ } \
+ _val; \
+ })
+
+static inline int
+nm_steal_fd (int *p_fd)
+{
+ int fd;
+
+ if ( p_fd
+ && ((fd = *p_fd) >= 0)) {
+ *p_fd = -1;
+ return fd;
+ }
+ return -1;
+}
+
+/**
+ * nm_close:
+ *
+ * Like close() but throws an assertion if the input fd is
+ * invalid. Closing an invalid fd is a programming error, so
+ * it's better to catch it early.
+ */
+static inline int
+nm_close (int fd)
+{
+ int r;
+
+ r = close (fd);
+ nm_assert (r != -1 || fd < 0 || errno != EBADF);
+ return r;
+}
+
+#define NM_PID_T_INVAL ((pid_t) -1)
+
#endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 413526d..022c065 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -24,6 +24,17 @@
#include "nm-shared-utils.h"
#include <errno.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <fcntl.h>
+
+/*****************************************************************************/
+
+const void *const _NM_PTRARRAY_EMPTY[1] = { NULL };
+
+/*****************************************************************************/
+
+const NMIPAddr nm_ip_addr_zero = { 0 };
/*****************************************************************************/
@@ -86,7 +97,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
{
char *p = *buf;
va_list args;
- gint retval;
+ int retval;
if (*len == 0)
return;
@@ -95,7 +106,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
retval = g_vsnprintf (p, *len, format, args);
va_end (args);
- if (retval >= *len) {
+ if ((gsize) retval >= *len) {
*buf = &p[*len];
*len = 0;
} else {
@@ -104,6 +115,461 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
}
}
+/**
+ * nm_utils_strbuf_seek_end:
+ * @buf: the input/output buffer
+ * @len: the input/output lenght of the buffer.
+ *
+ * Commonly, one uses nm_utils_strbuf_append*(), to incrementally
+ * append strings to the buffer. However, sometimes we need to use
+ * existing API to write to the buffer.
+ * After doing so, we want to adjust the buffer counter.
+ * Essentially,
+ *
+ * g_snprintf (buf, len, ...);
+ * nm_utils_strbuf_seek_end (&buf, &len);
+ *
+ * is almost the same as
+ *
+ * nm_utils_strbuf_append (&buf, &len, ...);
+ *
+ * The only difference is the behavior when the string got truncated:
+ * nm_utils_strbuf_append() will recognize that and set the remaining
+ * length to zero.
+ *
+ * In general, the behavior is:
+ *
+ * - if *len is zero, do nothing
+ * - if the buffer contains a NUL byte within the first *len characters,
+ * the buffer is pointed to the NUL byte and len is adjusted. In this
+ * case, the remaining *len is always >= 1.
+ * In particular, that is also the case if the NUL byte is at the very last
+ * position ((*buf)[*len -1]). That happens, when the previous operation
+ * either fit the string exactly into the buffer or the string was truncated
+ * by g_snprintf(). The difference cannot be determined.
+ * - if the buffer contains no NUL bytes within the first *len characters,
+ * write NUL at the last position, set *len to zero, and point *buf past
+ * the NUL byte. This would happen with
+ *
+ * strncpy (buf, long_str, len);
+ * nm_utils_strbuf_seek_end (&buf, &len).
+ *
+ * where strncpy() does truncate the string and not NUL terminate it.
+ * nm_utils_strbuf_seek_end() would then NUL terminate it.
+ */
+void
+nm_utils_strbuf_seek_end (char **buf, gsize *len)
+{
+ gsize l;
+ char *end;
+
+ nm_assert (len);
+ nm_assert (buf && *buf);
+
+ if (*len <= 1) {
+ if ( *len == 1
+ && (*buf)[0])
+ goto truncate;
+ return;
+ }
+
+ end = memchr (*buf, 0, *len);
+ if (end) {
+ l = end - *buf;
+ nm_assert (l < *len);
+
+ *buf = end;
+ *len -= l;
+ return;
+ }
+
+truncate:
+ /* hm, no NUL character within len bytes.
+ * Just NUL terminate the array and consume them
+ * all. */
+ *buf += *len;
+ (*buf)[-1] = '\0';
+ *len = 0;
+ return;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_gbytes_equals:
+ * @bytes: (allow-none): a #GBytes array to compare. Note that
+ * %NULL is treated like an #GBytes array of length zero.
+ * @mem_data: the data pointer with @mem_len bytes
+ * @mem_len: the length of the data pointer
+ *
+ * Returns: %TRUE if @bytes contains the same data as @mem_data. As a
+ * special case, a %NULL @bytes is treated like an empty array.
+ */
+gboolean
+nm_utils_gbytes_equal_mem (GBytes *bytes,
+ gconstpointer mem_data,
+ gsize mem_len)
+{
+ gconstpointer p;
+ gsize l;
+
+ if (!bytes) {
+ /* as a special case, let %NULL GBytes compare idential
+ * to an empty array. */
+ return (mem_len == 0);
+ }
+
+ p = g_bytes_get_data (bytes, &l);
+ return l == mem_len
+ && ( mem_len == 0 /* allow @mem_data to be %NULL */
+ || memcmp (p, mem_data, mem_len) == 0);
+}
+
+GVariant *
+nm_utils_gbytes_to_variant_ay (GBytes *bytes)
+{
+ const guint8 *p;
+ gsize l;
+
+ if (!bytes) {
+ /* for convenience, accept NULL to return an empty variant */
+ return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0);
+ }
+
+ p = g_bytes_get_data (bytes, &l);
+ return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1);
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_strquote:
+ * @buf: the output buffer of where to write the quoted @str argument.
+ * @buf_len: the size of @buf.
+ * @str: (allow-none): the string to quote.
+ *
+ * Writes @str to @buf with quoting. The resulting buffer
+ * is always NUL terminated, unless @buf_len is zero.
+ * If @str is %NULL, it writes "(null)".
+ *
+ * If @str needs to be truncated, the closing quote is '^' instead
+ * of '"'.
+ *
+ * This is similar to nm_strquote_a(), which however uses alloca()
+ * to allocate a new buffer. Also, here @buf_len is the size of @buf,
+ * while nm_strquote_a() has the number of characters to print. The latter
+ * doesn't include the quoting.
+ *
+ * Returns: the input buffer with the quoted string.
+ */
+const char *
+nm_strquote (char *buf, gsize buf_len, const char *str)
+{
+ const char *const buf0 = buf;
+
+ if (!str) {
+ nm_utils_strbuf_append_str (&buf, &buf_len, "(null)");
+ goto out;
+ }
+
+ if (G_UNLIKELY (buf_len <= 2)) {
+ switch (buf_len) {
+ case 2:
+ *(buf++) = '^';
+ /* fall-through */
+ case 1:
+ *(buf++) = '\0';
+ break;
+ }
+ goto out;
+ }
+
+ *(buf++) = '"';
+ buf_len--;
+
+ nm_utils_strbuf_append_str (&buf, &buf_len, str);
+
+ /* if the string was too long we indicate truncation with a
+ * '^' instead of a closing quote. */
+ if (G_UNLIKELY (buf_len <= 1)) {
+ switch (buf_len) {
+ case 1:
+ buf[-1] = '^';
+ break;
+ case 0:
+ buf[-2] = '^';
+ break;
+ default:
+ nm_assert_not_reached ();
+ break;
+ }
+ } else {
+ nm_assert (buf_len >= 2);
+ *(buf++) = '"';
+ *(buf++) = '\0';
+ }
+
+out:
+ return buf0;
+}
+
+/*****************************************************************************/
+
+char _nm_utils_to_string_buffer[];
+
+void
+nm_utils_to_string_buffer_init (char **buf, gsize *len)
+{
+ if (!*buf) {
+ *buf = _nm_utils_to_string_buffer;
+ *len = sizeof (_nm_utils_to_string_buffer);
+ }
+}
+
+gboolean
+nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len)
+{
+ nm_utils_to_string_buffer_init (buf, len);
+ if (!obj) {
+ g_strlcpy (*buf, "(null)", *len);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
+ gsize n_descs,
+ unsigned flags,
+ char *buf,
+ gsize len)
+{
+ gsize i;
+ char *p;
+
+#if NM_MORE_ASSERTS > 10
+ nm_assert (descs);
+ nm_assert (n_descs > 0);
+ for (i = 0; i < n_descs; i++) {
+ gsize j;
+
+ nm_assert (descs[i].name && descs[i].name[0]);
+ for (j = 0; j < i; j++)
+ nm_assert (descs[j].flag != descs[i].flag);
+ }
+#endif
+
+ nm_utils_to_string_buffer_init (&buf, &len);
+
+ if (!len)
+ return buf;
+
+ buf[0] = '\0';
+ p = buf;
+ if (!flags) {
+ for (i = 0; i < n_descs; i++) {
+ if (!descs[i].flag) {
+ nm_utils_strbuf_append_str (&p, &len, descs[i].name);
+ break;
+ }
+ }
+ return buf;
+ }
+
+ for (i = 0; flags && i < n_descs; i++) {
+ if ( descs[i].flag
+ && NM_FLAGS_ALL (flags, descs[i].flag)) {
+ flags &= ~descs[i].flag;
+
+ if (buf[0] != '\0')
+ nm_utils_strbuf_append_c (&p, &len, ',');
+ nm_utils_strbuf_append_str (&p, &len, descs[i].name);
+ }
+ }
+ if (flags) {
+ if (buf[0] != '\0')
+ nm_utils_strbuf_append_c (&p, &len, ',');
+ nm_utils_strbuf_append (&p, &len, "0x%x", flags);
+ }
+ return buf;
+};
+
+/*****************************************************************************/
+
+/**
+ * _nm_utils_ip4_prefix_to_netmask:
+ * @prefix: a CIDR prefix
+ *
+ * Returns: the netmask represented by the prefix, in network byte order
+ **/
+guint32
+_nm_utils_ip4_prefix_to_netmask (guint32 prefix)
+{
+ return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
+}
+
+/**
+ * _nm_utils_ip4_get_default_prefix:
+ * @ip: an IPv4 address (in network byte order)
+ *
+ * When the Internet was originally set up, various ranges of IP addresses were
+ * segmented into three network classes: A, B, and C. This function will return
+ * a prefix that is associated with the IP address specified defining where it
+ * falls in the predefined classes.
+ *
+ * Returns: the default class prefix for the given IP
+ **/
+/* The function is originally from ipcalc.c of Red Hat's initscripts. */
+guint32
+_nm_utils_ip4_get_default_prefix (guint32 ip)
+{
+ if (((ntohl (ip) & 0xFF000000) >> 24) <= 127)
+ return 8; /* Class A - 255.0.0.0 */
+ else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191)
+ return 16; /* Class B - 255.255.0.0 */
+
+ return 24; /* Class C - 255.255.255.0 */
+}
+
+gboolean
+nm_utils_ip_is_site_local (int addr_family,
+ const void *address)
+{
+ in_addr_t addr4;
+
+ switch (addr_family) {
+ case AF_INET:
+ /* RFC1918 private addresses
+ * 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */
+ addr4 = ntohl (*((const in_addr_t *) address));
+ return (addr4 & 0xff000000) == 0x0a000000
+ || (addr4 & 0xfff00000) == 0xac100000
+ || (addr4 & 0xffff0000) == 0xc0a80000;
+ case AF_INET6:
+ return IN6_IS_ADDR_SITELOCAL (address);
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+}
+
+/*****************************************************************************/
+
+gboolean
+nm_utils_parse_inaddr_bin (int addr_family,
+ const char *text,
+ gpointer out_addr)
+{
+ NMIPAddr addrbin;
+
+ g_return_val_if_fail (text, FALSE);
+
+ if (addr_family == AF_UNSPEC)
+ addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
+ else
+ g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
+
+ /* use a temporary variable @addrbin, to guarantee that @out_addr
+ * is only modified on success. */
+ if (inet_pton (addr_family, text, &addrbin) != 1)
+ return FALSE;
+
+ if (out_addr) {
+ switch (addr_family) {
+ case AF_INET:
+ *((in_addr_t *) out_addr) = addrbin.addr4;
+ break;
+ case AF_INET6:
+ *((struct in6_addr *) out_addr) = addrbin.addr6;
+ break;
+ default:
+ nm_assert_not_reached ();
+ }
+ }
+ return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr (int addr_family,
+ const char *text,
+ char **out_addr)
+{
+ NMIPAddr addrbin;
+ char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+
+ nm_assert (!out_addr || !*out_addr);
+
+ if (!nm_utils_parse_inaddr_bin (addr_family, text, &addrbin))
+ return FALSE;
+ NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof
(addrstr_buf))));
+ return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr_prefix_bin (int addr_family,
+ const char *text,
+ gpointer out_addr,
+ int *out_prefix)
+{
+ gs_free char *addrstr_free = NULL;
+ int prefix = -1;
+ const char *slash;
+ const char *addrstr;
+ NMIPAddr addrbin;
+ int addr_len;
+
+ g_return_val_if_fail (text, FALSE);
+
+ if (addr_family == AF_UNSPEC)
+ addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
+
+ if (addr_family == AF_INET)
+ addr_len = sizeof (in_addr_t);
+ else if (addr_family == AF_INET6)
+ addr_len = sizeof (struct in6_addr);
+ else
+ g_return_val_if_reached (FALSE);
+
+ slash = strchr (text, '/');
+ if (slash)
+ addrstr = addrstr_free = g_strndup (text, slash - text);
+ else
+ addrstr = text;
+
+ if (inet_pton (addr_family, addrstr, &addrbin) != 1)
+ return FALSE;
+
+ if (slash) {
+ prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10,
+ 0,
+ addr_family == AF_INET ? 32 : 128,
+ -1);
+ if (prefix == -1)
+ return FALSE;
+ }
+
+ if (out_addr)
+ memcpy (out_addr, &addrbin, addr_len);
+ NM_SET_OUT (out_prefix, prefix);
+ return TRUE;
+}
+
+gboolean
+nm_utils_parse_inaddr_prefix (int addr_family,
+ const char *text,
+ char **out_addr,
+ int *out_prefix)
+{
+ NMIPAddr addrbin;
+ char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
+
+ if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addrbin, out_prefix))
+ return FALSE;
+ NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof
(addrstr_buf))));
+ return TRUE;
+}
+
/*****************************************************************************/
/* _nm_utils_ascii_str_to_int64:
@@ -158,6 +624,286 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma
/*****************************************************************************/
+/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data().
+ * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different
+ * signature. I guess, in glib there are knowledgeable people that ensure
+ * that this additional argument doesn't cause problems due to different ABI
+ * for every architecture that glib supports.
+ * For NetworkManager, we'd rather avoid such stunts.
+ **/
+int
+nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ const char *s1 = *((const char **) a);
+ const char *s2 = *((const char **) b);
+
+ return strcmp (s1, s2);
+}
+
+int
+nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+ const guint32 a = *((const guint32 *) p_a);
+ const guint32 b = *((const guint32 *) p_b);
+
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+}
+
+int
+nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+ /* p_a and p_b are two pointers to a pointer, where the pointer is
+ * interpreted as a integer using GPOINTER_TO_INT().
+ *
+ * That is the case of a hash-table that uses GINT_TO_POINTER() to
+ * convert integers as pointers, and the resulting keys-as-array
+ * array. */
+ const int a = GPOINTER_TO_INT (*((gconstpointer *) p_a));
+ const int b = GPOINTER_TO_INT (*((gconstpointer *) p_b));
+
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ return 0;
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_dbus_path_get_last_component (const char *dbus_path)
+{
+ if (dbus_path) {
+ dbus_path = strrchr (dbus_path, '/');
+ if (dbus_path)
+ return dbus_path + 1;
+ }
+ return NULL;
+}
+
+static gint64
+_dbus_path_component_as_num (const char *p)
+{
+ gint64 n;
+
+ /* no odd stuff. No leading zeros, only a non-negative, decimal integer.
+ *
+ * Otherwise, there would be multiple ways to encode the same number "10"
+ * and "010". That is just confusing. A number has no leading zeros,
+ * if it has, it's not a number (as far as we are concerned here). */
+ if (p[0] == '0') {
+ if (p[1] != '\0')
+ return -1;
+ else
+ return 0;
+ }
+ if (!(p[0] >= '1' && p[0] <= '9'))
+ return -1;
+ if (!NM_STRCHAR_ALL (&p[1], ch, (ch >= '0' && ch <= '9')))
+ return -1;
+ n = _nm_utils_ascii_str_to_int64 (p, 10, 0, G_MAXINT64, -1);
+ nm_assert (n == -1 || nm_streq0 (p, nm_sprintf_bufa (100, "%"G_GINT64_FORMAT, n)));
+ return n;
+}
+
+int
+nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b)
+{
+ const char *l_a, *l_b;
+ gsize plen;
+ gint64 n_a, n_b;
+
+ /* compare function for two D-Bus paths. It behaves like
+ * strcmp(), except, if both paths have the same prefix,
+ * and both end in a (positive) number, then the paths
+ * will be sorted by number. */
+
+ NM_CMP_SELF (dbus_path_a, dbus_path_b);
+
+ /* if one or both paths have no slash (and no last component)
+ * compare the full paths directly. */
+ if ( !(l_a = nm_utils_dbus_path_get_last_component (dbus_path_a))
+ || !(l_b = nm_utils_dbus_path_get_last_component (dbus_path_b)))
+ goto comp_full;
+
+ /* check if both paths have the same prefix (up to the last-component). */
+ plen = l_a - dbus_path_a;
+ if (plen != (l_b - dbus_path_b))
+ goto comp_full;
+ NM_CMP_RETURN (strncmp (dbus_path_a, dbus_path_b, plen));
+
+ n_a = _dbus_path_component_as_num (l_a);
+ n_b = _dbus_path_component_as_num (l_b);
+ if (n_a == -1 && n_b == -1)
+ goto comp_l;
+
+ /* both components must be convertiable to a number. If they are not,
+ * (and only one of them is), then we must always strictly sort numeric parts
+ * after non-numeric components. If we wouldn't, we wouldn't have
+ * a total order.
+ *
+ * An example of a not total ordering would be:
+ * "8" < "010" (numeric)
+ * "0x" < "8" (lexical)
+ * "0x" > "010" (lexical)
+ * We avoid this, by forcing that a non-numeric entry "0x" always sorts
+ * before numeric entries.
+ *
+ * Additionally, _dbus_path_component_as_num() would also reject "010" as
+ * not a valid number.
+ */
+ if (n_a == -1)
+ return -1;
+ if (n_b == -1)
+ return 1;
+
+ NM_CMP_DIRECT (n_a, n_b);
+ nm_assert (nm_streq (dbus_path_a, dbus_path_b));
+ return 0;
+
+comp_full:
+ NM_CMP_DIRECT_STRCMP0 (dbus_path_a, dbus_path_b);
+ return 0;
+comp_l:
+ NM_CMP_DIRECT_STRCMP0 (l_a, l_b);
+ nm_assert (nm_streq (dbus_path_a, dbus_path_b));
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_strsplit_set:
+ * @str: the string to split.
+ * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n",
+ * like bash's $IFS.
+ * @allow_escaping: whether delimiters can be escaped by a backslash
+ *
+ * This is a replacement for g_strsplit_set() which avoids copying
+ * each word once (the entire strv array), but instead copies it once
+ * and all words point into that internal copy.
+ *
+ * Another difference from g_strsplit_set() is that this never returns
+ * empty words. Multiple delimiters are combined and treated as one.
+ *
+ * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are
+ * not treated as a separator. Such delimiters and their escape
+ * character are copied to the current word without unescaping them.
+ *
+ * Returns: %NULL if @str is %NULL or contains only delimiters.
+ * Otherwise, a %NULL terminated strv array containing non-empty
+ * words, split at the delimiter characters (delimiter characters
+ * are removed).
+ * The strings to which the result strv array points to are allocated
+ * after the returned result itself. Don't free the strings themself,
+ * but free everything with g_free().
+ */
+const char **
+nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping)
+{
+ const char **ptr, **ptr0;
+ gsize alloc_size, plen, i;
+ gsize str_len;
+ char *s0;
+ char *s;
+ guint8 delimiters_table[256];
+ gboolean escaped = FALSE;
+
+ if (!str)
+ return NULL;
+
+ /* initialize lookup table for delimiter */
+ if (!delimiters)
+ delimiters = " \t\n";
+ memset (delimiters_table, 0, sizeof (delimiters_table));
+ for (i = 0; delimiters[i]; i++)
+ delimiters_table[(guint8) delimiters[i]] = 1;
+
+#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \
+ ((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc))
+
+#define next_char(p, esc) \
+ G_STMT_START { \
+ if (esc) \
+ esc = FALSE; \
+ else \
+ esc = p[0] == '\\'; \
+ p++; \
+ } G_STMT_END
+
+ /* skip initial delimiters, and return of the remaining string is
+ * empty. */
+ while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped))
+ next_char (str, escaped);
+
+ if (!str[0])
+ return NULL;
+
+ str_len = strlen (str) + 1;
+ alloc_size = 8;
+
+ /* we allocate the buffer larger, so to copy @str at the
+ * end of it as @s0. */
+ ptr0 = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
+ s0 = (char *) &ptr0[alloc_size + 1];
+ memcpy (s0, str, str_len);
+
+ plen = 0;
+ s = s0;
+ ptr = ptr0;
+
+ while (TRUE) {
+ if (plen >= alloc_size) {
+ const char **ptr_old = ptr;
+
+ /* reallocate the buffer. Note that for now the string
+ * continues to be in ptr0/s0. We fix that at the end. */
+ alloc_size *= 2;
+ ptr = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
+ memcpy (ptr, ptr_old, sizeof (const char *) * plen);
+ if (ptr_old != ptr0)
+ g_free (ptr_old);
+ }
+
+ ptr[plen++] = s;
+
+ nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped));
+
+ while (TRUE) {
+ next_char (s, escaped);
+ if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped))
+ break;
+ if (s[0] == '\0')
+ goto done;
+ }
+
+ s[0] = '\0';
+ next_char (s, escaped);
+ while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped))
+ next_char (s, escaped);
+ if (s[0] == '\0')
+ break;
+ }
+done:
+ ptr[plen] = NULL;
+
+ if (ptr != ptr0) {
+ /* we reallocated the buffer. We must copy over the
+ * string @s0 and adjust the pointers. */
+ s = (char *) &ptr[alloc_size + 1];
+ memcpy (s, s0, str_len);
+ for (i = 0; i < plen; i++)
+ ptr[i] = &s[ptr[i] - s0];
+ g_free (ptr0);
+ }
+
+ return ptr;
+}
+
/**
* nm_utils_strv_find_first:
* @list: the strv list to search
@@ -204,11 +950,40 @@ nm_utils_strv_find_first (char **list, gssize len, const char *needle)
return -1;
}
+char **
+_nm_utils_strv_cleanup (char **strv,
+ gboolean strip_whitespace,
+ gboolean skip_empty,
+ gboolean skip_repeated)
+{
+ guint i, j;
+
+ if (!strv || !*strv)
+ return strv;
+
+ if (strip_whitespace) {
+ for (i = 0; strv[i]; i++)
+ g_strstrip (strv[i]);
+ }
+ if (!skip_empty && !skip_repeated)
+ return strv;
+ j = 0;
+ for (i = 0; strv[i]; i++) {
+ if ( (skip_empty && !*strv[i])
+ || (skip_repeated && nm_utils_strv_find_first (strv, j, strv[i]) >= 0))
+ g_free (strv[i]);
+ else
+ strv[j++] = strv[i];
+ }
+ strv[j] = NULL;
+ return strv;
+}
+
/*****************************************************************************/
-gint
+int
_nm_utils_ascii_str_to_bool (const char *str,
- gint default_value)
+ int default_value)
{
gsize len;
char *s = NULL;
@@ -290,7 +1065,7 @@ nm_utils_error_is_cancelled (GError *error,
*/
gboolean
nm_g_object_set_property (GObject *object,
- const gchar *property_name,
+ const char *property_name,
const GValue *value,
GError **error)
{
@@ -363,4 +1138,934 @@ nm_g_object_set_property (GObject *object,
return TRUE;
}
-/*****************************************************************************/
+gboolean
+nm_g_object_set_property_boolean (GObject *object,
+ const char *property_name,
+ gboolean value,
+ GError **error)
+{
+ nm_auto_unset_gvalue GValue gvalue = { 0 };
+
+ g_value_init (&gvalue, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&gvalue, !!value);
+ return nm_g_object_set_property (object, property_name, &gvalue, error);
+}
+
+gboolean
+nm_g_object_set_property_uint (GObject *object,
+ const char *property_name,
+ guint value,
+ GError **error)
+{
+ nm_auto_unset_gvalue GValue gvalue = { 0 };
+
+ g_value_init (&gvalue, G_TYPE_UINT);
+ g_value_set_uint (&gvalue, value);
+ return nm_g_object_set_property (object, property_name, &gvalue, error);
+}
+
+GParamSpec *
+nm_g_object_class_find_property_from_gtype (GType gtype,
+ const char *property_name)
+{
+ nm_auto_unref_gtypeclass GObjectClass *gclass = NULL;
+
+ gclass = g_type_class_ref (gtype);
+ return g_object_class_find_property (gclass, property_name);
+}
+
+/*****************************************************************************/
+
+static void
+_str_append_escape (GString *s, char ch)
+{
+ g_string_append_c (s, '\\');
+ g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07));
+ g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07));
+ g_string_append_c (s, '0' + ( ((guchar) ch) & 07));
+}
+
+gconstpointer
+nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free)
+{
+ GString *gstr;
+ gsize len;
+ const char *s;
+
+ g_return_val_if_fail (to_free, NULL);
+ g_return_val_if_fail (out_len, NULL);
+
+ if (!str) {
+ *out_len = 0;
+ *to_free = NULL;
+ return NULL;
+ }
+
+ len = strlen (str);
+
+ s = memchr (str, '\\', len);
+ if (!s) {
+ *out_len = len;
+ *to_free = NULL;
+ return str;
+ }
+
+ gstr = g_string_new_len (NULL, len);
+
+ g_string_append_len (gstr, str, s - str);
+ str = s;
+
+ for (;;) {
+ char ch;
+ guint v;
+
+ nm_assert (str[0] == '\\');
+
+ ch = (++str)[0];
+
+ if (ch == '\0') {
+ // error. Trailing '\\'
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ v = ch - '0';
+ ch = (++str)[0];
+ if (ch >= '0' && ch <= '7') {
+ v = v * 8 + (ch - '0');
+ ch = (++str)[0];
+ if (ch >= '0' && ch <= '7') {
+ v = v * 8 + (ch - '0');
+ ch = (++str)[0];
+ }
+ }
+ ch = v;
+ } else {
+ switch (ch) {
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'v': ch = '\v'; break;
+ default:
+ /* Here we handle "\\\\", but all other unexpected escape sequences are
really a bug.
+ * Take them literally, after removing the escape character */
+ break;
+ }
+ str++;
+ }
+
+ g_string_append_c (gstr, ch);
+
+ s = strchr (str, '\\');
+ if (!s) {
+ g_string_append (gstr, str);
+ break;
+ }
+
+ g_string_append_len (gstr, str, s - str);
+ str = s;
+ }
+
+ *out_len = gstr->len;
+ *to_free = gstr->str;
+ return g_string_free (gstr, FALSE);
+}
+
+/**
+ * nm_utils_buf_utf8safe_escape:
+ * @buf: byte array, possibly in utf-8 encoding, may have NUL characters.
+ * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated
+ * string.
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ * @to_free: (out): return the pointer location of the string
+ * if a copying was necessary.
+ *
+ * Based on the assumption, that @buf contains UTF-8 encoded bytes,
+ * this will return valid UTF-8 sequence, and invalid sequences
+ * will be escaped with backslash (C escaping, like g_strescape()).
+ * This is sanitize non UTF-8 characters. The result is valid
+ * UTF-8.
+ *
+ * The operation can be reverted with nm_utils_buf_utf8safe_unescape().
+ * Note that if, and only if @buf contains no NUL bytes, the operation
+ * can also be reverted with g_strcompress().
+ *
+ * Depending on @flags, valid UTF-8 characters are not escaped at all
+ * (except the escape character '\\'). This is the difference to g_strescape(),
+ * which escapes all non-ASCII characters. This allows to pass on
+ * valid UTF-8 characters as-is and can be directly shown to the user
+ * as UTF-8 -- with exception of the backslash escape character,
+ * invalid UTF-8 sequences, and other (depending on @flags).
+ *
+ * Returns: the escaped input buffer, as valid UTF-8. If no escaping
+ * is necessary, it returns the input @buf. Otherwise, an allocated
+ * string @to_free is returned which must be freed by the caller
+ * with g_free. The escaping can be reverted by g_strcompress().
+ **/
+const char *
+nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char
**to_free)
+{
+ const char *const str = buf;
+ const char *p = NULL;
+ const char *s;
+ gboolean nul_terminated = FALSE;
+ GString *gstr;
+
+ g_return_val_if_fail (to_free, NULL);
+
+ *to_free = NULL;
+
+ if (buflen == 0)
+ return NULL;
+
+ if (buflen < 0) {
+ if (!str)
+ return NULL;
+ buflen = strlen (str);
+ if (buflen == 0)
+ return str;
+ nul_terminated = TRUE;
+ }
+
+ if ( g_utf8_validate (str, buflen, &p)
+ && nul_terminated) {
+ /* note that g_utf8_validate() does not allow NUL character inside @str. Good.
+ * We can treat @str like a NUL terminated string. */
+ if (!NM_STRCHAR_ANY (str, ch,
+ ( ch == '\\' \
+ || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL)
\
+ && ch < ' ') \
+ || ( NM_FLAGS_HAS (flags,
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
+ && ((guchar) ch) >= 127))))
+ return str;
+ }
+
+ gstr = g_string_sized_new (buflen + 5);
+
+ s = str;
+ do {
+ buflen -= p - s;
+ nm_assert (buflen >= 0);
+
+ for (; s < p; s++) {
+ char ch = s[0];
+
+ if (ch == '\\')
+ g_string_append (gstr, "\\\\");
+ else if ( ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \
+ && ch < ' ') \
+ || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \
+ && ((guchar) ch) >= 127))
+ _str_append_escape (gstr, ch);
+ else
+ g_string_append_c (gstr, ch);
+ }
+
+ if (buflen <= 0)
+ break;
+
+ _str_append_escape (gstr, p[0]);
+
+ buflen--;
+ if (buflen == 0)
+ break;
+
+ s = &p[1];
+ g_utf8_validate (s, buflen, &p);
+ } while (TRUE);
+
+ *to_free = g_string_free (gstr, FALSE);
+ return *to_free;
+}
+
+const char *
+nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free)
+{
+ gconstpointer p;
+ gsize l;
+
+ if (bytes)
+ p = g_bytes_get_data (bytes, &l);
+ else {
+ p = NULL;
+ l = 0;
+ }
+
+ return nm_utils_buf_utf8safe_escape (p, l, flags, to_free);
+}
+
+/*****************************************************************************/
+
+const char *
+nm_utils_str_utf8safe_unescape (const char *str, char **to_free)
+{
+ g_return_val_if_fail (to_free, NULL);
+
+ if (!str || !strchr (str, '\\')) {
+ *to_free = NULL;
+ return str;
+ }
+ return (*to_free = g_strcompress (str));
+}
+
+/**
+ * nm_utils_str_utf8safe_escape:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ * @to_free: (out): return the pointer location of the string
+ * if a copying was necessary.
+ *
+ * Returns the possible non-UTF-8 NUL terminated string @str
+ * and uses backslash escaping (C escaping, like g_strescape())
+ * to sanitize non UTF-8 characters. The result is valid
+ * UTF-8.
+ *
+ * The operation can be reverted with g_strcompress() or
+ * nm_utils_str_utf8safe_unescape().
+ *
+ * Depending on @flags, valid UTF-8 characters are not escaped at all
+ * (except the escape character '\\'). This is the difference to g_strescape(),
+ * which escapes all non-ASCII characters. This allows to pass on
+ * valid UTF-8 characters as-is and can be directly shown to the user
+ * as UTF-8 -- with exception of the backslash escape character,
+ * invalid UTF-8 sequences, and other (depending on @flags).
+ *
+ * Returns: the escaped input string, as valid UTF-8. If no escaping
+ * is necessary, it returns the input @str. Otherwise, an allocated
+ * string @to_free is returned which must be freed by the caller
+ * with g_free. The escaping can be reverted by g_strcompress().
+ **/
+const char *
+nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free)
+{
+ return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free);
+}
+
+/**
+ * nm_utils_str_utf8safe_escape_cp:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ *
+ * Like nm_utils_str_utf8safe_escape(), except the returned value
+ * is always a copy of the input and must be freed by the caller.
+ *
+ * Returns: the escaped input string in UTF-8 encoding. The returned
+ * value should be freed with g_free().
+ * The escaping can be reverted by g_strcompress().
+ **/
+char *
+nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+ char *s;
+
+ nm_utils_str_utf8safe_escape (str, flags, &s);
+ return s ?: g_strdup (str);
+}
+
+char *
+nm_utils_str_utf8safe_unescape_cp (const char *str)
+{
+ return str ? g_strcompress (str) : NULL;
+}
+
+char *
+nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+ char *str_to_free;
+
+ nm_utils_str_utf8safe_escape (str, flags, &str_to_free);
+ if (str_to_free) {
+ g_free (str);
+ return str_to_free;
+ }
+ return str;
+}
+
+/*****************************************************************************/
+
+/* taken from systemd's fd_wait_for_event(). Note that the timeout
+ * is here in nano-seconds, not micro-seconds. */
+int
+nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns)
+{
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = event,
+ };
+ struct timespec ts, *pts;
+ int r;
+
+ if (timeout_ns < 0)
+ pts = NULL;
+ else {
+ ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND);
+ ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND);
+ pts = &ts;
+ }
+
+ r = ppoll (&pollfd, 1, pts, NULL);
+ if (r < 0)
+ return -errno;
+ if (r == 0)
+ return 0;
+ return pollfd.revents;
+}
+
+/* taken from systemd's loop_read() */
+ssize_t
+nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+ uint8_t *p = buf;
+ ssize_t n = 0;
+
+ g_return_val_if_fail (fd >= 0, -EINVAL);
+ g_return_val_if_fail (buf, -EINVAL);
+
+ /* If called with nbytes == 0, let's call read() at least
+ * once, to validate the operation */
+
+ if (nbytes > (size_t) SSIZE_MAX)
+ return -EINVAL;
+
+ do {
+ ssize_t k;
+
+ k = read (fd, p, nbytes);
+ if (k < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (errno == EAGAIN && do_poll) {
+
+ /* We knowingly ignore any return value here,
+ * and expect that any error/EOF is reported
+ * via read() */
+
+ (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1);
+ continue;
+ }
+
+ return n > 0 ? n : -errno;
+ }
+
+ if (k == 0)
+ return n;
+
+ g_assert ((size_t) k <= nbytes);
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ } while (nbytes > 0);
+
+ return n;
+}
+
+/* taken from systemd's loop_read_exact() */
+int
+nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
+{
+ ssize_t n;
+
+ n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll);
+ if (n < 0)
+ return (int) n;
+ if ((size_t) n != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+NMUtilsNamedValue *
+nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
+{
+ GHashTableIter iter;
+ NMUtilsNamedValue *values;
+ guint i, len;
+
+ if ( !hash
+ || !(len = g_hash_table_size (hash))) {
+ NM_SET_OUT (out_len, 0);
+ return NULL;
+ }
+
+ i = 0;
+ values = g_new (NMUtilsNamedValue, len + 1);
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter,
+ (gpointer *) &values[i].name,
+ (gpointer *) &values[i].value_ptr))
+ i++;
+ nm_assert (i == len);
+ values[i].name = NULL;
+ values[i].value_ptr = NULL;
+
+ if (len > 1) {
+ g_qsort_with_data (values, len, sizeof (values[0]),
+ nm_utils_named_entry_cmp_with_data, NULL);
+ }
+
+ NM_SET_OUT (out_len, len);
+ return values;
+}
+
+gpointer *
+nm_utils_hash_keys_to_array (GHashTable *hash,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ guint *out_len)
+{
+ guint len;
+ gpointer *keys;
+
+ /* by convention, we never return an empty array. In that
+ * case, always %NULL. */
+ if ( !hash
+ || g_hash_table_size (hash) == 0) {
+ NM_SET_OUT (out_len, 0);
+ return NULL;
+ }
+
+ keys = g_hash_table_get_keys_as_array (hash, &len);
+ if ( len > 1
+ && compare_func) {
+ g_qsort_with_data (keys,
+ len,
+ sizeof (gpointer),
+ compare_func,
+ user_data);
+ }
+ NM_SET_OUT (out_len, len);
+ return keys;
+}
+
+char **
+nm_utils_strv_make_deep_copied (const char **strv)
+{
+ gsize i;
+
+ /* it takes a strv dictionary, and copies each
+ * strings. Note that this updates @strv *in-place*
+ * and returns it. */
+
+ if (!strv)
+ return NULL;
+ for (i = 0; strv[i]; i++)
+ strv[i] = g_strdup (strv[i]);
+
+ return (char **) strv;
+}
+
+/*****************************************************************************/
+
+gssize
+nm_utils_ptrarray_find_binary_search (gconstpointer *list,
+ gsize len,
+ gconstpointer needle,
+ GCompareDataFunc cmpfcn,
+ gpointer user_data,
+ gssize *out_idx_first,
+ gssize *out_idx_last)
+{
+ gssize imin, imax, imid, i2min, i2max, i2mid;
+ int cmp;
+
+ g_return_val_if_fail (list || !len, ~((gssize) 0));
+ g_return_val_if_fail (cmpfcn, ~((gssize) 0));
+
+ imin = 0;
+ if (len > 0) {
+ imax = len - 1;
+
+ while (imin <= imax) {
+ imid = imin + (imax - imin) / 2;
+
+ cmp = cmpfcn (list[imid], needle, user_data);
+ if (cmp == 0) {
+ /* we found a matching entry at index imid.
+ *
+ * Does the caller request the first/last index as well (in case that
+ * there are multiple entries which compare equal). */
+
+ if (out_idx_first) {
+ i2min = imin;
+ i2max = imid + 1;
+ while (i2min <= i2max) {
+ i2mid = i2min + (i2max - i2min) / 2;
+
+ cmp = cmpfcn (list[i2mid], needle, user_data);
+ if (cmp == 0)
+ i2max = i2mid -1;
+ else {
+ nm_assert (cmp < 0);
+ i2min = i2mid + 1;
+ }
+ }
+ *out_idx_first = i2min;
+ }
+ if (out_idx_last) {
+ i2min = imid + 1;
+ i2max = imax;
+ while (i2min <= i2max) {
+ i2mid = i2min + (i2max - i2min) / 2;
+
+ cmp = cmpfcn (list[i2mid], needle, user_data);
+ if (cmp == 0)
+ i2min = i2mid + 1;
+ else {
+ nm_assert (cmp > 0);
+ i2max = i2mid - 1;
+ }
+ }
+ *out_idx_last = i2min - 1;
+ }
+ return imid;
+ }
+
+ if (cmp < 0)
+ imin = imid + 1;
+ else
+ imax = imid - 1;
+ }
+ }
+
+ /* return the inverse of @imin. This is a negative number, but
+ * also is ~imin the position where the value should be inserted. */
+ imin = ~imin;
+ NM_SET_OUT (out_idx_first, imin);
+ NM_SET_OUT (out_idx_last, imin);
+ return imin;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_array_find_binary_search:
+ * @list: the list to search. It must be sorted according to @cmpfcn ordering.
+ * @elem_size: the size in bytes of each element in the list
+ * @len: the number of elements in @list
+ * @needle: the value that is searched
+ * @cmpfcn: the compare function. The elements @list are passed as first
+ * argument to @cmpfcn, while @needle is passed as second. Usually, the
+ * needle is the same data type as inside the list, however, that is
+ * not necessary, as long as @cmpfcn takes care to cast the two arguments
+ * accordingly.
+ * @user_data: optional argument passed to @cmpfcn
+ *
+ * Performs binary search for @needle in @list. On success, returns the
+ * (non-negative) index where the compare function found the searched element.
+ * On success, it returns a negative value. Note that the return negative value
+ * is the bitwise inverse of the position where the element should be inserted.
+ *
+ * If the list contains multiple matching elements, an arbitrary index is
+ * returned.
+ *
+ * Returns: the index to the element in the list, or the (negative, bitwise inverted)
+ * position where it should be.
+ */
+gssize
+nm_utils_array_find_binary_search (gconstpointer list,
+ gsize elem_size,
+ gsize len,
+ gconstpointer needle,
+ GCompareDataFunc cmpfcn,
+ gpointer user_data)
+{
+ gssize imin, imax, imid;
+ int cmp;
+
+ g_return_val_if_fail (list || !len, ~((gssize) 0));
+ g_return_val_if_fail (cmpfcn, ~((gssize) 0));
+ g_return_val_if_fail (elem_size > 0, ~((gssize) 0));
+
+ imin = 0;
+ if (len == 0)
+ return ~imin;
+
+ imax = len - 1;
+
+ while (imin <= imax) {
+ imid = imin + (imax - imin) / 2;
+
+ cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data);
+ if (cmp == 0)
+ return imid;
+
+ if (cmp < 0)
+ imin = imid + 1;
+ else
+ imax = imid - 1;
+ }
+
+ /* return the inverse of @imin. This is a negative number, but
+ * also is ~imin the position where the value should be inserted. */
+ return ~imin;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_hash_table_equal:
+ * @a: one #GHashTable
+ * @b: other #GHashTable
+ * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is
+ * treated like an empty hash. It means, a %NULL hash will compare equal
+ * to an empty hash.
+ * @equal_func: the equality function, for comparing the values.
+ * If %NULL, the values are not compared. In that case, the function
+ * only checks, if both dictionaries have the same keys -- according
+ * to @b's key equality function.
+ * Note that the values of @a will be passed as first argument
+ * to @equal_func.
+ *
+ * Compares two hash tables, whether they have equal content.
+ * This only makes sense, if @a and @b have the same key types and
+ * the same key compare-function.
+ *
+ * Returns: %TRUE, if both dictionaries have the same content.
+ */
+gboolean
+nm_utils_hash_table_equal (const GHashTable *a,
+ const GHashTable *b,
+ gboolean treat_null_as_empty,
+ NMUtilsHashTableEqualFunc equal_func)
+{
+ guint n;
+ GHashTableIter iter;
+ gconstpointer key, v_a, v_b;
+
+ if (a == b)
+ return TRUE;
+ if (!treat_null_as_empty) {
+ if (!a || !b)
+ return FALSE;
+ }
+
+ n = a ? g_hash_table_size ((GHashTable *) a) : 0;
+ if (n != (b ? g_hash_table_size ((GHashTable *) b) : 0))
+ return FALSE;
+
+ if (n > 0) {
+ g_hash_table_iter_init (&iter, (GHashTable *) a);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &v_a)) {
+ if (!g_hash_table_lookup_extended ((GHashTable *) b, key, NULL, (gpointer *) &v_b))
+ return FALSE;
+ if ( equal_func
+ && !equal_func (v_a, v_b))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_get_start_time_for_pid:
+ * @pid: the process identifier
+ * @out_state: return the state character, like R, S, Z. See `man 5 proc`.
+ * @out_ppid: parent process id
+ *
+ * Originally copied from polkit source (src/polkit/polkitunixprocess.c)
+ * and adjusted.
+ *
+ * Returns: the timestamp when the process started (by parsing /proc/$PID/stat).
+ * If an error occurs (e.g. the process does not exist), 0 is returned.
+ *
+ * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds)
+ **/
+guint64
+nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
+{
+ guint64 start_time;
+ char filename[256];
+ gs_free char *contents = NULL;
+ size_t length;
+ gs_free const char **tokens = NULL;
+ char *p;
+ char state = ' ';
+ gint64 ppid = 0;
+
+ start_time = 0;
+ contents = NULL;
+
+ g_return_val_if_fail (pid > 0, 0);
+
+ nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+
+ if (!g_file_get_contents (filename, &contents, &length, NULL))
+ goto fail;
+
+ /* start time is the token at index 19 after the '(process name)' entry - since only this
+ * field can contain the ')' character, search backwards for this to avoid malicious
+ * processes trying to fool us
+ */
+ p = strrchr (contents, ')');
+ if (!p)
+ goto fail;
+ p += 2; /* skip ') ' */
+ if (p - contents >= (int) length)
+ goto fail;
+
+ state = p[0];
+
+ tokens = nm_utils_strsplit_set (p, " ", FALSE);
+
+ if (NM_PTRARRAY_LEN (tokens) < 20)
+ goto fail;
+
+ if (out_ppid) {
+ ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0);
+ if (ppid == 0)
+ goto fail;
+ }
+
+ start_time = _nm_utils_ascii_str_to_int64 (tokens[19], 10, 1, G_MAXINT64, 0);
+ if (start_time == 0)
+ goto fail;
+
+ NM_SET_OUT (out_state, state);
+ NM_SET_OUT (out_ppid, ppid);
+ return start_time;
+
+fail:
+ NM_SET_OUT (out_state, ' ');
+ NM_SET_OUT (out_ppid, 0);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ * _nm_utils_strv_sort:
+ * @strv: pointer containing strings that will be sorted
+ * in-place, %NULL is allowed, unless @len indicates
+ * that there are more elements.
+ * @len: the number of elements in strv. If negative,
+ * strv must be a NULL terminated array and the length
+ * will be calculated first. If @len is a positive
+ * number, all first @len elements in @strv must be
+ * non-NULL, valid strings.
+ *
+ * Ascending sort of the array @strv inplace, using plain strcmp() string
+ * comparison.
+ */
+void
+_nm_utils_strv_sort (const char **strv, gssize len)
+{
+ gsize l;
+
+ l = len < 0 ? (gsize) NM_PTRARRAY_LEN (strv) : (gsize) len;
+
+ if (l <= 1)
+ return;
+
+ nm_assert (l <= (gsize) G_MAXINT);
+
+ g_qsort_with_data (strv,
+ l,
+ sizeof (const char *),
+ nm_strcmp_p_with_data,
+ NULL);
+}
+
+/*****************************************************************************/
+
+gpointer
+_nm_utils_user_data_pack (int nargs, gconstpointer *args)
+{
+ int i;
+ gpointer *data;
+
+ nm_assert (nargs > 0);
+ nm_assert (args);
+
+ data = g_slice_alloc (((gsize) nargs) * sizeof (gconstpointer));
+ for (i = 0; i < nargs; i++)
+ data[i] = (gpointer) args[i];
+ return data;
+}
+
+void
+_nm_utils_user_data_unpack (gpointer user_data, int nargs, ...)
+{
+ gpointer *data = user_data;
+ va_list ap;
+ int i;
+
+ nm_assert (data);
+ nm_assert (nargs > 0);
+
+ va_start (ap, nargs);
+ for (i = 0; i < nargs; i++) {
+ gpointer *dst;
+
+ dst = va_arg (ap, gpointer *);
+ nm_assert (dst);
+
+ *dst = data[i];
+ }
+ va_end (ap);
+
+ g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data);
+}
+
+/*****************************************************************************/
+
+#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t')
+
+const char *
+_nm_utils_escape_spaces (const char *str, char **to_free)
+{
+ const char *ptr = str;
+ char *ret, *r;
+
+ *to_free = NULL;
+
+ if (!str)
+ return NULL;
+
+ while (TRUE) {
+ if (!*ptr)
+ return str;
+ if (IS_SPACE (*ptr))
+ break;
+ ptr++;
+ }
+
+ ptr = str;
+ ret = g_new (char, strlen (str) * 2 + 1);
+ r = ret;
+ *to_free = ret;
+ while (*ptr) {
+ if (IS_SPACE (*ptr))
+ *r++ = '\\';
+ *r++ = *ptr++;
+ }
+ *r = '\0';
+
+ return ret;
+}
+
+char *
+_nm_utils_unescape_spaces (char *str)
+{
+ guint i, j = 0;
+
+ if (!str)
+ return NULL;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] == '\\' && IS_SPACE (str[i+1]))
+ i++;
+ str[j++] = str[i];
+ }
+ str[j] = '\0';
+
+ return str;
+}
+
+#undef IS_SPACE
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index f1f9f51..b309973 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -22,8 +22,225 @@
#ifndef __NM_SHARED_UTILS_H__
#define __NM_SHARED_UTILS_H__
+#include <netinet/in.h>
+
/*****************************************************************************/
+static inline gboolean
+_NM_INT_NOT_NEGATIVE (gssize val)
+{
+ /* whether an enum (without negative values) is a signed int, depends on compiler options
+ * and compiler implementation.
+ *
+ * When using such an enum for accessing an array, one naturally wants to check
+ * that the enum is not negative. However, the compiler doesn't like a plain
+ * comparisong "enum_val >= 0", because (if the enum is unsigned), it will warn
+ * that the expression is always true *duh*. Not even a cast to a signed
+ * type helps to avoid the compiler warning in any case.
+ *
+ * The sole purpose of this function is to avoid a compiler warning, when checking
+ * that an enum is not negative. */
+ return val >= 0;
+}
+
+/*****************************************************************************/
+
+static inline char
+nm_utils_addr_family_to_char (int addr_family)
+{
+ switch (addr_family) {
+ case AF_INET: return '4';
+ case AF_INET6: return '6';
+ }
+ g_return_val_if_reached ('?');
+}
+
+static inline gsize
+nm_utils_addr_family_to_size (int addr_family)
+{
+ switch (addr_family) {
+ case AF_INET: return sizeof (in_addr_t);
+ case AF_INET6: return sizeof (struct in6_addr);
+ }
+ g_return_val_if_reached (0);
+}
+
+#define nm_assert_addr_family(addr_family) \
+ nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6))
+
+/*****************************************************************************/
+
+typedef struct {
+ union {
+ guint8 addr_ptr[1];
+ in_addr_t addr4;
+ struct in6_addr addr6;
+
+ /* NMIPAddr is really a union for IP addresses.
+ * However, as ethernet addresses fit in here nicely, use
+ * it also for an ethernet MAC address. */
+ guint8 addr_eth[6 /*ETH_ALEN*/];
+ };
+} NMIPAddr;
+
+extern const NMIPAddr nm_ip_addr_zero;
+
+static inline void
+nm_ip_addr_set (int addr_family, gpointer dst, const NMIPAddr *src)
+{
+ nm_assert_addr_family (addr_family);
+ nm_assert (dst);
+ nm_assert (src);
+
+ if (addr_family != AF_INET6)
+ *((in_addr_t *) dst) = src->addr4;
+ else
+ *((struct in6_addr *) dst) = src->addr6;
+}
+
+/*****************************************************************************/
+
+#define NM_CMP_RETURN(c) \
+ G_STMT_START { \
+ const int _cc = (c); \
+ if (_cc) \
+ return _cc < 0 ? -1 : 1; \
+ } G_STMT_END
+
+#define NM_CMP_SELF(a, b) \
+ G_STMT_START { \
+ typeof (a) _a = (a); \
+ typeof (b) _b = (b); \
+ \
+ if (_a == _b) \
+ return 0; \
+ if (!_a) \
+ return -1; \
+ if (!_b) \
+ return 1; \
+ } G_STMT_END
+
+#define NM_CMP_DIRECT(a, b) \
+ G_STMT_START { \
+ typeof (a) _a = (a); \
+ typeof (b) _b = (b); \
+ \
+ if (_a != _b) \
+ return (_a < _b) ? -1 : 1; \
+ } G_STMT_END
+
+#define NM_CMP_DIRECT_MEMCMP(a, b, size) \
+ NM_CMP_RETURN (memcmp ((a), (b), (size)))
+
+#define NM_CMP_DIRECT_STRCMP0(a, b) \
+ NM_CMP_RETURN (g_strcmp0 ((a), (b)))
+
+#define NM_CMP_DIRECT_IN6ADDR(a, b) \
+ G_STMT_START { \
+ const struct in6_addr *const _a = (a); \
+ const struct in6_addr *const _b = (b); \
+ NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \
+ } G_STMT_END
+
+#define NM_CMP_FIELD(a, b, field) \
+ NM_CMP_DIRECT (((a)->field), ((b)->field))
+
+#define NM_CMP_FIELD_UNSAFE(a, b, field) \
+ G_STMT_START { \
+ /* it's unsafe, because it evaluates the arguments more then once.
+ * This is necessary for bitfields, for which typeof() doesn't work. */ \
+ if (((a)->field) != ((b)->field)) \
+ return ((a)->field < ((b)->field)) ? -1 : 1; \
+ } G_STMT_END
+
+#define NM_CMP_FIELD_BOOL(a, b, field) \
+ NM_CMP_DIRECT (!!((a)->field), !!((b)->field))
+
+#define NM_CMP_FIELD_STR(a, b, field) \
+ NM_CMP_RETURN (strcmp (((a)->field), ((b)->field)))
+
+#define NM_CMP_FIELD_STR_INTERNED(a, b, field) \
+ G_STMT_START { \
+ const char *_a = ((a)->field); \
+ const char *_b = ((b)->field); \
+ \
+ if (_a != _b) { \
+ NM_CMP_RETURN (g_strcmp0 (_a, _b)); \
+ } \
+ } G_STMT_END
+
+#define NM_CMP_FIELD_STR0(a, b, field) \
+ NM_CMP_RETURN (g_strcmp0 (((a)->field), ((b)->field)))
+
+#define NM_CMP_FIELD_MEMCMP_LEN(a, b, field, len) \
+ NM_CMP_RETURN (memcmp (&((a)->field), &((b)->field), \
+ MIN (len, sizeof ((a)->field))))
+
+#define NM_CMP_FIELD_MEMCMP(a, b, field) \
+ NM_CMP_RETURN (memcmp (&((a)->field), \
+ &((b)->field), \
+ sizeof ((a)->field)))
+
+#define NM_CMP_FIELD_IN6ADDR(a, b, field) \
+ G_STMT_START { \
+ const struct in6_addr *const _a = &((a)->field); \
+ const struct in6_addr *const _b = &((b)->field); \
+ NM_CMP_RETURN (memcmp (_a, _b, sizeof (struct in6_addr))); \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+static inline gboolean
+nm_utils_mem_all_zero (gconstpointer mem, gsize len)
+{
+ const guint8 *p;
+
+ for (p = mem; len-- > 0; p++) {
+ if (*p != 0)
+ return FALSE;
+ }
+
+ /* incidentally, a buffer with len==0, is also *all-zero*. */
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+/* like g_memdup(). The difference is that the @size argument is of type
+ * gsize, while g_memdup() has type guint. Since, the size of container types
+ * like GArray is guint as well, this means trying to g_memdup() an
+ * array,
+ * g_memdup (array->data, array->len * sizeof (ElementType))
+ * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType)
+ * bytes. That seems unnecessarily dangerous to me.
+ * nm_memdup() avoids that, because its size argument is always large enough
+ * to contain all data that a GArray can hold.
+ *
+ * Another minor difference to g_memdup() is that the glib version also
+ * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1)
+ * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that
+ * is desirable, because @size MUST be correct at all times. @size
+ * may be zero, but one must not claim to have non-zero bytes when
+ * passing a %NULL @data pointer.
+ */
+static inline gpointer
+nm_memdup (gconstpointer data, gsize size)
+{
+ gpointer p;
+
+ if (size == 0)
+ return NULL;
+ p = g_malloc (size);
+ memcpy (p, data, size);
+ return p;
+}
+
+/*****************************************************************************/
+
+extern const void *const _NM_PTRARRAY_EMPTY[1];
+
+#define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY)
+
static inline void
_nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
{
@@ -40,17 +257,226 @@ _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
+void nm_utils_strbuf_seek_end (char **buf, gsize *len);
+
+const char *nm_strquote (char *buf, gsize buf_len, const char *str);
+
+static inline gboolean
+nm_utils_is_separator (const char c)
+{
+ return NM_IN_SET (c, ' ', '\t');
+}
+
+/*****************************************************************************/
+
+static inline gboolean
+nm_gbytes_equal0 (GBytes *a, GBytes *b)
+{
+ return a == b || (a && b && g_bytes_equal (a, b));
+}
+
+gboolean nm_utils_gbytes_equal_mem (GBytes *bytes,
+ gconstpointer mem_data,
+ gsize mem_len);
+
+GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes);
+
+/*****************************************************************************/
+
+static inline int
+nm_utils_hexchar_to_int (char ch)
+{
+ G_STATIC_ASSERT_EXPR ('0' < 'A');
+ G_STATIC_ASSERT_EXPR ('A' < 'a');
+
+ if (ch >= '0') {
+ if (ch <= '9')
+ return ch - '0';
+ if (ch >= 'A') {
+ if (ch <= 'F')
+ return ((int) ch) + (10 - (int) 'A');
+ if (ch >= 'a' && ch <= 'f')
+ return ((int) ch) + (10 - (int) 'a');
+ }
+ }
+ return -1;
+}
+
+/*****************************************************************************/
+
+const char *nm_utils_dbus_path_get_last_component (const char *dbus_path);
+
+int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
/*****************************************************************************/
+const char **nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping);
+
gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
+char **_nm_utils_strv_cleanup (char **strv,
+ gboolean strip_whitespace,
+ gboolean skip_empty,
+ gboolean skip_repeated);
+
+/*****************************************************************************/
+
+guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix);
+guint32 _nm_utils_ip4_get_default_prefix (guint32 ip);
+
+gboolean nm_utils_ip_is_site_local (int addr_family,
+ const void *address);
+
/*****************************************************************************/
+gboolean nm_utils_parse_inaddr_bin (int addr_family,
+ const char *text,
+ gpointer out_addr);
+
+gboolean nm_utils_parse_inaddr (int addr_family,
+ const char *text,
+ char **out_addr);
+
+gboolean nm_utils_parse_inaddr_prefix_bin (int addr_family,
+ const char *text,
+ gpointer out_addr,
+ int *out_prefix);
+
+gboolean nm_utils_parse_inaddr_prefix (int addr_family,
+ const char *text,
+ char **out_addr,
+ int *out_prefix);
+
gint64 _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback);
-gint _nm_utils_ascii_str_to_bool (const char *str,
- gint default_value);
+int _nm_utils_ascii_str_to_bool (const char *str,
+ int default_value);
+
+/*****************************************************************************/
+
+extern char _nm_utils_to_string_buffer[2096];
+
+void nm_utils_to_string_buffer_init (char **buf, gsize *len);
+gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len);
+
+/*****************************************************************************/
+
+typedef struct {
+ unsigned flag;
+ const char *name;
+} NMUtilsFlags2StrDesc;
+
+#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, }
+
+#define _NM_UTILS_FLAGS2STR_DEFINE(scope, fcn_name, flags_type, ...) \
+scope const char * \
+fcn_name (flags_type flags, char *buf, gsize len) \
+{ \
+ static const NMUtilsFlags2StrDesc descs[] = { \
+ __VA_ARGS__ \
+ }; \
+ G_STATIC_ASSERT (sizeof (flags_type) <= sizeof (unsigned)); \
+ return nm_utils_flags2str (descs, G_N_ELEMENTS (descs), flags, buf, len); \
+};
+
+#define NM_UTILS_FLAGS2STR_DEFINE(fcn_name, flags_type, ...) \
+ _NM_UTILS_FLAGS2STR_DEFINE (, fcn_name, flags_type, __VA_ARGS__)
+#define NM_UTILS_FLAGS2STR_DEFINE_STATIC(fcn_name, flags_type, ...) \
+ _NM_UTILS_FLAGS2STR_DEFINE (static, fcn_name, flags_type, __VA_ARGS__)
+
+const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs,
+ gsize n_descs,
+ unsigned flags,
+ char *buf,
+ gsize len);
+
+/*****************************************************************************/
+
+#define NM_UTILS_ENUM2STR(v, n) (void) 0; case v: s = ""n""; break; (void) 0
+#define NM_UTILS_ENUM2STR_IGNORE(v) (void) 0; case v: break; (void) 0
+
+#define _NM_UTILS_ENUM2STR_DEFINE(scope, fcn_name, lookup_type, int_fmt, ...) \
+scope const char * \
+fcn_name (lookup_type val, char *buf, gsize len) \
+{ \
+ nm_utils_to_string_buffer_init (&buf, &len); \
+ if (len) { \
+ const char *s = NULL; \
+ switch (val) { \
+ (void) 0, \
+ __VA_ARGS__ \
+ (void) 0; \
+ }; \
+ if (s) \
+ g_strlcpy (buf, s, len); \
+ else \
+ g_snprintf (buf, len, "(%"int_fmt")", val); \
+ } \
+ return buf; \
+}
+
+#define NM_UTILS_ENUM2STR_DEFINE(fcn_name, lookup_type, ...) \
+ _NM_UTILS_ENUM2STR_DEFINE (, fcn_name, lookup_type, "d", __VA_ARGS__)
+#define NM_UTILS_ENUM2STR_DEFINE_STATIC(fcn_name, lookup_type, ...) \
+ _NM_UTILS_ENUM2STR_DEFINE (static, fcn_name, lookup_type, "d", __VA_ARGS__)
+
+/*****************************************************************************/
+
+#define _nm_g_slice_free_fcn_define(mem_size) \
+static inline void \
+_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \
+{ \
+ g_slice_free1 (mem_size, mem_block); \
+}
+
+_nm_g_slice_free_fcn_define (1)
+_nm_g_slice_free_fcn_define (2)
+_nm_g_slice_free_fcn_define (4)
+_nm_g_slice_free_fcn_define (8)
+_nm_g_slice_free_fcn_define (10)
+_nm_g_slice_free_fcn_define (12)
+_nm_g_slice_free_fcn_define (16)
+
+#define _nm_g_slice_free_fcn1(mem_size) \
+ ({ \
+ void (*_fcn) (gpointer); \
+ \
+ /* If mem_size is a compile time constant, the compiler
+ * will be able to optimize this. Hence, you don't want
+ * to call this with a non-constant size argument. */ \
+ G_STATIC_ASSERT_EXPR ( ((mem_size) == 1) \
+ || ((mem_size) == 2) \
+ || ((mem_size) == 4) \
+ || ((mem_size) == 8) \
+ || ((mem_size) == 10) \
+ || ((mem_size) == 12) \
+ || ((mem_size) == 16)); \
+ switch ((mem_size)) { \
+ case 1: _fcn = _nm_g_slice_free_fcn_1; break; \
+ case 2: _fcn = _nm_g_slice_free_fcn_2; break; \
+ case 4: _fcn = _nm_g_slice_free_fcn_4; break; \
+ case 8: _fcn = _nm_g_slice_free_fcn_8; break; \
+ case 10: _fcn = _nm_g_slice_free_fcn_10; break; \
+ case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
+ case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
+ default: g_assert_not_reached (); _fcn = NULL; break; \
+ } \
+ _fcn; \
+ })
+
+/**
+ * nm_g_slice_free_fcn:
+ * @type: type argument for sizeof() operator that you would
+ * pass to g_slice_new().
+ *
+ * Returns: a function pointer with GDestroyNotify signature
+ * for g_slice_free(type,*).
+ *
+ * Only certain types are implemented. You'll get an assertion
+ * using the wrong type. */
+#define nm_g_slice_free_fcn(type) (_nm_g_slice_free_fcn1 (sizeof (type)))
+
+#define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64))
/*****************************************************************************/
@@ -62,10 +488,40 @@ gint _nm_utils_ascii_str_to_bool (const char *str,
* error reason. Depending on the usage, this might indicate a bug because
* usually the target object should stay alive as long as there are pending
* operations.
+ *
+ * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE: used for a very particular
+ * purpose during nm_device_check_connection_compatible() to indicate that
+ * the profile does not match the device already because their type differs.
+ * That is, there is a fundamental reason of trying to check a profile that
+ * cannot possibly match on this device.
+ * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE: used for a very particular
+ * purpose during nm_device_check_connection_available(), to indicate that the
+ * device is not available because it is unmanaged.
+ * @NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY: the profile is currently not
+ * available/compatible with the device, but this may be only temporary.
+ *
+ * @NM_UTILS_ERROR_INVALID_ARGUMENT: invalid argument.
*/
typedef enum {
NM_UTILS_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/
NM_UTILS_ERROR_CANCELLED_DISPOSING, /*< nick=CancelledDisposing >*/
+ NM_UTILS_ERROR_INVALID_ARGUMENT, /*< nick=InvalidArgument >*/
+
+ /* the following codes have a special meaning and are exactly used for
+ * nm_device_check_connection_compatible() and nm_device_check_connection_available().
+ *
+ * Actually, their meaning is not very important (so, don't think too
+ * hard about the name of these error codes). What is important, is their
+ * relative order (i.e. the integer value of the codes). When manager
+ * searches for a suitable device, it will check all devices whether
+ * a profile can be activated. If they all fail, it will pick the error
+ * message from the device that returned the *highest* error code,
+ * in the hope that this message makes the most sense for the caller.
+ * */
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
+ NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+
} NMUtilsError;
#define NM_UTILS_ERROR (nm_utils_error_quark ())
@@ -77,13 +533,298 @@ void nm_utils_error_set_cancelled (GError **error,
gboolean nm_utils_error_is_cancelled (GError *error,
gboolean consider_is_disposing);
+static inline void
+nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
+{
+ g_set_error_literal (error, NM_UTILS_ERROR, error_code, literal);
+}
+
+#define nm_utils_error_set(error, error_code, ...) \
+ g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__)
+
/*****************************************************************************/
gboolean nm_g_object_set_property (GObject *object,
- const gchar *property_name,
+ const char *property_name,
const GValue *value,
GError **error);
+gboolean nm_g_object_set_property_boolean (GObject *object,
+ const char *property_name,
+ gboolean value,
+ GError **error);
+
+gboolean nm_g_object_set_property_uint (GObject *object,
+ const char *property_name,
+ guint value,
+ GError **error);
+
+GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype,
+ const char *property_name);
+
+/*****************************************************************************/
+
+typedef enum {
+ NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0,
+ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001,
+ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002,
+} NMUtilsStrUtf8SafeFlags;
+
+const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags,
char **to_free);
+const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char
**to_free);
+gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free);
+
+const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free);
+const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free);
+
+char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags);
+char *nm_utils_str_utf8safe_unescape_cp (const char *str);
+
+char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags);
+
+static inline void
+nm_g_variant_unref_floating (GVariant *var)
+{
+ /* often a function wants to keep a reference to an input variant.
+ * It uses g_variant_ref_sink() to either increase the ref-count,
+ * or take ownership of a possibly floating reference.
+ *
+ * If the function doesn't actually want to do anything with the
+ * input variant, it still must make sure that a passed in floating
+ * reference is consumed. Hence, this helper which:
+ *
+ * - does nothing if @var is not floating
+ * - unrefs (consumes) @var if it is floating. */
+ if (g_variant_is_floating (var))
+ g_variant_unref (var);
+}
+
+/*****************************************************************************/
+
+static inline int
+nm_utf8_collate0 (const char *a, const char *b)
+{
+ if (!a)
+ return !b ? 0 : -1;
+ if (!b)
+ return 1;
+ return g_utf8_collate (a, b);
+}
+
+int nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data);
+int nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
+int nm_cmp_int2ptr_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data);
+
+/*****************************************************************************/
+
+typedef struct {
+ const char *name;
+} NMUtilsNamedEntry;
+
+typedef struct {
+ union {
+ NMUtilsNamedEntry named_entry;
+ const char *name;
+ };
+ union {
+ const char *value_str;
+ gconstpointer value_ptr;
+ };
+} NMUtilsNamedValue;
+
+#define nm_utils_named_entry_cmp nm_strcmp_p
+#define nm_utils_named_entry_cmp_with_data nm_strcmp_p_with_data
+
+NMUtilsNamedValue *nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len);
+
+gpointer *nm_utils_hash_keys_to_array (GHashTable *hash,
+ GCompareDataFunc compare_func,
+ gpointer user_data,
+ guint *out_len);
+
+static inline const char **
+nm_utils_strdict_get_keys (const GHashTable *hash,
+ gboolean sorted,
+ guint *out_length)
+{
+ return (const char **) nm_utils_hash_keys_to_array ((GHashTable *) hash,
+ sorted ? nm_strcmp_p_with_data : NULL,
+ NULL,
+ out_length);
+}
+
+char **nm_utils_strv_make_deep_copied (const char **strv);
+
+static inline char **
+nm_utils_strv_make_deep_copied_nonnull (const char **strv)
+{
+ return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1);
+}
+
+/*****************************************************************************/
+
+gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list,
+ gsize len,
+ gconstpointer needle,
+ GCompareDataFunc cmpfcn,
+ gpointer user_data,
+ gssize *out_idx_first,
+ gssize *out_idx_last);
+
+gssize nm_utils_array_find_binary_search (gconstpointer list,
+ gsize elem_size,
+ gsize len,
+ gconstpointer needle,
+ GCompareDataFunc cmpfcn,
+ gpointer user_data);
+
+/*****************************************************************************/
+
+typedef gboolean (*NMUtilsHashTableEqualFunc) (gconstpointer a,
+ gconstpointer b);
+
+gboolean nm_utils_hash_table_equal (const GHashTable *a,
+ const GHashTable *b,
+ gboolean treat_null_as_empty,
+ NMUtilsHashTableEqualFunc equal_func);
+
+/*****************************************************************************/
+
+void _nm_utils_strv_sort (const char **strv, gssize len);
+#define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len)
+
+/*****************************************************************************/
+
+#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000)
+#define NM_UTILS_NS_PER_MSEC ((gint64) 1000000)
+#define NM_UTILS_MSEC_PER_SECOND ((gint64) 1000)
+#define NM_UTILS_NS_TO_MSEC_CEIL(nsec) (((nsec) + (NM_UTILS_NS_PER_MSEC - 1)) / NM_UTILS_NS_PER_MSEC)
+
+/*****************************************************************************/
+
+int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns);
+ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll);
+int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll);
+
+/*****************************************************************************/
+
+static inline const char *
+nm_utils_dbus_normalize_object_path (const char *path)
+{
+ /* D-Bus does not allow an empty object path. Hence, whenever we mean NULL / no-object
+ * on D-Bus, it's path is actually "/".
+ *
+ * Normalize that away, and return %NULL in that case. */
+ if (path && path[0] == '/' && path[1] == '\0')
+ return NULL;
+ return path;
+}
+
+#define NM_DEFINE_GDBUS_ARG_INFO_FULL(name_, ...) \
+ ((GDBusArgInfo *) (&((const GDBusArgInfo) { \
+ .ref_count = -1, \
+ .name = name_, \
+ __VA_ARGS__ \
+ })))
+
+#define NM_DEFINE_GDBUS_ARG_INFO(name_, a_signature) \
+ NM_DEFINE_GDBUS_ARG_INFO_FULL ( \
+ name_, \
+ .signature = a_signature, \
+ )
+
+#define NM_DEFINE_GDBUS_ARG_INFOS(...) \
+ ((GDBusArgInfo **) ((const GDBusArgInfo *[]) { \
+ __VA_ARGS__ \
+ NULL, \
+ }))
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFO(name_, ...) \
+ ((GDBusPropertyInfo *) (&((const GDBusPropertyInfo) { \
+ .ref_count = -1, \
+ .name = name_, \
+ __VA_ARGS__ \
+ })))
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFO_READABLE(name_, m_signature) \
+ NM_DEFINE_GDBUS_PROPERTY_INFO ( \
+ name_, \
+ .signature = m_signature, \
+ .flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE, \
+ )
+
+#define NM_DEFINE_GDBUS_PROPERTY_INFOS(...) \
+ ((GDBusPropertyInfo **) ((const GDBusPropertyInfo *[]) { \
+ __VA_ARGS__ \
+ NULL, \
+ }))
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFO_INIT(name_, ...) \
+ { \
+ .ref_count = -1, \
+ .name = name_, \
+ __VA_ARGS__ \
+ }
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFO(name_, ...) \
+ ((GDBusSignalInfo *) (&((const GDBusSignalInfo) NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (name_,
__VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_SIGNAL_INFOS(...) \
+ ((GDBusSignalInfo **) ((const GDBusSignalInfo *[]) { \
+ __VA_ARGS__ \
+ NULL, \
+ }))
+
+#define NM_DEFINE_GDBUS_METHOD_INFO_INIT(name_, ...) \
+ { \
+ .ref_count = -1, \
+ .name = name_, \
+ __VA_ARGS__ \
+ }
+
+#define NM_DEFINE_GDBUS_METHOD_INFO(name_, ...) \
+ ((GDBusMethodInfo *) (&((const GDBusMethodInfo) NM_DEFINE_GDBUS_METHOD_INFO_INIT (name_,
__VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_METHOD_INFOS(...) \
+ ((GDBusMethodInfo **) ((const GDBusMethodInfo *[]) { \
+ __VA_ARGS__ \
+ NULL, \
+ }))
+
+#define NM_DEFINE_GDBUS_INTERFACE_INFO_INIT(name_, ...) \
+ { \
+ .ref_count = -1, \
+ .name = name_, \
+ __VA_ARGS__ \
+ }
+
+#define NM_DEFINE_GDBUS_INTERFACE_INFO(name_, ...) \
+ ((GDBusInterfaceInfo *) (&((const GDBusInterfaceInfo) NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (name_,
__VA_ARGS__))))
+
+#define NM_DEFINE_GDBUS_INTERFACE_VTABLE(...) \
+ ((GDBusInterfaceVTable *) (&((const GDBusInterfaceVTable) { \
+ __VA_ARGS__ \
+ })))
+
/*****************************************************************************/
+guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
+
+/*****************************************************************************/
+
+gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args);
+
+#define nm_utils_user_data_pack(...) \
+ _nm_utils_user_data_pack(NM_NARG (__VA_ARGS__), (gconstpointer[]) { __VA_ARGS__ })
+
+void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...);
+
+#define nm_utils_user_data_unpack(user_data, ...) \
+ _nm_utils_user_data_unpack(user_data, NM_NARG (__VA_ARGS__), __VA_ARGS__)
+
+/*****************************************************************************/
+
+const char *_nm_utils_escape_spaces (const char *str, char **to_free);
+char *_nm_utils_unescape_spaces (char *str);
+
#endif /* __NM_SHARED_UTILS_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]