about summary refs log tree commit diff
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/nm-utils/nm-macros-internal.h13
-rw-r--r--shared/nm-utils/nm-shared-utils.c138
-rw-r--r--shared/nm-utils/nm-shared-utils.h16
-rw-r--r--shared/nm-utils/nm-test-utils.h9
-rw-r--r--shared/nm-utils/nm-udev-utils.c12
-rw-r--r--shared/nm-version-macros.h2
6 files changed, 175 insertions, 15 deletions
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 6ca66d29..5fe4bc50 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -609,18 +609,13 @@ nm_clear_g_cancellable (GCancellable **cancellable)
 /*****************************************************************************/
 
 /* Determine whether @x is a power of two (@x being an integer type).
- * For the special cases @x equals zero or one, it also returns true.
- * In case @x being a signed type, for negative @x always return FALSE. */
+ * Basically, this returns TRUE, if @x has exactly one bit set.
+ * For negative values and zero, this always returns FALSE. */
 #define nm_utils_is_power_of_two(x) ({ \
 		typeof(x) __x = (x); \
 		\
-		/* Check if the value is negative. In that case, return FALSE.
-		 * The first expression is a compile time constant, depending on whether
-		 * the type is signed. The second expression is a clumsy way for (__x >= 0),
-		 * which otherwise causes a compiler warning for unsigned types. */ \
-		    (    (((typeof(__x)) -1) > ((typeof(__x)) 0)) \
-		      || (__x > 0 || __x == 0) ) \
-		 && ((__x & (__x - 1)) == 0); \
+		(    (__x > ((typeof(__x)) 0)) \
+		 && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \
 	})
 
 /*****************************************************************************/
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index 413526dc..e7f31cbc 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -364,3 +364,141 @@ nm_g_object_set_property (GObject *object,
 }
 
 /*****************************************************************************/
