diff options
| author | Sebastien Bacher <seb128@ubuntu.com> | 2020-07-02 11:35:14 +0200 |
|---|---|---|
| committer | Sebastien Bacher <seb128@ubuntu.com> | 2020-07-02 11:52:25 +0200 |
| commit | c6c4e39b012d544c1f1ed816686c8ae1599da793 (patch) | |
| tree | 094d5e07c1d135d0ee67d99e051fd6a9a367a6a4 /shared/nm-glib-aux | |
| parent | 7368e45ff5b3b8b328d55408bcd403e4d6c564d0 (diff) | |
| parent | acd7b014c72673f5b0c0b91309e27028886da167 (diff) | |
Merge remote-tracking branch 'salsa/debian/master' into ubuntu/master
Diffstat (limited to 'shared/nm-glib-aux')
| -rw-r--r-- | shared/nm-glib-aux/nm-glib.h | 12 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-macros-internal.h | 46 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.c | 336 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-shared-utils.h | 202 | ||||
| -rw-r--r-- | shared/nm-glib-aux/nm-str-buf.h | 61 |
5 files changed, 616 insertions, 41 deletions
diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h index 7a5b8edd..2df51795 100644 --- a/shared/nm-glib-aux/nm-glib.h +++ b/shared/nm-glib-aux/nm-glib.h @@ -382,9 +382,8 @@ _nm_g_hash_table_get_keys_as_array (GHashTable *hash_table, /*****************************************************************************/ -#if !GLIB_CHECK_VERSION(2, 44, 0) static inline gpointer -g_steal_pointer (gpointer pp) +_nm_g_steal_pointer (gpointer pp) { gpointer *ptr = (gpointer *) pp; gpointer ref; @@ -394,13 +393,20 @@ g_steal_pointer (gpointer pp) return ref; } + +#if !GLIB_CHECK_VERSION(2, 44, 0) +static inline gpointer +g_steal_pointer (gpointer pp) +{ + return _nm_g_steal_pointer (pp); +} #endif #ifdef g_steal_pointer #undef g_steal_pointer #endif #define g_steal_pointer(pp) \ - ((typeof (*(pp))) g_steal_pointer (pp)) + ((typeof (*(pp))) _nm_g_steal_pointer (pp)) /*****************************************************************************/ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index d5a8513e..f56ed856 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -630,6 +630,14 @@ NM_G_ERROR_MSG (GError *error) NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ }) +#define NM_ENSURE_NOT_NULL(ptr) \ + ({ \ + typeof (ptr) _ptr = (ptr); \ + \ + nm_assert (_ptr != NULL); \ + _ptr; \ + }) + #if _NM_CC_SUPPORT_GENERIC /* returns @value, if the type of @value matches @type. * This requires support for C11 _Generic(). If no support is @@ -929,23 +937,38 @@ nm_streq0 (const char *s1, const char *s2) #define NM_STR_HAS_PREFIX(str, prefix) \ ({ \ - const char *const _str = (str); \ + const char *const _str_has_prefix = (str); \ \ - _str && (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0); \ + nm_assert (strlen (prefix) == NM_STRLEN (prefix)); \ + \ + _str_has_prefix \ + && (strncmp (_str_has_prefix, ""prefix"", NM_STRLEN (prefix)) == 0); \ }) #define NM_STR_HAS_SUFFIX(str, suffix) \ ({ \ - const char *_str; \ + const char *const _str_has_suffix = (str); \ gsize _l; \ \ - ( (_str = (str)) \ - && ((_l = strlen (_str)) >= NM_STRLEN (suffix)) \ - && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ + nm_assert (strlen (suffix) == NM_STRLEN (suffix)); \ + \ + ( _str_has_suffix \ + && ((_l = strlen (_str_has_suffix)) >= NM_STRLEN (suffix)) \ + && (memcmp (&_str_has_suffix[_l - NM_STRLEN (suffix)], \ ""suffix"", \ NM_STRLEN (suffix)) == 0)); \ }) +/* whether @str starts with the string literal @prefix and is followed by + * some other text. It is like NM_STR_HAS_PREFIX() && !nm_streq() together. */ +#define NM_STR_HAS_PREFIX_WITH_MORE(str, prefix) \ + ({ \ + const char *const _str_has_prefix_with_more = (str); \ + \ + NM_STR_HAS_PREFIX (_str_has_prefix_with_more, ""prefix"") \ + && _str_has_prefix_with_more[NM_STRLEN (prefix)] != '\0'; \ + }) + /*****************************************************************************/ static inline GString * @@ -966,16 +989,23 @@ nm_gstring_add_space_delimiter (GString *str) return str; } +static inline gboolean +nm_str_is_empty (const char *str) +{ + /* %NULL is also accepted, and also "empty". */ + return !str || !str[0]; +} + static inline const char * nm_str_not_empty (const char *str) { - return str && str[0] ? str : NULL; + return !nm_str_is_empty (str) ? str : NULL; } static inline char * nm_strdup_not_empty (const char *str) { - return str && str[0] ? g_strdup (str) : NULL; + return !nm_str_is_empty (str) ? g_strdup (str) : NULL; } static inline char * diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index 5dd099a7..e29520b2 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -13,10 +13,14 @@ #include <sys/syscall.h> #include <glib-unix.h> #include <net/if.h> +#include <net/ethernet.h> #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 *)); + /*****************************************************************************/ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; @@ -85,6 +89,11 @@ nm_ip_addr_set_from_untrusted (int addr_family, /*****************************************************************************/ +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) { @@ -1060,6 +1069,47 @@ nm_utils_parse_inaddr_prefix (int addr_family, 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; +} + /*****************************************************************************/ gboolean @@ -2012,6 +2062,100 @@ nm_utils_escaped_tokens_options_split (char *str, /*****************************************************************************/ /** + * nm_utils_strsplit_quoted: + * @str: the string to split (e.g. from /proc/cmdline). + * + * This basically does that systemd's extract_first_word() does + * with the flags "EXTRACT_UNQUOTE | EXTRACT_RELAX". This is what + * systemd uses to parse /proc/cmdline, and we do too. + * + * Splits the string. We have nm_utils_strsplit_set() which + * supports a variety of flags. However, extending that already + * complex code to also support quotation and escaping is hard. + * Instead, add a naive implementation. + * + * 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_strv_find_first: * @list: the strv list to search * @len: the length of the list, or a negative value if @list is %NULL terminated. @@ -2456,11 +2600,12 @@ _str_buf_append_c_escape_octal (NMStrBuf *strbuf, } gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) +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; - gsize len; const char *s; + gsize len; g_return_val_if_fail (to_free, NULL); g_return_val_if_fail (out_len, NULL); @@ -2471,16 +2616,29 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr 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, FALSE); + nm_str_buf_init (&strbuf, len + 1u, FALSE); nm_str_buf_append_len (&strbuf, str, s - str); str = s; @@ -2494,7 +2652,7 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr ch = (++str)[0]; if (ch == '\0') { - // error. Trailing '\\' + /* error. Trailing '\\' */ break; } @@ -2533,7 +2691,14 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr s = strchr (str, '\\'); if (!s) { - nm_str_buf_append (&strbuf, str); + 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; } @@ -2541,6 +2706,11 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr 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)); } @@ -2670,16 +2840,33 @@ nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); } +char * +nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags) +{ + 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); +} + /*****************************************************************************/ const char * -nm_utils_str_utf8safe_unescape (const char *str, char **to_free) +nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { + const char *res; gsize len; g_return_val_if_fail (to_free, NULL); - return nm_utils_buf_utf8safe_unescape (str, &len, (gpointer *) to_free); + res = nm_utils_buf_utf8safe_unescape (str, flags, &len, (gpointer *) to_free); + + nm_assert ( (!res && len == 0) + || (strlen (res) <= len)); + + return res; } /** @@ -2737,11 +2924,11 @@ nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) } char * -nm_utils_str_utf8safe_unescape_cp (const char *str) +nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags) { char *s; - str = nm_utils_str_utf8safe_unescape (str, &s); + str = nm_utils_str_utf8safe_unescape (str, flags, &s); return s ?: g_strdup (str); } @@ -4789,6 +4976,8 @@ nm_str_buf_append_printf (NMStrBuf *strbuf, available = strbuf->_priv_allocated - strbuf->_priv_len; + nm_assert (available < G_MAXULONG); + va_start (args, format); l = g_vsnprintf (&strbuf->_priv_str[strbuf->_priv_len], available, @@ -4799,8 +4988,13 @@ nm_str_buf_append_printf (NMStrBuf *strbuf, nm_assert (l >= 0); nm_assert (l < G_MAXINT); - if ((gsize) l > available) { - gsize l2 = ((gsize) l) + 1u; + if ((gsize) l >= available) { + gsize l2; + + if (l == 0) + return; + + l2 = ((gsize) l) + 1u; nm_str_buf_maybe_expand (strbuf, l2, FALSE); @@ -4817,3 +5011,123 @@ nm_str_buf_append_printf (NMStrBuf *strbuf, strbuf->_priv_len += (gsize) l; } + +/*****************************************************************************/ + +/** + * nm_indirect_g_free: + * @arg: a pointer to a pointer that is to be freed. + * + * This does the same as nm_clear_g_free(arg) (g_clear_pointer (arg, g_free)). + * This is for example useful when you have a GArray with pointers and a + * clear function to free them. g_array_set_clear_func()'s destroy notify + * function gets a pointer to the array location, so we have to follow + * the first pointer. + */ +void +nm_indirect_g_free (gpointer arg) +{ + gpointer *p = arg; + + nm_clear_g_free (p); +} + +/*****************************************************************************/ + +static char * +attribute_escape (const char *src, char c1, char c2) +{ + char *ret, *dest; + + dest = ret = g_malloc (strlen (src) * 2 + 1); + + while (*src) { + if (*src == c1 || *src == c2 || *src == '\\') + *dest++ = '\\'; + *dest++ = *src++; + } + *dest++ = '\0'; + + 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; + } +} + +char * +_nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator) +{ + GString *str = NULL; + gs_free NMUtilsNamedValue *values = NULL; + guint len; + + g_return_val_if_fail (attr_separator, NULL); + g_return_val_if_fail (key_value_separator, NULL); + + if (!attributes || !g_hash_table_size (attributes)) + 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, + attr_separator, + key_value_separator); + return g_string_free (str, FALSE); +} diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index d5990c2d..b17c8d1a 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -178,6 +178,24 @@ nm_ip4_addr_is_localhost (in_addr_t addr4) /*****************************************************************************/ +struct ether_addr; + +static inline int +nm_utils_ether_addr_cmp (const struct ether_addr *a1, const struct ether_addr *a2) +{ + nm_assert (a1); + nm_assert (a2); + return memcmp (a1, a2, 6 /*ETH_ALEN*/); +} + +static inline gboolean +nm_utils_ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2) +{ + return nm_utils_ether_addr_cmp (a1, a2) == 0; +} + +/*****************************************************************************/ + #define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN static inline const char * @@ -662,6 +680,10 @@ nm_utils_escaped_tokens_escape_gstr (const char *str, /*****************************************************************************/ +char **nm_utils_strsplit_quoted (const char *str); + +/*****************************************************************************/ + static inline const char ** nm_utils_escaped_tokens_options_split_list (const char *str) { @@ -785,6 +807,11 @@ gboolean nm_utils_parse_inaddr_prefix (int addr_family, char **out_addr, int *out_prefix); +gboolean nm_utils_parse_next_line (const char **inout_ptr, + gsize *inout_len, + const char **out_line, + gsize *out_line_len); + gint64 nm_g_ascii_strtoll (const char *nptr, char **endptr, guint base); @@ -926,6 +953,21 @@ _nm_g_slice_free_fcn_define (32) /*****************************************************************************/ +/* Like g_error_matches() however: + * - as macro it is always inlined. + * - the @domain is usually a error quark getter function that cannot + * be inlined. This macro calls the getter only if there is an error (lazy). + * - accept a list of allowed codes, instead of only one. + */ +#define nm_g_error_matches(error, err_domain, ...) \ + ({ \ + const GError *const _error = (error); \ + \ + _error \ + && _error->domain == (err_domain) \ + && NM_IN_SET (_error->code, __VA_ARGS__); \ + }) + static inline void nm_g_set_error_take (GError **error, GError *error_take) { @@ -1143,6 +1185,25 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +#define _NM_G_PARAM_SPEC_CAST(param_spec, _value_type, _c_type) \ + ({ \ + const GParamSpec *const _param_spec = (param_spec); \ + \ + nm_assert ( !_param_spec \ + || _param_spec->value_type == (_value_type)); \ + ((const _c_type *) _param_spec); \ + }) + +#define NM_G_PARAM_SPEC_CAST_BOOLEAN(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_BOOLEAN, GParamSpecBoolean) +#define NM_G_PARAM_SPEC_CAST_UINT(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT, GParamSpecUInt) +#define NM_G_PARAM_SPEC_CAST_UINT64(param_spec) _NM_G_PARAM_SPEC_CAST (param_spec, G_TYPE_UINT64, GParamSpecUInt64) + +#define NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(param_spec) (NM_G_PARAM_SPEC_CAST_BOOLEAN (NM_ENSURE_NOT_NULL (param_spec))->default_value) +#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT(param_spec) (NM_G_PARAM_SPEC_CAST_UINT (NM_ENSURE_NOT_NULL (param_spec))->default_value) +#define NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(param_spec) (NM_G_PARAM_SPEC_CAST_UINT64 (NM_ENSURE_NOT_NULL (param_spec))->default_value) + +/*****************************************************************************/ + GType nm_g_type_find_implementing_class_for_property (GType gtype, const char *pname); @@ -1150,20 +1211,39 @@ GType nm_g_type_find_implementing_class_for_property (GType gtype, typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, + + /* This flag only has an effect during escaping. */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, + + /* This flag only has an effect during escaping. */ NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII = 0x0002, + + /* This flag only has an effect during escaping to ensure we + * don't leak secrets in memory. Note that during unescape we + * know the maximum result size from the beginning, and no + * reallocation happens. Thus, unescape always avoids leaking + * secrets already. */ NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET = 0x0004, + + /* This flag only has an effect during unescaping. It means + * that non-escaped whitespaces (g_ascii_isspace()) will be + * stripped from the front and end of the string. Note that + * this flag is only useful for gracefully accepting user input + * with spaces. With this flag, escape and unescape may no longer + * yield the original input. */ + NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES = 0x0008, } NMUtilsStrUtf8SafeFlags; const char *nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free); +char *nm_utils_buf_utf8safe_escape_cp (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags); const char *nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free); -gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free); +gconstpointer nm_utils_buf_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, gsize *out_len, gpointer *to_free); const char *nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); -const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free); +const char *nm_utils_str_utf8safe_unescape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free); char *nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); -char *nm_utils_str_utf8safe_unescape_cp (const char *str); +char *nm_utils_str_utf8safe_unescape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags); char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags); @@ -1211,6 +1291,30 @@ nm_g_variant_is_of_type (GVariant *value, } static inline void +nm_g_variant_builder_add_sv (GVariantBuilder *builder, const char *key, GVariant *val) +{ + g_variant_builder_add (builder, "{sv}", key, val); +} + +static inline void +nm_g_variant_builder_add_sv_bytearray (GVariantBuilder *builder, const char *key, const guint8 *arr, gsize len) +{ + g_variant_builder_add (builder, "{sv}", key, g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, arr, len, 1)); +} + +static inline void +nm_g_variant_builder_add_sv_uint32 (GVariantBuilder *builder, const char *key, guint32 val) +{ + nm_g_variant_builder_add_sv (builder, key, g_variant_new_uint32 (val)); +} + +static inline void +nm_g_variant_builder_add_sv_str (GVariantBuilder *builder, const char *key, const char *str) +{ + nm_g_variant_builder_add_sv (builder, key, g_variant_new_string (str)); +} + +static inline void nm_g_source_destroy_and_unref (GSource *source) { g_source_destroy (source); @@ -1355,6 +1459,8 @@ typedef struct { }; } NMUtilsNamedValue; +#define NM_UTILS_NAMED_VALUE_INIT(n, v) { .name = (n), .value_ptr = (v) } + NMUtilsNamedValue *nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash, guint *out_len, GCompareDataFunc compare_func, @@ -1436,6 +1542,22 @@ char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator); /*****************************************************************************/ static inline guint +nm_g_array_len (const GArray *arr) +{ + return arr ? arr->len : 0u; +} + +/*****************************************************************************/ + +static inline guint +nm_g_ptr_array_len (const GPtrArray *arr) +{ + return arr ? arr->len : 0u; +} + +/*****************************************************************************/ + +static inline guint nm_g_hash_table_size (GHashTable *hash) { return hash ? g_hash_table_size (hash) : 0u; @@ -1848,6 +1970,8 @@ nm_utils_strdup_reset (char **dst, const char *src) return TRUE; } +void nm_indirect_g_free (gpointer arg); + /*****************************************************************************/ /* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when @@ -1887,4 +2011,76 @@ gboolean nm_utils_ifname_valid (const char* name, NMUtilsIfaceType type, GError **error); +/*****************************************************************************/ + +static inline GArray * +nm_strvarray_ensure (GArray **p) +{ + if (!*p) { + *p = g_array_new (TRUE, FALSE, sizeof (char *)); + g_array_set_clear_func (*p, nm_indirect_g_free); + } + return *p; +} + +static inline void +nm_strvarray_add (GArray *array, const char *str) +{ + char *s; + + s = g_strdup (str); + g_array_append_val (array, s); +} + +static inline const char *const* +nm_strvarray_get_strv_non_empty (GArray *arr, guint *length) +{ + if (!arr || arr->len == 0) { + NM_SET_OUT (length, 0); + return NULL; + } + + NM_SET_OUT (length, arr->len); + return &g_array_index (arr, const char *, 0); +} + +static inline const char *const* +nm_strvarray_get_strv (GArray **arr, guint *length) +{ + if (!*arr) { + NM_SET_OUT (length, 0); + return (const char *const*) arr; + } + + NM_SET_OUT (length, (*arr)->len); + return &g_array_index (*arr, const char *, 0); +} + +static inline void +nm_strvarray_set_strv (GArray **array, const char *const*strv) +{ + gs_unref_array GArray *array_old = NULL; + + array_old = g_steal_pointer (array); + + if (!strv || !strv[0]) + return; + + nm_strvarray_ensure (array); + for (; strv[0]; strv++) + nm_strvarray_add (*array, strv[0]); +} + +/*****************************************************************************/ + +void _nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator); + +char *_nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-str-buf.h b/shared/nm-glib-aux/nm-str-buf.h index 8c73bfaf..da082abe 100644 --- a/shared/nm-glib-aux/nm-str-buf.h +++ b/shared/nm-glib-aux/nm-str-buf.h @@ -35,24 +35,30 @@ static inline void _nm_str_buf_assert (NMStrBuf *strbuf) { nm_assert (strbuf); - nm_assert (strbuf->_priv_str); - nm_assert (strbuf->_priv_allocated > 0); + nm_assert ((!!strbuf->_priv_str) == (strbuf->_priv_allocated > 0)); nm_assert (strbuf->_priv_len <= strbuf->_priv_allocated); } +static inline NMStrBuf +NM_STR_BUF_INIT (gsize allocated, gboolean do_bzero_mem) +{ + NMStrBuf strbuf = { + ._priv_str = allocated ? g_malloc (allocated) : NULL, + ._priv_allocated = allocated, + ._priv_len = 0, + ._priv_do_bzero_mem = do_bzero_mem, + }; + + return strbuf; +} + static inline void nm_str_buf_init (NMStrBuf *strbuf, gsize len, bool do_bzero_mem) { nm_assert (strbuf); - nm_assert (len > 0); - - strbuf->_priv_str = g_malloc (len); - strbuf->_priv_allocated = len; - strbuf->_priv_len = 0; - strbuf->_priv_do_bzero_mem = do_bzero_mem; - + *strbuf = NM_STR_BUF_INIT (len, do_bzero_mem); _nm_str_buf_assert (strbuf); } @@ -66,9 +72,6 @@ nm_str_buf_maybe_expand (NMStrBuf *strbuf, gboolean reserve_exact) { _nm_str_buf_assert (strbuf); - - /* currently we always require to reserve a non-zero number of bytes. */ - nm_assert (reserve > 0); nm_assert (strbuf->_priv_len < G_MAXSIZE - reserve); /* @reserve is the extra space that we require. */ @@ -164,6 +167,19 @@ nm_str_buf_erase (NMStrBuf *strbuf, /*****************************************************************************/ static inline void +nm_str_buf_append_c_repeated (NMStrBuf *strbuf, + char ch, + guint len) +{ + if (len > 0) { + nm_str_buf_maybe_expand (strbuf, len + 1, FALSE); + do { + strbuf->_priv_str[strbuf->_priv_len++] = ch; + } while (--len > 0); + } +} + +static inline void nm_str_buf_append_c (NMStrBuf *strbuf, char ch) { @@ -263,10 +279,16 @@ nm_str_buf_is_initalized (NMStrBuf *strbuf) * is of length "strbuf->len", which may be larger if the * returned string contains NUL characters (binary). The terminating * NUL character is always present after "strbuf->len" characters. + * If currently no buffer is allocated, this will return %NULL. */ -static inline const char * +static inline char * nm_str_buf_get_str (NMStrBuf *strbuf) { + _nm_str_buf_assert (strbuf); + + if (!strbuf->_priv_str) + return NULL; + nm_str_buf_maybe_expand (strbuf, 1, FALSE); strbuf->_priv_str[strbuf->_priv_len] = '\0'; return strbuf->_priv_str; @@ -288,16 +310,23 @@ nm_str_buf_get_str_unsafe (NMStrBuf *strbuf) * Returns: (transfer full): the string of the buffer * which must be freed by the caller. The @strbuf * is afterwards in undefined state, though it can be - * reused after nm_str_buf_init(). */ + * reused after nm_str_buf_init(). + * Note that if no string is allocated yet (after nm_str_buf_init() with + * length zero), this will return %NULL. */ static inline char * nm_str_buf_finalize (NMStrBuf *strbuf, gsize *out_len) { - nm_str_buf_maybe_expand (strbuf, 1, TRUE); - strbuf->_priv_str[strbuf->_priv_len] = '\0'; + _nm_str_buf_assert (strbuf); NM_SET_OUT (out_len, strbuf->_priv_len); + if (!strbuf->_priv_str) + return NULL; + + nm_str_buf_maybe_expand (strbuf, 1, TRUE); + strbuf->_priv_str[strbuf->_priv_len] = '\0'; + /* the buffer is in invalid state afterwards, however, we clear it * so far, that nm_auto_str_buf and nm_str_buf_destroy() is happy. */ return g_steal_pointer (&strbuf->_priv_str); |