diff options
| author | Michael Biebl <biebl@debian.org> | 2020-10-05 22:27:18 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2020-10-05 22:27:18 +0200 |
| commit | aafc1dbe4712c86189bbc1d4d54ad8cb4c69be7e (patch) | |
| tree | a2a9bb4d007339a0b1304540388230ccedac32bd /shared/nm-glib-aux/nm-shared-utils.c | |
| parent | e7b44ef4c80907346ec7492a09c45277459924fc (diff) | |
New upstream version 1.27.90 upstream/1.27.90
Diffstat (limited to 'shared/nm-glib-aux/nm-shared-utils.c')
| -rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.c | 7727 |
1 files changed, 4100 insertions, 3627 deletions
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 3d1b0098..b0e02381 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: LGPL-2.1+ +/* SPDX-License-Identifier: LGPL-2.1+ */ /* * Copyright (C) 2016 Red Hat, Inc. */ @@ -7,6 +7,7 @@ #include "nm-shared-utils.h" +#include <pwd.h> #include <arpa/inet.h> #include <poll.h> #include <fcntl.h> @@ -18,16 +19,19 @@ #include "nm-errno.h" #include "nm-str-buf.h" -G_STATIC_ASSERT (sizeof (NMUtilsNamedEntry) == sizeof (const char *)); -G_STATIC_ASSERT (G_STRUCT_OFFSET (NMUtilsNamedValue, value_ptr) == sizeof (const char *)); +G_STATIC_ASSERT(sizeof(NMUtilsNamedEntry) == sizeof(const char *)); +G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, value_ptr) == sizeof(const char *)); /*****************************************************************************/ -const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; +const char _nm_hexchar_table_lower[16] = "0123456789abcdef"; +const char _nm_hexchar_table_upper[16] = "0123456789ABCDEF"; + +const void *const _NM_PTRARRAY_EMPTY[1] = {NULL}; /*****************************************************************************/ -const NMIPAddr nm_ip_addr_zero = { }; +const NMIPAddr nm_ip_addr_zero = {}; /* this initializes a struct in_addr/in6_addr and allows for untrusted * arguments (like unsuitable @addr_family or @src_len). It's almost safe @@ -42,131 +46,66 @@ const NMIPAddr nm_ip_addr_zero = { }; * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. * If that matter to you, clear @dst before. */ gboolean -nm_ip_addr_set_from_untrusted (int addr_family, - gpointer dst, - gconstpointer src, - gsize src_len, - int *out_addr_family) -{ - nm_assert (dst); - - switch (addr_family) { - case AF_UNSPEC: - if (!out_addr_family) { - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert_not_reached (); - return FALSE; - } - switch (src_len) { - case sizeof (struct in_addr): addr_family = AF_INET; break; - case sizeof (struct in6_addr): addr_family = AF_INET6; break; - default: - return FALSE; - } - break; - case AF_INET: - if (src_len != sizeof (struct in_addr)) - return FALSE; - break; - case AF_INET6: - if (src_len != sizeof (struct in6_addr)) - return FALSE; - break; - default: - /* when the callers allow undefined @addr_family, they must provide - * an @out_addr_family argument. */ - nm_assert (out_addr_family); - return FALSE; - } - - nm_assert (src); - - memcpy (dst, src, src_len); - NM_SET_OUT (out_addr_family, addr_family); - return TRUE; +nm_ip_addr_set_from_untrusted(int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int * out_addr_family) +{ + nm_assert(dst); + + switch (addr_family) { + case AF_UNSPEC: + if (!out_addr_family) { + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert_not_reached(); + return FALSE; + } + switch (src_len) { + case sizeof(struct in_addr): + addr_family = AF_INET; + break; + case sizeof(struct in6_addr): + addr_family = AF_INET6; + break; + default: + return FALSE; + } + break; + case AF_INET: + if (src_len != sizeof(struct in_addr)) + return FALSE; + break; + case AF_INET6: + if (src_len != sizeof(struct in6_addr)) + return FALSE; + break; + default: + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert(out_addr_family); + return FALSE; + } + + nm_assert(src); + + memcpy(dst, src, src_len); + NM_SET_OUT(out_addr_family, addr_family); + return TRUE; } /*****************************************************************************/ -G_STATIC_ASSERT (ETH_ALEN == sizeof (struct ether_addr)); -G_STATIC_ASSERT (ETH_ALEN == 6); - -/*****************************************************************************/ - -gsize -nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested) -{ - gsize n, x; - - /* https://doc.qt.io/qt-5/containers.html#growth-strategies */ - - if (requested <= 40) { - /* small allocations. Increase in small steps of 8 bytes. - * - * We get thus sizes of 8, 16, 32, 40. */ - if (requested <= 8) - return 8; - if (requested <= 16) - return 16; - if (requested <= 32) - return 32; - - /* The return values for < 104 are essentially hard-coded, and the choice here is - * made without very strong reasons. - * - * We want to stay 24 bytes below the power-of-two border 64. Hence, return 40 here. - * However, the next step then is already 104 (128 - 24). It's a larger gap than in - * the steps before. - * - * It's not clear whether some of the steps should be adjusted (or how exactly). */ - return 40; - } - - if ( requested <= 0x2000u - 24u - || G_UNLIKELY (!true_realloc)) { - /* mid sized allocations. Return next power of two, minus 24 bytes extra space - * at the beginning. - * That means, we double the size as we grow. - * - * With !true_realloc, it means that the caller does not intend to call - * realloc() but instead clone the buffer. This is for example the case, when we - * want to nm_explicit_bzero() the old buffer. In that case we really want to grow - * the buffer exponentially every time and not increment in page sizes of 4K (below). - * - * We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */ - - if (G_UNLIKELY (requested > G_MAXSIZE / 2u - 24u)) - return G_MAXSIZE; - - x = requested + 24u; - n = 128u; - while (n < x) { - n <<= 1; - nm_assert (n > 128u); - } - - nm_assert (n > 24u && n - 24u >= requested); - return n - 24u; - } - - if (G_UNLIKELY (requested > G_MAXSIZE - 0x1000u - 24u)) - return G_MAXSIZE; - - /* For large allocations (with !true_realloc) we allocate memory in chunks of - * 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus - * realloc() is efficient by just reordering pages. */ - n = ((requested + (0x0FFFu + 24u)) & ~((gsize) 0x0FFFu)) - 24u; - nm_assert (n >= requested); - return n; -} +G_STATIC_ASSERT(ETH_ALEN == sizeof(struct ether_addr)); +G_STATIC_ASSERT(ETH_ALEN == 6); /*****************************************************************************/ pid_t -nm_utils_gettid (void) +nm_utils_gettid(void) { - return (pid_t) syscall (SYS_gettid); + return (pid_t) syscall(SYS_gettid); } /* Used for asserting that this function is called on the main-thread. @@ -175,155 +114,154 @@ nm_utils_gettid (void) * * When forking, the thread-id is again reset upon first call. */ gboolean -_nm_assert_on_main_thread (void) +_nm_assert_on_main_thread(void) { - G_LOCK_DEFINE_STATIC (lock); - static pid_t seen_tid; - static pid_t seen_pid; - pid_t tid; - pid_t pid; - gboolean success = FALSE; + G_LOCK_DEFINE_STATIC(lock); + static pid_t seen_tid; + static pid_t seen_pid; + pid_t tid; + pid_t pid; + gboolean success = FALSE; - tid = nm_utils_gettid (); - nm_assert (tid != 0); + tid = nm_utils_gettid(); + nm_assert(tid != 0); - G_LOCK (lock); + G_LOCK(lock); - if (G_LIKELY (tid == seen_tid)) { - /* we don't care about false positives (when the process forked, and the thread-id - * is accidentally re-used) . It's for assertions only. */ - success = TRUE; - } else { - pid = getpid (); - nm_assert (pid != 0); + if (G_LIKELY(tid == seen_tid)) { + /* we don't care about false positives (when the process forked, and the thread-id + * is accidentally re-used) . It's for assertions only. */ + success = TRUE; + } else { + pid = getpid(); + nm_assert(pid != 0); - if ( seen_tid == 0 - || seen_pid != pid) { - /* either this is the first time we call the function, or the process - * forked. In both cases, remember the thread-id. */ - seen_tid = tid; - seen_pid = pid; - success = TRUE; - } - } + if (seen_tid == 0 || seen_pid != pid) { + /* either this is the first time we call the function, or the process + * forked. In both cases, remember the thread-id. */ + seen_tid = tid; + seen_pid = pid; + success = TRUE; + } + } - G_UNLOCK (lock); + G_UNLOCK(lock); - return success; + return success; } /*****************************************************************************/ void -nm_utils_strbuf_append_c (char **buf, gsize *len, char c) -{ - switch (*len) { - case 0: - return; - case 1: - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - (*buf)[0] = c; - (*buf)[1] = '\0'; - (*len)--; - (*buf)++; - return; - } +nm_utils_strbuf_append_c(char **buf, gsize *len, char c) +{ + switch (*len) { + case 0: + return; + case 1: + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + (*buf)[0] = c; + (*buf)[1] = '\0'; + (*len)--; + (*buf)++; + return; + } } void -nm_utils_strbuf_append_bin (char **buf, gsize *len, gconstpointer str, gsize str_len) -{ - switch (*len) { - case 0: - return; - case 1: - if (str_len == 0) { - (*buf)[0] = '\0'; - return; - } - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - if (str_len == 0) { - (*buf)[0] = '\0'; - return; - } - if (str_len >= *len) { - memcpy (*buf, str, *len - 1); - (*buf)[*len - 1] = '\0'; - *buf = &(*buf)[*len]; - *len = 0; - } else { - memcpy (*buf, str, str_len); - *buf = &(*buf)[str_len]; - (*buf)[0] = '\0'; - *len -= str_len; - } - return; - } +nm_utils_strbuf_append_bin(char **buf, gsize *len, gconstpointer str, gsize str_len) +{ + switch (*len) { + case 0: + return; + case 1: + if (str_len == 0) { + (*buf)[0] = '\0'; + return; + } + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + if (str_len == 0) { + (*buf)[0] = '\0'; + return; + } + if (str_len >= *len) { + memcpy(*buf, str, *len - 1); + (*buf)[*len - 1] = '\0'; + *buf = &(*buf)[*len]; + *len = 0; + } else { + memcpy(*buf, str, str_len); + *buf = &(*buf)[str_len]; + (*buf)[0] = '\0'; + *len -= str_len; + } + return; + } } void -nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str) -{ - gsize src_len; - - switch (*len) { - case 0: - return; - case 1: - if (!str || !*str) { - (*buf)[0] = '\0'; - return; - } - (*buf)[0] = '\0'; - *len = 0; - (*buf)++; - return; - default: - if (!str || !*str) { - (*buf)[0] = '\0'; - return; - } - src_len = g_strlcpy (*buf, str, *len); - if (src_len >= *len) { - *buf = &(*buf)[*len]; - *len = 0; - } else { - *buf = &(*buf)[src_len]; - *len -= src_len; - } - return; - } +nm_utils_strbuf_append_str(char **buf, gsize *len, const char *str) +{ + gsize src_len; + + switch (*len) { + case 0: + return; + case 1: + if (!str || !*str) { + (*buf)[0] = '\0'; + return; + } + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + if (!str || !*str) { + (*buf)[0] = '\0'; + return; + } + src_len = g_strlcpy(*buf, str, *len); + if (src_len >= *len) { + *buf = &(*buf)[*len]; + *len = 0; + } else { + *buf = &(*buf)[src_len]; + *len -= src_len; + } + return; + } } void -nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) +nm_utils_strbuf_append(char **buf, gsize *len, const char *format, ...) { - char *p = *buf; - va_list args; - int retval; + char * p = *buf; + va_list args; + int retval; - if (*len == 0) - return; + if (*len == 0) + return; - va_start (args, format); - retval = g_vsnprintf (p, *len, format, args); - va_end (args); + va_start(args, format); + retval = g_vsnprintf(p, *len, format, args); + va_end(args); - if ((gsize) retval >= *len) { - *buf = &p[*len]; - *len = 0; - } else { - *buf = &p[retval]; - *len -= retval; - } + if ((gsize) retval >= *len) { + *buf = &p[*len]; + *len = 0; + } else { + *buf = &p[retval]; + *len -= retval; + } } /** @@ -369,59 +307,58 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) * nm_utils_strbuf_seek_end() would then NUL terminate it. */ void -nm_utils_strbuf_seek_end (char **buf, gsize *len) +nm_utils_strbuf_seek_end(char **buf, gsize *len) { - gsize l; - char *end; + gsize l; + char *end; - nm_assert (len); - nm_assert (buf && *buf); + nm_assert(len); + nm_assert(buf && *buf); - if (*len <= 1) { - if ( *len == 1 - && (*buf)[0]) - goto truncate; - return; - } + 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); + end = memchr(*buf, 0, *len); + if (end) { + l = end - *buf; + nm_assert(l < *len); - *buf = end; - *len -= l; - return; - } + *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; + /* hm, no NUL character within len bytes. + * Just NUL terminate the array and consume them + * all. */ + *buf += *len; + (*buf)[-1] = '\0'; + *len = 0; + return; } /*****************************************************************************/ GBytes * -nm_gbytes_get_empty (void) +nm_gbytes_get_empty(void) { - static GBytes *bytes = NULL; - GBytes *b; + static GBytes *bytes = NULL; + GBytes * b; again: - b = g_atomic_pointer_get (&bytes); - if (G_UNLIKELY (!b)) { - b = g_bytes_new_static ("", 0); - if (!g_atomic_pointer_compare_and_exchange (&bytes, NULL, b)) { - g_bytes_unref (b); - goto again; - } - } - return b; + b = g_atomic_pointer_get(&bytes); + if (G_UNLIKELY(!b)) { + b = g_bytes_new_static("", 0); + if (!g_atomic_pointer_compare_and_exchange(&bytes, NULL, b)) { + g_bytes_unref(b); + goto again; + } + } + return b; } /** @@ -435,38 +372,64 @@ again: * 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) +nm_utils_gbytes_equal_mem(GBytes *bytes, gconstpointer mem_data, gsize mem_len) { - gconstpointer p; - gsize l; + gconstpointer p; + gsize l; - if (!bytes) { - /* as a special case, let %NULL GBytes compare idential - * to an empty array. */ - return (mem_len == 0); - } + if (!bytes) { + /* as a special case, let %NULL GBytes compare identical + * 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); + 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) +nm_utils_gbytes_to_variant_ay(GBytes *bytes) { - const guint8 *p; - gsize l; + 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); + } - 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); +} + +/*****************************************************************************/ - p = g_bytes_get_data (bytes, &l); - return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1); +#define _variant_singleton_get(create_variant) \ + ({ \ + static GVariant *_singleton = NULL; \ + GVariant * _v; \ + \ +again: \ + _v = g_atomic_pointer_get(&_singleton); \ + if (G_UNLIKELY(!_v)) { \ + _v = (create_variant); \ + nm_assert(_v); \ + nm_assert(g_variant_is_floating(_v)); \ + g_variant_ref_sink(_v); \ + if (!g_atomic_pointer_compare_and_exchange(&_singleton, NULL, _v)) { \ + g_variant_unref(_v); \ + goto again; \ + } \ + } \ + _v; \ + }) + +GVariant * +nm_g_variant_singleton_u_0(void) +{ + return _variant_singleton_get(g_variant_new_uint32(0)); } /*****************************************************************************/ @@ -476,53 +439,46 @@ nm_utils_gbytes_to_variant_ay (GBytes *bytes) * Returns a floating reference. */ GVariant * -nm_utils_strdict_to_variant_ass (GHashTable *strdict) -{ - GHashTableIter iter; - const char *key, *value; - GVariantBuilder builder; - guint i, len; - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); - - if (!strdict) - goto out; - len = g_hash_table_size (strdict); - if (!len) - goto out; - - g_hash_table_iter_init (&iter, strdict); - if (!g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) - nm_assert_not_reached (); - - if (len == 1) - g_variant_builder_add (&builder, "{ss}", key, value); - else { - gs_free NMUtilsNamedValue *idx_free = NULL; - NMUtilsNamedValue *idx; - - if (len > 300 / sizeof (NMUtilsNamedValue)) { - idx_free = g_new (NMUtilsNamedValue, len); - idx = idx_free; - } else - idx = g_alloca (sizeof (NMUtilsNamedValue) * len); - - i = 0; - do { - idx[i].name = key; - idx[i].value_str = value; - i++; - } while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)); - nm_assert (i == len); - - nm_utils_named_value_list_sort (idx, len, NULL, NULL); - - for (i = 0; i < len; i++) - g_variant_builder_add (&builder, "{ss}", idx[i].name, idx[i].value_str); - } +nm_utils_strdict_to_variant_ass(GHashTable *strdict) +{ + gs_free NMUtilsNamedValue *values_free = NULL; + NMUtilsNamedValue values_prepared[20]; + const NMUtilsNamedValue * values; + GVariantBuilder builder; + guint i; + guint n; -out: - return g_variant_builder_end (&builder); + values = nm_utils_named_values_from_strdict(strdict, &n, values_prepared, &values_free); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); + for (i = 0; i < n; i++) { + g_variant_builder_add(&builder, "{ss}", values[i].name, values[i].value_str); + } + return g_variant_builder_end(&builder); +} + +/*****************************************************************************/ + +GVariant * +nm_utils_strdict_to_variant_asv(GHashTable *strdict) +{ + gs_free NMUtilsNamedValue *values_free = NULL; + NMUtilsNamedValue values_prepared[20]; + const NMUtilsNamedValue * values; + GVariantBuilder builder; + guint i; + guint n; + + values = nm_utils_named_values_from_strdict(strdict, &n, values_prepared, &values_free); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + for (i = 0; i < n; i++) { + g_variant_builder_add(&builder, + "{sv}", + values[i].name, + g_variant_new_string(values[i].value_str)); + } + return g_variant_builder_end(&builder); } /*****************************************************************************/ @@ -548,54 +504,54 @@ out: * 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'; - } +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; + return buf0; } /*****************************************************************************/ @@ -603,82 +559,81 @@ out: char _nm_utils_to_string_buffer[]; void -nm_utils_to_string_buffer_init (char **buf, gsize *len) +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); - } + 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_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; + 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) +nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs, + gsize n_descs, + unsigned flags, + char * buf, + gsize len) { - gsize i; - char *p; + 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); - } + 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_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; }; /*****************************************************************************/ @@ -690,34 +645,34 @@ nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, * Returns: the netmask represented by the prefix, in network byte order **/ guint32 -_nm_utils_ip4_prefix_to_netmask (guint32 prefix) +_nm_utils_ip4_prefix_to_netmask(guint32 prefix) { - return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF; + return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu; } gconstpointer -nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen) +nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen) { - g_return_val_if_fail (dst, NULL); + g_return_val_if_fail(dst, NULL); - switch (family) { - case AF_INET: - g_return_val_if_fail (plen <= 32, NULL); + switch (family) { + case AF_INET: + g_return_val_if_fail(plen <= 32, NULL); - if (!src) { - /* allow "self-assignment", by specifying %NULL as source. */ - src = dst; - } + if (!src) { + /* allow "self-assignment", by specifying %NULL as source. */ + src = dst; + } - *((guint32 *) dst) = nm_utils_ip4_address_clear_host_address (*((guint32 *) src), plen); - break; - case AF_INET6: - nm_utils_ip6_address_clear_host_address (dst, src, plen); - break; - default: - g_return_val_if_reached (NULL); - } - return dst; + *((guint32 *) dst) = nm_utils_ip4_address_clear_host_address(*((guint32 *) src), plen); + break; + case AF_INET6: + nm_utils_ip6_address_clear_host_address(dst, src, plen); + break; + default: + g_return_val_if_reached(NULL); + } + return dst; } /* nm_utils_ip4_address_clear_host_address: @@ -727,9 +682,9 @@ nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer * returns: the input address, with the host address set to 0. */ in_addr_t -nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen) +nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint8 plen) { - return addr & _nm_utils_ip4_prefix_to_netmask (plen); + return addr & _nm_utils_ip4_prefix_to_netmask(plen); } /* nm_utils_ip6_address_clear_host_address: @@ -741,54 +696,58 @@ nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen) * @dst and @src to the same destination or set @src NULL. */ const struct in6_addr * -nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen) +nm_utils_ip6_address_clear_host_address(struct in6_addr * dst, + const struct in6_addr *src, + guint8 plen) { - g_return_val_if_fail (plen <= 128, NULL); - g_return_val_if_fail (dst, NULL); + g_return_val_if_fail(plen <= 128, NULL); + g_return_val_if_fail(dst, NULL); - if (!src) - src = dst; + if (!src) + src = dst; - if (plen < 128) { - guint nbytes = plen / 8; - guint nbits = plen % 8; + if (plen < 128) { + guint nbytes = plen / 8; + guint nbits = plen % 8; - if (nbytes && dst != src) - memcpy (dst, src, nbytes); - if (nbits) { - dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits))); - nbytes++; - } - if (nbytes <= 15) - memset (&dst->s6_addr[nbytes], 0, 16 - nbytes); - } else if (src != dst) - *dst = *src; + if (nbytes && dst != src) + memcpy(dst, src, nbytes); + if (nbits) { + dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits))); + nbytes++; + } + if (nbytes <= 15) + memset(&dst->s6_addr[nbytes], 0, 16 - nbytes); + } else if (src != dst) + *dst = *src; - return dst; + return dst; } int -nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen) -{ - int nbytes; - guint8 va, vb, m; - - if (plen >= 128) - NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, sizeof (struct in6_addr)); - else { - nbytes = plen / 8; - if (nbytes) - NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, nbytes); - - plen = plen % 8; - if (plen != 0) { - m = ~((1 << (8 - plen)) - 1); - va = ((((const guint8 *) addr_a))[nbytes]) & m; - vb = ((((const guint8 *) addr_b))[nbytes]) & m; - NM_CMP_DIRECT (va, vb); - } - } - return 0; +nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a, + const struct in6_addr *addr_b, + guint8 plen) +{ + int nbytes; + guint8 va, vb, m; + + if (plen >= 128) + NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr)); + else { + nbytes = plen / 8; + if (nbytes) + NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes); + + plen = plen % 8; + if (plen != 0) { + m = ~((1 << (8 - plen)) - 1); + va = ((((const guint8 *) addr_a))[nbytes]) & m; + vb = ((((const guint8 *) addr_b))[nbytes]) & m; + NM_CMP_DIRECT(va, vb); + } + } + return 0; } /** @@ -804,347 +763,349 @@ nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struc **/ /* The function is originally from ipcalc.c of Red Hat's initscripts. */ guint32 -_nm_utils_ip4_get_default_prefix (guint32 ip) +_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 */ + 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 */ + 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); - } +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); + } } /*****************************************************************************/ static gboolean -_parse_legacy_addr4 (const char *text, in_addr_t *out_addr, GError **error) -{ - gs_free char *s_free = NULL; - struct in_addr a1; - guint8 bin[sizeof (a1)]; - char *s; - int i; - - if (inet_aton (text, &a1) != 1) { - g_set_error_literal (error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "address invalid according to inet_aton()"); - return FALSE; - } - - /* OK, inet_aton() accepted the format. That's good, because we want - * to accept IPv4 addresses in octal format, like 255.255.000.000. - * That's what "legacy" means here. inet_pton() doesn't accept those. - * - * But inet_aton() also ignores trailing garbage and formats with fewer than - * 4 digits. That is just too crazy and we don't do that. Perform additional checks - * and reject some forms that inet_aton() accepted. - * - * Note that we still should (of course) accept everything that inet_pton() - * accepts. However this code never gets called if inet_pton() succeeds - * (see below, aside the assertion code). */ - - if (NM_STRCHAR_ANY (text, ch, ( !(ch >= '0' && ch <= '9') - && !NM_IN_SET (ch, '.', 'x')))) { - /* We only accepts '.', digits, and 'x' for "0x". */ - g_set_error_literal (error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "contains an invalid character"); - return FALSE; - } - - s = nm_memdup_maybe_a (300, text, strlen (text) + 1, &s_free); - - for (i = 0; i < G_N_ELEMENTS (bin); i++) { - char *current_token = s; - gint32 v; - - s = strchr (s, '.'); - if (s) { - s[0] = '\0'; - s++; - } - - if ((i == G_N_ELEMENTS (bin) - 1) != (s == NULL)) { - /* Exactly for the last digit, we expect to have no more following token. - * But this isn't the case. Abort. */ - g_set_error (error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "wrong number of tokens (index %d, token '%s')", - i, s); - return FALSE; - } - - v = _nm_utils_ascii_str_to_int64 (current_token, 0, 0, 0xFF, -1); - if (v == -1) { - int errsv = errno; - - /* we do accept octal and hex (even with leading "0x"). But something - * about this token is wrong. */ - g_set_error (error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "invalid token '%s': %s (%d)", - current_token, - nm_strerror_native (errsv), - errsv); - return FALSE; - } - - bin[i] = v; - } - - if (memcmp (bin, &a1, sizeof (bin)) != 0) { - /* our parsing did not agree with what inet_aton() gave. Something - * is wrong. Abort. */ - g_set_error (error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_INVALID_ARGUMENT, - "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx", - a1.s_addr, - bin[0], bin[1], bin[2], bin[3]); - return FALSE; - } - - *out_addr = a1.s_addr; - return TRUE; +_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error) +{ + gs_free char * s_free = NULL; + struct in_addr a1; + guint8 bin[sizeof(a1)]; + char * s; + int i; + + if (inet_aton(text, &a1) != 1) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "address invalid according to inet_aton()"); + return FALSE; + } + + /* OK, inet_aton() accepted the format. That's good, because we want + * to accept IPv4 addresses in octal format, like 255.255.000.000. + * That's what "legacy" means here. inet_pton() doesn't accept those. + * + * But inet_aton() also ignores trailing garbage and formats with fewer than + * 4 digits. That is just too crazy and we don't do that. Perform additional checks + * and reject some forms that inet_aton() accepted. + * + * Note that we still should (of course) accept everything that inet_pton() + * accepts. However this code never gets called if inet_pton() succeeds + * (see below, aside the assertion code). */ + + if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) { + /* We only accepts '.', digits, and 'x' for "0x". */ + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "contains an invalid character"); + return FALSE; + } + + s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free); + + for (i = 0; i < G_N_ELEMENTS(bin); i++) { + char * current_token = s; + gint32 v; + + s = strchr(s, '.'); + if (s) { + s[0] = '\0'; + s++; + } + + if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) { + /* Exactly for the last digit, we expect to have no more following token. + * But this isn't the case. Abort. */ + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "wrong number of tokens (index %d, token '%s')", + i, + s); + return FALSE; + } + + v = _nm_utils_ascii_str_to_int64(current_token, 0, 0, 0xFF, -1); + if (v == -1) { + int errsv = errno; + + /* we do accept octal and hex (even with leading "0x"). But something + * about this token is wrong. */ + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "invalid token '%s': %s (%d)", + current_token, + nm_strerror_native(errsv), + errsv); + return FALSE; + } + + bin[i] = v; + } + + if (memcmp(bin, &a1, sizeof(bin)) != 0) { + /* our parsing did not agree with what inet_aton() gave. Something + * is wrong. Abort. */ + g_set_error( + error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_INVALID_ARGUMENT, + "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx", + a1.s_addr, + bin[0], + bin[1], + bin[2], + bin[3]); + return FALSE; + } + + *out_addr = a1.s_addr; + return TRUE; } gboolean -nm_utils_parse_inaddr_bin_full (int addr_family, - gboolean accept_legacy, - const char *text, - int *out_addr_family, - gpointer out_addr) -{ - NMIPAddr addrbin; - - g_return_val_if_fail (text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail (!out_addr || out_addr_family, FALSE); - addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); - - if (inet_pton (addr_family, text, &addrbin) != 1) { - if ( accept_legacy - && addr_family == AF_INET - && _parse_legacy_addr4 (text, &addrbin.addr4, NULL)) { - /* The address is in some legacy format which inet_aton() accepts, but not inet_pton(). - * Most likely octal digits (leading zeros). We accept the address. */ - } else - return FALSE; - } +nm_utils_parse_inaddr_bin_full(int addr_family, + gboolean accept_legacy, + const char *text, + int * out_addr_family, + gpointer out_addr) +{ + NMIPAddr addrbin; + + g_return_val_if_fail(text, FALSE); + + if (addr_family == AF_UNSPEC) { + g_return_val_if_fail(!out_addr || out_addr_family, FALSE); + addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; + } else + g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); + + if (inet_pton(addr_family, text, &addrbin) != 1) { + if (accept_legacy && addr_family == AF_INET + && _parse_legacy_addr4(text, &addrbin.addr4, NULL)) { + /* The address is in some legacy format which inet_aton() accepts, but not inet_pton(). + * Most likely octal digits (leading zeros). We accept the address. */ + } else + return FALSE; + } #if NM_MORE_ASSERTS > 10 - if (addr_family == AF_INET) { - gs_free_error GError *error = NULL; - in_addr_t a; - - /* The legacy parser should accept everything that inet_pton() accepts too. Meaning, - * it should strictly parse *more* formats. And of course, parse it the same way. */ - if (!_parse_legacy_addr4 (text, &a, &error)) { - char buf[INET_ADDRSTRLEN]; - - g_error ("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by legacy parser: %s", - text, _nm_utils_inet4_ntop (addrbin.addr4, buf), error->message); - } - nm_assert (addrbin.addr4 == a); - } + if (addr_family == AF_INET) { + gs_free_error GError *error = NULL; + in_addr_t a; + + /* The legacy parser should accept everything that inet_pton() accepts too. Meaning, + * it should strictly parse *more* formats. And of course, parse it the same way. */ + if (!_parse_legacy_addr4(text, &a, &error)) { + char buf[INET_ADDRSTRLEN]; + + g_error("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by " + "legacy parser: %s", + text, + _nm_utils_inet4_ntop(addrbin.addr4, buf), + error->message); + } + nm_assert(addrbin.addr4 == a); + } #endif - NM_SET_OUT (out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set (addr_family, out_addr, &addrbin); - return TRUE; + NM_SET_OUT(out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set(addr_family, out_addr, &addrbin); + return TRUE; } gboolean -nm_utils_parse_inaddr (int addr_family, - const char *text, - char **out_addr) +nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr) { - NMIPAddr addrbin; - char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; + NMIPAddr addrbin; + char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - g_return_val_if_fail (text, FALSE); + 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); + 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); - if (inet_pton (addr_family, text, &addrbin) != 1) - return FALSE; + if (inet_pton(addr_family, text, &addrbin) != 1) + return FALSE; - NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf)))); - return TRUE; + 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, - int *out_addr_family, - gpointer out_addr, - int *out_prefix) -{ - gs_free char *addrstr_free = NULL; - int prefix = -1; - const char *slash; - const char *addrstr; - NMIPAddr addrbin; - - g_return_val_if_fail (text, FALSE); - - if (addr_family == AF_UNSPEC) { - g_return_val_if_fail (!out_addr || out_addr_family, FALSE); - addr_family = strchr (text, ':') ? AF_INET6 : AF_INET; - } else - g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), 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) { - /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't - * do that. */ - prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10, - 0, - addr_family == AF_INET ? 32 : 128, - -1); - if (prefix == -1) - return FALSE; - } - - NM_SET_OUT (out_addr_family, addr_family); - if (out_addr) - nm_ip_addr_set (addr_family, out_addr, &addrbin); - NM_SET_OUT (out_prefix, prefix); - return TRUE; +nm_utils_parse_inaddr_prefix_bin(int addr_family, + const char *text, + int * out_addr_family, + gpointer out_addr, + int * out_prefix) +{ + gs_free char *addrstr_free = NULL; + int prefix = -1; + const char * slash; + const char * addrstr; + NMIPAddr addrbin; + + g_return_val_if_fail(text, FALSE); + + if (addr_family == AF_UNSPEC) { + g_return_val_if_fail(!out_addr || out_addr_family, FALSE); + addr_family = strchr(text, ':') ? AF_INET6 : AF_INET; + } else + g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE); + + slash = strchr(text, '/'); + if (slash) + addrstr = nm_strndup_a(300, text, slash - text, &addrstr_free); + else + addrstr = text; + + if (inet_pton(addr_family, addrstr, &addrbin) != 1) + return FALSE; + + if (slash) { + /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't + * do that. */ + prefix = + _nm_utils_ascii_str_to_int64(&slash[1], 10, 0, addr_family == AF_INET ? 32 : 128, -1); + if (prefix == -1) + return FALSE; + } + + NM_SET_OUT(out_addr_family, addr_family); + if (out_addr) + nm_ip_addr_set(addr_family, out_addr, &addrbin); + 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) +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)]; + NMIPAddr addrbin; + char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)]; - if (!nm_utils_parse_inaddr_prefix_bin (addr_family, text, &addr_family, &addrbin, out_prefix)) - return FALSE; - NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf)))); - return TRUE; + if (!nm_utils_parse_inaddr_prefix_bin(addr_family, text, &addr_family, &addrbin, out_prefix)) + 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_next_line (const char **inout_ptr, - gsize *inout_len, - const char **out_line, - gsize *out_line_len) -{ - const char *line_start; - const char *line_end; - - g_return_val_if_fail (inout_ptr, FALSE); - g_return_val_if_fail (inout_len, FALSE); - g_return_val_if_fail (out_line, FALSE); - - if (*inout_len <= 0) - goto error; - - line_start = *inout_ptr; - line_end = memchr (line_start, '\n', *inout_len); - if (!line_end) - line_end = memchr (line_start, '\0', *inout_len); - if (!line_end) { - line_end = line_start + *inout_len; - NM_SET_OUT (inout_len, 0); - } else - NM_SET_OUT (inout_len, *inout_len - (line_end - line_start) - 1); - - NM_SET_OUT (out_line, line_start); - NM_SET_OUT (out_line_len, (gsize) (line_end - line_start)); - - if (*inout_len > 0) - NM_SET_OUT (inout_ptr, line_end + 1); - else - NM_SET_OUT (inout_ptr, NULL); - return TRUE; - -error: - NM_SET_OUT (out_line, NULL); - NM_SET_OUT (out_line_len, 0); - return FALSE; +nm_utils_parse_next_line(const char **inout_ptr, + gsize * inout_len, + const char **out_line, + gsize * out_line_len) +{ + gboolean eol_is_carriage_return; + const char *line_start; + gsize line_len; + + nm_assert(inout_ptr); + nm_assert(inout_len); + nm_assert(*inout_len == 0 || *inout_ptr); + nm_assert(out_line); + nm_assert(out_line_len); + + if (G_UNLIKELY(*inout_len == 0)) + return FALSE; + + line_start = *inout_ptr; + + eol_is_carriage_return = FALSE; + for (line_len = 0;; line_len++) { + if (line_len >= *inout_len) { + /* if we consumed the entire line, we place the pointer at + * one character after the end. */ + *inout_ptr = &line_start[line_len]; + *inout_len = 0; + goto done; + } + switch (line_start[line_len]) { + case '\r': + eol_is_carriage_return = TRUE; + /* fall-through*/ + case '\0': + case '\n': + *inout_ptr = &line_start[line_len + 1]; + *inout_len = *inout_len - line_len - 1u; + if (eol_is_carriage_return && *inout_len > 0 && (*inout_ptr)[0] == '\n') { + /* also consume "\r\n" as one. */ + (*inout_len)--; + (*inout_ptr)++; + } + goto done; + } + } + +done: + *out_line = line_start; + *out_line_len = line_len; + return TRUE; } /*****************************************************************************/ gboolean -nm_utils_ipaddr_is_valid (int addr_family, - const char *str_addr) +nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr) { - nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - return str_addr - && nm_utils_parse_inaddr_bin (addr_family, - str_addr, - NULL, - NULL); + return str_addr && nm_utils_parse_inaddr_bin(addr_family, str_addr, NULL, NULL); } gboolean -nm_utils_ipaddr_is_normalized (int addr_family, - const char *str_addr) +nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr) { - NMIPAddr addr; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + NMIPAddr addr; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - if (!str_addr) - return FALSE; + if (!str_addr) + return FALSE; - if (!nm_utils_parse_inaddr_bin (addr_family, - str_addr, - &addr_family, - &addr)) - return FALSE; + if (!nm_utils_parse_inaddr_bin(addr_family, str_addr, &addr_family, &addr)) + return FALSE; - nm_utils_inet_ntop (addr_family, &addr, sbuf); - return nm_streq (sbuf, str_addr); + nm_utils_inet_ntop(addr_family, &addr, sbuf); + return nm_streq(sbuf, str_addr); } /*****************************************************************************/ @@ -1168,137 +1129,124 @@ nm_utils_ipaddr_is_normalized (int addr_family, * This wrapper tries to workaround that condition. */ gint64 -nm_g_ascii_strtoll (const char *nptr, - char **endptr, - guint base) +nm_g_ascii_strtoll(const char *nptr, char **endptr, guint base) { - int try_count = 2; - gint64 v; - const int errsv_orig = errno; - int errsv; + int try_count = 2; + gint64 v; + const int errsv_orig = errno; + int errsv; - nm_assert (nptr); - nm_assert (base == 0u || (base >= 2u && base <= 36u)); + nm_assert(nptr); + nm_assert(base == 0u || (base >= 2u && base <= 36u)); again: - errno = 0; - v = g_ascii_strtoll (nptr, endptr, base); - errsv = errno; + errno = 0; + v = g_ascii_strtoll(nptr, endptr, base); + errsv = errno; - if (errsv == 0) { - if (errsv_orig != 0) - errno = errsv_orig; - return v; - } + if (errsv == 0) { + if (errsv_orig != 0) + errno = errsv_orig; + return v; + } - if ( errsv == ERANGE - && NM_IN_SET (v, G_MININT64, G_MAXINT64)) - return v; + if (errsv == ERANGE && NM_IN_SET(v, G_MININT64, G_MAXINT64)) + return v; - if ( errsv == EINVAL - && v == 0 - && nptr - && nptr[0] == '\0') - return v; + if (errsv == EINVAL && v == 0 && nptr && nptr[0] == '\0') + return v; - if (try_count-- > 0) - goto again; + if (try_count-- > 0) + goto again; #if NM_MORE_ASSERTS - g_critical ("g_ascii_strtoll() for \"%s\" failed with errno=%d (%s) and v=%"G_GINT64_FORMAT, - nptr, - errsv, - nm_strerror_native (errsv), - v); + g_critical("g_ascii_strtoll() for \"%s\" failed with errno=%d (%s) and v=%" G_GINT64_FORMAT, + nptr, + errsv, + nm_strerror_native(errsv), + v); #endif - return v; + return v; } /* See nm_g_ascii_strtoll() */ guint64 -nm_g_ascii_strtoull (const char *nptr, - char **endptr, - guint base) +nm_g_ascii_strtoull(const char *nptr, char **endptr, guint base) { - int try_count = 2; - guint64 v; - const int errsv_orig = errno; - int errsv; + int try_count = 2; + guint64 v; + const int errsv_orig = errno; + int errsv; - nm_assert (nptr); - nm_assert (base == 0u || (base >= 2u && base <= 36u)); + nm_assert(nptr); + nm_assert(base == 0u || (base >= 2u && base <= 36u)); again: - errno = 0; - v = g_ascii_strtoull (nptr, endptr, base); - errsv = errno; + errno = 0; + v = g_ascii_strtoull(nptr, endptr, base); + errsv = errno; - if (errsv == 0) { - if (errsv_orig != 0) - errno = errsv_orig; - return v; - } + if (errsv == 0) { + if (errsv_orig != 0) + errno = errsv_orig; + return v; + } - if ( errsv == ERANGE - && NM_IN_SET (v, G_MAXUINT64)) - return v; + if (errsv == ERANGE && NM_IN_SET(v, G_MAXUINT64)) + return v; - if ( errsv == EINVAL - && v == 0 - && nptr - && nptr[0] == '\0') - return v; + if (errsv == EINVAL && v == 0 && nptr && nptr[0] == '\0') + return v; - if (try_count-- > 0) - goto again; + if (try_count-- > 0) + goto again; #if NM_MORE_ASSERTS - g_critical ("g_ascii_strtoull() for \"%s\" failed with errno=%d (%s) and v=%"G_GUINT64_FORMAT, - nptr, - errsv, - nm_strerror_native (errsv), - v); + g_critical("g_ascii_strtoull() for \"%s\" failed with errno=%d (%s) and v=%" G_GUINT64_FORMAT, + nptr, + errsv, + nm_strerror_native(errsv), + v); #endif - return v; + return v; } /* see nm_g_ascii_strtoll(). */ double -nm_g_ascii_strtod (const char *nptr, - char **endptr) +nm_g_ascii_strtod(const char *nptr, char **endptr) { - int try_count = 2; - double v; - int errsv; + int try_count = 2; + double v; + int errsv; - nm_assert (nptr); + nm_assert(nptr); again: - v = g_ascii_strtod (nptr, endptr); - errsv = errno; + v = g_ascii_strtod(nptr, endptr); + errsv = errno; - if (errsv == 0) - return v; + if (errsv == 0) + return v; - if (errsv == ERANGE) - return v; + if (errsv == ERANGE) + return v; - if (try_count-- > 0) - goto again; + if (try_count-- > 0) + goto again; #if NM_MORE_ASSERTS - g_critical ("g_ascii_strtod() for \"%s\" failed with errno=%d (%s) and v=%f", - nptr, - errsv, - nm_strerror_native (errsv), - v); + g_critical("g_ascii_strtod() for \"%s\" failed with errno=%d (%s) and v=%f", + nptr, + errsv, + nm_strerror_native(errsv), + v); #endif - /* Not really much else to do. Return the parsed value and leave errno set - * to the unexpected value. */ - return v; + /* Not really much else to do. Return the parsed value and leave errno set + * to the unexpected value. */ + return v; } /* _nm_utils_ascii_str_to_int64: @@ -1316,91 +1264,94 @@ again: * white space. **/ gint64 -_nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback) +_nm_utils_ascii_str_to_int64(const char *str, guint base, gint64 min, gint64 max, gint64 fallback) { - gint64 v; - const char *s = NULL; + gint64 v; + const char *s = NULL; - str = nm_str_skip_leading_spaces (str); - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } + str = nm_str_skip_leading_spaces(str); + if (!str || !str[0]) { + errno = EINVAL; + return fallback; + } - errno = 0; - v = nm_g_ascii_strtoll (str, (char **) &s, base); + errno = 0; + v = nm_g_ascii_strtoll(str, (char **) &s, base); - if (errno != 0) - return fallback; + if (errno != 0) + return fallback; - if (s[0] != '\0') { - s = nm_str_skip_leading_spaces (s); - if (s[0] != '\0') { - errno = EINVAL; - return fallback; - } - } - if (v > max || v < min) { - errno = ERANGE; - return fallback; - } + if (s[0] != '\0') { + s = nm_str_skip_leading_spaces(s); + if (s[0] != '\0') { + errno = EINVAL; + return fallback; + } + } + if (v > max || v < min) { + errno = ERANGE; + return fallback; + } - return v; + return v; } guint64 -_nm_utils_ascii_str_to_uint64 (const char *str, guint base, guint64 min, guint64 max, guint64 fallback) -{ - guint64 v; - const char *s = NULL; - - if (str) { - while (g_ascii_isspace (str[0])) - str++; - } - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } - - errno = 0; - v = nm_g_ascii_strtoull (str, (char **) &s, base); - - if (errno != 0) - return fallback; - if (s[0] != '\0') { - while (g_ascii_isspace (s[0])) - s++; - if (s[0] != '\0') { - errno = EINVAL; - return fallback; - } - } - if (v > max || v < min) { - errno = ERANGE; - return fallback; - } - - if ( v != 0 - && str[0] == '-') { - /* As documented, g_ascii_strtoull() accepts negative values, and returns their - * absolute value. We don't. */ - errno = ERANGE; - return fallback; - } - - return v; +_nm_utils_ascii_str_to_uint64(const char *str, + guint base, + guint64 min, + guint64 max, + guint64 fallback) +{ + guint64 v; + const char *s = NULL; + + if (str) { + while (g_ascii_isspace(str[0])) + str++; + } + if (!str || !str[0]) { + errno = EINVAL; + return fallback; + } + + errno = 0; + v = nm_g_ascii_strtoull(str, (char **) &s, base); + + if (errno != 0) + return fallback; + if (s[0] != '\0') { + while (g_ascii_isspace(s[0])) + s++; + if (s[0] != '\0') { + errno = EINVAL; + return fallback; + } + } + if (v > max || v < min) { + errno = ERANGE; + return fallback; + } + + if (v != 0 && str[0] == '-') { + /* As documented, g_ascii_strtoull() accepts negative values, and returns their + * absolute value. We don't. */ + errno = ERANGE; + return fallback; + } + + return v; } /*****************************************************************************/ int -nm_strcmp_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +nm_strcmp_with_data(gconstpointer a, gconstpointer b, gpointer user_data) { - const char *s1 = a; - const char *s2 = b; + const char *s1 = a; + const char *s2 = b; - return strcmp (s1, s2); + return strcmp(s1, s2); } /* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). @@ -1411,202 +1362,220 @@ nm_strcmp_with_data (gconstpointer a, gconstpointer b, gpointer user_data) * For NetworkManager, we'd rather avoid such stunts. **/ int -nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +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_strcmp0_p_with_data(gconstpointer a, gconstpointer b, gpointer user_data) { - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); - return strcmp (s1, s2); + return nm_strcmp0(s1, s2); } int -nm_strcmp0_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +nm_strcmp_ascii_case_with_data(gconstpointer a, gconstpointer b, gpointer user_data) { - const char *s1 = *((const char **) a); - const char *s2 = *((const char **) b); + const char *s1 = a; + const char *s2 = b; - return nm_strcmp0 (s1, s2); + return g_ascii_strcasecmp(s1, s2); } int -nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data) +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); + 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; + 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) +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)); + /* 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; + 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) +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; + 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; +_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; +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 convertible 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; + 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_CMP_DIRECT_STRCMP0(l_a, l_b); + nm_assert(nm_streq(dbus_path_a, dbus_path_b)); + return 0; } /*****************************************************************************/ +typedef struct { + union { + guint8 table[256]; + guint64 _dummy_for_alignment; + }; +} CharLookupTable; + static void -_char_lookup_table_set_one (guint8 lookup[static 256], - char ch) +_char_lookup_table_set_one(CharLookupTable *lookup, char ch) { - lookup[(guint8) ch] = 1; + lookup->table[(guint8) ch] = 1; } static void -_char_lookup_table_set_all (guint8 lookup[static 256], - const char *candidates) +_char_lookup_table_set_all(CharLookupTable *lookup, const char *candidates) { - while (candidates[0] != '\0') - _char_lookup_table_set_one (lookup, (candidates++)[0]); + while (candidates[0] != '\0') + _char_lookup_table_set_one(lookup, (candidates++)[0]); } static void -_char_lookup_table_init (guint8 lookup[static 256], - const char *candidates) +_char_lookup_table_init(CharLookupTable *lookup, const char *candidates) { - memset (lookup, 0, 256); - if (candidates) - _char_lookup_table_set_all (lookup, candidates); + *lookup = (CharLookupTable){ + .table = {0}, + }; + if (candidates) + _char_lookup_table_set_all(lookup, candidates); } static gboolean -_char_lookup_has (const guint8 lookup[static 256], - char ch) +_char_lookup_has(const CharLookupTable *lookup, char ch) { - nm_assert (lookup[(guint8) '\0'] == 0); - return lookup[(guint8) ch] != 0; + /* with some optimization levels, the compiler thinks this code + * might access uninitialized @lookup. It is not -- when you look at the + * callers of this function. */ + NM_PRAGMA_WARNING_DISABLE("-Wmaybe-uninitialized") + nm_assert(lookup->table[(guint8) '\0'] == 0); + return lookup->table[(guint8) ch] != 0; + NM_PRAGMA_WARNING_REENABLE } static gboolean -_char_lookup_has_all (const guint8 lookup[static 256], - const char *candidates) +_char_lookup_has_all(const CharLookupTable *lookup, const char *candidates) { - if (candidates) { - while (candidates[0] != '\0') { - if (!_char_lookup_has (lookup, (candidates++)[0])) - return FALSE; - } - } - return TRUE; + if (candidates) { + while (candidates[0] != '\0') { + if (!_char_lookup_has(lookup, (candidates++)[0])) + return FALSE; + } + } + return TRUE; } /** @@ -1635,344 +1604,329 @@ _char_lookup_has_all (const guint8 lookup[static 256], * like "g_strstrip((char *) iter[0])". */ const char ** -nm_utils_strsplit_set_full (const char *str, - const char *delimiters, - NMUtilsStrsplitSetFlags flags) -{ - const char **ptr; - gsize num_tokens; - gsize i_token; - gsize str_len_p1; - const char *c_str; - char *s; - guint8 ch_lookup[256]; - const gboolean f_escaped = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED); - const gboolean f_allow_escaping = f_escaped || NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING); - const gboolean f_preserve_empty = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY); - const gboolean f_strstrip = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); - - if (!str) - return NULL; - - if (!delimiters) { - nm_assert_not_reached (); - delimiters = " \t\n"; - } - _char_lookup_table_init (ch_lookup, delimiters); - - nm_assert ( !f_allow_escaping - || !_char_lookup_has (ch_lookup, '\\')); - - if (!f_preserve_empty) { - while (_char_lookup_has (ch_lookup, str[0])) - str++; - } - - if (!str[0]) { - /* We return %NULL here, also with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY. - * That makes nm_utils_strsplit_set_full() with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY - * different from g_strsplit_set(), which would in this case return an empty array. - * If you need to handle %NULL, and "" specially, then check the input string first. */ - return NULL; - } - -#define _char_is_escaped(str_start, str_cur) \ - ({ \ - const char *const _str_start = (str_start); \ - const char *const _str_cur = (str_cur); \ - const char *_str_i = (_str_cur); \ - \ - while ( _str_i > _str_start \ - && _str_i[-1] == '\\') \ - _str_i--; \ - (((_str_cur - _str_i) % 2) != 0); \ - }) - - num_tokens = 1; - c_str = str; - while (TRUE) { - - while (G_LIKELY (!_char_lookup_has (ch_lookup, c_str[0]))) { - if (c_str[0] == '\0') - goto done1; - c_str++; - } - - /* we assume escapings are not frequent. After we found - * this delimiter, check whether it was escaped by counting - * the backslashed before. */ - if ( f_allow_escaping - && _char_is_escaped (str, c_str)) { - /* the delimiter is escaped. This was not an accepted delimiter. */ - c_str++; - continue; - } - - c_str++; - - /* if we drop empty tokens, then we now skip over all consecutive delimiters. */ - if (!f_preserve_empty) { - while (_char_lookup_has (ch_lookup, c_str[0])) - c_str++; - if (c_str[0] == '\0') - break; - } - - num_tokens++; - } +nm_utils_strsplit_set_full(const char *str, const char *delimiters, NMUtilsStrsplitSetFlags flags) +{ + const char ** ptr; + gsize num_tokens; + gsize i_token; + gsize str_len_p1; + const char * c_str; + char * s; + CharLookupTable ch_lookup; + const gboolean f_escaped = NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED); + const gboolean f_allow_escaping = + f_escaped || NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING); + const gboolean f_preserve_empty = + NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY); + const gboolean f_strstrip = NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP); + + if (!str) + return NULL; + + if (!delimiters) { + nm_assert_not_reached(); + delimiters = " \t\n"; + } + _char_lookup_table_init(&ch_lookup, delimiters); + + nm_assert(!f_allow_escaping || !_char_lookup_has(&ch_lookup, '\\')); + + if (!f_preserve_empty) { + while (_char_lookup_has(&ch_lookup, str[0])) + str++; + } + + if (!str[0]) { + /* We return %NULL here, also with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY. + * That makes nm_utils_strsplit_set_full() with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY + * different from g_strsplit_set(), which would in this case return an empty array. + * If you need to handle %NULL, and "" specially, then check the input string first. */ + return NULL; + } + +#define _char_is_escaped(str_start, str_cur) \ + ({ \ + const char *const _str_start = (str_start); \ + const char *const _str_cur = (str_cur); \ + const char * _str_i = (_str_cur); \ + \ + while (_str_i > _str_start && _str_i[-1] == '\\') \ + _str_i--; \ + (((_str_cur - _str_i) % 2) != 0); \ + }) + + num_tokens = 1; + c_str = str; + while (TRUE) { + while (G_LIKELY(!_char_lookup_has(&ch_lookup, c_str[0]))) { + if (c_str[0] == '\0') + goto done1; + c_str++; + } + + /* we assume escapings are not frequent. After we found + * this delimiter, check whether it was escaped by counting + * the backslashed before. */ + if (f_allow_escaping && _char_is_escaped(str, c_str)) { + /* the delimiter is escaped. This was not an accepted delimiter. */ + c_str++; + continue; + } + + c_str++; + + /* if we drop empty tokens, then we now skip over all consecutive delimiters. */ + if (!f_preserve_empty) { + while (_char_lookup_has(&ch_lookup, c_str[0])) + c_str++; + if (c_str[0] == '\0') + break; + } + + num_tokens++; + } done1: - nm_assert (c_str[0] == '\0'); + nm_assert(c_str[0] == '\0'); - str_len_p1 = (c_str - str) + 1; + str_len_p1 = (c_str - str) + 1; - nm_assert (str[str_len_p1 - 1] == '\0'); + nm_assert(str[str_len_p1 - 1] == '\0'); - ptr = g_malloc ((sizeof (const char *) * (num_tokens + 1)) + str_len_p1); - s = (char *) &ptr[num_tokens + 1]; - memcpy (s, str, str_len_p1); + ptr = g_malloc((sizeof(const char *) * (num_tokens + 1)) + str_len_p1); + s = (char *) &ptr[num_tokens + 1]; + memcpy(s, str, str_len_p1); - i_token = 0; + i_token = 0; - while (TRUE) { + while (TRUE) { + nm_assert(i_token < num_tokens); + ptr[i_token++] = s; - nm_assert (i_token < num_tokens); - ptr[i_token++] = s; + if (s[0] == '\0') { + nm_assert(f_preserve_empty); + goto done2; + } + nm_assert(f_preserve_empty || !_char_lookup_has(&ch_lookup, s[0])); - if (s[0] == '\0') { - nm_assert (f_preserve_empty); - goto done2; - } - nm_assert ( f_preserve_empty - || !_char_lookup_has (ch_lookup, s[0])); + while (!_char_lookup_has(&ch_lookup, s[0])) { + if (G_UNLIKELY(s[0] == '\\' && f_allow_escaping)) { + s++; + if (s[0] == '\0') + goto done2; + s++; + } else if (s[0] == '\0') + goto done2; + else + s++; + } - while (!_char_lookup_has (ch_lookup, s[0])) { - if (G_UNLIKELY ( s[0] == '\\' - && f_allow_escaping)) { - s++; - if (s[0] == '\0') - goto done2; - s++; - } else if (s[0] == '\0') - goto done2; - else - s++; - } + nm_assert(_char_lookup_has(&ch_lookup, s[0])); + s[0] = '\0'; + s++; - nm_assert (_char_lookup_has (ch_lookup, s[0])); - s[0] = '\0'; - s++; - - if (!f_preserve_empty) { - while (_char_lookup_has (ch_lookup, s[0])) - s++; - if (s[0] == '\0') - goto done2; - } - } + if (!f_preserve_empty) { + while (_char_lookup_has(&ch_lookup, s[0])) + s++; + if (s[0] == '\0') + goto done2; + } + } done2: - nm_assert (i_token == num_tokens); - ptr[i_token] = NULL; - - if (f_strstrip) { - gsize i; - - i_token = 0; - for (i = 0; ptr[i]; i++) { - - s = (char *) nm_str_skip_leading_spaces (ptr[i]); - if (s[0] != '\0') { - char *s_last; - - s_last = &s[strlen (s) - 1]; - while ( s_last > s - && g_ascii_isspace (s_last[0]) - && ( ! f_allow_escaping - || !_char_is_escaped (s, s_last))) - (s_last--)[0] = '\0'; - } - - if ( !f_preserve_empty - && s[0] == '\0') - continue; - - ptr[i_token++] = s; - } - - if (i_token == 0) { - g_free (ptr); - return NULL; - } - ptr[i_token] = NULL; - } - - if (f_escaped) { - gsize i, j; - - /* We no longer need ch_lookup for its original purpose. Modify it, so it - * can detect the delimiters, '\\', and (optionally) whitespaces. */ - _char_lookup_table_set_one (ch_lookup, '\\'); - if (f_strstrip) - _char_lookup_table_set_all (ch_lookup, NM_ASCII_SPACES); - - for (i_token = 0; ptr[i_token]; i_token++) { - s = (char *) ptr[i_token]; - j = 0; - for (i = 0; s[i] != '\0'; ) { - if ( s[i] == '\\' - && _char_lookup_has (ch_lookup, s[i + 1])) - i++; - s[j++] = s[i++]; - } - s[j] = '\0'; - } - } - - nm_assert (ptr && ptr[0]); - return ptr; + nm_assert(i_token == num_tokens); + ptr[i_token] = NULL; + + if (f_strstrip) { + gsize i; + + i_token = 0; + for (i = 0; ptr[i]; i++) { + s = (char *) nm_str_skip_leading_spaces(ptr[i]); + if (s[0] != '\0') { + char *s_last; + + s_last = &s[strlen(s) - 1]; + while (s_last > s && g_ascii_isspace(s_last[0]) + && (!f_allow_escaping || !_char_is_escaped(s, s_last))) + (s_last--)[0] = '\0'; + } + + if (!f_preserve_empty && s[0] == '\0') + continue; + + ptr[i_token++] = s; + } + + if (i_token == 0) { + g_free(ptr); + return NULL; + } + ptr[i_token] = NULL; + } + + if (f_escaped) { + gsize i, j; + + /* We no longer need ch_lookup for its original purpose. Modify it, so it + * can detect the delimiters, '\\', and (optionally) whitespaces. */ + _char_lookup_table_set_one(&ch_lookup, '\\'); + if (f_strstrip) + _char_lookup_table_set_all(&ch_lookup, NM_ASCII_SPACES); + + for (i_token = 0; ptr[i_token]; i_token++) { + s = (char *) ptr[i_token]; + j = 0; + for (i = 0; s[i] != '\0';) { + if (s[i] == '\\' && _char_lookup_has(&ch_lookup, s[i + 1])) + i++; + s[j++] = s[i++]; + } + s[j] = '\0'; + } + } + + nm_assert(ptr && ptr[0]); + return ptr; } /*****************************************************************************/ const char * -nm_utils_escaped_tokens_escape_full (const char *str, - const char *delimiters, - const char *delimiters_as_needed, - NMUtilsEscapedTokensEscapeFlags flags, - char **out_to_free) -{ - guint8 ch_lookup[256]; - guint8 ch_lookup_as_needed[256]; - gboolean has_ch_lookup_as_needed = FALSE; - char *ret; - gsize str_len; - gsize alloc_len; - gsize n_escapes; - gsize i, j; - gboolean escape_leading_space; - gboolean escape_trailing_space; - gboolean escape_backslash_as_needed; - - nm_assert ( !delimiters_as_needed - || ( delimiters_as_needed[0] - && NM_FLAGS_HAS (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED))); - - if (!str || str[0] == '\0') { - *out_to_free = NULL; - return str; - } - - str_len = strlen (str); - - _char_lookup_table_init (ch_lookup, delimiters); - if ( !delimiters - || NM_FLAGS_HAS (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_SPACES)) { - flags &= ~( NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE - | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE); - _char_lookup_table_set_all (ch_lookup, NM_ASCII_SPACES); - } - - if (NM_FLAGS_HAS (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS)) { - _char_lookup_table_set_one (ch_lookup, '\\'); - escape_backslash_as_needed = FALSE; - } else if (_char_lookup_has (ch_lookup, '\\')) - escape_backslash_as_needed = FALSE; - else { - escape_backslash_as_needed = NM_FLAGS_HAS (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED); - if (escape_backslash_as_needed) { - if ( NM_FLAGS_ANY (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE - | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE) - && !_char_lookup_has_all (ch_lookup, NM_ASCII_SPACES)) { - /* ESCAPE_LEADING_SPACE and ESCAPE_TRAILING_SPACE implies that we escape backslash - * before whitespaces. */ - if (!has_ch_lookup_as_needed) { - has_ch_lookup_as_needed = TRUE; - _char_lookup_table_init (ch_lookup_as_needed, NULL); - } - _char_lookup_table_set_all (ch_lookup_as_needed, NM_ASCII_SPACES); - } - if ( delimiters_as_needed - && !_char_lookup_has_all (ch_lookup, delimiters_as_needed)) { - if (!has_ch_lookup_as_needed) { - has_ch_lookup_as_needed = TRUE; - _char_lookup_table_init (ch_lookup_as_needed, NULL); - } - _char_lookup_table_set_all (ch_lookup_as_needed, delimiters_as_needed); - } - } - } - - escape_leading_space = NM_FLAGS_HAS (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE) - && g_ascii_isspace (str[0]) - && !_char_lookup_has (ch_lookup, str[0]); - if (str_len == 1) - escape_trailing_space = FALSE; - else { - escape_trailing_space = NM_FLAGS_HAS (flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE) - && g_ascii_isspace (str[str_len - 1]) - && !_char_lookup_has (ch_lookup, str[str_len - 1]); - } - - n_escapes = 0; - for (i = 0; str[i] != '\0'; i++) { - if (_char_lookup_has (ch_lookup, str[i])) - n_escapes++; - else if ( str[i] == '\\' - && escape_backslash_as_needed - && ( _char_lookup_has (ch_lookup, str[i + 1]) - || NM_IN_SET (str[i + 1], '\0', '\\') - || ( has_ch_lookup_as_needed - && _char_lookup_has (ch_lookup_as_needed, str[i + 1])))) - n_escapes++; - } - if (escape_leading_space) - n_escapes++; - if (escape_trailing_space) - n_escapes++; - - if (n_escapes == 0u) { - *out_to_free = NULL; - return str; - } - - alloc_len = str_len + n_escapes + 1u; - ret = g_new (char, alloc_len); - - j = 0; - i = 0; - - if (escape_leading_space) { - ret[j++] = '\\'; - ret[j++] = str[i++]; - } - for (; str[i] != '\0'; i++) { - if (_char_lookup_has (ch_lookup, str[i])) - ret[j++] = '\\'; - else if ( str[i] == '\\' - && escape_backslash_as_needed - && ( _char_lookup_has (ch_lookup, str[i + 1]) - || NM_IN_SET (str[i + 1], '\0', '\\') - || ( has_ch_lookup_as_needed - && _char_lookup_has (ch_lookup_as_needed, str[i + 1])))) - ret[j++] = '\\'; - ret[j++] = str[i]; - } - if (escape_trailing_space) { - nm_assert ( !_char_lookup_has (ch_lookup, ret[j - 1]) - && g_ascii_isspace (ret[j - 1])); - ret[j] = ret[j - 1]; - ret[j - 1] = '\\'; - j++; - } - - nm_assert (j == alloc_len - 1); - ret[j] = '\0'; - nm_assert (strlen (ret) == j); - - *out_to_free = ret; - return ret; +nm_utils_escaped_tokens_escape_full(const char * str, + const char * delimiters, + const char * delimiters_as_needed, + NMUtilsEscapedTokensEscapeFlags flags, + char ** out_to_free) +{ + CharLookupTable ch_lookup; + CharLookupTable ch_lookup_as_needed; + gboolean has_ch_lookup_as_needed = FALSE; + char * ret; + gsize str_len; + gsize alloc_len; + gsize n_escapes; + gsize i, j; + gboolean escape_leading_space; + gboolean escape_trailing_space; + gboolean escape_backslash_as_needed; + + nm_assert( + !delimiters_as_needed + || (delimiters_as_needed[0] + && NM_FLAGS_HAS(flags, + NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED))); + + if (!str || str[0] == '\0') { + *out_to_free = NULL; + return str; + } + + str_len = strlen(str); + + _char_lookup_table_init(&ch_lookup, delimiters); + if (!delimiters || NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_SPACES)) { + flags &= ~(NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE + | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE); + _char_lookup_table_set_all(&ch_lookup, NM_ASCII_SPACES); + } + + if (NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_ALWAYS)) { + _char_lookup_table_set_one(&ch_lookup, '\\'); + escape_backslash_as_needed = FALSE; + } else if (_char_lookup_has(&ch_lookup, '\\')) + escape_backslash_as_needed = FALSE; + else { + escape_backslash_as_needed = + NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_BACKSLASH_AS_NEEDED); + if (escape_backslash_as_needed) { + if (NM_FLAGS_ANY(flags, + NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE + | NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE) + && !_char_lookup_has_all(&ch_lookup, NM_ASCII_SPACES)) { + /* ESCAPE_LEADING_SPACE and ESCAPE_TRAILING_SPACE implies that we escape backslash + * before whitespaces. */ + if (!has_ch_lookup_as_needed) { + has_ch_lookup_as_needed = TRUE; + _char_lookup_table_init(&ch_lookup_as_needed, NULL); + } + _char_lookup_table_set_all(&ch_lookup_as_needed, NM_ASCII_SPACES); + } + if (delimiters_as_needed && !_char_lookup_has_all(&ch_lookup, delimiters_as_needed)) { + if (!has_ch_lookup_as_needed) { + has_ch_lookup_as_needed = TRUE; + _char_lookup_table_init(&ch_lookup_as_needed, NULL); + } + _char_lookup_table_set_all(&ch_lookup_as_needed, delimiters_as_needed); + } + } + } + + escape_leading_space = + NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_LEADING_SPACE) + && g_ascii_isspace(str[0]) && !_char_lookup_has(&ch_lookup, str[0]); + if (str_len == 1) + escape_trailing_space = FALSE; + else { + escape_trailing_space = + NM_FLAGS_HAS(flags, NM_UTILS_ESCAPED_TOKENS_ESCAPE_FLAGS_ESCAPE_TRAILING_SPACE) + && g_ascii_isspace(str[str_len - 1]) && !_char_lookup_has(&ch_lookup, str[str_len - 1]); + } + + n_escapes = 0; + for (i = 0; str[i] != '\0'; i++) { + if (_char_lookup_has(&ch_lookup, str[i])) + n_escapes++; + else if (str[i] == '\\' && escape_backslash_as_needed + && (_char_lookup_has(&ch_lookup, str[i + 1]) || NM_IN_SET(str[i + 1], '\0', '\\') + || (has_ch_lookup_as_needed + && _char_lookup_has(&ch_lookup_as_needed, str[i + 1])))) + n_escapes++; + } + if (escape_leading_space) + n_escapes++; + if (escape_trailing_space) + n_escapes++; + + if (n_escapes == 0u) { + *out_to_free = NULL; + return str; + } + + alloc_len = str_len + n_escapes + 1u; + ret = g_new(char, alloc_len); + + j = 0; + i = 0; + + if (escape_leading_space) { + ret[j++] = '\\'; + ret[j++] = str[i++]; + } + for (; str[i] != '\0'; i++) { + if (_char_lookup_has(&ch_lookup, str[i])) + ret[j++] = '\\'; + else if (str[i] == '\\' && escape_backslash_as_needed + && (_char_lookup_has(&ch_lookup, str[i + 1]) || NM_IN_SET(str[i + 1], '\0', '\\') + || (has_ch_lookup_as_needed + && _char_lookup_has(&ch_lookup_as_needed, str[i + 1])))) + ret[j++] = '\\'; + ret[j++] = str[i]; + } + if (escape_trailing_space) { + nm_assert(!_char_lookup_has(&ch_lookup, ret[j - 1]) && g_ascii_isspace(ret[j - 1])); + ret[j] = ret[j - 1]; + ret[j - 1] = '\\'; + j++; + } + + nm_assert(j == alloc_len - 1); + ret[j] = '\0'; + nm_assert(strlen(ret) == j); + + *out_to_free = ret; + return ret; } /** @@ -1998,66 +1952,63 @@ nm_utils_escaped_tokens_escape_full (const char *str, * not be escaped, unless they are at the beginning or the end of key/value. */ void -nm_utils_escaped_tokens_options_split (char *str, - const char **out_key, - const char **out_val) -{ - const char *val = NULL; - gsize i; - gsize j; - gsize last_space_idx; - gboolean last_space_has; - - nm_assert (str); - - i = 0; - while (g_ascii_isspace (str[i])) - i++; - - j = 0; - last_space_idx = 0; - last_space_has = FALSE; - while (str[i] != '\0') { - if (g_ascii_isspace (str[i])) { - if (!last_space_has) { - last_space_has = TRUE; - last_space_idx = j; - } - } else { - if (str[i] == '\\') { - if ( NM_IN_SET (str[i + 1u], '\\', ',', '=') - || g_ascii_isspace (str[i + 1u])) - i++; - } else if (str[i] == '=') { - /* Encounter an unescaped '=' character. When we still parse the key, this - * is the separator we were waiting for. If we are parsing the value, - * we take the character verbatim. */ - if (!val) { - if (last_space_has) { - str[last_space_idx] = '\0'; - j = last_space_idx + 1; - last_space_has = FALSE; - } else - str[j++] = '\0'; - val = &str[j]; - i++; - while (g_ascii_isspace (str[i])) - i++; - continue; - } - } - last_space_has = FALSE; - } - str[j++] = str[i++]; - } - - if (last_space_has) - str[last_space_idx] = '\0'; - else - str[j] = '\0'; - - *out_key = str; - *out_val = val; +nm_utils_escaped_tokens_options_split(char *str, const char **out_key, const char **out_val) +{ + const char *val = NULL; + gsize i; + gsize j; + gsize last_space_idx; + gboolean last_space_has; + + nm_assert(str); + + i = 0; + while (g_ascii_isspace(str[i])) + i++; + + j = 0; + last_space_idx = 0; + last_space_has = FALSE; + while (str[i] != '\0') { + if (g_ascii_isspace(str[i])) { + if (!last_space_has) { + last_space_has = TRUE; + last_space_idx = j; + } + } else { + if (str[i] == '\\') { + if (NM_IN_SET(str[i + 1u], '\\', ',', '=') || g_ascii_isspace(str[i + 1u])) + i++; + } else if (str[i] == '=') { + /* Encounter an unescaped '=' character. When we still parse the key, this + * is the separator we were waiting for. If we are parsing the value, + * we take the character verbatim. */ + if (!val) { + if (last_space_has) { + str[last_space_idx] = '\0'; + j = last_space_idx + 1; + last_space_has = FALSE; + } else + str[j++] = '\0'; + val = &str[j]; + i++; + while (g_ascii_isspace(str[i])) + i++; + continue; + } + } + last_space_has = FALSE; + } + str[j++] = str[i++]; + } + + if (last_space_has) + str[last_space_idx] = '\0'; + else + str[j] = '\0'; + + *out_key = str; + *out_val = val; } /*****************************************************************************/ @@ -2078,80 +2029,80 @@ nm_utils_escaped_tokens_options_split (char *str, * Returns: (transfer full): the split string. */ char ** -nm_utils_strsplit_quoted (const char *str) -{ - gs_unref_ptrarray GPtrArray *arr = NULL; - gs_free char *str_out = NULL; - guint8 ch_lookup[256]; - - nm_assert (str); - - _char_lookup_table_init (ch_lookup, NM_ASCII_WHITESPACES); - - for (;;) { - char quote; - gsize j; - - while (_char_lookup_has (ch_lookup, str[0])) - str++; - - if (str[0] == '\0') - break; - - if (!str_out) - str_out = g_new (char, strlen (str) + 1); - - quote = '\0'; - j = 0; - for (;;) { - if (str[0] == '\\') { - str++; - if (str[0] == '\0') - break; - str_out[j++] = str[0]; - str++; - continue; - } - if (quote) { - if (str[0] == '\0') - break; - if (str[0] == quote) { - quote = '\0'; - str++; - continue; - } - str_out[j++] = str[0]; - str++; - continue; - } - if (str[0] == '\0') - break; - if (NM_IN_SET (str[0], '\'', '"')) { - quote = str[0]; - str++; - continue; - } - if (_char_lookup_has (ch_lookup, str[0])) { - str++; - break; - } - str_out[j++] = str[0]; - str++; - } - - if (!arr) - arr = g_ptr_array_new (); - g_ptr_array_add (arr, g_strndup (str_out, j)); - } - - if (!arr) - return g_new0 (char *, 1); - - g_ptr_array_add (arr, NULL); - - /* We want to return an optimally sized strv array, with no excess - * memory allocated. Hence, clone once more. */ - return nm_memdup (arr->pdata, sizeof (char *) * arr->len); +nm_utils_strsplit_quoted(const char *str) +{ + gs_unref_ptrarray GPtrArray *arr = NULL; + gs_free char * str_out = NULL; + CharLookupTable ch_lookup; + + nm_assert(str); + + _char_lookup_table_init(&ch_lookup, NM_ASCII_WHITESPACES); + + for (;;) { + char quote; + gsize j; + + while (_char_lookup_has(&ch_lookup, str[0])) + str++; + + if (str[0] == '\0') + break; + + if (!str_out) + str_out = g_new(char, strlen(str) + 1); + + quote = '\0'; + j = 0; + for (;;) { + if (str[0] == '\\') { + str++; + if (str[0] == '\0') + break; + str_out[j++] = str[0]; + str++; + continue; + } + if (quote) { + if (str[0] == '\0') + break; + if (str[0] == quote) { + quote = '\0'; + str++; + continue; + } + str_out[j++] = str[0]; + str++; + continue; + } + if (str[0] == '\0') + break; + if (NM_IN_SET(str[0], '\'', '"')) { + quote = str[0]; + str++; + continue; + } + if (_char_lookup_has(&ch_lookup, str[0])) { + str++; + break; + } + str_out[j++] = str[0]; + str++; + } + + if (!arr) + arr = g_ptr_array_new(); + g_ptr_array_add(arr, g_strndup(str_out, j)); + } + + if (!arr) + return g_new0(char *, 1); + + g_ptr_array_add(arr, NULL); + + /* We want to return an optimally sized strv array, with no excess + * memory allocated. Hence, clone once more. */ + return nm_memdup(arr->pdata, sizeof(char *) * arr->len); } /*****************************************************************************/ @@ -2170,140 +2121,154 @@ nm_utils_strsplit_quoted (const char *str) * Returns: index of first occurrence or -1 if @needle is not found in @list. */ gssize -nm_utils_strv_find_first (char **list, gssize len, const char *needle) -{ - gssize i; - - if (len > 0) { - g_return_val_if_fail (list, -1); - - if (!needle) { - /* if we search a list with known length, %NULL is a valid @needle. */ - for (i = 0; i < len; i++) { - if (!list[i]) - return i; - } - } else { - for (i = 0; i < len; i++) { - if (list[i] && !strcmp (needle, list[i])) - return i; - } - } - } else if (len < 0) { - g_return_val_if_fail (needle, -1); - - if (list) { - for (i = 0; list[i]; i++) { - if (strcmp (needle, list[i]) == 0) - return i; - } - } - } - return -1; +nm_utils_strv_find_first(char **list, gssize len, const char *needle) +{ + gssize i; + + if (len > 0) { + g_return_val_if_fail(list, -1); + + if (!needle) { + /* if we search a list with known length, %NULL is a valid @needle. */ + for (i = 0; i < len; i++) { + if (!list[i]) + return i; + } + } else { + for (i = 0; i < len; i++) { + if (list[i] && !strcmp(needle, list[i])) + return i; + } + } + } else if (len < 0) { + g_return_val_if_fail(needle, -1); + + if (list) { + for (i = 0; list[i]; i++) { + if (strcmp(needle, list[i]) == 0) + return i; + } + } + } + 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) { - /* we only modify the strings pointed to by @strv if @strip_whitespace is - * requested. Otherwise, the strings themselves are untouched. */ - 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; +_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) { + /* we only modify the strings pointed to by @strv if @strip_whitespace is + * requested. Otherwise, the strings themselves are untouched. */ + 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; +} + +/*****************************************************************************/ + +GPtrArray * +_nm_g_ptr_array_copy(GPtrArray * array, + GCopyFunc func, + gpointer user_data, + GDestroyNotify element_free_func) +{ + GPtrArray *new_array; + guint i; + + g_return_val_if_fail(array, NULL); + + new_array = g_ptr_array_new_full(array->len, element_free_func); + for (i = 0; i < array->len; i++) { + g_ptr_array_add(new_array, func ? func(array->pdata[i], user_data) : array->pdata[i]); + } + return new_array; } /*****************************************************************************/ int -_nm_utils_ascii_str_to_bool (const char *str, - int default_value) +_nm_utils_ascii_str_to_bool(const char *str, int default_value) { - gs_free char *str_free = NULL; + gs_free char *str_free = NULL; - if (!str) - return default_value; + if (!str) + return default_value; - str = nm_strstrip_avoid_copy_a (300, str, &str_free); - if (str[0] == '\0') - return default_value; + str = nm_strstrip_avoid_copy_a(300, str, &str_free); + if (str[0] == '\0') + return default_value; - if ( !g_ascii_strcasecmp (str, "true") - || !g_ascii_strcasecmp (str, "yes") - || !g_ascii_strcasecmp (str, "on") - || !g_ascii_strcasecmp (str, "1")) - return TRUE; + if (!g_ascii_strcasecmp(str, "true") || !g_ascii_strcasecmp(str, "yes") + || !g_ascii_strcasecmp(str, "on") || !g_ascii_strcasecmp(str, "1")) + return TRUE; - if ( !g_ascii_strcasecmp (str, "false") - || !g_ascii_strcasecmp (str, "no") - || !g_ascii_strcasecmp (str, "off") - || !g_ascii_strcasecmp (str, "0")) - return FALSE; + if (!g_ascii_strcasecmp(str, "false") || !g_ascii_strcasecmp(str, "no") + || !g_ascii_strcasecmp(str, "off") || !g_ascii_strcasecmp(str, "0")) + return FALSE; - return default_value; + return default_value; } /*****************************************************************************/ -NM_CACHED_QUARK_FCN ("nm-utils-error-quark", nm_utils_error_quark) +NM_CACHED_QUARK_FCN("nm-utils-error-quark", nm_utils_error_quark); void -nm_utils_error_set_cancelled (GError **error, - gboolean is_disposing, - const char *instance_name) +nm_utils_error_set_cancelled(GError **error, gboolean is_disposing, const char *instance_name) { - if (is_disposing) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING, - "Disposing %s instance", - instance_name && *instance_name ? instance_name : "source"); - } else { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, - "Request cancelled"); - } + if (is_disposing) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_CANCELLED_DISPOSING, + "Disposing %s instance", + instance_name && *instance_name ? instance_name : "source"); + } else { + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Request cancelled"); + } } gboolean -nm_utils_error_is_cancelled_or_disposing (GError *error) +nm_utils_error_is_cancelled_or_disposing(GError *error) { - if (error) { - if (error->domain == G_IO_ERROR) - return NM_IN_SET (error->code, G_IO_ERROR_CANCELLED); - if (error->domain == NM_UTILS_ERROR) - return NM_IN_SET (error->code, NM_UTILS_ERROR_CANCELLED_DISPOSING); - } - return FALSE; + if (error) { + if (error->domain == G_IO_ERROR) + return NM_IN_SET(error->code, G_IO_ERROR_CANCELLED); + if (error->domain == NM_UTILS_ERROR) + return NM_IN_SET(error->code, NM_UTILS_ERROR_CANCELLED_DISPOSING); + } + return FALSE; } gboolean -nm_utils_error_is_notfound (GError *error) +nm_utils_error_is_notfound(GError *error) { - if (error) { - if (error->domain == G_IO_ERROR) - return NM_IN_SET (error->code, G_IO_ERROR_NOT_FOUND); - if (error->domain == G_FILE_ERROR) - return NM_IN_SET (error->code, G_FILE_ERROR_NOENT); - } - return FALSE; + if (error) { + if (error->domain == G_IO_ERROR) + return NM_IN_SET(error->code, G_IO_ERROR_NOT_FOUND); + if (error->domain == G_FILE_ERROR) + return NM_IN_SET(error->code, G_FILE_ERROR_NOENT); + } + return FALSE; } /*****************************************************************************/ @@ -2324,220 +2289,233 @@ nm_utils_error_is_notfound (GError *error) * Returns: whether the value was successfully set. */ gboolean -nm_g_object_set_property (GObject *object, - const char *property_name, - const GValue *value, - GError **error) -{ - GParamSpec *pspec; - nm_auto_unset_gvalue GValue tmp_value = G_VALUE_INIT; - GObjectClass *klass; - - g_return_val_if_fail (G_IS_OBJECT (object), FALSE); - g_return_val_if_fail (property_name != NULL, FALSE); - g_return_val_if_fail (G_IS_VALUE (value), FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - /* g_object_class_find_property() does g_param_spec_get_redirect_target(), - * where we differ from a plain g_object_set_property(). */ - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name); - - if (!pspec) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("object class '%s' has no property named '%s'"), - G_OBJECT_TYPE_NAME (object), - property_name); - return FALSE; - } - if (!(pspec->flags & G_PARAM_WRITABLE)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("property '%s' of object class '%s' is not writable"), - pspec->name, - G_OBJECT_TYPE_NAME (object)); - return FALSE; - } - if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("construct property \"%s\" for object '%s' can't be set after construction"), - pspec->name, G_OBJECT_TYPE_NAME (object)); - return FALSE; - } - - klass = g_type_class_peek (pspec->owner_type); - if (klass == NULL) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("'%s::%s' is not a valid property name; '%s' is not a GObject subtype"), - g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->owner_type)); - return FALSE; - } - - /* provide a copy to work from, convert (if necessary) and validate */ - g_value_init (&tmp_value, pspec->value_type); - if (!g_value_transform (value, &tmp_value)) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("unable to set property '%s' of type '%s' from value of type '%s'"), - pspec->name, - g_type_name (pspec->value_type), - G_VALUE_TYPE_NAME (value)); - return FALSE; - } - if ( g_param_value_validate (pspec, &tmp_value) - && !(pspec->flags & G_PARAM_LAX_VALIDATION)) { - gs_free char *contents = g_strdup_value_contents (value); - - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("value \"%s\" of type '%s' is invalid or out of range for property '%s' of type '%s'"), - contents, - G_VALUE_TYPE_NAME (value), - pspec->name, - g_type_name (pspec->value_type)); - return FALSE; - } - - g_object_set_property (object, property_name, &tmp_value); - return TRUE; -} - -#define _set_property(object, property_name, gtype, gtype_set, value, error) \ - G_STMT_START { \ - nm_auto_unset_gvalue GValue gvalue = { 0 }; \ - \ - g_value_init (&gvalue, gtype); \ - gtype_set (&gvalue, (value)); \ - return nm_g_object_set_property ((object), (property_name), &gvalue, (error)); \ - } G_STMT_END +nm_g_object_set_property(GObject * object, + const char * property_name, + const GValue *value, + GError ** error) +{ + GParamSpec * pspec; + nm_auto_unset_gvalue GValue tmp_value = G_VALUE_INIT; + GObjectClass * klass; + + g_return_val_if_fail(G_IS_OBJECT(object), FALSE); + g_return_val_if_fail(property_name != NULL, FALSE); + g_return_val_if_fail(G_IS_VALUE(value), FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + /* g_object_class_find_property() does g_param_spec_get_redirect_target(), + * where we differ from a plain g_object_set_property(). */ + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name); + + if (!pspec) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("object class '%s' has no property named '%s'"), + G_OBJECT_TYPE_NAME(object), + property_name); + return FALSE; + } + if (!(pspec->flags & G_PARAM_WRITABLE)) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("property '%s' of object class '%s' is not writable"), + pspec->name, + G_OBJECT_TYPE_NAME(object)); + return FALSE; + } + if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY)) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("construct property \"%s\" for object '%s' can't be set after construction"), + pspec->name, + G_OBJECT_TYPE_NAME(object)); + return FALSE; + } + + klass = g_type_class_peek(pspec->owner_type); + if (klass == NULL) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("'%s::%s' is not a valid property name; '%s' is not a GObject subtype"), + g_type_name(pspec->owner_type), + pspec->name, + g_type_name(pspec->owner_type)); + return FALSE; + } + + /* provide a copy to work from, convert (if necessary) and validate */ + g_value_init(&tmp_value, pspec->value_type); + if (!g_value_transform(value, &tmp_value)) { + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("unable to set property '%s' of type '%s' from value of type '%s'"), + pspec->name, + g_type_name(pspec->value_type), + G_VALUE_TYPE_NAME(value)); + return FALSE; + } + if (g_param_value_validate(pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION)) { + gs_free char *contents = g_strdup_value_contents(value); + + g_set_error(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("value \"%s\" of type '%s' is invalid or out of range for property '%s' of " + "type '%s'"), + contents, + G_VALUE_TYPE_NAME(value), + pspec->name, + g_type_name(pspec->value_type)); + return FALSE; + } + + g_object_set_property(object, property_name, &tmp_value); + return TRUE; +} + +#define _set_property(object, property_name, gtype, gtype_set, value, error) \ + G_STMT_START \ + { \ + nm_auto_unset_gvalue GValue gvalue = {0}; \ + \ + g_value_init(&gvalue, gtype); \ + gtype_set(&gvalue, (value)); \ + return nm_g_object_set_property((object), (property_name), &gvalue, (error)); \ + } \ + G_STMT_END gboolean -nm_g_object_set_property_string (GObject *object, - const char *property_name, - const char *value, - GError **error) +nm_g_object_set_property_string(GObject * object, + const char *property_name, + const char *value, + GError ** error) { - _set_property (object, property_name, G_TYPE_STRING, g_value_set_string, value, error); + _set_property(object, property_name, G_TYPE_STRING, g_value_set_string, value, error); } gboolean -nm_g_object_set_property_string_static (GObject *object, - const char *property_name, - const char *value, - GError **error) +nm_g_object_set_property_string_static(GObject * object, + const char *property_name, + const char *value, + GError ** error) { - _set_property (object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error); + _set_property(object, property_name, G_TYPE_STRING, g_value_set_static_string, value, error); } gboolean -nm_g_object_set_property_string_take (GObject *object, - const char *property_name, - char *value, - GError **error) +nm_g_object_set_property_string_take(GObject * object, + const char *property_name, + char * value, + GError ** error) { - _set_property (object, property_name, G_TYPE_STRING, g_value_take_string, value, error); + _set_property(object, property_name, G_TYPE_STRING, g_value_take_string, value, error); } gboolean -nm_g_object_set_property_boolean (GObject *object, - const char *property_name, - gboolean value, - GError **error) +nm_g_object_set_property_boolean(GObject * object, + const char *property_name, + gboolean value, + GError ** error) { - _set_property (object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error); + _set_property(object, property_name, G_TYPE_BOOLEAN, g_value_set_boolean, !!value, error); } gboolean -nm_g_object_set_property_char (GObject *object, - const char *property_name, - gint8 value, - GError **error) +nm_g_object_set_property_char(GObject * object, + const char *property_name, + gint8 value, + GError ** error) { - /* glib says about G_TYPE_CHAR: - * - * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer. - * - * This is always a (signed!) char. */ - _set_property (object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error); + /* glib says about G_TYPE_CHAR: + * + * The type designated by G_TYPE_CHAR is unconditionally an 8-bit signed integer. + * + * This is always a (signed!) char. */ + _set_property(object, property_name, G_TYPE_CHAR, g_value_set_schar, value, error); } gboolean -nm_g_object_set_property_uchar (GObject *object, - const char *property_name, - guint8 value, - GError **error) +nm_g_object_set_property_uchar(GObject * object, + const char *property_name, + guint8 value, + GError ** error) { - _set_property (object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error); + _set_property(object, property_name, G_TYPE_UCHAR, g_value_set_uchar, value, error); } gboolean -nm_g_object_set_property_int (GObject *object, - const char *property_name, - int value, - GError **error) +nm_g_object_set_property_int(GObject *object, const char *property_name, int value, GError **error) { - _set_property (object, property_name, G_TYPE_INT, g_value_set_int, value, error); + _set_property(object, property_name, G_TYPE_INT, g_value_set_int, value, error); } gboolean -nm_g_object_set_property_int64 (GObject *object, - const char *property_name, - gint64 value, - GError **error) +nm_g_object_set_property_int64(GObject * object, + const char *property_name, + gint64 value, + GError ** error) { - _set_property (object, property_name, G_TYPE_INT64, g_value_set_int64, value, error); + _set_property(object, property_name, G_TYPE_INT64, g_value_set_int64, value, error); } gboolean -nm_g_object_set_property_uint (GObject *object, - const char *property_name, - guint value, - GError **error) +nm_g_object_set_property_uint(GObject * object, + const char *property_name, + guint value, + GError ** error) { - _set_property (object, property_name, G_TYPE_UINT, g_value_set_uint, value, error); + _set_property(object, property_name, G_TYPE_UINT, g_value_set_uint, value, error); } gboolean -nm_g_object_set_property_uint64 (GObject *object, - const char *property_name, - guint64 value, - GError **error) +nm_g_object_set_property_uint64(GObject * object, + const char *property_name, + guint64 value, + GError ** error) { - _set_property (object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error); + _set_property(object, property_name, G_TYPE_UINT64, g_value_set_uint64, value, error); } gboolean -nm_g_object_set_property_flags (GObject *object, - const char *property_name, - GType gtype, - guint value, - GError **error) +nm_g_object_set_property_flags(GObject * object, + const char *property_name, + GType gtype, + guint value, + GError ** error) { - nm_assert (({ - nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); - G_IS_FLAGS_CLASS (gtypeclass); - })); - _set_property (object, property_name, gtype, g_value_set_flags, value, error); + nm_assert(({ + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref(gtype); + G_IS_FLAGS_CLASS(gtypeclass); + })); + _set_property(object, property_name, gtype, g_value_set_flags, value, error); } gboolean -nm_g_object_set_property_enum (GObject *object, - const char *property_name, - GType gtype, - int value, - GError **error) +nm_g_object_set_property_enum(GObject * object, + const char *property_name, + GType gtype, + int value, + GError ** error) { - nm_assert (({ - nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref (gtype); - G_IS_ENUM_CLASS (gtypeclass); - })); - _set_property (object, property_name, gtype, g_value_set_enum, value, error); + nm_assert(({ + nm_auto_unref_gtypeclass GTypeClass *gtypeclass = g_type_class_ref(gtype); + G_IS_ENUM_CLASS(gtypeclass); + })); + _set_property(object, property_name, gtype, g_value_set_enum, value, error); } GParamSpec * -nm_g_object_class_find_property_from_gtype (GType gtype, - const char *property_name) +nm_g_object_class_find_property_from_gtype(GType gtype, const char *property_name) { - nm_auto_unref_gtypeclass GObjectClass *gclass = NULL; + nm_auto_unref_gtypeclass GObjectClass *gclass = NULL; - gclass = g_type_class_ref (gtype); - return g_object_class_find_property (gclass, property_name); + gclass = g_type_class_ref(gtype); + return g_object_class_find_property(gclass, property_name); } /*****************************************************************************/ @@ -2557,163 +2535,208 @@ nm_g_object_class_find_property_from_gtype (GType gtype, * that added @pname. */ GType -nm_g_type_find_implementing_class_for_property (GType gtype, - const char *pname) +nm_g_type_find_implementing_class_for_property(GType gtype, const char *pname) { - nm_auto_unref_gtypeclass GObjectClass *klass = NULL; - GParamSpec *pspec; + nm_auto_unref_gtypeclass GObjectClass *klass = NULL; + GParamSpec * pspec; - g_return_val_if_fail (pname, G_TYPE_INVALID); + g_return_val_if_fail(pname, G_TYPE_INVALID); - klass = g_type_class_ref (gtype); - g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), G_TYPE_INVALID); + klass = g_type_class_ref(gtype); + g_return_val_if_fail(G_IS_OBJECT_CLASS(klass), G_TYPE_INVALID); - pspec = g_object_class_find_property (klass, pname); - g_return_val_if_fail (pspec, G_TYPE_INVALID); + pspec = g_object_class_find_property(klass, pname); + g_return_val_if_fail(pspec, G_TYPE_INVALID); - gtype = G_TYPE_FROM_CLASS (klass); + gtype = G_TYPE_FROM_CLASS(klass); - while (TRUE) { - nm_auto_unref_gtypeclass GObjectClass *k = NULL; + while (TRUE) { + nm_auto_unref_gtypeclass GObjectClass *k = NULL; - k = g_type_class_ref (g_type_parent (gtype)); + k = g_type_class_ref(g_type_parent(gtype)); - g_return_val_if_fail (G_IS_OBJECT_CLASS (k), G_TYPE_INVALID); + g_return_val_if_fail(G_IS_OBJECT_CLASS(k), G_TYPE_INVALID); - if (g_object_class_find_property (k, pname) != pspec) - return gtype; + if (g_object_class_find_property(k, pname) != pspec) + return gtype; - gtype = G_TYPE_FROM_CLASS (k); - } + gtype = G_TYPE_FROM_CLASS(k); + } } /*****************************************************************************/ static void -_str_buf_append_c_escape_octal (NMStrBuf *strbuf, - char ch) +_str_buf_append_c_escape_octal(NMStrBuf *strbuf, char ch) { - nm_str_buf_append_c4 (strbuf, - '\\', - '0' + ((char) ((((guchar) ch) >> 6) & 07)), - '0' + ((char) ((((guchar) ch) >> 3) & 07)), - '0' + ((char) ((((guchar) ch) ) & 07))); + nm_str_buf_append_c4(strbuf, + '\\', + '0' + ((char) ((((guchar) ch) >> 6) & 07)), + '0' + ((char) ((((guchar) ch) >> 3) & 07)), + '0' + ((char) ((((guchar) ch)) & 07))); } +/** + * nm_utils_buf_utf8safe_unescape: + * @str: (allow-none): the string to unescape. The string itself is a NUL terminated + * ASCII string, that can have C-style backslash escape sequences (which + * are to be unescaped). Non-ASCII characters (e.g. UTF-8) are taken verbatim, so + * it doesn't care that this string is UTF-8. However, usually this is a UTF-8 encoded + * string. + * @flags: flags for unescaping. The following flags are supported. + * %NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES performs a g_strstrip() on the input string, + * but preserving escaped spaces. For example, "a\\t " gives "a\t" (that is, the escaped space does + * not get stripped). Likewise, the invalid escape sequence "a\\ " results in "a " (stripping + * the unescaped space, but preserving the escaped one). + * @out_len: (out): the length of the parsed string. + * @to_free: (out): if @str requires unescaping, the function will clone the string. In + * that case, the allocated buffer will be returned here. + * + * See C-style escapes at https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences. + * Note that hex escapes ("\\xhh") and unicode escapes ("\\uhhhh", "\\Uhhhhhhhh") are not supported. + * + * Also, this function is very similar to g_strcompress() but without issuing g_warning() + * assertions and proper handling of "\\000" escape sequences. + * + * Invalid escape sequences (or non-UTF-8 input) are gracefully accepted. For example "\\ " + * is an invalid escape sequence, in this case the backslash is removed and " " gets returned. + * + * The function never leaks secrets in memory. + * + * Returns: the unescaped buffer of length @out_len. If @str is %NULL, this returns %NULL + * and sets @out_len to 0. Otherwise, a non-%NULL binary buffer is returned with + * @out_len bytes. Note that the binary buffer is guaranteed to be NUL terminated. That + * is @result[@out_len] is NUL. + * Note that the result is binary, and may have embedded NUL characters and non-UTF-8. + * If the function can avoid cloning the input string, it will return a pointer inside + * the input @str. For example, if there is no backslash, no cloning is necessary. In that + * case, @to_free will be %NULL. Otherwise, @to_free is set to a newly allocated buffer + * containing the unescaped string and returned. + */ gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free) -{ - gboolean strip_spaces = NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES); - NMStrBuf strbuf; - const char *s; - gsize len; - - 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; - } - - if (strip_spaces) - str = nm_str_skip_leading_spaces (str); - - len = strlen (str); - - s = memchr (str, '\\', len); - if (!s) { - if ( strip_spaces - && len > 0 - && g_ascii_isspace (str[len - 1])) { - len--; - while ( len > 0 - && g_ascii_isspace (str[len - 1])) - len--; - *out_len = len; - return (*to_free = g_strndup (str, len)); - } - *out_len = len; - *to_free = NULL; - return str; - } - - nm_str_buf_init (&strbuf, len + 1u, FALSE); - - nm_str_buf_append_len (&strbuf, 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') { - /* technically, escape sequences larger than \3FF are out of range - * and invalid. We don't check for that, and do the same as - * g_strcompress(): silently clip the value with & 0xFF. */ - v = v * 8 + (ch - '0'); - ++str; - } - } - 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++; - } - - nm_str_buf_append_c (&strbuf, ch); - - s = strchr (str, '\\'); - if (!s) { - gsize l = strlen (str); - - if (strip_spaces) { - while ( l > 0 - && g_ascii_isspace (str[l - 1])) - l--; - } - nm_str_buf_append_len (&strbuf, str, l); - break; - } - - nm_str_buf_append_len (&strbuf, str, s - str); - str = s; - } - - /* assert that no reallocation was necessary. For one, unescaping should - * never result in a longer string than the input. Also, when unescaping - * secrets, we want to ensure that we don't leak secrets in memory. */ - nm_assert (strbuf.allocated == len + 1u); - - return (*to_free = nm_str_buf_finalize (&strbuf, - out_len)); +nm_utils_buf_utf8safe_unescape(const char * str, + NMUtilsStrUtf8SafeFlags flags, + gsize * out_len, + gpointer * to_free) +{ + gboolean strip_spaces = NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES); + NMStrBuf strbuf; + const char *s; + gsize len; + + 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; + } + + if (strip_spaces) + str = nm_str_skip_leading_spaces(str); + + len = strlen(str); + + s = memchr(str, '\\', len); + if (!s) { + if (strip_spaces && len > 0 && g_ascii_isspace(str[len - 1])) { + len--; + while (len > 0 && g_ascii_isspace(str[len - 1])) + len--; + *out_len = len; + return (*to_free = g_strndup(str, len)); + } + *out_len = len; + *to_free = NULL; + return str; + } + + nm_str_buf_init(&strbuf, len + 1u, FALSE); + + nm_str_buf_append_len(&strbuf, 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') { + /* technically, escape sequences larger than \3FF are out of range + * and invalid. We don't check for that, and do the same as + * g_strcompress(): silently clip the value with & 0xFF. */ + v = v * 8 + (ch - '0'); + ++str; + } + } + 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++; + } + + nm_str_buf_append_c(&strbuf, ch); + + s = strchr(str, '\\'); + if (!s) { + gsize l = strlen(str); + + if (strip_spaces) { + while (l > 0 && g_ascii_isspace(str[l - 1])) + l--; + } + nm_str_buf_append_len(&strbuf, str, l); + break; + } + + nm_str_buf_append_len(&strbuf, str, s - str); + str = s; + } + + /* assert that no reallocation was necessary. For one, unescaping should + * never result in a longer string than the input. Also, when unescaping + * secrets, we want to ensure that we don't leak secrets in memory. */ + nm_assert(strbuf.allocated == len + 1u); + + return (*to_free = nm_str_buf_finalize(&strbuf, out_len)); } /** @@ -2748,126 +2771,125 @@ nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, * 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; - NMStrBuf strbuf; - - 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; - } - - nm_str_buf_init (&strbuf, - buflen + 5, - NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET)); - - s = str; - do { - buflen -= p - s; - nm_assert (buflen >= 0); - - for (; s < p; s++) { - char ch = s[0]; - - nm_assert (ch); - if (ch == '\\') - nm_str_buf_append_c2 (&strbuf, '\\', '\\'); - 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_buf_append_c_escape_octal (&strbuf, ch); - else - nm_str_buf_append_c (&strbuf, ch); - } - - if (buflen <= 0) - break; - - _str_buf_append_c_escape_octal (&strbuf, p[0]); - - buflen--; - if (buflen == 0) - break; - - s = &p[1]; - (void) g_utf8_validate (s, buflen, &p); - } while (TRUE); - - return (*to_free = nm_str_buf_finalize (&strbuf, NULL)); +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; + NMStrBuf strbuf; + + 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; + } + + nm_str_buf_init(&strbuf, buflen + 5, NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET)); + + s = str; + do { + buflen -= p - s; + nm_assert(buflen >= 0); + + for (; s < p; s++) { + char ch = s[0]; + + nm_assert(ch); + if (ch == '\\') + nm_str_buf_append_c2(&strbuf, '\\', '\\'); + 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_buf_append_c_escape_octal(&strbuf, ch); + else + nm_str_buf_append_c(&strbuf, ch); + } + + if (buflen <= 0) + break; + + _str_buf_append_c_escape_octal(&strbuf, p[0]); + + buflen--; + if (buflen == 0) + break; + + s = &p[1]; + (void) g_utf8_validate(s, buflen, &p); + } while (TRUE); + + return (*to_free = nm_str_buf_finalize(&strbuf, NULL)); } const char * -nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) +nm_utils_buf_utf8safe_escape_bytes(GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) { - gconstpointer p; - gsize l; + gconstpointer p; + gsize l; - if (bytes) - p = g_bytes_get_data (bytes, &l); - else { - p = NULL; - l = 0; - } + 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); + return nm_utils_buf_utf8safe_escape(p, l, flags, to_free); } char * -nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags) +nm_utils_buf_utf8safe_escape_cp(gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags) { - const char *s_const; - char *s; + const char *s_const; + char * s; - s_const = nm_utils_buf_utf8safe_escape (buf, buflen, flags, &s); - nm_assert (!s || s == s_const); - return s ?: g_strdup (s_const); + s_const = nm_utils_buf_utf8safe_escape(buf, buflen, flags, &s); + nm_assert(!s || s == s_const); + return s ?: g_strdup(s_const); } /*****************************************************************************/ const char * -nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) +nm_utils_str_utf8safe_unescape(const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { - const char *res; - gsize len; + const char *res; + gsize len; - g_return_val_if_fail (to_free, NULL); + g_return_val_if_fail(to_free, NULL); - res = nm_utils_buf_utf8safe_unescape (str, flags, &len, (gpointer *) to_free); + res = nm_utils_buf_utf8safe_unescape(str, flags, &len, (gpointer *) to_free); - nm_assert ( (!res && len == 0) - || (strlen (res) <= len)); + nm_assert((!res && len == 0) || (strlen(res) <= len)); - return res; + return res; } /** @@ -2898,9 +2920,9 @@ nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, * 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) +nm_utils_str_utf8safe_escape(const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { - return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free); + return nm_utils_buf_utf8safe_escape(str, -1, flags, to_free); } /** @@ -2916,34 +2938,34 @@ nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, ch * The escaping can be reverted by g_strcompress(). **/ char * -nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) +nm_utils_str_utf8safe_escape_cp(const char *str, NMUtilsStrUtf8SafeFlags flags) { - char *s; + char *s; - nm_utils_str_utf8safe_escape (str, flags, &s); - return s ?: g_strdup (str); + nm_utils_str_utf8safe_escape(str, flags, &s); + return s ?: g_strdup(str); } char * -nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) +nm_utils_str_utf8safe_unescape_cp(const char *str, NMUtilsStrUtf8SafeFlags flags) { - char *s; + char *s; - str = nm_utils_str_utf8safe_unescape (str, flags, &s); - return s ?: g_strdup (str); + str = nm_utils_str_utf8safe_unescape(str, flags, &s); + return s ?: g_strdup(str); } char * -nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags) +nm_utils_str_utf8safe_escape_take(char *str, NMUtilsStrUtf8SafeFlags flags) { - char *str_to_free; + 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; + nm_utils_str_utf8safe_escape(str, flags, &str_to_free); + if (str_to_free) { + g_free(str); + return str_to_free; + } + return str; } /*****************************************************************************/ @@ -2951,358 +2973,569 @@ nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags) /* 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_nsec) -{ - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - struct timespec ts, *pts; - int r; - - if (timeout_nsec < 0) - pts = NULL; - else { - ts.tv_sec = (time_t) (timeout_nsec / NM_UTILS_NSEC_PER_SEC); - ts.tv_nsec = (long int) (timeout_nsec % NM_UTILS_NSEC_PER_SEC); - pts = &ts; - } - - r = ppoll (&pollfd, 1, pts, NULL); - if (r < 0) - return -NM_ERRNO_NATIVE (errno); - if (r == 0) - return 0; - return pollfd.revents; +nm_utils_fd_wait_for_event(int fd, int event, gint64 timeout_nsec) +{ + struct pollfd pollfd = { + .fd = fd, + .events = event, + }; + struct timespec ts, *pts; + int r; + + if (timeout_nsec < 0) + pts = NULL; + else { + ts.tv_sec = (time_t)(timeout_nsec / NM_UTILS_NSEC_PER_SEC); + ts.tv_nsec = (long int) (timeout_nsec % NM_UTILS_NSEC_PER_SEC); + pts = &ts; + } + + r = ppoll(&pollfd, 1, pts, NULL); + if (r < 0) + return -NM_ERRNO_NATIVE(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) +nm_utils_fd_read_loop(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p = buf; - ssize_t n = 0; + uint8_t *p = buf; + ssize_t n = 0; - g_return_val_if_fail (fd >= 0, -EINVAL); - g_return_val_if_fail (buf, -EINVAL); + 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 called with nbytes == 0, let's call read() at least + * once, to validate the operation */ - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; + if (nbytes > (size_t) SSIZE_MAX) + return -EINVAL; - do { - ssize_t k; + do { + ssize_t k; - k = read (fd, p, nbytes); - if (k < 0) { - int errsv = errno; + k = read(fd, p, nbytes); + if (k < 0) { + int errsv = errno; - if (errsv == EINTR) - continue; + if (errsv == EINTR) + continue; - if (errsv == EAGAIN && do_poll) { + if (errsv == EAGAIN && do_poll) { + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via read() */ - /* 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; + } - (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1); - continue; - } + return n > 0 ? n : -NM_ERRNO_NATIVE(errsv); + } - return n > 0 ? n : -NM_ERRNO_NATIVE (errsv); - } + if (k == 0) + return n; - if (k == 0) - return n; + g_assert((size_t) k <= nbytes); - g_assert ((size_t) k <= nbytes); + p += k; + nbytes -= k; + n += k; + } while (nbytes > 0); - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; + 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) +nm_utils_fd_read_loop_exact(int fd, void *buf, size_t nbytes, bool do_poll) { - ssize_t n; + 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; + 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; + return 0; } /*****************************************************************************/ +G_STATIC_ASSERT(G_STRUCT_OFFSET(NMUtilsNamedValue, name) == 0); + NMUtilsNamedValue * -nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash, - guint *out_len, - GCompareDataFunc compare_func, - gpointer user_data) -{ - 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 (compare_func) - nm_utils_named_value_list_sort (values, len, compare_func, user_data); - - NM_SET_OUT (out_len, len); - return values; +nm_utils_named_values_from_strdict_full(GHashTable * hash, + guint * out_len, + GCompareDataFunc compare_func, + gpointer user_data, + NMUtilsNamedValue * provided_buffer, + guint provided_buffer_len, + NMUtilsNamedValue **out_allocated_buffer) +{ + GHashTableIter iter; + NMUtilsNamedValue *values; + guint i, len; + + nm_assert(provided_buffer_len == 0 || provided_buffer); + nm_assert(!out_allocated_buffer || !*out_allocated_buffer); + + if (!hash || !(len = g_hash_table_size(hash))) { + NM_SET_OUT(out_len, 0); + return NULL; + } + + if (provided_buffer_len >= len + 1) { + /* the buffer provided by the caller is large enough. Use it. */ + values = provided_buffer; + } else { + /* allocate a new buffer. */ + values = g_new(NMUtilsNamedValue, len + 1); + NM_SET_OUT(out_allocated_buffer, values); + } + + i = 0; + g_hash_table_iter_init(&iter, hash); + while (g_hash_table_iter_next(&iter, (gpointer *) &values[i].name, &values[i].value_ptr)) + i++; + nm_assert(i == len); + values[i].name = NULL; + values[i].value_ptr = NULL; + + if (compare_func) + nm_utils_named_value_list_sort(values, len, compare_func, user_data); + + NM_SET_OUT(out_len, len); + return values; } gssize -nm_utils_named_value_list_find (const NMUtilsNamedValue *arr, - gsize len, - const char *name, - gboolean sorted) +nm_utils_named_value_list_find(const NMUtilsNamedValue *arr, + gsize len, + const char * name, + gboolean sorted) { - gsize i; + gsize i; - nm_assert (name); + nm_assert(name); #if NM_MORE_ASSERTS > 5 - { - for (i = 0; i < len; i++) { - const NMUtilsNamedValue *v = &arr[i]; - - nm_assert (v->name); - if ( sorted - && i > 0) - nm_assert (strcmp (arr[i - 1].name, v->name) < 0); - } - } - - nm_assert ( !sorted - || nm_utils_named_value_list_is_sorted (arr, len, FALSE, NULL, NULL)); + { + for (i = 0; i < len; i++) { + const NMUtilsNamedValue *v = &arr[i]; + + nm_assert(v->name); + if (sorted && i > 0) + nm_assert(strcmp(arr[i - 1].name, v->name) < 0); + } + } + + nm_assert(!sorted || nm_utils_named_value_list_is_sorted(arr, len, FALSE, NULL, NULL)); #endif - if (sorted) { - return nm_utils_array_find_binary_search (arr, - sizeof (NMUtilsNamedValue), - len, - &name, - nm_strcmp_p_with_data, - NULL); - } - for (i = 0; i < len; i++) { - if (nm_streq (arr[i].name, name)) - return i; - } - return ~((gssize) len); + if (sorted) { + return nm_utils_array_find_binary_search(arr, + sizeof(NMUtilsNamedValue), + len, + &name, + nm_strcmp_p_with_data, + NULL); + } + for (i = 0; i < len; i++) { + if (nm_streq(arr[i].name, name)) + return i; + } + return ~((gssize) len); } gboolean -nm_utils_named_value_list_is_sorted (const NMUtilsNamedValue *arr, - gsize len, - gboolean accept_duplicates, - GCompareDataFunc compare_func, - gpointer user_data) +nm_utils_named_value_list_is_sorted(const NMUtilsNamedValue *arr, + gsize len, + gboolean accept_duplicates, + GCompareDataFunc compare_func, + gpointer user_data) { - gsize i; - int c_limit; + gsize i; + int c_limit; - if (len == 0) - return TRUE; + if (len == 0) + return TRUE; - g_return_val_if_fail (arr, FALSE); + g_return_val_if_fail(arr, FALSE); - if (!compare_func) - compare_func = nm_strcmp_p_with_data; + if (!compare_func) + compare_func = nm_strcmp_p_with_data; - c_limit = accept_duplicates ? 0 : -1; + c_limit = accept_duplicates ? 0 : -1; - for (i = 1; i < len; i++) { - int c; + for (i = 1; i < len; i++) { + int c; - c = compare_func (&arr[i - 1], &arr[i], user_data); - if (c > c_limit) - return FALSE; - } - return TRUE; + c = compare_func(&arr[i - 1], &arr[i], user_data); + if (c > c_limit) + return FALSE; + } + return TRUE; } void -nm_utils_named_value_list_sort (NMUtilsNamedValue *arr, - gsize len, - GCompareDataFunc compare_func, - gpointer user_data) +nm_utils_named_value_list_sort(NMUtilsNamedValue *arr, + gsize len, + GCompareDataFunc compare_func, + gpointer user_data) { - if (len == 0) - return; + if (len == 0) + return; - g_return_if_fail (arr); + g_return_if_fail(arr); - if (len == 1) - return; + if (len == 1) + return; - g_qsort_with_data (arr, - len, - sizeof (NMUtilsNamedValue), - compare_func ?: nm_strcmp_p_with_data, - user_data); + g_qsort_with_data(arr, + len, + sizeof(NMUtilsNamedValue), + compare_func ?: nm_strcmp_p_with_data, + user_data); } /*****************************************************************************/ 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; +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; } gpointer * -nm_utils_hash_values_to_array (GHashTable *hash, - GCompareDataFunc compare_func, - gpointer user_data, - guint *out_len) -{ - GHashTableIter iter; - gpointer value; - gpointer *arr; - guint i, len; - - if ( !hash - || (len = g_hash_table_size (hash)) == 0u) { - NM_SET_OUT (out_len, 0); - return NULL; - } - - arr = g_new (gpointer, ((gsize) len) + 1); - i = 0; - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &value)) - arr[i++] = value; - - nm_assert (i == len); - arr[len] = NULL; - - if ( len > 1 - && compare_func) { - g_qsort_with_data (arr, - len, - sizeof (gpointer), - compare_func, - user_data); - } - - NM_SET_OUT (out_len, len); - return arr; +nm_utils_hash_values_to_array(GHashTable * hash, + GCompareDataFunc compare_func, + gpointer user_data, + guint * out_len) +{ + GHashTableIter iter; + gpointer value; + gpointer * arr; + guint i, len; + + if (!hash || (len = g_hash_table_size(hash)) == 0u) { + NM_SET_OUT(out_len, 0); + return NULL; + } + + arr = g_new(gpointer, ((gsize) len) + 1); + i = 0; + g_hash_table_iter_init(&iter, hash); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &value)) + arr[i++] = value; + + nm_assert(i == len); + arr[len] = NULL; + + if (len > 1 && compare_func) { + g_qsort_with_data(arr, len, sizeof(gpointer), compare_func, user_data); + } + + NM_SET_OUT(out_len, len); + return arr; +} + +static gboolean +_utils_hashtable_equal(GHashTable * hash_a, + GHashTable * hash_b, + GCompareDataFunc cmp_values, + gpointer user_data) +{ + GHashTableIter h; + gpointer a_key; + gpointer a_val; + gpointer b_val; + + nm_assert(hash_a); + nm_assert(hash_b); + nm_assert(hash_a != hash_b); + nm_assert(g_hash_table_size(hash_a) == g_hash_table_size(hash_b)); + + /* We rely on both hashes to have the same hash/equal function. Otherwise, we would have to iterate + * both hashes and check whether all keys/values are present in the respective other hash (which + * would be O(n^2), since we couldn't use the plain lookup function. That is not a useful thing + * for this function. */ + + g_hash_table_iter_init(&h, hash_a); + while (g_hash_table_iter_next(&h, &a_key, &a_val)) { + if (!g_hash_table_lookup_extended(hash_b, a_key, NULL, &b_val)) + return FALSE; + + if (!cmp_values) { + /* we accept %NULL compare function to indicate that we don't care about the key. */ + continue; + } + + if (cmp_values(a_val, b_val, user_data) != 0) + return FALSE; + } + + return TRUE; } +/** + * nm_utils_hashtable_equal: + * @a: (allow-none): the hash table or %NULL + * @b: (allow-none): the other hash table or %NULL + * @cmp_values: (allow-none): if %NULL, only the keys + * will be compared. Otherwise, this function is used to + * check whether all keys are equal. + * @user_data: the argument for @cmp_values. + * + * It is required that both @a and @b have the same hash and equals + * function. + * + * Returns: %TRUE, if both keys have the same keys and (if + * @cmp_values is given) all values are the same. + */ gboolean -nm_utils_hashtable_same_keys (const GHashTable *a, - const GHashTable *b) +nm_utils_hashtable_equal(const GHashTable *a, + const GHashTable *b, + GCompareDataFunc cmp_values, + gpointer user_data) { - GHashTableIter h; - const char *k; + GHashTable *hash_a = (GHashTable *) a; + GHashTable *hash_b = (GHashTable *) b; + gboolean same; + guint size; + + if (hash_a == hash_b) + return TRUE; - if (a == b) - return TRUE; - if (!a || !b) - return FALSE; - if (g_hash_table_size ((GHashTable *) a) != g_hash_table_size ((GHashTable *) b)) - return FALSE; + if (!hash_a || !hash_b) + return FALSE; - g_hash_table_iter_init (&h, (GHashTable *) a); - while (g_hash_table_iter_next (&h, (gpointer) &k, NULL)) { - if (!g_hash_table_contains ((GHashTable *) b, k)) - return FALSE; - } + size = g_hash_table_size(hash_a); + if (size != g_hash_table_size(hash_b)) + return FALSE; + + if (size == 0) + return TRUE; + + same = _utils_hashtable_equal(hash_a, hash_b, cmp_values, user_data); #if NM_MORE_ASSERTS > 5 - g_hash_table_iter_init (&h, (GHashTable *) b); - while (g_hash_table_iter_next (&h, (gpointer) &k, NULL)) - nm_assert (g_hash_table_contains ((GHashTable *) a, k)); + nm_assert(same == _utils_hashtable_equal(hash_b, hash_a, cmp_values, user_data)); #endif - return TRUE; + return same; +} + +typedef struct { + gpointer key; + gpointer val; +} HashTableCmpData; + +typedef struct { + GCompareDataFunc cmp_keys; + gpointer user_data; +} HashTableUserData; + +static int +_hashtable_cmp_func(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const HashTableUserData *d = user_data; + const HashTableCmpData * d_a = *((const HashTableCmpData *const *) a); + const HashTableCmpData * d_b = *((const HashTableCmpData *const *) b); + + NM_CMP_RETURN(d->cmp_keys(d_a, d_b, d->user_data)); + return 0; +} + +/** + * nm_utils_hashtable_cmp: + * @a: (allow-none): the hash to compare. May be %NULL. + * @b: (allow-none): the other hash to compare. May be %NULL. + * @do_fast_precheck: if %TRUE, assume that the hashes are equal + * and that it is worth calling nm_utils_hashtable_equal() first. + * That requires, that both hashes have the same equals function + * which is compatible with the @cmp_keys function. + * @cmp_keys: the compare function for keys. Usually, the hash/equal function + * of both hashes corresponds to this function. If you set @do_fast_precheck + * to false, then this is not a requirement. + * @cmp_values: (allow-none): if %NULL, only the keys are compared. + * Otherwise, the values must are also compared with this function. + * + * Both hashes must have keys/values of the same domain, so that + * they can be effectively compared with @cmp_keys and @cmp_values. + * + * %NULL hashes compare equal to %NULL, but not to empty hashes. + * + * Returns: 0 if both hashes are equal, or -1 or 1 if one of the hashes + * sorts before/after. + */ +int +nm_utils_hashtable_cmp(const GHashTable *a, + const GHashTable *b, + gboolean do_fast_precheck, + GCompareDataFunc cmp_keys, + GCompareDataFunc cmp_values, + gpointer user_data) +{ + GHashTable *hash_a = (GHashTable *) a; + GHashTable *hash_b = (GHashTable *) b; + gs_free HashTableCmpData *cmp_array_free = NULL; + HashTableCmpData * cmp_array_a; + HashTableCmpData * cmp_array_b; + GHashTableIter h; + gpointer i_key; + gpointer i_val; + gsize size2; + guint size; + guint i; + + nm_assert(cmp_keys); + + NM_CMP_SELF(hash_a, hash_b); + + size = g_hash_table_size(hash_a); + + NM_CMP_DIRECT(size, g_hash_table_size(hash_b)); + + if (size == 0) + return 0; + + if (do_fast_precheck) { + gboolean same; + + /* we expect that the hashes are equal and the caller ensures us that they + * use the same hash/equal functions. Do a fast path check first... + * + * It's unclear whether this is worth it. The full comparison is O(n*ln(n)), + * while the fast check (using the hash lookup) is O(n). But then, the pre-check + * makes additional requirements on the hash's hash/equal functions -- the + * full comparison does not make such requirements. */ + same = _utils_hashtable_equal(hash_a, hash_b, cmp_values, user_data); +#if NM_MORE_ASSERTS > 5 + nm_assert(same == _utils_hashtable_equal(hash_b, hash_a, cmp_values, user_data)); +#endif + if (same) + return 0; + } + + size2 = ((gsize) size) * 2u; + if (size2 > 600u / sizeof(HashTableCmpData)) { + cmp_array_free = g_new(HashTableCmpData, size2); + cmp_array_a = cmp_array_free; + } else + cmp_array_a = g_newa(HashTableCmpData, size2); + cmp_array_b = &cmp_array_a[size]; + + i = 0; + g_hash_table_iter_init(&h, hash_a); + while (g_hash_table_iter_next(&h, &i_key, &i_val)) { + nm_assert(i < size); + cmp_array_a[i++] = (HashTableCmpData){ + .key = i_key, + .val = i_val, + }; + } + nm_assert(i == size); + + i = 0; + g_hash_table_iter_init(&h, hash_b); + while (g_hash_table_iter_next(&h, &i_key, &i_val)) { + nm_assert(i < size); + cmp_array_b[i++] = (HashTableCmpData){ + .key = i_key, + .val = i_val, + }; + } + nm_assert(i == size); + + g_qsort_with_data(cmp_array_a, + size, + sizeof(HashTableCmpData), + _hashtable_cmp_func, + &((HashTableUserData){ + .cmp_keys = cmp_keys, + .user_data = user_data, + })); + + g_qsort_with_data(cmp_array_b, + size, + sizeof(HashTableCmpData), + _hashtable_cmp_func, + &((HashTableUserData){ + .cmp_keys = cmp_keys, + .user_data = user_data, + })); + + for (i = 0; i < size; i++) { + NM_CMP_RETURN(cmp_keys(cmp_array_a[i].key, cmp_array_b[i].key, user_data)); + } + + if (cmp_values) { + for (i = 0; i < size; i++) { + NM_CMP_RETURN(cmp_values(cmp_array_a[i].val, cmp_array_b[i].val, user_data)); + } + } + + /* the fast-precheck should have already told that the arrays are equal. */ + nm_assert(!do_fast_precheck); + + return 0; } char ** -nm_utils_strv_make_deep_copied (const char **strv) +nm_utils_strv_make_deep_copied(const char **strv) { - gsize i; + gsize i; - /* it takes a strv list, and copies each - * strings. Note that this updates @strv *in-place* - * and returns it. */ + /* it takes a strv list, 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]); + if (!strv) + return NULL; + for (i = 0; strv[i]; i++) + strv[i] = g_strdup(strv[i]); - return (char **) strv; + return (char **) strv; } char ** -nm_utils_strv_make_deep_copied_n (const char **strv, gsize len) +nm_utils_strv_make_deep_copied_n(const char **strv, gsize len) { - gsize i; + gsize i; - /* it takes a strv array with len elements, and copies each - * strings. Note that this updates @strv *in-place* - * and returns it. */ + /* it takes a strv array with len elements, and copies each + * strings. Note that this updates @strv *in-place* + * and returns it. */ - if (!strv) - return NULL; - for (i = 0; i < len; i++) - strv[i] = g_strdup (strv[i]); + if (!strv) + return NULL; + for (i = 0; i < len; i++) + strv[i] = g_strdup(strv[i]); - return (char **) strv; + return (char **) strv; } /** @@ -3324,7 +3557,7 @@ nm_utils_strv_make_deep_copied_n (const char **strv, gsize len) * * Note that if @len is non-negative, then it still must not * contain any %NULL pointers within the first @len elements. - * Otherwise you would leak elements if you try to free the + * Otherwise, you would leak elements if you try to free the * array with g_strfreev(). Allowing that would be error prone. * * Returns: (transfer full): a clone of the strv array. Always @@ -3332,124 +3565,198 @@ nm_utils_strv_make_deep_copied_n (const char **strv, gsize len) * cloned or not. */ char ** -nm_utils_strv_dup (gpointer strv, - gssize len, - gboolean deep_copied) -{ - gsize i, l; - char **v; - const char *const *const src = strv; - - if (len < 0) - l = NM_PTRARRAY_LEN (src); - else - l = len; - if (l == 0) { - /* this function never returns an empty strv array. If you - * need that, handle it yourself. */ - return NULL; - } - - v = g_new (char *, l + 1); - for (i = 0; i < l; i++) { - - if (G_UNLIKELY (!src[i])) { - /* NULL strings are not allowed. Clear the remainder of the array - * and return it (with assertion failure). */ - l++; - for (; i < l; i++) - v[i] = NULL; - g_return_val_if_reached (v); - } - - if (deep_copied) - v[i] = g_strdup (src[i]); - else - v[i] = (char *) src[i]; - } - v[l] = NULL; - return v; +_nm_utils_strv_dup(const char *const *strv, gssize len, gboolean deep_copied) +{ + gsize i, l; + char **v; + + if (len < 0) + 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. */ + return NULL; + } + + v = g_new(char *, l + 1); + for (i = 0; i < l; i++) { + if (G_UNLIKELY(!strv[i])) { + /* NULL strings are not allowed. Clear the remainder of the array + * and return it (with assertion failure). */ + l++; + for (; i < l; i++) + v[i] = NULL; + g_return_val_if_reached(v); + } + + if (deep_copied) + v[i] = g_strdup(strv[i]); + else + v[i] = (char *) strv[i]; + } + v[l] = NULL; + return v; +} + +const char ** +_nm_utils_strv_dup_packed(const char *const *strv, gssize len) + +{ + gs_free gsize *str_len_free = NULL; + gsize * str_len; + const char ** result; + gsize mem_len; + gsize pre_len; + gsize len2; + char * sbuf; + gsize i; + + nm_assert(len >= -1); + + if (G_LIKELY(len < 0)) { + if (!strv || !strv[0]) { + /* This function never returns an empty strv array. If you need that, handle it + * yourself. */ + return NULL; + } + len2 = NM_PTRARRAY_LEN(strv); + } else { + if (len == 0) + return NULL; + len2 = len; + } + + if (len2 > 300u / sizeof(gsize)) { + str_len_free = g_new(gsize, len2); + str_len = str_len_free; + } else + str_len = g_newa(gsize, len2); + + mem_len = 0; + for (i = 0; i < len2; i++) { + gsize l; + + if (G_LIKELY(strv[i])) + l = strlen(strv[i]) + 1u; + else + l = 0; + str_len[i] = l; + mem_len += l; + } + + pre_len = sizeof(const char *) * (len2 + 1u); + + result = g_malloc(pre_len + mem_len); + sbuf = &(((char *) result)[pre_len]); + for (i = 0; i < len2; i++) { + gsize l; + + if (G_UNLIKELY(!strv[i])) { + /* Technically there is no problem with accepting NULL strings. But that + * does not really result in a strv array, and likely this only happens due + * to a bug. We want to catch such bugs by asserting. + * + * We clear the remainder of the buffer and fail with an assertion. */ + len2++; + for (; i < len2; i++) + result[i] = NULL; + g_return_val_if_reached(result); + } + + result[i] = sbuf; + + l = str_len[i]; + memcpy(sbuf, strv[i], l); + sbuf += l; + } + result[i] = NULL; + nm_assert(i == len2); + nm_assert(sbuf == (&((const char *) result)[pre_len]) + mem_len); + + return result; } /*****************************************************************************/ 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_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; } /*****************************************************************************/ @@ -3479,42 +3786,42 @@ nm_utils_ptrarray_find_binary_search (gconstpointer *list, * 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) +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; + 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)); + 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; + imin = 0; + if (len == 0) + return ~imin; - imax = len - 1; + imax = len - 1; - while (imin <= imax) { - imid = imin + (imax - imin) / 2; + while (imin <= imax) { + imid = imin + (imax - imin) / 2; - cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data); - if (cmp == 0) - return imid; + 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; - } + 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; + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + return ~imin; } /*****************************************************************************/ @@ -3540,38 +3847,37 @@ nm_utils_array_find_binary_search (gconstpointer list, * 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_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; } /*****************************************************************************/ @@ -3591,65 +3897,65 @@ nm_utils_hash_table_equal (const GHashTable *a, * 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) +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; + 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; + start_time = 0; + contents = NULL; - g_return_val_if_fail (pid > 0, 0); + g_return_val_if_fail(pid > 0, 0); - G_STATIC_ASSERT_EXPR (sizeof (GPid) >= sizeof (pid_t)); + G_STATIC_ASSERT_EXPR(sizeof(GPid) >= sizeof(pid_t)); - nm_sprintf_buf (filename, "/proc/%"G_PID_FORMAT"/stat", (GPid) pid); + nm_sprintf_buf(filename, "/proc/%" G_PID_FORMAT "/stat", (GPid) pid); - if (!g_file_get_contents (filename, &contents, &length, NULL)) - goto fail; + 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; + /* 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]; + state = p[0]; - tokens = nm_utils_strsplit_set (p, " "); + tokens = nm_utils_strsplit_set(p, " "); - if (NM_PTRARRAY_LEN (tokens) < 20) - goto fail; + 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; - } + 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; + 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; + 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_SET_OUT(out_state, ' '); + NM_SET_OUT(out_ppid, 0); + return 0; } /*****************************************************************************/ @@ -3669,29 +3975,25 @@ fail: * comparison. */ void -_nm_utils_strv_sort (const char **strv, gssize len) +_nm_utils_strv_sort(const char **strv, gssize len) { - GCompareDataFunc cmp; - gsize l; + GCompareDataFunc cmp; + gsize l; - if (len < 0) { - l = NM_PTRARRAY_LEN (strv); - cmp = nm_strcmp_p_with_data; - } else { - l = len; - cmp = nm_strcmp0_p_with_data; - } + if (len < 0) { + l = NM_PTRARRAY_LEN(strv); + cmp = nm_strcmp_p_with_data; + } else { + l = len; + cmp = nm_strcmp0_p_with_data; + } - if (l <= 1) - return; + if (l <= 1) + return; - nm_assert (l <= (gsize) G_MAXINT); + nm_assert(l <= (gsize) G_MAXINT); - g_qsort_with_data (strv, - l, - sizeof (const char *), - cmp, - NULL); + g_qsort_with_data(strv, l, sizeof(const char *), cmp, NULL); } /** @@ -3718,31 +4020,28 @@ _nm_utils_strv_sort (const char **strv, gssize len) * Returns: 0 if the arrays are equal (using strcmp). **/ int -_nm_utils_strv_cmp_n (const char *const*strv1, - gssize len1, - const char *const*strv2, - gssize len2) +_nm_utils_strv_cmp_n(const char *const *strv1, gssize len1, const char *const *strv2, gssize len2) { - gsize n, n2; + gsize n, n2; - if (len1 < 0) { - if (!strv1) - return (len2 < 0 && !strv2) ? 0 : -1; - n = NM_PTRARRAY_LEN (strv1); - } else - n = len1; + if (len1 < 0) { + if (!strv1) + return (len2 < 0 && !strv2) ? 0 : -1; + n = NM_PTRARRAY_LEN(strv1); + } else + n = len1; - if (len2 < 0) { - if (!strv2) - return 1; - n2 = NM_PTRARRAY_LEN (strv2); - } else - n2 = len2; + if (len2 < 0) { + if (!strv2) + return 1; + n2 = NM_PTRARRAY_LEN(strv2); + } else + n2 = len2; - NM_CMP_DIRECT (n, n2); - for (; n > 0; n--, strv1++, strv2++) - NM_CMP_DIRECT_STRCMP0 (*strv1, *strv2); - return 0; + NM_CMP_DIRECT(n, n2); + for (; n > 0; n--, strv1++, strv2++) + NM_CMP_DIRECT_STRCMP0(*strv1, *strv2); + return 0; } /*****************************************************************************/ @@ -3760,17 +4059,16 @@ _nm_utils_strv_cmp_n (const char *const*strv1, * %NULL if not found. */ GSList * -nm_utils_g_slist_find_str (const GSList *list, - const char *needle) +nm_utils_g_slist_find_str(const GSList *list, const char *needle) { - nm_assert (needle); + nm_assert(needle); - for (; list; list = list->next) { - nm_assert (list->data); - if (nm_streq (list->data, needle)) - return (GSList *) list; - } - return NULL; + for (; list; list = list->next) { + nm_assert(list->data); + if (nm_streq(list->data, needle)) + return (GSList *) list; + } + return NULL; } /** @@ -3779,250 +4077,246 @@ nm_utils_g_slist_find_str (const GSList *list, * @b: the right #GSList of strings to compare. * * Compares two string lists. The data elements are compared with - * strcmp(), alloing %NULL elements. + * strcmp(), allowing %NULL elements. * * Returns: 0, 1, or -1, depending on how the lists compare. */ int -nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b) +nm_utils_g_slist_strlist_cmp(const GSList *a, const GSList *b) { - while (TRUE) { - if (!a) - return !b ? 0 : -1; - if (!b) - return 1; - NM_CMP_DIRECT_STRCMP0 (a->data, b->data); - a = a->next; - b = b->next; - } + while (TRUE) { + if (!a) + return !b ? 0 : -1; + if (!b) + return 1; + NM_CMP_DIRECT_STRCMP0(a->data, b->data); + a = a->next; + b = b->next; + } } char * -nm_utils_g_slist_strlist_join (const GSList *a, const char *separator) +nm_utils_g_slist_strlist_join(const GSList *a, const char *separator) { - GString *str = NULL; + GString *str = NULL; - if (!a) - return NULL; + if (!a) + return NULL; - for (; a; a = a->next) { - if (!str) - str = g_string_new (NULL); - else - g_string_append (str, separator); - g_string_append (str, a->data); - } - return g_string_free (str, FALSE); + for (; a; a = a->next) { + if (!str) + str = g_string_new(NULL); + else + g_string_append(str, separator); + g_string_append(str, a->data); + } + return g_string_free(str, FALSE); } /*****************************************************************************/ gpointer -_nm_utils_user_data_pack (int nargs, gconstpointer *args) +_nm_utils_user_data_pack(int nargs, gconstpointer *args) { - int i; - gpointer *data; + int i; + gpointer *data; - nm_assert (nargs > 0); - nm_assert (args); + 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; + 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, ...) +_nm_utils_user_data_unpack(gpointer user_data, int nargs, ...) { - gpointer *data = user_data; - va_list ap; - int i; + gpointer *data = user_data; + va_list ap; + int i; - nm_assert (data); - nm_assert (nargs > 0); + nm_assert(data); + nm_assert(nargs > 0); - va_start (ap, nargs); - for (i = 0; i < nargs; i++) { - gpointer *dst; + va_start(ap, nargs); + for (i = 0; i < nargs; i++) { + gpointer *dst; - dst = va_arg (ap, gpointer *); - nm_assert (dst); + dst = va_arg(ap, gpointer *); + nm_assert(dst); - *dst = data[i]; - } - va_end (ap); + *dst = data[i]; + } + va_end(ap); - g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), data); + g_slice_free1(((gsize) nargs) * sizeof(gconstpointer), data); } /*****************************************************************************/ typedef struct { - gpointer callback_user_data; - GCancellable *cancellable; - GSource *source; - NMUtilsInvokeOnIdleCallback callback; - gulong cancelled_id; + gpointer callback_user_data; + GCancellable * cancellable; + GSource * source; + NMUtilsInvokeOnIdleCallback callback; + gulong cancelled_id; } InvokeOnIdleData; static gboolean -_nm_utils_invoke_on_idle_cb_idle (gpointer user_data) +_nm_utils_invoke_on_idle_cb_idle(gpointer user_data) { - InvokeOnIdleData *data = user_data; + InvokeOnIdleData *data = user_data; - nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); + nm_clear_g_signal_handler(data->cancellable, &data->cancelled_id); - data->callback (data->callback_user_data, data->cancellable); + data->callback(data->callback_user_data, data->cancellable); - nm_g_object_unref (data->cancellable); - g_source_destroy (data->source); - nm_g_slice_free (data); - return G_SOURCE_REMOVE; + nm_g_object_unref(data->cancellable); + g_source_destroy(data->source); + nm_g_slice_free(data); + return G_SOURCE_REMOVE; } static void -_nm_utils_invoke_on_idle_cb_cancelled (GCancellable *cancellable, - InvokeOnIdleData *data) +_nm_utils_invoke_on_idle_cb_cancelled(GCancellable *cancellable, InvokeOnIdleData *data) { - /* on cancellation, we invoke the callback synchronously. */ - nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); - nm_clear_g_source_inst (&data->source); - data->callback (data->callback_user_data, data->cancellable); - nm_g_object_unref (data->cancellable); - nm_g_slice_free (data); + /* on cancellation, we invoke the callback synchronously. */ + nm_clear_g_signal_handler(data->cancellable, &data->cancelled_id); + nm_clear_g_source_inst(&data->source); + data->callback(data->callback_user_data, data->cancellable); + nm_g_object_unref(data->cancellable); + nm_g_slice_free(data); } static void -_nm_utils_invoke_on_idle_start (gboolean use_timeout, - guint timeout_msec, - GCancellable *cancellable, - NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data) -{ - InvokeOnIdleData *data; - GSource *source; - - g_return_if_fail (callback); - - data = g_slice_new (InvokeOnIdleData); - *data = (InvokeOnIdleData) { - .callback = callback, - .callback_user_data = callback_user_data, - .cancellable = nm_g_object_ref (cancellable), - .cancelled_id = 0, - }; - - if (cancellable) { - if (g_cancellable_is_cancelled (cancellable)) { - /* the cancellable is already cancelled. We ignore the timeout - * and always schedule an idle action. */ - use_timeout = FALSE; - } else { - /* if we are passed a non-cancelled cancellable, we register to the "cancelled" - * signal an invoke the callback synchronously (from the signal handler). - * - * We don't do that, - * - if the cancellable is already cancelled (because we don't want to invoke - * the callback synchronously from the caller). - * - if we have no cancellable at hand. */ - data->cancelled_id = g_signal_connect (cancellable, - "cancelled", - G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled), - data); - } - } - - if (use_timeout) { - source = nm_g_timeout_source_new (timeout_msec, - G_PRIORITY_DEFAULT, - _nm_utils_invoke_on_idle_cb_idle, - data, - NULL); - } else { - source = nm_g_idle_source_new (G_PRIORITY_DEFAULT, - _nm_utils_invoke_on_idle_cb_idle, - data, - NULL); - } - - /* use the current thread default context. */ - g_source_attach (source, - g_main_context_get_thread_default ()); - - data->source = source; +_nm_utils_invoke_on_idle_start(gboolean use_timeout, + guint timeout_msec, + GCancellable * cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data) +{ + InvokeOnIdleData *data; + GSource * source; + + g_return_if_fail(callback); + + data = g_slice_new(InvokeOnIdleData); + *data = (InvokeOnIdleData){ + .callback = callback, + .callback_user_data = callback_user_data, + .cancellable = nm_g_object_ref(cancellable), + .cancelled_id = 0, + }; + + if (cancellable) { + if (g_cancellable_is_cancelled(cancellable)) { + /* the cancellable is already cancelled. We ignore the timeout + * and always schedule an idle action. */ + use_timeout = FALSE; + } else { + /* if we are passed a non-cancelled cancellable, we register to the "cancelled" + * signal an invoke the callback synchronously (from the signal handler). + * + * We don't do that, + * - if the cancellable is already cancelled (because we don't want to invoke + * the callback synchronously from the caller). + * - if we have no cancellable at hand. */ + data->cancelled_id = g_signal_connect(cancellable, + "cancelled", + G_CALLBACK(_nm_utils_invoke_on_idle_cb_cancelled), + data); + } + } + + if (use_timeout) { + source = nm_g_timeout_source_new(timeout_msec, + G_PRIORITY_DEFAULT, + _nm_utils_invoke_on_idle_cb_idle, + data, + NULL); + } else { + source = + nm_g_idle_source_new(G_PRIORITY_DEFAULT, _nm_utils_invoke_on_idle_cb_idle, data, NULL); + } + + /* use the current thread default context. */ + g_source_attach(source, g_main_context_get_thread_default()); + + data->source = source; } void -nm_utils_invoke_on_idle (GCancellable *cancellable, - NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data) +nm_utils_invoke_on_idle(GCancellable * cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data) { - _nm_utils_invoke_on_idle_start (FALSE, 0, cancellable, callback, callback_user_data); + _nm_utils_invoke_on_idle_start(FALSE, 0, cancellable, callback, callback_user_data); } void -nm_utils_invoke_on_timeout (guint timeout_msec, - GCancellable *cancellable, - NMUtilsInvokeOnIdleCallback callback, - gpointer callback_user_data) +nm_utils_invoke_on_timeout(guint timeout_msec, + GCancellable * cancellable, + NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data) { - _nm_utils_invoke_on_idle_start (TRUE, timeout_msec, cancellable, callback, callback_user_data); + _nm_utils_invoke_on_idle_start(TRUE, timeout_msec, cancellable, callback, callback_user_data); } /*****************************************************************************/ int -nm_utils_getpagesize (void) +nm_utils_getpagesize(void) { - static volatile int val = 0; - long l; - int v; + static volatile int val = 0; + long l; + int v; - v = g_atomic_int_get (&val); + v = g_atomic_int_get(&val); - if (G_UNLIKELY (v == 0)) { - l = sysconf (_SC_PAGESIZE); + if (G_UNLIKELY(v == 0)) { + l = sysconf(_SC_PAGESIZE); - g_return_val_if_fail (l > 0 && l < G_MAXINT, 4*1024); + g_return_val_if_fail(l > 0 && l < G_MAXINT, 4 * 1024); - v = (int) l; - if (!g_atomic_int_compare_and_exchange (&val, 0, v)) { - v = g_atomic_int_get (&val); - g_return_val_if_fail (v > 0, 4*1024); - } - } + v = (int) l; + if (!g_atomic_int_compare_and_exchange(&val, 0, v)) { + v = g_atomic_int_get(&val); + g_return_val_if_fail(v > 0, 4 * 1024); + } + } - nm_assert (v > 0); + nm_assert(v > 0); #if NM_MORE_ASSERTS > 5 - nm_assert (v == getpagesize ()); - nm_assert (v == sysconf (_SC_PAGESIZE)); + nm_assert(v == getpagesize()); + nm_assert(v == sysconf(_SC_PAGESIZE)); #endif - return v; + return v; } gboolean -nm_utils_memeqzero (gconstpointer data, gsize length) +nm_utils_memeqzero(gconstpointer data, gsize length) { - const unsigned char *p = data; - int len; + const unsigned char *p = data; + int len; - /* Taken from https://github.com/rustyrussell/ccan/blob/9d2d2c49f053018724bcc6e37029da10b7c3d60d/ccan/mem/mem.c#L92, - * CC-0 licensed. */ + /* Taken from https://github.com/rustyrussell/ccan/blob/9d2d2c49f053018724bcc6e37029da10b7c3d60d/ccan/mem/mem.c#L92, + * CC-0 licensed. */ - /* Check first 16 bytes manually */ - for (len = 0; len < 16; len++) { - if (!length) - return TRUE; - if (*p) - return FALSE; - p++; - length--; - } + /* Check first 16 bytes manually */ + for (len = 0; len < 16; len++) { + if (!length) + return TRUE; + if (*p) + return FALSE; + p++; + length--; + } - /* Now we know that's zero, memcmp with self. */ - return memcmp (data, p, length) == 0; + /* Now we know that's zero, memcmp with self. */ + return memcmp(data, p, length) == 0; } /** @@ -4046,273 +4340,255 @@ nm_utils_memeqzero (gconstpointer data, gsize length) * Returns: the binary value converted to a hex string. If @out is given, * this always returns @out. If @out is %NULL, a newly allocated string * is returned. This never returns %NULL, for buffers of length zero - * an empty string is returend. + * an empty string is returned. */ char * -nm_utils_bin2hexstr_full (gconstpointer addr, - gsize length, - char delimiter, - gboolean upper_case, - char *out) -{ - const guint8 *in = addr; - const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; - char *out0; - - if (out) - out0 = out; - else { - out0 = out = g_new (char, length == 0 - ? 1u - : ( delimiter == '\0' - ? length * 2u + 1u - : length * 3u)); - } - - /* @out must contain at least @length*3 bytes if @delimiter is set, - * otherwise, @length*2+1. */ - - if (length > 0) { - nm_assert (in); - for (;;) { - const guint8 v = *in++; - - *out++ = LOOKUP[v >> 4]; - *out++ = LOOKUP[v & 0x0F]; - length--; - if (!length) - break; - if (delimiter) - *out++ = delimiter; - } - } - - *out = '\0'; - return out0; +nm_utils_bin2hexstr_full(gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char * out) +{ + const guint8 *in = addr; + const char * LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + char * out0; + + if (out) + out0 = out; + else { + out0 = out = + g_new(char, length == 0 ? 1u : (delimiter == '\0' ? length * 2u + 1u : length * 3u)); + } + + /* @out must contain at least @length*3 bytes if @delimiter is set, + * otherwise, @length*2+1. */ + + if (length > 0) { + nm_assert(in); + for (;;) { + const guint8 v = *in++; + + *out++ = LOOKUP[v >> 4]; + *out++ = LOOKUP[v & 0x0F]; + length--; + if (!length) + break; + if (delimiter) + *out++ = delimiter; + } + } + + *out = '\0'; + return out0; } guint8 * -nm_utils_hexstr2bin_full (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - guint8 *buffer, - gsize buffer_len, - gsize *out_len) -{ - const char *in = hexstr; - guint8 *out = buffer; - gboolean delimiter_has = TRUE; - guint8 delimiter = '\0'; - gsize len; - - nm_assert (hexstr); - nm_assert (buffer); - nm_assert (required_len > 0 || out_len); - - if ( allow_0x_prefix - && in[0] == '0' - && in[1] == 'x') - in += 2; - - while (TRUE) { - const guint8 d1 = in[0]; - guint8 d2; - int i1, i2; - - i1 = nm_utils_hexchar_to_int (d1); - if (i1 < 0) - goto fail; - - /* If there's no leading zero (ie "aa:b:cc") then fake it */ - d2 = in[1]; - if ( d2 - && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { - *out++ = (i1 << 4) + i2; - d2 = in[2]; - if (!d2) - break; - in += 2; - } else { - /* Fake leading zero */ - *out++ = i1; - if (!d2) { - if (!delimiter_has) { - /* when using no delimiter, there must be pairs of hex chars */ - goto fail; - } - break; - } - in += 1; - } - - if (--buffer_len == 0) - goto fail; - - if (delimiter_has) { - if (d2 != delimiter) { - if (delimiter) - goto fail; - if (delimiter_candidates) { - while (delimiter_candidates[0]) { - if (delimiter_candidates++[0] == d2) - delimiter = d2; - } - } - if (!delimiter) { - if (delimiter_required) - goto fail; - delimiter_has = FALSE; - continue; - } - } - in++; - } - } - - len = out - buffer; - if ( required_len == 0 - || len == required_len) { - NM_SET_OUT (out_len, len); - return buffer; - } +nm_utils_hexstr2bin_full(const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + gboolean hexdigit_pairs_required, + const char *delimiter_candidates, + gsize required_len, + guint8 * buffer, + gsize buffer_len, + gsize * out_len) +{ + const char *in = hexstr; + guint8 * out = buffer; + gboolean delimiter_has = TRUE; + guint8 delimiter = '\0'; + gsize len; + + nm_assert(hexstr); + nm_assert(buffer); + nm_assert(required_len > 0 || out_len); + + if (allow_0x_prefix && in[0] == '0' && in[1] == 'x') + in += 2; + + while (TRUE) { + const guint8 d1 = in[0]; + guint8 d2; + int i1, i2; + + i1 = nm_utils_hexchar_to_int(d1); + if (i1 < 0) + goto fail; + + /* If there's no leading zero (ie "aa:b:cc") then fake it */ + d2 = in[1]; + if (d2 && (i2 = nm_utils_hexchar_to_int(d2)) >= 0) { + *out++ = (i1 << 4) + i2; + d2 = in[2]; + if (!d2) + break; + in += 2; + } else { + /* Fake leading zero */ + *out++ = i1; + if (!d2) { + if (!delimiter_has || hexdigit_pairs_required) { + /* when using no delimiter, there must be pairs of hex chars */ + goto fail; + } + break; + } else if (hexdigit_pairs_required) + goto fail; + in += 1; + } + + if (--buffer_len == 0) + goto fail; + + if (delimiter_has) { + if (d2 != delimiter) { + if (delimiter) + goto fail; + if (delimiter_candidates) { + while (delimiter_candidates[0]) { + if (delimiter_candidates++[0] == d2) + delimiter = d2; + } + } + if (!delimiter) { + if (delimiter_required) + goto fail; + delimiter_has = FALSE; + continue; + } + } + in++; + } + } + + len = out - buffer; + if (required_len == 0 || len == required_len) { + NM_SET_OUT(out_len, len); + return buffer; + } fail: - NM_SET_OUT (out_len, 0); - return NULL; + NM_SET_OUT(out_len, 0); + return NULL; } guint8 * -nm_utils_hexstr2bin_alloc (const char *hexstr, - gboolean allow_0x_prefix, - gboolean delimiter_required, - const char *delimiter_candidates, - gsize required_len, - gsize *out_len) -{ - guint8 *buffer; - gsize buffer_len, len; - - if (G_UNLIKELY (!hexstr)) { - NM_SET_OUT (out_len, 0); - g_return_val_if_fail (hexstr, NULL); - } - - nm_assert (required_len > 0 || out_len); - - if ( allow_0x_prefix - && hexstr[0] == '0' - && hexstr[1] == 'x') - hexstr += 2; - - if (!hexstr[0]) - goto fail; - - if (required_len > 0) - buffer_len = required_len; - else - buffer_len = strlen (hexstr) / 2 + 3; - - buffer = g_malloc (buffer_len); - - if (nm_utils_hexstr2bin_full (hexstr, - FALSE, - delimiter_required, - delimiter_candidates, - required_len, - buffer, - buffer_len, - &len)) { - NM_SET_OUT (out_len, len); - return buffer; - } - - g_free (buffer); +nm_utils_hexstr2bin_alloc(const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize * out_len) +{ + guint8 *buffer; + gsize buffer_len, len; + + if (G_UNLIKELY(!hexstr)) { + NM_SET_OUT(out_len, 0); + g_return_val_if_fail(hexstr, NULL); + } + + nm_assert(required_len > 0 || out_len); + + if (allow_0x_prefix && hexstr[0] == '0' && hexstr[1] == 'x') + hexstr += 2; + + if (!hexstr[0]) + goto fail; + + if (required_len > 0) + buffer_len = required_len; + else + buffer_len = strlen(hexstr) / 2 + 3; + + buffer = g_malloc(buffer_len); + + if (nm_utils_hexstr2bin_full(hexstr, + FALSE, + delimiter_required, + FALSE, + delimiter_candidates, + required_len, + buffer, + buffer_len, + &len)) { + NM_SET_OUT(out_len, len); + return buffer; + } + + g_free(buffer); fail: - NM_SET_OUT (out_len, 0); - return NULL; + NM_SET_OUT(out_len, 0); + return NULL; } /*****************************************************************************/ GVariant * -nm_utils_gvariant_vardict_filter (GVariant *src, - gboolean (*filter_fcn) (const char *key, - GVariant *val, - char **out_key, - GVariant **out_val, - gpointer user_data), - gpointer user_data) -{ - GVariantIter iter; - GVariantBuilder builder; - const char *key; - GVariant *val; - - g_return_val_if_fail (src && g_variant_is_of_type (src, G_VARIANT_TYPE_VARDICT), NULL); - g_return_val_if_fail (filter_fcn, NULL); - - g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); - - g_variant_iter_init (&iter, src); - while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) { - _nm_unused gs_unref_variant GVariant *val_free = val; - gs_free char *key2 = NULL; - gs_unref_variant GVariant *val2 = NULL; - - if (filter_fcn (key, - val, - &key2, - &val2, - user_data)) { - g_variant_builder_add (&builder, - "{sv}", - key2 ?: key, - val2 ?: val); - } - } - - return g_variant_builder_end (&builder); +nm_utils_gvariant_vardict_filter(GVariant *src, + gboolean (*filter_fcn)(const char *key, + GVariant * val, + char ** out_key, + GVariant ** out_val, + gpointer user_data), + gpointer user_data) +{ + GVariantIter iter; + GVariantBuilder builder; + const char * key; + GVariant * val; + + g_return_val_if_fail(src && g_variant_is_of_type(src, G_VARIANT_TYPE_VARDICT), NULL); + g_return_val_if_fail(filter_fcn, NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_iter_init(&iter, src); + while (g_variant_iter_next(&iter, "{&sv}", &key, &val)) { + _nm_unused gs_unref_variant GVariant *val_free = val; + gs_free char * key2 = NULL; + gs_unref_variant GVariant *val2 = NULL; + + if (filter_fcn(key, val, &key2, &val2, user_data)) { + g_variant_builder_add(&builder, "{sv}", key2 ?: key, val2 ?: val); + } + } + + return g_variant_builder_end(&builder); } static gboolean -_gvariant_vardict_filter_drop_one (const char *key, - GVariant *val, - char **out_key, - GVariant **out_val, - gpointer user_data) +_gvariant_vardict_filter_drop_one(const char *key, + GVariant * val, + char ** out_key, + GVariant ** out_val, + gpointer user_data) { - return !nm_streq (key, user_data); + return !nm_streq(key, user_data); } GVariant * -nm_utils_gvariant_vardict_filter_drop_one (GVariant *src, - const char *key) +nm_utils_gvariant_vardict_filter_drop_one(GVariant *src, const char *key) { - return nm_utils_gvariant_vardict_filter (src, - _gvariant_vardict_filter_drop_one, - (gpointer) key); + return nm_utils_gvariant_vardict_filter(src, _gvariant_vardict_filter_drop_one, (gpointer) key); } /*****************************************************************************/ static gboolean -debug_key_matches (const char *key, - const char *token, - guint length) +debug_key_matches(const char *key, const char *token, guint length) { - /* may not call GLib functions: see note in g_parse_debug_string() */ - for (; length; length--, key++, token++) { - char k = (*key == '_') ? '-' : g_ascii_tolower (*key ); - char t = (*token == '_') ? '-' : g_ascii_tolower (*token); + /* may not call GLib functions: see note in g_parse_debug_string() */ + for (; length; length--, key++, token++) { + char k = (*key == '_') ? '-' : g_ascii_tolower(*key); + char t = (*token == '_') ? '-' : g_ascii_tolower(*token); - if (k != t) - return FALSE; - } + if (k != t) + return FALSE; + } - return *key == '\0'; + return *key == '\0'; } /** @@ -4327,415 +4603,436 @@ debug_key_matches (const char *key, * Returns: the flags */ guint -nm_utils_parse_debug_string (const char *string, - const GDebugKey *keys, - guint nkeys) +nm_utils_parse_debug_string(const char *string, const GDebugKey *keys, guint nkeys) { - guint i; - guint result = 0; - const char *q; + guint i; + guint result = 0; + const char *q; - if (string == NULL) - return 0; + if (string == NULL) + return 0; - while (*string) { - q = strpbrk (string, ":;, \t"); - if (!q) - q = string + strlen (string); + while (*string) { + q = strpbrk(string, ":;, \t"); + if (!q) + q = string + strlen(string); - for (i = 0; i < nkeys; i++) { - if (debug_key_matches (keys[i].key, string, q - string)) - result |= keys[i].value; - } + for (i = 0; i < nkeys; i++) { + if (debug_key_matches(keys[i].key, string, q - string)) + result |= keys[i].value; + } - string = q; - if (*string) - string++; - } + string = q; + if (*string) + string++; + } - return result; + return result; } /*****************************************************************************/ GSource * -nm_g_idle_source_new (int priority, - GSourceFunc func, - gpointer user_data, - GDestroyNotify destroy_notify) +nm_g_idle_source_new(int priority, + GSourceFunc func, + gpointer user_data, + GDestroyNotify destroy_notify) { - GSource *source; + GSource *source; - source = g_idle_source_new (); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority (source, priority); - g_source_set_callback (source, func, user_data, destroy_notify); - return source; + source = g_idle_source_new(); + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + g_source_set_callback(source, func, user_data, destroy_notify); + return source; } GSource * -nm_g_timeout_source_new (guint timeout_msec, - int priority, - GSourceFunc func, - gpointer user_data, - GDestroyNotify destroy_notify) +nm_g_timeout_source_new(guint timeout_msec, + int priority, + GSourceFunc func, + gpointer user_data, + GDestroyNotify destroy_notify) { - GSource *source; + GSource *source; - source = g_timeout_source_new (timeout_msec); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority (source, priority); - g_source_set_callback (source, func, user_data, destroy_notify); - return source; + source = g_timeout_source_new(timeout_msec); + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + g_source_set_callback(source, func, user_data, destroy_notify); + return source; } GSource * -nm_g_unix_signal_source_new (int signum, - int priority, - GSourceFunc handler, - gpointer user_data, - GDestroyNotify notify) +nm_g_timeout_source_new_seconds(guint timeout_sec, + int priority, + GSourceFunc func, + gpointer user_data, + GDestroyNotify destroy_notify) { - GSource *source; + GSource *source; - source = g_unix_signal_source_new (signum); + source = g_timeout_source_new_seconds(timeout_sec); + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + g_source_set_callback(source, func, user_data, destroy_notify); + return source; +} + +GSource * +nm_g_unix_signal_source_new(int signum, + int priority, + GSourceFunc handler, + gpointer user_data, + GDestroyNotify notify) +{ + GSource *source; + + source = g_unix_signal_source_new(signum); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority (source, priority); - g_source_set_callback (source, handler, user_data, notify); - return source; + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + g_source_set_callback(source, handler, user_data, notify); + return source; } GSource * -nm_g_unix_fd_source_new (int fd, - GIOCondition io_condition, - int priority, - gboolean (*source_func) (int fd, - GIOCondition condition, - gpointer user_data), - gpointer user_data, - GDestroyNotify destroy_notify) +nm_g_unix_fd_source_new(int fd, + GIOCondition io_condition, + int priority, + gboolean (*source_func)(int fd, GIOCondition condition, gpointer user_data), + gpointer user_data, + GDestroyNotify destroy_notify) { - GSource *source; + GSource *source; - source = g_unix_fd_source_new (fd, io_condition); + source = g_unix_fd_source_new(fd, io_condition); - if (priority != G_PRIORITY_DEFAULT) - g_source_set_priority (source, priority); - g_source_set_callback (source, G_SOURCE_FUNC (source_func), user_data, destroy_notify); - return source; + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority(source, priority); + g_source_set_callback(source, G_SOURCE_FUNC(source_func), user_data, destroy_notify); + return source; } /*****************************************************************************/ -#define _CTX_LOG(fmt, ...) \ - G_STMT_START { \ - if (FALSE) { \ - gint64 _ts = g_get_monotonic_time () / 100; \ - \ - g_printerr (">>>> [%"G_GINT64_FORMAT".%05"G_GINT64_FORMAT"] [src:%p]: " fmt "\n", \ - _ts / 10000, \ - _ts % 10000, \ - (ctx_src), \ - ##__VA_ARGS__); \ - } \ - } G_STMT_END +#define _CTX_LOG(fmt, ...) \ + G_STMT_START \ + { \ + if (FALSE) { \ + gint64 _ts = g_get_monotonic_time() / 100; \ + \ + g_printerr(">>>> [%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] [src:%p]: " fmt "\n", \ + _ts / 10000, \ + _ts % 10000, \ + (ctx_src), \ + ##__VA_ARGS__); \ + } \ + } \ + G_STMT_END typedef struct { - int fd; - guint events; - guint registered_events; - union { - int one; - int *many; - } idx; - gpointer tag; - bool stale:1; - bool has_many_idx:1; + int fd; + guint events; + guint registered_events; + union { + int one; + int *many; + } idx; + gpointer tag; + bool stale : 1; + bool has_many_idx : 1; } PollData; typedef struct { - GSource source; - GMainContext *context; - GHashTable *fds; - GPollFD *fds_arr; - int fds_len; - int max_priority; - bool acquired:1; + GSource source; + GMainContext *context; + GHashTable * fds; + GPollFD * fds_arr; + int fds_len; + int max_priority; + bool acquired : 1; } CtxIntegSource; static void -_poll_data_free (gpointer user_data) +_poll_data_free(gpointer user_data) { - PollData *poll_data = user_data; + PollData *poll_data = user_data; - if (poll_data->has_many_idx) - g_free (poll_data->idx.many); - nm_g_slice_free (poll_data); + if (poll_data->has_many_idx) + g_free(poll_data->idx.many); + nm_g_slice_free(poll_data); } static void -_ctx_integ_source_reacquire (CtxIntegSource *ctx_src) +_ctx_integ_source_reacquire(CtxIntegSource *ctx_src) { - if (G_LIKELY ( ctx_src->acquired - && g_main_context_is_owner (ctx_src->context))) - return; + if (G_LIKELY(ctx_src->acquired && g_main_context_is_owner(ctx_src->context))) + return; - /* the parent context now iterates on a different thread. - * We need to release and reacquire the inner context. */ + /* the parent context now iterates on a different thread. + * We need to release and reacquire the inner context. */ - if (ctx_src->acquired) - g_main_context_release (ctx_src->context); + if (ctx_src->acquired) + g_main_context_release(ctx_src->context); - if (G_UNLIKELY (!g_main_context_acquire (ctx_src->context))) { - /* Nobody is supposed to reacquire the context while we use it. This is a bug - * of the user. */ - ctx_src->acquired = FALSE; - g_return_if_reached (); - } - ctx_src->acquired = TRUE; + if (G_UNLIKELY(!g_main_context_acquire(ctx_src->context))) { + /* Nobody is supposed to reacquire the context while we use it. This is a bug + * of the user. */ + ctx_src->acquired = FALSE; + g_return_if_reached(); + } + ctx_src->acquired = TRUE; } static gboolean -_ctx_integ_source_prepare (GSource *source, - int *out_timeout) -{ - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - int max_priority; - int timeout = -1; - gboolean any_ready; - int fds_allocated; - int fds_len_old; - gs_free GPollFD *fds_arr_old = NULL; - GHashTableIter h_iter; - PollData *poll_data; - gboolean fds_changed; - int i; - - _CTX_LOG ("prepare..."); - - _ctx_integ_source_reacquire (ctx_src); - - any_ready = g_main_context_prepare (ctx_src->context, &max_priority); - - fds_arr_old = g_steal_pointer (&ctx_src->fds_arr); - fds_len_old = ctx_src->fds_len; - - fds_allocated = NM_MAX (1, fds_len_old); /* there is at least the wakeup's FD */ - ctx_src->fds_arr = g_new (GPollFD, fds_allocated); - - while ((ctx_src->fds_len = g_main_context_query (ctx_src->context, - max_priority, - &timeout, - ctx_src->fds_arr, - fds_allocated)) > fds_allocated) { - fds_allocated = ctx_src->fds_len; - g_free (ctx_src->fds_arr); - ctx_src->fds_arr = g_new (GPollFD, fds_allocated); - } - - fds_changed = FALSE; - if (fds_len_old != ctx_src->fds_len) - fds_changed = TRUE; - else { - for (i = 0; i < ctx_src->fds_len; i++) { - if ( fds_arr_old[i].fd != ctx_src->fds_arr[i].fd - || fds_arr_old[i].events != ctx_src->fds_arr[i].events) { - fds_changed = TRUE; - break; - } - } - } - - if (G_UNLIKELY (fds_changed)) { - - g_hash_table_iter_init (&h_iter, ctx_src->fds); - while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) - poll_data->stale = TRUE; - - for (i = 0; i < ctx_src->fds_len; i++) { - const GPollFD *fd = &ctx_src->fds_arr[i]; - - poll_data = g_hash_table_lookup (ctx_src->fds, &fd->fd); - - if (G_UNLIKELY (!poll_data)) { - poll_data = g_slice_new (PollData); - *poll_data = (PollData) { - .fd = fd->fd, - .idx.one = i, - .has_many_idx = FALSE, - .events = fd->events, - .registered_events = 0, - .tag = NULL, - .stale = FALSE, - }; - g_hash_table_add (ctx_src->fds, poll_data); - nm_assert (poll_data == g_hash_table_lookup (ctx_src->fds, &fd->fd)); - continue; - } - - if (G_LIKELY (poll_data->stale)) { - if (poll_data->has_many_idx) { - g_free (poll_data->idx.many); - poll_data->has_many_idx = FALSE; - } - poll_data->events = fd->events; - poll_data->idx.one = i; - poll_data->stale = FALSE; - continue; - } - - /* How odd. We have duplicate FDs. In fact, currently g_main_context_query() always - * coalesces the FDs and this cannot happen. However, that is not documented behavior, - * so we should not rely on that. So we need to keep a list of indexes... */ - poll_data->events |= fd->events; - if (!poll_data->has_many_idx) { - int idx0; - - idx0 = poll_data->idx.one; - poll_data->has_many_idx = TRUE; - poll_data->idx.many = g_new (int, 4); - poll_data->idx.many[0] = 2; /* number allocated */ - poll_data->idx.many[1] = 2; /* number used */ - poll_data->idx.many[2] = idx0; - poll_data->idx.many[3] = i; - } else { - if (poll_data->idx.many[0] == poll_data->idx.many[1]) { - poll_data->idx.many[0] *= 2; - poll_data->idx.many = g_realloc (poll_data->idx.many, sizeof (int) * (2 + poll_data->idx.many[0])); - } - poll_data->idx.many[2 + poll_data->idx.many[1]] = i; - poll_data->idx.many[1]++; - } - - } - - g_hash_table_iter_init (&h_iter, ctx_src->fds); - while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) { - if (poll_data->stale) { - nm_assert (poll_data->tag); - nm_assert (poll_data->events == poll_data->registered_events); - _CTX_LOG ("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); - g_source_remove_unix_fd (&ctx_src->source, poll_data->tag); - g_hash_table_iter_remove (&h_iter); - continue; - } - if (!poll_data->tag) { - _CTX_LOG ("prepare: add poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); - poll_data->registered_events = poll_data->events; - poll_data->tag = g_source_add_unix_fd (&ctx_src->source, poll_data->fd, poll_data->registered_events); - continue; - } - if (poll_data->registered_events != poll_data->events) { - _CTX_LOG ("prepare: update poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); - poll_data->registered_events = poll_data->events; - g_source_modify_unix_fd (&ctx_src->source, poll_data->tag, poll_data->registered_events); - } - } - } - - NM_SET_OUT (out_timeout, timeout); - ctx_src->max_priority = max_priority; - - _CTX_LOG ("prepare: done, any-ready=%d, timeout=%d, max-priority=%d", any_ready, timeout, max_priority); - - /* we always need to poll, because we have some file descriptors. */ - return FALSE; +_ctx_integ_source_prepare(GSource *source, int *out_timeout) +{ + CtxIntegSource *ctx_src = ((CtxIntegSource *) source); + int max_priority; + int timeout = -1; + gboolean any_ready; + int fds_allocated; + int fds_len_old; + gs_free GPollFD *fds_arr_old = NULL; + GHashTableIter h_iter; + PollData * poll_data; + gboolean fds_changed; + int i; + + _CTX_LOG("prepare..."); + + _ctx_integ_source_reacquire(ctx_src); + + any_ready = g_main_context_prepare(ctx_src->context, &max_priority); + + fds_arr_old = g_steal_pointer(&ctx_src->fds_arr); + fds_len_old = ctx_src->fds_len; + + fds_allocated = NM_MAX(1, fds_len_old); /* there is at least the wakeup's FD */ + ctx_src->fds_arr = g_new(GPollFD, fds_allocated); + + while ((ctx_src->fds_len = g_main_context_query(ctx_src->context, + max_priority, + &timeout, + ctx_src->fds_arr, + fds_allocated)) + > fds_allocated) { + fds_allocated = ctx_src->fds_len; + g_free(ctx_src->fds_arr); + ctx_src->fds_arr = g_new(GPollFD, fds_allocated); + } + + fds_changed = FALSE; + if (fds_len_old != ctx_src->fds_len) + fds_changed = TRUE; + else { + for (i = 0; i < ctx_src->fds_len; i++) { + if (fds_arr_old[i].fd != ctx_src->fds_arr[i].fd + || fds_arr_old[i].events != ctx_src->fds_arr[i].events) { + fds_changed = TRUE; + break; + } + } + } + + if (G_UNLIKELY(fds_changed)) { + g_hash_table_iter_init(&h_iter, ctx_src->fds); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) + poll_data->stale = TRUE; + + for (i = 0; i < ctx_src->fds_len; i++) { + const GPollFD *fd = &ctx_src->fds_arr[i]; + + poll_data = g_hash_table_lookup(ctx_src->fds, &fd->fd); + + if (G_UNLIKELY(!poll_data)) { + poll_data = g_slice_new(PollData); + *poll_data = (PollData){ + .fd = fd->fd, + .idx.one = i, + .has_many_idx = FALSE, + .events = fd->events, + .registered_events = 0, + .tag = NULL, + .stale = FALSE, + }; + g_hash_table_add(ctx_src->fds, poll_data); + nm_assert(poll_data == g_hash_table_lookup(ctx_src->fds, &fd->fd)); + continue; + } + + if (G_LIKELY(poll_data->stale)) { + if (poll_data->has_many_idx) { + g_free(poll_data->idx.many); + poll_data->has_many_idx = FALSE; + } + poll_data->events = fd->events; + poll_data->idx.one = i; + poll_data->stale = FALSE; + continue; + } + + /* How odd. We have duplicate FDs. In fact, currently g_main_context_query() always + * coalesces the FDs and this cannot happen. However, that is not documented behavior, + * so we should not rely on that. So we need to keep a list of indexes... */ + poll_data->events |= fd->events; + if (!poll_data->has_many_idx) { + int idx0; + + idx0 = poll_data->idx.one; + poll_data->has_many_idx = TRUE; + poll_data->idx.many = g_new(int, 4); + poll_data->idx.many[0] = 2; /* number allocated */ + poll_data->idx.many[1] = 2; /* number used */ + poll_data->idx.many[2] = idx0; + poll_data->idx.many[3] = i; + } else { + if (poll_data->idx.many[0] == poll_data->idx.many[1]) { + poll_data->idx.many[0] *= 2; + poll_data->idx.many = + g_realloc(poll_data->idx.many, sizeof(int) * (2 + poll_data->idx.many[0])); + } + poll_data->idx.many[2 + poll_data->idx.many[1]] = i; + poll_data->idx.many[1]++; + } + } + + g_hash_table_iter_init(&h_iter, ctx_src->fds); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) { + if (poll_data->stale) { + nm_assert(poll_data->tag); + nm_assert(poll_data->events == poll_data->registered_events); + _CTX_LOG("prepare: remove poll fd=%d, events=0x%x", + poll_data->fd, + poll_data->events); + g_source_remove_unix_fd(&ctx_src->source, poll_data->tag); + g_hash_table_iter_remove(&h_iter); + continue; + } + if (!poll_data->tag) { + _CTX_LOG("prepare: add poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); + poll_data->registered_events = poll_data->events; + poll_data->tag = g_source_add_unix_fd(&ctx_src->source, + poll_data->fd, + poll_data->registered_events); + continue; + } + if (poll_data->registered_events != poll_data->events) { + _CTX_LOG("prepare: update poll fd=%d, events=0x%x", + poll_data->fd, + poll_data->events); + poll_data->registered_events = poll_data->events; + g_source_modify_unix_fd(&ctx_src->source, + poll_data->tag, + poll_data->registered_events); + } + } + } + + NM_SET_OUT(out_timeout, timeout); + ctx_src->max_priority = max_priority; + + _CTX_LOG("prepare: done, any-ready=%d, timeout=%d, max-priority=%d", + any_ready, + timeout, + max_priority); + + /* we always need to poll, because we have some file descriptors. */ + return FALSE; } static gboolean -_ctx_integ_source_check (GSource *source) +_ctx_integ_source_check(GSource *source) { - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - GHashTableIter h_iter; - gboolean some_ready; - PollData *poll_data; + CtxIntegSource *ctx_src = ((CtxIntegSource *) source); + GHashTableIter h_iter; + gboolean some_ready; + PollData * poll_data; - nm_assert (ctx_src->context); + nm_assert(ctx_src->context); - _CTX_LOG ("check"); + _CTX_LOG("check"); - _ctx_integ_source_reacquire (ctx_src); + _ctx_integ_source_reacquire(ctx_src); - g_hash_table_iter_init (&h_iter, ctx_src->fds); - while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) { - guint revents; + g_hash_table_iter_init(&h_iter, ctx_src->fds); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) { + guint revents; - revents = g_source_query_unix_fd (&ctx_src->source, poll_data->tag); - if (G_UNLIKELY (poll_data->has_many_idx)) { - int num = poll_data->idx.many[1]; - int *p_idx = &poll_data->idx.many[2]; + revents = g_source_query_unix_fd(&ctx_src->source, poll_data->tag); + if (G_UNLIKELY(poll_data->has_many_idx)) { + int num = poll_data->idx.many[1]; + int *p_idx = &poll_data->idx.many[2]; - for (; num > 0; num--, p_idx++) - ctx_src->fds_arr[*p_idx].revents = revents; - } else - ctx_src->fds_arr[poll_data->idx.one].revents = revents; - } + for (; num > 0; num--, p_idx++) + ctx_src->fds_arr[*p_idx].revents = revents; + } else + ctx_src->fds_arr[poll_data->idx.one].revents = revents; + } - some_ready = g_main_context_check (ctx_src->context, - ctx_src->max_priority, - ctx_src->fds_arr, - ctx_src->fds_len); + some_ready = g_main_context_check(ctx_src->context, + ctx_src->max_priority, + ctx_src->fds_arr, + ctx_src->fds_len); - _CTX_LOG ("check (some-ready=%d)...", some_ready); + _CTX_LOG("check (some-ready=%d)...", some_ready); - return some_ready; + return some_ready; } static gboolean -_ctx_integ_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) +_ctx_integ_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); + CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - nm_assert (ctx_src->context); + nm_assert(ctx_src->context); - _ctx_integ_source_reacquire (ctx_src); + _ctx_integ_source_reacquire(ctx_src); - _CTX_LOG ("dispatch"); + _CTX_LOG("dispatch"); - g_main_context_dispatch (ctx_src->context); + g_main_context_dispatch(ctx_src->context); - return G_SOURCE_CONTINUE; + return G_SOURCE_CONTINUE; } static void -_ctx_integ_source_finalize (GSource *source) +_ctx_integ_source_finalize(GSource *source) { - CtxIntegSource *ctx_src = ((CtxIntegSource *) source); - GHashTableIter h_iter; - PollData *poll_data; + CtxIntegSource *ctx_src = ((CtxIntegSource *) source); + GHashTableIter h_iter; + PollData * poll_data; - g_return_if_fail (ctx_src->context); + g_return_if_fail(ctx_src->context); - _CTX_LOG ("finalize..."); + _CTX_LOG("finalize..."); - g_hash_table_iter_init (&h_iter, ctx_src->fds); - while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) { - nm_assert (poll_data->tag); - _CTX_LOG ("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); - g_source_remove_unix_fd (&ctx_src->source, poll_data->tag); - g_hash_table_iter_remove (&h_iter); - } + g_hash_table_iter_init(&h_iter, ctx_src->fds); + while (g_hash_table_iter_next(&h_iter, (gpointer *) &poll_data, NULL)) { + nm_assert(poll_data->tag); + _CTX_LOG("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events); + g_source_remove_unix_fd(&ctx_src->source, poll_data->tag); + g_hash_table_iter_remove(&h_iter); + } - nm_clear_pointer (&ctx_src->fds, g_hash_table_unref); - nm_clear_g_free (&ctx_src->fds_arr); - ctx_src->fds_len = 0; + nm_clear_pointer(&ctx_src->fds, g_hash_table_unref); + nm_clear_g_free(&ctx_src->fds_arr); + ctx_src->fds_len = 0; - if (ctx_src->acquired) { - ctx_src->acquired = FALSE; - g_main_context_release (ctx_src->context); - } + if (ctx_src->acquired) { + ctx_src->acquired = FALSE; + g_main_context_release(ctx_src->context); + } - nm_clear_pointer (&ctx_src->context, g_main_context_unref); + nm_clear_pointer(&ctx_src->context, g_main_context_unref); } static GSourceFuncs ctx_integ_source_funcs = { - .prepare = _ctx_integ_source_prepare, - .check = _ctx_integ_source_check, - .dispatch = _ctx_integ_source_dispatch, - .finalize = _ctx_integ_source_finalize, + .prepare = _ctx_integ_source_prepare, + .check = _ctx_integ_source_check, + .dispatch = _ctx_integ_source_dispatch, + .finalize = _ctx_integ_source_finalize, }; /** @@ -4760,260 +5057,260 @@ static GSourceFuncs ctx_integ_source_funcs = { * outer context. */ GSource * -nm_utils_g_main_context_create_integrate_source (GMainContext *inner_context) +nm_utils_g_main_context_create_integrate_source(GMainContext *inner_context) { - CtxIntegSource *ctx_src; + CtxIntegSource *ctx_src; - g_return_val_if_fail (inner_context, NULL); + g_return_val_if_fail(inner_context, NULL); - if (!g_main_context_acquire (inner_context)) { - /* We require to acquire the context while it's integrated. We need to keep it acquired - * for the entire duration. - * - * This is also necessary because g_source_attach() only wakes up the context, if - * the context is currently acquired. */ - g_return_val_if_reached (NULL); - } + if (!g_main_context_acquire(inner_context)) { + /* We require to acquire the context while it's integrated. We need to keep it acquired + * for the entire duration. + * + * This is also necessary because g_source_attach() only wakes up the context, if + * the context is currently acquired. */ + g_return_val_if_reached(NULL); + } - ctx_src = (CtxIntegSource *) g_source_new (&ctx_integ_source_funcs, sizeof (CtxIntegSource)); + ctx_src = (CtxIntegSource *) g_source_new(&ctx_integ_source_funcs, sizeof(CtxIntegSource)); - g_source_set_name (&ctx_src->source, "ContextIntegrateSource"); + g_source_set_name(&ctx_src->source, "ContextIntegrateSource"); - ctx_src->context = g_main_context_ref (inner_context); - ctx_src->fds = g_hash_table_new_full (nm_pint_hash, nm_pint_equals, _poll_data_free, NULL); - ctx_src->fds_len = 0; - ctx_src->fds_arr = NULL; - ctx_src->acquired = TRUE; - ctx_src->max_priority = G_MAXINT; + ctx_src->context = g_main_context_ref(inner_context); + ctx_src->fds = g_hash_table_new_full(nm_pint_hash, nm_pint_equals, _poll_data_free, NULL); + ctx_src->fds_len = 0; + ctx_src->fds_arr = NULL; + ctx_src->acquired = TRUE; + ctx_src->max_priority = G_MAXINT; - _CTX_LOG ("create new integ-source for %p", inner_context); + _CTX_LOG("create new integ-source for %p", inner_context); - return &ctx_src->source; + return &ctx_src->source; } gboolean -nm_utils_ifname_valid_kernel (const char *name, GError **error) -{ - int i; - - /* This function follows kernel's interface validation - * function dev_valid_name() in net/core/dev.c. - */ - - if (!name) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name is missing")); - return FALSE; - } - - if (name[0] == '\0') { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name is too short")); - return FALSE; - } - - if ( name[0] == '.' - && ( name[1] == '\0' - || ( name[1] == '.' - && name[2] == '\0'))) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name is reserved")); - return FALSE; - } - - for (i = 0; i < IFNAMSIZ; i++) { - char ch = name[i]; - - if (ch == '\0') - return TRUE; - if ( NM_IN_SET (ch, '/', ':') - || g_ascii_isspace (ch)) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name contains an invalid character")); - return FALSE; - } - } - - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name is longer than 15 characters")); - return FALSE; +nm_utils_ifname_valid_kernel(const char *name, GError **error) +{ + int i; + + /* This function follows kernel's interface validation + * function dev_valid_name() in net/core/dev.c. + */ + + if (!name) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name is missing")); + return FALSE; + } + + if (name[0] == '\0') { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name is too short")); + return FALSE; + } + + if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name is reserved")); + return FALSE; + } + + for (i = 0; i < IFNAMSIZ; i++) { + char ch = name[i]; + + if (ch == '\0') + return TRUE; + if (NM_IN_SET(ch, '/', ':') || g_ascii_isspace(ch)) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name contains an invalid character")); + return FALSE; + } + } + + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name is longer than 15 characters")); + return FALSE; } /*****************************************************************************/ static gboolean -_nm_utils_ifname_valid_kernel (const char *name, GError **error) -{ - if (!nm_utils_ifname_valid_kernel (name, error)) - return FALSE; - - if (strchr (name, '%')) { - /* Kernel's dev_valid_name() accepts (almost) any binary up to 15 chars. - * However, '%' is treated special as a format specifier. Try - * - * ip link add 'dummy%dx' type dummy - * - * Don't allow that for "connection.interface-name", which either - * matches an existing netdev name (thus, it cannot have a '%') or - * is used to configure a name (in which case we don't want kernel - * to replace the format specifier). */ - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("'%%' is not allowed in interface names")); - return FALSE; - } - - if (NM_IN_STRSET (name, "all", - "default", - "bonding_masters")) { - /* Certain names are not allowed. The "all" and "default" names are reserved - * due to their directories in "/proc/sys/net/ipv4/conf/" and "/proc/sys/net/ipv6/conf/". - * - * Also, there is "/sys/class/net/bonding_masters" file. - */ - nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - _("'%s' is not allowed as interface name"), name); - return FALSE; - } - - return TRUE; +_nm_utils_ifname_valid_kernel(const char *name, GError **error) +{ + if (!nm_utils_ifname_valid_kernel(name, error)) + return FALSE; + + if (strchr(name, '%')) { + /* Kernel's dev_valid_name() accepts (almost) any binary up to 15 chars. + * However, '%' is treated special as a format specifier. Try + * + * ip link add 'dummy%dx' type dummy + * + * Don't allow that for "connection.interface-name", which either + * matches an existing netdev name (thus, it cannot have a '%') or + * is used to configure a name (in which case we don't want kernel + * to replace the format specifier). */ + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("'%%' is not allowed in interface names")); + return FALSE; + } + + if (NM_IN_STRSET(name, "all", "default", "bonding_masters")) { + /* Certain names are not allowed. The "all" and "default" names are reserved + * due to their directories in "/proc/sys/net/ipv4/conf/" and "/proc/sys/net/ipv6/conf/". + * + * Also, there is "/sys/class/net/bonding_masters" file. + */ + nm_utils_error_set(error, + NM_UTILS_ERROR_UNKNOWN, + _("'%s' is not allowed as interface name"), + name); + return FALSE; + } + + return TRUE; } static gboolean -_nm_utils_ifname_valid_ovs (const char *name, GError **error) -{ - const char *ch; - - /* OVS actually accepts a wider range of chars (all printable UTF-8 chars), - NetworkManager restricts this to ASCII char as it's a safer option for - now since OVS is not well documented on this matter. - */ - for (ch = name; *ch; ++ch) { - if ( *ch == '\\' - || *ch == '/' - || !g_ascii_isgraph (*ch)) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name must be alphanumerical with " - "no forward or backward slashes")); - return FALSE; - } - }; - return TRUE; +_nm_utils_ifname_valid_ovs(const char *name, GError **error) +{ + const char *ch; + + /* OVS actually accepts a wider range of chars (all printable UTF-8 chars), + * NetworkManager restricts this to ASCII char as it's a safer option for + * now since OVS is not well documented on this matter. + **/ + for (ch = name; *ch; ++ch) { + if (*ch == '\\' || *ch == '/' || !g_ascii_isgraph(*ch)) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name must be alphanumerical with " + "no forward or backward slashes")); + return FALSE; + } + }; + return TRUE; } gboolean -nm_utils_ifname_valid (const char* name, - NMUtilsIfaceType type, - GError **error) -{ - g_return_val_if_fail (!error || !(*error), FALSE); - - if (!name || !(name[0])) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name must not be empty")); - return FALSE; - } - - if (!g_utf8_validate (name, -1, NULL)) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - _("interface name must be UTF-8 encoded")); - return FALSE; - } - - switch (type) { - case NMU_IFACE_KERNEL: - return _nm_utils_ifname_valid_kernel (name, error); - case NMU_IFACE_OVS: - return _nm_utils_ifname_valid_ovs (name, error); - case NMU_IFACE_OVS_AND_KERNEL: - return _nm_utils_ifname_valid_kernel (name, error) - && _nm_utils_ifname_valid_ovs (name, error); - case NMU_IFACE_ANY: { - gs_free_error GError *local = NULL; - - if (_nm_utils_ifname_valid_kernel (name, error ? &local : NULL)) - return TRUE; - if (_nm_utils_ifname_valid_ovs (name, NULL)) - return TRUE; - if (error) - g_propagate_error (error, g_steal_pointer (&local)); - return FALSE; - } - } - - g_return_val_if_reached (FALSE); +nm_utils_ifname_valid(const char *name, NMUtilsIfaceType type, GError **error) +{ + g_return_val_if_fail(!error || !(*error), FALSE); + + if (!name || !(name[0])) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name must not be empty")); + return FALSE; + } + + if (!g_utf8_validate(name, -1, NULL)) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + _("interface name must be UTF-8 encoded")); + return FALSE; + } + + switch (type) { + case NMU_IFACE_KERNEL: + return _nm_utils_ifname_valid_kernel(name, error); + case NMU_IFACE_OVS: + return _nm_utils_ifname_valid_ovs(name, error); + case NMU_IFACE_OVS_AND_KERNEL: + return _nm_utils_ifname_valid_kernel(name, error) + && _nm_utils_ifname_valid_ovs(name, error); + case NMU_IFACE_ANY: + { + gs_free_error GError *local = NULL; + + if (_nm_utils_ifname_valid_kernel(name, error ? &local : NULL)) + return TRUE; + if (_nm_utils_ifname_valid_ovs(name, NULL)) + return TRUE; + if (error) + g_propagate_error(error, g_steal_pointer(&local)); + return FALSE; + } + } + + g_return_val_if_reached(FALSE); } /*****************************************************************************/ void -_nm_str_buf_ensure_size (NMStrBuf *strbuf, - gsize new_size, - gboolean reserve_exact) +_nm_str_buf_ensure_size(NMStrBuf *strbuf, gsize new_size, gboolean reserve_exact) { - _nm_str_buf_assert (strbuf); + _nm_str_buf_assert(strbuf); - /* Currently this only supports strictly growing the buffer. */ - nm_assert (new_size > strbuf->_priv_allocated); + /* Currently, this only supports strictly growing the buffer. */ + nm_assert(new_size > strbuf->_priv_allocated); - if (!reserve_exact) { - new_size = nm_utils_get_next_realloc_size (!strbuf->_priv_do_bzero_mem, - new_size); - } + if (!reserve_exact) { + new_size = nm_utils_get_next_realloc_size(!strbuf->_priv_do_bzero_mem, new_size); + } - strbuf->_priv_str = nm_secret_mem_realloc (strbuf->_priv_str, - strbuf->_priv_do_bzero_mem, - strbuf->_priv_allocated, - new_size); - strbuf->_priv_allocated = new_size; + strbuf->_priv_str = nm_secret_mem_realloc(strbuf->_priv_str, + strbuf->_priv_do_bzero_mem, + strbuf->_priv_allocated, + new_size); + strbuf->_priv_allocated = new_size; } void -nm_str_buf_append_printf (NMStrBuf *strbuf, - const char *format, - ...) +nm_str_buf_append_printf(NMStrBuf *strbuf, const char *format, ...) { - va_list args; - gsize available; - int l; + va_list args; + gsize available; + int l; - _nm_str_buf_assert (strbuf); + _nm_str_buf_assert(strbuf); - available = strbuf->_priv_allocated - strbuf->_priv_len; + available = strbuf->_priv_allocated - strbuf->_priv_len; - nm_assert (available < G_MAXULONG); + nm_assert(available < G_MAXULONG); - va_start (args, format); - l = g_vsnprintf (&strbuf->_priv_str[strbuf->_priv_len], - available, - format, - args); - va_end (args); + va_start(args, format); + l = g_vsnprintf(&strbuf->_priv_str[strbuf->_priv_len], available, format, args); + va_end(args); - nm_assert (l >= 0); - nm_assert (l < G_MAXINT); + nm_assert(l >= 0); + nm_assert(l < G_MAXINT); - if ((gsize) l >= available) { - gsize l2; + if ((gsize) l >= available) { + gsize l2; - if (l == 0) - return; + if (l == 0) + return; - l2 = ((gsize) l) + 1u; + l2 = ((gsize) l) + 1u; - nm_str_buf_maybe_expand (strbuf, l2, FALSE); + nm_str_buf_maybe_expand(strbuf, l2, FALSE); - va_start (args, format); - l = g_vsnprintf (&strbuf->_priv_str[strbuf->_priv_len], - l2, - format, - args); - va_end (args); + va_start(args, format); + l = g_vsnprintf(&strbuf->_priv_str[strbuf->_priv_len], l2, format, args); + va_end(args); - nm_assert (l >= 0); - nm_assert ((gsize) l == l2 - 1u); - } + nm_assert(l >= 0); + nm_assert((gsize) l == l2 - 1u); + } - strbuf->_priv_len += (gsize) l; + strbuf->_priv_len += (gsize) l; } /*****************************************************************************/ @@ -5029,109 +5326,285 @@ nm_str_buf_append_printf (NMStrBuf *strbuf, * the first pointer. */ void -nm_indirect_g_free (gpointer arg) +nm_indirect_g_free(gpointer arg) { - gpointer *p = arg; + gpointer *p = arg; - nm_clear_g_free (p); + nm_clear_g_free(p); } /*****************************************************************************/ static char * -attribute_escape (const char *src, char c1, char c2) +attribute_escape(const char *src, char c1, char c2) { - char *ret, *dest; + char *ret, *dest; - dest = ret = g_malloc (strlen (src) * 2 + 1); + dest = ret = g_malloc(strlen(src) * 2 + 1); - while (*src) { - if (*src == c1 || *src == c2 || *src == '\\') - *dest++ = '\\'; - *dest++ = *src++; - } - *dest++ = '\0'; + while (*src) { + if (*src == c1 || *src == c2 || *src == '\\') + *dest++ = '\\'; + *dest++ = *src++; + } + *dest++ = '\0'; - return ret; + return ret; } void -_nm_utils_format_variant_attributes_full (GString *str, - const NMUtilsNamedValue *values, - guint num_values, - char attr_separator, - char key_value_separator) -{ - const char *name, *value; - GVariant *variant; - char *escaped; - char buf[64]; - char sep = 0; - guint i; - - for (i = 0; i < num_values; i++) { - name = values[i].name; - variant = (GVariant *) values[i].value_ptr; - value = NULL; - - if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) - value = nm_sprintf_buf (buf, "%u", g_variant_get_uint32 (variant)); - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32)) - value = nm_sprintf_buf (buf, "%d", (int) g_variant_get_int32 (variant)); - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64)) - value = nm_sprintf_buf (buf, "%"G_GUINT64_FORMAT, g_variant_get_uint64 (variant)); - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) - value = nm_sprintf_buf (buf, "%hhu", g_variant_get_byte (variant)); - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) - value = g_variant_get_boolean (variant) ? "true" : "false"; - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) - value = g_variant_get_string (variant, NULL); - else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING)) { - /* FIXME: there is no guarantee that the byte array - * is valid UTF-8.*/ - value = g_variant_get_bytestring (variant); - } else - continue; - - if (sep) - g_string_append_c (str, sep); - - escaped = attribute_escape (name, attr_separator, key_value_separator); - g_string_append (str, escaped); - g_free (escaped); - - g_string_append_c (str, key_value_separator); - - escaped = attribute_escape (value, attr_separator, key_value_separator); - g_string_append (str, escaped); - g_free (escaped); - - sep = attr_separator; - } +_nm_utils_format_variant_attributes_full(GString * str, + const NMUtilsNamedValue * values, + guint num_values, + const NMVariantAttributeSpec *const *spec, + char attr_separator, + char key_value_separator) +{ + const NMVariantAttributeSpec *const *s; + const char * name, *value; + GVariant * variant; + char * escaped; + char buf[64]; + char sep = 0; + guint i; + + for (i = 0; i < num_values; i++) { + name = values[i].name; + variant = values[i].value_ptr; + value = NULL; + s = NULL; + + if (spec) { + for (s = spec; *s; s++) { + if (nm_streq0((*s)->name, name)) + break; + } + + if (!*s) + continue; + } + + if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32)) + value = nm_sprintf_buf(buf, "%u", g_variant_get_uint32(variant)); + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT32)) + value = nm_sprintf_buf(buf, "%d", (int) g_variant_get_int32(variant)); + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT64)) + value = nm_sprintf_buf(buf, "%" G_GUINT64_FORMAT, g_variant_get_uint64(variant)); + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BYTE)) + value = nm_sprintf_buf(buf, "%hhu", g_variant_get_byte(variant)); + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) + value = g_variant_get_boolean(variant) ? "true" : "false"; + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) + value = g_variant_get_string(variant, NULL); + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BYTESTRING)) { + /* FIXME: there is no guarantee that the byte array + * is valid UTF-8.*/ + value = g_variant_get_bytestring(variant); + } else + continue; + + if (sep) + g_string_append_c(str, sep); + + escaped = attribute_escape(name, attr_separator, key_value_separator); + g_string_append(str, escaped); + g_free(escaped); + + if (!s || !*s || !(*s)->no_value) { + g_string_append_c(str, key_value_separator); + + escaped = attribute_escape(value, attr_separator, key_value_separator); + g_string_append(str, escaped); + g_free(escaped); + } + + sep = attr_separator; + } } char * -_nm_utils_format_variant_attributes (GHashTable *attributes, - char attr_separator, - char key_value_separator) +_nm_utils_format_variant_attributes(GHashTable * attributes, + const NMVariantAttributeSpec *const *spec, + char attr_separator, + char key_value_separator) { - GString *str = NULL; - gs_free NMUtilsNamedValue *values = NULL; - guint len; + gs_free NMUtilsNamedValue *values_free = NULL; + NMUtilsNamedValue values_prepared[20]; + const NMUtilsNamedValue * values; + GString * str = NULL; + guint len; + + g_return_val_if_fail(attr_separator, NULL); + g_return_val_if_fail(key_value_separator, NULL); - g_return_val_if_fail (attr_separator, NULL); - g_return_val_if_fail (key_value_separator, NULL); + if (!attributes) + return NULL; - if (!attributes || !g_hash_table_size (attributes)) - return NULL; + values = nm_utils_named_values_from_strdict(attributes, &len, values_prepared, &values_free); + if (len == 0) + return NULL; - values = nm_utils_named_values_from_str_dict (attributes, &len); + str = g_string_new(""); + _nm_utils_format_variant_attributes_full(str, + values, + len, + spec, + attr_separator, + key_value_separator); + return g_string_free(str, FALSE); +} + +/*****************************************************************************/ + +gboolean +nm_utils_is_localhost(const char *name) +{ + static const char *const NAMES[] = { + "localhost", + "localhost4", + "localhost6", + "localhost.localdomain", + "localhost4.localdomain4", + "localhost6.localdomain6", + }; + gsize name_len; + int i; - str = g_string_new (""); - _nm_utils_format_variant_attributes_full (str, - values, - len, - attr_separator, - key_value_separator); - return g_string_free (str, FALSE); + if (!name) + return FALSE; + + /* This tries to identify local host and domain names + * described in RFC6761 plus the redhatism of localdomain. + * + * Similar to systemd's is_localhost(). */ + + name_len = strlen(name); + + if (name_len == 0) + return FALSE; + + if (name[name_len - 1] == '.') { + /* one trailing dot is fine. Hide it. */ + name_len--; + } + + for (i = 0; i < (int) G_N_ELEMENTS(NAMES); i++) { + const char *n = NAMES[i]; + gsize l = strlen(n); + gsize s; + + if (name_len < l) + continue; + + s = name_len - l; + + if (g_ascii_strncasecmp(&name[s], n, l) != 0) + continue; + + /* we accept the name if it is equal to one of the well-known names, + * or if it is some prefix, a '.' and the well-known name. */ + if (s == 0) + return TRUE; + if (name[s - 1] == '.') + return TRUE; + } + + return FALSE; +} + +gboolean +nm_utils_is_specific_hostname(const char *name) +{ + if (nm_str_is_empty(name)) + return FALSE; + + if (nm_streq(name, "(none)")) { + /* This is not a special hostname. Probably an artefact by somebody wrongly + * printing NULL. */ + return FALSE; + } + + if (nm_utils_is_localhost(name)) + return FALSE; + + /* FIXME: properly validate the hostname, like systemd's hostname_is_valid() */ + + return TRUE; +} + +/*****************************************************************************/ + +/* taken from systemd's uid_to_name(). */ +char * +nm_utils_uid_to_name(uid_t uid) +{ + gs_free char *buf_heap = NULL; + char buf_stack[4096]; + gsize bufsize; + char * buf; + + bufsize = sizeof(buf_stack); + buf = buf_stack; + + 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 (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; + } +} + +/* taken from systemd's nss_user_record_by_name() */ +gboolean +nm_utils_name_to_uid(const char *name, uid_t *out_uid) +{ + gs_free char *buf_heap = NULL; + char buf_stack[4096]; + gsize bufsize; + char * buf; + + if (!name) + return nm_assert_unreachable_val(FALSE); + + bufsize = sizeof(buf_stack); + buf = buf_stack; + + for (;;) { + struct passwd *result; + struct passwd pwd; + int r; + + r = getpwnam_r(name, &pwd, buf, bufsize, &result); + if (r == 0) { + if (!result) + return FALSE; + NM_SET_OUT(out_uid, pwd.pw_uid); + return TRUE; + } + + if (r != ERANGE) + return FALSE; + + if (bufsize > G_MAXSIZE / 2u) + return FALSE; + + bufsize *= 2u; + g_free(buf_heap); + buf_heap = g_malloc(bufsize); + buf = buf_heap; + } } |