From e126f3e804c35480c4f075777430419d6ece23da Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Sun, 23 Sep 2018 10:10:27 +0200 Subject: New upstream version 1.12.4 --- shared/nm-utils/nm-shared-utils.c | 676 +++----------------------------------- 1 file changed, 53 insertions(+), 623 deletions(-) (limited to 'shared/nm-utils/nm-shared-utils.c') diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index 022c0652..d0019c11 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -97,7 +97,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) { char *p = *buf; va_list args; - int retval; + gint retval; if (*len == 0) return; @@ -106,7 +106,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) retval = g_vsnprintf (p, *len, format, args); va_end (args); - if ((gsize) retval >= *len) { + if (retval >= *len) { *buf = &p[*len]; *len = 0; } else { @@ -115,131 +115,6 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) } } -/** - * nm_utils_strbuf_seek_end: - * @buf: the input/output buffer - * @len: the input/output lenght of the buffer. - * - * Commonly, one uses nm_utils_strbuf_append*(), to incrementally - * append strings to the buffer. However, sometimes we need to use - * existing API to write to the buffer. - * After doing so, we want to adjust the buffer counter. - * Essentially, - * - * g_snprintf (buf, len, ...); - * nm_utils_strbuf_seek_end (&buf, &len); - * - * is almost the same as - * - * nm_utils_strbuf_append (&buf, &len, ...); - * - * The only difference is the behavior when the string got truncated: - * nm_utils_strbuf_append() will recognize that and set the remaining - * length to zero. - * - * In general, the behavior is: - * - * - if *len is zero, do nothing - * - if the buffer contains a NUL byte within the first *len characters, - * the buffer is pointed to the NUL byte and len is adjusted. In this - * case, the remaining *len is always >= 1. - * In particular, that is also the case if the NUL byte is at the very last - * position ((*buf)[*len -1]). That happens, when the previous operation - * either fit the string exactly into the buffer or the string was truncated - * by g_snprintf(). The difference cannot be determined. - * - if the buffer contains no NUL bytes within the first *len characters, - * write NUL at the last position, set *len to zero, and point *buf past - * the NUL byte. This would happen with - * - * strncpy (buf, long_str, len); - * nm_utils_strbuf_seek_end (&buf, &len). - * - * where strncpy() does truncate the string and not NUL terminate it. - * nm_utils_strbuf_seek_end() would then NUL terminate it. - */ -void -nm_utils_strbuf_seek_end (char **buf, gsize *len) -{ - gsize l; - char *end; - - nm_assert (len); - nm_assert (buf && *buf); - - if (*len <= 1) { - if ( *len == 1 - && (*buf)[0]) - goto truncate; - return; - } - - end = memchr (*buf, 0, *len); - if (end) { - l = end - *buf; - nm_assert (l < *len); - - *buf = end; - *len -= l; - return; - } - -truncate: - /* hm, no NUL character within len bytes. - * Just NUL terminate the array and consume them - * all. */ - *buf += *len; - (*buf)[-1] = '\0'; - *len = 0; - return; -} - -/*****************************************************************************/ - -/** - * nm_utils_gbytes_equals: - * @bytes: (allow-none): a #GBytes array to compare. Note that - * %NULL is treated like an #GBytes array of length zero. - * @mem_data: the data pointer with @mem_len bytes - * @mem_len: the length of the data pointer - * - * Returns: %TRUE if @bytes contains the same data as @mem_data. As a - * special case, a %NULL @bytes is treated like an empty array. - */ -gboolean -nm_utils_gbytes_equal_mem (GBytes *bytes, - gconstpointer mem_data, - gsize mem_len) -{ - gconstpointer p; - gsize l; - - if (!bytes) { - /* as a special case, let %NULL GBytes compare idential - * to an empty array. */ - return (mem_len == 0); - } - - p = g_bytes_get_data (bytes, &l); - return l == mem_len - && ( mem_len == 0 /* allow @mem_data to be %NULL */ - || memcmp (p, mem_data, mem_len) == 0); -} - -GVariant * -nm_utils_gbytes_to_variant_ay (GBytes *bytes) -{ - const guint8 *p; - gsize l; - - if (!bytes) { - /* for convenience, accept NULL to return an empty variant */ - return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0); - } - - p = g_bytes_get_data (bytes, &l); - return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1); -} - /*****************************************************************************/ /** @@ -781,7 +656,6 @@ comp_l: * @str: the string to split. * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n", * like bash's $IFS. - * @allow_escaping: whether delimiters can be escaped by a backslash * * This is a replacement for g_strsplit_set() which avoids copying * each word once (the entire strv array), but instead copies it once @@ -790,10 +664,6 @@ comp_l: * Another difference from g_strsplit_set() is that this never returns * empty words. Multiple delimiters are combined and treated as one. * - * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are - * not treated as a separator. Such delimiters and their escape - * character are copied to the current word without unescaping them. - * * Returns: %NULL if @str is %NULL or contains only delimiters. * Otherwise, a %NULL terminated strv array containing non-empty * words, split at the delimiter characters (delimiter characters @@ -803,7 +673,7 @@ comp_l: * but free everything with g_free(). */ const char ** -nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping) +nm_utils_strsplit_set (const char *str, const char *delimiters) { const char **ptr, **ptr0; gsize alloc_size, plen, i; @@ -811,7 +681,6 @@ nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_e char *s0; char *s; guint8 delimiters_table[256]; - gboolean escaped = FALSE; if (!str) return NULL; @@ -823,23 +692,13 @@ nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_e for (i = 0; delimiters[i]; i++) delimiters_table[(guint8) delimiters[i]] = 1; -#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \ - ((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc)) - -#define next_char(p, esc) \ - G_STMT_START { \ - if (esc) \ - esc = FALSE; \ - else \ - esc = p[0] == '\\'; \ - p++; \ - } G_STMT_END +#define _is_delimiter(ch, delimiters_table) \ + ((delimiters_table)[(guint8) (ch)] != 0) /* skip initial delimiters, and return of the remaining string is * empty. */ - while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped)) - next_char (str, escaped); - + while (_is_delimiter (str[0], delimiters_table)) + str++; if (!str[0]) return NULL; @@ -871,20 +730,20 @@ nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_e ptr[plen++] = s; - nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)); + nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table)); while (TRUE) { - next_char (s, escaped); - if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) + s++; + if (_is_delimiter (s[0], delimiters_table)) break; if (s[0] == '\0') goto done; } s[0] = '\0'; - next_char (s, escaped); - while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) - next_char (s, escaped); + s++; + while (_is_delimiter (s[0], delimiters_table)) + s++; if (s[0] == '\0') break; } @@ -981,9 +840,9 @@ _nm_utils_strv_cleanup (char **strv, /*****************************************************************************/ -int +gint _nm_utils_ascii_str_to_bool (const char *str, - int default_value) + gint default_value) { gsize len; char *s = NULL; @@ -1065,7 +924,7 @@ nm_utils_error_is_cancelled (GError *error, */ gboolean nm_g_object_set_property (GObject *object, - const char *property_name, + const gchar *property_name, const GValue *value, GError **error) { @@ -1140,7 +999,7 @@ nm_g_object_set_property (GObject *object, gboolean nm_g_object_set_property_boolean (GObject *object, - const char *property_name, + const gchar *property_name, gboolean value, GError **error) { @@ -1153,7 +1012,7 @@ nm_g_object_set_property_boolean (GObject *object, gboolean nm_g_object_set_property_uint (GObject *object, - const char *property_name, + const gchar *property_name, guint value, GError **error) { @@ -1185,112 +1044,20 @@ _str_append_escape (GString *s, char ch) g_string_append_c (s, '0' + ( ((guchar) ch) & 07)); } -gconstpointer -nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) -{ - GString *gstr; - gsize len; - const char *s; - - g_return_val_if_fail (to_free, NULL); - g_return_val_if_fail (out_len, NULL); - - if (!str) { - *out_len = 0; - *to_free = NULL; - return NULL; - } - - len = strlen (str); - - s = memchr (str, '\\', len); - if (!s) { - *out_len = len; - *to_free = NULL; - return str; - } - - gstr = g_string_new_len (NULL, len); - - g_string_append_len (gstr, str, s - str); - str = s; - - for (;;) { - char ch; - guint v; - - nm_assert (str[0] == '\\'); - - ch = (++str)[0]; - - if (ch == '\0') { - // error. Trailing '\\' - break; - } - - if (ch >= '0' && ch <= '9') { - v = ch - '0'; - ch = (++str)[0]; - if (ch >= '0' && ch <= '7') { - v = v * 8 + (ch - '0'); - ch = (++str)[0]; - if (ch >= '0' && ch <= '7') { - v = v * 8 + (ch - '0'); - ch = (++str)[0]; - } - } - ch = v; - } else { - switch (ch) { - case 'b': ch = '\b'; break; - case 'f': ch = '\f'; break; - case 'n': ch = '\n'; break; - case 'r': ch = '\r'; break; - case 't': ch = '\t'; break; - case 'v': ch = '\v'; break; - default: - /* Here we handle "\\\\", but all other unexpected escape sequences are really a bug. - * Take them literally, after removing the escape character */ - break; - } - str++; - } - - g_string_append_c (gstr, ch); - - s = strchr (str, '\\'); - if (!s) { - g_string_append (gstr, str); - break; - } - - g_string_append_len (gstr, str, s - str); - str = s; - } - - *out_len = gstr->len; - *to_free = gstr->str; - return g_string_free (gstr, FALSE); -} - /** - * nm_utils_buf_utf8safe_escape: - * @buf: byte array, possibly in utf-8 encoding, may have NUL characters. - * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated - * string. + * nm_utils_str_utf8safe_escape: + * @str: NUL terminated input string, possibly in utf-8 encoding * @flags: #NMUtilsStrUtf8SafeFlags flags * @to_free: (out): return the pointer location of the string * if a copying was necessary. * - * Based on the assumption, that @buf contains UTF-8 encoded bytes, - * this will return valid UTF-8 sequence, and invalid sequences - * will be escaped with backslash (C escaping, like g_strescape()). - * This is sanitize non UTF-8 characters. The result is valid + * Returns the possible non-UTF-8 NUL terminated string @str + * and uses backslash escaping (C escaping, like g_strescape()) + * to sanitize non UTF-8 characters. The result is valid * UTF-8. * - * The operation can be reverted with nm_utils_buf_utf8safe_unescape(). - * Note that if, and only if @buf contains no NUL bytes, the operation - * can also be reverted with g_strcompress(). + * The operation can be reverted with g_strcompress() or + * nm_utils_str_utf8safe_unescape(). * * Depending on @flags, valid UTF-8 characters are not escaped at all * (except the escape character '\\'). This is the difference to g_strescape(), @@ -1299,105 +1066,61 @@ nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_fr * as UTF-8 -- with exception of the backslash escape character, * invalid UTF-8 sequences, and other (depending on @flags). * - * Returns: the escaped input buffer, as valid UTF-8. If no escaping - * is necessary, it returns the input @buf. Otherwise, an allocated + * Returns: the escaped input string, as valid UTF-8. If no escaping + * is necessary, it returns the input @str. Otherwise, an allocated * string @to_free is returned which must be freed by the caller * with g_free. The escaping can be reverted by g_strcompress(). **/ const char * -nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free) +nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) { - const char *const str = buf; const char *p = NULL; - const char *s; - gboolean nul_terminated = FALSE; - GString *gstr; + GString *s; g_return_val_if_fail (to_free, NULL); *to_free = NULL; + if (!str || !str[0]) + return str; - 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; - } + if ( g_utf8_validate (str, -1, &p) + && !NM_STRCHAR_ANY (str, ch, + ( ch == '\\' \ + || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ + && ch < ' ') \ + || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ + && ((guchar) ch) >= 127)))) + return str; - gstr = g_string_sized_new (buflen + 5); + s = g_string_sized_new ((p - str) + strlen (p) + 5); - s = str; do { - buflen -= p - s; - nm_assert (buflen >= 0); - - for (; s < p; s++) { - char ch = s[0]; + for (; str < p; str++) { + char ch = str[0]; if (ch == '\\') - g_string_append (gstr, "\\\\"); + g_string_append (s, "\\\\"); else if ( ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ && ch < ' ') \ || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ && ((guchar) ch) >= 127)) - _str_append_escape (gstr, ch); + _str_append_escape (s, ch); else - g_string_append_c (gstr, ch); + g_string_append_c (s, ch); } - if (buflen <= 0) - break; - - _str_append_escape (gstr, p[0]); - - buflen--; - if (buflen == 0) + if (p[0] == '\0') break; + _str_append_escape (s, p[0]); - s = &p[1]; - g_utf8_validate (s, buflen, &p); + str = &p[1]; + g_utf8_validate (str, -1, &p); } while (TRUE); - *to_free = g_string_free (gstr, FALSE); + *to_free = g_string_free (s, FALSE); return *to_free; } -const char * -nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - gconstpointer p; - gsize l; - - if (bytes) - p = g_bytes_get_data (bytes, &l); - else { - p = NULL; - l = 0; - } - - return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); -} - -/*****************************************************************************/ - const char * nm_utils_str_utf8safe_unescape (const char *str, char **to_free) { @@ -1410,39 +1133,6 @@ nm_utils_str_utf8safe_unescape (const char *str, char **to_free) return (*to_free = g_strcompress (str)); } -/** - * nm_utils_str_utf8safe_escape: - * @str: NUL terminated input string, possibly in utf-8 encoding - * @flags: #NMUtilsStrUtf8SafeFlags flags - * @to_free: (out): return the pointer location of the string - * if a copying was necessary. - * - * Returns the possible non-UTF-8 NUL terminated string @str - * and uses backslash escaping (C escaping, like g_strescape()) - * to sanitize non UTF-8 characters. The result is valid - * UTF-8. - * - * The operation can be reverted with g_strcompress() or - * nm_utils_str_utf8safe_unescape(). - * - * Depending on @flags, valid UTF-8 characters are not escaped at all - * (except the escape character '\\'). This is the difference to g_strescape(), - * which escapes all non-ASCII characters. This allows to pass on - * valid UTF-8 characters as-is and can be directly shown to the user - * as UTF-8 -- with exception of the backslash escape character, - * invalid UTF-8 sequences, and other (depending on @flags). - * - * Returns: the escaped input string, as valid UTF-8. If no escaping - * is necessary, it returns the input @str. Otherwise, an allocated - * string @to_free is returned which must be freed by the caller - * with g_free. The escaping can be reverted by g_strcompress(). - **/ -const char * -nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) -{ - return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free); -} - /** * nm_utils_str_utf8safe_escape_cp: * @str: NUL terminated input string, possibly in utf-8 encoding @@ -1660,209 +1350,6 @@ nm_utils_strv_make_deep_copied (const char **strv) /*****************************************************************************/ -gssize -nm_utils_ptrarray_find_binary_search (gconstpointer *list, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data, - gssize *out_idx_first, - gssize *out_idx_last) -{ - gssize imin, imax, imid, i2min, i2max, i2mid; - int cmp; - - g_return_val_if_fail (list || !len, ~((gssize) 0)); - g_return_val_if_fail (cmpfcn, ~((gssize) 0)); - - imin = 0; - if (len > 0) { - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn (list[imid], needle, user_data); - if (cmp == 0) { - /* we found a matching entry at index imid. - * - * Does the caller request the first/last index as well (in case that - * there are multiple entries which compare equal). */ - - if (out_idx_first) { - i2min = imin; - i2max = imid + 1; - while (i2min <= i2max) { - i2mid = i2min + (i2max - i2min) / 2; - - cmp = cmpfcn (list[i2mid], needle, user_data); - if (cmp == 0) - i2max = i2mid -1; - else { - nm_assert (cmp < 0); - i2min = i2mid + 1; - } - } - *out_idx_first = i2min; - } - if (out_idx_last) { - i2min = imid + 1; - i2max = imax; - while (i2min <= i2max) { - i2mid = i2min + (i2max - i2min) / 2; - - cmp = cmpfcn (list[i2mid], needle, user_data); - if (cmp == 0) - i2min = i2mid + 1; - else { - nm_assert (cmp > 0); - i2max = i2mid - 1; - } - } - *out_idx_last = i2min - 1; - } - return imid; - } - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - imin = ~imin; - NM_SET_OUT (out_idx_first, imin); - NM_SET_OUT (out_idx_last, imin); - return imin; -} - -/*****************************************************************************/ - -/** - * nm_utils_array_find_binary_search: - * @list: the list to search. It must be sorted according to @cmpfcn ordering. - * @elem_size: the size in bytes of each element in the list - * @len: the number of elements in @list - * @needle: the value that is searched - * @cmpfcn: the compare function. The elements @list are passed as first - * argument to @cmpfcn, while @needle is passed as second. Usually, the - * needle is the same data type as inside the list, however, that is - * not necessary, as long as @cmpfcn takes care to cast the two arguments - * accordingly. - * @user_data: optional argument passed to @cmpfcn - * - * Performs binary search for @needle in @list. On success, returns the - * (non-negative) index where the compare function found the searched element. - * On success, it returns a negative value. Note that the return negative value - * is the bitwise inverse of the position where the element should be inserted. - * - * If the list contains multiple matching elements, an arbitrary index is - * returned. - * - * Returns: the index to the element in the list, or the (negative, bitwise inverted) - * position where it should be. - */ -gssize -nm_utils_array_find_binary_search (gconstpointer list, - gsize elem_size, - gsize len, - gconstpointer needle, - GCompareDataFunc cmpfcn, - gpointer user_data) -{ - gssize imin, imax, imid; - int cmp; - - g_return_val_if_fail (list || !len, ~((gssize) 0)); - g_return_val_if_fail (cmpfcn, ~((gssize) 0)); - g_return_val_if_fail (elem_size > 0, ~((gssize) 0)); - - imin = 0; - if (len == 0) - return ~imin; - - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data); - if (cmp == 0) - return imid; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - } - - /* return the inverse of @imin. This is a negative number, but - * also is ~imin the position where the value should be inserted. */ - return ~imin; -} - -/*****************************************************************************/ - -/** - * nm_utils_hash_table_equal: - * @a: one #GHashTable - * @b: other #GHashTable - * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is - * treated like an empty hash. It means, a %NULL hash will compare equal - * to an empty hash. - * @equal_func: the equality function, for comparing the values. - * If %NULL, the values are not compared. In that case, the function - * only checks, if both dictionaries have the same keys -- according - * to @b's key equality function. - * Note that the values of @a will be passed as first argument - * to @equal_func. - * - * Compares two hash tables, whether they have equal content. - * This only makes sense, if @a and @b have the same key types and - * the same key compare-function. - * - * Returns: %TRUE, if both dictionaries have the same content. - */ -gboolean -nm_utils_hash_table_equal (const GHashTable *a, - const GHashTable *b, - gboolean treat_null_as_empty, - NMUtilsHashTableEqualFunc equal_func) -{ - guint n; - GHashTableIter iter; - gconstpointer key, v_a, v_b; - - if (a == b) - return TRUE; - if (!treat_null_as_empty) { - if (!a || !b) - return FALSE; - } - - n = a ? g_hash_table_size ((GHashTable *) a) : 0; - if (n != (b ? g_hash_table_size ((GHashTable *) b) : 0)) - return FALSE; - - if (n > 0) { - g_hash_table_iter_init (&iter, (GHashTable *) a); - while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &v_a)) { - if (!g_hash_table_lookup_extended ((GHashTable *) b, key, NULL, (gpointer *) &v_b)) - return FALSE; - if ( equal_func - && !equal_func (v_a, v_b)) - return FALSE; - } - } - - return TRUE; -} - -/*****************************************************************************/ - /** * nm_utils_get_start_time_for_pid: * @pid: the process identifier @@ -1882,10 +1369,10 @@ 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; + gs_free gchar *contents = NULL; size_t length; gs_free const char **tokens = NULL; - char *p; + gchar *p; char state = ' '; gint64 ppid = 0; @@ -1912,7 +1399,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) state = p[0]; - tokens = nm_utils_strsplit_set (p, " ", FALSE); + tokens = nm_utils_strsplit_set (p, " "); if (NM_PTRARRAY_LEN (tokens) < 20) goto fail; @@ -2012,60 +1499,3 @@ _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data); } - -/*****************************************************************************/ - -#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t') - -const char * -_nm_utils_escape_spaces (const char *str, char **to_free) -{ - const char *ptr = str; - char *ret, *r; - - *to_free = NULL; - - if (!str) - return NULL; - - while (TRUE) { - if (!*ptr) - return str; - if (IS_SPACE (*ptr)) - break; - ptr++; - } - - ptr = str; - ret = g_new (char, strlen (str) * 2 + 1); - r = ret; - *to_free = ret; - while (*ptr) { - if (IS_SPACE (*ptr)) - *r++ = '\\'; - *r++ = *ptr++; - } - *r = '\0'; - - return ret; -} - -char * -_nm_utils_unescape_spaces (char *str) -{ - guint i, j = 0; - - if (!str) - return NULL; - - for (i = 0; str[i]; i++) { - if (str[i] == '\\' && IS_SPACE (str[i+1])) - i++; - str[j++] = str[i]; - } - str[j] = '\0'; - - return str; -} - -#undef IS_SPACE -- cgit 1.3.0-6-gf8a5 From 6518e361171f64bcaaa4bf868139362ed95cc2e0 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Sat, 20 Oct 2018 01:30:31 +0200 Subject: New upstream version 1.14.2 --- shared/nm-utils/nm-shared-utils.c | 720 +++++++++++++++++++++++++++++++++++--- 1 file changed, 667 insertions(+), 53 deletions(-) (limited to 'shared/nm-utils/nm-shared-utils.c') diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index d0019c11..ba38237b 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -97,7 +97,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) { char *p = *buf; va_list args; - gint retval; + int retval; if (*len == 0) return; @@ -106,7 +106,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) retval = g_vsnprintf (p, *len, format, args); va_end (args); - if (retval >= *len) { + if ((gsize) retval >= *len) { *buf = &p[*len]; *len = 0; } else { @@ -115,6 +115,131 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) } } +/** + * nm_utils_strbuf_seek_end: + * @buf: the input/output buffer + * @len: the input/output lenght of the buffer. + * + * Commonly, one uses nm_utils_strbuf_append*(), to incrementally + * append strings to the buffer. However, sometimes we need to use + * existing API to write to the buffer. + * After doing so, we want to adjust the buffer counter. + * Essentially, + * + * g_snprintf (buf, len, ...); + * nm_utils_strbuf_seek_end (&buf, &len); + * + * is almost the same as + * + * nm_utils_strbuf_append (&buf, &len, ...); + * + * The only difference is the behavior when the string got truncated: + * nm_utils_strbuf_append() will recognize that and set the remaining + * length to zero. + * + * In general, the behavior is: + * + * - if *len is zero, do nothing + * - if the buffer contains a NUL byte within the first *len characters, + * the buffer is pointed to the NUL byte and len is adjusted. In this + * case, the remaining *len is always >= 1. + * In particular, that is also the case if the NUL byte is at the very last + * position ((*buf)[*len -1]). That happens, when the previous operation + * either fit the string exactly into the buffer or the string was truncated + * by g_snprintf(). The difference cannot be determined. + * - if the buffer contains no NUL bytes within the first *len characters, + * write NUL at the last position, set *len to zero, and point *buf past + * the NUL byte. This would happen with + * + * strncpy (buf, long_str, len); + * nm_utils_strbuf_seek_end (&buf, &len). + * + * where strncpy() does truncate the string and not NUL terminate it. + * nm_utils_strbuf_seek_end() would then NUL terminate it. + */ +void +nm_utils_strbuf_seek_end (char **buf, gsize *len) +{ + gsize l; + char *end; + + nm_assert (len); + nm_assert (buf && *buf); + + if (*len <= 1) { + if ( *len == 1 + && (*buf)[0]) + goto truncate; + return; + } + + end = memchr (*buf, 0, *len); + if (end) { + l = end - *buf; + nm_assert (l < *len); + + *buf = end; + *len -= l; + return; + } + +truncate: + /* hm, no NUL character within len bytes. + * Just NUL terminate the array and consume them + * all. */ + *buf += *len; + (*buf)[-1] = '\0'; + *len = 0; + return; +} + +/*****************************************************************************/ + +/** + * nm_utils_gbytes_equals: + * @bytes: (allow-none): a #GBytes array to compare. Note that + * %NULL is treated like an #GBytes array of length zero. + * @mem_data: the data pointer with @mem_len bytes + * @mem_len: the length of the data pointer + * + * Returns: %TRUE if @bytes contains the same data as @mem_data. As a + * special case, a %NULL @bytes is treated like an empty array. + */ +gboolean +nm_utils_gbytes_equal_mem (GBytes *bytes, + gconstpointer mem_data, + gsize mem_len) +{ + gconstpointer p; + gsize l; + + if (!bytes) { + /* as a special case, let %NULL GBytes compare idential + * to an empty array. */ + return (mem_len == 0); + } + + p = g_bytes_get_data (bytes, &l); + return l == mem_len + && ( mem_len == 0 /* allow @mem_data to be %NULL */ + || memcmp (p, mem_data, mem_len) == 0); +} + +GVariant * +nm_utils_gbytes_to_variant_ay (GBytes *bytes) +{ + const guint8 *p; + gsize l; + + if (!bytes) { + /* for convenience, accept NULL to return an empty variant */ + return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0); + } + + p = g_bytes_get_data (bytes, &l); + return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, p, l, 1); +} + /*****************************************************************************/ /** @@ -497,6 +622,50 @@ _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 ma 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 = 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] == '-') { + /* I don't know why, but g_ascii_strtoull() accepts minus signs ("-2" gives 18446744073709551614). + * For "-0" that is OK, but otherwise not. */ + errno = ERANGE; + return fallback; + } + + return v; +} + /*****************************************************************************/ /* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). @@ -656,6 +825,7 @@ comp_l: * @str: the string to split. * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n", * like bash's $IFS. + * @allow_escaping: whether delimiters can be escaped by a backslash * * This is a replacement for g_strsplit_set() which avoids copying * each word once (the entire strv array), but instead copies it once @@ -664,6 +834,10 @@ comp_l: * Another difference from g_strsplit_set() is that this never returns * empty words. Multiple delimiters are combined and treated as one. * + * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are + * not treated as a separator. Such delimiters and their escape + * character are copied to the current word without unescaping them. + * * Returns: %NULL if @str is %NULL or contains only delimiters. * Otherwise, a %NULL terminated strv array containing non-empty * words, split at the delimiter characters (delimiter characters @@ -673,7 +847,7 @@ comp_l: * but free everything with g_free(). */ const char ** -nm_utils_strsplit_set (const char *str, const char *delimiters) +nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping) { const char **ptr, **ptr0; gsize alloc_size, plen, i; @@ -681,6 +855,7 @@ nm_utils_strsplit_set (const char *str, const char *delimiters) char *s0; char *s; guint8 delimiters_table[256]; + gboolean escaped = FALSE; if (!str) return NULL; @@ -692,13 +867,23 @@ nm_utils_strsplit_set (const char *str, const char *delimiters) for (i = 0; delimiters[i]; i++) delimiters_table[(guint8) delimiters[i]] = 1; -#define _is_delimiter(ch, delimiters_table) \ - ((delimiters_table)[(guint8) (ch)] != 0) +#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \ + ((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc)) + +#define next_char(p, esc) \ + G_STMT_START { \ + if (esc) \ + esc = FALSE; \ + else \ + esc = p[0] == '\\'; \ + p++; \ + } G_STMT_END /* skip initial delimiters, and return of the remaining string is * empty. */ - while (_is_delimiter (str[0], delimiters_table)) - str++; + while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped)) + next_char (str, escaped); + if (!str[0]) return NULL; @@ -730,20 +915,20 @@ nm_utils_strsplit_set (const char *str, const char *delimiters) ptr[plen++] = s; - nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table)); + nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)); while (TRUE) { - s++; - if (_is_delimiter (s[0], delimiters_table)) + next_char (s, escaped); + if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) break; if (s[0] == '\0') goto done; } s[0] = '\0'; - s++; - while (_is_delimiter (s[0], delimiters_table)) - s++; + next_char (s, escaped); + while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) + next_char (s, escaped); if (s[0] == '\0') break; } @@ -840,9 +1025,9 @@ _nm_utils_strv_cleanup (char **strv, /*****************************************************************************/ -gint +int _nm_utils_ascii_str_to_bool (const char *str, - gint default_value) + int default_value) { gsize len; char *s = NULL; @@ -924,7 +1109,7 @@ nm_utils_error_is_cancelled (GError *error, */ gboolean nm_g_object_set_property (GObject *object, - const gchar *property_name, + const char *property_name, const GValue *value, GError **error) { @@ -999,7 +1184,7 @@ nm_g_object_set_property (GObject *object, gboolean nm_g_object_set_property_boolean (GObject *object, - const gchar *property_name, + const char *property_name, gboolean value, GError **error) { @@ -1012,7 +1197,7 @@ nm_g_object_set_property_boolean (GObject *object, gboolean nm_g_object_set_property_uint (GObject *object, - const gchar *property_name, + const char *property_name, guint value, GError **error) { @@ -1044,20 +1229,112 @@ _str_append_escape (GString *s, char ch) g_string_append_c (s, '0' + ( ((guchar) ch) & 07)); } +gconstpointer +nm_utils_buf_utf8safe_unescape (const char *str, gsize *out_len, gpointer *to_free) +{ + GString *gstr; + gsize len; + const char *s; + + g_return_val_if_fail (to_free, NULL); + g_return_val_if_fail (out_len, NULL); + + if (!str) { + *out_len = 0; + *to_free = NULL; + return NULL; + } + + len = strlen (str); + + s = memchr (str, '\\', len); + if (!s) { + *out_len = len; + *to_free = NULL; + return str; + } + + gstr = g_string_new_len (NULL, len); + + g_string_append_len (gstr, str, s - str); + str = s; + + for (;;) { + char ch; + guint v; + + nm_assert (str[0] == '\\'); + + ch = (++str)[0]; + + if (ch == '\0') { + // error. Trailing '\\' + break; + } + + if (ch >= '0' && ch <= '9') { + v = ch - '0'; + ch = (++str)[0]; + if (ch >= '0' && ch <= '7') { + v = v * 8 + (ch - '0'); + ch = (++str)[0]; + if (ch >= '0' && ch <= '7') { + v = v * 8 + (ch - '0'); + ++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++; + } + + g_string_append_c (gstr, ch); + + s = strchr (str, '\\'); + if (!s) { + g_string_append (gstr, str); + break; + } + + g_string_append_len (gstr, str, s - str); + str = s; + } + + *out_len = gstr->len; + *to_free = gstr->str; + return g_string_free (gstr, FALSE); +} + /** - * nm_utils_str_utf8safe_escape: - * @str: NUL terminated input string, possibly in utf-8 encoding + * nm_utils_buf_utf8safe_escape: + * @buf: byte array, possibly in utf-8 encoding, may have NUL characters. + * @buflen: the length of @buf in bytes, or -1 if @buf is a NUL terminated + * string. * @flags: #NMUtilsStrUtf8SafeFlags flags * @to_free: (out): return the pointer location of the string * if a copying was necessary. * - * Returns the possible non-UTF-8 NUL terminated string @str - * and uses backslash escaping (C escaping, like g_strescape()) - * to sanitize non UTF-8 characters. The result is valid + * Based on the assumption, that @buf contains UTF-8 encoded bytes, + * this will return valid UTF-8 sequence, and invalid sequences + * will be escaped with backslash (C escaping, like g_strescape()). + * This is sanitize non UTF-8 characters. The result is valid * UTF-8. * - * The operation can be reverted with g_strcompress() or - * nm_utils_str_utf8safe_unescape(). + * The operation can be reverted with nm_utils_buf_utf8safe_unescape(). + * Note that if, and only if @buf contains no NUL bytes, the operation + * can also be reverted with g_strcompress(). * * Depending on @flags, valid UTF-8 characters are not escaped at all * (except the escape character '\\'). This is the difference to g_strescape(), @@ -1066,61 +1343,105 @@ _str_append_escape (GString *s, char ch) * as UTF-8 -- with exception of the backslash escape character, * invalid UTF-8 sequences, and other (depending on @flags). * - * Returns: the escaped input string, as valid UTF-8. If no escaping - * is necessary, it returns the input @str. Otherwise, an allocated + * Returns: the escaped input buffer, as valid UTF-8. If no escaping + * is necessary, it returns the input @buf. Otherwise, an allocated * string @to_free is returned which must be freed by the caller * with g_free. The escaping can be reverted by g_strcompress(). **/ const char * -nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) +nm_utils_buf_utf8safe_escape (gconstpointer buf, gssize buflen, NMUtilsStrUtf8SafeFlags flags, char **to_free) { + const char *const str = buf; const char *p = NULL; - GString *s; + const char *s; + gboolean nul_terminated = FALSE; + GString *gstr; g_return_val_if_fail (to_free, NULL); *to_free = NULL; - if (!str || !str[0]) - return str; - if ( g_utf8_validate (str, -1, &p) - && !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; + 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; + } - s = g_string_sized_new ((p - str) + strlen (p) + 5); + gstr = g_string_sized_new (buflen + 5); + s = str; do { - for (; str < p; str++) { - char ch = str[0]; + buflen -= p - s; + nm_assert (buflen >= 0); + + for (; s < p; s++) { + char ch = s[0]; if (ch == '\\') - g_string_append (s, "\\\\"); + g_string_append (gstr, "\\\\"); else if ( ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL) \ && ch < ' ') \ || ( NM_FLAGS_HAS (flags, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII) \ && ((guchar) ch) >= 127)) - _str_append_escape (s, ch); + _str_append_escape (gstr, ch); else - g_string_append_c (s, ch); + g_string_append_c (gstr, ch); } - if (p[0] == '\0') + if (buflen <= 0) + break; + + _str_append_escape (gstr, p[0]); + + buflen--; + if (buflen == 0) break; - _str_append_escape (s, p[0]); - str = &p[1]; - g_utf8_validate (str, -1, &p); + s = &p[1]; + g_utf8_validate (s, buflen, &p); } while (TRUE); - *to_free = g_string_free (s, FALSE); + *to_free = g_string_free (gstr, FALSE); return *to_free; } +const char * +nm_utils_buf_utf8safe_escape_bytes (GBytes *bytes, NMUtilsStrUtf8SafeFlags flags, char **to_free) +{ + gconstpointer p; + gsize l; + + if (bytes) + p = g_bytes_get_data (bytes, &l); + else { + p = NULL; + l = 0; + } + + return nm_utils_buf_utf8safe_escape (p, l, flags, to_free); +} + +/*****************************************************************************/ + const char * nm_utils_str_utf8safe_unescape (const char *str, char **to_free) { @@ -1133,6 +1454,39 @@ nm_utils_str_utf8safe_unescape (const char *str, char **to_free) return (*to_free = g_strcompress (str)); } +/** + * nm_utils_str_utf8safe_escape: + * @str: NUL terminated input string, possibly in utf-8 encoding + * @flags: #NMUtilsStrUtf8SafeFlags flags + * @to_free: (out): return the pointer location of the string + * if a copying was necessary. + * + * Returns the possible non-UTF-8 NUL terminated string @str + * and uses backslash escaping (C escaping, like g_strescape()) + * to sanitize non UTF-8 characters. The result is valid + * UTF-8. + * + * The operation can be reverted with g_strcompress() or + * nm_utils_str_utf8safe_unescape(). + * + * Depending on @flags, valid UTF-8 characters are not escaped at all + * (except the escape character '\\'). This is the difference to g_strescape(), + * which escapes all non-ASCII characters. This allows to pass on + * valid UTF-8 characters as-is and can be directly shown to the user + * as UTF-8 -- with exception of the backslash escape character, + * invalid UTF-8 sequences, and other (depending on @flags). + * + * Returns: the escaped input string, as valid UTF-8. If no escaping + * is necessary, it returns the input @str. Otherwise, an allocated + * string @to_free is returned which must be freed by the caller + * with g_free. The escaping can be reverted by g_strcompress(). + **/ +const char * +nm_utils_str_utf8safe_escape (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free) +{ + return nm_utils_buf_utf8safe_escape (str, -1, flags, to_free); +} + /** * nm_utils_str_utf8safe_escape_cp: * @str: NUL terminated input string, possibly in utf-8 encoding @@ -1350,6 +1704,209 @@ nm_utils_strv_make_deep_copied (const char **strv) /*****************************************************************************/ +gssize +nm_utils_ptrarray_find_binary_search (gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last) +{ + gssize imin, imax, imid, i2min, i2max, i2mid; + int cmp; + + g_return_val_if_fail (list || !len, ~((gssize) 0)); + g_return_val_if_fail (cmpfcn, ~((gssize) 0)); + + imin = 0; + if (len > 0) { + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn (list[imid], needle, user_data); + if (cmp == 0) { + /* we found a matching entry at index imid. + * + * Does the caller request the first/last index as well (in case that + * there are multiple entries which compare equal). */ + + if (out_idx_first) { + i2min = imin; + i2max = imid + 1; + while (i2min <= i2max) { + i2mid = i2min + (i2max - i2min) / 2; + + cmp = cmpfcn (list[i2mid], needle, user_data); + if (cmp == 0) + i2max = i2mid -1; + else { + nm_assert (cmp < 0); + i2min = i2mid + 1; + } + } + *out_idx_first = i2min; + } + if (out_idx_last) { + i2min = imid + 1; + i2max = imax; + while (i2min <= i2max) { + i2mid = i2min + (i2max - i2min) / 2; + + cmp = cmpfcn (list[i2mid], needle, user_data); + if (cmp == 0) + i2min = i2mid + 1; + else { + nm_assert (cmp > 0); + i2max = i2mid - 1; + } + } + *out_idx_last = i2min - 1; + } + return imid; + } + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + } + + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + imin = ~imin; + NM_SET_OUT (out_idx_first, imin); + NM_SET_OUT (out_idx_last, imin); + return imin; +} + +/*****************************************************************************/ + +/** + * nm_utils_array_find_binary_search: + * @list: the list to search. It must be sorted according to @cmpfcn ordering. + * @elem_size: the size in bytes of each element in the list + * @len: the number of elements in @list + * @needle: the value that is searched + * @cmpfcn: the compare function. The elements @list are passed as first + * argument to @cmpfcn, while @needle is passed as second. Usually, the + * needle is the same data type as inside the list, however, that is + * not necessary, as long as @cmpfcn takes care to cast the two arguments + * accordingly. + * @user_data: optional argument passed to @cmpfcn + * + * Performs binary search for @needle in @list. On success, returns the + * (non-negative) index where the compare function found the searched element. + * On success, it returns a negative value. Note that the return negative value + * is the bitwise inverse of the position where the element should be inserted. + * + * If the list contains multiple matching elements, an arbitrary index is + * returned. + * + * Returns: the index to the element in the list, or the (negative, bitwise inverted) + * position where it should be. + */ +gssize +nm_utils_array_find_binary_search (gconstpointer list, + gsize elem_size, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data) +{ + gssize imin, imax, imid; + int cmp; + + g_return_val_if_fail (list || !len, ~((gssize) 0)); + g_return_val_if_fail (cmpfcn, ~((gssize) 0)); + g_return_val_if_fail (elem_size > 0, ~((gssize) 0)); + + imin = 0; + if (len == 0) + return ~imin; + + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn (&((const char *) list)[elem_size * imid], needle, user_data); + if (cmp == 0) + return imid; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + /* return the inverse of @imin. This is a negative number, but + * also is ~imin the position where the value should be inserted. */ + return ~imin; +} + +/*****************************************************************************/ + +/** + * nm_utils_hash_table_equal: + * @a: one #GHashTable + * @b: other #GHashTable + * @treat_null_as_empty: if %TRUE, when either @a or @b is %NULL, it is + * treated like an empty hash. It means, a %NULL hash will compare equal + * to an empty hash. + * @equal_func: the equality function, for comparing the values. + * If %NULL, the values are not compared. In that case, the function + * only checks, if both dictionaries have the same keys -- according + * to @b's key equality function. + * Note that the values of @a will be passed as first argument + * to @equal_func. + * + * Compares two hash tables, whether they have equal content. + * This only makes sense, if @a and @b have the same key types and + * the same key compare-function. + * + * Returns: %TRUE, if both dictionaries have the same content. + */ +gboolean +nm_utils_hash_table_equal (const GHashTable *a, + const GHashTable *b, + gboolean treat_null_as_empty, + NMUtilsHashTableEqualFunc equal_func) +{ + guint n; + GHashTableIter iter; + gconstpointer key, v_a, v_b; + + if (a == b) + return TRUE; + if (!treat_null_as_empty) { + if (!a || !b) + return FALSE; + } + + n = a ? g_hash_table_size ((GHashTable *) a) : 0; + if (n != (b ? g_hash_table_size ((GHashTable *) b) : 0)) + return FALSE; + + if (n > 0) { + g_hash_table_iter_init (&iter, (GHashTable *) a); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &v_a)) { + if (!g_hash_table_lookup_extended ((GHashTable *) b, key, NULL, (gpointer *) &v_b)) + return FALSE; + if ( equal_func + && !equal_func (v_a, v_b)) + return FALSE; + } + } + + return TRUE; +} + +/*****************************************************************************/ + /** * nm_utils_get_start_time_for_pid: * @pid: the process identifier @@ -1369,10 +1926,10 @@ 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 gchar *contents = NULL; + gs_free char *contents = NULL; size_t length; gs_free const char **tokens = NULL; - gchar *p; + char *p; char state = ' '; gint64 ppid = 0; @@ -1399,7 +1956,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) state = p[0]; - tokens = nm_utils_strsplit_set (p, " "); + tokens = nm_utils_strsplit_set (p, " ", FALSE); if (NM_PTRARRAY_LEN (tokens) < 20) goto fail; @@ -1499,3 +2056,60 @@ _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data); } + +/*****************************************************************************/ + +#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t') + +const char * +_nm_utils_escape_spaces (const char *str, char **to_free) +{ + const char *ptr = str; + char *ret, *r; + + *to_free = NULL; + + if (!str) + return NULL; + + while (TRUE) { + if (!*ptr) + return str; + if (IS_SPACE (*ptr)) + break; + ptr++; + } + + ptr = str; + ret = g_new (char, strlen (str) * 2 + 1); + r = ret; + *to_free = ret; + while (*ptr) { + if (IS_SPACE (*ptr)) + *r++ = '\\'; + *r++ = *ptr++; + } + *r = '\0'; + + return ret; +} + +char * +_nm_utils_unescape_spaces (char *str) +{ + guint i, j = 0; + + if (!str) + return NULL; + + for (i = 0; str[i]; i++) { + if (str[i] == '\\' && IS_SPACE (str[i+1])) + i++; + str[j++] = str[i]; + } + str[j] = '\0'; + + return str; +} + +#undef IS_SPACE -- cgit 1.3.0-6-gf8a5