diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in
index 2b500257c..10d5029ff 100644
--- a/docs/reference/glib/glib-sections.txt.in
+++ b/docs/reference/glib/glib-sections.txt.in
@@ -1464,6 +1464,7 @@ utimbuf
String Utility Functions
string_utils
glib.h,glib/gprintf.h
+g_set_str
g_strdup
g_strndup
g_strdupv
diff --git a/glib/gstrfuncs.h b/glib/gstrfuncs.h
index 37d2728d8..3c1dc45ec 100644
--- a/glib/gstrfuncs.h
+++ b/glib/gstrfuncs.h
@@ -32,9 +32,11 @@
#endif
#include
+#include
#include
#include
#include
+#include
G_BEGIN_DECLS
@@ -363,6 +365,51 @@ gboolean g_ascii_string_to_unsigned (const gchar *str,
guint64 *out_num,
GError **error);
+/**
+ * g_set_str: (skip)
+ * @str_pointer: (inout) (not optional) (nullable): a pointer to either a string or %NULL
+ * @new_str: (nullable): a string to assign to @str_pointer, or %NULL
+ *
+ * Updates a pointer to a string to a copy of @new_str. The previous string
+ * pointed to by @str_pointer will be freed with g_free().
+ *
+ * @str_pointer must not be %NULL, but can point to a %NULL value.
+ *
+ * One convenient usage of this function is in implementing property settings:
+ * |[
+ * void
+ * foo_set_bar (Foo *foo,
+ * const char *new_bar)
+ * {
+ * g_return_if_fail (IS_FOO (foo));
+ *
+ * if (g_set_str (&foo->bar, new_bar))
+ * g_object_notify (foo, "bar");
+ * }
+ * ]|
+ *
+ * Returns: %TRUE if the value of @str_pointer changed, %FALSE otherwise
+ *
+ * Since: 2.76
+ */
+GLIB_AVAILABLE_STATIC_INLINE_IN_2_76
+static inline gboolean
+g_set_str (char **str_pointer,
+ const char *new_str)
+{
+ char *copy;
+
+ if (*str_pointer == new_str ||
+ (*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0))
+ return FALSE;
+
+ copy = g_strdup (new_str);
+ g_free (*str_pointer);
+ *str_pointer = copy;
+
+ return TRUE;
+}
+
G_END_DECLS
#endif /* __G_STRFUNCS_H__ */
diff --git a/glib/tests/strfuncs.c b/glib/tests/strfuncs.c
index 082eec074..0ae3f89c1 100644
--- a/glib/tests/strfuncs.c
+++ b/glib/tests/strfuncs.c
@@ -2546,6 +2546,33 @@ test_ascii_string_to_number_pathological (void)
g_assert_cmpint (svalue, ==, G_MININT64);
}
+static void
+test_set_str (void)
+{
+ char *str = NULL;
+
+ g_assert_false (g_set_str (&str, NULL));
+ g_assert_null (str);
+
+ g_assert_true (g_set_str (&str, ""));
+ g_assert_false (g_set_str (&str, ""));
+ g_assert_nonnull (str);
+ g_assert_true ((gpointer)str != (gpointer)"");
+ g_assert_cmpstr (str, ==, "");
+
+ g_assert_true (g_set_str (&str, NULL));
+ g_assert_null (str);
+
+ g_assert_true (g_set_str (&str, ""));
+ g_assert_true (g_set_str (&str, "test"));
+ g_assert_cmpstr (str, ==, "test");
+
+ g_assert_true (g_set_str (&str, &str[2]));
+ g_assert_cmpstr (str, ==, "st");
+
+ g_free (str);
+}
+
int
main (int argc,
char *argv[])
@@ -2563,6 +2590,7 @@ main (int argc,
g_test_add_func ("/strfuncs/has-suffix", test_has_suffix);
g_test_add_func ("/strfuncs/memdup", test_memdup);
g_test_add_func ("/strfuncs/memdup2", test_memdup2);
+ g_test_add_func ("/strfuncs/set_str", test_set_str);
g_test_add_func ("/strfuncs/stpcpy", test_stpcpy);
g_test_add_func ("/strfuncs/str_match_string", test_str_match_string);
g_test_add_func ("/strfuncs/str_tokenize_and_fold", test_str_tokenize_and_fold);