gsm: Add APN conversion functions

These functions are currently part of openbsc but also needed by
other projects.

The function have been renamed as follows:

  gprs_apn_to_str -> osmo_apn_to_str
  gprs_str_to_apn -> osmo_apn_from_str

Sponsored-by: On-Waves ehf
diff --git a/src/gsm/apn.c b/src/gsm/apn.c
index 413130a..ccf36b9 100644
--- a/src/gsm/apn.c
+++ b/src/gsm/apn.c
@@ -36,3 +36,78 @@
 	}
 	return osmo_apn_qualify(atoi(cbuf), atoi(nbuf), ni);
 }
+
+/**
+ * Convert an encoded APN into a dot-separated string.
+ *
+ * \param out_str      the destination buffer (size must be >= max(app_enc_len,1))
+ * \param apn_enc      the encoded APN
+ * \param apn_enc_len  the length of the encoded APN
+ *
+ * \returns out_str on success and NULL otherwise
+ */
+char * osmo_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t apn_enc_len)
+{
+	char *str = out_str;
+	size_t rest_chars = apn_enc_len;
+
+	while (rest_chars > 0 && apn_enc[0]) {
+		size_t label_size = apn_enc[0];
+		if (label_size + 1 > rest_chars)
+			return NULL;
+
+		memmove(str, apn_enc + 1, label_size);
+		str += label_size;
+		rest_chars -= label_size + 1;
+		apn_enc += label_size + 1;
+
+		if (rest_chars)
+			*(str++) = '.';
+	}
+	str[0] = '\0';
+
+	return out_str;
+}
+
+/**
+ * Convert a dot-separated string into an encoded APN.
+ *
+ * \param apn_enc          the encoded APN
+ * \param max_apn_enc_len  the size of the apn_enc buffer
+ * \param str              the source string
+ *
+ * \returns out_str on success and NULL otherwise
+ */
+int osmo_apn_from_str(uint8_t *apn_enc, size_t max_apn_enc_len, const char *str)
+{
+	uint8_t *last_len_field;
+	int len;
+
+	/* Can we even write the length field to the output? */
+	if (max_apn_enc_len == 0)
+		return -1;
+
+	/* Remember where we need to put the length once we know it */
+	last_len_field = apn_enc;
+	len = 1;
+	apn_enc += 1;
+
+	while (str[0]) {
+		if (len >= max_apn_enc_len)
+			return -1;
+
+		if (str[0] == '.') {
+			*last_len_field = (apn_enc - last_len_field) - 1;
+			last_len_field = apn_enc;
+		} else {
+			*apn_enc = str[0];
+		}
+		apn_enc += 1;
+		str += 1;
+		len += 1;
+	}
+
+	*last_len_field = (apn_enc - last_len_field) - 1;
+
+	return len;
+}
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 917a77d..7eebe7f 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -270,6 +270,8 @@
 
 osmo_apn_qualify;
 osmo_apn_qualify_from_imsi;
+osmo_apn_to_str;
+osmo_apn_from_str;
 
 local: *;
 };