diff options
| author | Michael Biebl <biebl@debian.org> | 2024-01-25 09:46:18 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2024-01-25 09:46:18 +0100 |
| commit | 70e18d99b8e3e77bb37e218d7ac582130156f8ef (patch) | |
| tree | d40c587e6d3f0e094ff558e415f1bb9803643214 /src/libnm-glib-aux | |
| parent | d4d8b2b91f7ba000d97a8b2aab48c85000c11314 (diff) | |
New upstream version 1.45.90 upstream/1.45.90
Diffstat (limited to 'src/libnm-glib-aux')
| -rw-r--r-- | src/libnm-glib-aux/nm-dedup-multi.h | 2 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-enum-utils.c | 129 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-enum-utils.h | 12 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-errno.c | 23 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-glib.h | 380 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-hash-utils.h | 29 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-macros-internal.h | 26 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-shared-utils.c | 304 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-shared-utils.h | 367 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-test-utils.h | 39 | ||||
| -rw-r--r-- | src/libnm-glib-aux/tests/test-shared-general.c | 286 |
11 files changed, 1049 insertions, 548 deletions
diff --git a/src/libnm-glib-aux/nm-dedup-multi.h b/src/libnm-glib-aux/nm-dedup-multi.h index 87a2b815..ebc5d0a1 100644 --- a/src/libnm-glib-aux/nm-dedup-multi.h +++ b/src/libnm-glib-aux/nm-dedup-multi.h @@ -37,7 +37,7 @@ typedef enum _NMDedupMultiIdxMode { /*****************************************************************************/ -#define _NMDedupMultiObj_Align (MAX(_nm_alignof(void *), _nm_alignof(gint64))) +#define _NMDedupMultiObj_Align (NM_MAX_CONST(_nm_alignof(void *), _nm_alignof(gint64))) struct _NMDedupMultiObj { union { diff --git a/src/libnm-glib-aux/nm-enum-utils.c b/src/libnm-glib-aux/nm-enum-utils.c index 2593a9cd..212c0021 100644 --- a/src/libnm-glib-aux/nm-enum-utils.c +++ b/src/libnm-glib-aux/nm-enum-utils.c @@ -320,53 +320,118 @@ _nm_utils_enum_from_str_full(GType type, const char ** _nm_utils_enum_get_values(GType type, int from, int to) { - GTypeClass *klass; - GPtrArray *array; - int i; + int i; + GArray *values_full = _nm_utils_enum_get_values_full(type, from, to, NULL); + GPtrArray *values = g_ptr_array_sized_new(values_full->len + 1); + + for (i = 0; i < values_full->len; i++) { + NMUtilsEnumValueInfoFull *v = &nm_g_array_index(values_full, NMUtilsEnumValueInfoFull, i); + g_ptr_array_add(values, (gpointer) v->nick); + } + + g_ptr_array_add(values, NULL); + g_array_unref(values_full); + return (const char **) g_ptr_array_free(values, FALSE); +} + +static void +_free_value_info_full(NMUtilsEnumValueInfoFull *value_info_full) +{ + g_free(value_info_full->aliases); +} + +static void +_init_value_info_full(NMUtilsEnumValueInfoFull *v, bool is_flag, const char *nick, int value) +{ char sbuf[64]; + const char *value_str = is_flag ? g_intern_string(nm_sprintf_buf(sbuf, "0x%x", value)) + : g_intern_string(nm_sprintf_buf(sbuf, "%d", value)); - klass = g_type_class_ref(type); - array = g_ptr_array_new(); + v->nick = _enum_is_valid_enum_nick(nick) ? nick : value_str; + v->aliases = NULL; + v->value_str = value_str; + v->value = value; +} + +/** + * _nm_utils_enum_get_values_full: + * @type: the enum or flags type + * @from: lowest value to return + * @to: highest value to return + * @value_infos: (nullable): additional value aliases + * + * Get the enum or flags values within the given range, putting together the + * value, name and aliases of each of them. + * + * If @value_infos is NULL, no memory will be allocated and deallocated for the + * aliases and #NMUtilsEnumValueInfoFull:aliases will be NULL in the returned + * data. + * + * The caller is responsible of releasing the container, but not the contained + * data. Only #NMUtilsEnumValueInfoFull:aliases can be stolen (and set to NULL), + * and then the caller becomes the responsible to release it. + * + * Return: (transfer container): an array of #NMUtilsEnumValueInfoFull. + */ +GArray * +_nm_utils_enum_get_values_full(GType type, + int from, + int to, + const NMUtilsEnumValueInfo *value_infos) +{ + NMUtilsEnumValueInfoFull v; + GArray *array; + int i; + + nm_auto_unref_gtypeclass GTypeClass *klass = g_type_class_ref(type); + g_return_val_if_fail(G_IS_ENUM_CLASS(klass) || G_IS_FLAGS_CLASS(klass), NULL); + + _ASSERT_enum_values_info(type, value_infos); + + array = g_array_new(FALSE, FALSE, sizeof(NMUtilsEnumValueInfoFull)); if (G_IS_ENUM_CLASS(klass)) { GEnumClass *enum_class = G_ENUM_CLASS(klass); - GEnumValue *enum_value; for (i = 0; i < enum_class->n_values; i++) { - enum_value = &enum_class->values[i]; - if (enum_value->value >= from && enum_value->value <= to) { - if (_enum_is_valid_enum_nick(enum_value->value_nick)) - g_ptr_array_add(array, (gpointer) enum_value->value_nick); - else - g_ptr_array_add( - array, - (gpointer) g_intern_string(nm_sprintf_buf(sbuf, "%d", enum_value->value))); + GEnumValue *enum_val = &enum_class->values[i]; + + if (enum_val->value >= from && enum_val->value <= to) { + _init_value_info_full(&v, FALSE, enum_val->value_nick, enum_val->value); + g_array_append_val(array, v); } } - } else if (G_IS_FLAGS_CLASS(klass)) { + } else { GFlagsClass *flags_class = G_FLAGS_CLASS(klass); - GFlagsValue *flags_value; for (i = 0; i < flags_class->n_values; i++) { - flags_value = &flags_class->values[i]; - if (flags_value->value >= (guint) from && flags_value->value <= (guint) to) { - if (_enum_is_valid_flags_nick(flags_value->value_nick)) - g_ptr_array_add(array, (gpointer) flags_value->value_nick); - else - g_ptr_array_add( - array, - (gpointer) g_intern_string( - nm_sprintf_buf(sbuf, "0x%x", (unsigned) flags_value->value))); + GFlagsValue *flags_val = &flags_class->values[i]; + + if (flags_val->value >= (guint) from && flags_val->value <= (guint) to) { + _init_value_info_full(&v, TRUE, flags_val->value_nick, flags_val->value); + g_array_append_val(array, v); } } - } else { - g_type_class_unref(klass); - g_ptr_array_free(array, TRUE); - g_return_val_if_reached(NULL); } - g_type_class_unref(klass); - g_ptr_array_add(array, NULL); + if (value_infos) { + g_array_set_clear_func(array, (GDestroyNotify) _free_value_info_full); + + for (i = 0; i < array->len; i++) { + NMUtilsEnumValueInfoFull *vi_full = + &nm_g_array_index(array, NMUtilsEnumValueInfoFull, i); + GPtrArray *aliases = g_ptr_array_new(); + + const NMUtilsEnumValueInfo *vi; + for (vi = value_infos; vi && vi->nick; vi++) { + if (vi->value == vi_full->value) + g_ptr_array_add(aliases, (gpointer) vi->nick); + } + + g_ptr_array_add(aliases, NULL); + vi_full->aliases = (const char **) g_ptr_array_free(aliases, FALSE); + } + } - return (const char **) g_ptr_array_free(array, FALSE); + return array; } diff --git a/src/libnm-glib-aux/nm-enum-utils.h b/src/libnm-glib-aux/nm-enum-utils.h index 6478cadf..a90e13fe 100644 --- a/src/libnm-glib-aux/nm-enum-utils.h +++ b/src/libnm-glib-aux/nm-enum-utils.h @@ -15,6 +15,13 @@ typedef struct _NMUtilsEnumValueInfo { int value; } NMUtilsEnumValueInfo; +typedef struct _NMUtilsEnumValueInfoFull { + const char *nick; + const char **aliases; + const char *value_str; + int value; +} NMUtilsEnumValueInfoFull; + char *_nm_utils_enum_to_str_full(GType type, int value, const char *sep, @@ -27,6 +34,11 @@ gboolean _nm_utils_enum_from_str_full(GType type, const char **_nm_utils_enum_get_values(GType type, int from, int to); +GArray *_nm_utils_enum_get_values_full(GType type, + int from, + int to, + const NMUtilsEnumValueInfo *value_infos); + /*****************************************************************************/ #endif /* __NM_ENUM_UTILS_H__ */ diff --git a/src/libnm-glib-aux/nm-errno.c b/src/libnm-glib-aux/nm-errno.c index b76386a6..682bfea2 100644 --- a/src/libnm-glib-aux/nm-errno.c +++ b/src/libnm-glib-aux/nm-errno.c @@ -6,6 +6,7 @@ #include "libnm-glib-aux/nm-default-glib-i18n-lib.h" #include "nm-errno.h" +#include "libnm-std-aux/nm-std-utils.h" /*****************************************************************************/ @@ -100,26 +101,10 @@ nm_strerror(int nmerr) const char * nm_strerror_native_r(int errsv, char *buf, gsize buf_size) { - char *buf2; + NM_AUTO_PROTECT_ERRNO(errsv2); + const char *buf2; - nm_assert(buf); - nm_assert(buf_size > 0); - -#if (!defined(__GLIBC__) && !defined(__UCLIBC__)) || ((_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE) - /* XSI-compliant */ - { - int errno_saved = errno; - - if (strerror_r(errsv, buf, buf_size) != 0) { - g_snprintf(buf, buf_size, "Unspecified errno %d", errsv); - errno = errno_saved; - } - buf2 = buf; - } -#else - /* GNU-specific */ - buf2 = strerror_r(errsv, buf, buf_size); -#endif + buf2 = _nm_strerror_r(errsv, buf, buf_size); /* like g_strerror(), ensure that the error message is UTF-8. */ if (!g_get_charset(NULL) && !g_utf8_validate(buf2, -1, NULL)) { diff --git a/src/libnm-glib-aux/nm-glib.h b/src/libnm-glib-aux/nm-glib.h index 9c90d429..cf76cf13 100644 --- a/src/libnm-glib-aux/nm-glib.h +++ b/src/libnm-glib-aux/nm-glib.h @@ -29,22 +29,6 @@ /*****************************************************************************/ -static inline void -__g_type_ensure(GType type) -{ -#if !GLIB_CHECK_VERSION(2, 34, 0) - if (G_UNLIKELY(type == (GType) -1)) - g_error("can't happen"); -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - g_type_ensure(type); - G_GNUC_END_IGNORE_DEPRECATIONS; -#endif -} -#define g_type_ensure __g_type_ensure - -/*****************************************************************************/ - /* glib 2.58+ defines an improved, type-safe variant of g_clear_pointer(). Reimplement * that. * @@ -81,71 +65,6 @@ __g_type_ensure(GType type) /*****************************************************************************/ -#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. - */ -#define g_test_expect_message(log_domain, log_level, pattern) -#define g_test_assert_expected_messages() - -#else - -/* We build with -DGLIB_MAX_ALLOWED_VERSION set to 2.32 to make sure we don't - * accidentally use new API that we shouldn't. But we don't want warnings for - * the APIs that we emulate above. - */ - -#define g_test_expect_message(domain, level, format...) \ - G_STMT_START \ - { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_expect_message(domain, level, format); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END - -#define g_test_assert_expected_messages_internal(domain, file, line, func) \ - G_STMT_START \ - { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_test_assert_expected_messages_internal(domain, file, line, func); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END - -#endif - -/*****************************************************************************/ - -#if GLIB_CHECK_VERSION(2, 35, 0) -/* For glib >= 2.36, g_type_init() is deprecated. - * But since 2.35.1 (7c42ab23b55c43ab96d0ac2124b550bf1f49c1ec) this function - * does nothing. Replace the call with empty statement. */ -#define nm_g_type_init() \ - G_STMT_START \ - { \ - (void) 0; \ - } \ - G_STMT_END -#else -#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) \ @@ -189,136 +108,6 @@ nm_glib_check_version(guint major, guint minor, guint micro) /*****************************************************************************/ -/* g_test_skip() is only available since glib 2.38. Add a compatibility wrapper. */ -static inline void -__nmtst_g_test_skip(const char *msg) -{ -#if GLIB_CHECK_VERSION(2, 38, 0) - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - g_test_skip(msg); - G_GNUC_END_IGNORE_DEPRECATIONS -#else - g_debug("%s", msg); -#endif -} -#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 -__g_test_add_data_func_full(const char *testpath, - gpointer test_data, - GTestDataFunc test_func, - GDestroyNotify data_free_func) -{ -#if GLIB_CHECK_VERSION(2, 34, 0) - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - g_test_add_data_func_full(testpath, test_data, test_func, data_free_func); - G_GNUC_END_IGNORE_DEPRECATIONS -#else - g_return_if_fail(testpath != NULL); - g_return_if_fail(testpath[0] == '/'); - g_return_if_fail(test_func != NULL); - - g_test_add_vtable(testpath, - 0, - test_data, - NULL, - (GTestFixtureFunc) test_func, - (GTestFixtureFunc) data_free_func); -#endif -} -#define g_test_add_data_func_full __g_test_add_data_func_full - -/*****************************************************************************/ - -static inline gboolean -nm_g_hash_table_replace(GHashTable *hash, gpointer key, gpointer value) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_replace(hash, key, value); -#else - gboolean contained = g_hash_table_contains(hash, key); - - g_hash_table_replace(hash, key, value); - return !contained; -#endif -} - -static inline gboolean -nm_g_hash_table_insert(GHashTable *hash, gpointer key, gpointer value) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_insert(hash, key, value); -#else - gboolean contained = g_hash_table_contains(hash, key); - - g_hash_table_insert(hash, key, value); - return !contained; -#endif -} - -static inline gboolean -nm_g_hash_table_add(GHashTable *hash, gpointer key) -{ - /* glib 2.40 added a return value indicating whether the key already existed - * (910191597a6c2e5d5d460e9ce9efb4f47d9cc63c). */ -#if GLIB_CHECK_VERSION(2, 40, 0) - return g_hash_table_add(hash, key); -#else - gboolean contained = g_hash_table_contains(hash, key); - - g_hash_table_add(hash, key); - return !contained; -#endif -} - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) || defined(NM_GLIB_COMPAT_H_TEST) -static inline void -_nm_g_ptr_array_insert(GPtrArray *array, int index_, gpointer data) -{ - g_return_if_fail(array); - g_return_if_fail(index_ >= -1); - g_return_if_fail(index_ <= (int) array->len); - - g_ptr_array_add(array, data); - - if (index_ != -1 && index_ != (int) (array->len - 1)) { - memmove(&(array->pdata[index_ + 1]), - &(array->pdata[index_]), - (array->len - index_ - 1) * sizeof(gpointer)); - array->pdata[index_] = data; - } -} -#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 -#define g_ptr_array_insert(array, index, data) \ - G_STMT_START \ - { \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - g_ptr_array_insert(array, index, data); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - } \ - G_STMT_END -#endif - -/*****************************************************************************/ - #if !GLIB_CHECK_VERSION(2, 54, 0) static inline gboolean g_ptr_array_find(GPtrArray *haystack, gconstpointer needle, guint *index_) @@ -346,106 +135,6 @@ g_ptr_array_find(GPtrArray *haystack, gconstpointer needle, guint *index_) /*****************************************************************************/ -#if !GLIB_CHECK_VERSION(2, 40, 0) -static inline gboolean -_g_key_file_save_to_file(GKeyFile *key_file, const char *filename, GError **error) -{ - char *contents; - gboolean success; - gsize length; - - g_return_val_if_fail(key_file != NULL, FALSE); - g_return_val_if_fail(filename != NULL, FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - contents = g_key_file_to_data(key_file, &length, NULL); - g_assert(contents != NULL); - - success = g_file_set_contents(filename, contents, length, error); - g_free(contents); - - return success; -} -#define g_key_file_save_to_file(key_file, filename, error) \ - _g_key_file_save_to_file(key_file, filename, error) -#else -#define g_key_file_save_to_file(key_file, filename, error) \ - ({ \ - gboolean _success; \ - \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ - _success = g_key_file_save_to_file(key_file, filename, error); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - _success; \ - }) -#endif - -/*****************************************************************************/ - -#if GLIB_CHECK_VERSION(2, 36, 0) -#define g_credentials_get_unix_pid(creds, error) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS(g_credentials_get_unix_pid)((creds), (error)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#else -#define g_credentials_get_unix_pid(creds, error) \ - ({ \ - struct ucred *native_creds; \ - \ - native_creds = g_credentials_get_native((creds), G_CREDENTIALS_TYPE_LINUX_UCRED); \ - g_assert(native_creds); \ - native_creds->pid; \ - }) -#endif - -/*****************************************************************************/ - -#if !GLIB_CHECK_VERSION(2, 40, 0) || defined(NM_GLIB_COMPAT_H_TEST) -static inline gpointer * -_nm_g_hash_table_get_keys_as_array(GHashTable *hash_table, guint *length) -{ - GHashTableIter iter; - gpointer key, *ret; - guint i = 0; - - g_return_val_if_fail(hash_table, NULL); - - ret = g_new0(gpointer, g_hash_table_size(hash_table) + 1); - g_hash_table_iter_init(&iter, hash_table); - - while (g_hash_table_iter_next(&iter, &key, NULL)) - ret[i++] = key; - - ret[i] = NULL; - - if (length) - *length = i; - - return ret; -} -#endif -#if !GLIB_CHECK_VERSION(2, 40, 0) -#define g_hash_table_get_keys_as_array(hash_table, length) \ - ({ _nm_g_hash_table_get_keys_as_array(hash_table, length); }) -#else -#define g_hash_table_get_keys_as_array(hash_table, length) \ - ({ \ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS(g_hash_table_get_keys_as_array) \ - ((hash_table), (length)); \ - G_GNUC_END_IGNORE_DEPRECATIONS \ - }) -#endif - -/*****************************************************************************/ - -#ifndef g_info -/* g_info was only added with 2.39.2 */ -#define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__) -#endif - -/*****************************************************************************/ - #ifdef g_steal_pointer #undef g_steal_pointer #endif @@ -486,69 +175,6 @@ _nm_g_strv_contains(const char *const *strv, const char *str) /*****************************************************************************/ -static inline GVariant * -_nm_g_variant_new_take_string(char *string) -{ -#if !GLIB_CHECK_VERSION(2, 36, 0) - GVariant *value; - - g_return_val_if_fail(string != NULL, NULL); - g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL); - - value = g_variant_new_string(string); - g_free(string); - return value; -#elif !GLIB_CHECK_VERSION(2, 38, 0) - GVariant *value; - GBytes *bytes; - - g_return_val_if_fail(string != NULL, NULL); - g_return_val_if_fail(g_utf8_validate(string, -1, NULL), NULL); - - bytes = g_bytes_new_take(string, strlen(string) + 1); - value = g_variant_new_from_bytes(G_VARIANT_TYPE_STRING, bytes, TRUE); - g_bytes_unref(bytes); - - return value; -#else - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - return g_variant_new_take_string(string); - G_GNUC_END_IGNORE_DEPRECATIONS -#endif -} -#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 - -/*****************************************************************************/ - /* Recent glib also casts the results to typeof(Obj), but only if * * ( defined(g_has_typeof) && GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_56 ) @@ -766,4 +392,10 @@ _nm_deprecated("Don't use this API") void _nm_forbidden_glib_api_n(gconstpointer /*****************************************************************************/ +/* Use either NM_MIN()/NM_MAX() or (if that doesn't work) use NM_MIN_CONST()/NM_MAX_CONST(). */ +#undef MIN +#undef MAX + +/*****************************************************************************/ + #endif /* __NM_GLIB_H__ */ diff --git a/src/libnm-glib-aux/nm-hash-utils.h b/src/libnm-glib-aux/nm-hash-utils.h index c1306200..703c00a4 100644 --- a/src/libnm-glib-aux/nm-hash-utils.h +++ b/src/libnm-glib-aux/nm-hash-utils.h @@ -14,6 +14,30 @@ #define NM_HASH_SEED_16(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \ ((const guint8[16]){a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af}) +struct _nm_packed _nm_hash_seed_16_u64_data { + guint64 s1; + guint64 s2; +}; + +G_STATIC_ASSERT(sizeof(struct _nm_hash_seed_16_u64_data) == 16); +G_STATIC_ASSERT(sizeof(struct _nm_hash_seed_16_u64_data) == sizeof(guint64) * 2); + +/* c_siphash_init() has a seed of 16 bytes (NM_HASH_SEED_16()). That is + * cumbersome to use, because we usually just hardcode an arbitrarily chosen, + * fixed number. + * + * This macro takes a u64 (in host-endianness) and returns a 16 byte seed + * buffer. The number will be big endian encoded, to be architecture + * independent. */ +#define NM_HASH_SEED_16_U64(u64) \ + ((const guint8 *) ((gpointer) \ + & ((struct _nm_hash_seed_16_u64_data){ \ + .s1 = htobe64((u64)), \ + .s2 = 0, \ + }))) + +/*****************************************************************************/ + void nm_hash_siphash42_init(CSipHash *h, guint static_seed); /* Siphash24 of binary buffer @arr and @len, using the randomized seed from @@ -22,7 +46,7 @@ void nm_hash_siphash42_init(CSipHash *h, guint static_seed); * Note, that this is guaranteed to use siphash42 under the hood (contrary to * all other NMHash API, which leave this undefined). That matters at the point, * where the caller needs to be sure that a reasonably strong hashing algorithm - * is used. (Yes, NMHash is all about siphash24, but otherwise that is not promised + * is used. (Yes, NMHash is all about siphash42, but otherwise that is not promised * anywhere). * * Another difference is, that this returns guint64 (not guint like other NMHash functions). @@ -32,6 +56,9 @@ void nm_hash_siphash42_init(CSipHash *h, guint static_seed); * Then, why not use c_siphash_hash() directly? Because this also uses the randomized, * per-run hash-seed like nm_hash_init(). So, you get siphash24 with a random * seed (which is cached for the current run of the program). + * + * WARNING: the static_seed gets randomized like with nm_hash*(). If you want a reproducible + * siphash42, use instead `c_siphash_hash(NM_HASH_SEED_16_U64(number), ptr, len)`. */ static inline guint64 nm_hash_siphash42(guint static_seed, const void *ptr, gsize n) diff --git a/src/libnm-glib-aux/nm-macros-internal.h b/src/libnm-glib-aux/nm-macros-internal.h index 9972dc44..a9253ed4 100644 --- a/src/libnm-glib-aux/nm-macros-internal.h +++ b/src/libnm-glib-aux/nm-macros-internal.h @@ -571,10 +571,10 @@ nm_str_realloc(char *str) } #define NM_GOBJECT_PROPERTIES_DEFINE_NOTIFY(suffix, obj_type) \ - static inline void _nm_gobject_notify_together_impl##suffix( \ + static inline void _nm_gobject_notify_together_full_v##suffix( \ obj_type *obj, \ - guint n, \ - const _PropertyEnums##suffix *props) \ + const _PropertyEnums##suffix *props, \ + guint n) \ { \ GObject *const gobj = (GObject *) obj; \ GParamSpec *pspec_first = NULL; \ @@ -614,7 +614,7 @@ nm_str_realloc(char *str) \ _nm_unused static inline void _notify##suffix(obj_type *obj, _PropertyEnums##suffix prop) \ { \ - _nm_gobject_notify_together_impl##suffix(obj, 1, &prop); \ + _nm_gobject_notify_together_full_v##suffix(obj, &prop, 1); \ } \ _NM_DUMMY_STRUCT_FOR_TRAILING_SEMICOLON @@ -631,15 +631,15 @@ nm_str_realloc(char *str) /* 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_full(suffix, obj, ...) \ - G_STMT_START \ - { \ - const _PropertyEnums##suffix _props[] = {__VA_ARGS__}; \ - \ - G_STATIC_ASSERT(G_N_ELEMENTS(_props) == NM_NARG(__VA_ARGS__)); \ - \ - _nm_gobject_notify_together_impl##suffix(obj, G_N_ELEMENTS(_props), _props); \ - } \ +#define nm_gobject_notify_together_full(suffix, obj, ...) \ + G_STMT_START \ + { \ + const _PropertyEnums##suffix _props[] = {__VA_ARGS__}; \ + \ + G_STATIC_ASSERT(G_N_ELEMENTS(_props) == NM_NARG(__VA_ARGS__)); \ + \ + _nm_gobject_notify_together_full_v##suffix(obj, _props, G_N_ELEMENTS(_props)); \ + } \ G_STMT_END #define nm_gobject_notify_together(obj, ...) nm_gobject_notify_together_full(, obj, __VA_ARGS__) diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c index 34a3af20..7d623bd9 100644 --- a/src/libnm-glib-aux/nm-shared-utils.c +++ b/src/libnm-glib-aux/nm-shared-utils.c @@ -623,6 +623,149 @@ nm_g_variant_maybe_singleton_i(gint32 value) /*****************************************************************************/ +static int +_variant_type_cmp(const GVariantType *type1, const GVariantType *type2) +{ + const char *string1; + const char *string2; + gsize size; + + NM_CMP_SELF(type1, type2); + + size = g_variant_type_get_string_length(type1); + + NM_CMP_DIRECT(size, g_variant_type_get_string_length(type2)); + + string1 = g_variant_type_peek_string(type1); + string2 = g_variant_type_peek_string(type2); + + NM_CMP_DIRECT_MEMCMP(string1, string2, size); + return 0; +} + +int +nm_g_variant_type_cmp(const GVariantType *type1, const GVariantType *type2) +{ + int r; + + r = _variant_type_cmp(type1, type2); + nm_assert((!!g_variant_type_equal(type1, type2)) == (r == 0)); + return r; +} + +/*****************************************************************************/ + +typedef enum { + VARIANT_CMP_TYPE_VARIANT, + VARIANT_CMP_TYPE_STRDICT, + VARIANT_CMP_TYPE_VARDICT, +} VariantCmpType; + +static int +_variant_cmp_array(GVariant *value1, GVariant *value2, VariantCmpType type) +{ + gsize len; + gsize i; + + len = g_variant_n_children(value1); + + NM_CMP_DIRECT(len, g_variant_n_children(value2)); + + for (i = 0; i < len; i++) { + gs_unref_variant GVariant *child1 = g_variant_get_child_value(value1, i); + gs_unref_variant GVariant *child2 = g_variant_get_child_value(value2, i); + const char *key1; + const char *key2; + const char *val1_str; + const char *val2_str; + + nm_assert(child1); + nm_assert(child2); + + switch (type) { + case VARIANT_CMP_TYPE_VARIANT: + NM_CMP_RETURN(nm_g_variant_cmp(child1, child2)); + break; + case VARIANT_CMP_TYPE_STRDICT: + g_variant_get(child1, "{&s&s}", &key1, &val1_str); + g_variant_get(child2, "{&s&s}", &key2, &val2_str); + NM_CMP_DIRECT_STRCMP(key1, key2); + NM_CMP_DIRECT_STRCMP(val1_str, val2_str); + break; + case VARIANT_CMP_TYPE_VARDICT: + { + gs_unref_variant GVariant *val1_var = NULL; + gs_unref_variant GVariant *val2_var = NULL; + + g_variant_get(child1, "{&sv}", &key1, &val1_var); + g_variant_get(child2, "{&sv}", &key2, &val2_var); + NM_CMP_DIRECT_STRCMP(key1, key2); + NM_CMP_RETURN(nm_g_variant_cmp(val1_var, val2_var)); + break; + } + } + } + + return 0; +} + +static int +_variant_cmp_generic(GVariant *value1, GVariant *value2) +{ + gs_free char *str1 = NULL; + gs_free char *str2 = NULL; + + /* This is like g_variant_equal(), which also resorts to pretty-printing + * the variants for comparison. + * + * Note that the variant types are already checked and equal. We thus don't + * need to include the type annotation. */ + str1 = g_variant_print(value1, FALSE); + str2 = g_variant_print(value2, FALSE); + + NM_CMP_DIRECT_STRCMP(str1, str2); + return 0; +} + +static int +_variant_cmp(GVariant *value1, GVariant *value2) +{ + const GVariantType *type; + + NM_CMP_SELF(value1, value2); + + type = g_variant_get_type(value1); + + NM_CMP_RETURN(nm_g_variant_type_cmp(type, g_variant_get_type(value2))); + + if (g_variant_type_is_basic(type)) + NM_CMP_RETURN(g_variant_compare(value1, value2)); + else if (g_variant_type_is_subtype_of(type, G_VARIANT_TYPE("a{ss}"))) + NM_CMP_RETURN(_variant_cmp_array(value1, value2, VARIANT_CMP_TYPE_STRDICT)); + else if (g_variant_type_is_subtype_of(type, G_VARIANT_TYPE("a{sv}"))) + NM_CMP_RETURN(_variant_cmp_array(value1, value2, VARIANT_CMP_TYPE_VARDICT)); + else if (g_variant_type_is_array(type) || g_variant_type_is_tuple(type)) + NM_CMP_RETURN(_variant_cmp_array(value1, value2, VARIANT_CMP_TYPE_VARIANT)); + else + NM_CMP_RETURN(_variant_cmp_generic(value1, value2)); + + return 0; +} + +int +nm_g_variant_cmp(GVariant *value1, GVariant *value2) +{ + int r; + + r = _variant_cmp(value1, value2); + + nm_assert((!!nm_g_variant_equal(value1, value2)) == (r == 0)); + + return r; +} + +/*****************************************************************************/ + GHashTable * nm_strdict_clone(GHashTable *src) { @@ -1937,7 +2080,7 @@ nm_utils_strsplit_quoted(const char *str) } if (!arr) - return g_new0(char *, 1); + return nm_strv_empty_new(); /* We want to return an optimally sized strv array, with no excess * memory allocated. Hence, clone once more. */ @@ -1964,8 +2107,6 @@ nm_utils_strsplit_quoted(const char *str) * Searches @list for @needle and returns the index of the first match (based * on strcmp()). * - * For convenience, @list has type 'char**' instead of 'const char **'. - * * Returns: index of first occurrence or -1 if @needle is not found in @list. */ gssize @@ -1984,7 +2125,7 @@ _nm_strv_find_first(const char *const *list, gssize len, const char *needle) } } else { for (i = 0; i < len; i++) { - if (list[i] && !strcmp(needle, list[i])) + if (list[i] && nm_streq(needle, list[i])) return i; } } @@ -1993,7 +2134,7 @@ _nm_strv_find_first(const char *const *list, gssize len, const char *needle) if (list) { for (i = 0; list[i]; i++) { - if (strcmp(needle, list[i]) == 0) + if (nm_streq(needle, list[i])) return i; } } @@ -2078,7 +2219,7 @@ nm_strv_is_same_unordered(const char *const *strv1, } const char ** -nm_strv_cleanup_const(const char **strv, gboolean skip_empty, gboolean skip_repeated) +nm_strv_cleanup_const(const char **strv, gboolean no_empty, gboolean no_duplicates) { gsize i; gsize j; @@ -2086,13 +2227,12 @@ nm_strv_cleanup_const(const char **strv, gboolean skip_empty, gboolean skip_repe if (!strv || !*strv) return strv; - if (!skip_empty && !skip_repeated) + if (!no_empty && !no_duplicates) return strv; j = 0; for (i = 0; strv[i]; i++) { - if ((skip_empty && !*strv[i]) - || (skip_repeated && nm_strv_find_first(strv, j, strv[i]) >= 0)) + if ((no_empty && !*strv[i]) || (no_duplicates && nm_strv_contains(strv, j, strv[i]))) continue; strv[j++] = strv[i]; } @@ -2101,7 +2241,7 @@ nm_strv_cleanup_const(const char **strv, gboolean skip_empty, gboolean skip_repe } char ** -nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean skip_empty, gboolean skip_repeated) +nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean no_empty, gboolean no_duplicates) { gsize i; gsize j; @@ -2115,12 +2255,11 @@ nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean skip_empty, gbo for (i = 0; strv[i]; i++) g_strstrip(strv[i]); } - if (!skip_empty && !skip_repeated) + if (!no_empty && !no_duplicates) return strv; j = 0; for (i = 0; strv[i]; i++) { - if ((skip_empty && !*strv[i]) - || (skip_repeated && nm_strv_find_first(strv, j, strv[i]) >= 0)) + if ((no_empty && !*strv[i]) || (no_duplicates && nm_strv_contains(strv, j, strv[i]))) g_free(strv[i]); else strv[j++] = strv[i]; @@ -2221,6 +2360,44 @@ nm_utils_error_is_notfound(GError *error) /*****************************************************************************/ +void +nm_gobject_notify_together_by_pspec_v(gpointer obj, + const GParamSpec *const *param_specs, + gsize param_specs_len) +{ + GObject *const gobj = obj; + gboolean frozen = FALSE; + const GParamSpec *pspec_first = NULL; + + nm_assert(G_IS_OBJECT(gobj)); + nm_assert(param_specs_len > 0); + + while (param_specs_len-- > 0) { + const GParamSpec *pspec = (param_specs++)[0]; + + if (!pspec) + continue; + + if (!frozen) { + if (!pspec_first) { + pspec_first = pspec; + continue; + } + frozen = TRUE; + g_object_freeze_notify(gobj); + g_object_notify_by_pspec(gobj, (GParamSpec *) pspec_first); + } + g_object_notify_by_pspec(gobj, (GParamSpec *) pspec); + } + + if (frozen) + g_object_thaw_notify(gobj); + else if (pspec_first) + g_object_notify_by_pspec(gobj, (GParamSpec *) pspec_first); +} + +/*****************************************************************************/ + /** * nm_g_object_set_property: * @object: the target object @@ -3584,6 +3761,9 @@ nm_strv_make_deep_copied_n(const char **strv, gsize len) * the returned array must be freed with g_strfreev(). Otherwise, the * strings themself are not copied. You must take care of who owns the * strings yourself. + * @preserved_empty: affects how to handle if the strv array is empty (length 0). + * If TRUE, results in a non-NULL, empty, allocated strv array. If FALSE, + * returns NULL instead of an empty strv array. * * Like g_strdupv(), with two differences: * @@ -3602,7 +3782,10 @@ nm_strv_make_deep_copied_n(const char **strv, gsize len) * cloned or not. */ char ** -_nm_strv_dup(const char *const *strv, gssize len, gboolean deep_copied) +_nm_strv_dup_full(const char *const *strv, + gssize len, + gboolean deep_copied, + gboolean preserve_empty) { gsize i, l; char **v; @@ -3611,13 +3794,16 @@ _nm_strv_dup(const char *const *strv, gssize len, gboolean deep_copied) l = NM_PTRARRAY_LEN(strv); else l = len; - if (l == 0) { - /* this function never returns an empty strv array. If you - * need that, handle it yourself. */ + + if (l == 0 && !preserve_empty) { + /* An empty strv array is not returned (as requested by + * !preserved_empty). Instead, return NULL. */ return NULL; } - v = g_new(char *, l + 1); + nm_assert(l < G_MAXSIZE); + + v = g_new(char *, l + 1u); for (i = 0; i < l; i++) { if (G_UNLIKELY(!strv[i])) { /* NULL strings are not allowed. Clear the remainder of the array @@ -5775,40 +5961,81 @@ nm_utils_is_specific_hostname(const char *name) /*****************************************************************************/ -/* taken from systemd's uid_to_name(). */ -char * -nm_utils_uid_to_name(uid_t uid) +typedef struct { + struct passwd pw; + _nm_alignas(max_align_t) char buf[]; +} GetPwuidData; + +/** + * nm_getpwuid: + * uid: the user Id to look up + * + * Calls getpwuid_r() to lookup the passwd entry. See the manual. + * Allocates and returns a suitable buffer. + * + * The returned buffer is likely much large than required. You don't want to + * keep this buffer around for longer than necessary. + * + * Returns: (transfer full): the passwd entry, if found or NULL on error + * or if the entry was not found. + */ +struct passwd * +nm_getpwuid(uid_t uid) { - gs_free char *buf_heap = NULL; - char buf_stack[4096]; - gsize bufsize; - char *buf; + gs_free GetPwuidData *data = NULL; + gsize bufsize; + const gsize OFFSET = G_STRUCT_OFFSET(GetPwuidData, buf); + long int size_max; - bufsize = sizeof(buf_stack); - buf = buf_stack; + size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size_max > 0 && ((unsigned long int) size_max < G_MAXSIZE - OFFSET)) + bufsize = size_max; + else + bufsize = 4096; for (;;) { - struct passwd pwbuf; struct passwd *pw = NULL; int r; - r = getpwuid_r(uid, &pwbuf, buf, bufsize, &pw); - if (r == 0 && pw) - return nm_strdup_not_empty(pw->pw_name); + if (bufsize >= G_MAXSIZE - OFFSET) + return NULL; + + nm_clear_g_free(&data); + data = g_malloc(OFFSET + bufsize); + + r = getpwuid_r(uid, &data->pw, data->buf, bufsize, &pw); + if (r == 0) { + if (!pw) + return NULL; + nm_assert(pw == (gpointer) data); + return (gpointer) g_steal_pointer(&data); + } if (r != ERANGE) return NULL; if (bufsize > G_MAXSIZE / 2u) return NULL; - bufsize *= 2u; - g_free(buf_heap); - buf_heap = g_malloc(bufsize); - buf = buf_heap; } } +const char * +nm_passwd_name(const struct passwd *pw) +{ + /* Normalize "pw->pw_name" and return it. */ + return pw ? nm_str_not_empty(pw->pw_name) : NULL; +} + +char * +nm_utils_uid_to_name(uid_t uid) +{ + gs_free struct passwd *pw = NULL; + + pw = nm_getpwuid(uid); + return g_strdup(nm_passwd_name(pw)); +} + /* taken from systemd's nss_user_record_by_name() */ gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid) @@ -5886,6 +6113,9 @@ nm_utils_exp10(gint16 ex) gboolean _nm_utils_is_empty_ssid_arr(const guint8 *ssid, gsize len) { + if (len == 0) + return TRUE; + /* Single white space is for Linksys APs */ if (len == 1 && ssid[0] == ' ') return TRUE; @@ -6925,10 +7155,10 @@ _poll_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) else wait_ms = 0; if (poll_task_data->sleep_timeout_ms > 0) - wait_ms = MAX(wait_ms, poll_task_data->sleep_timeout_ms); + wait_ms = NM_MAX(wait_ms, poll_task_data->sleep_timeout_ms); poll_task_data->source_next_poll = - nm_g_source_attach(nm_g_timeout_source_new(MAX(1, wait_ms), + nm_g_source_attach(nm_g_timeout_source_new(NM_MAX(1, wait_ms), G_PRIORITY_DEFAULT, _poll_start_cb, poll_task_data, diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h index e37cf5e6..ea38e083 100644 --- a/src/libnm-glib-aux/nm-shared-utils.h +++ b/src/libnm-glib-aux/nm-shared-utils.h @@ -97,6 +97,7 @@ typedef enum _nm_packed { /* No type, empty value */ NM_PORT_KIND_NONE, NM_PORT_KIND_BOND, + NM_PORT_KIND_BRIDGE, } NMPortKind; /*****************************************************************************/ @@ -157,6 +158,7 @@ typedef enum { #define _NM_LINK_TYPE_SW_MASTER_FIRST NM_LINK_TYPE_BRIDGE NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND, + NM_LINK_TYPE_HSR, NM_LINK_TYPE_TEAM, #define _NM_LINK_TYPE_SW_MASTER_LAST NM_LINK_TYPE_TEAM @@ -331,9 +333,10 @@ gboolean nm_utils_memeqzero(gconstpointer data, gsize length); extern const void *const _NM_PTRARRAY_EMPTY[1]; -#define NM_PTRARRAY_EMPTY(type) ((type const *) _NM_PTRARRAY_EMPTY) -#define NM_STRV_EMPTY() ((char **) _NM_PTRARRAY_EMPTY) -#define NM_STRV_EMPTY_CC() NM_PTRARRAY_EMPTY(const char *) +#define NM_PTRARRAY_EMPTY(type) ((type const *) _NM_PTRARRAY_EMPTY) +#define NM_STRV_EMPTY() ((char **) _NM_PTRARRAY_EMPTY) +#define NM_STRV_EMPTY_CC() NM_PTRARRAY_EMPTY(const char *) +#define NM_PTRARRAY_EMPTY_NEW(type) (g_new0(type, 1)) static inline void nm_strbuf_init(char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len) @@ -528,12 +531,10 @@ gssize _nm_strv_find_first(const char *const *list, gssize len, const char *need gboolean nm_strv_has_duplicate(const char *const *list, gssize len, gboolean is_sorted); -const char **nm_strv_cleanup_const(const char **strv, gboolean skip_empty, gboolean skip_repeated); +const char **nm_strv_cleanup_const(const char **strv, gboolean no_empty, gboolean no_duplicates); -char **nm_strv_cleanup(char **strv, - gboolean strip_whitespace, - gboolean skip_empty, - gboolean skip_repeated); +char ** +nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean no_empty, gboolean no_duplicates); gboolean nm_strv_is_same_unordered(const char *const *strv1, gssize len1, @@ -1114,6 +1115,21 @@ nm_utils_error_set_literal(GError **error, int error_code, const char *literal) /*****************************************************************************/ +void nm_gobject_notify_together_by_pspec_v(gpointer obj, + const GParamSpec *const *param_specs, + gsize param_specs_len); + +#define nm_gobject_notify_together_by_pspec(obj, ...) \ + G_STMT_START \ + { \ + const GParamSpec *const _arr[] = {__VA_ARGS__}; \ + \ + G_STATIC_ASSERT(NM_NARG(__VA_ARGS__) == G_N_ELEMENTS(_arr)); \ + \ + nm_gobject_notify_together_by_pspec_v((obj), _arr, G_N_ELEMENTS(_arr)); \ + } \ + G_STMT_END + gboolean nm_g_object_set_property(GObject *object, const char *property_name, const GValue *value, @@ -1402,6 +1418,10 @@ nm_g_variant_builder_add_sv_str(GVariantBuilder *builder, const char *key, const nm_g_variant_builder_add_sv(builder, key, g_variant_new_string(str)); } +int nm_g_variant_type_cmp(const GVariantType *type1, const GVariantType *type2); + +int nm_g_variant_cmp(GVariant *value1, GVariant *value2); + static inline void nm_g_source_destroy_and_unref(GSource *source) { @@ -1851,6 +1871,8 @@ int nm_utils_hashtable_cmp(const GHashTable *a, GCompareDataFunc cmp_values, gpointer user_data); +#define nm_strv_empty_new() NM_PTRARRAY_EMPTY_NEW(char *) + char **nm_strv_make_deep_copied(const char **strv); char **nm_strv_make_deep_copied_n(const char **strv, gsize len); @@ -1858,13 +1880,18 @@ char **nm_strv_make_deep_copied_n(const char **strv, gsize len); static inline char ** nm_strv_make_deep_copied_nonnull(const char **strv) { - return nm_strv_make_deep_copied(strv) ?: g_new0(char *, 1); + return nm_strv_make_deep_copied(strv) ?: nm_strv_empty_new(); } -char **_nm_strv_dup(const char *const *strv, gssize len, gboolean deep_copied); +char **_nm_strv_dup_full(const char *const *strv, + gssize len, + gboolean deep_copied, + gboolean preserve_empty); -#define nm_strv_dup(strv, len, deep_copied) \ - _nm_strv_dup(NM_CAST_STRV_CC(strv), (len), (deep_copied)) +#define nm_strv_dup_full(strv, len, deep_copied, preserve_empty) \ + _nm_strv_dup_full(NM_CAST_STRV_CC(strv), (len), (deep_copied), (preserve_empty)) + +#define nm_strv_dup(strv, len, deep_copied) nm_strv_dup_full((strv), (len), (deep_copied), FALSE) const char **_nm_strv_dup_packed(const char *const *strv, gssize len); @@ -1942,7 +1969,27 @@ nm_g_array_unref(GArray *arr) * When accessing index zero, then this returns NULL if-and-only-if * "arr" is NULL or "arr->data" is NULL. In all other cases, this * returns the pointer &((Type*) arr->data)[idx]. Note that the pointer - * may not be followed, if "idx" is equal to "arr->len". */ + * may not be followed, if "idx" is equal to "arr->len". + * + * The reason to allow access one element past the length is the + * following usage: + * + * ptr = nm_g_array_index_p(arr, Type, 0); + * end = nm_g_array_index_p(arr, Type, length); + * for (; ptr < end; ptr++) { ... } + * + * Another usage is to get a buffer, if the length might be zero. If + * length is zero, you cannot dereference the pointer, but it can be convenient + * to not require special casing: + * + * // length might be zero. + * nm_memdup(nm_g_array_index_p(arr, Type, length), sizeof(Type) * length); + * + * Note that in C, it's valid point one past the end of an array. So getting + * a pointer at index "length" is valid, and what nm_g_array_index_p() allows. + * If you don't need that, nm_g_array_index() is usually preferable, + * because it asserts against access at index "length". + */ #define nm_g_array_index_p(arr, Type, idx) \ ({ \ const GArray *const _arr_55 = (arr); \ @@ -2509,6 +2556,21 @@ nm_strv_ptrarray_get_unsafe(GPtrArray *arr, guint *out_len) return (const char *const *) arr->pdata; } +static inline char ** +nm_strv_ptrarray_to_strv_full(const GPtrArray *a, gboolean not_null) +{ + if (!a) + return not_null ? nm_strv_empty_new() : NULL; + return nm_strv_dup_full((const char *const *) a->pdata, a->len, TRUE, TRUE); +} + +static inline char ** +nm_strv_ptrarray_to_strv(const GPtrArray *a) +{ + /* Returns never NULL ("not_null"!) */ + return nm_strv_ptrarray_to_strv_full(a, TRUE); +} + static inline GPtrArray * nm_strv_ptrarray_clone(const GPtrArray *src, gboolean null_if_empty) { @@ -2977,100 +3039,248 @@ nm_strvarray_ensure(GArray **p) *p = g_array_new(TRUE, FALSE, sizeof(char *)); g_array_set_clear_func(*p, nm_indirect_g_free); } else - nm_assert(g_array_get_element_size(*p) == sizeof(char *)); + nm_assert(sizeof(char *) == g_array_get_element_size(*p)); return *p; } static inline void -nm_strvarray_add(GArray *array, const char *str) +nm_strvarray_add_take(GArray *array, char *str) { - char *s; - nm_assert(array); - nm_assert(g_array_get_element_size(array) == sizeof(char *)); + nm_assert(sizeof(char *) == g_array_get_element_size(array)); - s = g_strdup(str); - g_array_append_val(array, s); + /* The array is used as a NULL terminated strv array. Adding NULL is most + * likely a bug. Assert against it. */ + nm_assert(str); + + g_array_append_val(array, str); +} + +static inline void +nm_strvarray_add(GArray *array, const char *str) +{ + nm_strvarray_add_take(array, g_strdup(str)); } static inline const char * -nm_strvarray_get_idx(GArray *array, guint idx) +nm_strvarray_get_idx(const GArray *array, guint idx) { return nm_g_array_index(array, const char *, idx); } +/* nm_strvarray_get_idxnull_or_greturn() permits access at `len`, + * returning NULL. If the access is out of bounds, the assertion + * will fail (and also return NULL). */ +#define nm_strvarray_get_idxnull_or_greturn(arr, idx) \ + ({ \ + GArray *_arr = (arr); \ + gsize _idx = (idx); \ + guint _len = nm_g_array_len(_arr); \ + \ + g_return_val_if_fail(_idx <= _len, NULL); \ + \ + _idx == _len ? NULL : nm_strvarray_get_idx(_arr, _idx); \ + }) + +/** + * nm_strvarray_get_strv_full: + * @arr: the strvarray. + * @length: (out) (nullable): optionally return the length of the result. + * @not_null: if true and @arr is NULL, return NM_STRV_EMPTY_CC() (otherwise NULL). + * @preserve_empty: if true and the array is empty, return an empty + * strv array. Otherwise, return NULL. + * + * If "arr" is NULL, this returns NULL, unless "not_null" is true (in which + * case the static NM_STRV_EMPTY_CC() is returned). + * If "arr" is empty, it depends on: + * - if "preserve_empty" or "not_null", then the resulting strv array is the empty "arr". + * - otherwise NULL is returned. + * Otherwise, returns the non-empty, non-deep-cloned strv array. + * + * Like nm_strvarray_get_strv_full_dup(), but the strings are not cloned. + * + * Returns: (transfer none): a strv list or NULL. + */ static inline const char *const * -nm_strvarray_get_strv_non_empty(GArray *arr, guint *length) +nm_strvarray_get_strv_full(const GArray *arr, + guint *length, + gboolean not_null, + gboolean preserve_empty) { - nm_assert(!arr || g_array_get_element_size(arr) == sizeof(char *)); - - if (!arr || arr->len == 0) { + if (!arr) { NM_SET_OUT(length, 0); - return NULL; + return not_null ? NM_STRV_EMPTY_CC() : NULL; } + nm_assert(sizeof(char *) == g_array_get_element_size((GArray *) arr)); + NM_SET_OUT(length, arr->len); + + if (arr->len == 0 && !(preserve_empty || not_null)) + return NULL; + return &g_array_index(arr, const char *, 0); } +/** + * nm_strvarray_get_strv_full_dup: + * @arr: the strvarray. + * @length: (out) (nullable): optionally return the length of the result. + * @not_null: if true, never return NULL but allocate an empty strv array. + * @preserve_empty: if true and the array is empty, return an empty + * strv array. Otherwise, return NULL. + * + * If "arr" is NULL, this returns NULL, unless "not_null" is true (in which case + * am empty strv array is allocated. + * If "arr" is empty, it depends on: + * - if "preserve_empty" || "not_null", then the resulting strv array is allocated (and empty). + * - otherwise, NULL is returned. + * Otherwise, return the non-empty, deep-cloned strv array. + * + * Like nm_strvarray_get_strv_full(), but the strings are cloned. + * + * Returns: (transfer full): a deep-cloned strv list or NULL. + */ static inline char ** -nm_strvarray_get_strv_non_empty_dup(GArray *arr, guint *length) +nm_strvarray_get_strv_full_dup(const GArray *arr, + guint *length, + gboolean not_null, + gboolean preserve_empty) { - const char *const *strv; + if (!arr) { + NM_SET_OUT(length, 0); + return not_null ? nm_strv_empty_new() : NULL; + } - nm_assert(!arr || g_array_get_element_size(arr) == sizeof(char *)); + nm_assert(sizeof(char *) == g_array_get_element_size((GArray *) arr)); - if (!arr || arr->len == 0) { - NM_SET_OUT(length, 0); + NM_SET_OUT(length, arr->len); + + if (arr->len == 0) { + if (preserve_empty || not_null) + return nm_strv_empty_new(); return NULL; } - NM_SET_OUT(length, arr->len); - strv = &g_array_index(arr, const char *, 0); - return nm_strv_dup(strv, arr->len, TRUE); + return nm_strv_dup(&g_array_index(arr, const char *, 0), arr->len, TRUE); } +/** + * nm_strvarray_get_strv_notnull: + * @arr: the strvarray. + * @length: (out) (nullable): optionally return the length of the result. + * + * This never returns NULL. If @arr is NULL, this returns NM_STRV_EMPTY_CC(). + * + * Like nm_strvarray_get_strv_notempty(), but never returns NULL. + * + * Returns: (transfer none): a pointer to the strv list in @arr or NM_STRV_EMPTY_CC(). + */ static inline const char *const * -nm_strvarray_get_strv(GArray **arr, guint *length) +nm_strvarray_get_strv_notnull(const GArray *arr, guint *length) { - if (!*arr) { - NM_SET_OUT(length, 0); - return (const char *const *) arr; - } + return nm_strvarray_get_strv_full(arr, length, TRUE, TRUE); +} - nm_assert(g_array_get_element_size(*arr) == sizeof(char *)); +/** + * nm_strvarray_get_strv_notempty: + * @arr: the strvarray. + * @length: (out) (nullable): optionally return the length of the result. + * + * This never returns an empty strv array. If @arr is NULL or empty, this + * returns NULL. + * + * Like nm_strvarray_get_strv_notempty_dup(), but does not clone strings. + * + * Returns: (transfer none): a pointer to the strv list in @arr or NULL. + */ +static inline const char *const * +nm_strvarray_get_strv_notempty(const GArray *arr, guint *length) +{ + return nm_strvarray_get_strv_full(arr, length, FALSE, FALSE); +} - NM_SET_OUT(length, (*arr)->len); - return &g_array_index(*arr, const char *, 0); +/** + * nm_strvarray_get_strv_notempty_dup: + * @arr: the strvarray. + * @length: (out) (nullable): optionally return the length of the result. + * + * This never returns an empty strv array. If @arr is NULL or empty, this + * returns NULL. + * + * Like nm_strvarray_get_strv_notempty(), but clones strings. + * + * Returns: (transfer full): a deep-cloned strv list or NULL. + */ +static inline char ** +nm_strvarray_get_strv_notempty_dup(const GArray *arr, guint *length) +{ + return nm_strvarray_get_strv_full_dup(arr, length, FALSE, FALSE); } +/** + * nm_strvarray_set_strv_full: + * @array: a pointer to the array to set. + * @strv: the strv array. May be NULL. + * @preserve_empty: how to treat if strv is empty (strv[0]==NULL). + * + * The old array will be freed (in a way so that the function is self-assignment + * safe). + * + * If "strv" is NULL, then the resulting GArray is NULL. + * If "strv" is empty, then it depends on "preserve_empty": + * - if "preserve_empty", then the resulting GArray is allocated (and empty). + * - if "!preserve_empty", then the resulting GArray is NULL. + * If "strv" is not empty, a GArray gets allocated and the strv array deep-cloned. + */ static inline void -nm_strvarray_set_strv(GArray **array, const char *const *strv) +nm_strvarray_set_strv_full(GArray **array, const char *const *strv, gboolean preserve_empty) { gs_unref_array GArray *array_old = NULL; array_old = g_steal_pointer(array); - nm_assert(!array_old || g_array_get_element_size(array_old) == sizeof(char *)); + nm_assert(!array_old || sizeof(char *) == g_array_get_element_size(array_old)); + + if (!strv) + return; - if (!strv || !strv[0]) + if (!strv[0] && !preserve_empty) { + /* An empty strv array is treated like NULL. Don't allocate a GArray. */ return; + } nm_strvarray_ensure(array); for (; strv[0]; strv++) nm_strvarray_add(*array, strv[0]); } +/** + * nm_strvarray_set_strv: + * @array: a pointer to the array to set. + * @strv: the strv array. May be NULL. + * + * The old array will be freed (in a way so that the function is self-assignment + * safe). + * + * Note that this will never initialize an empty GArray. If strv is NULL or + * empty, the @array pointer will be set to NULL. */ +static inline void +nm_strvarray_set_strv(GArray **array, const char *const *strv) +{ + nm_strvarray_set_strv_full(array, strv, FALSE); +} + static inline gssize -nm_strvarray_find_first(GArray *strv, const char *needle) +nm_strvarray_find_first(const GArray *strv, const char *needle) { guint i; nm_assert(needle); if (strv) { - nm_assert(g_array_get_element_size(strv) == sizeof(char *)); + nm_assert(sizeof(char *) == g_array_get_element_size((GArray *) strv)); for (i = 0; i < strv->len; i++) { if (nm_streq(needle, g_array_index(strv, const char *, i))) return i; @@ -3079,6 +3289,8 @@ nm_strvarray_find_first(GArray *strv, const char *needle) return -1; } +#define nm_strvarray_contains(strv, needle) (nm_strvarray_find_first((strv), (needle)) >= 0) + static inline gboolean nm_strvarray_remove_first(GArray *strv, const char *needle) { @@ -3093,11 +3305,42 @@ nm_strvarray_remove_first(GArray *strv, const char *needle) return TRUE; } +#define nm_strvarray_remove_index(strv, idx) \ + G_STMT_START \ + { \ + GArray *const _strv = (strv); \ + typeof(idx) _idx = (idx); \ + \ + nm_assert(_strv); \ + nm_assert((uintmax_t) _idx < _strv->len); \ + nm_assert(sizeof(char *) == g_array_get_element_size(_strv)); \ + \ + g_array_remove_index(_strv, (guint) _idx); \ + } \ + G_STMT_END + +static inline void +nm_strvarray_ensure_and_add(GArray **p, const char *str) +{ + nm_strvarray_add(nm_strvarray_ensure(p), str); +} + +static inline gboolean +nm_strvarray_ensure_and_add_unique(GArray **p, const char *str) +{ + nm_assert(p); + + if (nm_strvarray_contains(*p, str)) + return FALSE; + nm_strvarray_add(nm_strvarray_ensure(p), str); + return TRUE; +} + static inline int nm_strvarray_cmp(const GArray *a, const GArray *b) { - nm_assert(!a || sizeof(const char *const *) == g_array_get_element_size((GArray *) a)); - nm_assert(!b || sizeof(const char *const *) == g_array_get_element_size((GArray *) b)); + nm_assert(!a || sizeof(char *) == g_array_get_element_size((GArray *) a)); + nm_assert(!b || sizeof(char *) == g_array_get_element_size((GArray *) b)); NM_CMP_SELF(a, b); @@ -3109,7 +3352,7 @@ nm_strvarray_cmp(const GArray *a, const GArray *b) static inline int _nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len) { - nm_assert(!strv || sizeof(const char *const *) == g_array_get_element_size((GArray *) strv)); + nm_assert(!strv || sizeof(char *) == g_array_get_element_size((GArray *) strv)); return nm_strv_cmp_n(nm_g_array_data(strv), strv ? ((gssize) strv->len) : -1, ss, ss_len); } @@ -3119,6 +3362,24 @@ _nm_strvarray_cmp_strv(const GArray *strv, const char *const *ss, gsize ss_len) #define nm_strvarray_equal_strv(strv, ss, ss_len) \ (nm_strvarray_cmp_strv((strv), (ss), (ss_len)) == 0) +static inline gboolean +nm_strvarray_clear(GArray **array) +{ + gboolean cleared = FALSE; + + nm_assert(array); + nm_assert(!*array || sizeof(char *) == g_array_get_element_size(*array)); + + if (*array) { + /* We always clear the GArray, but we return TRUE only if the + * array was non-empty before. */ + if ((*array)->len > 0) + cleared = TRUE; + nm_clear_pointer(array, g_array_unref); + } + return cleared; +} + /*****************************************************************************/ struct _NMVariantAttributeSpec { @@ -3163,6 +3424,12 @@ gboolean nm_utils_is_localhost(const char *name); gboolean nm_utils_is_specific_hostname(const char *name); +struct passwd; + +struct passwd *nm_getpwuid(uid_t uid); + +const char *nm_passwd_name(const struct passwd *pw); + char *nm_utils_uid_to_name(uid_t uid); gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid); diff --git a/src/libnm-glib-aux/nm-test-utils.h b/src/libnm-glib-aux/nm-test-utils.h index de6fd0f1..2a6a5d3a 100644 --- a/src/libnm-glib-aux/nm-test-utils.h +++ b/src/libnm-glib-aux/nm-test-utils.h @@ -134,7 +134,7 @@ ({ \ gboolean _not_expired = TRUE; \ const gint64 nmtst_wait_start_us = g_get_monotonic_time(); \ - const gint64 nmtst_wait_duration_us = (max_wait_ms) *1000L; \ + const gint64 nmtst_wait_duration_us = (max_wait_ms) * 1000L; \ const gint64 nmtst_wait_end_us = nmtst_wait_start_us + nmtst_wait_duration_us; \ gint64 _nmtst_wait_remaining_us = nmtst_wait_duration_us; \ int _nmtst_wait_iteration = 0; \ @@ -497,8 +497,6 @@ __nmtst_init(int *argc, __nmtst_internal.assert_logging = !!assert_logging; - nm_g_type_init(); - is_debug = g_test_verbose(); nmtst_debug = g_getenv("NMTST_DEBUG"); @@ -1228,7 +1226,7 @@ nmtst_rand_perm_strv(const char *const *strv) /* this returns a (scrambled) SHALLOW copy of the strv array! */ n = NM_PTRARRAY_LEN(strv); - res = (const char **) (nm_strv_dup(strv, n, FALSE) ?: g_new0(char *, 1)); + res = (const char **) (nm_strv_dup(strv, n, FALSE) ?: nm_strv_empty_new()); nmtst_rand_perm(NULL, res, res, sizeof(char *), n); return res; } @@ -1803,7 +1801,7 @@ nmtst_inet_from_string(int addr_family, const char *str) static inline const char * nmtst_inet_to_string(int addr_family, gconstpointer addr) { - static _nm_thread_local char buf[NM_CONST_MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; + static _nm_thread_local char buf[NM_MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); g_assert(addr); @@ -2568,20 +2566,21 @@ nmtst_assert_connection_unnormalizable(NMConnection *con, g_clear_error(&error); } -static inline void -nmtst_assert_setting_verifies(NMSetting *setting) -{ - /* assert that the setting verifies without an error */ - - GError *error = NULL; - gboolean success; - - g_assert(NM_IS_SETTING(setting)); - - success = nm_setting_verify(setting, NULL, &error); - g_assert_no_error(error); - g_assert(success); -} +#define nmtst_assert_setting_verifies(setting) \ + G_STMT_START \ + { \ + NMSetting *_setting = NM_SETTING(setting); \ + GError *_error = NULL; \ + gboolean _success; \ + \ + /* assert that the setting verifies without an error */ \ + \ + g_assert(NM_IS_SETTING(_setting)); \ + \ + _success = nm_setting_verify(_setting, NULL, &_error); \ + nmtst_assert_success(_success, _error); \ + } \ + G_STMT_END #if defined(__NM_SIMPLE_CONNECTION_H__) && NM_CHECK_VERSION(1, 10, 0) \ && (!defined(NM_VERSION_MAX_ALLOWED) || NM_VERSION_MAX_ALLOWED >= NM_VERSION_1_10) @@ -2605,7 +2604,7 @@ _nmtst_assert_connection_has_settings(NMConnection *connection, va_start(ap, has_at_most); while ((name = va_arg(ap, const char *))) { - if (!nm_g_hash_table_add(names, (gpointer) name)) + if (!g_hash_table_add(names, (gpointer) name)) g_assert_not_reached(); g_ptr_array_add(names_arr, (gpointer) name); } diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 84d87d48..b19ac1ce 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -5,6 +5,8 @@ #include "libnm-glib-aux/nm-default-glib-i18n-prog.h" +#include <pwd.h> + #include "libnm-std-aux/unaligned.h" #include "libnm-glib-aux/nm-random-utils.h" #include "libnm-glib-aux/nm-str-buf.h" @@ -32,6 +34,13 @@ G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) <= _nm_alignof(NMIPAddr)); /*****************************************************************************/ +G_STATIC_ASSERT(_NM_INT_IS_SIGNED(1)); +G_STATIC_ASSERT(!_NM_INT_IS_SIGNED(1u)); +G_STATIC_ASSERT(_NM_INT_SAME_SIGNEDNESS((short) 1, 1l)); +G_STATIC_ASSERT(!_NM_INT_SAME_SIGNEDNESS((unsigned short) 1, 1l)); + +/*****************************************************************************/ + static void test_nm_static_assert(void) { @@ -58,6 +67,20 @@ test_nm_static_assert(void) /*****************************************************************************/ static void +test_max(void) +{ + /* Check that NM_MAX() of constant expressions is itself a constant. We + * build with -Wvla, so this is a constant! */ + char buf1[NM_MAX(55, 40)]; + char buf2[NM_MAX(1, NM_MAX(40, 55))]; + + G_STATIC_ASSERT(sizeof(buf1) == 55); + G_STATIC_ASSERT(sizeof(buf2) == 55); +} + +/*****************************************************************************/ + +static void test_gpid(void) { const int *int_ptr; @@ -427,7 +450,7 @@ _strv_cmp_fuzz_input(const char *const *in, if (l < 0) ss = g_strdupv((char **) in); else if (l == 0) { - ss = nmtst_get_rand_bool() ? NULL : g_new0(char *, 1); + ss = nmtst_get_rand_bool() ? NULL : nm_strv_empty_new(); } else { ss = nm_memdup(in, sizeof(const char *) * l); for (i = 0; i < (gsize) l; i++) @@ -2573,6 +2596,258 @@ test_nm_prioq(void) /*****************************************************************************/ +static const char * +_getpwuid_name(uid_t uid) +{ + static struct passwd *pw; + + pw = getpwuid(uid); + return pw ? nm_str_not_empty(pw->pw_name) : NULL; +} + +static void +test_uid_to_name(void) +{ + int i; + + for (i = 0; i < 20; i++) { + gs_free char *name = NULL; + gs_free struct passwd *pw = NULL; + uid_t uid; + + if (i < 5) + uid = i; + else + uid = nmtst_get_rand_uint32() % 2000u; + + name = nm_utils_uid_to_name(uid); + g_assert_cmpstr(name, ==, _getpwuid_name(uid)); + + pw = nm_getpwuid(uid); + g_assert_cmpstr(name, ==, nm_passwd_name(pw)); + } +} + +/*****************************************************************************/ + +static void +compare_ints(void) +{ + GVariant *value1, *value2; + + value1 = g_variant_new_int32(5); + value2 = g_variant_new_int32(5); + g_assert(nm_g_variant_cmp(value1, value2) == 0); + + g_variant_unref(value2); + value2 = g_variant_new_int32(10); + g_assert(nm_g_variant_cmp(value1, value2) < 0); + + g_variant_unref(value2); + value2 = g_variant_new_int32(-1); + g_assert(nm_g_variant_cmp(value1, value2) > 0); + + g_variant_unref(value1); + g_variant_unref(value2); +} + +static void +compare_strings(void) +{ + GVariant *value1, *value2; + const char *str1 = "hello"; + const char *str2 = "world"; + + value1 = g_variant_new_string(str1); + value2 = g_variant_new_string(str1); + g_assert(nm_g_variant_cmp(value1, value2) == 0); + + g_variant_unref(value2); + value2 = g_variant_new_string(str2); + g_assert(nm_g_variant_cmp(value1, value2) < 0); + + g_assert(nm_g_variant_cmp(value2, value1) > 0); + + g_variant_unref(value1); + g_variant_unref(value2); +} + +static void +compare_strv(void) +{ + GVariant *value1, *value2; + const char *const strv1[] = {"foo", "bar", "baz", NULL}; + const char *const strv2[] = {"foo", "bar", "bar", NULL}; + const char *const strv3[] = {"foo", "bar", NULL}; + const char *const strv4[] = {"foo", "bar", "baz", "bam", NULL}; + + value1 = g_variant_new_strv(strv1, -1); + value2 = g_variant_new_strv(strv1, -1); + g_assert(nm_g_variant_cmp(value1, value2) == 0); + + g_variant_unref(value2); + value2 = g_variant_new_strv(strv2, -1); + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value2); + value2 = g_variant_new_strv(strv3, -1); + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value2); + value2 = g_variant_new_strv(strv4, -1); + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value1); + g_variant_unref(value2); +} + +static void +compare_arrays(void) +{ + GVariant *value1, *value2; + guint32 array[] = {0, 1, 2, 3, 4}; + + value1 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + array, + G_N_ELEMENTS(array), + sizeof(guint32)); + value2 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + array, + G_N_ELEMENTS(array), + sizeof(guint32)); + + g_assert(nm_g_variant_cmp(value1, value2) == 0); + + g_variant_unref(value2); + value2 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + array + 1, + G_N_ELEMENTS(array) - 1, + sizeof(guint32)); + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + array[0] = 7; + g_variant_unref(value2); + value2 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + array, + G_N_ELEMENTS(array), + sizeof(guint32)); + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value1); + g_variant_unref(value2); +} + +static void +compare_str_hash(void) +{ + GVariant *value1, *value2; + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_variant_builder_add(&builder, "{ss}", "key1", "hello"); + g_variant_builder_add(&builder, "{ss}", "key2", "world"); + g_variant_builder_add(&builder, "{ss}", "key3", "!"); + value1 = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_variant_builder_add(&builder, "{ss}", "key3", "!"); + g_variant_builder_add(&builder, "{ss}", "key2", "world"); + g_variant_builder_add(&builder, "{ss}", "key1", "hello"); + value2 = g_variant_builder_end(&builder); + + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value1); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "key1", g_variant_new_string("hello")); + g_variant_builder_add(&builder, "{sv}", "key2", g_variant_new_string("world")); + g_variant_builder_add(&builder, "{sv}", "key3", g_variant_new_string("!")); + value1 = g_variant_builder_end(&builder); + + g_variant_unref(value2); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "key1", g_variant_new_string("hello")); + g_variant_builder_add(&builder, "{sv}", "key2", g_variant_new_string("world")); + g_variant_builder_add(&builder, "{sv}", "key3", g_variant_new_string("!")); + value2 = g_variant_builder_end(&builder); + + g_assert(nm_g_variant_cmp(value1, value2) == 0); + + g_variant_unref(value2); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_variant_builder_add(&builder, "{ss}", "key1", "hello"); + g_variant_builder_add(&builder, "{ss}", "key3", "!"); + value2 = g_variant_builder_end(&builder); + + g_assert(nm_g_variant_cmp(value1, value2) != 0); + g_assert(nm_g_variant_cmp(value2, value1) != 0); + + g_variant_unref(value2); + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + g_variant_builder_add(&builder, "{ss}", "key1", "hello"); + g_variant_builder_add(&builder, "{ss}", "key2", "moon"); + g_variant_builder_add(&builder, "{ss}", "key3", "!"); + value2 = g_variant_builder_end(&builder); + + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value1); + g_variant_unref(value2); +} + +static void +compare_ip6_addresses(void) +{ + GVariant *value1, *value2; + struct in6_addr addr1; + struct in6_addr addr2; + struct in6_addr addr3; + guint32 prefix1 = 64; + guint32 prefix2 = 64; + guint32 prefix3 = 0; + + inet_pton(AF_INET6, "1:2:3:4:5:6:7:8", &addr1); + inet_pton(AF_INET6, "ffff:2:3:4:5:6:7:8", &addr2); + inet_pton(AF_INET6, "::", &addr3); + + value1 = g_variant_new( + "(@ayu@ay)", + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr1.s6_addr, 16, 1), + prefix1, + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1)); + + value2 = g_variant_new( + "(@ayu@ay)", + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr1.s6_addr, 16, 1), + prefix1, + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1)); + + g_assert(nm_g_variant_cmp(value1, value2) == 0); + + g_variant_unref(value2); + value2 = g_variant_new( + "(@ayu@ay)", + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr2.s6_addr, 16, 1), + prefix2, + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1)); + + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value2); + value2 = g_variant_new( + "(@ayu@ay)", + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1), + prefix3, + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1)); + + g_assert(nm_g_variant_cmp(value1, value2) != 0); + + g_variant_unref(value1); + g_variant_unref(value2); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -2581,6 +2856,7 @@ main(int argc, char **argv) nmtst_init(&argc, &argv, TRUE); g_test_add_func("/general/test_nm_static_assert", test_nm_static_assert); + g_test_add_func("/general/test_max", test_max); g_test_add_func("/general/test_gpid", test_gpid); g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp); g_test_add_func("/general/test_timespect_to", test_timespect_to); @@ -2623,6 +2899,14 @@ main(int argc, char **argv) g_test_add_func("/general/test_garray", test_garray); g_test_add_func("/general/test_nm_prioq", test_nm_prioq); g_test_add_func("/general/test_nm_random", test_nm_random); + g_test_add_func("/general/test_uid_to_name", test_uid_to_name); + + g_test_add_func("/libnm/compare/ints", compare_ints); + g_test_add_func("/libnm/compare/strings", compare_strings); + g_test_add_func("/libnm/compare/strv", compare_strv); + g_test_add_func("/libnm/compare/arrays", compare_arrays); + g_test_add_func("/libnm/compare/str_hash", compare_str_hash); + g_test_add_func("/libnm/compare/ip6_addresses", compare_ip6_addresses); return g_test_run(); } |