add osmo_quote_str(),osmo_quote_str_buf() and test

Rationale: with osmo_escape_str(), you get the escaped contents of the string,
but not so graceful handling of NULL strings. The caller needs to quote it, and
for NULL strings not quote it.

osmo_quote_str() is like osmo_escape_str() but always quotes a non-NULL string,
and for a NULL string returns a literal NULL, i.e. it should (tm) give the
exact C representation of a string.

That's useful in testing, to show exactly what char* situation we have, without
jumping through hoops like
  if (str)
  	printf("\"%s\"", osmo_escape_str(str, -1));
  else
  	printf("NULL");

Copy the unit test for osmo_escape_str() and adjust. To indicate that the
double quotes are returned by osmo_quote_str(), use single quotes in the test
printf()s.

I considered allowing to pick the quoting characters by further arguments, but
that complicates things: we'd need to escape the quoting characters. Just
hardcode double quotes like C.

Change-Id: I6f1b3709b32c23fc52f70ad9ecc9439c62b02a12
diff --git a/src/utils.c b/src/utils.c
index 109aac0..32ea87c 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -554,4 +554,42 @@
 	return osmo_escape_str_buf(str, in_len, namebuf, sizeof(namebuf));
 }
 
+/*! Like osmo_escape_str(), but returns double-quotes around a string, or "NULL" for a NULL string.
+ * This allows passing any char* value and get its C representation as string.
+ * \param[in] str  A string that may contain any characters.
+ * \param[in] len  Pass -1 to print until nul char, or >= 0 to force a length.
+ * \returns buf containing an escaped representation, possibly truncated, or str itself.
+ */
+const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize)
+{
+	const char *res;
+	int l;
+	if (!str)
+		return "NULL";
+	if (bufsize < 3)
+		return "<buf-too-small>";
+	buf[0] = '"';
+	res = osmo_escape_str_buf(str, in_len, buf + 1, bufsize - 2);
+	/* if osmo_escape_str_buf() returned the str itself, we need to copy it to buf to be able to
+	 * quote it. */
+	if (res == str) {
+		/* max_len = bufsize - two quotes - nul term */
+		int max_len = bufsize - 2 - 1;
+		if (in_len >= 0)
+			max_len = OSMO_MIN(in_len, max_len);
+		/* It is not allowed to pass unterminated strings into osmo_strlcpy() :/ */
+		strncpy(buf + 1, str, max_len);
+		buf[1 + max_len] = '\0';
+	}
+	l = strlen(buf);
+	buf[l] = '"';
+	buf[l+1] = '\0'; /* both osmo_escape_str_buf() and max_len above ensure room for '\0' */
+	return buf;
+}
+
+const char *osmo_quote_str(const char *str, int in_len)
+{
+	return osmo_quote_str_buf(str, in_len, namebuf, sizeof(namebuf));
+}
+
 /*! @} */