+
+static void
+_str_append_escape (GString *s, char ch)
+{
+	g_string_append_c (s, '\\');
+	g_string_append_c (s, '0' + ((((guchar) ch) >> 6) & 07));
+	g_string_append_c (s, '0' + ((((guchar) ch) >> 3) & 07));
+	g_string_append_c (s, '0' + ( ((guchar) ch)       & 07));
+}
+
+/**
+ * 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)
+{
+	const char *p = NULL;
+	GString *s;
+
+	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;
+
+	s = g_string_sized_new ((p - str) + strlen (p) + 5);
+
+	do {
+		for (; str < p; str++) {
+			char ch = str[0];
+
+			if (ch == '\\')
+				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 (s, ch);
+			else
+				g_string_append_c (s, ch);
+		}
+
+		if (p[0] == '\0')
+			break;
+		_str_append_escape (s, p[0]);
+
+		str = &p[1];
+		g_utf8_validate (str, -1, &p);
+	} while (TRUE);
+
+	*to_free = g_string_free (s, FALSE);
+	return *to_free;
+}
+
+const char *
+nm_utils_str_utf8safe_unescape (const char *str, char **to_free)
+{
+	g_return_val_if_fail (to_free, NULL);
+
+	if (!str || !strchr (str, '\\')) {
+		*to_free = NULL;
+		return str;
+	}
+	return (*to_free = g_strcompress (str));
+}
+
+/**
+ * nm_utils_str_utf8safe_escape_cp:
+ * @str: NUL terminated input string, possibly in utf-8 encoding
+ * @flags: #NMUtilsStrUtf8SafeFlags flags
+ *
+ * Like nm_utils_str_utf8safe_escape(), except the returned value
+ * is always a copy of the input and must be freed by the caller.
+ *
+ * Returns: the escaped input string in UTF-8 encoding. The returned
+ *   value should be freed with g_free().
+ *   The escaping can be reverted by g_strcompress().
+ **/
+char *
+nm_utils_str_utf8safe_escape_cp (const char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+	char *s;
+
+	nm_utils_str_utf8safe_escape (str, flags, &s);
+	return s ?: g_strdup (str);
+}
+
+char *
+nm_utils_str_utf8safe_unescape_cp (const char *str)
+{
+	return str ? g_strcompress (str) : NULL;
+}
+
+char *
+nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags)
+{
+	char *str_to_free;
+
+	nm_utils_str_utf8safe_escape (str, flags, &str_to_free);
+	if (str_to_free) {
+		g_free (str);
+		return str_to_free;
+	}
+	return str;
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index f1f9f518..69f9533d 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -86,4 +86,20 @@ gboolean nm_g_object_set_property (GObject *object,
 
 /*****************************************************************************/
 
+typedef enum {
+	NM_UTILS_STR_UTF8_SAFE_FLAG_NONE                = 0,
+	NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL         = 0x0001,
+	NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII    = 0x0002,
+} NMUtilsStrUtf8SafeFlags;
+
+const char *nm_utils_str_utf8safe_escape   (const char *str, NMUtilsStrUtf8SafeFlags flags, char **to_free);
+const char *nm_utils_str_utf8safe_unescape (const char *str, char **to_free);
+
+char *nm_utils_str_utf8safe_escape_cp   (const char *str, NMUtilsStrUtf8SafeFlags flags);
+char *nm_utils_str_utf8safe_unescape_cp (const char *str);
+
+char *nm_utils_str_utf8safe_escape_take (char *str, NMUtilsStrUtf8SafeFlags flags);
+
+/*****************************************************************************/
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index 0dfdfce6..bc521131 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -1183,6 +1183,15 @@ nmtst_file_get_contents (const char *filename)
 	return contents;
 }
 
+#define nmtst_file_set_contents(filename, content) \
+	G_STMT_START { \
+		GError *_error = NULL; \
+		gboolean _success; \
+		\
+		_success = g_file_set_contents ((filename), (content), -1, &_error); \
+		nmtst_assert_success (_success, _error); \
+	} G_STMT_END
+
 /*****************************************************************************/
 
 static inline void
diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-utils/nm-udev-utils.c
index 5552d592..79d4426d 100644
--- a/shared/nm-utils/nm-udev-utils.c
+++ b/shared/nm-utils/nm-udev-utils.c
@@ -67,8 +67,9 @@ nm_udev_utils_property_decode (const char *uproperty, char **to_free)
 		if (   p[0] == '\\'
 		    && p[1] == 'x'
 		    && (a = g_ascii_xdigit_value (p[2])) >= 0
-		    && (b = g_ascii_xdigit_value (p[3])) >= 0) {
-			if (!unescaped) {
+		    && (b = g_ascii_xdigit_value (p[3])) >= 0
+		    && (a || b)) {
+			if (!n) {
 				gssize l = p - uproperty;
 
 				unescaped = g_malloc (l + strlen (p) + 1 - 3);
@@ -84,11 +85,12 @@ nm_udev_utils_property_decode (const char *uproperty, char **to_free)
 		}
 	}
 
-	if (!unescaped) {
+	if (!n) {
 		*to_free = NULL;
 		return uproperty;
 	}
 
+	*n++ = '\0';
 	return (*to_free = unescaped);
 }
 
@@ -144,7 +146,7 @@ nm_udev_utils_enumerate (struct udev *uclient,
 		for (n = 0; subsystems[n]; n++) {
 			const char *subsystem;
 			const char *devtype;
-			gs_free char *to_free;
+			gs_free char *to_free = NULL;
 
 			_subsystem_split (subsystems[n], &subsystem, &devtype, &to_free);
 
@@ -240,7 +242,7 @@ nm_udev_client_new (const char *const*subsystems,
 			/* install subsystem filters to only wake up for certain events */
 			for (n = 0; self->subsystems[n]; n++) {
 				if (self->monitor) {
-					gs_free char *to_free;
+					gs_free char *to_free = NULL;
 					const char *subsystem;
 					const char *devtype;
 
diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h
index a0b61c6f..e7a2f329 100644
--- a/shared/nm-version-macros.h
+++ b/shared/nm-version-macros.h
@@ -45,7 +45,7 @@
  * Evaluates to the micro version number of NetworkManager which this source
  * compiled against.
  */
-#define NM_MICRO_VERSION (0)
+#define NM_MICRO_VERSION (2)
 
 /**
  * NM_CHECK_VERSION: