+char *utoa_internal_dup(wchar_t const *s, int charset, int *lenp, int careful)
+{
+ char *outbuf;
+ int outpos, outlen, len, ret, err;
+ charset_state state = CHARSET_INIT_STATE;
+
+ if (!s) {
+ return dupstr("");
+ }
+
+ len = ustrlen(s);
+
+ outlen = len + 10;
+ outbuf = snewn(outlen, char);
+
+ outpos = 0;
+ outbuf[outpos] = '\0';
+
+ while (len > 0) {
+ err = 0;
+ ret = charset_from_unicode(&s, &len,
+ outbuf + outpos, outlen - outpos - 1,
+ charset, &state, (careful ? &err : NULL));
+ if (err) {
+ sfree(outbuf);
+ return NULL;
+ }
+ if (!ret) {
+ outlen = outlen * 3 / 2;
+ outbuf = sresize(outbuf, outlen, char);
+ }
+ outpos += ret;
+ outbuf[outpos] = '\0';
+ }
+ /*
+ * Clean up
+ */
+ outlen = outpos + 32;
+ outbuf = sresize(outbuf, outlen, char);
+ ret = charset_from_unicode(NULL, 0,
+ outbuf + outpos, outlen - outpos + 1,
+ charset, &state, NULL);
+ outpos += ret;
+ outbuf[outpos] = '\0';
+ if (lenp)
+ *lenp = outpos;
+ return outbuf;
+}
+
+char *utoa_dup(wchar_t const *s, int charset)
+{
+ return utoa_internal_dup(s, charset, NULL, FALSE);
+}
+
+char *utoa_dup_len(wchar_t const *s, int charset, int *len)
+{
+ return utoa_internal_dup(s, charset, len, FALSE);
+}
+
+char *utoa_careful_dup(wchar_t const *s, int charset)
+{
+ return utoa_internal_dup(s, charset, NULL, TRUE);
+}
+
+wchar_t *ufroma_dup(char const *s, int charset) {
+ int len;
+ wchar_t *buf = NULL;
+
+ len = strlen(s) + 1;
+ do {
+ buf = sresize(buf, len, wchar_t);
+ ustrfroma(s, buf, len, charset);
+ len = (3 * len) / 2 + 1; /* this guarantees a strict increase */
+ } while (ustrlen(buf) >= len-1);
+
+ buf = sresize(buf, ustrlen(buf)+1, wchar_t);
+ return buf;
+}
+
+char *utoa_locale_dup(wchar_t const *s)
+{
+ /*
+ * This variant uses the C library locale.
+ */
+ char *ret;
+ int len, outlen;
+ size_t siz;
+
+ len = ustrlen(s);
+
+ outlen = 1 + MB_CUR_MAX * len;
+ ret = snewn(outlen+1, char);
+
+ siz = wcstombs(ret, s, outlen);
+
+ if (siz) {
+ assert(siz <= (size_t)(outlen));
+ ret[siz] = '\0';
+ ret = sresize(ret, siz+1, char);
+ return ret;
+ }
+
+ /*
+ * If that failed, try a different strategy (which we will also
+ * attempt in the total absence of wcstombs). Retrieve the
+ * locale's charset from nl_langinfo or equivalent, and use
+ * normal utoa_dup.
+ */
+ return utoa_dup(s, charset_from_locale());
+}
+
+wchar_t *ufroma_locale_dup(char const *s)
+{
+ /*
+ * This variant uses the C library locale.
+ */
+ wchar_t *ret;
+ int len, outlen;
+ size_t siz;
+
+ len = strlen(s);
+
+ outlen = 1 + 2*len;
+ ret = snewn(outlen+1, wchar_t); /* be conservative */
+
+ siz = mbstowcs(ret, s, outlen);
+
+ if (siz) {
+ assert(siz <= (size_t)(outlen));
+ ret[siz] = L'\0';
+ ret = sresize(ret, siz+1, wchar_t);
+ return ret;
+ }
+
+ /*
+ * If that failed, try a different strategy (which we will also
+ * attempt in the total absence of wcstombs). Retrieve the
+ * locale's charset from nl_langinfo or equivalent, and use
+ * normal ufroma_dup.
+ */
+ return ufroma_dup(s, charset_from_locale());
+}
+
+int ustrlen(wchar_t const *s) {